ajstanley
11 months ago
144 changed files with 4087 additions and 988 deletions
@ -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@v3 |
||||||
|
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 }} |
@ -1,4 +1,4 @@ |
|||||||
broker_url: 'tcp://localhost:61613' |
broker_url: 'tcp://localhost:61613' |
||||||
jwt_expiry: '+2 hour' |
jwt_expiry: '+2 hour' |
||||||
gemini_url: '' |
delete_media_and_files: TRUE |
||||||
gemini_pseudo_bundles: [] |
gemini_pseudo_bundles: [] |
||||||
|
@ -0,0 +1,3 @@ |
|||||||
|
.container .islandora-media-items { |
||||||
|
margin: 0; |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
islandora: |
||||||
|
version: VERSION |
||||||
|
css: |
||||||
|
theme: |
||||||
|
css/islandora.css: {} |
@ -0,0 +1,16 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* @file |
||||||
|
* Post updates. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Set default value for delete_media_and_files field in settings. |
||||||
|
*/ |
||||||
|
function islandora_post_update_delete_media_and_files() { |
||||||
|
$config_factory = \Drupal::configFactory(); |
||||||
|
$config = $config_factory->getEditable('islandora.settings'); |
||||||
|
$config->set('delete_media_and_files', TRUE); |
||||||
|
$config->save(TRUE); |
||||||
|
} |
@ -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); |
|
@ -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); |
|
@ -1,9 +1,4 @@ |
|||||||
maxDepth: -1 |
maxDepth: -1 |
||||||
includeSelf: FALSE |
includeSelf: FALSE |
||||||
referenceField: field_member_of |
referenceFields: |
||||||
dependencies: |
- field_member_of |
||||||
module: |
|
||||||
- islandora |
|
||||||
enforced: |
|
||||||
module: |
|
||||||
- islandora_breadcrumbs |
|
||||||
|
@ -1,8 +1,7 @@ |
|||||||
name: 'Islandora Breadcrumbs' |
name: 'Islandora Breadcrumbs' |
||||||
type: module |
type: module |
||||||
description: 'Builds breadcrumbs based on field_member_of relationships.' |
description: 'Builds breadcrumbs based on field_member_of relationships.' |
||||||
core: 8.x |
core_version_requirement: ^9 || ^10 |
||||||
core_version_requirement: ^8 || ^9 |
|
||||||
package: Islandora |
package: Islandora |
||||||
dependencies: |
dependencies: |
||||||
- drupal:islandora |
- islandora:islandora |
||||||
|
@ -0,0 +1,18 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* @file |
||||||
|
* Install/update hook implementations. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Update referenceField config to referenceFields. |
||||||
|
*/ |
||||||
|
function islandora_breadcrumbs_update_8001() { |
||||||
|
$config_factory = \Drupal::configFactory(); |
||||||
|
$config = $config_factory->getEditable('islandora_breadcrumbs.breadcrumbs'); |
||||||
|
$config->set('referenceFields', [$config->get('referenceField')]); |
||||||
|
$config->clear('referenceField'); |
||||||
|
$config->save(); |
||||||
|
return "Updated referenceFields config."; |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
system.islandora_breadcrumbs_settings: |
||||||
|
title: 'Breadcrumbs Settings' |
||||||
|
parent: system.admin_config_islandora |
||||||
|
route_name: system.islandora_breadcrumbs_settings |
||||||
|
description: 'Configure Islandora breadcrumb settings' |
@ -0,0 +1,7 @@ |
|||||||
|
system.islandora_breadcrumbs_settings: |
||||||
|
path: '/admin/config/islandora/breadcrumbs' |
||||||
|
defaults: |
||||||
|
_form: 'Drupal\islandora_breadcrumbs\Form\IslandoraBreadcrumbsSettingsForm' |
||||||
|
_title: 'Islandora Breadcrumbs Settings' |
||||||
|
requirements: |
||||||
|
_permission: 'administer site configuration' |
@ -1,6 +1,6 @@ |
|||||||
services: |
services: |
||||||
islandora_breadcrumbs.breadcrumb: |
islandora_breadcrumbs.breadcrumb: |
||||||
class: Drupal\islandora_breadcrumbs\IslandoraBreadcrumbBuilder |
class: Drupal\islandora_breadcrumbs\IslandoraBreadcrumbBuilder |
||||||
arguments: ['@entity_type.manager', '@config.factory'] |
arguments: ['@entity_type.manager', '@config.factory', '@islandora.utils'] |
||||||
tags: |
tags: |
||||||
- { name: breadcrumb_builder, priority: 100 } |
- { name: breadcrumb_builder, priority: 100 } |
||||||
|
@ -0,0 +1,132 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora_breadcrumbs\Form; |
||||||
|
|
||||||
|
use Drupal\Core\Form\ConfigFormBase; |
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Configure islandora_breadcrumbs settings. |
||||||
|
*/ |
||||||
|
class IslandoraBreadcrumbsSettingsForm extends ConfigFormBase { |
||||||
|
|
||||||
|
/** |
||||||
|
* Config settings. |
||||||
|
* |
||||||
|
* @var string |
||||||
|
*/ |
||||||
|
const SETTINGS = 'islandora_breadcrumbs.breadcrumbs'; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getFormId() { |
||||||
|
return 'islandora_breadcrumbs_settings'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
protected function getEditableConfigNames() { |
||||||
|
return [ |
||||||
|
static::SETTINGS, |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function buildForm(array $form, FormStateInterface $form_state) { |
||||||
|
|
||||||
|
$config = $this->config(static::SETTINGS); |
||||||
|
|
||||||
|
$form['maxDepth'] = [ |
||||||
|
'#type' => 'number', |
||||||
|
'#default_value' => $config->get('maxDepth'), |
||||||
|
'#min' => -1, |
||||||
|
'#step' => 1, |
||||||
|
'#title' => $this->t('Maximum number of ancestor breadcrumbs'), |
||||||
|
'#description' => $this->t("Stops adding ancestor references when the chain reaches this number. The count does not include the current node when enabled. The default value, '-1' disables this feature."), |
||||||
|
]; |
||||||
|
|
||||||
|
$form['includeSelf'] = [ |
||||||
|
'#type' => 'checkbox', |
||||||
|
'#title' => $this->t('Include the current node in the breadcrumbs?'), |
||||||
|
'#default_value' => $config->get('includeSelf'), |
||||||
|
]; |
||||||
|
|
||||||
|
// Using the textarea instead of a select so the site maintainer can |
||||||
|
// provide an ordered list of items rather than simply selecting from a |
||||||
|
// list which enforces it's own order. |
||||||
|
$form['referenceFields'] = [ |
||||||
|
'#type' => 'textarea', |
||||||
|
'#title' => $this->t('Entity Reference fields to follow'), |
||||||
|
'#default_value' => implode("\n", $config->get('referenceFields')), |
||||||
|
'#description' => $this->t("Entity Reference field machine names to follow when building the breadcrumbs.<br>One per line.<br>Valid options: @options", |
||||||
|
[ |
||||||
|
"@options" => implode(", ", static::getNodeEntityReferenceFields()), |
||||||
|
] |
||||||
|
), |
||||||
|
'#element_validate' => [[get_class($this), 'validateReferenceFields']], |
||||||
|
|
||||||
|
]; |
||||||
|
|
||||||
|
return parent::buildForm($form, $form_state); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a list of node entity reference field machine names. |
||||||
|
* |
||||||
|
* We use this for building the form field description and for |
||||||
|
* validating the reference fields value. |
||||||
|
*/ |
||||||
|
protected static function getNodeEntityReferenceFields() { |
||||||
|
return array_keys(\Drupal::service('entity_field.manager')->getFieldMapByFieldType('entity_reference')['node']); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Turns a text area into an array of values. |
||||||
|
* |
||||||
|
* Used for validating the field reference text area |
||||||
|
* and saving the form state. |
||||||
|
*/ |
||||||
|
protected static function textToArray($string) { |
||||||
|
return array_filter(array_map('trim', explode("\n", $string)), 'strlen'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Callback for settings form. |
||||||
|
* |
||||||
|
* @param array $element |
||||||
|
* An associative array containing the properties and children of the |
||||||
|
* generic form element. |
||||||
|
* @param \Drupal\Core\Form\FormStateInterface $form_state |
||||||
|
* The current state of the form for the form this element belongs to. |
||||||
|
* |
||||||
|
* @see \Drupal\Core\Render\Element\FormElement::processPattern() |
||||||
|
*/ |
||||||
|
public static function validateReferenceFields(array $element, FormStateInterface $form_state) { |
||||||
|
|
||||||
|
$valid_fields = static::getNodeEntityReferenceFields(); |
||||||
|
|
||||||
|
foreach (static::textToArray($element['#value']) as $value) { |
||||||
|
if (!in_array($value, $valid_fields)) { |
||||||
|
$form_state->setError($element, t('"@field" is not a valid entity reference field!', ["@field" => $value])); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function submitForm(array &$form, FormStateInterface $form_state) { |
||||||
|
$this->configFactory->getEditable(static::SETTINGS) |
||||||
|
->set('referenceFields', static::textToArray($form_state->getValue('referenceFields'))) |
||||||
|
->set('maxDepth', $form_state->getValue('maxDepth')) |
||||||
|
->set('includeSelf', $form_state->getValue('includeSelf')) |
||||||
|
->save(); |
||||||
|
|
||||||
|
parent::submitForm($form, $form_state); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,5 +1,24 @@ |
|||||||
algos: |
algos: |
||||||
sha1: sha1 |
blake2b_128: '0' |
||||||
|
blake2b_160: '0' |
||||||
|
blake2b_224: '0' |
||||||
|
blake2b_256: '0' |
||||||
|
blake2b_384: '0' |
||||||
|
blake2b_512: '0' |
||||||
md5: '0' |
md5: '0' |
||||||
|
sha1: sha1 |
||||||
|
sha224: '0' |
||||||
sha256: '0' |
sha256: '0' |
||||||
dedupe: false |
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: { } |
||||||
|
@ -0,0 +1,20 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* @file |
||||||
|
* Post-update hooks. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Add index to field_weight. |
||||||
|
*/ |
||||||
|
function islandora_core_feature_post_update_add_index_to_field_weight() { |
||||||
|
$storage = \Drupal::entityTypeManager()->getStorage('field_storage_config'); |
||||||
|
$field = $storage->load('node.field_weight'); |
||||||
|
$indexes = $field->getIndexes(); |
||||||
|
$indexes += [ |
||||||
|
'value' => ['value'], |
||||||
|
]; |
||||||
|
$field->setIndexes($indexes); |
||||||
|
$field->save(); |
||||||
|
} |
@ -1,8 +1,7 @@ |
|||||||
name: 'Islandora IIIF' |
name: 'Islandora IIIF' |
||||||
type: module |
type: module |
||||||
description: 'IIIF support for Islandora' |
description: 'IIIF support for Islandora' |
||||||
core: 8.x |
core_version_requirement: ^9 || ^10 |
||||||
core_version_requirement: ^8 || ^9 |
|
||||||
package: Islandora |
package: Islandora |
||||||
dependencies: |
dependencies: |
||||||
- drupal:islandora |
- drupal:islandora |
||||||
|
@ -1,8 +1,7 @@ |
|||||||
name: 'Islandora Image' |
name: 'Islandora Image' |
||||||
type: module |
type: module |
||||||
description: 'Islandora Image derivative actions' |
description: 'Islandora Image derivative actions' |
||||||
core: 8.x |
core_version_requirement: ^9 || ^10 |
||||||
core_version_requirement: ^8 || ^9 |
|
||||||
package: Islandora |
package: Islandora |
||||||
dependencies: |
dependencies: |
||||||
- drupal:islandora |
- drupal:islandora |
||||||
|
@ -1,8 +1,7 @@ |
|||||||
name: 'Islandora Text Extraction' |
name: 'Islandora Text Extraction' |
||||||
type: module |
type: module |
||||||
description: 'Islandora 8 module to connect to Hypercube microservice, and to get text from PDF ingest' |
description: 'Islandora 8 module to connect to Hypercube microservice, and to get text from PDF ingest' |
||||||
core: 8.x |
core_version_requirement: ^9 || ^10 |
||||||
core_version_requirement: ^8 || ^9 |
|
||||||
package: 'Islandora' |
package: 'Islandora' |
||||||
dependencies: |
dependencies: |
||||||
- drupal:islandora |
- drupal:islandora |
||||||
|
@ -0,0 +1,11 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* Islandora exceptions. |
||||||
|
* |
||||||
|
* @package islandora |
||||||
|
*/ |
||||||
|
class IslandoraDerivativeException extends \RuntimeException { |
||||||
|
} |
@ -0,0 +1,258 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\Database\Connection; |
||||||
|
use Drupal\Core\Datetime\DateFormatterInterface; |
||||||
|
use Drupal\Core\DependencyInjection\DependencySerializationTrait; |
||||||
|
use Drupal\Core\Entity\EntityTypeManagerInterface; |
||||||
|
use Drupal\Core\Messenger\MessengerInterface; |
||||||
|
use Drupal\Core\Session\AccountProxyInterface; |
||||||
|
use Drupal\Core\StringTranslation\StringTranslationTrait; |
||||||
|
use Drupal\Core\Url; |
||||||
|
use Drupal\file\FileInterface; |
||||||
|
use Drupal\islandora\IslandoraUtils; |
||||||
|
use Drupal\media\MediaInterface; |
||||||
|
use Drupal\node\NodeInterface; |
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpException; |
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract addition batch processor. |
||||||
|
*/ |
||||||
|
abstract class AbstractBatchProcessor { |
||||||
|
|
||||||
|
use FieldTrait; |
||||||
|
use DependencySerializationTrait; |
||||||
|
use StringTranslationTrait; |
||||||
|
|
||||||
|
/** |
||||||
|
* The entity type manager service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|null |
||||||
|
*/ |
||||||
|
protected ?EntityTypeManagerInterface $entityTypeManager = NULL; |
||||||
|
|
||||||
|
/** |
||||||
|
* The database connection serivce. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Database\Connection|null |
||||||
|
*/ |
||||||
|
protected ?Connection $database; |
||||||
|
|
||||||
|
/** |
||||||
|
* The current user. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Session\AccountProxyInterface|null |
||||||
|
*/ |
||||||
|
protected ?AccountProxyInterface $currentUser; |
||||||
|
|
||||||
|
/** |
||||||
|
* The messenger service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Messenger\MessengerInterface |
||||||
|
*/ |
||||||
|
protected MessengerInterface $messenger; |
||||||
|
|
||||||
|
/** |
||||||
|
* The date formatter service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Datetime\DateFormatterInterface |
||||||
|
*/ |
||||||
|
protected DateFormatterInterface $dateFormatter; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
*/ |
||||||
|
public function __construct( |
||||||
|
EntityTypeManagerInterface $entity_type_manager, |
||||||
|
Connection $database, |
||||||
|
AccountProxyInterface $current_user, |
||||||
|
MessengerInterface $messenger, |
||||||
|
DateFormatterInterface $date_formatter |
||||||
|
) { |
||||||
|
$this->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 <a target="_blank" href=":uri">bulk created entity of type %type, with ID %id</a>.', |
||||||
|
'@count validation errors present in <a target="_blank" href=":uri">bulk created entity of type %type, with ID %id</a>.', |
||||||
|
[ |
||||||
|
'%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.')); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\Batch\BatchBuilder; |
||||||
|
use Drupal\Core\Field\BaseFieldDefinition; |
||||||
|
use Drupal\Core\Field\FieldDefinitionInterface; |
||||||
|
use Drupal\Core\Field\FieldItemList; |
||||||
|
use Drupal\Core\Field\FieldStorageDefinitionInterface; |
||||||
|
use Drupal\Core\Field\WidgetInterface; |
||||||
|
use Drupal\Core\Form\FormBase; |
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
use Drupal\Core\Session\AccountProxyInterface; |
||||||
|
use Drupal\field\FieldStorageConfigInterface; |
||||||
|
use Drupal\media\MediaTypeInterface; |
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Children addition wizard's second step. |
||||||
|
*/ |
||||||
|
abstract class AbstractFileSelectionForm extends FormBase { |
||||||
|
|
||||||
|
use WizardTrait; |
||||||
|
|
||||||
|
const BATCH_PROCESSOR = 'abstract.abstract'; |
||||||
|
|
||||||
|
/** |
||||||
|
* The current user. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Session\AccountProxyInterface|null |
||||||
|
*/ |
||||||
|
protected ?AccountProxyInterface $currentUser; |
||||||
|
|
||||||
|
/** |
||||||
|
* The batch processor service. |
||||||
|
* |
||||||
|
* @var \Drupal\islandora\Form\AddChildrenWizard\AbstractBatchProcessor|null |
||||||
|
*/ |
||||||
|
protected ?AbstractBatchProcessor $batchProcessor; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public static function create(ContainerInterface $container): self { |
||||||
|
$instance = parent::create($container); |
||||||
|
|
||||||
|
$instance->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->batchProcessor, '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->batchProcessor, 'batchOperation'], |
||||||
|
[$delta, $info, $cached_values] |
||||||
|
); |
||||||
|
} |
||||||
|
batch_set($builder->toArray()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,125 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\DependencyInjection\ClassResolverInterface; |
||||||
|
use Drupal\Core\Form\FormBuilderInterface; |
||||||
|
use Drupal\Core\Render\RendererInterface; |
||||||
|
use Drupal\Core\Routing\RouteMatchInterface; |
||||||
|
use Drupal\Core\Session\AccountProxyInterface; |
||||||
|
use Drupal\Core\TempStore\SharedTempStoreFactory; |
||||||
|
use Drupal\ctools\Wizard\FormWizardBase; |
||||||
|
use Drupal\islandora\IslandoraUtils; |
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Bulk children addition wizard base form. |
||||||
|
*/ |
||||||
|
abstract class AbstractForm extends FormWizardBase { |
||||||
|
|
||||||
|
const TEMPSTORE_ID = 'abstract.abstract'; |
||||||
|
const TYPE_SELECTION_FORM = MediaTypeSelectionForm::class; |
||||||
|
const FILE_SELECTION_FORM = AbstractFileSelectionForm::class; |
||||||
|
|
||||||
|
/** |
||||||
|
* The Islandora Utils service. |
||||||
|
* |
||||||
|
* @var \Drupal\islandora\IslandoraUtils |
||||||
|
*/ |
||||||
|
protected IslandoraUtils $utils; |
||||||
|
|
||||||
|
/** |
||||||
|
* The current node ID. |
||||||
|
* |
||||||
|
* @var mixed|null |
||||||
|
*/ |
||||||
|
protected $nodeId; |
||||||
|
|
||||||
|
/** |
||||||
|
* The current route match. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Routing\RouteMatchInterface |
||||||
|
*/ |
||||||
|
protected RouteMatchInterface $currentRoute; |
||||||
|
|
||||||
|
/** |
||||||
|
* The current user. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Session\AccountProxyInterface |
||||||
|
*/ |
||||||
|
protected AccountProxyInterface $currentUser; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
*/ |
||||||
|
public function __construct( |
||||||
|
SharedTempStoreFactory $tempstore, |
||||||
|
FormBuilderInterface $builder, |
||||||
|
ClassResolverInterface $class_resolver, |
||||||
|
EventDispatcherInterface $event_dispatcher, |
||||||
|
RouteMatchInterface $route_match, |
||||||
|
RendererInterface $renderer, |
||||||
|
$tempstore_id, |
||||||
|
AccountProxyInterface $current_user, |
||||||
|
$machine_name = NULL, |
||||||
|
$step = NULL |
||||||
|
) { |
||||||
|
parent::__construct($tempstore, $builder, $class_resolver, $event_dispatcher, $route_match, $renderer, $tempstore_id, |
||||||
|
$machine_name, $step); |
||||||
|
|
||||||
|
$this->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]; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\Access\AccessResult; |
||||||
|
use Drupal\Core\Access\AccessResultInterface; |
||||||
|
use Drupal\Core\DependencyInjection\ContainerInjectionInterface; |
||||||
|
use Drupal\Core\Routing\RouteMatch; |
||||||
|
use Drupal\islandora\IslandoraUtils; |
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Access checker. |
||||||
|
* |
||||||
|
* The _wizard/_form route enhancers do not really allow for access checking |
||||||
|
* things, so let's roll it separately for now. |
||||||
|
*/ |
||||||
|
class Access implements ContainerInjectionInterface { |
||||||
|
|
||||||
|
/** |
||||||
|
* The Islandora utils service. |
||||||
|
* |
||||||
|
* @var \Drupal\islandora\IslandoraUtils |
||||||
|
*/ |
||||||
|
protected IslandoraUtils $utils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
*/ |
||||||
|
public function __construct(IslandoraUtils $utils) { |
||||||
|
$this->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')); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\islandora\IslandoraUtils; |
||||||
|
use Drupal\node\NodeInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Children addition batch processor. |
||||||
|
*/ |
||||||
|
class ChildBatchProcessor extends AbstractBatchProcessor { |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
protected function getNode($info, array $values) : NodeInterface { |
||||||
|
$taxonomy_term_storage = $this->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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
use Drupal\Core\Url; |
||||||
|
|
||||||
|
/** |
||||||
|
* Children addition wizard's second step. |
||||||
|
*/ |
||||||
|
class ChildFileSelectionForm extends AbstractFileSelectionForm { |
||||||
|
|
||||||
|
public const BATCH_PROCESSOR = 'islandora.upload_children.batch_processor'; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getFormId() { |
||||||
|
return 'islandora_add_children_wizard_file_selection'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function submitForm(array &$form, FormStateInterface $form_state) { |
||||||
|
parent::submitForm($form, $form_state); |
||||||
|
|
||||||
|
$cached_values = $form_state->getTemporaryValue('wizard'); |
||||||
|
$form_state->setRedirectUrl(Url::fromUri("internal:/node/{$cached_values['node']}/members")); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
/** |
||||||
|
* Bulk children addition wizard base form. |
||||||
|
*/ |
||||||
|
class ChildForm extends AbstractForm { |
||||||
|
|
||||||
|
const TEMPSTORE_ID = 'islandora.upload_children'; |
||||||
|
const TYPE_SELECTION_FORM = ChildTypeSelectionForm::class; |
||||||
|
const FILE_SELECTION_FORM = ChildFileSelectionForm::class; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getMachineName() { |
||||||
|
return strtr("islandora_add_children_wizard__{userid}__{nodeid}", [ |
||||||
|
'{userid}' => $this->currentUser->id(), |
||||||
|
'{nodeid}' => $this->nodeId, |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\Cache\CacheableMetadata; |
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
use Drupal\islandora\IslandoraUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Children addition wizard's first step. |
||||||
|
*/ |
||||||
|
class ChildTypeSelectionForm extends MediaTypeSelectionForm { |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getFormId() : string { |
||||||
|
return 'islandora_add_children_type_selection'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Memoization for ::getNodeBundleOptions(). |
||||||
|
* |
||||||
|
* @var array|null |
||||||
|
*/ |
||||||
|
protected ?array $nodeBundleOptions = NULL; |
||||||
|
|
||||||
|
/** |
||||||
|
* Indicate presence of model field on node bundles. |
||||||
|
* |
||||||
|
* Populated as a side effect of ::getNodeBundleOptions(). |
||||||
|
* |
||||||
|
* @var array|null |
||||||
|
*/ |
||||||
|
protected ?array $nodeBundleHasModelField = NULL; |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper; get the node bundle options available to the current user. |
||||||
|
* |
||||||
|
* @return array |
||||||
|
* An associative array mapping node bundle machine names to their human- |
||||||
|
* readable labels. |
||||||
|
*/ |
||||||
|
protected function getNodeBundleOptions() : array { |
||||||
|
if ($this->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', |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\Entity\EntityFieldManagerInterface; |
||||||
|
use Drupal\Core\Field\FieldDefinitionInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Field lookup helper trait. |
||||||
|
*/ |
||||||
|
trait FieldTrait { |
||||||
|
|
||||||
|
use MediaTypeTrait; |
||||||
|
|
||||||
|
/** |
||||||
|
* The entity field manager service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Entity\EntityFieldManagerInterface|null |
||||||
|
*/ |
||||||
|
protected ?EntityFieldManagerInterface $entityFieldManager = NULL; |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper; get field instance, given our required values. |
||||||
|
* |
||||||
|
* @param array $values |
||||||
|
* See ::getMediaType() for which values are required. |
||||||
|
* |
||||||
|
* @return \Drupal\Core\Field\FieldDefinitionInterface |
||||||
|
* The target field. |
||||||
|
* |
||||||
|
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException |
||||||
|
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException |
||||||
|
*/ |
||||||
|
protected function getField(array $values): FieldDefinitionInterface { |
||||||
|
$media_type = $this->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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\node\NodeInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Media addition batch processor. |
||||||
|
*/ |
||||||
|
class MediaBatchProcessor extends AbstractBatchProcessor { |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
protected function getNode($info, array $values) : NodeInterface { |
||||||
|
return $this->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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
use Drupal\Core\Url; |
||||||
|
|
||||||
|
/** |
||||||
|
* Media addition wizard's second step. |
||||||
|
*/ |
||||||
|
class MediaFileSelectionForm extends AbstractFileSelectionForm { |
||||||
|
|
||||||
|
public const BATCH_PROCESSOR = 'islandora.upload_media.batch_processor'; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getFormId() { |
||||||
|
return 'islandora_add_media_wizard_file_selection'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function submitForm(array &$form, FormStateInterface $form_state) { |
||||||
|
parent::submitForm($form, $form_state); |
||||||
|
|
||||||
|
$cached_values = $form_state->getTemporaryValue('wizard'); |
||||||
|
$form_state->setRedirectUrl(Url::fromUri("internal:/node/{$cached_values['node']}/media")); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
/** |
||||||
|
* Bulk children addition wizard base form. |
||||||
|
*/ |
||||||
|
class MediaForm extends AbstractForm { |
||||||
|
|
||||||
|
const TEMPSTORE_ID = 'islandora.upload_media'; |
||||||
|
const TYPE_SELECTION_FORM = MediaTypeSelectionForm::class; |
||||||
|
const FILE_SELECTION_FORM = MediaFileSelectionForm::class; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getMachineName() { |
||||||
|
return strtr("islandora_add_media_wizard__{userid}__{nodeid}", [ |
||||||
|
'{userid}' => $this->currentUser->id(), |
||||||
|
'{nodeid}' => $this->nodeId, |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,227 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\Cache\CacheableMetadata; |
||||||
|
use Drupal\Core\Entity\EntityFieldManagerInterface; |
||||||
|
use Drupal\Core\Entity\EntityTypeBundleInfoInterface; |
||||||
|
use Drupal\Core\Entity\EntityTypeManagerInterface; |
||||||
|
use Drupal\Core\Form\FormBase; |
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
use Drupal\islandora\IslandoraUtils; |
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Children addition wizard's first step. |
||||||
|
*/ |
||||||
|
class MediaTypeSelectionForm extends FormBase { |
||||||
|
|
||||||
|
/** |
||||||
|
* Cacheable metadata that is instantiated and used internally. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Cache\CacheableMetadata|null |
||||||
|
*/ |
||||||
|
protected ?CacheableMetadata $cacheableMetadata = NULL; |
||||||
|
|
||||||
|
/** |
||||||
|
* The entity type bundle info service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface|null |
||||||
|
*/ |
||||||
|
protected ?EntityTypeBundleInfoInterface $entityTypeBundleInfo; |
||||||
|
|
||||||
|
/** |
||||||
|
* The entity type manager service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|null |
||||||
|
*/ |
||||||
|
protected ?EntityTypeManagerInterface $entityTypeManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* The entity field manager service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Entity\EntityFieldManagerInterface|null |
||||||
|
*/ |
||||||
|
protected ?EntityFieldManagerInterface $entityFieldManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* The Islandora Utils service. |
||||||
|
* |
||||||
|
* @var \Drupal\islandora\IslandoraUtils|null |
||||||
|
*/ |
||||||
|
protected ?IslandoraUtils $utils; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public static function create(ContainerInterface $container) : self { |
||||||
|
$instance = parent::create($container); |
||||||
|
|
||||||
|
$instance->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 <a target="_blank" href=":url">Portland Common Data Model: Use Extension</a>. "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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Core\Entity\EntityTypeManagerInterface; |
||||||
|
use Drupal\media\MediaTypeInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Media type lookup helper trait. |
||||||
|
*/ |
||||||
|
trait MediaTypeTrait { |
||||||
|
|
||||||
|
/** |
||||||
|
* The entity type manager service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|null |
||||||
|
*/ |
||||||
|
protected ?EntityTypeManagerInterface $entityTypeManager = NULL; |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper; get media type, given our required values. |
||||||
|
* |
||||||
|
* @param array $values |
||||||
|
* An associative array which must contain at least: |
||||||
|
* - media_type: The machine name of the media type to load. |
||||||
|
* |
||||||
|
* @return \Drupal\media\MediaTypeInterface |
||||||
|
* The loaded media type. |
||||||
|
* |
||||||
|
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException |
||||||
|
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException |
||||||
|
*/ |
||||||
|
protected function getMediaType(array $values): MediaTypeInterface { |
||||||
|
return $this->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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\islandora\Form\AddChildrenWizard; |
||||||
|
|
||||||
|
use Drupal\Component\Plugin\PluginManagerInterface; |
||||||
|
use Drupal\Core\Field\FieldDefinitionInterface; |
||||||
|
use Drupal\Core\Field\WidgetInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Wizard/widget lookup helper trait. |
||||||
|
*/ |
||||||
|
trait WizardTrait { |
||||||
|
|
||||||
|
use FieldTrait; |
||||||
|
|
||||||
|
/** |
||||||
|
* The widget plugin manager service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Field\WidgetPluginManager |
||||||
|
*/ |
||||||
|
protected PluginManagerInterface $widgetPluginManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper; get the base widget for the given field. |
||||||
|
* |
||||||
|
* @param \Drupal\Core\Field\FieldDefinitionInterface $field |
||||||
|
* The field for which get obtain the widget. |
||||||
|
* |
||||||
|
* @return \Drupal\Core\Field\WidgetInterface |
||||||
|
* The widget. |
||||||
|
*/ |
||||||
|
protected function getWidget(FieldDefinitionInterface $field): WidgetInterface { |
||||||
|
return $this->widgetPluginManager->getInstance([ |
||||||
|
'field_definition' => $field, |
||||||
|
'form_mode' => 'default', |
||||||
|
'prepare' => TRUE, |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue