Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HPC-10021: Code cleanup, refactoring, exclude already selected articles from add articles screen, add sorting to article selection table #1332

Merged
merged 1 commit into from
Feb 21, 2025
Merged
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
52 changes: 45 additions & 7 deletions config/views.view.article_selection.yml
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,45 @@ display:
field_identifier: ''
exposed: false
granularity: second
arguments: { }
arguments:
nid:
id: nid
table: node_field_data
field: nid
relationship: none
group_type: group
admin_label: ''
entity_type: node
entity_field: nid
plugin_id: node_nid
default_action: default
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: entity_browser_widget_context
default_argument_options:
context_key: selected_ids
fallback: all
multiple: or
summary_options:
base_path: ''
count: true
override: false
items_per_page: 25
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: true
not: true
filters:
type:
id: type
Expand Down Expand Up @@ -812,15 +850,15 @@ display:
status: status
field_protected: field_protected
changed: changed
default: '-1'
default: changed
info:
entity_browser_select:
align: ''
separator: ''
empty_column: false
responsive: ''
title:
sortable: false
sortable: true
default_sort_order: asc
align: ''
separator: ''
Expand All @@ -832,22 +870,22 @@ display:
empty_column: false
responsive: ''
status:
sortable: false
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
field_protected:
sortable: false
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
changed:
sortable: false
default_sort_order: asc
sortable: true
default_sort_order: desc
align: ''
separator: ''
empty_column: false
Expand Down
50 changes: 4 additions & 46 deletions html/modules/custom/ghi_content/ghi_content.module
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ use Drupal\ghi_content\Entity\ContentBase;
use Drupal\ghi_content\Entity\Document;
use Drupal\ghi_content\RemoteContent\RemoteContentImageInterface;
use Drupal\ghi_sections\Entity\Section;
use Drupal\ghi_sections\Entity\SectionNodeInterface;
use Drupal\ghi_sections\SectionManager;
use Drupal\hpc_common\Helpers\RequestHelper;
use Drupal\layout_builder\SectionStorageInterface;
use Drupal\node\NodeInterface;
use Drupal\taxonomy\TermInterface;
use Drupal\user\Entity\User;
use Drupal\views\ViewExecutable;

Expand Down Expand Up @@ -473,53 +471,13 @@ function ghi_content_preprocess_views_view_field__field_tags(&$variables) {

/**
* Implements hook_views_pre_view().
*
* Pre-populate the tag filter on article selection entity browsers if opening
* the selection dialog for the first time.
*/
function ghi_content_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
if ($view->id() != 'article_selection') {
return;
}
$exposed_input = $view->getExposedInput();
if (array_key_exists('tags', $exposed_input)) {
return;
}
// Get the path to get the node entity.
$original_path = \Drupal::requestStack()->getCurrentRequest()->query->get('original_path');
if (!$original_path) {
return;
}
$node_key = array_values(array_filter(explode('/', $original_path), function ($part) {
return str_starts_with($part, 'node.') ? $part : NULL;
}))[0] ?? NULL;
if (!$node_key) {
return;
if ($display_id == 'entity_browser_table') {
/** @var \Drupal\ghi_content\EntityBrowser\ArticleSelection $artice_selection_browser */
$artice_selection_browser = \Drupal::service('ghi_content.entity_browser.article_selection');
$artice_selection_browser->preView($view, $args);
}
[$entity_type_id, $entity_id] = explode('.', $node_key);
$node = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id);

// Now see if we have qualified nodes and if these have tags.
$tags = [];
if ($node instanceof SectionNodeInterface) {
$section_tags = $node->getTags();
foreach ($section_tags as $term_id => $label) {
$tags[] = $label . ' (' . $term_id . ')';
}
}
elseif ($node instanceof Document) {
$tags = array_map(function (TermInterface $term) {
return $term->label() . ' (' . $term->id() . ')';
}, $node->getTags(TRUE));
}
if (empty($tags)) {
return;
}

// Populate the exposed tag filter with these found tags.
$exposed_input = $view->getExposedInput();
$exposed_input['tags'] = implode(', ', $tags);
$view->setExposedInput($exposed_input);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions html/modules/custom/ghi_content/ghi_content.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ services:
ghi_content.contextual_links.block_handler:
class: Drupal\ghi_content\ContextualLinks\BlockHandler
arguments: ['@entity_type.manager', '@layout_builder.tempstore_repository']
ghi_content.entity_browser.article_selection:
class: Drupal\ghi_content\EntityBrowser\ArticleSelection
arguments: ['@entity_type.manager', '@request_stack']
ghi_content.breadcrumb:
class: Drupal\ghi_content\Breadcrumb\AdminContentBreadcrumbBuilder
arguments: ['@entity_type.manager']
Expand Down
139 changes: 139 additions & 0 deletions html/modules/custom/ghi_content/src/EntityBrowser/ArticleSelection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php

namespace Drupal\ghi_content\EntityBrowser;

use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\Element\EntityAutocomplete;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\ghi_content\Entity\Document;
use Drupal\ghi_sections\Entity\SectionNodeInterface;
use Drupal\node\NodeInterface;
use Drupal\views\ViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* Service class for article select entity browsers.
*
* This class contains some logic to imrove the UI and UX of the entity browser
* used to select articles.
*/
class ArticleSelection implements ContainerInjectionInterface {

/**
* The view id that this service class handles.
*/
const VIEW_ID = 'article_selection';

/**
* Identifier for the tag filter on the view.
*/
const TAG_FILTER = 'tags';

/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;

/**
* The current request.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;

/**
* Public constructor.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack) {
$this->entityTypeManager = $entity_type_manager;
$this->request = $request_stack->getCurrentRequest();
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('request_stack'),
);
}

/**
* Act on the pre_view hook of an entity browser view.
*
* Pre-populate the tag filter on article selection entity browsers when
* opening the selection dialog for the first time.
*
* @param \Drupal\views\ViewExecutable $view
* The view to modifiy.
* @param array $args
* An array of arguments.
*/
public function preView(ViewExecutable $view, array $args) {
$exposed_input = $view->getExposedInput();
$already_submitted = array_key_exists(self::TAG_FILTER, $exposed_input);
if ($view->id() != self::VIEW_ID || $already_submitted) {
return;
}

// Now see if we have a qualified node and if it has tags.
$node = $this->getCurrentNode();
$tags = $node ? $this->getTagsFromNode($node) : NULL;
if (empty($tags)) {
return;
}

// Populate the exposed tag filter with these found tags.
$exposed_input = $view->getExposedInput();
$exposed_input[self::TAG_FILTER] = EntityAutocomplete::getEntityLabels($tags);
$view->setExposedInput($exposed_input);
}

/**
* Get the current node from the request.
*
* @return \Drupal\node\NodeInterface|null
* A node object if it has been found.
*/
private function getCurrentNode() {
// Get the path to get the node entity.
$original_path = $this->request->query->get('original_path');
if (!$original_path) {
return NULL;
}
$node_key = array_values(array_filter(explode('/', $original_path), function ($part) {
return str_starts_with($part, 'node.') ? $part : NULL;
}))[0] ?? NULL;
if (!$node_key) {
return NULL;
}
[$entity_type_id, $entity_id] = explode('.', $node_key);
return $this->entityTypeManager->getStorage($entity_type_id)->load($entity_id);
}

/**
* Get the tags from the given node.
*
* This only works with a limited set of known node types.
*
* @param \Drupal\node\NodeInterface $node
* A node object.
*
* @return \Drupal\taxonomy\TermInterface[]
* An array of tag entities.
*/
private function getTagsFromNode(NodeInterface $node) {
if ($node instanceof SectionNodeInterface) {
return $node->getTagEntities();
}
if ($node instanceof Document) {
return $node->getTags(TRUE);
}
return NULL;
}

}
10 changes: 10 additions & 0 deletions html/modules/custom/ghi_form_elements/css/entity_browser.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ form.entity-browser-form > div.view > .view-content {
margin: 0;
}

form.entity-browser-form > div.view > .view-content table th {
white-space: nowrap;
}
form.entity-browser-form > div.view > .view-content table th > a,
form.entity-browser-form > div.view > .view-content table th > a:active,
form.entity-browser-form > div.view > .view-content table th > a:focus {
box-shadow: none !important;
}


form.entity-browser-form > div.view > .view-content .entity-browser-selection-counter {
position: absolute;
bottom: 1rem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Attribute\FormElement;
use Drupal\Core\Render\Element;
Expand Down Expand Up @@ -138,6 +139,15 @@ public static function processArticleSelect(array &$element, FormStateInterface
],
'#wrapper_id' => $wrapper_id,
'#default_value' => $selected_articles,
'#widget_context' => [
// This is used to exclude already selected articles from the selection
// view. Technically, this feeds into the default value for a
// contextual filter.
// @see https://www.drupal.org/project/entity_browser/issues/2865928
'selected_ids' => array_map(function (EntityInterface $entity) {
return $entity->id();
}, $selected_articles),
],
];

$element['#attached']['library'][] = 'ghi_form_elements/entity_browser';
Expand Down
7 changes: 7 additions & 0 deletions html/modules/custom/ghi_sections/src/Entity/Section.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ public function getTags() {
return $tags;
}

/**
* {@inheritdoc}
*/
public function getTagEntities() {
return $this->get('field_tags')->referencedEntities() ?? [];
}

/**
* {@inheritdoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ public function getPageTitleMetaData();
*/
public function getTags();

/**
* Get the tags associated to the section.
*
* @return \Drupal\taxonomy\TermInterface[]
* An array of tag terms, keyed by the tag id.
*/
public function getTagEntities();

/**
* Get the base object for a section.
*
Expand Down
Loading