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