Skip to content

Commit 712ce97

Browse files
authored
Merge pull request #1332 from UN-OCHA/berliner/HPC-10021
HPC-10021: Code cleanup, refactoring, exclude already selected articles from add articles screen, add sorting to article selection table
2 parents 6d71c65 + 8572196 commit 712ce97

File tree

8 files changed

+226
-53
lines changed

8 files changed

+226
-53
lines changed

config/views.view.article_selection.yml

+45-7
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,45 @@ display:
487487
field_identifier: ''
488488
exposed: false
489489
granularity: second
490-
arguments: { }
490+
arguments:
491+
nid:
492+
id: nid
493+
table: node_field_data
494+
field: nid
495+
relationship: none
496+
group_type: group
497+
admin_label: ''
498+
entity_type: node
499+
entity_field: nid
500+
plugin_id: node_nid
501+
default_action: default
502+
exception:
503+
value: all
504+
title_enable: false
505+
title: All
506+
title_enable: false
507+
title: ''
508+
default_argument_type: entity_browser_widget_context
509+
default_argument_options:
510+
context_key: selected_ids
511+
fallback: all
512+
multiple: or
513+
summary_options:
514+
base_path: ''
515+
count: true
516+
override: false
517+
items_per_page: 25
518+
summary:
519+
sort_order: asc
520+
number_of_records: 0
521+
format: default_summary
522+
specify_validation: false
523+
validate:
524+
type: none
525+
fail: 'not found'
526+
validate_options: { }
527+
break_phrase: true
528+
not: true
491529
filters:
492530
type:
493531
id: type
@@ -812,15 +850,15 @@ display:
812850
status: status
813851
field_protected: field_protected
814852
changed: changed
815-
default: '-1'
853+
default: changed
816854
info:
817855
entity_browser_select:
818856
align: ''
819857
separator: ''
820858
empty_column: false
821859
responsive: ''
822860
title:
823-
sortable: false
861+
sortable: true
824862
default_sort_order: asc
825863
align: ''
826864
separator: ''
@@ -832,22 +870,22 @@ display:
832870
empty_column: false
833871
responsive: ''
834872
status:
835-
sortable: false
873+
sortable: true
836874
default_sort_order: asc
837875
align: ''
838876
separator: ''
839877
empty_column: false
840878
responsive: ''
841879
field_protected:
842-
sortable: false
880+
sortable: true
843881
default_sort_order: asc
844882
align: ''
845883
separator: ''
846884
empty_column: false
847885
responsive: ''
848886
changed:
849-
sortable: false
850-
default_sort_order: asc
887+
sortable: true
888+
default_sort_order: desc
851889
align: ''
852890
separator: ''
853891
empty_column: false

html/modules/custom/ghi_content/ghi_content.module

+4-46
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ use Drupal\ghi_content\Entity\ContentBase;
2323
use Drupal\ghi_content\Entity\Document;
2424
use Drupal\ghi_content\RemoteContent\RemoteContentImageInterface;
2525
use Drupal\ghi_sections\Entity\Section;
26-
use Drupal\ghi_sections\Entity\SectionNodeInterface;
2726
use Drupal\ghi_sections\SectionManager;
2827
use Drupal\hpc_common\Helpers\RequestHelper;
2928
use Drupal\layout_builder\SectionStorageInterface;
3029
use Drupal\node\NodeInterface;
31-
use Drupal\taxonomy\TermInterface;
3230
use Drupal\user\Entity\User;
3331
use Drupal\views\ViewExecutable;
3432

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

474472
/**
475473
* Implements hook_views_pre_view().
476-
*
477-
* Pre-populate the tag filter on article selection entity browsers if opening
478-
* the selection dialog for the first time.
479474
*/
480475
function ghi_content_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
481-
if ($view->id() != 'article_selection') {
482-
return;
483-
}
484-
$exposed_input = $view->getExposedInput();
485-
if (array_key_exists('tags', $exposed_input)) {
486-
return;
487-
}
488-
// Get the path to get the node entity.
489-
$original_path = \Drupal::requestStack()->getCurrentRequest()->query->get('original_path');
490-
if (!$original_path) {
491-
return;
492-
}
493-
$node_key = array_values(array_filter(explode('/', $original_path), function ($part) {
494-
return str_starts_with($part, 'node.') ? $part : NULL;
495-
}))[0] ?? NULL;
496-
if (!$node_key) {
497-
return;
476+
if ($display_id == 'entity_browser_table') {
477+
/** @var \Drupal\ghi_content\EntityBrowser\ArticleSelection $artice_selection_browser */
478+
$artice_selection_browser = \Drupal::service('ghi_content.entity_browser.article_selection');
479+
$artice_selection_browser->preView($view, $args);
498480
}
499-
[$entity_type_id, $entity_id] = explode('.', $node_key);
500-
$node = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
501-
502-
// Now see if we have qualified nodes and if these have tags.
503-
$tags = [];
504-
if ($node instanceof SectionNodeInterface) {
505-
$section_tags = $node->getTags();
506-
foreach ($section_tags as $term_id => $label) {
507-
$tags[] = $label . ' (' . $term_id . ')';
508-
}
509-
}
510-
elseif ($node instanceof Document) {
511-
$tags = array_map(function (TermInterface $term) {
512-
return $term->label() . ' (' . $term->id() . ')';
513-
}, $node->getTags(TRUE));
514-
}
515-
if (empty($tags)) {
516-
return;
517-
}
518-
519-
// Populate the exposed tag filter with these found tags.
520-
$exposed_input = $view->getExposedInput();
521-
$exposed_input['tags'] = implode(', ', $tags);
522-
$view->setExposedInput($exposed_input);
523481
}
524482

525483
/**

html/modules/custom/ghi_content/ghi_content.services.yml

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ services:
3232
ghi_content.contextual_links.block_handler:
3333
class: Drupal\ghi_content\ContextualLinks\BlockHandler
3434
arguments: ['@entity_type.manager', '@layout_builder.tempstore_repository']
35+
ghi_content.entity_browser.article_selection:
36+
class: Drupal\ghi_content\EntityBrowser\ArticleSelection
37+
arguments: ['@entity_type.manager', '@request_stack']
3538
ghi_content.breadcrumb:
3639
class: Drupal\ghi_content\Breadcrumb\AdminContentBreadcrumbBuilder
3740
arguments: ['@entity_type.manager']
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?php
2+
3+
namespace Drupal\ghi_content\EntityBrowser;
4+
5+
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
6+
use Drupal\Core\Entity\Element\EntityAutocomplete;
7+
use Drupal\Core\Entity\EntityTypeManagerInterface;
8+
use Drupal\ghi_content\Entity\Document;
9+
use Drupal\ghi_sections\Entity\SectionNodeInterface;
10+
use Drupal\node\NodeInterface;
11+
use Drupal\views\ViewExecutable;
12+
use Symfony\Component\DependencyInjection\ContainerInterface;
13+
use Symfony\Component\HttpFoundation\RequestStack;
14+
15+
/**
16+
* Service class for article select entity browsers.
17+
*
18+
* This class contains some logic to imrove the UI and UX of the entity browser
19+
* used to select articles.
20+
*/
21+
class ArticleSelection implements ContainerInjectionInterface {
22+
23+
/**
24+
* The view id that this service class handles.
25+
*/
26+
const VIEW_ID = 'article_selection';
27+
28+
/**
29+
* Identifier for the tag filter on the view.
30+
*/
31+
const TAG_FILTER = 'tags';
32+
33+
/**
34+
* The entity type manager service.
35+
*
36+
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
37+
*/
38+
protected $entityTypeManager;
39+
40+
/**
41+
* The current request.
42+
*
43+
* @var \Symfony\Component\HttpFoundation\Request
44+
*/
45+
protected $request;
46+
47+
/**
48+
* Public constructor.
49+
*/
50+
public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack) {
51+
$this->entityTypeManager = $entity_type_manager;
52+
$this->request = $request_stack->getCurrentRequest();
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public static function create(ContainerInterface $container) {
59+
return new static(
60+
$container->get('entity_type.manager'),
61+
$container->get('request_stack'),
62+
);
63+
}
64+
65+
/**
66+
* Act on the pre_view hook of an entity browser view.
67+
*
68+
* Pre-populate the tag filter on article selection entity browsers when
69+
* opening the selection dialog for the first time.
70+
*
71+
* @param \Drupal\views\ViewExecutable $view
72+
* The view to modifiy.
73+
* @param array $args
74+
* An array of arguments.
75+
*/
76+
public function preView(ViewExecutable $view, array $args) {
77+
$exposed_input = $view->getExposedInput();
78+
$already_submitted = array_key_exists(self::TAG_FILTER, $exposed_input);
79+
if ($view->id() != self::VIEW_ID || $already_submitted) {
80+
return;
81+
}
82+
83+
// Now see if we have a qualified node and if it has tags.
84+
$node = $this->getCurrentNode();
85+
$tags = $node ? $this->getTagsFromNode($node) : NULL;
86+
if (empty($tags)) {
87+
return;
88+
}
89+
90+
// Populate the exposed tag filter with these found tags.
91+
$exposed_input = $view->getExposedInput();
92+
$exposed_input[self::TAG_FILTER] = EntityAutocomplete::getEntityLabels($tags);
93+
$view->setExposedInput($exposed_input);
94+
}
95+
96+
/**
97+
* Get the current node from the request.
98+
*
99+
* @return \Drupal\node\NodeInterface|null
100+
* A node object if it has been found.
101+
*/
102+
private function getCurrentNode() {
103+
// Get the path to get the node entity.
104+
$original_path = $this->request->query->get('original_path');
105+
if (!$original_path) {
106+
return NULL;
107+
}
108+
$node_key = array_values(array_filter(explode('/', $original_path), function ($part) {
109+
return str_starts_with($part, 'node.') ? $part : NULL;
110+
}))[0] ?? NULL;
111+
if (!$node_key) {
112+
return NULL;
113+
}
114+
[$entity_type_id, $entity_id] = explode('.', $node_key);
115+
return $this->entityTypeManager->getStorage($entity_type_id)->load($entity_id);
116+
}
117+
118+
/**
119+
* Get the tags from the given node.
120+
*
121+
* This only works with a limited set of known node types.
122+
*
123+
* @param \Drupal\node\NodeInterface $node
124+
* A node object.
125+
*
126+
* @return \Drupal\taxonomy\TermInterface[]
127+
* An array of tag entities.
128+
*/
129+
private function getTagsFromNode(NodeInterface $node) {
130+
if ($node instanceof SectionNodeInterface) {
131+
return $node->getTagEntities();
132+
}
133+
if ($node instanceof Document) {
134+
return $node->getTags(TRUE);
135+
}
136+
return NULL;
137+
}
138+
139+
}

html/modules/custom/ghi_form_elements/css/entity_browser.css

+10
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ form.entity-browser-form > div.view > .view-content {
5353
margin: 0;
5454
}
5555

56+
form.entity-browser-form > div.view > .view-content table th {
57+
white-space: nowrap;
58+
}
59+
form.entity-browser-form > div.view > .view-content table th > a,
60+
form.entity-browser-form > div.view > .view-content table th > a:active,
61+
form.entity-browser-form > div.view > .view-content table th > a:focus {
62+
box-shadow: none !important;
63+
}
64+
65+
5666
form.entity-browser-form > div.view > .view-content .entity-browser-selection-counter {
5767
position: absolute;
5868
bottom: 1rem;

html/modules/custom/ghi_form_elements/src/Element/ArticleSelect.php

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Drupal\Component\Utility\Html;
66
use Drupal\Component\Utility\NestedArray;
7+
use Drupal\Core\Entity\EntityInterface;
78
use Drupal\Core\Form\FormStateInterface;
89
use Drupal\Core\Render\Attribute\FormElement;
910
use Drupal\Core\Render\Element;
@@ -138,6 +139,15 @@ public static function processArticleSelect(array &$element, FormStateInterface
138139
],
139140
'#wrapper_id' => $wrapper_id,
140141
'#default_value' => $selected_articles,
142+
'#widget_context' => [
143+
// This is used to exclude already selected articles from the selection
144+
// view. Technically, this feeds into the default value for a
145+
// contextual filter.
146+
// @see https://www.drupal.org/project/entity_browser/issues/2865928
147+
'selected_ids' => array_map(function (EntityInterface $entity) {
148+
return $entity->id();
149+
}, $selected_articles),
150+
],
141151
];
142152

143153
$element['#attached']['library'][] = 'ghi_form_elements/entity_browser';

html/modules/custom/ghi_sections/src/Entity/Section.php

+7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ public function getTags() {
5858
return $tags;
5959
}
6060

61+
/**
62+
* {@inheritdoc}
63+
*/
64+
public function getTagEntities() {
65+
return $this->get('field_tags')->referencedEntities() ?? [];
66+
}
67+
6168
/**
6269
* {@inheritdoc}
6370
*/

html/modules/custom/ghi_sections/src/Entity/SectionNodeInterface.php

+8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ public function getPageTitleMetaData();
3636
*/
3737
public function getTags();
3838

39+
/**
40+
* Get the tags associated to the section.
41+
*
42+
* @return \Drupal\taxonomy\TermInterface[]
43+
* An array of tag terms, keyed by the tag id.
44+
*/
45+
public function getTagEntities();
46+
3947
/**
4048
* Get the base object for a section.
4149
*

0 commit comments

Comments
 (0)