From ec6ff698afdb7d46b436b67b100c7d8221370831 Mon Sep 17 00:00:00 2001 From: Alexander O'Neill Date: Wed, 7 Nov 2018 14:36:18 -0400 Subject: [PATCH] Add support for Ibid. --- bibcite_footnotes.module | 132 ++++++++++-------- .../Filter/ReferenceFootnotesFilter.php | 104 ++++++++++---- ...l.twig => bibcite-footnote-link.html.twig} | 0 ...l.twig => bibcite-footnote-list.html.twig} | 0 4 files changed, 148 insertions(+), 88 deletions(-) rename templates/{footnote-link.html.twig => bibcite-footnote-link.html.twig} (100%) rename templates/{footnote-list.html.twig => bibcite-footnote-list.html.twig} (100%) diff --git a/bibcite_footnotes.module b/bibcite_footnotes.module index 39c617e..bdbd215 100644 --- a/bibcite_footnotes.module +++ b/bibcite_footnotes.module @@ -10,8 +10,31 @@ use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Drupal\Core\Link; 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(). */ 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. * @@ -37,29 +60,21 @@ function bibcite_footnotes_help($route_name, RouteMatchInterface $route_match) { * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ -function bibcite_footnotes_preprocess_footnote_list(&$variables) { - $config = \Drupal::config('filter.format.rich_text_references');//filters.filter_reference_footnotes.settings'); - $footnotes = $variables['footnotes']; +function bibcite_footnotes_preprocess_bibcite_footnote_list(&$variables) { + $config = $variables['config']; + $footnotes = $variables['notes']; $notes = [ '#theme' => 'item_list', '#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'], '#wrapper_attributes' => ['class' => 'container'], ]; - $references= [ - '#theme' => 'item_list', - '#list_type' => 'ul', - '#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'); + $notes['#attached']['library'][] = 'bibcite_footnotes/reference_footnote'; + $dont_show_backlink_text = $config['reference_dont_show_backlink_text']; + $sort_references_by = $config['reference_sort_by']; $citation_tools = new CitationTools(); foreach ($footnotes as $fn) { $item = [ @@ -97,53 +112,66 @@ function bibcite_footnotes_preprocess_footnote_list(&$variables) { $override_page_in_citation = FALSE; } - if (!empty($fn['text'])) { - // Handle the case where an endnote contains text and a reference. - // It will appear in both lists. But the link text for the endnote should - // always be the title of the note. So make a copy of the biuld arrray - // and change the link text. - $note_build = $build + ['#type' => 'markup', '#markup' => ' ' . $fn['text'] . '']; - $note_build['footnote-link'] = Link::fromTextAndUrl($fn['value'], $url)->toRenderable(); - if (!empty($reference_entity_id)) { - $citation_build = $citation_tools->getRenderableReference($reference_entity_id); - $note_build[] = $citation_build; - } - $note_item = [] + $item; - $note_item['#markup'] = render($note_build); - - $notes['#items'][] = $note_item; + if ($fn['ibid']) { + $build['#type'] = 'markup'; + $build['#markup'] = ' ' + . t('Ibid') . '.' + . (!empty($fn['page']) ? ', ' . $fn['page'] : '') + . ''; + } + else if (!empty($fn['text']) && $variables['note_type'] == ReferenceFootnotesFilter::ENDNOTE) { + $build['#type'] = 'markup'; + $build['#markup'] = ' ' . $fn['text'] . ''; + $build['footnote-link'] = Link::fromTextAndUrl($fn['value'], $url)->toRenderable(); } if (!empty($reference_entity_id)) { - $citation_build = $citation_tools->getRenderableReference($reference_entity_id); - if ($override_page_in_citation) { - $citation_build['#data']['page'] = $override_page_in_citation; + if (!$fn['ibid']) { + $citation_build = $citation_tools->getRenderableReference($reference_entity_id); + if ($override_page_in_citation) { + $citation_build['#data']['page'] = $override_page_in_citation; + } + + $build[] = $citation_build; } - $build[] = $citation_build; + $render = render($build); - $item['#markup'] = $render; $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') { - usort($references['#items'], '_bibcite_footnotes_reference_array_cmp'); + if ($sort_references_by == 'alphabetical' && $variables['note_type'] == ReferenceFootnotesFilter::REFERENCE) { + usort($notes['#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) { $a1 = (!empty($a['sort']) ? strtolower($a['sort']) : ''); $b1 = (!empty($b['sort']) ? strtolower($b['sort']) : ''); 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']['#markup'] = '

Hello!

'; $fn = $variables['fn']['fn']; @@ -169,17 +197,3 @@ function bibcite_footnotes_preprocess_footnote_link(&$variables) { $link = Link::fromTextAndUrl($fn['value'], $url)->toRenderable(); $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'; -} - diff --git a/src/Plugin/Filter/ReferenceFootnotesFilter.php b/src/Plugin/Filter/ReferenceFootnotesFilter.php index 3b24eed..032d386 100644 --- a/src/Plugin/Filter/ReferenceFootnotesFilter.php +++ b/src/Plugin/Filter/ReferenceFootnotesFilter.php @@ -7,7 +7,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\footnotes\Plugin\Filter\FootnotesFilter; /** - * Provides a base filter for Reference Footnotes filter. + * Reference Footnotes filter. * * @Filter( * id = "filter_reference_footnotes", @@ -17,7 +17,8 @@ use Drupal\footnotes\Plugin\Filter\FootnotesFilter; * type = \Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE, * cache = FALSE, * settings = { - * "footnotes_collapse" = FALSE, + * "footnotes_footnotefootnote_linkcollapse" = FALSE, + * "footnotes_ibid" = FALSE, * "notes_section_label" = "Notes", * "references_section_label" = "References" * }, @@ -25,6 +26,8 @@ use Drupal\footnotes\Plugin\Filter\FootnotesFilter; * ) */ class ReferenceFootnotesFilter extends FootnotesFilter { + const ENDNOTE = 0; + const REFERENCE = 1; /** * Object with configuration for reference footnotes. @@ -66,10 +69,15 @@ class ReferenceFootnotesFilter extends FootnotesFilter { public function settingsForm(array $form, FormStateInterface $form_state) { $settings['footnotes_collapse'] = [ '#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'], '#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'] = [ '#type' => 'textfield', '#title' => t('Notes section label'), @@ -133,21 +141,33 @@ class ReferenceFootnotesFilter extends FootnotesFilter { } if ($op == 'output footer') { + + 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 - // themed as a list Drupal 7 requires we use "render element" which - // 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 + // themed as a list $markup = [ - '#theme' => 'footnote_list', - '#footnotes' => $store_matches, + '#theme' => 'bibcite_footnote_list', + '#notes' => $notes, + '#note_type' => self::ENDNOTE, + '#config' => $this->settings, ]; $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. $n = 0; @@ -246,30 +266,18 @@ class ReferenceFootnotesFilter extends FootnotesFilter { // Drupal 7 requires we use "render element" which just introduces a wrapper // around the old array. $fn = [ - '#theme' => 'footnote_link', + '#theme' => 'bibcite_footnote_link', 'fn' => $fn, ]; + + $result = \Drupal::service('renderer')->render($fn, FALSE); return $result; } /** - * Search the $store_matches array for footnote text that matches. - * - * 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. + * @inheritdoc */ private function findFootnote($text, &$store_matches) { if (!empty($store_matches)) { @@ -304,4 +312,42 @@ class ReferenceFootnotesFilter extends FootnotesFilter { } 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; + } } diff --git a/templates/footnote-link.html.twig b/templates/bibcite-footnote-link.html.twig similarity index 100% rename from templates/footnote-link.html.twig rename to templates/bibcite-footnote-link.html.twig diff --git a/templates/footnote-list.html.twig b/templates/bibcite-footnote-list.html.twig similarity index 100% rename from templates/footnote-list.html.twig rename to templates/bibcite-footnote-list.html.twig