Commit 0493d9fb authored by Gábor Hojtsy's avatar Gábor Hojtsy
Browse files

Issue #2843083 by timmillwood, Manuel Garcia, Sam152, naveenvalecha,...

Issue #2843083 by timmillwood, Manuel Garcia, Sam152, naveenvalecha, amateescu, yoroy, Gábor Hojtsy, xjm, jojototh, Bojhan, lauriii: Select entity type / bundle in workflow settings
parent 1228959c
......@@ -62,15 +62,6 @@ function content_moderation_entity_type_alter(array &$entity_types) {
->entityTypeAlter($entity_types);
}
/**
* Implements hook_entity_operation().
*/
function content_moderation_entity_operation(EntityInterface $entity) {
return \Drupal::service('class_resolver')
->getInstanceFromDefinition(EntityTypeInfo::class)
->entityOperation($entity);
}
/**
* Implements hook_entity_presave().
*/
......
......@@ -6,11 +6,6 @@ view any unpublished content:
title: 'View content moderation'
description: 'View content moderation.'
'administer content moderation':
title: 'Administer content moderation'
description: 'Administer workflows on content entities.'
'restrict access': TRUE
view latest version:
title: 'View the latest version'
description: 'View the latest version of an entity. (Also requires "View any unpublished content" permission)'
......
content_moderation.workflow_type_edit_form:
path: '/admin/config/workflow/workflows/manage/{workflow}/type/{entity_type_id}'
defaults:
_form: '\Drupal\content_moderation\Form\WorkflowTypeEditForm'
_title_callback: '\Drupal\content_moderation\Form\WorkflowTypeEditForm::getTitle'
requirements:
_permission: 'administer workflows'
......@@ -3,12 +3,10 @@
namespace Drupal\content_moderation;
use Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList;
use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
use Drupal\Core\Entity\BundleEntityFormBase;
use Drupal\Core\Entity\ContentEntityFormInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
......@@ -17,13 +15,10 @@
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Url;
use Drupal\content_moderation\Entity\Handler\BlockContentModerationHandler;
use Drupal\content_moderation\Entity\Handler\ModerationHandler;
use Drupal\content_moderation\Entity\Handler\NodeModerationHandler;
use Drupal\content_moderation\Form\BundleModerationConfigurationForm;
use Drupal\content_moderation\Routing\EntityModerationRouteProvider;
use Drupal\content_moderation\Routing\EntityTypeModerationRouteProvider;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -127,11 +122,6 @@ public function entityTypeAlter(array &$entity_types) {
// The ContentModerationState entity type should never be moderated.
if ($entity_type->isRevisionable() && $entity_type_id != 'content_moderation_state') {
$entity_types[$entity_type_id] = $this->addModerationToEntityType($entity_type);
// Add additional moderation support to entity types whose bundles are
// managed by a config entity type.
if ($entity_type->getBundleEntityType()) {
$entity_types[$entity_type->getBundleEntityType()] = $this->addModerationToBundleEntityType($entity_types[$entity_type->getBundleEntityType()]);
}
}
}
}
......@@ -169,67 +159,6 @@ protected function addModerationToEntityType(ContentEntityTypeInterface $type) {
return $type;
}
/**
* Configures moderation configuration support on a entity type definition.
*
* That "configuration support" includes a configuration form, a hypermedia
* link, and a route provider to tie it all together. There's also a
* moderation handler for per-entity-type variation.
*
* @param \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $type
* The config entity definition to modify.
*
* @return \Drupal\Core\Config\Entity\ConfigEntityTypeInterface
* The modified config entity definition.
*/
protected function addModerationToBundleEntityType(ConfigEntityTypeInterface $type) {
if ($type->hasLinkTemplate('edit-form') && !$type->hasLinkTemplate('moderation-form')) {
$type->setLinkTemplate('moderation-form', $type->getLinkTemplate('edit-form') . '/moderation');
}
if (!$type->getFormClass('moderation')) {
$type->setFormClass('moderation', BundleModerationConfigurationForm::class);
}
// @todo Core forgot to add a direct way to manipulate route_provider, so
// we have to do it the sloppy way for now.
$providers = $type->getRouteProviderClasses() ?: [];
if (empty($providers['moderation'])) {
$providers['moderation'] = EntityTypeModerationRouteProvider::class;
$type->setHandlerClass('route_provider', $providers);
}
return $type;
}
/**
* Adds an operation on bundles that should have a Moderation form.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity on which to define an operation.
*
* @return array
* An array of operation definitions.
*
* @see hook_entity_operation()
*/
public function entityOperation(EntityInterface $entity) {
$operations = [];
$type = $entity->getEntityType();
$bundle_of = $type->getBundleOf();
if ($this->currentUser->hasPermission('administer content moderation') && $bundle_of &&
$this->moderationInfo->canModerateEntitiesOfEntityType($this->entityTypeManager->getDefinition($bundle_of))
) {
$operations['manage-moderation'] = [
'title' => t('Manage moderation'),
'weight' => 27,
'url' => Url::fromRoute("entity.{$type->id()}.moderation", [$entity->getEntityTypeId() => $entity->id()]),
];
}
return $operations;
}
/**
* Gets the "extra fields" for a bundle.
*
......
<?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;
}
}
<?php
namespace Drupal\content_moderation\Form;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\workflows\WorkflowInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* The form for editing entity types associated with a workflow.
*/
class WorkflowTypeEditForm extends FormBase {
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity type bundle information service.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $bundleInfo;
/**
* The moderation information service.
*
* @var \Drupal\content_moderation\ModerationInformationInterface
*/
protected $moderationInformation;
/**
* The workflow entity object.
*
* @var \Drupal\workflows\WorkflowInterface
*/
protected $workflow;
/**
* The entity type definition object.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('entity_type.bundle.info'),
$container->get('content_moderation.moderation_information')
);
}
/**
* {@inheritdoc}
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_info , ModerationInformationInterface $moderation_information) {
$this->entityTypeManager = $entity_type_manager;
$this->bundleInfo = $bundle_info;
$this->moderationInformation = $moderation_information;
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'workflow_type_edit_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, WorkflowInterface $workflow = NULL, $entity_type_id = NULL) {
$this->workflow = $workflow;
try {
$this->entityType = $this->entityTypeManager->getDefinition($entity_type_id);
}
catch (PluginNotFoundException $e) {
throw new NotFoundHttpException();
}
$options = $defaults = [];
foreach ($this->bundleInfo->getBundleInfo($this->entityType->id()) as $bundle_id => $bundle) {
// Check if moderation is enabled for this bundle on any workflow.
$moderation_enabled = $this->moderationInformation->shouldModerateEntitiesOfBundle($this->entityType, $bundle_id);
// Check if moderation is enabled for this bundle on this workflow.
$workflow_moderation_enabled = $this->workflow->getTypePlugin()->appliesToEntityTypeAndBundle($this->entityType->id(), $bundle_id);
// Only show bundles that are not enabled anywhere, or enabled on this
// workflow.
if (!$moderation_enabled || $workflow_moderation_enabled) {
// Add the bundle to the options if it's not enabled on a workflow,
// unless the workflow it's enabled on is this one.
$options[$bundle_id] = [
'type' => $bundle['label'],
];
// Add the bundle to the list of default values if it's enabled on this
// workflow.
$defaults[$bundle_id] = $workflow_moderation_enabled;
}
}
if (!empty($options)) {
$bundles_header = $this->t('All @entity_type types', ['@entity_type' => $this->entityType->getLabel()]);
if ($bundle_entity_type_id = $this->entityType->getBundleEntityType()) {
$bundles_header = $this->t('All @entity_type_plural_label', ['@entity_type_plural_label' => $this->entityTypeManager->getDefinition($bundle_entity_type_id)->getPluralLabel()]);
}
$form['bundles'] = [
'#type' => 'tableselect',
'#header' => [
'type' => $bundles_header,
],
'#options' => $options,
'#default_value' => $defaults,
'#attributes' => ['class' => ['no-highlight']],
];
}
$form['actions'] = ['#type' => 'actions'];
$form['actions']['submit'] = [
'#type' => 'submit',
'#button_type' => 'primary',
'#value' => $this->t('Save'),
'#ajax' => [
'callback' => [$this, 'ajaxcallback'],
],
];
$form['actions']['cancel'] = [
'#type' => 'button',
'#value' => $this->t('Cancel'),
'#ajax' => [
'callback' => [$this, 'ajaxcallback'],
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
foreach ($form_state->getValue('bundles') as $bundle_id => $checked) {
if ($checked) {
$this->workflow->getTypePlugin()->addEntityTypeAndBundle($this->entityType->id(), $bundle_id);
}
else {
$this->workflow->getTypePlugin()->removeEntityTypeAndBundle($this->entityType->id(), $bundle_id);
}
}
$this->workflow->save();
}
/**
* Ajax callback to close the modal and update the selected text.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An ajax response object.
*/
public function ajaxCallback() {
$selected_bundles = [];
foreach ($this->bundleInfo->getBundleInfo($this->entityType->id()) as $bundle_id => $bundle) {
if ($this->workflow->getTypePlugin()->appliesToEntityTypeAndBundle($this->entityType->id(), $bundle_id)) {
$selected_bundles[$bundle_id] = $bundle['label'];
}
}
$response = new AjaxResponse();
$response->addCommand(new CloseDialogCommand());
$response->addCommand(new HtmlCommand('#selected-' . $this->entityType->id(), !empty($selected_bundles) ? implode(', ', $selected_bundles) : $this->t('none')));
return $response;
}
/**
* Route title callback.
*/
public function getTitle(WorkflowInterface $workflow = NULL, $entity_type_id) {
$this->entityType = $this->entityTypeManager->getDefinition($entity_type_id);
$title = $this->t('Select the @entity_type types for the @workflow', ['@entity_type' => $this->entityType->getLabel(), '@workflow' => $workflow->label()]);
if ($bundle_entity_type_id = $this->entityType->getBundleEntityType()) {
$title = $this->t('Select the @entity_type_plural_label for the @workflow', ['@entity_type_plural_label' => $this->entityTypeManager->getDefinition($bundle_entity_type_id)->getPluralLabel(), '@workflow' => $workflow->label()]);
}
return $title;
}
}
......@@ -76,19 +76,6 @@ public static function create(ContainerInterface $container, $base_plugin_id) {
public function getDerivativeDefinitions($base_plugin_definition) {
$this->derivatives = [];
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
if ($this->moderationInfo->canModerateEntitiesOfEntityType($entity_type)) {
$bundle_id = $entity_type->getBundleEntityType();
$this->derivatives["$bundle_id.moderation_tab"] = [
'route_name' => "entity.$bundle_id.moderation",
'title' => $this->t('Manage moderation'),
// @todo - are we sure they all have an edit_form?
'base_route' => "entity.$bundle_id.edit_form",
'weight' => 30,
] + $base_plugin_definition;
}
}
$latest_version_entities = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $type) {
return $this->moderationInfo->canModerateEntitiesOfEntityType($type) && $type->hasLinkTemplate('latest-version');
});
......
......@@ -2,7 +2,10 @@
namespace Drupal\content_moderation\Plugin\WorkflowType;
use Drupal\Component\Serialization\Json;
use Drupal\content_moderation\ModerationInformationInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Form\FormStateInterface;
......@@ -10,7 +13,8 @@
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\content_moderation\ContentModerationState;
use Drupal\workflows\Plugin\WorkflowTypeBase;
use Drupal\Core\Url;
use Drupal\workflows\Plugin\WorkflowTypeFormBase;
use Drupal\workflows\StateInterface;
use Drupal\workflows\WorkflowInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -27,7 +31,7 @@
* },
* )
*/
class ContentModeration extends WorkflowTypeBase implements ContainerFactoryPluginInterface {
class ContentModeration extends WorkflowTypeFormBase implements ContainerFactoryPluginInterface {
use StringTranslationTrait;
......@@ -39,11 +43,38 @@ class ContentModeration extends WorkflowTypeBase implements ContainerFactoryPlug
protected $entityTypeManager;
/**
* Creates an instance of the ContentModeration WorkflowType plugin.
* The entity type bundle info service.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $entityTypeBundleInfo;
/**
* The moderation information service.
*
* @var \Drupal\content_moderation\ModerationInformationInterface
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
protected $moderationInfo;
/**
* Constructs a ContentModeration object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.