From 9db057b6447b424ae38d2799e4188544d9774664 Mon Sep 17 00:00:00 2001 From: astanley Date: Wed, 28 May 2025 15:12:42 -0300 Subject: [PATCH] Added bulk media updates --- islandora_batch_action.links.menu.yml | 10 +- islandora_batch_action.routing.yml | 8 ++ islandora_batch_action.services.yml | 6 +- src/Batch/ActionBatch.php | 80 ++++++++++++++ src/Batch/NodeActionBatch.php | 76 ------------- src/Form/MediaActionsForm.php | 100 ++++++++++++++++++ src/Form/MissingDerivativesFm.php | 56 ++++------ src/IslandoraBatchActionUtils.php | 80 ++++++++++++++ .../Action/GenerateImageMediaDimensions.php | 85 +++++++++++++++ 9 files changed, 383 insertions(+), 118 deletions(-) create mode 100644 src/Batch/ActionBatch.php delete mode 100644 src/Batch/NodeActionBatch.php create mode 100644 src/Form/MediaActionsForm.php create mode 100644 src/Plugin/Action/GenerateImageMediaDimensions.php diff --git a/islandora_batch_action.links.menu.yml b/islandora_batch_action.links.menu.yml index 2d3858c..1964ca6 100644 --- a/islandora_batch_action.links.menu.yml +++ b/islandora_batch_action.links.menu.yml @@ -4,4 +4,12 @@ islandora_batch_action_create_media: route_name: islandora_batch_action.missing_derivatives_fm parent: system.admin_config_media menu_name: administration - weight: 10 \ No newline at end of file + weight: 10 + +islandora_batch_action_add_dimensions: + title: 'Bulk media actions' + description: 'Runs media actions' + route_name: islandora_batch_action.generate_dimensions + parent: system.admin_config_media + menu_name: administration + weight: 10 diff --git a/islandora_batch_action.routing.yml b/islandora_batch_action.routing.yml index 9ebf081..bbcf984 100644 --- a/islandora_batch_action.routing.yml +++ b/islandora_batch_action.routing.yml @@ -5,3 +5,11 @@ islandora_batch_action.missing_derivatives_fm: _form: 'Drupal\islandora_batch_action\Form\MissingDerivativesFm' requirements: _permission: 'administer media' + +islandora_batch_action.generate_dimensions: + path: '/islandora-batch-action/generate-dimensions' + defaults: + _title: 'Bulk Actions' + _form: 'Drupal\islandora_batch_action\Form\MediaActionsForm' + requirements: + _permission: 'administer media' diff --git a/islandora_batch_action.services.yml b/islandora_batch_action.services.yml index a948152..53fff5f 100644 --- a/islandora_batch_action.services.yml +++ b/islandora_batch_action.services.yml @@ -9,6 +9,6 @@ services: class: Drupal\islandora_batch_action\IslandoraBatchActionUtils arguments: ['@database', '@entity_type.manager', '@logger.channel.islandora', '@islandora.utils'] - islandora_batch_action.node_action_batch: - class: 'Drupal\islandora_batch_action\Batch\NodeActionBatch' - arguments: ['@entity_type.manager', '@plugin.manager.action'] \ No newline at end of file + islandora_batch_action.action_batch: + class: 'Drupal\islandora_batch_action\Batch\ActionBatch' + arguments: ['@entity_type.manager', '@plugin.manager.action'] diff --git a/src/Batch/ActionBatch.php b/src/Batch/ActionBatch.php new file mode 100644 index 0000000..7b3e553 --- /dev/null +++ b/src/Batch/ActionBatch.php @@ -0,0 +1,80 @@ +setTitle(t('Performing batch action on entities.')) + ->setInitMessage(t('Starting the batch action.')) + ->setProgressMessage(t('Processing @current out of @total.')) + ->setErrorMessage(t('An error occurred.')); + + foreach ($entity_ids as $entity_id) { + $batch_builder->addOperation([__CLASS__, 'processEntity'], [ + $entity_id, + $action_id, + $entity_type, + ]); + } + batch_set($batch_builder->toArray()); + } + + /** + * Processes an individual entity for the batch operation. + * + * @param int $entity_id + * The entity ID to process. + * @param string $action_id + * The action plugin ID. + * @param array $context + * The batch context array. + */ + public static function processEntity(int $entity_id, string $action_id, string $entity_type, array &$context): void { + $entityTypeManager = \Drupal::service('entity_type.manager'); + $entity = $entityTypeManager->getStorage($entity_type)->load($entity_id); + if (!$entity) { + $context['results']['failed'][] = $entity_id; + return; + } + $action_storage = $entityTypeManager->getStorage('action'); + $configured_action = $action_storage->load($action_id); + + if (!$configured_action) { + $context['results']['failed'][] = $entity_id; + return; + } + try { + $configured_action->execute([$entity]); + $context['results']['processed'][] = $entity_id; + } + catch (\Exception $e) { + \Drupal::logger('islandora_batch_action') + ->error('Batch action failed for node @nid: @message', [ + '@nid' => $entity_id, + '@message' => $e->getMessage(), + ]); + $context['results']['failed'][] = $entity_id; + } + } + +} diff --git a/src/Batch/NodeActionBatch.php b/src/Batch/NodeActionBatch.php deleted file mode 100644 index b79955c..0000000 --- a/src/Batch/NodeActionBatch.php +++ /dev/null @@ -1,76 +0,0 @@ -setTitle(t('Performing batch action on nodes.')) - ->setInitMessage(t('Starting the batch action.')) - ->setProgressMessage(t('Processing @current out of @total.')) - ->setErrorMessage(t('An error occurred.')); - - // Register the operation for each node. - foreach ($node_ids as $node_id) { - $batch_builder->addOperation([__CLASS__, 'processNode'], [$node_id, $action_id]); - } - - batch_set($batch_builder->toArray()); - } - - /** - * Processes an individual node for the batch operation. - * - * @param int $node_id - * The node ID to process. - * @param string $action_id - * The action plugin ID. - * @param array $context - * The batch context array. - */ - public static function processNode(int $node_id, string $action_id, array &$context): void { - $entityTypeManager = \Drupal::service('entity_type.manager'); - $node = $entityTypeManager->getStorage('node')->load($node_id); - if (!$node) { - $context['results']['failed'][] = $node_id; - return; - } - $action_storage = $entityTypeManager->getStorage('action'); - $configured_action = $action_storage->load($action_id); - - if (!$configured_action) { - $context['results']['failed'][] = $node_id; - return; - } - try { - $configured_action->execute([$node]); - $context['results']['processed'][] = $node_id; - } - catch (\Exception $e) { - \Drupal::logger('islandora_batch_action')->error('Batch action failed for node @nid: @message', [ - '@nid' => $node_id, - '@message' => $e->getMessage(), - ]); - $context['results']['failed'][] = $node_id; - } - - } - -} diff --git a/src/Form/MediaActionsForm.php b/src/Form/MediaActionsForm.php new file mode 100644 index 0000000..e798fb7 --- /dev/null +++ b/src/Form/MediaActionsForm.php @@ -0,0 +1,100 @@ +islandoraBatchActionUtils = $islandoraBatchActionUtils; + $this->batchJob = $batchJob; + } + + public static function create(ContainerInterface $container) { + return new static( + $container->get('islandora_batch_action.utils'), + $container->get('islandora_batch_action.action_batch'), + ); + + } + + /** + * {@inheritdoc} + */ + public function getFormId(): string { + return 'islandora_batch_action_generate_dimensions'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state): array { + + $form['media_type'] = [ + '#type' => 'select', + '#title' => $this->t('Media Type'), + '#options' => $this->islandoraBatchActionUtils->getMediaOptions(), + '#required' => TRUE, + ]; + $form['usage'] = [ + '#type' => 'select', + '#title' => $this->t('Media Use'), + '#options' => $this->islandoraBatchActionUtils->getVocabularyOptions('islandora_media_use'), + '#required' => TRUE, + ]; + $form['media_action'] = [ + '#type' => 'select', + '#title' => $this->t('Action'), + '#options' => $this->islandoraBatchActionUtils->getMediaActions(), + '#required' => TRUE, + ]; + + $form['actions'] = [ + '#type' => 'actions', + 'submit' => [ + '#type' => 'submit', + '#value' => $this->t('Create batch'), + ], + ]; + + return $form; + } + + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state): void { + $media_type = $form_state->getValue('media_type'); + $tid = $form_state->getValue('usage'); + $mids = $this->islandoraBatchActionUtils->getMediaList($media_type, $tid); + $action_id = $form_state->getValue('media_action'); + $this->batchJob->run($mids, $action_id, 'media'); + } + +} diff --git a/src/Form/MissingDerivativesFm.php b/src/Form/MissingDerivativesFm.php index 6726c09..1498a9e 100644 --- a/src/Form/MissingDerivativesFm.php +++ b/src/Form/MissingDerivativesFm.php @@ -9,7 +9,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\islandora_batch_action\IslandoraBatchActionUtils; -use Drupal\islandora_batch_action\Batch\NodeActionBatch; +use Drupal\islandora_batch_action\Batch\ActionBatch; use Drupal\Core\Action\ActionManager; /** @@ -34,9 +34,9 @@ final class MissingDerivativesFm extends FormBase { /** * The batch job service. * - * @var \Drupal\islandora_batch_action\Batch\NodeActionBatch + * @var \Drupal\islandora_batch_action\Batch\ActionBatch */ - protected NodeActionBatch $batchJob; + protected ActionBatch $batchJob; /** * The plugin manager for actions. @@ -52,16 +52,17 @@ final class MissingDerivativesFm extends FormBase { * The entity type manager service. * @param \Drupal\islandora_batch_action\IslandoraBatchActionUtils $islandoraBatchActionUtils * The Batch Action utils. - * @param \Drupal\islandora_batch_action\Batch\NodeActionBatch $batchJob + * @param \Drupal\islandora_batch_action\Batch\ActionBatch $batchJob * The batch job service. * @param \Drupal\Core\Action\ActionManager $actionPluginManager * The action plugin manager. */ public function __construct( EntityTypeManagerInterface $entityTypeManager, - IslandoraBatchActionUtils $islandoraBatchActionUtils, - NodeActionBatch $batchJob, - ActionManager $actionPluginManager) { + IslandoraBatchActionUtils $islandoraBatchActionUtils, + ActionBatch $batchJob, + ActionManager $actionPluginManager + ) { $this->entityTypeManager = $entityTypeManager; $this->islandoraBatchActionUtils = $islandoraBatchActionUtils; $this->batchJob = $batchJob; @@ -73,10 +74,10 @@ final class MissingDerivativesFm extends FormBase { */ public static function create(ContainerInterface $container) { return new static( - $container->get('entity_type.manager'), - $container->get('islandora_batch_action.utils'), - $container->get('islandora_batch_action.node_action_batch'), - $container->get('plugin.manager.action'), + $container->get('entity_type.manager'), + $container->get('islandora_batch_action.utils'), + $container->get('islandora_batch_action.action_batch'), + $container->get('plugin.manager.action'), ); } @@ -94,19 +95,19 @@ final class MissingDerivativesFm extends FormBase { $form['model'] = [ '#type' => 'select', '#title' => $this->t('Islandora Model'), - '#options' => $this->getVocabularyOptions('islandora_models'), + '#options' => $this->islandoraBatchActionUtils->getVocabularyOptions('islandora_models'), '#required' => TRUE, ]; $form['usage'] = [ '#type' => 'select', '#title' => $this->t('Derivative Type'), - '#options' => $this->getVocabularyOptions('islandora_media_use'), + '#options' => $this->islandoraBatchActionUtils->getVocabularyOptions('islandora_media_use'), '#required' => TRUE, ]; $form['actions_id'] = [ '#type' => 'select', '#title' => $this->t('Derivative Actions'), - '#options' => $this->getDerivativeActions(), + '#options' => $this->islandoraBatchActionUtils->getDerivativeActions(), '#required' => TRUE, ]; @@ -130,10 +131,11 @@ final class MissingDerivativesFm extends FormBase { $action_id = $form_state->getValue('actions_id'); $nids = $this->islandoraBatchActionUtils->generateNodeList($model, $usage); if (empty($nids)) { - $this->messenger()->addWarning($this->t('No nodes found for the selected model and usage.')); + $this->messenger() + ->addWarning($this->t('No nodes found for the selected model and usage.')); return; } - $this->batchJob->run($nids, $action_id); + $this->batchJob->run($nids, $action_id, 'node');; } /** @@ -159,26 +161,4 @@ final class MissingDerivativesFm extends FormBase { return $options; } - /** - * Gets filtered actions. - */ - public function getDerivativeActions(): array { - $options = []; - $user_actions = $this->entityTypeManager - ->getStorage('action') - ->loadMultiple(); - - foreach ($user_actions as $action) { - if ($action_plugin = $action->getPlugin()) { - if (method_exists($action_plugin, 'getConfiguration')) { - $config = $action_plugin->getConfiguration(); - if (isset($config['event'])&& $config['event'] == 'Generate Derivative') { - $options[$action->id()] = $action->label(); - } - } - } - } - return $options; - } - } diff --git a/src/IslandoraBatchActionUtils.php b/src/IslandoraBatchActionUtils.php index f6a4bc3..a39bb26 100644 --- a/src/IslandoraBatchActionUtils.php +++ b/src/IslandoraBatchActionUtils.php @@ -7,6 +7,7 @@ namespace Drupal\islandora_batch_action; use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Logger\LoggerChannelInterface; +use Drupal\media\Entity\MediaType; /** * Provides utility functions for bulk derivative actions in Islandora. @@ -56,4 +57,83 @@ final class IslandoraBatchActionUtils { return $mainQuery->execute()->fetchCol(); } + /** + * Generates an options array from a taxonomy vocabulary. + * + * @param string $vocabulary + * The machine name of the taxonomy vocabulary. + * + * @return array + * An associative array of term IDs and term names. + */ + public function getVocabularyOptions(string $vocabulary): array { + $options = []; + + $terms = $this->entityTypeManager + ->getStorage('taxonomy_term') + ->loadTree($vocabulary); + + foreach ($terms as $term) { + $options[$term->tid] = $term->name; + } + + return $options; + } + + /** + * Gets filtered actions. + */ + public function getDerivativeActions(): array { + $options = []; + $user_actions = $this->entityTypeManager + ->getStorage('action') + ->loadMultiple(); + + foreach ($user_actions as $action) { + if ($action_plugin = $action->getPlugin()) { + if (method_exists($action_plugin, 'getConfiguration')) { + $config = $action_plugin->getConfiguration(); + if (isset($config['event']) && $config['event'] == 'Generate Derivative') { + $options[$action->id()] = $action->label(); + } + } + } + } + return $options; + } + + /** + * Gets media actions. + */ + public function getMediaActions(): array { + $options = []; + $user_actions = $this->entityTypeManager + ->getStorage('action') + ->loadMultiple(); + + foreach ($user_actions as $action) { + if ($action->getType() === 'media') { + $options[$action->id()] = $action->label(); + } + } + return $options; + } + + public function getMediaList($media_type, $term_id): array { + $mids = \Drupal::entityQuery('media') + ->condition('bundle', $media_type) + ->condition('field_media_use.target_id', $term_id) + ->accessCheck(TRUE) + ->execute(); + return $mids; + } + + public function getMediaOptions() { + $media_options = []; + foreach (MediaType::loadMultiple() as $machine_name => $media_type) { + $media_options[$machine_name] = $media_type->label(); + } + return $media_options; + } + } diff --git a/src/Plugin/Action/GenerateImageMediaDimensions.php b/src/Plugin/Action/GenerateImageMediaDimensions.php new file mode 100644 index 0000000..4ca93c5 --- /dev/null +++ b/src/Plugin/Action/GenerateImageMediaDimensions.php @@ -0,0 +1,85 @@ +get('entity_type.manager'), + $container->get('file_system') + ); + } + + /** + * Access check. + */ + public function access($entity, AccountInterface $account = NULL, $return_as_object = FALSE): AccessResultInterface|bool { + $access = $entity->access('update', $account, TRUE) + ->andIf($entity->get('field_height')->access('edit', $account, TRUE)); + return $return_as_object ? $access : $access->isAllowed(); + } + + /** + * Action execution. + */ + public function execute(ContentEntityInterface $entity = NULL): void { + $height = $entity->get('field_height')->getValue()[0]['value']; + $width = $entity->get('field_width')->getValue()[0]['value']; + $field = $entity->hasField('field_media_file') ? 'field_media_file' : 'field_media_image'; + if (!$width || !$height) { + if ($entity->hasField($field) && !$entity->get($field)->isEmpty()) { + $file = $entity->get($field)->entity; + $realpath = $this->fileSystem->realpath($file->getFileUri()); + $dimensions = getimagesize($realpath); + if ($dimensions) { + $entity->set('field_height', $dimensions[1]); + $entity->set('field_width', $dimensions[0]); + $entity->save(); + } + } + } + } +}