updated contrib modules

This commit is contained in:
Bachir Soussi Chiadmi
2018-03-13 14:16:18 +01:00
parent e668535a4e
commit 272fa07ccf
202 changed files with 5165 additions and 1725 deletions
@@ -0,0 +1,87 @@
<?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();
}
}
@@ -23,7 +23,7 @@ trait RevisionControllerTrait {
*
* @return \Drupal\Core\Language\LanguageManagerInterface
*/
public abstract function languageManager();
abstract public function languageManager();
/**
* Determines if the user has permission to revert revisions.
@@ -55,7 +55,6 @@ trait RevisionControllerTrait {
*
* @return array
* A link render array.
*
*/
abstract protected function buildRevertRevisionLink(EntityInterface $entity_revision);
@@ -117,24 +116,17 @@ trait RevisionControllerTrait {
$langcode = $this->languageManager()
->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
->getId();
$entity_storage = $this->entityTypeManager()
->getStorage($entity->getEntityTypeId());
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $entity_storage */
$entity_storage = $this->entityTypeManager()->getStorage($entity->getEntityTypeId());
$revision_ids = $this->revisionIds($entity);
$entity_revisions = $entity_storage->loadMultipleRevisions($revision_ids);
$header = [$this->t('Revision'), $this->t('Operations')];
$rows = [];
$revision_ids = $this->revisionIds($entity);
// @todo Expand the entity storage to load multiple revisions.
$entity_revisions = array_combine($revision_ids, array_map(function($vid) use ($entity_storage) {
return $entity_storage->loadRevision($vid);
}, $revision_ids));
foreach ($entity_revisions as $revision) {
$row = [];
/** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)
->isRevisionTranslationAffected()
) {
if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) {
$row[] = $this->getRevisionDescription($revision, $revision->isDefaultRevision());
if ($revision->isDefaultRevision()) {
@@ -1,17 +0,0 @@
<?php
namespace Drupal\entity\Entity;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
interface RevisionableEntityBundleInterface extends ConfigEntityInterface {
/**
* Returns whether a new revision should be created by default.
*
* @return bool
* TRUE if a new revision should be created by default.
*/
public function shouldCreateNewRevision();
}
@@ -2,13 +2,7 @@
namespace Drupal\entity;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\user\EntityOwnerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides generic entity permissions which are still cacheable.
@@ -1,37 +0,0 @@
<?php
namespace Drupal\entity;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityViewBuilder as CoreEntityViewBuilder;
/**
* Provides a entity view builder with contextual links support
*/
class EntityViewBuilder extends CoreEntityViewBuilder {
/**
* {@inheritdoc}
*/
protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
$entity_type_id = $entity->getEntityTypeId();
if (($entity instanceof ContentEntityInterface && $entity->isDefaultRevision()) || !$entity->getEntityType()->isRevisionable()) {
$build['#contextual_links'][$entity_type_id] = [
'route_parameters' => [
$entity_type_id => $entity->id()
],
];
}
else {
$build['#contextual_links'][$entity_type_id . '_revision'] = [
'route_parameters' => [
$entity_type_id => $entity->id(),
$entity_type_id . '_revision' => $entity->getRevisionId(),
],
];
}
}
}
@@ -1,226 +0,0 @@
<?php
namespace Drupal\entity\Form;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Url;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides an entities deletion confirmation form.
*/
class DeleteMultiple extends ConfirmFormBase {
/**
* 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\user\SharedTempStore
*/
protected $tempStore;
/**
* The entity type id.
*
* @var string
*/
protected $entityTypeId;
/**
* The selection, in the entity_id => langcodes format.
*
* @var array
*/
protected $selection = [];
/**
* 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\user\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
*/
public function __construct(AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory) {
$this->currentUser = $current_user;
$this->entityTypeManager = $entity_type_manager;
$this->tempStore = $temp_store_factory->get('entity_delete_multiple_confirm');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('current_user'),
$container->get('entity_type.manager'),
$container->get('user.private_tempstore')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'entity_delete_multiple_confirm';
}
/**
* {@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?');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.' . $this->entityTypeId . '.collection');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete');
}
/**
* {@inheritdoc}
*
* @param string $entity_type_id
* The entity type id.
*/
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) {
$this->entityTypeId = $entity_type_id;
$this->selection = $this->tempStore->get($this->currentUser->id());
if (empty($this->entityTypeId) || empty($this->selection)) {
return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString());
}
$storage = $this->entityTypeManager->getStorage($this->entityTypeId);
/** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */
$entities = $storage->loadMultiple(array_keys($this->selection));
$items = [];
foreach ($this->selection as $id => $langcodes) {
foreach ($langcodes as $langcode) {
$entity = $entities[$id]->getTranslation($langcode);
$key = $id . ':' . $langcode;
$default_key = $id . ':' . $entity->getUntranslated()->language()->getId();
// If we have a translated entity we build a nested list of translations
// that will be deleted.
$languages = $entity->getTranslationLanguages();
if (count($languages) > 1 && $entity->isDefaultTranslation()) {
$names = [];
foreach ($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 translations will be deleted:</em>', ['@label' => $entity->label()]),
],
'deleted_translations' => [
'#theme' => 'item_list',
'#items' => $names,
],
];
}
elseif (!isset($items[$default_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 = [];
$storage = $this->entityTypeManager->getStorage($this->entityTypeId);
/** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */
$entities = $storage->loadMultiple(array_keys($this->selection));
foreach ($this->selection as $id => $langcodes) {
foreach ($langcodes as $langcode) {
$entity = $entities[$id]->getTranslation($langcode);
if ($entity->isDefaultTranslation()) {
$delete_entities[$id] = $entity;
unset($delete_translations[$id]);
$total_count += count($entity->getTranslationLanguages());
}
elseif (!isset($delete_entities[$id])) {
$delete_translations[$id][] = $entity;
}
}
}
if ($delete_entities) {
$storage->delete($delete_entities);
$this->logger('content')->notice('Deleted @count @entity_type items.', [
'@count' => count($delete_entities),
'@entity_type' => $this->entityTypeId,
]);
}
if ($delete_translations) {
$count = 0;
/** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */
foreach ($delete_translations as $id => $translations) {
$entity = $entities[$id]->getUntranslated();
foreach ($translations as $translation) {
$entity->removeTranslation($translation->language()->getId());
}
$entity->save();
$count += count($translations);
}
if ($count) {
$total_count += $count;
$this->logger('content')->notice('Deleted @count @entity_type translations.', [
'@count' => $count,
'@entity_type' => $this->entityTypeId,
]);
}
}
if ($total_count) {
drupal_set_message($this->formatPlural($total_count, 'Deleted 1 item.', 'Deleted @count items.'));
}
$this->tempStore->delete($this->currentUser->id());
$form_state->setRedirectUrl($this->getCancelUrl());
}
}
@@ -0,0 +1,323 @@
<?php
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;
/**
* Provides an entities deletion confirmation form.
*/
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.");
}
}
@@ -1,159 +0,0 @@
<?php
namespace Drupal\entity\Form;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\entity\Entity\RevisionableEntityBundleInterface;
/**
* Extends the base entity form with revision support in the UI.
*/
class RevisionableContentEntityForm extends ContentEntityForm {
/**
* The entity being used by this form.
*
* @var \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface|\Drupal\entity\Revision\EntityRevisionLogInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function prepareEntity() {
parent::prepareEntity();
$bundle_entity = $this->getBundleEntity();
// Set up default values, if required.
if (!$this->entity->isNew()) {
$this->entity->setRevisionLogMessage(NULL);
}
if ($bundle_entity instanceof RevisionableEntityBundleInterface) {
// Always use the default revision setting.
$this->entity->setNewRevision($bundle_entity && $bundle_entity->shouldCreateNewRevision());
}
}
/**
* Returns the bundle entity of the entity, or NULL if there is none.
*
* @return \Drupal\Core\Entity\EntityInterface|null
*/
protected function getBundleEntity() {
if ($bundle_key = $this->entity->getEntityType()->getKey('bundle')) {
return $this->entity->{$bundle_key}->referencedEntities()[0];
}
return NULL;
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$entity_type = $this->entity->getEntityType();
$bundle_entity = $this->getBundleEntity();
$account = $this->currentUser();
if ($this->operation == 'edit') {
$form['#title'] = $this->t('Edit %bundle_label @label', [
'%bundle_label' => $bundle_entity ? $bundle_entity->label() : '',
'@label' => $this->entity->label(),
]);
}
$form['advanced'] = [
'#type' => 'vertical_tabs',
'#weight' => 99,
];
// Add a log field if the "Create new revision" option is checked, or if the
// current user has the ability to check that option.
// @todo Could we autogenerate this form by using some widget on the
// revision info field.
$form['revision_information'] = [
'#type' => 'details',
'#title' => $this->t('Revision information'),
// Open by default when "Create new revision" is checked.
'#open' => $this->entity->isNewRevision(),
'#group' => 'advanced',
'#weight' => 20,
'#access' => $this->entity->isNewRevision() || $account->hasPermission($entity_type->get('admin_permission')),
];
$form['revision_information']['revision'] = [
'#type' => 'checkbox',
'#title' => $this->t('Create new revision'),
'#default_value' => $this->entity->isNewRevision(),
'#access' => $account->hasPermission($entity_type->get('admin_permission')),
];
// Check the revision log checkbox when the log textarea is filled in.
// This must not happen if "Create new revision" is enabled by default,
// since the state would auto-disable the checkbox otherwise.
if (!$this->entity->isNewRevision()) {
$form['revision_information']['revision']['#states'] = [
'checked' => [
'textarea[name="revision_log"]' => ['empty' => FALSE],
],
];
}
$form['revision_information']['revision_log'] = [
'#type' => 'textarea',
'#title' => $this->t('Revision log message'),
'#rows' => 4,
'#default_value' => $this->entity->getRevisionLogMessage(),
'#description' => $this->t('Briefly describe the changes you have made.'),
];
return parent::form($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
// Save as a new revision if requested to do so.
if (!$form_state->isValueEmpty('revision')) {
$this->entity->setNewRevision();
}
$insert = $this->entity->isNew();
$this->entity->save();
$context = ['@type' => $this->entity->bundle(), '%info' => $this->entity->label()];
$logger = $this->logger('content');
$bundle_entity = $this->getBundleEntity();
$t_args = ['@type' => $bundle_entity ? $bundle_entity->label() : 'None', '%info' => $this->entity->label()];
if ($insert) {
$logger->notice('@type: added %info.', $context);
drupal_set_message($this->t('@type %info has been created.', $t_args));
}
else {
$logger->notice('@type: updated %info.', $context);
drupal_set_message($this->t('@type %info has been updated.', $t_args));
}
if ($this->entity->id()) {
$form_state->setValue('id', $this->entity->id());
$form_state->set('id', $this->entity->id());
if ($this->entity->getEntityType()->hasLinkTemplate('collection')) {
$form_state->setRedirectUrl($this->entity->toUrl('collection'));
}
else {
$form_state->setRedirectUrl($this->entity->toUrl('canonical'));
}
}
else {
// In the unlikely case something went wrong on save, the entity will be
// rebuilt and entity form redisplayed.
drupal_set_message($this->t('The entity could not be saved.'), 'error');
$form_state->setRebuild();
}
}
}
@@ -0,0 +1,81 @@
<?php
namespace Drupal\entity\Menu;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Menu\LocalActionDefault;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides a local action to add an entity.
*/
class EntityAddLocalAction extends LocalActionDefault {
use StringTranslationTrait;
/**
* The entity type.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* Constructs a EntityAddLocalAction 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\Routing\RouteProviderInterface $route_provider
* The route provider to load routes by name.
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, EntityTypeInterface $entity_type, TranslationInterface $string_translation) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider);
$this->entityType = $entity_type;
$this->setStringTranslation($string_translation);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
/* @var \Drupal\Core\Entity\EntityTypeManagerInterface */
$entity_type_manager = $container->get('entity_type.manager');
// The plugin ID is of the form
// "entity.entity_actions:entity.$entity_type_id.collection".
// @see entity.links.action.yml
// @see \Drupal\entity\Menu\EntityCollectionLocalActionProvider::buildLocalActions()
list(, $derivate_id) = explode(':', $plugin_id);
list(, $entity_type_id, ) = explode('.', $derivate_id);
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('router.route_provider'),
$entity_type_manager->getDefinition($entity_type_id),
$container->get('string_translation')
);
}
/**
* {@inheritdoc}
*/
public function getTitle(Request $request = NULL) {
return (string) $this->t('Add @entity', [
'@entity' => (string) $this->entityType->getSingularLabel(),
]);
}
}
@@ -0,0 +1,41 @@
<?php
namespace Drupal\entity\Menu;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Provides a action link to the add page or add form on the collection.
*/
class EntityCollectionLocalActionProvider implements EntityLocalActionProviderInterface {
/**
* {@inheritdoc}
*/
public function buildLocalActions(EntityTypeInterface $entity_type) {
$actions = [];
if ($entity_type->hasLinkTemplate('collection')) {
$entity_type_id = $entity_type->id();
if ($entity_type->hasLinkTemplate('add-page')) {
$route_name = "entity.$entity_type_id.add_page";
}
elseif ($entity_type->hasLinkTemplate('add-form')) {
$route_name = "entity.$entity_type_id.add_form";
}
if (isset($route_name)) {
$actions[$route_name] = [
// The title is translated at runtime by EntityAddLocalAction.
/* @see \Drupal\entity\Menu\EntityAddLocalAction::getTitle() */
'title' => 'Add ' . $entity_type->getSingularLabel(),
'route_name' => $route_name,
'appears_on' => ["entity.$entity_type_id.collection"],
'class' => EntityAddLocalAction::class,
];
}
}
return $actions;
}
}
@@ -0,0 +1,23 @@
<?php
namespace Drupal\entity\Menu;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Provides an interface for entity local action providers.
*/
interface EntityLocalActionProviderInterface {
/**
* Builds local actions for the given entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return array[]
* An array of local action definitions.
*/
public function buildLocalActions(EntityTypeInterface $entity_type);
}
@@ -5,7 +5,7 @@ namespace Drupal\entity\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\PrivateTempStoreFactory;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -22,7 +22,7 @@ class DeleteAction extends ActionBase implements ContainerFactoryPluginInterface
/**
* The tempstore object.
*
* @var \Drupal\user\SharedTempStore
* @var \Drupal\Core\TempStore\SharedTempStore
*/
protected $tempStore;
@@ -42,7 +42,7 @@ class DeleteAction extends ActionBase implements ContainerFactoryPluginInterface
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param AccountInterface $current_user
* Current user.
@@ -62,7 +62,7 @@ class DeleteAction extends ActionBase implements ContainerFactoryPluginInterface
$configuration,
$plugin_id,
$plugin_definition,
$container->get('user.private_tempstore'),
$container->get('tempstore.private'),
$container->get('current_user')
);
}
@@ -71,13 +71,13 @@ class DeleteAction extends ActionBase implements ContainerFactoryPluginInterface
* {@inheritdoc}
*/
public function executeMultiple(array $entities) {
/** @var \Drupal\Core\Entity\ContentEntityInterface[] $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(), $selection);
$this->tempStore->set($this->currentUser->id() . ':' . $this->getPluginDefinition()['type'], $selection);
}
/**
@@ -46,7 +46,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->getLowercaseLabel()]);
$definition['label'] = t('Delete @entity_type', ['@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;
@@ -0,0 +1,58 @@
<?php
namespace Drupal\entity\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Derives local actions for entity types.
*/
class EntityActionsDeriver extends DeriverBase implements ContainerDeriverInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs an entity local actions deriver.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static($container->get('entity_type.manager'));
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
if (!$this->derivatives) {
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
$handlers = $entity_type->getHandlerClasses();
if (isset($handlers['local_action_provider'])) {
foreach ($handlers['local_action_provider'] as $class) {
/** @var \Drupal\entity\Menu\EntityLocalActionProviderInterface $handler */
$handler = $this->entityTypeManager->createHandlerInstance($class, $entity_type);
$this->derivatives += $handler->buildLocalActions($entity_type);
}
}
}
}
return $this->derivatives;
}
}
@@ -13,12 +13,8 @@ abstract class RevisionableContentEntityBase extends BaseRevisionableContentEnti
* {@inheritdoc}
*/
protected function urlRouteParameters($rel) {
$uri_route_parameters = [];
$uri_route_parameters = parent::urlRouteParameters($rel);
if ($rel != 'collection') {
// The entity ID is needed as a route parameter.
$uri_route_parameters[$this->getEntityTypeId()] = $this->id();
}
if (strpos($this->getEntityType()->getLinkTemplate($rel), $this->getEntityTypeId() . '_revision') !== FALSE) {
$uri_route_parameters[$this->getEntityTypeId() . '_revision'] = $this->getRevisionId();
}
@@ -0,0 +1,26 @@
<?php
namespace Drupal\entity\Routing;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider as CoreAdminHtmlRouteProvider;
/**
* Provides HTML routes for entities with administrative add/edit/delete pages.
*/
class AdminHtmlRouteProvider extends CoreAdminHtmlRouteProvider {
/**
* {@inheritdoc}
*/
protected function getCollectionRoute(EntityTypeInterface $entity_type) {
$route = parent::getCollectionRoute($entity_type);
if ($route && $entity_type->hasHandlerClass('permission_provider')) {
$admin_permission = $entity_type->getAdminPermission();
$overview_permission = "access {$entity_type->id()} overview";
$route->setRequirement('_permission', "$admin_permission+$overview_permission");
}
return $route;
}
}
@@ -0,0 +1,26 @@
<?php
namespace Drupal\entity\Routing;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider as CoreDefaultHtmlRouteProvider;
/**
* Provides HTML routes for entities.
*/
class DefaultHtmlRouteProvider extends CoreDefaultHtmlRouteProvider {
/**
* {@inheritdoc}
*/
protected function getCollectionRoute(EntityTypeInterface $entity_type) {
$route = parent::getCollectionRoute($entity_type);
if ($route && $entity_type->hasHandlerClass('permission_provider')) {
$admin_permission = $entity_type->getAdminPermission();
$overview_permission = "access {$entity_type->id()} overview";
$route->setRequirement('_permission', "$admin_permission+$overview_permission");
}
return $route;
}
}
@@ -36,9 +36,9 @@ class DeleteMultipleRouteProvider implements EntityRouteProviderInterface {
protected function deleteMultipleFormRoute(EntityTypeInterface $entity_type) {
if ($entity_type->hasLinkTemplate('delete-multiple-form')) {
$route = new Route($entity_type->getLinkTemplate('delete-multiple-form'));
$route->setDefault('_form', '\Drupal\entity\Form\DeleteMultiple');
$route->setDefault('_form', '\Drupal\entity\Form\DeleteMultipleForm');
$route->setDefault('entity_type_id', $entity_type->id());
$route->setRequirement('_permission', $entity_type->getAdminPermission());
$route->setRequirement('_entity_delete_multiple_access', $entity_type->id());
return $route;
}
@@ -2,13 +2,8 @@
namespace Drupal\entity;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\user\EntityOwnerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides generic entity permissions which are cached per user.
@@ -25,7 +20,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* - create $bundle $entity_type
*
* As this class supports "view own ($bundle) $entity_type" it is just cacheable
* per user, which might harm performance of sites. Given that please use
* per user, which might harm performance of sites. Given that please use
* \Drupal\entity\EntityPermissionProvider unless you need the feature, or your
* entity type is not really user facing (commerce orders for example).
*