Compare commits

...

21 Commits
csl ... 8.x-1.x

Author SHA1 Message Date
ppound 90bffe7d38 corrected info yaml file 7 months ago
Paul Pound addaacb747 changed package name to roblib in composer json 8 months ago
Paul Pound 235afbfa7a Drupal 10 compatibility 8 months ago
Alexander O'Neill 8e0aec1db4 Fix PHP notices. 3 years ago
Alexander O'Neill afdaad2ed0 Drupal 9 compatibility. 3 years ago
Alexander O'Neill 551c3ccd2f Properly check for collapsed notes option. 3 years ago
Alexander O'Neill 8ea9977217 Add help text for footnote value. 3 years ago
Alexander O'Neill 18fe38cf06 Remove dependency on entity_reference. 3 years ago
Alexander O'Neill 07f3f833ea Remove dependency on Footnotes module. 4 years ago
Alexander O'Neill c7029ddc94 Drupal 9 compatibility. 4 years ago
Alexander O'Neill 49e966ff7b Add exampe Article with Citations module. 6 years ago
Alexander O'Neill cb4bd4a37f Update README to reflect new functionality. 6 years ago
Alexander O'Neill 2e31f62636 WIP for bisect. 6 years ago
Alexander O'Neill 27b1ef8b3b Fix JS error on empty citations list. 6 years ago
Alexander O'Neill 1aa85ca327 Add HTML help to footnotes box. 6 years ago
Alexander O'Neill eb9813d52a Fix bug where HTML was being escaped when re-showing the dialog. 6 years ago
Alexander O'Neill 64f8005088 Support HTML in footnotes. 6 years ago
Alexander O'Neill 43080534a7 Sort 'Works Cited' list by author, title. 6 years ago
Alexander O'Neill 659edba4e1 Fix field name length 6 years ago
Alexander O'Neill 073e15df92 Add Works Cited view. 6 years ago
Alexander O'Neill d5fef241ba Stpo using separate notes sections. Works Cited can be rendered from teh field. 6 years ago
  1. 87
      README.md
  2. 7
      bibcite_footnotes.info.yml
  3. 89
      bibcite_footnotes.module
  4. 4
      bibcite_footnotes_article_with_citations/README.md
  5. 13
      bibcite_footnotes_article_with_citations/bibcite_footnotes_article_with_citations.info.yml
  6. 113
      bibcite_footnotes_article_with_citations/config/install/core.entity_form_display.node.bibcite_fn_article_references.default.yml
  7. 57
      bibcite_footnotes_article_with_citations/config/install/core.entity_view_display.node.bibcite_fn_article_references.default.yml
  8. 51
      bibcite_footnotes_article_with_citations/config/install/editor.editor.basic_html_with_references.yml
  9. 21
      bibcite_footnotes_article_with_citations/config/install/field.field.node.bibcite_fn_article_references.body.yml
  10. 98
      bibcite_footnotes_article_with_citations/config/install/field.field.node.bibcite_fn_article_references.field_bibcite_fn_works_cited.yml
  11. 37
      bibcite_footnotes_article_with_citations/config/install/field.field.node.bibcite_fn_article_references.field_image.yml
  12. 28
      bibcite_footnotes_article_with_citations/config/install/field.field.node.bibcite_fn_article_references.field_tags.yml
  13. 87
      bibcite_footnotes_article_with_citations/config/install/filter.format.basic_html_with_references.yml
  14. 17
      bibcite_footnotes_article_with_citations/config/install/node.type.bibcite_fn_article_references.yml
  15. 23
      composer.json
  16. 24
      config/optional/field.storage.node.field_bibcite_fn_works_cited.yml
  17. 85
      css/reference_footnote.css
  18. 21
      js/plugins/reference_footnotes/dialogs/footnotes.js
  19. 16
      js/plugins/reference_footnotes/plugin.js
  20. 10
      src/CitationTools.php
  21. 5
      src/Plugin/CKEditorPlugin/ReferenceFootnotes.php
  22. 136
      src/Plugin/Filter/ReferenceFootnotesFilter.php

87
README.md

@ -6,57 +6,88 @@ Contents
* Introduction * Introduction
* Installation * Installation
* Configuration * Use
* Current Maintainers * Current Maintainers
Introduction Introduction
------------ ------------
Provides a CKEditor plugin that lets a user select from a list of citations which Provides a CKEditor plugin that lets a user select from a list of citations which
appear in a formatted list at the bottom of the text area that contains endnotes appear in a formatted list at the bottom of the text that contains endnotes
and references. and references.
Installation Installation
------------ ------------
Install and set up the Footnotes module as per its installation instructions, including ### Works Cited field
enabling and configuring the Footnotes text filter. If the 'Allowed HTML tags' filter is enabled
you will need to add the following HTML tags for the reference footnotes to display properly.: 1. Go to Administration > Structure > Content Types and edit the type that will include Reference Footnotes
2. In the Manage Fields tab, press Add Field.
3. In "Re-use an existing field" select "Entity reference: field_bibcite_fn_works_cited".
4. Enter an appropriate label and press 'Save and continue'.
5. On the next page select all the items in "Reference type" that should be available to choose from as footnotes.
6. Customize the field under "Manage form display" and "Manage display", typically you'll want to
place the Works Cited field directly under the Body field. See below for configuring with
Inline Entity Form.
7. In "Manage display", select "Rendered entity" in the Format drop-down for the Works Cited field.
8. Press the Settings icon and under "View mode" choose "Citation" - a new view mode created
by this module.
#### Inline Enity Form
It's recommended to also install [Inline Entity Form][1] which allowes a user to
create new references directly in the node edit form.
1. In "Entity Form Display", select ''Inline Entity Form - complex".
2. Press the settings button in the rightmost column.
3. Enabling "Allow users to add new reference entities" and
"Allow users to add existing reference entities" is recommended.
[1]: https://www.drupal.org/project/inline_entity_form
### CKEditor filter and toolbar button
1. Enable the module.
2. Go to Administration > Configuration > Content authoring > Text formats and editors.
3. Edit the text format you want to add Reference Footnotes to
4. Enable the Reference Footnotes Filter
5. Drag the Reference Footnotes button into the active buttons toolbar.
6. Configure the options for the Reference Footnotes Filter.
If the 'Allowed HTML tags' filter is enabled add these HTML tags to the allowed list:
```html ```html
<a class href id> <div class id> <span class id> <ul class id type> <a class href id> <div class id> <span class id> <ol class id type>
<ol class id start type> <sup> <li class id> <sup> <li class id>
``` ```
Configuration Use
------------- ---
Add a field to a node type that is an Entity Reference field pointing to See the documentation for [Bibliography and Citation - Import][2] module for how to
Bibcite Reference entities. add citations exported from a citation management system like RefWorks or EndNote.
Go to Admin -> Configuration -> Content Authoring -> Text Formats and Editors. [2]: https://www.drupal.org/project/bibcite
Edit a Rich text format that will include reference footnotes. ### Adding a Works Cited item
Drag the Reference Footnotes button into the active set of buttons in the text format If Inline Entity Form is enabled, an author can create new citations or add references to
you want to use References in and save the settings. previously-imported or created references directly with the 'Add new reference' and
'Add existing reference' buttons.
You will then need to create or import one or more references using the Bibliography & With Inline Entity Form, newly-added citations are immediately selectable in the
Citations project. Those will then appear in a select list when you are editing a text area Reference Footnotes dialog.
and click on the Reference Footnote button.
Optionally you can install Inline Entity Form which allowes a user to If not using the inline entity form, you can select previously imported or created references
create new references directly in the node edit form.
Adding References to Content When you edit the node again, and you click on the Reference Footnote editor toolbar button,
---------------------------- a list of citations will be available to choose from.
If not using the inline entity form, select the references that appear in the ### Citation Formatting
list of referenced Bibcite reference types that should be available to the editor,
ten save the node.
When you edit the node again, and you click on the Reference Footnote button, The Works Cited field will use the Default style selected in the
a list of citaitons will be available to choose from. Bibliography & Citation administration pages at Administration > Configuration >
Bibliography & Citation > Settings in the Processing tab under Processor.'
Current Maintainers Current Maintainers
------------------- -------------------
@ -66,4 +97,4 @@ Current Maintainers
Sponsors Sponsors
-------- --------
* This work is supported by the [University of Prince Edward Island Robertson Librrary](https://library.upei.ca). * This work is supported by the [University of Prince Edward Island Robertson Library](https://library.upei.ca).

7
bibcite_footnotes.info.yml

@ -1,10 +1,11 @@
name: 'BibCite Footnotes' name: 'BibCite Footnotes'
type: module type: module
description: 'Inline footnote links for BibCite References' description: 'Inline footnote links for BibCite References'
core: 8.x core_version_requirement: ^8.0 || ^9 || ^10
package: 'Bibliography & Citation' package: 'Bibliography & Citation'
dependencies: dependencies:
- bibcite:bibcite - bibcite:bibcite
- bibcite:bibcite_entity - bibcite:bibcite_entity
- entity_reference - fakeobjects:fakeobjects
- footnotes:footnotes

89
bibcite_footnotes.module

@ -25,7 +25,7 @@ function bibcite_footnotes_theme() {
], ],
'bibcite_footnote_list' => [ 'bibcite_footnote_list' => [
'render element' => 'footnotes', 'render element' => 'footnotes',
'path' => drupal_get_path('module', 'bibcite_footnotes') . '/templates', 'path' => \Drupal::service('extension.list.module')->getPath('bibcite_footnotes') . '/templates',
'template' => 'bibcite-footnote-list', 'template' => 'bibcite-footnote-list',
'variables' => [ 'variables' => [
'notes' => [], 'notes' => [],
@ -69,14 +69,14 @@ function bibcite_footnotes_preprocess_bibcite_footnote_list(&$variables) {
$notes = [ $notes = [
'#theme' => 'item_list', '#theme' => 'item_list',
'#list_type' => 'ul', '#list_type' => 'ul',
'#title' => $variables['note_type'] == ReferenceFootnotesFilter::ENDNOTE ? $config['notes_section_label'] : $config['references_section_label'], '#title' => $variables['note_type'] == $config['notes_section_label'],
'#attributes' => ['class' => 'footnotes'], '#attributes' => ['class' => 'footnotes'],
'#wrapper_attributes' => ['class' => 'container'], '#wrapper_attributes' => ['class' => 'container'],
]; ];
$notes['#attached']['library'][] = 'bibcite_footnotes/reference_footnote'; $notes['#attached']['library'][] = 'bibcite_footnotes/reference_footnote';
$dont_show_backlink_text = $config['reference_dont_show_backlink_text']; $dont_show_backlink_text = $config['reference_dont_show_backlink_text'];
$sort_references_by = $config['reference_sort_by'];
$citation_tools = new CitationTools(); $citation_tools = new CitationTools();
if (empty($footnotes)) { if (empty($footnotes)) {
return; return;
@ -91,7 +91,7 @@ function bibcite_footnotes_preprocess_bibcite_footnote_list(&$variables) {
$build = []; $build = [];
$reference_entity_id = $fn['reference']; $reference_entity_id = $fn['reference'];
$footnote_link_text = ($dont_show_backlink_text && $reference_entity_id) ? '^' : $fn['value']; $footnote_link_text = ($dont_show_backlink_text) ? '^' : $fn['value'];
if (!is_array($fn['ref_id'])) { if (!is_array($fn['ref_id'])) {
$url = Url::fromUserInput('#' . $fn['ref_id'], ['attributes' => ['id' => $fn['fn_id'], 'class' => 'footnote-link']]); $url = Url::fromUserInput('#' . $fn['ref_id'], ['attributes' => ['id' => $fn['fn_id'], 'class' => 'footnote-link']]);
$link = Link::fromTextAndUrl(($footnote_link_text), $url)->toRenderable(); $link = Link::fromTextAndUrl(($footnote_link_text), $url)->toRenderable();
@ -126,8 +126,8 @@ function bibcite_footnotes_preprocess_bibcite_footnote_list(&$variables) {
. '</span>', . '</span>',
]; ];
} }
else if (!empty($fn['text']) && $variables['note_type'] == ReferenceFootnotesFilter::ENDNOTE) { else if (!empty($fn['text'])) {
$build['footnote-link'] = Link::fromTextAndUrl($fn['value'], $url)->toRenderable(); $build['footnote-link'] = Link::fromTextAndUrl($footnote_link_text, $url)->toRenderable();
$build['footnote-text'] = [ $build['footnote-text'] = [
'#type' => 'markup', '#type' => 'markup',
'#markup' => ' <span class="endnote-text">' . $fn['text'] . '</span>', '#markup' => ' <span class="endnote-text">' . $fn['text'] . '</span>',
@ -145,8 +145,9 @@ function bibcite_footnotes_preprocess_bibcite_footnote_list(&$variables) {
$build[] = $citation_build; $build[] = $citation_build;
} }
$render = render($build); //$render = render($build);
$item['sort'] = trim(strip_tags(render($citation_build))); $citation_build_render = \Drupal::service('renderer')->render($citation_build);
$item['sort'] = trim(strip_tags($citation_build_render));
} }
// $item['#markup'] = $render; // $item['#markup'] = $render;
@ -155,9 +156,6 @@ function bibcite_footnotes_preprocess_bibcite_footnote_list(&$variables) {
$render = FALSE; $render = FALSE;
} }
if ($sort_references_by == 'alphabetical' && $variables['note_type'] == ReferenceFootnotesFilter::REFERENCE) {
usort($notes['#items'], '_bibcite_footnotes_reference_array_cmp');
}
$variables['notes'] = $notes; $variables['notes'] = $notes;
} }
@ -194,21 +192,18 @@ function bibcite_footnotes_preprocess_bibcite_footnote_link(&$variables) {
$citation_data = $citation_tools->getRenderableReference($citation_entity_id); $citation_data = $citation_tools->getRenderableReference($citation_entity_id);
// Citation contains a page reference, so construct parenthetical footnote. // Citation contains a page reference, so construct parenthetical footnote.
if (!empty($fn['page'])) { // @TODO: Make it explicit.
if (!empty($fn['page'] && !is_numeric($fn['value']))) {
$fn['value'] = "({$fn['value']}, {$fn['page']})"; $fn['value'] = "({$fn['value']}, {$fn['page']})";
$citation_data['#data']['page'] = $fn['page']; $citation_data['#data']['page'] = $fn['page'];
$class .= '-inline'; $class .= '-inline';
} }
$citation_data_render = \Drupal::service('renderer')->render($citation_data);
$title = trim(strip_tags(render($citation_data))); $title = trim(strip_tags($citation_data_render));
$url = Url::fromUserInput('#' . $fn['fn_id'], ['attributes' => ['id' => $fn['ref_id'], 'class' => $class, 'title' => $title]]); $url = Url::fromUserInput('#' . $fn['fn_id'], ['attributes' => ['id' => $fn['ref_id'], 'class' => $class, 'title' => $title]]);
// We need to delete the cached markup to render it in a different mode.
unset($citation_data['#markup']);
unset($citation_data['#children']);
$citation_data['#data']['mode'] = 'citation'; $link = Link::fromTextAndUrl($fn['value'], $url)->toRenderable();
$link = Link::fromTextAndUrl(render($citation_data), $url)->toRenderable();
unset($variables['fn']['fn']); unset($variables['fn']['fn']);
$variables['fn']['fn'] = $link; $variables['fn']['fn'] = $link;
// $variables['fn']['fn'][] = $link; // $variables['fn']['fn'][] = $link;
@ -247,7 +242,7 @@ function bibcite_footnotes_form_node_form_alter(&$form, FormStateInterface &$for
/** /**
* @var FieldConfig $field_config * @var FieldConfig $field_config
*/ */
$field_config = $ief_instance['instance']; if ($field_config = $ief_instance['instance']) {
// Check if this is a bibcite_reference field type. // Check if this is a bibcite_reference field type.
if ($field_config->getSetting('handler') == 'default:bibcite_reference') { if ($field_config->getSetting('handler') == 'default:bibcite_reference') {
@ -267,6 +262,7 @@ function bibcite_footnotes_form_node_form_alter(&$form, FormStateInterface &$for
} }
} }
} }
}
$triggering_element = $form_state->getTriggeringElement(); $triggering_element = $form_state->getTriggeringElement();
if (!$triggering_element) { if (!$triggering_element) {
@ -312,8 +308,59 @@ function bibcite_footnotes_get_ckeditor_select_item($entity): array {
$data = $serializer->normalize($entity, 'csl'); $data = $serializer->normalize($entity, 'csl');
$build = ['#theme' => 'bibcite_citation', '#data' => $data]; $build = ['#theme' => 'bibcite_citation', '#data' => $data];
$citation_text = trim(strip_tags(render($build))); $build_render = \Drupal::service('renderer')->render($build);
$citation_text = trim(strip_tags($build_render));
// Attempt to match up pre-saved entities with the eventual saved ones. // Attempt to match up pre-saved entities with the eventual saved ones.
$citation_key = $entity->id->first()->getValue()['value']; $citation_key = $entity->id->first()->getValue()['value'];
return [$citation_text, $citation_key]; return [$citation_text, $citation_key];
} }
/**
* Implementation of hook_preprocess_field().
* Sort the works cited list by rendered text, which results in author(s) then title.
*
* @param $variables
* @param $hook
*/
function bibcite_footnotes_preprocess_field(&$variables, $hook) {
$citation_tools = new CitationTools();
if ($variables['field_name'] == 'field_bibcite_fn_works_cited') {
foreach($variables['element'] as $index => $element) {
if (is_numeric($index)) {
$renderable = $citation_tools->getRenderableReference($element['#bibcite_reference']);
$renderable = \Drupal::service('renderer')->render($renderable);
$items[] = [
'orig_index' => $index,
'orig_element' => $element,
'rendered' => trim(strip_tags($renderable)),
];
}
}
uasort($items, '_bibcite_footnotes_sort_rendered_references');
$i = 0;
foreach ($items as $item) {
$variables['element'][$i] = $item['orig_element'];
$variables['element']['#items'][$i] = $item['orig_element'];
$variables['items'][$i]['content'] = $item['orig_element'];
$i += 1;
}
}
}
/**
* Comparator function for theme field preprocess.
*
* @param $a
* The first renderable element.
* @param $b
* The second renderable element.
*
* @return int
*/
function _bibcite_footnotes_sort_rendered_references($a, $b) {
if ($a['rendered'] == $b['rendered']) {
return 0;
}
return strtolower($a['rendered']) < strtolower($b['rendered']) ? -1 : 1;
}

4
bibcite_footnotes_article_with_citations/README.md

@ -0,0 +1,4 @@
Description
-----------
Example content type and text format to demonstrate a fully-configured Reference Footnotes setup.

13
bibcite_footnotes_article_with_citations/bibcite_footnotes_article_with_citations.info.yml

@ -0,0 +1,13 @@
name: 'BibCite Footnotes Article with Citations'
type: module
description: 'Example module to show a fully-configured content type and text format with Reference Footnotes.'
core_version_requirement: ^8 || ^9 || ^10
package: 'Bibliography & Citation'
dependencies:
- bibcite_footnotees:bibcite_footnotes
- bibcite:bibcite_import
- bibcite:bibcite_bibtex
- bibcite:bibcite_endnote
- bibcite:bibcite_ris
- bibcite:bibcite_marc
- inline_entity_form:inline_entity_form

113
bibcite_footnotes_article_with_citations/config/install/core.entity_form_display.node.bibcite_fn_article_references.default.yml

@ -0,0 +1,113 @@
langcode: en
status: true
dependencies:
config:
- field.field.node.bibcite_fn_article_references.body
- field.field.node.bibcite_fn_article_references.field_bibcite_fn_works_cited
- field.field.node.bibcite_fn_article_references.field_image
- field.field.node.bibcite_fn_article_references.field_tags
- image.style.thumbnail
- node.type.bibcite_fn_article_references
module:
- image
- inline_entity_form
- path
- text
id: node.bibcite_fn_article_references.default
targetEntityType: node
bundle: bibcite_fn_article_references
mode: default
content:
body:
type: text_textarea_with_summary
weight: 7
settings:
rows: 9
summary_rows: 3
placeholder: ''
third_party_settings: { }
region: content
created:
type: datetime_timestamp
weight: 2
region: content
settings: { }
third_party_settings: { }
field_bibcite_fn_works_cited:
weight: 8
settings:
form_mode: default
label_singular: ''
label_plural: ''
allow_new: true
allow_existing: true
match_operator: CONTAINS
override_labels: false
collapsible: false
collapsed: false
allow_duplicate: false
third_party_settings: { }
type: inline_entity_form_complex
region: content
field_image:
weight: 10
settings:
progress_indicator: throbber
preview_image_style: thumbnail
third_party_settings: { }
type: image_image
region: content
field_tags:
weight: 9
settings:
match_operator: CONTAINS
size: 60
placeholder: ''
third_party_settings: { }
type: entity_reference_autocomplete
region: content
path:
type: path
weight: 5
region: content
settings: { }
third_party_settings: { }
promote:
type: boolean_checkbox
settings:
display_label: true
weight: 3
region: content
third_party_settings: { }
status:
type: boolean_checkbox
settings:
display_label: true
weight: 6
region: content
third_party_settings: { }
sticky:
type: boolean_checkbox
settings:
display_label: true
weight: 4
region: content
third_party_settings: { }
title:
type: string_textfield
weight: 0
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
uid:
type: entity_reference_autocomplete
weight: 1
settings:
match_operator: CONTAINS
size: 60
placeholder: ''
region: content
third_party_settings: { }
hidden: { }

57
bibcite_footnotes_article_with_citations/config/install/core.entity_view_display.node.bibcite_fn_article_references.default.yml

@ -0,0 +1,57 @@
langcode: en
status: true
dependencies:
config:
- field.field.node.bibcite_fn_article_references.body
- field.field.node.bibcite_fn_article_references.field_bibcite_fn_works_cited
- field.field.node.bibcite_fn_article_references.field_image
- field.field.node.bibcite_fn_article_references.field_tags
- node.type.bibcite_fn_article_references
module:
- image
- text
- user
id: node.bibcite_fn_article_references.default
targetEntityType: node
bundle: bibcite_fn_article_references
mode: default
content:
body:
label: hidden
type: text_default
weight: 1
settings: { }
third_party_settings: { }
region: content
field_bibcite_fn_works_cited:
weight: 2
label: above
settings:
view_mode: citation
link: false
third_party_settings: { }
type: entity_reference_entity_view
region: content
field_image:
weight: 4
label: above
settings:
image_style: ''
image_link: ''
third_party_settings: { }
type: image
region: content
field_tags:
weight: 3
label: above
settings:
link: true
third_party_settings: { }
type: entity_reference_label
region: content
links:
weight: 0
region: content
settings: { }
third_party_settings: { }
hidden: { }

51
bibcite_footnotes_article_with_citations/config/install/editor.editor.basic_html_with_references.yml

@ -0,0 +1,51 @@
langcode: en
status: true
dependencies:
config:
- filter.format.basic_html_with_references
module:
- ckeditor
format: basic_html_with_references
editor: ckeditor
settings:
toolbar:
rows:
-
-
name: Formatting
items:
- Bold
- Italic
-
name: Links
items:
- DrupalLink
- DrupalUnlink
-
name: Lists
items:
- BulletedList
- NumberedList
-
name: Media
items:
- Blockquote
- DrupalImage
-
name: Tools
items:
- Source
- reference_footnotes
plugins:
stylescombo:
styles: ''
language:
language_list: un
image_upload:
status: false
scheme: public
directory: inline-images
max_size: ''
max_dimensions:
width: null
height: null

21
bibcite_footnotes_article_with_citations/config/install/field.field.node.bibcite_fn_article_references.body.yml

@ -0,0 +1,21 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.body
- node.type.bibcite_fn_article_references
module:
- text
id: node.bibcite_fn_article_references.body
field_name: body
entity_type: node
bundle: bibcite_fn_article_references
label: Body
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
display_summary: true
field_type: text_with_summary

98
bibcite_footnotes_article_with_citations/config/install/field.field.node.bibcite_fn_article_references.field_bibcite_fn_works_cited.yml

@ -0,0 +1,98 @@
langcode: en
status: true
dependencies:
config:
- bibcite_entity.bibcite_reference_type.artwork
- bibcite_entity.bibcite_reference_type.audiovisual
- bibcite_entity.bibcite_reference_type.bill
- bibcite_entity.bibcite_reference_type.book
- bibcite_entity.bibcite_reference_type.book_chapter
- bibcite_entity.bibcite_reference_type.broadcast
- bibcite_entity.bibcite_reference_type.case
- bibcite_entity.bibcite_reference_type.chart
- bibcite_entity.bibcite_reference_type.classical
- bibcite_entity.bibcite_reference_type.conference_paper
- bibcite_entity.bibcite_reference_type.conference_proceedings
- bibcite_entity.bibcite_reference_type.database
- bibcite_entity.bibcite_reference_type.film
- bibcite_entity.bibcite_reference_type.government_report
- bibcite_entity.bibcite_reference_type.hearing
- bibcite_entity.bibcite_reference_type.journal
- bibcite_entity.bibcite_reference_type.journal_article
- bibcite_entity.bibcite_reference_type.legal_ruling
- bibcite_entity.bibcite_reference_type.magazine_article
- bibcite_entity.bibcite_reference_type.manuscript
- bibcite_entity.bibcite_reference_type.map
- bibcite_entity.bibcite_reference_type.miscellaneous
- bibcite_entity.bibcite_reference_type.miscellaneous_section
- bibcite_entity.bibcite_reference_type.newspaper_article
- bibcite_entity.bibcite_reference_type.patent
- bibcite_entity.bibcite_reference_type.personal
- bibcite_entity.bibcite_reference_type.presentation
- bibcite_entity.bibcite_reference_type.report
- bibcite_entity.bibcite_reference_type.software
- bibcite_entity.bibcite_reference_type.statute
- bibcite_entity.bibcite_reference_type.thesis
- bibcite_entity.bibcite_reference_type.unpublished
- bibcite_entity.bibcite_reference_type.web_article
- bibcite_entity.bibcite_reference_type.web_project_page
- bibcite_entity.bibcite_reference_type.web_service
- bibcite_entity.bibcite_reference_type.website
- field.storage.node.field_bibcite_fn_works_cited
- node.type.bibcite_fn_article_references
id: node.bibcite_fn_article_references.field_bibcite_fn_works_cited
field_name: field_bibcite_fn_works_cited
entity_type: node
bundle: bibcite_fn_article_references
label: 'Works Cited'
description: 'List of works mentioned in this article.'
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
handler: 'default:bibcite_reference'
handler_settings:
target_bundles:
artwork: artwork
audiovisual: audiovisual
bill: bill
book: book
book_chapter: book_chapter
broadcast: broadcast
case: case
chart: chart
classical: classical
conference_paper: conference_paper
conference_proceedings: conference_proceedings
database: database
film: film
government_report: government_report
hearing: hearing
journal: journal
journal_article: journal_article
legal_ruling: legal_ruling
magazine_article: magazine_article
manuscript: manuscript
map: map
miscellaneous: miscellaneous
miscellaneous_section: miscellaneous_section
newspaper_article: newspaper_article
patent: patent
personal: personal
presentation: presentation
report: report
software: software
statute: statute
thesis: thesis
unpublished: unpublished
web_article: web_article
web_project_page: web_project_page
web_service: web_service
website: website
sort:
field: title
direction: ASC
auto_create: false
auto_create_bundle: artwork
field_type: entity_reference

37
bibcite_footnotes_article_with_citations/config/install/field.field.node.bibcite_fn_article_references.field_image.yml

@ -0,0 +1,37 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_image
- node.type.bibcite_fn_article_references
module:
- image
id: node.bibcite_fn_article_references.field_image
field_name: field_image
entity_type: node
bundle: bibcite_fn_article_references
label: Image
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
file_directory: '[date:custom:Y]-[date:custom:m]'
file_extensions: 'png gif jpg jpeg'
max_filesize: ''
max_resolution: ''
min_resolution: ''
alt_field: true
alt_field_required: true
title_field: false
title_field_required: false
default_image:
uuid: ''
alt: ''
title: ''
width: null
height: null
handler: 'default:file'
handler_settings: { }
field_type: image

28
bibcite_footnotes_article_with_citations/config/install/field.field.node.bibcite_fn_article_references.field_tags.yml

@ -0,0 +1,28 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_tags
- node.type.bibcite_fn_article_references
- taxonomy.vocabulary.tags
id: node.bibcite_fn_article_references.field_tags
field_name: field_tags
entity_type: node
bundle: bibcite_fn_article_references
label: Tags
description: 'Enter a comma-separated list. For example: Amsterdam, Mexico City, "Cleveland, Ohio"'
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
handler: 'default:taxonomy_term'
handler_settings:
target_bundles:
tags: tags
sort:
field: name
direction: asc
auto_create: true
auto_create_bundle: ''
field_type: entity_reference

87
bibcite_footnotes_article_with_citations/config/install/filter.format.basic_html_with_references.yml

@ -0,0 +1,87 @@
langcode: en
status: true
dependencies:
module:
- bibcite_footnotes
- editor
- footnotes
name: 'Basic HTML with References'
format: basic_html_with_references
weight: 0
filters:
filter_reference_footnotes:
id: filter_reference_footnotes
provider: bibcite_footnotes
status: true
weight: -50
settings:
footnotes_collapse: '1'
footnotes_ibid: '0'
notes_section_label: Notes
reference_dont_show_backlink_text: '0'
works_cited_sort_by: alphabetical
editor_file_reference:
id: editor_file_reference
provider: editor
status: true
weight: -45
settings: { }
filter_html_image_secure:
id: filter_html_image_secure
provider: filter
status: true
weight: -46
settings: { }
filter_url:
id: filter_url
provider: filter
status: false
weight: -41
settings:
filter_url_length: 72
filter_html:
id: filter_html
provider: filter
status: true
weight: -49
settings:
allowed_html: '<a href hreflang id class="" title> <div id class=""> <em> <strong> <cite> <blockquote cite> <code> <ul type class=""> <ol id type class=""> <li id class=""> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <span id class=""> <sup> <img src alt data-entity-type data-entity-uuid data-align data-caption> <p> <fn id>'
filter_html_help: true
filter_html_nofollow: false
filter_autop:
id: filter_autop
provider: filter
status: false
weight: -42
settings: { }
filter_align:
id: filter_align
provider: filter
status: true
weight: -48
settings: { }
filter_caption:
id: filter_caption
provider: filter
status: true
weight: -47
settings: { }
filter_htmlcorrector:
id: filter_htmlcorrector
provider: filter
status: false
weight: -40
settings: { }
filter_html_escape:
id: filter_html_escape
provider: filter
status: false
weight: -43
settings: { }
filter_footnotes:
id: filter_footnotes
provider: footnotes
status: false
weight: -44
settings:
footnotes_collapse: '0'

17
bibcite_footnotes_article_with_citations/config/install/node.type.bibcite_fn_article_references.yml

@ -0,0 +1,17 @@
langcode: en
status: true
dependencies:
module:
- menu_ui
third_party_settings:
menu_ui:
available_menus:
- main
parent: 'main:'
name: 'Article with References'
type: bibcite_fn_article_references
description: 'Article that includes a Works Cited field and in-text references.'
help: ''
new_revision: true
preview_mode: 1
display_submitted: true

23
composer.json

@ -0,0 +1,23 @@
{
"name": "roblib/bibcite_footnotes",
"description": "Add citation footnotes with automatic numbering.",
"type": "drupal-module",
"homepage": "https://drupal.org/project/bibcite_footnotes",
"authors": [
{
"name": "Alexander O'Neill (alxp)",
"homepage": "https://www.drupal.org/u/alxp",
"role": "Maintainer"
}
],
"support": {
"issues": "https://drupal.org/project/issues/bibcite_footnotes",
"source": "https://cgit.drupalcode.org/bibcite_footnotes"
},
"license": "GPL-2.0+",
"minimum-stability": "dev",
"require": {
"drupal/core": ">=8.6",
"drupal/fakeobjects": "^1.0"
}
}

24
config/optional/field.storage.node.field_bibcite_fn_works_cited.yml

@ -0,0 +1,24 @@
langcode: en
status: true
dependencies:
module:
- bibcite_entity
- node
third_party_settings:
field_permissions:
permission_type: public
_core:
default_config_hash: pxSiuPskUjLuu_0TFIFVYFyZtkIelIPBysbXt_LyLVk
id: node.field_bibcite_fn_works_cited
field_name: field_bibcite_fn_works_cited
entity_type: node
type: entity_reference
settings:
target_type: bibcite_reference
module: core
locked: false
cardinality: -1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false

85
css/reference_footnote.css

@ -8,3 +8,88 @@
content: " "; content: " ";
white-space: pre; white-space: pre;
} }
/*
* CSS specific to Bibcite Footnotes module.
* (Borrowed from Footnotes module.
*
*
* Thanks to binford2k@lug.wsu.edu for this tip and drinkypoo
* for the question leading up to it. http://drupal.org/node/80538
*/
/* Add empty space before footnotes and a black line on top. */
.footnotes {
clear: both;
margin-top: 4em;
margin-bottom: 2em;
border-top: 1px solid #000;
}
/* Make footnotes appear in a smaller font */
.footnotes {
font-size: 0.9em;
}
/*
Make the footnote a supertext^1
*/
.see-footnote {
vertical-align: top;
position: relative;
top: -0.25em;
font-size: 0.9em;
}
/* Hide the bullet of the UL list of footnotes */
ul.footnotes {
list-style-type: none;
margin-left: 0;
padding-left: 0;
}
ul.footnotes li {
margin-left: 2.5em;
list-style-type: none;
background: none; /* Garland theme sets a bullet via background image, this must be unset! See bug 861634 */
}
/* Move the footnote number outside of the margin for footnote text (hanging indent) */
ul.footnotes {
/* This is apparently very needed for the "position: absolute;" below to work correctly */
position: relative;
}
.footnotes .footnote-label {
position: absolute;
left: 0;
z-index: 2;
}
/* Highlight the target footnote (or ref number, if a backlink was used) when user clicks a footnote. */
.see-footnote:target,
.footnotes .footnote:target {
background-color: #eee;
}
.see-footnote:target {
border: solid 1px #aaa;
}
/* Note: This CSS has a minor bug on all versions of IE in that the footnote numbers
are aligned with the absolute bottom of their space, thus being a couple of pixels
lower than their corresponding line of text. IE5.5 has a serious bug in that the numbers
are not shifted left at all, thus being garbled together with the start of their text. */
/*
Make the multiple backlinks a supertext^1
*/
.footnotes .footnote-multi {
vertical-align: top;
position: relative;
top: -0.25em;
font-size: 0.75em;
}
/*
* Textile Footnotes
*/
/* First footnote */
#fn1 {
border-top: 1px solid #000;
margin-top: 3em;
}
.footnote {
font-size: 0.9em;
}

21
js/plugins/reference_footnotes/dialogs/footnotes.js

@ -14,7 +14,7 @@
{ {
id: 'reference', id: 'reference',
type: 'select', type: 'select',
items: [[" - None - ", 0]].concat(drupalSettings.bibcite_footnotes.references), items: [[" - None - ", 0]].concat(typeof(drupalSettings.bibcite_footnotes) !== 'undefined' ? drupalSettings.bibcite_footnotes.references : []),
label: Drupal.t('Reference Footnote item:'), label: Drupal.t('Reference Footnote item:'),
setup: function (element) { setup: function (element) {
if (isEdit) if (isEdit)
@ -27,13 +27,18 @@
label: Drupal.t('Or add free-form footnote text :'), label: Drupal.t('Or add free-form footnote text :'),
setup: function (element) { setup: function (element) {
if (isEdit) { if (isEdit) {
var text = element.getText(); var markup = element.getHtml();
this.setValue(text); this.setValue(markup);
} }
} }
}, },
{
id: 'html-help',
type: 'html',
html: 'HTML tags can be used, e.g., &lt;strong&gt;, &lt;em&gt;, &lt;a href=&quot;...&quot;&gt;',
},
{ {
id: 'page', id: 'page',
type: 'text', type: 'text',
@ -56,6 +61,12 @@
if (isEdit) if (isEdit)
this.setValue(element.getAttribute('value')); this.setValue(element.getAttribute('value'));
} }
},
{
id: 'value-help',
type: 'html',
html: 'Leave blank for an automatic sequential reference number, or enter a custom footnote value',
} }
], ],
} }
@ -74,13 +85,15 @@
} }
// Re-add buttons from the current state of Settings. // Re-add buttons from the current state of Settings.
if (typeof (drupalSettings.bibcite_footnotes) !== 'undefined') {
drupalSettings.bibcite_footnotes.references.forEach(function (reference) { drupalSettings.bibcite_footnotes.references.forEach(function (reference) {
var newReference = document.createElement('option'); var newReference = document.createElement('option');
newReference.text = reference[0]; newReference.text = reference[0];
newReference.setAttribute("value", reference[1]); newReference.setAttribute("value", reference[1]);
selectBox.add(newReference); selectBox.add(newReference);
}); });
}
this.setupContent( this.realObj ); this.setupContent( this.realObj );
}, },
onOk : function() { onOk : function() {

16
js/plugins/reference_footnotes/plugin.js

@ -26,8 +26,22 @@
); );
}, },
beforeInit: function( editor )
{
// Adapt some critical editor configuration for better support
// of BBCode environment.
var config = editor.config;
CKEDITOR.tools.extend( config,
{
enterMode : CKEDITOR.ENTER_BR,
basicEntities: false,
entities : false,
fillEmptyBlocks : false
}, true );
},
init: function( editor ) init: function( editor )
{ {
editor.addCommand('createreferencefootnotes', new CKEDITOR.dialogCommand('createreferencefootnotes', { editor.addCommand('createreferencefootnotes', new CKEDITOR.dialogCommand('createreferencefootnotes', {
allowedContent: 'fn[value]' allowedContent: 'fn[value]'
})); }));
@ -103,7 +117,7 @@ CKEDITOR.plugins.reference_footnotes = {
} }
if (text && text.length > 0 ) { if (text && text.length > 0 ) {
realElement.setText(text); realElement.setHtml(text);
} }
realElement.setAttribute('value',value); realElement.setAttribute('value',value);
if (page && page.length > 0) { if (page && page.length > 0) {

10
src/CitationTools.php

@ -11,12 +11,14 @@ namespace Drupal\bibcite_footnotes;
class CitationTools { class CitationTools {
public function getRenderableReference($reference_entity_id) { public function getRenderableReference($reference_entity) {
$reference_storage = \Drupal::entityTypeManager() if (is_numeric($reference_entity)) {
$reference_entity = \Drupal::entityTypeManager()
->getStorage('bibcite_reference') ->getStorage('bibcite_reference')
->load($reference_entity_id); ->load($reference_entity);
}
$serializer = \Drupal::service('serializer'); $serializer = \Drupal::service('serializer');
$data = $serializer->normalize($reference_storage, 'csl'); $data = $serializer->normalize($reference_entity, 'csl');
return ['#theme' => 'bibcite_citation', '#data' => $data]; return ['#theme' => 'bibcite_citation', '#data' => $data];
} }
} }

5
src/Plugin/CKEditorPlugin/ReferenceFootnotes.php

@ -33,7 +33,8 @@ class ReferenceFootnotes extends CKEditorPluginBase {
return [ return [
'reference_footnotes' => [ 'reference_footnotes' => [
'label' => t('Reference Footnotes'), 'label' => t('Reference Footnotes'),
'image' => drupal_get_path('module', 'bibcite_footnotes') . '/js/plugins/reference_footnotes/images/icon.png', 'image' => \Drupal::service('extension.list.module')->getPath('bibcite_footnotes') .
'/js/plugins/reference_footnotes/images/icon.png',
], ],
]; ];
} }
@ -44,7 +45,7 @@ class ReferenceFootnotes extends CKEditorPluginBase {
public function getFile() { public function getFile() {
// Make sure that the path to the plugin.js matches the file structure of // Make sure that the path to the plugin.js matches the file structure of
// the CKEditor plugin you are implementing. // the CKEditor plugin you are implementing.
return drupal_get_path('module', 'bibcite_footnotes') . '/js/plugins/reference_footnotes/plugin.js'; return \Drupal::service('extension.list.module')->getPath('bibcite_footnotes') . '/js/plugins/reference_footnotes/plugin.js';
} }
/** /**

136
src/Plugin/Filter/ReferenceFootnotesFilter.php

@ -4,7 +4,9 @@ namespace Drupal\bibcite_footnotes\Plugin\Filter;
use Drupal\Component\Utility\Xss; use Drupal\Component\Utility\Xss;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\footnotes\Plugin\Filter\FootnotesFilter; use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/** /**
* Reference Footnotes filter. * Reference Footnotes filter.
@ -20,14 +22,11 @@ use Drupal\footnotes\Plugin\Filter\FootnotesFilter;
* "footnotes_footnotefootnote_linkcollapse" = FALSE, * "footnotes_footnotefootnote_linkcollapse" = FALSE,
* "footnotes_ibid" = FALSE, * "footnotes_ibid" = FALSE,
* "notes_section_label" = "Notes", * "notes_section_label" = "Notes",
* "references_section_label" = "References"
* }, * },
* weight = 0 * weight = 0
* ) * )
*/ */
class ReferenceFootnotesFilter extends FootnotesFilter { class ReferenceFootnotesFilter extends FilterBase {
const ENDNOTE = 0;
const REFERENCE = 1;
/** /**
* Object with configuration for reference footnotes. * Object with configuration for reference footnotes.
@ -49,6 +48,7 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
public function __construct(array $configuration, $plugin_id, array $plugin_definition) { public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition); parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->renderer = \Drupal::service('renderer');
$this->config = \Drupal::config('reference_footnotes.settings'); $this->config = \Drupal::config('reference_footnotes.settings');
$this->configEditable = \Drupal::configFactory() $this->configEditable = \Drupal::configFactory()
->getEditable('reference_footnotes.settings'); ->getEditable('reference_footnotes.settings');
@ -80,34 +80,27 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
]; ];
$settings['notes_section_label'] = [ $settings['notes_section_label'] = [
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Notes section label'), '#title' => t('Notes section heading label'),
'#default_value' => $this->settings['notes_section_label'], '#default_value' => $this->settings['notes_section_label'],
]; ];
$settings['references_section_label'] = [
'#type' => 'textfield',
'#title' => $this->t('References section label'),
'#description' => $this->t('E.g., "Works Cited" for MLA style.'),
'#default_value' => $this->settings['references_section_label'],
];
$settings['reference_dont_show_backlink_text'] = [ $settings['reference_dont_show_backlink_text'] = [
'#type' => 'checkbox', '#type' => 'checkbox',
'#title' => $this->t("Don't show note 'value' text in reference list."), '#title' => $this->t("Don't show note 'value' text in reference list."),
'#description' => $this->t("Suitable for MLA-style citations, like (Smith, 33-22)"), '#description' => $this->t("Suitable for MLA-style citations, like (Smith, 33-22)"),
'#default_value' => $this->settings['reference_dont_show_backlink_text'], '#default_value' => $this->settings['reference_dont_show_backlink_text'],
]; ];
$settings['reference_sort_by'] = [ $settings['works_cited_sort_by'] = [
'#type' => 'select', '#type' => 'select',
'#title' => $this->t("Sort references list by"), '#title' => $this->t("Sort Workd Cited list by"),
'#options' => [ '#options' => [
'order' => $this->t("Order in text"), 'weight' => $this->t("Manually"),
'alphabetical' => $this->t("Alphabetical"), 'alphabetical' => $this->t("Alphabetically"),
], ],
'#default_value' => $this->settings['reference_sort_by'], '#default_value' => $this->settings['works_cited_sort_by'],
]; ];
return $settings; return $settings;
} }
/** /**
* Helper function called from preg_replace_callback() above. * Helper function called from preg_replace_callback() above.
* *
@ -136,7 +129,7 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
if ($op == 'prepare') { if ($op == 'prepare') {
// In the 'prepare' case, the first argument contains the options to use. // In the 'prepare' case, the first argument contains the options to use.
// The name 'matches' is incorrect, we just use the variable anyway. // The name 'matches' is incorrect, we just use the variable anyway.
$opt_collapse = $matches; $opt_collapse = isset($matches['footnotes_collapse']) && $matches['footnotes_collapse'];
return 0; return 0;
} }
@ -145,8 +138,6 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
if (count($store_matches) > 0) { if (count($store_matches) > 0) {
// Separate out endontes and reference notes. // 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']) { if ($this->settings['footnotes_ibid']) {
$this->ibidemify($references); $this->ibidemify($references);
@ -155,19 +146,10 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
// themed as a list // themed as a list
$markup = [ $markup = [
'#theme' => 'bibcite_footnote_list', '#theme' => 'bibcite_footnote_list',
'#notes' => $notes, '#notes' => $store_matches,
'#note_type' => self::ENDNOTE,
'#config' => $this->settings, '#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;
@ -332,22 +314,88 @@ class ReferenceFootnotesFilter extends FootnotesFilter {
} }
} }
protected function extractNotesByType($type, $footnotes) { /**
$notes = []; * {@inheritdoc}
foreach ($footnotes as $fn) { */
switch ($type) { public function process($text, $langcode) {
case self::ENDNOTE: // Supporting both [fn] and <fn> now. Thanks to fletchgqc
if (!empty($fn['text'])) { // http://drupal.org/node/268026.
$notes[] = $fn; // Convert all square brackets to angle brackets. This way all further code
// just manipulates angle brackets. (Angle brackets are preferred here for
// the simple reason that square brackets are more tedious to use in
// regexps).
if (is_array($text)) {
implode($text);
} }
break; $text = preg_replace('|\[fn([^\]]*)\]|', '<fn$1>', $text);
case self::REFERENCE: $text = preg_replace('|\[/fn\]|', '</fn>', $text);
if (!empty($fn['reference'])) { $text = preg_replace('|\[footnotes([^\]]*)\]|', '<footnotes$1>', $text);
$notes[] = $fn;
// Check that there are an even number of open and closing tags.
// If there is one closing tag missing, append this to the end.
// If there is more disparity, throw a warning and continue.
// A closing tag may sometimes be missing when we are processing a teaser
// and it has been cut in the middle of the footnote.
// See http://drupal.org/node/253326
$foo = [];
$open_tags = preg_match_all("|<fn([^>]*)>|", $text, $foo);
$close_tags = preg_match_all("|</fn>|", $text, $foo);
if ($open_tags == $close_tags + 1) {
$text = $text . '</fn>';
} }
break; elseif ($open_tags > $close_tags + 1) {
trigger_error($this->t("You have unclosed fn tags. This is invalid and will
produce unpredictable results."));
} }
// Before doing the replacement, the callback function needs to know which
// options to use.
$this->replaceCallback($this->settings, 'prepare');
$pattern = '|<fn([^>]*)>(.*?)</fn>|s';
$text = preg_replace_callback($pattern, [
$this,
'replaceCallback',
], $text);
// Replace tag <footnotes> with the list of footnotes.
// If tag is not present, by default add the footnotes at the end.
// Thanks to acp on drupal.org for this idea. see
// http://drupal.org/node/87226.
$footer = $this->replaceCallback(NULL, 'output footer');
$pattern = '|(<footnotes([^\]]*)>)|';
if (preg_match($pattern, $text) > 0) {
$text = preg_replace($pattern, $footer, $text, 1);
} }
return $notes; elseif (!empty($footer)) {
$text .= "\n\n" . $footer;
}
$result = new FilterProcessResult($text);
$result->setAttachments([
'library' => [
'footnotes/footnotes',
],
]);
return $result;
}
/**
* Helper function to return a random text string.
*
* @return string
* Random (lowercase) alphanumeric string.
*/
public function randstr() {
$chars = "abcdefghijklmnopqrstuwxyz1234567890";
$str = "";
// Seeding with srand() not necessary in modern PHP versions.
for ($i = 0; $i < 7; $i++) {
$n = rand(0, strlen($chars) - 1);
$str .= substr($chars, $n, 1);
}
return $str;
} }
} }

Loading…
Cancel
Save