diff --git a/.github/workflows/build-2.x.yml b/.github/workflows/build-2.x.yml
index 439395b0a..529aeff02 100644
--- a/.github/workflows/build-2.x.yml
+++ b/.github/workflows/build-2.x.yml
@@ -1,38 +1,31 @@
-# This is a basic workflow to help you get started with Actions
-
name: CI
-# Controls when the action will run.
on:
- # Triggers the workflow on push or pull request events but only for the 2.x branch
push:
branches: [ 2.x ]
pull_request:
branches: [ 2.x ]
-
- # Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
-# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
- # This workflow contains a single job called "build"
build:
- # The type of runner that the job will run on
+ env:
+ DRUPAL_VERSION: ${{ matrix.drupal-version }}
+ SCRIPT_DIR: ${{ github.workspace }}/islandora_ci
+ DRUPAL_DIR: /opt/drupal
+ PHPUNIT_FILE: ${{ github.workspace }}/build_dir/phpunit.xml
+
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.allowed_failure }}
strategy:
fail-fast: false
matrix:
- # PHP 8.1 fails - see https://github.com/Islandora/islandora/issues/887
- php-versions: ["7.4", "8.0"]
- # test-suite functional-javascript will appear to pass but will skip tests; missing chromedriver.
+ php-versions: ["8.1", "8.2", "8.3"]
test-suite: ["kernel", "functional", "functional-javascript"]
- # Not yet Drupal 10 ready - see https://github.com/Islandora/islandora/issues/888
- drupal-version: ["9.3.x", "9.4.x", "9.5.x-dev"]
+ drupal-version: ["10.2.x", "10.3.x", "10.4.x-dev"]
mysql: ["8.0"]
allowed_failure: [false]
-
name: PHP ${{ matrix.php-versions }} | drupal ${{ matrix.drupal-version }} | mysql ${{ matrix.mysql }} | test-suite ${{ matrix.test-suite }}
services:
@@ -50,17 +43,15 @@ jobs:
- 61616:61616
- 61613:61613
- # Steps represent a sequence of tasks that will be executed as part of the job
steps:
-
- # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
with:
path: build_dir
- name: Checkout islandora_ci
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
with:
repository: islandora/islandora_ci
ref: github-actions
@@ -78,15 +69,8 @@ jobs:
sudo apt-get remove -y mysql-client mysql-common
sudo apt-get install -y mysql-client
- - name: Set environment variables
- run: |
- echo "DRUPAL_VERSION=${{ matrix.drupal-version }}" >> $GITHUB_ENV
- echo "SCRIPT_DIR=$GITHUB_WORKSPACE/islandora_ci" >> $GITHUB_ENV
- echo "DRUPAL_DIR=/opt/drupal" >> $GITHUB_ENV
- echo "PHPUNIT_FILE=$GITHUB_WORKSPACE/build_dir/phpunit.xml" >> $GITHUB_ENV
-
- name: Cache Composer dependencies
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: /tmp/composer-cache
key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
@@ -111,7 +95,6 @@ jobs:
run: |
cd $DRUPAL_DIR/web
drush --uri=127.0.0.1:8282 en -y islandora_audio islandora_breadcrumbs islandora_iiif islandora_image islandora_video islandora_text_extraction_defaults
- drush --uri=127.0.0.1:8282 fim -y islandora_core_feature,islandora_text_extraction_defaults
- name: Copy PHPunit file
run: cp $PHPUNIT_FILE $DRUPAL_DIR/web/core/phpunit.xml
@@ -119,8 +102,20 @@ jobs:
- name: Test scripts
run: $SCRIPT_DIR/travis_scripts.sh
+ - name: Start chromedriver
+ if: matrix.test-suite == 'functional-javascript'
+ run: |-
+ /usr/local/share/chromedriver-linux64/chromedriver \
+ --log-path=/tmp/chromedriver.log \
+ --verbose \
+ --allowed-ips= \
+ --allowed-origins=* &
+
- name: PHPUNIT tests
run: |
cd $DRUPAL_DIR/web/core
$DRUPAL_DIR/vendor/bin/phpunit --verbose --testsuite "${{ matrix.test-suite }}"
+ - name: Print chromedriver logs
+ if: matrix.test-suite == 'functional-javascript'
+ run: cat /tmp/chromedriver.log
diff --git a/.github/workflows/gitlab-mirror.yml b/.github/workflows/gitlab-mirror.yml
new file mode 100644
index 000000000..fa3eda50b
--- /dev/null
+++ b/.github/workflows/gitlab-mirror.yml
@@ -0,0 +1,26 @@
+name: Mirror and run GitLab CI
+
+on:
+ push:
+ branches: [2.x]
+ tags: '*'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Mirror + trigger CI
+ uses: SvanBoxel/gitlab-mirror-and-ci-action@master
+ with:
+ args: "https://git.drupalcode.org/project/islandora"
+ env:
+ FOLLOW_TAGS: "true"
+ FORCE_PUSH: "false"
+ GITLAB_HOSTNAME: "git.drupal.org"
+ GITLAB_USERNAME: "project_34868_bot"
+ GITLAB_PASSWORD: ${{ secrets.GITLAB_PASSWORD }}
+ GITLAB_PROJECT_ID: "34868"
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/README.md b/README.md
index d891c0e6f..546d21923 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# ![Islandora](https://cloud.githubusercontent.com/assets/2371345/25624809/f95b0972-2f30-11e7-8992-a8f135402cdc.png) Islandora
+# Islandora
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.4-8892BF.svg?style=flat-square)](https://php.net/)
[![Build Status](https://github.com/islandora/islandora/actions/workflows/build-2.x.yml/badge.svg)](https://github.com/Islandora/islandora/actions)
@@ -91,21 +91,21 @@ Having problems or solved a problem? Check out the Islandora google groups for a
Current maintainers:
-* [Danny Lamb](https://github.com/dannylamb)
+* [Islandora Technical Advisory Group](https://github.com/Islandora/islandora-community/wiki/Technical-Advisory-Group-%28TAG%29)
## Sponsors
-* UPEI
-* discoverygarden inc.
-* LYRASIS
-* McMaster University
-* University of Limerick
-* York University
-* University of Manitoba
-* Simon Fraser University
-* PALS
-* American Philosophical Society
-* Common Media Inc.
+* [American Philosophical Society](https://www.amphilsoc.org/)
+* [Born-Digital, Inc.](https://www.born-digital.com/)
+* [discoverygarden inc.](https://www.discoverygarden.ca/)
+* [LYRASIS](https://www.lyrasis.org/)
+* [McMaster University](https://www.mcmaster.ca/)
+* [PALS](https://www.mnpals.org/)
+* [University of Limerick](https://www.ul.ie/)
+* [University of Manitoba](https://umanitoba.ca/)
+* [UPEI](https://www.upei.ca/)
+* [Simon Fraser University](https://www.sfu.ca/)
+* [York University](https://www.yorku.ca/)
## Development
diff --git a/composer.json b/composer.json
index 24eb9e5ba..9a3c693e8 100644
--- a/composer.json
+++ b/composer.json
@@ -14,30 +14,32 @@
}
],
"require": {
- "drupal/context": "^4.0@beta",
- "drupal/search_api": "~1.8",
- "islandora/jsonld": "^2",
- "stomp-php/stomp-php": "4.* || ^5",
- "drupal/jwt": "^1.0.0-beta5",
- "drupal/filehash": "^1.1 || ^2",
- "drupal/prepopulate" : "^2.2",
- "drupal/eva" : "^2.0",
- "drupal/features" : "^3.7",
- "drupal/migrate_plus" : "^5.1",
+ "drupal/context": "^4 || ^5@RC",
+ "drupal/ctools": "^3.8 || ^4",
+ "drupal/eva" : "^3.0",
+ "drupal/file_replace": "^1.1",
+ "drupal/filehash": "^2 || ^3",
+ "drupal/flysystem" : "^2.0@alpha",
+ "drupal/jsonld": "^2 || ^3",
+ "drupal/jwt": "^1.1 || ^2",
+ "drupal/migrate_plus" : "^5.1 || ^6",
"drupal/migrate_source_csv" : "^3.4",
+ "drupal/prepopulate" : "^2.2",
+ "drupal/search_api": "^1.8",
"drupal/token" : "^1.3",
- "drupal/flysystem" : "^2.0@alpha",
- "islandora/crayfish-commons": "^2",
- "drupal/file_replace": "^1.1"
+ "islandora/chullo": "^2.0",
+ "islandora/fedora-entity-mapper": "^1.0",
+ "stomp-php/stomp-php": "4.* || ^5"
},
"require-dev": {
"phpunit/phpunit": "^6",
- "squizlabs/php_codesniffer": "2.7.1",
+ "squizlabs/php_codesniffer": "^2.7.1",
"drupal/coder": "*",
"sebastian/phpcpd": "*"
},
"suggest": {
- "drupal/transliterate_filenames": "Sanitizes filenames when they are uploaded so they don't break your repository."
+ "drupal/transliterate_filenames": "Sanitizes filenames when they are uploaded so they don't break your repository.",
+ "drupal/coi": "Some configuration fields work with Config Override Inspector."
},
"license": "GPL-2.0-or-later",
"authors": [
diff --git a/config/install/islandora.settings.yml b/config/install/islandora.settings.yml
index 8fb25fb8c..1497c291e 100644
--- a/config/install/islandora.settings.yml
+++ b/config/install/islandora.settings.yml
@@ -1,4 +1,4 @@
broker_url: 'tcp://localhost:61613'
jwt_expiry: '+2 hour'
-gemini_url: ''
+delete_media_and_files: TRUE
gemini_pseudo_bundles: []
diff --git a/config/install/system.action.delete_node_and_media.yml b/config/install/system.action.delete_node_and_media.yml
new file mode 100644
index 000000000..74fee13cd
--- /dev/null
+++ b/config/install/system.action.delete_node_and_media.yml
@@ -0,0 +1,12 @@
+status: true
+dependencies:
+ enforced:
+ module:
+ - islandora
+ module:
+ - islandora
+id: delete_node_and_media
+label: 'Delete node(s) and associated media'
+type: node
+plugin: delete_node_and_media
+configuration: { }
\ No newline at end of file
diff --git a/config/schema/islandora.schema.yml b/config/schema/islandora.schema.yml
index e85d57396..41b7338d1 100644
--- a/config/schema/islandora.schema.yml
+++ b/config/schema/islandora.schema.yml
@@ -14,15 +14,18 @@ islandora.settings:
jwt_expiry:
type: string
label: 'How long JWTs should last before expiring.'
+ delete_media_and_files:
+ type: boolean
+ label: 'Node Delete with Media and Files'
+ redirect_after_media_save:
+ type: boolean
+ label: 'Redirect to node after media save.'
upload_form_location:
type: string
label: 'Upload Form Location'
upload_form_allowed_mimetypes:
type: string
label: 'Upload Form Allowed Extensions'
- gemini_url:
- type: uri
- label: 'Url to Gemini microservice'
gemini_pseudo_bundles:
type: sequence
label: 'List of node, media and taxonomy terms that should include the linked Fedora URI'
@@ -78,6 +81,10 @@ action.configuration.delete_media_and_file:
type: action_configuration_default
label: 'Delete media and file'
+action.configuration.delete_node_and_media:
+ type: action_configuration_default
+ label: 'Delete node and media'
+
condition.plugin.node_has_term:
type: condition.plugin
mapping:
@@ -166,10 +173,14 @@ condition.plugin.node_had_namespace:
pid_field:
type: ignore
label: 'PID field'
-
+
field.formatter.settings.islandora_image:
type: field.formatter.settings.image
label: 'Islandora image field display format settings'
+ mapping:
+ image_alt_text:
+ type: string
+ label: "Alt text source"
condition.plugin.islandora_entity_bundle:
type: condition.plugin
diff --git a/css/islandora.css b/css/islandora.css
new file mode 100644
index 000000000..696587ac1
--- /dev/null
+++ b/css/islandora.css
@@ -0,0 +1,3 @@
+.container .islandora-media-items {
+ margin: 0;
+}
diff --git a/islandora.info.yml b/islandora.info.yml
index 9cd89cddc..a43196a42 100644
--- a/islandora.info.yml
+++ b/islandora.info.yml
@@ -4,31 +4,30 @@ name: 'islandora'
description: "Islandora Core"
type: module
package: Islandora
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^10.2
dependencies:
+ - context:context_ui
+ - ctools:ctools
+ - drupal:action
+ - drupal:basic_auth
- drupal:block
+ - drupal:content_translation
+ - drupal:link
+ - drupal:media
- drupal:node
- - drupal:path
- - drupal:text
- drupal:options
- - drupal:link
- - drupal:jsonld
- - drupal:search_api
- - drupal:jwt
+ - drupal:path
- drupal:rest
- - drupal:filehash
- - drupal:basic_auth
- - drupal:context_ui
- - drupal:action
- - drupal:eva
- drupal:taxonomy
+ - drupal:text
- drupal:views_ui
- - drupal:media
- - drupal:prepopulate
- - drupal:features_ui
- - drupal:migrate_source_csv
- - drupal:content_translation
- - drupal:flysystem
- - drupal:token
- - drupal:file_replace
+ - eva:eva
+ - file_replace:file_replace
+ - filehash:filehash
+ - flysystem:flysystem
+ - jsonld:jsonld
+ - jwt:jwt
+ - migrate_source_csv:migrate_source_csv
+ - prepopulate:prepopulate
+ - search_api:search_api
+ - token:token
diff --git a/islandora.install b/islandora.install
index f9eb1225f..fb569eb4a 100644
--- a/islandora.install
+++ b/islandora.install
@@ -5,6 +5,11 @@
* Install/update hook implementations.
*/
+use Drupal\Core\Extension\ExtensionNameLengthException;
+use Drupal\Core\Extension\MissingDependencyException;
+use Drupal\Core\Utility\UpdateException;
+use Symfony\Component\Yaml\Yaml;
+
/**
* Adds common namespaces to jsonld.settings.
*/
@@ -174,3 +179,61 @@ function update_jsonld_included_namespaces() {
->warning("Could not find required jsonld.settings to add default RDF namespaces.");
}
}
+
+/**
+ * Ensure that ctools is enabled.
+ */
+function islandora_update_8007() {
+ $module_handler = \Drupal::moduleHandler();
+ if ($module_handler->moduleExists('ctools')) {
+ return t('The "@module_name" module is already enabled, no action necessary.', [
+ '@module_name' => 'ctools',
+ ]);
+ }
+
+ /** @var \Drupal\Core\Extension\ModuleInstallerInterface $installer */
+ $installer = \Drupal::service('module_installer');
+
+ try {
+ if ($installer->install(['ctools'], TRUE)) {
+ return t('The "@module_name" module was enabled successfully.', [
+ '@module_name' => 'ctools',
+ ]);
+ }
+ }
+ catch (ExtensionNameLengthException | MissingDependencyException $e) {
+ throw new UpdateException('Failed; ensure that the ctools module is available in the Drupal installation.', 0, $e);
+ }
+ catch (\Exception $e) {
+ throw new UpdateException('Failed; encountered an exception while trying to enable ctools.', 0, $e);
+ }
+
+ // Theoretically impossible to hit, as ModuleInstaller::install() only returns
+ // TRUE (or throws/propagates an exception), but... probably a good idea to
+ // have the here, just in case?
+ throw new UpdateException('Failed; hit the end of the update hook implementation, which is not expected.');
+}
+
+/**
+ * Set config to no redirect after media save.
+ */
+function islandora_update_8008() {
+ $config = \Drupal::configFactory()->getEditable('islandora.settings');
+ if ($config) {
+ $config->set('redirect_after_media_save', FALSE);
+ $config->save(TRUE);
+ return t('A new configuration option, "Redirect after media save" is now available.
+ It has been turned off to preserve existing behaviour. To enable this setting visit
+ Configuration > Islandora > Core Settings.');
+ }
+}
+
+/**
+ * Add "Delete node and media" action.
+ */
+function islandora_update_9001(&$sandbox) {
+ $config_id = 'system.action.delete_node_and_media';
+ $config_path = \Drupal::service('extension.list.module')->getPath('islandora') . '/config/install/' . $config_id . '.yml';
+ $data = Yaml::parseFile($config_path);
+ \Drupal::configFactory()->getEditable($config_id)->setData($data)->save(TRUE);
+}
diff --git a/islandora.libraries.yml b/islandora.libraries.yml
new file mode 100644
index 000000000..840dc294b
--- /dev/null
+++ b/islandora.libraries.yml
@@ -0,0 +1,5 @@
+islandora:
+ version: VERSION
+ css:
+ theme:
+ css/islandora.css: {}
diff --git a/islandora.module b/islandora.module
index 5b5446d13..153eb1679 100644
--- a/islandora.module
+++ b/islandora.module
@@ -16,17 +16,18 @@
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
+use Drupal\file\FileInterface;
use Drupal\islandora\Form\IslandoraSettingsForm;
-use Drupal\node\NodeInterface;
use Drupal\media\MediaInterface;
-use Drupal\file\FileInterface;
-use Drupal\taxonomy\TermInterface;
-use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\node\NodeInterface;
use Drupal\serialization\Normalizer\CacheableNormalizerInterface;
+use Drupal\taxonomy\TermInterface;
/**
* Implements hook_help().
@@ -181,7 +182,8 @@ function islandora_file_insert(FileInterface $file) {
*/
function islandora_file_update(FileInterface $file) {
// Exit early if unchanged.
- if ($file->filehash != NULL && $file->original->filehash != NULL && $file->filehash['sha1'] == $file->original->filehash['sha1']) {
+ if ($file->hasField('sha1') && $file->original->hasField('sha1')
+ && $file->sha1->getString() == $file->original->sha1->getString()) {
return;
}
@@ -329,19 +331,120 @@ function islandora_form_alter(&$form, FormStateInterface $form_state, $form_id)
if ($node) {
$form['name']['widget'][0]['value']['#default_value'] = $node->getTitle();
}
+ $form['actions']['submit']['#submit'][] = 'islandora_media_custom_form_submit';
+ }
+ }
+
+ $form_object = $form_state->getFormObject();
+ $utils = \Drupal::service('islandora.utils');
+ $config = \Drupal::config('islandora.settings')->get('delete_media_and_files');
+
+ if ($config == 1 && $form_object instanceof EntityForm) {
+ $entity = $form_object->getEntity();
+
+ if ($entity->getEntityTypeId() == "node" && $utils->isIslandoraType($entity->getEntityTypeId(), $entity->bundle()) && strpos($form['#form_id'], 'delete_form') !== FALSE) {
+ $medias = $utils->getMedia($form_state->getFormObject()->getEntity());
+ if (count($medias) != 0) {
+ $form['delete_associated_content'] = [
+ '#type' => 'checkbox',
+ '#title' => t('Delete all associated medias and nodes'),
+ ];
+
+ $media_list = [];
+
+ foreach ($medias as $media) {
+ $media_list[] = $media->getName();
+ }
+
+ $form['container'] = [
+ '#type' => 'container',
+ '#states' => [
+ 'visible' => [
+ ':input[name="delete_associated_content"]' => ['checked' => TRUE],
+ ],
+ ],
+ ];
+
+ $form['container']['media_items'] = [
+ '#theme' => 'item_list',
+ '#type' => 'ul',
+ '#items' => $media_list,
+ '#attributes' => ['class' => ['islandora-media-items']],
+ '#wrapper_attributes' => ['class' => ['container']],
+ '#attached' => [
+ 'library' => [
+ 'islandora/islandora',
+ ],
+ ],
+ ];
+
+ $form['actions']['submit']['#submit'][] = 'islandora_object_delete_form_submit';
+ return $form;
+ }
+ }
+ }
+
+ return $form;
+}
+
+/**
+ * Redirect submit handler for media save.
+ */
+function islandora_media_custom_form_submit(&$form, FormStateInterface $form_state) {
+ // Check configuration to see whether a redirect is desired.
+ $redirect = \Drupal::config('islandora.settings')->get('redirect_after_media_save');
+ if ($redirect) {
+ $params = \Drupal::request()->query->all();
+ if (!empty($params)) {
+ $target_id = $params['edit']['field_media_of']['widget'][0]['target_id'];
+ $url = Url::fromRoute('view.media_of.page_1', ['node' => $target_id]);
+ $form_state->setRedirectUrl($url);
}
}
}
/**
- * Implements hook_field_widget_WIDGET_TYPE_form_alter().
+ * Implements a submit handler for the delete form.
*/
-function islandora_field_widget_image_image_form_alter(&$element, $form_state, $context) {
+function islandora_object_delete_form_submit($form, FormStateInterface $form_state) {
+ $result = $form_state->getValues('delete_associated_content');
+ if ($result['delete_associated_content'] == 1) {
+ $utils = \Drupal::service('islandora.utils');
+ $node = $form_state->getFormObject()->getEntity();
+ $medias = $utils->getMedia($node);
+ $results = $utils->deleteMediaAndFiles($medias);
+ $logger = \Drupal::logger('logger.channel.islandora');
+ $messenger = \Drupal::messenger();
+ if (isset($results['inaccessible'])) {
+ $messenger->addWarning($results['inaccessible']);
+ }
+ $logger->notice($results['deleted']);
+ $build = [
+ 'heading' => [
+ '#type' => 'html_tag',
+ '#tag' => 'div',
+ '#value' => t("The repository item @node and @media", [
+ '@node' => $node->getTitle(),
+ '@media' => $results['deleted'],
+ ]),
+ ],
+ ];
+
+ $message = \Drupal::service('renderer')->renderPlain($build);
+ $messenger->deleteByType('status');
+ $messenger->addStatus($message);
+ }
+}
+
+/**
+ * Implements hook_field_widget_single_element_WIDGET_TYPE_form_alter().
+ */
+function islandora_field_widget_single_element_image_image_form_alter(&$element, $form_state, $context) {
$element['#process'][] = 'islandora_add_default_image_alt_text';
}
/**
- * Callback for hook_field_widget_WIDGET_TYPE_form_alter().
+ * Callback for hook_field_widget_single_element_WIDGET_TYPE_form_alter().
*/
function islandora_add_default_image_alt_text($element, $form_state, $form) {
if ($element['alt']['#access']) {
@@ -404,6 +507,7 @@ function islandora_form_block_form_alter(&$form, FormStateInterface $form_state,
unset($form['visibility']['media_is_islandora_media']);
unset($form['visibility']['media_uses_filesystem']);
unset($form['visibility']['node_had_namespace']);
+ unset($form['visibility']['node_has_ancestor']);
unset($form['visibility']['node_has_parent']);
unset($form['visibility']['node_has_term']);
unset($form['visibility']['node_is_islandora_object']);
@@ -441,12 +545,11 @@ function islandora_entity_view(array &$build, EntityInterface $entity, EntityVie
$route_match_item = \Drupal::routeMatch()->getParameters()->get($entity->getEntityTypeId());
// Ensure the entity matches the route.
if ($entity === $route_match_item) {
- if ($display->getComponent('field_gemini_uri')) {
- $mapper = \Drupal::service('islandora.entity_mapper');
- $flysystem_config = Settings::get('flysystem');
- $fedora_root = $flysystem_config['fedora']['config']['root'];
+ $flysystem_config = Settings::get('flysystem');
+ $fedora_root = $flysystem_config['fedora']['config']['root'] ?? NULL;
+ if ($display->getComponent('field_gemini_uri') && $fedora_root) {
$fedora_root = rtrim($fedora_root, '/');
-
+ $mapper = \Drupal::service('islandora.entity_mapper');
if ($entity->getEntityTypeId() == 'media') {
// Check if the source file is in Fedora or not.
$media_source_service = \Drupal::service('islandora.media_source_service');
@@ -515,7 +618,7 @@ function islandora_preprocess_views_view_table(&$variables) {
// Check for a weight selector field.
foreach ($variables['view']->field as $field_key => $field) {
- if ($field->options['plugin_id'] == 'integer_weight_selector') {
+ if ($field->getPluginId() == 'integer_weight_selector') {
// Check if the weight selector is on the first column.
$is_first_column = array_search($field_key, array_keys($variables['view']->field)) > 0 ? FALSE : TRUE;
diff --git a/islandora.post_update.php b/islandora.post_update.php
new file mode 100644
index 000000000..0a29e56ed
--- /dev/null
+++ b/islandora.post_update.php
@@ -0,0 +1,16 @@
+getEditable('islandora.settings');
+ $config->set('delete_media_and_files', TRUE);
+ $config->save(TRUE);
+}
diff --git a/islandora.routing.yml b/islandora.routing.yml
index 5387e9a47..f72db51c8 100644
--- a/islandora.routing.yml
+++ b/islandora.routing.yml
@@ -37,14 +37,15 @@ islandora.add_member_to_node_page:
_entity_create_any_access: 'node'
islandora.upload_children:
- path: '/node/{node}/members/upload'
+ path: '/node/{node}/members/upload/{step}'
defaults:
- _form: '\Drupal\islandora\Form\AddChildrenForm'
+ _wizard: '\Drupal\islandora\Form\AddChildrenWizard\ChildForm'
_title: 'Upload Children'
+ step: 'type_selection'
options:
_admin_route: 'TRUE'
requirements:
- _custom_access: '\Drupal\islandora\Form\AddChildrenForm::access'
+ _custom_access: '\Drupal\islandora\Form\AddChildrenWizard\Access::childAccess'
islandora.add_media_to_node_page:
path: '/node/{node}/media/add'
@@ -58,14 +59,15 @@ islandora.add_media_to_node_page:
_entity_create_any_access: 'media'
islandora.upload_media:
- path: '/node/{node}/media/upload'
+ path: '/node/{node}/media/upload/{step}'
defaults:
- _form: '\Drupal\islandora\Form\AddMediaForm'
+ _wizard: '\Drupal\islandora\Form\AddChildrenWizard\MediaForm'
_title: 'Add media'
+ step: 'type_selection'
options:
_admin_route: 'TRUE'
requirements:
- _custom_access: '\Drupal\islandora\Form\AddMediaForm::access'
+ _custom_access: '\Drupal\islandora\Form\AddChildrenWizard\Access::mediaAccess'
islandora.media_source_update:
path: '/media/{media}/source'
@@ -107,3 +109,10 @@ islandora.confirm_delete_media_and_file:
_form: 'Drupal\islandora\Form\ConfirmDeleteMediaAndFile'
requirements:
_permission: 'administer media+delete any media'
+
+islandora.confirm_delete_node_and_media:
+ path: '/node/delete_with_media'
+ defaults:
+ _form: 'Drupal\islandora\Form\ConfirmDeleteNodeAndMedia'
+ requirements:
+ _permission: 'administer media+delete any media'
diff --git a/islandora.services.yml b/islandora.services.yml
index 4b3a9d16e..36ef7dac3 100644
--- a/islandora.services.yml
+++ b/islandora.services.yml
@@ -31,6 +31,9 @@ services:
logger.channel.islandora:
parent: logger.channel_base
arguments: ['islandora']
+ logger.channel.fedora_flysystem:
+ parent: logger.channel_base
+ arguments: ['fedora_flysystem']
islandora.media_route_context_provider:
class: Drupal\islandora\ContextProvider\MediaRouteContextProvider
arguments: ['@current_route_match']
@@ -51,11 +54,27 @@ services:
arguments: ['@entity_type.manager', '@current_user', '@language_manager', '@file_system', '@islandora.utils']
islandora.utils:
class: Drupal\islandora\IslandoraUtils
- arguments: ['@entity_type.manager', '@entity_field.manager', '@context.manager', '@flysystem_factory', '@language_manager']
+ arguments: ['@entity_type.manager', '@entity_field.manager', '@context.manager', '@flysystem_factory', '@language_manager', '@current_user']
islandora.entity_mapper:
- class: Islandora\Crayfish\Commons\EntityMapper\EntityMapper
+ class: Islandora\EntityMapper\EntityMapper
islandora.stomp.auth_header_listener:
class: Drupal\islandora\EventSubscriber\StompHeaderEventSubscriber
arguments: ['@jwt.authentication.jwt']
tags:
- { name: event_subscriber }
+ islandora.upload_children.batch_processor:
+ class: Drupal\islandora\Form\AddChildrenWizard\ChildBatchProcessor
+ arguments:
+ - '@entity_type.manager'
+ - '@database'
+ - '@current_user'
+ - '@messenger'
+ - '@date.formatter'
+ islandora.upload_media.batch_processor:
+ class: Drupal\islandora\Form\AddChildrenWizard\MediaBatchProcessor
+ arguments:
+ - '@entity_type.manager'
+ - '@database'
+ - '@current_user'
+ - '@messenger'
+ - '@date.formatter'
diff --git a/islandora.tokens.inc b/islandora.tokens.inc
index 8d1c4b6b2..d2c424c53 100644
--- a/islandora.tokens.inc
+++ b/islandora.tokens.inc
@@ -19,6 +19,18 @@ function islandora_token_info() {
'name' => t('Islandora Tokens'),
'description' => t('Tokens for Islandora objects.'),
];
+ $node['media-original-file:filename'] = [
+ 'name' => t('Media: Original File filename without extension.'),
+ 'description' => t('File name without extension of original uploaded file associated with Islandora Object via Media.'),
+ ];
+ $node['media-original-file:basename'] = [
+ 'name' => t('Media: Original File filename with extension.'),
+ 'description' => t('File name with extension of original uploaded file associated with Islandora Object via Media.'),
+ ];
+ $node['media-original-file:extension'] = [
+ 'name' => t('Media: Original File extension.'),
+ 'description' => t('File extension of original uploaded file associated with Islandora Object via Media.'),
+ ];
$node['media-thumbnail-image:url'] = [
'name' => t('Media: Thumbnail Image URL.'),
'description' => t('URL of Thumbnail Image associated with Islandora Object via Media.'),
@@ -68,8 +80,29 @@ function islandora_tokens($type, $tokens, array $data, array $options, Bubbleabl
return;
}
$islandoraUtils = \Drupal::service('islandora.utils');
+ if (!$islandoraUtils->isIslandoraType('node', $data['node']->bundle())) {
+ return;
+ }
foreach ($tokens as $name => $original) {
switch ($name) {
+ case 'media-original-file:basename':
+ case 'media-original-file:filename':
+ case 'media-original-file:extension':
+ $term = $islandoraUtils->getTermForUri('http://pcdm.org/use#OriginalFile');
+ $media = $islandoraUtils->getMediaWithTerm($data['node'], $term);
+ // Is there media?
+ if ($media) {
+ $file = \Drupal::service('islandora.media_source_service')->getSourceFile($media);
+ if (!empty($file)) {
+ $path_info = pathinfo($file->createFileUrl());
+ $key = explode(':', $name)[1];
+ if (array_key_exists($key, $path_info)) {
+ $replacements[$original] = $path_info[$key];
+ }
+ }
+ }
+ break;
+
case 'media-thumbnail-image:url':
case 'media_thumbnail_image:url':
$term = $islandoraUtils->getTermForUri('http://pcdm.org/use#ThumbnailImage');
@@ -79,7 +112,7 @@ function islandora_tokens($type, $tokens, array $data, array $options, Bubbleabl
if ($media) {
$file = \Drupal::service('islandora.media_source_service')->getSourceFile($media);
if (!empty($file)) {
- $url = $file->url();
+ $url = $file->createFileUrl();
$replacements[$original] = $url;
}
}
@@ -104,7 +137,7 @@ function islandora_tokens($type, $tokens, array $data, array $options, Bubbleabl
break;
case 'pdf_url':
- $replacements[$original] = ' ' . islandora_url_to_service_file_media_by_mimetype($data['node'], 'application/pdf');
+ $replacements[$original] = islandora_url_to_service_file_media_by_mimetype($data['node'], 'application/pdf');
break;
}
}
@@ -158,4 +191,5 @@ function islandora_url_to_service_file_media_by_mimetype($node, $mime_type) {
}
}
}
+ return '';
}
diff --git a/modules/islandora_advanced_search/islandora_advanced_search.info.yml b/modules/islandora_advanced_search/islandora_advanced_search.info.yml
index 524e9dc74..a5ad90efb 100644
--- a/modules/islandora_advanced_search/islandora_advanced_search.info.yml
+++ b/modules/islandora_advanced_search/islandora_advanced_search.info.yml
@@ -4,8 +4,10 @@ name: 'Islandora Advanced Search'
description: "Creates an Advanced Search block and other enhancements to search."
type: module
package: Islandora
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9 || ^10
dependencies:
- drupal:facets
- drupal:facets_summary
- drupal:search_api_solr
+lifecycle: deprecated
+lifecycle_link: https://groups.google.com/g/islandora/c/SEOAWJrfE_M
diff --git a/modules/islandora_advanced_search/islandora_advanced_search.module b/modules/islandora_advanced_search/islandora_advanced_search.module
index cbf52667a..f08dd3460 100644
--- a/modules/islandora_advanced_search/islandora_advanced_search.module
+++ b/modules/islandora_advanced_search/islandora_advanced_search.module
@@ -13,51 +13,13 @@
*/
use Drupal\block\Entity\Block;
-use Drupal\Component\Utility\Unicode;
use Drupal\Core\Form\FormStateInterface;
use Drupal\islandora_advanced_search\AdvancedSearchQuery;
-use Drupal\islandora_advanced_search\Form\SettingsForm;
use Drupal\islandora_advanced_search\Utilities;
use Drupal\search_api\Query\QueryInterface as DrupalQueryInterface;
use Drupal\views\ViewExecutable;
use Solarium\Core\Query\QueryInterface as SolariumQueryInterface;
-/**
- * Implements hook_theme().
- */
-function islandora_advanced_search_theme() {
- return [
- 'facets_item_list__include_exclude_links' => [
- 'template' => 'facets/facets-item-list--include-exclude-links',
- 'base hook' => 'facets_item_list',
- ],
- 'facets_result_item__include_exclude_links' => [
- 'template' => 'facets/facets-result-item--include-exclude-links',
- 'base hook' => 'facets_result_item',
- ],
- 'facets_result_item__summary' => [
- 'template' => 'facets/facets-result-item--summary',
- 'base hook' => 'facets_result_item',
- ],
- ];
-}
-
-/**
- * Implements hook_library_info_alter().
- */
-function islandora_advanced_search_library_info_alter(&$libraries, $extension) {
- if ($extension == 'facets') {
- // Override facets module javascript with customizations.
- $path = '/' . drupal_get_path('module', 'islandora_advanced_search') . '/js/facets';
- $libraries['soft-limit']['js'] = [
- "$path/soft-limit.js" => [],
- ];
- $libraries['drupal.facets.views-ajax']['js'] = [
- "$path/facets-views-ajax.js" => [],
- ];
- }
-}
-
/**
* Implements hook_search_api_solr_converted_query_alter().
*/
@@ -99,20 +61,6 @@ function islandora_advanced_search_form_block_form_alter(&$form, FormStateInterf
$form['visibility'][$condition_id] = $condition_form;
}
-/**
- * Implements hook_preprocess_block__facets_summary().
- */
-function islandora_advanced_search_preprocess_block__facets_summary(&$variables) {
- // Copy data-attributes to the content as the javascript expects
- // there to be no elements between the data declaration and the
- // content of the block.
- foreach ($variables['attributes'] as $key => $value) {
- if (substr($key, 0, 4) === "data") {
- $variables['content_attributes'][$key] = $value;
- }
- }
-}
-
/**
* Implements hook_preprocess_preprocess_views_view().
*/
@@ -139,53 +87,3 @@ function islandora_advanced_search_views_pre_view(ViewExecutable $view, $display
$advanced_search_query = new AdvancedSearchQuery();
$advanced_search_query->alterView(\Drupal::request(), $view, $display_id);
}
-
-/**
- * Implements hook_preprocess_facets_summary_item_list().
- */
-function islandora_advanced_search_preprocess_facets_summary_item_list(&$variables) {
- foreach ($variables['items'] as &$item) {
- $item['attributes']['class'][] = 'facet-summary-item';
- }
-}
-
-/**
- * Implements hook_preprocess_facets_item_list().
- */
-function islandora_advanced_search_preprocess_facets_item_list(&$variables) {
- $widget = $variables['facet']->getWidget();
- $soft_limit = $widget['config']['soft_limit'];
- // Break into two groups less / more which can display be toggled as a single
- // element change rather than showing / hiding all
elements individually.
- // As its slow and causes the page to snap when loading.
- $variables['less'] = array_slice($variables['items'], 0, $soft_limit);
- $variables['more'] = array_slice($variables['items'], $soft_limit);
- $variables['show_more_label'] = $widget['config']['soft_limit_settings']['show_more_label'];
-}
-
-/**
- * Implements hook_preprocess_facets_result_item().
- */
-function islandora_advanced_search_preprocess_facets_result_item(&$variables) {
- $settings = \Drupal::config(SettingsForm::CONFIG_NAME);
- $length = $settings->get(SettingsForm::FACET_TRUNCATE);
- if (is_numeric($length)) {
- // Limit the length of facets display to at most 32 characters.
- if (is_string($variables['value'])) {
- $variables['value'] = Unicode::truncate(
- $variables['value'],
- $length,
- TRUE,
- TRUE
- );
- }
- elseif (is_string($variables['value']['text']['#title'])) {
- $variables['value']['text']['#title'] = Unicode::truncate(
- $variables['value']['text']['#title'],
- $length,
- TRUE,
- TRUE
- );
- }
- }
-}
diff --git a/modules/islandora_advanced_search/js/facets/facets-views-ajax.js b/modules/islandora_advanced_search/js/facets/facets-views-ajax.js
deleted file mode 100644
index 0b04ea33d..000000000
--- a/modules/islandora_advanced_search/js/facets/facets-views-ajax.js
+++ /dev/null
@@ -1,147 +0,0 @@
-//# sourceURL=modules/contrib/islandora/modules/islandora_advanced_search/js/facets/facets-view.ajax.js
-/**
- * @file
- * Overrides the facets-view-ajax.js behavior from the 'facets' module.
- */
-(function ($, Drupal) {
- "use strict";
-
- // Generate events on push state.
- (function (history) {
- var pushState = history.pushState;
- history.pushState = function (state, title, url) {
- var ret = pushState.apply(this, arguments);
- var event = new Event("pushstate");
- window.dispatchEvent(event);
- return ret;
- };
- })(window.history);
-
- function reload(url) {
- // Update View.
- if (drupalSettings && drupalSettings.views && drupalSettings.views.ajaxViews) {
- var view_path = drupalSettings.views.ajax_path;
- $.each(drupalSettings.views.ajaxViews, function (views_dom_id) {
- var views_parameters = Drupal.Views.parseQueryString(url);
- var views_arguments = Drupal.Views.parseViewArgs(url, "search");
- var views_settings = $.extend(
- {},
- Drupal.views.instances[views_dom_id].settings,
- views_arguments,
- views_parameters
- );
- var views_ajax_settings =
- Drupal.views.instances[views_dom_id].element_settings;
- views_ajax_settings.submit = views_settings;
- views_ajax_settings.url =
- view_path + "?" + $.param(Drupal.Views.parseQueryString(url));
- Drupal.ajax(views_ajax_settings).execute();
- });
- }
-
- // Replace filter, pager, summary, and facet blocks.
- var blocks = {};
- $(
- ".block[class*='block-plugin-id--islandora-advanced-search-result-pager'], .block[class*='block-plugin-id--views-exposed-filter-block'], .block[class*='block-plugin-id--facet']"
- ).each(function () {
- var id = $(this).attr("id");
- var block_id = id
- .slice("block-".length, id.length)
- .replace(/--.*$/g, "")
- .replace(/-/g, "_");
- blocks[block_id] = "#" + id;
- });
- Drupal.ajax({
- url: Drupal.url("islandora-advanced-search-ajax-blocks"),
- submit: {
- link: url,
- blocks: blocks,
- },
- }).execute();
- }
-
- // On location change reload all the blocks / ajax view.
- window.addEventListener("pushstate", function (e) {
- reload(window.location.href);
- });
-
- window.addEventListener("popstate", function (e) {
- if (e.state != null) {
- reload(window.location.href);
- }
- });
-
- /**
- * Push state on form/pager/facet change.
- */
- Drupal.behaviors.islandoraAdvancedSearchViewsAjax = {
- attach: function (context, settings) {
- window.historyInitiated = true;
-
- // Remove existing behavior from form.
- if (settings && settings.views && settings.views.ajaxViews) {
- $.each(settings.views.ajaxViews, function (index, settings) {
- var exposed_form = $(
- "form#views-exposed-form-" +
- settings.view_name.replace(/_/g, "-") +
- "-" +
- settings.view_display_id.replace(/_/g, "-")
- );
- exposed_form
- .once()
- .find("input[type=submit], input[type=image]")
- .not("[data-drupal-selector=edit-reset]")
- .each(function (index) {
- $(this).unbind("click");
- $(this).click(function (e) {
- // Let ctrl/cmd click open in a new window.
- if (e.shiftKey || e.ctrlKey || e.metaKey) {
- return;
- }
- e.preventDefault();
- e.stopPropagation();
- var href = window.location.href;
- var params = Drupal.Views.parseQueryString(href);
- // Remove the page if set as submitting the form should always take
- // the user to the first page (facets do the same).
- delete params.page;
- // Include values from the form in the URL.
- $.each(exposed_form.serializeArray(), function () {
- params[this.name] = this.value;
- });
- href = href.split("?")[0] + "?" + $.param(params);
- window.history.pushState(null, document.title, href);
- });
- });
- });
- }
-
- // Attach behavior to pager, summary, facet links.
- $("[data-drupal-pager-id], [data-drupal-facets-summary-id], [data-drupal-facet-id]")
- .once()
- .find("a:not(.facets-soft-limit-link)")
- .click(function (e) {
- // Let ctrl/cmd click open in a new window.
- if (e.shiftKey || e.ctrlKey || e.metaKey) {
- return;
- }
- e.preventDefault();
- window.history.pushState(null, document.title, $(this).attr("href"));
- });
-
- // Trigger on sort change.
- $('[data-drupal-pager-id] select[name="order"]')
- .once()
- .change(function () {
- var href = window.location.href;
- var params = Drupal.Views.parseQueryString(href);
- var selection = $(this).val();
- var option = $('option[value="' + selection + '"]');
- params.sort_order = option.data("sort_order");
- params.sort_by = option.data("sort_by");
- href = href.split("?")[0] + "?" + $.param(params);
- window.history.pushState(null, document.title, href);
- });
- },
- };
-})(jQuery, Drupal);
diff --git a/modules/islandora_advanced_search/js/facets/soft-limit.js b/modules/islandora_advanced_search/js/facets/soft-limit.js
deleted file mode 100644
index a81a267ce..000000000
--- a/modules/islandora_advanced_search/js/facets/soft-limit.js
+++ /dev/null
@@ -1,70 +0,0 @@
-//# sourceURL=modules/contrib/islandora/modules/islandora_advanced_search/js/facets/soft-limit.js
-/**
- * @file
- * Overrides the soft-limit.js behavior from the 'facets' module.
- * As when having many facets the original version causes the page to slow down and snap to hidden when rendering.
- */
-(function ($) {
-
- 'use strict';
-
- Drupal.behaviors.facetSoftLimit = {
- attach: function (context, settings) {
- if (settings.facets.softLimit !== 'undefined') {
- $.each(settings.facets.softLimit, function (facet, limit) {
- Drupal.facets.applySoftLimit(facet, limit, settings);
- });
- }
- }
- };
-
- Drupal.facets = Drupal.facets || {};
-
- /**
- * Applies the soft limit UI feature to a specific facets list.
- *
- * @param {string} facet
- * The facet id.
- * @param {string} limit
- * The maximum amount of items to show.
- * @param {object} settings
- * Settings.
- */
- Drupal.facets.applySoftLimit = function (facet, limit, settings) {
- var zero_based_limit = (limit - 1);
- var facet_id = facet;
- var facetsList = $('ul[data-drupal-facet-id="' + facet_id + '"]');
-
- // In case of multiple instances of a facet, we need to key them.
- if (facetsList.length > 1) {
- facetsList.each(function (key, $value) {
- $(this).attr('data-drupal-facet-id', facet_id + '-' + key);
- });
- }
-
- // Add "Show more" / "Show less" links.
- facetsList.filter(function () {
- return $(this).next('ul').length == 1; // Has expanding list.
- }).each(function () {
- var facet = $(this);
- var expand = facet.next('ul');
- var link = expand.next('a');
- var showLessLabel = settings.facets.softLimitSettings[facet_id].showLessLabel;
- var showMoreLabel = settings.facets.softLimitSettings[facet_id].showMoreLabel;
- link.text(showMoreLabel)
- .once()
- .on('click', function () {
- if (!expand.is(":visible")) {
- expand.slideDown();
- $(this).addClass('open').text(showLessLabel);
- }
- else {
- expand.slideUp();
- $(this).removeClass('open').text(showMoreLabel);
- }
- return false;
- })
- });
- };
-
-})(jQuery);
diff --git a/modules/islandora_advanced_search/src/Form/AdvancedSearchForm.php b/modules/islandora_advanced_search/src/Form/AdvancedSearchForm.php
index c00ce5665..09dd1646d 100644
--- a/modules/islandora_advanced_search/src/Form/AdvancedSearchForm.php
+++ b/modules/islandora_advanced_search/src/Form/AdvancedSearchForm.php
@@ -71,7 +71,7 @@ public function __construct(Request $request, RouteMatchInterface $current_route
*/
public static function create(ContainerInterface $container) {
return new static(
- $container->get('request_stack')->getMasterRequest(),
+ $container->get('request_stack')->getMainRequest(),
$container->get('current_route_match')
);
}
diff --git a/modules/islandora_advanced_search/src/Plugin/Block/AdvancedSearchBlock.php b/modules/islandora_advanced_search/src/Plugin/Block/AdvancedSearchBlock.php
index c5f5adb8c..2aa77f124 100644
--- a/modules/islandora_advanced_search/src/Plugin/Block/AdvancedSearchBlock.php
+++ b/modules/islandora_advanced_search/src/Plugin/Block/AdvancedSearchBlock.php
@@ -108,7 +108,7 @@ public static function create(ContainerInterface $container, array $configuratio
$plugin_definition,
$container->get('plugin.manager.search_api.display'),
$container->get('form_builder'),
- $container->get('request_stack')->getMasterRequest()
+ $container->get('request_stack')->getMainRequest()
);
}
diff --git a/modules/islandora_advanced_search/src/Plugin/Block/SearchResultsPagerBlock.php b/modules/islandora_advanced_search/src/Plugin/Block/SearchResultsPagerBlock.php
index f2e4a1703..e1e477d83 100644
--- a/modules/islandora_advanced_search/src/Plugin/Block/SearchResultsPagerBlock.php
+++ b/modules/islandora_advanced_search/src/Plugin/Block/SearchResultsPagerBlock.php
@@ -58,7 +58,7 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration,
$plugin_id,
$plugin_definition,
- $container->get('request_stack')->getMasterRequest()
+ $container->get('request_stack')->getMainRequest()
);
}
diff --git a/modules/islandora_audio/islandora_audio.info.yml b/modules/islandora_audio/islandora_audio.info.yml
index 998590f57..5e6beb5ae 100644
--- a/modules/islandora_audio/islandora_audio.info.yml
+++ b/modules/islandora_audio/islandora_audio.info.yml
@@ -2,7 +2,6 @@ name: 'Islandora Audio'
description: 'Islandora audio derivative actions'
type: module
package: Islandora
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9 || ^10
dependencies:
- drupal:islandora
diff --git a/modules/islandora_audio/tests/src/Functional/GenerateAudioDerivativeTest.php b/modules/islandora_audio/tests/src/Functional/GenerateAudioDerivativeTest.php
index fc1c6188e..094a7856a 100644
--- a/modules/islandora_audio/tests/src/Functional/GenerateAudioDerivativeTest.php
+++ b/modules/islandora_audio/tests/src/Functional/GenerateAudioDerivativeTest.php
@@ -40,7 +40,7 @@ public function testGenerateAudioDerivativeFromScratch() {
// Create an action to generate a audio derivative.
$this->drupalGet('admin/config/system/actions');
$this->getSession()->getPage()->findById("edit-action")->selectOption("Generate a audio derivative");
- $this->getSession()->getPage()->pressButton($this->t('Create'));
+ $this->getSession()->getPage()->pressButton('Create');
$this->assertSession()->statusCodeEquals(200);
$this->getSession()->getPage()->fillField('edit-label', "Generate audio test derivative");
@@ -53,7 +53,7 @@ public function testGenerateAudioDerivativeFromScratch() {
$this->getSession()->getPage()->fillField('edit-args', "-f mp3");
$this->getSession()->getPage()->fillField('edit-scheme', "public");
$this->getSession()->getPage()->fillField('edit-path', "derp.mov");
- $this->getSession()->getPage()->pressButton($this->t('Save'));
+ $this->getSession()->getPage()->pressButton('Save');
$this->assertSession()->statusCodeEquals(200);
// Create a context and add the action as a derivative reaction.
@@ -66,9 +66,10 @@ public function testGenerateAudioDerivativeFromScratch() {
'name[0][value]' => 'Test Media',
'files[field_media_file_0]' => __DIR__ . '/../../fixtures/test_file.txt',
'field_media_of[0][target_id]' => 'Test Node',
- 'field_tags[0][target_id]' => 'Preservation Master',
+ 'field_media_use[0][target_id]' => $this->preservationMasterTerm->label(),
];
- $this->drupalPostForm('media/add/' . $this->testMediaType->id(), $values, $this->t('Save'));
+ $this->drupalGet('media/add/' . $this->testMediaType->id());
+ $this->submitForm($values, 'Save');
$expected = [
'source_uri' => 'test_file.txt',
diff --git a/modules/islandora_breadcrumbs/config/install/islandora_breadcrumbs.breadcrumbs.yml b/modules/islandora_breadcrumbs/config/install/islandora_breadcrumbs.breadcrumbs.yml
index ea34ee2ed..aabb58916 100644
--- a/modules/islandora_breadcrumbs/config/install/islandora_breadcrumbs.breadcrumbs.yml
+++ b/modules/islandora_breadcrumbs/config/install/islandora_breadcrumbs.breadcrumbs.yml
@@ -2,9 +2,3 @@ maxDepth: -1
includeSelf: FALSE
referenceFields:
- field_member_of
-dependencies:
- module:
- - islandora
- enforced:
- module:
- - islandora_breadcrumbs
diff --git a/modules/islandora_breadcrumbs/islandora_breadcrumbs.info.yml b/modules/islandora_breadcrumbs/islandora_breadcrumbs.info.yml
index 56a10bc14..661ec70a2 100644
--- a/modules/islandora_breadcrumbs/islandora_breadcrumbs.info.yml
+++ b/modules/islandora_breadcrumbs/islandora_breadcrumbs.info.yml
@@ -1,8 +1,7 @@
name: 'Islandora Breadcrumbs'
type: module
description: 'Builds breadcrumbs based on field_member_of relationships.'
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9 || ^10
package: Islandora
dependencies:
- - drupal:islandora
+ - islandora:islandora
diff --git a/modules/islandora_breadcrumbs/tests/src/Functional/BreadcrumbsTest.php b/modules/islandora_breadcrumbs/tests/src/Functional/BreadcrumbsTest.php
index 80f5dbee0..ee35a1ed3 100644
--- a/modules/islandora_breadcrumbs/tests/src/Functional/BreadcrumbsTest.php
+++ b/modules/islandora_breadcrumbs/tests/src/Functional/BreadcrumbsTest.php
@@ -20,7 +20,7 @@ class BreadcrumbsTest extends IslandoraFunctionalTestBase {
*
* @var array
*/
- public static $modules = [
+ protected static $modules = [
'islandora_breadcrumbs',
];
@@ -56,7 +56,7 @@ class BreadcrumbsTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
// Create some nodes.
diff --git a/modules/islandora_core_feature/config/install/features.bundle.islandora.yml b/modules/islandora_core_feature/config/install/features.bundle.islandora.yml
deleted file mode 100644
index 9c3587314..000000000
--- a/modules/islandora_core_feature/config/install/features.bundle.islandora.yml
+++ /dev/null
@@ -1,100 +0,0 @@
-langcode: en
-status: true
-dependencies:
- enforced:
- module:
- - islandora_core_feature
-name: Islandora
-machine_name: islandora
-description: 'Features for islandora'
-assignments:
- alter:
- core: true
- uuid: true
- user_permissions: true
- enabled: true
- weight: 0
- base:
- types:
- config:
- comment_type: comment_type
- node_type: node_type
- content:
- user: user
- enabled: true
- weight: -2
- core:
- types:
- config:
- date_format: date_format
- field_storage_config: field_storage_config
- entity_form_mode: entity_form_mode
- image_style: image_style
- menu: menu
- responsive_image_style: responsive_image_style
- user_role: user_role
- entity_view_mode: entity_view_mode
- enabled: true
- weight: 5
- dependency:
- enabled: true
- weight: 15
- exclude:
- types:
- config:
- features_bundle: features_bundle
- curated: true
- module:
- installed: true
- profile: true
- namespace: true
- namespace_any: false
- enabled: true
- weight: -5
- existing:
- enabled: true
- weight: 12
- forward_dependency:
- enabled: true
- weight: 4
- namespace:
- enabled: true
- weight: 0
- optional:
- types:
- config: { }
- enabled: true
- weight: 0
- packages:
- enabled: true
- weight: -20
- profile:
- curated: true
- standard:
- files: true
- dependencies: true
- types:
- config:
- block: block
- language_content_settings: language_content_settings
- configurable_language: configurable_language
- migration: migration
- shortcut_set: shortcut_set
- tour: tour
- enabled: true
- weight: 10
- site:
- types:
- config:
- action: action
- contact_form: contact_form
- block_content_type: block_content_type
- rdf_mapping: rdf_mapping
- search_page: search_page
- taxonomy_vocabulary: taxonomy_vocabulary
- editor: editor
- filter_format: filter_format
- enabled: true
- weight: 7
-profile_name: ''
-is_profile: false
diff --git a/modules/islandora_core_feature/config/install/filehash.settings.yml b/modules/islandora_core_feature/config/install/filehash.settings.yml
deleted file mode 100644
index 7deecc696..000000000
--- a/modules/islandora_core_feature/config/install/filehash.settings.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-algos:
- sha1: sha1
- md5: '0'
- sha256: '0'
-dedupe: 0
diff --git a/modules/islandora_core_feature/config/install/core.entity_view_mode.media.source.yml b/modules/islandora_core_feature/config/optional/core.entity_view_mode.media.source.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/core.entity_view_mode.media.source.yml
rename to modules/islandora_core_feature/config/optional/core.entity_view_mode.media.source.yml
diff --git a/modules/islandora_core_feature/config/install/field.field.taxonomy_term.islandora_media_use.field_external_uri.yml b/modules/islandora_core_feature/config/optional/field.field.taxonomy_term.islandora_media_use.field_external_uri.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.field.taxonomy_term.islandora_media_use.field_external_uri.yml
rename to modules/islandora_core_feature/config/optional/field.field.taxonomy_term.islandora_media_use.field_external_uri.yml
diff --git a/modules/islandora_core_feature/config/install/field.field.taxonomy_term.islandora_models.field_external_uri.yml b/modules/islandora_core_feature/config/optional/field.field.taxonomy_term.islandora_models.field_external_uri.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.field.taxonomy_term.islandora_models.field_external_uri.yml
rename to modules/islandora_core_feature/config/optional/field.field.taxonomy_term.islandora_models.field_external_uri.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_file_size.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_file_size.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_file_size.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_file_size.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_height.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_height.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_height.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_height.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_media_audio_file.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_media_audio_file.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_media_audio_file.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_media_audio_file.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_media_document.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_media_document.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_media_document.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_media_document.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_media_file.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_media_file.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_media_file.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_media_file.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_media_image.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_media_image.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_media_image.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_media_image.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_media_of.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_media_of.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_media_of.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_media_of.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_media_use.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_media_use.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_media_use.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_media_use.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_media_video_file.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_media_video_file.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_media_video_file.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_media_video_file.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_mime_type.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_mime_type.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_mime_type.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_mime_type.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_original_name.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_original_name.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_original_name.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_original_name.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.media.field_width.yml b/modules/islandora_core_feature/config/optional/field.storage.media.field_width.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.media.field_width.yml
rename to modules/islandora_core_feature/config/optional/field.storage.media.field_width.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.node.field_member_of.yml b/modules/islandora_core_feature/config/optional/field.storage.node.field_member_of.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.node.field_member_of.yml
rename to modules/islandora_core_feature/config/optional/field.storage.node.field_member_of.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.node.field_model.yml b/modules/islandora_core_feature/config/optional/field.storage.node.field_model.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.node.field_model.yml
rename to modules/islandora_core_feature/config/optional/field.storage.node.field_model.yml
diff --git a/modules/islandora_core_feature/config/install/field.storage.node.field_weight.yml b/modules/islandora_core_feature/config/optional/field.storage.node.field_weight.yml
similarity index 90%
rename from modules/islandora_core_feature/config/install/field.storage.node.field_weight.yml
rename to modules/islandora_core_feature/config/optional/field.storage.node.field_weight.yml
index 97619cd26..4976d1d60 100644
--- a/modules/islandora_core_feature/config/install/field.storage.node.field_weight.yml
+++ b/modules/islandora_core_feature/config/optional/field.storage.node.field_weight.yml
@@ -14,6 +14,8 @@ module: core
locked: false
cardinality: 1
translatable: true
-indexes: { }
+indexes:
+ value:
+ - value
persist_with_no_fields: false
custom_storage: false
diff --git a/modules/islandora_core_feature/config/install/field.storage.taxonomy_term.field_external_uri.yml b/modules/islandora_core_feature/config/optional/field.storage.taxonomy_term.field_external_uri.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/field.storage.taxonomy_term.field_external_uri.yml
rename to modules/islandora_core_feature/config/optional/field.storage.taxonomy_term.field_external_uri.yml
diff --git a/modules/islandora_core_feature/config/optional/filehash.settings.yml b/modules/islandora_core_feature/config/optional/filehash.settings.yml
new file mode 100644
index 000000000..3dcae2973
--- /dev/null
+++ b/modules/islandora_core_feature/config/optional/filehash.settings.yml
@@ -0,0 +1,24 @@
+algos:
+ blake2b_128: '0'
+ blake2b_160: '0'
+ blake2b_224: '0'
+ blake2b_256: '0'
+ blake2b_384: '0'
+ blake2b_512: '0'
+ md5: '0'
+ sha1: sha1
+ sha224: '0'
+ sha256: '0'
+ sha384: '0'
+ sha512_224: '0'
+ sha512_256: '0'
+ sha512: '0'
+ sha3_224: '0'
+ sha3_256: '0'
+ sha3_384: '0'
+ sha3_512: '0'
+dedupe: 0
+rehash: true
+original: true
+dedupe_original: false
+mime_types: { }
diff --git a/modules/islandora_core_feature/config/install/migrate_plus.migration.islandora_tags.yml b/modules/islandora_core_feature/config/optional/migrate_plus.migration.islandora_tags.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/migrate_plus.migration.islandora_tags.yml
rename to modules/islandora_core_feature/config/optional/migrate_plus.migration.islandora_tags.yml
diff --git a/modules/islandora_core_feature/config/install/migrate_plus.migration_group.islandora.yml b/modules/islandora_core_feature/config/optional/migrate_plus.migration_group.islandora.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/migrate_plus.migration_group.islandora.yml
rename to modules/islandora_core_feature/config/optional/migrate_plus.migration_group.islandora.yml
diff --git a/modules/islandora_core_feature/config/install/rest.resource.entity.file.yml b/modules/islandora_core_feature/config/optional/rest.resource.entity.file.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/rest.resource.entity.file.yml
rename to modules/islandora_core_feature/config/optional/rest.resource.entity.file.yml
diff --git a/modules/islandora_core_feature/config/install/rest.resource.entity.media.yml b/modules/islandora_core_feature/config/optional/rest.resource.entity.media.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/rest.resource.entity.media.yml
rename to modules/islandora_core_feature/config/optional/rest.resource.entity.media.yml
diff --git a/modules/islandora_core_feature/config/install/rest.resource.entity.node.yml b/modules/islandora_core_feature/config/optional/rest.resource.entity.node.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/rest.resource.entity.node.yml
rename to modules/islandora_core_feature/config/optional/rest.resource.entity.node.yml
diff --git a/modules/islandora_core_feature/config/install/rest.resource.entity.taxonomy_term.yml b/modules/islandora_core_feature/config/optional/rest.resource.entity.taxonomy_term.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/rest.resource.entity.taxonomy_term.yml
rename to modules/islandora_core_feature/config/optional/rest.resource.entity.taxonomy_term.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.delete_file_as_fedora_external_content.yml b/modules/islandora_core_feature/config/optional/system.action.delete_file_as_fedora_external_content.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.delete_file_as_fedora_external_content.yml
rename to modules/islandora_core_feature/config/optional/system.action.delete_file_as_fedora_external_content.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.delete_media_from_triplestore.yml b/modules/islandora_core_feature/config/optional/system.action.delete_media_from_triplestore.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.delete_media_from_triplestore.yml
rename to modules/islandora_core_feature/config/optional/system.action.delete_media_from_triplestore.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.delete_node_from_fedora.yml b/modules/islandora_core_feature/config/optional/system.action.delete_node_from_fedora.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.delete_node_from_fedora.yml
rename to modules/islandora_core_feature/config/optional/system.action.delete_node_from_fedora.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.delete_node_from_triplestore.yml b/modules/islandora_core_feature/config/optional/system.action.delete_node_from_triplestore.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.delete_node_from_triplestore.yml
rename to modules/islandora_core_feature/config/optional/system.action.delete_node_from_triplestore.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.delete_taxonomy_term_in_fedora.yml b/modules/islandora_core_feature/config/optional/system.action.delete_taxonomy_term_in_fedora.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.delete_taxonomy_term_in_fedora.yml
rename to modules/islandora_core_feature/config/optional/system.action.delete_taxonomy_term_in_fedora.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.delete_taxonomy_term_in_triplestore.yml b/modules/islandora_core_feature/config/optional/system.action.delete_taxonomy_term_in_triplestore.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.delete_taxonomy_term_in_triplestore.yml
rename to modules/islandora_core_feature/config/optional/system.action.delete_taxonomy_term_in_triplestore.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.index_file_as_fedora_external_content.yml b/modules/islandora_core_feature/config/optional/system.action.index_file_as_fedora_external_content.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.index_file_as_fedora_external_content.yml
rename to modules/islandora_core_feature/config/optional/system.action.index_file_as_fedora_external_content.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.index_media_in_fedora.yml b/modules/islandora_core_feature/config/optional/system.action.index_media_in_fedora.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.index_media_in_fedora.yml
rename to modules/islandora_core_feature/config/optional/system.action.index_media_in_fedora.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.index_media_in_triplestore.yml b/modules/islandora_core_feature/config/optional/system.action.index_media_in_triplestore.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.index_media_in_triplestore.yml
rename to modules/islandora_core_feature/config/optional/system.action.index_media_in_triplestore.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.index_node_in_fedora.yml b/modules/islandora_core_feature/config/optional/system.action.index_node_in_fedora.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.index_node_in_fedora.yml
rename to modules/islandora_core_feature/config/optional/system.action.index_node_in_fedora.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.index_node_in_triplestore.yml b/modules/islandora_core_feature/config/optional/system.action.index_node_in_triplestore.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.index_node_in_triplestore.yml
rename to modules/islandora_core_feature/config/optional/system.action.index_node_in_triplestore.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.index_taxonomy_term_in_fedora.yml b/modules/islandora_core_feature/config/optional/system.action.index_taxonomy_term_in_fedora.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.index_taxonomy_term_in_fedora.yml
rename to modules/islandora_core_feature/config/optional/system.action.index_taxonomy_term_in_fedora.yml
diff --git a/modules/islandora_core_feature/config/install/system.action.index_taxonomy_term_in_the_triplestore.yml b/modules/islandora_core_feature/config/optional/system.action.index_taxonomy_term_in_the_triplestore.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/system.action.index_taxonomy_term_in_the_triplestore.yml
rename to modules/islandora_core_feature/config/optional/system.action.index_taxonomy_term_in_the_triplestore.yml
diff --git a/modules/islandora_core_feature/config/install/taxonomy.vocabulary.islandora_media_use.yml b/modules/islandora_core_feature/config/optional/taxonomy.vocabulary.islandora_media_use.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/taxonomy.vocabulary.islandora_media_use.yml
rename to modules/islandora_core_feature/config/optional/taxonomy.vocabulary.islandora_media_use.yml
diff --git a/modules/islandora_core_feature/config/install/taxonomy.vocabulary.islandora_models.yml b/modules/islandora_core_feature/config/optional/taxonomy.vocabulary.islandora_models.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/taxonomy.vocabulary.islandora_models.yml
rename to modules/islandora_core_feature/config/optional/taxonomy.vocabulary.islandora_models.yml
diff --git a/modules/islandora_core_feature/config/install/views.view.all_taxonomy_terms.yml b/modules/islandora_core_feature/config/optional/views.view.all_taxonomy_terms.yml
similarity index 99%
rename from modules/islandora_core_feature/config/install/views.view.all_taxonomy_terms.yml
rename to modules/islandora_core_feature/config/optional/views.view.all_taxonomy_terms.yml
index 56b450669..8c3cb0f3d 100644
--- a/modules/islandora_core_feature/config/install/views.view.all_taxonomy_terms.yml
+++ b/modules/islandora_core_feature/config/optional/views.view.all_taxonomy_terms.yml
@@ -168,4 +168,3 @@ display:
- url.query_args
- user.permissions
tags: { }
-
diff --git a/modules/islandora_core_feature/config/install/views.view.display_media.yml b/modules/islandora_core_feature/config/optional/views.view.display_media.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/views.view.display_media.yml
rename to modules/islandora_core_feature/config/optional/views.view.display_media.yml
diff --git a/modules/islandora_core_feature/config/install/views.view.file_checksum.yml b/modules/islandora_core_feature/config/optional/views.view.file_checksum.yml
similarity index 62%
rename from modules/islandora_core_feature/config/install/views.view.file_checksum.yml
rename to modules/islandora_core_feature/config/optional/views.view.file_checksum.yml
index b498ba666..e529f21ec 100644
--- a/modules/islandora_core_feature/config/install/views.view.file_checksum.yml
+++ b/modules/islandora_core_feature/config/optional/views.view.file_checksum.yml
@@ -1,14 +1,15 @@
langcode: en
status: true
dependencies:
- enforced:
- module:
- - islandora_core_feature
module:
- file
- filehash
- rest
- serialization
+ - user
+ enforced:
+ module:
+ - islandora_core_feature
id: file_checksum
label: 'File Checksum'
module: views
@@ -16,71 +17,24 @@ description: 'Exposes a REST endpoint for getting the checksum of a File'
tag: ''
base_table: file_managed
base_field: fid
-core: 8.x
display:
default:
- display_plugin: default
id: default
display_title: Master
+ display_plugin: default
position: 0
display_options:
- access:
- type: none
- options: { }
- cache:
- type: tag
- options: { }
- query:
- type: views_query
- options:
- disable_sql_rewrite: false
- distinct: false
- replica: false
- query_comment: ''
- query_tags: { }
- exposed_form:
- type: basic
- options:
- submit_button: Apply
- reset_button: false
- reset_button_label: Reset
- exposed_sorts_label: 'Sort by'
- expose_sort_order: true
- sort_asc_label: Asc
- sort_desc_label: Desc
- pager:
- type: mini
- options:
- items_per_page: 10
- offset: 0
- id: 0
- total_pages: null
- expose:
- items_per_page: false
- items_per_page_label: 'Items per page'
- items_per_page_options: '5, 10, 25, 50'
- items_per_page_options_all: false
- items_per_page_options_all_label: '- All -'
- offset: false
- offset_label: Offset
- tags:
- previous: ‹‹
- next: ››
- style:
- type: serializer
- row:
- type: 'entity:file'
- options:
- relationship: none
- view_mode: default
fields:
sha1:
id: sha1
- table: filehash
+ table: file_managed
field: sha1
relationship: none
group_type: group
admin_label: ''
+ entity_type: file
+ entity_field: sha1
+ plugin_id: field
label: ''
exclude: false
alter:
@@ -92,7 +46,7 @@ display:
external: false
replace_spaces: false
path_case: none
- trim_whitespace: false
+ trim_whitespace: true
alt: ''
rel: ''
link_class: ''
@@ -106,7 +60,7 @@ display:
more_link: false
more_link_text: ''
more_link_path: ''
- strip_tags: false
+ strip_tags: true
trim: false
preserve_tags: ''
html: false
@@ -122,13 +76,120 @@ display:
hide_empty: false
empty_zero: false
hide_alter_empty: true
- plugin_id: standard
- filters: { }
- sorts: { }
- header: { }
- footer: { }
+ click_sort_column: value
+ type: filehash
+ settings: { }
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ original_sha1:
+ id: original_sha1
+ table: file_managed
+ field: original_sha1
+ relationship: none
+ group_type: group
+ admin_label: ''
+ entity_type: file
+ entity_field: original_sha1
+ plugin_id: field
+ label: ''
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: true
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: true
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: false
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: false
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: value
+ type: filehash
+ settings: { }
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ pager:
+ type: mini
+ options:
+ offset: 0
+ items_per_page: 10
+ total_pages: null
+ id: 0
+ tags:
+ next: ››
+ previous: ‹‹
+ expose:
+ items_per_page: false
+ items_per_page_label: 'Items per page'
+ items_per_page_options: '5, 10, 25, 50'
+ items_per_page_options_all: false
+ items_per_page_options_all_label: '- All -'
+ offset: false
+ offset_label: Offset
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ access:
+ type: perm
+ options:
+ perm: 'view checksums'
+ cache:
+ type: tag
+ options: { }
empty: { }
- relationships: { }
+ sorts: { }
arguments:
fid:
id: fid
@@ -137,6 +198,9 @@ display:
relationship: none
group_type: group
admin_label: ''
+ entity_type: file
+ entity_field: fid
+ plugin_id: file_fid
default_action: 'not found'
exception:
value: all
@@ -151,8 +215,8 @@ display:
summary_options:
base_path: ''
count: true
- items_per_page: 25
override: false
+ items_per_page: 25
summary:
sort_order: asc
number_of_records: 0
@@ -164,31 +228,47 @@ display:
validate_options: { }
break_phrase: false
not: false
- entity_type: file
- entity_field: fid
- plugin_id: file_fid
+ filters: { }
+ style:
+ type: serializer
+ row:
+ type: 'entity:file'
+ options:
+ relationship: none
+ view_mode: default
+ query:
+ type: views_query
+ options:
+ query_comment: ''
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_tags: { }
+ relationships: { }
+ header: { }
+ footer: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
+ - 'languages:language_content'
- 'languages:language_interface'
- request_format
- url
- url.query_args
+ - user.permissions
tags: { }
rest_export_1:
- display_plugin: rest_export
id: rest_export_1
display_title: 'REST export'
+ display_plugin: rest_export
position: 1
display_options:
- display_extenders: { }
- path: checksum/%file
pager:
type: some
options:
- items_per_page: 10
offset: 0
+ items_per_page: 10
style:
type: serializer
options:
@@ -198,6 +278,19 @@ display:
type: data_field
options:
field_options: { }
+ display_extenders:
+ matomo:
+ enabled: false
+ keyword_gets: ''
+ keyword_behavior: first
+ keyword_concat_separator: ' '
+ category_behavior: none
+ category_gets: ''
+ category_concat_separator: ' '
+ category_fallback: ''
+ category_facets: { }
+ category_facets_concat_separator: ', '
+ path: checksum/%file
auth:
- basic_auth
- jwt_auth
@@ -205,7 +298,9 @@ display:
cache_metadata:
max-age: -1
contexts:
+ - 'languages:language_content'
- 'languages:language_interface'
- request_format
- url
+ - user.permissions
tags: { }
diff --git a/modules/islandora_core_feature/config/install/views.view.manage_members.yml b/modules/islandora_core_feature/config/optional/views.view.manage_members.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/views.view.manage_members.yml
rename to modules/islandora_core_feature/config/optional/views.view.manage_members.yml
diff --git a/modules/islandora_core_feature/config/install/views.view.media_of.yml b/modules/islandora_core_feature/config/optional/views.view.media_of.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/views.view.media_of.yml
rename to modules/islandora_core_feature/config/optional/views.view.media_of.yml
diff --git a/modules/islandora_core_feature/config/install/views.view.non_fedora_files.yml b/modules/islandora_core_feature/config/optional/views.view.non_fedora_files.yml
similarity index 99%
rename from modules/islandora_core_feature/config/install/views.view.non_fedora_files.yml
rename to modules/islandora_core_feature/config/optional/views.view.non_fedora_files.yml
index 88b0f308e..b90494f5d 100644
--- a/modules/islandora_core_feature/config/install/views.view.non_fedora_files.yml
+++ b/modules/islandora_core_feature/config/optional/views.view.non_fedora_files.yml
@@ -194,4 +194,3 @@ display:
- url.query_args
- user.permissions
tags: { }
-
diff --git a/modules/islandora_core_feature/config/install/views.view.reorder_children.yml b/modules/islandora_core_feature/config/optional/views.view.reorder_children.yml
similarity index 100%
rename from modules/islandora_core_feature/config/install/views.view.reorder_children.yml
rename to modules/islandora_core_feature/config/optional/views.view.reorder_children.yml
diff --git a/modules/islandora_core_feature/islandora_core_feature.features.yml b/modules/islandora_core_feature/islandora_core_feature.features.yml
deleted file mode 100755
index 5af003a72..000000000
--- a/modules/islandora_core_feature/islandora_core_feature.features.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-bundle: islandora
-excluded:
- - language.content_settings.taxonomy_term.islandora_media_use
- - language.content_settings.taxonomy_term.islandora_models
-required:
- - features.bundle.islandora
- - field.storage.media.field_file_size
- - field.storage.media.field_height
- - field.storage.media.field_media_of
- - field.storage.media.field_media_use
- - field.storage.media.field_mime_type
- - field.storage.media.field_width
- - field.storage.node.field_member_of
- - field.storage.node.field_model
- - field.storage.node.field_weight
- - field.storage.taxonomy_term.field_external_uri
diff --git a/modules/islandora_core_feature/islandora_core_feature.info.yml b/modules/islandora_core_feature/islandora_core_feature.info.yml
index bf4f8d7a6..38992ea7b 100755
--- a/modules/islandora_core_feature/islandora_core_feature.info.yml
+++ b/modules/islandora_core_feature/islandora_core_feature.info.yml
@@ -1,13 +1,11 @@
name: 'Islandora Core Feature'
description: 'Minimum configuration required for Islandora.'
type: module
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9 || ^10
dependencies:
- drupal:basic_auth
- drupal:content_translation
- drupal:eva
- - drupal:features
- drupal:field
- drupal:file
- drupal:filehash
diff --git a/modules/islandora_core_feature/islandora_core_feature.post_update.php b/modules/islandora_core_feature/islandora_core_feature.post_update.php
new file mode 100644
index 000000000..10547231a
--- /dev/null
+++ b/modules/islandora_core_feature/islandora_core_feature.post_update.php
@@ -0,0 +1,20 @@
+getStorage('field_storage_config');
+ $field = $storage->load('node.field_weight');
+ $indexes = $field->getIndexes();
+ $indexes += [
+ 'value' => ['value'],
+ ];
+ $field->setIndexes($indexes);
+ $field->save();
+}
diff --git a/modules/islandora_iiif/README.md b/modules/islandora_iiif/README.md
index ab06524be..3375e4d20 100644
--- a/modules/islandora_iiif/README.md
+++ b/modules/islandora_iiif/README.md
@@ -1,4 +1,4 @@
-# Islandora IIIF
+# Islandora IIIF
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.2-8892BF.svg?style=flat-square)](https://php.net/)
[![Contribution Guidelines](http://img.shields.io/badge/CONTRIBUTING-Guidelines-blue.svg)](./CONTRIBUTING.md)
@@ -6,12 +6,12 @@
## Introduction
-Provides IIIF manifests using views.
+Provides [IIIF manifests](https://iiif.io) using views.
## Requirements
- `islandora` and `islandora_core_feature`
-- A IIIF image server (such as Cantaloupe)
+- A IIIF image server (such as Cantaloupe)
## Installation
@@ -20,7 +20,7 @@ For a full digital repository solution, see our [installation documentation](htt
To download/enable just this module, use the following from the command line:
```bash
-$ composer require islandora/islandora
+$ composer require drupal/islandora
$ drush en islandora_core_feature
$ drush mim islandora_tags
$ drush en islandora_iiif
@@ -32,9 +32,53 @@ You can set the following configuration at `admin/config/islandora/iiif`:
- IIIF Image server location
- The URL to your IIIF image server (without trailing slash).
+### Views Style Plugin
+
+This module implements a Views Style plugin. It provides the following settings:
+
+1. Tile Source: A field that was added to the views list of fields with the image to be served. This should be a File or Image type field on a Media.
+2. Structured Text field: This lets you specify a file field where OCR text with positional data, e.g., hOCR can be found.
+3. Structured Text Term: If your Islandora Object has a separate media with hOCR, point to it with this setting.
+4. File Width and Height fields: If you want to use TIFF or JP2 files directly, set the fields where the files' width and height can be found with these settings.
+
+### Action to add image dimensions from the IIIF server
+
+The module also provides an action that lets a site owner populate a TIFF or JP2 image's width and
+height attributes into fields on the media so the IIIF server is not bogged down trying to generate a manifest if
+it doesn't have them.
+
+It is an advanced action that must be configured. Go to
+Admin -> Actions UI -> Actions, choose
+"Add image dimensions retrieved from the IIIF server" from the Create Action drop-down
+And on the next screen, choose the Media Use term which this should be applied to (as
+it is a node action), as well as the width
+and height fields that the action should
+populate.
+
+To use it, either:
+- Add it as a derivative reaction (in Contexts) to a node, or
+- Use it as a batch action in a View, such as on a Paged Content object's list of child pages.
+
+### Setting up the action as a Contexts Derivative Reaction
+
+These instructions assume a standard Islandora Starter Site, so if you have different field names or Contexts, adjust accordingly.
+
+1. Go to admin/config/system/actions
+2. Choose "Add image dimensions retrieved from the IIIF server" from the Create advanced Action drop-down and then click Create.
+3. Enter Original File for the Source Media Use term.
+4. Choose media -- file -- Width and Media -- File -- Height for the corresponding configuration values and click Save.
+5. Go to admin/structure/context, and click Duplicate on the Page Derivatives row.
+6. Name the new context something like "Retrieve Page Dimensions" and edit it.
+7. This is the tricky bit, delete 'Original File' from the 'Media has term with URI' field and replace it with Service File. The explanation for this is that to retrieve a file from the IIIF server, it must be part of an Islandora Media that has been fully created and saved and given a URL. This hasn't happened yet when Original File derivatives are being created, so we need to hang our action onto a derivative that is created after the original one.
+8. Under Reactions, deselect the existing actions and select "Add image dimensions from IIIF server" and click Save and continue.
+9. Go back to your Paged Content object and add another child with a File media, to which you should upload another TIFF or JP2 file.
+10. Without going to the Original File Book Manifestt, make sure that a service file has been generated, then click Edit on the Original File media.
+11. Ensure that the Width and Height fields are populated with the correct values based on the file.
+
+
## Documentation
-Official documentation is available on the [Islandora 8 documentation site](https://islandora.github.io/documentation/).
+Official documentation is available on the [Islandora 2 documentation site](https://islandora.github.io/documentation/).
## Development
@@ -42,8 +86,6 @@ If you would like to contribute, please get involved by attending our weekly [Te
If you would like to contribute code to the project, you need to be covered by an Islandora Foundation [Contributor License Agreement](http://islandora.ca/sites/default/files/islandora_cla.pdf) or [Corporate Contributor License Agreement](http://islandora.ca/sites/default/files/islandora_ccla.pdf). Please see the [Contributors](http://islandora.ca/resources/contributors) pages on Islandora.ca for more information.
-We recommend using the [islandora-playbook](https://github.com/Islandora-Devops/islandora-playbook) to get started.
-
## License
[GPLv2](http://www.gnu.org/licenses/gpl-2.0.txt)
diff --git a/modules/islandora_iiif/config/schema/islandora_iiif.schema.yml b/modules/islandora_iiif/config/schema/islandora_iiif.schema.yml
index fc62c5c4c..46a7f569c 100644
--- a/modules/islandora_iiif/config/schema/islandora_iiif.schema.yml
+++ b/modules/islandora_iiif/config/schema/islandora_iiif.schema.yml
@@ -5,11 +5,39 @@ islandora_iiif.settings:
iiif_server:
type: string
label: 'IIIF Server Url'
+ use_relative_paths:
+ type: boolean
+ label: 'Use relative paths in manifest.'
+ show_title:
+ type: string
+ label: 'Show title in view'
views.style.iiif_manifest:
type: views_style
mapping:
iiif_tile_field:
type: sequence
+ label: "Tile source field(s)"
+ sequence:
+ type: string
+ iiif_ocr_file_field:
+ type: sequence
+ label: "Structured OCR data file field"
sequence:
type: string
+ structured_text_term_uri:
+ type: string
+ label: "Structured text term"
+ search_endpoint:
+ type: string
+ label: "Search endpoint path"
+
+action.configuration.media_attributes_from_iiif_action:
+ type: mapping
+ mapping:
+ source_term_uri:
+ type: string
+ width_field:
+ type: string
+ height_field:
+ type: string
diff --git a/modules/islandora_iiif/islandora_iiif.info.yml b/modules/islandora_iiif/islandora_iiif.info.yml
index 0492158a2..39b835c09 100644
--- a/modules/islandora_iiif/islandora_iiif.info.yml
+++ b/modules/islandora_iiif/islandora_iiif.info.yml
@@ -1,8 +1,7 @@
name: 'Islandora IIIF'
type: module
description: 'IIIF support for Islandora'
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9 || ^10
package: Islandora
dependencies:
- drupal:islandora
diff --git a/modules/islandora_iiif/islandora_iiif.install b/modules/islandora_iiif/islandora_iiif.install
new file mode 100644
index 000000000..dc9af68bf
--- /dev/null
+++ b/modules/islandora_iiif/islandora_iiif.install
@@ -0,0 +1,41 @@
+listAll();
+ $views_configs = array_values(array_filter($all_configs, function ($config) {
+ return str_starts_with($config, 'views.view.');
+ }));
+
+ foreach ($views_configs as $views_config_name) {
+ $needs_save = FALSE;
+ $view_config = $config_factory->getEditable($views_config_name);
+ $displays = $view_config->get('display');
+ foreach ($displays as $display_name => $display) {
+ if ($display['display_plugin'] == 'rest_export'
+ && $display['display_options']['style']['type'] == 'iiif_manifest'
+ &&!empty($display['display_options']['style']['options']['iiif_ocr_file_field'])) {
+
+ $display['display_options']['style']['options']['advanced']['iiif_ocr_file_field'] = $display['display_options']['style']['options']['iiif_ocr_file_field'];
+ unset($display['display_options']['style']['options']['iiif_ocr_file_field']);
+ $view_config->set('display.' . $display_name . '.display_options.style.options', $display['display_options']['style']['options']);
+ $needs_save = TRUE;
+ }
+ }
+ if ($needs_save) {
+ $view_config->save();
+ }
+ }
+}
diff --git a/modules/islandora_iiif/islandora_iiif.services.yml b/modules/islandora_iiif/islandora_iiif.services.yml
new file mode 100644
index 000000000..fd39211cd
--- /dev/null
+++ b/modules/islandora_iiif/islandora_iiif.services.yml
@@ -0,0 +1,4 @@
+services:
+ islandora_iiif:
+ class: Drupal\islandora_iiif\IiifInfo
+ arguments: ['@config.factory', '@http_client', '@logger.channel.islandora', '@jwt.authentication.jwt']
diff --git a/modules/islandora_iiif/src/Form/IslandoraIIIFConfigForm.php b/modules/islandora_iiif/src/Form/IslandoraIIIFConfigForm.php
index dc750a5a9..41bd84a4e 100644
--- a/modules/islandora_iiif/src/Form/IslandoraIIIFConfigForm.php
+++ b/modules/islandora_iiif/src/Form/IslandoraIIIFConfigForm.php
@@ -66,13 +66,37 @@ public function getFormId() {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
+ $options = [
+ 'none' => $this->t('None'),
+ 'view' => $this->t("From view title"),
+ 'node' => $this->t("From node title"),
+ ];
$config = $this->config('islandora_iiif.settings');
$form['iiif_server'] = [
'#type' => 'url',
'#title' => $this->t('IIIF Image server location'),
'#description' => $this->t('Please enter the image server location without trailing slash. e.g. http://www.example.org/iiif/2.'),
'#default_value' => $config->get('iiif_server'),
+ '#config' => [
+ 'key' => 'islandora_iiif.settings:iiif_server',
+ ],
+ ];
+
+ $form['use_relative_paths'] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t("Use relative file paths in manifest."),
+ '#description' => $this->t("Check this if your IIIF Server is configured to access files locally. If unchecked, the absolute URL will be given and the IIIF server will make requests to this site to retrieve images."),
+ '#default_value' => $config->get('use_relative_paths'),
];
+
+ $form['show_title'] = [
+ '#type' => 'select',
+ '#options' => $options,
+ '#title' => $this->t("Show title in viewer."),
+ '#description' => $this->t("Show title on your viewer, if viewer allows"),
+ '#default_value' => $config->get('show_title'),
+ ];
+
return parent::buildForm($form, $form_state);
}
@@ -99,6 +123,8 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
$this->config('islandora_iiif.settings')
->set('iiif_server', $form_state->getValue('iiif_server'))
+ ->set('use_relative_paths', $form_state->getValue('use_relative_paths'))
+ ->set('show_title', $form_state->getValue('show_title'))
->save();
}
diff --git a/modules/islandora_iiif/src/IiifInfo.php b/modules/islandora_iiif/src/IiifInfo.php
new file mode 100644
index 000000000..f2b1138ad
--- /dev/null
+++ b/modules/islandora_iiif/src/IiifInfo.php
@@ -0,0 +1,151 @@
+configFactory = $config_factory;
+
+ $this->iiifConfig = $this->configFactory->get('islandora_iiif.settings');
+ $this->httpClient = $http_client;
+ $this->logger = $channel;
+ $this->jwtAuth = $jwt_auth;
+ }
+
+ /**
+ * The IIIF base URL for an image.
+ *
+ * Visiting this URL will resolve to the info.json for the image.
+ *
+ * @return string
+ * The absolute URL on the IIIF server.
+ */
+ public function baseUrl($image) {
+
+ if ($this->iiifConfig->get('use_relative_paths')) {
+ $file_url = ltrim($image->createFileUrl(TRUE), '/');
+ }
+ else {
+ $file_url = $image->createFileUrl(FALSE);
+ }
+
+ $iiif_address = $this->iiifConfig->get('iiif_server');
+ $iiif_url = rtrim($iiif_address, '/') . '/' . urlencode($file_url);
+
+ return $iiif_url;
+ }
+
+ /**
+ * Retrieve an image's original dimensions via the IIIF server.
+ *
+ * @param \Drupal\File\FileInterface $file
+ * The image file.
+ *
+ * @return array|false
+ * The image dimensions in an array as [$width, $height]
+ */
+ public function getImageDimensions(FileInterface $file) {
+ $iiif_url = $this->baseUrl($file);
+ try {
+ $info_json = $this->httpClient->request('get', $iiif_url, [
+ 'headers' => [
+ 'Authorization' => 'Bearer ' . $this->jwtAuth->generateToken(),
+ ],
+ ])->getBody();
+ $resource = json_decode($info_json, TRUE);
+ $width = $resource['width'];
+ $height = $resource['height'];
+ if (is_numeric($width) && is_numeric($height)) {
+ return [intval($width), intval($height)];
+ }
+ }
+ catch (ClientException | ConnectException | RequestException | ServerException $e) {
+ $this->logger->info("Error getting image file dimensions from IIIF server: " . $e->getMessage());
+ }
+ return FALSE;
+ }
+
+ /**
+ * The IIIF base URL for an image.
+ *
+ * Visiting this URL resolves to the image resized to the maximum dimensions.
+ *
+ * @param \Drupal\file\FileInterface $image
+ * The image entity.
+ * @param int $width
+ * The maximum width of the image to be returned. 0 for no constraint.
+ * @param int $height
+ * The maxim um height of the image to be returned. 0 for no contraint.
+ *
+ * @return string
+ * The IIIF URl to retrieve the full image with the given max dimensions.
+ */
+ public function getImageWithMaxDimensions(FileInterface $image, $width = 0, $height = 0) {
+ $base_url = $this->baseUrl($image);
+ return $base_url . "/full/!$width,$height/0/default.jpg";
+
+ }
+
+}
diff --git a/modules/islandora_iiif/src/Plugin/Action/MediaAttributesFromIiif.php b/modules/islandora_iiif/src/Plugin/Action/MediaAttributesFromIiif.php
new file mode 100644
index 000000000..43813b506
--- /dev/null
+++ b/modules/islandora_iiif/src/Plugin/Action/MediaAttributesFromIiif.php
@@ -0,0 +1,302 @@
+configFactory = $config_factory;
+ $this->entityTypeManager = $entity_type_manager;
+ $this->httpClient = $http_client;
+ $this->iiifInfo = $iiif_info;
+ $this->utils = $islandora_utils;
+ $this->mediaSource = $media_source;
+ $this->logger = $channel;
+ $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('entity_type.manager'),
+ $container->get('datetime.time'),
+ $container->get('http_client'),
+ $container->get('islandora_iiif'),
+ $container->get('islandora.utils'),
+ $container->get('islandora.media_source_service'),
+ $container->get('logger.channel.islandora'),
+ $container->get('entity_field.manager'),
+ $container->get('config.factory')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function execute($entity = NULL) {
+ $width = $height = FALSE;
+
+ // Get the selected media use term.
+ $source_term = $this->utils->getTermForUri($this->configuration['source_term_uri']);
+
+ $source_mids = $this->utils->getMediaReferencingNodeAndTerm($entity, $source_term);
+ if (!empty($source_mids)) {
+
+ foreach ($source_mids as $source_mid) {
+
+ /**
+ * @var \Drupal\Media\MediaInterface
+ */
+ $source_media = $this->entityTypeManager->getStorage('media')->load($source_mid);
+
+ // Get the media MIME Type.
+ $source_file = $this->mediaSource->getSourceFile($source_media);
+ $mime_type = $source_file->getMimeType();
+
+ if (in_array($mime_type, ['image/tiff', 'image/jp2'])) {
+ [$width, $height] = $this->iiifInfo->getImageDimensions($source_file);
+ }
+
+ $width_field = $this->getShortFieldName($this->configuration['width_field']);
+ $height_field = $this->getShortFieldName($this->configuration['height_field']);
+ if ($source_media->hasField($width_field) && $source_media->hasField($height_field)) {
+ $source_media->set($width_field, $width);
+ $source_media->set($height_field, $height);
+ $source_media->save();
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+
+ /**
+* @var \Drupal\Core\Entity\EntityInterface $object
+*/
+ return $object->access('update', $account, $return_as_object);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+
+ $integer_fields = $this->getIntegerFields();
+
+ $form['source_term'] = [
+ '#type' => 'entity_autocomplete',
+ '#target_type' => 'taxonomy_term',
+ '#title' => $this->t('Media use term of media to process.'),
+ '#default_value' => $this->utils->getTermForUri($this->configuration['source_term_uri']),
+ '#required' => TRUE,
+ '#description' => $this->t('Term that indicates the child media on which to perform this action.'),
+ ];
+
+ $form['width_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Width Field'),
+ '#description' => $this->t("Field to populate with an image's width."),
+ '#default_value' => $this->configuration['width_field'],
+ '#options' => $integer_fields,
+ ];
+
+ $form['height_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Height Field'),
+ '#description' => $this->t("Field to populate with an image's height."),
+ '#default_value' => $this->configuration['height_field'],
+ '#options' => $integer_fields,
+ ];
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function defaultConfiguration() {
+ $config = parent::defaultConfiguration();
+
+ $config['source_term_uri'] = '';
+ $config['width_field'] = '';
+ $config['height_field'] = '';
+
+ return $config;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+ $tid = $form_state->getValue('source_term');
+ $term = $this->entityTypeManager->getStorage('taxonomy_term')->load($tid);
+ $this->configuration['source_term_uri'] = $this->utils->getUriForTerm($term);
+
+ $this->configuration['width_field'] = $form_state->getValue('width_field');
+ $this->configuration['height_field'] = $form_state->getValue('height_field');
+
+ }
+
+ /**
+ * Get all fields of an entity that are integer types.
+ *
+ * @return array
+ * The integer type fields.
+ */
+ protected function getIntegerFields() {
+ // Get media types.
+ $media_types = $this->entityTypeManager->getStorage('media_type')->loadMultiple();
+ $all_integer_fields = [];
+ foreach (array_keys($media_types) as $key => $value) {
+ $fields = $this->entityFieldManager->getFieldDefinitions("media", $value);
+
+ $integer_fields = array_filter(
+ $fields,
+ function ($field_value, $field_key) {
+ // Only keep fields of type 'integer'.
+ return (strpos($field_value->getType(), 'integer') > -1)
+ && is_a($field_value, '\Drupal\Core\Field\FieldConfigInterface');
+ }, ARRAY_FILTER_USE_BOTH
+ );
+ foreach ($integer_fields as $integer_field) {
+ $all_integer_fields[$integer_field->id()] = $integer_field->getTargetEntityTypeId()
+ . ' -- ' . $integer_field->getTargetBundle() . ' -- ' . $integer_field->getLabel();
+ }
+
+ }
+ return $all_integer_fields;
+ }
+
+ /**
+ * Returns the last part of a qualified field name.
+ *
+ * @param string $field_id
+ * The full field id, e.g., 'media.file.field_height'.
+ *
+ * @return string
+ * The short field name, e.g., 'field_height'.
+ */
+ protected function getShortFieldName(string $field_id): string {
+ [$entity_type, $bundle, $field_name] = explode('.', $field_id);
+ return $field_name;
+ }
+
+}
diff --git a/modules/islandora_iiif/src/Plugin/views/style/IIIFManifest.php b/modules/islandora_iiif/src/Plugin/views/style/IIIFManifest.php
index 194670016..86389c0d8 100644
--- a/modules/islandora_iiif/src/Plugin/views/style/IIIFManifest.php
+++ b/modules/islandora_iiif/src/Plugin/views/style/IIIFManifest.php
@@ -2,21 +2,25 @@
namespace Drupal\islandora_iiif\Plugin\views\style;
-use Drupal\views\Plugin\views\style\StylePluginBase;
+use Drupal\Core\Config\ImmutableConfig;
+use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Field\FieldItemInterface;
+use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Url;
+use Drupal\islandora\IslandoraUtils;
+use Drupal\islandora_iiif\IiifInfo;
+use Drupal\media\MediaInterface;
+use Drupal\taxonomy\TermInterface;
+use Drupal\views\Plugin\views\style\StylePluginBase;
use Drupal\views\ResultRow;
+use GuzzleHttp\Client;
use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\HttpFoundation\Request;
-use Drupal\Core\Config\ImmutableConfig;
-use Drupal\Core\File\FileSystemInterface;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\ClientException;
-use GuzzleHttp\Exception\ConnectException;
-use GuzzleHttp\Exception\ServerException;
+use Symfony\Component\Serializer\SerializerInterface;
/**
* Provide serializer format for IIIF Manifest.
@@ -32,6 +36,13 @@
*/
class IIIFManifest extends StylePluginBase {
+ /**
+ * Islandora utility functions.
+ *
+ * @var \Drupal\islandora\IslandoraUtils
+ */
+ protected $utils;
+
/**
* {@inheritdoc}
*/
@@ -56,6 +67,13 @@ class IIIFManifest extends StylePluginBase {
*/
protected $serializer;
+ /**
+ * The IIIF Info service.
+ *
+ * @var \Drupal\islandora_iiif\IiifInfo
+ */
+ protected $iiifInfo;
+
/**
* The request service.
*
@@ -84,6 +102,13 @@ class IIIFManifest extends StylePluginBase {
*/
protected $fileSystem;
+ /**
+ * The Guzzle HTTP Client.
+ *
+ * @var \GuzzleHttp\Client
+ */
+ protected $httpClient;
+
/**
* The messenger.
*
@@ -91,10 +116,31 @@ class IIIFManifest extends StylePluginBase {
*/
protected $messenger;
+ /**
+ * Module Handler for running hooks.
+ *
+ * @var \Drupal\Core\Extention\ModuleHandlerInterface
+ */
+ protected $moduleHandler;
+
+ /**
+ * Memoized structured text term.
+ *
+ * @var \Drupal\taxonomy\TermInterface|null
+ */
+ protected ?TermInterface $structuredTextTerm;
+
+ /**
+ * Flag to track if we _have_ attempted a lookup, as the value is nullable.
+ *
+ * @var bool
+ */
+ protected bool $structuredTextTermMemoized = FALSE;
+
/**
* {@inheritdoc}
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, SerializerInterface $serializer, Request $request, ImmutableConfig $iiif_config, EntityTypeManagerInterface $entity_type_manager, FileSystemInterface $file_system, Client $http_client, MessengerInterface $messenger) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, SerializerInterface $serializer, Request $request, ImmutableConfig $iiif_config, EntityTypeManagerInterface $entity_type_manager, FileSystemInterface $file_system, Client $http_client, MessengerInterface $messenger, ModuleHandlerInterface $moduleHandler, IslandoraUtils $utils, IiifInfo $iiif_info) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->serializer = $serializer;
@@ -104,6 +150,10 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
$this->fileSystem = $file_system;
$this->httpClient = $http_client;
$this->messenger = $messenger;
+ $this->utils = $utils;
+ $this->moduleHandler = $moduleHandler;
+ $this->utils = $utils;
+ $this->iiifInfo = $iiif_info;
}
/**
@@ -120,10 +170,23 @@ public static function create(ContainerInterface $container, array $configuratio
$container->get('entity_type.manager'),
$container->get('file_system'),
$container->get('http_client'),
- $container->get('messenger')
+ $container->get('messenger'),
+ $container->get('module_handler'),
+ $container->get('islandora.utils'),
+ $container->get('islandora_iiif')
);
}
+ /**
+ * Return the request property.
+ *
+ * @return \Symfony\Component\HttpFoundation\Request
+ * The Symfony request object
+ */
+ public function getRequest() {
+ return $this->request;
+ }
+
/**
* {@inheritdoc}
*/
@@ -136,17 +199,35 @@ public function render() {
$request_url = $this->request->getRequestUri();
// Strip off the last URI component to get the base ID of the URL.
// @todo assumming the view is a path like /node/1/manifest.json
- $url_components = explode('/', $request_url);
+ $url_components = explode('/', trim($request_url, '/'));
array_pop($url_components);
- $content_path = implode('/', $url_components);
- $iiif_base_id = $request_host . '/' . $content_path;
+ $content_path = '/' . implode('/', $url_components);
+ $iiif_base_id = "{$request_host}{$content_path}";
+ $display = $this->iiifConfig->get('show_title');
+ switch ($display) {
+ case 'none':
+ $label = '';
+ break;
+
+ case 'view':
+ $label = $this->view->getTitle();
+ break;
+
+ case 'node':
+ $label = $this->getEntityTitle($content_path);
+
+ break;
+
+ default:
+ $label = $this->t("IIIF Manifest");
+ }
// @see https://iiif.io/api/presentation/2.1/#manifest
$json += [
'@type' => 'sc:Manifest',
'@id' => $request_url,
// If the View has a title, set the View title as the manifest label.
- 'label' => $this->view->getTitle() ?: $this->getEntityTitle($content_path),
+ 'label' => $label,
'@context' => 'http://iiif.io/api/presentation/2/context.json',
// @see https://iiif.io/api/presentation/2.1/#sequence
'sequences' => [
@@ -170,6 +251,12 @@ public function render() {
$content_type = 'json';
+ // Add a search endpoint if one is defined.
+ $this->addSearchEndpoint($json, $url_components);
+
+ // Give other modules a chance to alter the manifest.
+ $this->moduleHandler->alter('islandora_iiif_manifest', $json, $this);
+
return $this->serializer->serialize($json, $content_type, ['views_style_plugin' => $this]);
}
@@ -189,22 +276,28 @@ public function render() {
*/
protected function getTileSourceFromRow(ResultRow $row, $iiif_address, $iiif_base_id) {
$canvases = [];
- foreach ($this->options['iiif_tile_field'] as $iiif_tile_field) {
+ foreach (array_filter(array_values($this->options['iiif_tile_field'])) as $iiif_tile_field) {
$viewsField = $this->view->field[$iiif_tile_field];
$entity = $viewsField->getEntity($row);
if (isset($entity->{$viewsField->definition['field_name']})) {
-
/** @var \Drupal\Core\Field\FieldItemListInterface $images */
$images = $entity->{$viewsField->definition['field_name']};
- foreach ($images as $image) {
+ foreach ($images as $i => $image) {
if (!$image->entity->access('view')) {
// If the user does not have permission to view the file, skip it.
continue;
}
+
// Create the IIIF URL for this file
// Visiting $iiif_url will resolve to the info.json for the image.
- $file_url = $image->entity->createFileUrl(FALSE);
+ if ($this->iiifConfig->get('use_relative_paths')) {
+ $file_url = ltrim($image->entity->createFileUrl(TRUE), '/');
+ }
+ else {
+ $file_url = $image->entity->createFileUrl(FALSE);
+ }
+
$mime_type = $image->entity->getMimeType();
$iiif_url = rtrim($iiif_address, '/') . '/' . urlencode($file_url);
@@ -212,37 +305,13 @@ protected function getTileSourceFromRow(ResultRow $row, $iiif_address, $iiif_bas
$canvas_id = $iiif_base_id . '/canvas/' . $entity->id();
$annotation_id = $iiif_base_id . '/annotation/' . $entity->id();
- // Try to fetch the IIIF metadata for the image.
- try {
- $info_json = $this->httpClient->get($iiif_url)->getBody();
- $resource = json_decode($info_json, TRUE);
- $width = $resource['width'];
- $height = $resource['height'];
- }
- catch (ClientException | ServerException | ConnectException $e) {
- // If we couldn't get the info.json from IIIF
- // 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.
- $properties = $image->getProperties();
- $width = isset($properties['width']) ? $properties['width'] : 0;
- $height = isset($properties['height']) ? $properties['height'] : 0;
-
- // If this is a TIFF AND we don't know the width/height
- // see if we can get the image size via PHP's core function.
- if ($mime_type === 'image/tiff' && !$width || !$height) {
- $uri = $image->entity->getFileUri();
- $path = $this->fileSystem->realpath($uri);
- $image_size = getimagesize($path);
- if ($image_size) {
- $width = $image_size[0];
- $height = $image_size[1];
- }
- }
- }
+ [$width, $height] = $this->getCanvasDimensions($iiif_url, $entity, $image, $mime_type);
+
+ if ($width == 0) {
+ continue;
}
- $canvases[] = [
+ $tmp_canvas = [
// @see https://iiif.io/api/presentation/2.1/#canvas
'@id' => $canvas_id,
'@type' => 'sc:Canvas',
@@ -271,6 +340,24 @@ protected function getTileSourceFromRow(ResultRow $row, $iiif_address, $iiif_bas
],
],
];
+
+ if ($ocr_url = $this->getOcrUrl($entity)) {
+ $tmp_canvas['seeAlso'] = [
+ '@id' => $ocr_url,
+ 'format' => 'text/vnd.hocr+html',
+ 'profile' => 'http://kba.cloud/hocr-spec',
+ 'label' => 'hOCR embedded text',
+ ];
+ }
+
+ // Give other modules a chance to alter the canvas.
+ $alter_options = [
+ 'options' => $this->options,
+ 'views_plugin' => $this,
+ ];
+ $this->moduleHandler->alter('islandora_iiif_manifest_canvas', $tmp_canvas, $row, $alter_options);
+
+ $canvases[] = $tmp_canvas;
}
}
}
@@ -278,6 +365,130 @@ protected function getTileSourceFromRow(ResultRow $row, $iiif_address, $iiif_bas
return $canvases;
}
+ /**
+ * Try to fetch the IIIF metadata for the image.
+ *
+ * @param string $iiif_url
+ * Base URL of the canvas.
+ * @param \Drupal\media\MediaInterface $media
+ * The Media entity.
+ * @param \Drupal\Core\Field\FieldItemInterface $image
+ * The image field.
+ * @param string $mime_type
+ * The mime type of the image.
+ *
+ * @return [string]
+ * The width and height of the image.
+ */
+ protected function getCanvasDimensions(string $iiif_url, MediaInterface $media, FieldItemInterface $image, string $mime_type) {
+
+ if (isset($image->width) && is_numeric($image->width)
+ && isset($image->height) && is_numeric($image->height)) {
+ return [intval($image->width),
+ intval($image->height),
+ ];
+ }
+
+ if ($properties = $image->getProperties()
+ && isset($properties['width']) && is_numeric($properties['width'])
+ && isset($properties['height']) && is_numeric($properties['width'])) {
+ return [intval($properties['width']),
+ intval($properties['height']),
+ ];
+ }
+
+ $entity = $image->entity;
+
+ if ($entity->hasField('field_height') && !$entity->get('field_height')->isEmpty()
+ && $entity->get('field_height')->value > 0
+ && $entity->hasField('field_width')
+ && !$entity->get('field_width')->isEmpty()
+ && $entity->get('field_width')->value > 0) {
+ return [$entity->get('field_width')->value,
+ $entity->get('field_height')->value,
+ ];
+ }
+
+ // If the media has width and height fields, return those values.
+ $width_field = !empty($this->options['advanced']['custom_width_height']['width_field']) ? $this->options['advanced']['custom_width_height']['height_field'] : 'field_width';
+ $height_field = !empty($this->options['advanced']['custom_width_height']['height_field']) ? $this->options['advanced']['custom_width_height']['height_field'] : 'field_height';
+ if ($media->hasField($height_field)
+ && !$media->get($height_field)->isEmpty()
+ && $media->get($height_field)->value > 0
+ && $media->hasField($width_field)
+ && !$media->get($width_field)->isEmpty()
+ && $media->get($width_field)->value > 0) {
+ return [intval($media->get($width_field)->value),
+ intval($media->get($height_field)->value),
+ ];
+ }
+
+ if ($mime_type === 'image/tiff') {
+ // If this is a TIFF AND we don't know the width/height
+ // see if we can get the image size via PHP's core function.
+ $uri = $image->entity->getFileUri();
+ $path = $this->fileSystem->realpath($uri);
+ if (!empty($path)) {
+ $image_size = getimagesize($path);
+ if ($image_size) {
+ return [intval($image_size[0]),
+ intval($image_size[1]),
+ ];
+ }
+ }
+ }
+
+ // As a last resort, get it from the IIIF server.
+ // This can be very slow and will fail if there are too many pages.
+ $dimensions = $this->iiifInfo->getImageDimensions($image->entity);
+ if ($dimensions !== FALSE) {
+ $this->storeImageDimensions($media, $dimensions[0], $dimensions[1]);
+ return $dimensions;
+ }
+
+ return [0, 0];
+ }
+
+ /**
+ * Retrieves a URL text with positional data such as hOCR.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity at the current row.
+ *
+ * @return string|false
+ * The URL where the OCR text is found.
+ */
+ protected function getOcrUrl(EntityInterface $entity) {
+ $ocr_url = FALSE;
+ $iiif_ocr_file_field = !empty($this->options['advanced']['iiif_ocr_file_field']) ? array_filter(array_values($this->options['advanced']['iiif_ocr_file_field'])) : [];
+ $ocrField = count($iiif_ocr_file_field) > 0 ? $this->view->field[$iiif_ocr_file_field[0]] : NULL;
+ if ($ocrField) {
+ $ocr_entity = $entity;
+ $ocr_field_name = $ocrField->definition['field_name'];
+ if (!is_null($ocr_field_name)) {
+ $ocrs = $ocr_entity->{$ocr_field_name};
+ $ocr = $ocrs[0] ?? FALSE;
+ if ($ocr) {
+ $ocr_url = $ocr->entity->createFileUrl(FALSE);
+ }
+ }
+ }
+ elseif ($structured_text_term = $this->getStructuredTextTerm()) {
+ $parent_node = $this->utils->getParentNode($entity);
+ $ocr_entity_array = $this->utils->getMediaReferencingNodeAndTerm($parent_node, $structured_text_term);
+ $ocr_entity_id = is_array($ocr_entity_array) ? array_shift($ocr_entity_array) : NULL;
+ $ocr_entity = $ocr_entity_id ? $this->entityTypeManager->getStorage('media')->load($ocr_entity_id) : NULL;
+ if ($ocr_entity) {
+ $ocr_file_source = $ocr_entity->getSource();
+ $ocr_fid = $ocr_file_source->getSourceFieldValue($ocr_entity);
+ $ocr_file = $this->entityTypeManager->getStorage('file')->load($ocr_fid);
+ $ocr_url = $ocr_file->createFileUrl(FALSE);
+ }
+ }
+
+ return $ocr_url;
+ }
+
/**
* Pull a title from the node or media passed to this view.
*
@@ -313,10 +524,37 @@ protected function defineOptions() {
$options = parent::defineOptions();
$options['iiif_tile_field'] = ['default' => ''];
+ $options['iiif_ocr_file_field'] = ['default' => ''];
return $options;
}
+ /**
+ * Add the configured search endpoint to the manifest.
+ *
+ * @param array $json
+ * The IIIF manifest.
+ * @param array $url_components
+ * The search endpoint URL as array.
+ */
+ protected function addSearchEndpoint(array &$json, array $url_components) {
+ $url_base = $this->getRequest()->getSchemeAndHttpHost();
+ $hocr_search_path = $this->options['search_endpoint'] ?? NULL;
+
+ if ($hocr_search_path) {
+ $hocr_search_url = $url_base . '/' . ltrim($hocr_search_path, '/');
+
+ $hocr_search_url = str_replace('%node', $url_components[1], $hocr_search_url);
+
+ $json['service'][] = [
+ "@context" => "http://iiif.io/api/search/0/context.json",
+ "@id" => $hocr_search_url,
+ "profile" => "http://iiif.io/api/search/0/search",
+ "label" => t("Search inside this work"),
+ ];
+ }
+ }
+
/**
* {@inheritdoc}
*/
@@ -336,6 +574,8 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
// File formatters.
'file_default', 'file_url_plain',
];
+ $dimensions_field_options = [];
+
/** @var \Drupal\views\Plugin\views\field\FieldPluginBase[] $fields */
foreach ($fields as $field_name => $field) {
// If this is a known Islandora file/image field
@@ -349,6 +589,10 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
(!empty($field->options['type']) && in_array($field->options['type'], $file_views_field_formatters))) {
$field_options[$field_name] = $field->adminLabel();
}
+ else {
+ // Put it in the list of fields that may contain the custom value.
+ $dimensions_field_options[$field_name] = $field->adminLabel();
+ }
}
// If no fields to choose from, add an error message indicating such.
@@ -357,6 +601,8 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
You will need to add a field to this View'), 'error');
}
+ $dimensions_field_options = array_merge(['' => ' - None -- '], $dimensions_field_options);
+
$form['iiif_tile_field'] = [
'#title' => $this->t('Tile source field(s)'),
'#type' => 'checkboxes',
@@ -368,6 +614,58 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
// otherwise could lock up the form when setting up a View.
'#required' => count($field_options) > 0,
];
+
+ $form['advanced'] = [
+ '#type' => 'details',
+ '#title' => $this->t('Advanced'),
+ '#open' => FALSE,
+ ];
+
+ $form['advanced']['custom_width_height'] = [
+ '#type' => 'fieldset',
+ '#title' => $this->t('Custom width and height fields.'),
+ '#description' => $this->t('Use these if the media type of the image does not have built in Width and height fields, e.g., File. As a fallback, if the media has fields with the name "field_width" and "field_height" this formatter will try and get the width from that.'),
+ ];
+
+ $form['advanced']['custom_width_height']['height_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Custom Height field'),
+ '#default_value' => $this->options['advanced']['custom_width_height']['height_field'],
+ '#options' => $dimensions_field_options,
+ ];
+
+ $form['advanced']['custom_width_height']['width_field'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Custom width field'),
+ '#default_value' => $this->options['advanced']['custom_width_height']['width_field'],
+ '#options' => $dimensions_field_options,
+ ];
+
+ $form['advanced']['iiif_ocr_file_field'] = [
+ '#title' => $this->t('Structured OCR data file field'),
+ '#type' => 'checkboxes',
+ '#default_value' => $this->options['advanced']['iiif_ocr_file_field'],
+ '#description' => $this->t("If the hOCR is a field on the same entity as the image source field above, select it here. If it's found in a related entity via the term below, leave this blank."),
+ '#options' => $field_options,
+ '#required' => FALSE,
+ ];
+
+ $form['structured_text_term'] = [
+ '#type' => 'entity_autocomplete',
+ '#target_type' => 'taxonomy_term',
+ '#title' => $this->t('Structured OCR text term'),
+ '#default_value' => $this->getStructuredTextTerm(),
+ '#required' => FALSE,
+ '#description' => $this->t('Term indicating the media that holds structured text, such as hOCR, for the given object. Use this if the text is on a separate media from the tile source.'),
+ ];
+
+ $form['search_endpoint'] = [
+ '#type' => 'textfield',
+ '#title' => $this->t("Search endpoint path."),
+ '#description' => $this->t("If there is a search endpoint to search within the book that returns IIIF annotations, put it here. Use %node substitution where needed.
E.g., paged-content-search/%node"),
+ '#default_value' => !empty($this->options['search_endpoint']) ? $this->options['search_endpoint'] : '',
+ '#required' => FALSE,
+ ];
}
/**
@@ -380,4 +678,74 @@ public function getFormats() {
return ['json' => 'json'];
}
+ /**
+ * Submit handler for options form.
+ *
+ * Used to store the structured text media term by URL instead of Ttid.
+ *
+ * @param array $form
+ * The form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The form state object.
+ */
+ // @codingStandardsIgnoreStart
+ public function submitOptionsForm(&$form, FormStateInterface $form_state) {
+ // @codingStandardsIgnoreEnd
+ $style_options = $form_state->getValue('style_options');
+ $tid = $style_options['structured_text_term'];
+ unset($style_options['structured_text_term']);
+ $term = $this->entityTypeManager->getStorage('taxonomy_term')->load($tid);
+ if ($term) {
+ $style_options['structured_text_term_uri'] = $this->utils->getUriForTerm($term);
+ }
+ $form_state->setValue('style_options', $style_options);
+ parent::submitOptionsForm($form, $form_state);
+ }
+
+ /**
+ * Get the structured text term.
+ *
+ * @return \Drupal\taxonomy\TermInterface|null
+ * The term if it could be found; otherwise, NULL.
+ */
+ protected function getStructuredTextTerm() : ?TermInterface {
+ if (!$this->structuredTextTermMemoized) {
+ $this->structuredTextTermMemoized = TRUE;
+ $uri = $this->options['structured_text_term_uri'] ?? NULL;
+ $this->structuredTextTerm = $uri ? $this->utils->getTermForUri($uri) : NULL;
+ }
+
+ return $this->structuredTextTerm;
+ }
+
+ /**
+ * Store the image dimensions back onto the entity.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ * The entity to store the dimensions on to.
+ * @param int $width
+ * The image's width.
+ * @param int $height
+ * The image's height.
+ */
+ protected function storeImageDimensions(EntityInterface $entity, $width, $height) {
+ $height_field = !empty($this->options['advanced']['custom_width_height']['height_field']) ? $this->view->field[$this->options['advanced']['custom_width_height']['height_field']]->definition['field_name'] : 'field_height';
+ $width_field = !empty($this->options['advanced']['custom_width_height']['width_field']) ? $this->view->field[$this->options['advanced']['custom_width_height']['width_field']]->definition['field_name'] : 'field_width';
+
+ $needs_save = FALSE;
+ if ($entity->hasField($height_field) && $entity->get($height_field)->getString() !== $height) {
+ $entity->set($height_field, $height);
+ $needs_save = TRUE;
+ }
+
+ if ($entity->hasField($width_field) && $entity->get($width_field)->getString() !== $width) {
+ $entity->set($width_field, $width);
+ $needs_save = TRUE;
+ }
+
+ if ($needs_save) {
+ $entity->save();
+ }
+ }
+
}
diff --git a/modules/islandora_image/islandora_image.info.yml b/modules/islandora_image/islandora_image.info.yml
index 87076462c..277966f38 100644
--- a/modules/islandora_image/islandora_image.info.yml
+++ b/modules/islandora_image/islandora_image.info.yml
@@ -1,8 +1,7 @@
name: 'Islandora Image'
type: module
description: 'Islandora Image derivative actions'
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9 || ^10
package: Islandora
dependencies:
- drupal:islandora
diff --git a/modules/islandora_image/src/Plugin/Action/GenerateImageDerivativeFile.php b/modules/islandora_image/src/Plugin/Action/GenerateImageDerivativeFile.php
index a18f076e3..e5784f70d 100644
--- a/modules/islandora_image/src/Plugin/Action/GenerateImageDerivativeFile.php
+++ b/modules/islandora_image/src/Plugin/Action/GenerateImageDerivativeFile.php
@@ -6,7 +6,10 @@
use Drupal\islandora\Plugin\Action\AbstractGenerateDerivativeMediaFile;
/**
- * Emits a Node for generating derivatives event.
+ * Emits a Media for generating derivatives event.
+ *
+ * Attaches the result as a file in an image field on the emitting
+ * Media ("multi-file media").
*
* @Action(
* id = "generate_image_derivative_file",
@@ -24,7 +27,6 @@ public function 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'] = $this->config->get('default_scheme');
$config['inputargs'] = '';
return $config;
@@ -35,8 +37,30 @@ public function defaultConfiguration() {
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
- $form['mimetype']['#description'] = $this->t('Mimetype to convert to (e.g. application/xml, etc...)');
+ $map = $this->entityFieldManager->getFieldMapByFieldType('image');
+ $file_fields = $map['media'];
+ $file_options = array_combine(array_keys($file_fields), array_keys($file_fields));
+ $file_options = array_merge(['' => ''], $file_options);
+ // @todo figure out how to write to thumbnail, which is not a real field.
+ // see https://github.com/Islandora/islandora/issues/891.
+ unset($file_options['thumbnail']);
+
+ $form['destination_field_name'] = [
+ '#required' => TRUE,
+ '#type' => 'select',
+ '#options' => $file_options,
+ '#title' => $this->t('Destination Image field'),
+ '#default_value' => $this->configuration['destination_field_name'],
+ '#description' => $this->t('This Action stores the derivative in an
+ Image field. If you are creating a TIFF or JP2, instead use
+ "Generate a Derivative File for Media Attachment". Selected target field
+ must be an additional field, not the media\'s main storage field.
+ Selected target field must be present on the media.'),
+ ];
+
$form['mimetype']['#value'] = 'image/jpeg';
+ $form['mimetype']['#description'] = 'Mimetype to convert to. Must be
+ compatible with the destination image field.';
$form['mimetype']['#type'] = 'hidden';
// adjust args title and description for better clarity
@@ -53,7 +77,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
]
];
$form = $this->utils->array_insert_after($form, 'mimetype', $new);
-
return $form;
}
diff --git a/modules/islandora_image/tests/src/Functional/GenerateImageDerivativeTest.php b/modules/islandora_image/tests/src/Functional/GenerateImageDerivativeTest.php
index b6e016fc1..17c1beae0 100644
--- a/modules/islandora_image/tests/src/Functional/GenerateImageDerivativeTest.php
+++ b/modules/islandora_image/tests/src/Functional/GenerateImageDerivativeTest.php
@@ -42,7 +42,7 @@ public function testGenerateImageDerivativeFromScratch() {
// Create an action to generate a jpeg thumbnail.
$this->drupalGet('admin/config/system/actions');
$this->getSession()->getPage()->findById("edit-action")->selectOption("Generate an image derivative");
- $this->getSession()->getPage()->pressButton($this->t('Create'));
+ $this->getSession()->getPage()->pressButton('Create');
$this->assertSession()->statusCodeEquals(200);
$this->getSession()->getPage()->fillField('edit-label', "Generate image test derivative");
@@ -55,7 +55,7 @@ public function testGenerateImageDerivativeFromScratch() {
$this->getSession()->getPage()->fillField('edit-args', "-thumbnail 20x20");
$this->getSession()->getPage()->fillField('edit-scheme', "public");
$this->getSession()->getPage()->fillField('edit-path', "derp.jpeg");
- $this->getSession()->getPage()->pressButton($this->t('Save'));
+ $this->getSession()->getPage()->pressButton('Save');
$this->assertSession()->statusCodeEquals(200);
// Create a context and add the action as a derivative reaction.
@@ -68,9 +68,10 @@ public function testGenerateImageDerivativeFromScratch() {
'name[0][value]' => 'Test Media',
'files[field_media_file_0]' => __DIR__ . '/../../fixtures/test_file.txt',
'field_media_of[0][target_id]' => 'Test Node',
- 'field_tags[0][target_id]' => 'Preservation Master',
+ 'field_media_use[0][target_id]' => $this->preservationMasterTerm->label(),
];
- $this->drupalPostForm('media/add/' . $this->testMediaType->id(), $values, $this->t('Save'));
+ $this->drupalGet('media/add/' . $this->testMediaType->id());
+ $this->submitForm($values, 'Save');
$expected = [
'source_uri' => 'test_file.txt',
diff --git a/modules/islandora_text_extraction/islandora_text_extraction.info.yml b/modules/islandora_text_extraction/islandora_text_extraction.info.yml
index 67687f901..fb768e2ab 100644
--- a/modules/islandora_text_extraction/islandora_text_extraction.info.yml
+++ b/modules/islandora_text_extraction/islandora_text_extraction.info.yml
@@ -1,8 +1,7 @@
name: 'Islandora Text Extraction'
type: module
description: 'Islandora 8 module to connect to Hypercube microservice, and to get text from PDF ingest'
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9 || ^10
package: 'Islandora'
dependencies:
- drupal:islandora
diff --git a/modules/islandora_text_extraction/islandora_text_extraction.module b/modules/islandora_text_extraction/islandora_text_extraction.module
index 5d6f64370..ca330dd42 100644
--- a/modules/islandora_text_extraction/islandora_text_extraction.module
+++ b/modules/islandora_text_extraction/islandora_text_extraction.module
@@ -40,6 +40,10 @@ function islandora_text_extraction_media_presave(MediaInterface $media) {
$file = File::load($file_id);
if ($file) {
$data = file_get_contents($file->getFileUri());
+ // Check if it's already markup like hOCR.
+ if (substr($data, 0, 5) == 'set('field_edited_text', $data);
$media->field_edited_text->format = 'basic_html';
diff --git a/modules/islandora_text_extraction/src/Controller/MediaSourceController.php b/modules/islandora_text_extraction/src/Controller/MediaSourceController.php
index 14c36ebdb..6b8863084 100644
--- a/modules/islandora_text_extraction/src/Controller/MediaSourceController.php
+++ b/modules/islandora_text_extraction/src/Controller/MediaSourceController.php
@@ -5,6 +5,7 @@
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\File\FileSystem;
use Drupal\Core\File\FileSystemInterface;
+use Drupal\file\FileRepository;
use Drupal\media\Entity\Media;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -42,14 +43,24 @@ class MediaSourceController extends ControllerBase {
*/
protected $fileSystem;
+ /**
+ * File repository service.
+ *
+ * @var \Drupal\file\FileRepository
+ */
+ protected $fileRepository;
+
/**
* MediaSourceController constructor.
*
* @param \Drupal\Core\File\FileSystem $fileSystem
* Filesystem service.
+ * @param \Drupal\file\FileRepository $fileRepository
+ * File Repository service.
*/
- public function __construct(FileSystem $fileSystem) {
+ public function __construct(FileSystem $fileSystem, FileRepository $fileRepository) {
$this->fileSystem = $fileSystem;
+ $this->fileRepository = $fileRepository;
}
/**
@@ -63,7 +74,8 @@ public function __construct(FileSystem $fileSystem) {
*/
public static function create(ContainerInterface $container) {
return new static(
- $container->get('file_system')
+ $container->get('file_system'),
+ $container->get('file.repository'),
);
}
@@ -98,7 +110,7 @@ public function attachToMedia(
if (!$this->fileSystem->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS)) {
throw new HttpException(500, "The destination directory does not exist, could not be created, or is not writable");
}
- $file = file_save_data($contents, $content_location, FileSystemInterface::EXISTS_REPLACE);
+ $file = $this->fileRepository->writeData($contents, $content_location, FileSystemInterface::EXISTS_REPLACE);
if ($media->hasField($destination_field)) {
$media->{$destination_field}->setValue([
'target_id' => $file->id(),
@@ -108,7 +120,11 @@ public function attachToMedia(
$this->getLogger('islandora')->warning("Field $destination_field is not defined in Media Type {$media->bundle()}");
}
if ($media->hasField($destination_text_field)) {
- $media->{$destination_text_field}->setValue(nl2br($contents));
+ // @todo The request actually has a malformed parameter string, ?text_format=plain_text?connection_close=true.
+ if (substr($request->query->get('text_format'), 0, 10) == 'plain_text') {
+ $contents = nl2br($contents);
+ }
+ $media->{$destination_text_field}->setValue($contents);
}
else {
$this->getLogger('islandora')->warning("Field $destination_text_field is not defined in Media Type {$media->bundle()}");
diff --git a/modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivative.php b/modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivative.php
index 745c57727..e60931a85 100644
--- a/modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivative.php
+++ b/modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivative.php
@@ -22,9 +22,13 @@ class GenerateOCRDerivative extends AbstractGenerateDerivative {
public function defaultConfiguration() {
$config = parent::defaultConfiguration();
$config['path'] = '[date:custom:Y]-[date:custom:m]/[node:nid]-[term:name].txt';
- $config['mimetype'] = 'application/xml';
+ $config['event'] = 'Generate Derivative';
+ $config['source_term_uri'] = 'http://pcdm.org/use#OriginalFile';
+ $config['derivative_term_uri'] = 'http://pcdm.org/use#ExtractedText';
+ $config['mimetype'] = 'text/plain';
$config['queue'] = 'islandora-connector-ocr';
- $config['destination_media_type'] = 'file';
+ $config['destination_media_type'] = 'extracted_text';
+ $config['scheme'] = 'fedora';
return $config;
}
@@ -33,11 +37,9 @@ public function defaultConfiguration() {
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
- $form['mimetype']['#description'] = $this->t('Mimetype to convert to (e.g. application/xml, etc...)');
- $form['mimetype']['#value'] = 'text/plain';
- $form['mimetype']['#type'] = 'textfield';
- unset($form['args']);
+ $form['args']['#description'] = $this->t("Arguments to send to Tesseract. To generate hOCR, use:
-c tessedit_create_hocr=1 -c hocr_font_info=0
");
+
return $form;
}
@@ -46,8 +48,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
*/
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') {
+ $mime = $form_state->getValue('mimetype');
+ $exploded_mime = explode('/', $mime);
+ if ($exploded_mime[0] != 'text' && $mime != 'application/xml') {
$form_state->setErrorByName(
'mimetype',
$this->t('Please enter file mimetype (e.g. text/plain.)')
diff --git a/modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivativeFile.php b/modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivativeFile.php
index f6b8034a9..565d7564c 100644
--- a/modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivativeFile.php
+++ b/modules/islandora_text_extraction/src/Plugin/Action/GenerateOCRDerivativeFile.php
@@ -8,7 +8,7 @@
use Drupal\islandora\Plugin\Action\AbstractGenerateDerivativeMediaFile;
/**
- * Emits a Node for generating fits derivatives event.
+ * Generates OCR derivatives event.
*
* @Action(
* id = "generate_extracted_text_file",
@@ -29,6 +29,7 @@ public function defaultConfiguration() {
$config['destination_media_type'] = 'file';
$config['scheme'] = $this->config->get('default_scheme');
$config['destination_text_field_name'] = '';
+ $config['text_format'] = 'plain_text';
return $config;
}
@@ -38,7 +39,7 @@ public function defaultConfiguration() {
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));
+ $field_options = ['none' => $this->t('None')] + array_combine(array_keys($file_fields), array_keys($file_fields));
$form = parent::buildConfigurationForm($form, $form_state);
$form['mimetype']['#description'] = $this->t('Mimetype to convert to (e.g. application/xml, etc...)');
$form['mimetype']['#value'] = 'text/plain';
@@ -48,13 +49,23 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
$last = array_slice($form, count($form) - $position + 1);
$middle['destination_text_field_name'] = [
- '#required' => TRUE,
+ '#required' => FALSE,
'#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.'),
];
+ $middle['text_format'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Format'),
+ '#options' => [
+ 'plain_text' => $this->t('Plain text'),
+ 'hocr' => $this->t('hOCR text with positional data'),
+ ],
+ '#default_value' => $this->configuration['text_format'],
+ '#description' => $this->t("The type of text to be returned."),
+ ];
$form = array_merge($first, $middle, $last);
unset($form['args']);
@@ -81,17 +92,29 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form
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');
+ $this->configuration['text_format'] = $form_state->getValue('text_format');
+ switch ($form_state->getValue('text_format')) {
+ case 'hocr':
+ $this->configuration['args'] = '-c tessedit_create_hocr=1 -c hocr_font_info=0';
+ break;
+
+ case 'plain_text':
+ $this->configuration['args'] = '';
+ break;
+ }
}
/**
* 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'],
+ 'text_format' => $this->configuration['text_format'],
];
$data['destination_uri'] = Url::fromRoute('islandora_text_extraction.attach_file_to_media', $route_params)
->setAbsolute()
diff --git a/modules/islandora_text_extraction/src/Plugin/Field/FieldFormatter/OcrTextFormatter.php b/modules/islandora_text_extraction/src/Plugin/Field/FieldFormatter/OcrTextFormatter.php
index 2e0669437..055e76d97 100644
--- a/modules/islandora_text_extraction/src/Plugin/Field/FieldFormatter/OcrTextFormatter.php
+++ b/modules/islandora_text_extraction/src/Plugin/Field/FieldFormatter/OcrTextFormatter.php
@@ -132,8 +132,9 @@ protected function viewValue(FieldItemInterface $item) {
$fileItem = $item->getValue();
$file = $this->entityTypeManager->getStorage('file')->load($fileItem['target_id']);
$contents = file_get_contents($file->getFileUri());
- if (mb_detect_encoding($contents) != 'UTF-8') {
- $contents = utf8_encode($contents);
+ $detected_encoding = mb_detect_encoding($contents);
+ if ($detected_encoding != 'UTF-8') {
+ $contents = mb_convert_encoding($contents, 'UTF-8', $detected_encoding);
}
$contents = nl2br($contents);
return $contents;
diff --git a/modules/islandora_text_extraction/tests/src/Functional/LoadTest.php b/modules/islandora_text_extraction/tests/src/Functional/LoadTest.php
index 31dca62c8..172ae73ab 100644
--- a/modules/islandora_text_extraction/tests/src/Functional/LoadTest.php
+++ b/modules/islandora_text_extraction/tests/src/Functional/LoadTest.php
@@ -17,7 +17,7 @@ class LoadTest extends IslandoraFunctionalTestBase {
*
* @var array
*/
- public static $modules = ['islandora_text_extraction'];
+ protected static $modules = ['islandora_text_extraction'];
/**
* A user with permission to administer site configuration.
@@ -29,7 +29,7 @@ class LoadTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
$this->user = $this->drupalCreateUser(['administer site configuration']);
$this->drupalLogin($this->user);
diff --git a/modules/islandora_text_extraction_defaults/config/install/core.entity_form_display.media.extracted_text.default.yml b/modules/islandora_text_extraction_defaults/config/optional/core.entity_form_display.media.extracted_text.default.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/core.entity_form_display.media.extracted_text.default.yml
rename to modules/islandora_text_extraction_defaults/config/optional/core.entity_form_display.media.extracted_text.default.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/core.entity_view_display.media.extracted_text.default.yml b/modules/islandora_text_extraction_defaults/config/optional/core.entity_view_display.media.extracted_text.default.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/core.entity_view_display.media.extracted_text.default.yml
rename to modules/islandora_text_extraction_defaults/config/optional/core.entity_view_display.media.extracted_text.default.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_edited_text.yml b/modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_edited_text.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_edited_text.yml
rename to modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_edited_text.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_media_file.yml b/modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_media_file.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_media_file.yml
rename to modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_media_file.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_media_of.yml b/modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_media_of.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_media_of.yml
rename to modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_media_of.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_media_use.yml b/modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_media_use.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_media_use.yml
rename to modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_media_use.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_mime_type.yml b/modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_mime_type.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/field.field.media.extracted_text.field_mime_type.yml
rename to modules/islandora_text_extraction_defaults/config/optional/field.field.media.extracted_text.field_mime_type.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/field.storage.media.field_edited_text.yml b/modules/islandora_text_extraction_defaults/config/optional/field.storage.media.field_edited_text.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/field.storage.media.field_edited_text.yml
rename to modules/islandora_text_extraction_defaults/config/optional/field.storage.media.field_edited_text.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/language.content_settings.media.extracted_text.yml b/modules/islandora_text_extraction_defaults/config/optional/language.content_settings.media.extracted_text.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/language.content_settings.media.extracted_text.yml
rename to modules/islandora_text_extraction_defaults/config/optional/language.content_settings.media.extracted_text.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/media.type.extracted_text.yml b/modules/islandora_text_extraction_defaults/config/optional/media.type.extracted_text.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/media.type.extracted_text.yml
rename to modules/islandora_text_extraction_defaults/config/optional/media.type.extracted_text.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/rdf.mapping.media.extracted_text.yml b/modules/islandora_text_extraction_defaults/config/optional/rdf.mapping.media.extracted_text.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/rdf.mapping.media.extracted_text.yml
rename to modules/islandora_text_extraction_defaults/config/optional/rdf.mapping.media.extracted_text.yml
diff --git a/modules/islandora_text_extraction_defaults/config/install/system.action.get_ocr_from_image.yml b/modules/islandora_text_extraction_defaults/config/optional/system.action.get_ocr_from_image.yml
similarity index 100%
rename from modules/islandora_text_extraction_defaults/config/install/system.action.get_ocr_from_image.yml
rename to modules/islandora_text_extraction_defaults/config/optional/system.action.get_ocr_from_image.yml
diff --git a/modules/islandora_text_extraction_defaults/islandora_text_extraction_defaults.features.yml b/modules/islandora_text_extraction_defaults/islandora_text_extraction_defaults.features.yml
deleted file mode 100644
index 9e48e9fd5..000000000
--- a/modules/islandora_text_extraction_defaults/islandora_text_extraction_defaults.features.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-bundle: islandora
-required: true
diff --git a/modules/islandora_text_extraction_defaults/islandora_text_extraction_defaults.info.yml b/modules/islandora_text_extraction_defaults/islandora_text_extraction_defaults.info.yml
index 5b9aa14f7..8596dbd32 100644
--- a/modules/islandora_text_extraction_defaults/islandora_text_extraction_defaults.info.yml
+++ b/modules/islandora_text_extraction_defaults/islandora_text_extraction_defaults.info.yml
@@ -1,8 +1,7 @@
name: 'Islandora Text Extraction Defaults'
type: module
description: 'Default config for the Islandora Text Extraction module.'
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9 || ^10
package: Islandora
dependencies:
- drupal:field
diff --git a/modules/islandora_video/islandora_video.info.yml b/modules/islandora_video/islandora_video.info.yml
index 48eb82f2f..aa51af113 100644
--- a/modules/islandora_video/islandora_video.info.yml
+++ b/modules/islandora_video/islandora_video.info.yml
@@ -2,7 +2,6 @@ name: 'Islandora Video'
description: 'Islandora video derivative actions'
type: module
package: Islandora
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^9 || ^10
dependencies:
- drupal:islandora
diff --git a/modules/islandora_video/tests/src/Functional/GenerateVideoDerivativeTest.php b/modules/islandora_video/tests/src/Functional/GenerateVideoDerivativeTest.php
index 8714a2f10..2f79e18e9 100644
--- a/modules/islandora_video/tests/src/Functional/GenerateVideoDerivativeTest.php
+++ b/modules/islandora_video/tests/src/Functional/GenerateVideoDerivativeTest.php
@@ -37,7 +37,7 @@ public function testGenerateVideoDerivativeFromScratch() {
// Create an action to generate a jpeg thumbnail.
$this->drupalGet('admin/config/system/actions');
$this->getSession()->getPage()->findById("edit-action")->selectOption("Generate a video derivative");
- $this->getSession()->getPage()->pressButton($this->t('Create'));
+ $this->getSession()->getPage()->pressButton('Create');
$this->assertSession()->statusCodeEquals(200);
$this->getSession()->getPage()->fillField('edit-label', "Generate video test derivative");
@@ -50,7 +50,7 @@ public function testGenerateVideoDerivativeFromScratch() {
$this->getSession()->getPage()->fillField('edit-args', "-f mp4");
$this->getSession()->getPage()->fillField('edit-scheme', "public");
$this->getSession()->getPage()->fillField('edit-path', "derp.mov");
- $this->getSession()->getPage()->pressButton($this->t('Save'));
+ $this->getSession()->getPage()->pressButton('Save');
$this->assertSession()->statusCodeEquals(200);
// Create a context and add the action as a derivative reaction.
@@ -63,9 +63,10 @@ public function testGenerateVideoDerivativeFromScratch() {
'name[0][value]' => 'Test Media',
'files[field_media_file_0]' => __DIR__ . '/../../fixtures/test_file.txt',
'field_media_of[0][target_id]' => 'Test Node',
- 'field_tags[0][target_id]' => 'Preservation Master',
+ 'field_media_use[0][target_id]' => $this->preservationMasterTerm->label(),
];
- $this->drupalPostForm('media/add/' . $this->testMediaType->id(), $values, $this->t('Save'));
+ $this->drupalGet('media/add/' . $this->testMediaType->id());
+ $this->submitForm($values, 'Save');
$expected = [
'source_uri' => 'test_file.txt',
diff --git a/phpunit.xml b/phpunit.xml
index a40917813..bc0609fba 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -47,7 +47,7 @@
-
+
@@ -58,7 +58,7 @@
../modules/contrib/islandora/tests/src/Functional
- ../modules/contrib/isladnora/modules/*/tests/src/Functional
+ ../modules/contrib/islandora/modules/*/tests/src/Functional
../modules/contrib/islandora/tests/src/FunctionalJavascript
diff --git a/src/Controller/ManageMediaController.php b/src/Controller/ManageMediaController.php
index bd670561a..025d1d9dc 100644
--- a/src/Controller/ManageMediaController.php
+++ b/src/Controller/ManageMediaController.php
@@ -6,6 +6,7 @@
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\RouteMatch;
use Drupal\node\Entity\Node;
+use Drupal\Core\Url;
use Drupal\node\NodeInterface;
/**
@@ -25,7 +26,7 @@ class ManageMediaController extends ManageMembersController {
public function addToNodePage(NodeInterface $node) {
$field = IslandoraUtils::MEDIA_OF_FIELD;
- return $this->generateTypeList(
+ $add_media_list = $this->generateTypeList(
'media',
'media_type',
'entity.media.add_form',
@@ -33,6 +34,21 @@ public function addToNodePage(NodeInterface $node) {
$field,
['query' => ["edit[$field][widget][0][target_id]" => $node->id()]]
);
+
+ $manage_link = Url::fromRoute('entity.media_type.collection')->toRenderArray();
+ $manage_link['#title'] = $this->t('Manage media types');
+ $manage_link['#type'] = 'link';
+ $manage_link['#prefix'] = ' ';
+ $manage_link['#suffix'] = '.';
+
+ return [
+ '#type' => 'markup',
+ '#markup' => $this->t("The following media types can be added because they have the @field
field.", [
+ '@field' => $field,
+ ]),
+ 'manage_link' => $manage_link,
+ 'add_media' => $add_media_list,
+ ];
}
/**
diff --git a/src/Controller/ManageMembersController.php b/src/Controller/ManageMembersController.php
index 7f480fb3d..c7b2df6a4 100644
--- a/src/Controller/ManageMembersController.php
+++ b/src/Controller/ManageMembersController.php
@@ -7,6 +7,7 @@
use Drupal\Core\Entity\Controller\EntityController;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Link;
+use Drupal\Core\Url;
use Drupal\islandora\IslandoraUtils;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -28,7 +29,7 @@ class ManageMembersController extends EntityController {
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
- protected $entityFieldManger;
+ protected $entityFieldManager;
/**
* The renderer.
@@ -88,7 +89,8 @@ public static function create(ContainerInterface $container) {
*/
public function addToNodePage(NodeInterface $node) {
$field = IslandoraUtils::MEMBER_OF_FIELD;
- return $this->generateTypeList(
+
+ $add_node_list = $this->generateTypeList(
'node',
'node_type',
'node.add',
@@ -96,6 +98,21 @@ public function addToNodePage(NodeInterface $node) {
$field,
['query' => ["edit[$field][widget][0][target_id]" => $node->id()]]
);
+
+ $manage_link = Url::fromRoute('entity.node_type.collection')->toRenderArray();
+ $manage_link['#title'] = $this->t('Manage content types');
+ $manage_link['#type'] = 'link';
+ $manage_link['#prefix'] = ' ';
+ $manage_link['#suffix'] = '.';
+
+ return [
+ '#type' => 'markup',
+ '#markup' => $this->t("The following content types can be added because they have the @field
field.", [
+ '@field' => $field,
+ ]),
+ 'manage_link' => $manage_link,
+ 'add_node' => $add_node_list,
+ ];
}
/**
diff --git a/src/Controller/MediaSourceController.php b/src/Controller/MediaSourceController.php
index bab43fcfc..02a652131 100644
--- a/src/Controller/MediaSourceController.php
+++ b/src/Controller/MediaSourceController.php
@@ -136,7 +136,7 @@ public function put(MediaInterface $media, Request $request) {
* @param \Drupal\media\MediaTypeInterface $media_type
* Media type for new media.
* @param \Drupal\taxonomy\TermInterface $taxonomy_term
- * Term from the 'Behavior' vocabulary to give to new media.
+ * Term from the 'Media Use' vocabulary to give to new media.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*
@@ -280,8 +280,7 @@ public function attachToMedia(
*/
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'));
+ return AccessResult::allowedIf($media->access('update', $account));
}
}
diff --git a/src/Event/StompHeaderEvent.php b/src/Event/StompHeaderEvent.php
index d6d93c22c..1381a9209 100644
--- a/src/Event/StompHeaderEvent.php
+++ b/src/Event/StompHeaderEvent.php
@@ -6,7 +6,7 @@
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\HttpFoundation\ParameterBag;
-use Symfony\Component\EventDispatcher\Event;
+use Drupal\Component\EventDispatcher\Event;
/**
* Event used to build headers for STOMP.
diff --git a/src/EventGenerator/EmitEvent.php b/src/EventGenerator/EmitEvent.php
index 683f3e8b6..12700d732 100644
--- a/src/EventGenerator/EmitEvent.php
+++ b/src/EventGenerator/EmitEvent.php
@@ -14,6 +14,7 @@
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\islandora\Event\StompHeaderEvent;
use Drupal\islandora\Event\StompHeaderEventException;
+use Drupal\islandora\Exception\IslandoraDerivativeException;
use Stomp\Exception\StompException;
use Stomp\StatefulStomp;
use Stomp\Transport\Message;
@@ -159,8 +160,8 @@ public function execute($entity = NULL) {
$data = $this->generateData($entity);
$event = $this->eventDispatcher->dispatch(
- StompHeaderEvent::EVENT_NAME,
- new StompHeaderEvent($entity, $user, $data, $this->getConfiguration())
+ new StompHeaderEvent($entity, $user, $data, $this->getConfiguration()),
+ StompHeaderEvent::EVENT_NAME
);
$message = new Message(
@@ -168,6 +169,10 @@ public function execute($entity = NULL) {
$event->getHeaders()->all()
);
}
+ catch (IslandoraDerivativeException $e) {
+ $this->logger->info($e->getMessage());
+ return;
+ }
catch (StompHeaderEventException $e) {
$this->logger->error($e->getMessage());
$this->messenger->addError($e->getMessage());
diff --git a/src/EventGenerator/EventGenerator.php b/src/EventGenerator/EventGenerator.php
index 6b3a4c5cf..ab172225d 100644
--- a/src/EventGenerator/EventGenerator.php
+++ b/src/EventGenerator/EventGenerator.php
@@ -2,13 +2,13 @@
namespace Drupal\islandora\EventGenerator;
+use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Site\Settings;
use Drupal\islandora\IslandoraUtils;
use Drupal\islandora\MediaSource\MediaSourceService;
use Drupal\user\UserInterface;
-use Drupal\Core\Site\Settings;
-use Drupal\media\Entity\Media;
-use Drupal\Core\Entity\EntityStorageInterface;
/**
* The default EventGenerator implementation.
@@ -147,8 +147,19 @@ public function generateEvent(EntityInterface $entity, UserInterface $user, arra
}
}
- unset($data["event"]);
- unset($data["queue"]);
+ $allowed_keys = [
+ "file_upload_uri",
+ "fedora_uri",
+ "source_uri",
+ "destination_uri",
+ "args",
+ "mimetype",
+ "source_field",
+ ];
+ $keys_to_unset = array_diff(array_keys($data), $allowed_keys);
+ foreach ($keys_to_unset as $key) {
+ unset($data[$key]);
+ }
if (!empty($data)) {
$event["attachment"] = [
@@ -175,25 +186,26 @@ protected function isNewRevision(EntityInterface $entity) {
$revision_ids = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId())->revisionIds($entity);
return count($revision_ids) > 1;
}
- elseif ($entity->getEntityTypeId() == "media") {
- $mediaStorage = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId());
- return count($this->getRevisionIds($entity, $mediaStorage)) > 1;
+ elseif (in_array($entity->getEntityTypeId(), ["media", "taxonomy_term"])) {
+ $entity_storage = \Drupal::entityTypeManager()->getStorage($entity->getEntityTypeId());
+ return count($this->getRevisionIds($entity, $entity_storage)) > 1;
}
}
/**
- * Method to get the revisionIds of a media object.
+ * Method to get the revisionIds of an entity.
*
- * @param \Drupal\media\Entity\Media $media
- * Media object.
- * @param \Drupal\Core\Entity\EntityStorageInterface $media_storage
- * Media Storage.
+ * @param \Drupal\entity\Entity\ContentEntityInterface $entity
+ * Entity instance such as a Media or Taxonomy term.
+ * @param \Drupal\Core\Entity\EntityStorageInterface $entity_storage
+ * Entity Storage.
*/
- protected function getRevisionIds(Media $media, EntityStorageInterface $media_storage) {
- $result = $media_storage->getQuery()
+ protected function getRevisionIds(ContentEntityInterface $entity, EntityStorageInterface $entity_storage) {
+ $result = $entity_storage->getQuery()
->allRevisions()
- ->condition($media->getEntityType()->getKey('id'), $media->id())
- ->sort($media->getEntityType()->getKey('revision'), 'DESC')
+ ->accessCheck(TRUE)
+ ->condition($entity->getEntityType()->getKey('id'), $entity->id())
+ ->sort($entity->getEntityType()->getKey('revision'), 'DESC')
->execute();
return array_keys($result);
}
diff --git a/src/EventSubscriber/LinkHeaderSubscriber.php b/src/EventSubscriber/LinkHeaderSubscriber.php
index ce33ce2e1..f7e5725b8 100644
--- a/src/EventSubscriber/LinkHeaderSubscriber.php
+++ b/src/EventSubscriber/LinkHeaderSubscriber.php
@@ -2,6 +2,7 @@
namespace Drupal\islandora\EventSubscriber;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Drupal\Core\Access\AccessManagerInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
@@ -13,7 +14,6 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
@@ -312,9 +312,9 @@ protected function generateRestLinks(EntityInterface $entity) {
/**
* Adds resource-specific link headers to appropriate responses.
*
- * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
+ * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event
* Event containing the response.
*/
- abstract public function onResponse(FilterResponseEvent $event);
+ abstract public function onResponse(ResponseEvent $event);
}
diff --git a/src/EventSubscriber/MediaLinkHeaderSubscriber.php b/src/EventSubscriber/MediaLinkHeaderSubscriber.php
index 3cebbbaae..0f406cf57 100644
--- a/src/EventSubscriber/MediaLinkHeaderSubscriber.php
+++ b/src/EventSubscriber/MediaLinkHeaderSubscriber.php
@@ -2,10 +2,10 @@
namespace Drupal\islandora\EventSubscriber;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Drupal\Core\Url;
use Drupal\media\MediaInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
/**
* Subscribes to MediaLinkHeader Event.
@@ -17,7 +17,7 @@ class MediaLinkHeaderSubscriber extends LinkHeaderSubscriber implements EventSub
/**
* {@inheritdoc}
*/
- public function onResponse(FilterResponseEvent $event) {
+ public function onResponse(ResponseEvent $event) {
$response = $event->getResponse();
$media = $this->getObject($response, 'media');
diff --git a/src/EventSubscriber/NodeLinkHeaderSubscriber.php b/src/EventSubscriber/NodeLinkHeaderSubscriber.php
index e00533f7a..c4cdaea8c 100644
--- a/src/EventSubscriber/NodeLinkHeaderSubscriber.php
+++ b/src/EventSubscriber/NodeLinkHeaderSubscriber.php
@@ -2,9 +2,9 @@
namespace Drupal\islandora\EventSubscriber;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Drupal\node\NodeInterface;
use Drupal\islandora\IslandoraUtils;
-use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
@@ -17,10 +17,10 @@ class NodeLinkHeaderSubscriber extends LinkHeaderSubscriber implements EventSubs
/**
* Adds node-specific link headers to appropriate responses.
*
- * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
+ * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event
* Event containing the response.
*/
- public function onResponse(FilterResponseEvent $event) {
+ public function onResponse(ResponseEvent $event) {
$response = $event->getResponse();
$node = $this->getObject($response, 'node');
diff --git a/src/Exception/IslandoraDerivativeException.php b/src/Exception/IslandoraDerivativeException.php
new file mode 100644
index 000000000..7efe47736
--- /dev/null
+++ b/src/Exception/IslandoraDerivativeException.php
@@ -0,0 +1,11 @@
+fedora = $fedora;
$this->mimeTypeGuesser = $mime_type_guesser;
+ $this->logger = $logger;
}
/**
@@ -143,14 +159,8 @@ protected function getMetadataFromHeaders(Response $response) {
// NonRDFSource's are considered files. Everything else is a
// directory.
$type = 'dir';
- // phpcs:disable
- if (class_exists(\GuzzleHttp\Psr7\Header::class)) {
- $links = \GuzzleHttp\Psr7\Header::parse($response->getHeader('Link'));
- }
- else {
- $links = \GuzzleHttp\Psr7\parse_header($response->getHeader('Link'));
- }
- // phpcs:enable
+ $links = Header::parse($response->getHeader('Link'));
+
foreach ($links as $link) {
if ($link['rel'] == 'type' && $link[0] == '') {
$type = 'file';
@@ -259,7 +269,7 @@ protected function transformToMetadata($uri) {
*/
public function write($path, $contents, Config $config) {
$headers = [
- 'Content-Type' => $this->mimeTypeGuesser->guess($path),
+ 'Content-Type' => $this->mimeTypeGuesser->guessMimeType($path),
];
if ($this->has($path)) {
$fedora_url = $path;
@@ -274,17 +284,17 @@ public function write($path, $contents, Config $config) {
$headers
);
if (isset($response) && $response->getStatusCode() == 201) {
- \Drupal::logger('fedora_flysystem')->info('Created a version in Fedora for ' . $fedora_url);
+ $this->logger->info('Created a version in Fedora for ' . $fedora_url);
}
else {
- \Drupal::logger('fedora_flysystem')->error(
+ $this->logger->error(
"Client error: `Failed to create a Fedora version of $fedora_url`. Response is " . print_r($response, TRUE)
);
}
}
catch (\Exception $e) {
- \Drupal::logger('fedora_flysystem')->error('Caught exception when creating version: ' . $e->getMessage() . "\n");
+ $this->logger->error('Caught exception when creating version: ' . $e->getMessage() . "\n");
}
}
@@ -386,14 +396,7 @@ private function deleteTombstone($path) {
$return = NULL;
if ($response->getStatusCode() == 410) {
$return = FALSE;
- // phpcs:disable
- if (class_exists(\GuzzleHttp\Psr7\Header::class)) {
- $link_headers = \GuzzleHttp\Psr7\Header::parse($response->getHeader('Link'));
- }
- else {
- $link_headers = \GuzzleHttp\Psr7\parse_header($response->getHeader('Link'));
- }
- // phpcs:enable
+ $link_headers = Header::parse($response->getHeader('Link'));
if ($link_headers) {
$tombstones = array_filter($link_headers, function ($o) {
return (isset($o['rel']) && $o['rel'] == 'hasTombstone');
diff --git a/src/Flysystem/Fedora.php b/src/Flysystem/Fedora.php
index fe7af7bae..0cbf1a12a 100644
--- a/src/Flysystem/Fedora.php
+++ b/src/Flysystem/Fedora.php
@@ -3,6 +3,7 @@
namespace Drupal\islandora\Flysystem;
use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Url;
@@ -12,12 +13,11 @@
use Drupal\jwt\Authentication\Provider\JwtAuth;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\HandlerStack;
-use GuzzleHttp\Client;
-use Islandora\Chullo\IFedoraApi;
use Islandora\Chullo\FedoraApi;
+use Islandora\Chullo\IFedoraApi;
use Psr\Http\Message\RequestInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
+use Symfony\Component\Mime\MimeTypeGuesserInterface;
/**
* Drupal plugin for the Fedora Flysystem adapter.
@@ -38,7 +38,7 @@ class Fedora implements FlysystemPluginInterface, ContainerFactoryPluginInterfac
/**
* Mimetype guesser.
*
- * @var \Symfony\Component\HttpFoundation\File\Mimetype\MimeTypeGuesserInterface
+ * @var \Symfony\Component\Mime\MimeTypeGuesserInterface
*/
protected $mimeTypeGuesser;
@@ -49,24 +49,35 @@ class Fedora implements FlysystemPluginInterface, ContainerFactoryPluginInterfac
*/
protected $languageManager;
+ /**
+ * Logger.
+ *
+ * @var \Drupal\Core\Logger\LoggerChannelInterface
+ */
+ protected $logger;
+
/**
* Constructs a Fedora plugin for Flysystem.
*
* @param \Islandora\Chullo\IFedoraApi $fedora
* Fedora client.
- * @param \Symfony\Component\HttpFoundation\File\Mimetype\MimeTypeGuesserInterface $mime_type_guesser
+ * @param \Symfony\Component\Mime\MimeTypeGuesserInterface $mime_type_guesser
* Mimetype guesser.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* Language manager.
+ * @param \Drupal\Core\Logger\LoggerChannelInterface $logger
+ * The fedora adapter logger channel.
*/
public function __construct(
IFedoraApi $fedora,
MimeTypeGuesserInterface $mime_type_guesser,
- LanguageManagerInterface $language_manager
+ LanguageManagerInterface $language_manager,
+ LoggerChannelInterface $logger
) {
$this->fedora = $fedora;
$this->mimeTypeGuesser = $mime_type_guesser;
$this->languageManager = $language_manager;
+ $this->logger = $logger;
}
/**
@@ -77,17 +88,14 @@ public static function create(ContainerInterface $container, array $configuratio
// Construct guzzle client to middleware that adds JWT.
$stack = HandlerStack::create();
$stack->push(static::addJwt($container->get('jwt.authentication.jwt')));
- $client = new Client([
- 'handler' => $stack,
- 'base_uri' => $configuration['root'],
- ]);
- $fedora = new FedoraApi($client);
+ $fedora = FedoraApi::createWithHandler($configuration['root'], $stack);
// Return it.
return new static(
$fedora,
$container->get('file.mime_type.guesser'),
- $container->get('language_manager')
+ $container->get('language_manager'),
+ $container->get('logger.channel.fedora_flysystem')
);
}
@@ -116,7 +124,7 @@ public static function addJwt(JwtAuth $jwt) {
* {@inheritdoc}
*/
public function getAdapter() {
- return new FedoraAdapter($this->fedora, $this->mimeTypeGuesser);
+ return new FedoraAdapter($this->fedora, $this->mimeTypeGuesser, $this->logger);
}
/**
diff --git a/src/Form/AddChildrenForm.php b/src/Form/AddChildrenForm.php
index 0ff724962..528b42832 100644
--- a/src/Form/AddChildrenForm.php
+++ b/src/Form/AddChildrenForm.php
@@ -229,7 +229,7 @@ public function buildNodeFinished($success, $results, $operations) {
* @param \Drupal\Core\Routing\RouteMatch $route_match
* The current routing match.
*
- * @return \Drupal\Core\Access\AccessResultAllowed|\Drupal\Core\Access\AccessResultForbidden
+ * @return \Drupal\Core\Access\AccessResultInterface
* Whether we can or can't show the "thing".
*/
public function access(RouteMatch $route_match) {
diff --git a/src/Form/AddChildrenWizard/AbstractBatchProcessor.php b/src/Form/AddChildrenWizard/AbstractBatchProcessor.php
new file mode 100644
index 000000000..6193c0c30
--- /dev/null
+++ b/src/Form/AddChildrenWizard/AbstractBatchProcessor.php
@@ -0,0 +1,258 @@
+entityTypeManager = $entity_type_manager;
+ $this->database = $database;
+ $this->currentUser = $current_user;
+ $this->messenger = $messenger;
+ $this->dateFormatter = $date_formatter;
+ }
+
+ /**
+ * Implements callback_batch_operation() for our child addition batch.
+ */
+ public function batchOperation($delta, $info, array $values, &$context) {
+ $transaction = $this->database->startTransaction();
+
+ try {
+ $entities[] = $node = $this->getNode($info, $values);
+ $entities[] = $this->createMedia($node, $info, $values);
+
+ $context['results'] = array_merge_recursive($context['results'], [
+ 'validation_violations' => $this->validationClassification($entities),
+ ]);
+ $context['results']['count'] = ($context['results']['count'] ?? 0) + 1;
+ }
+ catch (HttpExceptionInterface $e) {
+ $transaction->rollBack();
+ throw $e;
+ }
+ catch (\Exception $e) {
+ $transaction->rollBack();
+ throw new HttpException(500, $e->getMessage(), $e);
+ }
+ }
+
+ /**
+ * Loads the file indicated.
+ *
+ * @param mixed $info
+ * Widget values.
+ *
+ * @return \Drupal\file\FileInterface|null
+ * The loaded file.
+ *
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+ * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+ */
+ protected function getFile($info) : ?FileInterface {
+ return (is_array($info) && isset($info['target_id'])) ?
+ $this->entityTypeManager->getStorage('file')->load($info['target_id']) :
+ NULL;
+ }
+
+ /**
+ * Get the node to which to attach our media.
+ *
+ * @param mixed $info
+ * Info from the widget used to create the request.
+ * @param array $values
+ * Additional form inputs.
+ *
+ * @return \Drupal\node\NodeInterface
+ * The node to which to attach the created media.
+ */
+ abstract protected function getNode($info, array $values) : NodeInterface;
+
+ /**
+ * Get a name to use for bulk-created assets.
+ *
+ * @param mixed $info
+ * Widget values.
+ * @param array $values
+ * Form values.
+ *
+ * @return string
+ * An applicable name.
+ *
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+ * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+ */
+ protected function getName($info, array $values) : string {
+ $file = $this->getFile($info);
+ return $file ? $file->getFilename() : strtr('Bulk ingest, {date}', [
+ '{date}' => $this->dateFormatter->format(time(), 'long'),
+ ]);
+ }
+
+ /**
+ * Create a media referencing the given file, associated with the given node.
+ *
+ * @param \Drupal\node\NodeInterface $node
+ * The node to which the media should be associated.
+ * @param mixed $info
+ * The widget info for the media source field.
+ * @param array $values
+ * Values from the wizard, which should contain at least:
+ * - media_type: The machine name/ID of the media type as which to create
+ * the media
+ * - use: An array of the selected "media use" terms.
+ *
+ * @return \Drupal\media\MediaInterface
+ * The created media entity.
+ *
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+ * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+ * @throws \Drupal\Core\Entity\EntityStorageException
+ */
+ protected function createMedia(NodeInterface $node, $info, array $values) : MediaInterface {
+ $taxonomy_term_storage = $this->entityTypeManager->getStorage('taxonomy_term');
+
+ // Create a media with the file attached and also pointing at the node.
+ $field = $this->getField($values);
+
+ $media_values = array_merge(
+ [
+ 'bundle' => $values['media_type'],
+ 'name' => $this->getName($info, $values),
+ IslandoraUtils::MEDIA_OF_FIELD => $node,
+ IslandoraUtils::MEDIA_USAGE_FIELD => ($values['use'] ?
+ $taxonomy_term_storage->loadMultiple($values['use']) :
+ NULL),
+ 'uid' => $this->currentUser->id(),
+ // XXX: Published... no constant?
+ 'status' => 1,
+ ],
+ [
+ $field->getName() => [
+ $info,
+ ],
+ ]
+ );
+ $media = $this->entityTypeManager->getStorage('media')->create($media_values);
+ if ($media->save() !== SAVED_NEW) {
+ throw new \Exception("Failed to create media.");
+ }
+
+ return $media;
+ }
+
+ /**
+ * Helper to bulk process validatable entities.
+ *
+ * @param array $entities
+ * An array of entities to scan for validation violations.
+ *
+ * @return array
+ * An associative array mapping entity type IDs to entity IDs to a count
+ * of validation violations found on then given entity.
+ */
+ protected function validationClassification(array $entities) {
+ $violations = [];
+
+ foreach ($entities as $entity) {
+ $entity_violations = $entity->validate();
+ if ($entity_violations->count() > 0) {
+ $violations[$entity->getEntityTypeId()][$entity->id()] = $entity_violations->count();
+ }
+ }
+
+ return $violations;
+ }
+
+ /**
+ * Implements callback_batch_finished() for our child addition batch.
+ */
+ public function batchProcessFinished($success, $results, $operations): void {
+ if ($success) {
+ foreach ($results['validation_violations'] ?? [] as $entity_type => $info) {
+ foreach ($info as $id => $count) {
+ $this->messenger->addWarning($this->formatPlural(
+ $count,
+ '1 validation error present in bulk created entity of type %type, with ID %id.',
+ '@count validation errors present in bulk created entity of type %type, with ID %id.',
+ [
+ '%type' => $entity_type,
+ ':uri' => Url::fromRoute("entity.{$entity_type}.canonical", [$entity_type => $id])->toString(),
+ '%id' => $id,
+ ]
+ ));
+ }
+ }
+ }
+ else {
+ $this->messenger->addError($this->t('Encountered an error when processing.'));
+ }
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/AbstractFileSelectionForm.php b/src/Form/AddChildrenWizard/AbstractFileSelectionForm.php
new file mode 100644
index 000000000..445c251e9
--- /dev/null
+++ b/src/Form/AddChildrenWizard/AbstractFileSelectionForm.php
@@ -0,0 +1,187 @@
+entityTypeManager = $container->get('entity_type.manager');
+ $instance->widgetPluginManager = $container->get('plugin.manager.field.widget');
+ $instance->entityFieldManager = $container->get('entity_field.manager');
+ $instance->currentUser = $container->get('current_user');
+
+ $instance->batchProcessor = $container->get(static::BATCH_PROCESSOR);
+
+ return $instance;
+ }
+
+ /**
+ * Helper; get the media type, based off discovering from form state.
+ *
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The form state.
+ *
+ * @return \Drupal\media\MediaTypeInterface
+ * The target media type.
+ *
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+ * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+ */
+ protected function getMediaTypeFromFormState(FormStateInterface $form_state): MediaTypeInterface {
+ return $this->getMediaType($form_state->getTemporaryValue('wizard'));
+ }
+
+ /**
+ * Helper; get field instance, based off discovering from form state.
+ *
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The form state.
+ *
+ * @return \Drupal\Core\Field\FieldDefinitionInterface
+ * The field definition.
+ */
+ protected function getFieldFromFormState(FormStateInterface $form_state): FieldDefinitionInterface {
+ $cached_values = $form_state->getTemporaryValue('wizard');
+
+ $field = $this->getField($cached_values);
+ $def = $field->getFieldStorageDefinition();
+ if ($def instanceof FieldStorageConfigInterface) {
+ $def->set('cardinality', FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
+ }
+ elseif ($def instanceof BaseFieldDefinition) {
+ $def->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
+ }
+ else {
+ throw new \Exception('Unable to remove cardinality limit.');
+ }
+
+ return $field;
+ }
+
+ /**
+ * Helper; get widget for the field, based on discovering from form state.
+ *
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The form state.
+ *
+ * @return \Drupal\Core\Field\WidgetInterface
+ * The widget.
+ */
+ protected function getWidgetFromFormState(FormStateInterface $form_state): WidgetInterface {
+ return $this->getWidget($this->getFieldFromFormState($form_state));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(array $form, FormStateInterface $form_state): array {
+ // Using the media type selected in the previous step, grab the
+ // media bundle's "source" field, and create a multi-file upload widget
+ // for it, with the same kind of constraints.
+ $field = $this->getFieldFromFormState($form_state);
+ $items = FieldItemList::createInstance($field, $field->getName(), $this->getMediaTypeFromFormState($form_state)->getTypedData());
+
+ $form['#tree'] = TRUE;
+ $form['#parents'] = [];
+ $widget = $this->getWidgetFromFormState($form_state);
+ $form['files'] = $widget->form(
+ $items,
+ $form,
+ $form_state
+ );
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+ $cached_values = $form_state->getTemporaryValue('wizard');
+
+ $widget = $this->getWidgetFromFormState($form_state);
+ $builder = (new BatchBuilder())
+ ->setTitle($this->t('Bulk creating...'))
+ ->setInitMessage($this->t('Initializing...'))
+ ->setFinishCallback([$this, 'batchProcessFinished']);
+ $values = $form_state->getValue($this->getField($cached_values)->getName());
+ $massaged_values = $widget->massageFormValues($values, $form, $form_state);
+ foreach ($massaged_values as $delta => $info) {
+ $builder->addOperation(
+ [$this, 'batchOperation'],
+ [$delta, $info, $cached_values]
+ );
+ }
+ batch_set($builder->toArray());
+ }
+
+ /**
+ * Wrap batch processor operation call to side-step serialization issues.
+ *
+ * Previously, we referred to the method on the processors directly; however,
+ * this can lead to issues regarding the (un)serialization of the services as
+ * which the processors are implemented. For example, if decorating one of the
+ * processors to extend it, it loses the reference back to be able to load the
+ * "inner"/decorated processor.
+ *
+ * @see \Drupal\islandora\Form\AddChildrenWizard\AbstractBatchProcessor::batchOperation()
+ */
+ public function batchOperation($delta, $info, $cached_values, &$context) : void {
+ $this->batchProcessor->batchOperation($delta, $info, $cached_values, $context);
+ }
+
+ /**
+ * Wrap batch processor finished call to side-step serialization issues.
+ *
+ * Previously, we referred to the method on the processors directly; however,
+ * this can lead to issues regarding the (un)serialization of the services as
+ * which the processors are implemented. For example, if decorating one of the
+ * processors to extend it, it loses the reference back to be able to load the
+ * "inner"/decorated processor.
+ *
+ * @see \Drupal\islandora\Form\AddChildrenWizard\AbstractBatchProcessor::batchProcessFinished()
+ */
+ public function batchProcessFinished($success, $results, $operations) : void {
+ $this->batchProcessor->batchProcessFinished($success, $results, $operations);
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/AbstractForm.php b/src/Form/AddChildrenWizard/AbstractForm.php
new file mode 100644
index 000000000..e9fac3875
--- /dev/null
+++ b/src/Form/AddChildrenWizard/AbstractForm.php
@@ -0,0 +1,125 @@
+nodeId = $this->routeMatch->getParameter('node');
+ $this->currentUser = $current_user;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getParameters() : array {
+ return array_merge(
+ parent::getParameters(),
+ [
+ 'tempstore_id' => static::TEMPSTORE_ID,
+ 'current_user' => \Drupal::service('current_user'),
+ ]
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getOperations($cached_values) {
+ $ops = [];
+
+ $ops['type_selection'] = [
+ 'title' => $this->t('Type Selection'),
+ 'form' => static::TYPE_SELECTION_FORM,
+ 'values' => [
+ 'node' => $this->nodeId,
+ ],
+ ];
+ $ops['file_selection'] = [
+ 'title' => $this->t('Widget Input for Selected Type'),
+ 'form' => static::FILE_SELECTION_FORM,
+ 'values' => [
+ 'node' => $this->nodeId,
+ ],
+ ];
+
+ return $ops;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getNextParameters($cached_values) {
+ return parent::getNextParameters($cached_values) + ['node' => $this->nodeId];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPreviousParameters($cached_values) {
+ return parent::getPreviousParameters($cached_values) + ['node' => $this->nodeId];
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/Access.php b/src/Form/AddChildrenWizard/Access.php
new file mode 100644
index 000000000..0adafde51
--- /dev/null
+++ b/src/Form/AddChildrenWizard/Access.php
@@ -0,0 +1,71 @@
+utils = $utils;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) : self {
+ return new static(
+ $container->get('islandora.utils')
+ );
+ }
+
+ /**
+ * Check if the user can create any "Islandora" nodes and media.
+ *
+ * @param \Drupal\Core\Routing\RouteMatch $route_match
+ * The current routing match.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ * Whether we can or cannot show the "thing".
+ */
+ public function childAccess(RouteMatch $route_match) : AccessResultInterface {
+ return AccessResult::allowedIf($this->utils->canCreateIslandoraEntity('node', 'node_type'))
+ ->andIf($this->mediaAccess($route_match));
+
+ }
+
+ /**
+ * Check if the user can create any "Islandora" media.
+ *
+ * @param \Drupal\Core\Routing\RouteMatch $route_match
+ * The current routing match.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ * Whether we can or cannot show the "thing".
+ */
+ public function mediaAccess(RouteMatch $route_match) : AccessResultInterface {
+ return AccessResult::allowedIf($this->utils->canCreateIslandoraEntity('media', 'media_type'));
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/ChildBatchProcessor.php b/src/Form/AddChildrenWizard/ChildBatchProcessor.php
new file mode 100644
index 000000000..084e7816e
--- /dev/null
+++ b/src/Form/AddChildrenWizard/ChildBatchProcessor.php
@@ -0,0 +1,57 @@
+entityTypeManager->getStorage('taxonomy_term');
+ $node_storage = $this->entityTypeManager->getStorage('node');
+ $parent = $node_storage->load($values['node']);
+
+ // Create a node (with the filename?) (and also belonging to the target
+ // node).
+ /** @var \Drupal\node\NodeInterface $node */
+ $node = $node_storage->create([
+ 'type' => $values['bundle'],
+ 'title' => $this->getName($info, $values),
+ IslandoraUtils::MEMBER_OF_FIELD => $parent,
+ 'uid' => $this->currentUser->id(),
+ 'status' => NodeInterface::PUBLISHED,
+ IslandoraUtils::MODEL_FIELD => ($values['model'] ?
+ $taxonomy_term_storage->load($values['model']) :
+ NULL),
+ ]);
+
+ if ($node->save() !== SAVED_NEW) {
+ throw new \Exception("Failed to create node.");
+ }
+
+ return $node;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function batchProcessFinished($success, $results, $operations): void {
+ if ($success) {
+ $this->messenger->addMessage($this->formatPlural(
+ $results['count'],
+ 'Added 1 child node.',
+ 'Added @count child nodes.'
+ ));
+ }
+
+ parent::batchProcessFinished($success, $results, $operations);
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/ChildFileSelectionForm.php b/src/Form/AddChildrenWizard/ChildFileSelectionForm.php
new file mode 100644
index 000000000..9783d0823
--- /dev/null
+++ b/src/Form/AddChildrenWizard/ChildFileSelectionForm.php
@@ -0,0 +1,32 @@
+getTemporaryValue('wizard');
+ $form_state->setRedirectUrl(Url::fromUri("internal:/node/{$cached_values['node']}/members"));
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/ChildForm.php b/src/Form/AddChildrenWizard/ChildForm.php
new file mode 100644
index 000000000..0b9a197f4
--- /dev/null
+++ b/src/Form/AddChildrenWizard/ChildForm.php
@@ -0,0 +1,24 @@
+ $this->currentUser->id(),
+ '{nodeid}' => $this->nodeId,
+ ]);
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/ChildTypeSelectionForm.php b/src/Form/AddChildrenWizard/ChildTypeSelectionForm.php
new file mode 100644
index 000000000..f57959971
--- /dev/null
+++ b/src/Form/AddChildrenWizard/ChildTypeSelectionForm.php
@@ -0,0 +1,157 @@
+nodeBundleOptions === NULL) {
+ $this->nodeBundleOptions = [];
+ $this->nodeBundleHasModelField = [];
+
+ $access_handler = $this->entityTypeManager->getAccessControlHandler('node');
+ foreach ($this->entityTypeBundleInfo->getBundleInfo('node') as $bundle => $info) {
+ $access = $access_handler->createAccess(
+ $bundle,
+ NULL,
+ [],
+ TRUE
+ );
+ $this->cacheableMetadata->addCacheableDependency($access);
+ if (!$access->isAllowed()) {
+ continue;
+ }
+ $this->nodeBundleOptions[$bundle] = $info['label'];
+ $fields = $this->entityFieldManager->getFieldDefinitions('node', $bundle);
+ $this->nodeBundleHasModelField[$bundle] = array_key_exists(IslandoraUtils::MODEL_FIELD, $fields);
+ }
+ }
+
+ return $this->nodeBundleOptions;
+ }
+
+ /**
+ * Generates a mapping of taxonomy term IDs to their names.
+ *
+ * @return \Generator
+ * The mapping of taxonomy term IDs to their names.
+ *
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+ * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+ */
+ protected function getModelOptions() : \Generator {
+ $terms = $this->entityTypeManager->getStorage('taxonomy_term')
+ ->loadTree('islandora_models', 0, NULL, TRUE);
+ foreach ($terms as $term) {
+ yield $term->id() => $term->getName();
+ }
+ }
+
+ /**
+ * Helper; map node bundles supporting the "has model" field, for #states.
+ *
+ * @return \Generator
+ * Yields associative array mapping the string 'value' to the bundles which
+ * have the given field.
+ */
+ protected function mapModelStates() : \Generator {
+ $this->getNodeBundleOptions();
+ foreach (array_keys(array_filter($this->nodeBundleHasModelField)) as $bundle) {
+ yield ['value' => $bundle];
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(array $form, FormStateInterface $form_state) {
+ $this->cacheableMetadata = CacheableMetadata::createFromRenderArray($form)
+ ->addCacheContexts([
+ 'url',
+ 'url.query_args',
+ ]);
+ $cached_values = $form_state->getTemporaryValue('wizard');
+
+ $form['bundle'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Content Type'),
+ '#description' => $this->t('Each child created will have this content type.'),
+ '#empty_value' => '',
+ '#default_value' => $cached_values['bundle'] ?? '',
+ '#options' => $this->getNodeBundleOptions(),
+ '#required' => TRUE,
+ ];
+
+ $model_states = iterator_to_array($this->mapModelStates());
+ $form['model'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Model'),
+ '#description' => $this->t('Each child will be tagged with this model.'),
+ '#options' => iterator_to_array($this->getModelOptions()),
+ '#empty_value' => '',
+ '#default_value' => $cached_values['model'] ?? '',
+ '#states' => [
+ 'visible' => [
+ ':input[name="bundle"]' => $model_states,
+ ],
+ 'required' => [
+ ':input[name="bundle"]' => $model_states,
+ ],
+ ],
+ ];
+
+ $this->cacheableMetadata->applyTo($form);
+ return parent::buildForm($form, $form_state);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static function keysToSave() : array {
+ return array_merge(
+ parent::keysToSave(),
+ [
+ 'bundle',
+ 'model',
+ ]
+ );
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/FieldTrait.php b/src/Form/AddChildrenWizard/FieldTrait.php
new file mode 100644
index 000000000..830f95cda
--- /dev/null
+++ b/src/Form/AddChildrenWizard/FieldTrait.php
@@ -0,0 +1,66 @@
+getMediaType($values);
+ $media_source = $media_type->getSource();
+ $source_field = $media_source->getSourceFieldDefinition($media_type);
+
+ $fields = $this->entityFieldManager()->getFieldDefinitions('media', $media_type->id());
+
+ return $fields[$source_field->getFieldStorageDefinition()->getName()] ??
+ $media_source->createSourceField($media_type);
+ }
+
+ /**
+ * Lazy-initialization of the entity field manager service.
+ *
+ * @return \Drupal\Core\Entity\EntityFieldManagerInterface
+ * The entity field manager service.
+ */
+ protected function entityFieldManager() : EntityFieldManagerInterface {
+ if ($this->entityFieldManager === NULL) {
+ $this->setEntityFieldManager(\Drupal::service('entity_field.manager'));
+ }
+ return $this->entityFieldManager;
+ }
+
+ /**
+ * Setter for entity field manager.
+ */
+ public function setEntityFieldManager(EntityFieldManagerInterface $entity_field_manager) : self {
+ $this->entityFieldManager = $entity_field_manager;
+ return $this;
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/MediaBatchProcessor.php b/src/Form/AddChildrenWizard/MediaBatchProcessor.php
new file mode 100644
index 000000000..9a54f03b6
--- /dev/null
+++ b/src/Form/AddChildrenWizard/MediaBatchProcessor.php
@@ -0,0 +1,34 @@
+entityTypeManager->getStorage('node')->load($values['node']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function batchProcessFinished($success, $results, $operations): void {
+ if ($success) {
+ $this->messenger->addMessage($this->formatPlural(
+ $results['count'],
+ 'Added 1 media.',
+ 'Added @count media.'
+ ));
+ }
+
+ parent::batchProcessFinished($success, $results, $operations);
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/MediaFileSelectionForm.php b/src/Form/AddChildrenWizard/MediaFileSelectionForm.php
new file mode 100644
index 000000000..534c73093
--- /dev/null
+++ b/src/Form/AddChildrenWizard/MediaFileSelectionForm.php
@@ -0,0 +1,32 @@
+getTemporaryValue('wizard');
+ $form_state->setRedirectUrl(Url::fromUri("internal:/node/{$cached_values['node']}/media"));
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/MediaForm.php b/src/Form/AddChildrenWizard/MediaForm.php
new file mode 100644
index 000000000..2e6fa2177
--- /dev/null
+++ b/src/Form/AddChildrenWizard/MediaForm.php
@@ -0,0 +1,24 @@
+ $this->currentUser->id(),
+ '{nodeid}' => $this->nodeId,
+ ]);
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/MediaTypeSelectionForm.php b/src/Form/AddChildrenWizard/MediaTypeSelectionForm.php
new file mode 100644
index 000000000..b06d004dc
--- /dev/null
+++ b/src/Form/AddChildrenWizard/MediaTypeSelectionForm.php
@@ -0,0 +1,227 @@
+entityTypeBundleInfo = $container->get('entity_type.bundle.info');
+ $instance->entityTypeManager = $container->get('entity_type.manager');
+ $instance->entityFieldManager = $container->get('entity_field.manager');
+ $instance->utils = $container->get('islandora.utils');
+
+ return $instance;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormId() : string {
+ return 'islandora_add_media_type_selection';
+ }
+
+ /**
+ * Memoization for ::getMediaBundleOptions().
+ *
+ * @var array|null
+ */
+ protected ?array $mediaBundleOptions = NULL;
+
+ /**
+ * Indicate presence of usage field on media bundles.
+ *
+ * Populated as a side effect in ::getMediaBundleOptions().
+ *
+ * @var array|null
+ */
+ protected ?array $mediaBundleUsageField = NULL;
+
+ /**
+ * Helper; get options for media types.
+ *
+ * @return array
+ * An associative array mapping the machine name of the media type to its
+ * human-readable label.
+ */
+ protected function getMediaBundleOptions() : array {
+ if ($this->mediaBundleOptions === NULL) {
+ $this->mediaBundleOptions = [];
+ $this->mediaBundleUsageField = [];
+
+ $access_handler = $this->entityTypeManager->getAccessControlHandler('media');
+ foreach ($this->entityTypeBundleInfo->getBundleInfo('media') as $bundle => $info) {
+ if (!$this->utils->isIslandoraType('media', $bundle)) {
+ continue;
+ }
+ $access = $access_handler->createAccess(
+ $bundle,
+ NULL,
+ [],
+ TRUE
+ );
+ $this->cacheableMetadata->addCacheableDependency($access);
+ if (!$access->isAllowed()) {
+ continue;
+ }
+ $this->mediaBundleOptions[$bundle] = $info['label'];
+ $fields = $this->entityFieldManager->getFieldDefinitions('media', $bundle);
+ $this->mediaBundleUsageField[$bundle] = array_key_exists(IslandoraUtils::MEDIA_USAGE_FIELD, $fields);
+ }
+ }
+
+ return $this->mediaBundleOptions;
+ }
+
+ /**
+ * Helper; list the terms of the "islandora_media_use" vocabulary.
+ *
+ * @return \Generator
+ * Generates term IDs as keys mapping to term names.
+ *
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+ * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+ */
+ protected function getMediaUseOptions() : \Generator {
+ /** @var \Drupal\taxonomy\TermInterface[] $terms */
+ $terms = $this->entityTypeManager->getStorage('taxonomy_term')
+ ->loadTree('islandora_media_use', 0, NULL, TRUE);
+
+ foreach ($terms as $term) {
+ yield $term->id() => $term->getName();
+ }
+ }
+
+ /**
+ * Helper; map media types supporting the usage field for use with #states.
+ *
+ * @return \Generator
+ * Yields associative array mapping the string 'value' to the bundles which
+ * have the given field.
+ */
+ protected function mapUseStates(): \Generator {
+ $this->getMediaBundleOptions();
+ foreach (array_keys(array_filter($this->mediaBundleUsageField)) as $bundle) {
+ yield ['value' => $bundle];
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(array $form, FormStateInterface $form_state) {
+ $this->cacheableMetadata = CacheableMetadata::createFromRenderArray($form)
+ ->addCacheContexts([
+ 'url',
+ 'url.query_args',
+ ]);
+ $cached_values = $form_state->getTemporaryValue('wizard');
+
+ $form['media_type'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Media Type'),
+ '#description' => $this->t('Each media created will have this type.'),
+ '#empty_value' => '',
+ '#default_value' => $cached_values['media_type'] ?? '',
+ '#options' => $this->getMediaBundleOptions(),
+ '#required' => TRUE,
+ ];
+ $use_states = iterator_to_array($this->mapUseStates());
+ $form['use'] = [
+ '#type' => 'checkboxes',
+ '#title' => $this->t('Usage'),
+ '#description' => $this->t('Defined by Portland Common Data Model: Use Extension. "Original File" will trigger creation of derivatives.', [
+ ':url' => 'https://pcdm.org/2015/05/12/use',
+ ]),
+ '#options' => iterator_to_array($this->getMediaUseOptions()),
+ '#default_value' => $cached_values['use'] ?? [],
+ '#states' => [
+ 'visible' => [
+ ':input[name="media_type"]' => $use_states,
+ ],
+ 'required' => [
+ ':input[name="media_type"]' => $use_states,
+ ],
+ ],
+ ];
+
+ $this->cacheableMetadata->applyTo($form);
+ return $form;
+ }
+
+ /**
+ * Helper; enumerate keys to persist in form state.
+ *
+ * @return string[]
+ * The keys to be persisted in our temp value in form state.
+ */
+ protected static function keysToSave() : array {
+ return [
+ 'media_type',
+ 'use',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+ $cached_values = $form_state->getTemporaryValue('wizard');
+ foreach (static::keysToSave() as $key) {
+ $cached_values[$key] = $form_state->getValue($key);
+ }
+ $form_state->setTemporaryValue('wizard', $cached_values);
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/MediaTypeTrait.php b/src/Form/AddChildrenWizard/MediaTypeTrait.php
new file mode 100644
index 000000000..36cf6ff2a
--- /dev/null
+++ b/src/Form/AddChildrenWizard/MediaTypeTrait.php
@@ -0,0 +1,58 @@
+entityTypeManager()->getStorage('media_type')->load($values['media_type']);
+ }
+
+ /**
+ * Lazy-initialization of the entity type manager service.
+ *
+ * @return \Drupal\Core\Entity\EntityTypeManagerInterface
+ * The entity type manager service.
+ */
+ protected function entityTypeManager() : EntityTypeManagerInterface {
+ if ($this->entityTypeManager === NULL) {
+ $this->setEntityTypeManager(\Drupal::service('entity_type.manager'));
+ }
+ return $this->entityTypeManager;
+ }
+
+ /**
+ * Setter for the entity type manager service.
+ */
+ public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) : self {
+ $this->entityTypeManager = $entity_type_manager;
+ return $this;
+ }
+
+}
diff --git a/src/Form/AddChildrenWizard/WizardTrait.php b/src/Form/AddChildrenWizard/WizardTrait.php
new file mode 100644
index 000000000..dd56450fa
--- /dev/null
+++ b/src/Form/AddChildrenWizard/WizardTrait.php
@@ -0,0 +1,40 @@
+widgetPluginManager->getInstance([
+ 'field_definition' => $field,
+ 'form_mode' => 'default',
+ 'prepare' => TRUE,
+ ]);
+ }
+
+}
diff --git a/src/Form/ConfirmDeleteMediaAndFile.php b/src/Form/ConfirmDeleteMediaAndFile.php
index 64c1bff3c..90e61878a 100644
--- a/src/Form/ConfirmDeleteMediaAndFile.php
+++ b/src/Form/ConfirmDeleteMediaAndFile.php
@@ -2,7 +2,6 @@
namespace Drupal\islandora\Form;
-use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Form\DeleteMultipleForm;
use Drupal\Core\Form\FormStateInterface;
@@ -10,8 +9,7 @@
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
-use Drupal\file\Entity\File;
-use Drupal\islandora\MediaSource\MediaSourceService;
+use Drupal\islandora\IslandoraUtils;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -21,11 +19,11 @@
class ConfirmDeleteMediaAndFile extends DeleteMultipleForm {
/**
- * Media source service.
+ * The current user.
*
- * @var \Drupal\islandora\MediaSource\MediaSourceService
+ * @var \Drupal\Core\Session\AccountInterface
*/
- protected $mediaSourceService;
+ protected $currentUser;
/**
* Logger.
@@ -42,23 +40,22 @@ class ConfirmDeleteMediaAndFile extends DeleteMultipleForm {
protected $selection = [];
/**
- * Entity field manager.
+ * The Islandora Utils service.
*
- * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+ * @var \Drupal\islandora\IslandoraUtils
*/
- protected $entityFieldManager;
+ protected IslandoraUtils $utils;
/**
* {@inheritdoc}
*/
- public function __construct(AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, PrivateTempStoreFactory $temp_store_factory, MessengerInterface $messenger, MediaSourceService $media_source_service, LoggerInterface $logger) {
+ public function __construct(AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, MessengerInterface $messenger, LoggerInterface $logger, IslandoraUtils $utils) {
$this->currentUser = $current_user;
$this->entityTypeManager = $entity_type_manager;
- $this->entityFieldManager = $entity_field_manager;
$this->tempStore = $temp_store_factory->get('media_and_file_delete_confirm');
$this->messenger = $messenger;
- $this->mediaSourceService = $media_source_service;
$this->logger = $logger;
+ $this->utils = $utils;
}
/**
@@ -68,11 +65,11 @@ public static function create(ContainerInterface $container) {
return new static(
$container->get('current_user'),
$container->get('entity_type.manager'),
- $container->get('entity_field.manager'),
$container->get('tempstore.private'),
$container->get('messenger'),
- $container->get('islandora.media_source_service'),
- $container->get('logger.channel.islandora'));
+ $container->get('logger.channel.islandora'),
+ $container->get('islandora.utils')
+ );
}
/**
@@ -111,88 +108,13 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
public function submitForm(array &$form, FormStateInterface $form_state) {
// Similar to parent::submitForm(), but let's blend in the related files and
// optimize based on the fact that we know we're working with media.
- $total_count = 0;
- $delete_media = [];
- $delete_media_translations = [];
- $delete_files = [];
- $inaccessible_entities = [];
$media_storage = $this->entityTypeManager->getStorage('media');
- $file_storage = $this->entityTypeManager->getStorage('file');
$media = $media_storage->loadMultiple(array_keys($this->selection));
- foreach ($this->selection as $id => $selected_langcodes) {
- $entity = $media[$id];
- if (!$entity->access('delete', $this->currentUser)) {
- $inaccessible_entities[] = $entity;
- continue;
- }
- // Check for files.
- $fields = $this->entityFieldManager->getFieldDefinitions('media', $entity->bundle());
- foreach ($fields as $field) {
- $type = $field->getType();
- if ($type == 'file' || $type == 'image') {
- $target_id = $entity->get($field->getName())->target_id;
- $file = File::load($target_id);
- if ($file) {
- if (!$file->access('delete', $this->currentUser)) {
- $inaccessible_entities[] = $file;
- continue;
- }
- $delete_files[$file->id()] = $file;
- $total_count++;
- }
- }
- }
-
- foreach ($selected_langcodes as $langcode) {
- // We're only working with media, which are translatable.
- $entity = $entity->getTranslation($langcode);
- if ($entity->isDefaultTranslation()) {
- $delete_media[$id] = $entity;
- unset($delete_media_translations[$id]);
- $total_count += count($entity->getTranslationLanguages());
- }
- elseif (!isset($delete_media[$id])) {
- $delete_media_translations[$id][] = $entity;
- }
- }
- }
- if ($delete_media) {
- $media_storage->delete($delete_media);
- foreach ($delete_media as $entity) {
- $this->logger->notice('The media %label has been deleted.', [
- '%label' => $entity->label(),
- ]);
- }
- }
- if ($delete_files) {
- $file_storage->delete($delete_files);
- foreach ($delete_files as $entity) {
- $this->logger->notice('The file %label has been deleted.', [
- '%label' => $entity->label(),
- ]);
- }
- }
- if ($delete_media_translations) {
- foreach ($delete_media_translations as $id => $translations) {
- $entity = $media[$id];
- foreach ($translations as $translation) {
- $entity->removeTranslation($translation->language()->getId());
- }
- $entity->save();
- foreach ($translations as $translation) {
- $this->logger->notice('The media %label @language translation has been deleted', [
- '%label' => $entity->label(),
- '@language' => $translation->language()->getName(),
- ]);
- }
- $total_count += count($translations);
- }
- }
- if ($total_count) {
- $this->messenger->addStatus($this->getDeletedMessage($total_count));
- }
- if ($inaccessible_entities) {
- $this->messenger->addWarning($this->getInaccessibleMessage(count($inaccessible_entities)));
+ $results = $this->utils->deleteMediaAndFiles($media);
+ $this->logger->notice($results['deleted']);
+ $this->messenger->addStatus($results['deleted']);
+ if (isset($results['inaccessible'])) {
+ $this->messenger->addWarning($results['inaccessible']);
}
$this->tempStore->delete($this->currentUser->id());
$form_state->setRedirectUrl($this->getCancelUrl());
diff --git a/src/Form/ConfirmDeleteNodeAndMedia.php b/src/Form/ConfirmDeleteNodeAndMedia.php
new file mode 100755
index 000000000..d39aa702b
--- /dev/null
+++ b/src/Form/ConfirmDeleteNodeAndMedia.php
@@ -0,0 +1,163 @@
+currentUser = $current_user;
+ $this->entityTypeManager = $entity_type_manager;
+ $this->entityFieldManager = $entity_field_manager;
+ $this->tempStore = $temp_store_factory->get('node_and_media_delete_confirm');
+ $this->messenger = $messenger;
+ $this->utils = $utils;
+ $this->mediaSourceService = $media_source_service;
+ $this->logger = $logger;
+ $this->deletedMediaCount = 0;
+ $this->deletedFileCount = 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('current_user'),
+ $container->get('entity_type.manager'),
+ $container->get('entity_field.manager'),
+ $container->get('tempstore.private'),
+ $container->get('messenger'),
+ $container->get('islandora.utils'),
+ $container->get('islandora.media_source_service'),
+ $container->get('logger.channel.islandora'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormId() {
+ return 'node_and_media_delete_confirm_form';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getQuestion() {
+ return $this->formatPlural(count($this->selection),
+ 'Are you sure you want to delete this node and its associated media and files?',
+ 'Are you sure you want to delete these nodes and their associated media and files?');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCancelUrl() {
+ return new Url('entity.media.collection');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL) {
+ return parent::buildForm($form, $form_state, 'node');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+ $deleted_media = 0;
+ $node_storage = $this->entityTypeManager->getStorage('node');
+ $nodes = $node_storage->loadMultiple(array_keys($this->selection));
+ $deleteable_nodes = [];
+ foreach ($nodes as $node) {
+ if ($node->access('delete', $this->currentUser)) {
+ $deleteable_nodes[] = $node;
+ }
+ else {
+ $nondeleteable_nodes = $node;
+ }
+ }
+ foreach ($deleteable_nodes as $candidate) {
+ $media = $this->utils->getMedia($candidate);
+ $this->utils->deleteMediaAndFiles($media);
+ $candidate->delete();
+ }
+ $this->messenger->addStatus($this->getDeletedMessage(count($deleteable_nodes)));
+ if ($nondeleteable_nodes) {
+ $failures = count($nondeleteable_nodes);
+ $this->messenger->addStatus($this->formatPlural($failures, 'Unable to delete 1 node', 'Unable to delete @count nodes'));
+ }
+ $this->tempStore->delete($this->currentUser->id());
+ $form_state->setRedirectUrl($this->getCancelUrl());
+ }
+
+}
diff --git a/src/Form/IslandoraSettingsForm.php b/src/Form/IslandoraSettingsForm.php
index 5049ef161..ad4c83b3e 100644
--- a/src/Form/IslandoraSettingsForm.php
+++ b/src/Form/IslandoraSettingsForm.php
@@ -42,6 +42,8 @@ class IslandoraSettingsForm extends ConfigFormBase {
'year',
];
const GEMINI_PSEUDO_FIELD = 'field_gemini_uri';
+ const NODE_DELETE_MEDIA_AND_FILES = 'delete_media_and_files';
+ const REDIRECT_AFTER_MEDIA_SAVE = 'redirect_after_media_save';
/**
* To list the available bundle types.
@@ -127,6 +129,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
'#type' => 'textfield',
'#title' => $this->t('URL'),
'#default_value' => $config->get(self::BROKER_URL),
+ '#config' => [
+ 'key' => 'islandora.settings:' . self::BROKER_URL,
+ ],
];
$broker_user = $config->get(self::BROKER_USER);
$form['broker_info']['provide_user_creds'] = [
@@ -147,6 +152,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
$state_selector => ['checked' => TRUE],
],
],
+ '#config' => [
+ 'key' => 'islandora.settings:' . self::BROKER_USER,
+ ],
];
$form['broker_info'][self::BROKER_PASSWORD] = [
'#type' => 'password',
@@ -157,6 +165,10 @@ public function buildForm(array $form, FormStateInterface $form_state) {
$state_selector => ['checked' => TRUE],
],
],
+ '#config' => [
+ 'key' => 'islandora.settings:' . self::BROKER_PASSWORD,
+ 'secret' => TRUE,
+ ],
];
$form[self::JWT_EXPIRY] = [
'#type' => 'textfield',
@@ -201,10 +213,29 @@ public function buildForm(array $form, FormStateInterface $form_state) {
$fedora_url = NULL;
}
+ $form[self::NODE_DELETE_MEDIA_AND_FILES] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Node Delete with Media and Files'),
+ '#description' => $this->t('Adds a checkbox in the "Delete" tab of islandora objects to delete media and files associated with the object.'
+ ),
+ '#default_value' => (bool) $config->get(self::NODE_DELETE_MEDIA_AND_FILES),
+ ];
+
+ $form[self::REDIRECT_AFTER_MEDIA_SAVE] = [
+ '#type' => 'checkbox',
+ '#title' => $this->t('Redirect after media save.'),
+ '#description' => $this->t('Redirect to node-specific media list after creation of media.'),
+ '#default_value' => (bool) $config->get(self::REDIRECT_AFTER_MEDIA_SAVE),
+ ];
+
$form[self::FEDORA_URL] = [
'#type' => 'textfield',
'#title' => $this->t('Fedora URL'),
- '#attributes' => ['readonly' => 'readonly'],
+ '#description' => $this->t('Read-only. This value is set in settings.php as the URL for the Fedora flysystem.'),
+ '#attributes' => [
+ 'readonly' => 'readonly',
+ 'disabled' => 'disabled',
+ ],
'#default_value' => $fedora_url,
];
@@ -351,6 +382,8 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
->set(self::UPLOAD_FORM_LOCATION, $form_state->getValue(self::UPLOAD_FORM_LOCATION))
->set(self::UPLOAD_FORM_ALLOWED_MIMETYPES, $form_state->getValue(self::UPLOAD_FORM_ALLOWED_MIMETYPES))
->set(self::GEMINI_PSEUDO, $new_pseudo_types)
+ ->set(self::NODE_DELETE_MEDIA_AND_FILES, $form_state->getValue(self::NODE_DELETE_MEDIA_AND_FILES))
+ ->set(self::REDIRECT_AFTER_MEDIA_SAVE, $form_state->getValue(self::REDIRECT_AFTER_MEDIA_SAVE))
->save();
parent::submitForm($form, $form_state);
diff --git a/src/IslandoraContextManager.php b/src/IslandoraContextManager.php
index 801e3253e..ed6f74af9 100644
--- a/src/IslandoraContextManager.php
+++ b/src/IslandoraContextManager.php
@@ -13,6 +13,14 @@
*/
class IslandoraContextManager extends ContextManager {
+ /**
+ * Allow the contexts to be reset before evaluation.
+ */
+ protected function resetContextEvaluation() {
+ $this->contexts = [];
+ $this->contextConditionsEvaluated = FALSE;
+ }
+
/**
* Evaluate all context conditions.
*
@@ -22,10 +30,14 @@ class IslandoraContextManager extends ContextManager {
public function evaluateContexts(array $provided = []) {
$this->activeContexts = [];
-
+ // XXX: Ensure that no earlier executed contexts in the request are still
+ // present when being triggered via Islandora's ContextProviders.
+ if (!empty($provided)) {
+ $this->resetContextEvaluation();
+ }
/** @var \Drupal\context\ContextInterface $context */
foreach ($this->getContexts() as $context) {
- if ($this->evaluateContextConditions($context, $provided) && !$context->disabled()) {
+ if (!$context->disabled() && $this->evaluateContextConditions($context, $provided)) {
$this->activeContexts[$context->id()] = $context;
}
}
@@ -48,7 +60,11 @@ public function evaluateContextConditions(ContextInterface $context, array $prov
$conditions = $context->getConditions();
// Apply context to any context aware conditions.
- $this->applyContexts($conditions, $provided);
+ // Abort if the application of contexts has been unsuccessful
+ // similarly to BlockAccessControlHandler::checkAccess().
+ if (!$this->applyContexts($conditions, $provided)) {
+ return FALSE;
+ }
// Set the logic to use when validating the conditions.
$logic = $context->requiresAllConditions()
@@ -76,6 +92,13 @@ public function evaluateContextConditions(ContextInterface $context, array $prov
* TRUE if conditions pass
*/
protected function applyContexts(ConditionPluginCollection &$conditions, array $provided = []) {
+
+ // If no contexts to check, the return should be TRUE.
+ // For example, empty is the same as sitewide condition.
+ if (count($conditions) === 0) {
+ return TRUE;
+ }
+ $passed = FALSE;
foreach ($conditions as $condition) {
if ($condition instanceof ContextAwarePluginInterface) {
try {
@@ -86,14 +109,15 @@ protected function applyContexts(ConditionPluginCollection &$conditions, array $
$contexts = $provided;
}
$this->contextHandler->applyContextMapping($condition, $contexts);
+ $passed = TRUE;
}
catch (ContextException $e) {
- return FALSE;
+ continue;
}
}
}
- return TRUE;
+ return $passed;
}
}
diff --git a/src/IslandoraUtils.php b/src/IslandoraUtils.php
index 43f2fb78b..bf80aa24e 100644
--- a/src/IslandoraUtils.php
+++ b/src/IslandoraUtils.php
@@ -10,7 +10,9 @@
use Drupal\Core\Entity\Query\QueryException;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Site\Settings;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\file\FileInterface;
use Drupal\flysystem\FlysystemFactory;
@@ -26,13 +28,15 @@
* Utility functions for figuring out when to fire derivative reactions.
*/
class IslandoraUtils {
-
+ use StringTranslationTrait;
const EXTERNAL_URI_FIELD = 'field_external_uri';
const MEDIA_OF_FIELD = 'field_media_of';
const MEDIA_USAGE_FIELD = 'field_media_use';
+
const MEMBER_OF_FIELD = 'field_member_of';
+
const MODEL_FIELD = 'field_model';
/**
@@ -68,7 +72,14 @@ class IslandoraUtils {
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
- protected $languageManager;
+ protected LanguageManagerInterface $languageManager;
+
+ /**
+ * The current user.
+ *
+ * @var \Drupal\Core\Session\AccountInterface
+ */
+ protected AccountInterface $currentUser;
/**
* Constructor.
@@ -83,19 +94,23 @@ class IslandoraUtils {
* Flysystem factory.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* Language manager.
+ * @param \Drupal\Core\Session\AccountInterface $current_user
+ * The current user.
*/
public function __construct(
EntityTypeManagerInterface $entity_type_manager,
EntityFieldManagerInterface $entity_field_manager,
ContextManager $context_manager,
FlysystemFactory $flysystem_factory,
- LanguageManagerInterface $language_manager
+ LanguageManagerInterface $language_manager,
+ AccountInterface $current_user
) {
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
$this->contextManager = $context_manager;
$this->flysystemFactory = $flysystem_factory;
$this->languageManager = $language_manager;
+ $this->currentUser = $current_user;
}
/**
@@ -148,6 +163,7 @@ public function getMedia(NodeInterface $node) {
return [];
}
$mids = $this->entityTypeManager->getStorage('media')->getQuery()
+ ->accessCheck(TRUE)
->condition(self::MEDIA_OF_FIELD, $node->id())
->execute();
if (empty($mids)) {
@@ -208,6 +224,7 @@ function ($field) {
// Query for media that reference this file.
$query = $this->entityTypeManager->getStorage('media')->getQuery();
+ $query->accessCheck(TRUE);
$group = $query->orConditionGroup();
foreach ($conditions as $condition) {
$group->condition($condition, $fid);
@@ -252,6 +269,7 @@ public function getTermForUri($uri) {
}
$results = $query
+ ->accessCheck(TRUE)
->condition($orGroup)
->execute();
@@ -420,7 +438,6 @@ public function executeDerivativeReactions($reaction_type, NodeInterface $node,
* TRUE if the fields have changed.
*/
public function haveFieldsChanged(ContentEntityInterface $entity, ContentEntityInterface $original) {
-
$field_definitions = $this->entityFieldManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle());
$ignore_list = ['vid' => 1, 'changed' => 1, 'path' => 1];
@@ -498,6 +515,7 @@ public function getMediaReferencingNodeAndTerm(NodeInterface $node, TermInterfac
array_walk($node_fields, $remove_entity);
$query = $this->entityTypeManager->getStorage('media')->getQuery();
+ $query->accessCheck(TRUE);
$taxon_condition = $this->getEntityQueryOrCondition($query, $term_fields, $term->id());
$query->condition($taxon_condition);
$node_condition = $this->getEntityQueryOrCondition($query, $node_fields, $node->id());
@@ -524,7 +542,8 @@ public function getMediaReferencingNodeAndTerm(NodeInterface $node, TermInterfac
* Array of fields.
*/
public function getReferencingFields($entity_type, $target_type) {
- $fields = $this->entityTypeManager->getStorage('field_storage_config')->getQuery()
+ $fields = $this->entityTypeManager->getStorage('field_storage_config')
+ ->getQuery()
->condition('entity_type', $entity_type)
->condition('settings.target_type', $target_type)
->execute();
@@ -653,8 +672,6 @@ public function isIslandoraType($entity_type, $bundle) {
public function canCreateIslandoraEntity($entity_type, $bundle_type) {
$bundles = $this->entityTypeManager->getStorage($bundle_type)->loadMultiple();
$access_control_handler = $this->entityTypeManager->getAccessControlHandler($entity_type);
-
- $allowed = [];
foreach (array_keys($bundles) as $bundle) {
// Skip bundles that aren't 'Islandora' types.
if (!$this->isIslandoraType($entity_type, $bundle)) {
@@ -767,10 +784,77 @@ protected function getParentsByEntityReference(ContentEntityInterface $entity, a
* Credit: https://gist.github.com/wpscholar/0deadce1bbfa4adb4e4c
*/
function array_insert_after( array $array, $key, array $new ) {
- $keys = array_keys( $array );
- $index = array_search( $key, $keys );
- $pos = false === $index ? count( $array ) : $index + 1;
- return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) );
+ $keys = array_keys( $array );
+ $index = array_search( $key, $keys );
+ $pos = false === $index ? count( $array ) : $index + 1;
+ return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) );
+ }
+
+ /**
+ * Deletes Media and all associated files.
+ *
+ * @param \Drupal\media\MediaInterface[] $media
+ * Array of media objects to be deleted along with their files.
+ *
+ * @return array
+ * Associative array keyed 'deleted' and 'inaccessible'.
+ *
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+ * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+ * @throws \Drupal\Core\Entity\EntityStorageException
+ */
+ public function deleteMediaAndFiles(array $media) {
+ $results = [];
+ $delete_media = [];
+ $delete_files = [];
+ $inaccessible_entities = [];
+ $media_storage = $this->entityTypeManager->getStorage('media');
+ $file_storage = $this->entityTypeManager->getStorage('file');
+ foreach ($media as $entity) {
+ if (!$entity->access('delete', $this->currentUser)) {
+ $inaccessible_entities[] = $entity;
+ continue;
+ }
+ else {
+ $delete_media[$entity->id()] = $entity;
+ }
+ // Check for source and additional files.
+ $fields = $this->entityFieldManager->getFieldDefinitions('media', $entity->bundle());
+ foreach ($fields as $field) {
+ if ($field->getName() == 'thumbnail') {
+ continue;
+ }
+ $type = $field->getType();
+ if ($type == 'file' || $type == 'image') {
+ $target_id = $entity->get($field->getName())->target_id;
+ $file = $file_storage->load($target_id);
+ if ($file) {
+ if (!$file->access('delete', $this->currentUser)) {
+ $inaccessible_entities[] = $file;
+ continue;
+ }
+ if (!array_key_exists($file->id(), $delete_files)) {
+ $delete_files[$file->id()] = $file;
+ }
+ }
+ }
+ }
+ }
+ if ($delete_media) {
+ $media_storage->delete($delete_media);
+ }
+ if ($delete_files) {
+ $file_storage->delete($delete_files);
+ }
+ $results['deleted'] = $this->formatPlural(
+ count($delete_media), 'The media with the id @media has been deleted.',
+ 'The medias with the ids @media have been deleted.',
+ ['@media' => implode(", ", array_keys($delete_media))],
+ );
+ if ($inaccessible_entities) {
+ $results['inaccessible'] = $this->formatPlural($inaccessible_entities, "@count item has not been deleted because you do not have the necessary permissions.", "@count items have not been deleted because you do not have the necessary permissions.");
+ }
+ return $results;
}
}
diff --git a/src/MediaSource/MediaSourceService.php b/src/MediaSource/MediaSourceService.php
index 9399e3343..533789857 100644
--- a/src/MediaSource/MediaSourceService.php
+++ b/src/MediaSource/MediaSourceService.php
@@ -3,6 +3,7 @@
namespace Drupal\islandora\MediaSource;
use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Session\AccountInterface;
@@ -113,8 +114,12 @@ public function getSourceFieldName($media_type) {
* @param \Drupal\media\MediaInterface $media
* Media whose source field you are searching for.
*
- * @return \Drupal\file\FileInterface
- * File if it exists
+ * @return \Drupal\file\FileInterface|\Drupal\Core\Entity\EntityInterface|false|null
+ * The first source entity if there is one, generally expected to be of
+ * \Drupal\file\FileInterface. Boolean FALSE if there was no such entity.
+ * NULL if the source field does not refer to Drupal entities (as in, the
+ * field is not a \Drupal\Core\Field\EntityReferenceFieldItemListInterface
+ * implementation).
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
@@ -127,10 +132,13 @@ public function getSourceFile(MediaInterface $media) {
}
// Get the file from the media.
- $files = $media->get($source_field)->referencedEntities();
- $file = reset($files);
+ $source_list = $media->get($source_field);
+ if ($source_list instanceof EntityReferenceFieldItemListInterface) {
+ $files = $source_list->referencedEntities();
+ return reset($files);
+ }
- return $file;
+ return NULL;
}
/**
@@ -268,8 +276,8 @@ public function putToNode(
'uri' => $content_location,
'filename' => $this->fileSystem->basename($content_location),
'filemime' => $mimetype,
- 'status' => FILE_STATUS_PERMANENT,
]);
+ $file->setPermanent();
// Validate file extension.
$source_field_config = $this->entityTypeManager->getStorage('field_config')->load("media.$bundle.$source_field");
@@ -349,8 +357,8 @@ public function putToMedia(
'uri' => $content_location,
'filename' => $this->fileSystem->basename($content_location),
'filemime' => $mimetype,
- 'status' => FILE_STATUS_PERMANENT,
]);
+ $file->setPermanent();
// Validate file extension.
$bundle = $media->bundle();
diff --git a/src/Plugin/Action/AbstractGenerateDerivative.php b/src/Plugin/Action/AbstractGenerateDerivative.php
index b22201e11..b44db477b 100644
--- a/src/Plugin/Action/AbstractGenerateDerivative.php
+++ b/src/Plugin/Action/AbstractGenerateDerivative.php
@@ -5,6 +5,7 @@
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
+use Drupal\islandora\Exception\IslandoraDerivativeException;
/**
* Emits a Node event.
@@ -60,6 +61,13 @@ protected function generateData(EntityInterface $entity) {
throw new \RuntimeException("Could not locate taxonomy term with uri: " . $this->configuration['derivative_term_uri'], 500);
}
+ // See if there is a destination media already set, and abort if it's the
+ // same as the source media. Dont cause an error, just don't continue.
+ $derivative_media = $this->utils->getMediaWithTerm($entity, $derivative_term);
+ if (!is_null($derivative_media) && $derivative_media->id() == $source_media->id()) {
+ throw new IslandoraDerivativeException("Halting derivative, as source and target media are the same. Derivative term: [" . $this->configuration['derivative_term_uri'] . "] Source term: [" . $this->configuration['source_term_uri'] . "] Node id: [" . $entity->id() . "].", 500);
+ }
+
$route_params = [
'node' => $entity->id(),
'media_type' => $this->configuration['destination_media_type'],
diff --git a/src/Plugin/Action/AbstractGenerateDerivativeMediaFile.php b/src/Plugin/Action/AbstractGenerateDerivativeMediaFile.php
index 157765919..f1a9807e9 100644
--- a/src/Plugin/Action/AbstractGenerateDerivativeMediaFile.php
+++ b/src/Plugin/Action/AbstractGenerateDerivativeMediaFile.php
@@ -7,7 +7,10 @@
use Drupal\Core\Url;
/**
- * Emits a Node for generating derivatives event.
+ * Emits a Media for generating derivatives event.
+ *
+ * Attaches the result as a file in a file field on the emitting
+ * Media ("multi-file media").
*
* @Action(
* id = "generate_derivative_file",
@@ -100,15 +103,23 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
$image_options = array_combine(array_keys($image_fields), array_keys($image_fields));
$file_options = array_merge(['' => ''], $file_options, $image_options);
+
+ // @todo figure out how to write to thumbnail, which is not a real field.
+ // see https://github.com/Islandora/islandora/issues/891.
+ unset($file_options['thumbnail']);
+
$form['event']['#disabled'] = 'disabled';
$form['destination_field_name'] = [
'#required' => TRUE,
'#type' => 'select',
'#options' => $file_options,
- '#title' => $this->t('Destination File field Name'),
+ '#title' => $this->t('Destination File field'),
'#default_value' => $this->configuration['destination_field_name'],
- '#description' => $this->t('File field on Media Type to hold derivative. Cannot be the same as source'),
+ '#description' => $this->t('This Action stores a derivative file
+ in a File or Image field on a media. The destination field
+ must be an additional field, not the media\'s main storage field.
+ Selected destination field must be present on the media.'),
];
$form['inputargs'] = [
diff --git a/src/Plugin/Action/DeleteNodeAndMedia.php b/src/Plugin/Action/DeleteNodeAndMedia.php
new file mode 100755
index 000000000..2dd243872
--- /dev/null
+++ b/src/Plugin/Action/DeleteNodeAndMedia.php
@@ -0,0 +1,46 @@
+currentUser = $current_user;
+ $this->tempStore = $temp_store_factory->get('node_and_media_delete_confirm');
+ $this->entityTypeManager = $entity_type_manager;
+ $this->configuration = $configuration;
+ $this->pluginId = $plugin_id;
+ $this->pluginDefinition = $plugin_definition;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function executeMultiple(array $entities): void {
+ $selection = [];
+ foreach ($entities as $entity) {
+ $langcode = $entity->language()->getId();
+ $selection[$entity->id()][$langcode] = $langcode;
+ }
+ $this->tempStore->set("{$this->currentUser->id()}:node", $selection);
+ }
+
+}
diff --git a/src/Plugin/Condition/NodeHasParent.php b/src/Plugin/Condition/NodeHasParent.php
index f9ef0cbf5..813fd00b1 100644
--- a/src/Plugin/Condition/NodeHasParent.php
+++ b/src/Plugin/Condition/NodeHasParent.php
@@ -137,9 +137,11 @@ public function evaluate() {
* TRUE if entity references the specified parent.
*/
protected function evaluateEntity(EntityInterface $entity) {
+ $parent_reference_field = $this->configuration['parent_reference_field'];
foreach ($entity->referencedEntities() as $referenced_entity) {
- if ($entity->getEntityTypeID() == 'node' && $referenced_entity->getEntityTypeId() == 'node') {
- $parent_reference_field = $this->configuration['parent_reference_field'];
+ // Check whether the entity and the referenced entity are nodes.
+ // Also make sure that the field exists.
+ if ($entity->getEntityTypeID() == 'node' && $entity->hasField($parent_reference_field) && $referenced_entity->getEntityTypeId() == 'node') {
$field = $entity->get($parent_reference_field);
if (!$field->isEmpty()) {
$nids = $field->getValue();
diff --git a/src/Plugin/Condition/NodeReferencedByNode.php b/src/Plugin/Condition/NodeReferencedByNode.php
index a2abac808..d6db01ea0 100644
--- a/src/Plugin/Condition/NodeReferencedByNode.php
+++ b/src/Plugin/Condition/NodeReferencedByNode.php
@@ -16,7 +16,7 @@
* @Condition(
* id = "node_referenced_by_node",
* label = @Translation("Node is referenced by other nodes"),
- * context = {
+ * context_definitions = {
* "node" = @ContextDefinition("entity:node", required = TRUE , label = @Translation("node"))
* }
* )
@@ -128,6 +128,7 @@ protected function evaluateEntity(EntityInterface $entity) {
$config = FieldStorageConfig::loadByName('node', $reference_field);
if ($config) {
$id_count = \Drupal::entityQuery('node')
+ ->accessCheck(TRUE)
->condition($reference_field, $entity->id())
->count()
->execute();
diff --git a/src/Plugin/Field/FieldFormatter/IslandoraImageFormatter.php b/src/Plugin/Field/FieldFormatter/IslandoraImageFormatter.php
index 6c6e87da4..ffe6d47a2 100644
--- a/src/Plugin/Field/FieldFormatter/IslandoraImageFormatter.php
+++ b/src/Plugin/Field/FieldFormatter/IslandoraImageFormatter.php
@@ -5,9 +5,12 @@
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\File\FileUrlGeneratorInterface;
+use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatter;
use Drupal\islandora\IslandoraUtils;
+use Drupal\islandora\MediaSource\MediaSourceService;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -33,6 +36,13 @@ class IslandoraImageFormatter extends ImageFormatter {
*/
protected $utils;
+ /**
+ * Islandora media source service.
+ *
+ * @var \Drupal\islandora\MediaSource\MediaSourceService
+ */
+ protected $mediaSourceService;
+
/**
* Constructs an IslandoraImageFormatter object.
*
@@ -56,6 +66,10 @@ class IslandoraImageFormatter extends ImageFormatter {
* The image style storage.
* @param \Drupal\islandora\IslandoraUtils $utils
* Islandora utils.
+ * @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
+ * The File URL Generator.
+ * @param \Drupal\islandora\MediaSource\MediaSourceService $media_source_service
+ * Utils to get the source file from media.
*/
public function __construct(
$plugin_id,
@@ -67,7 +81,9 @@ public function __construct(
array $third_party_settings,
AccountInterface $current_user,
EntityStorageInterface $image_style_storage,
- IslandoraUtils $utils
+ IslandoraUtils $utils,
+ FileUrlGeneratorInterface $file_url_generator,
+ MediaSourceService $media_source_service
) {
parent::__construct(
$plugin_id,
@@ -78,9 +94,11 @@ public function __construct(
$view_mode,
$third_party_settings,
$current_user,
- $image_style_storage
+ $image_style_storage,
+ $file_url_generator
);
$this->utils = $utils;
+ $this->mediaSourceService = $media_source_service;
}
/**
@@ -97,10 +115,41 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration['third_party_settings'],
$container->get('current_user'),
$container->get('entity_type.manager')->getStorage('image_style'),
- $container->get('islandora.utils')
+ $container->get('islandora.utils'),
+ $container->get('file_url_generator'),
+ $container->get('islandora.media_source_service')
);
}
+ /**
+ * {@inheritdoc}
+ */
+ public static function defaultSettings() {
+ return [
+ 'image_alt_text' => 'local',
+ ] + parent::defaultSettings();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ $element = parent::settingsForm($form, $form_state);
+ $alt_text_options = [
+ 'local' => $this->t('Local'),
+ 'original_file_fallback' => $this->t('Local, with fallback to Original File'),
+ 'original_file' => $this->t('Original File'),
+ ];
+ $element['image_alt_text'] = [
+ '#title' => $this->t('Alt text source'),
+ '#type' => 'select',
+ '#default_value' => $this->getSetting('image_alt_text'),
+ '#empty_option' => $this->t('None'),
+ '#options' => $alt_text_options,
+ ];
+ return $element;
+ }
+
/**
* {@inheritdoc}
*/
@@ -108,28 +157,68 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = parent::viewElements($items, $langcode);
$image_link_setting = $this->getSetting('image_link');
- // Check if the formatter involves a link.
- if ($image_link_setting != 'content') {
+ $alt_text_setting = $this->getsetting('image_alt_text');
+
+ // Check if we can leave the image as-is:
+ if ($image_link_setting !== 'content' && $alt_text_setting === 'local') {
return $elements;
}
-
$entity = $items->getEntity();
- if ($entity->isNew() || $entity->getEntityTypeId() != 'media') {
+ if ($entity->isNew() || $entity->getEntityTypeId() !== 'media') {
return $elements;
}
- $node = $this->utils->getParentNode($entity);
-
- if ($node === NULL) {
- return $elements;
+ if ($alt_text_setting === 'none') {
+ foreach ($elements as $element) {
+ $element['#item']->set('alt', '');
+ }
}
- $url = $node->toUrl();
+ if ($image_link_setting === 'content' || $alt_text_setting === 'original_file' || $alt_text_setting === 'original_file_fallback') {
+ $node = $this->utils->getParentNode($entity);
+ if ($node === NULL) {
+ return $elements;
+ }
- foreach ($elements as &$element) {
- $element['#url'] = $url;
- }
+ if ($image_link_setting === 'content') {
+ // Set image link.
+ $url = $node->toUrl();
+ foreach ($elements as &$element) {
+ $element['#url'] = $url;
+ }
+ unset($element);
+ }
+
+ if ($alt_text_setting === 'original_file' || $alt_text_setting === 'original_file_fallback') {
+ $original_file_term = $this->utils->getTermForUri("http://pcdm.org/use#OriginalFile");
+ if ($original_file_term !== NULL) {
+ $original_file_media = $this->utils->getMediaWithTerm($node, $original_file_term);
+
+ if ($original_file_media !== NULL) {
+ $source_field_name = $this->mediaSourceService->getSourceFieldName($original_file_media->bundle());
+ if ($original_file_media->hasField($source_field_name)) {
+ $original_file_files = $original_file_media->get($source_field_name);
+ // XXX: Support the multifile media use case where there could
+ // be multiple files in the source field.
+ $i = 0;
+ foreach ($original_file_files as $file) {
+ if (isset($file->alt)) {
+ $alt_text = $file->get('alt')->getValue();
+ if (isset($elements[$i])) {
+ $element = $elements[$i];
+ if ($alt_text_setting === 'original_file' || $element['#item']->get('alt')->getValue() === '') {
+ $elements[$i]['#item']->set('alt', $alt_text);
+ }
+ $i++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
return $elements;
}
diff --git a/src/Plugin/Field/FieldType/MediaTrackItem.php b/src/Plugin/Field/FieldType/MediaTrackItem.php
index d281ed216..68756ae59 100644
--- a/src/Plugin/Field/FieldType/MediaTrackItem.php
+++ b/src/Plugin/Field/FieldType/MediaTrackItem.php
@@ -16,7 +16,7 @@
* id = "media_track",
* label = @Translation("Media track"),
* description = @Translation("This field stores the ID of a media track file as an integer value."),
- * category = @Translation("Reference"),
+ * category = "reference",
* default_widget = "media_track",
* default_formatter = "file_default",
* column_groups = {
diff --git a/src/Plugin/views/filter/NodeHasMediaUse.php b/src/Plugin/views/filter/NodeHasMediaUse.php
index 0209505d6..1686c4217 100644
--- a/src/Plugin/views/filter/NodeHasMediaUse.php
+++ b/src/Plugin/views/filter/NodeHasMediaUse.php
@@ -2,8 +2,13 @@
namespace Drupal\islandora\Plugin\views\filter;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\islandora\IslandoraUtils;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Views Filter on Having Media of a Type.
@@ -14,14 +19,56 @@
*/
class NodeHasMediaUse extends FilterPluginBase {
+ /**
+ * Islandora's utility service.
+ *
+ * @var \Drupal\islandora\IslandoraUtils
+ */
+ protected IslandoraUtils $utils;
+
+ /**
+ * Drupal's entity type manager service.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected EntityTypeManagerInterface $entityTypeManager;
+
+ /**
+ * Drupal's database connection service.
+ *
+ * @var \Drupal\Core\Database\Connection
+ */
+ protected Connection $connection;
+
+ /**
+ * Logger service.
+ *
+ * @var \Psr\Log\LoggerInterface
+ */
+ protected LoggerInterface $logger;
+
+ /**
+ * {@inheritDoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
+
+ $instance->utils = $container->get('islandora.utils');
+ $instance->entityTypeManager = $container->get('entity_type.manager');
+ $instance->connection = $container->get('database');
+ $instance->logger = $container->get('logger.factory')->get('islanodra');
+
+ return $instance;
+ }
+
/**
* {@inheritdoc}
*/
protected function defineOptions() {
- return [
- 'use_uri' => ['default' => NULL],
- 'negated' => ['default' => FALSE],
- ];
+ $options = parent::defineOptions();
+ $options['use_uri'] = ['default' => NULL];
+ $options['negated'] = ['default' => FALSE];
+ return $options;
}
/**
@@ -29,7 +76,7 @@ protected function defineOptions() {
*/
public function validateOptionsForm(&$form, FormStateInterface $form_state) {
$uri = $form_state->getValues()['options']['use_uri'];
- $term = \Drupal::service('islandora.utils')->getTermForUri($uri);
+ $term = $this->utils->getTermForUri($uri);
if (empty($term)) {
$form_state->setError($form['use_uri'], $this->t('Could not find term with URI: "%uri"', ['%uri' => $uri]));
}
@@ -39,7 +86,7 @@ public function validateOptionsForm(&$form, FormStateInterface $form_state) {
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
- $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(['vid' => 'islandora_media_use']);
+ $terms = $this->entityTypeManager->getStorage('taxonomy_term')->loadByProperties(['vid' => 'islandora_media_use']);
$uris = [];
foreach ($terms as $term) {
foreach ($term->get('field_external_uri')->getValue() as $uri) {
@@ -67,7 +114,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
*/
public function adminSummary() {
$operator = ($this->options['negated']) ? "does not have" : "has";
- $term = \Drupal::service('islandora.utils')->getTermForUri($this->options['use_uri']);
+ $term = $this->utils->getTermForUri($this->options['use_uri']);
$label = (empty($term)) ? 'BROKEN TERM URI' : $term->label();
return "Node {$operator} a '{$label}' media";
}
@@ -77,18 +124,21 @@ public function adminSummary() {
*/
public function query() {
$condition = ($this->options['negated']) ? 'NOT IN' : 'IN';
- $utils = \Drupal::service('islandora.utils');
- $term = $utils->getTermForUri($this->options['use_uri']);
+ $term = $this->utils->getTermForUri($this->options['use_uri']);
if (empty($term)) {
- \Drupal::logger('islandora')->warning('Node Has Media Filter could not find term with URI: "%uri"', ['%uri' => $this->options['use_uri']]);
+ $this->logger->warning('Node Has Media Filter could not find term with URI: "%uri"', ['%uri' => $this->options['use_uri']]);
return;
}
- $sub_query = \Drupal::database()->select('media', 'm');
- $sub_query->join('media__field_media_use', 'use', 'm.mid = use.entity_id');
- $sub_query->join('media__field_media_of', 'of', 'm.mid = of.entity_id');
- $sub_query->fields('of', ['field_media_of_target_id'])
- ->condition('use.field_media_use_target_id', $term->id());
- $this->query->addWhere(0, 'nid', $sub_query, $condition);
+ $sub_query = $this->connection->select('media', 'm');
+ $use_alias = $sub_query->join('media__field_media_use', 'use', 'm.mid = %alias.entity_id');
+ $of_alias = $sub_query->join('media__field_media_of', 'of', 'm.mid = %alias.entity_id');
+ $sub_query->fields($of_alias, ['field_media_of_target_id'])
+ ->condition("{$use_alias}.field_media_use_target_id", $term->id());
+
+ /** @var \Drupal\views\Plugin\views\query\Sql $query */
+ $query = $this->query;
+ $alias = $query->ensureTable('node_field_data', $this->relationship);
+ $query->addWhere(0, "{$alias}.nid", $sub_query, $condition);
}
}
diff --git a/src/PresetReaction/PresetReaction.php b/src/PresetReaction/PresetReaction.php
index 98aa69462..4ebd68235 100644
--- a/src/PresetReaction/PresetReaction.php
+++ b/src/PresetReaction/PresetReaction.php
@@ -7,6 +7,7 @@
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -21,12 +22,20 @@ class PresetReaction extends ContextReactionPluginBase implements ContainerFacto
*/
protected $actionStorage;
+ /**
+ * The logger.
+ *
+ * @var \Psr\Log\LoggerInterface
+ */
+ protected $logger;
+
/**
* {@inheritdoc}
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $action_storage) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $action_storage, LoggerInterface $logger) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->actionStorage = $action_storage;
+ $this->logger = $logger;
}
/**
@@ -37,7 +46,8 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration,
$plugin_id,
$plugin_definition,
- $container->get('entity_type.manager')->getStorage('action')
+ $container->get('entity_type.manager')->getStorage('action'),
+ $container->get('logger.factory')->get('islandora')
);
}
@@ -56,7 +66,20 @@ public function execute(EntityInterface $entity = NULL) {
$action_ids = $config['actions'];
foreach ($action_ids as $action_id) {
$action = $this->actionStorage->load($action_id);
- $action->execute([$entity]);
+ if (empty($action)) {
+ $this->logger->warning('Action "@action" not found.', ['@action' => $action_id]);
+ continue;
+ }
+ try {
+ $action->execute([$entity]);
+ }
+ catch (\Exception $e) {
+ $this->logger->error('Error executing action "@action" on entity "@entity": @message', [
+ '@action' => $action->label(),
+ '@entity' => $entity->label(),
+ '@message' => $e->getMessage(),
+ ]);
+ }
}
}
diff --git a/tests/fixtures/config/core.entity_form_display.media.test_media_type.default.yml b/tests/fixtures/config/core.entity_form_display.media.test_media_type.default.yml
index 19fe419b6..ea8eac008 100644
--- a/tests/fixtures/config/core.entity_form_display.media.test_media_type.default.yml
+++ b/tests/fixtures/config/core.entity_form_display.media.test_media_type.default.yml
@@ -1,9 +1,11 @@
+uuid: 9151a0fe-7729-4943-b506-dd6f8d12ceac
langcode: en
status: true
dependencies:
config:
+ - field.field.media.test_media_type.field_media_file
- field.field.media.test_media_type.field_media_of
- - field.field.media.test_media_type.field_tags
+ - field.field.media.test_media_type.field_media_use
- media.type.test_media_type
module:
- path
@@ -34,15 +36,17 @@ content:
region: content
settings:
match_operator: CONTAINS
+ match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
- field_tags:
+ field_media_use:
type: entity_reference_autocomplete
weight: 3
region: content
settings:
match_operator: CONTAINS
+ match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
@@ -64,6 +68,7 @@ content:
weight: 4
settings:
match_operator: CONTAINS
+ match_limit: 10
size: 60
placeholder: ''
region: content
diff --git a/tests/fixtures/config/core.entity_form_display.node.test_type.default.yml b/tests/fixtures/config/core.entity_form_display.node.test_type.default.yml
index 2560ec6e7..68724265a 100644
--- a/tests/fixtures/config/core.entity_form_display.node.test_type.default.yml
+++ b/tests/fixtures/config/core.entity_form_display.node.test_type.default.yml
@@ -1,12 +1,13 @@
+uuid: 90a6909f-a2aa-44e8-8b61-4cd54ec6974f
langcode: en
status: true
dependencies:
config:
- field.field.node.test_type.field_member_of
+ - field.field.node.test_type.field_model
- node.type.test_type
module:
- path
- - text
id: node.test_type.default
targetEntityType: node
bundle: test_type
@@ -19,14 +20,25 @@ content:
settings: { }
third_party_settings: { }
field_member_of:
+ type: entity_reference_autocomplete
weight: 122
+ region: content
settings:
match_operator: CONTAINS
+ match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
+ field_model:
type: entity_reference_autocomplete
+ weight: 123
region: content
+ settings:
+ match_operator: CONTAINS
+ match_limit: 10
+ size: 60
+ placeholder: ''
+ third_party_settings: { }
langcode:
type: language_select
weight: 2
@@ -42,24 +54,24 @@ content:
third_party_settings: { }
promote:
type: boolean_checkbox
- settings:
- display_label: true
weight: 15
region: content
+ settings:
+ display_label: true
third_party_settings: { }
status:
type: boolean_checkbox
- settings:
- display_label: true
weight: 120
region: content
+ settings:
+ display_label: true
third_party_settings: { }
sticky:
type: boolean_checkbox
- settings:
- display_label: true
weight: 16
region: content
+ settings:
+ display_label: true
third_party_settings: { }
title:
type: string_textfield
@@ -72,10 +84,11 @@ content:
uid:
type: entity_reference_autocomplete
weight: 5
+ region: content
settings:
match_operator: CONTAINS
+ match_limit: 10
size: 60
placeholder: ''
- region: content
third_party_settings: { }
hidden: { }
diff --git a/tests/fixtures/config/core.entity_form_display.node.test_type.secondary.yml b/tests/fixtures/config/core.entity_form_display.node.test_type.secondary.yml
index b1fdb88ec..f8f05beb1 100644
--- a/tests/fixtures/config/core.entity_form_display.node.test_type.secondary.yml
+++ b/tests/fixtures/config/core.entity_form_display.node.test_type.secondary.yml
@@ -1,12 +1,12 @@
+uuid: e24c2b3c-60e4-4ff5-99cb-80e5e67e7b04
langcode: en
status: true
dependencies:
config:
+ - core.entity_form_mode.node.secondary
- field.field.node.test_type.field_member_of
+ - field.field.node.test_type.field_model
- node.type.test_type
- module:
- - path
- - text
id: node.test_type.secondary
targetEntityType: node
bundle: test_type
@@ -23,6 +23,8 @@ content:
hidden:
created: true
field_media: true
+ field_member_of: true
+ field_model: true
field_node: true
langcode: true
path: true
diff --git a/tests/fixtures/config/core.entity_form_mode.node.secondary.yml b/tests/fixtures/config/core.entity_form_mode.node.secondary.yml
index 07f45bbe2..e1fc76345 100644
--- a/tests/fixtures/config/core.entity_form_mode.node.secondary.yml
+++ b/tests/fixtures/config/core.entity_form_mode.node.secondary.yml
@@ -1,9 +1,10 @@
+uuid: d9f22219-ff4c-48cc-a98a-6ccaad7a880d
langcode: en
status: true
dependencies:
module:
- node
id: node.secondary
-label: Secondary
+label: Secondary
targetEntityType: node
-cache: true
+cache: true
\ No newline at end of file
diff --git a/tests/fixtures/config/core.entity_view_display.node.test_type.default.yml b/tests/fixtures/config/core.entity_view_display.node.test_type.default.yml
index cf798265b..e4414e611 100644
--- a/tests/fixtures/config/core.entity_view_display.node.test_type.default.yml
+++ b/tests/fixtures/config/core.entity_view_display.node.test_type.default.yml
@@ -1,11 +1,12 @@
+uuid: 36f4aecf-0e14-4281-a213-ca7d129da52a
langcode: en
status: true
dependencies:
config:
- field.field.node.test_type.field_member_of
+ - field.field.node.test_type.field_model
- node.type.test_type
module:
- - text
- user
id: node.test_type.default
targetEntityType: node
@@ -13,14 +14,24 @@ bundle: test_type
mode: default
content:
field_member_of:
- weight: 102
+ type: entity_reference_label
label: above
settings:
link: true
third_party_settings: { }
+ weight: 102
+ region: content
+ field_model:
type: entity_reference_label
+ label: above
+ settings:
+ link: true
+ third_party_settings: { }
+ weight: 103
region: content
links:
+ settings: { }
+ third_party_settings: { }
weight: 100
region: content
hidden:
diff --git a/tests/fixtures/config/core.entity_view_display.node.test_type.teaser.yml b/tests/fixtures/config/core.entity_view_display.node.test_type.teaser.yml
index d67060f72..f72954281 100644
--- a/tests/fixtures/config/core.entity_view_display.node.test_type.teaser.yml
+++ b/tests/fixtures/config/core.entity_view_display.node.test_type.teaser.yml
@@ -1,19 +1,25 @@
-uuid: 0308339a-a9e5-4a04-8ce2-9f62ed504e34
+uuid: b337f462-8e64-4853-be65-9e03b94515bf
langcode: en
status: true
dependencies:
config:
- core.entity_view_mode.node.teaser
+ - field.field.node.test_type.field_member_of
+ - field.field.node.test_type.field_model
- node.type.test_type
module:
- - text
- user
id: node.test_type.teaser
targetEntityType: node
bundle: test_type
mode: teaser
content:
+ links:
+ settings: { }
+ third_party_settings: { }
+ weight: 100
+ region: content
hidden:
- body: true
- links: true
- langcode: true
+ field_member_of: true
+ field_model: true
+ langcode: true
\ No newline at end of file
diff --git a/tests/fixtures/config/rest.resource.entity.file.yml b/tests/fixtures/config/rest.resource.entity.file.yml
index 6a136c3cb..dbd6bb62c 100644
--- a/tests/fixtures/config/rest.resource.entity.file.yml
+++ b/tests/fixtures/config/rest.resource.entity.file.yml
@@ -1,3 +1,4 @@
+uuid: 11c4e25e-6b06-4270-b934-243e4f4aade1
langcode: en
status: true
dependencies:
@@ -26,3 +27,4 @@ configuration:
supported_auth:
- basic_auth
- jwt_auth
+ - cookie
diff --git a/tests/fixtures/config/rest.resource.entity.media.yml b/tests/fixtures/config/rest.resource.entity.media.yml
index 3ed0286e7..cd89243d3 100644
--- a/tests/fixtures/config/rest.resource.entity.media.yml
+++ b/tests/fixtures/config/rest.resource.entity.media.yml
@@ -1,3 +1,4 @@
+uuid: 9a5633b1-6a1a-40b2-8482-c24cf44122ff
langcode: en
status: true
dependencies:
@@ -5,7 +6,7 @@ dependencies:
- basic_auth
- jsonld
- jwt
- - media_entity
+ - media
- serialization
- user
id: entity.media
diff --git a/tests/fixtures/config/rest.resource.entity.node.yml b/tests/fixtures/config/rest.resource.entity.node.yml
index e7d4c7cc3..a3e253e2e 100644
--- a/tests/fixtures/config/rest.resource.entity.node.yml
+++ b/tests/fixtures/config/rest.resource.entity.node.yml
@@ -1,3 +1,4 @@
+uuid: 08a90469-0355-4b41-a4d6-cb6b53072b8c
langcode: en
status: true
dependencies:
diff --git a/tests/fixtures/config/rest.resource.entity.taxonomy_term.yml b/tests/fixtures/config/rest.resource.entity.taxonomy_term.yml
index 25b6fbb26..16d96c3ef 100644
--- a/tests/fixtures/config/rest.resource.entity.taxonomy_term.yml
+++ b/tests/fixtures/config/rest.resource.entity.taxonomy_term.yml
@@ -1,3 +1,4 @@
+uuid: 7534e393-12a7-498c-a4a3-a7bbe4ff9a5d
langcode: en
status: true
dependencies:
diff --git a/tests/modules/integer_weight_test_views/integer_weight_test_views.info.yml b/tests/modules/integer_weight_test_views/integer_weight_test_views.info.yml
index 7e2981427..77b12ec37 100644
--- a/tests/modules/integer_weight_test_views/integer_weight_test_views.info.yml
+++ b/tests/modules/integer_weight_test_views/integer_weight_test_views.info.yml
@@ -2,7 +2,7 @@ name: 'Integer weight test views'
type: module
description: 'Provides default views for integer weight views tests.'
package: Testing
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^8 || ^9 || ^10
dependencies:
- drupal:node
- drupal:views
diff --git a/tests/modules/integer_weight_test_views/test_views/views.view.test_integer_weight.yml b/tests/modules/integer_weight_test_views/test_views/views.view.test_integer_weight.yml
index 08dc4912a..9ccb76a81 100644
--- a/tests/modules/integer_weight_test_views/test_views/views.view.test_integer_weight.yml
+++ b/tests/modules/integer_weight_test_views/test_views/views.view.test_integer_weight.yml
@@ -4,6 +4,7 @@ dependencies:
config:
- node.type.repo_item
module:
+ - islandora
- node
- user
id: test_integer_weight
@@ -13,87 +14,36 @@ description: ''
tag: ''
base_table: node_field_data
base_field: nid
-core: 8.x
display:
default:
- display_plugin: default
id: default
display_title: Master
+ display_plugin: default
position: 0
display_options:
- access:
- type: perm
- options:
- perm: 'access content'
- cache:
- type: tag
- options: { }
- query:
- type: views_query
- options:
- disable_sql_rewrite: false
- distinct: false
- replica: false
- query_comment: ''
- query_tags: { }
- exposed_form:
- type: basic
- options:
- submit_button: Apply
- reset_button: false
- reset_button_label: Reset
- exposed_sorts_label: 'Sort by'
- expose_sort_order: true
- sort_asc_label: Asc
- sort_desc_label: Desc
- pager:
- type: mini
- options:
- items_per_page: 10
- offset: 0
- id: 0
- total_pages: null
- expose:
- items_per_page: false
- items_per_page_label: 'Items per page'
- items_per_page_options: '5, 10, 25, 50'
- items_per_page_options_all: false
- items_per_page_options_all_label: '- All -'
- offset: false
- offset_label: Offset
- tags:
- previous: ‹‹
- next: ››
- style:
- type: table
- row:
- type: fields
+ title: 'test weight'
fields:
title:
id: title
table: node_field_data
field: title
+ relationship: none
+ group_type: group
+ admin_label: ''
entity_type: node
entity_field: title
+ plugin_id: field
+ label: Title
+ exclude: false
alter:
alter_text: false
make_link: false
absolute: false
- trim: false
word_boundary: false
ellipsis: false
strip_tags: false
+ trim: false
html: false
- hide_empty: false
- empty_zero: false
- settings:
- link_to_entity: true
- plugin_id: field
- relationship: none
- group_type: group
- admin_label: ''
- label: Title
- exclude: false
element_type: ''
element_class: ''
element_label_type: ''
@@ -103,9 +53,13 @@ display:
element_wrapper_class: ''
element_default_classes: true
empty: ''
+ hide_empty: false
+ empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
+ settings:
+ link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
@@ -123,7 +77,8 @@ display:
relationship: none
group_type: group
admin_label: ''
- label: 'Integer weight selector (field_integer_weight)'
+ plugin_id: integer_weight_selector
+ label: 'Integer Weight Selector (field_integer_weight)'
exclude: false
alter:
alter_text: false
@@ -164,44 +119,57 @@ display:
hide_empty: false
empty_zero: false
hide_alter_empty: true
- range: '20'
- plugin_id: integer_weight_selector
- filters:
- status:
- value: '1'
- table: node_field_data
- field: status
- plugin_id: boolean
- entity_type: node
- entity_field: status
- id: status
+ pager:
+ type: mini
+ options:
+ offset: 0
+ items_per_page: 10
+ total_pages: null
+ id: 0
+ tags:
+ next: ››
+ previous: ‹‹
expose:
- operator: ''
- group: 1
- type:
- id: type
- table: node_field_data
- field: type
- value:
- repo_item:repo_item
- entity_type: node
- entity_field: type
- plugin_id: bundle
+ items_per_page: false
+ items_per_page_label: 'Items per page'
+ items_per_page_options: '5, 10, 25, 50'
+ items_per_page_options_all: false
+ items_per_page_options_all_label: '- All -'
+ offset: false
+ offset_label: Offset
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ access:
+ type: perm
+ options:
+ perm: 'access content'
+ cache:
+ type: tag
+ options: { }
+ empty: { }
sorts:
created:
id: created
table: node_field_data
field: created
- order: DESC
- entity_type: node
- entity_field: created
- plugin_id: date
relationship: none
group_type: group
admin_label: ''
- exposed: false
+ entity_type: node
+ entity_field: created
+ plugin_id: date
+ order: DESC
expose:
label: ''
+ exposed: false
granularity: second
field_integer_weight_value:
id: field_integer_weight_value
@@ -210,17 +178,82 @@ display:
relationship: none
group_type: group
admin_label: ''
+ plugin_id: standard
order: ASC
+ expose:
+ label: ''
+ field_identifier: ''
+ exposed: false
+ arguments: { }
+ filters:
+ status:
+ id: status
+ table: node_field_data
+ field: status
+ entity_type: node
+ entity_field: status
+ plugin_id: boolean
+ value: '1'
+ group: 1
+ expose:
+ operator: ''
+ type:
+ id: type
+ table: node_field_data
+ field: type
+ relationship: none
+ group_type: group
+ admin_label: ''
+ entity_type: node
+ entity_field: type
+ plugin_id: bundle
+ operator: in
+ value:
+ repo_item: repo_item
+ group: 1
exposed: false
expose:
+ operator_id: ''
label: ''
- plugin_id: standard
- title: 'test weight'
+ description: ''
+ use_operator: false
+ operator: ''
+ operator_limit_selection: false
+ operator_list: { }
+ identifier: ''
+ required: false
+ remember: false
+ multiple: false
+ remember_roles:
+ authenticated: authenticated
+ reduce: false
+ is_grouped: false
+ group_info:
+ label: ''
+ description: ''
+ identifier: ''
+ optional: true
+ widget: select
+ multiple: false
+ remember: false
+ default_group: All
+ default_group_multiple: { }
+ group_items: { }
+ style:
+ type: table
+ row:
+ type: fields
+ query:
+ type: views_query
+ options:
+ query_comment: ''
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_tags: { }
+ relationships: { }
header: { }
footer: { }
- empty: { }
- relationships: { }
- arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
@@ -232,9 +265,9 @@ display:
- user.permissions
tags: { }
page_1:
- display_plugin: page
id: page_1
display_title: Page
+ display_plugin: page
position: 1
display_options:
display_extenders: { }
diff --git a/tests/src/Functional/AddChildTest.php b/tests/src/Functional/AddChildTest.php
index 9fc2f9e23..ed5282c73 100644
--- a/tests/src/Functional/AddChildTest.php
+++ b/tests/src/Functional/AddChildTest.php
@@ -9,13 +9,19 @@
*/
class AddChildTest extends IslandoraFunctionalTestBase {
+ /**
+ * The taxonomy term representing "Collection" items.
+ *
+ * @var \Drupal\taxonomy\TermInterface
+ */
+ protected $collectionTerm;
+
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
- $this->parent =
$this->collectionTerm = $this->container->get('entity_type.manager')->getStorage('taxonomy_term')->create([
'name' => 'Collection',
'vid' => $this->testVocabulary->id(),
diff --git a/tests/src/Functional/AddMediaToNodeTest.php b/tests/src/Functional/AddMediaToNodeTest.php
index 329097757..ab67a6604 100644
--- a/tests/src/Functional/AddMediaToNodeTest.php
+++ b/tests/src/Functional/AddMediaToNodeTest.php
@@ -3,7 +3,7 @@
namespace Drupal\Tests\islandora\Functional;
use Drupal\Core\Url;
-use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
+use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
/**
* Tests the RelatedLinkHeader view alter.
@@ -12,7 +12,7 @@
*/
class AddMediaToNodeTest extends IslandoraFunctionalTestBase {
- use EntityReferenceTestTrait;
+ use EntityReferenceFieldCreationTrait;
/**
* Node to hold the media.
@@ -31,7 +31,7 @@ class AddMediaToNodeTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
$this->node = $this->container->get('entity_type.manager')->getStorage('node')->create([
diff --git a/tests/src/Functional/ContentEntityTypeTest.php b/tests/src/Functional/ContentEntityTypeTest.php
index 362ff7fbb..2fa9b5746 100644
--- a/tests/src/Functional/ContentEntityTypeTest.php
+++ b/tests/src/Functional/ContentEntityTypeTest.php
@@ -39,7 +39,7 @@ public function testContentEntityType() {
$this->addCondition('test', 'content_entity_type');
$this->getSession()->getPage()->checkField("edit-conditions-content-entity-type-types-node");
$this->getSession()->getPage()->findById("edit-conditions-content-entity-type-context-mapping-node")->selectOption("@node.node_route_context:node");
- $this->getSession()->getPage()->pressButton($this->t('Save and continue'));
+ $this->getSession()->getPage()->pressButton('Save and continue');
$this->addPresetReaction('test', 'index', 'hello_world');
// Create a new node confirm Hello World! is printed to the screen.
@@ -52,7 +52,8 @@ public function testContentEntityType() {
'name[0][value]' => 'Test Media',
'files[field_media_file_0]' => __DIR__ . '/../../fixtures/test_file.txt',
];
- $this->drupalPostForm('media/add/' . $this->testMediaType->id(), $values, $this->t('Save'));
+ $this->drupalGet('media/add/' . $this->testMediaType->id());
+ $this->submitForm($values, 'Save');
$this->assertSession()->pageTextNotContains("Hello World!");
}
diff --git a/tests/src/Functional/DeleteMediaTest.php b/tests/src/Functional/DeleteMediaTest.php
index f112c7006..76298501a 100644
--- a/tests/src/Functional/DeleteMediaTest.php
+++ b/tests/src/Functional/DeleteMediaTest.php
@@ -16,7 +16,7 @@ class DeleteMediaTest extends IslandoraFunctionalTestBase {
*
* @var array
*/
- public static $modules = [
+ protected static $modules = [
'media_test_views',
'context_ui',
'field_ui',
@@ -47,11 +47,13 @@ class DeleteMediaTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
+ $permissions = ['create media', 'delete any media', 'delete any file'];
+
// Create a test user.
- $this->account = $this->createUser(['create media', 'delete any media']);
+ $this->account = $this->createUser($permissions);
list($this->file, $this->media) = $this->makeMediaAndFile($this->account);
}
@@ -86,7 +88,7 @@ public function testDeleteMediaAndFile() {
$this->assertSession()->pageTextContains('Are you sure you want to delete this media and associated files?');
$page->pressButton('Delete');
// Should assert that a media and file were deleted.
- $this->assertSession()->pageTextContains('Deleted 2 items.');
+ $this->assertSession()->pageTextContains("The media with the id $mid has been deleted");
// Attempt to reload the entities.
// Both media and file should be gone.
diff --git a/tests/src/Functional/DeleteNodeWithMediaAndFile.php b/tests/src/Functional/DeleteNodeWithMediaAndFile.php
new file mode 100644
index 000000000..5ee19b7ce
--- /dev/null
+++ b/tests/src/Functional/DeleteNodeWithMediaAndFile.php
@@ -0,0 +1,104 @@
+drupalCreateUser([
+ 'delete any media',
+ 'create media',
+ 'view media',
+ 'bypass node access',
+ 'access files overview',
+ 'administer site configuration',
+ ]);
+ $this->drupalLogin($account);
+
+ $assert_session = $this->assertSession();
+
+ $testImageMediaType = $this->createMediaType('image', ['id' => 'test_image_media_type']);
+ $testImageMediaType->save();
+
+ $this->createEntityReferenceField('media', $testImageMediaType->id(), 'field_media_of', 'Media Of', 'node', 'default', [], 2);
+
+ $node = $this->container->get('entity_type.manager')->getStorage('node')->create([
+ 'type' => 'test_type',
+ 'title' => 'node',
+ ]);
+ $node->save();
+
+ // Make an image for the Media.
+ $file = $this->container->get('entity_type.manager')->getStorage('file')->create([
+ 'uid' => $account->id(),
+ 'uri' => "public://test.jpeg",
+ 'filename' => "test.jpeg",
+ 'filemime' => "image/jpeg",
+ ]);
+ $file->setPermanent();
+ $file->save();
+
+ $this->drupalGet("node/1/delete");
+ $assert_session->pageTextNotContains('Delete all associated medias and nodes');
+
+ // Make the media, and associate it with the image and node.
+ $media1 = $this->container->get('entity_type.manager')->getStorage('media')->create([
+ 'bundle' => $testImageMediaType->id(),
+ 'name' => 'Media1',
+ 'field_media_image' =>
+ [
+ 'target_id' => $file->id(),
+ 'alt' => 'Some Alt',
+ 'title' => 'Some Title',
+ ],
+ 'field_media_of' => ['target_id' => $node->id()],
+ ]);
+ $media1->save();
+
+ $media2 = $this->container->get('entity_type.manager')->getStorage('media')->create([
+ 'bundle' => $testImageMediaType->id(),
+ 'name' => 'Media2',
+ 'field_media_image' =>
+ [
+ 'target_id' => $file->id(),
+ 'alt' => 'Some Alt',
+ 'title' => 'Some Title',
+ ],
+ 'field_media_of' => ['target_id' => $node->id()],
+ ]);
+ $media2->save();
+
+ $this->drupalGet("admin/config/islandora/core");
+ $assert_session->pageTextContains('Node Delete with Media and Files');
+ \Drupal::configFactory()->getEditable('islandora.settings')->set('delete_media_and_files', TRUE)->save();
+
+ $delete = ['delete_associated_content' => TRUE];
+
+ $this->drupalGet("node/1/delete");
+ $assert_session->pageTextContains('Media1');
+ $assert_session->pageTextContains('Media2');
+ $this->submitForm($delete, 'Delete');
+
+ $assert_session->pageTextContains($media1->id());
+ $assert_session->pageTextContains($media2->id());
+
+ $this->drupalGet("media/1/delete");
+ $assert_session->pageTextContains('Page not found');
+
+ $this->drupalGet("media/2/delete");
+ $assert_session->pageTextContains('Page not found');
+
+ $this->drupalGet("/admin/content/files");
+ $assert_session->pageTextNotContains('test.jpeg');
+
+ }
+
+}
diff --git a/tests/src/Functional/DerivativeReactionTest.php b/tests/src/Functional/DerivativeReactionTest.php
index e1b1c8276..720b8a45b 100644
--- a/tests/src/Functional/DerivativeReactionTest.php
+++ b/tests/src/Functional/DerivativeReactionTest.php
@@ -19,7 +19,7 @@ class DerivativeReactionTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
$this->node = $this->container->get('entity_type.manager')->getStorage('node')->create([
@@ -52,7 +52,8 @@ public function testExecuteDerivativeReaction() {
'files[field_media_file_0]' => __DIR__ . '/../../fixtures/test_file.txt',
'field_media_of[0][target_id]' => 'Test Node',
];
- $this->drupalPostForm('media/add/' . $this->testMediaType->id(), $values, $this->t('Save'));
+ $this->drupalGet('media/add/' . $this->testMediaType->id());
+ $this->submitForm($values, 'Save');
// field_media_of is set and there's a file, so derivatives should fire.
$this->assertSession()->pageTextContains("Hello World!");
@@ -70,9 +71,9 @@ public function testExecuteDerivativeReaction() {
'files[field_media_file_0]' => __DIR__ . '/../../fixtures/test_file2.txt',
];
$this->drupalGet($media_url . '/edit');
- $this->getSession()->getPage()->pressButton($this->t('Remove'));
+ $this->getSession()->getPage()->pressButton('Remove');
$this->getSession()->getPage()->fillField('files[field_media_file_0]', __DIR__ . '/../../fixtures/test_file2.txt');
- $this->getSession()->getPage()->pressButton($this->t('Save'));
+ $this->getSession()->getPage()->pressButton('Save');
$this->assertSession()->pageTextContains("Hello World!");
}
diff --git a/tests/src/Functional/EmitNodeEventTest.php b/tests/src/Functional/EmitNodeEventTest.php
index 83f062ae7..a18ffbc8f 100644
--- a/tests/src/Functional/EmitNodeEventTest.php
+++ b/tests/src/Functional/EmitNodeEventTest.php
@@ -44,7 +44,7 @@ public function testEmitEvent() {
$this->addCondition('test', 'content_entity_type');
$this->getSession()->getPage()->checkField("edit-conditions-content-entity-type-types-node");
$this->getSession()->getPage()->findById("edit-conditions-content-entity-type-context-mapping-node")->selectOption("@node.node_route_context:node");
- $this->getSession()->getPage()->pressButton($this->t('Save and continue'));
+ $this->getSession()->getPage()->pressButton('Save and continue');
$this->addPresetReaction('test', 'index', $action_id);
$this->assertSession()->statusCodeEquals(200);
@@ -68,7 +68,7 @@ public function testEmitEvent() {
protected function createEmitAction($entity_type, $event_type) {
$this->drupalGet('admin/config/system/actions');
$this->getSession()->getPage()->findById("edit-action")->selectOption("Emit a $entity_type event to a queue/topic");
- $this->getSession()->getPage()->pressButton($this->t('Create'));
+ $this->getSession()->getPage()->pressButton('Create');
$this->assertSession()->statusCodeEquals(200);
$action_id = "emit_" . $entity_type . "_" . lcfirst($event_type);
@@ -76,7 +76,7 @@ protected function createEmitAction($entity_type, $event_type) {
$this->getSession()->getPage()->fillField('edit-id', $action_id);
$this->getSession()->getPage()->fillField('edit-queue', "emit-$entity_type-" . lcfirst($event_type));
$this->getSession()->getPage()->findById("edit-event")->selectOption($event_type);
- $this->getSession()->getPage()->pressButton($this->t('Save'));
+ $this->getSession()->getPage()->pressButton('Save');
$this->assertSession()->statusCodeEquals(200);
return $action_id;
diff --git a/tests/src/Functional/EntityBundleTest.php b/tests/src/Functional/EntityBundleTest.php
index bde1ee40e..27183826f 100644
--- a/tests/src/Functional/EntityBundleTest.php
+++ b/tests/src/Functional/EntityBundleTest.php
@@ -27,7 +27,7 @@ public function testEntityBundleType() {
$this->addCondition('test', 'islandora_entity_bundle');
$this->getSession()->getPage()->checkField("edit-conditions-islandora-entity-bundle-bundles-test-type");
$this->getSession()->getPage()->findById("edit-conditions-islandora-entity-bundle-context-mapping-node")->selectOption("@node.node_route_context:node");
- $this->getSession()->getPage()->pressButton($this->t('Save and continue'));
+ $this->getSession()->getPage()->pressButton('Save and continue');
$this->addPresetReaction('test', 'index', 'hello_world');
// Create a new test_type confirm Hello World! is printed to the screen.
diff --git a/tests/src/Functional/FormDisplayAlterReactionTest.php b/tests/src/Functional/FormDisplayAlterReactionTest.php
index d394b7288..fe70aa645 100644
--- a/tests/src/Functional/FormDisplayAlterReactionTest.php
+++ b/tests/src/Functional/FormDisplayAlterReactionTest.php
@@ -49,7 +49,7 @@ public function testViewModeAlter() {
$this->drupalGet("admin/structure/context/test/reaction/add/form_display_alter");
$this->getSession()->getPage()->findById("edit-reactions-form-display-alter-mode")->selectOption('node.secondary');
- $this->getSession()->getPage()->pressButton($this->t('Save and continue'));
+ $this->getSession()->getPage()->pressButton('Save and continue');
$this->assertSession()->statusCodeEquals(200);
drupal_flush_all_caches();
diff --git a/tests/src/Functional/GenerateDerivativeTestBase.php b/tests/src/Functional/GenerateDerivativeTestBase.php
index 0f67d5919..c5ec9701c 100644
--- a/tests/src/Functional/GenerateDerivativeTestBase.php
+++ b/tests/src/Functional/GenerateDerivativeTestBase.php
@@ -29,7 +29,7 @@ abstract class GenerateDerivativeTestBase extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
$this->createUserAndLogin();
diff --git a/tests/src/Functional/IndexingTest.php b/tests/src/Functional/IndexingTest.php
index e995329d7..cf2dbb667 100644
--- a/tests/src/Functional/IndexingTest.php
+++ b/tests/src/Functional/IndexingTest.php
@@ -12,7 +12,7 @@ class IndexingTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
// Create an action that dsm's "Goodbye, Cruel World!".
@@ -63,9 +63,10 @@ public function testIndexing() {
// Add the Goodbye World reaction.
$this->addPresetReaction('test', 'delete', 'goodbye_world');
+ $this->drupalGet("$url/delete");
// Delete the node.
- $this->drupalPostForm("$url/delete", [], $this->t('Delete'));
+ $this->submitForm([], 'Delete');
$this->assertSession()->statusCodeEquals(200);
// Confirm Goodbye, Cruel World! is printed to the screen.
diff --git a/tests/src/Functional/IslandoraFunctionalTestBase.php b/tests/src/Functional/IslandoraFunctionalTestBase.php
index 2e4c88e89..e58ec0162 100644
--- a/tests/src/Functional/IslandoraFunctionalTestBase.php
+++ b/tests/src/Functional/IslandoraFunctionalTestBase.php
@@ -8,7 +8,7 @@
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\link\LinkItemInterface;
use Drupal\Tests\BrowserTestBase;
-use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
+use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\Tests\TestFileCreationTrait;
@@ -17,7 +17,7 @@
*/
class IslandoraFunctionalTestBase extends BrowserTestBase {
- use EntityReferenceTestTrait;
+ use EntityReferenceFieldCreationTrait;
use TestFileCreationTrait;
use MediaTypeCreationTrait;
use StringTranslationTrait;
@@ -88,7 +88,7 @@ class IslandoraFunctionalTestBase extends BrowserTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
// Delete the node rest config that's bootstrapped with Drupal.
@@ -306,7 +306,7 @@ protected function addPresetReaction($context_id, $reaction_type, $action_id) {
->getPage()
->findById("edit-reactions-$reaction_type-actions")
->selectOption($action_id);
- $this->getSession()->getPage()->pressButton($this->t('Save and continue'));
+ $this->getSession()->getPage()->pressButton('Save and continue');
$this->assertSession()->statusCodeEquals(200);
}
@@ -314,7 +314,8 @@ protected function addPresetReaction($context_id, $reaction_type, $action_id) {
* Create a new node by posting its add form.
*/
protected function postNodeAddForm($bundle_id, $values, $button_text) {
- $this->drupalPostForm("node/add/$bundle_id", $values, $this->t('@text', ['@text' => $button_text]));
+ $this->drupalGet("node/add/$bundle_id");
+ $this->submitForm($values, $button_text);
$this->assertSession()->statusCodeEquals(200);
}
@@ -322,7 +323,8 @@ protected function postNodeAddForm($bundle_id, $values, $button_text) {
* Create a new node by posting its add form.
*/
protected function postTermAddForm($taxomony_id, $values, $button_text) {
- $this->drupalPostForm("admin/structure/taxonomy/manage/$taxomony_id/add", $values, $this->t('@text', ['@text' => $button_text]));
+ $this->drupalGet("admin/structure/taxonomy/manage/$taxomony_id/add");
+ $this->submitForm($values, $button_text);
$this->assertSession()->statusCodeEquals(200);
}
@@ -330,7 +332,8 @@ protected function postTermAddForm($taxomony_id, $values, $button_text) {
* Edits a node by posting its edit form.
*/
protected function postEntityEditForm($entity_url, $values, $button_text) {
- $this->drupalPostForm("$entity_url/edit", $values, $this->t('@text', ['@text' => $button_text]));
+ $this->drupalGet("$entity_url/edit");
+ $this->submitForm($values, $button_text);
$this->assertSession()->statusCodeEquals(200);
}
@@ -435,8 +438,8 @@ protected function makeMediaAndFile(AccountInterface $account) {
'uri' => "public://test_file.txt",
'filename' => "test_file.txt",
'filemime' => "text/plain",
- 'status' => FILE_STATUS_PERMANENT,
]);
+ $file->setPermanent();
$file->save();
// Get the source field for the media.
diff --git a/tests/src/Functional/IslandoraImageFormatterTest.php b/tests/src/Functional/IslandoraImageFormatterTest.php
index 33f6e1e6b..1b40f7a8d 100644
--- a/tests/src/Functional/IslandoraImageFormatterTest.php
+++ b/tests/src/Functional/IslandoraImageFormatterTest.php
@@ -59,8 +59,8 @@ public function testIslandoraImageFormatter() {
'uri' => "public://test.jpeg",
'filename' => "test.jpeg",
'filemime' => "image/jpeg",
- 'status' => FILE_STATUS_PERMANENT,
]);
+ $file->setPermanent();
$file->save();
// Make the media, and associate it with the image and node.
@@ -92,7 +92,7 @@ public function testIslandoraImageFormatter() {
':title' => 'Some Title',
]
);
- $this->assertEqual(count($elements), 1, 'Image linked to content formatter displaying points to Node and not Media.');
+ $this->assertEquals(count($elements), 1, 'Image linked to content formatter displaying points to Node and not Media.');
}
}
diff --git a/tests/src/Functional/IslandoraSettingsFormTest.php b/tests/src/Functional/IslandoraSettingsFormTest.php
index 92cfc6a29..18bfcafb4 100644
--- a/tests/src/Functional/IslandoraSettingsFormTest.php
+++ b/tests/src/Functional/IslandoraSettingsFormTest.php
@@ -14,7 +14,7 @@ class IslandoraSettingsFormTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
// Create a test user.
@@ -36,20 +36,25 @@ public function testJwtExpiry() {
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains("JWT Expiry");
$this->assertSession()->fieldValueEquals('edit-jwt-expiry', '+2 hour');
+ $this->drupalGet('/admin/config/islandora/core');
// Blank is not allowed.
- $this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => ""], $this->t('Save configuration'));
+ $this->submitForm(['edit-jwt-expiry' => ""], 'Save configuration');
$this->assertSession()->pageTextContainsOnce('"" is not a valid time or interval expression.');
+ $this->drupalGet('/admin/config/islandora/core');
// Negative is not allowed.
- $this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => "-2 hours"], $this->t('Save configuration'));
+ $this->submitForm(['edit-jwt-expiry' => "-2 hours"], 'Save configuration');
$this->assertSession()->pageTextContainsOnce('Time or interval expression cannot be negative');
+ $this->drupalGet('/admin/config/islandora/core');
// Must include an integer value.
- $this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => "last hour"], $this->t('Save configuration'));
+ $this->submitForm(['edit-jwt-expiry' => "last hour"], 'Save configuration');
$this->assertSession()->pageTextContainsOnce('No numeric interval specified, for example "1 day"');
+ $this->drupalGet('/admin/config/islandora/core');
// Must have an accepted interval.
- $this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => "1 fortnight"], $this->t('Save configuration'));
+ $this->submitForm(['edit-jwt-expiry' => "1 fortnight"], 'Save configuration');
$this->assertSession()->pageTextContainsOnce('No time interval found, please include one of');
+ $this->drupalGet('/admin/config/islandora/core');
// Test a valid setting.
- $this->drupalPostForm('/admin/config/islandora/core', ['edit-jwt-expiry' => "2 weeks"], $this->t('Save configuration'));
+ $this->submitForm(['edit-jwt-expiry' => "2 weeks"], 'Save configuration');
$this->assertSession()->pageTextContainsOnce('The configuration options have been saved.');
}
diff --git a/tests/src/Functional/JsonldSelfReferenceReactionTest.php b/tests/src/Functional/JsonldSelfReferenceReactionTest.php
index f3c882718..c8acfcc96 100644
--- a/tests/src/Functional/JsonldSelfReferenceReactionTest.php
+++ b/tests/src/Functional/JsonldSelfReferenceReactionTest.php
@@ -2,6 +2,8 @@
namespace Drupal\Tests\islandora\Functional;
+use function GuzzleHttp\json_decode;
+
/**
* Class MappingUriPredicateReactionTest.
*
@@ -10,10 +12,17 @@
*/
class JsonldSelfReferenceReactionTest extends IslandoraFunctionalTestBase {
+ /**
+ * An RDF Mapping object.
+ *
+ * @var \Drupal\rdf\Entity\RdfMapping
+ */
+ protected $rdfMapping;
+
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
$types = ['schema:Thing'];
@@ -51,7 +60,7 @@ public function testMappingReaction() {
$this->postNodeAddForm('test_type',
['title[0][value]' => 'Test Node'],
- $this->t('Save'));
+ 'Save');
$this->assertSession()->pageTextContains("Test Node");
$url = $this->getUrl();
@@ -61,7 +70,7 @@ public function testMappingReaction() {
$contents = $this->drupalGet($url . '?_format=jsonld');
$this->assertSession()->statusCodeEquals(200);
- $json = \GuzzleHttp\json_decode($contents, TRUE);
+ $json = json_decode($contents, TRUE);
$this->assertArrayHasKey('http://purl.org/dc/terms/title',
$json['@graph'][0], 'Missing dcterms:title key');
$this->assertEquals(
@@ -103,7 +112,7 @@ public function testMappingReaction() {
drupal_flush_all_caches();
$new_contents = $this->drupalGet($url . '?_format=jsonld');
- $json = \GuzzleHttp\json_decode($new_contents, TRUE);
+ $json = json_decode($new_contents, TRUE);
$this->assertEquals(
'Test Node',
$json['@graph'][0]['http://purl.org/dc/terms/title'][0]['@value'],
@@ -123,7 +132,7 @@ public function testMappingReaction() {
$this->assertSession()
->pageTextContains("The context $context_name has been saved");
$new_contents = $this->drupalGet($url . '?_format=jsonld');
- $json = \GuzzleHttp\json_decode($new_contents, TRUE);
+ $json = json_decode($new_contents, TRUE);
$this->assertEquals(
'Test Node',
$json['@graph'][0]['http://purl.org/dc/terms/title'][0]['@value'],
@@ -161,7 +170,7 @@ public function testMappingReactionForMedia() {
$contents = $this->drupalGet($media_url . '?_format=jsonld');
$this->assertSession()->statusCodeEquals(200);
- $json = \GuzzleHttp\json_decode($contents, TRUE);
+ $json = json_decode($contents, TRUE);
$this->assertEquals(
"$media_url?_format=jsonld",
$json['@graph'][0]['@id'],
@@ -186,7 +195,7 @@ public function testMappingReactionForMedia() {
drupal_flush_all_caches();
$new_contents = $this->drupalGet($media_url . '?_format=jsonld');
- $json = \GuzzleHttp\json_decode($new_contents, TRUE);
+ $json = json_decode($new_contents, TRUE);
$this->assertEquals(
"$media_url?_format=jsonld",
$json['@graph'][0]['http://www.iana.org/assignments/relation/describedby'][0]['@id'],
diff --git a/tests/src/Functional/JsonldTypeAlterReactionTest.php b/tests/src/Functional/JsonldTypeAlterReactionTest.php
index e5d21abc6..7e2005842 100644
--- a/tests/src/Functional/JsonldTypeAlterReactionTest.php
+++ b/tests/src/Functional/JsonldTypeAlterReactionTest.php
@@ -2,6 +2,8 @@
namespace Drupal\Tests\islandora\Functional;
+use function GuzzleHttp\json_decode;
+
/**
* Tests Jsonld Alter Reaction.
*
@@ -20,23 +22,61 @@ public function testMappingReaction() {
'administer node fields',
]);
$this->drupalLogin($account);
+ $this->drupalGet('admin/structure/types/manage/test_type/fields/add-field');
// Add the typed predicate we will select in the reaction config.
// Taken from FieldUiTestTrait->fieldUIAddNewField.
- $this->drupalPostForm('admin/structure/types/manage/test_type/fields/add-field', [
- 'new_storage_type' => 'string',
- 'label' => 'Typed Predicate',
- 'field_name' => 'type_predicate',
- ], $this->t('Save and continue'));
- $this->drupalPostForm(NULL, [], $this->t('Save field settings'));
- $this->drupalPostForm(NULL, [], $this->t('Save settings'));
- $this->assertRaw('field_type_predicate', 'Redirected to "Manage fields" page.');
+ if (version_compare(\Drupal::VERSION, '10.3.x-dev', 'lt')) {
+ $this->getSession()->getPage()->selectFieldOption('new_storage_type', 'plain_text');
+ // For Drupal 10.2, we first need to submit the form with the elements
+ // displayed on initial page load. The form is using AJAX to send a
+ // second element after we selected the radio button above
+ // we can instead get the second element by submitting the form
+ // and having it throw an error since the required field is missing.
+ // @todo refactor this as a functional javascript test.
+ $this->submitForm([
+ 'new_storage_type' => 'plain_text',
+ 'label' => 'Typed Predicate',
+ 'field_name' => 'type_predicate',
+ ], 'Continue');
+
+ // Now we can proceed, selecting the plain text (i.e. string)
+ // for the second element now that the element is displayed after
+ // the initial form submission.
+ $this->getSession()->getPage()->selectFieldOption('group_field_options_wrapper', 'string');
+ $this->submitForm([
+ 'new_storage_type' => 'plain_text',
+ 'label' => 'Typed Predicate',
+ 'field_name' => 'type_predicate',
+ 'group_field_options_wrapper' => 'string',
+ ], 'Continue');
+ }
+ else {
+ $this->getSession()->getPage()->selectFieldOption('new_storage_type', 'plain_text');
+ // For Drupal 10.3 the label is not visible at first.
+ // @todo refactor this as a functional javascript test.
+ $this->submitForm([
+ 'new_storage_type' => 'plain_text',
+ ], 'Continue');
+
+ // Now we can proceed, entering a label and selecting Text (plain)
+ // for the second element now that the elements are displayed after
+ // the initial form submission.
+ $this->getSession()->getPage()->selectFieldOption('group_field_options_wrapper', 'string');
+ $this->submitForm([
+ 'label' => 'Typed Predicate',
+ 'field_name' => 'type_predicate',
+ 'group_field_options_wrapper' => 'string',
+ ], 'Continue');
+ }
+ $this->submitForm([], 'Save settings');
+ $this->assertSession()->responseContains('field_type_predicate');
// Add the test node.
$this->postNodeAddForm('test_type', [
'title[0][value]' => 'Test Node',
'field_type_predicate[0][value]' => 'schema:Organization',
- ], $this->t('Save'));
+ ], 'Save');
$this->assertSession()->pageTextContains("Test Node");
$url = $this->getUrl();
@@ -46,7 +86,7 @@ public function testMappingReaction() {
$contents = $this->drupalGet($url . '?_format=jsonld');
$this->assertSession()->statusCodeEquals(200);
- $json = \GuzzleHttp\json_decode($contents, TRUE);
+ $json = json_decode($contents, TRUE);
$this->assertArrayHasKey('@type',
$json['@graph'][0], 'Missing @type');
$this->assertEquals(
@@ -73,7 +113,7 @@ public function testMappingReaction() {
$this->addCondition('test', 'islandora_entity_bundle');
$this->getSession()->getPage()->checkField("edit-conditions-islandora-entity-bundle-bundles-test-type");
$this->getSession()->getPage()->findById("edit-conditions-islandora-entity-bundle-context-mapping-node")->selectOption("@node.node_route_context:node");
- $this->getSession()->getPage()->pressButton($this->t('Save and continue'));
+ $this->getSession()->getPage()->pressButton('Save and continue');
// The first time a Context is saved, you need to clear the cache.
// Subsequent changes to the context don't need a cache rebuild, though.
@@ -81,7 +121,7 @@ public function testMappingReaction() {
// Check for the new @type from the field_type_predicate value.
$new_contents = $this->drupalGet($url . '?_format=jsonld');
- $json = \GuzzleHttp\json_decode($new_contents, TRUE);
+ $json = json_decode($new_contents, TRUE);
$this->assertTrue(
in_array('http://schema.org/Organization', $json['@graph'][0]['@type']),
'Missing altered @type value of http://schema.org/Organization'
diff --git a/tests/src/Functional/LinkHeaderTest.php b/tests/src/Functional/LinkHeaderTest.php
index 7cb741d55..78dd6c994 100644
--- a/tests/src/Functional/LinkHeaderTest.php
+++ b/tests/src/Functional/LinkHeaderTest.php
@@ -42,7 +42,7 @@ class LinkHeaderTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
$account = $this->createUserAndLogin();
@@ -142,7 +142,7 @@ public function testMediaLinkHeaders() {
$media_url = $this->media->toUrl('canonical', ['absolute' => TRUE])->toString();
// Perform a GET request as anonymous.
- $this->drupalGet($media_url, [], ['Cache-Control: no-cache']);
+ $this->drupalGet($media_url, [], ['Cache-Control' => 'no-cache']);
// Check link headers.
$this->assertTrue(
$this->validateLinkHeaderWithUrl('describes', $file_url, '', 'text/plain') == 1,
@@ -174,7 +174,7 @@ public function testMediaLinkHeaders() {
$this->drupalLogin($account);
// Perform a GET request with update media permissions.
- $this->drupalGet($media_url, [], ['Cache-Control: no-cache']);
+ $this->drupalGet($media_url, [], ['Cache-Control' => 'no-cache']);
// Check link headers again, the edit-media link header should be present.
$this->assertTrue(
diff --git a/tests/src/Functional/MediaSourceUpdateTest.php b/tests/src/Functional/MediaSourceUpdateTest.php
index fdea6aef9..3938e9b47 100644
--- a/tests/src/Functional/MediaSourceUpdateTest.php
+++ b/tests/src/Functional/MediaSourceUpdateTest.php
@@ -35,7 +35,7 @@ class MediaSourceUpdateTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
// Make a user with appropriate permissions.
@@ -52,8 +52,8 @@ public function setUp() {
'uri' => "public://test_file.txt",
'filename' => "test_file.txt",
'filemime' => "text/plain",
- 'status' => FILE_STATUS_PERMANENT,
]);
+ $this->file->setPermanent();
$this->file->save();
// Get the source field for the media.
diff --git a/tests/src/Functional/NodeHasTermTest.php b/tests/src/Functional/NodeHasTermTest.php
index eff5b5c34..2b4ee16f8 100644
--- a/tests/src/Functional/NodeHasTermTest.php
+++ b/tests/src/Functional/NodeHasTermTest.php
@@ -13,7 +13,7 @@ class NodeHasTermTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
diff --git a/tests/src/Functional/ViewModeAlterReactionTest.php b/tests/src/Functional/ViewModeAlterReactionTest.php
index 72cdfe447..904414027 100644
--- a/tests/src/Functional/ViewModeAlterReactionTest.php
+++ b/tests/src/Functional/ViewModeAlterReactionTest.php
@@ -26,7 +26,7 @@ class ViewModeAlterReactionTest extends IslandoraFunctionalTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
// Node to be referenced via member of.
@@ -75,7 +75,7 @@ public function testViewModeAlter() {
$this->drupalGet("admin/structure/context/test/reaction/add/view_mode_alter");
$this->getSession()->getPage()->findById("edit-reactions-view-mode-alter-mode")->selectOption('node.teaser');
- $this->getSession()->getPage()->pressButton($this->t('Save and continue'));
+ $this->getSession()->getPage()->pressButton('Save and continue');
$this->assertSession()->statusCodeEquals(200);
drupal_flush_all_caches();
diff --git a/tests/src/FunctionalJavascript/IntegerWeightTest.php b/tests/src/FunctionalJavascript/IntegerWeightTest.php
index ba289aa49..5e10133c4 100644
--- a/tests/src/FunctionalJavascript/IntegerWeightTest.php
+++ b/tests/src/FunctionalJavascript/IntegerWeightTest.php
@@ -8,6 +8,7 @@
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
use Drupal\node\Entity\Node;
+use Drupal\views\Tests\ViewTestData;
/**
* Test integer weight selector.
@@ -30,6 +31,7 @@ class IntegerWeightTest extends WebDriverTestBase {
'views',
'field_ui',
'integer_weight_test_views',
+ 'islandora',
];
/**
@@ -80,29 +82,18 @@ class IntegerWeightTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
+ $this->drupalCreateContentType([
+ 'type' => 'repo_item',
+ 'name' => 'Repository Item',
+ ]);
- $this->adminUser = $this->drupalCreateUser(
- [
- 'administer content types',
- 'administer node fields',
- 'administer node display',
- ]
- );
-
- // Create dummy repo_item type to sort (since we don't have
- // repository_object without islandora_defaults).
- $type = $this->container->get('entity_type.manager')->getStorage('node_type')
- ->create([
- 'type' => 'repo_item',
- 'name' => 'Repository Item',
- ]);
- $type->save();
- $this->container->get('router.builder')->rebuild();
+ $account = $this->createUser(['edit any repo_item content'], 'test', TRUE);
+ $this->drupalLogin($account);
$fieldStorage = FieldStorageConfig::create([
- 'fieldName' => static::$fieldName,
+ 'field_name' => static::$fieldName,
'entity_type' => 'node',
'type' => static::$fieldType,
]);
@@ -124,16 +115,18 @@ public function setUp() {
$this->nodes[] = $node;
}
- ViewsTestData::createTestViews(get_class($this), ['integer_weight_test_views']);
+ ViewTestData::createTestViews(get_class($this), ['integer_weight_test_views']);
}
/**
* Test integer weight selector.
*/
public function testIntegerWeightSelector() {
- $this->drupalGet('test-integer-weight');
- $page = $this->getSession()->getPage();
+ $web_assert = $this->assertSession();
+ $this->drupalGet('/test-integer-weight');
+ $web_assert->pageTextContains('Item 1');
+ $page = $this->getSession()->getPage();
$weight_select1 = $page->findField("field_integer_weight[0][weight]");
$weight_select2 = $page->findField("field_integer_weight[1][weight]");
$weight_select3 = $page->findField("field_integer_weight[2][weight]");
@@ -153,8 +146,8 @@ public function testIntegerWeightSelector() {
$this->assertSession()->pageTextNotContains('You have unsaved changes.');
// Drag and drop 'Item 1' over 'Item 2'.
- $dragged = $this->xpath("//tr[@class='draggable'][1]//a[@class='tabledrag-handle']")[0];
- $target = $this->xpath("//tr[@class='draggable'][2]//a[@class='tabledrag-handle']")[0];
+ $dragged = $this->xpath("//tr[contains(@class, 'draggable')][1]//a[contains(@class, 'tabledrag-handle')]")[0];
+ $target = $this->xpath("//tr[contains(@class, 'draggable')][2]//a[contains(@class, 'tabledrag-handle')]")[0];
$dragged->dragTo($target);
// Pause for javascript to do it's thing.
diff --git a/tests/src/Kernel/EventGeneratorTest.php b/tests/src/Kernel/EventGeneratorTest.php
index c423cda3f..34a5ae925 100644
--- a/tests/src/Kernel/EventGeneratorTest.php
+++ b/tests/src/Kernel/EventGeneratorTest.php
@@ -41,7 +41,7 @@ class EventGeneratorTest extends IslandoraKernelTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
// Create a test user.
@@ -49,7 +49,7 @@ public function setUp() {
$test_type = NodeType::create([
'type' => 'test_type',
- 'label' => 'Test Type',
+ 'name' => 'Test Type',
]);
$test_type->save();
@@ -116,7 +116,6 @@ public function testGenerateDeleteEvent() {
['event' => 'delete', 'queue' => 'islandora-indexing-fcrepo-delete']
);
$msg = json_decode($json, TRUE);
- $msg = json_decode($json, TRUE);
$this->assertBasicStructure($msg);
$this->assertTrue($msg["type"] == "Delete", "Event must be of type 'Delete'.");
diff --git a/tests/src/Kernel/FedoraAdapterTest.php b/tests/src/Kernel/FedoraAdapterTest.php
index 10c5beb11..6d8fee284 100644
--- a/tests/src/Kernel/FedoraAdapterTest.php
+++ b/tests/src/Kernel/FedoraAdapterTest.php
@@ -2,12 +2,15 @@
namespace Drupal\Tests\islandora\Kernel;
+use Prophecy\PhpUnit\ProphecyTrait;
+use GuzzleHttp\Psr7\Utils;
+use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\islandora\Flysystem\Adapter\FedoraAdapter;
use GuzzleHttp\Psr7\Response;
use Islandora\Chullo\IFedoraApi;
use League\Flysystem\Config;
use Prophecy\Argument;
-use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
+use Symfony\Component\Mime\MimeTypeGuesserInterface;
/**
* Tests the Fedora adapter for Flysystem.
@@ -17,6 +20,31 @@
*/
class FedoraAdapterTest extends IslandoraKernelTestBase {
+ use ProphecyTrait;
+ /**
+ * A mimetype guesser prophecy.
+ *
+ * @var \Prophecy\Prophecy\ObjectProphecy
+ */
+ private $mimeGuesser;
+
+ /**
+ * A logger prophecy.
+ *
+ * @var \Prophecy\Prophecy\ObjectProphecy
+ */
+ private $logger;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setUp(): void {
+ parent::setUp();
+ $this->mimeGuesser = $this->prophesize(MimeTypeGuesserInterface::class)
+ ->reveal();
+ $this->logger = $this->prophesize(LoggerChannelInterface::class)->reveal();
+ }
+
/**
* Shared functionality for an adapter.
*/
@@ -32,13 +60,8 @@ protected function createAdapterBase() {
]);
$prophecy->getHeader('Content-Type')->willReturn(['text/plain']);
$prophecy->getHeader('Content-Length')->willReturn([strlen("DERP")]);
- // phpcs:disable
- if (class_exists(\GuzzleHttp\Psr7\Utils::class)) {
- $prophecy->getBody()->willReturn(\GuzzleHttp\Psr7\Utils::streamFor("DERP"));
- } else {
- $prophecy->getBody()->willReturn(\GuzzleHttp\Psr7\stream_for("DERP"));
- }
- // phpcs:enable
+ $prophecy->getBody()->willReturn(Utils::streamFor("DERP"));
+
return $prophecy;
}
@@ -55,10 +78,7 @@ protected function createAdapterForFail() {
$prophecy->getResource('', ['Connection' => 'close'])->willReturn($response);
$api = $prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -73,10 +93,7 @@ protected function createAdapterForFile() {
$prophecy->getResource('', ['Connection' => 'close'])->willReturn($response);
$api = $prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -98,10 +115,7 @@ protected function createAdapterForDirectory() {
$prophecy->getResourceHeaders('', ['Connection' => 'close'])->willReturn($response);
$api = $prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -126,10 +140,7 @@ protected function createAdapterForWrite() {
$api = $fedora_prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -149,10 +160,7 @@ protected function createAdapterForWriteFail() {
$api = $fedora_prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -180,10 +188,7 @@ protected function createAdapterForCreateDir() {
$api = $fedora_prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -199,10 +204,7 @@ protected function createAdapterForDelete() {
$fedora_prophecy->getResourceHeaders('', ['Connection' => 'close'])->willReturn($prophecy->reveal());
$api = $fedora_prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -218,10 +220,7 @@ protected function createAdapterForDeleteFail() {
$api = $fedora_prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -236,7 +235,7 @@ protected function createAdapterForDeleteWithTombstone() {
$head_prophecy = $this->prophesize(Response::class);
$head_prophecy->getStatusCode()->willReturn(410);
$head_prophecy->getHeader('Link')
- ->willReturn('; rel="hasTombstone"');
+ ->willReturn(['; rel="hasTombstone"']);
$tombstone_prophecy = $this->prophesize(Response::class);
$tombstone_prophecy->getStatusCode()->willReturn(204);
@@ -249,10 +248,7 @@ protected function createAdapterForDeleteWithTombstone() {
$api = $fedora_prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -267,7 +263,7 @@ protected function createAdapterForDeleteWithTombstoneFail() {
$head_prophecy = $this->prophesize(Response::class);
$head_prophecy->getStatusCode()->willReturn(410);
$head_prophecy->getHeader('Link')
- ->willReturn('; rel="hasTombstone"');
+ ->willReturn(['; rel="hasTombstone"']);
$tombstone_prophecy = $this->prophesize(Response::class);
$tombstone_prophecy->getStatusCode()->willReturn(500);
@@ -280,10 +276,7 @@ protected function createAdapterForDeleteWithTombstoneFail() {
$api = $fedora_prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- return new FedoraAdapter($api, $mime_guesser);
+ return new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
}
/**
@@ -361,7 +354,7 @@ public function testRead() {
$metadata = $adapter->read('');
$this->assertFileMetadata($metadata);
- $this->assertTrue($metadata['contents'] == "DERP", "Expecting 'content' of 'DERP', received '${metadata['contents']}'");
+ $this->assertTrue($metadata['contents'] == "DERP", "Expecting 'content' of 'DERP', received '{$metadata['contents']}'");
}
/**
@@ -644,10 +637,7 @@ public function testRename() {
$api = $fedora_prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- $adapter = new FedoraAdapter($api, $mime_guesser);
+ $adapter = new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
$this->assertTrue($adapter->rename('', '') == TRUE, "rename() must return TRUE on success");
}
@@ -664,10 +654,7 @@ public function testCreateDirFail() {
$api = $fedora_prophecy->reveal();
- $mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)
- ->reveal();
-
- $adapter = new FedoraAdapter($api, $mime_guesser);
+ $adapter = new FedoraAdapter($api, $this->mimeGuesser, $this->logger);
$this->assertTrue($adapter->createDir('', $this->prophesize(Config::class)
->reveal()) == FALSE, "createDir() must return FALSE on fail");
diff --git a/tests/src/Kernel/FedoraPluginTest.php b/tests/src/Kernel/FedoraPluginTest.php
index 674915078..92a8137ca 100644
--- a/tests/src/Kernel/FedoraPluginTest.php
+++ b/tests/src/Kernel/FedoraPluginTest.php
@@ -2,11 +2,13 @@
namespace Drupal\Tests\islandora\Kernel;
+use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\islandora\Flysystem\Fedora;
-use League\Flysystem\AdapterInterface;
use Islandora\Chullo\IFedoraApi;
+use League\Flysystem\AdapterInterface;
+use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Http\Message\ResponseInterface;
-use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
+use Symfony\Component\Mime\MimeTypeGuesserInterface;
/**
* Tests the Fedora plugin for Flysystem.
@@ -16,6 +18,8 @@
*/
class FedoraPluginTest extends IslandoraKernelTestBase {
+ use ProphecyTrait;
+
/**
* Mocks up a plugin.
*/
@@ -32,8 +36,9 @@ protected function createPlugin($return_code) {
$mime_guesser = $this->prophesize(MimeTypeGuesserInterface::class)->reveal();
$language_manager = $this->container->get('language_manager');
+ $logger = $this->prophesize(LoggerChannelInterface::class)->reveal();
- return new Fedora($api, $mime_guesser, $language_manager);
+ return new Fedora($api, $mime_guesser, $language_manager, $logger);
}
/**
diff --git a/tests/src/Kernel/IslandoraKernelTestBase.php b/tests/src/Kernel/IslandoraKernelTestBase.php
index 5a95cb682..1c98db3e9 100644
--- a/tests/src/Kernel/IslandoraKernelTestBase.php
+++ b/tests/src/Kernel/IslandoraKernelTestBase.php
@@ -12,7 +12,7 @@ abstract class IslandoraKernelTestBase extends KernelTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = [
+ protected static $modules = [
'system',
'user',
'field',
@@ -43,7 +43,7 @@ abstract class IslandoraKernelTestBase extends KernelTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
// Bootstrap minimal Drupal environment to run the tests.
diff --git a/tests/src/Kernel/JwtEventSubscriberTest.php b/tests/src/Kernel/JwtEventSubscriberTest.php
index f97eab9fb..9493ab78a 100644
--- a/tests/src/Kernel/JwtEventSubscriberTest.php
+++ b/tests/src/Kernel/JwtEventSubscriberTest.php
@@ -2,6 +2,7 @@
namespace Drupal\Tests\islandora\Kernel;
+use Prophecy\PhpUnit\ProphecyTrait;
use Drupal\jwt\Authentication\Event\JwtAuthGenerateEvent;
use Drupal\jwt\Authentication\Event\JwtAuthValidEvent;
use Drupal\jwt\Authentication\Event\JwtAuthValidateEvent;
@@ -19,6 +20,7 @@
*/
class JwtEventSubscriberTest extends IslandoraKernelTestBase {
+ use ProphecyTrait;
use UserCreationTrait;
/**
@@ -31,7 +33,7 @@ class JwtEventSubscriberTest extends IslandoraKernelTestBase {
/**
* {@inheritdoc}
*/
- public function setUp() {
+ public function setUp(): void {
parent::setUp();
$this->user = $this->createUser();