3 namespace Drupal\content_moderation\Plugin\WorkflowType;
5 use Drupal\Core\Access\AccessResult;
6 use Drupal\Core\Entity\EntityTypeManagerInterface;
7 use Drupal\Core\Entity\EntityPublishedInterface;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
10 use Drupal\Core\Session\AccountInterface;
11 use Drupal\Core\StringTranslation\StringTranslationTrait;
12 use Drupal\content_moderation\ContentModerationState;
13 use Drupal\workflows\Plugin\WorkflowTypeBase;
14 use Drupal\workflows\StateInterface;
15 use Drupal\workflows\WorkflowInterface;
16 use Symfony\Component\DependencyInjection\ContainerInterface;
19 * Attaches workflows to content entity types and their bundles.
22 * id = "content_moderation",
23 * label = @Translation("Content moderation"),
30 class ContentModeration extends WorkflowTypeBase implements ContainerFactoryPluginInterface {
32 use StringTranslationTrait;
35 * The entity type manager.
37 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
39 protected $entityTypeManager;
42 * Creates an instance of the ContentModeration WorkflowType plugin.
44 public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
45 parent::__construct($configuration, $plugin_id, $plugin_definition);
46 $this->entityTypeManager = $entity_type_manager;
52 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
57 $container->get('entity_type.manager')
64 public function initializeWorkflow(WorkflowInterface $workflow) {
66 ->addState('draft', $this->t('Draft'))
67 ->setStateWeight('draft', -5)
68 ->addState('published', $this->t('Published'))
69 ->setStateWeight('published', 0)
70 ->addTransition('create_new_draft', $this->t('Create New Draft'), ['draft', 'published'], 'draft')
71 ->addTransition('publish', $this->t('Publish'), ['draft', 'published'], 'published');
78 public function checkWorkflowAccess(WorkflowInterface $entity, $operation, AccountInterface $account) {
79 if ($operation === 'view') {
80 return AccessResult::allowedIfHasPermission($account, 'view content moderation');
82 return parent::checkWorkflowAccess($entity, $operation, $account);
88 public function decorateState(StateInterface $state) {
89 if (isset($this->configuration['states'][$state->id()])) {
90 $state = new ContentModerationState($state, $this->configuration['states'][$state->id()]['published'], $this->configuration['states'][$state->id()]['default_revision']);
93 $state = new ContentModerationState($state);
101 public function buildStateConfigurationForm(FormStateInterface $form_state, WorkflowInterface $workflow, StateInterface $state = NULL) {
102 /** @var \Drupal\content_moderation\ContentModerationState $state */
103 $is_required_state = isset($state) ? in_array($state->id(), $this->getRequiredStates(), TRUE) : FALSE;
106 $form['published'] = [
107 '#type' => 'checkbox',
108 '#title' => $this->t('Published'),
109 '#description' => $this->t('When content reaches this state it should be published.'),
110 '#default_value' => isset($state) ? $state->isPublishedState() : FALSE,
111 '#disabled' => $is_required_state,
114 $form['default_revision'] = [
115 '#type' => 'checkbox',
116 '#title' => $this->t('Default revision'),
117 '#description' => $this->t('When content reaches this state it should be made the default revision; this is implied for published states.'),
118 '#default_value' => isset($state) ? $state->isDefaultRevisionState() : FALSE,
119 '#disabled' => $is_required_state,
120 // @todo Add form #state to force "make default" on when "published" is
122 // @see https://www.drupal.org/node/2645614
128 * Gets the entity types the workflow is applied to.
131 * The entity types the workflow is applied to.
133 public function getEntityTypes() {
134 return array_keys($this->configuration['entity_types']);
138 * Gets any bundles the workflow is applied to for the given entity type.
140 * @param string $entity_type_id
141 * The entity type ID to get the bundles for.
144 * The bundles of the entity type the workflow is applied to or an empty
145 * array if the entity type is not applied to the workflow.
147 public function getBundlesForEntityType($entity_type_id) {
148 return isset($this->configuration['entity_types'][$entity_type_id]) ? $this->configuration['entity_types'][$entity_type_id] : [];
152 * Checks if the workflow applies to the supplied entity type and bundle.
154 * @param string $entity_type_id
155 * The entity type ID to check.
156 * @param string $bundle_id
157 * The bundle ID to check.
160 * TRUE if the workflow applies to the supplied entity type ID and bundle
163 public function appliesToEntityTypeAndBundle($entity_type_id, $bundle_id) {
164 return in_array($bundle_id, $this->getBundlesForEntityType($entity_type_id), TRUE);
168 * Removes an entity type ID / bundle ID from the workflow.
170 * @param string $entity_type_id
171 * The entity type ID to remove.
172 * @param string $bundle_id
173 * The bundle ID to remove.
175 public function removeEntityTypeAndBundle($entity_type_id, $bundle_id) {
176 $key = array_search($bundle_id, $this->configuration['entity_types'][$entity_type_id], TRUE);
177 if ($key !== FALSE) {
178 unset($this->configuration['entity_types'][$entity_type_id][$key]);
179 if (empty($this->configuration['entity_types'][$entity_type_id])) {
180 unset($this->configuration['entity_types'][$entity_type_id]);
183 $this->configuration['entity_types'][$entity_type_id] = array_values($this->configuration['entity_types'][$entity_type_id]);
189 * Add an entity type ID / bundle ID to the workflow.
191 * @param string $entity_type_id
192 * The entity type ID to add. It is responsibility of the caller to provide
193 * a valid entity type ID.
194 * @param string $bundle_id
195 * The bundle ID to add. It is responsibility of the caller to provide a
198 public function addEntityTypeAndBundle($entity_type_id, $bundle_id) {
199 if (!$this->appliesToEntityTypeAndBundle($entity_type_id, $bundle_id)) {
200 $this->configuration['entity_types'][$entity_type_id][] = $bundle_id;
201 sort($this->configuration['entity_types'][$entity_type_id]);
202 ksort($this->configuration['entity_types']);
209 public function defaultConfiguration() {
210 // This plugin does not store anything per transition.
214 'published' => FALSE,
215 'default_revision' => FALSE,
219 'default_revision' => TRUE,
222 'entity_types' => [],
229 public function calculateDependencies() {
230 $dependencies = parent::calculateDependencies();
231 foreach ($this->getEntityTypes() as $entity_type_id) {
232 $entity_definition = $this->entityTypeManager->getDefinition($entity_type_id);
233 foreach ($this->getBundlesForEntityType($entity_type_id) as $bundle) {
234 $dependency = $entity_definition->getBundleConfigDependency($bundle);
235 $dependencies[$dependency['type']][] = $dependency['name'];
238 return $dependencies;
244 public function onDependencyRemoval(array $dependencies) {
245 $changed = parent::onDependencyRemoval($dependencies);
247 // When bundle config entities are removed, ensure they are cleaned up from
249 foreach ($dependencies['config'] as $removed_config) {
250 if ($entity_type_id = $removed_config->getEntityType()->getBundleOf()) {
251 $bundle_id = $removed_config->id();
252 $this->removeEntityTypeAndBundle($entity_type_id, $bundle_id);
257 // When modules that provide entity types are removed, ensure they are also
258 // removed from the workflow.
259 if (!empty($dependencies['module'])) {
260 // Gather all entity definitions provided by the dependent modules which
261 // are being removed.
262 $module_entity_definitions = [];
263 foreach ($this->entityTypeManager->getDefinitions() as $entity_definition) {
264 if (in_array($entity_definition->getProvider(), $dependencies['module'])) {
265 $module_entity_definitions[] = $entity_definition;
269 // For all entity types provided by the uninstalled modules, remove any
270 // configuration for those types.
271 foreach ($module_entity_definitions as $module_entity_definition) {
272 foreach ($this->getBundlesForEntityType($module_entity_definition->id()) as $bundle) {
273 $this->removeEntityTypeAndBundle($module_entity_definition->id(), $bundle);
285 public function getConfiguration() {
286 $configuration = parent::getConfiguration();
287 // Ensure that states and entity types are ordered consistently.
288 ksort($configuration['states']);
289 ksort($configuration['entity_types']);
290 return $configuration;
296 public function getInitialState(WorkflowInterface $workflow, $entity = NULL) {
297 if ($entity instanceof EntityPublishedInterface) {
298 return $workflow->getState($entity->isPublished() ? 'published' : 'draft');
300 return parent::getInitialState($workflow);