diff --git a/modules/islandora_breadcrumbs/config/install/islandora_breadcrumbs.breadcrumbs.yml b/modules/islandora_breadcrumbs/config/install/islandora_breadcrumbs.breadcrumbs.yml index ad166b50b..ea34ee2ed 100644 --- a/modules/islandora_breadcrumbs/config/install/islandora_breadcrumbs.breadcrumbs.yml +++ b/modules/islandora_breadcrumbs/config/install/islandora_breadcrumbs.breadcrumbs.yml @@ -1,6 +1,7 @@ maxDepth: -1 includeSelf: FALSE -referenceField: field_member_of +referenceFields: + - field_member_of dependencies: module: - islandora diff --git a/modules/islandora_breadcrumbs/config/schema/islandora_breadcrumbs.schema.yml b/modules/islandora_breadcrumbs/config/schema/islandora_breadcrumbs.schema.yml index 6bc44096d..4175cbf51 100644 --- a/modules/islandora_breadcrumbs/config/schema/islandora_breadcrumbs.schema.yml +++ b/modules/islandora_breadcrumbs/config/schema/islandora_breadcrumbs.schema.yml @@ -7,6 +7,9 @@ islandora_breadcrumbs.breadcrumbs: includeSelf: type: boolean label: 'Include Self' - referenceField: - type: string - label: 'Reference Field' + referenceFields: + type: sequence + label: 'Reference Fields' + sequence: + type: string + label: 'Reference Field' diff --git a/modules/islandora_breadcrumbs/islandora_breadcrumbs.install b/modules/islandora_breadcrumbs/islandora_breadcrumbs.install new file mode 100644 index 000000000..2ca9ada68 --- /dev/null +++ b/modules/islandora_breadcrumbs/islandora_breadcrumbs.install @@ -0,0 +1,18 @@ +getEditable('islandora_breadcrumbs.breadcrumbs'); + $config->set('referenceFields', [$config->get('referenceField')]); + $config->clear('referenceField'); + $config->save(); + return "Updated referenceFields config."; +} diff --git a/modules/islandora_breadcrumbs/islandora_breadcrumbs.links.menu.yml b/modules/islandora_breadcrumbs/islandora_breadcrumbs.links.menu.yml new file mode 100644 index 000000000..dcf995346 --- /dev/null +++ b/modules/islandora_breadcrumbs/islandora_breadcrumbs.links.menu.yml @@ -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' diff --git a/modules/islandora_breadcrumbs/islandora_breadcrumbs.routing.yml b/modules/islandora_breadcrumbs/islandora_breadcrumbs.routing.yml new file mode 100644 index 000000000..e9fd9ac1b --- /dev/null +++ b/modules/islandora_breadcrumbs/islandora_breadcrumbs.routing.yml @@ -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' diff --git a/modules/islandora_breadcrumbs/src/Form/IslandoraBreadcrumbsSettingsForm.php b/modules/islandora_breadcrumbs/src/Form/IslandoraBreadcrumbsSettingsForm.php new file mode 100644 index 000000000..679a565e6 --- /dev/null +++ b/modules/islandora_breadcrumbs/src/Form/IslandoraBreadcrumbsSettingsForm.php @@ -0,0 +1,132 @@ +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.
One per line.
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); + } + +} diff --git a/modules/islandora_breadcrumbs/src/IslandoraBreadcrumbBuilder.php b/modules/islandora_breadcrumbs/src/IslandoraBreadcrumbBuilder.php index 620f22895..5ed7f6d06 100644 --- a/modules/islandora_breadcrumbs/src/IslandoraBreadcrumbBuilder.php +++ b/modules/islandora_breadcrumbs/src/IslandoraBreadcrumbBuilder.php @@ -64,7 +64,14 @@ public function applies(RouteMatchInterface $attributes) { $nid = $attributes->getRawParameters()->get('node'); if (!empty($nid)) { $node = $this->nodeStorage->load($nid); - return (!empty($node) && $node->hasField($this->config->get('referenceField'))); + if (empty($node)) { + return FALSE; + } + foreach ($this->config->get('referenceFields') as $field) { + if ($node->hasField($field)) { + return TRUE; + } + } } } @@ -76,9 +83,10 @@ public function build(RouteMatchInterface $route_match) { $nid = $route_match->getRawParameters()->get('node'); $node = $this->nodeStorage->load($nid); $breadcrumb = new Breadcrumb(); + $breadcrumb->addCacheableDependency($this->config); $breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '')); - $chain = array_reverse($this->utils->findAncestors($node, [$this->config->get('referenceField')], $this->config->get('maxDepth'))); + $chain = array_reverse($this->utils->findAncestors($node, $this->config->get('referenceFields'), $this->config->get('maxDepth'))); // XXX: Handle a looping breadcrumb scenario by filtering the present // node out and then optionally re-adding it after if set to do so. @@ -86,7 +94,7 @@ public function build(RouteMatchInterface $route_match) { return $link !== $nid; }); if ($this->config->get('includeSelf')) { - array_push($chain, $node); + array_push($chain, $nid); } $breadcrumb->addCacheableDependency($node); diff --git a/src/IslandoraUtils.php b/src/IslandoraUtils.php index e5071a05b..f81cb7472 100644 --- a/src/IslandoraUtils.php +++ b/src/IslandoraUtils.php @@ -714,7 +714,7 @@ public function findAncestors(ContentEntityInterface $entity, array $fields = [s * @param int $current_height * The current height of the recursion. */ - protected function findAncestorsByEntityReference(ContentEntityInterface $entity, array &$context, array $fields = [self::MEMBER_OF_FIELD], int $current_height = 0): void { + protected function findAncestorsByEntityReference(ContentEntityInterface $entity, array &$context, array $fields = [self::MEMBER_OF_FIELD], int $current_height = 1): void { $parents = $this->getParentsByEntityReference($entity, $fields); foreach ($parents as $parent) { if (isset($context['ancestors'][$parent->id()])) {