diff --git a/.gitignore b/.gitignore index 3d72576..cabdd8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ +/composer.lock +/vendor/ + .DS_Store -.idea \ No newline at end of file +.idea diff --git a/.travis.yml b/.travis.yml index 42d63fd..3cd27af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,103 +1,71 @@ language: php +sudo: true php: - - 5.5 - - 5.6 - - 7.0 - -matrix: - allow_failures: - - php: 7.0 +- 5.6 +- 7.0 +#- 7.1 +#- 7.2 env: global: - - MESSAGE_MODULE='message' - - MESSAGE_REPO='https://github.com/Gizra/message.git' - - MESSAGE_VERSION='8.x-1.x' - - MESSAGE_NOTIFY_MODULE='message_notify' - - MESSAGE_NOTIFY_REPO='https://github.com/Gizra/message_notify.git' - - MESSAGE_NOTIFY_VERSION='8.x-1.x' - - MESSAGE_UI_MODULE='message_ui' - - MESSAGE_UI_REPO='https://github.com/mccrodp/message_ui.git' - - MESSAGE_UI_VERSION='8.x-1.x' - - MODULE_NAME='message_private' - - MODULE_TEST_GROUP='Message Private' - - DRUPAL_REPO='git://drupalcode.org/project/drupal.git' - - DRUPAL_VERSION='8.0.5' - - PHPCS_VERSION='2.0.*@dev' - - CODER_VERSION='8.2.0-beta1' - -before_install: - # Ensure we have the latest sources. - - sudo apt-get -y update - - # Composer. - - sed -i '1i export PATH="$HOME/.composer/vendor/bin:$PATH"' $HOME/.bashrc - - source $HOME/.bashrc - # - composer self-update - - # Drush. - - composer global require "youngj/httpserver:dev-master#41dd2b7" - - composer global require drush/drush:~8 - - # Codesniffer. - - composer global require squizlabs/php_codesniffer:$PHPCS_VERSION - - # Coder. - - composer global require drupal/coder:$CODER_VERSION - - ln -s ~/.composer/vendor/drupal/coder/coder_sniffer/Drupal ~/.composer/vendor/squizlabs/php_codesniffer/CodeSniffer/Standards/ + - COMPOSER_MEMORY_LIMIT=2G + - DRUPAL_DIR=${TRAVIS_BUILD_DIR}/../build + - MODULES_DIR=${DRUPAL_DIR}/modules/contrib + - MODULE_DIR=${MODULES_DIR}/message_private + matrix: + - TEST_SUITE=PHP_CodeSniffer + - TEST_SUITE=8.5.x +# - TEST_SUITE=8.6.x +# - TEST_SUITE=8.7.x + +# Only run the coding standards check once. +matrix: + exclude: + - php: 5.6 + env: TEST_SUITE=PHP_CodeSniffer +# - php: 7.0 +# env: TEST_SUITE=PHP_CodeSniffer +# - php: 7.1 +# env: TEST_SUITE=PHP_CodeSniffer + +mysql: + database: message_private + username: root + encoding: utf8 - # Ensure the PHP environment is ready. - - phpenv rehash +before_script: +# Remove Xdebug as we don't need it and it causes "PHP Fatal error: Maximum +# function nesting level of '256' reached." +# We also don't care if that file exists or not on PHP 7. +- phpenv config-rm xdebug.ini || true -install: - # Basic PHP packages. - - sudo apt-get install -y --force-yes php5-cgi php5-mysql +# Require the version of Drupal 8 core under test. +- test ${TEST_SUITE} == "PHP_CodeSniffer" || composer require --no-update drupal/core:$TEST_SUITE webflo/drupal-core-require-dev:$TEST_SUITE - # Move the checked out module into the correct structure. - - mkdir /tmp/$MODULE_NAME - - mv * /tmp/$MODULE_NAME/ - - git clone --branch $DRUPAL_VERSION $DRUPAL_REPO drupal --depth 1 - - mv /tmp/$MODULE_NAME drupal/modules/ - - cd drupal/modules - - git clone --branch $MESSAGE_VERSION $MESSAGE_REPO - - git clone --branch $MESSAGE_NOTIFY_VERSION $MESSAGE_NOTIFY_REPO - - git clone --branch $MESSAGE_UI_VERSION $MESSAGE_UI_REPO - - cd ../ +# PHPUnit 6 is required when running tests on PHP 7.x. +- test ${TEST_SUITE} == "PHP_CodeSniffer" || test ${TRAVIS_PHP_VERSION:0:3} == "5.6" || composer require --no-update --update-with-dependencies --dev phpunit/phpunit:~6 -before_script: +# Install dependencies. +- composer install - # Install Composer dependencies. - - composer self-update && composer install +# Create database. +- test ${TEST_SUITE} == "PHP_CodeSniffer" || mysql -e 'CREATE DATABASE message_private' - # This fixes a fail when install Drupal. - - echo 'sendmail_path = /bin/true' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini +# Symlink the module into the Drupal site. +- test ${TEST_SUITE} == "PHP_CodeSniffer" || mkdir -p $MODULES_DIR +- test ${TEST_SUITE} == "PHP_CodeSniffer" || ln -s $TRAVIS_BUILD_DIR $MODULE_DIR - # Mysql might time out for long tests, increase the wait timeout. - - mysql -e 'SET @@GLOBAL.wait_timeout=1200' +# Start a web server on port 8888 in the background. +- test ${TEST_SUITE} == "PHP_CodeSniffer" || nohup php -S localhost:8888 --docroot $DRUPAL_DIR > /dev/null 2>&1 & - # Install Drupal and enable the required modules (including this one). - - mysql -e 'create database drupal;' - - cd $TRAVIS_BUILD_DIR/drupal && drush --yes site-install testing --db-url="mysql://root@127.0.0.1/drupal" - - cd $TRAVIS_BUILD_DIR/drupal && drush --yes en $MODULE_NAME - - cd $TRAVIS_BUILD_DIR/drupal && drush --yes en simpletest +# Wait until the web server is responding. +- test ${TEST_SUITE} == "PHP_CodeSniffer" || until curl -s localhost:8888; do true; done > /dev/null - # Start a web server. - - drush runserver 127.0.0.1:8080 & - - until netstat -an 2>/dev/null | grep '8080.*LISTEN'; do true; done +# Export web server URL for browser tests. +- test ${TEST_SUITE} == "PHP_CodeSniffer" || export SIMPLETEST_BASE_URL=http://localhost:8888 -script: - # Run code sniffer. - - phpcs --report=full --standard=Drupal $TRAVIS_BUILD_DIR/drupal/modules/$MODULE_NAME +# Export database variable for kernel tests. +- test ${TEST_SUITE} == "PHP_CodeSniffer" || export SIMPLETEST_DB=mysql://root:@127.0.0.1/message_private - # Run the tests - - php $TRAVIS_BUILD_DIR/drupal/core/scripts/run-tests.sh --verbose --color --php `which php` --url http://127.0.0.1:8080 "$MODULE_TEST_GROUP" | tee /tmp/test.txt; TEST_EXIT=${PIPESTATUS[0]}; echo $TEST_EXIT - # Check if we had fails in the run-tests.sh script - # Exit with the inverted value, because if there are no fails found, it will - # exit with 1 and for us that is a good thing so invert it to 0. Travis has - # some issues with the exclamation mark in front so we have to fiddle a bit. - # Also make the grep case insensitive and fail on run-tests.sh regular fails - # as well on fatal errors. - - TEST_OUTPUT=$(! egrep -i "([0-9]+ fails)|(PHP Fatal error)|([0-9]+ exceptions)" /tmp/test.txt > /dev/null)$?; echo $TEST_OUTPUT - # Exit the build - - if [ $TEST_EXIT -eq 0 ] && [ $TEST_OUTPUT -eq 0 ]; then exit 0; else exit 1; fi +script: ${TRAVIS_BUILD_DIR}/scripts/travis-ci/run-tests.sh ${TEST_SUITE} diff --git a/.travis/apache-vhost.conf b/.travis/apache-vhost.conf deleted file mode 100644 index 7e91196..0000000 --- a/.travis/apache-vhost.conf +++ /dev/null @@ -1,17 +0,0 @@ - - DocumentRoot %TRAVIS_BUILD_DIR% - - - Options FollowSymLinks MultiViews ExecCGI - AllowOverride All - Order deny,allow - Allow from all - - - - AddHandler php5-fcgi .php - Action php5-fcgi /php5-fcgi - Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi - FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -host 127.0.0.1:9000 -pass-header Authorization - - diff --git a/README.md b/README.md index f97ba70..bb0e7d5 100755 --- a/README.md +++ b/README.md @@ -13,13 +13,14 @@ CONTENTS OF THIS FILE INTRODUCTION ------------ A message template and entity reference fields, enabling sending and receiving -private messages using The Message Stack. Messages of template "Private Message" can -be sent by creating the private_message message instance with fields referencing -user entities. +private messages using The Message Stack. Messages of template "Private Message" +can be sent by creating the private_message message instance with fields +referencing user entities. -The message_private module includes the following. -+ A message template "Private Message" with entity reference field referencing users -+ A message view, message_private for "User Messages" +The message_private module includes the following: +* A message template "Private Message" with entity reference field referencing + users +* A message view, message_private for "User Messages" DEPENDENCIES @@ -39,8 +40,9 @@ INSTALLATION CONFIGURATION ------------- -Show notification boolean on user form fields: /admin/config/people/accounts/form-display -This will be addressed automatically in a hook later. +Show notification boolean on user form fields: +`/admin/config/people/accounts/form-display`. This will be addressed +automatically in a hook later. HOW TO USE diff --git a/composer.json b/composer.json index d72e0e5..c477239 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,35 @@ { "name": "drupal/message_private", - "description": "Message Private", + "description": "Private messaging module based on Message stack.", "type": "drupal-module", "license": "GPL-2.0+", "minimum-stability": "dev", - "require": {} + "prefer-stable": true, + "homepage": "https://www.drupal.org/project/message_private", + "require": { + "php": "^5.6|^7.0", + "drupal/core": "^8.6", + "drupal/message": "^1.0", + "drupal/message_notify": "^1.0", + "drupal/message_ui": "^1.0" + }, + "require-dev": { + "composer/installers": "^1.2", + "drupal-composer/drupal-scaffold": "^2.5", + "drupal/coder": "^8.3.1", + "webflo/drupal-core-require-dev": "~8.5" + }, + "repositories": [ + { + "type": "composer", + "url": "https://packages.drupal.org/8" + } + ], + "extra": { + "installer-paths": { + "../build/core": ["type:drupal-core"], + "../build/modules/contrib/{$name}": ["type:drupal-module"], + "../build/themes/contrib/{$name}": ["type:drupal-theme"] + } + } } diff --git a/config/optional/message_private.settings.yml b/config/optional/message_private.settings.yml index 60e5129..0559280 100644 --- a/config/optional/message_private.settings.yml +++ b/config/optional/message_private.settings.yml @@ -2,4 +2,4 @@ message_private_message_limit: 0 email_notify: 1 message_add_local_action: 1 default_limit: 0 -default_interval: 0 \ No newline at end of file +default_interval: 0 diff --git a/message_private.info.yml b/message_private.info.yml index 5c55bc8..0511853 100644 --- a/message_private.info.yml +++ b/message_private.info.yml @@ -9,4 +9,3 @@ dependencies: - message - message_notify - message_ui - diff --git a/message_private.install b/message_private.install index 84078a6..2d4ac58 100644 --- a/message_private.install +++ b/message_private.install @@ -1,20 +1,21 @@ 'user', 'bundle' => 'user', 'include_deleted' => TRUE, - ); + ]; // Get the fields based on the above properties. $fields = \Drupal::entityManager()->getStorage('field_config')->loadByProperties($properties); @@ -23,7 +24,7 @@ function message_private_uninstall() { foreach ($fields as $field) { /* @var \Drupal\Core\Field\FieldConfigBase $field */ // If we have the user notify field, purge the data and remove. - if(!empty($field->getName() == 'field_message_private_usr_notify')) { + if (!empty($field->getName() == 'field_message_private_usr_notify')) { // Purge previously deleted field tables for entity type. Drupal::entityManager()->getStorage($field->getTargetEntityTypeId())->purgeFieldData($field, 100); field_cron(); diff --git a/message_private.links.task.yml b/message_private.links.task.yml index 721bd98..bbd2892 100644 --- a/message_private.links.task.yml +++ b/message_private.links.task.yml @@ -18,4 +18,3 @@ message_private.messages.sent: # Provide dynamic local tasks. message_private.messages: deriver: 'Drupal\message_private\Plugin\Derivative\DynamicLocalTasks' - diff --git a/message_private.module b/message_private.module index 7b03ecd..5b02abf 100755 --- a/message_private.module +++ b/message_private.module @@ -1,19 +1,23 @@ moduleExists('markdown') ? \Drupal\Component\Utility\Xss::filterAdmin(\Drupal::moduleHandler()->invoke('markdown', 'filter', ['process', 0, -1, $output])) : '

Message Private README

' . \Drupal\Component\Utility\Html::escape($output) . '
'; + $module_hander = \Drupal::moduleHandler(); + return $module_hander->moduleExists('markdown') ? Xss::filterAdmin($module_hander->invoke('markdown', 'filter', [ + 'process', + 0, + -1, + $output, + ])) : '

Message Private README

' . Html::escape($output) . '
'; } } @@ -110,7 +119,12 @@ function message_private_message_access(EntityInterface $message, $operation, Ac * @return bool * TRUE if the user is allowed perform the operation, FALSE otherwise. */ -// @todo: code as Access class & associate service entry to local task/actions. + +/** + * Provides an access callback. + * + * @todo: code as Access class & associate service entry to local task/actions. + */ function message_private_access_callback($message) { if ($message->template == 'private_message') { return \Drupal::currentUser()->hasPermission('bypass private message access control') || \Drupal::currentUser()->hasPermission('view a private_message message instance'); @@ -124,25 +138,30 @@ function message_private_access_callback($message) { * This function is defined to override that provided by Message UI module. This * allows override specific values of the form such as the cancel link. It hides * the message_text on the edit and create form, and adds custom validation. + * + * @todo - check form ID is matching D8 form id. */ -// @todo - check form ID is matching D8 form id. function message_private_form_message_private_message_form_alter(&$form, FormStateInterface $form_state) { _message_private_message_form_alter($form, $form_state); } +/** + * Implements hook_form_FORM_ID_alter(). + */ function message_private_form_message_private_message_edit_form_alter(&$form, FormStateInterface $form_state) { _message_private_message_form_alter($form, $form_state); } -/* - * Helper function to modify message form +/** + * Helper function to modify message form. */ -function _message_private_message_form_alter(&$form, FormStateInterface $form_state){ +function _message_private_message_form_alter(&$form, FormStateInterface $form_state) { $entity = $form_state->getFormObject()->getEntity(); if ($entity->bundle() == 'private_message') { $form['field_message_private_to_user']['widget']['add_more']['#value'] = new TranslatableMarkup('Add another user'); if (isset($form['text']['#type'])) { - $form['text']['#type'] = 'hidden'; // @todo - is this necessary now? + // @todo - is this necessary now? + $form['text']['#type'] = 'hidden'; } // @todo - owner access and validation callback needs testing. $form['owner']['#access'] = \Drupal::currentUser()->hasPermission('bypass private message access control'); @@ -159,7 +178,12 @@ function _message_private_message_form_alter(&$form, FormStateInterface $form_st * @param mixed $form_state * The form state including values submitted. */ -// @todo - test this is called and if form should be passed by reference. + +/** + * Provides a validation callback. + * + * @todo - test this is called and if form should be passed by reference. + */ function message_private_form_message_private_message_form_validate(&$form, FormStateInterface $form_state) { // If there is an imposed message limit set in the admin settings interface. // The correct configuration object could not be determined. You'll need to @@ -187,10 +211,10 @@ function message_private_form_message_private_message_form_validate(&$form, Form // Get total amount of this user's messages since last interval. $query = \Drupal::entityQuery('message'); $total = $query->condition('template', 'private_message') - ->condition('timestamp', $interval_timestamp, '>') - ->condition('uid', $user->id()) - ->count() - ->execute(); + ->condition('timestamp', $interval_timestamp, '>') + ->condition('uid', $user->id()) + ->count() + ->execute(); // Display error preventing message create when total messages over limit. if ($total >= $limit) { @@ -208,7 +232,7 @@ function message_private_form_message_private_message_form_validate(&$form, Form * this may be an issue with the message module itself as I don't * think escaping strings for email should be done in this module. */ -function message_private_message_insert(\Drupal\message\MessageInterface $message) { +function message_private_message_insert(MessageInterface $message) { // Prepare message notifications for private messages if notifications are on. // The correct configuration object could not be determined. You'll need to // rewrite this call manually. @@ -219,12 +243,12 @@ function message_private_message_insert(\Drupal\message\MessageInterface $messag // function message_notify_send_message tries to re-save as a new message. $message = Message::load($message->id()); - $mail = array(); + $mail = []; $users = $message->get('field_message_private_to_user'); if (!empty($users)) { // Make users an array if user ref field is configured to 1 entry. - $users = is_array($users) ? $users : array($users); + $users = is_array($users) ? $users : [$users]; foreach ($users as $user) { $notify = $user->get('field_private_message_notify'); if (!empty($notify) && is_array($notify)) { @@ -241,7 +265,7 @@ function message_private_message_insert(\Drupal\message\MessageInterface $messag /* @var \Drupal\message_notify\MessageNotifier $message_notifier */ $message_notifier = \Drupal::service('message_notify.sender'); // @todo - figure out how to pass the email addresses to the notify plugin / hook. - $message_notifier->send($message, array('mail' => implode(',', $mail), 'email')); + $message_notifier->send($message, ['mail' => implode(',', $mail), 'email']); } } } @@ -251,8 +275,9 @@ function message_private_message_insert(\Drupal\message\MessageInterface $messag * * If email notifications are disabled, hide the per user setting on user * profiles, unless the user is in role with bypass access control permission. + * + * @todo - check form ID matches D8 form id & add config for user notify field. */ -// @todo - check form ID matches D8 form id & add config for user notify field. function message_private_form_user_profile_form_alter(&$form, FormStateInterface $form_state, $form_id) { // The correct configuration object could not be determined. You'll need to // rewrite this call manually. @@ -264,30 +289,32 @@ function message_private_form_user_profile_form_alter(&$form, FormStateInterface /** * Implements hook_theme(). + * + * @todo - Is this still required in D8 to make twig template discoverable? */ -// @todo - Is this still required in D8 to make twig template discoverable? function message_private_theme() { - return array( - 'message_private' => array( + return [ + 'message_private' => [ 'render element' => 'elements', 'template' => 'message--private_message', 'base hook' => 'message_private', - ), - // Template is discoverable if the theme name is the same as the template with '_' instead of '-' - 'message_private__inbox' => array( - 'variables' => array('messages' => array()), - ), - 'message_private__sent' => array( - 'variables' => array('messages' => array()), - ), - ); + ], + // Template is discoverable if the theme name is the same as the template + // with '_' instead of '-'. + 'message_private__inbox' => [ + 'variables' => ['messages' => []], + ], + 'message_private__sent' => [ + 'variables' => ['messages' => []], + ], + ]; } - /** * Implements hook_message_view_alter(). + * + * @todo - Is this still required in D8 to make twig template discoverable? */ -// @todo - Is this still required in D8 to make twig template discoverable? function message_private_message_view_alter(&$build) { // Use template_preprocess_message_private for private_message messages only. if (!empty($build['#bundle']) && $build['#bundle'] == 'private_message') { @@ -297,8 +324,9 @@ function message_private_message_view_alter(&$build) { /** * Process variables for message--private_message.html.twig. + * + * @todo - Is there a better alternative to this? */ -// @todo - Is there a better alternative to this? function template_preprocess_message_private(&$variables) { // Call the parent message template function defined in message module. if (function_exists('template_preprocess_message')) { @@ -312,19 +340,18 @@ function template_preprocess_message_private(&$variables) { if (!empty($message) && $uid = $message->uid) { $variables['date'] = format_date($message->timestamp); // @FIXME -// theme() has been renamed to _theme() and should NEVER be called directly. -// Calling _theme() directly can alter the expected output and potentially -// introduce security issues (see https://www.drupal.org/node/2195739). You -// should use renderable arrays instead. -// -// -// @see https://www.drupal.org/node/2195739 -// $variables['name'] = theme('username', array('account' => user_load($uid))); - - $variables['submitted'] = t('Sent by !username on !datetime', array( - '!username' => $variables['name'], - '!datetime' => $variables['date'], - )); + // theme() has been renamed to _theme() and should NEVER be called directly. + // Calling _theme() directly can alter the expected output and potentially + // introduce security issues (see https://www.drupal.org/node/2195739). You + // should use renderable arrays instead. + // + // + // @see https://www.drupal.org/node/2195739 + // $variables['name'] = theme('username', ['account' => user_load($uid)]); + $variables['submitted'] = t('Sent by !username on !datetime', [ + '!username' => $variables['name'], + '!datetime' => $variables['date'], + ]); } } @@ -339,10 +366,11 @@ function template_preprocess_message_private(&$variables) { * * @return mixed * Either a role id or null. + * + * @todo - Is there a better location for this? */ -// @todo - Is there a better location for this? function _message_private_max_message_limit_role($roles) { - $limits = array(); + $limits = []; // The correct configuration object could not be determined. You'll need to // rewrite this call manually. @@ -371,7 +399,6 @@ function _message_private_max_message_limit_role($roles) { // rewrite this call manually. $limit = \Drupal::config('message_private.settings')->get($limit_name); - // Ensure we have existing valid numerical values for both variables. if (!empty($limit) && ctype_digit($limit) && !empty($interval) && ctype_digit($interval)) { $limits[$rid] = $interval / $limit; @@ -385,6 +412,7 @@ function _message_private_max_message_limit_role($roles) { * Views Plugin access callback. * * @return \Drupal\Core\Access\AccessResult + * The access result object. */ function _message_private_inbox_access() { $account = \Drupal::currentUser(); @@ -396,36 +424,38 @@ function _message_private_inbox_access() { return AccessResult::allowed(); } - // Allow if the user has the bypass permission + // Allow if the user has the bypass permission. return AccessResult::allowedIfHasPermission($account, 'bypass private message access control'); } - -function message_private_entity_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) { +/** + * Implements hook_entity_view_alter(). + */ +function message_private_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) { if ($entity->bundle() === 'private_message') { if ($build['#view_mode'] == 'inbox') { $component = $display->getComponent('author'); - // Add the author field + // Add the author field. $author = User::load($entity->get('uid')->getValue()[0]['target_id']); - $build['author'] = array( + $build['author'] = [ '#type' => 'link', '#url' => $author->toUrl(), '#title' => $author->getDisplayName(), '#weight' => $component['weight'], - '#attributes' => array( - 'class' => array('message-private-author') - ) + '#attributes' => [ + 'class' => ['message-private-author'], + ], - ); + ]; $created = $entity->getCreatedTime(); $component = $display->getComponent('created'); $build['created'] = [ - '#markup' => '

Hello

' . \Drupal::service('date.formatter')->format($created, 'date_text'), - '#weight'=> $component['weight'] + '#markup' => '

Hello

' . \Drupal::service('date.formatter')->format($created, 'date_text'), + '#weight' => $component['weight'], ]; } @@ -450,3 +480,13 @@ function message_private_entity_extra_field_info() { ]; return $extra_fields; } + +/** + * Implements hook_entity_type_alter(). + */ +function message_private_entity_type_alter(array &$entity_types) { + + // Set the access class for message to our module version. + $entity_types['message'] + ->setAccessClass('default', 'Drupal\message_private\MessagePrivateAccessControlHandler'); +} diff --git a/phpcs-ruleset.xml.dist b/phpcs-ruleset.xml.dist new file mode 100644 index 0000000..c32ebc3 --- /dev/null +++ b/phpcs-ruleset.xml.dist @@ -0,0 +1,20 @@ + + + + +Drupal coding standard for contributed modules + + +*.gif +*.less +*.png + + +*.min.css +*.min.js + + + + + + diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..cb63668 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,10 @@ + + + Default PHP CodeSniffer configuration for Drupal modules. + + + + + . + ./vendor + diff --git a/scripts/travis-ci/run-tests.sh b/scripts/travis-ci/run-tests.sh new file mode 100755 index 0000000..58ca5c5 --- /dev/null +++ b/scripts/travis-ci/run-tests.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Run either PHPUnit tests or PHP_CodeSniffer tests on Travis CI, depending +# on the passed in parameter. + +mysql_to_ramdisk() { + sudo service mysql stop + sudo mv /var/lib/mysql /var/run/tmpfs + sudo ln -s /var/run/tmpfs /var/lib/mysql + sudo service mysql start +} + +case "$1" in + PHP_CodeSniffer) + cd ${TRAVIS_BUILD_DIR}/ + ./vendor/bin/phpcs + exit $? + ;; + 8.*.x) + mysql_to_ramdisk + cd ${TRAVIS_BUILD_DIR}/ + ls -la + ls -la ../build/modules/contrib/ + ./vendor/bin/phpunit -c ${DRUPAL_DIR}/core ./tests + exit $? + ;; + *) + echo "Unknown test '$1'" + exit 1 +esac diff --git a/src/Controller/MessagePrivateController.php b/src/Controller/MessagePrivateController.php index 2121b27..ca656c7 100644 --- a/src/Controller/MessagePrivateController.php +++ b/src/Controller/MessagePrivateController.php @@ -3,11 +3,8 @@ namespace Drupal\message_private\Controller; use Drupal\Core\Url; -use Drupal\user\entity\User; use Drupal\Core\Controller\ControllerBase; -use Drupal\message\Entity\Message; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; -use Drupal\message\MessageTemplateInterface; /** * Controller for viewing private messages. @@ -37,36 +34,35 @@ public function __construct() { } /** - * Generates output of all messages sent to the current user + * Generates output of all messages sent to the current user. * - * - * @return + * @return array * A render array for a list of the messages; */ public function inBox($user) { - $messages = array(); + $messages = []; // Get messages sent to the current user // Return build array. if (!empty($messages)) { - return array( + return [ '#theme' => 'message_private__inbox', - '#messages' => $messages - ); + '#messages' => $messages, + ]; } else { $url = Url::fromRoute('message.template_add'); - return array( - '#markup' => 'You have no messages in your inbox. Try sending a message to someone sending a message to someone.' - ); + return [ + '#markup' => 'You have no messages in your inbox. Try sending a message to someone sending a message to someone.', + ]; } } /** * Generates form output for adding a new message entity of message_template. * - * @param \Drupal\message\MessageTemplateInterface $message_template - * The message template object. + * @param mixed $user + * TBD. * * @return array * An array as expected by drupal_render(). @@ -74,18 +70,17 @@ public function inBox($user) { public function sent($user) { // Return build array. if (!empty($messages)) { - return array( + return [ '#theme' => 'message_private__inbox', - '#messages' => $messages - ); + '#messages' => $messages, + ]; } else { $url = Url::fromRoute('message.template_add'); - return array( - '#markup' => 'You have no messages in your inbox. Try sending a message to someone sending a message to someone.' - ); + return [ + '#markup' => 'You have no messages in your inbox. Try sending a message to someone sending a message to someone.', + ]; } } - } diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php index a4587d5..7c66606 100644 --- a/src/Form/SettingsForm.php +++ b/src/Form/SettingsForm.php @@ -1,9 +1,4 @@ $this->t('Enter a message limit') . ' ' . MESSAGE_PRIVATE_MESSAGE_LIMIT_MIN . ' - ' . MESSAGE_PRIVATE_MESSAGE_LIMIT_MAX, ]; - - $form['interval_limit'][MESSAGE_PRIVATE_DEFAULT_INDEX]['default_interval'] = [ - '#type' => 'textfield', - '#title' => $this->t('Interval'), - '#default_value' => $config->get('default_interval'), - '#description' => $this->t('Enter an interval in minutes') . ' ' . MESSAGE_PRIVATE_MESSAGE_INTERVAL_MIN . ' - ' . MESSAGE_PRIVATE_MESSAGE_INTERVAL_MAX, - ]; - + $form['interval_limit'][MESSAGE_PRIVATE_DEFAULT_INDEX]['default_interval'] = [ + '#type' => 'textfield', + '#title' => $this->t('Interval'), + '#default_value' => $config->get('default_interval'), + '#description' => $this->t('Enter an interval in minutes') . ' ' . MESSAGE_PRIVATE_MESSAGE_INTERVAL_MIN . ' - ' . MESSAGE_PRIVATE_MESSAGE_INTERVAL_MAX, + ]; // Generate variable names for all roles used with get/set in admin form. foreach (user_roles() as $id => $role) { @@ -150,26 +142,26 @@ public function buildForm(array $form, FormStateInterface $form_state) { $limit_name = 'message_private_' . $role_name . '_limit'; $interval_name = 'message_private_' . $role_name . '_interval'; - $form['interval_limit'][$id] = array( + $form['interval_limit'][$id] = [ '#type' => 'fieldset', '#title' => $role->label(), '#collapsible' => TRUE, '#collapsed' => TRUE, - ); + ]; - $form['interval_limit'][$id][$limit_name] = array( + $form['interval_limit'][$id][$limit_name] = [ '#type' => 'textfield', '#title' => $this->t('Limit'), '#default_value' => $config->get($limit_name), '#description' => $this->t('Enter a message limit') . ' ' . MESSAGE_PRIVATE_MESSAGE_LIMIT_MIN . ' - ' . MESSAGE_PRIVATE_MESSAGE_LIMIT_MAX, - ); + ]; - $form['interval_limit'][$id][$interval_name] = array( + $form['interval_limit'][$id][$interval_name] = [ '#type' => 'textfield', '#title' => $this->t('Interval'), '#default_value' => $config->get($interval_name), '#description' => $this->t('Enter an interval in minutes') . ' ' . MESSAGE_PRIVATE_MESSAGE_INTERVAL_MIN . ' - ' . MESSAGE_PRIVATE_MESSAGE_INTERVAL_MAX, - ); + ]; } return parent::buildForm($form, $form_state); @@ -236,9 +228,10 @@ public function validateForm(array &$form, FormStateInterface $form_state) { } /** - * Validate limit and interval values and show any errors on the form elements. + * Validate limit and interval values and show any errors on form elements. * * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state object. * @param mixed $limit_element * Limit form element reference. * @param mixed $interval_element @@ -259,7 +252,7 @@ private function validateFieldset(FormStateInterface $form_state, &$limit_elemen } // Check is numeric and between the boundaries. if (!ctype_digit($interval) || $interval > MESSAGE_PRIVATE_MESSAGE_INTERVAL_MAX || $interval < MESSAGE_PRIVATE_MESSAGE_INTERVAL_MIN) { - $form_state->setErrorByName($interval_element, + $form_state->setErrorByName($interval_element, $this->t('Enter a numerical interval in minutes between') . ' ' . MESSAGE_PRIVATE_MESSAGE_INTERVAL_MIN . ' - ' . MESSAGE_PRIVATE_MESSAGE_INTERVAL_MAX . '.'); } @@ -270,4 +263,5 @@ private function validateFieldset(FormStateInterface $form_state, &$limit_elemen $form_state->setErrorByName($interval_element); } } + } diff --git a/src/MessagePrivateAccessControlHandler.php b/src/MessagePrivateAccessControlHandler.php new file mode 100644 index 0000000..f5f959e --- /dev/null +++ b/src/MessagePrivateAccessControlHandler.php @@ -0,0 +1,64 @@ +hasPermission('administer message private') + || $account->hasPermission('bypass private message access control')) { + return AccessResult::allowed()->cachePerPermissions(); + } + // Verify that the user can apply the op. + if ($account->hasPermission($operation . ' any private message') + || $account->hasPermission($operation . ' own private messages', $account)) { + if ($operation != 'create') { + // Check if the user is message author. + /* @var Drupal\message\Entity\message $message */ + if ($entity->getOwnerId() == $account->id()) { + return AccessResult::allowed()->cachePerPermissions(); + } + // Grant view access for recipients of the private message. + if ($operation == 'view') { + $users = $entity->get('field_message_private_to_user')->getValue(); + if ($users && is_array($users)) { + foreach ($users as $user_ref) { + if ($user_ref['target_id'] == $account->id()) { + return AccessResult::allowed()->cachePerPermissions(); + } + } + } + } + // Deny if user is not message author or viewing recipient. + return AccessResult::forbidden()->cachePerPermissions(); + } + else { + return AccessResult::allowed()->cachePerPermissions(); + } + } + // Unknown operation, no opinion. + return AccessResult::neutral(); + } + + /** + * {@inheritdoc} + */ + protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { + return AccessResult::allowedIfHasPermission($account, 'create a private message'); + } + +} diff --git a/src/Plugin/Block/MessageHistory.php b/src/Plugin/Block/MessageHistory.php index 928556b..8894e39 100644 --- a/src/Plugin/Block/MessageHistory.php +++ b/src/Plugin/Block/MessageHistory.php @@ -8,7 +8,7 @@ use Drupal\Core\Extension\ModuleHandler; /** - * Provides a 'Message History' Block with links to all new messages + * Provides a 'Message History' Block with links to all new messages. * * @Block( * id = "message_private_new_message", @@ -18,7 +18,7 @@ class MessageHistory extends BlockBase implements ContainerFactoryPluginInterface { /** - * The module handler + * The module handler. * * @var \Drupal\Core\Extension\ModuleHandler */ @@ -41,7 +41,6 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->moduleHandler = $module_handler; } - /** * {@inheritdoc} */ @@ -66,32 +65,35 @@ public function build() { foreach ($result as $row) { $children[$row->mid] = [ 'New message' => [ - '#markup' => ''. $row->name . '', + '#markup' => '' . $row->name . '', '#wrapper_attributes' => [ 'class' => ['message-history-item'], ], - ] + ], ]; } if (empty($children)) { return [ - '#markup' => t('You have no new messages') + '#markup' => t('You have no new messages'), ]; } $items[] = [ '#theme' => 'item_list', '#items' => $children, '#cache' => [ - 'max-age' => 0 - ] + 'max-age' => 0, + ], ]; return $items; } + /** + * TBD. + */ protected function getUnreadMessages() { - // Find messages for the current user + // Find messages for the current user. return db_query("SELECT mfd.mid, mfd.uid, ufd.name FROM {message_field_data} mfd LEFT JOIN {message__field_message_private_to_user} pu @@ -106,14 +108,17 @@ protected function getUnreadMessages() { AND pu.field_message_private_to_user_target_id = :uid AND mfd.created > :limit AND mfd.uid != :uid", [ - ':uid' => \Drupal::currentUser()->id(), - ':limit' => HISTORY_READ_LIMIT - ]); + ':uid' => \Drupal::currentUser()->id(), + ':limit' => HISTORY_READ_LIMIT, + ]); } + /** + * TBD. + */ protected function messageHistoryModuleRequired() { return [ - '#markup' => t('Enable Message History Module to display New Messages block') + '#markup' => t('Enable Message History Module to display New Messages block'), ]; } diff --git a/src/Plugin/Derivative/DynamicLocalTasks.php b/src/Plugin/Derivative/DynamicLocalTasks.php index 3368765..1210373 100644 --- a/src/Plugin/Derivative/DynamicLocalTasks.php +++ b/src/Plugin/Derivative/DynamicLocalTasks.php @@ -1,21 +1,17 @@ derivatives['message_private.messages']['route_name'] = 'message_private.user.add'; return $this->derivatives; } + } diff --git a/src/Plugin/views/access/InboxPermission.php b/src/Plugin/views/access/InboxPermission.php index 93a6d56..ab49c11 100644 --- a/src/Plugin/views/access/InboxPermission.php +++ b/src/Plugin/views/access/InboxPermission.php @@ -1,10 +1,5 @@ setRequirement('_custom_access', '_message_private_inbox_access'); } + /** + * TBD. + */ public function summaryTitle() { return $this->t('InboxPermission'); } diff --git a/tests/src/Functional/MessagePrivatePermissions.php b/tests/src/Functional/MessagePrivatePermissionsTests.php similarity index 72% rename from tests/src/Functional/MessagePrivatePermissions.php rename to tests/src/Functional/MessagePrivatePermissionsTests.php index 7c67c35..03dc118 100644 --- a/tests/src/Functional/MessagePrivatePermissions.php +++ b/tests/src/Functional/MessagePrivatePermissionsTests.php @@ -1,10 +1,5 @@ 'Message Private permissions', - 'description' => 'Testing the use case of message_private_message_access hook.', - 'group' => 'Message Private', - ); - } + public static $modules = [ + 'message', + 'message_notify', + 'message_ui', + 'message_private', + ]; /** * {@inheritdoc} */ - function setUp() { + public function setUp() { parent::setUp(); $this->accessHandler = \Drupal::entityManager()->getAccessControlHandler('message'); @@ -71,25 +65,30 @@ function setUp() { /** * Test private message workflow. */ - function testMessageUiPermissions() { - $this->drupalLogin($this->account); // User login. - $create_url = '/message/add/private_message'; // Set our create url. + public function testMessageUiPermissions() { + // User login. + $this->drupalLogin($this->account); + // Set our create url. + $create_url = '/message/add/private_message'; // Verify the user can't create the message. $this->drupalGet($create_url); - $this->assertResponse(403); // The user can't create a private message. + // The user can't create a private message. + $this->assertResponse(403); // Grant and check create permissions for a message. $this->grantMessagePrivatePermission('create'); $this->drupalGet($create_url); // If we get a valid response. - $this->assertResponse(200); // Check for valid response. + // Check for valid response. + $this->assertResponse(200); // Create a message at current page / url. - $this->drupalPostForm(NULL, array(), t('Save')); + $this->drupalPostForm(NULL, [], t('Save')); - $msg_url = '/message/1'; // Create the message url. + // Create the message url. + $msg_url = '/message/1'; // Verify the user now can see the text. $this->grantMessagePrivatePermission('view'); @@ -119,7 +118,7 @@ function testMessageUiPermissions() { // Grant the permission to the user. $this->grantMessagePrivatePermission('delete'); - $this->drupalPostForm($msg_url . '/delete', array(), t('Delete')); + $this->drupalPostForm($msg_url . '/delete', [], t('Delete')); // The user can delete a private message. $this->assertResponse(200); @@ -132,15 +131,15 @@ function testMessageUiPermissions() { // The user cannot administer message_private. $this->assertResponse(403); - user_role_grant_permissions($this->rid, array('administer message private')); + user_role_grant_permissions($this->rid, ['administer message private']); $this->drupalGet($admin_url); // The user can administer message_private. $this->assertResponse(200); - // Create a new user with the bypass access permission and verify the bypass. + // Create a user with the bypass access permission and verify the bypass. $this->drupalLogout(); - $user = $this->drupalCreateUser(array('bypass private message access control')); + $user = $this->drupalCreateUser(['bypass private message access control']); // Verify the user can by pass the message access control. $this->drupalLogin($user); @@ -153,11 +152,11 @@ function testMessageUiPermissions() { /** * Grant to the user a specific permission. * - * @param $operation - * The type of operation - create, update, delete or view. + * @param string $operation + * The type of operation - create, update, delete or view. */ private function grantMessagePrivatePermission($operation) { - user_role_grant_permissions($this->rid, array($operation . ' private_message message')); + user_role_grant_permissions($this->rid, [$operation . ' private_message message']); } /** @@ -167,17 +166,17 @@ public function testMessagePrivateAccessHook() { $this->drupalLogin($this->account); // Setting up the operation and the expected value from the access callback. - $permissions = array( + $permissions = [ 'create' => TRUE, 'view' => TRUE, 'delete' => FALSE, 'update' => FALSE, - ); + ]; // Get the message template and create an instance. $message_template = $this->loadMessageTemplate('private_message'); /* @var $message Message */ - $message = Message::create(array('template' => $message_template->id())); + $message = Message::create(['template' => $message_template->id()]); $message->setOwner($this->account); $message->save(); @@ -186,12 +185,13 @@ public function testMessagePrivateAccessHook() { // check which value need to return. If the access control function will // return the expected value then we know the hook got in action. $message->{$op} = $value; - $params = array( + $params = [ '@operation' => $op, '@value' => $value, - ); + ]; $this->assertEqual($value, $this->accessHandler->access($message, $op, $this->account), new FormattableMarkup('The hook return @value for @operation', $params)); } } + } diff --git a/tests/src/Unit/FakeTest.php b/tests/src/Unit/FakeTest.php new file mode 100644 index 0000000..e6c5aee --- /dev/null +++ b/tests/src/Unit/FakeTest.php @@ -0,0 +1,21 @@ +assertTrue(TRUE); + } + +}