Browse Source

merge 8.x-1.x

pull/793/head
Eli Zoller 4 years ago
parent
commit
c1d719bc02
  1. 8
      .travis.yml
  2. 6
      composer.json
  3. 75
      islandora.install
  4. 3
      modules/islandora_breadcrumbs/src/IslandoraBreadcrumbBuilder.php
  5. 14
      modules/islandora_breadcrumbs/tests/src/Functional/BreadcrumbsTest.php
  6. 25
      modules/islandora_core_feature/config/install/field.storage.media.field_media_document.yml
  7. 2
      modules/islandora_iiif/islandora_iiif.routing.yml
  8. 42
      modules/islandora_iiif/src/Plugin/views/style/IIIFManifest.php
  9. 22
      modules/islandora_text_extraction/islandora_text_extraction.module
  10. 8
      modules/islandora_text_extraction/islandora_text_extraction.services.yml
  11. 65
      modules/islandora_text_extraction/src/SearchReindexer.php
  12. 2
      modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_mime_type.yml
  13. 51
      modules/islandora_text_extraction_defaults/config/install/rdf.mapping.media.extracted_text.yml
  14. 10
      src/EventSubscriber/LinkHeaderSubscriber.php
  15. 31
      src/Flysystem/Fedora.php
  16. 127
      src/Form/IslandoraSettingsForm.php
  17. 14
      src/IslandoraUtils.php
  18. 2
      src/Plugin/Action/AbstractGenerateDerivative.php
  19. 2
      src/Plugin/Condition/ContentEntityType.php
  20. 10
      src/Plugin/Condition/EntityBundle.php
  21. 2
      src/Plugin/Condition/FileUsesFilesystem.php
  22. 2
      src/Plugin/Condition/MediaHasMimetype.php
  23. 2
      src/Plugin/Condition/MediaHasTerm.php
  24. 2
      src/Plugin/Condition/MediaUsesFilesystem.php
  25. 6
      src/Plugin/Condition/NodeHadNamespace.php
  26. 5
      src/Plugin/Condition/NodeHasParent.php
  27. 2
      src/Plugin/Condition/NodeHasTerm.php
  28. 11
      src/Plugin/Condition/NodeIsPublished.php
  29. 2
      src/Plugin/Condition/ParentNodeHasTerm.php
  30. 3
      src/Plugin/ContextReaction/JsonldTypeAlterReaction.php
  31. 5
      src/StompFactory.php
  32. 6
      tests/src/Functional/EntityBundleTest.php
  33. 26
      tests/src/Functional/IslandoraSettingsFormTest.php
  34. 6
      tests/src/Functional/JsonldTypeAlterReactionTest.php

8
.travis.yml

@ -27,12 +27,12 @@ install:
- $SCRIPT_DIR/travis_setup_drupal.sh - $SCRIPT_DIR/travis_setup_drupal.sh
- git -C "$TRAVIS_BUILD_DIR" checkout -b travis-testing - git -C "$TRAVIS_BUILD_DIR" checkout -b travis-testing
- cd $DRUPAL_DIR; - cd $DRUPAL_DIR;
- chmod -R u+w web/sites/default
- COMPOSER_MEMORY_LIMIT=-1 php -d memory_limit=-1 $COMPOSER_PATH config repositories.local path "$TRAVIS_BUILD_DIR" - COMPOSER_MEMORY_LIMIT=-1 php -d memory_limit=-1 $COMPOSER_PATH config repositories.local path "$TRAVIS_BUILD_DIR"
- COMPOSER_MEMORY_LIMIT=-1 php -d memory_limit=-1 $COMPOSER_PATH require "islandora/islandora:dev-travis-testing as dev-8.x-1.x" --prefer-source --update-with-dependencies - COMPOSER_MEMORY_LIMIT=-1 php -d memory_limit=-1 $COMPOSER_PATH require "islandora/islandora:dev-travis-testing as dev-8.x-1.x" --prefer-source --update-with-dependencies
- cd web; drush --uri=127.0.0.1:8282 en -y islandora - cd web
- (drush -y --uri=127.0.0.1:8282 en islandora_core_feature; drush -y --uri=127.0.0.1:8282 fim islandora_core_feature) - drush --uri=127.0.0.1:8282 en -y islandora_audio islandora_breadcrumbs islandora_iiif islandora_image islandora_video islandora_text_extraction_defaults
- drush -y --uri=127.0.0.1:8282 en islandora_audio islandora_breadcrumbs islandora_iiif islandora_image islandora_video - drush --uri=127.0.0.1:8282 fim -y islandora_core_feature,islandora_text_extraction_defaults
- (drush -y --uri=127.0.0.1:8282 en islandora_text_extraction_defaults; drush -y --uri=127.0.0.1:8282 fim islandora_text_extraction_defaults)
script: script:
- $SCRIPT_DIR/travis_scripts.sh - $SCRIPT_DIR/travis_scripts.sh

6
composer.json

@ -18,13 +18,13 @@
"drupal/search_api": "~1.8", "drupal/search_api": "~1.8",
"islandora/jsonld": "dev-8.x-1.x", "islandora/jsonld": "dev-8.x-1.x",
"stomp-php/stomp-php": "4.*", "stomp-php/stomp-php": "4.*",
"drupal/jwt": "1.0.0-alpha6", "drupal/jwt": "^1.0.0-beta5",
"drupal/filehash": "^1.1", "drupal/filehash": "^1.1",
"drupal/prepopulate" : "^2.2", "drupal/prepopulate" : "^2.2",
"drupal/eva" : "^2.0", "drupal/eva" : "^2.0",
"drupal/features" : "^3.7", "drupal/features" : "^3.7",
"drupal/migrate_plus" : "^4.1", "drupal/migrate_plus" : "^5.1",
"drupal/migrate_tools" : "^4.1", "drupal/migrate_tools" : "^5.0",
"drupal/migrate_source_csv" : "^2.1", "drupal/migrate_source_csv" : "^2.1",
"drupal/token" : "^1.3", "drupal/token" : "^1.3",
"drupal/flysystem" : "^1.0", "drupal/flysystem" : "^1.0",

75
islandora.install

@ -5,42 +5,6 @@
* Install/update hook implementations. * Install/update hook implementations.
*/ */
/**
* Implements hook_schema().
*/
function islandora_schema() {
$schema = [];
$schema['islandora_version_count'] = [
'description' => 'Keeps track of the number of changes to an entity',
'fields' => [
'id' => [
'description' => 'Autoincrementing id for record',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
],
'uuid' => [
'description' => 'UUID for an entity',
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'unique' => TRUE,
],
'count' => [
'description' => 'Number of times an entity has been updated.',
'type' => 'int',
'unsigned' => TRUE,
'default' => 0,
],
],
'primary key' => ['id'],
'unique keys' => [
'uuid' => ['uuid'],
],
];
return $schema;
}
/** /**
* Delete the 'delete_media' action we used to provide, if it exists. * Delete the 'delete_media' action we used to provide, if it exists.
* *
@ -52,3 +16,42 @@ function islandora_update_8001(&$sandbox) {
$action->delete(); $action->delete();
} }
} }
/**
* Replaces 'entity_bundle' conditions with 'islandora_entity_bundle'.
*
* This prevents plugin naming collisions between islandora and ctools.
*/
function islandora_update_8002(&$sandbox) {
// Find contexts that have the old 'entity_bundle' condition.
$results = \Drupal::entityQuery('context')->condition('conditions.entity_bundle.id', 'entity_bundle')->execute();
if (empty($results)) {
return;
}
// Set each context config to use 'islandora_entity_bundle' instead.
foreach ($results as $result) {
$config = \Drupal::configFactory()->getEditable("context.context.$result");
$condition = $config->get('conditions.entity_bundle');
$condition['id'] = 'islandora_entity_bundle';
$config->set('conditions.islandora_entity_bundle', $condition);
$config->clear('conditions.entity_bundle');
$config->save();
}
// Force drupal to reload the config.
\Drupal::service('plugin.manager.condition')->clearCachedDefinitions();
}
/**
* Deletes the islandora_version_count table.
*
* We never implemented the functionality.
*/
function islandora_update_8003(&$sandbox) {
\Drupal::service('database')
->schema()
->dropTable('islandora_version_count');
}

3
modules/islandora_breadcrumbs/src/IslandoraBreadcrumbBuilder.php

@ -107,7 +107,8 @@ class IslandoraBreadcrumbBuilder implements BreadcrumbBuilderInterface {
// Find the next in the chain, if there are any. // Find the next in the chain, if there are any.
if ($entity->hasField($this->config->get('referenceField')) && if ($entity->hasField($this->config->get('referenceField')) &&
!$entity->get($this->config->get('referenceField'))->isEmpty()) { !$entity->get($this->config->get('referenceField'))->isEmpty() &&
$entity->get($this->config->get('referenceField'))->entity instanceof EntityInterface) {
$this->walkMembership($entity->get($this->config->get('referenceField'))->entity, $crumbs); $this->walkMembership($entity->get($this->config->get('referenceField'))->entity, $crumbs);
} }
} }

14
modules/islandora_breadcrumbs/tests/src/Functional/BreadcrumbsTest.php

@ -2,6 +2,7 @@
namespace Drupal\Tests\islandora_breadcrumbs\Functional; namespace Drupal\Tests\islandora_breadcrumbs\Functional;
use Drupal\Core\Url;
use Drupal\Tests\islandora\Functional\IslandoraFunctionalTestBase; use Drupal\Tests\islandora\Functional\IslandoraFunctionalTestBase;
use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait; use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait;
@ -85,6 +86,14 @@ class BreadcrumbsTest extends IslandoraFunctionalTestBase {
]); ]);
$this->nodeD->set('field_member_of', [$this->nodeC->id()]); $this->nodeD->set('field_member_of', [$this->nodeC->id()]);
$this->nodeD->save(); $this->nodeD->save();
$this->drupalPlaceBlock(
'system_breadcrumb_block',
[
'region' => 'content',
'theme' => $this->config('system.theme')->get('default'),
]
);
} }
/** /**
@ -92,9 +101,10 @@ class BreadcrumbsTest extends IslandoraFunctionalTestBase {
*/ */
public function testDefaults() { public function testDefaults() {
$breadcrumbs = [ $breadcrumbs = [
$this->nodeC->toUrl()->toString() => $this->nodeC->label(), Url::fromRoute('<front>')->toString() => 'Home',
$this->nodeB->toUrl()->toString() => $this->nodeB->label(),
$this->nodeA->toUrl()->toString() => $this->nodeA->label(), $this->nodeA->toUrl()->toString() => $this->nodeA->label(),
$this->nodeB->toUrl()->toString() => $this->nodeB->label(),
$this->nodeC->toUrl()->toString() => $this->nodeC->label(),
]; ];
$this->assertBreadcrumb($this->nodeD->toUrl()->toString(), $breadcrumbs); $this->assertBreadcrumb($this->nodeD->toUrl()->toString(), $breadcrumbs);

25
modules/islandora_core_feature/config/install/field.storage.media.field_media_document.yml

@ -0,0 +1,25 @@
langcode: en
status: true
dependencies:
enforced:
module:
- islandora_core_feature
module:
- file
- media
id: media.field_media_document
field_name: field_media_document
entity_type: media
type: file
settings:
display_field: false
display_default: false
uri_scheme: fedora
target_type: file
module: file
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false

2
modules/islandora_iiif/islandora_iiif.routing.yml

@ -4,6 +4,6 @@ islandora_iiif.islandora_iiif_config_form:
_form: '\Drupal\islandora_iiif\Form\IslandoraIIIFConfigForm' _form: '\Drupal\islandora_iiif\Form\IslandoraIIIFConfigForm'
_title: 'IslandoraIIIFConfigForm' _title: 'IslandoraIIIFConfigForm'
requirements: requirements:
_permission: 'access administration pages' _permission: 'administer site configuration'
options: options:
_admin_route: TRUE _admin_route: TRUE

42
modules/islandora_iiif/src/Plugin/views/style/IIIFManifest.php

@ -12,6 +12,7 @@ use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\File\FileSystem; use Drupal\Core\File\FileSystem;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ServerException; use GuzzleHttp\Exception\ServerException;
/** /**
@ -190,28 +191,25 @@ class IIIFManifest extends StylePluginBase {
$width = $resource['width']; $width = $resource['width'];
$height = $resource['height']; $height = $resource['height'];
} }
catch (ClientException $e) { catch (ClientException | ServerException | ConnectException $e) {
} // If we couldn't get the info.json from IIIF
catch (ServerException $e) { // try seeing if we can get it from Drupal.
} if (empty($width) || empty($height)) {
// Get the image properties so we know the image width/height.
// If we couldn't get the info.json from IIIF $properties = $image->getProperties();
// try seeing if we can get it from Drupal. $width = isset($properties['width']) ? $properties['width'] : 0;
if (empty($width) || empty($height)) { $height = isset($properties['height']) ? $properties['height'] : 0;
// Get the image properties so we know the image width/height.
$properties = $image->getProperties(); // If this is a TIFF AND we don't know the width/height
$width = isset($properties['width']) ? $properties['width'] : 0; // see if we can get the image size via PHP's core function.
$height = isset($properties['height']) ? $properties['height'] : 0; if ($mime_type === 'image/tiff' && !$width || !$height) {
$uri = $image->entity->getFileUri();
// If this is a TIFF AND we don't know the width/height $path = $this->fileSystem->realpath($uri);
// see if we can get the image size via PHP's core function. $image_size = getimagesize($path);
if ($mime_type === 'image/tiff' && !$width || !$height) { if ($image_size) {
$uri = $image->entity->getFileUri(); $width = $image_size[0];
$path = $this->fileSystem->realpath($uri); $height = $image_size[1];
$image_size = getimagesize($path); }
if ($image_size) {
$width = $image_size[0];
$height = $image_size[1];
} }
} }
} }

22
modules/islandora_text_extraction/islandora_text_extraction.module

@ -44,6 +44,28 @@ function islandora_text_extraction_media_presave(MediaInterface $media) {
} }
} }
/**
* Implements hook_media_insert().
*/
function islandora_text_extraction_media_insert(MediaInterface $media) {
if ($media->bundle() != 'extracted_text') {
return;
}
\Drupal::service('islandora_text_extraction.search_reindexer')->reindexParent($media);
}
/**
* Implements hook_media_update().
*/
function islandora_text_extraction_media_update(MediaInterface $media) {
if ($media->bundle() != 'extracted_text') {
return;
}
\Drupal::service('islandora_text_extraction.search_reindexer')->reindexParent($media);
}
/** /**
* Implements hook_form_form_id_alter(). * Implements hook_form_form_id_alter().
*/ */

8
modules/islandora_text_extraction/islandora_text_extraction.services.yml

@ -0,0 +1,8 @@
services:
logger.channel.islandora_text_extraction:
parent: logger.channel_base
arguments: ['islandora_text_extraction']
islandora_text_extraction.search_reindexer:
class: Drupal\islandora_text_extraction\SearchReindexer
arguments: ['@islandora.utils', '@logger.channel.islandora_text_extraction']

65
modules/islandora_text_extraction/src/SearchReindexer.php

@ -0,0 +1,65 @@
<?php
namespace Drupal\islandora_text_extraction;
use Drupal\islandora\IslandoraUtils;
use Drupal\media\MediaInterface;
use Psr\Log\LoggerInterface;
/**
* Creates a GeminiClient as a Drupal service.
*
* @package Drupal\islandora
*/
class SearchReindexer {
/**
* Islandora Utils.
*
* @var \Drupal\islandora\IslandoraUtils
*/
protected $utils;
/**
* Logger.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Constructor.
*
* @param \Drupal\islandora\IslandoraUtils $utils
* Islandora utils.
* @param \Psr\Log\LoggerInterface $logger
* The logger channel.
*/
public function __construct(IslandoraUtils $utils, LoggerInterface $logger) {
$this->utils = $utils;
$this->logger = $logger;
}
/**
* Reindexes parent node for a media. No-op if parent does not exist.
*
* @param Drupal\media\MediaInterface $media
* Media whose parent you want to reindex.
*/
public function reindexParent(MediaInterface $media) {
$parent = $this->utils->getParentNode($media);
if ($parent === NULL) {
return;
}
$this->logger->debug(
"Re-indexing parent node @nid for extracted text @mid using the search_api",
['@nid' => $parent->id(), '@mid' => $media->id()]
);
$parent->original = $parent;
search_api_entity_update($parent);
}
}

2
modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_mime_type.yml

@ -11,7 +11,7 @@ bundle: extracted_text
label: 'MIME type' label: 'MIME type'
description: '' description: ''
required: false required: false
translatable: true translatable: false
default_value: default_value:
- -
value: text/plain value: text/plain

51
modules/islandora_text_extraction_defaults/config/install/rdf.mapping.media.extracted_text.yml

@ -0,0 +1,51 @@
langcode: en
status: true
dependencies:
config:
- media.type.extracted_text
enforced:
module:
- islandora_text_extraction_defaults
module:
- media
id: media.extracted_text
targetEntityType: media
bundle: extracted_text
types:
- 'pcdm:File'
fieldMappings:
name:
properties:
- 'dcterms:title'
- 'rdf:label'
created:
properties:
- 'schema:dateCreated'
datatype_callback:
callable: 'Drupal\rdf\CommonDataConverter::dateIso8601Value'
changed:
properties:
- 'schema:dateModified'
datatype_callback:
callable: 'Drupal\rdf\CommonDataConverter::dateIso8601Value'
uid:
properties:
- 'schema:author'
mapping_type: rel
field_mime_type:
properties:
- 'ebucore:hasMimeType'
field_media_of:
properties:
- 'pcdm:fileOf'
mapping_type: rel
field_original_name:
properties:
- 'premis3:originalName'
field_tags:
properties:
- 'schema:additionalType'
mapping_type: rel
field_file_size:
properties:
- 'premis:hasSize'

10
src/EventSubscriber/LinkHeaderSubscriber.php

@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\islandora\IslandoraUtils; use Drupal\islandora\IslandoraUtils;
use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -205,7 +206,14 @@ abstract class LinkHeaderSubscriber implements EventSubscriberInterface {
// Headers are subject to an access check. // Headers are subject to an access check.
if ($referencedEntity->access('view')) { if ($referencedEntity->access('view')) {
$entity_url = $this->utils->getEntityUrl($referencedEntity); try {
$entity_url = $this->utils->getEntityUrl($referencedEntity);
}
catch (UndefinedLinkTemplateException $e) {
// Not all referencable entities can generate canonical URLs, for
// example: block entities.
continue;
}
// Taxonomy terms are written out as // Taxonomy terms are written out as
// <url>; rel="tag"; title="Tag Name" // <url>; rel="tag"; title="Tag Name"

31
src/Flysystem/Fedora.php

@ -10,6 +10,7 @@ use Drupal\flysystem\Plugin\FlysystemPluginInterface;
use Drupal\flysystem\Plugin\FlysystemUrlTrait; use Drupal\flysystem\Plugin\FlysystemUrlTrait;
use Drupal\islandora\Flysystem\Adapter\FedoraAdapter; use Drupal\islandora\Flysystem\Adapter\FedoraAdapter;
use Drupal\jwt\Authentication\Provider\JwtAuth; use Drupal\jwt\Authentication\Provider\JwtAuth;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\HandlerStack; use GuzzleHttp\HandlerStack;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use Islandora\Chullo\IFedoraApi; use Islandora\Chullo\IFedoraApi;
@ -123,20 +124,28 @@ class Fedora implements FlysystemPluginInterface, ContainerFactoryPluginInterfac
*/ */
public function ensure($force = FALSE) { public function ensure($force = FALSE) {
// Check fedora root for sanity. // Check fedora root for sanity.
$response = $this->fedora->getResourceHeaders(''); try {
$response = $this->fedora->getResourceHeaders('');
if ($response->getStatusCode() != 200) { $statusCode = $response->getStatusCode();
return [[ $message = '%url returned %status';
'severity' => RfcLogLevel::ERROR, }
'message' => '%url returned %status', catch (ConnectException $e) {
'context' => [ // Fedora is unavailable.
'%url' => $this->fedora->getBaseUri(), $message = '%url is unavailable, cannot connect.';
'%status' => $response->getStatusCode(), $statusCode = 500;
}
if ($statusCode != 200) {
return [
[
'severity' => RfcLogLevel::ERROR,
'message' => $message,
'context' => [
'%url' => $this->fedora->getBaseUri(),
'%status' => $statusCode,
],
], ],
],
]; ];
} }
return []; return [];
} }

127
src/Form/IslandoraSettingsForm.php

@ -22,10 +22,22 @@ class IslandoraSettingsForm extends ConfigFormBase {
const CONFIG_NAME = 'islandora.settings'; const CONFIG_NAME = 'islandora.settings';
const BROKER_URL = 'broker_url'; const BROKER_URL = 'broker_url';
const BROKER_USER = 'broker_user';
const BROKER_PASSWORD = 'broker_password';
const JWT_EXPIRY = 'jwt_expiry'; const JWT_EXPIRY = 'jwt_expiry';
const GEMINI_URL = 'gemini_url'; const GEMINI_URL = 'gemini_url';
const GEMINI_PSEUDO = 'gemini_pseudo_bundles'; const GEMINI_PSEUDO = 'gemini_pseudo_bundles';
const FEDORA_URL = 'fedora_url'; const FEDORA_URL = 'fedora_url';
const TIME_INTERVALS = [
'sec',
'second',
'min',
'minute',
'hour',
'week',
'month',
'year',
];
/** /**
* To list the available bundle types. * To list the available bundle types.
@ -34,6 +46,13 @@ class IslandoraSettingsForm extends ConfigFormBase {
*/ */
private $entityTypeBundleInfo; private $entityTypeBundleInfo;
/**
* The saved password (if set).
*
* @var string
*/
private $brokerPassword;
/** /**
* Constructs a \Drupal\system\ConfigFormBase object. * Constructs a \Drupal\system\ConfigFormBase object.
* *
@ -45,6 +64,7 @@ class IslandoraSettingsForm extends ConfigFormBase {
public function __construct(ConfigFactoryInterface $config_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info) { public function __construct(ConfigFactoryInterface $config_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
$this->setConfigFactory($config_factory); $this->setConfigFactory($config_factory);
$this->entityTypeBundleInfo = $entity_type_bundle_info; $this->entityTypeBundleInfo = $entity_type_bundle_info;
$this->brokerPassword = $this->config(self::CONFIG_NAME)->get(self::BROKER_PASSWORD);
} }
/** /**
@ -79,17 +99,53 @@ class IslandoraSettingsForm extends ConfigFormBase {
public function buildForm(array $form, FormStateInterface $form_state) { public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config(self::CONFIG_NAME); $config = $this->config(self::CONFIG_NAME);
$form[self::BROKER_URL] = [ $form['broker_info'] = [
'#type' => 'details',
'#title' => $this->t('Broker'),
'#open' => TRUE,
];
$form['broker_info'][self::BROKER_URL] = [
'#type' => 'textfield', '#type' => 'textfield',
'#title' => $this->t('Broker URL'), '#title' => $this->t('URL'),
'#default_value' => $config->get(self::BROKER_URL), '#default_value' => $config->get(self::BROKER_URL),
]; ];
$broker_user = $config->get(self::BROKER_USER);
$form['broker_info']['provide_user_creds'] = [
'#type' => 'checkbox',
'#title' => $this->t('Provide user identification'),
'#default_value' => $broker_user ? TRUE : FALSE,
];
$state_selector = 'input[name="provide_user_creds"]';
$form['broker_info'][self::BROKER_USER] = [
'#type' => 'textfield',
'#title' => $this->t('User'),
'#default_value' => $broker_user,
'#states' => [
'visible' => [
$state_selector => ['checked' => TRUE],
],
'required' => [
$state_selector => ['checked' => TRUE],
],
],
];
$form['broker_info'][self::BROKER_PASSWORD] = [
'#type' => 'password',
'#title' => $this->t('Password'),
'#description' => $this->t('If this field is left blank and the user is filled out, the current password will not be changed.'),
'#states' => [
'visible' => [
$state_selector => ['checked' => TRUE],
],
],
];
$form[self::JWT_EXPIRY] = [ $form[self::JWT_EXPIRY] = [
'#type' => 'textfield', '#type' => 'textfield',
'#title' => $this->t('JWT Expiry'), '#title' => $this->t('JWT Expiry'),
'#default_value' => $config->get(self::JWT_EXPIRY), '#default_value' => $config->get(self::JWT_EXPIRY),
'#description' => 'Eg: 60, "2 days", "10h", "7d". A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default ("120" is equal to "120ms").', '#description' => $this->t('A positive time interval expression. Eg: "60 secs", "2 days", "10 hours", "7 weeks". Be sure you provide the time units (@unit), plurals are accepted.',
['@unit' => implode(self::TIME_INTERVALS, ", ")]
),
]; ];
$form[self::GEMINI_URL] = [ $form[self::GEMINI_URL] = [
@ -142,14 +198,24 @@ class IslandoraSettingsForm extends ConfigFormBase {
public function validateForm(array &$form, FormStateInterface $form_state) { public function validateForm(array &$form, FormStateInterface $form_state) {
// Validate broker url by actually connecting with a stomp client. // Validate broker url by actually connecting with a stomp client.
$brokerUrl = $form_state->getValue(self::BROKER_URL); $brokerUrl = $form_state->getValue(self::BROKER_URL);
// Attempt to subscribe to a dummy queue. // Attempt to subscribe to a dummy queue.
try { try {
$stomp = new StatefulStomp( $client = new Client($brokerUrl);
new Client( if ($form_state->getValue('provide_user_creds')) {
$brokerUrl $broker_password = $form_state->getValue(self::BROKER_PASSWORD);
) // When stored password type fields aren't rendered again.
); if (!$broker_password) {
// Use the stored password if it exists.
if (!$this->brokerPassword) {
$form_state->setErrorByName(self::BROKER_PASSWORD, $this->t('A password must be supplied'));
}
else {
$broker_password = $this->brokerPassword;
}
}
$client->setLogin($form_state->getValue(self::BROKER_USER), $broker_password);
}
$stomp = new StatefulStomp($client);
$stomp->subscribe('dummy-queue-for-validation'); $stomp->subscribe('dummy-queue-for-validation');
$stomp->unsubscribe(); $stomp->unsubscribe();
} }
@ -165,7 +231,8 @@ class IslandoraSettingsForm extends ConfigFormBase {
} }
// Validate jwt expiry as a valid time string. // Validate jwt expiry as a valid time string.
$expiry = $form_state->getValue(self::JWT_EXPIRY); $expiry = trim($form_state->getValue(self::JWT_EXPIRY));
$expiry = strtolower($expiry);
if (strtotime($expiry) === FALSE) { if (strtotime($expiry) === FALSE) {
$form_state->setErrorByName( $form_state->setErrorByName(
self::JWT_EXPIRY, self::JWT_EXPIRY,
@ -175,6 +242,28 @@ class IslandoraSettingsForm extends ConfigFormBase {
) )
); );
} }
elseif (substr($expiry, 0, 1) == "-") {
$form_state->setErrorByName(
self::JWT_EXPIRY,
$this->t('Time or interval expression cannot be negative')
);
}
elseif (intval($expiry) === 0) {
$form_state->setErrorByName(
self::JWT_EXPIRY,
$this->t('No numeric interval specified, for example "1 day"')
);
}
else {
if (!preg_match("/\b(" . implode(self::TIME_INTERVALS, "|") . ")s?\b/", $expiry)) {
$form_state->setErrorByName(
self::JWT_EXPIRY,
$this->t("No time interval found, please include one of (@int). Plurals are also accepted.",
['@int' => implode(self::TIME_INTERVALS, ", ")]
)
);
}
}
// Needed for the elseif below. // Needed for the elseif below.
$pseudo_types = array_filter($form_state->getValue(self::GEMINI_PSEUDO)); $pseudo_types = array_filter($form_state->getValue(self::GEMINI_PSEUDO));
@ -224,6 +313,22 @@ class IslandoraSettingsForm extends ConfigFormBase {
$pseudo_types = array_filter($form_state->getValue(self::GEMINI_PSEUDO)); $pseudo_types = array_filter($form_state->getValue(self::GEMINI_PSEUDO));
$broker_password = $form_state->getValue(self::BROKER_PASSWORD);
// If there's no user set delete what may have been here before as password
// fields will also be blank.
if (!$form_state->getValue('provide_user_creds')) {
$config->clear(self::BROKER_USER);
$config->clear(self::BROKER_PASSWORD);
}
else {
$config->set(self::BROKER_USER, $form_state->getValue(self::BROKER_USER));
// If the password has changed update it as well.
if ($broker_password && $broker_password != $this->brokerPassword) {
$config->set(self::BROKER_PASSWORD, $broker_password);
}
}
$config $config
->set(self::BROKER_URL, $form_state->getValue(self::BROKER_URL)) ->set(self::BROKER_URL, $form_state->getValue(self::BROKER_URL))
->set(self::JWT_EXPIRY, $form_state->getValue(self::JWT_EXPIRY)) ->set(self::JWT_EXPIRY, $form_state->getValue(self::JWT_EXPIRY))

14
src/IslandoraUtils.php

@ -563,15 +563,17 @@ class IslandoraUtils {
* *
* @return string * @return string
* The entity URL. * The entity URL.
*
* @throws \Drupal\Core\Entity\Exception\UndefinedLinkTemplateException
* Thrown if the given entity does not specify a "canonical" template.
* @throws \Drupal\Core\Entity\EntityMalformedException
*/ */
public function getEntityUrl(EntityInterface $entity) { public function getEntityUrl(EntityInterface $entity) {
$undefined = $this->languageManager->getLanguage('und'); $undefined = $this->languageManager->getLanguage('und');
$entity_type = $entity->getEntityTypeId(); return $entity->toUrl('canonical', [
return Url::fromRoute( 'absolute' => TRUE,
"entity.$entity_type.canonical", 'language' => $undefined,
[$entity_type => $entity->id()], ])->toString();
['absolute' => TRUE, 'language' => $undefined]
)->toString();
} }
/** /**

2
src/Plugin/Action/AbstractGenerateDerivative.php

@ -160,7 +160,7 @@ class AbstractGenerateDerivative extends EmitEvent {
// Find the term for the derivative and use it to set the destination url // Find the term for the derivative and use it to set the destination url
// in the data array. // in the data array.
$derivative_term = $this->utils->getTermForUri($this->configuration['derivative_term_uri']); $derivative_term = $this->utils->getTermForUri($this->configuration['derivative_term_uri']);
if (!$source_term) { if (!$derivative_term) {
throw new \RuntimeException("Could not locate derivative term with uri" . $this->configuration['derivative_term_uri'], 500); throw new \RuntimeException("Could not locate derivative term with uri" . $this->configuration['derivative_term_uri'], 500);
} }

2
src/Plugin/Condition/ContentEntityType.php

@ -11,7 +11,7 @@ use Drupal\Core\Form\FormStateInterface;
* @Condition( * @Condition(
* id = "content_entity_type", * id = "content_entity_type",
* label = @Translation("Content Entity Type"), * label = @Translation("Content Entity Type"),
* context = { * context_definitions = {
* "node" = @ContextDefinition("entity:node", required = FALSE, label = @Translation("Node")), * "node" = @ContextDefinition("entity:node", required = FALSE, label = @Translation("Node")),
* "media" = @ContextDefinition("entity:media", required = FALSE, label = @Translation("Media")), * "media" = @ContextDefinition("entity:media", required = FALSE, label = @Translation("Media")),
* "file" = @ContextDefinition("entity:file", required = FALSE, label = @Translation("File")), * "file" = @ContextDefinition("entity:file", required = FALSE, label = @Translation("File")),

10
src/Plugin/Condition/EntityBundle.php

@ -8,10 +8,12 @@ use Drupal\Core\Form\FormStateInterface;
/** /**
* Provides a 'Entity Bundle' condition. * Provides a 'Entity Bundle' condition.
* *
* Namespaced to avoid conflict with ctools entity_bundle plugin.
*
* @Condition( * @Condition(
* id = "entity_bundle", * id = "islandora_entity_bundle",
* label = @Translation("Entity Bundle"), * label = @Translation("Entity Bundle"),
* context = { * context_definitions = {
* "node" = @ContextDefinition("entity:node", required = FALSE, label = @Translation("Node")), * "node" = @ContextDefinition("entity:node", required = FALSE, label = @Translation("Node")),
* "media" = @ContextDefinition("entity:media", required = FALSE, label = @Translation("Media")), * "media" = @ContextDefinition("entity:media", required = FALSE, label = @Translation("Media")),
* "taxonomy_term" = @ContextDefinition("entity:taxonomy_term", required = FALSE, label = @Translation("Term")) * "taxonomy_term" = @ContextDefinition("entity:taxonomy_term", required = FALSE, label = @Translation("Term"))
@ -61,11 +63,11 @@ class EntityBundle extends ConditionPluginBase {
if ($context->hasContextValue()) { if ($context->hasContextValue()) {
$entity = $context->getContextValue(); $entity = $context->getContextValue();
if (!empty($this->configuration['bundles'][$entity->bundle()])) { if (!empty($this->configuration['bundles'][$entity->bundle()])) {
return !$this->isNegated(); return TRUE;
} }
} }
} }
return $this->isNegated(); return FALSE;
} }
/** /**

2
src/Plugin/Condition/FileUsesFilesystem.php

@ -16,7 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @Condition( * @Condition(
* id = "file_uses_filesystem", * id = "file_uses_filesystem",
* label = @Translation("File uses filesystem"), * label = @Translation("File uses filesystem"),
* context = { * context_definitions = {
* "file" = @ContextDefinition("entity:file", required = TRUE , label = @Translation("file")) * "file" = @ContextDefinition("entity:file", required = TRUE , label = @Translation("file"))
* } * }
* ) * )

2
src/Plugin/Condition/MediaHasMimetype.php

@ -152,7 +152,7 @@ class MediaHasMimetype extends ConditionPluginBase implements ContainerFactoryPl
foreach ($media as $medium) { foreach ($media as $medium) {
$file = $this->mediaSource->getSourceFile($medium); $file = $this->mediaSource->getSourceFile($medium);
if (in_array($file->getMimeType(), $mimetypes)) { if (in_array($file->getMimeType(), $mimetypes)) {
return $this->isNegated() ? FALSE : TRUE; return TRUE;
} }
} }
} }

2
src/Plugin/Condition/MediaHasTerm.php

@ -8,7 +8,7 @@ namespace Drupal\islandora\Plugin\Condition;
* @Condition( * @Condition(
* id = "media_has_term", * id = "media_has_term",
* label = @Translation("Media has term with URI"), * label = @Translation("Media has term with URI"),
* context = { * context_definitions = {
* "media" = @ContextDefinition("entity:media", required = TRUE , label = @Translation("media")) * "media" = @ContextDefinition("entity:media", required = TRUE , label = @Translation("media"))
* } * }
* ) * )

2
src/Plugin/Condition/MediaUsesFilesystem.php

@ -13,7 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @Condition( * @Condition(
* id = "media_uses_filesystem", * id = "media_uses_filesystem",
* label = @Translation("Media uses filesystem"), * label = @Translation("Media uses filesystem"),
* context = { * context_definitions = {
* "media" = @ContextDefinition("entity:media", required = TRUE , label = @Translation("media")) * "media" = @ContextDefinition("entity:media", required = TRUE , label = @Translation("media"))
* } * }
* ) * )

6
src/Plugin/Condition/NodeHadNamespace.php

@ -16,7 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @Condition( * @Condition(
* id = "node_had_namespace", * id = "node_had_namespace",
* label = @Translation("Node had 7.x namespace"), * label = @Translation("Node had 7.x namespace"),
* context = { * context_definitions = {
* "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node")) * "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node"))
* } * }
* ) * )
@ -154,12 +154,12 @@ class NodeHadNamespace extends ConditionPluginBase implements ContainerFactoryPl
foreach ($registered_namespaces as &$registered_namespace) { foreach ($registered_namespaces as &$registered_namespace) {
$registered_namespace = trim($registered_namespace); $registered_namespace = trim($registered_namespace);
if (in_array($namespace, $registered_namespaces)) { if (in_array($namespace, $registered_namespaces)) {
return $this->isNegated() ? FALSE : TRUE; return TRUE;
} }
} }
} }
return $this->isNegated() ? TRUE : FALSE; return FALSE;
} }
/** /**

5
src/Plugin/Condition/NodeHasParent.php

@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @Condition( * @Condition(
* id = "node_has_parent", * id = "node_has_parent",
* label = @Translation("Node has parent"), * label = @Translation("Node has parent"),
* context = { * context_definitions = {
* "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node")) * "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node"))
* } * }
* ) * )
@ -145,12 +145,13 @@ class NodeHasParent extends ConditionPluginBase implements ContainerFactoryPlugi
$nids = $field->getValue(); $nids = $field->getValue();
foreach ($nids as $nid) { foreach ($nids as $nid) {
if ($nid['target_id'] == $this->configuration['parent_nid']) { if ($nid['target_id'] == $this->configuration['parent_nid']) {
return $this->isNegated() ? FALSE : TRUE; return TRUE;
} }
} }
} }
} }
} }
return FALSE;
} }
/** /**

2
src/Plugin/Condition/NodeHasTerm.php

@ -16,7 +16,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @Condition( * @Condition(
* id = "node_has_term", * id = "node_has_term",
* label = @Translation("Node has term with URI"), * label = @Translation("Node has term with URI"),
* context = { * context_definitions = {
* "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node")) * "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node"))
* } * }
* ) * )

11
src/Plugin/Condition/NodeIsPublished.php

@ -13,7 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* @Condition( * @Condition(
* id = "node_is_published", * id = "node_is_published",
* label = @Translation("Node is published"), * label = @Translation("Node is published"),
* context = { * context_definitions = {
* "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node")) * "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node"))
* } * }
* ) * )
@ -72,11 +72,12 @@ class NodeIsPublished extends ConditionPluginBase implements ContainerFactoryPlu
if (!$node && !$this->isNegated()) { if (!$node && !$this->isNegated()) {
return FALSE; return FALSE;
} }
if ($node->isPublished() && !$this->isNegated()) { elseif (!$node) {
return TRUE; return FALSE;
}
else {
return $node->isPublished();
} }
return FALSE;
} }
/** /**

2
src/Plugin/Condition/ParentNodeHasTerm.php

@ -8,7 +8,7 @@ namespace Drupal\islandora\Plugin\Condition;
* @Condition( * @Condition(
* id = "parent_node_has_term", * id = "parent_node_has_term",
* label = @Translation("Parent node for media has term with URI"), * label = @Translation("Parent node for media has term with URI"),
* context = { * context_definitions = {
* "media" = @ContextDefinition("entity:media", required = TRUE , label = @Translation("media")) * "media" = @ContextDefinition("entity:media", required = TRUE , label = @Translation("media"))
* } * }
* ) * )

3
src/Plugin/ContextReaction/JsonldTypeAlterReaction.php

@ -44,6 +44,9 @@ class JsonldTypeAlterReaction extends NormalizerAlterReaction {
// Search for the entity in the graph. // Search for the entity in the graph.
foreach ($normalized['@graph'] as &$elem) { foreach ($normalized['@graph'] as &$elem) {
if (!is_array($elem['@type'])) {
$elem['@type'] = [$elem['@type']];
}
if ($elem['@id'] === $this->getSubjectUrl($entity)) { if ($elem['@id'] === $this->getSubjectUrl($entity)) {
foreach ($entity->get($config['source_field'])->getValue() as $type) { foreach ($entity->get($config['source_field'])->getValue() as $type) {
// If the configured field is using an entity reference, // If the configured field is using an entity reference,

5
src/StompFactory.php

@ -25,13 +25,16 @@ class StompFactory {
// Get broker url from config. // Get broker url from config.
$settings = $config->get(IslandoraSettingsForm::CONFIG_NAME); $settings = $config->get(IslandoraSettingsForm::CONFIG_NAME);
$brokerUrl = $settings->get(IslandoraSettingsForm::BROKER_URL); $brokerUrl = $settings->get(IslandoraSettingsForm::BROKER_URL);
$brokerUser = $settings->get(IslandoraSettingsForm::BROKER_USER);
// Try a sensible default if one hasn't been configured. // Try a sensible default if one hasn't been configured.
if (empty($brokerUrl)) { if (empty($brokerUrl)) {
$brokerUrl = "tcp://localhost:61613"; $brokerUrl = "tcp://localhost:61613";
} }
$client = new Client($brokerUrl); $client = new Client($brokerUrl);
if ($brokerUser) {
$client->setLogin($brokerUser, $settings->get(IslandoraSettingsForm::BROKER_PASSWORD));
}
return new StatefulStomp($client); return new StatefulStomp($client);
} }

6
tests/src/Functional/EntityBundleTest.php

@ -24,9 +24,9 @@ class EntityBundleTest extends IslandoraFunctionalTestBase {
$this->drupalLogin($account); $this->drupalLogin($account);
$this->createContext('Test', 'test'); $this->createContext('Test', 'test');
$this->addCondition('test', 'entity_bundle'); $this->addCondition('test', 'islandora_entity_bundle');
$this->getSession()->getPage()->checkField("edit-conditions-entity-bundle-bundles-test-type"); $this->getSession()->getPage()->checkField("edit-conditions-islandora-entity-bundle-bundles-test-type");
$this->getSession()->getPage()->findById("edit-conditions-entity-bundle-context-mapping-node")->selectOption("@node.node_route_context:node"); $this->getSession()->getPage()->findById("edit-conditions-islandora-entity-bundle-context-mapping-node")->selectOption("@node.node_route_context:node");
$this->getSession()->getPage()->pressButton(t('Save and continue')); $this->getSession()->getPage()->pressButton(t('Save and continue'));
$this->addPresetReaction('test', 'index', 'hello_world'); $this->addPresetReaction('test', 'index', 'hello_world');

26
tests/src/Functional/IslandoraSettingsFormTest.php

@ -58,4 +58,30 @@ class IslandoraSettingsFormTest extends IslandoraFunctionalTestBase {
} }
/**
* Test form validation for JWT expiry.
*/
public function testJwtExpiry() {
$this->drupalGet('/admin/config/islandora/core');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains("JWT Expiry");
$this->assertSession()->fieldValueEquals('edit-jwt-expiry', '+2 hour');
// Blank is not allowed.
$this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => ""], t('Save configuration'));
$this->assertSession()->pageTextContainsOnce('"" is not a valid time or interval expression.');
// Negative is not allowed.
$this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => "-2 hours"], t('Save configuration'));
$this->assertSession()->pageTextContainsOnce('Time or interval expression cannot be negative');
// Must include an integer value.
$this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => "last hour"], t('Save configuration'));
$this->assertSession()->pageTextContainsOnce('No numeric interval specified, for example "1 day"');
// Must have an accepted interval.
$this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => "1 fortnight"], t('Save configuration'));
$this->assertSession()->pageTextContainsOnce('No time interval found, please include one of');
// Test a valid setting.
$this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => "2 weeks"], t('Save configuration'));
$this->assertSession()->pageTextContainsOnce('The configuration options have been saved.');
}
} }

6
tests/src/Functional/JsonldTypeAlterReactionTest.php

@ -70,9 +70,9 @@ class JsonldTypeAlterReactionTest extends JsonldSelfReferenceReactionTest {
$this->assertSession() $this->assertSession()
->pageTextContains("The context $context_name has been saved"); ->pageTextContains("The context $context_name has been saved");
$this->addCondition('test', 'entity_bundle'); $this->addCondition('test', 'islandora_entity_bundle');
$this->getSession()->getPage()->checkField("edit-conditions-entity-bundle-bundles-test-type"); $this->getSession()->getPage()->checkField("edit-conditions-islandora-entity-bundle-bundles-test-type");
$this->getSession()->getPage()->findById("edit-conditions-entity-bundle-context-mapping-node")->selectOption("@node.node_route_context:node"); $this->getSession()->getPage()->findById("edit-conditions-islandora-entity-bundle-context-mapping-node")->selectOption("@node.node_route_context:node");
$this->getSession()->getPage()->pressButton(t('Save and continue')); $this->getSession()->getPage()->pressButton(t('Save and continue'));
// The first time a Context is saved, you need to clear the cache. // The first time a Context is saved, you need to clear the cache.

Loading…
Cancel
Save