Browse Source

multifile changes

pull/755/head
Alan Stanley 6 years ago
parent
commit
a0ab8c77cb
  1. 1
      islandora.info.yml
  2. 17
      islandora.module
  3. 12
      islandora.permissions.yml
  4. 14
      islandora.routing.yml
  5. 28
      modules/islandora_audio/config/schema/islandora_audio.info.yml
  6. 6
      modules/islandora_breadcrumbs/islandora_breadcrumbs.services.yml
  7. 28
      modules/islandora_image/config/schema/islandora_image.info.yml
  8. 43
      modules/islandora_image/src/Plugin/Action/GenerateImageDerivativeFile.php
  9. 23
      modules/islandora_text_extraction/islandora_text_extraction.install
  10. 13
      modules/islandora_text_extraction/islandora_text_extraction.routing.yml
  11. 54
      modules/islandora_text_extraction/src/Controller/MediaSourceController.php
  12. 103
      modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivativeFile.php
  13. 28
      modules/islandora_video/config/schema/islandora_video.info.yml
  14. 50
      src/Controller/MediaSourceController.php
  15. 1
      src/EventGenerator/EventGenerator.php
  16. 318
      src/Plugin/Action/AbstractGenerateDerivativeMediaFile.php
  17. 10
      src/Plugin/Action/DeleteMediaAndFile.php
  18. 58
      src/Plugin/ContextReaction/DerivativeFileReaction.php
  19. 32
      src/Plugin/ContextReaction/DerivativeReaction.php
  20. 174
      src/Plugin/ContextReaction/MappingUriPredicateReaction.php
  21. 195
      tests/src/Functional/MappingUriPredicateReactionTest.php

1
islandora.info.yml

@ -31,3 +31,4 @@ dependencies:
- content_translation
- flysystem
- token
- hook_post_action

17
islandora.module

@ -106,7 +106,6 @@ function islandora_node_delete(NodeInterface $node) {
*/
function islandora_media_insert(MediaInterface $media) {
$utils = \Drupal::service('islandora.utils');
// Execute index reactions.
$utils->executeMediaReactions('\Drupal\islandora\Plugin\ContextReaction\IndexReaction', $media);
@ -136,7 +135,6 @@ function islandora_media_update(MediaInterface $media) {
// Execute index reactions.
$utils->executeMediaReactions('\Drupal\islandora\Plugin\ContextReaction\IndexReaction', $media);
// Does it have a source field?
$source_field = $media_source_service->getSourceFieldName($media->bundle());
if (empty($source_field)) {
@ -147,7 +145,6 @@ function islandora_media_update(MediaInterface $media) {
if ($media->get($source_field)->equals($media->original->get($source_field))) {
return;
}
// If it has a parent node...
$node = $utils->getParentNode($media);
if ($node) {
@ -157,6 +154,7 @@ function islandora_media_update(MediaInterface $media) {
$node,
$media
);
$utils->executeMediaReactions('\Drupal\islandora\Plugin\ContextReaction\DerivativeFileReaction', $media);
}
}
@ -170,6 +168,19 @@ function islandora_media_delete(MediaInterface $media) {
$utils->executeMediaReactions('\Drupal\islandora\Plugin\ContextReaction\DeleteReaction', $media);
}
/**
* Implements hook_ENTITYTYPE_postsave().
*/
function islandora_media_postsave(EntityInterface $media, $op) {
$utils = \Drupal::service('islandora.utils');
// Add derived file to the media.
if ($op == 'insert') {
$utils->executeMediaReactions('\Drupal\islandora\Plugin\ContextReaction\DerivativeFileReaction', $media);
}
}
/**
* Implements hook_file_insert().
*/

12
islandora.permissions.yml

@ -1,12 +0,0 @@
view checksums:
title: 'View Checksums'
description: 'Allows access to viewing file checksums'
manage members:
title: 'Manage Members'
description: 'Allows access to managing members for content'
manage media:
title: 'Manage Media'
description: 'Allows access to managing media for content'

14
islandora.routing.yml

@ -57,3 +57,17 @@ islandora.media_source_put_to_node:
_custom_access: '\Drupal\islandora\Controller\MediaSourceController::putToNodeAccess'
options:
_auth: ['basic_auth', 'cookie', 'jwt_auth']
islandora.attach_file_to_media:
path: '/media/add_derivative/{media}/{destination_field}'
defaults:
_controller: '\Drupal\islandora\Controller\MediaSourceController::attachToMedia'
methods: [GET, PUT]
requirements:
_custom_access: '\Drupal\islandora\Controller\MediaSourceController::attachToMediaAccess'
options:
_auth: ['basic_auth', 'cookie', 'jwt_auth']
no_cache: 'TRUE'
parameters:
media:
type: entity:media

28
modules/islandora_audio/config/schema/islandora_audio.info.yml

@ -0,0 +1,28 @@
action.configuration.generate_audio_derivative:
type: mapping
label: 'Generate a audio derivative...'
mapping:
queue:
type: text
label: 'Queue'
event:
type: text
label: 'Event Type'
source_term_uri:
type: text
label: 'Source term uri'
derivative_term_uri:
type: text
label: 'Destination term uri'
mimetype:
type: text
label: 'Audio Mimetype'
args:
type: text
label: 'FFMpeg Arguments'
scheme:
type: text
label: 'Flysystem scheme'
path:
type: text
label: 'File path with extension'

6
modules/islandora_breadcrumbs/islandora_breadcrumbs.services.yml

@ -1,6 +0,0 @@
services:
islandora_breadcrumbs.breadcrumb:
class: Drupal\islandora_breadcrumbs\IslandoraBreadcrumbBuilder
arguments: ['@entity_type.manager', '@config.factory']
tags:
- { name: breadcrumb_builder, priority: 100 }

28
modules/islandora_image/config/schema/islandora_image.info.yml

@ -0,0 +1,28 @@
action.configuration.generate_image_derivative:
type: mapping
label: 'Generate an image derivative...'
mapping:
queue:
type: text
label: 'Queue'
event:
type: text
label: 'Event Type'
source_term_uri:
type: text
label: 'Source term uri'
derivative_term_uri:
type: text
label: 'Destination term uri'
mimetype:
type: text
label: 'Image Mimetype'
args:
type: text
label: 'Convert Arguments'
scheme:
type: text
label: 'Flysystem scheme'
path:
type: text
label: 'File path with extension'

43
modules/islandora_image/src/Plugin/Action/GenerateImageDerivativeFile.php

@ -0,0 +1,43 @@
<?php
namespace Drupal\islandora_image\Plugin\Action;
use Drupal\Core\Form\FormStateInterface;
use Drupal\islandora\Plugin\Action\AbstractGenerateDerivativeMediaFile;
/**
* Emits a Node for generating derivatives event.
*
* @Action(
* id = "generate_image_derivative_file",
* label = @Translation("Generate an Image Derivative for Media Attachment"),
* type = "media"
* )
*/
class GenerateImageDerivativeFile extends AbstractGenerateDerivativeMediaFile {
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$config = parent::defaultConfiguration();
$config['path'] = '[date:custom:Y]-[date:custom:m]/[media:mid]-ImageService.jpg';
$config['mimetype'] = 'application/xml';
$config['queue'] = 'islandora-connector-houdini';
$config['destination_media_type'] = 'file';
$config['scheme'] = file_default_scheme();
return $config;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$form['mimetype']['#description'] = t('Mimetype to convert to (e.g. application/xml, etc...)');
$form['mimetype']['#value'] = 'image/jpeg';
$form['mimetype']['#type'] = 'hidden';
return $form;
}
}

23
modules/islandora_text_extraction/islandora_text_extraction.install

@ -0,0 +1,23 @@
<?php
/**
* @file
* Install/update hook implementations.
*/
use Drupal\field\Entity\FieldConfig;
/**
* Implements hook_install().
*/
function islandora_text_extraction_install() {
// Add txt extension if it doesn't already exist;.
$field = FieldConfig::load("media.file.field_media_file");
$fieldSettings = $field->getSettings();
$extensions = $fieldSettings['file_extensions'];
if (!strpos($extensions, 'txt')) {
$fieldSettings['file_extensions'] .= ' txt';
$field->set('settings', $fieldSettings);
$field->save();
}
}

13
modules/islandora_text_extraction/islandora_text_extraction.routing.yml

@ -0,0 +1,13 @@
islandora_text_extraction.attach_file_to_media:
path: '/media/add_ocr/{media}/{destination_field}/{destination_text_field}'
defaults:
_controller: '\Drupal\islandora_text_extraction\Controller\MediaSourceController::attachToMedia'
methods: [GET, PUT]
requirements:
_custom_access: '\Drupal\islandora\Controller\MediaSourceController::attachToMediaAccess'
options:
_auth: ['basic_auth', 'cookie', 'jwt_auth']
no_cache: 'TRUE'
parameters:
media:
type: entity:media

54
modules/islandora_text_extraction/src/Controller/MediaSourceController.php

@ -0,0 +1,54 @@
<?php
namespace Drupal\islandora_text_extraction\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\media\Entity\Media;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Class MediaSourceController.
*/
class MediaSourceController extends ControllerBase {
/**
* Adds file to existing media.
*
* @param Drupal\media\Entity\Media $media
* The media to which file is added.
* @param string $destination_field
* The name of the media field to add file reference.
* @param string $destination_text_field
* The name of the media field to add file reference.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*
* @return \Symfony\Component\HttpFoundation\Response
* 201 on success with a Location link header.
*
* @throws \Drupal\Core\Entity\EntityStorageException
* @throws \Drupal\Core\TypedData\Exception\ReadOnlyException
*/
public function attachToMedia(
Media $media,
string $destination_field,
string $destination_text_field,
Request $request) {
$content_location = $request->headers->get('Content-Location', "");
$contents = $request->getContent();
if ($contents) {
\Drupal::logger('Alan_dev')->warning("Content location is $content_location");
$file = file_save_data($contents, $content_location, FILE_EXISTS_REPLACE);
$media->{$destination_field}->setValue([
'target_id' => $file->id(),
]);
$media->{$destination_text_field}->setValue(nl2br($contents));
$media->save();
return new Response("<h1>Complete</h1>");
}
}
}

103
modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivativeFile.php

@ -0,0 +1,103 @@
<?php
namespace Drupal\islandora_text_extraction\Plugin\Action;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\islandora\Plugin\Action\AbstractGenerateDerivativeMediaFile;
/**
* Emits a Node for generating fits derivatives event.
*
* @Action(
* id = "generate_extracted_text_file",
* label = @Translation("Generate an Extracted Text derivative file"),
* type = "media"
* )
*/
class GenerateOCRDerivativeFile extends AbstractGenerateDerivativeMediaFile {
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$config = parent::defaultConfiguration();
$config['path'] = '[date:custom:Y]-[date:custom:m]/[media:mid]-extracted_text.txt';
$config['mimetype'] = 'application/xml';
$config['queue'] = 'islandora-connector-ocr';
$config['destination_media_type'] = 'file';
$config['scheme'] = file_default_scheme();
$config['destination_text_field_name'] = '';
return $config;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$map = $this->entityFieldManager->getFieldMapByFieldType('text_long');
$file_fields = $map['media'];
$field_options = array_combine(array_keys($file_fields), array_keys($file_fields));
$form = parent::buildConfigurationForm($form, $form_state);
$form['mimetype']['#description'] = t('Mimetype to convert to (e.g. application/xml, etc...)');
$form['mimetype']['#value'] = 'text/plain';
$form['mimetype']['#type'] = 'hidden';
$position = array_search('destination_field_name', array_keys($form));
$first = array_slice($form, 0, $position);
$last = array_slice($form, count($form) - $position + 1);
$middle['destination_text_field_name'] = [
'#required' => TRUE,
'#type' => 'select',
'#options' => $field_options,
'#title' => $this->t('Destination Text field Name'),
'#default_value' => $this->configuration['destination_text_field_name'],
'#description' => $this->t('Text field on Media Type to hold extracted text.'),
];
$form = array_merge($first, $middle, $last);
unset($form['args']);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::validateConfigurationForm($form, $form_state);
$exploded_mime = explode('/', $form_state->getValue('mimetype'));
if ($exploded_mime[0] != 'text') {
$form_state->setErrorByName(
'mimetype',
t('Please enter file mimetype (e.g. application/xml.)')
);
}
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::submitConfigurationForm($form, $form_state);
$this->configuration['destination_text_field_name'] = $form_state->getValue('destination_text_field_name');
}
/**
* Override this to return arbitrary data as an array to be json encoded.
*/
protected function generateData(EntityInterface $entity) {
$data = parent::generateData($entity);
$route_params = [
'media' => $entity->id(),
'destination_field' => $this->configuration['destination_field_name'],
'destination_text_field' => $this->configuration['destination_text_field_name'],
];
$data['destination_uri'] = Url::fromRoute('islandora_text_extraction.attach_file_to_media', $route_params)
->setAbsolute()
->toString();
return $data;
}
}

28
modules/islandora_video/config/schema/islandora_video.info.yml

@ -0,0 +1,28 @@
action.configuration.generate_video_derivative:
type: mapping
label: 'Generate a video derivative...'
mapping:
queue:
type: text
label: 'Queue'
event:
type: text
label: 'Event Type'
source_term_uri:
type: text
label: 'Source term uri'
derivative_term_uri:
type: text
label: 'Destination term uri'
mimetype:
type: text
label: 'Video Mimetype'
args:
type: text
label: 'FFMpeg Arguments'
scheme:
type: text
label: 'Flysystem scheme'
path:
type: text
label: 'File path with extension'

50
src/Controller/MediaSourceController.php

@ -18,6 +18,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Drupal\media\Entity\Media;
/**
* Class MediaSourceController.
@ -210,4 +211,53 @@ class MediaSourceController extends ControllerBase {
return AccessResult::allowedIf($node->access('update', $account) && $account->hasPermission('create media'));
}
/**
* Adds file to existing media.
*
* @param \Drupal\media\Entity\Media $media
* The media to which file is added.
* @param string $destination_field
* The name of the media field to add file reference.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*
* @return \Symfony\Component\HttpFoundation\Response
* 201 on success with a Location link header.
*
* @throws \Drupal\Core\Entity\EntityStorageException
* @throws \Drupal\Core\TypedData\Exception\ReadOnlyException
*/
public function attachToMedia(
Media $media,
string $destination_field,
Request $request) {
$content_location = $request->headers->get('Content-Location', "");
$contents = $request->getContent();
if ($contents) {
$file = file_save_data($contents, $content_location, FILE_EXISTS_REPLACE);
$media->{$destination_field}->setValue([
'target_id' => $file->id(),
]);
$media->save();
}
return new Response("<h1>Complete</h1>");
}
/**
* Checks for permissions to update a node and update media.
*
* @param \Drupal\Core\Session\AccountInterface $account
* Account for user making the request.
* @param \Drupal\Core\Routing\RouteMatch $route_match
* Route match to get Node from url params.
*
* @return \Drupal\Core\Access\AccessResultInterface
* Access result.
*/
public function attachToMediaAccess(AccountInterface $account, RouteMatch $route_match) {
$media = $route_match->getParameter('media');
$node = $this->utils->getParentNode($media);
return AccessResult::allowedIf($node->access('update', $account) && $account->hasPermission('create media'));
}
}

1
src/EventGenerator/EventGenerator.php

@ -156,7 +156,6 @@ class EventGenerator implements EventGeneratorInterface {
"mediaType" => "application/json",
];
}
return json_encode($event);
}

318
src/Plugin/Action/AbstractGenerateDerivativeMediaFile.php

@ -0,0 +1,318 @@
<?php
namespace Drupal\islandora\Plugin\Action;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\islandora\IslandoraUtils;
use Drupal\islandora\EventGenerator\EmitEvent;
use Drupal\islandora\EventGenerator\EventGeneratorInterface;
use Drupal\islandora\MediaSource\MediaSourceService;
use Drupal\jwt\Authentication\Provider\JwtAuth;
use Drupal\token\Token;
use Stomp\StatefulStomp;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Emits a Node for generating derivatives event.
*
* @Action(
* id = "generate_derivative_file",
* label = @Translation("Generate a Derivative File for Media Attachment"),
* type = "media"
* )
*/
class AbstractGenerateDerivativeMediaFile extends EmitEvent {
/**
* Islandora utility functions.
*
* @var \Drupal\islandora\IslandoraUtils
*/
protected $utils;
/**
* Media source service.
*
* @var \Drupal\islandora\MediaSource\MediaSourceService
*/
protected $mediaSource;
/**
* Token replacement service.
*
* @var \Drupal\token\Token
*/
protected $token;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManager
*/
protected $entityFieldManager;
/**
* Constructs a EmitEvent action.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Session\AccountInterface $account
* Current user.
* @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager
* Entity type manager.
* @param \Drupal\islandora\EventGenerator\EventGeneratorInterface $event_generator
* EventGenerator service to serialize AS2 events.
* @param \Stomp\StatefulStomp $stomp
* Stomp client.
* @param \Drupal\jwt\Authentication\Provider\JwtAuth $auth
* JWT Auth client.
* @param \Drupal\islandora\IslandoraUtils $utils
* Islandora utility functions.
* @param \Drupal\islandora\MediaSource\MediaSourceService $media_source
* Media source service.
* @param \Drupal\token\Token $token
* Token service.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* Field Manager service.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
AccountInterface $account,
EntityTypeManager $entity_type_manager,
EventGeneratorInterface $event_generator,
StatefulStomp $stomp,
JwtAuth $auth,
IslandoraUtils $utils,
MediaSourceService $media_source,
Token $token,
EntityFieldManagerInterface $entity_field_manager
) {
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition,
$account,
$entity_type_manager,
$event_generator,
$stomp,
$auth
);
$this->utils = $utils;
$this->mediaSource = $media_source;
$this->token = $token;
$this->entityFieldManager = $entity_field_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('current_user'),
$container->get('entity_type.manager'),
$container->get('islandora.eventgenerator'),
$container->get('islandora.stomp'),
$container->get('jwt.authentication.jwt'),
$container->get('islandora.utils'),
$container->get('islandora.media_source_service'),
$container->get('token'),
$container->get('entity_field.manager')
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$uri = 'http://pcdm.org/use#OriginalFile';
return [
'queue' => 'islandora-connector-houdini',
'event' => 'Generate Derivative',
'source_term_uri' => $uri,
'mimetype' => '',
'args' => '',
'scheme' => file_default_scheme(),
'path' => '[date:custom:Y]-[date:custom:m]/[media:mid].bin',
'source_field_name' => 'field_media_file',
'destination_field_name' => '',
];
}
/**
* Override this to return arbitrary data as an array to be json encoded.
*/
protected function generateData(EntityInterface $entity) {
$data = parent::generateData($entity);
if (get_class($entity) != 'Drupal\media\Entity\Media') {
return;
}
$source_file = $this->mediaSource->getSourceFile($entity);
if (!$source_file) {
throw new \RuntimeException("Could not locate source file for media {$entity->id()}", 500);
}
$data['source_uri'] = $this->utils->getDownloadUrl($source_file);
$route_params = [
'media' => $entity->id(),
'destination_field' => $this->configuration['destination_field_name'],
];
$data['destination_uri'] = Url::fromRoute('islandora.attach_file_to_media', $route_params)
->setAbsolute()
->toString();
$token_data = [
'media' => $entity,
];
$path = $this->token->replace($data['path'], $token_data);
$data['file_upload_uri'] = $data['scheme'] . '://' . $path;
$allowed = ['queue',
'event',
'args',
'source_uri',
'destination_uri',
'file_upload_uri',
'mimetype',
];
foreach ($data as $key => $value) {
if (!in_array($key, $allowed)) {
unset($data[$key]);
}
}
return $data;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$schemes = $this->utils->getFilesystemSchemes();
$scheme_options = array_combine($schemes, $schemes);
$form = parent::buildConfigurationForm($form, $form_state);
$map = $this->entityFieldManager->getFieldMapByFieldType('file');
$file_fields = $map['media'];
$file_options = array_combine(array_keys($file_fields), array_keys($file_fields));
$file_options = array_merge(['' => ''], $file_options);
$form['event']['#disabled'] = 'disabled';
$form['destination_field_name'] = [
'#required' => TRUE,
'#type' => 'select',
'#options' => $file_options,
'#title' => $this->t('Destination File field Name'),
'#default_value' => $this->configuration['destination_field_name'],
'#description' => $this->t('File field on Media Type to hold derivative. Cannot be the same as source'),
];
$form['mimetype'] = [
'#type' => 'textfield',
'#title' => t('Mimetype'),
'#default_value' => $this->configuration['mimetype'],
'#required' => TRUE,
'#rows' => '8',
'#description' => t('Mimetype to convert to (e.g. image/jpeg, video/mp4, etc...)'),
];
$form['args'] = [
'#type' => 'textfield',
'#title' => t('Additional arguments'),
'#default_value' => $this->configuration['args'],
'#rows' => '8',
'#description' => t('Additional command line arguments'),
];
$form['scheme'] = [
'#type' => 'select',
'#title' => t('File system'),
'#options' => $scheme_options,
'#default_value' => $this->configuration['scheme'],
'#required' => TRUE,
];
$form['path'] = [
'#type' => 'textfield',
'#title' => t('File path'),
'#default_value' => $this->configuration['path'],
'#description' => t('Path within the upload destination where files will be stored. Includes the filename and optional extension.'),
];
$form['queue'] = [
'#type' => 'textfield',
'#title' => t('Queue name'),
'#default_value' => $this->configuration['queue'],
'#description' => t('Queue name to send along to help routing events, CHANGE WITH CARE. Defaults to :queue', [
':queue' => $this->defaultConfiguration()['queue'],
]),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::validateConfigurationForm($form, $form_state);
$exploded_mime = explode('/', $form_state->getValue('mimetype'));
if (count($exploded_mime) != 2) {
$form_state->setErrorByName(
'mimetype',
t('Please enter a mimetype (e.g. image/jpeg, video/mp4, audio/mp3, etc...)')
);
}
if (empty($exploded_mime[1])) {
$form_state->setErrorByName(
'mimetype',
t('Please enter a mimetype (e.g. image/jpeg, video/mp4, audio/mp3, etc...)')
);
}
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::submitConfigurationForm($form, $form_state);
$this->configuration['mimetype'] = $form_state->getValue('mimetype');
$this->configuration['args'] = $form_state->getValue('args');
$this->configuration['scheme'] = $form_state->getValue('scheme');
$this->configuration['path'] = trim($form_state->getValue('path'), '\\/');
$this->configuration['destination_field_name'] = $form_state->getValue('destination_field_name');
}
/**
* Find a media_type by id and return it or nothing.
*
* @param string $entity_id
* The media type.
*
* @return \Drupal\Core\Entity\EntityInterface|string
* Return the loaded entity or nothing.
*
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* Thrown by getStorage() if the entity type doesn't exist.
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* Thrown by getStorage() if the storage handler couldn't be loaded.
*/
protected function getEntityById($entity_id) {
$entity_ids = $this->entityTypeManager->getStorage('media_type')
->getQuery()->condition('id', $entity_id)->execute();
$id = reset($entity_ids);
if ($id !== FALSE) {
return $this->entityTypeManager->getStorage('media_type')->load($id);
}
return '';
}
}

10
src/Plugin/Action/DeleteMediaAndFile.php

@ -9,6 +9,7 @@ use Drupal\Core\Session\AccountInterface;
use Drupal\islandora\MediaSource\MediaSourceService;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\file\Entity\File;
/**
* Deletes a media and its source file.
@ -101,6 +102,15 @@ class DeleteMediaAndFile extends ActionBase implements ContainerFactoryPluginInt
$source_field = $this->mediaSourceService->getSourceFieldName($entity->bundle());
foreach ($entity->get($source_field)->referencedEntities() as $file) {
$file->delete();
// Check for non-source fields;
foreach ($entity->getFieldDefinitions() as $field) {
if ($field->getType() == 'file') {
$fid = $entity->get($field->getName())->getValue()[0]['target_id'];
if ($fid) {
File::load($fid)->delete();
}
}
}
}
$entity->delete();
}

58
src/Plugin/ContextReaction/DerivativeFileReaction.php

@ -0,0 +1,58 @@
<?php
namespace Drupal\islandora\Plugin\ContextReaction;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\islandora\Plugin\Action\AbstractGenerateDerivativeMediaFile;
use Drupal\islandora\PresetReaction\PresetReaction;
/**
* Derivative context reaction.
*
* @ContextReaction(
* id = "file_derivative",
* label = @Translation("Derive file For Existing Media")
* )
*/
class DerivativeFileReaction extends PresetReaction {
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$actions = $this->actionStorage->loadByProperties(['type' => 'media']);
foreach ($actions as $action) {
$plugin = $action->getPlugin();
if ($plugin instanceof AbstractGenerateDerivativeMediaFile) {
$options[ucfirst($action->getType())][$action->id()] = $action->label();
}
}
$config = $this->getConfiguration();
$form['actions'] = [
'#title' => $this->t('Actions'),
'#description' => $this->t('Pre-configured actions to execute. Multiple actions may be selected by shift or ctrl clicking.'),
'#type' => 'select',
'#multiple' => TRUE,
'#options' => $options,
'#default_value' => isset($config['actions']) ? $config['actions'] : '',
'#size' => 15,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function execute(EntityInterface $entity = NULL) {
$config = $this->getConfiguration();
$action_ids = $config['actions'];
foreach ($action_ids as $action_id) {
$action = $this->actionStorage->load($action_id);
$action->execute([$entity]);
}
}
}

32
src/Plugin/ContextReaction/DerivativeReaction.php

@ -2,6 +2,8 @@
namespace Drupal\islandora\Plugin\ContextReaction;
use Drupal\Core\Form\FormStateInterface;
use Drupal\islandora\Plugin\Action\AbstractGenerateDerivative;
use Drupal\islandora\PresetReaction\PresetReaction;
/**
@ -12,4 +14,32 @@ use Drupal\islandora\PresetReaction\PresetReaction;
* label = @Translation("Derivative")
* )
*/
class DerivativeReaction extends PresetReaction {}
class DerivativeReaction extends PresetReaction {
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$actions = $this->actionStorage->loadMultiple();
$options = [];
foreach ($actions as $action) {
$plugin = $action->getPlugin();
if ($plugin instanceof AbstractGenerateDerivative) {
$options[ucfirst($action->getType())][$action->id()] = $action->label();
}
}
$config = $this->getConfiguration();
$form['actions'] = [
'#title' => $this->t('Actions'),
'#description' => $this->t('Pre-configured actions to execute. Multiple actions may be selected by shift or ctrl clicking.'),
'#type' => 'select',
'#multiple' => TRUE,
'#options' => $options,
'#default_value' => isset($config['actions']) ? $config['actions'] : '',
'#size' => 15,
];
return $form;
}
}

174
src/Plugin/ContextReaction/MappingUriPredicateReaction.php

@ -0,0 +1,174 @@
<?php
namespace Drupal\islandora\Plugin\ContextReaction;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\islandora\ContextReaction\NormalizerAlterReaction;
use Drupal\islandora\MediaSource\MediaSourceService;
use Drupal\jsonld\Normalizer\NormalizerBase;
use Drupal\media\MediaInterface;
use Drupal\islandora\IslandoraUtils;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Map URI to predicate context reaction.
*
* @ContextReaction(
* id = "islandora_map_uri_predicate",
* label = @Translation("Map URI to predicate")
* )
*/
class MappingUriPredicateReaction extends NormalizerAlterReaction {
const URI_PREDICATE = 'drupal_uri_predicate';
/**
* Media source service.
*
* @var \Drupal\islandora\MediaSource\MediaSourceService
*/
protected $mediaSource;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration,
$plugin_id,
$plugin_definition,
ConfigFactoryInterface $config_factory,
IslandoraUtils $utils,
MediaSourceService $media_source) {
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition,
$config_factory,
$utils
);
$this->mediaSource = $media_source;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('config.factory'),
$container->get('islandora.utils'),
$container->get('islandora.media_source_service')
);
}
/**
* {@inheritdoc}
*/
public function summary() {
return $this->t('Map Drupal URI to configured predicate.');
}
/**
* {@inheritdoc}
*/
public function execute(EntityInterface $entity = NULL, array &$normalized = NULL, array $context = NULL) {
$config = $this->getConfiguration();
$drupal_predicate = $config[self::URI_PREDICATE];
if (!is_null($drupal_predicate) && !empty($drupal_predicate)) {
$url = $this->getSubjectUrl($entity);
if ($context['needs_jsonldcontext'] === FALSE) {
$drupal_predicate = NormalizerBase::escapePrefix($drupal_predicate, $context['namespaces']);
}
if (isset($normalized['@graph']) && is_array($normalized['@graph'])) {
foreach ($normalized['@graph'] as &$graph) {
if (isset($graph['@id']) && $graph['@id'] == $url) {
// Swap media and file urls.
if ($entity instanceof MediaInterface) {
$file = $this->mediaSource->getSourceFile($entity);
$graph['@id'] = $this->utils->getDownloadUrl($file);
}
if (isset($graph[$drupal_predicate])) {
if (!is_array($graph[$drupal_predicate])) {
if ($graph[$drupal_predicate] == $url) {
// Don't add it if it already exists.
return;
}
$tmp = $graph[$drupal_predicate];
$graph[$drupal_predicate] = [$tmp];
}
elseif (array_search($url, array_column($graph[$drupal_predicate], '@id'))) {
// Don't add it if it already exists.
return;
}
}
else {
$graph[$drupal_predicate] = [];
}
$graph[$drupal_predicate][] = ["@id" => $url];
return;
}
}
}
}
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$config = $this->getConfiguration();
$form[self::URI_PREDICATE] = [
'#type' => 'textfield',
'#title' => $this->t('Drupal URI predicate'),
'#description' => $this->t("The Drupal object's URI will be added to the resource with this predicate. Must use a defined prefix."),
'#default_value' => isset($config[self::URI_PREDICATE]) ? $config[self::URI_PREDICATE] : '',
'#size' => 35,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
$drupal_predicate = $form_state->getValue(self::URI_PREDICATE);
if (!is_null($drupal_predicate) and !empty($drupal_predicate)) {
if (preg_match('/^https?:\/\//', $drupal_predicate)) {
// Can't validate all URIs so we have to trust them.
return;
}
elseif (preg_match('/^([^\s:]+):/', $drupal_predicate, $matches)) {
$predicate_prefix = $matches[1];
$rdf = rdf_get_namespaces();
$rdf_prefixes = array_keys($rdf);
if (!in_array($predicate_prefix, $rdf_prefixes)) {
$form_state->setErrorByName(
self::URI_PREDICATE,
$this->t('Namespace prefix @prefix is not registered.',
['@prefix' => $predicate_prefix]
)
);
}
}
else {
$form_state->setErrorByName(
self::URI_PREDICATE,
$this->t('Predicate must use a defined prefix or be a full URI')
);
}
}
parent::validateConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->setConfiguration([self::URI_PREDICATE => $form_state->getValue(self::URI_PREDICATE)]);
}
}

195
tests/src/Functional/MappingUriPredicateReactionTest.php

@ -0,0 +1,195 @@
<?php
namespace Drupal\Tests\islandora\Functional;
/**
* Class MappingUriPredicateReactionTest.
*
* @package Drupal\Tests\islandora\Functional
* @group islandora
*/
class MappingUriPredicateReactionTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$types = ['schema:Thing'];
$created_mapping = [
'properties' => ['schema:dateCreated'],
'datatype' => 'xsd:dateTime',
'datatype_callback' => ['callable' => 'Drupal\rdf\CommonDataConverter::dateIso8601Value'],
];
// Save bundle mapping config.
$this->rdfMapping = rdf_get_mapping('node', 'test_type')
->setBundleMapping(['types' => $types])
->setFieldMapping('created', $created_mapping)
->setFieldMapping('title', [
'properties' => ['dc:title'],
'datatype' => 'xsd:string',
])
->save();
$this->container->get('router.builder')->rebuildIfNeeded();
}
/**
* @covers \Drupal\islandora\Plugin\ContextReaction\MappingUriPredicateReaction
*/
public function testMappingReaction() {
$account = $this->drupalCreateUser([
'bypass node access',
'administer contexts',
]);
$this->drupalLogin($account);
$context_name = 'test';
$reaction_id = 'islandora_map_uri_predicate';
$this->postNodeAddForm('test_type',
['title[0][value]' => 'Test Node'],
t('Save'));
$this->assertSession()->pageTextContains("Test Node");
$url = $this->getUrl();
// Make sure the node exists.
$this->drupalGet($url);
$this->assertSession()->statusCodeEquals(200);
$contents = $this->drupalGet($url . '?_format=jsonld');
$this->assertSession()->statusCodeEquals(200);
$json = \GuzzleHttp\json_decode($contents, TRUE);
$this->assertArrayHasKey('http://purl.org/dc/terms/title',
$json['@graph'][0], 'Missing dcterms:title key');
$this->assertEquals(
'Test Node',
$json['@graph'][0]['http://purl.org/dc/terms/title'][0]['@value'],
'Missing title value'
);
$this->assertArrayNotHasKey('http://www.w3.org/2002/07/owl#sameAs',
$json['@graph'][0], 'Has predicate when not configured');
$this->createContext('Test', $context_name);
$this->drupalGet("admin/structure/context/$context_name/reaction/add/$reaction_id");
$this->assertSession()->statusCodeEquals(200);
$this->drupalGet("admin/structure/context/$context_name");
// Can't use an undefined prefix.
$this->getSession()->getPage()
->fillField("Drupal URI predicate", "bob:smith");
$this->getSession()->getPage()->pressButton("Save and continue");
$this->assertSession()
->pageTextContains("Namespace prefix bob is not registered");
// Can't use a straight string.
$this->getSession()->getPage()
->fillField("Drupal URI predicate", "woohoo");
$this->getSession()->getPage()->pressButton("Save and continue");
$this->assertSession()
->pageTextContains("Predicate must use a defined prefix or be a full URI");
// Use an existing prefix.
$this->getSession()->getPage()
->fillField("Drupal URI predicate", "owl:sameAs");
$this->getSession()->getPage()->pressButton("Save and continue");
$this->assertSession()
->pageTextContains("The context $context_name has been saved");
$new_contents = $this->drupalGet($url . '?_format=jsonld');
$json = \GuzzleHttp\json_decode($new_contents, TRUE);
$this->assertEquals(
'Test Node',
$json['@graph'][0]['http://purl.org/dc/terms/title'][0]['@value'],
'Missing title value'
);
$this->assertEquals(
"$url?_format=jsonld",
$json['@graph'][0]['http://www.w3.org/2002/07/owl#sameAs'][0]['@id'],
'Missing alter added predicate.'
);
$this->drupalGet("admin/structure/context/$context_name");
// Change to a random URL.
$this->getSession()->getPage()
->fillField("Drupal URI predicate", "http://example.org/first/second");
$this->getSession()->getPage()->pressButton("Save and continue");
$this->assertSession()
->pageTextContains("The context $context_name has been saved");
$new_contents = $this->drupalGet($url . '?_format=jsonld');
$json = \GuzzleHttp\json_decode($new_contents, TRUE);
$this->assertEquals(
'Test Node',
$json['@graph'][0]['http://purl.org/dc/terms/title'][0]['@value'],
'Missing title value'
);
$this->assertArrayNotHasKey('http://www.w3.org/2002/07/owl#sameAs',
$json['@graph'][0], 'Still has old predicate');
$this->assertEquals(
"$url?_format=jsonld",
$json['@graph'][0]['http://example.org/first/second'][0]['@id'],
'Missing alter added predicate.'
);
}
/**
* @covers \Drupal\islandora\Plugin\ContextReaction\MappingUriPredicateReaction
*/
public function testMappingReactionForMedia() {
$account = $this->drupalCreateUser([
'create media',
'view media',
'administer contexts',
]);
$this->drupalLogin($account);
$context_name = 'test';
$reaction_id = 'islandora_map_uri_predicate';
list($file, $media) = $this->makeMediaAndFile($account);
$media_url = $media->url('canonical', ['absolute' => TRUE]);
$file_url = $file->url('canonical', ['absolute' => TRUE]);
$this->drupalGet($media_url);
$this->assertSession()->statusCodeEquals(200);
$contents = $this->drupalGet($media_url . '?_format=jsonld');
$this->assertSession()->statusCodeEquals(200);
$json = \GuzzleHttp\json_decode($contents, TRUE);
$this->assertEquals(
"$media_url?_format=jsonld",
$json['@graph'][0]['@id'],
'Swapped file and media urls when not configured'
);
$this->assertArrayNotHasKey('http://www.iana.org/assignments/relation/describedby',
$json['@graph'][0], 'Has predicate when not configured');
$this->createContext('Test', $context_name);
$this->drupalGet("admin/structure/context/$context_name/reaction/add/$reaction_id");
$this->assertSession()->statusCodeEquals(200);
// Use an existing prefix.
$this->getSession()->getPage()
->fillField("Drupal URI predicate", "iana:describedby");
$this->getSession()->getPage()->pressButton("Save and continue");
$this->assertSession()
->pageTextContains("The context $context_name has been saved");
$new_contents = $this->drupalGet($media_url . '?_format=jsonld');
$json = \GuzzleHttp\json_decode($new_contents, TRUE);
$this->assertEquals(
"$media_url?_format=jsonld",
$json['@graph'][0]['http://www.iana.org/assignments/relation/describedby'][0]['@id'],
'Missing alter added predicate.'
);
$this->assertEquals(
$file_url,
$json['@graph'][0]['@id'],
'Alter did not swap "@id" of media with file url.'
);
}
}
Loading…
Cancel
Save