From 562dd8c8d73cf2588c601b5d2b85d330c1852974 Mon Sep 17 00:00:00 2001 From: Paul Pound Date: Tue, 20 Oct 2026 15:03:46 -0300 Subject: [PATCH] Export a node's media and files as part of its OCFL object Add an OcflExporter service that builds the complete object for a node: node.json plus the binaries of - media pointing at the node through the "media of" field (Islandora: field_media_of), stored under media/{media uuid}/, - media referenced from the node's own media reference fields, and - files in the node's own file/image fields, under files/{field}/. node.json gains a media list (each media's metadata, how it is associated with the node and the logical paths of its files) and a files list. Standalone media objects include their own files too. Automatic exports are queued and run once per node when the request ends (needs_destruction), so saving a node together with several media creates a single version. Media inserts, updates and deletes queue the media's parent nodes, before and after the change, so derivatives and removed media are reflected in the node's object. A successful export invalidates the entity's cache tags so the export status shown on its page is current. The controller, bulk actions and hooks now go through the exporter. New settings, with an update hook to add them to existing sites: include_files, media_of_field, member_of_field and model_field (the latter two are used by the node serializer for membership data). Co-Authored-By: Claude Opus 5.5 --- config/install/ocfl_export.settings.yml | 4 + config/schema/ocfl_export.schema.yml | 12 + ocfl_export.install | 27 ++ ocfl_export.module | 82 ++--- ocfl_export.services.yml | 13 + src/Controller/OcflExportController.php | 15 +- src/Form/OcflSettingsForm.php | 39 +++ src/OcflExporter.php | 390 ++++++++++++++++++++++++ src/Plugin/Action/ExportMediaToOcfl.php | 12 +- src/Plugin/Action/ExportNodeToOcfl.php | 12 +- 10 files changed, 520 insertions(+), 86 deletions(-) create mode 100644 ocfl_export.install create mode 100644 src/OcflExporter.php diff --git a/config/install/ocfl_export.settings.yml b/config/install/ocfl_export.settings.yml index cc04d27..4436b48 100644 --- a/config/install/ocfl_export.settings.yml +++ b/config/install/ocfl_export.settings.yml @@ -4,3 +4,7 @@ content_types: [] auto_export_media_enabled: true media_types: [] ocfl_version: '1.1' +include_files: true +member_of_field: field_member_of +media_of_field: field_media_of +model_field: field_model diff --git a/config/schema/ocfl_export.schema.yml b/config/schema/ocfl_export.schema.yml index 363a125..16d0b4c 100644 --- a/config/schema/ocfl_export.schema.yml +++ b/config/schema/ocfl_export.schema.yml @@ -26,3 +26,15 @@ ocfl_export.settings: ocfl_version: type: string label: 'OCFL version' + include_files: + type: boolean + label: 'Copy media and file binaries into OCFL objects' + member_of_field: + type: string + label: 'Node field holding parent membership (isMemberOf)' + media_of_field: + type: string + label: 'Media field pointing at the owning node' + model_field: + type: string + label: 'Node field holding the model taxonomy term' diff --git a/ocfl_export.install b/ocfl_export.install new file mode 100644 index 0000000..951e7d7 --- /dev/null +++ b/ocfl_export.install @@ -0,0 +1,27 @@ +getEditable('ocfl_export.settings'); + $defaults = [ + 'include_files' => TRUE, + 'member_of_field' => 'field_member_of', + 'media_of_field' => 'field_media_of', + 'model_field' => 'field_model', + ]; + foreach ($defaults as $key => $value) { + if ($config->get($key) === NULL) { + $config->set($key, $value); + } + } + $config->save(); +} diff --git a/ocfl_export.module b/ocfl_export.module index 94ae6d7..0363c2a 100644 --- a/ocfl_export.module +++ b/ocfl_export.module @@ -12,33 +12,30 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Url; use Drupal\media\MediaInterface; use Drupal\node\NodeInterface; +use Drupal\ocfl_export\OcflExporter; /** * Implements hook_node_insert(). */ function ocfl_export_node_insert(NodeInterface $node): void { - _ocfl_export_save_node($node); + _ocfl_export_exporter()->queueNode($node); } /** * Implements hook_node_update(). */ function ocfl_export_node_update(NodeInterface $node): void { - _ocfl_export_save_node($node); + _ocfl_export_exporter()->queueNode($node); } /** * Implements hook_node_delete(). */ function ocfl_export_node_delete(NodeInterface $node): void { - $config = \Drupal::config('ocfl_export.settings'); + $exporter = _ocfl_export_exporter(); + $exporter->unqueueNode($node); - if (!$config->get('auto_export_enabled')) { - return; - } - - $content_types = $config->get('content_types') ?: []; - if (!empty($content_types) && !in_array($node->bundle(), $content_types, TRUE)) { + if (!$exporter->shouldAutoExportNode($node)) { return; } @@ -55,38 +52,6 @@ function ocfl_export_node_delete(NodeInterface $node): void { } } -/** - * Helper function to save node to OCFL storage. - */ -function _ocfl_export_save_node(NodeInterface $node): void { - $config = \Drupal::config('ocfl_export.settings'); - - if (!$config->get('auto_export_enabled')) { - return; - } - - $content_types = $config->get('content_types') ?: []; - if (!empty($content_types) && !in_array($node->bundle(), $content_types, TRUE)) { - return; - } - - try { - /** @var \Drupal\ocfl_export\NodeSerializerService $serializer */ - $serializer = \Drupal::service('ocfl_export.node_serializer'); - /** @var \Drupal\ocfl_export\OcflStorageService $storage */ - $storage = \Drupal::service('ocfl_export.storage'); - - $data = $serializer->serialize($node); - $storage->saveObject($node->uuid(), $data); - } - catch (\Exception $e) { - \Drupal::logger('ocfl_export')->error('Failed to export node @nid to OCFL: @message', [ - '@nid' => $node->id(), - '@message' => $e->getMessage(), - ]); - } -} - /** * Implements hook_media_insert(). */ @@ -105,14 +70,12 @@ function ocfl_export_media_update(MediaInterface $media): void { * Implements hook_media_delete(). */ function ocfl_export_media_delete(MediaInterface $media): void { - $config = \Drupal::config('ocfl_export.settings'); + $exporter = _ocfl_export_exporter(); - if (!$config->get('auto_export_media_enabled')) { - return; - } + // The owning node's object should no longer include this media. + $exporter->queueParentNodes($media); - $media_types = $config->get('media_types') ?: []; - if (!empty($media_types) && !in_array($media->bundle(), $media_types, TRUE)) { + if (!$exporter->shouldAutoExportMedia($media)) { return; } @@ -133,25 +96,17 @@ function ocfl_export_media_delete(MediaInterface $media): void { * Helper function to save media to OCFL storage. */ function _ocfl_export_save_media(MediaInterface $media): void { - $config = \Drupal::config('ocfl_export.settings'); + $exporter = _ocfl_export_exporter(); - if (!$config->get('auto_export_media_enabled')) { - return; - } + // Islandora media (and derivatives) belong to a node's object. + $exporter->queueParentNodes($media); - $media_types = $config->get('media_types') ?: []; - if (!empty($media_types) && !in_array($media->bundle(), $media_types, TRUE)) { + if (!$exporter->shouldAutoExportMedia($media)) { return; } try { - /** @var \Drupal\ocfl_export\MediaSerializerService $serializer */ - $serializer = \Drupal::service('ocfl_export.media_serializer'); - /** @var \Drupal\ocfl_export\OcflStorageService $storage */ - $storage = \Drupal::service('ocfl_export.storage'); - - $data = $serializer->serialize($media); - $storage->saveObject($media->uuid(), $data); + $exporter->exportMedia($media); } catch (\Exception $e) { \Drupal::logger('ocfl_export')->error('Failed to export media @mid to OCFL: @message', [ @@ -161,6 +116,13 @@ function _ocfl_export_save_media(MediaInterface $media): void { } } +/** + * Get the OCFL exporter service. + */ +function _ocfl_export_exporter(): OcflExporter { + return \Drupal::service('ocfl_export.exporter'); +} + /** * Implements hook_node_view(). */ diff --git a/ocfl_export.services.yml b/ocfl_export.services.yml index 336b315..c7ed67b 100644 --- a/ocfl_export.services.yml +++ b/ocfl_export.services.yml @@ -19,3 +19,16 @@ services: arguments: - '@entity_type.manager' - '@datetime.time' + + ocfl_export.exporter: + class: Drupal\ocfl_export\OcflExporter + arguments: + - '@entity_type.manager' + - '@entity_field.manager' + - '@config.factory' + - '@ocfl_export.storage' + - '@ocfl_export.node_serializer' + - '@ocfl_export.media_serializer' + - '@logger.factory' + tags: + - { name: needs_destruction } diff --git a/src/Controller/OcflExportController.php b/src/Controller/OcflExportController.php index bc7046a..9e673a0 100644 --- a/src/Controller/OcflExportController.php +++ b/src/Controller/OcflExportController.php @@ -9,8 +9,7 @@ use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Url; use Drupal\media\MediaInterface; use Drupal\node\NodeInterface; -use Drupal\ocfl_export\MediaSerializerService; -use Drupal\ocfl_export\NodeSerializerService; +use Drupal\ocfl_export\OcflExporter; use Drupal\ocfl_export\OcflStorageService; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -24,8 +23,7 @@ class OcflExportController extends ControllerBase { public function __construct( protected OcflStorageService $storageService, - protected NodeSerializerService $nodeSerializerService, - protected MediaSerializerService $mediaSerializerService, + protected OcflExporter $exporter, MessengerInterface $messenger, ) { $this->messenger = $messenger; @@ -37,8 +35,7 @@ class OcflExportController extends ControllerBase { public static function create(ContainerInterface $container): static { return new static( $container->get('ocfl_export.storage'), - $container->get('ocfl_export.node_serializer'), - $container->get('ocfl_export.media_serializer'), + $container->get('ocfl_export.exporter'), $container->get('messenger'), ); } @@ -54,8 +51,7 @@ class OcflExportController extends ControllerBase { */ public function exportNode(NodeInterface $node): RedirectResponse { try { - $data = $this->nodeSerializerService->serialize($node); - $success = $this->storageService->saveObject($node->uuid(), $data); + $success = $this->exporter->exportNode($node, 'Manual export'); if ($success) { $this->messenger->addStatus($this->t('Node "@title" has been exported to OCFL storage.', [ @@ -100,8 +96,7 @@ class OcflExportController extends ControllerBase { */ public function exportMedia(MediaInterface $media): RedirectResponse { try { - $data = $this->mediaSerializerService->serialize($media); - $success = $this->storageService->saveObject($media->uuid(), $data); + $success = $this->exporter->exportMedia($media, 'Manual export'); if ($success) { $this->messenger->addStatus($this->t('Media "@name" has been exported to OCFL storage.', [ diff --git a/src/Form/OcflSettingsForm.php b/src/Form/OcflSettingsForm.php index 7a0656a..9cdc597 100644 --- a/src/Form/OcflSettingsForm.php +++ b/src/Form/OcflSettingsForm.php @@ -155,6 +155,41 @@ class OcflSettingsForm extends ConfigFormBase { '#default_value' => $config->get('media_types') ?: [], ]; + // Files and relationships. + $form['content'] = [ + '#type' => 'details', + '#title' => $this->t('Files and Relationships'), + '#open' => TRUE, + ]; + + $form['content']['include_files'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Copy files into OCFL objects'), + '#description' => $this->t('Copy the binaries of associated media and of file/image fields into each object. Unchanged files are not copied again in later versions. When unchecked, only metadata is exported.'), + '#default_value' => $config->get('include_files') ?? TRUE, + ]; + + $form['content']['media_of_field'] = [ + '#type' => 'textfield', + '#title' => $this->t('"Media of" field'), + '#description' => $this->t('Media field that references the node the media belongs to (Islandora: field_media_of). Media found through it are included in the OCFL object of that node. Leave empty to disable.'), + '#default_value' => $config->get('media_of_field'), + ]; + + $form['content']['member_of_field'] = [ + '#type' => 'textfield', + '#title' => $this->t('"Member of" field'), + '#description' => $this->t('Node field that references parent nodes (Islandora: field_member_of). Used to record direct parents and the full collection hierarchy. Leave empty to disable.'), + '#default_value' => $config->get('member_of_field'), + ]; + + $form['content']['model_field'] = [ + '#type' => 'textfield', + '#title' => $this->t('Model field'), + '#description' => $this->t('Node field that references the model term (Islandora: field_model). Leave empty to disable.'), + '#default_value' => $config->get('model_field'), + ]; + // Initialize button. $form['actions']['initialize'] = [ '#type' => 'submit', @@ -196,6 +231,10 @@ class OcflSettingsForm extends ConfigFormBase { ->set('auto_export_media_enabled', (bool) $form_state->getValue('auto_export_media_enabled')) ->set('media_types', array_values($mediaTypes)) ->set('ocfl_version', $form_state->getValue('ocfl_version')) + ->set('include_files', (bool) $form_state->getValue('include_files')) + ->set('media_of_field', trim((string) $form_state->getValue('media_of_field'))) + ->set('member_of_field', trim((string) $form_state->getValue('member_of_field'))) + ->set('model_field', trim((string) $form_state->getValue('model_field'))) ->save(); parent::submitForm($form, $form_state); diff --git a/src/OcflExporter.php b/src/OcflExporter.php new file mode 100644 index 0000000..a9148da --- /dev/null +++ b/src/OcflExporter.php @@ -0,0 +1,390 @@ +logger = $loggerFactory->get('ocfl_export'); + } + + /** + * Export a node, its associated media and its files as one OCFL object. + * + * @param \Drupal\node\NodeInterface $node + * The node. + * @param string|null $message + * Optional OCFL version message. + * + * @return bool + * TRUE on success. + */ + public function exportNode(NodeInterface $node, ?string $message = NULL): bool { + $data = $this->nodeSerializer->serialize($node); + $files = []; + + $data['files'] = $this->collectEntityFiles($node, 'files', TRUE, $files); + + $data['media'] = []; + foreach ($this->getAssociatedMedia($node) as $item) { + /** @var \Drupal\media\MediaInterface $media */ + $media = $item['media']; + $mediaData = $this->mediaSerializer->serialize($media); + unset($mediaData['_metadata']); + $mediaData['association'] = $item['association']; + $mediaData['files'] = $this->collectEntityFiles($media, 'media/' . $media->uuid(), FALSE, $files, ['thumbnail']); + $data['media'][] = $mediaData; + } + + if (!$this->includeFiles()) { + $files = []; + } + + return $this->save($node, $data, $files, $message); + } + + /** + * Export a media entity and its files as its own OCFL object. + * + * @param \Drupal\media\MediaInterface $media + * The media entity. + * @param string|null $message + * Optional OCFL version message. + * + * @return bool + * TRUE on success. + */ + public function exportMedia(MediaInterface $media, ?string $message = NULL): bool { + $data = $this->mediaSerializer->serialize($media); + $files = []; + $data['files'] = $this->collectEntityFiles($media, 'files', TRUE, $files, ['thumbnail']); + + if (!$this->includeFiles()) { + $files = []; + } + + return $this->save($media, $data, $files, $message); + } + + /** + * Save an entity's OCFL object and refresh its rendered export status. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The exported entity. + * @param array $data + * The metadata. + * @param array $files + * Map of logical path => file URI. + * @param string|null $message + * Optional OCFL version message. + * + * @return bool + * TRUE on success. + */ + protected function save(EntityInterface $entity, array $data, array $files, ?string $message): bool { + $saved = $this->storage->saveObject($entity->uuid(), $data, $files, $message); + if ($saved) { + // The export status block is part of the cached entity render. + Cache::invalidateTags($entity->getCacheTagsToInvalidate()); + } + return $saved; + } + + /** + * Queue a node for automatic export if the settings allow it. + * + * @param \Drupal\node\NodeInterface $node + * The node. + */ + public function queueNode(NodeInterface $node): void { + if ($this->shouldAutoExportNode($node)) { + $this->queue[(int) $node->id()] = (int) $node->id(); + } + } + + /** + * Queue the nodes a media entity belongs to (before and after the change). + * + * @param \Drupal\media\MediaInterface $media + * The media entity being inserted, updated or deleted. + */ + public function queueParentNodes(MediaInterface $media): void { + $field = $this->configFactory->get('ocfl_export.settings')->get('media_of_field'); + if (!$field || !$media->hasField($field)) { + return; + } + + $parents = $media->get($field)->referencedEntities(); + $original = method_exists($media, 'getOriginal') ? $media->getOriginal() : ($media->original ?? NULL); + if ($original instanceof MediaInterface && $original->hasField($field)) { + $parents = array_merge($parents, $original->get($field)->referencedEntities()); + } + + foreach ($parents as $parent) { + if ($parent instanceof NodeInterface) { + $this->queueNode($parent); + } + } + } + + /** + * Remove a node from the export queue (e.g. because it was deleted). + * + * @param \Drupal\node\NodeInterface $node + * The node. + */ + public function unqueueNode(NodeInterface $node): void { + unset($this->queue[(int) $node->id()]); + } + + /** + * {@inheritdoc} + */ + public function destruct(): void { + if (!$this->queue) { + return; + } + + $nids = $this->queue; + $this->queue = []; + $nodes = $this->entityTypeManager->getStorage('node')->loadMultiple($nids); + + foreach ($nodes as $node) { + try { + if (!$this->exportNode($node)) { + $this->logger->error('Failed to export node @nid to OCFL.', ['@nid' => $node->id()]); + } + } + catch (\Exception $e) { + $this->logger->error('Failed to export node @nid to OCFL: @message', [ + '@nid' => $node->id(), + '@message' => $e->getMessage(), + ]); + } + } + } + + /** + * Whether a node should be exported automatically. + * + * @param \Drupal\node\NodeInterface $node + * The node. + * + * @return bool + * TRUE if automatic export is enabled for the node's bundle. + */ + public function shouldAutoExportNode(NodeInterface $node): bool { + $config = $this->configFactory->get('ocfl_export.settings'); + if (!$config->get('auto_export_enabled')) { + return FALSE; + } + $types = $config->get('content_types') ?: []; + return empty($types) || in_array($node->bundle(), $types, TRUE); + } + + /** + * Whether a media entity should be exported automatically. + * + * @param \Drupal\media\MediaInterface $media + * The media entity. + * + * @return bool + * TRUE if automatic export is enabled for the media's bundle. + */ + public function shouldAutoExportMedia(MediaInterface $media): bool { + $config = $this->configFactory->get('ocfl_export.settings'); + if (!$config->get('auto_export_media_enabled')) { + return FALSE; + } + $types = $config->get('media_types') ?: []; + return empty($types) || in_array($media->bundle(), $types, TRUE); + } + + /** + * Find the media associated with a node. + * + * @param \Drupal\node\NodeInterface $node + * The node. + * + * @return array + * Keyed by media ID; each value has 'media' (the entity) and + * 'association' (a list such as ['media_of', 'field:field_image']). + */ + protected function getAssociatedMedia(NodeInterface $node): array { + $associated = []; + $mediaStorage = $this->entityTypeManager->getStorage('media'); + + // Islandora: media point at the node they belong to. + $mediaOfField = $this->configFactory->get('ocfl_export.settings')->get('media_of_field'); + $mediaFieldStorage = $this->entityFieldManager->getFieldStorageDefinitions('media'); + if ($mediaOfField && isset($mediaFieldStorage[$mediaOfField])) { + $mids = $mediaStorage->getQuery() + ->accessCheck(FALSE) + ->condition($mediaOfField, $node->id()) + ->sort('mid') + ->execute(); + foreach ($mediaStorage->loadMultiple($mids) as $mid => $media) { + $associated[$mid] = ['media' => $media, 'association' => ['media_of']]; + } + } + + // Plain Drupal: the node references media from its own fields. + foreach ($node->getFieldDefinitions() as $fieldName => $definition) { + if ($definition->getType() !== 'entity_reference' || $definition->getSetting('target_type') !== 'media') { + continue; + } + foreach ($node->get($fieldName)->referencedEntities() as $media) { + $mid = (int) $media->id(); + $associated[$mid]['media'] = $media; + $associated[$mid]['association'][] = 'field:' . $fieldName; + } + } + + ksort($associated); + return $associated; + } + + /** + * Collect the files held in an entity's file and image fields. + * + * @param \Drupal\Core\Entity\FieldableEntityInterface $entity + * The entity. + * @param string $prefix + * Logical directory the files are placed under. + * @param bool $groupByField + * Whether to add a sub-directory per field. + * @param array $files + * Map of logical path => file URI, added to in place. + * @param string[] $skipFields + * Field names to ignore. + * + * @return array + * Descriptions of the files for inclusion in node.json. + */ + protected function collectEntityFiles(FieldableEntityInterface $entity, string $prefix, bool $groupByField, array &$files, array $skipFields = []): array { + $descriptions = []; + + foreach ($entity->getFieldDefinitions() as $fieldName => $definition) { + if (!in_array($definition->getType(), static::FILE_FIELD_TYPES, TRUE) || in_array($fieldName, $skipFields, TRUE)) { + continue; + } + + foreach ($entity->get($fieldName) as $delta => $item) { + $file = $item->entity; + if (!$file instanceof FileInterface) { + continue; + } + + $dir = $groupByField ? $prefix . '/' . $fieldName : $prefix; + $logicalPath = $this->uniqueLogicalPath($dir, $file, $files); + $files[$logicalPath] = $file->getFileUri(); + + $descriptions[] = [ + 'logical_path' => $this->includeFiles() ? $logicalPath : NULL, + 'field' => $fieldName, + 'delta' => $delta, + 'fid' => (int) $file->id(), + 'uuid' => $file->uuid(), + 'filename' => $file->getFilename(), + 'filemime' => $file->getMimeType(), + 'filesize' => (int) $file->getSize(), + 'uri' => $file->getFileUri(), + ]; + } + } + + return $descriptions; + } + + /** + * Build a safe logical path that doesn't collide with one already used. + * + * @param string $dir + * The logical directory. + * @param \Drupal\file\FileInterface $file + * The file. + * @param array $files + * Logical paths already in use, as keys. + * + * @return string + * The logical path. + */ + protected function uniqueLogicalPath(string $dir, FileInterface $file, array $files): string { + $name = preg_replace('/[\x00-\x1F\x7F\/\\\\]/u', '_', trim((string) $file->getFilename())); + if ($name === '' || $name === '.' || $name === '..') { + $name = 'file-' . $file->id(); + } + + $path = $dir . '/' . $name; + if (!isset($files[$path])) { + return $path; + } + + $info = pathinfo($name); + $extension = isset($info['extension']) ? '.' . $info['extension'] : ''; + return $dir . '/' . $info['filename'] . '-' . $file->id() . $extension; + } + + /** + * Whether binary files should be copied into objects. + */ + protected function includeFiles(): bool { + return (bool) ($this->configFactory->get('ocfl_export.settings')->get('include_files') ?? TRUE); + } + +} diff --git a/src/Plugin/Action/ExportMediaToOcfl.php b/src/Plugin/Action/ExportMediaToOcfl.php index 08f0d86..a12708d 100644 --- a/src/Plugin/Action/ExportMediaToOcfl.php +++ b/src/Plugin/Action/ExportMediaToOcfl.php @@ -8,8 +8,7 @@ use Drupal\Core\Action\ActionBase; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; use Drupal\media\MediaInterface; -use Drupal\ocfl_export\MediaSerializerService; -use Drupal\ocfl_export\OcflStorageService; +use Drupal\ocfl_export\OcflExporter; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -29,8 +28,7 @@ class ExportMediaToOcfl extends ActionBase implements ContainerFactoryPluginInte array $configuration, $plugin_id, $plugin_definition, - protected OcflStorageService $storageService, - protected MediaSerializerService $serializerService, + protected OcflExporter $exporter, protected LoggerInterface $logger, ) { parent::__construct($configuration, $plugin_id, $plugin_definition); @@ -44,8 +42,7 @@ class ExportMediaToOcfl extends ActionBase implements ContainerFactoryPluginInte $configuration, $plugin_id, $plugin_definition, - $container->get('ocfl_export.storage'), - $container->get('ocfl_export.media_serializer'), + $container->get('ocfl_export.exporter'), $container->get('logger.factory')->get('ocfl_export'), ); } @@ -59,8 +56,7 @@ class ExportMediaToOcfl extends ActionBase implements ContainerFactoryPluginInte } try { - $data = $this->serializerService->serialize($media); - $success = $this->storageService->saveObject($media->uuid(), $data); + $success = $this->exporter->exportMedia($media, 'Bulk action export'); if ($success) { $this->logger->info('Batch exported media @mid (@name) to OCFL.', [ diff --git a/src/Plugin/Action/ExportNodeToOcfl.php b/src/Plugin/Action/ExportNodeToOcfl.php index aeb9ae5..6161e3b 100644 --- a/src/Plugin/Action/ExportNodeToOcfl.php +++ b/src/Plugin/Action/ExportNodeToOcfl.php @@ -8,8 +8,7 @@ use Drupal\Core\Action\ActionBase; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; use Drupal\node\NodeInterface; -use Drupal\ocfl_export\NodeSerializerService; -use Drupal\ocfl_export\OcflStorageService; +use Drupal\ocfl_export\OcflExporter; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -29,8 +28,7 @@ class ExportNodeToOcfl extends ActionBase implements ContainerFactoryPluginInter array $configuration, $plugin_id, $plugin_definition, - protected OcflStorageService $storageService, - protected NodeSerializerService $serializerService, + protected OcflExporter $exporter, protected LoggerInterface $logger, ) { parent::__construct($configuration, $plugin_id, $plugin_definition); @@ -44,8 +42,7 @@ class ExportNodeToOcfl extends ActionBase implements ContainerFactoryPluginInter $configuration, $plugin_id, $plugin_definition, - $container->get('ocfl_export.storage'), - $container->get('ocfl_export.node_serializer'), + $container->get('ocfl_export.exporter'), $container->get('logger.factory')->get('ocfl_export'), ); } @@ -59,8 +56,7 @@ class ExportNodeToOcfl extends ActionBase implements ContainerFactoryPluginInter } try { - $data = $this->serializerService->serialize($node); - $success = $this->storageService->saveObject($node->uuid(), $data); + $success = $this->exporter->exportNode($node, 'Bulk action export'); if ($success) { $this->logger->info('Batch exported node @nid (@title) to OCFL.', [