Browse Source

added instructions

main
astanley 7 months ago
parent
commit
19fd416b52
  1. 14
      css/form-styles.css
  2. 12
      islandora_inplace_media.routing.yml
  3. 46
      src/Controller/InstructionsController.php
  4. 181
      src/Form/CreateMediaFromFileForm.php
  5. 91
      src/Utils.php

14
css/form-styles.css

@ -1,13 +1,13 @@
/* Use Flexbox to align form fields side by side */
.media-fields-wrapper {
display: flex;
gap: 20px; /* Space between elements */
align-items: center;
gap: 20px;
align-items: flex-start;
flex-wrap: wrap;
/* Optional, improves mobile responsiveness */
}
/* Ensure each field takes up equal space */
.media-side-by-side {
flex: 1;
min-width: 200px; /* Prevents elements from getting too small */
.media-field-container {
width: 250px;
flex: 0 0 auto;
}

12
islandora_inplace_media.routing.yml

@ -1,7 +1,17 @@
islandora_inplace_media.create_media_from_file:
path: '/islandora-inplace-media/create-media-from-file'
defaults:
_title: 'Create Media From File'
_title: 'Create Media From Files'
_form: 'Drupal\islandora_inplace_media\Form\CreateMediaFromFileForm'
requirements:
_permission: 'access content'
islandora_inplace_media.instructions_modal:
path: '/instructions-modal'
defaults:
_controller: '\Drupal\islandora_inplace_media\Controller\InstructionsController::modal'
_title: 'Instructions'
requirements:
_permission: 'access content'
options:
_format: 'html'

46
src/Controller/InstructionsController.php

@ -0,0 +1,46 @@
<?php
namespace Drupal\islandora_inplace_media\Controller;
use Drupal\Core\Controller\ControllerBase;
/**
*
*/
class InstructionsController extends ControllerBase {
/**
*
*/
public function modal() {
$html = "
<p>Upload your files to the server. If the files use the naming convention of {nid}_filename the created media will be
associated with the node identified by that nid. If not, the media will still be created, but will have to be associated
manually to a node through the UI.
</p>
<p> If <strong><em>Already in place</em></strong> 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. </p>
<p>The source directory holds files to be ingested. The source directory must be identified by the full
server path e.g. <strong><em>/var/www/upload_folder</em></strong>.
</p>
<p>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.</p>
<p> 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.
<p>
<p>File ownership is normally <strong>www-data:www-data</strong> on Apache systems and <strong>nginx:nginx</strong> for
a Docker based Islandora installation. The default value is normally fine, but check with your systems administrator
if you are unsure.
</p>";
return [
'#type' => 'markup',
'#markup' => $html,
'#attached' => [
'library' => [
'core/drupal.dialog',
],
],
];
}
}

181
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('<front>');
}

91
src/Utils.php

@ -16,75 +16,76 @@ class Utils {
/**
* Processes individual file.
*/
public static function processMediaBatch($file_name, $dir, $dest, $media_type, $media_use_tid, &$context) {
public static function processMediaBatch($file_name, $build_data, &$context) {
if (!isset($context['results'])) {
$context['results'] = [];
}
$file_types = [
'audio' => 'field_media_audio_file',
'document' => 'field_media_document',
'file' => 'field_media_file',
'image' => 'field_media_image',
'video' => 'field_media_video_file',
];
$file_type = $file_types[$media_type] ?? 'field_media_file';
$media_type = $build_data['media_type'];
$file_type = $build_data['file_type'] ?? 'field_media_file';
$logger = \Drupal::logger('islandora_inplace_media');
$fileSystem = \Drupal::service('file_system');
$source_path = $dir . '/' . $file_name;
$destination_path = $dest . '/' . $file_name;
$source_path = $build_data['source_dir'] . '/' . $file_name;
$destination_path = $build_data['destination_path'] . '/' . $file_name;
if (!\file_exists($source_path)) {
$logger->warning('File does not exist: @file', ['@file' => $source_path]);
return;
}
if ($source_path === $destination_path) {
$path = $destination_path;
}
else {
$path = $fileSystem->copy($source_path, $destination_path, FileSystemInterface::EXISTS_RENAME);
}
$file = \Drupal::service('file.repository')->loadByUri($path);
if (!$file) {
$absolute_path = $fileSystem->realpath($destination_path);
if ($absolute_path) {
chown($absolute_path, $build_data['ownership']);
}
$file = File::create([
'uri' => $path,
'status' => 1,
]);
$file->save();
}
$moved_file = $fileSystem->move($source_path, $destination_path, FileSystemInterface::EXISTS_RENAME);
$absolute_path = $fileSystem->realpath($destination_path);
chown($absolute_path, 'www-data');
chgrp($absolute_path, 'lib-dev');
$new_file = File::create([
'uri' => $moved_file,
'status' => 1,
]);
$new_file->save();
$context['results'][$new_file->id()] = $moved_file;
$context['results'][$file->id()] = $path;
if (preg_match('/^(\d+)_/', $file_name, $matches)) {
$nid = $matches[1] ?? NULL;
}
if ($nid) {
$media = Media::create([
"bundle" => $media_type,
"name" => $file_name,
$file_type => [
"target_id" => $new_file->id(),
],
'field_media_use' => [
"target_id" => $media_use_tid,
],
"field_media_of" => $nid,
]);
$media->save();
}
$media = Media::create([
"bundle" => $media_type,
"name" => $file_name,
$file_type => [
"target_id" => $file->id(),
],
'field_media_use' => [
"target_id" => $build_data['media_use'],
],
"field_media_of" => $nid,
]);
$media->save();
}
/**
* Builds batch from input directory.
*/
public static function buildMediaBatch(string $dir, string $dest, string $media_type, string $media_use_tid) {
if (!is_dir($dir)) {
\Drupal::logger('islandora_inplace_media')->error('Source directory does not exist: @dir', ['@dir' => $dir]);
public static function buildMediaBatch($build_data) {
$source_dir = $build_data['source_dir'];
if ($source_dir && !is_dir($source_dir)) {
\Drupal::logger('islandora_inplace_media')->error('Source directory does not exist: @dir', ['@dir' => $source_dir]);
return;
}
// Use a service within the function, not in the batch context.
$destination = $build_data['destination_path'];
$fileSystem = \Drupal::service('file_system');
$fileSystem->prepareDirectory($dest, FileSystemInterface::CREATE_DIRECTORY);
$fileSystem->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY);
$files = scandir($dir);
$files = scandir($source_dir);
$files = array_diff($files, ['.', '..']);
$batch = [
@ -97,7 +98,7 @@ class Utils {
foreach ($files as $file_name) {
$batch['operations'][] = [
[self::class, 'processMediaBatch'],
[$file_name, $dir, $dest, $media_type, $media_use_tid],
[$file_name, $build_data],
];
}

Loading…
Cancel
Save