Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions changelogs/DP-45941.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#
# Write your changelog entry here. Every pull request must have a changelog yml file.
#
# Change types:
# #############################################################################
# You can use one of the following types:
# - Added: For new features.
# - Changed: For changes to existing functionality.
# - Deprecated: For soon-to-be removed features.
# - Removed: For removed features.
# - Fixed: For any bug fixes.
# - Security: In case of vulnerabilities.
#
# Format
# #############################################################################
# The format is crucial. Please follow the examples below. For reference, the requirements are:
# - All 3 parts are required and you must include "Type", "description" and "issue".
# - "Type" must be left aligned and followed by a colon.
# - "description" must be indented with 2 spaces followed by a colon
# - "issue" must be indented with 4 spaces followed by a colon.
# - "issue" is for the Jira ticket number only e.g. DP-1234
# - No extra spaces, indents, or blank lines are allowed.
#
# Example:
# #############################################################################
# Fixed:
# - description: Fixes scrolling on edit pages in Safari.
# issue: DP-13314
#
# You may add more than 1 description & issue for each type using the following format:
# Changed:
# - description: Automating the release branch.
# issue: DP-10166
# - description: Second change item that needs a description.
# issue: DP-19875
# - description: Third change item that needs a description along with an issue.
# issue: DP-19843
#
Changed:
- description: Allow Editors to Reference Blocked Users in “Authored By” Field for all report filters.
issue: DP-45941
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@
"drupal/r4032login": "^2.2",
"drupal/rabbit_hole": "^1.1",
"drupal/redirect": "^1",
"drupal/reference_blocked_users": "^1.0",
"drupal/require_on_publish": "^2.0",
"drupal/scheduled_transitions": "^2.7",
"drupal/schema_metatag": "^3.0",
Expand Down
57 changes: 54 additions & 3 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions conf/drupal/config/core.extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ module:
rabbit_hole: 0
redirect: 0
redirect_404: 0
reference_blocked_users: 0
require_on_publish: 0
responsive_image: 0
rest: 0
Expand Down
2 changes: 2 additions & 0 deletions conf/drupal/config/user.role.editor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ dependencies:
- node
- quick_node_clone
- rabbit_hole
- reference_blocked_users
- scheduled_transitions
- system
- taxonomy
Expand Down Expand Up @@ -238,6 +239,7 @@ permissions:
- 'rabbit hole administer node'
- 'rabbit hole bypass node'
- 'rebuild node access permissions'
- 'reference blocked users'
- 'reorder layout paragraphs components'
- 'reschedule scheduled transitions media document'
- 'reschedule scheduled transitions node advisory'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityAutocompleteMatcher as DefaultAutocompleteMatcher;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\user\UserInterface;

/**
* Matcher class to get autocompletion results for entity reference.
Expand Down Expand Up @@ -87,15 +89,24 @@ public function getMatches($target_type, $selection_handler, $selection_settings
else {
$key = "$label ($entity_id)";
}
// Strip troublesome characters like starting/trailing white spaces, line breaks and tags.
$key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(Html::decodeEntities(strip_tags($key)))));
// Strip extra spaces, line breaks, and tags.
$key = preg_replace(
'/\s\s+/',
' ',
str_replace("\n", '', trim(Html::decodeEntities(strip_tags($key))))
);
// Names containing commas or quotes must be wrapped in quotes.
$key = Tags::encode($key);
$entity = \Drupal::entityTypeManager()->getStorage($target_type)->load($entity_id);
$entity = $this->entityTypeManager
->getStorage($target_type)
->load($entity_id);
if ($entity) {
if ($entity->getEntityType()->id() == 'node' && !$entity->isPublished()) {
if ($entity instanceof EntityPublishedInterface && !$entity->isPublished()) {
$label .= " (unpublished)";
}
if ($entity instanceof UserInterface && !$entity->isActive()) {
$label .= " (blocked)";
}
}
$matches[] = [
'value' => $key,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Drupal\Tests\mass_fields\ExistingSite;

use MassGov\Dtt\MassExistingSiteBase;

/**
* Verifies blocked users are clearly marked in entity autocomplete labels.
*/
class BlockedUserAutocompleteLabelTest extends MassExistingSiteBase {

/**
* Tests blocked-user lifecycle in entity autocomplete results.
*/
public function testBlockedUserAutocompleteLabel(): void {
$name_prefix = 'blocked_user_label_' . $this->randomMachineName(6);

// Step 1: Create an author and content authored by that user.
$author_user = $this->createUser([], $name_prefix . '_author');
$author_user->activate();
$author_user->save();

$node = $this->createNode([
'type' => 'info_details',
'title' => 'Blocked author coverage ' . $name_prefix,
'uid' => $author_user->id(),
'field_info_detail_overview' => '<p>Autocomplete coverage body</p>',
]);
$this->assertEquals($author_user->id(), (int) $node->getOwnerId());

// Step 2: Block that author user.
$author_user->block();
$author_user->save();

// Step 3: Create another user who searches in autocomplete.
$editor_user = $this->createUser(['reference blocked users']);
$this->drupalLogin($editor_user);

// Additional active account with similar prefix for mixed results.
$active_user = $this->createUser([], $name_prefix . '_active');
$active_user->activate();
$active_user->save();

$matcher = \Drupal::service('mass_fields.autocomplete_matcher');
$matches = $matcher->getMatches('user', 'default', [], $name_prefix);

$labels = array_column($matches, 'label');
$labels_as_string = implode("\n", $labels);

$this->assertStringContainsString($active_user->getDisplayName(), $labels_as_string);
$this->assertStringContainsString($author_user->getDisplayName() . ' (blocked)', $labels_as_string);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

declare(strict_types=1);

namespace Drupal\Tests\mass_views\ExistingSite;

use Drupal\file\Entity\File;
use Drupal\user\Entity\Role;
use MassGov\Dtt\MassExistingSiteBase;
use weitzman\DrupalTestTraits\Entity\MediaCreationTrait;

/**
* Verifies blocked-user author filtering behavior in Documents admin view.
*/
class BlockedAuthorsFilterTest extends MassExistingSiteBase {

use MediaCreationTrait;

/**
* Ensure editors can filter by blocked users in "Authored by".
*/
public function testEditorCanUseBlockedAuthorsFilter(): void {
$editor_role = Role::load('editor');
$this->assertNotNull($editor_role);
$this->assertTrue(
$editor_role->hasPermission('reference blocked users'),
'Editor role must include permission to reference blocked users.'
);

$author = $this->createUser([], 'blocked_author_' . $this->randomMachineName(6));
$author->activate();
$author->save();

$active_author = $this->createUser([], 'active_author_' . $this->randomMachineName(6));
$active_author->activate();
$active_author->save();

$this->createDocumentMediaForAuthor(
'Blocked author document ' . $this->randomMachineName(6),
(int) $author->id()
);
$active_author_title = 'Active author document ' . $this->randomMachineName(6);
$editable_media_id = $this->createDocumentMediaForAuthor(
$active_author_title,
(int) $active_author->id()
);

// Simulate former employee account lifecycle.
$author->block();
$author->save();

$editor = $this->createUser([], 'editor_user_' . $this->randomMachineName(6));
$editor->addRole('editor');
$editor->save();
$this->drupalLogin($editor);

$this->drupalGet('admin/ma-dash/documents');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->fieldExists('edit-uid');

// Filter by blocked author in exposed "Authored by" filter.
$this->submitForm(
[
'uid' => $author->getAccountName() . ' (' . $author->id() . ')',
],
'Filter',
'views-exposed-form-all-documents-page-1'
);

$this->assertSession()->pageTextContains('Blocked author document');
$this->assertSession()->pageTextNotContains($active_author_title);

// Also verify active users are still referenceable in the same field.
$this->submitForm(
[
'uid' => $active_author->getAccountName() . ' (' . $active_author->id() . ')',
],
'Filter',
'views-exposed-form-all-documents-page-1'
);

$this->assertSession()->pageTextContains($active_author_title);
$this->assertSession()->pageTextNotContains('Blocked author document');

// Keep one editable media id for JS workflow coverage in companion test.
$this->assertGreaterThan(0, $editable_media_id);
}

/**
* Creates a published document media item for a specific author.
*/
private function createDocumentMediaForAuthor(string $title, int $author_id): int {
$destination = 'public://' . $this->randomMachineName(12) . '.txt';
$file = File::create([
'uri' => $destination,
]);
$file->setPermanent();
$file->save();

$src = 'core/tests/Drupal/Tests/Component/FileCache/Fixtures/llama-23.txt';
\Drupal::service('file_system')->copy($src, $destination, TRUE);

$media = $this->createMedia([
'bundle' => 'document',
'title' => $title,
'field_title' => $title,
'uid' => $author_id,
'field_upload_file' => [
'target_id' => $file->id(),
],
'status' => 1,
]);

return (int) $media->id();
}

}
Loading
Loading