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()])) {