diff --git a/.docksal/docksal-local.env b/.docksal/docksal-local.env index 8ecd27dba..c49944789 100644 --- a/.docksal/docksal-local.env +++ b/.docksal/docksal-local.env @@ -1 +1,3 @@ MYSQL_PORT_MAPPING='3380:3306' +XDEBUG_ENABLED=1 +XDEBUG_MODE=coverage diff --git a/.docksal/docksal.env b/.docksal/docksal.env index 7da5888bd..8923009e6 100644 --- a/.docksal/docksal.env +++ b/.docksal/docksal.env @@ -1,4 +1,4 @@ DOCKSAL_STACK=default DOCROOT=html DB_IMAGE="docksal/mariadb:10.6" -CLI_IMAGE='docksal/cli:php8.2-build' +CLI_IMAGE='docksal/cli:php8.3-build' diff --git a/.docksal/etc/mysql/my.cnf b/.docksal/etc/mysql/my.cnf new file mode 100644 index 000000000..5a9ce8345 --- /dev/null +++ b/.docksal/etc/mysql/my.cnf @@ -0,0 +1,2 @@ +[mysqld] +innodb_force_recovery=0 diff --git a/.docksal/etc/php/php.ini b/.docksal/etc/php/php.ini index 5e25967e8..cea362e77 100644 --- a/.docksal/etc/php/php.ini +++ b/.docksal/etc/php/php.ini @@ -1,2 +1 @@ -; Mail settings -sendmail_path = '/usr/bin/msmtp -t --host=mail --port=1025 --read-envelope-from' +xdebug.mode=coverage diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index c54b86212..51a8cab4d 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -137,7 +137,7 @@ jobs: docker compose -f tests/docker-compose.yml exec -T drupal chmod -R 777 /srv/www/html/sites/default/files /srv/www/html/sites/default/private docker compose -f tests/docker-compose.yml exec -T drupal mkdir -p /srv/www/html/build/logs docker compose -f tests/docker-compose.yml exec -T drupal chmod -R 777 /srv/www/html/build/logs - docker compose -f tests/docker-compose.yml exec -T -w /srv/www -e XDEBUG_MODE=coverage -e BROWSERTEST_OUTPUT_DIRECTORY=/srv/www/html/sites/default/files/browser_output -e DTT_BASE_URL=http://127.0.0.1 drupal ./vendor/bin/phpunit --coverage-clover /srv/www/html/build/logs/clover.xml --debug + docker compose -f tests/docker-compose.yml exec -T -w /srv/www -e XDEBUG_MODE=coverage -e BROWSERTEST_OUTPUT_DIRECTORY=/srv/www/html/sites/default/files/browser_output -e SIMPLETEST_BASE_URL=http://127.0.0.1 -e DTT_BASE_URL=http://127.0.0.1 drupal ./vendor/bin/phpunit --coverage-clover /srv/www/html/build/logs/clover.xml --debug docker cp "$(docker compose -f tests/docker-compose.yml ps -q drupal)":/srv/www/html/build/logs/clover.xml . env: fail-fast: true diff --git a/config/filter.format.markdown.yml b/config/filter.format.markdown.yml index ae94b2e9e..63c51a4c0 100644 --- a/config/filter.format.markdown.yml +++ b/config/filter.format.markdown.yml @@ -82,4 +82,4 @@ filters: status: false weight: 0 settings: - replace_empty: '0' + replace_empty: 0 diff --git a/config/system.action.user_add_role_action.contributor.yml b/config/system.action.user_add_role_action.contributor.yml new file mode 100644 index 000000000..40e9e3b35 --- /dev/null +++ b/config/system.action.user_add_role_action.contributor.yml @@ -0,0 +1,14 @@ +uuid: 8a1c6d2a-cf03-48d7-9dc8-ba12b7239159 +langcode: en +status: true +dependencies: + config: + - user.role.contributor + module: + - user +id: user_add_role_action.contributor +label: 'Add the Contributor role role to the selected user(s)' +type: user +plugin: user_add_role_action +configuration: + rid: contributor diff --git a/config/system.action.user_remove_role_action.contributor.yml b/config/system.action.user_remove_role_action.contributor.yml new file mode 100644 index 000000000..efe9281cb --- /dev/null +++ b/config/system.action.user_remove_role_action.contributor.yml @@ -0,0 +1,14 @@ +uuid: e0c6f28b-74a1-41a4-a75f-b9c12fe98676 +langcode: en +status: true +dependencies: + config: + - user.role.contributor + module: + - user +id: user_remove_role_action.contributor +label: 'Remove the Contributor role role from the selected user(s)' +type: user +plugin: user_remove_role_action +configuration: + rid: contributor diff --git a/config/user.role.authenticated.yml b/config/user.role.authenticated.yml index 4d6f02efa..5b47d3617 100644 --- a/config/user.role.authenticated.yml +++ b/config/user.role.authenticated.yml @@ -3,7 +3,6 @@ langcode: en status: true dependencies: config: - - filter.format.markdown - filter.format.markdown_editor - filter.format.token_markdown - node.type.job @@ -49,7 +48,6 @@ permissions: - 'subscribe to jobs' - 'subscribe to training' - 'use enhanced input forms' - - 'use text format markdown' - 'use text format markdown_editor' - 'use text format token_markdown' - 'view media' diff --git a/config/user.role.contributor.yml b/config/user.role.contributor.yml new file mode 100644 index 000000000..e702fb568 --- /dev/null +++ b/config/user.role.contributor.yml @@ -0,0 +1,27 @@ +uuid: 42dacb5f-f71a-4e37-858e-d4cf46729e39 +langcode: en +status: true +dependencies: + config: + - media.type.image_report + - node.type.report + module: + - media + - node + - reliefweb_files + - reliefweb_form +id: contributor +label: Contributor +weight: 9 +is_admin: null +permissions: + - 'access reliefweb private files' + - 'create image_report media' + - 'create report content' + - 'delete media' + - 'delete own image_report media' + - 'display source attention messages' + - 'edit own image_report media' + - 'edit own report content' + - 'update media' + - 'view own unpublished media' diff --git a/config/user.role.user_manager.yml b/config/user.role.user_manager.yml index 247d3393a..3b20e9a6a 100644 --- a/config/user.role.user_manager.yml +++ b/config/user.role.user_manager.yml @@ -16,6 +16,7 @@ permissions: - 'administer subscriptions' - 'administer users' - 'assign beta_tester role' + - 'assign contributor role' - 'assign editor role' - 'assign external_disaster_manager role' - 'assign job_importer role' diff --git a/html/modules/custom/reliefweb_entities/reliefweb_entities.module b/html/modules/custom/reliefweb_entities/reliefweb_entities.module index c594ede42..637214902 100644 --- a/html/modules/custom/reliefweb_entities/reliefweb_entities.module +++ b/html/modules/custom/reliefweb_entities/reliefweb_entities.module @@ -86,7 +86,7 @@ function reliefweb_entities_entity_bundle_field_info_alter(array &$fields, Entit * Add bundle classes to nodes and terms to handle business logic. */ function reliefweb_entities_entity_bundle_info_alter(&$bundles) { - $namespace = '\Drupal\reliefweb_entities\Entity'; + $namespace = 'Drupal\reliefweb_entities\Entity'; foreach ($bundles as $entity_type_id => $items) { foreach ($items as $bundle => $info) { @@ -96,6 +96,8 @@ function reliefweb_entities_entity_bundle_info_alter(&$bundles) { } if ($class !== FALSE && is_subclass_of($class, BundleEntityInterface::class)) { $label = ucwords(str_replace(['_', '-'], ' ', $bundle)); + // No leading \ otherwise EntityTypeRepository::getEntityTypeFromClass + // fails. $bundles[$entity_type_id][$bundle]['class'] = $class; $bundles[$entity_type_id][$bundle]['label'] = $label; } diff --git a/html/modules/custom/reliefweb_entities/src/DocumentTrait.php b/html/modules/custom/reliefweb_entities/src/DocumentTrait.php index 8d2098a4d..dee23b8db 100644 --- a/html/modules/custom/reliefweb_entities/src/DocumentTrait.php +++ b/html/modules/custom/reliefweb_entities/src/DocumentTrait.php @@ -4,6 +4,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\Core\Entity\EntityPublishedInterface; +use Drupal\Core\Entity\RevisionLogInterface; use Drupal\Core\Field\EntityReferenceFieldItemList; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Url; @@ -394,4 +395,42 @@ protected function updateSourceModerationStatus() { } } + /** + * Update the status to refused if any of the sources is blocked. + */ + protected function updateModerationStatusFromSourceStatus() { + if (!$this->hasField('field_source') || $this->field_source->isEmpty()) { + return; + } + + $blocked = []; + foreach ($this->field_source as $item) { + $source = $item->entity; + if (empty($source) || !($source instanceof Source)) { + continue; + } + + if ($source->getModerationStatus() === 'blocked') { + $blocked[] = $source->label(); + } + } + + if (!empty($blocked)) { + $this->setModerationStatus('refused'); + + // Add a message to the revision log. + if ($this instanceof RevisionLogInterface) { + $message = 'Submissions from "' . implode('", "', $blocked) . '" are no longer allowed.'; + + $log = $this->getRevisionLogMessage(); + if (empty($log)) { + $this->setRevisionLogMessage($message); + } + else { + $this->setRevisionLogMessage($message . ' ' . $log); + } + } + } + } + } diff --git a/html/modules/custom/reliefweb_entities/src/Entity/Report.php b/html/modules/custom/reliefweb_entities/src/Entity/Report.php index fdea3da90..d4dbc56be 100644 --- a/html/modules/custom/reliefweb_entities/src/Entity/Report.php +++ b/html/modules/custom/reliefweb_entities/src/Entity/Report.php @@ -13,11 +13,14 @@ use Drupal\reliefweb_entities\DocumentTrait; use Drupal\reliefweb_moderation\EntityModeratedInterface; use Drupal\reliefweb_moderation\EntityModeratedTrait; +use Drupal\reliefweb_moderation\Helpers\UserPostingRightsHelper; use Drupal\reliefweb_revisions\EntityRevisionedInterface; use Drupal\reliefweb_revisions\EntityRevisionedTrait; use Drupal\reliefweb_utility\Helpers\DateHelper; use Drupal\reliefweb_utility\Helpers\ReliefWebStateHelper; +use Drupal\reliefweb_utility\Helpers\TaxonomyHelper; use Drupal\reliefweb_utility\Helpers\UrlHelper; +use Drupal\reliefweb_utility\Helpers\UserHelper; /** * Bundle class for report nodes. @@ -210,8 +213,6 @@ public function getAttachments(?array $build = NULL) { * {@inheritdoc} */ public function preSave(EntityStorageInterface $storage) { - parent::preSave($storage); - // Change the publication date if bury is selected, to the original // publication date. if (!empty($this->field_bury->value) && !$this->field_original_publication_date->isEmpty()) { @@ -249,13 +250,21 @@ public function preSave(EntityStorageInterface $storage) { '@date' => DateHelper::format($this->field_embargo_date->value, 'custom', 'd M Y H:i e'), ]); - $log = trim($this->getRevisionLogMessage()); + $log = !empty($this->getRevisionLogMessage()) ? trim($this->getRevisionLogMessage()) : ''; $log = $message . (!empty($log) ? "\n" . $log : ''); $this->setRevisionLogMessage($log); } // Prepare notifications. $this->preparePublicationNotification(); + + // Update the entity status based on the user posting rights. + $this->updateModerationStatusFromPostingRights(); + + // Update the entity status based on the source(s) moderation status. + $this->updateModerationStatusFromSourceStatus(); + + parent::preSave($storage); } /** @@ -353,6 +362,102 @@ protected function sendPublicationNotification() { ->mail('reliefweb_entities', 'report_publication_notification', $to, $langcode, $parameters, $from, TRUE); } + /** + * Update the status for the entity based on the user posting rights. + */ + protected function updateModerationStatusFromPostingRights() { + // In theory the revision user here, is the current user saving the entity. + /** @var \Drupal\user\UserInterface|null $user */ + $user = $this->getRevisionUser(); + $status = $this->getModerationStatus(); + + // Skip if there is no revision user. That should normally not happen with + // new content but some old revisions may reference users that don't exist + // anymore (which should not happen either but...). + if (empty($user)) { + return; + } + + // For non editors, we determine the real status based on the user + // posting rights for the selected sources. + if (!UserHelper::userHasRoles(['editor'], $user) && in_array($status, ['to-review'])) { + // Retrieve the list of sources and check the user rights. + if (!$this->field_source->isEmpty()) { + // Extract source ids. + $sources = []; + foreach ($this->field_source as $item) { + if (!empty($item->target_id)) { + $sources[] = $item->target_id; + } + } + + // Get the user's posting right for the document. + $rights = []; + foreach (UserPostingRightsHelper::getUserPostingRights($user, $sources) as $tid => $data) { + $rights[$data[$this->bundle()] ?? 0][] = $tid; + } + + // Blocked for some sources. + if (!empty($rights[1])) { + $status = 'refused'; + } + // Trusted for all the sources. + elseif (isset($rights[3]) && count($rights[3]) === count($sources)) { + $status = 'published'; + } + // Trusted for at least 1. + elseif (isset($rights[3]) && count($rights[3]) > 0) { + $status = 'to-review'; + } + // Allowed for all the sources. + elseif (isset($rights[2]) && count($rights[2]) === count($sources)) { + $status = 'to-review'; + } + // Unverified for some sources. + else { + $status = 'pending'; + } + + $this->setModerationStatus($status); + + // Add messages indicating the posting rights for easier review. + $message = ''; + if (!empty($rights[1])) { + $message = trim($message . strtr(' Blocked user for @sources.', [ + '@sources' => implode(', ', TaxonomyHelper::getSourceShortnames($rights[1])), + ])); + } + if (!empty($rights[0])) { + $message = trim($message . strtr(' Unverified user for @sources.', [ + '@sources' => implode(', ', TaxonomyHelper::getSourceShortnames($rights[0])), + ])); + } + if (!empty($rights[2])) { + $message = trim($message . strtr(' Allowed user for @sources.', [ + '@sources' => implode(', ', TaxonomyHelper::getSourceShortnames($rights[2])), + ])); + } + if (!empty($rights[3])) { + $message = trim($message . strtr(' Trusted user for @sources.', [ + '@sources' => implode(', ', TaxonomyHelper::getSourceShortnames($rights[3])), + ])); + } + + // Update the log message. + if (!empty($message)) { + $revision_log_field = $this->getEntityType() + ->getRevisionMetadataKey('revision_log_message'); + + if (!empty($revision_log_field)) { + $log = trim($this->{$revision_log_field}->value ?? ''); + $log = $message . (!empty($log) ? ' ' . $log : ''); + $this->{$revision_log_field}->value = $log; + } + } + } + } + } + /** * Temporarily store the email address to notify after publication. * diff --git a/html/modules/custom/reliefweb_entities/src/Entity/Source.php b/html/modules/custom/reliefweb_entities/src/Entity/Source.php index df559c8e5..e2c008350 100644 --- a/html/modules/custom/reliefweb_entities/src/Entity/Source.php +++ b/html/modules/custom/reliefweb_entities/src/Entity/Source.php @@ -146,18 +146,20 @@ protected function getOrganizationDetails() { $meta = []; // Organization type. - $type = $this->field_organization_type->entity->label(); - $meta['type'] = [ - 'type' => 'link', - 'label' => $this->t('Organization type'), - 'value' => [ - 'url' => RiverServiceBase::getRiverUrl('source', [ - 'search' => 'type.exact:"' . $type . '"', - ]), - 'title' => $type, - 'external' => FALSE, - ], - ]; + if ($this->hasField('field_organization_type') && !$this->get('field_organization_type')->isEmpty()) { + $type = $this->field_organization_type->entity->label(); + $meta['type'] = [ + 'type' => 'link', + 'label' => $this->t('Organization type'), + 'value' => [ + 'url' => RiverServiceBase::getRiverUrl('source', [ + 'search' => 'type.exact:"' . $type . '"', + ]), + 'title' => $type, + 'external' => FALSE, + ], + ]; + } // Headquarters. $countries = []; diff --git a/html/modules/custom/reliefweb_entities/src/EntityFormAlterServiceBase.php b/html/modules/custom/reliefweb_entities/src/EntityFormAlterServiceBase.php index f006e7ffd..985b28fed 100644 --- a/html/modules/custom/reliefweb_entities/src/EntityFormAlterServiceBase.php +++ b/html/modules/custom/reliefweb_entities/src/EntityFormAlterServiceBase.php @@ -582,7 +582,7 @@ public static function retrievePotentialNewSourceInformation(EntityModeratedInte } /** - * Add the user information to a job/training node form. + * Add the user information to a job/training/report node form. * * @param array $form * The entity form. @@ -594,7 +594,7 @@ protected function addUserInformation(array &$form, FormStateInterface $form_sta $entity_id = $entity->id(); $bundle = $entity->bundle(); - // It's only for jobs and training. + // It's only for jobs and trainings. if (!in_array($bundle, ['job', 'training'])) { return; } diff --git a/html/modules/custom/reliefweb_entities/src/OpportunityDocumentTrait.php b/html/modules/custom/reliefweb_entities/src/OpportunityDocumentTrait.php index 44ed15aa7..03c4126df 100644 --- a/html/modules/custom/reliefweb_entities/src/OpportunityDocumentTrait.php +++ b/html/modules/custom/reliefweb_entities/src/OpportunityDocumentTrait.php @@ -2,14 +2,12 @@ namespace Drupal\reliefweb_entities; -use Drupal\Core\Entity\RevisionLogInterface; -use Drupal\reliefweb_entities\Entity\Source; use Drupal\reliefweb_moderation\Helpers\UserPostingRightsHelper; use Drupal\reliefweb_utility\Helpers\TaxonomyHelper; use Drupal\reliefweb_utility\Helpers\UserHelper; /** - * Trait for "opportunity" documents like jobs and training. + * Trait for "opportunity" documents like jobs and trainings. * * @see Drupal\reliefweb_entities\DocuemntInterface */ @@ -103,44 +101,6 @@ protected function updateModerationStatusFromExpirationDate() { } } - /** - * Update the status to refused if any of the sources is blocked. - */ - protected function updateModerationStatusFromSourceStatus() { - if (!$this->hasField('field_source') || $this->field_source->isEmpty()) { - return; - } - - $blocked = []; - foreach ($this->field_source as $item) { - $source = $item->entity; - if (empty($source) || !($source instanceof Source)) { - continue; - } - - if ($source->getModerationStatus() === 'blocked') { - $blocked[] = $source->label(); - } - } - - if (!empty($blocked)) { - $this->setModerationStatus('refused'); - - // Add a message to the revision log. - if ($this instanceof RevisionLogInterface) { - $message = 'Submissions from "' . implode('", "', $blocked) . '" are no longer allowed.'; - - $log = $this->getRevisionLogMessage(); - if (empty($log)) { - $this->setRevisionLogMessage($message); - } - else { - $this->setRevisionLogMessage($message . ' ' . $log); - } - } - } - } - /** * Update creation date when the opportunity is published for the first time. */ diff --git a/html/modules/custom/reliefweb_entities/src/Services/ReportFormAlter.php b/html/modules/custom/reliefweb_entities/src/Services/ReportFormAlter.php index ee73f401e..d52c58e15 100644 --- a/html/modules/custom/reliefweb_entities/src/Services/ReportFormAlter.php +++ b/html/modules/custom/reliefweb_entities/src/Services/ReportFormAlter.php @@ -9,6 +9,7 @@ use Drupal\reliefweb_entities\Entity\Report; use Drupal\reliefweb_entities\EntityFormAlterServiceBase; use Drupal\reliefweb_form\Helpers\FormHelper; +use Drupal\reliefweb_moderation\Helpers\UserPostingRightsHelper; use Drupal\reliefweb_utility\Helpers\UrlHelper; /** @@ -102,6 +103,11 @@ protected function addBundleFormAlterations(array &$form, FormStateInterface $fo ); } + // Special tweaks for contributors. + if ($this->currentUser->hasRole('contributor')) { + $this->alterFieldsForContributors($form, $form_state); + } + // Validate the attachments. $form['#validate'][] = [$this, 'validateAttachment']; @@ -110,6 +116,43 @@ protected function addBundleFormAlterations(array &$form, FormStateInterface $fo // Prevent saving from a blocked source. $form['#validate'][] = [$this, 'validateBlockedSource']; + + // Prevent saving if user is blocked for a source. + $form['#validate'][] = [$this, 'validatePostingRightsBlockedSource']; + } + + /** + * Prevent saving if user is blocked for a source. + * + * @param array $form + * Form to alter. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Form state. + */ + public function validatePostingRightsBlockedSource(array $form, FormStateInterface &$form_state) { + $ids = []; + foreach ($form_state->getValue('field_source', []) as $item) { + if (!empty($item['target_id'])) { + $ids[] = $item['target_id']; + } + } + + $rights = UserPostingRightsHelper::getUserConsolidatedPostingRight($this->currentUser, 'report', $ids); + // Blocked for at least one source. + if (!empty($rights) && isset($rights['code']) && $rights['code'] == 1) { + $sources = $this->getEntityTypeManager() + ->getStorage('taxonomy_term') + ->loadMultiple($rights['sources']); + + /** @var \Drupal\taxonomy\Entity\Term $term */ + array_walk($sources, function (&$term) { + $term = $term->label(); + }); + + $form_state->setErrorByName('field_source', $this->t('Publications from "@sources" are not allowed.', [ + '@sources' => implode('", "', $sources), + ])); + } } /** @@ -400,4 +443,34 @@ public function validateEmbargoDate(array $form, FormStateInterface &$form_state } } + /** + * Make alterations for Contributor role. + * + * @param array $form + * Form to alter. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * Form state. + */ + protected function alterFieldsForContributors(array &$form, FormStateInterface $form_state) { + // Default to submit for new documents otherwise preserve the value, for + // example when editing a report created by an editor. + if ($form_state->getFormObject()?->getEntity()?->isNew() === TRUE) { + $form['field_origin']['widget']['#default_value'] = '1'; + } + // Change the field to 'hidden' to hide it while perserving its value so + // that the alteration and validation of the origin notes field still work. + // @see ::alterOriginFields() + $form['field_origin']['widget']['#type'] = 'hidden'; + + // Hide fields. + $form['field_bury']['#access'] = FALSE; + $form['field_feature']['#access'] = FALSE; + $form['field_notify']['#access'] = FALSE; + + $form['field_headline']['#access'] = FALSE; + $form['field_headline_title']['#access'] = FALSE; + $form['field_headline_summary']['#access'] = FALSE; + $form['field_headline_image']['#access'] = FALSE; + } + } diff --git a/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportAddMultipleSourcesTest.php b/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportAddMultipleSourcesTest.php new file mode 100644 index 000000000..c2bf3ea10 --- /dev/null +++ b/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportAddMultipleSourcesTest.php @@ -0,0 +1,388 @@ +contributor = $this->createUserIfNeeded(2884910, 'report unverified'); + if (!$this->contributor->hasRole('contributor')) { + $this->contributor->addRole('contributor'); + $this->contributor->save(); + } + + $rights = [ + 0 => 'unverified', + 1 => 'blocked', + 2 => 'allowed', + 3 => 'trusted', + ]; + + foreach ($rights as $id => $right) { + $label = 'Src ' . $right; + $field_name = 'source_' . $right; + + // Create term and assign rights. + $this->{$field_name} = $this->createTermIfNeeded('source', 9999900 + $id, $label, [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $this->{$field_name}->set('field_user_posting_rights', [ + [ + 'id' => $this->contributor->id(), + 'job' => '0', + 'training' => '0', + 'report' => $id, + 'notes' => '', + ], + ]); + $this->{$field_name}->save(); + } + + // Create term and assign rights. + $this->source_random = $this->createTermIfNeeded('source', 9999999, 'Src random', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + } + + /** + * Test adding a report - blocked. + */ + public function testAddReportAsContributorBlockedWithoutRandom() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $this->drupalLogin($this->contributor); + + $edit = $this->getEditFields($title); + $edit['field_source[]'] = [ + $this->source_unverified->id(), + $this->source_blocked->id(), + $this->source_allowed->id(), + $this->source_trusted->id(), + ]; + + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has thrown an error. + $this->assertSession()->pageTextContains('Publications from "Src blocked" are not allowed.'); + } + + /** + * Test adding a report - unverified. + */ + public function testAddReportAsContributorUnverifiedWithoutRandom() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $this->drupalLogin($this->contributor); + + $edit = $this->getEditFields($title); + $edit['field_source[]'] = [ + $this->source_unverified->id(), + $this->source_allowed->id(), + $this->source_trusted->id(), + ]; + + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(200); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'to-review'); + } + + /** + * Test adding a report - allowed. + */ + public function testAddReportAsContributorAllowedWithoutRandom() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $this->drupalLogin($this->contributor); + + $edit = $this->getEditFields($title); + $edit['field_source[]'] = [ + $this->source_allowed->id(), + $this->source_trusted->id(), + ]; + + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(200); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'to-review'); + } + + /** + * Test adding a report - trusted. + */ + public function testAddReportAsContributorTrustedWithoutRandom() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $this->drupalLogin($this->contributor); + + $edit = $this->getEditFields($title); + $edit['field_source[]'] = [ + $this->source_trusted->id(), + ]; + + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(200); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'published'); + } + + /** + * Test adding a report - blocked. + */ + public function testAddReportAsContributorBlockedWithRandom() { + $title = $this->randomMachineName(32); + + $this->drupalLogin($this->contributor); + + $edit = $this->getEditFields($title); + $edit['field_source[]'] = [ + $this->source_unverified->id(), + $this->source_blocked->id(), + $this->source_allowed->id(), + $this->source_trusted->id(), + $this->source_random->id(), + ]; + + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has thrown an error. + $this->assertSession()->pageTextContains('Publications from "Src blocked" are not allowed.'); + } + + /** + * Test adding a report - unverified. + */ + public function testAddReportAsContributorUnverifiedWithRandom() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $this->drupalLogin($this->contributor); + + $edit = $this->getEditFields($title); + $edit['field_source[]'] = [ + $this->source_unverified->id(), + $this->source_allowed->id(), + $this->source_trusted->id(), + $this->source_random->id(), + ]; + + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(200); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'to-review'); + } + + /** + * Test adding a report - allowed. + */ + public function testAddReportAsContributorAllowedWithRandom() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $this->drupalLogin($this->contributor); + + $edit = $this->getEditFields($title); + $edit['field_source[]'] = [ + $this->source_allowed->id(), + $this->source_trusted->id(), + $this->source_random->id(), + ]; + + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(200); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'to-review'); + } + + /** + * Test adding a report - trusted. + */ + public function testAddReportAsContributorTrustedWithRandom() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $this->drupalLogin($this->contributor); + + $edit = $this->getEditFields($title); + $edit['field_source[]'] = [ + $this->source_trusted->id(), + $this->source_random->id(), + ]; + + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(200); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'to-review'); + } + + protected function getEditFields($title) { + $term_language = $this->createTermIfNeeded('language', 267, 'English'); + $term_country = $this->createTermIfNeeded('country', 34, 'Belgium'); + $term_format = $this->createTermIfNeeded('content_format', 11, 'UN Document'); + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Create a node. + $edit = []; + $edit['title[0][value]'] = $title; + $edit['field_language[' . $term_language->id() . ']'] = $term_language->id(); + $edit['field_country[]'] = [$term_country->id()]; + $edit['field_primary_country'] = $term_country->id(); + $edit['field_content_format'] = $term_format->id(); + $edit['field_source[]'] = [$term_source->id()]; + + return $edit; + } +} diff --git a/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportAddTest.php b/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportAddTest.php new file mode 100644 index 000000000..d393c0f69 --- /dev/null +++ b/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportAddTest.php @@ -0,0 +1,509 @@ +get('name'); + $title = $this->randomMachineName(32); + + $admin = User::load(1); + $this->drupalLogin($admin); + + $edit = $this->getEditFields($title); + $edit['field_origin_notes[0][value]'] = 'https://example.com/' . $title; + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Publish'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('ABC Color'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + $this->assertSession()->elementTextEquals('css', '.rw-moderation-information__status.rw-moderation-status', 'Published'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + } + + /** + * Test adding a report as admin, draft. + */ + public function testAddReportAsAdminDraft() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $admin = User::load(1); + $this->drupalLogin($admin); + + $edit = $this->getEditFields($title); + $edit['field_origin_notes[0][value]'] = 'https://example.com/' . $title; + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Save as draft'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('ABC Color'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + $this->assertSession()->elementTextEquals('css', '.rw-moderation-information__status.rw-moderation-status', 'Draft'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(404); + } + + /** + * Test adding a report as contributor, draft, unverified. + */ + public function testAddReportAsContributorDraftUnverified() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $user = $this->createUserIfNeeded(2884910, 'report unverified'); + if (!$user->hasRole('contributor')) { + $user->addRole('contributor'); + $user->save(); + } + $this->drupalLogin($user); + + // Create term first so we can assign posting rights. + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $term_source->set('field_user_posting_rights', [ + [ + 'id' => $user->id(), + 'job' => '0', + 'training' => '0', + 'report' => '0', // Unverified. + 'notes' => '', + ], + ]); + $term_source->save(); + + $edit = $this->getEditFields($title); + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Save as draft'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('ABC Color'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(404); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'draft'); + } + + /** + * Test adding a report as contributor, submit, unverified. + */ + public function testAddReportAsContributorSubmitUnverified() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $user = $this->createUserIfNeeded(2884910, 'report unverified'); + if (!$user->hasRole('contributor')) { + $user->addRole('contributor'); + $user->save(); + } + $this->drupalLogin($user); + + // Create term first so we can assign posting rights. + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $term_source->set('field_user_posting_rights', [ + [ + 'id' => $user->id(), + 'job' => '0', + 'training' => '0', + 'report' => '0', // Unverified. + 'notes' => '', + ], + ]); + $term_source->save(); + + $edit = $this->getEditFields($title); + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('ABC Color'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(404); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'pending'); + } + + /** + * Test adding a report as contributor, draft, blocked. + */ + public function testAddReportAsContributorDraftBlocked() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $user = $this->createUserIfNeeded(2884910, 'report blocked'); + if (!$user->hasRole('contributor')) { + $user->addRole('contributor'); + $user->save(); + } + $this->drupalLogin($user); + + // Create term first so we can assign posting rights. + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $term_source->set('field_user_posting_rights', [ + [ + 'id' => $user->id(), + 'job' => '0', + 'training' => '0', + 'report' => '1', // Blocked. + 'notes' => '', + ], + ]); + $term_source->save(); + + $edit = $this->getEditFields($title); + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Save as draft'); + + // Check that the report has thrown an error. + $this->assertSession()->pageTextContains('Publications from "ABC Color" are not allowed.'); + } + + /** + * Test adding a report as contributor, submit, blocked. + */ + public function testAddReportAsContributorSubmitBlocked() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $user = $this->createUserIfNeeded(2884910, 'report blocked'); + if (!$user->hasRole('contributor')) { + $user->addRole('contributor'); + $user->save(); + } + $this->drupalLogin($user); + + // Create term first so we can assign posting rights. + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $term_source->set('field_user_posting_rights', [ + [ + 'id' => $user->id(), + 'job' => '0', + 'training' => '0', + 'report' => '1', // Blocked. + 'notes' => '', + ], + ]); + $term_source->save(); + + $edit = $this->getEditFields($title); + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has thrown an error. + $this->assertSession()->pageTextContains('Publications from "ABC Color" are not allowed.'); + } + + /** + * Test adding a report as contributor, draft, allowed. + */ + public function testAddReportAsContributorDraftAllowed() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $user = $this->createUserIfNeeded(2884910, 'report allowed'); + if (!$user->hasRole('contributor')) { + $user->addRole('contributor'); + $user->save(); + } + $this->drupalLogin($user); + + // Create term first so we can assign posting rights. + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $term_source->set('field_user_posting_rights', [ + [ + 'id' => $user->id(), + 'job' => '0', + 'training' => '0', + 'report' => '2', // Allowed. + 'notes' => '', + ], + ]); + $term_source->save(); + + $edit = $this->getEditFields($title); + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Save as draft'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('ABC Color'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(404); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'draft'); + } + + /** + * Test adding a report as contributor, submit, allowed. + */ + public function testAddReportAsContributorSubmitAllowed() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $user = $this->createUserIfNeeded(2884910, 'report allowed'); + if (!$user->hasRole('contributor')) { + $user->addRole('contributor'); + $user->save(); + } + $this->drupalLogin($user); + + // Create term first so we can assign posting rights. + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $term_source->set('field_user_posting_rights', [ + [ + 'id' => $user->id(), + 'job' => '0', + 'training' => '0', + 'report' => '2', // Allowed. + 'notes' => '', + ], + ]); + $term_source->save(); + + $edit = $this->getEditFields($title); + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('ABC Color'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(200); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'to-review'); + } + + /** + * Test adding a report as contributor, draft, trusted. + */ + public function testAddReportAsContributorDraftTrusted() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $user = $this->createUserIfNeeded(2884910, 'report trusted'); + if (!$user->hasRole('contributor')) { + $user->addRole('contributor'); + $user->save(); + } + $this->drupalLogin($user); + + // Create term first so we can assign posting rights. + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $term_source->set('field_user_posting_rights', [ + [ + 'id' => $user->id(), + 'job' => '0', + 'training' => '0', + 'report' => '3', // Trusted. + 'notes' => '', + ], + ]); + $term_source->save(); + + $edit = $this->getEditFields($title); + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Save as draft'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('ABC Color'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->statusCodeEquals(404); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'draft'); + } + + /** + * Test adding a report as contributor, submit, trusted. + */ + public function testAddReportAsContributorSubmitTrusted() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = $this->randomMachineName(32); + + $user = $this->createUserIfNeeded(2884910, 'report trusted'); + if (!$user->hasRole('contributor')) { + $user->addRole('contributor'); + $user->save(); + } + $this->drupalLogin($user); + + // Create term first so we can assign posting rights. + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $term_source->set('field_user_posting_rights', [ + [ + 'id' => $user->id(), + 'job' => '0', + 'training' => '0', + 'report' => '3', // Trusted. + 'notes' => '', + ], + ]); + $term_source->save(); + + $edit = $this->getEditFields($title); + $this->drupalGet('node/add/report'); + $this->submitForm($edit, 'Submit'); + + // Check that the report has been created. + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->pageTextContains('Report ' . $edit['title[0][value]'] . ' has been created.'); + $this->assertSession()->pageTextContains('Belgium'); + $this->assertSession()->pageTextContains('ABC Color'); + $this->assertSession()->pageTextContains('UN Document'); + $this->assertSession()->pageTextContains('English'); + + // Check as anonymous. + $this->drupalGet('user/logout'); + $node = $this->getNodeByTitle($title); + $this->drupalGet($node->toUrl()); + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + + // Check moderation status. + $this->assertEquals($node->moderation_status->value, 'published'); + } + + protected function getEditFields($title) { + $term_language = $this->createTermIfNeeded('language', 267, 'English'); + $term_country = $this->createTermIfNeeded('country', 34, 'Belgium'); + $term_format = $this->createTermIfNeeded('content_format', 11, 'UN Document'); + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Create a node. + $edit = []; + $edit['title[0][value]'] = $title; + $edit['field_language[' . $term_language->id() . ']'] = $term_language->id(); + $edit['field_country[]'] = [$term_country->id()]; + $edit['field_primary_country'] = $term_country->id(); + $edit['field_content_format'] = $term_format->id(); + $edit['field_source[]'] = [$term_source->id()]; + + return $edit; + } +} diff --git a/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportBase.php b/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportBase.php new file mode 100644 index 000000000..905bdb5e7 --- /dev/null +++ b/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportBase.php @@ -0,0 +1,54 @@ + $vocabulary, + 'tid' => $id, + 'name' => $title, + ] + $extra); + $term->save(); + + return $term; + } + + /** + * Create user if needed. + */ + protected function createUserIfNeeded($id, $name, array $extra = []) : User { + if ($user = User::load($id)) { + return $user; + } + + $user = User::create([ + 'uid' => $id, + 'name' => $name, + 'mail' => $this->randomMachineName(32) . '@localhost.localdomain', + 'status' => 1, + ] + $extra); + $user->save(); + + return $user; + } + +} diff --git a/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportCreateTest.php b/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportCreateTest.php new file mode 100644 index 000000000..a80081322 --- /dev/null +++ b/html/modules/custom/reliefweb_entities/tests/src/ExistingSite/RwReportCreateTest.php @@ -0,0 +1,322 @@ +get('name'); + $title = 'My report'; + $user = User::load(1); + $this->drupalLogin($user); + + $term_language = $this->createTermIfNeeded('language', 267, 'English'); + $term_country = $this->createTermIfNeeded('country', 34, 'Belgium'); + $term_format = $this->createTermIfNeeded('content_format', 11, 'UN Document'); + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + $report = Report::create([ + 'uid' => $user->id(), + 'type' => 'report', + 'title' => $title, + 'moderation_status' => 'draft', + 'field_origin' => 0, + 'field_origin_notes' => 'https://www.example.com/my-report', + 'field_language' => $term_language->id(), + 'field_country' => [ + $term_country->id(), + ], + 'field_primary_country' => $term_country->id(), + 'field_content_format' => $term_format->id(), + 'field_source' => [ + $term_source->id(), + ], + ]); + + // Report will be saved as draft. + $report->save(); + + // OK for user. + $this->drupalGet($report->toUrl()); + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->elementTextEquals('css', '.rw-article__title.rw-page-title', $title); + + // 404 for anonymous. + $this->drupalGet('user/logout'); + $this->drupalGet($report->toUrl()); + $this->assertSession()->statusCodeEquals(404); + } + + /** + * Test report. + */ + public function testCreateReportAsAdminPublished() { + $site_name = \Drupal::config('system.site')->get('name'); + $title = 'My report'; + $user = User::load(1); + $this->drupalLogin($user); + + $term_language = $this->createTermIfNeeded('language', 267, 'English'); + $term_country = $this->createTermIfNeeded('country', 34, 'Belgium'); + $term_format = $this->createTermIfNeeded('content_format', 11, 'UN Document'); + $term_source = $this->createTermIfNeeded('source', 43679, 'ABC Color', [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + $report = Report::create([ + 'uid' => $user->id(), + 'type' => 'report', + 'title' => $title, + 'moderation_status' => 'published', + 'field_origin' => 0, + 'field_origin_notes' => 'https://www.example.com/my-report', + 'field_language' => $term_language->id(), + 'field_country' => [ + $term_country->id(), + ], + 'field_primary_country' => $term_country->id(), + 'field_content_format' => $term_format->id(), + 'field_source' => [ + $term_source->id(), + ], + ]); + + // Report will be saved as published. + $report->save(); + + // OK for user. + $this->drupalGet($report->toUrl()); + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->elementTextEquals('css', '.rw-article__title.rw-page-title', $title); + + // 404 for anonymous. + $this->drupalGet('user/logout'); + $this->drupalGet($report->toUrl()); + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->elementTextEquals('css', '.rw-article__title.rw-page-title', $title); + } + + /** + * Test report as contributor unverified, draft. + */ + public function testCreateReportAsContributorUnverifiedDraft() { + $title = 'My report - unverified'; + $this->setUserPostingRightsGetSourceTerm(0, 'Unverified'); + $moderation_status = 'draft'; + $expected_moderation_status = 'draft'; + + $this->runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, FALSE); + } + + /** + * Test report as contributor unverified, pending. + */ + public function testCreateReportAsContributorUnverifiedToReview() { + $title = 'My report - unverified'; + $this->setUserPostingRightsGetSourceTerm(0, 'Unverified'); + $moderation_status = 'pending'; + $expected_moderation_status = 'pending'; + + $this->runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, FALSE); + } + + /** + * Test report as contributor blocked, draft. + */ + public function testCreateReportAsContributorBlockedDraft() { + $title = 'My report - blocked'; + $this->setUserPostingRightsGetSourceTerm(1, 'blocked'); + $moderation_status = 'draft'; + $expected_moderation_status = 'draft'; + + $this->runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, FALSE); + } + + /** + * Test report as contributor blocked, to-review. + */ + public function testCreateReportAsContributorBlockedToReview() { + $title = 'My report - blocked'; + $this->setUserPostingRightsGetSourceTerm(1, 'blocked'); + $moderation_status = 'to-review'; + $expected_moderation_status = 'refused'; + + $this->runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, FALSE); + } + + /** + * Test report as contributor allowed, draft. + */ + public function testCreateReportAsContributorAllowedDraft() { + $title = 'My report - allowed'; + $this->setUserPostingRightsGetSourceTerm(2, 'allowed'); + $moderation_status = 'draft'; + $expected_moderation_status = 'draft'; + + $this->runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, FALSE); + } + + /** + * Test report as contributor allowed, to-review. + */ + public function testCreateReportAsContributorAllowedToReview() { + $title = 'My report - allowed'; + $this->setUserPostingRightsGetSourceTerm(2, 'allowed'); + $moderation_status = 'to-review'; + $expected_moderation_status = 'to-review'; + + $this->runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, TRUE); + } + + /** + * Test report as contributor trusted, draft. + */ + public function testCreateReportAsContributorTrustedDraft() { + $title = 'My report - trusted'; + $this->setUserPostingRightsGetSourceTerm(3, 'trusted'); + $moderation_status = 'draft'; + $expected_moderation_status = 'draft'; + + $this->runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, FALSE); + } + + /** + * Test report as contributor trusted, to-review. + */ + public function testCreateReportAsContributorTrustedToReview() { + $title = 'My report - trusted'; + $this->setUserPostingRightsGetSourceTerm(3, 'trusted'); + $moderation_status = 'to-review'; + $expected_moderation_status = 'published'; + + $this->runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, TRUE); + } + + /** + * Test report as contributor trusted, to-review but blocked source. + */ + public function testCreateReportAsContributorTrustedToReviewBlockedSource() { + $title = 'My report - trusted'; + $term_source = $this->setUserPostingRightsGetSourceTerm(3, 'trusted', 2884910, 999999, 'Blocked source'); + $term_source + ->set('moderation_status', 'blocked') + ->save(); + + $moderation_status = 'to-review'; + $expected_moderation_status = 'refused'; + + $this->runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, FALSE, [ + 'field_source' => [ + $term_source->id(), + ], + ]); + } + + /** + * Test report as contributor. + */ + protected function runTestCreateReportAsContributor($title, $moderation_status, $expected_moderation_status, $will_be_public, array $overrides = []) { + $site_name = \Drupal::config('system.site')->get('name'); + + $user = User::load(2884910); + $this->drupalLogin($user); + + $term_language = $this->createTermIfNeeded('language', 267, 'English'); + $term_country = $this->createTermIfNeeded('country', 34, 'Belgium'); + $term_format = $this->createTermIfNeeded('content_format', 11, 'UN Document'); + $term_source = Term::load(43679); + + $report = Report::create($overrides + [ + 'uid' => $user->id(), + 'revision_uid' => $user->id(), + 'type' => 'report', + 'title' => $title, + 'moderation_status' => $moderation_status, + 'field_origin' => 0, + 'field_origin_notes' => 'https://www.example.com/my-report', + 'field_language' => $term_language->id(), + 'field_country' => [ + $term_country->id(), + ], + 'field_primary_country' => $term_country->id(), + 'field_content_format' => $term_format->id(), + 'field_source' => [ + $term_source->id(), + ], + ]); + + // Report will be saved as draft. + $report->save(); + + // OK for user. + $this->drupalGet($report->toUrl()); + $this->assertSession()->titleEquals($title . ' - Belgium | ' . $site_name); + $this->assertSession()->elementTextEquals('css', '.rw-article__title.rw-page-title', $title); + + // Test for anonymous. + $this->drupalGet('user/logout'); + $this->drupalGet($report->toUrl()); + if ($will_be_public) { + $this->assertSession()->statusCodeEquals(200); + } + else { + $this->assertSession()->statusCodeEquals(404); + } + + // Check moderation status. + $this->assertEquals($report->moderation_status->value, $expected_moderation_status); + } + + /** + * Set user posting rights. + */ + protected function setUserPostingRightsGetSourceTerm($right, $label, $uid = 2884910, $tid = 43679, $term_label = 'ABC Color') : Term { + $user = $this->createUserIfNeeded($uid, $label); + if (!$user->hasRole('contributor')) { + $user->addRole('contributor'); + $user->save(); + } + + // Create term first so we can assign posting rights. + $term_source = $this->createTermIfNeeded('source', $tid, $term_label, [ + 'field_allowed_content_types' => [ + 1, + ], + ]); + + // Set posting right to + $term_source->set('field_user_posting_rights', [ + [ + 'id' => $user->id(), + 'job' => '0', + 'training' => '0', + 'report' => $right, + 'notes' => '', + ], + ]); + $term_source->save(); + + drupal_static_reset('reliefweb_moderation_getUserPostingRights'); + + return $term_source; + } +} diff --git a/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php b/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php index 3100b3609..6edc0d14d 100644 --- a/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php +++ b/html/modules/custom/reliefweb_entraid/tests/src/ExistingSite/ReliefwebEntraidLoginTest.php @@ -49,6 +49,9 @@ protected function tearDown(): void { * @covers ::redirectLogin() */ public function testRedirectLogin() { + + global $base_url; + // Skip if the module is not installed. if (!$this->container->get('module_handler')->moduleExists('reliefweb_entraid')) { $this->assertTrue(TRUE); @@ -75,9 +78,9 @@ public function testRedirectLogin() { // Set the endpoints. We just point at the robots.txt as we know it exists // and so, if the reponse status code in 200, then the redirection worked. $data = $entraid_config->get(); - $data['settings']['authorization_endpoint_wa'] = 'http://localhost/robots.txt'; - $data['settings']['token_endpoint_wa'] = 'http://localhost/robots.txt'; - $data['settings']['iss_allowed_domains'] = 'http://localhost/robots.txt'; + $data['settings']['authorization_endpoint_wa'] = $base_url . '/robots.txt'; + $data['settings']['token_endpoint_wa'] = $base_url . '/robots.txt'; + $data['settings']['iss_allowed_domains'] = $base_url . '/robots.txt'; $entraid_config->setData($data)->save(); // If the redirection works, a 200 will be returned. diff --git a/html/modules/custom/reliefweb_fields/components/reliefweb-user-posting-rights/reliefweb-user-posting-rights.css b/html/modules/custom/reliefweb_fields/components/reliefweb-user-posting-rights/reliefweb-user-posting-rights.css index 2f1bd063e..254b8e659 100644 --- a/html/modules/custom/reliefweb_fields/components/reliefweb-user-posting-rights/reliefweb-user-posting-rights.css +++ b/html/modules/custom/reliefweb_fields/components/reliefweb-user-posting-rights/reliefweb-user-posting-rights.css @@ -41,7 +41,7 @@ flex-wrap: wrap; align-items: center; } -.field--type-reliefweb-user-posting-rights div[data-filters][data-job="all"][data-training="all"] ~ ul li { +.field--type-reliefweb-user-posting-rights div[data-filters][data-job="all"][data-training="all"][data-report="all"] ~ ul li { display: flex; } .field--type-reliefweb-user-posting-rights div[data-filters][data-job="0"] ~ ul li[data-job="0"] { @@ -68,8 +68,22 @@ .field--type-reliefweb-user-posting-rights div[data-filters][data-training="3"] ~ ul li[data-training="3"] { display: flex; } +.field--type-reliefweb-user-posting-rights div[data-filters][data-report="0"] ~ ul li[data-report="0"] { + display: flex; +} +.field--type-reliefweb-user-posting-rights div[data-filters][data-report="1"] ~ ul li[data-report="1"] { + display: flex; +} +.field--type-reliefweb-user-posting-rights div[data-filters][data-report="2"] ~ ul li[data-report="2"] { + display: flex; +} +.field--type-reliefweb-user-posting-rights div[data-filters][data-report="3"] ~ ul li[data-report="3"] { + display: flex; +} + .field--type-reliefweb-user-posting-rights [data-job] label.job span:after, -.field--type-reliefweb-user-posting-rights [data-training] label.training span:after { +.field--type-reliefweb-user-posting-rights [data-training] label.training span:after, +.field--type-reliefweb-user-posting-rights [data-report] label.report span:after { display: inline-block; width: 12px; height: 12px; @@ -79,21 +93,26 @@ border-radius: 4px; } .field--type-reliefweb-user-posting-rights [data-job="0"] label.job span:after, -.field--type-reliefweb-user-posting-rights [data-training="0"] label.training span:after { +.field--type-reliefweb-user-posting-rights [data-training="0"] label.training span:after, +.field--type-reliefweb-user-posting-rights [data-report="0"] label.report span:after { background: #f49e2c; } .field--type-reliefweb-user-posting-rights [data-job="1"] label.job span:after, -.field--type-reliefweb-user-posting-rights [data-training="1"] label.training span:after { +.field--type-reliefweb-user-posting-rights [data-training="1"] label.training span:after, +.field--type-reliefweb-user-posting-rights [data-report="1"] label.report span:after { background: #da190b; } .field--type-reliefweb-user-posting-rights [data-job="2"] label.job span:after, -.field--type-reliefweb-user-posting-rights [data-training="2"] label.training span:after { +.field--type-reliefweb-user-posting-rights [data-training="2"] label.training span:after, +.field--type-reliefweb-user-posting-rights [data-report="2"] label.report span:after { background: #076d96; } .field--type-reliefweb-user-posting-rights [data-job="3"] label.job span:after, -.field--type-reliefweb-user-posting-rights [data-training="3"] label.training span:after { +.field--type-reliefweb-user-posting-rights [data-training="3"] label.training span:after, +.field--type-reliefweb-user-posting-rights [data-report="3"] label.report span:after { background: #88bb09; } + .field--type-reliefweb-user-posting-rights ul { margin: 0; padding: 0; diff --git a/html/modules/custom/reliefweb_fields/components/reliefweb-user-posting-rights/reliefweb-user-posting-rights.js b/html/modules/custom/reliefweb_fields/components/reliefweb-user-posting-rights/reliefweb-user-posting-rights.js index 13bf15e58..2ee8e1cf7 100644 --- a/html/modules/custom/reliefweb_fields/components/reliefweb-user-posting-rights/reliefweb-user-posting-rights.js +++ b/html/modules/custom/reliefweb_fields/components/reliefweb-user-posting-rights/reliefweb-user-posting-rights.js @@ -117,6 +117,7 @@ container.setAttribute('data-status', data.status ? 'active' : 'blocked'); container.setAttribute('data-job', data.job); container.setAttribute('data-training', data.training); + container.setAttribute('data-report', data.report); // User info. var info = document.createElement('div'); @@ -147,6 +148,7 @@ // Rights. actions.appendChild(this.createSelect('job', data.job, disabled)); actions.appendChild(this.createSelect('training', data.training, disabled)); + actions.appendChild(this.createSelect('report', data.report, disabled)); // Remove. actions.appendChild(this.createButton('remove', t('Remove'), true, '', disabled)); @@ -205,6 +207,7 @@ container.setAttribute('data-filters', ''); container.setAttribute('data-job', 'all'); container.setAttribute('data-training', 'all'); + container.setAttribute('data-report', 'all'); var title = document.createElement('span'); title.appendChild(document.createTextNode(t('Filter: '))); @@ -213,6 +216,7 @@ // Rights filters. container.appendChild(this.createSelect('job', '', false, true)); container.appendChild(this.createSelect('training', '', false, true)); + container.appendChild(this.createSelect('report', '', false, true)); return container; }, @@ -418,6 +422,7 @@ id: element.getAttribute('data-id'), job: Math.max(element.querySelector('select[data-name="job"]').selectedIndex, 0), training: Math.max(element.querySelector('select[data-name="training"]').selectedIndex, 0), + report: Math.max(element.querySelector('select[data-name="report"]').selectedIndex, 0), notes: element.querySelector('textarea').value.trim() }; }, @@ -511,7 +516,7 @@ var name = target.getAttribute('data-name'); // Update the rights attributes of the user row. - if (name === 'job' || name === 'training') { + if (name === 'job' || name === 'training' || name === 'report') { var parent = target.parentNode.parentNode; // If the parent is not the filter container, then it's a select diff --git a/html/modules/custom/reliefweb_fields/reliefweb_fields.install b/html/modules/custom/reliefweb_fields/reliefweb_fields.install new file mode 100644 index 000000000..3d2d88028 --- /dev/null +++ b/html/modules/custom/reliefweb_fields/reliefweb_fields.install @@ -0,0 +1,65 @@ +get('taxonomy_term.field_schema_data.field_user_posting_rights'); + foreach ($kv_schema as $table => &$info) { + $schema = Database::getConnection()->schema(); + if (!$schema->fieldExists($table, 'field_user_posting_rights_report')) { + $spec = [ + 'description' => 'Report posting rights: 0 = unverified; 1 = blocked; 2 = allowed; 3 = trusted.', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => '', + 'default' => 0, + ]; + $schema->addField($table, 'field_user_posting_rights_report', $spec); + + $spec = [ + 'fields' => [ + 'field_user_posting_rights_report' => [ + 'description' => 'Report posting rights: 0 = unverified; 1 = blocked; 2 = allowed; 3 = trusted.', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => '', + 'default' => 0, + ], + ], + 'indexes' => [ + 'field_user_posting_rights_report' => ['field_user_posting_rights_report'], + ], + ]; + + $schema->addIndex($table, 'field_user_posting_rights_report', [ + 'field_user_posting_rights_report', + ], $spec); + } + + $info['fields']['field_user_posting_rights_report'] = [ + 'description' => 'Report posting rights: 0 = unverified; 1 = blocked; 2 = allowed; 3 = trusted.', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => '', + 'default' => 0, + ]; + $info['indexes']['field_user_posting_rights_report'] = [ + 'field_user_posting_rights_report', + ]; + } + + \Drupal::keyValue('entity.storage_schema.sql')->set('taxonomy_term.field_schema_data.field_user_posting_rights', $kv_schema); + + $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + $field_storage_definition = $entity_definition_update_manager->getFieldStorageDefinition('field_user_posting_rights', 'taxonomy_term'); + $field_storage_definition->setSetting('column_changes_handled', TRUE); + $entity_definition_update_manager->updateFieldStorageDefinition($field_storage_definition); +} diff --git a/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldType/ReliefWebUserPostingRights.php b/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldType/ReliefWebUserPostingRights.php index 39ecbedea..c356821b2 100644 --- a/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldType/ReliefWebUserPostingRights.php +++ b/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldType/ReliefWebUserPostingRights.php @@ -50,6 +50,13 @@ public static function schema(FieldStorageDefinitionInterface $field_definition) 'not null' => TRUE, 'default' => 0, ], + 'report' => [ + 'description' => 'Report posting rights: 0 = unverified; 1 = blocked; 2 = allowed; 3 = trusted.', + 'type' => 'int', + 'size' => 'tiny', + 'not null' => TRUE, + 'default' => 0, + ], 'notes' => [ 'description' => 'Notes', 'type' => 'text', @@ -60,6 +67,7 @@ public static function schema(FieldStorageDefinitionInterface $field_definition) 'id' => ['id'], 'job' => ['job'], 'training' => ['training'], + 'report' => ['report'], ], ]; } @@ -80,6 +88,10 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel ->setLabel(new TranslatableMarkup('Training')) ->setRequired(FALSE); + $properties['report'] = DataDefinition::create('integer') + ->setLabel(new TranslatableMarkup('Report')) + ->setRequired(FALSE); + $properties['notes'] = DataDefinition::create('string') ->setLabel(new TranslatableMarkup('Notes')) ->setRequired(FALSE); @@ -136,6 +148,17 @@ public function getConstraints() { ], ], ]); + $constraints[] = $constraint_manager->create('ComplexData', [ + 'report' => [ + 'AllowedValues' => [ + 'choices' => [0, 1, 2, 3], + 'strict' => TRUE, + 'message' => $this->t('%name: the Report rights must be one of 0, 1, 2 or 3.', [ + '%name' => $this->getFieldDefinition()->getLabel(), + ]), + ], + ], + ]); return $constraints; } @@ -147,6 +170,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin $values['url'] = mt_rand(3, 1000000); $values['job'] = mt_rand(0, 3); $values['training'] = mt_rand(0, 3); + $values['report'] = mt_rand(0, 3); return $values; } diff --git a/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldWidget/ReliefWebUserPostingRights.php b/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldWidget/ReliefWebUserPostingRights.php index 227ece21c..55c795ec8 100644 --- a/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldWidget/ReliefWebUserPostingRights.php +++ b/html/modules/custom/reliefweb_fields/src/Plugin/Field/FieldWidget/ReliefWebUserPostingRights.php @@ -174,6 +174,7 @@ public function massageFormValues(array $values, array $form, FormStateInterface 'id' => intval($item['id'], 10), 'job' => intval($item['job'], 10), 'training' => intval($item['training'], 10), + 'report' => intval($item['report'], 10), 'notes' => $item['notes'], ]; } @@ -200,6 +201,7 @@ public static function normalizeData(array $data) { $data['id'] = intval($data['id'] ?? $data['uid'], 10); $data['job'] = isset($data['job']) ? intval($data['job'], 10) : 0; $data['training'] = isset($data['training']) ? intval($data['training'], 10) : 0; + $data['report'] = isset($data['report']) ? intval($data['report'], 10) : 0; $data['notes'] = isset($data['notes']) ? trim($data['notes']) : ''; $data['name'] = trim($data['name']); @@ -210,6 +212,7 @@ public static function normalizeData(array $data) { if ($data['status'] === 0) { $data['job'] = 1; $data['training'] = 1; + $data['report'] = 1; } return $data; @@ -410,7 +413,8 @@ public static function updateFields($op, UserInterface $user) { $query->condition($query ->orConditionGroup() ->condition($field_name . '.job', 1, '<>') - ->condition($field_name . '.training', 1, '<>')); + ->condition($field_name . '.training', 1, '<>') + ->condition($field_name . '.report', 1, '<>')); } $ids = $query?->execute(); @@ -435,9 +439,10 @@ public static function updateFields($op, UserInterface $user) { } // Set the rights to 'blocked' if the account is blocked. elseif ($blocked) { - if ($item['job'] != 1 || $item['training'] != 1) { + if ($item['job'] != 1 || $item['training'] != 1 || $item['report'] != 1) { $items[$delta]['job'] = 1; $items[$delta]['training'] = 1; + $items[$delta]['report'] = 1; if (!empty($item['notes'])) { $items[$delta]['notes'] .= ' ' . $message; } @@ -450,9 +455,10 @@ public static function updateFields($op, UserInterface $user) { // Reset the rights to 'unverified' if the email changed but // preserve the 'blocked' rights. elseif ($email_changed) { - if ($item['job'] > 1 || $item['training'] > 1) { + if ($item['job'] > 1 || $item['training'] > 1 || $item['report'] > 1) { $items[$delta]['job'] = $item['job'] == 1 ? 1 : 0; $items[$delta]['training'] = $item['training'] == 1 ? 1 : 0; + $items[$delta]['report'] = $item['report'] == 1 ? 1 : 0; if (!empty($item['notes'])) { $items[$delta]['notes'] .= ' ' . $message; } diff --git a/html/modules/custom/reliefweb_files/src/Controller/ImageStyleDownloadController.php b/html/modules/custom/reliefweb_files/src/Controller/ImageStyleDownloadController.php index 6af910a20..504b0ced0 100644 --- a/html/modules/custom/reliefweb_files/src/Controller/ImageStyleDownloadController.php +++ b/html/modules/custom/reliefweb_files/src/Controller/ImageStyleDownloadController.php @@ -122,7 +122,7 @@ public function deliver(Request $request, $scheme, ImageStyleInterface $image_st // Let other modules handle the file if it's not a file matching the pattern // used for the reliefweb files. if (preg_match($pattern, $uri) !== 1) { - return parent::deliver($request, $scheme, $image_style); + return parent::deliver($request, $scheme, $image_style, $required_derivative_scheme); } // Check the image token. We return a 404 as it's more likely to be cached diff --git a/html/modules/custom/reliefweb_form/src/Controller/NodeForm.php b/html/modules/custom/reliefweb_form/src/Controller/NodeForm.php index 59c210194..c91a898ba 100644 --- a/html/modules/custom/reliefweb_form/src/Controller/NodeForm.php +++ b/html/modules/custom/reliefweb_form/src/Controller/NodeForm.php @@ -51,7 +51,10 @@ public function getSourceAttentionMessages($bundle) { // For reports, we simply load the messages from the report attention field. if ($bundle === 'report') { - $messages = $this->loadSourceAttentionMessages($bundle, $ids); + // No messages for contributors. + if (!$this->currentUser()->hasRole('contributor')) { + $messages = $this->loadSourceAttentionMessages($bundle, $ids); + } } // For jobs or training, we combine the job and training attention messages // as the information is useful for both teams. diff --git a/html/modules/custom/reliefweb_moderation/src/Helpers/UserPostingRightsHelper.php b/html/modules/custom/reliefweb_moderation/src/Helpers/UserPostingRightsHelper.php index f059d1862..cb259bd76 100644 --- a/html/modules/custom/reliefweb_moderation/src/Helpers/UserPostingRightsHelper.php +++ b/html/modules/custom/reliefweb_moderation/src/Helpers/UserPostingRightsHelper.php @@ -98,7 +98,7 @@ public static function getEntityAuthorPostingRights(EntityInterface $entity) { * Posting rights as an associative array keyed by source id. */ public static function getUserPostingRights(?AccountInterface $account = NULL, array $sources = []) { - static $users; + $users = &drupal_static('reliefweb_moderation_getUserPostingRights'); $helper = new UserPostingRightsHelper(); $account = $account ?: \Drupal::currentUser(); @@ -117,12 +117,14 @@ public static function getUserPostingRights(?AccountInterface $account = NULL, a $id_field = $helper->getFieldColumnName('taxonomy_term', 'field_user_posting_rights', 'id'); $job_field = $helper->getFieldColumnName('taxonomy_term', 'field_user_posting_rights', 'job'); $training_field = $helper->getFieldColumnName('taxonomy_term', 'field_user_posting_rights', 'training'); + $report_field = $helper->getFieldColumnName('taxonomy_term', 'field_user_posting_rights', 'report'); // Get the rights associated with the user id. $query = $helper->getDatabase()->select($table, $table); $query->addField($table, 'entity_id', 'tid'); $query->addField($table, $job_field, 'job'); $query->addField($table, $training_field, 'training'); + $query->addField($table, $report_field, 'report'); $query->condition($table . '.bundle', 'source', '='); $query->condition($table . '.' . $id_field, $account->id(), '='); if (!empty($sources)) { @@ -142,6 +144,7 @@ public static function getUserPostingRights(?AccountInterface $account = NULL, a 'tid' => $tid, 'job' => 0, 'training' => 0, + 'report' => 0, ]; } } @@ -158,7 +161,7 @@ public static function getUserPostingRights(?AccountInterface $account = NULL, a * Compute the "final" posting right for a document based on an account's * rights for the given sources. * - * Currently only for jobs and training. + * Currently only for jobs, trainings and reports. * * @param \Drupal\Core\Session\AccountInterface $account * A user's account object or the current user if NULL. @@ -172,8 +175,8 @@ public static function getUserPostingRights(?AccountInterface $account = NULL, a * the right applies. */ public static function getUserConsolidatedPostingRight(AccountInterface $account, $bundle, array $sources) { - // Not a job nor training or no sources, consider the user 'unverified'. - if (empty($account->uid) || ($bundle !== 'job' && $bundle !== 'training') || empty($sources)) { + // Not a job, training nor report or no sources, 'unverified'. + if (empty($account->id()) || ($bundle !== 'job' && $bundle !== 'training' && $bundle !== 'report') || empty($sources)) { return [ 'code' => 0, 'name' => 'unverified', @@ -251,8 +254,8 @@ public static function userHasPostingRights(AccountInterface $account, EntityInt $owner = $entity->getOwnerId() === $account->id() && $account->id() > 0; } - // Only applies to job and training. - if ($bundle === 'job' || $bundle === 'training') { + // Only applies to job, training and report. + if ($bundle === 'job' || $bundle === 'training' || $bundle === 'report') { // Check for sources for which the user is blocked, allowed or trusted. // // Note: if there is no source or the user in unverified for the sources diff --git a/html/modules/custom/reliefweb_moderation/src/ModerationServiceBase.php b/html/modules/custom/reliefweb_moderation/src/ModerationServiceBase.php index f056f74b4..0023ca00c 100644 --- a/html/modules/custom/reliefweb_moderation/src/ModerationServiceBase.php +++ b/html/modules/custom/reliefweb_moderation/src/ModerationServiceBase.php @@ -1913,8 +1913,8 @@ protected function joinPostingRights(Select $query, array $definition, $entity_t return ''; } - // This is only valid for jobs and training. Skip it otherwise. - if ($bundle !== 'job' && $bundle !== 'training') { + // This is only valid for jobs, trainings and reports. Skip it otherwise. + if ($bundle !== 'job' && $bundle !== 'training' && $bundle !== 'report') { return ''; } diff --git a/html/modules/custom/reliefweb_moderation/src/Services/ReportModeration.php b/html/modules/custom/reliefweb_moderation/src/Services/ReportModeration.php index 50d214e32..fa7d19e2e 100644 --- a/html/modules/custom/reliefweb_moderation/src/Services/ReportModeration.php +++ b/html/modules/custom/reliefweb_moderation/src/Services/ReportModeration.php @@ -6,7 +6,9 @@ use Drupal\Core\Link; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; +use Drupal\node\NodeInterface; use Drupal\reliefweb_moderation\EntityModeratedInterface; +use Drupal\reliefweb_moderation\Helpers\UserPostingRightsHelper; use Drupal\reliefweb_moderation\ModerationServiceBase; use Drupal\reliefweb_utility\Helpers\UserHelper; @@ -109,6 +111,12 @@ public function getRows(array $results) { // Country and source info. $info = []; + + // User posting rights. + if ($entity instanceof NodeInterface && $entity->getOwner()->hasRole('contributor')) { + $info['posting_rights'] = UserPostingRightsHelper::renderRight(UserPostingRightsHelper::getEntityAuthorPostingRights($entity)); + } + // Country. $country_link = $this->getTaxonomyTermLink($entity->field_primary_country->first()); if (!empty($country_link)) { @@ -201,8 +209,10 @@ public function getStatuses() { 'draft' => $this->t('Draft'), 'on-hold' => $this->t('On-hold'), 'to-review' => $this->t('To review'), + 'pending' => $this->t('Pending'), 'published' => $this->t('Published'), 'embargoed' => $this->t('Embargoed'), + 'refused' => $this->t('Refused'), 'archive' => $this->t('Archived'), 'reference' => $this->t('Reference'), ]; @@ -221,29 +231,90 @@ public function getFilterDefaultStatuses() { * {@inheritdoc} */ public function getEntityFormSubmitButtons($status, EntityModeratedInterface $entity) { - $buttons = [ - 'draft' => [ + $buttons = []; + $new = empty($status) || $status === 'draft' || $entity->isNew(); + + // Only show save as draft for non-published but editable documents. + if ($new || in_array($status, ['draft', 'on-hold'])) { + $buttons['draft'] = [ '#value' => $this->t('Save as draft'), - ], - 'to-review' => [ - '#value' => $this->t('To review'), - ], - 'published' => [ - '#value' => $this->t('Publish'), - ], - 'on-hold' => [ - '#value' => $this->t('On-hold'), - ], - 'reference' => [ - '#value' => $this->t('Reference'), - ], - ]; + ]; + } - // @todo replace with permission. - if (UserHelper::userHasRoles(['administrator', 'webmaster'])) { - $buttons['archive'] = [ - '#value' => $this->t('Archive'), + // Editors can publish, put on hold or refuse a document. + // @todo use permission. + if (UserHelper::userHasRoles(['editor'])) { + $buttons = [ + 'draft' => [ + '#value' => $this->t('Save as draft'), + ], + 'to-review' => [ + '#value' => $this->t('To review'), + ], + 'published' => [ + '#value' => $this->t('Publish'), + ], + 'on-hold' => [ + '#value' => $this->t('On-hold'), + ], + 'reference' => [ + '#value' => $this->t('Reference'), + ], + ]; + } + elseif (UserHelper::userHasRoles(['administrator', 'webmaster'])) { + $buttons = [ + 'draft' => [ + '#value' => $this->t('Save as draft'), + ], + 'to-review' => [ + '#value' => $this->t('To review'), + ], + 'published' => [ + '#value' => $this->t('Publish'), + ], + 'on-hold' => [ + '#value' => $this->t('On-hold'), + ], + 'reference' => [ + '#value' => $this->t('Reference'), + ], + 'archive' => [ + '#value' => $this->t('Archive'), + ], + ]; + } + // Other users can submit for review, on-hold or published if trusted. + else { + $buttons = [ + 'draft' => [ + '#value' => $this->t('Save as draft'), + ], + 'to-review' => [ + '#value' => $new ? $this->t('Submit') : $this->t('Submit changes'), + ], + 'on-hold' => [ + '#value' => $this->t('On-hold'), + ], ]; + + // Add confirmation when attempting to change published document. + if ($status === 'published') { + $message = $this->t('Press OK to submit the changes for review by the ReliefWeb editors. The report may become unpublished while being reviewed.'); + $buttons['to-review']['#attributes']['onclick'] = 'return confirm("' . $message . '")'; + } + } + + if (UserHelper::userHasRoles(['contributor'])) { + // Warning message when saving as a draft. + if (isset($buttons['draft'])) { + $message = $this->t('You are saving this document as a draft. It will not be visible to visitors. If you wish to proceed with the publication kindly click on @buttons instead.', [ + '@buttons' => implode(' or ', array_map(function ($item) { + return $item['#value']; + }, array_slice($buttons, 1))), + ]); + $buttons['draft']['#attributes']['onclick'] = 'return confirm("' . $message . '")'; + } } return $buttons; @@ -260,7 +331,7 @@ public function isPublishedStatus($status) { * {@inheritdoc} */ public function isEditableStatus($status, ?AccountInterface $account = NULL) { - if ($status === 'archive') { + if ($status === 'archive' || $status === 'refused') { return UserHelper::userHasRoles(['administrator', 'webmaster'], $account); } return TRUE; @@ -316,6 +387,7 @@ protected function initFilterDefinitions(array $filters = []) { 'original_publication_date', 'author', 'user_role', + 'posting_rights', 'reviewer', 'reviewed', 'comments', diff --git a/html/modules/custom/reliefweb_moderation/src/Services/SourceModeration.php b/html/modules/custom/reliefweb_moderation/src/Services/SourceModeration.php index d8b83b87b..5be9d3d39 100644 --- a/html/modules/custom/reliefweb_moderation/src/Services/SourceModeration.php +++ b/html/modules/custom/reliefweb_moderation/src/Services/SourceModeration.php @@ -225,9 +225,10 @@ public function entityPresave(EntityModeratedInterface $entity) { // Update the posting rights field, setting everything as blocked. if (!$entity->get('field_user_posting_rights')->isEmpty()) { foreach ($entity->get('field_user_posting_rights') as $item) { - if ($item->get('job')->getValue() != 1 || $item->get('training')->getValue() != 1) { + if ($item->get('job')->getValue() != 1 || $item->get('training')->getValue() != 1 || $item->get('report')->getValue() != 1) { $item->get('job')->setValue(1); $item->get('training')->setValue(1); + $item->get('report')->setValue(1); $changed = TRUE; } } diff --git a/html/modules/custom/reliefweb_revisions/src/Services/EntityHistory.php b/html/modules/custom/reliefweb_revisions/src/Services/EntityHistory.php index 44ef43965..4884ec104 100644 --- a/html/modules/custom/reliefweb_revisions/src/Services/EntityHistory.php +++ b/html/modules/custom/reliefweb_revisions/src/Services/EntityHistory.php @@ -927,6 +927,7 @@ protected function formatReliefWebUserPostingRightsFieldDiff(FieldDefinitionInte 'removed' => array_diff_key($previous, $current), 'modified-training' => [], 'modified-job' => [], + 'modified-report' => [], 'modified-notes' => [], ]; @@ -935,6 +936,7 @@ protected function formatReliefWebUserPostingRightsFieldDiff(FieldDefinitionInte 'removed' => $this->t('Removed'), 'modified-training' => $this->t('Modified Training'), 'modified-job' => $this->t('Modified Job'), + 'modified-report' => $this->t('Modified Report'), 'modified-notes' => $this->t('Modified Notes'), ]; @@ -951,7 +953,7 @@ protected function formatReliefWebUserPostingRightsFieldDiff(FieldDefinitionInte $previous_item = $previous[$key]; $current_item = $current[$key]; // Rights change. - foreach (['job', 'training'] as $type) { + foreach (['job', 'training', 'report'] as $type) { if ($previous_item[$type] !== $current_item[$type]) { $item['change'] = new FormattableMarkup('@before → @after', [ '@before' => UserPostingRightsHelper::renderRight($rights[$previous_item[$type]]), @@ -995,9 +997,10 @@ protected function formatReliefWebUserPostingRightsFieldDiff(FieldDefinitionInte // Add the rights when a user is added. if ($category === 'added') { - $markup[] = '(job: @job, training: @training)'; + $markup[] = '(job: @job, training: @training, report: @report)'; $replacements['@job'] = UserPostingRightsHelper::renderRight($rights[$item['job']]); $replacements['@training'] = UserPostingRightsHelper::renderRight($rights[$item['training']]); + $replacements['@report'] = UserPostingRightsHelper::renderRight($rights[$item['report']]); } // Add the rights changes. diff --git a/html/modules/custom/reliefweb_user_posts/src/Form/UserPostsPageFilterForm.php b/html/modules/custom/reliefweb_user_posts/src/Form/UserPostsPageFilterForm.php index 6805d7d02..cece24eab 100644 --- a/html/modules/custom/reliefweb_user_posts/src/Form/UserPostsPageFilterForm.php +++ b/html/modules/custom/reliefweb_user_posts/src/Form/UserPostsPageFilterForm.php @@ -30,14 +30,29 @@ public function buildForm(array $form, FormStateInterface $form_state, ?Moderati // Link to create a new entity. $url_options = ['attributes' => ['target' => '_blank']]; - $links = $this->t('Create a new Job vacancy or a new Training program', [ - '@job_url' => Url::fromRoute('node.add', [ - 'node_type' => 'job', - ], $url_options)->toString(), - '@training_url' => Url::fromRoute('node.add', [ - 'node_type' => 'training', - ], $url_options)->toString(), - ]); + if ($user && $user->hasPermission('create report content')) { + $links = $this->t('Create a new Job vacancy, a new Training program or a new Report', [ + '@job_url' => Url::fromRoute('node.add', [ + 'node_type' => 'job', + ], $url_options)->toString(), + '@training_url' => Url::fromRoute('node.add', [ + 'node_type' => 'training', + ], $url_options)->toString(), + '@report_url' => Url::fromRoute('node.add', [ + 'node_type' => 'report', + ], $url_options)->toString(), + ]); + } + else { + $links = $this->t('Create a new Job vacancy or a new Training program', [ + '@job_url' => Url::fromRoute('node.add', [ + 'node_type' => 'job', + ], $url_options)->toString(), + '@training_url' => Url::fromRoute('node.add', [ + 'node_type' => 'training', + ], $url_options)->toString(), + ]); + } // Add intro. $form['intro'] = [ diff --git a/html/modules/custom/reliefweb_user_posts/src/Services/UserPostsService.php b/html/modules/custom/reliefweb_user_posts/src/Services/UserPostsService.php index 32b11619b..b77f3ea9f 100644 --- a/html/modules/custom/reliefweb_user_posts/src/Services/UserPostsService.php +++ b/html/modules/custom/reliefweb_user_posts/src/Services/UserPostsService.php @@ -87,6 +87,7 @@ public function getBundle() { return [ 'job', 'training', + 'report', ]; } @@ -108,15 +109,25 @@ public function getTitle() { * {@inheritdoc} */ public function getStatuses() { - return [ - 'draft' => $this->t('draft'), - 'pending' => $this->t('pending'), - 'published' => $this->t('published'), - 'on-hold' => $this->t('on-hold'), - 'refused' => $this->t('refused'), - 'expired' => $this->t('expired'), - 'duplicate' => $this->t('duplicate'), + $statuses = [ + 'draft' => $this->t('Draft'), + 'pending' => $this->t('Pending'), + 'published' => $this->t('Published'), + 'on-hold' => $this->t('On-hold'), + 'refused' => $this->t('Refused'), + 'expired' => $this->t('Expired'), + 'duplicate' => $this->t('Duplicate'), ]; + + if ($this->currentUser->hasRole('contributor')) { + $statuses += [ + 'to-review' => $this->t('To review'), + 'embargoed' => $this->t('Embargoed'), + 'reference' => $this->t('Reference'), + ]; + } + + return $statuses; } /** @@ -229,9 +240,12 @@ public function getRows(array $results) { $cells['deadline'] = $this->formatDate($entity->field_registration_deadline->value); } } - else { + elseif ($entity->bundle() === 'job') { $cells['deadline'] = $this->formatDate($entity->field_job_closing_date->value); } + else { + $cells['deadline'] = $this->t('N/A'); + } $rows[] = $cells; } @@ -261,6 +275,14 @@ protected function initFilterDefinitions(array $filters = []) { ]; // Filter by bundle. + $allowed_bundles = [ + 'job' => $this->t('Job'), + 'training' => $this->t('Training'), + ]; + if ($this->currentUser->hasPermission('create report content')) { + $allowed_bundles['report'] = $this->t('Report'); + } + $definitions['bundle'] = [ 'type' => 'property', 'field' => 'type', @@ -268,10 +290,7 @@ protected function initFilterDefinitions(array $filters = []) { 'shortcut' => 'ty', 'form' => 'other', 'operator' => 'OR', - 'values' => [ - 'job' => $this->t('Job'), - 'training' => $this->t('Training'), - ], + 'values' => $allowed_bundles, ]; // Limit sources. @@ -356,6 +375,9 @@ protected function getSourcesTheUserHasPostedFor($filter, $term, $conditions, ar elseif (isset($rights[$source->value]['training']) && $rights[$source->value]['training'] > $min_right) { $allowed_sources[] = $source; } + elseif (isset($rights[$source->value]['report']) && $rights[$source->value]['report'] > $min_right) { + $allowed_sources[] = $source; + } } } @@ -462,6 +484,9 @@ protected function filterQuery(Select $query, array $filters = []) { if (empty($filters['bundle']) || !empty($filters['bundle']['training'])) { $types[] = 'training'; } + if (empty($filters['bundle']) || !empty($filters['bundle']['report'])) { + $types[] = 'report'; + } // Get the user rights keyed by source ids and store the ones // for which the user is allowed to post. diff --git a/html/modules/custom/reliefweb_users/src/Controller/UserController.php b/html/modules/custom/reliefweb_users/src/Controller/UserController.php index b7cdf9060..a9215068d 100644 --- a/html/modules/custom/reliefweb_users/src/Controller/UserController.php +++ b/html/modules/custom/reliefweb_users/src/Controller/UserController.php @@ -127,7 +127,7 @@ protected function usersAdminList($storage, $query_parameters) { 'mail' => ['data' => $this->t('Mail'), 'field' => 'u.mail'], 'status' => ['data' => $this->t('Status'), 'field' => 'u.status'], 'role' => $this->t('Roles'), - 'sources' => $this->t('Sources (Job, Training)'), + 'sources' => $this->t('Sources (Job, Training, Reports)'), 'created' => ['data' => $this->t('Member for'), 'field' => 'u.created'], 'access' => ['data' => $this->t('Last access'), 'field' => 'u.access'], 'edit' => $this->t('Edit'), @@ -170,7 +170,7 @@ protected function usersAdminList($storage, $query_parameters) { } // Posting rights filter. - if (isset($filters['job_rights']) || isset($filters['training_rights'])) { + if (isset($filters['job_rights']) || isset($filters['training_rights']) || isset($filters['report_rights'])) { $query->innerJoin('taxonomy_term__field_user_posting_rights', 'fpr', '%alias.field_user_posting_rights_id = u.uid'); if (isset($filters['job_rights'])) { $query->condition('fpr.field_user_posting_rights_job', $rights[$filters['job_rights']], '='); @@ -178,6 +178,9 @@ protected function usersAdminList($storage, $query_parameters) { if (isset($filters['training_rights'])) { $query->condition('fpr.field_user_posting_rights_training', $rights[$filters['training_rights']], '='); } + if (isset($filters['report_rights'])) { + $query->condition('fpr.field_user_posting_rights_report', $rights[$filters['report_rights']], '='); + } } // Set group by. @@ -272,6 +275,7 @@ public function getUserSources(array &$users) { $query->addField('f', 'field_user_posting_rights_id', 'uid'); $query->addField('f', 'field_user_posting_rights_job', 'job'); $query->addField('f', 'field_user_posting_rights_training', 'training'); + $query->addField('f', 'field_user_posting_rights_report', 'report'); $query->addField('f', 'entity_id', 'tid'); $query->addExpression('COALESCE(fs.field_shortname_value, td.name)', 'name'); $query->condition('f.field_user_posting_rights_id', array_keys($users), 'IN'); @@ -281,11 +285,22 @@ public function getUserSources(array &$users) { foreach ($query->execute() as $record) { $job = $record->job; $training = $record->training; + $report = $record->report; + $link = Link::fromTextAndUrl($record->name, URL::fromUserInput('/taxonomy/term/' . $record->tid . '/user-posting-rights', [ 'attributes' => ['target' => '_blank'], ])); - $row = '