Commit cbd3299e authored by alexpott's avatar alexpott

Issue #2916740 by chr.fritsch, xjm, Manuel Garcia, tstoeckler, alexpott,...

Issue #2916740 by chr.fritsch, xjm, Manuel Garcia, tstoeckler, alexpott, seanB, Sam152, amateescu, tim.plunkett, dawehner, larowlan, Berdir: Add generic entity actions
parent c9ca954c
...@@ -374,3 +374,8 @@ block.settings.field_block:*:*: ...@@ -374,3 +374,8 @@ block.settings.field_block:*:*:
mapping: mapping:
formatter: formatter:
type: field_formatter type: field_formatter
# Schema for entity actions.
action.configuration.entity:*:*:
type: action_configuration_default
label: 'Entity action'
<?php
namespace Drupal\Core\Action\Plugin\Action\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a base action for each entity type with specific interfaces.
*/
abstract class EntityActionDeriverBase extends DeriverBase implements ContainerDeriverInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a new EntityActionDeriverBase object.
*
* @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'));
}
/**
* Indicates whether the deriver can be used for the provided entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return bool
* TRUE if the entity type can be used, FALSE otherwise.
*/
abstract protected function isApplicable(EntityTypeInterface $entity_type);
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
if (empty($this->derivatives)) {
$definitions = [];
foreach ($this->getApplicableEntityTypes() as $entity_type_id => $entity_type) {
$definition = $base_plugin_definition;
$definition['type'] = $entity_type_id;
$definition['label'] = sprintf('%s %s', $base_plugin_definition['action_label'], $entity_type->getSingularLabel());
$definitions[$entity_type_id] = $definition;
}
$this->derivatives = $definitions;
}
return parent::getDerivativeDefinitions($base_plugin_definition);
}
/**
* Gets a list of applicable entity types.
*
* The list consists of all entity types which match the conditions for the
* given deriver.
* For example, if the action applies to entities that are publishable,
* this method will find all entity types that are publishable.
*
* @return \Drupal\Core\Entity\EntityTypeInterface[]
* The applicable entity types, keyed by entity type ID.
*/
protected function getApplicableEntityTypes() {
$entity_types = $this->entityTypeManager->getDefinitions();
$entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
return $this->isApplicable($entity_type);
});
return $entity_types;
}
}
<?php
namespace Drupal\Core\Action\Plugin\Action\Derivative;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Provides an action deriver that finds entity types of EntityChangedInterface.
*
* @see \Drupal\Core\Action\Plugin\Action\SaveAction
*/
class EntityChangedActionDeriver extends EntityActionDeriverBase {
/**
* {@inheritdoc}
*/
protected function isApplicable(EntityTypeInterface $entity_type) {
return $entity_type->entityClassImplements(EntityChangedInterface::class);
}
}
<?php
namespace Drupal\Core\Action\Plugin\Action\Derivative;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Provides an action deriver that finds publishable entity types.
*
* @see \Drupal\Core\Action\Plugin\Action\PublishAction
* @see \Drupal\Core\Action\Plugin\Action\UnpublishAction
*/
class EntityPublishedActionDeriver extends EntityActionDeriverBase {
/**
* {@inheritdoc}
*/
protected function isApplicable(EntityTypeInterface $entity_type) {
return $entity_type->entityClassImplements(EntityPublishedInterface::class);
}
}
<?php
namespace Drupal\Core\Action\Plugin\Action;
use Drupal\Component\Plugin\DependentPluginInterface;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Base class for entity-based actions.
*/
abstract class EntityActionBase extends ActionBase implements DependentPluginInterface, ContainerFactoryPluginInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a EntityActionBase object.
*
* @param mixed[] $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\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager')
);
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
$module_name = $this->entityTypeManager
->getDefinition($this->getPluginDefinition()['type'])
->getProvider();
return ['module' => [$module_name]];
}
}
<?php
namespace Drupal\Core\Action\Plugin\Action;
use Drupal\Core\Session\AccountInterface;
/**
* Publishes an entity.
*
* @Action(
* id = "entity:publish_action",
* action_label = @Translation("Publish"),
* deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver",
* )
*/
class PublishAction extends EntityActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setPublished()->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
$key = $object->getEntityType()->getKey('published');
/** @var \Drupal\Core\Entity\EntityInterface $object */
$result = $object->access('update', $account, TRUE)
->andIf($object->$key->access('edit', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
}
}
<?php
namespace Drupal\Core\Action\Plugin\Action;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides an action that can save any entity.
*
* @Action(
* id = "entity:save_action",
* action_label = @Translation("Save"),
* deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityChangedActionDeriver",
* )
*/
class SaveAction extends EntityActionBase {
/**
* The time service.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* Constructs a SaveAction object.
*
* @param mixed[] $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\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
$this->time = $time;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('datetime.time')
);
}
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setChangedTime($this->time->getRequestTime())->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
// It's not necessary to check the changed field access here, because
// Drupal\Core\Field\ChangedFieldItemList would anyway return 'not allowed'.
// Also changing the changed field value is only a workaround to trigger an
// entity resave. Without a field change, this would not be possible.
/** @var \Drupal\Core\Entity\EntityInterface $object */
return $object->access('update', $account, $return_as_object);
}
}
<?php
namespace Drupal\Core\Action\Plugin\Action;
use Drupal\Core\Session\AccountInterface;
/**
* Unpublishes an entity.
*
* @Action(
* id = "entity:unpublish_action",
* action_label = @Translation("Unpublish"),
* deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver",
* )
*/
class UnpublishAction extends EntityActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setUnpublished()->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
$key = $object->getEntityType()->getKey('published');
/** @var \Drupal\Core\Entity\EntityInterface $object */
$result = $object->access('update', $account, TRUE)
->andIf($object->$key->access('edit', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
}
}
...@@ -23,6 +23,12 @@ process: ...@@ -23,6 +23,12 @@ process:
imagecache_flush_action: 0 imagecache_flush_action: 0
imagecache_generate_all_action: 0 imagecache_generate_all_action: 0
imagecache_generate_action: 0 imagecache_generate_action: 0
comment_publish_action: entity:publish_action:comment
comment_unpublish_action: entity:unpublish_action:comment
comment_save_action: entity:save_action:comment
node_publish_action: entity:publish_action:node
node_unpublish_action: entity:unpublish_action:node
node_save_action: entity:save_action:node
bypass: true bypass: true
- -
plugin: skip_on_empty plugin: skip_on_empty
......
...@@ -20,6 +20,12 @@ process: ...@@ -20,6 +20,12 @@ process:
system_send_email_action: action_send_email_action system_send_email_action: action_send_email_action
system_message_action: action_message_action system_message_action: action_message_action
system_block_ip_action: 0 system_block_ip_action: 0
comment_publish_action: entity:publish_action:comment
comment_unpublish_action: entity:unpublish_action:comment
comment_save_action: entity:save_action:comment
node_publish_action: entity:publish_action:node
node_unpublish_action: entity:unpublish_action:node
node_save_action: entity:save_action:node
bypass: true bypass: true
- -
plugin: skip_on_empty plugin: skip_on_empty
......
...@@ -6,5 +6,5 @@ dependencies: ...@@ -6,5 +6,5 @@ dependencies:
id: comment_publish_action id: comment_publish_action
label: 'Publish comment' label: 'Publish comment'
type: comment type: comment
plugin: comment_publish_action plugin: entity:publish_action:comment
configuration: { } configuration: { }
...@@ -6,5 +6,5 @@ dependencies: ...@@ -6,5 +6,5 @@ dependencies:
id: comment_save_action id: comment_save_action
label: 'Save comment' label: 'Save comment'
type: comment type: comment
plugin: comment_save_action plugin: entity:save_action:comment
configuration: { } configuration: { }
...@@ -6,5 +6,5 @@ dependencies: ...@@ -6,5 +6,5 @@ dependencies:
id: comment_unpublish_action id: comment_unpublish_action
label: 'Unpublish comment' label: 'Unpublish comment'
type: comment type: comment
plugin: comment_unpublish_action plugin: entity:unpublish_action:comment
configuration: { } configuration: { }
...@@ -15,10 +15,14 @@ field.widget.settings.comment_default: ...@@ -15,10 +15,14 @@ field.widget.settings.comment_default:
type: mapping type: mapping
label: 'Comment display format settings' label: 'Comment display format settings'
# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
# @see https://www.drupal.org/node/2919303
action.configuration.comment_publish_action: action.configuration.comment_publish_action:
type: action_configuration_default type: action_configuration_default
label: 'Publish comment configuration' label: 'Publish comment configuration'
# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
# @see https://www.drupal.org/node/2919303
action.configuration.comment_save_action: action.configuration.comment_save_action:
type: action_configuration_default type: action_configuration_default
label: 'Save comment configuration' label: 'Save comment configuration'
...@@ -34,6 +38,8 @@ action.configuration.comment_unpublish_by_keyword_action: ...@@ -34,6 +38,8 @@ action.configuration.comment_unpublish_by_keyword_action:
type: string type: string
label: 'Keyword' label: 'Keyword'
# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
# @see https://www.drupal.org/node/2919303
action.configuration.comment_unpublish_action: action.configuration.comment_unpublish_action:
type: action_configuration_default type: action_configuration_default
label: 'Unpublish comment configuration' label: 'Unpublish comment configuration'
......
...@@ -2,37 +2,32 @@ ...@@ -2,37 +2,32 @@
namespace Drupal\comment\Plugin\Action; namespace Drupal\comment\Plugin\Action;
use Drupal\Core\Action\ActionBase; use Drupal\Core\Action\Plugin\Action\PublishAction;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
/** /**
* Publishes a comment. * Publishes a comment.
* *
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
* Use \Drupal\Core\Action\Plugin\Action\PublishAction instead.
*
* @see \Drupal\Core\Action\Plugin\Action\PublishAction
* @see https://www.drupal.org/node/2919303
*
* @Action( * @Action(
* id = "comment_publish_action", * id = "comment_publish_action",
* label = @Translation("Publish comment"), * label = @Translation("Publish comment"),
* type = "comment" * type = "comment"
* ) * )
*/ */
class PublishComment extends ActionBase { class PublishComment extends PublishAction {
/**
* {@inheritdoc}
*/
public function execute($comment = NULL) {
$comment->setPublished(TRUE);
$comment->save();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
/** @var \Drupal\comment\CommentInterface $object */ parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
$result = $object->status->access('edit', $account, TRUE) @trigger_error(__NAMESPACE__ . '\PublishComment is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\PublishAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
->andIf($object->access('update', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
} }
} }
...@@ -2,33 +2,33 @@ ...@@ -2,33 +2,33 @@
namespace Drupal\comment\Plugin\Action; namespace Drupal\comment\Plugin\Action;
use Drupal\Core\Action\ActionBase; use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Action\Plugin\Action\SaveAction;
use Drupal\Core\Entity\EntityTypeManagerInterface;
/** /**
* Saves a comment. * Saves a comment.
* *
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
* Use \Drupal\Core\Action\Plugin\Action\SaveAction instead.
*
* @see \Drupal\Core\Action\Plugin\Action\SaveAction
* @see https://www.drupal.org/node/2919303
*
* @Action( * @Action(
* id = "comment_save_action", * id = "comment_save_action",
* label = @Translation("Save comment"), * label = @Translation("Save comment"),
* type = "comment" * type = "comment"
* ) * )
*/ */
class SaveComment extends ActionBase { class SaveComment extends SaveAction {
/**
* {@inheritdoc}
*/
public function execute($comment = NULL) {
$comment->save();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time) {
/** @var \Drupal\comment\CommentInterface $object */ parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $time);
return $object->access('update', $account, $return_as_object); @trigger_error(__NAMESPACE__ . '\SaveComment is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\SaveAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
} }
} }
...@@ -2,37 +2,32 @@ ...@@ -2,37 +2,32 @@
namespace Drupal\comment\Plugin\Action; namespace Drupal\comment\Plugin\Action;
use Drupal\Core\Action\ActionBase; use Drupal\Core\Action\Plugin\Action\UnpublishAction;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
/** /**
* Unpublishes a comment. * Unpublishes a comment.
* *
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
* Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead.
*
* @see \Drupal\Core\Action\Plugin\Action\UnpublishAction
* @see https://www.drupal.org/node/2919303
*
* @Action( * @Action(
* id = "comment_unpublish_action", * id = "comment_unpublish_action",
* label = @Translation("Unpublish comment"), * label = @Translation("Unpublish comment"),
* type = "comment" * type = "comment"
* ) * )
*/ */
class UnpublishComment extends ActionBase { class UnpublishComment extends UnpublishAction {
/**
* {@inheritdoc}
*/
public function execute($comment = NULL) {
$comment->setPublished(FALSE);
$comment->save();
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) { public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
/** @var \Drupal\comment\CommentInterface $object */ parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
$result = $object->status->access('edit', $account, TRUE) @trigger_error(__NAMESPACE__ . '\UnpublishComment is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
->andIf($object->access('update', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
} }
} }
...@@ -32,7 +32,7 @@ public function testCommentPublishUnpublishActions() { ...@@ -32,7 +32,7 @@ public function testCommentPublishUnpublishActions() {
$action = Action::load('comment_unpublish_action'); $action = Action::load('comment_unpublish_action');
$action->execute([$comment]); $action->execute([$comment]);
$this->assertTrue($comment->isPublished() === FALSE, 'Comment was unpublished'); $this->assertTrue($comment->isPublished() === FALSE, 'Comment was unpublished');
$this->assertArraySubset(['module' => ['comment']], $action->getDependencies());
// Publish a comment. // Publish a comment.
$action = Action::load('comment_publish_action'); $action = Action::load('comment_publish_action');
$action->execute([$comment]); $action->execute([$comment]);
......
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
use Drupal\content_moderation\EntityOperations; use Drupal\content_moderation\EntityOperations;
use Drupal\content_moderation\EntityTypeInfo; use Drupal\content_moderation\EntityTypeInfo;
use Drupal\content_moderation\ContentPreprocess; use Drupal\content_moderation\ContentPreprocess;
use Drupal\content_moderation\Plugin\Action\ModerationOptOutPublishNode; use Drupal\content_moderation\Plugin\Action\ModerationOptOutPublish;
use Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublishNode; use Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublish;
use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\workflows\WorkflowInterface; use Drupal\workflows\WorkflowInterface;