contrib modules updates
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
Entity API module
|
||||
-----------------
|
||||
|
||||
This module contains improvements and extensions to the Drupal 8 Entity system.
|
||||
The goal is to bring useful improvements to upcoming Drupal 8 minor releases
|
||||
(e.g. 8.1, 8.2, ..) while maintaining backwards compatibility in future Entity
|
||||
API module versions (based on the improvements which went into core).
|
||||
|
||||
@todo: Explain version compatibility pattern.
|
||||
@todo: Add overview of all items and maintainers.
|
||||
Provides improvements and extensions to the Drupal 8 Entity system.
|
||||
Acts as a staging ground for Drupal core, with each core minor release (8.5, 8.6, 8.7)
|
||||
receiving a portion of this module's functionality.
|
||||
|
||||
Current functionality:
|
||||
- Local action providers (core issue: #2976861)
|
||||
- Permission providers (core issue: #2809177)
|
||||
- Query access API (Change record: https://www.drupal.org/node/3002038, core issue: #777578)
|
||||
- Bundle plugin API (plugin-based entity bundles, currently not proposed for core inclusion)
|
||||
- A generic UI for revisions (WIP, see #2625122)
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
"homepage": "http://drupal.org/project/entity",
|
||||
"license": "GPL-2.0+",
|
||||
"require": {
|
||||
"drupal/core": "~8.5"
|
||||
"drupal/core": "^8.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ description: Provides expanded entity APIs, which will be moved to Drupal core o
|
||||
type: module
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- drupal:system (>=8.5.0)
|
||||
- drupal:system (>=8.6.0)
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-06-08
|
||||
version: '8.x-1.0-beta4'
|
||||
# Information added by Drupal.org packaging script on 2018-10-11
|
||||
version: '8.x-1.0-rc1'
|
||||
core: '8.x'
|
||||
project: 'entity'
|
||||
datestamp: 1528452194
|
||||
datestamp: 1539272605
|
||||
|
||||
@@ -5,8 +5,14 @@
|
||||
* Provides expanded entity APIs.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\entity\BundlePlugin\BundlePluginHandler;
|
||||
use Drupal\entity\QueryAccess\EntityQueryAlter;
|
||||
use Drupal\entity\QueryAccess\ViewsQueryAlter;
|
||||
use Drupal\views\Plugin\views\query\QueryPluginBase;
|
||||
use Drupal\views\Plugin\views\query\Sql;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
/**
|
||||
* Gets the entity types which use bundle plugins.
|
||||
@@ -86,3 +92,29 @@ function entity_module_preuninstall($module) {
|
||||
\Drupal::service('entity.bundle_plugin_installer')->uninstallBundles($entity_type, [$module]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_query_TAG_alter().
|
||||
*/
|
||||
function entity_query_entity_query_alter(SelectInterface $query) {
|
||||
$entity_type_id = $query->getMetaData('entity_type');
|
||||
if ($query->hasTag($entity_type_id . '_access')) {
|
||||
$entity_type_manager = \Drupal::entityTypeManager();
|
||||
$entity_type = $entity_type_manager->getDefinition($entity_type_id);
|
||||
|
||||
\Drupal::service('class_resolver')
|
||||
->getInstanceFromDefinition(EntityQueryAlter::class)
|
||||
->alter($query, $entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_query_alter().
|
||||
*/
|
||||
function entity_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
|
||||
if ($query instanceof Sql) {
|
||||
\Drupal::service('class_resolver')
|
||||
->getInstanceFromDefinition(ViewsQueryAlter::class)
|
||||
->alter($query, $view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
services:
|
||||
access_check.entity_delete_multiple:
|
||||
class: Drupal\entity\Access\EntityDeleteMultipleAccessCheck
|
||||
arguments: ['@entity_type.manager', '@tempstore.private', '@request_stack']
|
||||
tags:
|
||||
- { name: access_check, applies_to: _entity_delete_multiple_access }
|
||||
|
||||
access_checker.entity_revision:
|
||||
class: \Drupal\entity\Access\EntityRevisionRouteAccessChecker
|
||||
arguments: ['@entity_type.manager', '@current_route_match']
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\Access;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\TempStore\PrivateTempStoreFactory;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Checks if the current user has delete access to the items of the tempstore.
|
||||
*/
|
||||
class EntityDeleteMultipleAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The tempstore service.
|
||||
*
|
||||
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
/**
|
||||
* Request stack service.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityDeleteMultipleAccessCheck.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
|
||||
* The tempstore service.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack service.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, RequestStack $request_stack) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->tempStore = $temp_store_factory->get('entity_delete_multiple_confirm');
|
||||
$this->requestStack = $request_stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has delete access for at least one item of the store.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* Run access checks for this account.
|
||||
* @param string $entity_type_id
|
||||
* Entity type ID.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResult
|
||||
* Allowed or forbidden, neutral if tempstore is empty.
|
||||
*/
|
||||
public function access(AccountInterface $account, $entity_type_id) {
|
||||
if (!$this->requestStack->getCurrentRequest()->getSession()) {
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
$selection = $this->tempStore->get($account->id() . ':' . $entity_type_id);
|
||||
if (empty($selection) || !is_array($selection)) {
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
$entities = $this->entityTypeManager->getStorage($entity_type_id)->loadMultiple(array_keys($selection));
|
||||
foreach ($entities as $entity) {
|
||||
// As long as the user has access to delete one entity allow access to the
|
||||
// delete form. Access will be checked again in
|
||||
// Drupal\Core\Entity\Form\DeleteMultipleForm::submit() in case it has
|
||||
// changed in the meantime.
|
||||
if ($entity->access('delete', $account)) {
|
||||
return AccessResult::allowed();
|
||||
}
|
||||
}
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -26,7 +26,7 @@ class EntityRevisionRouteAccessChecker implements AccessInterface {
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $accessCache = array();
|
||||
protected $accessCache = [];
|
||||
|
||||
/**
|
||||
* The currently active route match object.
|
||||
|
||||
@@ -13,8 +13,8 @@ use Drupal\Core\Entity\EntityTypeInterface;
|
||||
* Provided permissions:
|
||||
* - administer $entity_type
|
||||
* - access $entity_type overview
|
||||
* - view ($bundle) $entity_type
|
||||
* - view own unpublished $entity_type
|
||||
* - view ($bundle) $entity_type
|
||||
* - update (own|any) ($bundle) $entity_type
|
||||
* - delete (own|any) ($bundle) $entity_type
|
||||
* - create $bundle $entity_type
|
||||
|
||||
@@ -2,322 +2,13 @@
|
||||
|
||||
namespace Drupal\entity\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\BaseFormIdInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\TypedData\TranslatableInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\TempStore\PrivateTempStoreFactory;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Entity\Form\DeleteMultipleForm as CoreDeleteMultipleForm;
|
||||
|
||||
@trigger_error('\Drupal\entity\Form\DeleteMultipleForm has been deprecated in favor of \Drupal\Core\Entity\Form\DeleteMultipleForm. Use that instead.');
|
||||
|
||||
/**
|
||||
* Provides an entities deletion confirmation form.
|
||||
*
|
||||
* @deprecated Use \Drupal\Core\Entity\Form\DeleteMultipleForm instead.
|
||||
*/
|
||||
class DeleteMultipleForm extends ConfirmFormBase implements BaseFormIdInterface {
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The tempstore.
|
||||
*
|
||||
* @var \Drupal\Core\TempStore\SharedTempStore
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
/**
|
||||
* The messenger service.
|
||||
*
|
||||
* @var \Drupal\Core\Messenger\MessengerInterface
|
||||
*/
|
||||
protected $messenger;
|
||||
|
||||
/**
|
||||
* The entity type ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId;
|
||||
|
||||
/**
|
||||
* The selection, in the entity_id => langcodes format.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $selection = [];
|
||||
|
||||
/**
|
||||
* The entity type definition.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* Constructs a new DeleteMultiple object.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current user.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
|
||||
* The messenger service.
|
||||
*/
|
||||
public function __construct(AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, MessengerInterface $messenger) {
|
||||
$this->currentUser = $current_user;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->tempStore = $temp_store_factory->get('entity_delete_multiple_confirm');
|
||||
$this->messenger = $messenger;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('current_user'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('tempstore.private'),
|
||||
$container->get('messenger')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseFormId() {
|
||||
return 'entity_delete_multiple_confirm_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
// Get entity type ID from the route because ::buildForm has not yet been
|
||||
// called.
|
||||
$entity_type_id = $this->getRouteMatch()->getParameter('entity_type_id');
|
||||
return $entity_type_id . '_delete_multiple_confirm_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->formatPlural(count($this->selection), 'Are you sure you want to delete this @item?', 'Are you sure you want to delete these @items?', [
|
||||
'@item' => $this->entityType->getSingularLabel(),
|
||||
'@items' => $this->entityType->getPluralLabel(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
if ($this->entityType->hasLinkTemplate('collection')) {
|
||||
return new Url('entity.' . $this->entityTypeId . '.collection');
|
||||
}
|
||||
else {
|
||||
return new Url('<front>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) {
|
||||
$this->entityTypeId = $entity_type_id;
|
||||
$this->entityType = $this->entityTypeManager->getDefinition($this->entityTypeId);
|
||||
$this->selection = $this->tempStore->get($this->currentUser->id() . ':' . $entity_type_id);
|
||||
if (empty($this->entityTypeId) || empty($this->selection)) {
|
||||
return new RedirectResponse($this->getCancelUrl()
|
||||
->setAbsolute()
|
||||
->toString());
|
||||
}
|
||||
|
||||
$items = [];
|
||||
$entities = $this->entityTypeManager->getStorage($entity_type_id)->loadMultiple(array_keys($this->selection));
|
||||
foreach ($this->selection as $id => $selected_langcodes) {
|
||||
$entity = $entities[$id];
|
||||
foreach ($selected_langcodes as $langcode) {
|
||||
$key = $id . ':' . $langcode;
|
||||
if ($entity instanceof TranslatableInterface) {
|
||||
$entity = $entity->getTranslation($langcode);
|
||||
$default_key = $id . ':' . $entity->getUntranslated()->language()->getId();
|
||||
|
||||
// Build a nested list of translations that will be deleted if the
|
||||
// entity has multiple translations.
|
||||
$entity_languages = $entity->getTranslationLanguages();
|
||||
if (count($entity_languages) > 1 && $entity->isDefaultTranslation()) {
|
||||
$names = [];
|
||||
foreach ($entity_languages as $translation_langcode => $language) {
|
||||
$names[] = $language->getName();
|
||||
unset($items[$id . ':' . $translation_langcode]);
|
||||
}
|
||||
$items[$default_key] = [
|
||||
'label' => [
|
||||
'#markup' => $this->t('@label (Original translation) - <em>The following @entity_type translations will be deleted:</em>',
|
||||
[
|
||||
'@label' => $entity->label(),
|
||||
'@entity_type' => $this->entityType->getSingularLabel(),
|
||||
]),
|
||||
],
|
||||
'deleted_translations' => [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $names,
|
||||
],
|
||||
];
|
||||
}
|
||||
elseif (!isset($items[$default_key])) {
|
||||
$items[$key] = $entity->label();
|
||||
}
|
||||
}
|
||||
elseif (!isset($items[$key])) {
|
||||
$items[$key] = $entity->label();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form['entities'] = [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $items,
|
||||
];
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$total_count = 0;
|
||||
$delete_entities = [];
|
||||
$delete_translations = [];
|
||||
$inaccessible_entities = [];
|
||||
$storage = $this->entityTypeManager->getStorage($this->entityTypeId);
|
||||
|
||||
$entities = $storage->loadMultiple(array_keys($this->selection));
|
||||
foreach ($this->selection as $id => $selected_langcodes) {
|
||||
$entity = $entities[$id];
|
||||
if (!$entity->access('delete', $this->currentUser)) {
|
||||
$inaccessible_entities[] = $entity;
|
||||
continue;
|
||||
}
|
||||
foreach ($selected_langcodes as $langcode) {
|
||||
if ($entity instanceof TranslatableInterface) {
|
||||
$entity = $entity->getTranslation($langcode);
|
||||
// If the entity is the default translation then deleting it will
|
||||
// delete all the translations.
|
||||
if ($entity->isDefaultTranslation()) {
|
||||
$delete_entities[$id] = $entity;
|
||||
// If there are translations already marked for deletion then remove
|
||||
// them as they will be deleted anyway.
|
||||
unset($delete_translations[$id]);
|
||||
// Update the total count. Since a single delete will delete all
|
||||
// translations, we need to add the number of translations to the
|
||||
// count.
|
||||
$total_count += count($entity->getTranslationLanguages());
|
||||
}
|
||||
// Add the translation to the list of translations to be deleted
|
||||
// unless the default translation is being deleted.
|
||||
elseif (!isset($delete_entities[$id])) {
|
||||
$delete_translations[$id][] = $entity;
|
||||
}
|
||||
}
|
||||
elseif (!isset($delete_entities[$id])) {
|
||||
$delete_entities[$id] = $entity;
|
||||
$total_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($delete_entities) {
|
||||
$storage->delete($delete_entities);
|
||||
foreach ($delete_entities as $entity) {
|
||||
$this->logger($entity->getEntityType()->getProvider())->notice('The @entity-type %label has been deleted.', [
|
||||
'@entity-type' => $entity->getEntityType()->getLowercaseLabel(),
|
||||
'%label' => $entity->label(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($delete_translations) {
|
||||
/** @var \Drupal\Core\Entity\TranslatableInterface[][] $delete_translations */
|
||||
foreach ($delete_translations as $id => $translations) {
|
||||
$entity = $entities[$id]->getUntranslated();
|
||||
foreach ($translations as $translation) {
|
||||
$entity->removeTranslation($translation->language()->getId());
|
||||
}
|
||||
$entity->save();
|
||||
foreach ($translations as $translation) {
|
||||
$this->logger($entity->getEntityType()->getProvider())->notice('The @entity-type %label @language translation has been deleted.', [
|
||||
'@entity-type' => $entity->getEntityType()->getLowercaseLabel(),
|
||||
'%label' => $entity->label(),
|
||||
'@language' => $translation->language()->getName(),
|
||||
]);
|
||||
}
|
||||
$total_count += count($translations);
|
||||
}
|
||||
}
|
||||
|
||||
if ($total_count) {
|
||||
$this->messenger->addStatus($this->getDeletedMessage($total_count));
|
||||
}
|
||||
if ($inaccessible_entities) {
|
||||
$this->messenger->addWarning($this->getInaccessibleMessage(count($inaccessible_entities)));
|
||||
}
|
||||
$this->tempStore->delete($this->currentUser->id());
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to show the user after an item was deleted.
|
||||
*
|
||||
* @param int $count
|
||||
* Count of deleted translations.
|
||||
*
|
||||
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* The item deleted message.
|
||||
*/
|
||||
protected function getDeletedMessage($count) {
|
||||
return $this->formatPlural($count, 'Deleted @count item.', 'Deleted @count items.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to show the user when an item has not been deleted.
|
||||
*
|
||||
* @param int $count
|
||||
* Count of deleted translations.
|
||||
*
|
||||
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* The item inaccessible message.
|
||||
*/
|
||||
protected function getInaccessibleMessage($count) {
|
||||
return $this->formatPlural($count, "@count item has not been deleted because you do not have the necessary permissions.", "@count items have not been deleted because you do not have the necessary permissions.");
|
||||
}
|
||||
|
||||
}
|
||||
class DeleteMultipleForm extends CoreDeleteMultipleForm {}
|
||||
|
||||
@@ -119,10 +119,17 @@ class RevisionRevertForm extends ConfirmFormBase {
|
||||
$original_revision_timestamp = $this->revision->getRevisionCreationTime();
|
||||
|
||||
$this->revision->setRevisionLogMessage($this->t('Copy of the revision from %date.', ['%date' => $this->dateFormatter->format($original_revision_timestamp)]));
|
||||
drupal_set_message(t('@type %title has been reverted to the revision from %revision-date.', ['@type' => $this->getBundleLabel($this->revision), '%title' => $this->revision->label(), '%revision-date' => $this->dateFormatter->format($original_revision_timestamp)]));
|
||||
$this->messenger()->addStatus(t('@type %title has been reverted to the revision from %revision-date.', [
|
||||
'@type' => $this->getBundleLabel($this->revision),
|
||||
'%title' => $this->revision->label(),
|
||||
'%revision-date' => $this->dateFormatter->format($original_revision_timestamp),
|
||||
]));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('@type %title has been reverted', ['@type' => $this->getBundleLabel($this->revision), '%title' => $this->revision->label()]));
|
||||
$this->messenger()->addStatus(t('@type %title has been reverted', [
|
||||
'@type' => $this->getBundleLabel($this->revision),
|
||||
'%title' => $this->revision->label(),
|
||||
]));
|
||||
}
|
||||
|
||||
$this->revision->save();
|
||||
|
||||
@@ -11,15 +11,14 @@ use Drupal\Core\Form\FormStateInterface;
|
||||
/**
|
||||
* Extends the base entity form with revision support in the UI.
|
||||
*
|
||||
* @deprecated in favor of \Drupal\Core\Entity\ContentEntityForm. Use that
|
||||
* instead.
|
||||
* @deprecated Use \Drupal\Core\Entity\ContentEntityForm instead.
|
||||
*/
|
||||
class RevisionableContentEntityForm extends ContentEntityForm {
|
||||
|
||||
/**
|
||||
* The entity being used by this form.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface|\Drupal\entity\Revision\EntityRevisionLogInterface
|
||||
* @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\Core\Entity\RevisionLogInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
@@ -49,7 +48,8 @@ class RevisionableContentEntityForm extends ContentEntityForm {
|
||||
* The bundle entity, or NULL if there is none.
|
||||
*/
|
||||
protected function getBundleEntity() {
|
||||
if ($bundle_key = $this->entity->getEntityType()->getKey('bundle')) {
|
||||
if ($this->entity->getEntityType()->getBundleEntityType()) {
|
||||
$bundle_key = $this->entity->getEntityType()->getKey('bundle');
|
||||
return $this->entity->{$bundle_key}->referencedEntities()[0];
|
||||
}
|
||||
return NULL;
|
||||
|
||||
@@ -30,6 +30,12 @@ class EntityCollectionLocalActionProvider implements EntityLocalActionProviderIn
|
||||
/* @see \Drupal\entity\Menu\EntityAddLocalAction::getTitle() */
|
||||
'title' => 'Add ' . $entity_type->getSingularLabel(),
|
||||
'route_name' => $route_name,
|
||||
'options' => [
|
||||
// Redirect back to the collection after form submission.
|
||||
'query' => [
|
||||
'destination' => $entity_type->getLinkTemplate('collection'),
|
||||
],
|
||||
],
|
||||
'appears_on' => ["entity.$entity_type_id.collection"],
|
||||
'class' => EntityAddLocalAction::class,
|
||||
];
|
||||
|
||||
@@ -2,96 +2,19 @@
|
||||
|
||||
namespace Drupal\entity\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\TempStore\PrivateTempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Action\Plugin\Action\DeleteAction as CoreDeleteAction;
|
||||
|
||||
@trigger_error('\Drupal\entity\Plugin\Action\DeleteAction has been deprecated in favor of \Drupal\Core\Action\Plugin\Action\DeleteAction. Use that instead.');
|
||||
|
||||
/**
|
||||
* Redirects to an entity deletion form.
|
||||
*
|
||||
* @deprecated Use "entity:delete_action" instead.
|
||||
*
|
||||
* @Action(
|
||||
* id = "entity_delete_action",
|
||||
* label = @Translation("Delete entity"),
|
||||
* deriver = "Drupal\entity\Plugin\Action\Derivative\DeleteActionDeriver",
|
||||
* )
|
||||
*/
|
||||
class DeleteAction extends ActionBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The tempstore object.
|
||||
*
|
||||
* @var \Drupal\Core\TempStore\SharedTempStore
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Constructs a new DeleteAction object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* Current user.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
|
||||
$this->currentUser = $current_user;
|
||||
$this->tempStore = $temp_store_factory->get('entity_delete_multiple_confirm');
|
||||
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('tempstore.private'),
|
||||
$container->get('current_user')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function executeMultiple(array $entities) {
|
||||
/** @var \Drupal\Core\Entity\EntityInterface[] $entities */
|
||||
$selection = [];
|
||||
foreach ($entities as $entity) {
|
||||
$langcode = $entity->language()->getId();
|
||||
$selection[$entity->id()][$langcode] = $langcode;
|
||||
}
|
||||
$this->tempStore->set($this->currentUser->id() . ':' . $this->getPluginDefinition()['type'], $selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($object = NULL) {
|
||||
$this->executeMultiple([$object]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
return $object->access('delete', $account, $return_as_object);
|
||||
}
|
||||
|
||||
}
|
||||
class DeleteAction extends CoreDeleteAction {}
|
||||
|
||||
+7
-3
@@ -4,13 +4,14 @@ namespace Drupal\entity\Plugin\Action\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a delete action for each content entity type.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
class DeleteActionDeriver extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
@@ -46,7 +47,7 @@ class DeleteActionDeriver extends DeriverBase implements ContainerDeriverInterfa
|
||||
$definitions = [];
|
||||
foreach ($this->getParticipatingEntityTypes() as $entity_type_id => $entity_type) {
|
||||
$definition = $base_plugin_definition;
|
||||
$definition['label'] = t('Delete @entity_type', ['@entity_type' => $entity_type->getSingularLabel()]);
|
||||
$definition['label'] = t('Delete @entity_type (Deprecated)', ['@entity_type' => $entity_type->getSingularLabel()]);
|
||||
$definition['type'] = $entity_type_id;
|
||||
$definition['confirm_form_route_name'] = 'entity.' . $entity_type_id . '.delete_multiple_form';
|
||||
$definitions[$entity_type_id] = $definition;
|
||||
@@ -69,7 +70,10 @@ class DeleteActionDeriver extends DeriverBase implements ContainerDeriverInterfa
|
||||
protected function getParticipatingEntityTypes() {
|
||||
$entity_types = $this->entityTypeManager->getDefinitions();
|
||||
$entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
|
||||
return $entity_type->entityClassImplements(ContentEntityInterface::class) && $entity_type->hasLinkTemplate('delete-multiple-form');
|
||||
// Core requires a "delete-multiple-confirm" form to be declared as well,
|
||||
// if it's missing, it's safe to assume that the entity type is still
|
||||
// relying on previous Entity API contrib behavior.
|
||||
return $entity_type->hasLinkTemplate('delete-multiple-form') && !$entity_type->hasHandlerClass('form', 'delete-multiple-confirm');
|
||||
});
|
||||
|
||||
return $entity_types;
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\QueryAccess;
|
||||
|
||||
/**
|
||||
* Represents a single query access condition.
|
||||
*/
|
||||
final class Condition {
|
||||
|
||||
/**
|
||||
* The supported operators.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected static $supportedOperators = [
|
||||
'=', '<>', '<', '<=', '>', '>=', 'BETWEEN', 'NOT BETWEEN',
|
||||
'IN', 'NOT IN', 'IS NULL', 'IS NOT NULL',
|
||||
];
|
||||
|
||||
/**
|
||||
* The field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* The value.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* The operator.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $operator;
|
||||
|
||||
/**
|
||||
* Constructs a new Condition object.
|
||||
*
|
||||
* @param string $field
|
||||
* The field, with an optional column name. E.g: 'uid', 'address.locality'.
|
||||
* @param mixed $value
|
||||
* The value.
|
||||
* @param string $operator
|
||||
* The operator.
|
||||
* Possible values: =, <>, <, <=, >, >=, BETWEEN, NOT BETWEEN,
|
||||
* IN, NOT IN, IS NULL, IS NOT NULL.
|
||||
*/
|
||||
public function __construct($field, $value, $operator = NULL) {
|
||||
// Provide a default based on the data type of the value.
|
||||
if (!isset($operator)) {
|
||||
$operator = is_array($value) ? 'IN' : '=';
|
||||
}
|
||||
// Validate the selected operator.
|
||||
if (!in_array($operator, self::$supportedOperators)) {
|
||||
throw new \InvalidArgumentException(sprintf('Unrecognized operator "%s".', $operator));
|
||||
}
|
||||
|
||||
$this->field = $field;
|
||||
$this->value = $value;
|
||||
$this->operator = $operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getField() {
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue() {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOperator() {
|
||||
return $this->operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of the condition.
|
||||
*
|
||||
* Used for debugging purposes.
|
||||
*
|
||||
* @return string
|
||||
* The string representation of the condition.
|
||||
*/
|
||||
public function __toString() {
|
||||
if (in_array($this->operator, ['IS NULL', 'IS NOT NULL'])) {
|
||||
return "{$this->field} {$this->operator}";
|
||||
}
|
||||
else {
|
||||
if (is_array($this->value)) {
|
||||
$value = "['" . implode("', '", $this->value) . "']";
|
||||
}
|
||||
else {
|
||||
$value = "'" . $this->value . "'";
|
||||
}
|
||||
|
||||
return "{$this->field} {$this->operator} $value";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\QueryAccess;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* Represents a group of query access conditions.
|
||||
*
|
||||
* Used by query access handlers for filtering lists of entities based on
|
||||
* granted permissions.
|
||||
*
|
||||
* Examples:
|
||||
* @code
|
||||
* // Filter by node type and uid.
|
||||
* $condition_group = new ConditionGroup();
|
||||
* $condition_group->addCondition('type', ['article', 'page']);
|
||||
* $condition_group->addCondition('uid', '1');
|
||||
*
|
||||
* // Filter by node type or status.
|
||||
* $condition_group = new ConditionGroup('OR');
|
||||
* $condition_group->addCondition('type', ['article', 'page']);
|
||||
* $condition_group->addCondition('status', '1', '<>');
|
||||
*
|
||||
* // Nested condition groups: node type AND (uid OR status).
|
||||
* $condition_group = new ConditionGroup();
|
||||
* $condition_group->addCondition('type', ['article', 'page']);
|
||||
* $condition_group->addCondition((new ConditionGroup('OR'))
|
||||
* ->addCondition('uid', 1)
|
||||
* ->addCondition('status', '1')
|
||||
* );
|
||||
* @endcode
|
||||
*/
|
||||
final class ConditionGroup implements \Countable, RefinableCacheableDependencyInterface {
|
||||
|
||||
use RefinableCacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The conjunction.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $conjunction;
|
||||
|
||||
/**
|
||||
* The conditions.
|
||||
*
|
||||
* @var \Drupal\entity\QueryAccess\Condition[]|\Drupal\entity\QueryAccess\ConditionGroup[]
|
||||
*/
|
||||
protected $conditions = [];
|
||||
|
||||
/**
|
||||
* Whether the condition group is always FALSE.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $alwaysFalse = FALSE;
|
||||
|
||||
/**
|
||||
* Constructs a new ConditionGroup object.
|
||||
*
|
||||
* @param string $conjunction
|
||||
* The conjunction.
|
||||
*/
|
||||
public function __construct($conjunction = 'AND') {
|
||||
$this->conjunction = $conjunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conjunction.
|
||||
*
|
||||
* @return string
|
||||
* The conjunction. Possible values: AND, OR.
|
||||
*/
|
||||
public function getConjunction() {
|
||||
return $this->conjunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all conditions and nested condition groups.
|
||||
*
|
||||
* @return \Drupal\entity\QueryAccess\Condition[]|\Drupal\entity\QueryAccess\ConditionGroup[]
|
||||
* The conditions, where each one is either a Condition or a nested
|
||||
* ConditionGroup. Returned by reference, to allow callers to replace
|
||||
* or remove conditions.
|
||||
*/
|
||||
public function &getConditions() {
|
||||
return $this->conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition.
|
||||
*
|
||||
* @param string|\Drupal\entity\QueryAccess\ConditionGroup $field
|
||||
* Either a condition group (for nested AND/OR conditions), or a
|
||||
* field name with an optional column name. E.g: 'uid', 'address.locality'.
|
||||
* @param mixed $value
|
||||
* The value.
|
||||
* @param string $operator
|
||||
* The operator.
|
||||
* Possible values: =, <>, <, <=, >, >=, BETWEEN, NOT BETWEEN,
|
||||
* IN, NOT IN, IS NULL, IS NOT NULL.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCondition($field, $value = NULL, $operator = NULL) {
|
||||
if ($field instanceof ConditionGroup) {
|
||||
if ($field->count() === 1) {
|
||||
// The condition group only has a single condition, merge it.
|
||||
$this->conditions[] = reset($field->getConditions());
|
||||
$this->addCacheTags($field->getCacheTags());
|
||||
$this->addCacheContexts($field->getCacheContexts());
|
||||
$this->mergeCacheMaxAge($field->getCacheMaxAge());
|
||||
}
|
||||
elseif ($field->count() > 1) {
|
||||
$this->conditions[] = $field;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->conditions[] = new Condition($field, $value, $operator);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the condition group is always FALSE.
|
||||
*
|
||||
* Used when the user doesn't have access to any entities, to ensure that a
|
||||
* query returns no results.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the condition group is always FALSE.
|
||||
*/
|
||||
public function isAlwaysFalse() {
|
||||
return $this->alwaysFalse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the condition group should always be FALSE.
|
||||
*
|
||||
* @param bool $always_false
|
||||
* Whether the condition group should always be FALSE.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function alwaysFalse($always_false = TRUE) {
|
||||
$this->alwaysFalse = $always_false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the contained conditions when the condition group is cloned.
|
||||
*/
|
||||
public function __clone() {
|
||||
foreach ($this->conditions as $i => $condition) {
|
||||
$this->conditions[$i] = clone $condition;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of the condition group.
|
||||
*
|
||||
* @return string
|
||||
* The string representation of the condition group.
|
||||
*/
|
||||
public function __toString() {
|
||||
// Special case for a single, nested condition group:
|
||||
if (count($this->conditions) == 1) {
|
||||
return (string) reset($this->conditions);
|
||||
}
|
||||
$lines = [];
|
||||
foreach ($this->conditions as $condition) {
|
||||
$lines[] = str_replace("\n", "\n ", (string) $condition);
|
||||
}
|
||||
return $lines ? "(\n " . implode("\n {$this->conjunction}\n ", $lines) . "\n)" : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count() {
|
||||
return count($this->conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
$tags = $this->cacheTags;
|
||||
foreach ($this->conditions as $condition) {
|
||||
if ($condition instanceof ConditionGroup) {
|
||||
$tags = array_merge($tags, $condition->getCacheTags());
|
||||
}
|
||||
}
|
||||
return Cache::mergeTags($tags, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
$cache_contexts = $this->cacheContexts;
|
||||
foreach ($this->conditions as $condition) {
|
||||
if ($condition instanceof ConditionGroup) {
|
||||
$cache_contexts = array_merge($cache_contexts, $condition->getCacheContexts());
|
||||
}
|
||||
}
|
||||
return Cache::mergeContexts($cache_contexts);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
$max_age = $this->cacheMaxAge;
|
||||
foreach ($this->conditions as $condition) {
|
||||
if ($condition instanceof ConditionGroup) {
|
||||
$max_age = Cache::mergeMaxAges($max_age, $condition->getCacheMaxAge());
|
||||
}
|
||||
}
|
||||
return $max_age;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\QueryAccess;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\Sql\Tables;
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Defines a class for altering entity queries.
|
||||
*
|
||||
* EntityQuery doesn't have an alter hook, forcing this class to operate
|
||||
* on the underlying SQL query, duplicating the EntityQuery condition logic.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class EntityQueryAlter implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The renderer.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityQueryAlter object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
|
||||
* The entity field manager.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack.
|
||||
*/
|
||||
public function __construct(EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer, RequestStack $request_stack) {
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->renderer = $renderer;
|
||||
$this->requestStack = $request_stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('renderer'),
|
||||
$container->get('request_stack')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the select query for the given entity type.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\SelectInterface $query
|
||||
* The select query.
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*/
|
||||
public function alter(SelectInterface $query, EntityTypeInterface $entity_type) {
|
||||
if (!$entity_type->hasHandlerClass('query_access')) {
|
||||
return;
|
||||
}
|
||||
$entity_type_id = $entity_type->id();
|
||||
$storage = $this->entityTypeManager->getStorage($entity_type_id);
|
||||
if (!$storage instanceof SqlContentEntityStorage) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Drupal\entity\QueryAccess\QueryAccessHandlerInterface $query_access */
|
||||
$query_access = $this->entityTypeManager->getHandler($entity_type_id, 'query_access');
|
||||
$conditions = $query_access->getConditions('view');
|
||||
if ($conditions->isAlwaysFalse()) {
|
||||
$query->where('1 = 0');
|
||||
}
|
||||
elseif (count($conditions)) {
|
||||
$sql_conditions = $this->mapConditions($conditions, $query);
|
||||
$query->condition($sql_conditions);
|
||||
}
|
||||
|
||||
$this->applyCacheability(CacheableMetadata::createFromObject($conditions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an entity type's access conditions to SQL conditions.
|
||||
*
|
||||
* @param \Drupal\entity\QueryAccess\ConditionGroup $conditions
|
||||
* The access conditions.
|
||||
* @param \Drupal\Core\Database\Query\SelectInterface $query
|
||||
* The SQL query.
|
||||
* @param bool $nested_inside_or
|
||||
* Whether the access conditions are nested inside an OR condition.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\ConditionInterface
|
||||
* The SQL conditions.
|
||||
*/
|
||||
protected function mapConditions(ConditionGroup $conditions, SelectInterface $query, $nested_inside_or = FALSE) {
|
||||
$sql_condition = $query->conditionGroupFactory($conditions->getConjunction());
|
||||
$tables = new Tables($query);
|
||||
$nested_inside_or = $nested_inside_or || $conditions->getConjunction() == 'OR';
|
||||
foreach ($conditions->getConditions() as $condition) {
|
||||
if ($condition instanceof ConditionGroup) {
|
||||
$nested_sql_conditions = $this->mapConditions($condition, $query, $nested_inside_or);
|
||||
$sql_condition->condition($nested_sql_conditions);
|
||||
}
|
||||
else {
|
||||
// Access conditions don't specify a langcode.
|
||||
$langcode = NULL;
|
||||
$type = $nested_inside_or || $condition->getOperator() === 'IS NULL' ? 'LEFT' : 'INNER';
|
||||
$sql_field = $tables->addField($condition->getField(), $type, $langcode);
|
||||
$value = $condition->getValue();
|
||||
$operator = $condition->getOperator();
|
||||
// Using LIKE/NOT LIKE ensures a case insensitive comparison.
|
||||
// @see \Drupal\Core\Entity\Query\Sql\Condition::translateCondition().
|
||||
$case_sensitive = $tables->isFieldCaseSensitive($condition->getField());
|
||||
$operator_map = [
|
||||
'=' => 'LIKE',
|
||||
'<>' => 'NOT LIKE',
|
||||
];
|
||||
if (!$case_sensitive && isset($operator_map[$operator])) {
|
||||
$operator = $operator_map[$operator];
|
||||
$value = $query->escapeLike($value);
|
||||
}
|
||||
|
||||
$sql_condition->condition($sql_field, $value, $operator);
|
||||
}
|
||||
}
|
||||
|
||||
return $sql_condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the cacheablity metadata to the current request.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata
|
||||
* The cacheability metadata.
|
||||
*/
|
||||
protected function applyCacheability(CacheableMetadata $cacheable_metadata) {
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
if ($request->isMethodCacheable() && $this->renderer->hasRenderContext()) {
|
||||
$build = [];
|
||||
$cacheable_metadata->applyTo($build);
|
||||
$this->renderer->render($build);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\QueryAccess;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Defines the query access event.
|
||||
*
|
||||
* Allows modules to modify access conditions before they're applied to a query.
|
||||
*
|
||||
* The event ID is dynamic: entity.query_access.$entity_type_id
|
||||
*/
|
||||
class QueryAccessEvent extends Event {
|
||||
|
||||
/**
|
||||
* The conditions.
|
||||
*
|
||||
* @var \Drupal\entity\QueryAccess\ConditionGroup
|
||||
*/
|
||||
protected $conditions;
|
||||
|
||||
/**
|
||||
* The operation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $operation;
|
||||
|
||||
/**
|
||||
* The user for which to restrict access.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* Constructs a new QueryAccessEvent.
|
||||
*
|
||||
* @param \Drupal\entity\QueryAccess\ConditionGroup $conditions
|
||||
* The conditions.
|
||||
* @param string $operation
|
||||
* The operation. Usually one of "view", "update" or "delete".
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to restrict access.
|
||||
*/
|
||||
public function __construct(ConditionGroup $conditions, $operation, AccountInterface $account) {
|
||||
$this->conditions = $conditions;
|
||||
$this->operation = $operation;
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conditions.
|
||||
*
|
||||
* If $conditions->isAlwaysFalse() is TRUE, the user doesn't have access to
|
||||
* any entities, and the query is expected to return no results.
|
||||
* This can be reversed by calling $conditions->alwaysFalse(FALSE).
|
||||
*
|
||||
* If $conditions->isAlwaysFalse() is FALSE, and the condition group is
|
||||
* empty (count is 0), the user has full access, and the query doesn't
|
||||
* need to be restricted.
|
||||
*
|
||||
* @return \Drupal\entity\QueryAccess\ConditionGroup
|
||||
* The conditions.
|
||||
*/
|
||||
public function getConditions() {
|
||||
return $this->conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operation.
|
||||
*
|
||||
* @return string
|
||||
* The operation. Usually one of "view", "update" or "delete".
|
||||
*/
|
||||
public function getOperation() {
|
||||
return $this->operation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user for which to restrict access.
|
||||
*
|
||||
* @return \Drupal\Core\Session\AccountInterface
|
||||
* The user.
|
||||
*/
|
||||
public function getAccount() {
|
||||
return $this->account;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\QueryAccess;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Controls query access based on the generic entity permissions.
|
||||
*
|
||||
* @see \Drupal\entity\EntityAccessControlHandler
|
||||
* @see \Drupal\entity\EntityPermissionProvider
|
||||
*/
|
||||
class QueryAccessHandler extends QueryAccessHandlerBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildEntityOwnerConditions($operation, AccountInterface $account) {
|
||||
if ($operation == 'view') {
|
||||
// EntityPermissionProvider doesn't provide own/any view permissions.
|
||||
return $this->buildEntityConditions($operation, $account);
|
||||
}
|
||||
|
||||
return parent::buildEntityOwnerConditions($operation, $account);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\QueryAccess;
|
||||
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Provides common logic for query access handlers.
|
||||
*
|
||||
* @see \Drupal\entity\QueryAccess\QueryAccessHandler
|
||||
* @see \Drupal\entity\QueryAccess\UncacheableQueryAccessHandler
|
||||
*/
|
||||
abstract class QueryAccessHandlerBase implements EntityHandlerInterface, QueryAccessHandlerInterface {
|
||||
|
||||
/**
|
||||
* The entity type.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* The entity type bundle info.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $bundleInfo;
|
||||
|
||||
/**
|
||||
* The event dispatcher.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Constructs a new QueryAccessHandlerBase object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info
|
||||
* The entity type bundle info.
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||
* The event dispatcher.
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityTypeBundleInfoInterface $bundle_info, EventDispatcherInterface $event_dispatcher, AccountInterface $current_user) {
|
||||
$this->entityType = $entity_type;
|
||||
$this->bundleInfo = $bundle_info;
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
$this->currentUser = $current_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('event_dispatcher'),
|
||||
$container->get('current_user')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConditions($operation, AccountInterface $account = NULL) {
|
||||
$account = $account ?: $this->currentUser;
|
||||
$entity_type_id = $this->entityType->id();
|
||||
$conditions = $this->buildConditions($operation, $account);
|
||||
|
||||
// Allow other modules to modify the conditions before they are used.
|
||||
$event = new QueryAccessEvent($conditions, $operation, $account);
|
||||
$this->eventDispatcher->dispatch("entity.query_access.{$entity_type_id}", $event);
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the conditions for the given operation and user.
|
||||
*
|
||||
* @param string $operation
|
||||
* The access operation. Usually one of "view", "update" or "delete".
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to restrict access.
|
||||
*
|
||||
* @return \Drupal\entity\QueryAccess\ConditionGroup
|
||||
* The conditions.
|
||||
*/
|
||||
public function buildConditions($operation, AccountInterface $account) {
|
||||
$entity_type_id = $this->entityType->id();
|
||||
$has_owner = $this->entityType->entityClassImplements(EntityOwnerInterface::class);
|
||||
|
||||
if ($account->hasPermission("administer {$entity_type_id}")) {
|
||||
// The user has full access to all operations, no conditions needed.
|
||||
$conditions = new ConditionGroup('OR');
|
||||
$conditions->addCacheContexts(['user.permissions']);
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
if ($has_owner) {
|
||||
$entity_conditions = $this->buildEntityOwnerConditions($operation, $account);
|
||||
}
|
||||
else {
|
||||
$entity_conditions = $this->buildEntityConditions($operation, $account);
|
||||
}
|
||||
|
||||
$conditions = NULL;
|
||||
if ($operation == 'view' && $this->entityType->entityClassImplements(EntityPublishedInterface::class)) {
|
||||
$uid_key = $this->entityType->getKey('uid');
|
||||
$published_key = $this->entityType->getKey('published');
|
||||
$published_conditions = NULL;
|
||||
$unpublished_conditions = NULL;
|
||||
|
||||
if ($entity_conditions) {
|
||||
// Restrict the existing conditions to published entities only.
|
||||
$published_conditions = new ConditionGroup('AND');
|
||||
$published_conditions->addCacheContexts(['user.permissions']);
|
||||
$published_conditions->addCondition($entity_conditions);
|
||||
$published_conditions->addCondition($published_key, '1');
|
||||
}
|
||||
if ($has_owner && $account->hasPermission("view own unpublished $entity_type_id")) {
|
||||
$unpublished_conditions = new ConditionGroup('AND');
|
||||
$unpublished_conditions->addCacheContexts(['user']);
|
||||
$unpublished_conditions->addCondition($uid_key, $account->id());
|
||||
$unpublished_conditions->addCondition($published_key, '0');
|
||||
}
|
||||
|
||||
if ($published_conditions && $unpublished_conditions) {
|
||||
$conditions = new ConditionGroup('OR');
|
||||
$conditions->addCondition($published_conditions);
|
||||
$conditions->addCondition($unpublished_conditions);
|
||||
}
|
||||
elseif ($published_conditions) {
|
||||
$conditions = $published_conditions;
|
||||
}
|
||||
elseif ($unpublished_conditions) {
|
||||
$conditions = $unpublished_conditions;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$conditions = $entity_conditions;
|
||||
}
|
||||
|
||||
if (!$conditions) {
|
||||
// The user doesn't have access to any entities.
|
||||
// Falsify the query to ensure no results are returned.
|
||||
$conditions = new ConditionGroup('OR');
|
||||
$conditions->addCacheContexts(['user.permissions']);
|
||||
$conditions->alwaysFalse();
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the conditions for entities that have an owner.
|
||||
*
|
||||
* @param string $operation
|
||||
* The access operation. Usually one of "view", "update" or "delete".
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to restrict access.
|
||||
*
|
||||
* @return \Drupal\entity\QueryAccess\ConditionGroup|null
|
||||
* The conditions, or NULL if the user doesn't have access to any entity.
|
||||
*/
|
||||
protected function buildEntityOwnerConditions($operation, AccountInterface $account) {
|
||||
$entity_type_id = $this->entityType->id();
|
||||
$uid_key = $this->entityType->getKey('uid');
|
||||
$bundle_key = $this->entityType->getKey('bundle');
|
||||
|
||||
$conditions = new ConditionGroup('OR');
|
||||
$conditions->addCacheContexts(['user.permissions']);
|
||||
// Any $entity_type permission.
|
||||
if ($account->hasPermission("$operation any $entity_type_id")) {
|
||||
// The user has full access, no conditions needed.
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
// Own $entity_type permission.
|
||||
if ($account->hasPermission("$operation own $entity_type_id")) {
|
||||
$conditions->addCacheContexts(['user']);
|
||||
$conditions->addCondition($uid_key, $account->id());
|
||||
}
|
||||
|
||||
$bundles = array_keys($this->bundleInfo->getBundleInfo($entity_type_id));
|
||||
$bundles_with_any_permission = [];
|
||||
$bundles_with_own_permission = [];
|
||||
foreach ($bundles as $bundle) {
|
||||
if ($account->hasPermission("$operation any $bundle $entity_type_id")) {
|
||||
$bundles_with_any_permission[] = $bundle;
|
||||
}
|
||||
if ($account->hasPermission("$operation own $bundle $entity_type_id")) {
|
||||
$bundles_with_own_permission[] = $bundle;
|
||||
}
|
||||
}
|
||||
// Any $bundle permission.
|
||||
if ($bundles_with_any_permission) {
|
||||
$conditions->addCondition($bundle_key, $bundles_with_any_permission);
|
||||
}
|
||||
// Own $bundle permission.
|
||||
if ($bundles_with_own_permission) {
|
||||
$conditions->addCacheContexts(['user']);
|
||||
$conditions->addCondition((new ConditionGroup('AND'))
|
||||
->addCondition($uid_key, $account->id())
|
||||
->addCondition($bundle_key, $bundles_with_own_permission)
|
||||
);
|
||||
}
|
||||
|
||||
return $conditions->count() ? $conditions : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the conditions for entities that do not have an owner.
|
||||
*
|
||||
* @param string $operation
|
||||
* The access operation. Usually one of "view", "update" or "delete".
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to restrict access.
|
||||
*
|
||||
* @return \Drupal\entity\QueryAccess\ConditionGroup|null
|
||||
* The conditions, or NULL if the user doesn't have access to any entity.
|
||||
*/
|
||||
protected function buildEntityConditions($operation, AccountInterface $account) {
|
||||
$entity_type_id = $this->entityType->id();
|
||||
$bundle_key = $this->entityType->getKey('bundle');
|
||||
|
||||
$conditions = new ConditionGroup('OR');
|
||||
$conditions->addCacheContexts(['user.permissions']);
|
||||
// The $entity_type permission.
|
||||
if ($account->hasPermission("$operation $entity_type_id")) {
|
||||
// The user has full access, no conditions needed.
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
$bundles = array_keys($this->bundleInfo->getBundleInfo($entity_type_id));
|
||||
$bundles_with_any_permission = [];
|
||||
foreach ($bundles as $bundle) {
|
||||
if ($account->hasPermission("$operation $bundle $entity_type_id")) {
|
||||
$bundles_with_any_permission[] = $bundle;
|
||||
}
|
||||
}
|
||||
// The $bundle permission.
|
||||
if ($bundles_with_any_permission) {
|
||||
$conditions->addCondition($bundle_key, $bundles_with_any_permission);
|
||||
}
|
||||
|
||||
return $conditions->count() ? $conditions : NULL;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\QueryAccess;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Query access handlers control access to entities in queries.
|
||||
*
|
||||
* An entity defines a query access handler in its annotation:
|
||||
* @code
|
||||
* query_access = "\Drupal\entity\QueryAccess\QueryAccessHandler"
|
||||
* @code
|
||||
* The handler builds a set of conditions which are then applied to a query
|
||||
* to filter it. For example, if the user #22 only has access to view
|
||||
* their own entities, a uid = '22' condition will be built and applied.
|
||||
*
|
||||
* The following query types are supported:
|
||||
* - Entity queries with the $entity_type_id . '_access' tag.
|
||||
* - Views queries.
|
||||
*/
|
||||
interface QueryAccessHandlerInterface {
|
||||
|
||||
/**
|
||||
* Gets the conditions for the given operation and user.
|
||||
*
|
||||
* The "entity.query_access.$entity_type_id" event is fired to allow
|
||||
* modules to alter the conditions.
|
||||
*
|
||||
* @param string $operation
|
||||
* The access operation. Usually one of "view", "update" or "delete".
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to restrict access, or NULL
|
||||
* to assume the current user. Defaults to NULL.
|
||||
*
|
||||
* @return \Drupal\entity\QueryAccess\ConditionGroup
|
||||
* The conditions.
|
||||
*/
|
||||
public function getConditions($operation, AccountInterface $account = NULL);
|
||||
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\QueryAccess;
|
||||
|
||||
/**
|
||||
* Controls query access based on the uncacheable entity permissions.
|
||||
*
|
||||
* @see \Drupal\entity\UncacheableEntityAccessControlHandler
|
||||
* @see \Drupal\entity\UncacheableEntityPermissionProvider
|
||||
*/
|
||||
class UncacheableQueryAccessHandler extends QueryAccessHandlerBase {}
|
||||
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\QueryAccess;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\Condition as SqlCondition;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Sql\DefaultTableMapping;
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\views\Plugin\views\query\Sql;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Views;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Defines a class for altering views queries.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ViewsQueryAlter implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The renderer.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* Constructs a new ViewsQueryAlter object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
|
||||
* The entity field manager.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack.
|
||||
*/
|
||||
public function __construct(Connection $connection, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer, RequestStack $request_stack) {
|
||||
$this->connection = $connection;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->renderer = $renderer;
|
||||
$this->requestStack = $request_stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('database'),
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('renderer'),
|
||||
$container->get('request_stack')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the given views query.
|
||||
*
|
||||
* @param \Drupal\views\Plugin\views\query\Sql $query
|
||||
* The views query.
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view.
|
||||
*/
|
||||
public function alter(Sql $query, ViewExecutable $view) {
|
||||
$table_info = $query->getEntityTableInfo();
|
||||
$base_table = reset($table_info);
|
||||
if (empty($base_table['entity_type']) || $base_table['relationship_id'] != 'none') {
|
||||
return;
|
||||
}
|
||||
$entity_type_id = $base_table['entity_type'];
|
||||
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
|
||||
if (!$entity_type->hasHandlerClass('query_access')) {
|
||||
return;
|
||||
}
|
||||
$storage = $this->entityTypeManager->getStorage($entity_type_id);
|
||||
if (!$storage instanceof SqlContentEntityStorage) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Drupal\entity\QueryAccess\QueryAccessHandlerInterface $query_access */
|
||||
$query_access = $this->entityTypeManager->getHandler($entity_type_id, 'query_access');
|
||||
$conditions = $query_access->getConditions('view');
|
||||
if ($conditions->isAlwaysFalse()) {
|
||||
$query->addWhereExpression(0, '1 = 0');
|
||||
}
|
||||
elseif (count($conditions)) {
|
||||
// Store the data table, in case mapConditions() needs to join it in.
|
||||
$base_table['data_table'] = $entity_type->getDataTable();
|
||||
$field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type_id);
|
||||
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
|
||||
$table_mapping = $storage->getTableMapping();
|
||||
$sql_conditions = $this->mapConditions($conditions, $query, $base_table, $field_storage_definitions, $table_mapping);
|
||||
$query->addWhere(0, $sql_conditions);
|
||||
}
|
||||
|
||||
$this->applyCacheability(CacheableMetadata::createFromObject($conditions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps an entity type's access conditions to views SQL conditions.
|
||||
*
|
||||
* @param \Drupal\entity\QueryAccess\ConditionGroup $conditions
|
||||
* The access conditions.
|
||||
* @param \Drupal\views\Plugin\views\query\Sql $query
|
||||
* The views query.
|
||||
* @param array $base_table
|
||||
* The base table information.
|
||||
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_storage_definitions
|
||||
* The field storage definitions.
|
||||
* @param \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping
|
||||
* The table mapping.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\ConditionInterface
|
||||
* The SQL conditions.
|
||||
*/
|
||||
protected function mapConditions(ConditionGroup $conditions, Sql $query, array $base_table, array $field_storage_definitions, DefaultTableMapping $table_mapping) {
|
||||
$sql_condition = new SqlCondition($conditions->getConjunction());
|
||||
foreach ($conditions->getConditions() as $condition) {
|
||||
if ($condition instanceof ConditionGroup) {
|
||||
$nested_sql_conditions = $this->mapConditions($condition, $query, $base_table, $field_storage_definitions, $table_mapping);
|
||||
$sql_condition->condition($nested_sql_conditions);
|
||||
}
|
||||
else {
|
||||
$field = $condition->getField();
|
||||
$property_name = NULL;
|
||||
if (strpos($field, '.') !== FALSE) {
|
||||
list($field, $property_name) = explode('.', $field);
|
||||
}
|
||||
// Skip unknown fields.
|
||||
if (!isset($field_storage_definitions[$field])) {
|
||||
continue;
|
||||
}
|
||||
$field_storage_definition = $field_storage_definitions[$field];
|
||||
if (!$property_name) {
|
||||
$property_name = $field_storage_definition->getMainPropertyName();
|
||||
}
|
||||
|
||||
$column = $table_mapping->getFieldColumnName($field_storage_definition, $property_name);
|
||||
if ($table_mapping->requiresDedicatedTableStorage($field_storage_definitions[$field])) {
|
||||
if ($base_table['revision']) {
|
||||
$dedicated_table = $table_mapping->getDedicatedRevisionTableName($field_storage_definition);
|
||||
}
|
||||
else {
|
||||
$dedicated_table = $table_mapping->getDedicatedDataTableName($field_storage_definition);
|
||||
}
|
||||
// Views defaults to LEFT JOIN. For simplicity, we don't try to
|
||||
// use an INNER JOIN when it's safe to do so (AND conjunctions).
|
||||
$alias = $query->ensureTable($dedicated_table);
|
||||
}
|
||||
elseif ($base_table['revision'] && !$field_storage_definition->isRevisionable()) {
|
||||
// Workaround for #2652652, which causes $query->ensureTable()
|
||||
// to not work in this case, due to a missing relationship.
|
||||
if ($data_table = $query->getTableInfo($base_table['data_table'])) {
|
||||
$alias = $data_table['alias'];
|
||||
}
|
||||
else {
|
||||
$configuration = [
|
||||
'type' => 'INNER',
|
||||
'table' => $base_table['data_table'],
|
||||
'field' => 'id',
|
||||
'left_table' => $base_table['alias'],
|
||||
'left_field' => 'id',
|
||||
];
|
||||
/** @var \Drupal\Views\Plugin\views\join\JoinPluginBase $join */
|
||||
$join = Views::pluginManager('join')->createInstance('standard', $configuration);
|
||||
$alias = $query->addRelationship($base_table['data_table'], $join, $data_table);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$alias = $base_table['alias'];
|
||||
}
|
||||
|
||||
$value = $condition->getValue();
|
||||
$operator = $condition->getOperator();
|
||||
// Using LIKE/NOT LIKE ensures a case insensitive comparison.
|
||||
// @see \Drupal\Core\Entity\Query\Sql\Condition::translateCondition().
|
||||
$property_definitions = $field_storage_definition->getPropertyDefinitions();
|
||||
$case_sensitive = $property_definitions[$property_name]->getSetting('case_sensitive');
|
||||
$operator_map = [
|
||||
'=' => 'LIKE',
|
||||
'<>' => 'NOT LIKE',
|
||||
];
|
||||
if (!$case_sensitive && isset($operator_map[$operator])) {
|
||||
$operator = $operator_map[$operator];
|
||||
$value = $this->connection->escapeLike($value);
|
||||
}
|
||||
|
||||
$sql_condition->condition("$alias.$column", $value, $operator);
|
||||
}
|
||||
}
|
||||
|
||||
return $sql_condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the cacheablity metadata to the current request.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheableMetadata $cacheable_metadata
|
||||
* The cacheability metadata.
|
||||
*/
|
||||
protected function applyCacheability(CacheableMetadata $cacheable_metadata) {
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
if ($request->isMethodCacheable() && $this->renderer->hasRenderContext()) {
|
||||
$build = [];
|
||||
$cacheable_metadata->applyTo($build);
|
||||
$this->renderer->render($build);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,10 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Provides the HTML route for deleting multiple entities.
|
||||
*
|
||||
* @deprecated Since Drupal 8.6.x the core DefaultHtmlRouteProvider provides
|
||||
* the route for any entity type with a "delete-multiple-form" link template
|
||||
* and a "delete-multiple-confirm" form.
|
||||
*/
|
||||
class DeleteMultipleRouteProvider implements EntityRouteProviderInterface {
|
||||
|
||||
@@ -34,7 +38,10 @@ class DeleteMultipleRouteProvider implements EntityRouteProviderInterface {
|
||||
* The generated route, if available.
|
||||
*/
|
||||
protected function deleteMultipleFormRoute(EntityTypeInterface $entity_type) {
|
||||
if ($entity_type->hasLinkTemplate('delete-multiple-form')) {
|
||||
// Core requires a "delete-multiple-confirm" form to be declared as well,
|
||||
// if it's missing, it's safe to assume that the entity type is still
|
||||
// relying on previous Entity API contrib behavior.
|
||||
if ($entity_type->hasLinkTemplate('delete-multiple-form') && !$entity_type->hasHandlerClass('form', 'delete-multiple-confirm')) {
|
||||
$route = new Route($entity_type->getLinkTemplate('delete-multiple-form'));
|
||||
$route->setDefault('_form', '\Drupal\entity\Form\DeleteMultipleForm');
|
||||
$route->setDefault('entity_type_id', $entity_type->id());
|
||||
|
||||
@@ -14,9 +14,8 @@ use Drupal\user\EntityOwnerInterface;
|
||||
* Provided permissions:
|
||||
* - administer $entity_type
|
||||
* - access $entity_type overview
|
||||
* - view any ($bundle) $entity_type
|
||||
* - view own ($bundle) $entity_type
|
||||
* - view own unpublished $entity_type
|
||||
* - view (own|any) ($bundle) $entity_type
|
||||
* - update (own|any) ($bundle) $entity_type
|
||||
* - delete (own|any) ($bundle) $entity_type
|
||||
* - create $bundle $entity_type
|
||||
@@ -69,8 +68,8 @@ class UncacheableEntityPermissionProvider extends EntityPermissionProviderBase {
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["view any {$entity_type_id}"] = [
|
||||
'title' => $this->t('View any @type', [
|
||||
$permissions["view {$entity_type_id}"] = [
|
||||
'title' => $this->t('View @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
@@ -95,18 +94,25 @@ class UncacheableEntityPermissionProvider extends EntityPermissionProviderBase {
|
||||
$has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
|
||||
$plural_label = $entity_type->getPluralLabel();
|
||||
|
||||
$permissions["view any {$entity_type_id}"] = [
|
||||
'title' => $this->t('View any @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
if ($has_owner) {
|
||||
$permissions["view any {$entity_type_id}"] = [
|
||||
'title' => $this->t('View any @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["view own {$entity_type_id}"] = [
|
||||
'title' => $this->t('View own @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["view {$entity_type_id}"] = [
|
||||
'title' => $this->t('View @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($bundles as $bundle_name => $bundle_info) {
|
||||
if ($has_owner) {
|
||||
@@ -124,8 +130,8 @@ class UncacheableEntityPermissionProvider extends EntityPermissionProviderBase {
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["view any {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: View any @type', [
|
||||
$permissions["view {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: View @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
|
||||
+3
-3
@@ -6,8 +6,8 @@ package: Testing
|
||||
dependencies:
|
||||
- entity
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-06-08
|
||||
version: '8.x-1.0-beta4'
|
||||
# Information added by Drupal.org packaging script on 2018-10-11
|
||||
version: '8.x-1.0-rc1'
|
||||
core: '8.x'
|
||||
project: 'entity'
|
||||
datestamp: 1528452194
|
||||
datestamp: 1539272605
|
||||
|
||||
+3
-3
@@ -6,8 +6,8 @@ package: Testing
|
||||
dependencies:
|
||||
- entity
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-06-08
|
||||
version: '8.x-1.0-beta4'
|
||||
# Information added by Drupal.org packaging script on 2018-10-11
|
||||
version: '8.x-1.0-rc1'
|
||||
core: '8.x'
|
||||
project: 'entity'
|
||||
datestamp: 1528452194
|
||||
datestamp: 1539272605
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.entity_test_enhanced.assigned
|
||||
id: entity_test_enhanced.first.assigned
|
||||
field_name: assigned
|
||||
entity_type: entity_test_enhanced
|
||||
bundle: first
|
||||
label: Assigned
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: string
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.entity_test_enhanced.assigned
|
||||
id: entity_test_enhanced.second.assigned
|
||||
field_name: assigned
|
||||
entity_type: entity_test_enhanced
|
||||
bundle: second
|
||||
label: Assigned
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: string
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- entity_module_test
|
||||
id: entity_test_enhanced.assigned
|
||||
field_name: assigned
|
||||
entity_type: entity_test_enhanced
|
||||
type: string
|
||||
settings:
|
||||
max_length: 255
|
||||
is_ascii: false
|
||||
case_sensitive: true
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- entity_module_test
|
||||
id: entity_test_enhanced
|
||||
label: 'Enhanced entities'
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: entity_test_enhanced_field_data
|
||||
base_field: id
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
options: { }
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
name:
|
||||
table: entity_test_enhanced_field_data
|
||||
field: name
|
||||
id: name
|
||||
entity_type: null
|
||||
entity_field: name
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: string
|
||||
settings: { }
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
filters: { }
|
||||
sorts:
|
||||
id:
|
||||
id: id
|
||||
table: entity_test_enhanced_field_data
|
||||
field: id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: entity_test_enhanced
|
||||
entity_field: id
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
tags: { }
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- entity_module_test
|
||||
id: entity_test_enhanced_revisions
|
||||
label: 'Enhanced entities (Revisions)'
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: entity_test_enhanced_field_revision
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
options: { }
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
name:
|
||||
table: entity_test_enhanced_field_revision
|
||||
field: name
|
||||
id: name
|
||||
entity_type: null
|
||||
entity_field: name
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: string
|
||||
settings: { }
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
filters: { }
|
||||
sorts:
|
||||
vid:
|
||||
id: vid
|
||||
table: entity_test_enhanced_field_revision
|
||||
field: vid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: entity_test_enhanced
|
||||
entity_field: vid
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
tags: { }
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- entity_module_test
|
||||
id: entity_test_enhanced_with_owner
|
||||
label: 'Enhanced entities with an owner'
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: entity_test_enhanced_with_owner_field_data
|
||||
base_field: id
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
options: { }
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
name:
|
||||
table: entity_test_enhanced_with_owner_field_data
|
||||
field: name
|
||||
id: name
|
||||
entity_type: null
|
||||
entity_field: name
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: string
|
||||
settings: { }
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
filters: { }
|
||||
sorts:
|
||||
id:
|
||||
id: id
|
||||
table: entity_test_enhanced_with_owner_field_data
|
||||
field: id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: entity_test_enhanced_with_owner
|
||||
entity_field: id
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
tags: { }
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- entity_module_test
|
||||
id: entity_test_enhanced_with_owner_revisions
|
||||
label: 'Enhanced entities with an owner (Revisions)'
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: entity_test_enhanced_with_owner_field_revision
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
options: { }
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
name:
|
||||
table: entity_test_enhanced_with_owner_field_revision
|
||||
field: name
|
||||
id: name
|
||||
entity_type: null
|
||||
entity_field: name
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: string
|
||||
settings: { }
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
filters: { }
|
||||
sorts:
|
||||
vid:
|
||||
id: vid
|
||||
table: entity_test_enhanced_with_owner_field_revision
|
||||
field: vid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: entity_test_enhanced_with_owner
|
||||
entity_field: vid
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
tags: { }
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
entity_module_test.entity_test_enhanced_bundle.*:
|
||||
type: config_entity
|
||||
label: 'Entity test with enhancements - Bundle'
|
||||
mapping:
|
||||
id:
|
||||
type: string
|
||||
label: 'Type'
|
||||
label:
|
||||
type: label
|
||||
label: 'Label'
|
||||
description:
|
||||
type: text
|
||||
label: 'Description'
|
||||
new_revision:
|
||||
type: boolean
|
||||
label: 'New revision'
|
||||
+5
-3
@@ -2,9 +2,11 @@ name: Entity test
|
||||
type: module
|
||||
package: Testing
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- field
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-06-08
|
||||
version: '8.x-1.0-beta4'
|
||||
# Information added by Drupal.org packaging script on 2018-10-11
|
||||
version: '8.x-1.0-rc1'
|
||||
core: '8.x'
|
||||
project: 'entity'
|
||||
datestamp: 1528452194
|
||||
datestamp: 1539272605
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_entity_bundle_info().
|
||||
*/
|
||||
function entity_module_test_entity_bundle_info() {
|
||||
$bundles['entity_test_enhanced']['default']['label'] = t('Default');
|
||||
$bundles['entity_test_enhanced']['first']['label'] = t('First');
|
||||
$bundles['entity_test_enhanced']['second']['label'] = t('Second');
|
||||
$bundles['entity_test_enhanced_with_owner']['default']['label'] = t('Default');
|
||||
$bundles['entity_test_enhanced_with_owner']['first']['label'] = t('First');
|
||||
$bundles['entity_test_enhanced_with_owner']['second']['label'] = t('Second');
|
||||
|
||||
return $bundles;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
services:
|
||||
entity_module_test.query_access_subscriber:
|
||||
class: Drupal\entity_module_test\EventSubscriber\QueryAccessSubscriber
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
+11
-3
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Drupal\entity_module_test\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedTrait;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\entity\Revision\RevisionableContentEntityBase;
|
||||
@@ -21,7 +23,8 @@ use Drupal\entity\Revision\RevisionableContentEntityBase;
|
||||
* ),
|
||||
* handlers = {
|
||||
* "storage" = "\Drupal\Core\Entity\Sql\SqlContentEntityStorage",
|
||||
* "access" = "\Drupal\Core\Entity\EntityAccessControlHandler",
|
||||
* "access" = "\Drupal\entity\EntityAccessControlHandler",
|
||||
* "query_access" = "\Drupal\entity\QueryAccess\QueryAccessHandler",
|
||||
* "permission_provider" = "\Drupal\entity\EntityPermissionProvider",
|
||||
* "form" = {
|
||||
* "add" = "\Drupal\entity\Form\RevisionableContentEntityForm",
|
||||
@@ -37,6 +40,7 @@ use Drupal\entity\Revision\RevisionableContentEntityBase;
|
||||
* "collection" = "\Drupal\entity\Menu\EntityCollectionLocalActionProvider",
|
||||
* },
|
||||
* "list_builder" = "\Drupal\Core\Entity\EntityListBuilder",
|
||||
* "views_data" = "\Drupal\views\EntityViewsData",
|
||||
* },
|
||||
* base_table = "entity_test_enhanced",
|
||||
* data_table = "entity_test_enhanced_field_data",
|
||||
@@ -51,6 +55,8 @@ use Drupal\entity\Revision\RevisionableContentEntityBase;
|
||||
* "bundle" = "type",
|
||||
* "revision" = "vid",
|
||||
* "langcode" = "langcode",
|
||||
* "label" = "name",
|
||||
* "published" = "status",
|
||||
* },
|
||||
* links = {
|
||||
* "add-page" = "/entity_test_enhanced/add",
|
||||
@@ -63,16 +69,18 @@ use Drupal\entity\Revision\RevisionableContentEntityBase;
|
||||
* "revision-revert-form" = "/entity_test_enhanced/{entity_test_enhanced}/revisions/{entity_test_enhanced_revision}/revert",
|
||||
* "version-history" = "/entity_test_enhanced/{entity_test_enhanced}/revisions",
|
||||
* },
|
||||
* bundle_entity_type = "entity_test_enhanced_bundle",
|
||||
* )
|
||||
*/
|
||||
class EnhancedEntity extends RevisionableContentEntityBase {
|
||||
class EnhancedEntity extends RevisionableContentEntityBase implements EntityPublishedInterface {
|
||||
|
||||
use EntityPublishedTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
$fields += static::publishedBaseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['name'] = BaseFieldDefinition::create('string')
|
||||
->setLabel('Name')
|
||||
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity_module_test\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
|
||||
use Drupal\Core\Entity\EntityDescriptionInterface;
|
||||
use Drupal\Core\Entity\RevisionableEntityBundleInterface;
|
||||
|
||||
/**
|
||||
* Provides bundles for the test entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "entity_test_enhanced_bundle",
|
||||
* label = @Translation("Entity test with enhancments - Bundle"),
|
||||
* admin_permission = "administer entity_test_enhanced",
|
||||
* config_prefix = "entity_test_enhanced_bundle",
|
||||
* bundle_of = "entity_test_enhanced",
|
||||
* handlers = {
|
||||
* "access" = "\Drupal\entity\BundleEntityAccessControlHandler",
|
||||
* },
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "label",
|
||||
* "description"
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class EnhancedEntityBundle extends ConfigEntityBundleBase implements EntityDescriptionInterface, RevisionableEntityBundleInterface {
|
||||
|
||||
/**
|
||||
* The bundle ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The bundle label.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The bundle description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* Should new entities of this bundle have a new revision by default.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $new_revision = FALSE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDescription($description) {
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function shouldCreateNewRevision() {
|
||||
return $this->new_revision;
|
||||
}
|
||||
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity_module_test\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedTrait;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\entity\Revision\RevisionableContentEntityBase;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Provides a test entity which uses all the capabilities of entity module.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "entity_test_enhanced_with_owner",
|
||||
* label = @Translation("Enhanced entity with owner"),
|
||||
* label_collection = @Translation("Enhanced entities with owner"),
|
||||
* label_singular = @Translation("enhanced entity with owner"),
|
||||
* label_plural = @Translation("enhanced entities with owner"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count enhanced entity with owner",
|
||||
* plural = "@count enhanced entities with owner",
|
||||
* ),
|
||||
* handlers = {
|
||||
* "storage" = "\Drupal\Core\Entity\Sql\SqlContentEntityStorage",
|
||||
* "access" = "\Drupal\entity\UncacheableEntityAccessControlHandler",
|
||||
* "query_access" = "\Drupal\entity\QueryAccess\UncacheableQueryAccessHandler",
|
||||
* "permission_provider" = "\Drupal\entity\UncacheableEntityPermissionProvider",
|
||||
* "form" = {
|
||||
* "add" = "\Drupal\entity\Form\RevisionableContentEntityForm",
|
||||
* "edit" = "\Drupal\entity\Form\RevisionableContentEntityForm",
|
||||
* "delete" = "\Drupal\Core\Entity\EntityDeleteForm",
|
||||
* },
|
||||
* "route_provider" = {
|
||||
* "html" = "\Drupal\entity\Routing\DefaultHtmlRouteProvider",
|
||||
* "revision" = "\Drupal\entity\Routing\RevisionRouteProvider",
|
||||
* "delete-multiple" = "\Drupal\entity\Routing\DeleteMultipleRouteProvider",
|
||||
* },
|
||||
* "local_action_provider" = {
|
||||
* "collection" = "\Drupal\entity\Menu\EntityCollectionLocalActionProvider",
|
||||
* },
|
||||
* "list_builder" = "\Drupal\Core\Entity\EntityListBuilder",
|
||||
* "views_data" = "\Drupal\views\EntityViewsData",
|
||||
* },
|
||||
* base_table = "entity_test_enhanced_with_owner",
|
||||
* data_table = "entity_test_enhanced_with_owner_field_data",
|
||||
* revision_table = "entity_test_enhanced_with_owner_revision",
|
||||
* revision_data_table = "entity_test_enhanced_with_owner_field_revision",
|
||||
* translatable = TRUE,
|
||||
* revisionable = TRUE,
|
||||
* admin_permission = "administer entity_test_enhanced_with_owner",
|
||||
* permission_granularity = "bundle",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "bundle" = "type",
|
||||
* "revision" = "vid",
|
||||
* "langcode" = "langcode",
|
||||
* "label" = "name",
|
||||
* "uid" = "user_id",
|
||||
* "published" = "status",
|
||||
* },
|
||||
* links = {
|
||||
* "add-page" = "/entity_test_enhanced_with_owner/add",
|
||||
* "add-form" = "/entity_test_enhanced_with_owner/add/{type}",
|
||||
* "edit-form" = "/entity_test_enhanced_with_owner/{entity_test_enhanced_with_owner}/edit",
|
||||
* "canonical" = "/entity_test_enhanced_with_owner/{entity_test_enhanced_with_owner}",
|
||||
* "collection" = "/entity_test_enhanced_with_owner",
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class EnhancedEntityWithOwner extends RevisionableContentEntityBase implements EntityOwnerInterface, EntityPublishedInterface {
|
||||
|
||||
use EntityPublishedTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOwner() {
|
||||
return $this->get('user_id')->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOwnerId() {
|
||||
return $this->get('user_id')->target_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOwnerId($uid) {
|
||||
$this->set('user_id', $uid);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setOwner(UserInterface $account) {
|
||||
$this->set('user_id', $account->id());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
$fields += static::publishedBaseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['name'] = BaseFieldDefinition::create('string')
|
||||
->setLabel('Name')
|
||||
->setRevisionable(TRUE)
|
||||
->setDisplayOptions('view', [
|
||||
'label' => 'hidden',
|
||||
'type' => 'string',
|
||||
'weight' => -5,
|
||||
]);
|
||||
|
||||
$fields['user_id'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('User ID'))
|
||||
->setDescription(t('The ID of the associated user.'))
|
||||
->setSetting('target_type', 'user')
|
||||
->setSetting('handler', 'default')
|
||||
// Default EntityTest entities to have the root user as the owner, to
|
||||
// simplify testing.
|
||||
->setDefaultValue([0 => ['target_id' => 1]])
|
||||
->setTranslatable(TRUE)
|
||||
->setDisplayOptions('form', [
|
||||
'type' => 'entity_reference_autocomplete',
|
||||
'weight' => -1,
|
||||
'settings' => [
|
||||
'match_operator' => 'CONTAINS',
|
||||
'size' => '60',
|
||||
'placeholder' => '',
|
||||
],
|
||||
]);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity_module_test\EventSubscriber;
|
||||
|
||||
use Drupal\entity\QueryAccess\ConditionGroup;
|
||||
use Drupal\entity\QueryAccess\QueryAccessEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class QueryAccessSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
return [
|
||||
'entity.query_access.entity_test_enhanced' => 'onQueryAccess',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the access conditions based on the current user.
|
||||
*
|
||||
* This is just a convenient example for testing. A real subscriber would
|
||||
* ignore the account and extend the conditions to cover additional factors,
|
||||
* such as a custom entity field.
|
||||
*
|
||||
* @param \Drupal\entity\QueryAccess\QueryAccessEvent $event
|
||||
* The event.
|
||||
*/
|
||||
public function onQueryAccess(QueryAccessEvent $event) {
|
||||
$conditions = $event->getConditions();
|
||||
$email = $event->getAccount()->getEmail();
|
||||
|
||||
if ($email == 'user1@example.com') {
|
||||
// This user should not have access to any entities.
|
||||
$conditions->alwaysFalse();
|
||||
}
|
||||
elseif ($email == 'user2@example.com') {
|
||||
// This user should have access to entities with the IDs 1, 2, and 3.
|
||||
// The query access handler might have already set ->alwaysFalse()
|
||||
// due to the user not having any other access, so we make sure
|
||||
// to undo it with $conditions->alwaysFalse(TRUE).
|
||||
$conditions->alwaysFalse(FALSE);
|
||||
$conditions->addCondition('id', ['1', '2', '3']);
|
||||
}
|
||||
elseif ($email == 'user3@example.com') {
|
||||
// This user should only have access to entities assigned to "marketing",
|
||||
// or unassigned entities.
|
||||
$conditions->alwaysFalse(FALSE);
|
||||
$conditions->addCondition((new ConditionGroup('OR'))
|
||||
->addCondition('assigned', NULL, 'IS NULL')
|
||||
// Confirm that explicitly specifying the property name works.
|
||||
->addCondition('assigned.value', 'marketing')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-6
@@ -3,7 +3,6 @@
|
||||
namespace Drupal\Tests\entity\Functional;
|
||||
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntity;
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
|
||||
use Drupal\Tests\block\Traits\BlockCreationTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
@@ -33,11 +32,6 @@ class CollectionRouteAccessTest extends BrowserTestBase {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
EnhancedEntityBundle::create([
|
||||
'id' => 'default',
|
||||
'label' => 'Default',
|
||||
])->save();
|
||||
|
||||
$this->placeBlock('local_tasks_block');
|
||||
$this->placeBlock('system_breadcrumb_block');
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Drupal\Tests\entity\Functional;
|
||||
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntity;
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
@@ -35,10 +34,6 @@ class DeleteMultipleFormTest extends BrowserTestBase {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
EnhancedEntityBundle::create([
|
||||
'id' => 'default',
|
||||
'label' => 'Default',
|
||||
])->save();
|
||||
$this->account = $this->drupalCreateUser(['administer entity_test_enhanced']);
|
||||
$this->drupalLogin($this->account);
|
||||
}
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ class EntityLocalActionTest extends BrowserTestBase {
|
||||
*/
|
||||
public function testCollectionLocalAction() {
|
||||
$this->drupalGet('/entity_test_enhanced');
|
||||
$this->assertSession()->linkByHrefExists('/entity_test_enhanced/add');
|
||||
$this->assertSession()->linkByHrefExists('/entity_test_enhanced/add?destination=/entity_test_enhanced');
|
||||
$this->assertSession()->linkExists('Add enhanced entity');
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Drupal\Tests\entity\Functional;
|
||||
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntity;
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
|
||||
use Drupal\Tests\block\Traits\BlockCreationTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
@@ -40,11 +39,6 @@ class RevisionRouteAccessTest extends BrowserTestBase {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
EnhancedEntityBundle::create([
|
||||
'id' => 'default',
|
||||
'label' => 'Default',
|
||||
])->save();
|
||||
|
||||
$this->placeBlock('local_tasks_block');
|
||||
$this->placeBlock('system_breadcrumb_block');
|
||||
|
||||
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Kernel;
|
||||
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the bundle entity access control handler.
|
||||
*
|
||||
* @group entity
|
||||
*/
|
||||
class BundleEntityAccessControlHandlerTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity',
|
||||
'entity_module_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_enhanced');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the "view label" access checking.
|
||||
*/
|
||||
public function testAccess() {
|
||||
$bundle = EnhancedEntityBundle::create([
|
||||
'id' => 'default',
|
||||
'label' => 'Default',
|
||||
]);
|
||||
$bundle->save();
|
||||
|
||||
// The default user has no permissions related to entity_test_enhanced.
|
||||
$this->assertFalse($bundle->access('view label'));
|
||||
|
||||
$permissions = [
|
||||
'administer entity_test_enhanced',
|
||||
'view entity_test_enhanced',
|
||||
'view default entity_test_enhanced',
|
||||
];
|
||||
foreach ($permissions as $permission) {
|
||||
$account = $this->createUser([], [$permission]);
|
||||
$this->assertTrue($bundle->access('view label', $account));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ namespace Drupal\Tests\entity\Kernel;
|
||||
|
||||
use Drupal\entity\Plugin\Action\DeleteAction;
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntity;
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
|
||||
use Drupal\system\Entity\Action;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
@@ -40,12 +39,6 @@ class DeleteActionTest extends KernelTestBase {
|
||||
$this->installEntitySchema('entity_test_enhanced');
|
||||
$this->installSchema('system', ['key_value_expire', 'sequences']);
|
||||
|
||||
$bundle = EnhancedEntityBundle::create([
|
||||
'id' => 'default',
|
||||
'label' => 'Default',
|
||||
]);
|
||||
$bundle->save();
|
||||
|
||||
$this->user = User::create([
|
||||
'name' => 'username',
|
||||
'status' => 1,
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Kernel\QueryAccess;
|
||||
|
||||
use Drupal\entity\QueryAccess\Condition;
|
||||
use Drupal\entity\QueryAccess\ConditionGroup;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the condition group class.
|
||||
*
|
||||
* ConditionGroup uses \Drupal\Core\Cache\Cache internally, which makes it
|
||||
* impossible to use a unit test (due to Cache accessing the global container).
|
||||
*
|
||||
* @coversDefaultClass \Drupal\entity\QueryAccess\ConditionGroup
|
||||
* @group entity
|
||||
*/
|
||||
class ConditionGroupTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* ::covers getConjunction
|
||||
* ::covers addCondition
|
||||
* ::covers getConditions
|
||||
* ::covers count.
|
||||
*/
|
||||
public function testGetters() {
|
||||
$condition_group = new ConditionGroup();
|
||||
$condition_group->addCondition('uid', '2');
|
||||
$this->assertEquals('AND', $condition_group->getConjunction());
|
||||
$expected_conditions = [
|
||||
new Condition('uid', '2'),
|
||||
];
|
||||
$this->assertEquals($expected_conditions, $condition_group->getConditions());
|
||||
$this->assertEquals(1, $condition_group->count());
|
||||
$this->assertEquals("uid = '2'", $condition_group->__toString());
|
||||
|
||||
$condition_group = new ConditionGroup('OR');
|
||||
$condition_group->addCondition('type', ['article', 'page']);
|
||||
$condition_group->addCondition('status', '1', '<>');
|
||||
$this->assertEquals('OR', $condition_group->getConjunction());
|
||||
$expected_conditions = [
|
||||
new Condition('type', ['article', 'page']),
|
||||
new Condition('status', '1', '<>'),
|
||||
];
|
||||
$expected_lines = [
|
||||
"(",
|
||||
" type IN ['article', 'page']",
|
||||
" OR",
|
||||
" status <> '1'",
|
||||
")",
|
||||
];
|
||||
$this->assertEquals($expected_conditions, $condition_group->getConditions());
|
||||
$this->assertEquals(2, $condition_group->count());
|
||||
$this->assertEquals(implode("\n", $expected_lines), $condition_group->__toString());
|
||||
|
||||
// Nested condition group with a single condition.
|
||||
$condition_group = new ConditionGroup();
|
||||
$condition_group->addCondition('type', ['article', 'page']);
|
||||
$condition_group->addCondition((new ConditionGroup('AND'))
|
||||
->addCondition('status', '1')
|
||||
);
|
||||
$expected_conditions = [
|
||||
new Condition('type', ['article', 'page']),
|
||||
new Condition('status', '1'),
|
||||
];
|
||||
$expected_lines = [
|
||||
"(",
|
||||
" type IN ['article', 'page']",
|
||||
" AND",
|
||||
" status = '1'",
|
||||
")",
|
||||
];
|
||||
$this->assertEquals($expected_conditions, $condition_group->getConditions());
|
||||
$this->assertEquals('AND', $condition_group->getConjunction());
|
||||
$this->assertEquals(2, $condition_group->count());
|
||||
$this->assertEquals(implode("\n", $expected_lines), $condition_group->__toString());
|
||||
|
||||
// Nested condition group with multiple conditions.
|
||||
$condition_group = new ConditionGroup();
|
||||
$condition_group->addCondition('type', ['article', 'page']);
|
||||
$nested_condition_group = new ConditionGroup('OR');
|
||||
$nested_condition_group->addCondition('uid', '1');
|
||||
$nested_condition_group->addCondition('status', '1');
|
||||
$condition_group->addCondition($nested_condition_group);
|
||||
$expected_conditions = [
|
||||
new Condition('type', ['article', 'page']),
|
||||
$nested_condition_group,
|
||||
];
|
||||
$expected_lines = [
|
||||
"(",
|
||||
" type IN ['article', 'page']",
|
||||
" AND",
|
||||
" (",
|
||||
" uid = '1'",
|
||||
" OR",
|
||||
" status = '1'",
|
||||
" )",
|
||||
")",
|
||||
];
|
||||
$this->assertEquals($expected_conditions, $condition_group->getConditions());
|
||||
$this->assertEquals('AND', $condition_group->getConjunction());
|
||||
$this->assertEquals(2, $condition_group->count());
|
||||
$this->assertEquals(implode("\n", $expected_lines), $condition_group->__toString());
|
||||
}
|
||||
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Kernel\QueryAccess;
|
||||
|
||||
use Drupal\entity\QueryAccess\QueryAccessHandler;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the query access event.
|
||||
*
|
||||
* @group entity
|
||||
*/
|
||||
class QueryAccessEventTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The query access handler.
|
||||
*
|
||||
* @var \Drupal\entity\QueryAccess\QueryAccessHandler
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity',
|
||||
'entity_module_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_enhanced');
|
||||
|
||||
// Create uid: 1 here so that it's skipped in test cases.
|
||||
$admin_user = $this->createUser();
|
||||
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$entity_type = $entity_type_manager->getDefinition('entity_test_enhanced');
|
||||
$this->handler = QueryAccessHandler::createInstance($this->container, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the event.
|
||||
*/
|
||||
public function testEvent() {
|
||||
// By default, the first user should have full access, and the second
|
||||
// user should have no access. The QueryAccessSubscriber flips that.
|
||||
$first_user = $this->createUser(['mail' => 'user1@example.com'], ['administer entity_test_enhanced']);
|
||||
$second_user = $this->createUser(['mail' => 'user2@example.com']);
|
||||
|
||||
$conditions = $this->handler->getConditions('view', $first_user);
|
||||
$this->assertTrue($conditions->isAlwaysFalse());
|
||||
|
||||
$conditions = $this->handler->getConditions('view', $second_user);
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
}
|
||||
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Kernel\QueryAccess;
|
||||
|
||||
use Drupal\entity\QueryAccess\Condition;
|
||||
use Drupal\entity\QueryAccess\QueryAccessHandler;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the query access handler.
|
||||
*
|
||||
* Uses the "entity_test_enhanced" entity type, which has no owner.
|
||||
* UncacheableQueryAccessHandlerTest uses the "entity_test_enhanced_with_owner"
|
||||
* entity type, which has an owner. This ensures both sides (owner and
|
||||
* no owner) are covered.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\entity\QueryAccess\QueryAccessHandler
|
||||
* @group entity
|
||||
*/
|
||||
class QueryAccessHandlerTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The query access handler.
|
||||
*
|
||||
* @var \Drupal\entity\QueryAccess\QueryAccessHandler
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity',
|
||||
'entity_module_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_enhanced');
|
||||
|
||||
// Create uid: 1 here so that it's skipped in test cases.
|
||||
$admin_user = $this->createUser();
|
||||
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$entity_type = $entity_type_manager->getDefinition('entity_test_enhanced');
|
||||
$this->handler = QueryAccessHandler::createInstance($this->container, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConditions
|
||||
*/
|
||||
public function testNoAccess() {
|
||||
foreach (['view', 'update', 'delete'] as $operation) {
|
||||
$user = $this->createUser([], ['access content']);
|
||||
$conditions = $this->handler->getConditions($operation, $user);
|
||||
$this->assertEquals(0, $conditions->count());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertTrue($conditions->isAlwaysFalse());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConditions
|
||||
*/
|
||||
public function testAdmin() {
|
||||
foreach (['view', 'update', 'delete'] as $operation) {
|
||||
$user = $this->createUser([], ['administer entity_test_enhanced']);
|
||||
$conditions = $this->handler->getConditions($operation, $user);
|
||||
$this->assertEquals(0, $conditions->count());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConditions
|
||||
*/
|
||||
public function testView() {
|
||||
// Entity type permission.
|
||||
$user = $this->createUser([], ['view entity_test_enhanced']);
|
||||
$conditions = $this->handler->getConditions('view', $user);
|
||||
$expected_conditions = [
|
||||
new Condition('status', '1'),
|
||||
];
|
||||
$this->assertEquals(1, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
|
||||
// Bundle permission.
|
||||
$user = $this->createUser([], ['view first entity_test_enhanced']);
|
||||
$conditions = $this->handler->getConditions('view', $user);
|
||||
$expected_conditions = [
|
||||
new Condition('type', ['first']),
|
||||
new Condition('status', '1'),
|
||||
];
|
||||
$this->assertEquals('AND', $conditions->getConjunction());
|
||||
$this->assertEquals(2, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConditions
|
||||
*/
|
||||
public function testUpdateDelete() {
|
||||
foreach (['update', 'delete'] as $operation) {
|
||||
// Entity type permission.
|
||||
$user = $this->createUser([], ["$operation entity_test_enhanced"]);
|
||||
$conditions = $this->handler->getConditions($operation, $user);
|
||||
$this->assertEquals(0, $conditions->count());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
|
||||
// Bundle permission.
|
||||
$user = $this->createUser([], [
|
||||
"$operation first entity_test_enhanced",
|
||||
"$operation second entity_test_enhanced",
|
||||
]);
|
||||
$conditions = $this->handler->getConditions($operation, $user);
|
||||
$expected_conditions = [
|
||||
new Condition('type', ['first', 'second']),
|
||||
];
|
||||
$this->assertEquals('OR', $conditions->getConjunction());
|
||||
$this->assertEquals(1, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Kernel\QueryAccess;
|
||||
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntity;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\views\Tests\ViewResultAssertionTrait;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Test query access filtering for EntityQuery and Views.
|
||||
*
|
||||
* @group entity
|
||||
*
|
||||
* @see \Drupal\entity\QueryAccess\QueryAccessHandler
|
||||
* @see \Drupal\entity\QueryAccess\EntityQueryAlter
|
||||
* @see \Drupal\entity\QueryAccess\ViewsQueryAlter
|
||||
*/
|
||||
class QueryAccessTest extends EntityKernelTestBase {
|
||||
|
||||
use ViewResultAssertionTrait;
|
||||
|
||||
/**
|
||||
* The test entities.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityInterface[]
|
||||
*/
|
||||
protected $entities;
|
||||
|
||||
/**
|
||||
* The entity_test_enhanced storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity',
|
||||
'entity_module_test',
|
||||
'user',
|
||||
'views',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_enhanced');
|
||||
$this->installConfig(['entity_module_test']);
|
||||
|
||||
// Create uid: 1 here so that it's skipped in test cases.
|
||||
$admin_user = $this->createUser();
|
||||
|
||||
$first_entity = EnhancedEntity::create([
|
||||
'type' => 'first',
|
||||
'label' => 'First',
|
||||
'status' => 1,
|
||||
]);
|
||||
$first_entity->save();
|
||||
|
||||
$first_entity->set('name', 'First!');
|
||||
$first_entity->set('status', 0);
|
||||
$first_entity->setNewRevision(TRUE);
|
||||
$first_entity->save();
|
||||
|
||||
$second_entity = EnhancedEntity::create([
|
||||
'type' => 'first',
|
||||
'label' => 'Second',
|
||||
'status' => 0,
|
||||
]);
|
||||
$second_entity->save();
|
||||
|
||||
$second_entity->set('name', 'Second!');
|
||||
$second_entity->set('status', 1);
|
||||
$second_entity->setNewRevision(TRUE);
|
||||
$second_entity->save();
|
||||
|
||||
$third_entity = EnhancedEntity::create([
|
||||
'type' => 'second',
|
||||
'label' => 'Third',
|
||||
'status' => 1,
|
||||
]);
|
||||
$third_entity->save();
|
||||
|
||||
$third_entity->set('name', 'Third!');
|
||||
$third_entity->setNewRevision(TRUE);
|
||||
$third_entity->save();
|
||||
|
||||
$this->entities = [$first_entity, $second_entity, $third_entity];
|
||||
$this->storage = \Drupal::entityTypeManager()->getStorage('entity_test_enhanced');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests EntityQuery filtering.
|
||||
*/
|
||||
public function testEntityQuery() {
|
||||
// Admin permission, full access.
|
||||
$admin_user = $this->createUser([], ['administer entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($admin_user);
|
||||
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[0]->id(),
|
||||
$this->entities[1]->id(),
|
||||
$this->entities[2]->id(),
|
||||
], array_values($result));
|
||||
|
||||
// No view permissions, no access.
|
||||
$user = $this->createUser([], ['access content']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->execute();
|
||||
$this->assertEmpty($result);
|
||||
|
||||
// View (published-only).
|
||||
$user = $this->createUser([], ['view entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[1]->id(),
|
||||
$this->entities[2]->id(),
|
||||
], array_values($result));
|
||||
|
||||
// View $bundle (published-only).
|
||||
$user = $this->createUser([], ['view first entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[1]->id(),
|
||||
], array_values($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests EntityQuery filtering when all revisions are queried.
|
||||
*/
|
||||
public function testEntityQueryWithRevisions() {
|
||||
// Admin permission, full access.
|
||||
$admin_user = $this->createUser([], ['administer entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($admin_user);
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'1' => $this->entities[0]->id(),
|
||||
'2' => $this->entities[0]->id(),
|
||||
'3' => $this->entities[1]->id(),
|
||||
'4' => $this->entities[1]->id(),
|
||||
'5' => $this->entities[2]->id(),
|
||||
'6' => $this->entities[2]->id(),
|
||||
], $result);
|
||||
|
||||
// No view permissions, no access.
|
||||
$user = $this->createUser([], ['access content']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->execute();
|
||||
$this->assertEmpty($result);
|
||||
|
||||
// View (published-only).
|
||||
$user = $this->createUser([], ['view entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'1' => $this->entities[0]->id(),
|
||||
'4' => $this->entities[1]->id(),
|
||||
'5' => $this->entities[2]->id(),
|
||||
'6' => $this->entities[2]->id(),
|
||||
], $result);
|
||||
|
||||
// View $bundle (published-only).
|
||||
$user = $this->createUser([], ['view first entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'1' => $this->entities[0]->id(),
|
||||
'4' => $this->entities[1]->id(),
|
||||
], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Views filtering.
|
||||
*/
|
||||
public function testViews() {
|
||||
// Admin permission, full access.
|
||||
$admin_user = $this->createUser([], ['administer entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($admin_user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[0]->id()],
|
||||
['id' => $this->entities[1]->id()],
|
||||
['id' => $this->entities[2]->id()],
|
||||
], ['id' => 'id']);
|
||||
|
||||
// No view permissions, no access.
|
||||
$user = $this->createUser([], ['access content']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, []);
|
||||
|
||||
// View (published-only).
|
||||
$user = $this->createUser([], ['view entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[1]->id()],
|
||||
['id' => $this->entities[2]->id()],
|
||||
], ['id' => 'id']);
|
||||
|
||||
// View $bundle (published-only).
|
||||
$user = $this->createUser([], ['view first entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[1]->id()],
|
||||
], ['id' => 'id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Views filtering when all revisions are queried.
|
||||
*/
|
||||
public function testViewsWithRevisions() {
|
||||
// Admin permission, full access.
|
||||
$admin_user = $this->createUser([], ['administer entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($admin_user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '1', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '2', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '3', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '4', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '5', 'id' => $this->entities[2]->id()],
|
||||
['vid' => '6', 'id' => $this->entities[2]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
|
||||
// No view permissions, no access.
|
||||
$user = $this->createUser([], ['access content']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, []);
|
||||
|
||||
// View (published-only).
|
||||
$user = $this->createUser([], ['view entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '1', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '4', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '5', 'id' => $this->entities[2]->id()],
|
||||
['vid' => '6', 'id' => $this->entities[2]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
|
||||
// View $bundle (published-only).
|
||||
$user = $this->createUser([], ['view first entity_test_enhanced']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '1', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '4', 'id' => $this->entities[1]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests filtering based on a configurable field.
|
||||
*
|
||||
* QueryAccessSubscriber adds a condition that ensures that the field value
|
||||
* is either empty or matches "marketing".
|
||||
*
|
||||
* @see \Drupal\entity_module_test\EventSubscriber\QueryAccessSubscriber
|
||||
*/
|
||||
public function testConfigurableField() {
|
||||
$this->entities[0]->set('assigned', 'marketing');
|
||||
$this->entities[0]->save();
|
||||
// The field is case sensitive, so the third entity should be ignored.
|
||||
$this->entities[2]->set('assigned', 'MarKeTing');
|
||||
$this->entities[2]->save();
|
||||
$user = $this->createUser([
|
||||
'mail' => 'user3@example.com',
|
||||
], ['access content']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
// EntityQuery.
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[0]->id(),
|
||||
$this->entities[1]->id(),
|
||||
], array_values($result));
|
||||
|
||||
// EntityQuery with revisions.
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'1' => $this->entities[0]->id(),
|
||||
'2' => $this->entities[0]->id(),
|
||||
'3' => $this->entities[1]->id(),
|
||||
'4' => $this->entities[1]->id(),
|
||||
'5' => $this->entities[2]->id(),
|
||||
], $result);
|
||||
|
||||
// View.
|
||||
$view = Views::getView('entity_test_enhanced');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[0]->id()],
|
||||
['id' => $this->entities[1]->id()],
|
||||
], ['id' => 'id']);
|
||||
|
||||
// View with revisions.
|
||||
$view = Views::getView('entity_test_enhanced_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '1', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '2', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '3', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '4', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '5', 'id' => $this->entities[2]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
}
|
||||
|
||||
}
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Kernel\QueryAccess;
|
||||
|
||||
use Drupal\entity\QueryAccess\Condition;
|
||||
use Drupal\entity\QueryAccess\ConditionGroup;
|
||||
use Drupal\entity\QueryAccess\UncacheableQueryAccessHandler;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the uncacheable query access handler.
|
||||
*
|
||||
* Uses the "entity_test_enhanced_with_owner" entity type, which has an owner.
|
||||
* QueryAccessHandlerTest uses the "entity_test_enhanced" entity type, which
|
||||
* has no owner. This ensures both sides (owner and no owner) are covered.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\entity\QueryAccess\UncacheableQueryAccessHandler
|
||||
* @group entity
|
||||
*/
|
||||
class UncacheableQueryAccessHandlerTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The query access handler.
|
||||
*
|
||||
* @var \Drupal\entity\QueryAccess\UncacheableQueryAccessHandler
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity',
|
||||
'entity_module_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_enhanced_with_owner');
|
||||
|
||||
// Create uid: 1 here so that it's skipped in test cases.
|
||||
$admin_user = $this->createUser();
|
||||
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$entity_type = $entity_type_manager->getDefinition('entity_test_enhanced_with_owner');
|
||||
$this->handler = UncacheableQueryAccessHandler::createInstance($this->container, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConditions
|
||||
*/
|
||||
public function testNoAccess() {
|
||||
foreach (['view', 'update', 'delete'] as $operation) {
|
||||
$user = $this->createUser([], ['access content']);
|
||||
$conditions = $this->handler->getConditions($operation, $user);
|
||||
$this->assertEquals(0, $conditions->count());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertTrue($conditions->isAlwaysFalse());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConditions
|
||||
*/
|
||||
public function testAdmin() {
|
||||
foreach (['view', 'update', 'delete'] as $operation) {
|
||||
$user = $this->createUser([], ['administer entity_test_enhanced_with_owner']);
|
||||
$conditions = $this->handler->getConditions($operation, $user);
|
||||
$this->assertEquals(0, $conditions->count());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConditions
|
||||
*/
|
||||
public function testView() {
|
||||
// Any permission.
|
||||
$user = $this->createUser([], ['view any entity_test_enhanced_with_owner']);
|
||||
$conditions = $this->handler->getConditions('view', $user);
|
||||
$expected_conditions = [
|
||||
new Condition('status', '1'),
|
||||
];
|
||||
$this->assertEquals(1, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
|
||||
// Own permission.
|
||||
$user = $this->createUser([], ['view own entity_test_enhanced_with_owner']);
|
||||
$conditions = $this->handler->getConditions('view', $user);
|
||||
$expected_conditions = [
|
||||
new Condition('user_id', $user->id()),
|
||||
new Condition('status', '1'),
|
||||
];
|
||||
$this->assertEquals('AND', $conditions->getConjunction());
|
||||
$this->assertEquals(2, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user', 'user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
|
||||
// Any permission for the first bundle, own permission for the second.
|
||||
$user = $this->createUser([], [
|
||||
'view any first entity_test_enhanced_with_owner',
|
||||
'view own second entity_test_enhanced_with_owner',
|
||||
]);
|
||||
$conditions = $this->handler->getConditions('view', $user);
|
||||
$expected_conditions = [
|
||||
(new ConditionGroup('OR'))
|
||||
->addCacheContexts(['user', 'user.permissions'])
|
||||
->addCondition('type', ['first'])
|
||||
->addCondition((new ConditionGroup('AND'))
|
||||
->addCondition('user_id', $user->id())
|
||||
->addCondition('type', ['second'])
|
||||
),
|
||||
new Condition('status', '1'),
|
||||
];
|
||||
$this->assertEquals('AND', $conditions->getConjunction());
|
||||
$this->assertEquals(2, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user', 'user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
|
||||
// View own unpublished permission.
|
||||
$user = $this->createUser([], ['view own unpublished entity_test_enhanced_with_owner']);
|
||||
$conditions = $this->handler->buildConditions('view', $user);
|
||||
$expected_conditions = [
|
||||
new Condition('user_id', $user->id()),
|
||||
new Condition('status', '0'),
|
||||
];
|
||||
$this->assertEquals(2, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
|
||||
// Both view any and view own unpublished permissions.
|
||||
$user = $this->createUser([], [
|
||||
'view any entity_test_enhanced_with_owner',
|
||||
'view own unpublished entity_test_enhanced_with_owner',
|
||||
]);
|
||||
$conditions = $this->handler->buildConditions('view', $user);
|
||||
$expected_conditions = [
|
||||
new Condition('status', '1'),
|
||||
(new ConditionGroup('AND'))
|
||||
->addCondition('user_id', $user->id())
|
||||
->addCondition('status', '0')
|
||||
->addCacheContexts(['user']),
|
||||
];
|
||||
$this->assertEquals(2, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user', 'user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getConditions
|
||||
*/
|
||||
public function testUpdateDelete() {
|
||||
foreach (['update', 'delete'] as $operation) {
|
||||
// Any permission.
|
||||
$user = $this->createUser([], ["$operation any entity_test_enhanced_with_owner"]);
|
||||
$conditions = $this->handler->getConditions($operation, $user);
|
||||
$this->assertEquals(0, $conditions->count());
|
||||
$this->assertEquals(['user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
|
||||
// Own permission.
|
||||
$user = $this->createUser([], ["$operation own entity_test_enhanced_with_owner"]);
|
||||
$conditions = $this->handler->getConditions($operation, $user);
|
||||
$expected_conditions = [
|
||||
new Condition('user_id', $user->id()),
|
||||
];
|
||||
$this->assertEquals(1, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user', 'user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
|
||||
// Any permission for the first bundle, own permission for the second.
|
||||
$user = $this->createUser([], [
|
||||
"$operation any first entity_test_enhanced_with_owner",
|
||||
"$operation own second entity_test_enhanced_with_owner",
|
||||
]);
|
||||
$conditions = $this->handler->getConditions($operation, $user);
|
||||
$expected_conditions = [
|
||||
new Condition('type', ['first']),
|
||||
(new ConditionGroup('AND'))
|
||||
->addCondition('user_id', $user->id())
|
||||
->addCondition('type', ['second']),
|
||||
];
|
||||
$this->assertEquals('OR', $conditions->getConjunction());
|
||||
$this->assertEquals(2, $conditions->count());
|
||||
$this->assertEquals($expected_conditions, $conditions->getConditions());
|
||||
$this->assertEquals(['user', 'user.permissions'], $conditions->getCacheContexts());
|
||||
$this->assertFalse($conditions->isAlwaysFalse());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+511
@@ -0,0 +1,511 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Kernel\QueryAccess;
|
||||
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntityWithOwner;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\views\Tests\ViewResultAssertionTrait;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Test uncacheable query access filtering for EntityQuery and Views.
|
||||
*
|
||||
* @group entity
|
||||
*
|
||||
* @see \Drupal\entity\QueryAccess\UncacheableQueryAccessHandler
|
||||
* @see \Drupal\entity\QueryAccess\EntityQueryAlter
|
||||
* @see \Drupal\entity\QueryAccess\ViewsQueryAlter
|
||||
*/
|
||||
class UncacheableQueryAccessTest extends EntityKernelTestBase {
|
||||
|
||||
use ViewResultAssertionTrait;
|
||||
|
||||
/**
|
||||
* The test entities.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityInterface[]
|
||||
*/
|
||||
protected $entities;
|
||||
|
||||
/**
|
||||
* The entity_test_enhanced storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity',
|
||||
'entity_module_test',
|
||||
'user',
|
||||
'views',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_enhanced_with_owner');
|
||||
$this->installConfig(['entity_module_test']);
|
||||
|
||||
// Create uid: 1 here so that it's skipped in test cases.
|
||||
$admin_user = $this->createUser();
|
||||
|
||||
$first_entity = EnhancedEntityWithOwner::create([
|
||||
'type' => 'first',
|
||||
'name' => 'First',
|
||||
'status' => 1,
|
||||
]);
|
||||
$first_entity->save();
|
||||
|
||||
$first_entity->set('name', 'First!');
|
||||
$first_entity->set('status', 0);
|
||||
$first_entity->setNewRevision(TRUE);
|
||||
$first_entity->save();
|
||||
|
||||
$second_entity = EnhancedEntityWithOwner::create([
|
||||
'type' => 'first',
|
||||
'name' => 'Second',
|
||||
'status' => 0,
|
||||
]);
|
||||
$second_entity->save();
|
||||
|
||||
$second_entity->set('name', 'Second!');
|
||||
$second_entity->set('status', 1);
|
||||
$second_entity->setNewRevision(TRUE);
|
||||
$second_entity->save();
|
||||
|
||||
$third_entity = EnhancedEntityWithOwner::create([
|
||||
'type' => 'second',
|
||||
'name' => 'Third',
|
||||
'status' => 1,
|
||||
]);
|
||||
$third_entity->save();
|
||||
|
||||
$third_entity->set('name', 'Third!');
|
||||
$third_entity->setNewRevision(TRUE);
|
||||
$third_entity->save();
|
||||
|
||||
$this->entities = [$first_entity, $second_entity, $third_entity];
|
||||
$this->storage = \Drupal::entityTypeManager()->getStorage('entity_test_enhanced_with_owner');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests EntityQuery filtering.
|
||||
*/
|
||||
public function testEntityQuery() {
|
||||
// Admin permission, full access.
|
||||
$admin_user = $this->createUser([], ['administer entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($admin_user);
|
||||
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[0]->id(),
|
||||
$this->entities[1]->id(),
|
||||
$this->entities[2]->id(),
|
||||
], array_values($result));
|
||||
|
||||
// No view permissions, no access.
|
||||
$user = $this->createUser([], ['access content']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->execute();
|
||||
$this->assertEmpty($result);
|
||||
|
||||
// View own (published-only).
|
||||
$user = $this->createUser([], ['view own entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[1]->id(),
|
||||
], array_values($result));
|
||||
|
||||
// View any (published-only).
|
||||
$user = $this->createUser([], ['view any entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[1]->id(),
|
||||
$this->entities[2]->id(),
|
||||
], array_values($result));
|
||||
|
||||
// View own unpublished.
|
||||
$user = $this->createUser([], ['view own unpublished entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[0]->id(),
|
||||
], array_values($result));
|
||||
|
||||
// View own unpublished + view any (published-only).
|
||||
$user = $this->createUser([], [
|
||||
'view own unpublished entity_test_enhanced_with_owner',
|
||||
'view any entity_test_enhanced_with_owner',
|
||||
]);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[0]->id(),
|
||||
$this->entities[1]->id(),
|
||||
$this->entities[2]->id(),
|
||||
], array_values($result));
|
||||
|
||||
// View own $first_bundle + View any $second_bundle.
|
||||
$user = $this->createUser([], [
|
||||
'view own first entity_test_enhanced_with_owner',
|
||||
'view any second entity_test_enhanced_with_owner',
|
||||
]);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$result = $this->storage->getQuery()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
$this->entities[1]->id(),
|
||||
$this->entities[2]->id(),
|
||||
], array_values($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests EntityQuery filtering when all revisions are queried.
|
||||
*/
|
||||
public function testEntityQueryWithRevisions() {
|
||||
// Admin permission, full access.
|
||||
$admin_user = $this->createUser([], ['administer entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($admin_user);
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'1' => $this->entities[0]->id(),
|
||||
'2' => $this->entities[0]->id(),
|
||||
'3' => $this->entities[1]->id(),
|
||||
'4' => $this->entities[1]->id(),
|
||||
'5' => $this->entities[2]->id(),
|
||||
'6' => $this->entities[2]->id(),
|
||||
], $result);
|
||||
|
||||
// No view permissions, no access.
|
||||
$user = $this->createUser([], ['access content']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->execute();
|
||||
$this->assertEmpty($result);
|
||||
|
||||
// View own (published-only).
|
||||
$user = $this->createUser([], ['view own entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
// The user_id field is not revisionable, which means that updating it
|
||||
// will modify both revisions for each entity.
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'1' => $this->entities[0]->id(),
|
||||
'4' => $this->entities[1]->id(),
|
||||
], $result);
|
||||
|
||||
// View any (published-only).
|
||||
$user = $this->createUser([], ['view any entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'1' => $this->entities[0]->id(),
|
||||
'4' => $this->entities[1]->id(),
|
||||
'5' => $this->entities[2]->id(),
|
||||
'6' => $this->entities[2]->id(),
|
||||
], $result);
|
||||
|
||||
// View own unpublished.
|
||||
$user = $this->createUser([], ['view own unpublished entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'2' => $this->entities[0]->id(),
|
||||
'3' => $this->entities[1]->id(),
|
||||
], $result);
|
||||
|
||||
// View own unpublished + view any (published-only).
|
||||
$user = $this->createUser([], [
|
||||
'view own unpublished entity_test_enhanced_with_owner',
|
||||
'view any entity_test_enhanced_with_owner',
|
||||
]);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'1' => $this->entities[0]->id(),
|
||||
'2' => $this->entities[0]->id(),
|
||||
'4' => $this->entities[1]->id(),
|
||||
'5' => $this->entities[2]->id(),
|
||||
'6' => $this->entities[2]->id(),
|
||||
], $result);
|
||||
|
||||
// View own $first_bundle + View any $second_bundle.
|
||||
$user = $this->createUser([], [
|
||||
'view own first entity_test_enhanced_with_owner',
|
||||
'view any second entity_test_enhanced_with_owner',
|
||||
]);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$result = $this->storage->getQuery()->allRevisions()->sort('id')->execute();
|
||||
$this->assertEquals([
|
||||
'4' => $this->entities[1]->id(),
|
||||
'5' => $this->entities[2]->id(),
|
||||
'6' => $this->entities[2]->id(),
|
||||
], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Views filtering.
|
||||
*/
|
||||
public function testViews() {
|
||||
// Admin permission, full access.
|
||||
$admin_user = $this->createUser([], ['administer entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($admin_user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[0]->id()],
|
||||
['id' => $this->entities[1]->id()],
|
||||
['id' => $this->entities[2]->id()],
|
||||
], ['id' => 'id']);
|
||||
|
||||
// No view permissions, no access.
|
||||
$user = $this->createUser([], ['access content']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, []);
|
||||
|
||||
// View own (published-only).
|
||||
$user = $this->createUser([], ['view own entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[1]->id()],
|
||||
], ['id' => 'id']);
|
||||
|
||||
// View any (published-only).
|
||||
$user = $this->createUser([], ['view any entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[1]->id()],
|
||||
['id' => $this->entities[2]->id()],
|
||||
], ['id' => 'id']);
|
||||
|
||||
// View own unpublished.
|
||||
$user = $this->createUser([], ['view own unpublished entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[0]->id()],
|
||||
], ['id' => 'id']);
|
||||
|
||||
// View own unpublished + view any (published-only).
|
||||
$user = $this->createUser([], [
|
||||
'view own unpublished entity_test_enhanced_with_owner',
|
||||
'view any entity_test_enhanced_with_owner',
|
||||
]);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[0]->id()],
|
||||
['id' => $this->entities[1]->id()],
|
||||
['id' => $this->entities[2]->id()],
|
||||
], ['id' => 'id']);
|
||||
|
||||
// View own $first_bundle + View any $second_bundle.
|
||||
$user = $this->createUser([], [
|
||||
'view own first entity_test_enhanced_with_owner',
|
||||
'view any second entity_test_enhanced_with_owner',
|
||||
]);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['id' => $this->entities[1]->id()],
|
||||
['id' => $this->entities[2]->id()],
|
||||
], ['id' => 'id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Views filtering when all revisions are queried.
|
||||
*/
|
||||
public function testViewsWithRevisions() {
|
||||
// Admin permission, full access.
|
||||
$admin_user = $this->createUser([], ['administer entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($admin_user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '1', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '2', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '3', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '4', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '5', 'id' => $this->entities[2]->id()],
|
||||
['vid' => '6', 'id' => $this->entities[2]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
|
||||
// No view permissions, no access.
|
||||
$user = $this->createUser([], ['access content']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, []);
|
||||
|
||||
// View own (published-only).
|
||||
$user = $this->createUser([], ['view own entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '1', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '4', 'id' => $this->entities[1]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
|
||||
// View any (published-only).
|
||||
$user = $this->createUser([], ['view any entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '1', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '4', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '5', 'id' => $this->entities[2]->id()],
|
||||
['vid' => '6', 'id' => $this->entities[2]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
|
||||
// View own unpublished.
|
||||
$user = $this->createUser([], ['view own unpublished entity_test_enhanced_with_owner']);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '2', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '3', 'id' => $this->entities[1]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
|
||||
// View own unpublished + view any (published-only).
|
||||
$user = $this->createUser([], [
|
||||
'view own unpublished entity_test_enhanced_with_owner',
|
||||
'view any entity_test_enhanced_with_owner',
|
||||
]);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[0]->set('user_id', $user->id());
|
||||
$this->entities[0]->save();
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '1', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '2', 'id' => $this->entities[0]->id()],
|
||||
['vid' => '4', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '5', 'id' => $this->entities[2]->id()],
|
||||
['vid' => '6', 'id' => $this->entities[2]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
|
||||
// View own $first_bundle + View any $second_bundle.
|
||||
$user = $this->createUser([], [
|
||||
'view own first entity_test_enhanced_with_owner',
|
||||
'view any second entity_test_enhanced_with_owner',
|
||||
]);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
$this->entities[1]->set('user_id', $user->id());
|
||||
$this->entities[1]->save();
|
||||
|
||||
$view = Views::getView('entity_test_enhanced_with_owner_revisions');
|
||||
$view->execute();
|
||||
$this->assertIdenticalResultset($view, [
|
||||
['vid' => '4', 'id' => $this->entities[1]->id()],
|
||||
['vid' => '5', 'id' => $this->entities[2]->id()],
|
||||
['vid' => '6', 'id' => $this->entities[2]->id()],
|
||||
], ['vid' => 'vid']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Drupal\Tests\entity\Kernel;
|
||||
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntity;
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
@@ -30,12 +29,6 @@ class RevisionBasicUITest extends KernelTestBase {
|
||||
$this->installSchema('system', 'router');
|
||||
$this->installConfig(['system']);
|
||||
|
||||
$bundle = EnhancedEntityBundle::create([
|
||||
'id' => 'default',
|
||||
'label' => 'Default',
|
||||
]);
|
||||
$bundle->save();
|
||||
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
}
|
||||
|
||||
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Unit;
|
||||
|
||||
use Drupal\Core\Cache\Context\CacheContextsManager;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\entity\BundleEntityAccessControlHandler;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\entity\BundleEntityAccessControlHandler
|
||||
* @group entity
|
||||
*/
|
||||
class BundleEntityAccessControlHandlerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||
$module_handler->invokeAll(Argument::any(), Argument::any())->willReturn([]);
|
||||
$cache_contexts_manager = $this->prophesize(CacheContextsManager::class);
|
||||
$cache_contexts_manager->assertValidTokens(Argument::any())->willReturn(TRUE);
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('module_handler', $module_handler->reveal());
|
||||
$container->set('cache_contexts_manager', $cache_contexts_manager->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::checkAccess
|
||||
*
|
||||
* @dataProvider accessProvider
|
||||
*/
|
||||
public function testAccess(EntityInterface $entity, $operation, $account, $allowed) {
|
||||
$handler = new BundleEntityAccessControlHandler($entity->getEntityType());
|
||||
$handler->setStringTranslation($this->getStringTranslationStub());
|
||||
$result = $handler->access($entity, $operation, $account);
|
||||
$this->assertEquals($allowed, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testAccess().
|
||||
*
|
||||
* @return array
|
||||
* A list of testAccess method arguments.
|
||||
*/
|
||||
public function accessProvider() {
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->id()->willReturn('green_entity_bundle');
|
||||
$entity_type->getBundleOf()->willReturn('green_entity');
|
||||
$entity_type->getAdminPermission()->willReturn('administer green_entity');
|
||||
$entity_type = $entity_type->reveal();
|
||||
|
||||
$entity = $this->prophesize(ConfigEntityInterface::class);
|
||||
$entity->getEntityType()->willReturn($entity_type);
|
||||
$entity->getEntityTypeId()->willReturn('green_entity_bundle');
|
||||
$entity->id()->willReturn('default');
|
||||
$entity->uuid()->willReturn('fake uuid');
|
||||
$entity->language()->willReturn(new Language(['id' => LanguageInterface::LANGCODE_NOT_SPECIFIED]));
|
||||
|
||||
// User with no access.
|
||||
$user = $this->buildMockUser(1, 'access content');
|
||||
$data[] = [$entity->reveal(), 'view label', $user->reveal(), FALSE];
|
||||
|
||||
// Permissions which grant "view label" access.
|
||||
$permissions = [
|
||||
'administer green_entity',
|
||||
'view green_entity',
|
||||
'view default green_entity',
|
||||
'view own green_entity',
|
||||
'view any green_entity',
|
||||
'view own default green_entity',
|
||||
'view any default green_entity',
|
||||
];
|
||||
foreach ($permissions as $index => $permission) {
|
||||
$user = $this->buildMockUser(10 + $index, $permission);
|
||||
$data[] = [$entity->reveal(), 'view label', $user->reveal(), TRUE];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock user.
|
||||
*
|
||||
* @param int $uid
|
||||
* The user ID.
|
||||
* @param string $permission
|
||||
* The permission to grant.
|
||||
*
|
||||
* @return \Prophecy\Prophecy\ObjectProphecy
|
||||
* The user mock.
|
||||
*/
|
||||
protected function buildMockUser($uid, $permission) {
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$account->id()->willReturn($uid);
|
||||
$account->hasPermission($permission)->willReturn(TRUE);
|
||||
$account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Unit\QueryAccess;
|
||||
|
||||
use Drupal\entity\QueryAccess\Condition;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\entity\QueryAccess\Condition
|
||||
* @group entity
|
||||
*/
|
||||
class ConditionTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* ::covers __construct.
|
||||
*
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Unrecognized operator "INVALID".
|
||||
*/
|
||||
public function testInvalidOperator() {
|
||||
$condition = new Condition('uid', '1', 'INVALID');
|
||||
}
|
||||
|
||||
/**
|
||||
* ::covers getField
|
||||
* ::covers getValue
|
||||
* ::covers getOperator
|
||||
* ::covers __toString.
|
||||
*/
|
||||
public function testGetters() {
|
||||
$condition = new Condition('uid', '2');
|
||||
$this->assertEquals('uid', $condition->getField());
|
||||
$this->assertEquals('2', $condition->getValue());
|
||||
$this->assertEquals('=', $condition->getOperator());
|
||||
$this->assertEquals("uid = '2'", $condition->__toString());
|
||||
|
||||
$condition = new Condition('type', ['article', 'page']);
|
||||
$this->assertEquals('type', $condition->getField());
|
||||
$this->assertEquals(['article', 'page'], $condition->getValue());
|
||||
$this->assertEquals('IN', $condition->getOperator());
|
||||
$this->assertEquals("type IN ['article', 'page']", $condition->__toString());
|
||||
|
||||
$condition = new Condition('title', NULL, 'IS NULL');
|
||||
$this->assertEquals('title', $condition->getField());
|
||||
$this->assertEquals(NULL, $condition->getValue());
|
||||
$this->assertEquals('IS NULL', $condition->getOperator());
|
||||
$this->assertEquals("title IS NULL", $condition->__toString());
|
||||
}
|
||||
|
||||
}
|
||||
+4
-4
@@ -81,7 +81,7 @@ class UncacheableEntityPermissionProviderTest extends UnitTestCase {
|
||||
'create green_entity' => 'Create green entities',
|
||||
'update green_entity' => 'Update green entities',
|
||||
'delete green_entity' => 'Delete green entities',
|
||||
'view any green_entity' => 'View any green entities',
|
||||
'view green_entity' => 'View green entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
@@ -127,9 +127,9 @@ class UncacheableEntityPermissionProviderTest extends UnitTestCase {
|
||||
'create second white_entity' => 'Second: Create white entities',
|
||||
'update second white_entity' => 'Second: Update white entities',
|
||||
'delete second white_entity' => 'Second: Delete white entities',
|
||||
'view any white_entity' => 'View any white entities',
|
||||
'view any first white_entity' => 'First: View any white entities',
|
||||
'view any second white_entity' => 'Second: View any white entities',
|
||||
'view white_entity' => 'View white entities',
|
||||
'view first white_entity' => 'First: View white entities',
|
||||
'view second white_entity' => 'Second: View white entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user