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
6 changes: 3 additions & 3 deletions entityqueue_taxonomy.info.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: entityqueue_taxonomy
name: Entityqueue Taxonomy
type: module
description: Entityqueue Taxonomy -- smart queues for taxonomy terms
description: Creates entity queue for a taxonomy term.
core: 8.x
package: entityqueue
package: Entityqueue
dependencies:
- entityqueue
5 changes: 5 additions & 0 deletions entityqueue_taxonomy.links.contextual.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
entity.taxonomy_term.entityqueue_taxonomy:
title: 'Entity queue'
group: taxonomy_term
route_name: entity.taxonomy_term.entityqueue_taxonomy
weight: 10
5 changes: 5 additions & 0 deletions entityqueue_taxonomy.links.task.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
entity.taxonomy_term.entityqueue_taxonomy:
route_name: entity.taxonomy_term.entityqueue_taxonomy
title: 'Entity queue'
base_route: entity.taxonomy_term.canonical
weight: 10
72 changes: 67 additions & 5 deletions entityqueue_taxonomy.module
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
* Contains entityqueue_taxonomy.module.
*/

use Drupal\Component\Utility\Crypt;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Site\Settings;
use Drupal\entityqueue\Entity\EntityQueue;
use Drupal\entityqueue\Entity\EntitySubqueue;
use Drupal\taxonomy\TermInterface;
use Drupal\Core\Entity\EntityTypeInterface;

/**
* Implements hook_help().
Expand All @@ -18,18 +23,18 @@ function entityqueue_taxonomy_help($route_name, RouteMatchInterface $route_match
case 'help.page.entityqueue_taxonomy':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Entityqueue Taxonomy -- smart queues for taxonomy terms') . '</p>';
$output .= '<p>' . t('Creates entity queue for a taxonomy term.') . '</p>';
return $output;

default:
}
}

/**
* Implements hook_ENTITY_TYPE_insert.
* Implements hook_ENTITY_TYPE_insert().
*/
function entityqueue_taxonomy_term_insert(\Drupal\taxonomy\TermInterface $term) {
/** @var EntityQueue $entityqueues */
function entityqueue_taxonomy_term_insert(TermInterface $term) {
/** @var \Drupal\entityqueue\Entity\EntityQueue[] $entityqueues */
$entityqueues = EntityQueue::loadMultiple();
foreach ($entityqueues as $id => $entityqueue) {
if ($entityqueue->getHandler() === 'taxonomy_term') {
Expand All @@ -46,4 +51,61 @@ function entityqueue_taxonomy_term_insert(\Drupal\taxonomy\TermInterface $term)
}
}
}
}
}

/**
* Implements hook_form_alter().
*/
function entityqueue_taxonomy_form_alter(&$form, &$form_state, $form_id) {
if (preg_match('/entity_subqueue_(.*)_edit_form/i', $form_id, $matches)) {
$entityqueue_id = $matches[1];
$entityqueue = EntityQueue::load($entityqueue_id);
$subqueue_id = $form['name']['#default_value'];

$element = &$form['items']['widget']['add_more']['new_item']['target_id'];

$info = \Drupal::service('element_info')->getInfo($element['#type']);
// Overlay $info onto $element, retaining preexisting keys in $element.
$element += $info;

// Add vocabulary and taxonomy term ID to element to process.
preg_match("/{$entityqueue_id}_(\d+)/i", $subqueue_id, $matches);
$element['#taxonomy_term'] = $matches[1];
$element['#vocabulary'] = $entityqueue->getHandlerConfiguration()['vocabulary'];

// Add processing to alter autocomplete route.
$entity_autocomplete_process = $element['#process'][0];
$element['#process'][0] = 'entityqueue_taxonomy_entity_autocomplete_process';
array_unshift($element['#process'], $entity_autocomplete_process);
}
}

/**
* Entityqueue Taxonomy autocomplete element process to alter callback route
* and provide vocabulary and taxonomy term ID.
*/
function entityqueue_taxonomy_entity_autocomplete_process(array &$element) {
$element['#selection_handler'] = str_replace('default', 'entityqueue_taxonomy', $element['#selection_handler']);

// Store the selection settings in the key/value store and pass a hashed key
// in the route parameters.
$selection_settings = isset($element['#selection_settings']) ? $element['#selection_settings'] : [];
$data = serialize($selection_settings) . $element['#target_type'] . $element['#selection_handler'] . $element['#vocabulary'] . $element['#taxonomy_term'];
$selection_settings_key = Crypt::hmacBase64($data, Settings::getHashSalt());

$key_value_storage = \Drupal::keyValue('entity_autocomplete');
if (!$key_value_storage->has($selection_settings_key)) {
$key_value_storage->set($selection_settings_key, $selection_settings);
}

$element['#autocomplete_route_name'] = 'entityqueue_taxonomy.entity_autocomplete';
$element['#autocomplete_route_parameters'] = [
'target_type' => $element['#target_type'],
'selection_handler' => $element['#selection_handler'],
'vocabulary' => $element['#vocabulary'],
'taxonomy_term' => $element['#taxonomy_term'],
'selection_settings_key' => $selection_settings_key,
];

return $element;
}
21 changes: 21 additions & 0 deletions entityqueue_taxonomy.routing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
entity.taxonomy_term.entityqueue_taxonomy:
path: '/taxonomy/term/{taxonomy_term}/entityqueue-taxonomy'
defaults:
_controller: '\Drupal\entityqueue_taxonomy\Controller\EntityQueueTaxonomyUIController::taxonomyTermSubqueues'
entity_type_id: taxonomy_term
_title: 'Taxonomy entity queue'
options:
_admin_route: TRUE
parameters:
taxonomy_term:
type: entity:taxonomy_term
requirements:
_custom_access: '\Drupal\entityqueue_taxonomy\Controller\EntityQueueTaxonomyUIController::access'
taxonomy_term: \d+

entityqueue_taxonomy.entity_autocomplete:
path: '/entityqueue_taxonomy_autocomplete/{target_type}/{selection_handler}/{vocabulary}/{taxonomy_term}/{selection_settings_key}'
defaults:
_controller: '\Drupal\entityqueue_taxonomy\Controller\EntityQueueTaxonomyAutocompleteController::handleAutocomplete'
requirements:
_access: 'TRUE'
4 changes: 4 additions & 0 deletions entityqueue_taxonomy.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
entityqueue_taxonomy.autocomplete_matcher:
class: Drupal\entityqueue_taxonomy\EntityQueueTaxonomyAutocompleteMatcher
arguments: ['@plugin.manager.entity_reference_selection']
70 changes: 70 additions & 0 deletions src/Controller/EntityQueueTaxonomyAutocompleteController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Drupal\entityqueue_taxonomy\Controller;

use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Site\Settings;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\system\Controller\EntityAutocompleteController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

/**
* Defines a route controller for entity autocomplete form elements.
*/
class EntityQueueTaxonomyAutocompleteController extends EntityAutocompleteController {

/**
* The autocomplete matcher for entity references.
*
* @var \Drupal\entityqueue_taxonomy\EntityQueueTaxonomyAutocompleteMatcher
*/
protected $matcher;

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entityqueue_taxonomy.autocomplete_matcher'),
$container->get('keyvalue')->get('entity_autocomplete')
);
}

/**
* {@inheritdoc}
*/
public function handleAutocomplete(Request $request, $target_type, $selection_handler, $selection_settings_key, $vocabulary = NULL, $taxonomy_term = NULL) {
$matches = [];
// Get the typed string from the URL, if it exists.
if ($input = $request->query->get('q')) {
$typed_string = Tags::explode($input);
$typed_string = mb_strtolower(array_pop($typed_string));

// Selection settings are passed in as a hashed key of a serialized array
// stored in the key/value store.
$selection_settings = $this->keyValue->get($selection_settings_key, FALSE);
if ($selection_settings !== FALSE) {
$selection_settings_hash = Crypt::hmacBase64(serialize($selection_settings) . $target_type . $selection_handler . $vocabulary . $taxonomy_term, Settings::getHashSalt());
if ($selection_settings_hash !== $selection_settings_key) {
// Disallow access when the selection settings hash does not match the
// passed-in key.
throw new AccessDeniedHttpException('Invalid selection settings key.');
}
}
else {
// Disallow access when the selection settings key is not found in the
// key/value store.
throw new AccessDeniedHttpException();
}

// Provide tvocabulary and taxonomy term ID to matcher.
$matches = $this->matcher->getMatches($target_type, $selection_handler, $selection_settings, $typed_string, $vocabulary, $taxonomy_term);
}

return new JsonResponse($matches);
}

}
154 changes: 154 additions & 0 deletions src/Controller/EntityQueueTaxonomyUIController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?php

namespace Drupal\entityqueue_taxonomy\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\entityqueue\EntityQueueInterface;
use Drupal\taxonomy\TermInterface;

/**
* Returns responses for Entityqueue UI routes.
*/
class EntityQueueTaxonomyUIController extends ControllerBase {

/**
* Returns a form to add a new subqeue.
*
* @param \Drupal\entityqueue\EntityQueueInterface $entity_queue
* The queue this subqueue will be added to.
* @param \Drupal\taxonomy\TermInterface $taxonomy_term
* The taxonomy term.
*
* @return array
* The entity subqueue add form.
*/
public function addForm(EntityQueueInterface $entity_queue, TermInterface $taxonomy_term) {
$subqueue = $this->entityTypeManager()->getStorage('entity_subqueue')->create(
[
'queue' => $entity_queue->id(),
'name' => $entity_queue->id() . '_' . $taxonomy_term->id(),
'title' => $taxonomy_term->label(),
'langcode' => $entity_queue->language()->getId(),
]);
return $this->entityFormBuilder()->getForm($subqueue);
}

/**
* Returns a list of subqueues for the term.
*
* @param \Drupal\taxonomy\TermInterface $taxonomy_term
* The taxonomy term.
*
* @return array
* The term subqueues list.
*/
public function taxonomyTermSubqueues(TermInterface $taxonomy_term) {
$rows = [];
$queues = $this->getAvailableTaxonomyQueuesForEntity($this->currentUser());
$storage = $this->entityTypeManager->getStorage('entity_subqueue');

$destination = Url::fromRoute("entity.taxonomy_term.entityqueue_taxonomy", ['taxonomy_term' => $taxonomy_term->id()])->toString();

foreach ($queues as $queue) {
/** @var \Drupal\entityqueue\EntitySubqueueInterface $subqueue */
if ($subqueue = $storage->load($queue->id() . '_' . $taxonomy_term->id())) {
$operations = [
'data' => [
'#type' => 'operations',
'#links' => [
'edit' => [
'title' => $this->t('Edit subqueue items'),
'url' => $subqueue->toUrl('edit-form', ['query' => ['destination' => $destination]]),
],
],
],
];

if ($subqueue->isTranslatable()) {
$operations['data']['#links']['translate'] = [
'title' => $this->t('Translate'),
'url' => $subqueue->toUrl('drupal:content-translation-overview', ['query' => ['destination' => $destination]]),
];
}
}
else {
continue;
}

$rows[] = [
$queue->label(),
$operations,
];
}

return [
'#type' => 'table',
'#rows' => $rows,
'#header' => [
$this->t('Subqueue'),
$this->t('Operations'),
],
];

}

/**
* Checks access for a specific request.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param \Drupal\Core\Session\AccountInterface $account
* The account to check access.
* @param string $entity_type_id
* (optional) The entity type ID.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(RouteMatchInterface $route_match, AccountInterface $account, $entity_type_id = NULL) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $route_match->getParameter($entity_type_id);

$permisssion = 'update ' . $entity->bundle() . ' entityqueue';

if (!$account->hasPermission($permisssion)) {
return AccessResult::forbidden();
}

if ($entity && $this->getAvailableTaxonomyQueuesForEntity($account)) {
return AccessResult::allowed();
}

return AccessResult::forbidden();
}

/**
* Gets a list of queues which can hold this entity.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The account to check access.
*
* @return \Drupal\entityqueue\EntityQueueInterface[]
* An array of entity queues which can hold this entity.
*/
protected function getAvailableTaxonomyQueuesForEntity(AccountInterface $account) {
$storage = $this->entityTypeManager()->getStorage('entity_queue');

$queues = [];
/** @var \Drupal\entityqueue\Entity\EntityQueue[] $entityqueues */
$entityqueues = $storage->loadMultiple();

foreach ($entityqueues as $id => $entityqueue) {
if ($entityqueue->getHandler() === 'taxonomy_term' && $entityqueue->access('update', $account)) {
$queues[$entityqueue->id()] = $entityqueue;
}
}

return $queues;
}

}
Loading