Browse Source

Add support for Ibid.

fix
Alexander O'Neill 6 years ago
parent
commit
ec6ff698af
  1. 132
      bibcite_footnotes.module
  2. 104
      src/Plugin/Filter/ReferenceFootnotesFilter.php
  3. 0
      templates/bibcite-footnote-link.html.twig
  4. 0
      templates/bibcite-footnote-list.html.twig

132
bibcite_footnotes.module

@ -10,8 +10,31 @@ use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\Core\Link; use Drupal\Core\Link;
use Drupal\bibcite_footnotes\CitationTools; use Drupal\bibcite_footnotes\CitationTools;
use Drupal\bibcite_footnotes\Plugin\Filter\ReferenceFootnotesFilter;
/**_bibcite_foot /**
* Implements hook_theme().
*/
function bibcite_footnotes_theme() {
return [
'bibcite_footnote_link' => [
'render element' => 'fn',
'template' => 'bibcite-footnote-link',
],
'bibcite_footnote_list' => [
'render element' => 'footnotes',
'path' => drupal_get_path('module', 'bibcite_footnotes') . '/templates',
'template' => 'bibcite-footnote-list',
'variables' => [
'notes' => [],
'note_type' => [],
'config' => [],
],
],
];
}
/**
* Implements hook_help(). * Implements hook_help().
*/ */
function bibcite_footnotes_help($route_name, RouteMatchInterface $route_match) { function bibcite_footnotes_help($route_name, RouteMatchInterface $route_match) {
@ -28,7 +51,7 @@ function bibcite_footnotes_help($route_name, RouteMatchInterface $route_match) {
} }
/** /**
* Implementation of hook_preprocess_footnote_list(). * Implementation of hook_preprocess_bibcite_footnote_list().
* *
* Gatehrs all notes and prints out Notes and References as separate lists. * Gatehrs all notes and prints out Notes and References as separate lists.
* *
@ -37,29 +60,21 @@ function bibcite_footnotes_help($route_name, RouteMatchInterface $route_match) {
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/ */
function bibcite_footnotes_preprocess_footnote_list(&$variables) { function bibcite_footnotes_preprocess_bibcite_footnote_list(&$variables) {
$config = \Drupal::config('filter.format.rich_text_references');//filters.filter_reference_footnotes.settings'); $config = $variables['config'];
$footnotes = $variables['footnotes']; $footnotes = $variables['notes'];
$notes = [ $notes = [
'#theme' => 'item_list', '#theme' => 'item_list',
'#list_type' => 'ul', '#list_type' => 'ul',
'#title' => $config->get('filters.filter_reference_footnotes.settings.notes_section_label'), '#title' => $variables['note_type'] == ReferenceFootnotesFilter::ENDNOTE ? $config['notes_section_label'] : $config['references_section_label'],
'#attributes' => ['class' => 'footnotes'], '#attributes' => ['class' => 'footnotes'],
'#wrapper_attributes' => ['class' => 'container'], '#wrapper_attributes' => ['class' => 'container'],
]; ];
$references= [ $notes['#attached']['library'][] = 'bibcite_footnotes/reference_footnote';
'#theme' => 'item_list', $dont_show_backlink_text = $config['reference_dont_show_backlink_text'];
'#list_type' => 'ul', $sort_references_by = $config['reference_sort_by'];
'#title' => $config->get('filters.filter_reference_footnotes.settings.references_section_label'),
'#attributes' => ['class' => 'footnotes'],
'#wrapper_attributes' => ['class' => 'container'],
];
$references['#attached']['library'][] = 'bibcite_footnotes/reference_footnote';
$dont_show_backlink_text = $config->get('filters.filter_reference_footnotes.settings.reference_dont_show_backlink_text');
$sort_references_by = $config->get('filters.filter_reference_footnotes.settings.reference_sort_by');
$citation_tools = new CitationTools(); $citation_tools = new CitationTools();
foreach ($footnotes as $fn) { foreach ($footnotes as $fn) {
$item = [ $item = [
@ -97,53 +112,66 @@ function bibcite_footnotes_preprocess_footnote_list(&$variables) {
$override_page_in_citation = FALSE; $override_page_in_citation = FALSE;
} }
if (!empty($fn['text'])) { if ($fn['ibid']) {
// Handle the case where an endnote contains text and a reference. $build['#type'] = 'markup';
// It will appear in both lists. But the link text for the endnote should $build['#markup'] = ' <span class="endnote-text"><span class="ibid">'
// always be the title of the note. So make a copy of the biuld arrray . t('Ibid') . '.</span>'
// and change the link text. . (!empty($fn['page']) ? ', ' . $fn['page'] : '')
$note_build = $build + ['#type' => 'markup', '#markup' => ' <span class="endnote-text">' . $fn['text'] . '</span>']; . '</span>';
$note_build['footnote-link'] = Link::fromTextAndUrl($fn['value'], $url)->toRenderable(); }
if (!empty($reference_entity_id)) { else if (!empty($fn['text']) && $variables['note_type'] == ReferenceFootnotesFilter::ENDNOTE) {
$citation_build = $citation_tools->getRenderableReference($reference_entity_id); $build['#type'] = 'markup';
$note_build[] = $citation_build; $build['#markup'] = ' <span class="endnote-text">' . $fn['text'] . '</span>';
} $build['footnote-link'] = Link::fromTextAndUrl($fn['value'], $url)->toRenderable();
$note_item = [] + $item;
$note_item['#markup'] = render($note_build);
$notes['#items'][] = $note_item;
} }
if (!empty($reference_entity_id)) { if (!empty($reference_entity_id)) {
$citation_build = $citation_tools->getRenderableReference($reference_entity_id); if (!$fn['ibid']) {
if ($override_page_in_citation) { $citation_build = $citation_tools->getRenderableReference($reference_entity_id);
$citation_build['#data']['page'] = $override_page_in_citation; if ($override_page_in_citation) {
$citation_build['#data']['page'] = $override_page_in_citation;
}
$build[] = $citation_build;
} }
$build[] = $citation_build;
$render = render($build); $render = render($build);
$item['#markup'] = $render;
$item['sort'] = trim(strip_tags(render($citation_build))); $item['sort'] = trim(strip_tags(render($citation_build)));
$references['#items'][] = $item;
} }
$item['#markup'] = $render;
$notes['#items'][] = $item;
$reference_entity_id = FALSE;
$render = FALSE;
} }
$variables['notes'] = $notes; if ($sort_references_by == 'alphabetical' && $variables['note_type'] == ReferenceFootnotesFilter::REFERENCE) {
usort($notes['#items'], '_bibcite_footnotes_reference_array_cmp');
if ($sort_references_by == 'alphabetical') {
usort($references['#items'], '_bibcite_footnotes_reference_array_cmp');
} }
$variables['references'] = $references; $variables['notes'] = $notes;
} }
/**
* Comparator function for sorting lists of references.
*
* @param $a First item to compare
* @param $b Second item to compare
* @return int Result of the comparison.
*/
function _bibcite_footnotes_reference_array_cmp($a, $b) { function _bibcite_footnotes_reference_array_cmp($a, $b) {
$a1 = (!empty($a['sort']) ? strtolower($a['sort']) : ''); $a1 = (!empty($a['sort']) ? strtolower($a['sort']) : '');
$b1 = (!empty($b['sort']) ? strtolower($b['sort']) : ''); $b1 = (!empty($b['sort']) ? strtolower($b['sort']) : '');
return strcmp($a1, $b1); return strcmp($a1, $b1);
} }
function bibcite_footnotes_preprocess_footnote_link(&$variables) { /**
* Implementation of hook_preprocess_bibcite_footnote_link().
*
* Construct a link inside a block of text so that it points to the reference list.
*
* @param $variables
*/
function bibcite_footnotes_preprocess_bibcite_footnote_link(&$variables) {
// $variables['fn']['fn']['#type'] = 'markeup'; // $variables['fn']['fn']['#type'] = 'markeup';
// $variables['fn']['fn']['#markup'] = '<h2>Hello!</h2>'; // $variables['fn']['fn']['#markup'] = '<h2>Hello!</h2>';
$fn = $variables['fn']['fn']; $fn = $variables['fn']['fn'];
@ -169,17 +197,3 @@ function bibcite_footnotes_preprocess_footnote_link(&$variables) {
$link = Link::fromTextAndUrl($fn['value'], $url)->toRenderable(); $link = Link::fromTextAndUrl($fn['value'], $url)->toRenderable();
$variables['fn']['fn'][] = $link; $variables['fn']['fn'][] = $link;
} }
function bibcite_footnotes_theme_registry_alter(&$theme_registry) {
unset($theme_registry['footnote_list']['function']);
$theme_registry['footnote_list']['path'] = drupal_get_path('module', 'bibcite_footnotes') . '/templates';
$theme_registry['footnote_list']['template'] = 'footnote-list';
$theme_registry['footnote_list']['variables']['notes'] = [];
$theme_registry['footnote_list']['variables']['references'] = [];
$theme_registry['footnote_list']['variables']['footnotes'] = [];
unset($theme_registry['footnote_link']['function']);
$theme_registry['footnote_link']['path'] = drupal_get_path('module', 'bibcite_footnotes') . '/templates';
$theme_registry['footnote_link']['template'] = 'footnote-link';
}

104
src/Plugin/Filter/ReferenceFootnotesFilter.php

@ -7,7 +7,7 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\footnotes\Plugin\Filter\FootnotesFilter; use Drupal\footnotes\Plugin\Filter\FootnotesFilter;
/** /**
* Provides a base filter for Reference Footnotes filter. * Reference Footnotes filter.
* *
* @Filter( * @Filter(
* id = "filter_reference_footnotes", * id = "filter_reference_footnotes",
@ -17,7 +17,8 @@ use Drupal\footnotes\Plugin\Filter\FootnotesFilter;
* type = \Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE, * type = \Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
* cache = FALSE, * cache = FALSE,
* settings = { * settings = {
* "footnotes_collapse" = FALSE, * "footnotes_footnotefootnote_linkcollapse" = FALSE,
* "footnotes_ibid" = FALSE,
* "notes_section_label" = "Notes", * "notes_section_label" = "Notes",
* "references_section_label" = "References" * "references_section_label" = "References"
* }, * },
@ -25,6 +26,8 @@ use Drupal\footnotes\Plugin\Filter\FootnotesFilter;
* ) * )
*/ */
class ReferenceFootnotesFilter extends FootnotesFilter { class ReferenceFootnotesFilter extends FootnotesFilter {
const ENDNOTE = 0;
const REFERENCE = 1;
/** /**
* Object with configuration for reference footnotes. * Object with configuration for reference footnotes.
@ -66,10 +69,15 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
public function settingsForm(array $form, FormStateInterface $form_state) { public function settingsForm(array $form, FormStateInterface $form_state) {
$settings['footnotes_collapse'] = [ $settings['footnotes_collapse'] = [
'#type' => 'checkbox', '#type' => 'checkbox',
'#title' => t('Collapse reference footnotes with identical content'), '#title' => $this->t('Collapse reference footnotes with identical content'),
'#default_value' => $this->settings['footnotes_collapse'], '#default_value' => $this->settings['footnotes_collapse'],
'#description' => t('If two reference footnotes have the exact same content, they will be collapsed into one as if using the same value="" attribute.'), '#description' => t('If two reference footnotes have the exact same content, they will be collapsed into one as if using the same value="" attribute.'),
]; ];
$settings['footnotes_ibid'] = [
'#type' => 'checkbox',
'#title' => $this->t('Display subsequent instances of multiple references with \'Ibid.\''),
'#default_value' => $this->settings['footnotes_ibid'],
];
$settings['notes_section_label'] = [ $settings['notes_section_label'] = [
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Notes section label'), '#title' => t('Notes section label'),
@ -133,21 +141,33 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
} }
if ($op == 'output footer') { if ($op == 'output footer') {
if (count($store_matches) > 0) { if (count($store_matches) > 0) {
// Separate out endontes and reference notes.
$notes = $this->extractNotesByType(self::ENDNOTE, $store_matches);
$references = $this->extractNotesByType(self::REFERENCE, $store_matches);
if ($this->settings['footnotes_ibid']) {
$this->ibidemify($references);
}
// Only if there are stored fn matches, pass the array of fns to be // Only if there are stored fn matches, pass the array of fns to be
// themed as a list Drupal 7 requires we use "render element" which // themed as a list
// just introduces a wrapper around the old array.
// @FIXME
// theme() has been renamed to _theme() and should NEVER be called
// directly. Calling _theme() directly can alter the expected output and
// potentially introduce security issues
// (see https://www.drupal.org/node/2195739). You should use renderable
// arrays instead. @see https://www.drupal.org/node/2195739
$markup = [ $markup = [
'#theme' => 'footnote_list', '#theme' => 'bibcite_footnote_list',
'#footnotes' => $store_matches, '#notes' => $notes,
'#note_type' => self::ENDNOTE,
'#config' => $this->settings,
]; ];
$str = \Drupal::service('renderer')->render($markup, FALSE); $str = \Drupal::service('renderer')->render($markup, FALSE);
$markup = [
'#theme' => 'bibcite_footnote_list',
'#notes' => $references,
'#note_type' => self::REFERENCE,
'#config' => $this->settings,
];
$str .= \Drupal::service('renderer')->render($markup, FALSE);
} }
// Reset the static variables so they can be used again next time. // Reset the static variables so they can be used again next time.
$n = 0; $n = 0;
@ -246,30 +266,18 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
// Drupal 7 requires we use "render element" which just introduces a wrapper // Drupal 7 requires we use "render element" which just introduces a wrapper
// around the old array. // around the old array.
$fn = [ $fn = [
'#theme' => 'footnote_link', '#theme' => 'bibcite_footnote_link',
'fn' => $fn, 'fn' => $fn,
]; ];
$result = \Drupal::service('renderer')->render($fn, FALSE); $result = \Drupal::service('renderer')->render($fn, FALSE);
return $result; return $result;
} }
/** /**
* Search the $store_matches array for footnote text that matches. * @inheritdoc
*
* Note: This does a linear search on the $store_matches array. For a large
* list of footnotes it would be more efficient to maintain a separate array
* with the footnote content as key, in order to do a hash lookup at this
* stage. Since you typically only have a handful of footnotes, this simple
* search is assumed to be more efficient, but was not tested.
*
* @param string $text
* The footnote text.
* @param array $store_matches
* The matches array.
*
* @return string|false
* The value of the existing footnote, FALSE otherwise.
*/ */
private function findFootnote($text, &$store_matches) { private function findFootnote($text, &$store_matches) {
if (!empty($store_matches)) { if (!empty($store_matches)) {
@ -304,4 +312,42 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
} }
return $value; return $value;
} }
/**
* Determine references that are the same as one above it
* to be replaced with the string 'Ibid'.
*
* @param array $footnotes
*/
protected function ibidemify(&$footnotes) {
$prev_reference_id = FALSE;
foreach ($footnotes as $index => $fn) {
if ($prev_reference_id) {
if ($fn['reference'] == $prev_reference_id) {
$footnotes[$index]['ibid'] = TRUE;
continue;
}
}
$prev_reference_id = $fn['reference'];
}
}
protected function extractNotesByType($type, $footnotes) {
$notes = [];
foreach ($footnotes as $fn) {
switch ($type) {
case self::ENDNOTE:
if (!empty($fn['text'])) {
$notes[] = $fn;
}
break;
case self::REFERENCE:
if (!empty($fn['reference'])) {
$notes[] = $fn;
}
break;
}
}
return $notes;
}
} }

0
templates/footnote-link.html.twig → templates/bibcite-footnote-link.html.twig

0
templates/footnote-list.html.twig → templates/bibcite-footnote-list.html.twig

Loading…
Cancel
Save