Skip to content

Commit

Permalink
feat: improve display of guideline labels by prefixing their associat…
Browse files Browse the repository at this point in the history
…ed role

Refs: RW-1179
  • Loading branch information
orakili committed Feb 26, 2025
1 parent bbbb117 commit d7e3f4e
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,20 @@ function reliefweb_guidelines_file_access(EntityInterface $entity, $operation, A
}
return AccessResult::neutral();
}

/**
* Get a sanitized list of roles suitable for the guidelines.
*
* @return array
* Associative array of role ids to role names.
*/
function reliefweb_guidelines_get_user_roles(): array {
$roles = user_role_names(TRUE);

$allowed_roles = \Drupal::state()->get('reliefweb_guidelines_allowed_roles', [
'editor' => 'editor',
'contributor' => 'contributor',
]);

return array_intersect_key($roles, $allowed_roles);
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,17 @@ protected function getGuidelineList() {
}

/** @var \Drupal\reliefweb_guidelines\Entity\GuidelineList[] $guideline_lists */
$guideline_lists = $storage->loadMultiple($guideline_list_ids);
$guideline_lists = $this->loadOrderedGuidelines($guideline_list_ids);

$is_admin = $this->isUserAdmin($this->currentUser());

foreach ($guideline_lists as $guideline_list) {
$list[$guideline_list->id()]['title'] = $guideline_list->label();
// Admins can see all the guidelines so we use the prefixed label to
// differentiate the guideline lists. For other users, they normally only
// see one type of guideline (ex: guidelines for editors), in which case
// we use ::getName() to show the non prefixed label for better
// readability.
$list[$guideline_list->id()]['title'] = $is_admin ? $guideline_list->label() : $guideline_list->getName();
}

// Retrieve the guidelines that are children of those guideline lists.
Expand All @@ -121,13 +128,12 @@ protected function getGuidelineList() {
->condition('status', 1, '=')
->condition('type', 'field_guideline', '=')
->condition('parent', $guideline_list_ids, 'IN')
->sort('type', 'DESC')
->sort('weight', 'ASC')
->accessCheck(TRUE)
->execute();

/** @var \Drupal\guidelines\Entity\Guideline[] $guidelines */
$guidelines = $storage->loadMultiple($guideline_ids);
$guidelines = $this->loadOrderedGuidelines($guideline_ids);

// Prepare the guideline children.
foreach ($guidelines as $guideline) {
Expand Down Expand Up @@ -172,6 +178,22 @@ protected function getGuidelineList() {
return $list;
}

/**
* Load guidelines and return them in the same order of their IDs.
*
* @param array $ids
* Guideline entity IDs.
*
* @return array
* Asscociative array of loaded entities keyed by their IDs.
*/
protected function loadOrderedGuidelines(array $ids): array {
$entities = $this->entityTypeManager()->getStorage('guideline')->loadMultiple($ids);
$ordered_entities = array_intersect_key(array_flip($ids), $entities);
$ordered_entities = array_replace($ordered_entities, $entities);
return $ordered_entities;
}

/**
* Get the cache ID for the guidelines.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,16 @@ public function getDefaultModerationStatus() {
}

/**
* Return the label of the guideline list prefixed by its target user role.
*
* @return \Drupal\Core\Entity\EntityStorageInterface
* Markup with the prefixed label.
* {@inheritdoc}
*/
public function getRoleAndLabel(): MarkupInterface {
public function label(): string|MarkupInterface|null {
if (!$this->hasField('field_role') || $this->field_role?->isEmpty()) {
$role = $this->t('Editor');
}
else {
$role = $this->field_role->entity->label();
}
return $this->t('[@role] @label', ['@role' => $role, '@label' => $this->label()]);
return $this->t('[@role] @label', ['@role' => $role, '@label' => parent::label()]);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {

// Label.
$form['order'][$id]['label'] = $entity
?->toLink($entity->getRoleAndLabel(), 'edit-form')
?->toLink($entity->label(), 'edit-form')
?->toRenderable();

// Weight.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ public function getAccessibleGuidelineListIds(AccountInterface $user): array {
->getQuery()
->condition('status', 1, '=')
->condition('type', 'guideline_list', '=')
->sort('type', 'DESC')
->sort('weight', 'ASC')
->accessCheck(TRUE);

// Filter on the role(s) of the current user.
$user_roles = $user->getRoles();

// Skip the role filtering for admins.
if ($user->id() != 1 && !in_array('adminitrator', $user_roles)) {
if (!$this->isUserAdmin($user)) {
$role_conditions = $guideline_list_query->orConditionGroup();
$role_conditions->condition('field_role', $user_roles, 'IN');
// Consider guideline lists without a role, guidelines for editors.
Expand All @@ -52,4 +51,17 @@ public function getAccessibleGuidelineListIds(AccountInterface $user): array {
return $guideline_list_ids;
}

/**
* Check if a user is an administrator.
*
* @param \Drupal\Core\Session\AccountInterface $user
* User.
*
* @return bool
* TRUE if administrator.
*/
public function isUserAdmin(AccountInterface $user): bool {
return $user->id() == 1 || in_array('adminitrator', $user_roles);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Drupal\Core\Form\FormStateInterface;
use Drupal\reliefweb_entities\EntityFormAlterServiceBase;
use Drupal\reliefweb_form\Helpers\FormHelper;

/**
* Guideline list form alteration service.
Expand All @@ -16,6 +17,10 @@ class GuidelineListFormAlter extends EntityFormAlterServiceBase {
protected function addBundleFormAlterations(array &$form, FormStateInterface $form_state) {
// Hide the url alias as it's not used for guideline lists.
$form['path']['#access'] = FALSE;

// Only keep allowed user roles as selectable roles.
$roles_to_remove = array_diff_key(user_role_names(), reliefweb_guidelines_get_user_roles());
FormHelper::removeOptions($form, 'field_role', array_keys($roles_to_remove));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ protected function initFilterDefinitions(array $filters = []) {
// No specific widget as the join is enough.
'widget' => 'none',
'join_callback' => 'joinRole',
'values' => user_role_names(TRUE),
'values' => reliefweb_guidelines_get_user_roles(),
];

$definitions['changed'] = [
Expand All @@ -227,7 +227,7 @@ protected function initFilterDefinitions(array $filters = []) {
* @see ::joinField()
*/
protected function joinRole(Select $query, array $definition, $entity_type_id, $entity_base_table, $entity_id_field, $or = FALSE, $values = []) {
// Join the users_roles table.
// Join the role field table.
$table = 'guideline__field_role';
$query->innerJoin($table, $table, "%alias.entity_id = {$entity_base_table}.{$entity_id_field} AND %alias.field_role_target_id IN (:roles[])", [
':roles[]' => $values,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Drupal\reliefweb_guidelines\Services;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Database\Query\Select;
use Drupal\Core\Session\AccountInterface;
use Drupal\reliefweb_guidelines\Plugin\Field\FieldWidget\GuidelineFieldTargetSelectWidget;
use Drupal\reliefweb_moderation\EntityModeratedInterface;
Expand Down Expand Up @@ -48,6 +49,12 @@ public function getHeaders() {
'specifier' => 'name',
'sortable' => TRUE,
],
'role' => [
'label' => $this->t('Role'),
'type' => 'field',
'specifier' => 'field_role',
'sortable' => FALSE,
],
'date' => [
'label' => $this->t('Updated'),
'type' => 'property',
Expand Down Expand Up @@ -86,7 +93,7 @@ public function getRows(array $results) {
$info = [];
$list = $entity->getGuidelineList();
if (!empty($list)) {
$info['list'] = $list->toLink($list->getRoleAndLabel())->toString();
$info['list'] = $list->toLink($list->label())->toString();
}
$info['link'] = $entity->getLinkToGuidelines($this->t('<em>guidelines</em>'));
$data['info'] = array_filter($info);
Expand Down Expand Up @@ -114,6 +121,9 @@ public function getRows(array $results) {
// Filter out empty data.
$cells['data'] = array_filter($data);

// User role.
$cells['role'] = $list?->field_role?->entity?->label() ?? 'Editor';

// Date cell.
$cells['date'] = [
'date' => $entity->getChangedTime(),
Expand Down Expand Up @@ -272,9 +282,39 @@ protected function initFilterDefinitions(array $filters = []) {
'operator' => 'OR',
'allow_no_value' => TRUE,
];
$definitions['role'] = [
'type' => 'other',
'label' => $this->t('Role'),
'field' => 'field_role',
'form' => 'role',
// No specific widget as the join is enough.
'widget' => 'none',
'join_callback' => 'joinRole',
'values' => reliefweb_guidelines_get_user_roles(),
];
return $definitions;
}

/**
* Users roles join callback.
*
* @see ::joinField()
*/
protected function joinRole(Select $query, array $definition, $entity_type_id, $entity_base_table, $entity_id_field, $or = FALSE, $values = []) {
// Join the parent table.
$parent_table = 'guideline__parent';
$query->innerJoin($parent_table, $parent_table, "%alias.entity_id = {$entity_base_table}.{$entity_id_field}");

// Join the role field table.
$role_table = 'guideline__field_role';
$query->innerJoin($role_table, $role_table, "%alias.entity_id = {$parent_table}.parent_target_id AND %alias.field_role_target_id IN (:roles[])", [
':roles[]' => $values,
]);

// No field to return as the inner join is enough.
return '';
}

/**
* {@inheritdoc}
*/
Expand Down

0 comments on commit d7e3f4e

Please sign in to comment.