diff --git a/README.md b/README.md index 3580be8..103924f 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,13 @@ Cascades entity status to all referenced entities in an Islandora repository item +## Usage +- Node Update + - On status change update all media and assign same status. + - If node is collection, on status change, update status of all attached nodes. +- Media presave + - Assign the status of the Media Of node. + ## Requirements This module requires the following modules/libraries: diff --git a/islandora_entity_status.info.yml b/islandora_entity_status.info.yml index ed87f72..a7159ad 100644 --- a/islandora_entity_status.info.yml +++ b/islandora_entity_status.info.yml @@ -7,3 +7,4 @@ dependencies: - drupal:node - drupal:file - islandora:islandora + - islandora_events:islandora_events diff --git a/islandora_entity_status.module b/islandora_entity_status.module index f885804..6155934 100644 --- a/islandora_entity_status.module +++ b/islandora_entity_status.module @@ -5,60 +5,7 @@ * Hook implementations. */ -use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\islandora\IslandoraUtils; -use Drupal\media\Entity\Media; -use Drupal\media\MediaInterface; -use Drupal\node\Entity\Node; -use Drupal\node\NodeInterface; -use Twig\Markup; - -/** - * Implements hook_ENTITY_TYPE_presave(). - */ -function islandora_entity_status_media_presave(MediaInterface $media) { - if ($media->hasField(IslandoraUtils::MEDIA_OF_FIELD)) { - $media_of = $media->get(IslandoraUtils::MEDIA_OF_FIELD); - if (!$media_of->isEmpty()) { - $node = $media_of->referencedEntities()[0]; - if ($node instanceof NodeInterface) { - $node_status = intval($node->status->value); - $media->set('status', $node_status); - } - } - } -} - -/** - * Implements hook_ENTITY_TYPE_update(). - */ -function islandora_entity_status_node_update(EntityInterface $entity) { - // Check if the entity is a node with the bundle "islandora_object". - if ($entity->hasField(IslandoraUtils::MEMBER_OF_FIELD)) { - // Get the current node ID. - $nid = $entity->id(); - - // Query for media items that are associated with the current node. - $query = \Drupal::entityQuery('media') - ->accessCheck(FALSE) - ->condition(IslandoraUtils::MEDIA_OF_FIELD, $nid); - $media_ids = $query->execute(); - - // Load the media items and set their status to the same status as the node. - $media_items = Media::loadMultiple($media_ids); - foreach ($media_items as $media_item) { - $media_item->set('status', $entity->get('status')->value); - $media_item->save(); - } - - // Trigger the batch process for collection node. - $node_ids_to_update = find_collection_nodes($nid); - $latestStatus = $entity->get('status')->value; - - islandora_entity_status_trigger_batch_process($node_ids_to_update, $latestStatus); - } -} /** * Implements hook_form_FORM_ID_alter(). @@ -74,91 +21,3 @@ function islandora_entity_status_form_node_islandora_object_edit_form_alter(&$fo // Pass the confirmation message to JavaScript. $form['#attached']['drupalSettings']['custom_confirm_popup']['message'] = $message; } - -/** - * Find related nodes. - */ -function find_collection_nodes($currentNodeId) { - $relatedNodeIds = []; - - // Initial query to find nodes where the field_member_of contains - // the current node ID. - $query = \Drupal::entityQuery('node') - ->condition('type', 'islandora_object') - ->condition('field_member_of', $currentNodeId, '=') - ->accessCheck(FALSE); - - $result = $query->execute(); - - $relatedNodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple($result); - - if (!empty($relatedNodes)) { - foreach ($relatedNodes as $relatedNode) { - $relatedNodeIds[] = $relatedNode->id(); - } - } - - return $relatedNodeIds; -} - -/** - * Helper function to trigger the batch process. - */ -function islandora_entity_status_trigger_batch_process($node_ids, $node_status) { - // Create a batch operation. - $operations = [ - ['islandora_entity_status_batch_operation', [$node_ids, $node_status]], - ]; - - // Create a batch. - $batch = [ - 'title' => t('Processing nodes'), - 'operations' => $operations, - 'finished' => 'islandora_entity_status_batch_finished', - ]; - - // Add the batch to the queue. - batch_set($batch); -} - -/** - * Batch operation callback. - */ -function islandora_entity_status_batch_operation($node_ids, $node_status, &$context) { - // Perform your batch processing here. - // Update the status for each related node. - foreach ($node_ids as $relatedNodeId) { - $relatedNode = Node::load($relatedNodeId); - $relatedNode->set('status', $node_status); - $relatedNode->save(); - - // Update the progress. - $context['results'][] = t('Node %node processed and status set to %status.', - ['%node' => $relatedNodeId, '%status' => $node_status]); - } -} - -/** - * Batch finished callback. - */ -function islandora_entity_status_batch_finished($success, $results, $operations) { - $messenger = \Drupal::messenger(); - $message = ''; - - if ($success) { - if (!empty($results)) { - // Batch processing completed successfully. - // Display a message indicating success. - $messenger->addMessage(t('Batch processing completed successfully.')); - - foreach ($results as $result) { - $message .= '
' . $result; - } - $messenger->addMessage(new Markup($message, 'html')); - } - } - else { - // Batch processing failed. - $messenger->addError(t('Batch processing failed.')); - } -} diff --git a/islandora_entity_status.services.yml b/islandora_entity_status.services.yml index 78543d4..73a8a1c 100644 --- a/islandora_entity_status.services.yml +++ b/islandora_entity_status.services.yml @@ -5,3 +5,14 @@ services: - '@entity_type.manager' tags: - { name: drush.command } + + islandora_entity_status.islandora_node_entity_subscriber: + class: Drupal\islandora_entity_status\EventSubscriber\IslandoraNodeEventSubscriber + arguments: ['@entity_type.manager', '@messenger', '@renderer'] + tags: + - { name: event_subscriber } + + islandora_entity_status.islandora_media_entity_subscriber: + class: Drupal\islandora_entity_status\EventSubscriber\IslandoraMediaEventSubscriber + tags: + - { name: event_subscriber } diff --git a/src/EventSubscriber/IslandoraMediaEventSubscriber.php b/src/EventSubscriber/IslandoraMediaEventSubscriber.php new file mode 100644 index 0000000..cfaa154 --- /dev/null +++ b/src/EventSubscriber/IslandoraMediaEventSubscriber.php @@ -0,0 +1,37 @@ + 'onIslandoraMediaPresave', + ]; + } + + /** + * Reacts to the Islandora Media presave event. + * + * @param \Drupal\islandora_events\Event\IslandoraMediaEvent $event + * The Islandora Media event. + */ + public function onIslandoraMediaPresave(IslandoraMediaEvent $event) { + $media = $event->getMedia(); + $media_of_node = $event->getReferencedNode(); + + // Set media status same as media_of node status. + $media_of_node_status = intval($media_of_node->status->value); + $media->set('status', $media_of_node_status); + } + +} diff --git a/src/EventSubscriber/IslandoraNodeEventSubscriber.php b/src/EventSubscriber/IslandoraNodeEventSubscriber.php new file mode 100644 index 0000000..49b8bc2 --- /dev/null +++ b/src/EventSubscriber/IslandoraNodeEventSubscriber.php @@ -0,0 +1,244 @@ +entityTypeManager = $entity_type_manager; + $this->messenger = $messenger; + $this->renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + return [ + IslandoraNodeEvent::UPDATE => 'onIslandoraNodeUpdated', + IslandoraCollectionStatusUpdate::COLLECTION_STATUS_UPDATED => 'onCollectionStatusUpdated', + ]; + } + + /** + * Reacts to node update events. + * + * @param \Drupal\islandora_events\Event\IslandoraNodeEvent $event + * The Islandora node event. + */ + public function onIslandoraNodeUpdated(IslandoraNodeEvent $event) { + // Get the current node ID. + $node = $event->getNode(); + $nid = $node->id(); + + // When there is a change in status update all attached media. + if ($node->original->isPublished() != $node->isPublished()) { + // Query for media items that are associated with the current node. + $query = $this->entityTypeManager->getStorage('media')->getQuery(); + $query->accessCheck(FALSE); + $query->condition(IslandoraUtils::MEDIA_OF_FIELD, $nid); + $media_ids = $query->execute(); + + // Load the media items and set their status to the same status + // as the node. + $media_items = $this->entityTypeManager->getStorage('media') + ->loadMultiple($media_ids); + foreach ($media_items as $media_item) { + // We just need to save the media. + // Status is getting handled in pre_save. + // @see IslandoraMediaEntitySubscriber:onIslandoraMediaPresave. + $media_item->save(); + } + } + } + + /** + * Reacts to collection status updates. + * + * @param \Drupal\islandora_events\Event\IslandoraCollectionStatusUpdate $event + * The collection object update event. + */ + public function onCollectionStatusUpdated(IslandoraCollectionStatusUpdate $event) { + // Update status of all nodes attached to the collection. + $node = $event->getNode(); + $attached_nodes = $this->findNodesAttachedToCollection($node->id()); + + if (!empty($attached_nodes)) { + // On node or workflow status update of collection node + // we need to update all attached object node or workflow status. + $oldNodeStatus = $event->getOriginalNodeStatus(); + $updatedNodeStatus = $event->getUpdatedNodeStatus(); + $status['node'] = ($oldNodeStatus !== $updatedNodeStatus) ? $updatedNodeStatus : ''; + + $oldWorkflowStatus = $event->getOriginalWorkflowStatus(); + $updatedWorkflowStatus = $event->getUpdatedWorkflowStatus(); + $status['workflow'] = ($oldWorkflowStatus !== $updatedWorkflowStatus) ? $updatedWorkflowStatus : ''; + $this->triggerBatchProcess($attached_nodes, $status, 10); + } + } + + /** + * Find nodes attached to the Collection. + */ + protected function findNodesAttachedToCollection($currentNodeId) { + $relatedNodeIds = []; + + // Initial query to find nodes where the field_member_of contains + // the current node ID. + $query = $this->entityTypeManager->getStorage('node')->getQuery(); + $query->condition(IslandoraUtils::MEMBER_OF_FIELD, $currentNodeId); + $query->accessCheck(FALSE); + + $result = $query->execute(); + + $relatedNodes = $this->entityTypeManager->getStorage('node')->loadMultiple($result); + + if (!empty($relatedNodes)) { + foreach ($relatedNodes as $relatedNode) { + $relatedNodeIds[] = $relatedNode->id(); + } + } + + return $relatedNodeIds; + } + + /** + * Helper function to trigger the batch process. + */ + protected function triggerBatchProcess($node_ids, $status, $batch_size = 10) { + $operations = []; + foreach ($node_ids as $node_id) { + $operations[] = [ + [$this, 'islandoraEntityStatusBatchOperation'], + [$node_id, $status], + ]; + } + + $batch = [ + 'title' => $this->t('Processing nodes'), + 'operations' => $operations, + 'finished' => [$this, 'islandoraEntityStatusBatchFinished'], + 'batch_size' => $batch_size, + ]; + batch_set($batch); + } + + /** + * Batch operation callback. + */ + public function islandoraEntityStatusBatchOperation($node_id, $status, &$context) { + // Perform your batch processing here. + // Load the node using the entity type manager. + $node = $this->entityTypeManager->getStorage('node')->load($node_id); + if ($node) { + $message = ''; + + if (isset($status['node'])) { + // Update the status of the node. + $node->set('status', $status['node']); + $message .= 'Node status = ' . $status['node'] . ', '; + } + + if (!empty($status['workflow'])) { + // Set the moderation state. + $node->set('moderation_state', $status['workflow']); + $message .= 'Workflow status = ' . $status['workflow'] . ', '; + } + + $node->save(); + + // Remove trailing comma and space. + $message = rtrim($message, ', '); + + // Update the progress. + $context['results'][] = $this->t('Node %node processed and status set to %status.', [ + '%node' => $node_id, + '%status' => $message, + ]); + } + else { + // Handle the case where the node cannot be loaded. + $context['results'][] = $this->t('Failed to load node with ID %node.', ['%node' => $node_id]); + } + } + + /** + * Batch finished callback. + */ + public function islandoraEntityStatusBatchFinished($success, $results, $operations) { + $messenger = $this->messenger; + + if ($success) { + if (!empty($results)) { + // Batch processing completed successfully. + // Display a message indicating success. + $messages = []; + foreach ($results as $result) { + $messages[] = $result; + } + + $message = [ + '#theme' => 'item_list', + '#items' => $messages, + '#title' => $this->t('Processed Nodes'), + ]; + + $this->messenger->addMessage($this->renderer->renderRoot($message)); + } + else { + $messenger->addMessage($this->t('No nodes were processed.')); + } + } + else { + // Batch processing failed. + $messenger->addError($this->t('Batch processing failed.')); + } + } + +}