Last active
March 24, 2021 10:51
-
-
Save flocondetoile/7a69756634d45b60110c02c30ec26947 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Implements hook_entity_base_field_info(). | |
* | |
* - Provides a base field that displays the current workflow state on nodes. | |
* This field is intended to be used to expose this information. | |
*/ | |
function state_machine_workflow_entity_base_field_info(EntityTypeInterface $entity_type) { | |
$fields = []; | |
/** @var \Drupal\state_machine_workflow\WorkflowHelper $workflowHelper */ | |
$workflowHelper = \Drupal::service('state_machine_workflow.workflow.helper'); | |
if ($entity_type->id() === 'node') { | |
$fields['current_workflow_state'] = BaseFieldDefinition::create('current_workflow_state') | |
->setLabel(new TranslatableMarkup('Current workflow state')) | |
->setClass('\Drupal\state_machine_workflow\CurrentWorkflowStateFieldItemList') | |
->setDisplayOptions('form', ['type' => 'hidden']) | |
->setTranslatable(TRUE) | |
->setDisplayOptions('view', [ | |
'label' => 'hidden', | |
'type' => 'hidden', | |
'weight' => 0, | |
]) | |
->setDisplayConfigurable('form', TRUE) | |
->setDisplayConfigurable('view', TRUE) | |
->setComputed(TRUE); | |
} | |
return $fields; | |
} | |
/** | |
* Implements hook_field_formatter_info_alter(). | |
* | |
* Make sure the standard formatter for string can be used for the current | |
* workflow state computed field. | |
*/ | |
function state_machine_workflow_field_formatter_info_alter(array &$info) { | |
$info['string']['field_types'][] = 'current_workflow_state'; | |
} | |
/** | |
* Implements hook_views_data_alter(). | |
*/ | |
function state_machine_workflow_views_data_alter(array &$data) { | |
if (isset($data['node'])) { | |
// Add the current company computed field to Views. | |
$data['node']['current_workflow_state'] = [ | |
'title' => t('Current workflow state'), | |
'field' => [ | |
'id' => 'state_machine_workflow_view_current_workflow_state', | |
], | |
]; | |
} | |
} | |
================================================ | |
<?php | |
namespace Drupal\state_machine_workflow; | |
use Drupal\Core\Field\FieldItemList; | |
/** | |
* Item list for the current workflow state. | |
* | |
* @see \Drupal\state_machine_workflow\Plugin\Field\FieldType\CurrentWorkflowStateItem | |
*/ | |
class CurrentWorkflowStateFieldItemList extends FieldItemList { | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getIterator() { | |
$this->ensureLoaded(); | |
return new \ArrayIterator($this->list); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getValue($include_computed = FALSE) { | |
$this->ensureLoaded(); | |
return parent::getValue($include_computed); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function isEmpty() { | |
$this->ensureLoaded(); | |
return parent::isEmpty(); | |
} | |
/** | |
* Makes sure that the item list is never empty. | |
* | |
* For 'normal' fields that use database storage the field item list is | |
* initially empty, but since this is a computed field this always has a | |
* value. | |
* Make sure the item list is always populated, so this field is not skipped | |
* for rendering in EntityViewDisplay and friends. | |
* | |
* This trick has been borrowed from issue #2846554 which does the same for | |
* the PathItem field. | |
* | |
* @see https://www.drupal.org/node/2846554 | |
*/ | |
protected function ensureLoaded() { | |
if (!isset($this->list[0])) { | |
$this->list[0] = $this->createItem(0); | |
} | |
} | |
} | |
================================================ | |
<?php | |
namespace Drupal\state_machine_workflow\Plugin\Field\FieldType; | |
use Drupal\Core\Field\Plugin\Field\FieldType\StringItem; | |
use Drupal\Core\Entity\EntityInterface; | |
use Drupal\Core\TypedData\DataDefinitionInterface; | |
use Drupal\Core\TypedData\TypedDataInterface; | |
/** | |
* Computed field that shows the current workflow state. | |
* | |
* @FieldType( | |
* id = "current_workflow_state", | |
* label = @Translation("Current workflow state"), | |
* description = @Translation("Computed field that shows the current workflow state."), | |
* no_ui = TRUE, | |
* default_widget = "current_workflow_state_widget", | |
* default_formatter = "current_workflow_state_formatter" | |
* ) | |
*/ | |
class CurrentWorkflowStateItem extends StringItem { | |
/** | |
* The workflow helper service. | |
* | |
* @var \Drupal\state_machine_workflow\WorkflowHelperInterface | |
*/ | |
protected $workflowHelper; | |
/** | |
* The revision manager service. | |
* | |
* @var \Drupal\state_machine_workflow\RevisionManagerInterface | |
*/ | |
protected $revisionManager; | |
/** | |
* Whether or not the value has been calculated. | |
* | |
* @var bool | |
*/ | |
protected $isCalculated = FALSE; | |
/** | |
* {@inheritdoc} | |
*/ | |
public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) { | |
parent::__construct($definition, $name, $parent); | |
$this->workflowHelper = \Drupal::service('state_machine_workflow.workflow.helper'); | |
$this->revisionManager = \Drupal::service('state_machine_workflow.revision_manager'); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function __get($name) { | |
$this->ensureCalculated(); | |
return parent::__get($name); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function isEmpty() { | |
$this->ensureCalculated(); | |
return parent::isEmpty(); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getValue() { | |
$this->ensureCalculated(); | |
return parent::getValue(); | |
} | |
/** | |
* Makes sure that the value is populated. | |
* | |
* Normal fields get their data from the database and are populated if there | |
* is data available. Since this is a computed field we need to make sure | |
* there is always data available ourselves. | |
* | |
* This trick has been borrowed from issue #2846554 which does the same for | |
* the PathItem field. | |
* | |
* @todo Remove this when issue #2392845 is fixed. | |
* | |
* @see https://www.drupal.org/node/2392845 | |
* @see https://www.drupal.org/node/2846554 | |
*/ | |
protected function ensureCalculated() { | |
if (!$this->isCalculated) { | |
$entity = $this->getEntity(); | |
if (!$entity->isNew()) { | |
$latest_revision = $this->revisionManager->loadLatestRevision($entity); | |
$latest_revision = \Drupal::service('entity.repository')->getTranslationFromContext($latest_revision); | |
if ($this->workflowHelper->hasEntityStateField($latest_revision)) { | |
$field = $this->workflowHelper->getEntityStateField($latest_revision); | |
$state_id = $field->getValue()['value']; | |
$value = $field->getWorkflow()->getState($state_id)->getLabel(); | |
$this->setValue($value); | |
} | |
} | |
$this->isCalculated = TRUE; | |
} | |
} | |
/** | |
* Checks access to the given entity. | |
* | |
* By default, entity 'view' access is checked. However, a subclass can choose | |
* to exclude certain items from entity access checking by immediately | |
* granting access. | |
* | |
* @param \Drupal\Core\Entity\EntityInterface $entity | |
* The entity to check. | |
* | |
* @return bool|\Drupal\Core\Access\AccessResult | |
* A cacheable access result. | |
*/ | |
protected function checkAccess(EntityInterface $entity) { | |
return $entity->access('edit', NULL, FALSE); | |
} | |
} | |
================================================ | |
<?php | |
namespace Drupal\state_machine_workflow\Plugin\Field\FieldWidget; | |
use Drupal\Core\Field\FieldDefinitionInterface; | |
use Drupal\Core\Field\FieldItemListInterface; | |
use Drupal\Core\Field\WidgetBase; | |
use Drupal\Core\Form\FormStateInterface; | |
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | |
use Drupal\state_machine_workflow\WorkflowHelperInterface; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
/** | |
* Plugin implementation of the 'current_workflow_state_widget' widget. | |
* | |
* @FieldWidget( | |
* id = "current_workflow_state_widget", | |
* label = @Translation("Plain text"), | |
* field_types = { | |
* "current_workflow_state" | |
* } | |
* ) | |
*/ | |
class CurrentWorkflowStateWidget extends WidgetBase implements ContainerFactoryPluginInterface { | |
/** | |
* The workflow helper service. | |
* | |
* @var \Drupal\state_machine_workflow\WorkflowHelperInterface | |
*/ | |
protected $workflowHelper; | |
/** | |
* Constructs a SearchWidget object. | |
* | |
* @param string $plugin_id | |
* The plugin_id for the widget. | |
* @param mixed $plugin_definition | |
* The plugin implementation definition. | |
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition | |
* The definition of the field to which the widget is associated. | |
* @param array $settings | |
* The widget settings. | |
* @param array $third_party_settings | |
* Any third party settings. | |
* @param \Drupal\state_machine_workflow\WorkflowHelperInterface $workflow_helper | |
* The workflow helper service. | |
*/ | |
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, WorkflowHelperInterface $workflow_helper) { | |
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings); | |
$this->workflowHelper = $workflow_helper; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | |
return new static( | |
$plugin_id, | |
$plugin_definition, | |
$configuration['field_definition'], | |
$configuration['settings'], | |
$configuration['third_party_settings'], | |
$container->get('state_machine_workflow.workflow.helper') | |
); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public static function defaultSettings() { | |
return [ | |
'title' => 'Current workflow state', | |
'title_display' => 'before', | |
'show_for_new_entities' => FALSE, | |
] + parent::defaultSettings(); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function settingsForm(array $form, FormStateInterface $form_state) { | |
$elements = parent::settingsForm($form, $form_state); | |
$elements['title'] = [ | |
'#type' => 'textfield', | |
'#title' => $this->t('Label'), | |
'#default_value' => $this->getSetting('title'), | |
]; | |
$elements['title_display'] = [ | |
'#type' => 'radios', | |
'#title' => $this->t('Display label'), | |
'#default_value' => $this->getSetting('title_display'), | |
'#options' => [ | |
'before' => $this->t('Label goes before the element'), | |
'after' => $this->t('Label goes after the element'), | |
'invisible' => $this->t('Label is there but is made invisible using CSS'), | |
'attribute' => $this->t('Make it the title attribute (hover tooltip)'), | |
], | |
]; | |
$elements['show_for_new_entities'] = [ | |
'#type' => 'checkbox', | |
'#title' => $this->t('Show when creating a new entity'), | |
'#description' => $this->t('If unchecked, the widget is shown only on forms where an existing entity is being edited.'), | |
'#default_value' => $this->getSetting('show_for_new_entities'), | |
]; | |
return $elements; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function settingsSummary() { | |
$summary = [ | |
$this->t('Label: @title', [ | |
'@title' => $this->getSetting('title'), | |
]), | |
$this->t('Display label: @title_display', [ | |
'@title_display' => $this->getSetting('title_display'), | |
]), | |
]; | |
if ($this->getSetting('show_for_new_entities')) { | |
$summary[] = $this->t('Show when creating a new entity'); | |
} | |
return $summary; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { | |
/** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */ | |
$entity = $items->getEntity(); | |
$field = $this->workflowHelper->getEntityStateField($entity); | |
$state_id = $field->getValue()['value']; | |
$element['#title'] = $this->getSetting('title'); | |
$element['#title_display'] = $this->getSetting('title_display'); | |
$element['#type'] = 'item'; | |
$element['#element_validate'][] = [get_class($this), 'validateFormElement']; | |
$element['current_workflow_state']['#type'] = 'container'; | |
$element['current_workflow_state']['#attributes'] = ['class' => ['current-workflow-state']]; | |
$element['current_workflow_state']['label'] = [ | |
'#plain_text' => $field->getWorkflow()->getState($state_id)->getLabel(), | |
]; | |
// Show the widget only when the entity is not new, or when the specific | |
// setting is turned on. | |
$element['#access'] = !$entity->isNew() || $this->getSetting('show_for_new_entities'); | |
return $element; | |
} | |
/** | |
* Form element validation handler. | |
* | |
* @param array $element | |
* The form element. | |
* @param \Drupal\Core\Form\FormStateInterface $form_state | |
* The form state. | |
*/ | |
public static function validateFormElement(array &$element, FormStateInterface $form_state) { | |
// We have nothing to validate, this data is coming from a trusted source. | |
// Just set the value for the element directly. | |
$form_state->setValueForElement($element['current_workflow_state'], $element['current_workflow_state']['label']['#plain_text']); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment