Browse Source

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 <noreply@anthropic.com>
ocfl-files-and-hierarchy
Paul Pound 5 days ago
parent
commit
562dd8c8d7
  1. 4
      config/install/ocfl_export.settings.yml
  2. 12
      config/schema/ocfl_export.schema.yml
  3. 27
      ocfl_export.install
  4. 82
      ocfl_export.module
  5. 13
      ocfl_export.services.yml
  6. 15
      src/Controller/OcflExportController.php
  7. 39
      src/Form/OcflSettingsForm.php
  8. 390
      src/OcflExporter.php
  9. 12
      src/Plugin/Action/ExportMediaToOcfl.php
  10. 12
      src/Plugin/Action/ExportNodeToOcfl.php

4
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

12
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'

27
ocfl_export.install

@ -0,0 +1,27 @@
<?php
/**
* @file
* Install, update and uninstall functions for the OCFL Export module.
*/
declare(strict_types=1);
/**
* Add settings for file export and Islandora relationship fields.
*/
function ocfl_export_update_10001(): void {
$config = \Drupal::configFactory()->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();
}

82
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().
*/

13
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 }

15
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.', [

39
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);

390
src/OcflExporter.php

@ -0,0 +1,390 @@
<?php
declare(strict_types=1);
namespace Drupal\ocfl_export;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DestructableInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\file\FileInterface;
use Drupal\media\MediaInterface;
use Drupal\node\NodeInterface;
use Psr\Log\LoggerInterface;
/**
* Builds complete OCFL objects (metadata plus files) for nodes and media.
*
* A node object contains node.json plus the binaries of:
* - media that point at the node through the "media of" field (Islandora),
* stored under media/{media uuid}/;
* - media referenced from the node's own media reference fields, stored the
* same way;
* - files in the node's own file and image fields, stored under
* files/{field name}/.
*
* Automatic (hook driven) exports are queued and run once per node when the
* request ends, so saving a node together with several media creates a single
* new version rather than one per save.
*/
class OcflExporter implements DestructableInterface {
/**
* File field types whose files are copied into objects.
*/
protected const FILE_FIELD_TYPES = ['file', 'image'];
/**
* The logger channel.
*/
protected LoggerInterface $logger;
/**
* Node IDs queued for export at the end of the request.
*
* @var int[]
*/
protected array $queue = [];
public function __construct(
protected EntityTypeManagerInterface $entityTypeManager,
protected EntityFieldManagerInterface $entityFieldManager,
protected ConfigFactoryInterface $configFactory,
protected OcflStorageService $storage,
protected NodeSerializerService $nodeSerializer,
protected MediaSerializerService $mediaSerializer,
LoggerChannelFactoryInterface $loggerFactory,
) {
$this->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);
}
}

12
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.', [

12
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.', [

Loading…
Cancel
Save