flow-1.0.0-beta8/flow.module
flow.module
<?php
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\flow\Entity\EntitySaveHandler;
use Drupal\flow\Flow;
use Drupal\flow\FlowTaskQueue;
/**
* @file
* Flow module file.
*/
/**
* Implements hook_entity_create().
*/
function flow_entity_create(EntityInterface $entity) {
Flow::service()->apply($entity, 'create');
}
/**
* Implements hook_entity_presave().
*/
function flow_entity_presave(EntityInterface $entity) {
Flow::service()->apply($entity, 'save');
_flow_save_new_referenced_entities($entity);
}
/**
* Implements hook_entity_predelete().
*/
function flow_entity_predelete(EntityInterface $entity) {
Flow::service()->apply($entity, 'delete');
}
/**
* Implements hook_entity_insert().
*/
function flow_entity_insert(EntityInterface $entity) {
_flow_update_task_stack($entity);
_flow_cleanup_save_stack($entity);
_flow_process_after_task($entity, 'create');
_flow_process_after_task($entity, 'save');
}
/**
* Implements hook_entity_update().
*/
function flow_entity_update(EntityInterface $entity) {
_flow_update_task_stack($entity);
_flow_cleanup_save_stack($entity);
_flow_process_after_task($entity, 'save');
}
/**
* Implements hook_entity_delete().
*/
function flow_entity_delete(EntityInterface $entity) {
_flow_process_after_task($entity, 'delete');
}
/**
* Processes Flow tasks that are to be executed after task completion.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The Flow-related entity.
* @param string $task_mode
* The completed task mode.
*/
function _flow_process_after_task(EntityInterface $entity, string $task_mode): void {
if (!($entity instanceof ContentEntityInterface)) {
// Flow only takes care of content entities.
return;
}
$flow_is_active = Flow::isActive();
Flow::setActive(\Drupal::getContainer()->getParameter('flow.allow_nested_flow'));
try {
if (!isset(Flow::$stack[$task_mode])) {
Flow::$stack[$task_mode] = [];
}
$stack = &Flow::$stack[$task_mode];
array_push($stack, $entity);
FlowTaskQueue::service()->process($entity, $task_mode, TRUE);
$entity_needs_save = FALSE;
if (!empty(Flow::$save)) {
/** @var \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository */
$entity_repository = \Drupal::service('entity.repository');
$loaded = $entity->uuid() ? $entity_repository->loadEntityByUuid($entity->getEntityTypeId(), $entity->uuid()) : NULL;
$entity_needs_save = $loaded && $task_mode !== 'delete' && (in_array($entity, Flow::$save, TRUE) || array_filter(Flow::$save, function ($stacked) use ($entity) {return $entity->uuid() && ($stacked->uuid() === $entity->uuid());}));
EntitySaveHandler::service()->ensureSave(Flow::$save);
}
array_pop($stack);
if ($entity_needs_save) {
Flow::setActive(FALSE);
if ($entity instanceof RevisionableInterface) {
$entity->updateLoadedRevisionId();
$entity->setNewRevision(FALSE);
}
$entity->save();
}
}
finally {
Flow::setActive($flow_is_active);
}
}
/**
* Updates the Flow task stack using the given entity.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The Flow-related entity.
*/
function _flow_update_task_stack(EntityInterface $entity) {
if ($uuid = $entity->uuid()) {
foreach (Flow::$stack as &$stack) {
foreach ($stack as $i => $stacked) {
if (($stacked !== $entity) && ($uuid === $stacked->uuid())) {
$stack[$i] = $entity;
}
}
}
}
}
/**
* Removes the given entity from the save stack.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The Flow-related entity.
*/
function _flow_cleanup_save_stack(EntityInterface $entity) {
if (!empty(Flow::$save)) {
foreach (Flow::$save as $i => $stacked) {
if (($stacked === $entity) || ($entity->uuid() && ($entity->uuid() === $stacked->uuid()))) {
unset(Flow::$save[$i]);
}
}
}
}
/**
* Saves referenced entities which are new and not saved yet.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The Flow-related entity.
*/
function _flow_save_new_referenced_entities(EntityInterface $entity) {
foreach ($entity->referencedEntities() as $referenced) {
// Besides the check for whether the entity is new, also make sure not to
// run into an infinite loop by checking for non-existence of a
// back-reference. This scenario would then also lead to a fatal error,
// but at least it won't run into a loop that blocks even more resources.
if ($referenced->isNew() && ($entity !== $referenced) && !in_array($entity, $referenced->referencedEntities(), TRUE)) {
$flow_is_active = Flow::isActive();
Flow::setActive(FALSE);
try {
$referenced->save();
}
finally {
Flow::setActive($flow_is_active);
}
_flow_cleanup_save_stack($referenced);
}
}
}
