commit
503dc04d75
5 changed files with 314 additions and 0 deletions
@ -0,0 +1,7 @@ |
|||||||
|
name: Islandora Collection Harvest |
||||||
|
type: module |
||||||
|
description: Provides a form to select an Islandora collection and media use, build a ZIP, and download it via streams. |
||||||
|
core_version_requirement: ^10 |
||||||
|
package: Islandora |
||||||
|
dependencies: |
||||||
|
- drupal:islandora |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
access collection harvest: |
||||||
|
title: 'Access Collection Harvest form' |
||||||
|
description: 'Allows a user to access the collection harvest form to download media as ZIP' |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
islandora_collection_harvest.harvest_form: |
||||||
|
path: '/admin/islandora/collection-harvest' |
||||||
|
defaults: |
||||||
|
_form: '\Drupal\islandora_collection_harvest\Form\CollectionHarvestForm' |
||||||
|
_title: 'Collection Harvest' |
||||||
|
requirements: |
||||||
|
_permission: 'access collection harvest' |
||||||
|
|
||||||
|
islandora_collection_harvest.download_zip: |
||||||
|
path: '/islandora/collection-harvest/download/{filename}' |
||||||
|
defaults: |
||||||
|
_controller: '\Drupal\islandora_collection_harvest\Controller\HarvestDownloadController::download' |
||||||
|
requirements: |
||||||
|
_permission: 'access collection harvest' |
||||||
@ -0,0 +1,117 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Drupal\islandora_collection_harvest\Controller; |
||||||
|
|
||||||
|
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface; |
||||||
|
use Drupal\flysystem\FlysystemFactory; |
||||||
|
use Drupal\Core\File\FileSystemInterface; |
||||||
|
use Drupal\Core\TempStore\PrivateTempStoreFactory; |
||||||
|
use Symfony\Component\HttpFoundation\StreamedResponse; |
||||||
|
use Drupal\Core\Controller\ControllerBase; |
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
||||||
|
|
||||||
|
final class HarvestDownloadController extends ControllerBase { |
||||||
|
|
||||||
|
protected FileSystemInterface $fileSystem; |
||||||
|
|
||||||
|
protected PrivateTempStoreFactory $tempStoreFactory; |
||||||
|
|
||||||
|
protected FlysystemFactory $flysystemFactory; |
||||||
|
|
||||||
|
protected StreamWrapperManagerInterface $streamWrapperManager; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
FileSystemInterface $file_system, |
||||||
|
PrivateTempStoreFactory $tempStoreFactory, |
||||||
|
FlysystemFactory $flysystemFactory, |
||||||
|
StreamWrapperManagerInterface $streamWrapperManager |
||||||
|
) { |
||||||
|
$this->fileSystem = $file_system; |
||||||
|
$this->tempStoreFactory = $tempStoreFactory; |
||||||
|
$this->flysystemFactory = $flysystemFactory; |
||||||
|
$this->streamWrapperManager = $streamWrapperManager; |
||||||
|
} |
||||||
|
|
||||||
|
public static function create(ContainerInterface $container): self { |
||||||
|
return new self( |
||||||
|
$container->get('file_system'), |
||||||
|
$container->get('tempstore.private'), |
||||||
|
$container->get('flysystem_factory'), |
||||||
|
$container->get('stream_wrapper_manager') |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builds and downloads archive. |
||||||
|
* |
||||||
|
* @param string $filename |
||||||
|
* |
||||||
|
* @return \Symfony\Component\HttpFoundation\StreamedResponse |
||||||
|
*/ |
||||||
|
public function download(string $filename): StreamedResponse { |
||||||
|
$temp_store = $this->tempStoreFactory->get('islandora_collection_harvest'); |
||||||
|
$media_entities = $temp_store->get($filename); |
||||||
|
if (empty($media_entities)) { |
||||||
|
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException('No media found.'); |
||||||
|
} |
||||||
|
|
||||||
|
return new StreamedResponse(function() use ($media_entities) { |
||||||
|
$zip = new \ZipArchive(); |
||||||
|
$tmpfile = tempnam(sys_get_temp_dir(), 'collection_zip_'); |
||||||
|
$zip->open($tmpfile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); |
||||||
|
|
||||||
|
foreach ($media_entities as $media) { |
||||||
|
$source_field = $media->getSource() |
||||||
|
->getConfiguration()['source_field'] ?? NULL; |
||||||
|
if (!$source_field || !$media->hasField($source_field)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
$file = $media->get($source_field)->entity; |
||||||
|
if (!$file) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
$uri = $file->getFileUri(); |
||||||
|
$filename_in_zip = basename($uri); |
||||||
|
$real_path = $this->fileSystem->realpath($uri); |
||||||
|
|
||||||
|
if ($real_path && file_exists($real_path)) { |
||||||
|
$zip->addFile($real_path, $filename_in_zip); |
||||||
|
} |
||||||
|
else { |
||||||
|
// Flysystem v1 fallback |
||||||
|
try { |
||||||
|
$scheme = $this->streamWrapperManager->getScheme($uri); |
||||||
|
$path = substr($uri, strlen($scheme) + 3); |
||||||
|
$filesystem = $this->flysystemFactory->getFilesystem($scheme); |
||||||
|
|
||||||
|
if ($filesystem && $filesystem->has($path)) { |
||||||
|
$contents = $filesystem->read($path); |
||||||
|
if ($contents !== FALSE) { |
||||||
|
$tmp_fly = tempnam(sys_get_temp_dir(), 'zip_'); |
||||||
|
file_put_contents($tmp_fly, $contents); |
||||||
|
$zip->addFile($tmp_fly, $filename_in_zip); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
catch (\Exception $e) { |
||||||
|
$this->logger('islandora_collection_harvest')->error( |
||||||
|
'Error reading @uri from Flysystem: @msg', |
||||||
|
['@uri' => $uri, '@msg' => $e->getMessage()] |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$zip->close(); |
||||||
|
readfile($tmpfile); |
||||||
|
unlink($tmpfile); |
||||||
|
}, 200, [ |
||||||
|
'Content-Type' => 'application/zip', |
||||||
|
'Content-Disposition' => 'attachment; filename="' . $filename . '"', |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,173 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Drupal\islandora_collection_harvest\Form; |
||||||
|
|
||||||
|
use Drupal\Core\Form\FormBase; |
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
use Drupal\Core\Entity\EntityTypeManagerInterface; |
||||||
|
use Drupal\Core\File\FileSystemInterface; |
||||||
|
use Drupal\flysystem\FlysystemFactory; |
||||||
|
use Drupal\islandora\IslandoraUtils; |
||||||
|
use Drupal\Core\TempStore\PrivateTempStoreFactory; |
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Form for selecting Collection and Media Use and preparing ZIP download. |
||||||
|
*/ |
||||||
|
final class CollectionHarvestForm extends FormBase { |
||||||
|
|
||||||
|
protected EntityTypeManagerInterface $entityTypeManager; |
||||||
|
|
||||||
|
protected IslandoraUtils $utils; |
||||||
|
|
||||||
|
protected FileSystemInterface $fileSystem; |
||||||
|
|
||||||
|
protected FlysystemFactory $flysystemFactory; |
||||||
|
|
||||||
|
protected PrivateTempStoreFactory $tempStoreFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function __construct( |
||||||
|
EntityTypeManagerInterface $entity_type_manager, |
||||||
|
IslandoraUtils $utils, |
||||||
|
FileSystemInterface $file_system, |
||||||
|
FlysystemFactory $flysystem_factory, |
||||||
|
PrivateTempStoreFactory $temp_store_factory |
||||||
|
) { |
||||||
|
$this->entityTypeManager = $entity_type_manager; |
||||||
|
$this->utils = $utils; |
||||||
|
$this->fileSystem = $file_system; |
||||||
|
$this->flysystemFactory = $flysystem_factory; |
||||||
|
$this->tempStoreFactory = $temp_store_factory; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public static function create(ContainerInterface $container): self { |
||||||
|
return new self( |
||||||
|
$container->get('entity_type.manager'), |
||||||
|
$container->get('islandora.utils'), |
||||||
|
$container->get('file_system'), |
||||||
|
$container->get('flysystem_factory'), |
||||||
|
$container->get('tempstore.private') |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getFormId(): string { |
||||||
|
return 'islandora_collection_harvest_form'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function buildForm(array $form, FormStateInterface $form_state): array { |
||||||
|
$term = $this->utils->getTermForUri('http://purl.org/dc/dcmitype/Collection'); |
||||||
|
$collections = []; |
||||||
|
if ($term) { |
||||||
|
$query = $this->entityTypeManager->getStorage('node')->getQuery(); |
||||||
|
$query->condition('type', 'islandora_object'); |
||||||
|
$query->condition('field_model', $term->id(), 'IN'); |
||||||
|
$query->accessCheck(FALSE); |
||||||
|
$nids = $query->execute(); |
||||||
|
$nodes = $this->entityTypeManager->getStorage('node') |
||||||
|
->loadMultiple($nids); |
||||||
|
foreach ($nodes as $node) { |
||||||
|
$collections[$node->id()] = $node->label(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$vid = 'islandora_media_use'; |
||||||
|
$terms = $this->entityTypeManager->getStorage('taxonomy_term') |
||||||
|
->loadTree($vid); |
||||||
|
$media_use_options = []; |
||||||
|
foreach ($terms as $term) { |
||||||
|
$media_use_options[$term->tid] = $term->name; |
||||||
|
} |
||||||
|
|
||||||
|
$form['#attributes']['id'] = 'collection-harvest-form'; |
||||||
|
$form['collection'] = [ |
||||||
|
'#type' => 'select', |
||||||
|
'#title' => $this->t('Collection'), |
||||||
|
'#options' => $collections, |
||||||
|
'#required' => TRUE, |
||||||
|
]; |
||||||
|
|
||||||
|
$form['media_use'] = [ |
||||||
|
'#type' => 'select', |
||||||
|
'#title' => $this->t('Media Use'), |
||||||
|
'#options' => $media_use_options, |
||||||
|
'#required' => TRUE, |
||||||
|
]; |
||||||
|
|
||||||
|
$form['actions'] = [ |
||||||
|
'#type' => 'actions', |
||||||
|
'submit' => [ |
||||||
|
'#type' => 'submit', |
||||||
|
'#value' => $this->t('Build Download'), |
||||||
|
'#button_type' => 'primary', |
||||||
|
], |
||||||
|
]; |
||||||
|
return $form; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function submitForm(array &$form, FormStateInterface $form_state): void { |
||||||
|
$collection_id = $form_state->getValue('collection'); |
||||||
|
$media_use_tid = $form_state->getValue('media_use'); |
||||||
|
|
||||||
|
$term = $this->entityTypeManager->getStorage('taxonomy_term') |
||||||
|
->load($media_use_tid); |
||||||
|
if (!$term) { |
||||||
|
$this->messenger()->addError($this->t('Media use term not found.')); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Load nodes in collection |
||||||
|
$query = $this->entityTypeManager->getStorage('node')->getQuery(); |
||||||
|
$query->condition('type', 'islandora_object'); |
||||||
|
$query->condition('field_member_of', $collection_id); |
||||||
|
$query->accessCheck(FALSE); |
||||||
|
$nids = $query->execute(); |
||||||
|
|
||||||
|
$media_entities = []; |
||||||
|
foreach ($nids as $nid) { |
||||||
|
$node = $this->entityTypeManager->getStorage('node')->load($nid); |
||||||
|
$media = $this->utils->getMediaWithTerm($node, $term); |
||||||
|
if ($media) { |
||||||
|
$source_field = $media->getSource() |
||||||
|
->getConfiguration()['source_field'] ?? NULL; |
||||||
|
if ($source_field && $media->hasField($source_field) && !$media->get($source_field) |
||||||
|
->isEmpty()) { |
||||||
|
$media_entities[] = $media; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (empty($media_entities)) { |
||||||
|
$this->messenger() |
||||||
|
->addError($this->t('No media found for this collection.')); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Store in tempstore |
||||||
|
$temp_store = $this->tempStoreFactory->get('islandora_collection_harvest'); |
||||||
|
$zip_filename = 'collection_' . time() . '.zip'; |
||||||
|
$temp_store->set($zip_filename, $media_entities); |
||||||
|
$this->messenger() |
||||||
|
->addStatus($this->t('Your ZIP download has been initiated. Please wait for the download to start.')); |
||||||
|
|
||||||
|
// Redirect to download route |
||||||
|
$form_state->setRedirect('islandora_collection_harvest.download_zip', ['filename' => $zip_filename]); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue