Browse Source

Added bulk media updates

main
astanley 7 months ago
parent
commit
9db057b644
  1. 10
      islandora_batch_action.links.menu.yml
  2. 8
      islandora_batch_action.routing.yml
  3. 6
      islandora_batch_action.services.yml
  4. 80
      src/Batch/ActionBatch.php
  5. 76
      src/Batch/NodeActionBatch.php
  6. 100
      src/Form/MediaActionsForm.php
  7. 56
      src/Form/MissingDerivativesFm.php
  8. 80
      src/IslandoraBatchActionUtils.php
  9. 85
      src/Plugin/Action/GenerateImageMediaDimensions.php

10
islandora_batch_action.links.menu.yml

@ -4,4 +4,12 @@ islandora_batch_action_create_media:
route_name: islandora_batch_action.missing_derivatives_fm route_name: islandora_batch_action.missing_derivatives_fm
parent: system.admin_config_media parent: system.admin_config_media
menu_name: administration menu_name: administration
weight: 10 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

8
islandora_batch_action.routing.yml

@ -5,3 +5,11 @@ islandora_batch_action.missing_derivatives_fm:
_form: 'Drupal\islandora_batch_action\Form\MissingDerivativesFm' _form: 'Drupal\islandora_batch_action\Form\MissingDerivativesFm'
requirements: requirements:
_permission: 'administer media' _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'

6
islandora_batch_action.services.yml

@ -9,6 +9,6 @@ services:
class: Drupal\islandora_batch_action\IslandoraBatchActionUtils class: Drupal\islandora_batch_action\IslandoraBatchActionUtils
arguments: ['@database', '@entity_type.manager', '@logger.channel.islandora', '@islandora.utils'] arguments: ['@database', '@entity_type.manager', '@logger.channel.islandora', '@islandora.utils']
islandora_batch_action.node_action_batch: islandora_batch_action.action_batch:
class: 'Drupal\islandora_batch_action\Batch\NodeActionBatch' class: 'Drupal\islandora_batch_action\Batch\ActionBatch'
arguments: ['@entity_type.manager', '@plugin.manager.action'] arguments: ['@entity_type.manager', '@plugin.manager.action']

80
src/Batch/ActionBatch.php

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace Drupal\islandora_batch_action\Batch;
use Drupal\Core\Batch\BatchBuilder;
/**
* Provides a batch job to perform a Drupal Action on a list of node IDs.
*/
final class ActionBatch {
/**
* Executes the batch job.
*
* @param array $entity_ids
* An array of entity IDs.
* @param string $action_id
* The action plugin ID to perform on each node.
* @param string $entity_type
* Must be either node or media
*/
public function run(array $entity_ids, string $action_id, string $entity_type): void {
$batch_builder = new BatchBuilder();
$batch_builder->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;
}
}
}

76
src/Batch/NodeActionBatch.php

@ -1,76 +0,0 @@
<?php
declare(strict_types=1);
namespace Drupal\islandora_batch_action\Batch;
use Drupal\Core\Batch\BatchBuilder;
/**
* Provides a batch job to perform a Drupal Action on a list of node IDs.
*/
final class NodeActionBatch {
/**
* Executes the batch job.
*
* @param array $node_ids
* An array of node IDs.
* @param string $action_id
* The action plugin ID to perform on each node.
*/
public function run(array $node_ids, string $action_id): void {
$batch_builder = new BatchBuilder();
$batch_builder->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;
}
}
}

100
src/Form/MediaActionsForm.php

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace Drupal\islandora_batch_action\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\islandora_batch_action\IslandoraBatchActionUtils;
use Drupal\media\Entity\MediaType;
use Drupal\islandora_batch_action\Batch\ActionBatch;
/**
* Provides a Islandora Batch Action form.
*/
final class MediaActionsForm extends FormBase {
/**
* The Batch Action Utils service.
*
* @var \Drupal\islandora_batch_action\IslandoraBatchActionUtils
*/
protected IslandoraBatchActionUtils $islandoraBatchActionUtils;
/**
* The batch job service.
*
* @var \Drupal\islandora_batch_action\Batch\ActionBatch
*/
protected ActionBatch $batchJob;
public function __construct(IslandoraBatchActionUtils $islandoraBatchActionUtils, ActionBatch $batchJob,) {
$this->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');
}
}

56
src/Form/MissingDerivativesFm.php

@ -9,7 +9,7 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\islandora_batch_action\IslandoraBatchActionUtils; 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; use Drupal\Core\Action\ActionManager;
/** /**
@ -34,9 +34,9 @@ final class MissingDerivativesFm extends FormBase {
/** /**
* The batch job service. * 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. * The plugin manager for actions.
@ -52,16 +52,17 @@ final class MissingDerivativesFm extends FormBase {
* The entity type manager service. * The entity type manager service.
* @param \Drupal\islandora_batch_action\IslandoraBatchActionUtils $islandoraBatchActionUtils * @param \Drupal\islandora_batch_action\IslandoraBatchActionUtils $islandoraBatchActionUtils
* The Batch Action utils. * The Batch Action utils.
* @param \Drupal\islandora_batch_action\Batch\NodeActionBatch $batchJob * @param \Drupal\islandora_batch_action\Batch\ActionBatch $batchJob
* The batch job service. * The batch job service.
* @param \Drupal\Core\Action\ActionManager $actionPluginManager * @param \Drupal\Core\Action\ActionManager $actionPluginManager
* The action plugin manager. * The action plugin manager.
*/ */
public function __construct( public function __construct(
EntityTypeManagerInterface $entityTypeManager, EntityTypeManagerInterface $entityTypeManager,
IslandoraBatchActionUtils $islandoraBatchActionUtils, IslandoraBatchActionUtils $islandoraBatchActionUtils,
NodeActionBatch $batchJob, ActionBatch $batchJob,
ActionManager $actionPluginManager) { ActionManager $actionPluginManager
) {
$this->entityTypeManager = $entityTypeManager; $this->entityTypeManager = $entityTypeManager;
$this->islandoraBatchActionUtils = $islandoraBatchActionUtils; $this->islandoraBatchActionUtils = $islandoraBatchActionUtils;
$this->batchJob = $batchJob; $this->batchJob = $batchJob;
@ -73,10 +74,10 @@ final class MissingDerivativesFm extends FormBase {
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
return new static( return new static(
$container->get('entity_type.manager'), $container->get('entity_type.manager'),
$container->get('islandora_batch_action.utils'), $container->get('islandora_batch_action.utils'),
$container->get('islandora_batch_action.node_action_batch'), $container->get('islandora_batch_action.action_batch'),
$container->get('plugin.manager.action'), $container->get('plugin.manager.action'),
); );
} }
@ -94,19 +95,19 @@ final class MissingDerivativesFm extends FormBase {
$form['model'] = [ $form['model'] = [
'#type' => 'select', '#type' => 'select',
'#title' => $this->t('Islandora Model'), '#title' => $this->t('Islandora Model'),
'#options' => $this->getVocabularyOptions('islandora_models'), '#options' => $this->islandoraBatchActionUtils->getVocabularyOptions('islandora_models'),
'#required' => TRUE, '#required' => TRUE,
]; ];
$form['usage'] = [ $form['usage'] = [
'#type' => 'select', '#type' => 'select',
'#title' => $this->t('Derivative Type'), '#title' => $this->t('Derivative Type'),
'#options' => $this->getVocabularyOptions('islandora_media_use'), '#options' => $this->islandoraBatchActionUtils->getVocabularyOptions('islandora_media_use'),
'#required' => TRUE, '#required' => TRUE,
]; ];
$form['actions_id'] = [ $form['actions_id'] = [
'#type' => 'select', '#type' => 'select',
'#title' => $this->t('Derivative Actions'), '#title' => $this->t('Derivative Actions'),
'#options' => $this->getDerivativeActions(), '#options' => $this->islandoraBatchActionUtils->getDerivativeActions(),
'#required' => TRUE, '#required' => TRUE,
]; ];
@ -130,10 +131,11 @@ final class MissingDerivativesFm extends FormBase {
$action_id = $form_state->getValue('actions_id'); $action_id = $form_state->getValue('actions_id');
$nids = $this->islandoraBatchActionUtils->generateNodeList($model, $usage); $nids = $this->islandoraBatchActionUtils->generateNodeList($model, $usage);
if (empty($nids)) { 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; 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; 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;
}
} }

80
src/IslandoraBatchActionUtils.php

@ -7,6 +7,7 @@ namespace Drupal\islandora_batch_action;
use Drupal\Core\Database\Connection; use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Logger\LoggerChannelInterface; use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\media\Entity\MediaType;
/** /**
* Provides utility functions for bulk derivative actions in Islandora. * Provides utility functions for bulk derivative actions in Islandora.
@ -56,4 +57,83 @@ final class IslandoraBatchActionUtils {
return $mainQuery->execute()->fetchCol(); 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;
}
} }

85
src/Plugin/Action/GenerateImageMediaDimensions.php

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace Drupal\islandora_batch_action\Plugin\Action;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Annotation\Action;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a Generate Image Media Dimensions action.
*
* @Action(
* id = "islandora_batch_action_generate_image_media_dimensions",
* label = @Translation("Generate Image Media Dimensions"),
* type = "media",
* category = @Translation("Custom"),
* )
*/
final class GenerateImageMediaDimensions extends ActionBase implements ContainerFactoryPluginInterface {
/**
* Constructor.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
private readonly EntityTypeManagerInterface $entityTypeManager,
private readonly FileSystemInterface $fileSystem
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* Dependency injection factory.
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
return new self(
$configuration,
$plugin_id,
$plugin_definition,
$container->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();
}
}
}
}
}
Loading…
Cancel
Save