Skip to content
Snippets Groups Projects
Commit 564b9289 authored by Andrei Mateescu's avatar Andrei Mateescu
Browse files

Starting with a clean slate (again) for the 3.x branch.

parent 79b928b8
No related branches found
No related tags found
No related merge requests found
Showing
with 0 additions and 1500 deletions
<?php
namespace Drupal\trash\Controller;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Url;
use Drupal\trash\TrashModerationState;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Returns responses for trash module routes.
*/
class TrashController extends ControllerBase {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* The Content Moderation moderation information service.
*
* @var \Drupal\content_moderation\ModerationInformationInterface
*/
protected $moderationInformation;
/**
* Constructs an TrashController object.
*
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
* @param \Drupal\content_moderation\ModerationInformationInterface $moderation_information
* The Content Moderation moderation information service.
*/
public function __construct(Connection $connection, ModerationInformationInterface $moderation_information) {
$this->connection = $connection;
$this->moderationInformation = $moderation_information;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('database'),
$container->get('content_moderation.moderation_information')
);
}
/**
* Builds a listing of content entities.
*
* @return array
* A render array.
*/
public function entityList() {
$entities = $this->loadTrashedEntities();
$rows = [];
foreach ($entities as $entity) {
if ($entity instanceof ContentEntityInterface) {
$route_parameters = [
'entity_type_id' => $entity->getEntityTypeId(),
'entity_id' => $entity->id(),
];
$links = [
'restore' => [
'title' => $this->t('Restore'),
'url' => Url::fromRoute('trash.restore_form', $route_parameters),
],
'purge' => [
'title' => $this->t('Purge'),
'url' => Url::fromRoute('trash.purge_form', $route_parameters),
],
];
$id = $entity->id();
$rows[$id] = [];
$rows[$id]['type'] = $entity->getEntityType()->getLabel();
$rows[$id]['label'] = [
'data' => [
'#type' => 'link',
'#title' => $entity->label(),
'#access' => $entity->access('view'),
'#url' => $entity->toUrl(),
],
];
$rows[$id]['operations'] = [
'data' => [
'#type' => 'operations',
'#links' => $links,
],
];
}
}
return [
'#type' => 'table',
'#header' => $this->buildHeader(),
'#rows' => $rows,
'#empty' => $this->t('The trash bin is empty.'),
];
}
/**
* Loads one or more trashed entities of a given type.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* An array of entity objects indexed by their IDs. Returns an empty array
* if no matching entities are found.
*/
protected function loadTrashedEntities() {
$content_moderation_state = $this->entityTypeManager()
->getDefinition('content_moderation_state');
$data_table = $content_moderation_state->getDataTable();
$query = $this->connection
->select($data_table, 'data_table')
->fields('data_table', ['content_entity_type_id', 'content_entity_id'])
->condition('moderation_state', TrashModerationState::TRASHED);
$entity_data = $query->execute()
->fetchAllAssoc('content_entity_id', \PDO::FETCH_ASSOC);
$entities = [];
foreach ($entity_data as $entity_id => $item) {
$entities[] = $this->entityTypeManager()
->getStorage($item['content_entity_type_id'])
->load($entity_id);
}
return $entities;
}
/**
* Returns the table header render array for entity types.
*
* @return array
* A render array.
*/
protected function buildHeader() {
$header = [];
$header['type'] = [
'data' => $this->t('Type'),
];
$header['label'] = [
'data' => $this->t('Name'),
];
$header['operations'] = $this->t('Operations');
return $header;
}
}
<?php
namespace Drupal\trash\Controller;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFormBuilderInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\trash\TrashManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Returns response for entity delete route.
*/
class TrashDeleteController extends ControllerBase {
/**
* The route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The Content Moderation moderation information service.
*
* @var \Drupal\content_moderation\ModerationInformationInterface
*/
protected $moderationInformation;
/**
* The Entity Form Builder service.
*
* @var \Drupal\Core\Entity\EntityFormBuilderInterface
*/
protected $entityFormBuilder;
/**
* The Trash Manager service.
*
* @var \Drupal\trash\TrashManagerInterface
*/
protected $trashManager;
/**
* Constructs a new TrashDeleteController object.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\content_moderation\ModerationInformationInterface $moderation_information
* The Content Moderation moderation information service.
* @param \Drupal\Core\Entity\EntityFormBuilderInterface $entity_form_builder
* The Entity Form Builder service.
* @param \Drupal\trash\TrashManagerInterface $trash_manager
* The Trash Manager service.
*/
public function __construct(RouteMatchInterface $route_match, EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_information, EntityFormBuilderInterface $entity_form_builder, TrashManagerInterface $trash_manager) {
$this->routeMatch = $route_match;
$this->entityTypeManager = $entity_type_manager;
$this->moderationInformation = $moderation_information;
$this->entityFormBuilder = $entity_form_builder;
$this->trashManager = $trash_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('current_route_match'),
$container->get('entity_type.manager'),
$container->get('content_moderation.moderation_information'),
$container->get('entity.form_builder'),
$container->get('trash.manager')
);
}
/**
* Move an entity to trash.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|array
* Returns the redirect response if the entity is moderateable, otherwise
* the delete form of the entity.
*/
public function trashEntity() {
$parameters = $this->routeMatch->getParameters()->all();
$entities = array_filter($parameters, function ($parameter) {
return $parameter instanceof ContentEntityInterface;
});
$entity = reset($entities);
// Return the entity delete form for non-moderated entities.
if (!$this->moderationInformation->isModeratedEntity($entity)) {
return $this->entityFormBuilder->getForm($entity, 'delete');
}
$this->trashManager->trash($entity);
drupal_set_message($this->t('The @entity %label has been moved to the trash. <a href=":undo-page">Undo</a>', [
'@entity' => $entity->getEntityType()
->getLabel(),
'%label' => $entity->label(),
':undo-page' => Url::fromRoute('trash.restore_form', [
'entity_type_id' => $entity->getEntityTypeId(),
'entity_id' => $entity->id(),
])->toString(),
]));
$redirect_url = $this->getRedirectUrl($entity);
return $this->redirect($redirect_url->getRouteName(), $redirect_url->getRouteParameters());
}
/**
* Returns the url object for redirect path.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity for which we want the redirect url.
*
* @return \Drupal\Core\Url
* The redirect URL object.
*/
protected function getRedirectUrl(ContentEntityInterface $entity) {
if ($entity->hasLinkTemplate('collection')) {
// If available, return the collection URL.
return $entity->toUrl('collection');
}
else {
// Otherwise fall back to the front page.
return Url::fromRoute('<front>');
}
}
}
<?php
namespace Drupal\trash\Form;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides a entity purge confirmation form.
*/
class PurgeForm extends ConfirmFormBase {
/**
* The content entity.
*
* @var \Drupal\Core\Entity\ContentEntityInterface
*/
protected $entity;
/**
* The entity manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager')
);
}
/**
* Constructs a new PurgeForm.
*
* @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 function getFormId() {
return 'purge_form';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to purge "@label"?', ['@label' => $this->entity->label()]);
}
/**
* {@inheritdoc}
*/
public function getDescription() {
// TODO: rework this text.
return $this->t('Purging "@label" from the database should only be done as a last resort when sensitive information has been introduced inadvertently into a database. In clustered or replicated environments it is very difficult to guarantee that a particular purged document has been removed from all replicas.', ['@label' => $this->entity->label()]);
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Purge');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('trash.default', ['entity_type_id' => $this->entity->getEntityTypeId()]);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL, $entity_id = NULL) {
/** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
$storage = $this->entityTypeManager->getStorage($entity_type_id);
if (!$this->entity = $storage->load($entity_id)) {
throw new NotFoundHttpException($this->t('Deleted entity with ID @id was not found.', ['@id' => $entity_id]));
}
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$entity_type_id = $this->entity->getEntityTypeId();
$this->entityTypeManager
->getStorage($entity_type_id)
->delete([$this->entity]);
drupal_set_message($this->t('The @entity "%label" has been purged.', [
'@entity' => $this->entity->getEntityType()->get('label'),
'%label' => $this->entity->label(),
]));
$form_state->setRedirect('trash.default', ['entity_type_id' => $entity_type_id]);
}
}
<?php
namespace Drupal\trash\Form;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\trash\TrashManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides a entity restore confirmation form.
*/
class RestoreForm extends ConfirmFormBase {
/**
* The content entity.
*
* @var \Drupal\Core\Entity\ContentEntityInterface
*/
protected $entity;
/**
* The entity manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The Trash Manager service.
*
* @var \Drupal\trash\TrashManagerInterface
*/
protected $trashManager;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('trash.manager')
);
}
/**
* Constructs a new RestoreForm.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\trash\TrashManagerInterface $trash_manager
* The Trash Manager service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, TrashManagerInterface $trash_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->trashManager = $trash_manager;
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'restore_form';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to restore "@label"?', ['@label' => $this->entity->label()]);
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->t('The @entity "@label" will be restored.', [
'@entity' => $this->entity->getEntityType()
->get('label'),
'@label' => $this->entity->label(),
]);
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Restore');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('trash.default', ['entity_type_id' => $this->entity->getEntityTypeId()]);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL, $entity_id = NULL) {
if (!empty($entity_type_id)) {
/** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
$storage = $this->entityTypeManager->getStorage($entity_type_id);
if (!$this->entity = $storage->load($entity_id)) {
throw new NotFoundHttpException(t('Deleted entity with ID @id was not found.', ['@id' => $entity_id]));
}
}
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->trashManager->restore($this->entity);
drupal_set_message(t('The @entity "@label" has been restored.', [
'@entity' => $this->entity->getEntityType()
->get('label'),
'@label' => $this->entity->label(),
]));
$form_state->setRedirect('trash.default', ['entity_type_id' => $this->entity->getEntityTypeId()]);
}
}
<?php
namespace Drupal\trash\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines dynamic local tasks for trash module.
*/
class TrashLocalTasks extends DeriverBase implements ContainerDeriverInterface {
/**
* The Entity Type Manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The moderation information service.
*
* @var \Drupal\content_moderation\ModerationInformationInterface
*/
protected $moderationInformation;
/**
* Constructs a TrashLocalTasks object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity type manager service.
* @param \Drupal\content_moderation\ModerationInformationInterface $moderation_information
* The moderation information service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_information) {
$this->entityTypeManager = $entity_type_manager;
$this->moderationInformation = $moderation_information;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container->get('entity_type.manager'),
$container->get('content_moderation.moderation_information')
);
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
foreach ($this->getModeratedEntityTypes() as $entity_type_id => $entity_type) {
$this->derivatives["trash_$entity_type_id"] = $base_plugin_definition;
$this->derivatives["trash_$entity_type_id"]['title'] = $entity_type->get('label');
$this->derivatives["trash_$entity_type_id"]['route_name'] = 'trash.entity_list';
$this->derivatives["trash_$entity_type_id"]['parent_id'] = 'trash.default';
$this->derivatives["trash_$entity_type_id"]['route_parameters'] = ['entity_type_id' => $entity_type_id];
}
return $this->derivatives;
}
/**
* Returns the list of moderated entity types.
*
* @return \Drupal\Core\Entity\EntityTypeInterface[]|null
* Returns the list of moderated entity types.
*/
protected function getModeratedEntityTypes() {
$entity_types = $this->entityTypeManager->getDefinitions();
return array_filter($entity_types, function (EntityTypeInterface $entity_type) use ($entity_types) {
return $this->moderationInformation->canModerateEntitiesOfEntityType($entity_type);
});
}
}
<?php
namespace Drupal\trash\Plugin\Menu;
use Drupal\Core\Menu\LocalTaskDefault;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
class DeleteTab extends LocalTaskDefault implements ContainerFactoryPluginInterface {
use StringTranslationTrait;
/**
* @var \Drupal\content_moderation\ModerationInformation
*/
public $moderationInformation;
/**
* The entity.
*
* @var \Drupal\Core\Entity\ContentEntityInterface
*/
protected $entity;
/**
* Constructs a new DeleteTab object.
*
* @param array $configuration
* Plugin configuration.
* @param string $plugin_id
* Plugin ID.
* @param mixed $plugin_definition
* Plugin definition.
* @param \Drupal\content_moderation\ModerationInformation $moderation_information
* The moderation information.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, $moderation_information) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->moderationInformation = $moderation_information;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('content_moderation.moderation_information')
);
}
/**
* {@inheritdoc}
*/
public function getRouteParameters(RouteMatchInterface $route_match) {
// Override the node here with the latest revision.
$this->entity = $route_match->getParameter($this->pluginDefinition['entity_type_id']);
return parent::getRouteParameters($route_match);
}
/**
* {@inheritdoc}
*/
public function getTitle() {
return $this->moderationInformation->isModeratedEntity($this->entity)
? $this->t('Move to trash')
: $this->t('Delete');
}
}
\ No newline at end of file
<?php
namespace Drupal\trash\Plugin\views\field;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;
/**
* @ViewsField("trash_content_moderation_state_entity_label")
*/
class EntityLabel extends FieldPluginBase {
use FieldTrait;
/**
* {@inheritdoc}
*/
public function render(ResultRow $values) {
if (isset($values->_content_moderated_entity) && $values->_content_moderated_entity instanceof ContentEntityInterface) {
return $values->_content_moderated_entity->label();
}
}
/**
* {@inheritdoc}
*/
public function query() {
$this->ensureMyTable();
}
}
<?php
namespace Drupal\trash\Plugin\views\field;
use Drupal\Core\Url;
use Drupal\views\ResultRow;
/**
* @ViewsField("trash_content_moderation_state_entity_operations")
*/
class EntityOperations extends \Drupal\views\Plugin\views\field\EntityOperations {
/**
* {@inheritdoc}
*/
public function render(ResultRow $values) {
if (!isset($values->_content_moderated_entity)) {
return '';
}
/** @var \Drupal\content_moderation\ContentModerationStateInterface $entity */
$entity = $values->_content_moderated_entity;
$operations = [];
$operations['restore'] = [
'title' => t('Restore'),
'url' => Url::fromRoute('trash.restore_form', [
'entity_type_id' => $entity->getEntityTypeId(),
'entity_id' => $entity->id(),
], $this->getDestinationArray()),
];
$operations['purge'] = [
'title' => t('Purge'),
'url' => Url::fromRoute('trash.purge_form', [
'entity_type_id' => $entity->getEntityTypeId(),
'entity_id' => $entity->id(),
], $this->getDestinationArray()),
];
$build = [
'#type' => 'operations',
'#links' => $operations,
];
return $build;
}
/**
* {@inheritdoc}
*/
public function query() {
$this->ensureMyTable();
}
}
<?php
namespace Drupal\trash\Plugin\views\field;
trait FieldTrait {
/**
* {@inheritdoc}
*/
public function preRender(&$values) {
$entity_ids_by_type = [];
/** @var \Drupal\views\ResultRow $result_row */
foreach ($values as $result_row) {
if (!isset($result_row->_content_moderated_entity)) {
$entity_ids_by_type[$result_row->_entity->get('content_entity_type_id')->value][$result_row->_entity->get('content_entity_id')->value] = $result_row->_entity->id();
}
}
$entity_type_manager = \Drupal::entityTypeManager();
$entities = [];
foreach ($entity_ids_by_type as $entity_type => $entity_ids) {
$entities[$entity_type] = $entity_type_manager->getStorage($entity_type)->loadMultiple($entity_ids);
}
foreach ($values as $result_row) {
if (isset($entities[$result_row->_entity->get('content_entity_type_id')->value][$result_row->_entity->get('content_entity_id')->value])) {
$result_row->_content_moderated_entity = $entities[$result_row->_entity->get('content_entity_type_id')->value][$result_row->_entity->get('content_entity_id')->value];
}
}
}
}
<?php
namespace Drupal\trash\Routing;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Subscriber for content entity trash routes.
*/
class TrashRouteSubscriber extends RouteSubscriberBase {
/**
* The Entity Type Manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The moderation information service.
*
* @var \Drupal\content_moderation\ModerationInformationInterface
*/
protected $moderationInformation;
/**
* Constructs a TrashRouteSubscriber object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity type manager service.
* @param \Drupal\content_moderation\ModerationInformationInterface $moderation_information
* The moderation information service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_information) {
$this->entityTypeManager = $entity_type_manager;
$this->moderationInformation = $moderation_information;
}
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection) {
foreach ($this->getModeratedEntityTypes() as $entity_type_id => $entity_type) {
$route = $collection->get("entity.$entity_type_id.delete_form");
if (!empty($route)) {
$defaults = $route->getDefaults();
unset($defaults['_entity_form']);
$defaults['_controller'] = '\Drupal\trash\Controller\TrashDeleteController::trashEntity';
$route->setDefaults($defaults);
$route->setOption('parameters', [$entity_type_id => ['type' => 'entity:' . $entity_type_id]]);
$route->setRequirement('_csrf_token', 'TRUE');
}
}
}
/**
* Returns the list of Moderated content entity types.
*
* @return \Drupal\Core\Entity\ContentEntityInterface[]
* An array of entity objects indexed by their IDs. Returns an empty array
* if no matching entities are found.
*/
protected function getModeratedEntityTypes() {
$entity_types = $this->entityTypeManager->getDefinitions();
return array_filter($entity_types, function (EntityTypeInterface $entity_type) use ($entity_types) {
return $this->moderationInformation->canModerateEntitiesOfEntityType($entity_type);
});
}
}
<?php
namespace Drupal\trash;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
class TrashManager implements TrashManagerInterface {
/**
* The Entity Type Manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public function trash(ContentEntityInterface $entity) {
$entity->set('moderation_state', TrashModerationState::TRASHED);
$entity->save();
}
/**
* {@inheritdoc}
*/
public function restore(ContentEntityInterface $entity) {
$storage = $this->entityTypeManager->getStorage('content_moderation_state');
$entities = $storage
->getQuery()
->allRevisions()
->condition('content_entity_type_id', $entity->getEntityTypeId())
->condition('content_entity_id', $entity->id())
->condition('content_entity_revision_id', $entity->getRevisionId(), '<')
->condition('moderation_state', TrashModerationState::TRASHED, '!=')
->sort('content_entity_revision_id', 'DESC')
->range(0, 1)
->execute();
$revision_ids = array_keys($entities);
$revision_id = reset($revision_ids);
$content_moderation_state = $storage->loadRevision($revision_id);
$entity->set('moderation_state', $content_moderation_state->moderation_state->target_id);
$entity->save();
}
}
<?php
namespace Drupal\trash;
use Drupal\Core\Entity\ContentEntityInterface;
interface TrashManagerInterface {
/**
* Moves an entity to trash.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* A content entity object.
*/
public function trash(ContentEntityInterface $entity);
/**
* Restores an entity from trash.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* A content entity object.
*/
public function restore(ContentEntityInterface $entity);
}
<?php
namespace Drupal\trash;
class TrashModerationState {
const TRASHED = 'archived';
}
\ No newline at end of file
<?php
namespace Drupal\Tests\trash\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the delete tab on nodes and blocks.
*
* @group trash
*/
class AdminContentDeleteTest extends BrowserTestBase {
use TrashTestTrait;
public static $modules = [
'block_content',
'node',
'content_moderation',
'trash',
];
protected $permissions = [
'administer moderation states',
'view any unpublished content',
'use published_archived transition',
'use draft_draft transition',
'use published_draft transition',
'use published_published transition',
'use draft_published transition',
'use archived_published transition',
'use archived_draft transition',
'access content overview',
];
/**
* Test Node delete.
*/
public function testNodeDelete() {
$node_type = $this->createNodeType('test');
$this->permissions = array_merge($this->permissions,
[
'administer nodes',
'administer content types',
'access content',
'create test content',
'edit own test content',
'delete own test content',
]);
$editor1 = $this->drupalCreateUser($this->permissions);
$this->drupalLogin($editor1);
$session = $this->getSession();
$this->createContent($node_type, 'Test content', FALSE);
$this->drupalGet('/admin/content');
$this->assertTrue($session->getPage()->hasContent("Test content"));
$session->getPage()->clickLink('Delete');
$session->getPage()->findButton(t('Delete'))->click();
$this->drupalGet('/admin/content');
$this->assertFalse($session->getPage()->hasContent("Test content"));
$this->drupalGet('/admin/structure/types/manage/test/moderation');
$page = $session->getPage();
$page->checkField('edit-enable-moderation-state');
$page->findButton(t('Save'))->click();
$this->createContent($node_type, 'Moderated content');
$this->drupalGet('/admin/content');
$this->assertTrue($session->getPage()->hasContent("Moderated content"));
$session->getPage()->clickLink('Delete');
$this->assertTrue($session->getPage()->hasContent("The Content Moderated content has been moved to the trash."));
$this->drupalGet('/admin/trash');
$this->assertTrue($session->getPage()->hasContent("Moderated content"));
}
/**
* Test Block Delete.
*/
public function testBlockDelete() {
$block_type = $this->createBlockContentType('test');
$this->permissions = array_merge($this->permissions,
[
'administer blocks',
]);
$editor1 = $this->drupalCreateUser($this->permissions);
$this->drupalLogin($editor1);
$session = $this->getSession();
$this->createBlock($block_type, 'Test block', FALSE);
$this->drupalGet('/admin/structure/block/block-content');
$this->assertTrue($session->getPage()->hasContent('Test block'));
$session->getPage()->clickLink('Delete');
$session->getPage()->findButton(t('Delete'))->click();
$this->drupalGet('/admin/structure/block/block-content');
$this->assertFalse($session->getPage()->hasContent('Test block'));
$this->drupalGet('admin/structure/block/block-content/manage/' . $block_type->id() . '/moderation');
$page = $session->getPage();
$page->checkField('edit-enable-moderation-state');
$page->findButton(t('Save'))->click();
$this->createBlock($block_type, 'Moderated block');
$this->drupalGet('/admin/structure/block/block-content');
$this->assertTrue($session->getPage()->hasContent('Moderated block'));
$session->getPage()->clickLink('Delete');
$this->assertTrue($session->getPage()->hasContent("The Custom block Moderated block has been moved to the trash."));
$this->drupalGet('/admin/trash');
$this->assertTrue($session->getPage()->hasContent("Moderated block"));
}
}
<?php
namespace Drupal\Tests\trash\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests purging nodes and blocks from trash.
*
* @group trash
*/
class PurgeEntityTest extends BrowserTestBase {
use TrashTestTrait;
public static $modules = [
'block_content',
'node',
'content_moderation',
'trash'
];
protected $permissions = [
'administer moderation states',
'view any unpublished content',
'use published_archived transition',
'use draft_draft transition',
'use published_draft transition',
'use published_published transition',
'use draft_published transition',
'use archived_published transition',
'use archived_draft transition',
'access content overview',
];
public function testPurgeNode() {
$node_type = $this->createNodeType('test');
$this->permissions = array_merge($this->permissions,
[
'administer nodes',
'administer content types',
'access content',
'create test content',
'edit own test content',
'delete own test content',
]);
$editor1 = $this->drupalCreateUser($this->permissions);
$this->drupalLogin($editor1);
$session = $this->getSession();
$this->drupalGet('/admin/structure/types/manage/test/moderation');
$page = $session->getPage();
$page->checkField('edit-enable-moderation-state');
$page->findButton(t('Save'))->click();
$this->createContent($node_type, 'Moderated content');
$this->drupalGet('/admin/content');
$this->assertTrue($session->getPage()->hasContent("Moderated content"));
$session->getPage()->clickLink('Delete');
$this->assertTrue($session->getPage()->hasContent("The Content Moderated content has been moved to the trash."));
$this->drupalGet('/admin/trash');
$this->assertTrue($session->getPage()->hasContent('Moderated content'));
$session->getPage()->clickLink('Purge');
$session->getPage()->findButton(t('Purge'))->click();
$this->drupalGet('/admin/content');
$this->assertFalse($session->getPage()->hasContent("Moderated content"));
}
public function testPurgeBlock() {
$block_type = $this->createBlockContentType('test');
$this->permissions = array_merge($this->permissions,
[
'administer blocks',
]);
$editor1 = $this->drupalCreateUser($this->permissions);
$this->drupalLogin($editor1);
$session = $this->getSession();
$this->drupalGet('admin/structure/block/block-content/manage/' . $block_type->id() . '/moderation');
$page = $session->getPage();
$page->checkField('edit-enable-moderation-state');
$page->findButton(t('Save'))->click();
$this->createBlock($block_type, 'Moderated block');
$this->drupalGet('/admin/structure/block/block-content');
$this->assertTrue($session->getPage()->hasContent('Moderated block'));
$session->getPage()->clickLink('Delete');
$this->assertTrue($session->getPage()->hasContent("The Custom block Moderated block has been moved to the trash."));
$this->drupalGet('/admin/trash');
$this->assertTrue($session->getPage()->hasContent("Moderated block"));
$session->getPage()->clickLink('Purge');
$session->getPage()->findButton(t('Purge'))->click();
$this->drupalGet('/admin/structure/block/block-content');
$this->assertFalse($session->getPage()->hasContent('Moderated block'));
}
}
<?php
namespace Drupal\Tests\trash\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests restoring nodes and blocks from trash.
*
* @group trash
*/
class RestoreEntityTest extends BrowserTestBase {
use TrashTestTrait;
public static $modules = [
'block_content',
'node',
'content_moderation',
'trash'
];
protected $permissions = [
'administer moderation states',
'view any unpublished content',
'use published_archived transition',
'use draft_draft transition',
'use published_draft transition',
'use published_published transition',
'use draft_published transition',
'use archived_published transition',
'use archived_draft transition',
'access content overview',
];
public function testRestoreNode() {
$node_type = $this->createNodeType('test');
$this->permissions = array_merge($this->permissions,
[
'administer nodes',
'administer content types',
'access content',
'create test content',
'edit own test content',
'delete own test content',
]);
$editor1 = $this->drupalCreateUser($this->permissions);
$this->drupalLogin($editor1);
$session = $this->getSession();
$this->drupalGet('/admin/structure/types/manage/test/moderation');
$page = $session->getPage();
$page->checkField('edit-enable-moderation-state');
$page->findButton(t('Save'))->click();
$this->createContent($node_type, 'Moderated content');
$this->drupalGet('/admin/content');
$this->assertTrue($session->getPage()->hasContent("Moderated content"));
$session->getPage()->clickLink('Delete');
$this->assertTrue($session->getPage()->hasContent("The Content Moderated content has been moved to the trash."));
$this->drupalGet('/admin/trash');
$this->assertTrue($session->getPage()->hasContent('Moderated content'));
$session->getPage()->clickLink('Restore');
$session->getPage()->findButton(t('Restore'))->click();
$this->assertModerationState('node', 'Moderated content', 'published');
}
public function testRestoreBlock() {
$block_type = $this->createBlockContentType('test');
$this->permissions = array_merge($this->permissions,
[
'administer blocks',
]);
$editor1 = $this->drupalCreateUser($this->permissions);
$this->drupalLogin($editor1);
$session = $this->getSession();
$this->drupalGet('admin/structure/block/block-content/manage/' . $block_type->id() . '/moderation');
$page = $session->getPage();
$page->checkField('edit-enable-moderation-state');
$page->findButton(t('Save'))->click();
$this->createBlock($block_type, 'Moderated block');
$this->drupalGet('/admin/structure/block/block-content');
$this->assertTrue($session->getPage()->hasContent('Moderated block'));
$session->getPage()->clickLink('Delete');
$this->assertTrue($session->getPage()->hasContent("The Custom block Moderated block has been moved to the trash."));
$this->drupalGet('/admin/trash');
$this->assertTrue($session->getPage()->hasContent("Moderated block"));
$session->getPage()->clickLink('Restore');
$session->getPage()->findButton(t('Restore'))->click();
$this->assertModerationState('block_content', 'Moderated block', 'published');
}
}
<?php
namespace Drupal\Tests\trash\Functional;
use Drupal\block_content\BlockContentTypeInterface;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\node\Entity\NodeType;
use Drupal\node\NodeTypeInterface;
/**
*
*/
trait TrashTestTrait {
/**
* @param string $name
*
* @return \Drupal\node\NodeTypeInterface
*/
public function createNodeType($name) {
$node_type = NodeType::create([
'type' => $name,
'label' => $name,
]);
$node_type->save();
$this->assertTrue($node_type instanceof NodeTypeInterface);
return $node_type;
}
/**
* @param \Drupal\node\NodeTypeInterface $node_type
* @param string $title
* @param bool $moderated
*/
public function createContent(NodeTypeInterface $node_type, $title, $moderated = TRUE) {
$session = $this->getSession();
$this->drupalGet('/node/add/' . $node_type->id());
$this->assertSession()->statusCodeEquals(200);
$page = $session->getPage();
$page->fillField('Title', $title);
if ($moderated) {
$page->findButton('Save and Publish')->click();
}
else {
$page->findButton(t('Save'))->click();
}
$this->assertTrue($session->getPage()->hasContent("{$title} has been created"));
}
/**
* @param string $entity_type_id
* @param string $title
* @param string $moderation_state
*/
public function assertModerationState($entity_type_id, $title, $moderation_state) {
$storage = \Drupal::entityTypeManager()->getStorage($entity_type_id);
$label = $storage->getEntityType()->getKey('label');
$entities = $storage->loadByProperties([$label => $title]);
$entity = current($entities);
$this->assertTrue($entity->moderation_state->target_id == $moderation_state);
}
/**
* @param string $name
*
* @return \Drupal\block_content\BlockContentTypeInterface
*/
public function createBlockContentType($name) {
$block_type = BlockContentType::create([
'id' => $name,
'label' => $name,
]);
$block_type->save();
$this->assertTrue($block_type instanceof BlockContentTypeInterface);
return $block_type;
}
/**
* Create Block.
*/
public function createBlock(BlockContentTypeInterface $block_type, $title, $moderated = TRUE) {
$session = $this->getSession();
$this->drupalGet('/block/add/' . $block_type->id());
$this->assertSession()->statusCodeEquals(200);
$page = $session->getPage();
$page->fillField('Block description', $title);
if ($moderated) {
$page->findButton('Save and Publish')->click();
}
else {
$page->findButton(t('Save'))->click();
}
$this->assertTrue($session->getPage()->hasContent("{$block_type->id()} {$title} has been created"));
}
}
<?php
namespace Drupal\Tests\trash\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests undoing trashing of nodes and blocks from trash.
*
* @group trash
*/
class UndoTrashEntityTest extends BrowserTestBase {
use TrashTestTrait;
public static $modules = [
'block_content',
'node',
'content_moderation',
'trash'
];
protected $permissions = [
'administer moderation states',
'view any unpublished content',
'use published_archived transition',
'use draft_draft transition',
'use published_draft transition',
'use published_published transition',
'use draft_published transition',
'use archived_published transition',
'use archived_draft transition',
'access content overview',
];
public function testUndoTrashNode() {
$node_type = $this->createNodeType('test');
$this->permissions = array_merge($this->permissions,
[
'administer nodes',
'administer content types',
'access content',
'create test content',
'edit own test content',
'delete own test content',
]);
$editor1 = $this->drupalCreateUser($this->permissions);
$this->drupalLogin($editor1);
$session = $this->getSession();
$this->drupalGet('/admin/structure/types/manage/test/moderation');
$page = $session->getPage();
$page->checkField('edit-enable-moderation-state');
$page->findButton(t('Save'))->click();
$this->createContent($node_type, 'Moderated content');
$this->drupalGet('/admin/content');
$this->assertTrue($session->getPage()->hasContent("Moderated content"));
$session->getPage()->clickLink('Delete');
$this->assertTrue($session->getPage()->hasContent("The Content Moderated content has been moved to the trash."));
$this->clickLink('Undo');
$session->getPage()->findButton(t('Restore'))->click();
$this->assertModerationState('node', 'Moderated content', 'published');
}
public function testUndoTrashBlock() {
$block_type = $this->createBlockContentType('test');
$this->permissions = array_merge($this->permissions,
[
'administer blocks',
]);
$editor1 = $this->drupalCreateUser($this->permissions);
$this->drupalLogin($editor1);
$session = $this->getSession();
$this->drupalGet('admin/structure/block/block-content/manage/' . $block_type->id() . '/moderation');
$page = $session->getPage();
$page->checkField('edit-enable-moderation-state');
$page->findButton(t('Save'))->click();
$this->createBlock($block_type, 'Moderated block');
$this->drupalGet('/admin/structure/block/block-content');
$this->assertTrue($session->getPage()->hasContent('Moderated block'));
$session->getPage()->clickLink('Delete');
$this->assertTrue($session->getPage()->hasContent("The Custom block Moderated block has been moved to the trash."));
$this->clickLink('Undo');
$session->getPage()->findButton(t('Restore'))->click();
$this->assertModerationState('block_content', 'Moderated block', 'published');
}
}
name: Trash
type: module
description: 'Displays deleted entities and allows them to be restored.'
version: VERSION
core: 8.x
package: Core (Experimental)
dependencies:
- content_moderation
trash.default:
title: Trash
route_name: trash.default
base_route: system.admin_content
weight: 100
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment