updated core
This commit is contained in:
@@ -8,8 +8,8 @@ configure: entity.workflow.collection
|
||||
dependencies:
|
||||
- workflows
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
# Information added by Drupal.org packaging script on 2018-01-03
|
||||
version: '8.4.4'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1509719929
|
||||
datestamp: 1515021228
|
||||
|
||||
@@ -3,7 +3,7 @@ view any unpublished content:
|
||||
|
||||
view latest version:
|
||||
title: 'View the latest version'
|
||||
description: 'Requires the "View any unpublished content" permission'
|
||||
description: 'Requires the "View any unpublished content" or "View own unpublished content" permission'
|
||||
|
||||
permission_callbacks:
|
||||
- \Drupal\content_moderation\Permissions::transitionPermissions
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\content_moderation;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
|
||||
/**
|
||||
* An interface for Content moderation state entity.
|
||||
*
|
||||
* Content moderation state entities track the moderation state of other content
|
||||
* entities.
|
||||
*/
|
||||
interface ContentModerationStateInterface extends ContentEntityInterface, EntityOwnerInterface {
|
||||
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class EntityTypeInfo implements ContainerInjectionInterface {
|
||||
/**
|
||||
* Adds Moderation configuration to appropriate entity types.
|
||||
*
|
||||
* @param EntityTypeInterface[] $entity_types
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
|
||||
* The master entity type list to alter.
|
||||
*
|
||||
* @see hook_entity_type_alter()
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\content_moderation\Form;
|
||||
|
||||
use Drupal\content_moderation\Plugin\WorkflowType\ContentModeration;
|
||||
use Drupal\workflows\WorkflowInterface;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Form for configuring moderation usage on a given entity bundle.
|
||||
*/
|
||||
class BundleModerationConfigurationForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* Entity Type Manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('entity_type.manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Blank out the base form ID so that form alters that use the base form ID to
|
||||
* target both add and edit forms don't pick up this form.
|
||||
*/
|
||||
public function getBaseFormId() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
/* @var \Drupal\Core\Config\Entity\ConfigEntityInterface $bundle */
|
||||
$bundle = $this->getEntity();
|
||||
$bundle_of_entity_type = $this->entityTypeManager->getDefinition($bundle->getEntityType()->getBundleOf());
|
||||
/* @var \Drupal\workflows\WorkflowInterface[] $workflows */
|
||||
$workflows = $this->entityTypeManager->getStorage('workflow')->loadMultiple();
|
||||
|
||||
$options = array_map(function (WorkflowInterface $workflow) {
|
||||
return $workflow->label();
|
||||
}, array_filter($workflows, function (WorkflowInterface $workflow) {
|
||||
return $workflow->status() && $workflow->getTypePlugin() instanceof ContentModeration;
|
||||
}));
|
||||
|
||||
$selected_workflow = array_reduce($workflows, function ($carry, WorkflowInterface $workflow) use ($bundle_of_entity_type, $bundle) {
|
||||
$plugin = $workflow->getTypePlugin();
|
||||
if ($plugin instanceof ContentModeration && $plugin->appliesToEntityTypeAndBundle($bundle_of_entity_type->id(), $bundle->id())) {
|
||||
return $workflow->id();
|
||||
}
|
||||
return $carry;
|
||||
});
|
||||
$form['workflow'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Select the workflow to apply'),
|
||||
'#default_value' => $selected_workflow,
|
||||
'#options' => $options,
|
||||
'#required' => FALSE,
|
||||
'#empty_value' => '',
|
||||
];
|
||||
|
||||
$form['original_workflow'] = [
|
||||
'#type' => 'value',
|
||||
'#value' => $selected_workflow,
|
||||
];
|
||||
|
||||
$form['bundle'] = [
|
||||
'#type' => 'value',
|
||||
'#value' => $bundle->id(),
|
||||
];
|
||||
|
||||
$form['entity_type'] = [
|
||||
'#type' => 'value',
|
||||
'#value' => $bundle_of_entity_type->id(),
|
||||
];
|
||||
|
||||
// Add a special message when moderation is being disabled.
|
||||
if ($selected_workflow) {
|
||||
$form['enable_workflow_note'] = [
|
||||
'#type' => 'item',
|
||||
'#description' => $this->t('After disabling moderation, any existing forward drafts will be accessible via the "Revisions" tab.'),
|
||||
'#access' => !empty($selected_workflow)
|
||||
];
|
||||
}
|
||||
|
||||
return parent::form($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
// If moderation is enabled, revisions MUST be enabled as well. Otherwise we
|
||||
// can't have forward revisions.
|
||||
drupal_set_message($this->t('Your settings have been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$entity_type_id = $form_state->getValue('entity_type');
|
||||
$bundle_id = $form_state->getValue('bundle');
|
||||
$new_workflow_id = $form_state->getValue('workflow');
|
||||
$original_workflow_id = $form_state->getValue('original_workflow');
|
||||
if ($new_workflow_id === $original_workflow_id) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
if ($original_workflow_id) {
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entityTypeManager->getStorage('workflow')->load($original_workflow_id);
|
||||
$workflow->getTypePlugin()->removeEntityTypeAndBundle($entity_type_id, $bundle_id);
|
||||
$workflow->save();
|
||||
}
|
||||
if ($new_workflow_id) {
|
||||
/* @var \Drupal\workflows\WorkflowInterface $workflow */
|
||||
$workflow = $this->entityTypeManager->getStorage('workflow')->load($new_workflow_id);
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle($entity_type_id, $bundle_id);
|
||||
$workflow->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#submit' => ['::submitForm', '::save'],
|
||||
];
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Drupal\content_moderation\Form;
|
||||
|
||||
use Drupal\Component\Datetime\Time;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\RevisionLogInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
@@ -23,6 +24,13 @@ class EntityModerationForm extends FormBase {
|
||||
*/
|
||||
protected $moderationInfo;
|
||||
|
||||
/**
|
||||
* The time service.
|
||||
*
|
||||
* @var \Drupal\Component\Datetime\Time
|
||||
*/
|
||||
protected $time;
|
||||
|
||||
/**
|
||||
* The moderation state transition validation service.
|
||||
*
|
||||
@@ -37,9 +45,12 @@ class EntityModerationForm extends FormBase {
|
||||
* The moderation information service.
|
||||
* @param \Drupal\content_moderation\StateTransitionValidation $validation
|
||||
* The moderation state transition validation service.
|
||||
* @param \Drupal\Component\Datetime\Time $time
|
||||
* The time service.
|
||||
*/
|
||||
public function __construct(ModerationInformationInterface $moderation_info, StateTransitionValidation $validation) {
|
||||
public function __construct(ModerationInformationInterface $moderation_info, StateTransitionValidation $validation, Time $time) {
|
||||
$this->moderationInfo = $moderation_info;
|
||||
$this->time = $time;
|
||||
$this->validation = $validation;
|
||||
}
|
||||
|
||||
@@ -49,7 +60,8 @@ class EntityModerationForm extends FormBase {
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('content_moderation.moderation_information'),
|
||||
$container->get('content_moderation.state_transition_validation')
|
||||
$container->get('content_moderation.state_transition_validation'),
|
||||
$container->get('datetime.time')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,7 +134,7 @@ class EntityModerationForm extends FormBase {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
/** @var ContentEntityInterface $entity */
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $form_state->get('entity');
|
||||
|
||||
$new_state = $form_state->getValue('new_state');
|
||||
@@ -130,6 +142,7 @@ class EntityModerationForm extends FormBase {
|
||||
$entity->set('moderation_state', $new_state);
|
||||
|
||||
if ($entity instanceof RevisionLogInterface) {
|
||||
$entity->setRevisionCreationTime($this->time->getRequestTime());
|
||||
$entity->setRevisionLogMessage($form_state->getValue('revision_log'));
|
||||
$entity->setRevisionUserId($this->currentUser()->id());
|
||||
}
|
||||
|
||||
@@ -84,14 +84,15 @@ class ModerationInformation implements ModerationInformationInterface {
|
||||
*/
|
||||
public function getLatestRevisionId($entity_type_id, $entity_id) {
|
||||
if ($storage = $this->entityTypeManager->getStorage($entity_type_id)) {
|
||||
$revision_ids = $storage->getQuery()
|
||||
->allRevisions()
|
||||
$result = $storage->getQuery()
|
||||
->latestRevision()
|
||||
->condition($this->entityTypeManager->getDefinition($entity_type_id)->getKey('id'), $entity_id)
|
||||
->sort($this->entityTypeManager->getDefinition($entity_type_id)->getKey('revision'), 'DESC')
|
||||
->range(0, 1)
|
||||
// No access check is performed here since this is an API function and
|
||||
// should return the same ID regardless of the current user.
|
||||
->accessCheck(FALSE)
|
||||
->execute();
|
||||
if ($revision_ids) {
|
||||
return array_keys($revision_ids)[0];
|
||||
if ($result) {
|
||||
return key($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,13 +102,15 @@ class ModerationInformation implements ModerationInformationInterface {
|
||||
*/
|
||||
public function getDefaultRevisionId($entity_type_id, $entity_id) {
|
||||
if ($storage = $this->entityTypeManager->getStorage($entity_type_id)) {
|
||||
$revision_ids = $storage->getQuery()
|
||||
$result = $storage->getQuery()
|
||||
->currentRevision()
|
||||
->condition($this->entityTypeManager->getDefinition($entity_type_id)->getKey('id'), $entity_id)
|
||||
->sort($this->entityTypeManager->getDefinition($entity_type_id)->getKey('revision'), 'DESC')
|
||||
->range(0, 1)
|
||||
// No access check is performed here since this is an API function and
|
||||
// should return the same ID regardless of the current user.
|
||||
->accessCheck(FALSE)
|
||||
->execute();
|
||||
if ($revision_ids) {
|
||||
return array_keys($revision_ids)[0];
|
||||
if ($result) {
|
||||
return key($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ class ModerationStateFieldItemList extends FieldItemList {
|
||||
|
||||
// Change the entity's default revision flag and the publishing status only
|
||||
// if the new workflow state is a valid one.
|
||||
if ($workflow->getTypePlugin()->hasState($moderation_state_id)) {
|
||||
if ($workflow && $workflow->getTypePlugin()->hasState($moderation_state_id)) {
|
||||
/** @var \Drupal\content_moderation\ContentModerationState $current_state */
|
||||
$current_state = $workflow->getTypePlugin()->getState($moderation_state_id);
|
||||
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\content_moderation\Plugin\views\filter;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\views\Plugin\views\filter\FilterPluginBase;
|
||||
use Drupal\views\Plugin\ViewsHandlerManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Filter to show only the latest revision of an entity.
|
||||
*
|
||||
* @ingroup views_filter_handlers
|
||||
*
|
||||
* @ViewsFilter("latest_revision")
|
||||
*/
|
||||
class LatestRevision extends FilterPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* Entity Type Manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Views Handler Plugin Manager.
|
||||
*
|
||||
* @var \Drupal\views\Plugin\ViewsHandlerManager
|
||||
*/
|
||||
protected $joinHandler;
|
||||
|
||||
/**
|
||||
* Database Connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Constructs a new LatestRevision.
|
||||
*
|
||||
* @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\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* Entity Type Manager Service.
|
||||
* @param \Drupal\views\Plugin\ViewsHandlerManager $join_handler
|
||||
* Views Handler Plugin Manager.
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* Database Connection.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ViewsHandlerManager $join_handler, Connection $connection) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->joinHandler = $join_handler;
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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('plugin.manager.views.join'),
|
||||
$container->get('database')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function adminSummary() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function operatorForm(&$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canExpose() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
// The table doesn't exist until a moderated node has been saved at least
|
||||
// once. Just in case, disable this filter until then. Note that this means
|
||||
// the view will still show all revisions, not just latest, but this is
|
||||
// sufficiently edge-case-y that it's probably not worth the time to
|
||||
// handle more robustly.
|
||||
if (!$this->connection->schema()->tableExists('content_revision_tracker')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$table = $this->ensureMyTable();
|
||||
|
||||
/** @var \Drupal\views\Plugin\views\query\Sql $query */
|
||||
$query = $this->query;
|
||||
|
||||
$definition = $this->entityTypeManager->getDefinition($this->getEntityType());
|
||||
$keys = $definition->getKeys();
|
||||
|
||||
$definition = [
|
||||
'table' => 'content_revision_tracker',
|
||||
'type' => 'INNER',
|
||||
'field' => 'entity_id',
|
||||
'left_table' => $table,
|
||||
'left_field' => $keys['id'],
|
||||
'extra' => [
|
||||
['left_field' => $keys['langcode'], 'field' => 'langcode'],
|
||||
['left_field' => $keys['revision'], 'field' => 'revision_id'],
|
||||
['field' => 'entity_type', 'value' => $this->getEntityType()],
|
||||
],
|
||||
];
|
||||
|
||||
$join = $this->joinHandler->createInstance('standard', $definition);
|
||||
|
||||
$query->ensureTable('content_revision_tracker', $this->relationship, $join);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\content_moderation;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
use Drupal\Core\Database\SchemaObjectExistsException;
|
||||
|
||||
/**
|
||||
* Tracks metadata about revisions across entities.
|
||||
*/
|
||||
class RevisionTracker implements RevisionTrackerInterface {
|
||||
|
||||
/**
|
||||
* The name of the SQL table we use for tracking.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tableName;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Constructs a new RevisionTracker.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
* @param string $table
|
||||
* The table that should be used for tracking.
|
||||
*/
|
||||
public function __construct(Connection $connection, $table = 'content_revision_tracker') {
|
||||
$this->connection = $connection;
|
||||
$this->tableName = $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id) {
|
||||
try {
|
||||
$this->recordLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id);
|
||||
}
|
||||
catch (DatabaseExceptionWrapper $e) {
|
||||
$this->ensureTableExists();
|
||||
$this->recordLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the latest revision of a given entity.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The machine name of the type of entity.
|
||||
* @param string $entity_id
|
||||
* The Entity ID in question.
|
||||
* @param string $langcode
|
||||
* The langcode of the revision we're saving. Each language has its own
|
||||
* effective tree of entity revisions, so in different languages
|
||||
* different revisions will be "latest".
|
||||
* @param int $revision_id
|
||||
* The revision ID that is now the latest revision.
|
||||
*
|
||||
* @return int
|
||||
* One of the valid returns from a merge query's execute method.
|
||||
*/
|
||||
protected function recordLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id) {
|
||||
return $this->connection->merge($this->tableName)
|
||||
->keys([
|
||||
'entity_type' => $entity_type_id,
|
||||
'entity_id' => $entity_id,
|
||||
'langcode' => $langcode,
|
||||
])
|
||||
->fields([
|
||||
'revision_id' => $revision_id,
|
||||
])
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the table exists and create it if not.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the table was created, FALSE otherwise.
|
||||
*/
|
||||
protected function ensureTableExists() {
|
||||
try {
|
||||
if (!$this->connection->schema()->tableExists($this->tableName)) {
|
||||
$this->connection->schema()->createTable($this->tableName, $this->schemaDefinition());
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
catch (SchemaObjectExistsException $e) {
|
||||
// If another process has already created the table, attempting to
|
||||
// recreate it will throw an exception. In this case just catch the
|
||||
// exception and do nothing.
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the schema for the tracker table.
|
||||
*
|
||||
* @return array
|
||||
* The schema API definition for the SQL storage table.
|
||||
*/
|
||||
protected function schemaDefinition() {
|
||||
$schema = [
|
||||
'description' => 'Tracks the latest revision for any entity',
|
||||
'fields' => [
|
||||
'entity_type' => [
|
||||
'description' => 'The entity type',
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
],
|
||||
'entity_id' => [
|
||||
'description' => 'The entity ID',
|
||||
'type' => 'int',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
],
|
||||
'langcode' => [
|
||||
'description' => 'The language of the entity revision',
|
||||
'type' => 'varchar',
|
||||
'length' => 12,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
],
|
||||
'revision_id' => [
|
||||
'description' => 'The latest revision ID for this entity',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
],
|
||||
],
|
||||
'primary key' => ['entity_type', 'entity_id', 'langcode'],
|
||||
];
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\content_moderation;
|
||||
|
||||
/**
|
||||
* Tracks metadata about revisions across content entities.
|
||||
*/
|
||||
interface RevisionTrackerInterface {
|
||||
|
||||
/**
|
||||
* Sets the latest revision of a given entity.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The machine name of the type of entity.
|
||||
* @param string $entity_id
|
||||
* The Entity ID in question.
|
||||
* @param string $langcode
|
||||
* The langcode of the revision we're saving. Each language has its own
|
||||
* effective tree of entity revisions, so in different languages
|
||||
* different revisions will be "latest".
|
||||
* @param int $revision_id
|
||||
* The revision ID that is now the latest revision.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function setLatestRevision($entity_type_id, $entity_id, $langcode, $revision_id);
|
||||
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\content_moderation\Routing;
|
||||
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Dynamic route provider for the Content moderation module.
|
||||
*
|
||||
* Provides the following routes:
|
||||
* - The latest version tab, showing the latest revision of an entity, not the
|
||||
* default one.
|
||||
*/
|
||||
class EntityModerationRouteProvider implements EntityRouteProviderInterface, EntityHandlerInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* Constructs a new DefaultHtmlRouteProvider.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityFieldManagerInterface $entity_manager) {
|
||||
$this->entityFieldManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$container->get('entity_field.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRoutes(EntityTypeInterface $entity_type) {
|
||||
$collection = new RouteCollection();
|
||||
|
||||
if ($moderation_route = $this->getLatestVersionRoute($entity_type)) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$collection->add("entity.{$entity_type_id}.latest_version", $moderation_route);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the moderation-form route.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route|null
|
||||
* The generated route, if available.
|
||||
*/
|
||||
protected function getLatestVersionRoute(EntityTypeInterface $entity_type) {
|
||||
if ($entity_type->hasLinkTemplate('latest-version') && $entity_type->hasViewBuilderClass()) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$route = new Route($entity_type->getLinkTemplate('latest-version'));
|
||||
$route
|
||||
->addDefaults([
|
||||
'_entity_view' => "{$entity_type_id}.full",
|
||||
'_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::title',
|
||||
])
|
||||
// If the entity type is a node, unpublished content will be visible
|
||||
// if the user has the "view all unpublished content" permission.
|
||||
->setRequirement('_entity_access', "{$entity_type_id}.view")
|
||||
->setRequirement('_content_moderation_latest_version', 'TRUE')
|
||||
->setOption('_content_moderation_entity_type', $entity_type_id)
|
||||
->setOption('parameters', [
|
||||
$entity_type_id => [
|
||||
'type' => 'entity:' . $entity_type_id,
|
||||
'load_forward_revision' => 1,
|
||||
],
|
||||
]);
|
||||
|
||||
// Entity types with serial IDs can specify this in their route
|
||||
// requirements, improving the matching process.
|
||||
if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
|
||||
$route->setRequirement($entity_type_id, '\d+');
|
||||
}
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the ID key for a given entity type.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* An entity type.
|
||||
*
|
||||
* @return string|null
|
||||
* The type of the ID key for a given entity type, or NULL if the entity
|
||||
* type does not support fields.
|
||||
*/
|
||||
protected function getEntityTypeIdKeyType(EntityTypeInterface $entity_type) {
|
||||
if (!$entity_type->entityClassImplements(FieldableEntityInterface::class)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type->id());
|
||||
return $field_storage_definitions[$entity_type->getKey('id')]->getType();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\content_moderation\Routing;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Provides the moderation configuration routes for config entities.
|
||||
*/
|
||||
class EntityTypeModerationRouteProvider implements EntityRouteProviderInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRoutes(EntityTypeInterface $entity_type) {
|
||||
$collection = new RouteCollection();
|
||||
|
||||
if ($moderation_route = $this->getModerationFormRoute($entity_type)) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$collection->add("entity.{$entity_type_id}.moderation", $moderation_route);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the moderation-form route.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\Route|null
|
||||
* The generated route, if available.
|
||||
*/
|
||||
protected function getModerationFormRoute(EntityTypeInterface $entity_type) {
|
||||
if ($entity_type->hasLinkTemplate('moderation-form') && $entity_type->getFormClass('moderation')) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
|
||||
$route = new Route($entity_type->getLinkTemplate('moderation-form'));
|
||||
|
||||
// @todo Come up with a new permission.
|
||||
$route
|
||||
->setDefaults([
|
||||
'_entity_form' => "{$entity_type_id}.moderation",
|
||||
'_title' => 'Moderation',
|
||||
])
|
||||
->setRequirement('_permission', 'administer content moderation')
|
||||
->setOption('parameters', [
|
||||
$entity_type_id => ['type' => 'entity:' . $entity_type_id],
|
||||
]);
|
||||
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -8,8 +8,8 @@ dependencies:
|
||||
- content_moderation
|
||||
- node
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
# Information added by Drupal.org packaging script on 2018-01-03
|
||||
version: '8.4.4'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1509719929
|
||||
datestamp: 1515021228
|
||||
|
||||
-447
@@ -1,447 +0,0 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: test_content_moderation_latest_revision
|
||||
label: test_content_moderation_latest_revision
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: mini
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: ‹‹
|
||||
next: ››
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_data
|
||||
field: nid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: number_integer
|
||||
settings:
|
||||
thousand_separator: ''
|
||||
prefix_suffix: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
plugin_id: field
|
||||
revision_id:
|
||||
id: revision_id
|
||||
table: content_revision_tracker
|
||||
field: revision_id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
plugin_id: standard
|
||||
title:
|
||||
id: title
|
||||
table: node_field_revision
|
||||
field: title
|
||||
relationship: latest_revision__node
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: string
|
||||
settings:
|
||||
link_to_entity: false
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
plugin_id: field
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: content_moderation_state_field_revision
|
||||
field: moderation_state
|
||||
relationship: moderation_state
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: string
|
||||
settings: { }
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: moderation_state
|
||||
plugin_id: field
|
||||
moderation_state_1:
|
||||
id: moderation_state_1
|
||||
table: content_moderation_state_field_revision
|
||||
field: moderation_state
|
||||
relationship: moderation_state_1
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: string
|
||||
settings: { }
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: content_moderation_state
|
||||
entity_field: moderation_state
|
||||
plugin_id: field
|
||||
filters: { }
|
||||
sorts:
|
||||
nid:
|
||||
id: nid
|
||||
table: node_field_data
|
||||
field: nid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: ASC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
entity_type: node
|
||||
entity_field: nid
|
||||
plugin_id: standard
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships:
|
||||
latest_revision__node:
|
||||
id: latest_revision__node
|
||||
table: content_revision_tracker
|
||||
field: latest_revision__node
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: 'Content latest revision'
|
||||
required: false
|
||||
plugin_id: standard
|
||||
moderation_state_1:
|
||||
id: moderation_state_1
|
||||
table: node_field_revision
|
||||
field: moderation_state
|
||||
relationship: latest_revision__node
|
||||
group_type: group
|
||||
admin_label: 'Content moderation state (latest revision)'
|
||||
required: false
|
||||
entity_type: node
|
||||
plugin_id: standard
|
||||
moderation_state:
|
||||
id: moderation_state
|
||||
table: node_field_revision
|
||||
field: moderation_state
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: 'Content moderation state'
|
||||
required: false
|
||||
entity_type: node
|
||||
plugin_id: standard
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
rendering_language: '***LANGUAGE_entity_default***'
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_interface'
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
||||
+3
-3
@@ -9,8 +9,8 @@ dependencies:
|
||||
- node
|
||||
- views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
# Information added by Drupal.org packaging script on 2018-01-03
|
||||
version: '8.4.4'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1509719929
|
||||
datestamp: 1515021228
|
||||
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests the "Latest Revision" views filter.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class LatestRevisionViewsFilterTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation_test_views',
|
||||
'content_moderation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests view shows the correct node IDs.
|
||||
*/
|
||||
public function testViewShowsCorrectNids() {
|
||||
$this->createNodeType('Test', 'test');
|
||||
|
||||
$permissions = [
|
||||
'access content',
|
||||
'view all revisions',
|
||||
];
|
||||
$editor1 = $this->drupalCreateUser($permissions);
|
||||
|
||||
$this->drupalLogin($editor1);
|
||||
|
||||
// Make a pre-moderation node.
|
||||
/** @var Node $node_0 */
|
||||
$node_0 = Node::create([
|
||||
'type' => 'test',
|
||||
'title' => 'Node 0 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_0->save();
|
||||
|
||||
// Now enable moderation for subsequent nodes.
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'test');
|
||||
$workflow->save();
|
||||
|
||||
// Make a node that is only ever in Draft.
|
||||
/** @var Node $node_1 */
|
||||
$node_1 = Node::create([
|
||||
'type' => 'test',
|
||||
'title' => 'Node 1 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_1->moderation_state->value = 'draft';
|
||||
$node_1->save();
|
||||
|
||||
// Make a node that is in Draft, then Published.
|
||||
/** @var Node $node_2 */
|
||||
$node_2 = Node::create([
|
||||
'type' => 'test',
|
||||
'title' => 'Node 2 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_2->moderation_state->value = 'draft';
|
||||
$node_2->save();
|
||||
|
||||
$node_2->setTitle('Node 2 - Rev 2');
|
||||
$node_2->moderation_state->value = 'published';
|
||||
$node_2->save();
|
||||
|
||||
// Make a node that is in Draft, then Published, then Draft.
|
||||
/** @var Node $node_3 */
|
||||
$node_3 = Node::create([
|
||||
'type' => 'test',
|
||||
'title' => 'Node 3 - Rev 1',
|
||||
'uid' => $editor1->id(),
|
||||
]);
|
||||
$node_3->moderation_state->value = 'draft';
|
||||
$node_3->save();
|
||||
|
||||
$node_3->setTitle('Node 3 - Rev 2');
|
||||
$node_3->moderation_state->value = 'published';
|
||||
$node_3->save();
|
||||
|
||||
$node_3->setTitle('Node 3 - Rev 3');
|
||||
$node_3->moderation_state->value = 'draft';
|
||||
$node_3->save();
|
||||
|
||||
// Now show the View, and confirm that only the correct titles are showing.
|
||||
$this->drupalGet('/latest');
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->assertEquals(200, $this->getSession()->getStatusCode());
|
||||
$this->assertTrue($page->hasContent('Node 1 - Rev 1'));
|
||||
$this->assertTrue($page->hasContent('Node 2 - Rev 2'));
|
||||
$this->assertTrue($page->hasContent('Node 3 - Rev 3'));
|
||||
$this->assertFalse($page->hasContent('Node 2 - Rev 1'));
|
||||
$this->assertFalse($page->hasContent('Node 3 - Rev 1'));
|
||||
$this->assertFalse($page->hasContent('Node 3 - Rev 2'));
|
||||
$this->assertFalse($page->hasContent('Node 0 - Rev 1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new node type.
|
||||
*
|
||||
* @param string $label
|
||||
* The human-readable label of the type to create.
|
||||
* @param string $machine_name
|
||||
* The machine name of the type to create.
|
||||
*
|
||||
* @return NodeType
|
||||
* The node type just created.
|
||||
*/
|
||||
protected function createNodeType($label, $machine_name) {
|
||||
/** @var NodeType $node_type */
|
||||
$node_type = NodeType::create([
|
||||
'type' => $machine_name,
|
||||
'label' => $label,
|
||||
]);
|
||||
$node_type->save();
|
||||
|
||||
return $node_type;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -206,6 +206,7 @@ class ModerationFormTest extends ModerationStateTestBase {
|
||||
// Make a pending revision.
|
||||
$node->title = $this->randomMachineName();
|
||||
$node->moderation_state->value = 'draft';
|
||||
$node->setRevisionCreationTime(12345);
|
||||
$node->save();
|
||||
|
||||
$another_user = $this->drupalCreateUser($this->permissions);
|
||||
@@ -217,6 +218,10 @@ class ModerationFormTest extends ModerationStateTestBase {
|
||||
|
||||
$this->drupalGet(sprintf('node/%d/revisions', $node->id()));
|
||||
$this->assertText('by ' . $another_user->getAccountName());
|
||||
|
||||
// Verify the revision creation time has been updated.
|
||||
$node = $node->load($node->id());
|
||||
$this->assertGreaterThan(12345, $node->getRevisionCreationTime());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -88,11 +88,11 @@ class ModerationStateAccessTest extends BrowserTestBase {
|
||||
* @param string $machine_name
|
||||
* The machine name of the type to create.
|
||||
*
|
||||
* @return NodeType
|
||||
* @return \Drupal\node\Entity\NodeType
|
||||
* The node type just created.
|
||||
*/
|
||||
protected function createNodeType($label, $machine_name) {
|
||||
/** @var NodeType $node_type */
|
||||
/** @var \Drupal\node\Entity\NodeType $node_type */
|
||||
$node_type = NodeType::create([
|
||||
'type' => $machine_name,
|
||||
'label' => $label,
|
||||
|
||||
@@ -13,6 +13,8 @@ abstract class ModerationStateTestBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Profile to use.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'testing';
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Functional;
|
||||
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests permission access control around nodes.
|
||||
*
|
||||
@@ -19,7 +21,7 @@ class NodeAccessTest extends ModerationStateTestBase {
|
||||
'block',
|
||||
'block_content',
|
||||
'node',
|
||||
'node_access_test_empty',
|
||||
'node_access_test',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -49,6 +51,9 @@ class NodeAccessTest extends ModerationStateTestBase {
|
||||
$this->createContentTypeFromUi('Moderated content', 'moderated_content', FALSE);
|
||||
$this->grantUserPermissionToCreateContentOfType($this->adminUser, 'moderated_content');
|
||||
|
||||
// Add the private field to the node type.
|
||||
node_access_test_add_field(NodeType::load('moderated_content'));
|
||||
|
||||
// Rebuild permissions because hook_node_grants() is implemented by the
|
||||
// node_access_test_empty module.
|
||||
node_access_rebuild();
|
||||
@@ -58,6 +63,10 @@ class NodeAccessTest extends ModerationStateTestBase {
|
||||
* Verifies that a non-admin user can still access the appropriate pages.
|
||||
*/
|
||||
public function testPageAccess() {
|
||||
// Initially disable access grant records in
|
||||
// node_access_test_node_access_records().
|
||||
\Drupal::state()->set('node_access_test.private', TRUE);
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Access the node form before moderation is enabled, the publication state
|
||||
@@ -149,6 +158,30 @@ class NodeAccessTest extends ModerationStateTestBase {
|
||||
$this->assertResponse(403);
|
||||
$this->drupalGet($view_path);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Now create a private node that the user is not granted access to by the
|
||||
// node grants, but is granted access via hook_node_access().
|
||||
// @see node_access_test_node_access
|
||||
$node = $this->createNode([
|
||||
'type' => 'moderated_content',
|
||||
'private' => TRUE,
|
||||
'uid' => $this->adminUser->id(),
|
||||
]);
|
||||
$user = $this->createUser([
|
||||
'use editorial transition publish',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Grant access to the node via node_access_test_node_access().
|
||||
\Drupal::state()->set('node_access_test.allow_uid', $user->id());
|
||||
|
||||
$this->drupalGet($node->toUrl());
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Verify the moderation form is in place by publishing the node.
|
||||
$this->drupalPostForm(NULL, [], t('Apply'));
|
||||
$node = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($node->id());
|
||||
$this->assertEquals('published', $node->moderation_state->value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -71,60 +71,13 @@ class ContentModerationStateTest extends KernelTestBase {
|
||||
$this->entityTypeManager = $this->container->get('entity_type.manager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a bundle entity type for the specified entity type, if needed.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type identifier.
|
||||
*
|
||||
* @return string
|
||||
* The bundle identifier.
|
||||
*/
|
||||
protected function setupBundleEntityType($entity_type_id) {
|
||||
$bundle_id = $entity_type_id;
|
||||
$bundle_entity_type_id = $this->entityTypeManager->getDefinition($entity_type_id)->getBundleEntityType();
|
||||
if ($bundle_entity_type_id) {
|
||||
$bundle_entity_type_definition = $this->entityTypeManager->getDefinition($bundle_entity_type_id);
|
||||
$entity_type_storage = $this->entityTypeManager->getStorage($bundle_entity_type_id);
|
||||
|
||||
$entity_type = $entity_type_storage->create([
|
||||
$bundle_entity_type_definition->getKey('id') => 'example',
|
||||
]);
|
||||
if ($entity_type_id == 'media') {
|
||||
$entity_type->set('source', 'test');
|
||||
$entity_type->save();
|
||||
$source_field = $entity_type->getSource()->createSourceField($entity_type);
|
||||
$source_field->getFieldStorageDefinition()->save();
|
||||
$source_field->save();
|
||||
$entity_type->set('source_configuration', [
|
||||
'source_field' => $source_field->getName(),
|
||||
]);
|
||||
}
|
||||
$entity_type->save();
|
||||
$bundle_id = $entity_type->id();
|
||||
}
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle($entity_type_id, $bundle_id);
|
||||
$workflow->save();
|
||||
|
||||
return $bundle_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic monolingual content moderation through the API.
|
||||
*
|
||||
* @dataProvider basicModerationTestCases
|
||||
*/
|
||||
public function testBasicModeration($entity_type_id) {
|
||||
$bundle_id = $this->setupBundleEntityType($entity_type_id);
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
|
||||
$entity = $entity_storage->create([
|
||||
'title' => 'Test title',
|
||||
$this->entityTypeManager->getDefinition($entity_type_id)->getKey('bundle') => $bundle_id,
|
||||
]);
|
||||
$entity = $this->createEntity($entity_type_id);
|
||||
if ($entity instanceof EntityPublishedInterface) {
|
||||
$entity->setUnpublished();
|
||||
}
|
||||
@@ -175,6 +128,7 @@ class ContentModerationStateTest extends KernelTestBase {
|
||||
$entity->save();
|
||||
|
||||
// Revert to the previous (published) revision.
|
||||
$entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
|
||||
$previous_revision = $entity_storage->loadRevision(4);
|
||||
$previous_revision->isDefaultRevision(TRUE);
|
||||
$previous_revision->setNewRevision(TRUE);
|
||||
@@ -225,15 +179,9 @@ class ContentModerationStateTest extends KernelTestBase {
|
||||
* @dataProvider basicModerationTestCases
|
||||
*/
|
||||
public function testContentModerationStateDataRemoval($entity_type_id) {
|
||||
$bundle_id = $this->setupBundleEntityType($entity_type_id);
|
||||
|
||||
// Test content moderation state deletion.
|
||||
$entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $entity_storage->create([
|
||||
'title' => 'Test title',
|
||||
$this->entityTypeManager->getDefinition($entity_type_id)->getKey('bundle') => $bundle_id,
|
||||
]);
|
||||
$entity = $this->createEntity($entity_type_id);
|
||||
$entity->save();
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$entity->delete();
|
||||
@@ -242,10 +190,7 @@ class ContentModerationStateTest extends KernelTestBase {
|
||||
|
||||
// Test content moderation state revision deletion.
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity2 */
|
||||
$entity2 = $entity_storage->create([
|
||||
'title' => 'Test title',
|
||||
$this->entityTypeManager->getDefinition($entity_type_id)->getKey('bundle') => $bundle_id,
|
||||
]);
|
||||
$entity2 = $this->createEntity($entity_type_id);
|
||||
$entity2->save();
|
||||
$revision = clone $entity2;
|
||||
$revision->isDefaultRevision(FALSE);
|
||||
@@ -254,6 +199,7 @@ class ContentModerationStateTest extends KernelTestBase {
|
||||
$entity2 = $this->reloadEntity($entity2);
|
||||
$entity2->setNewRevision(TRUE);
|
||||
$entity2->save();
|
||||
$entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
|
||||
$entity_storage->deleteRevision($revision->getRevisionId());
|
||||
$content_moderation_state = ContentModerationState::loadFromModeratedEntity($revision);
|
||||
$this->assertFalse($content_moderation_state);
|
||||
@@ -263,15 +209,16 @@ class ContentModerationStateTest extends KernelTestBase {
|
||||
// Test content moderation state translation deletion.
|
||||
if ($this->entityTypeManager->getDefinition($entity_type_id)->isTranslatable()) {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity3 */
|
||||
$entity3 = $entity_storage->create([
|
||||
'title' => 'Test title',
|
||||
$this->entityTypeManager->getDefinition($entity_type_id)->getKey('bundle') => $bundle_id,
|
||||
]);
|
||||
$entity3 = $this->createEntity($entity_type_id);
|
||||
$langcode = 'it';
|
||||
ConfigurableLanguage::createFromLangcode($langcode)
|
||||
->save();
|
||||
$entity3->save();
|
||||
$translation = $entity3->addTranslation($langcode, ['title' => 'Titolo test']);
|
||||
// Make sure we add values for all of the required fields.
|
||||
if ($entity_type_id == 'block_content') {
|
||||
$translation->info = $this->randomString();
|
||||
}
|
||||
$translation->save();
|
||||
$content_moderation_state = ContentModerationState::loadFromModeratedEntity($entity3);
|
||||
$this->assertTrue($content_moderation_state->hasTranslation($langcode));
|
||||
@@ -589,6 +536,63 @@ class ContentModerationStateTest extends KernelTestBase {
|
||||
], $workflow->getDependencies());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entity.
|
||||
*
|
||||
* The entity will have required fields populated and the corresponding bundle
|
||||
* will be enabled for content moderation.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type ID.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\ContentEntityInterface
|
||||
* The created entity.
|
||||
*/
|
||||
protected function createEntity($entity_type_id) {
|
||||
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
|
||||
|
||||
$bundle_id = $entity_type_id;
|
||||
// Set up a bundle entity type for the specified entity type, if needed.
|
||||
if ($bundle_entity_type_id = $entity_type->getBundleEntityType()) {
|
||||
$bundle_entity_type = $this->entityTypeManager->getDefinition($bundle_entity_type_id);
|
||||
$bundle_entity_storage = $this->entityTypeManager->getStorage($bundle_entity_type_id);
|
||||
|
||||
$bundle_id = 'example';
|
||||
if (!$bundle_entity_storage->load($bundle_id)) {
|
||||
$bundle_entity = $bundle_entity_storage->create([
|
||||
$bundle_entity_type->getKey('id') => 'example',
|
||||
]);
|
||||
if ($entity_type_id == 'media') {
|
||||
$bundle_entity->set('source', 'test');
|
||||
$bundle_entity->save();
|
||||
$source_field = $bundle_entity->getSource()->createSourceField($bundle_entity);
|
||||
$source_field->getFieldStorageDefinition()->save();
|
||||
$source_field->save();
|
||||
$bundle_entity->set('source_configuration', [
|
||||
'source_field' => $source_field->getName(),
|
||||
]);
|
||||
}
|
||||
$bundle_entity->save();
|
||||
}
|
||||
}
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle($entity_type_id, $bundle_id);
|
||||
$workflow->save();
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
|
||||
$entity = $entity_storage->create([
|
||||
$entity_type->getKey('label') => 'Test title',
|
||||
$entity_type->getKey('bundle') => $bundle_id,
|
||||
]);
|
||||
// Make sure we add values for all of the required fields.
|
||||
if ($entity_type_id == 'block_content') {
|
||||
$entity->info = $this->randomString();
|
||||
}
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the entity after clearing the static cache.
|
||||
*
|
||||
|
||||
@@ -69,7 +69,7 @@ class EntityOperationsTest extends KernelTestBase {
|
||||
|
||||
// Verify the entity saved correctly, and that the presence of pending
|
||||
// revisions doesn't affect the default node load.
|
||||
/** @var Node $page */
|
||||
/** @var \Drupal\node\Entity\Node $page */
|
||||
$page = Node::load($id);
|
||||
$this->assertEquals('A', $page->getTitle());
|
||||
$this->assertTrue($page->isDefaultRevision());
|
||||
@@ -142,7 +142,7 @@ class EntityOperationsTest extends KernelTestBase {
|
||||
$id = $page->id();
|
||||
|
||||
// Verify the entity saved correctly.
|
||||
/** @var Node $page */
|
||||
/** @var \Drupal\node\Entity\Node $page */
|
||||
$page = Node::load($id);
|
||||
$this->assertEquals('A', $page->getTitle());
|
||||
$this->assertTrue($page->isDefaultRevision());
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestRev;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\content_moderation\ModerationInformation
|
||||
* @group content_moderation
|
||||
*/
|
||||
class ModerationInformationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['content_moderation', 'entity_test', 'user', 'workflows'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installConfig(['content_moderation']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getDefaultRevisionId
|
||||
* @covers ::getLatestRevisionId
|
||||
*/
|
||||
public function testDefaultAndLatestRevisionId() {
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_rev', 'entity_test_rev');
|
||||
$workflow->save();
|
||||
|
||||
$entity_test_rev = EntityTestRev::create([
|
||||
'name' => 'Default Revision',
|
||||
'moderation_state' => 'published',
|
||||
]);
|
||||
$entity_test_rev->save();
|
||||
|
||||
$entity_test_rev->name = 'Pending revision';
|
||||
$entity_test_rev->moderation_state = 'draft';
|
||||
$entity_test_rev->save();
|
||||
|
||||
/** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
|
||||
$moderation_info = \Drupal::service('content_moderation.moderation_information');
|
||||
|
||||
// Check that moderation information service returns the correct default
|
||||
// revision ID.
|
||||
$default_revision_id = $moderation_info->getDefaultRevisionId('entity_test_rev', $entity_test_rev->id());
|
||||
$this->assertSame(1, $default_revision_id);
|
||||
|
||||
// Check that moderation information service returns the correct latest
|
||||
// revision ID.
|
||||
$latest_revision_id = $moderation_info->getLatestRevisionId('entity_test_rev', $entity_test_rev->id());
|
||||
$this->assertSame(2, $latest_revision_id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -99,4 +99,29 @@ class ModerationStateFieldItemListTest extends KernelTestBase {
|
||||
$this->assertFalse($this->testNode->isDefaultRevision());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test updating the state for an entity without a workflow.
|
||||
*/
|
||||
public function testEntityWithNoWorkflow() {
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'example_no_workflow',
|
||||
]);
|
||||
$node_type->save();
|
||||
$test_node = Node::create([
|
||||
'type' => 'example_no_workflow',
|
||||
'title' => 'Test node with no workflow',
|
||||
]);
|
||||
$test_node->save();
|
||||
|
||||
/** @var \Drupal\content_moderation\ModerationInformationInterface $content_moderation_info */
|
||||
$content_moderation_info = \Drupal::service('content_moderation.moderation_information');
|
||||
$workflow = $content_moderation_info->getWorkflowForEntity($test_node);
|
||||
$this->assertNull($workflow);
|
||||
|
||||
$this->assertTrue($test_node->isPublished());
|
||||
$test_node->moderation_state->setValue('draft');
|
||||
// The entity is still published because there is not a workflow.
|
||||
$this->assertTrue($test_node->isPublished());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\content_moderation\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Tests\node\Traits\NodeCreationTrait;
|
||||
use Drupal\Tests\user\Traits\UserCreationTrait;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests with node access enabled.
|
||||
*
|
||||
* @group content_moderation
|
||||
*/
|
||||
class NodeAccessTest extends KernelTestBase {
|
||||
|
||||
use NodeCreationTrait;
|
||||
use UserCreationTrait;
|
||||
|
||||
/**
|
||||
* The moderation information service.
|
||||
*
|
||||
* @var \Drupal\content_moderation\ModerationInformationInterface
|
||||
*/
|
||||
protected $moderationInformation;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_moderation',
|
||||
'filter',
|
||||
'node',
|
||||
'node_access_test',
|
||||
'system',
|
||||
'user',
|
||||
'workflows',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('content_moderation_state');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('workflow');
|
||||
$this->installConfig(['content_moderation', 'filter']);
|
||||
$this->installSchema('system', ['sequences']);
|
||||
$this->installSchema('node', ['node_access']);
|
||||
|
||||
// Add a moderated node type.
|
||||
$node_type = NodeType::create([
|
||||
'type' => 'page',
|
||||
'label' => 'Page',
|
||||
]);
|
||||
$node_type->save();
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'page');
|
||||
$workflow->save();
|
||||
|
||||
$this->moderationInformation = \Drupal::service('content_moderation.moderation_information');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for moderation information methods with node access.
|
||||
*/
|
||||
public function testModerationInformation() {
|
||||
// Create an admin user.
|
||||
$user = $this->createUser([], NULL, TRUE);
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
|
||||
// Create a node.
|
||||
$node = $this->createNode(['type' => 'page']);
|
||||
$this->assertEquals($node->getRevisionId(), $this->moderationInformation->getDefaultRevisionId('node', $node->id()));
|
||||
$this->assertEquals($node->getRevisionId(), $this->moderationInformation->getLatestRevisionId('node', $node->id()));
|
||||
|
||||
// Create a non-admin user.
|
||||
$user = $this->createUser();
|
||||
\Drupal::currentUser()->setAccount($user);
|
||||
$this->assertEquals($node->getRevisionId(), $this->moderationInformation->getDefaultRevisionId('node', $node->id()));
|
||||
$this->assertEquals($node->getRevisionId(), $this->moderationInformation->getLatestRevisionId('node', $node->id()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class ModerationInformationTest extends UnitTestCase {
|
||||
/**
|
||||
* Builds a mock user.
|
||||
*
|
||||
* @return AccountInterface
|
||||
* @return \Drupal\Core\Session\AccountInterface
|
||||
* The mocked user.
|
||||
*/
|
||||
protected function getUser() {
|
||||
@@ -32,7 +32,7 @@ class ModerationInformationTest extends UnitTestCase {
|
||||
/**
|
||||
* Returns a mock Entity Type Manager.
|
||||
*
|
||||
* @return EntityTypeManagerInterface
|
||||
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
* The mocked entity type manager.
|
||||
*/
|
||||
protected function getEntityTypeManager() {
|
||||
|
||||
Reference in New Issue
Block a user