From 19fd416b52ea9dd7cabcf2ccb5b3991f2ac4cab3 Mon Sep 17 00:00:00 2001
From: astanley
If Already in place is selected, files in the destination folder will become Drupal managed files, + and those files will be used to build the media. This is the fastest and most efficient way to ingest very large files.
+The source directory holds files to be ingested. The source directory must be identified by the full + server path e.g. /var/www/upload_folder. +
+The destination directory is within the Drupal file system. The path is relative, and the folder will be created if + it does not already exist.
+The file storage system is defined within the Media Types definitions. The uploaded files will be stored + in the default directory unless another file system is selected. +
+
File ownership is normally www-data:www-data on Apache systems and nginx:nginx for + a Docker based Islandora installation. The default value is normally fine, but check with your systems administrator + if you are unsure. +
"; + return [ + '#type' => 'markup', + '#markup' => $html, + '#attached' => [ + 'library' => [ + 'core/drupal.dialog', + ], + ], + ]; + } + +} diff --git a/src/Form/CreateMediaFromFileForm.php b/src/Form/CreateMediaFromFileForm.php index 66435e6..54932bf 100644 --- a/src/Form/CreateMediaFromFileForm.php +++ b/src/Form/CreateMediaFromFileForm.php @@ -4,11 +4,15 @@ declare(strict_types=1); namespace Drupal\islandora_inplace_media\Form; +use Drupal\Core\StreamWrapper\StreamWrapperInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\islandora_inplace_media\Utils; use Drupal\Core\Entity\EntityTypeManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\Url; +use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface; +use Drupal\field\Entity\FieldConfig; /** * Provides an Islandora Inplace Media form. @@ -29,12 +33,21 @@ final class CreateMediaFromFileForm extends FormBase { */ protected $entityTypeManager; + /** + * The stream wrapper service. + * + * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface + */ + + protected $streamWrapperManager; + /** * {@inheritdoc} */ - public function __construct(Utils $mediaUtils, EntityTypeManagerInterface $entityTypeManager) { + public function __construct(Utils $mediaUtils, EntityTypeManagerInterface $entityTypeManager, StreamWrapperManagerInterface $streamWrapperManager) { $this->mediaUtils = $mediaUtils; $this->entityTypeManager = $entityTypeManager; + $this->streamWrapperManager = $streamWrapperManager; } /** @@ -43,7 +56,8 @@ final class CreateMediaFromFileForm extends FormBase { public static function create(ContainerInterface $container): self { return new self( $container->get('islandora_inplace_media.utils'), - $container->get('entity_type.manager') + $container->get('entity_type.manager'), + $container->get('stream_wrapper_manager') ); } @@ -58,6 +72,13 @@ final class CreateMediaFromFileForm extends FormBase { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state): array { + $server_software = $_SERVER['SERVER_SOFTWARE'] ?? ''; + $default_ownership = match (TRUE) { + stripos($server_software, 'apache') !== FALSE => 'www-data:www-data', + stripos($server_software, 'nginx') !== FALSE => 'nginx:nginx', + default => 'unknown:unknown', + }; + $vid = 'islandora_media_use'; $terms = $this->entityTypeManager->getStorage('taxonomy_term')->loadTree($vid); $media_use_options = []; @@ -68,46 +89,112 @@ final class CreateMediaFromFileForm extends FormBase { $term_default = $term->tid; } } + $wrappers = $this->streamWrapperManager->getWrappers(); + $file_system_options = []; + $wrappers = $this->streamWrapperManager->getWrappers(); + $file_system_options = []; + $file_system_options['default'] = $this->t("File system defined in media type"); + + $unwanted = ['temporary', 'assets']; + foreach ($wrappers as $scheme => $wrapper_info) { + if (in_array($scheme, $unwanted)) { + continue; + } + $class = $wrapper_info['class']; + if ( + is_a($class, StreamWrapperInterface::class, TRUE) && + class_exists($class) + ) { + $instance = new $class(); + $instance->setUri($scheme . '://'); + $file_system_options[$scheme] = $instance->getName() ?: $scheme; + } + } $media_types = $this->entityTypeManager->getStorage('media_type')->loadMultiple(); $types = array_map(fn($media_type) => $media_type->label(), $media_types); + $form['instructions'] = [ + '#type' => 'link', + '#title' => $this->t('View Instructions'), + '#url' => Url::fromRoute('islandora_inplace_media.instructions_modal'), + '#attributes' => [ + 'class' => ['use-ajax', 'button', 'button--small'], + 'data-dialog-type' => 'modal', + 'data-dialog-options' => json_encode([ + 'width' => 700, + ]), + ], + '#attached' => [ + 'library' => [ + 'core/drupal.dialog.ajax', + ], + ], + ]; + $form['in_place'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Already in place?'), + '#description' => $this->t("Check this box if files are already in the file system directory."), + ]; + $form['source'] = [ '#type' => 'textfield', - '#title' => $this->t('Enter full file path to input directory'), - '#required' => TRUE, + '#title' => $this->t('Enter the full file path to the input directory'), + '#states' => [ + 'visible' => [ + ':input[name="in_place"]' => ['checked' => FALSE], + ], + 'required' => [ + ':input[name="in_place"]' => ['checked' => FALSE], + ], + ], ]; - $form['directory'] = [ + + $form['destination'] = [ '#type' => 'textfield', - '#title' => $this->t('Enter the destination directory'), + '#title' => $this->t('Enter the directory on the file system'), '#required' => TRUE, ]; - $form['destination'] = [ + $form['file_system'] = [ '#type' => 'radios', - '#options' => [ - 'public' => $this->t('Public'), - 'private' => $this->t('Private'), - ], + '#options' => $file_system_options, '#title' => $this->t('Select file system'), '#required' => TRUE, - '#default_value' => 'public', + '#default_value' => 'default', ]; $form['field_wrapper'] = [ '#type' => 'container', '#attributes' => ['class' => ['media-fields-wrapper']], ]; - $form['field_wrapper']['media_type'] = [ + $form['field_wrapper']['media_type_wrapper'] = [ + '#type' => 'container', + '#attributes' => ['class' => ['media-field-container']], + ]; + $form['field_wrapper']['media_type_wrapper']['media_type'] = [ '#title' => $this->t("Media type."), '#type' => 'select', '#options' => $types, '#attributes' => ['class' => ['media-side-by-side']], ]; - $form['field_wrapper']['media_use'] = [ + $form['field_wrapper']['media_use_wrapper'] = [ + '#type' => 'container', + '#attributes' => ['class' => ['media-field-container']], + ]; + $form['field_wrapper']['media_use_wrapper']['media_use'] = [ '#title' => $this->t("Media use."), '#type' => 'select', '#options' => $media_use_options, '#default_value' => $term_default, '#attributes' => ['class' => ['media-side-by-side']], ]; + $form['field_wrapper']['ownership_wrapper'] = [ + '#type' => 'container', + '#attributes' => ['class' => ['media-field-container']], + ]; + $form['field_wrapper']['ownership_wrapper']['ownership'] = [ + '#type' => 'textfield', + '#title' => $this->t('File ownership'), + '#default_value' => $default_ownership, + ]; $form['actions'] = [ '#type' => 'actions', @@ -125,29 +212,69 @@ final class CreateMediaFromFileForm extends FormBase { * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state): void { - $source_path = $form_state->getValue('source'); - - if (!is_dir($source_path)) { + $file_system = $form_state->getValue('file_system') . "://"; + $source_directory = $form_state->getValue('in_place') ? $file_system . $form_state->getValue('destination') : $form_state->getValue('source'); + if (!is_dir($source_directory)) { $form_state->setErrorByName('source', $this->t('The specified directory does not exist.')); } + if (is_dir($source_directory)) { + $files = scandir($source_directory); + $files = array_diff($files, ['.', '..']); + if (!$files) { + $form_state->setErrorByName('source', $this->t('The specified directory is empty.')); + } + } } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state): void { - $source_path = $form_state->getValue('source'); - $directory = trim($form_state->getValue('directory'), '/'); - $file_system = $form_state->getValue('destination') === 'public' ? 'public://' : 'private://'; - $destination = "{$file_system}{$directory}"; + $file_types = [ + 'audio' => 'field_media_audio_file', + 'document' => 'field_media_document', + 'file' => 'field_media_file', + 'image' => 'field_media_image', + 'video' => 'field_media_video_file', + ]; $media_type = $form_state->getValue('media_type'); - $media_use = $form_state->getValue('media_use'); - $this->mediaUtils->buildMediaBatch($source_path, $destination, $media_type, $media_use); - $this->messenger()->addStatus($this->t('Media files have been processed from %source to %destination.', [ - '%source' => $source_path, - '%destination' => $destination, - ])); + $file_type = $file_types[$media_type]; + + if ($form_state->getValue('file_system') == 'default') { + $field_config = FieldConfig::loadByName('media', $media_type, $file_type); + if ($field_config) { + $settings = $field_config->getSettings(); + $scheme = $settings['uri_scheme'] ?? 'public'; + $file_system = $scheme . "://"; + } + } + else { + $file_system = $form_state->getValue('file_system') . "://"; + } + + $destination = trim($form_state->getValue('destination'), '/'); + $build_data = [ + 'source_dir' => $form_state->getValue('in_place') ? $file_system . $destination : $form_state->getValue('source'), + 'destination_path' => "{$file_system}{$destination}", + 'media_type' => $media_type, + 'file_type' => $file_type, + 'media_use' => $form_state->getValue('media_use'), + 'ownership' => $form_state->getValue('ownership'), + ]; + $this->mediaUtils->buildMediaBatch($build_data); + if ($form_state->getValue('in_place')) { + $status = $this->t('Media files in %destination have been processed', [ + '%destination' => $destination, + ]); + } + else { + $status = $this->t('Media files have been processed from %source to %destination.', [ + '%source' => $build_data['source_dir'], + '%destination' => $build_data['destination_path'], + ]); + } + $this->messenger()->addStatus($status); $form_state->setRedirect('