Skip to content

Commit

Permalink
Merge pull request #873 from seth-shaw-unlv/multiple-parents
Browse files Browse the repository at this point in the history
Multiple breadcrumb parent fields
  • Loading branch information
jordandukart authored May 18, 2022
2 parents d8d101e + e1428bb commit 1a13b3e
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
maxDepth: -1
includeSelf: FALSE
referenceField: field_member_of
referenceFields:
- field_member_of
dependencies:
module:
- islandora
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
18 changes: 18 additions & 0 deletions modules/islandora_breadcrumbs/islandora_breadcrumbs.install
Original file line number Diff line number Diff line change
@@ -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.";
}
Original file line number Diff line number Diff line change
@@ -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'
Original file line number Diff line number Diff line change
@@ -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'
Original file line number Diff line number Diff line change
@@ -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);
}

}
14 changes: 11 additions & 3 deletions modules/islandora_breadcrumbs/src/IslandoraBreadcrumbBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
}

Expand All @@ -76,17 +83,18 @@ 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'), '<front>'));

$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.
$chain = array_filter($chain, function ($link) use ($nid) {
return $link !== $nid;
});
if ($this->config->get('includeSelf')) {
array_push($chain, $node);
array_push($chain, $nid);
}
$breadcrumb->addCacheableDependency($node);

Expand Down
2 changes: 1 addition & 1 deletion src/IslandoraUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()])) {
Expand Down

0 comments on commit 1a13b3e

Please sign in to comment.