updated core

This commit is contained in:
Bachir Soussi Chiadmi
2018-01-24 13:36:32 +01:00
parent cd7dea45a9
commit f9374cf96d
1997 changed files with 5175 additions and 127715 deletions
@@ -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;
}
}
}
@@ -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
@@ -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: { }
@@ -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
@@ -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() {