@ -0,0 +1,85 @@ |
|||||||
|
2010-03-xx Footnotes 7.x-2.5 |
||||||
|
|
||||||
|
- First Drupal 7 release. No major feature changes over 6.x-2.5. |
||||||
|
|
||||||
|
2010-12-31 Footnotes 2.5 |
||||||
|
|
||||||
|
Major features |
||||||
|
|
||||||
|
- Add new addon module: Footnotes with Views by AlexisWilke. [#939738] |
||||||
|
|
||||||
|
Smaller fixes |
||||||
|
|
||||||
|
- Remove "DEPRECATED" text from Better URL filter and instead have a small |
||||||
|
"Note" about it not being available in Footnotes 7.x-x.x. [#1002436] |
||||||
|
- Correctly ignore also a tag containing linebreak between the > and <. |
||||||
|
Fixes bug [#1002434] |
||||||
|
- Add textarea to ignored tags. Anything inside a textarea will be ignored. |
||||||
|
Bug [#974760] |
||||||
|
|
||||||
|
The intention is to branch a Drupal 7 version out of this release. |
||||||
|
|
||||||
|
2010-10-03 Footnotes 2.4 |
||||||
|
|
||||||
|
Major features |
||||||
|
|
||||||
|
- Add Footnotes Wysiwyg module with TinyMCE AND CKEditor support. |
||||||
|
This deprecates Footnotes TinyMCE module, which is kept around for backward |
||||||
|
compatibility. |
||||||
|
[#728642] |
||||||
|
- Add i18n support via Drupal.t() also to TinyMCE module. [#672034] |
||||||
|
- New feature (option): Collapse identical footnotes into one, as if using same |
||||||
|
value="". [#808214] |
||||||
|
- Implement [#728658] Highlight footnote when clicking the link. Add mention in |
||||||
|
README.txt how to change the highlight color if needed (Footnotes cannot know |
||||||
|
what is an appropriate color, I picked #eeeeee as the safest choice). |
||||||
|
|
||||||
|
Smaller fixes |
||||||
|
|
||||||
|
- Bug [#761390] |
||||||
|
Two small improper CSS names |
||||||
|
...was fixed by changing underscores to dashes in css selectors/classes. |
||||||
|
- Deprecate Better URL filter as it is committed to Drupal 7 now. [#296208] |
||||||
|
- Bug [#761664] |
||||||
|
Footnotes are double numbered when CSS is not used, such as in RSS feeds. |
||||||
|
(Due to using OL list) |
||||||
|
...was fixed by migrating to UL list. This is also appropriate since after |
||||||
|
introduction of the value="" parameter the footnotes needn't comprise an |
||||||
|
ordered list. |
||||||
|
|
||||||
|
|
||||||
|
2010-02-25 Footnotes 2.3 |
||||||
|
|
||||||
|
- Reset $used_values in _footnotes_replace_callback() after use. [#723446] |
||||||
|
|
||||||
|
2010-01-17 Footnotes 2.2 |
||||||
|
|
||||||
|
- Add TinyMCE support as a separate plugin tinymce_footnotes |
||||||
|
(thanks elgreg #464066) |
||||||
|
- Can have multiple references to same footnote in body by repeating value="" |
||||||
|
(#636808) |
||||||
|
Small fixes |
||||||
|
- Move translations from "po" to "translations" subdirectory. #430656 |
||||||
|
- Rename footnotes-alternative_layout.css due to typo in filename. |
||||||
|
- fix css: Use "ol.footnotes li" instead of "ol.footnotes" as selector for |
||||||
|
"list-style-type: none." Makes it stronger. |
||||||
|
- fix html: columns="" should be cols="" bug: #687244 |
||||||
|
|
||||||
|
2008-09-07 Footnotes 2.1 |
||||||
|
|
||||||
|
- Add "clear: both" to css of footnotes section. http://drupal.org/node/303828 |
||||||
|
|
||||||
|
2008-07-30 Footnotes 2.0 |
||||||
|
|
||||||
|
- Add support for using [fn]square brackets[/fn] (268026) |
||||||
|
- Change documentation to talk about [fn] by default, but <fn> is still |
||||||
|
supported |
||||||
|
- Add support for specifying "value" attribute. (emfabric 282104) |
||||||
|
- Mention http://drupal.org/node/279420 in known issues |
||||||
|
- Adding Better URL filter (fork from core). http://drupal.org/node/161217 |
||||||
|
- There appears to also be a French translation now. Thanks Beginner! |
||||||
|
(Japanese already done earlier.) |
||||||
|
- Fix bug where teaser might cut into the middle of a footnote: |
||||||
|
http://drupal.org/node/253326 |
||||||
|
- Start using the Drupal theme system, footnotes can now be themed by site |
||||||
|
admins (emfabric 221156) |
||||||
@ -0,0 +1,674 @@ |
|||||||
|
GNU GENERAL PUBLIC LICENSE |
||||||
|
Version 3, 29 June 2007 |
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
||||||
|
Everyone is permitted to copy and distribute verbatim copies |
||||||
|
of this license document, but changing it is not allowed. |
||||||
|
|
||||||
|
Preamble |
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for |
||||||
|
software and other kinds of works. |
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed |
||||||
|
to take away your freedom to share and change the works. By contrast, |
||||||
|
the GNU General Public License is intended to guarantee your freedom to |
||||||
|
share and change all versions of a program--to make sure it remains free |
||||||
|
software for all its users. We, the Free Software Foundation, use the |
||||||
|
GNU General Public License for most of our software; it applies also to |
||||||
|
any other work released this way by its authors. You can apply it to |
||||||
|
your programs, too. |
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not |
||||||
|
price. Our General Public Licenses are designed to make sure that you |
||||||
|
have the freedom to distribute copies of free software (and charge for |
||||||
|
them if you wish), that you receive source code or can get it if you |
||||||
|
want it, that you can change the software or use pieces of it in new |
||||||
|
free programs, and that you know you can do these things. |
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you |
||||||
|
these rights or asking you to surrender the rights. Therefore, you have |
||||||
|
certain responsibilities if you distribute copies of the software, or if |
||||||
|
you modify it: responsibilities to respect the freedom of others. |
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether |
||||||
|
gratis or for a fee, you must pass on to the recipients the same |
||||||
|
freedoms that you received. You must make sure that they, too, receive |
||||||
|
or can get the source code. And you must show them these terms so they |
||||||
|
know their rights. |
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps: |
||||||
|
(1) assert copyright on the software, and (2) offer you this License |
||||||
|
giving you legal permission to copy, distribute and/or modify it. |
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains |
||||||
|
that there is no warranty for this free software. For both users' and |
||||||
|
authors' sake, the GPL requires that modified versions be marked as |
||||||
|
changed, so that their problems will not be attributed erroneously to |
||||||
|
authors of previous versions. |
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run |
||||||
|
modified versions of the software inside them, although the manufacturer |
||||||
|
can do so. This is fundamentally incompatible with the aim of |
||||||
|
protecting users' freedom to change the software. The systematic |
||||||
|
pattern of such abuse occurs in the area of products for individuals to |
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we |
||||||
|
have designed this version of the GPL to prohibit the practice for those |
||||||
|
products. If such problems arise substantially in other domains, we |
||||||
|
stand ready to extend this provision to those domains in future versions |
||||||
|
of the GPL, as needed to protect the freedom of users. |
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents. |
||||||
|
States should not allow patents to restrict development and use of |
||||||
|
software on general-purpose computers, but in those that do, we wish to |
||||||
|
avoid the special danger that patents applied to a free program could |
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that |
||||||
|
patents cannot be used to render the program non-free. |
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and |
||||||
|
modification follow. |
||||||
|
|
||||||
|
TERMS AND CONDITIONS |
||||||
|
|
||||||
|
0. Definitions. |
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License. |
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of |
||||||
|
works, such as semiconductor masks. |
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this |
||||||
|
License. Each licensee is addressed as "you". "Licensees" and |
||||||
|
"recipients" may be individuals or organizations. |
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work |
||||||
|
in a fashion requiring copyright permission, other than the making of an |
||||||
|
exact copy. The resulting work is called a "modified version" of the |
||||||
|
earlier work or a work "based on" the earlier work. |
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based |
||||||
|
on the Program. |
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without |
||||||
|
permission, would make you directly or secondarily liable for |
||||||
|
infringement under applicable copyright law, except executing it on a |
||||||
|
computer or modifying a private copy. Propagation includes copying, |
||||||
|
distribution (with or without modification), making available to the |
||||||
|
public, and in some countries other activities as well. |
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other |
||||||
|
parties to make or receive copies. Mere interaction with a user through |
||||||
|
a computer network, with no transfer of a copy, is not conveying. |
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices" |
||||||
|
to the extent that it includes a convenient and prominently visible |
||||||
|
feature that (1) displays an appropriate copyright notice, and (2) |
||||||
|
tells the user that there is no warranty for the work (except to the |
||||||
|
extent that warranties are provided), that licensees may convey the |
||||||
|
work under this License, and how to view a copy of this License. If |
||||||
|
the interface presents a list of user commands or options, such as a |
||||||
|
menu, a prominent item in the list meets this criterion. |
||||||
|
|
||||||
|
1. Source Code. |
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work |
||||||
|
for making modifications to it. "Object code" means any non-source |
||||||
|
form of a work. |
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official |
||||||
|
standard defined by a recognized standards body, or, in the case of |
||||||
|
interfaces specified for a particular programming language, one that |
||||||
|
is widely used among developers working in that language. |
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other |
||||||
|
than the work as a whole, that (a) is included in the normal form of |
||||||
|
packaging a Major Component, but which is not part of that Major |
||||||
|
Component, and (b) serves only to enable use of the work with that |
||||||
|
Major Component, or to implement a Standard Interface for which an |
||||||
|
implementation is available to the public in source code form. A |
||||||
|
"Major Component", in this context, means a major essential component |
||||||
|
(kernel, window system, and so on) of the specific operating system |
||||||
|
(if any) on which the executable work runs, or a compiler used to |
||||||
|
produce the work, or an object code interpreter used to run it. |
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all |
||||||
|
the source code needed to generate, install, and (for an executable |
||||||
|
work) run the object code and to modify the work, including scripts to |
||||||
|
control those activities. However, it does not include the work's |
||||||
|
System Libraries, or general-purpose tools or generally available free |
||||||
|
programs which are used unmodified in performing those activities but |
||||||
|
which are not part of the work. For example, Corresponding Source |
||||||
|
includes interface definition files associated with source files for |
||||||
|
the work, and the source code for shared libraries and dynamically |
||||||
|
linked subprograms that the work is specifically designed to require, |
||||||
|
such as by intimate data communication or control flow between those |
||||||
|
subprograms and other parts of the work. |
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users |
||||||
|
can regenerate automatically from other parts of the Corresponding |
||||||
|
Source. |
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that |
||||||
|
same work. |
||||||
|
|
||||||
|
2. Basic Permissions. |
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of |
||||||
|
copyright on the Program, and are irrevocable provided the stated |
||||||
|
conditions are met. This License explicitly affirms your unlimited |
||||||
|
permission to run the unmodified Program. The output from running a |
||||||
|
covered work is covered by this License only if the output, given its |
||||||
|
content, constitutes a covered work. This License acknowledges your |
||||||
|
rights of fair use or other equivalent, as provided by copyright law. |
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not |
||||||
|
convey, without conditions so long as your license otherwise remains |
||||||
|
in force. You may convey covered works to others for the sole purpose |
||||||
|
of having them make modifications exclusively for you, or provide you |
||||||
|
with facilities for running those works, provided that you comply with |
||||||
|
the terms of this License in conveying all material for which you do |
||||||
|
not control copyright. Those thus making or running the covered works |
||||||
|
for you must do so exclusively on your behalf, under your direction |
||||||
|
and control, on terms that prohibit them from making any copies of |
||||||
|
your copyrighted material outside their relationship with you. |
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under |
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10 |
||||||
|
makes it unnecessary. |
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological |
||||||
|
measure under any applicable law fulfilling obligations under article |
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or |
||||||
|
similar laws prohibiting or restricting circumvention of such |
||||||
|
measures. |
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid |
||||||
|
circumvention of technological measures to the extent such circumvention |
||||||
|
is effected by exercising rights under this License with respect to |
||||||
|
the covered work, and you disclaim any intention to limit operation or |
||||||
|
modification of the work as a means of enforcing, against the work's |
||||||
|
users, your or third parties' legal rights to forbid circumvention of |
||||||
|
technological measures. |
||||||
|
|
||||||
|
4. Conveying Verbatim Copies. |
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you |
||||||
|
receive it, in any medium, provided that you conspicuously and |
||||||
|
appropriately publish on each copy an appropriate copyright notice; |
||||||
|
keep intact all notices stating that this License and any |
||||||
|
non-permissive terms added in accord with section 7 apply to the code; |
||||||
|
keep intact all notices of the absence of any warranty; and give all |
||||||
|
recipients a copy of this License along with the Program. |
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey, |
||||||
|
and you may offer support or warranty protection for a fee. |
||||||
|
|
||||||
|
5. Conveying Modified Source Versions. |
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to |
||||||
|
produce it from the Program, in the form of source code under the |
||||||
|
terms of section 4, provided that you also meet all of these conditions: |
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified |
||||||
|
it, and giving a relevant date. |
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is |
||||||
|
released under this License and any conditions added under section |
||||||
|
7. This requirement modifies the requirement in section 4 to |
||||||
|
"keep intact all notices". |
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this |
||||||
|
License to anyone who comes into possession of a copy. This |
||||||
|
License will therefore apply, along with any applicable section 7 |
||||||
|
additional terms, to the whole of the work, and all its parts, |
||||||
|
regardless of how they are packaged. This License gives no |
||||||
|
permission to license the work in any other way, but it does not |
||||||
|
invalidate such permission if you have separately received it. |
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display |
||||||
|
Appropriate Legal Notices; however, if the Program has interactive |
||||||
|
interfaces that do not display Appropriate Legal Notices, your |
||||||
|
work need not make them do so. |
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent |
||||||
|
works, which are not by their nature extensions of the covered work, |
||||||
|
and which are not combined with it such as to form a larger program, |
||||||
|
in or on a volume of a storage or distribution medium, is called an |
||||||
|
"aggregate" if the compilation and its resulting copyright are not |
||||||
|
used to limit the access or legal rights of the compilation's users |
||||||
|
beyond what the individual works permit. Inclusion of a covered work |
||||||
|
in an aggregate does not cause this License to apply to the other |
||||||
|
parts of the aggregate. |
||||||
|
|
||||||
|
6. Conveying Non-Source Forms. |
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms |
||||||
|
of sections 4 and 5, provided that you also convey the |
||||||
|
machine-readable Corresponding Source under the terms of this License, |
||||||
|
in one of these ways: |
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product |
||||||
|
(including a physical distribution medium), accompanied by the |
||||||
|
Corresponding Source fixed on a durable physical medium |
||||||
|
customarily used for software interchange. |
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product |
||||||
|
(including a physical distribution medium), accompanied by a |
||||||
|
written offer, valid for at least three years and valid for as |
||||||
|
long as you offer spare parts or customer support for that product |
||||||
|
model, to give anyone who possesses the object code either (1) a |
||||||
|
copy of the Corresponding Source for all the software in the |
||||||
|
product that is covered by this License, on a durable physical |
||||||
|
medium customarily used for software interchange, for a price no |
||||||
|
more than your reasonable cost of physically performing this |
||||||
|
conveying of source, or (2) access to copy the |
||||||
|
Corresponding Source from a network server at no charge. |
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the |
||||||
|
written offer to provide the Corresponding Source. This |
||||||
|
alternative is allowed only occasionally and noncommercially, and |
||||||
|
only if you received the object code with such an offer, in accord |
||||||
|
with subsection 6b. |
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated |
||||||
|
place (gratis or for a charge), and offer equivalent access to the |
||||||
|
Corresponding Source in the same way through the same place at no |
||||||
|
further charge. You need not require recipients to copy the |
||||||
|
Corresponding Source along with the object code. If the place to |
||||||
|
copy the object code is a network server, the Corresponding Source |
||||||
|
may be on a different server (operated by you or a third party) |
||||||
|
that supports equivalent copying facilities, provided you maintain |
||||||
|
clear directions next to the object code saying where to find the |
||||||
|
Corresponding Source. Regardless of what server hosts the |
||||||
|
Corresponding Source, you remain obligated to ensure that it is |
||||||
|
available for as long as needed to satisfy these requirements. |
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided |
||||||
|
you inform other peers where the object code and Corresponding |
||||||
|
Source of the work are being offered to the general public at no |
||||||
|
charge under subsection 6d. |
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded |
||||||
|
from the Corresponding Source as a System Library, need not be |
||||||
|
included in conveying the object code work. |
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any |
||||||
|
tangible personal property which is normally used for personal, family, |
||||||
|
or household purposes, or (2) anything designed or sold for incorporation |
||||||
|
into a dwelling. In determining whether a product is a consumer product, |
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular |
||||||
|
product received by a particular user, "normally used" refers to a |
||||||
|
typical or common use of that class of product, regardless of the status |
||||||
|
of the particular user or of the way in which the particular user |
||||||
|
actually uses, or expects or is expected to use, the product. A product |
||||||
|
is a consumer product regardless of whether the product has substantial |
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent |
||||||
|
the only significant mode of use of the product. |
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods, |
||||||
|
procedures, authorization keys, or other information required to install |
||||||
|
and execute modified versions of a covered work in that User Product from |
||||||
|
a modified version of its Corresponding Source. The information must |
||||||
|
suffice to ensure that the continued functioning of the modified object |
||||||
|
code is in no case prevented or interfered with solely because |
||||||
|
modification has been made. |
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or |
||||||
|
specifically for use in, a User Product, and the conveying occurs as |
||||||
|
part of a transaction in which the right of possession and use of the |
||||||
|
User Product is transferred to the recipient in perpetuity or for a |
||||||
|
fixed term (regardless of how the transaction is characterized), the |
||||||
|
Corresponding Source conveyed under this section must be accompanied |
||||||
|
by the Installation Information. But this requirement does not apply |
||||||
|
if neither you nor any third party retains the ability to install |
||||||
|
modified object code on the User Product (for example, the work has |
||||||
|
been installed in ROM). |
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a |
||||||
|
requirement to continue to provide support service, warranty, or updates |
||||||
|
for a work that has been modified or installed by the recipient, or for |
||||||
|
the User Product in which it has been modified or installed. Access to a |
||||||
|
network may be denied when the modification itself materially and |
||||||
|
adversely affects the operation of the network or violates the rules and |
||||||
|
protocols for communication across the network. |
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided, |
||||||
|
in accord with this section must be in a format that is publicly |
||||||
|
documented (and with an implementation available to the public in |
||||||
|
source code form), and must require no special password or key for |
||||||
|
unpacking, reading or copying. |
||||||
|
|
||||||
|
7. Additional Terms. |
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this |
||||||
|
License by making exceptions from one or more of its conditions. |
||||||
|
Additional permissions that are applicable to the entire Program shall |
||||||
|
be treated as though they were included in this License, to the extent |
||||||
|
that they are valid under applicable law. If additional permissions |
||||||
|
apply only to part of the Program, that part may be used separately |
||||||
|
under those permissions, but the entire Program remains governed by |
||||||
|
this License without regard to the additional permissions. |
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option |
||||||
|
remove any additional permissions from that copy, or from any part of |
||||||
|
it. (Additional permissions may be written to require their own |
||||||
|
removal in certain cases when you modify the work.) You may place |
||||||
|
additional permissions on material, added by you to a covered work, |
||||||
|
for which you have or can give appropriate copyright permission. |
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you |
||||||
|
add to a covered work, you may (if authorized by the copyright holders of |
||||||
|
that material) supplement the terms of this License with terms: |
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the |
||||||
|
terms of sections 15 and 16 of this License; or |
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or |
||||||
|
author attributions in that material or in the Appropriate Legal |
||||||
|
Notices displayed by works containing it; or |
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or |
||||||
|
requiring that modified versions of such material be marked in |
||||||
|
reasonable ways as different from the original version; or |
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or |
||||||
|
authors of the material; or |
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some |
||||||
|
trade names, trademarks, or service marks; or |
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that |
||||||
|
material by anyone who conveys the material (or modified versions of |
||||||
|
it) with contractual assumptions of liability to the recipient, for |
||||||
|
any liability that these contractual assumptions directly impose on |
||||||
|
those licensors and authors. |
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further |
||||||
|
restrictions" within the meaning of section 10. If the Program as you |
||||||
|
received it, or any part of it, contains a notice stating that it is |
||||||
|
governed by this License along with a term that is a further |
||||||
|
restriction, you may remove that term. If a license document contains |
||||||
|
a further restriction but permits relicensing or conveying under this |
||||||
|
License, you may add to a covered work material governed by the terms |
||||||
|
of that license document, provided that the further restriction does |
||||||
|
not survive such relicensing or conveying. |
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you |
||||||
|
must place, in the relevant source files, a statement of the |
||||||
|
additional terms that apply to those files, or a notice indicating |
||||||
|
where to find the applicable terms. |
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the |
||||||
|
form of a separately written license, or stated as exceptions; |
||||||
|
the above requirements apply either way. |
||||||
|
|
||||||
|
8. Termination. |
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly |
||||||
|
provided under this License. Any attempt otherwise to propagate or |
||||||
|
modify it is void, and will automatically terminate your rights under |
||||||
|
this License (including any patent licenses granted under the third |
||||||
|
paragraph of section 11). |
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your |
||||||
|
license from a particular copyright holder is reinstated (a) |
||||||
|
provisionally, unless and until the copyright holder explicitly and |
||||||
|
finally terminates your license, and (b) permanently, if the copyright |
||||||
|
holder fails to notify you of the violation by some reasonable means |
||||||
|
prior to 60 days after the cessation. |
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is |
||||||
|
reinstated permanently if the copyright holder notifies you of the |
||||||
|
violation by some reasonable means, this is the first time you have |
||||||
|
received notice of violation of this License (for any work) from that |
||||||
|
copyright holder, and you cure the violation prior to 30 days after |
||||||
|
your receipt of the notice. |
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the |
||||||
|
licenses of parties who have received copies or rights from you under |
||||||
|
this License. If your rights have been terminated and not permanently |
||||||
|
reinstated, you do not qualify to receive new licenses for the same |
||||||
|
material under section 10. |
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies. |
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or |
||||||
|
run a copy of the Program. Ancillary propagation of a covered work |
||||||
|
occurring solely as a consequence of using peer-to-peer transmission |
||||||
|
to receive a copy likewise does not require acceptance. However, |
||||||
|
nothing other than this License grants you permission to propagate or |
||||||
|
modify any covered work. These actions infringe copyright if you do |
||||||
|
not accept this License. Therefore, by modifying or propagating a |
||||||
|
covered work, you indicate your acceptance of this License to do so. |
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients. |
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically |
||||||
|
receives a license from the original licensors, to run, modify and |
||||||
|
propagate that work, subject to this License. You are not responsible |
||||||
|
for enforcing compliance by third parties with this License. |
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an |
||||||
|
organization, or substantially all assets of one, or subdividing an |
||||||
|
organization, or merging organizations. If propagation of a covered |
||||||
|
work results from an entity transaction, each party to that |
||||||
|
transaction who receives a copy of the work also receives whatever |
||||||
|
licenses to the work the party's predecessor in interest had or could |
||||||
|
give under the previous paragraph, plus a right to possession of the |
||||||
|
Corresponding Source of the work from the predecessor in interest, if |
||||||
|
the predecessor has it or can get it with reasonable efforts. |
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the |
||||||
|
rights granted or affirmed under this License. For example, you may |
||||||
|
not impose a license fee, royalty, or other charge for exercise of |
||||||
|
rights granted under this License, and you may not initiate litigation |
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that |
||||||
|
any patent claim is infringed by making, using, selling, offering for |
||||||
|
sale, or importing the Program or any portion of it. |
||||||
|
|
||||||
|
11. Patents. |
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this |
||||||
|
License of the Program or a work on which the Program is based. The |
||||||
|
work thus licensed is called the contributor's "contributor version". |
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims |
||||||
|
owned or controlled by the contributor, whether already acquired or |
||||||
|
hereafter acquired, that would be infringed by some manner, permitted |
||||||
|
by this License, of making, using, or selling its contributor version, |
||||||
|
but do not include claims that would be infringed only as a |
||||||
|
consequence of further modification of the contributor version. For |
||||||
|
purposes of this definition, "control" includes the right to grant |
||||||
|
patent sublicenses in a manner consistent with the requirements of |
||||||
|
this License. |
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free |
||||||
|
patent license under the contributor's essential patent claims, to |
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and |
||||||
|
propagate the contents of its contributor version. |
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express |
||||||
|
agreement or commitment, however denominated, not to enforce a patent |
||||||
|
(such as an express permission to practice a patent or covenant not to |
||||||
|
sue for patent infringement). To "grant" such a patent license to a |
||||||
|
party means to make such an agreement or commitment not to enforce a |
||||||
|
patent against the party. |
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license, |
||||||
|
and the Corresponding Source of the work is not available for anyone |
||||||
|
to copy, free of charge and under the terms of this License, through a |
||||||
|
publicly available network server or other readily accessible means, |
||||||
|
then you must either (1) cause the Corresponding Source to be so |
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the |
||||||
|
patent license for this particular work, or (3) arrange, in a manner |
||||||
|
consistent with the requirements of this License, to extend the patent |
||||||
|
license to downstream recipients. "Knowingly relying" means you have |
||||||
|
actual knowledge that, but for the patent license, your conveying the |
||||||
|
covered work in a country, or your recipient's use of the covered work |
||||||
|
in a country, would infringe one or more identifiable patents in that |
||||||
|
country that you have reason to believe are valid. |
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or |
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a |
||||||
|
covered work, and grant a patent license to some of the parties |
||||||
|
receiving the covered work authorizing them to use, propagate, modify |
||||||
|
or convey a specific copy of the covered work, then the patent license |
||||||
|
you grant is automatically extended to all recipients of the covered |
||||||
|
work and works based on it. |
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within |
||||||
|
the scope of its coverage, prohibits the exercise of, or is |
||||||
|
conditioned on the non-exercise of one or more of the rights that are |
||||||
|
specifically granted under this License. You may not convey a covered |
||||||
|
work if you are a party to an arrangement with a third party that is |
||||||
|
in the business of distributing software, under which you make payment |
||||||
|
to the third party based on the extent of your activity of conveying |
||||||
|
the work, and under which the third party grants, to any of the |
||||||
|
parties who would receive the covered work from you, a discriminatory |
||||||
|
patent license (a) in connection with copies of the covered work |
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily |
||||||
|
for and in connection with specific products or compilations that |
||||||
|
contain the covered work, unless you entered into that arrangement, |
||||||
|
or that patent license was granted, prior to 28 March 2007. |
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting |
||||||
|
any implied license or other defenses to infringement that may |
||||||
|
otherwise be available to you under applicable patent law. |
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom. |
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or |
||||||
|
otherwise) that contradict the conditions of this License, they do not |
||||||
|
excuse you from the conditions of this License. If you cannot convey a |
||||||
|
covered work so as to satisfy simultaneously your obligations under this |
||||||
|
License and any other pertinent obligations, then as a consequence you may |
||||||
|
not convey it at all. For example, if you agree to terms that obligate you |
||||||
|
to collect a royalty for further conveying from those to whom you convey |
||||||
|
the Program, the only way you could satisfy both those terms and this |
||||||
|
License would be to refrain entirely from conveying the Program. |
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License. |
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have |
||||||
|
permission to link or combine any covered work with a work licensed |
||||||
|
under version 3 of the GNU Affero General Public License into a single |
||||||
|
combined work, and to convey the resulting work. The terms of this |
||||||
|
License will continue to apply to the part which is the covered work, |
||||||
|
but the special requirements of the GNU Affero General Public License, |
||||||
|
section 13, concerning interaction through a network will apply to the |
||||||
|
combination as such. |
||||||
|
|
||||||
|
14. Revised Versions of this License. |
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of |
||||||
|
the GNU General Public License from time to time. Such new versions will |
||||||
|
be similar in spirit to the present version, but may differ in detail to |
||||||
|
address new problems or concerns. |
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the |
||||||
|
Program specifies that a certain numbered version of the GNU General |
||||||
|
Public License "or any later version" applies to it, you have the |
||||||
|
option of following the terms and conditions either of that numbered |
||||||
|
version or of any later version published by the Free Software |
||||||
|
Foundation. If the Program does not specify a version number of the |
||||||
|
GNU General Public License, you may choose any version ever published |
||||||
|
by the Free Software Foundation. |
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future |
||||||
|
versions of the GNU General Public License can be used, that proxy's |
||||||
|
public statement of acceptance of a version permanently authorizes you |
||||||
|
to choose that version for the Program. |
||||||
|
|
||||||
|
Later license versions may give you additional or different |
||||||
|
permissions. However, no additional obligations are imposed on any |
||||||
|
author or copyright holder as a result of your choosing to follow a |
||||||
|
later version. |
||||||
|
|
||||||
|
15. Disclaimer of Warranty. |
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
||||||
|
|
||||||
|
16. Limitation of Liability. |
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
||||||
|
SUCH DAMAGES. |
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16. |
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided |
||||||
|
above cannot be given local legal effect according to their terms, |
||||||
|
reviewing courts shall apply local law that most closely approximates |
||||||
|
an absolute waiver of all civil liability in connection with the |
||||||
|
Program, unless a warranty or assumption of liability accompanies a |
||||||
|
copy of the Program in return for a fee. |
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS |
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs |
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest |
||||||
|
possible use to the public, the best way to achieve this is to make it |
||||||
|
free software which everyone can redistribute and change under these terms. |
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest |
||||||
|
to attach them to the start of each source file to most effectively |
||||||
|
state the exclusion of warranty; and each file should have at least |
||||||
|
the "copyright" line and a pointer to where the full notice is found. |
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.> |
||||||
|
Copyright (C) <year> <name of author> |
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License |
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail. |
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short |
||||||
|
notice like this when it starts in an interactive mode: |
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author> |
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
||||||
|
This is free software, and you are welcome to redistribute it |
||||||
|
under certain conditions; type `show c' for details. |
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate |
||||||
|
parts of the General Public License. Of course, your program's commands |
||||||
|
might be different; for a GUI interface, you would use an "about box". |
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school, |
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary. |
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see |
||||||
|
<http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program |
||||||
|
into proprietary programs. If your program is a subroutine library, you |
||||||
|
may consider it more useful to permit linking proprietary applications with |
||||||
|
the library. If this is what you want to do, use the GNU Lesser General |
||||||
|
Public License instead of this License. But first, please read |
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>. |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
# Merged BibCite Footnotes module |
||||||
|
|
||||||
|
This package merges the original `bibcite_footnotes` module and the CKEditor 5-only `bibcite_footnotes_2` module back into a single Drupal module named `bibcite_footnotes`. |
||||||
|
|
||||||
|
## What changed |
||||||
|
|
||||||
|
- Kept the original `bibcite_footnotes` machine name. |
||||||
|
- Removed the hard dependency on `fakeobjects` from the main `.info.yml` so the module can be used on CKEditor 5 / Drupal 10+ sites. |
||||||
|
- Added `bibcite_footnotes.ckeditor5.yml` for CKEditor 5 toolbar integration. |
||||||
|
- Added the CKEditor 5 `footnote_picker` library and supporting CSS/JS from `bibcite_footnotes_2`. |
||||||
|
- Repointed CKEditor 5 library references from `bibcite_footnotes_2/...` to `bibcite_footnotes/...`. |
||||||
|
- Removed the duplicate/stale CKEditor 5 PHP plugin class and old raw `js/ckeditor5` plugin files to avoid competing CKEditor 5 plugin definitions. |
||||||
|
- Kept the CKEditor 4 plugin files/classes for older Drupal 9 / CKEditor 4 use. |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
The optional `bibcite_footnotes_article_with_citations` submodule is still a CKEditor 4 example configuration because its installed editor config uses `editor: ckeditor` and the `reference_footnotes` CKEditor 4 toolbar button. |
||||||
|
|
||||||
|
On a CKEditor 5 site, enable the main module and add the `Footnote/Citation` button to the desired text format's CKEditor 5 toolbar. Make sure the text format allows the custom `<fn value page reference>` element and the `filter_reference_footnotes` filter is enabled. |
||||||
@ -0,0 +1,100 @@ |
|||||||
|
Bibcite Footnotes |
||||||
|
================= |
||||||
|
|
||||||
|
Contents |
||||||
|
-------- |
||||||
|
|
||||||
|
* Introduction |
||||||
|
* Installation |
||||||
|
* Use |
||||||
|
* Current Maintainers |
||||||
|
|
||||||
|
Introduction |
||||||
|
------------ |
||||||
|
|
||||||
|
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 that contains endnotes |
||||||
|
and references. |
||||||
|
|
||||||
|
Installation |
||||||
|
------------ |
||||||
|
|
||||||
|
### Works Cited field |
||||||
|
|
||||||
|
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 |
||||||
|
<a class href id> <div class id> <span class id> <ol class id type> |
||||||
|
<sup> <li class id> |
||||||
|
``` |
||||||
|
|
||||||
|
Use |
||||||
|
--- |
||||||
|
|
||||||
|
See the documentation for [Bibliography and Citation - Import][2] module for how to |
||||||
|
add citations exported from a citation management system like RefWorks or EndNote. |
||||||
|
|
||||||
|
[2]: https://www.drupal.org/project/bibcite |
||||||
|
|
||||||
|
### Adding a Works Cited item |
||||||
|
|
||||||
|
If Inline Entity Form is enabled, an author can create new citations or add references to |
||||||
|
previously-imported or created references directly with the 'Add new reference' and |
||||||
|
'Add existing reference' buttons. |
||||||
|
|
||||||
|
With Inline Entity Form, newly-added citations are immediately selectable in the |
||||||
|
Reference Footnotes dialog. |
||||||
|
|
||||||
|
If not using the inline entity form, you can select previously imported or created references |
||||||
|
|
||||||
|
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. |
||||||
|
|
||||||
|
### Citation Formatting |
||||||
|
|
||||||
|
The Works Cited field will use the Default style selected in the |
||||||
|
Bibliography & Citation administration pages at Administration > Configuration > |
||||||
|
Bibliography & Citation > Settings in the Processing tab under Processor.' |
||||||
|
|
||||||
|
Current Maintainers |
||||||
|
------------------- |
||||||
|
|
||||||
|
* Alexander O'Neill (https://www.drupal.org/u/alxp) |
||||||
|
|
||||||
|
Sponsors |
||||||
|
-------- |
||||||
|
|
||||||
|
* This work is supported by the [University of Prince Edward Island Robertson Library](https://library.upei.ca). |
||||||
@ -0,0 +1,57 @@ |
|||||||
|
INTRODUCTION |
||||||
|
------------------ |
||||||
|
|
||||||
|
The Footnotes module is used to easily create automatically numbered footnote references in an article or post (such as a reference to a URL). |
||||||
|
|
||||||
|
* For a full description of the module, visit the project page: |
||||||
|
https://www.drupal.org/project/footnotes |
||||||
|
|
||||||
|
* To submit bug reports and feature suggestions, or track changes: |
||||||
|
https://www.drupal.org/project/issues/footnotes |
||||||
|
|
||||||
|
REQUIREMENTS |
||||||
|
------------------ |
||||||
|
|
||||||
|
The Footnote module for Drupal 8 requires the following modules and plugins: |
||||||
|
|
||||||
|
* FakeObjects (https://www.drupal.org/project/fakeobjects) |
||||||
|
* CKEditor plugin (http://ckeditor.com/addon/fakeobjects) |
||||||
|
|
||||||
|
INSTALLATION |
||||||
|
---------------- |
||||||
|
* Before you can use the FakeObjects module, you need to download the plugin from http://ckeditor.com/addon/fakeobjects and place it in /libraries/fakeobjects. |
||||||
|
|
||||||
|
* In all other steps, install the module as you would normally install a contributed Drupal module. Visit |
||||||
|
https://www.drupal.org/docs/8/extending-drupal-8/installing-drupal-8-modules for further information. |
||||||
|
|
||||||
|
CONFIGURATION |
||||||
|
------------------- |
||||||
|
* To use the footnotes filter in some input formats, go to Configuration -> |
||||||
|
Text formats. |
||||||
|
|
||||||
|
* For the Text formats you want to support footnotes markup, select configure and activate a suitable footnotes filter. |
||||||
|
|
||||||
|
* In the place where you want to add a footnote enclose the footnote text within an fn tag:<code>[fn]like this[/fn]</code>. By default, footnotes are placed at the end of the text. You can also use a <code>[footnotes]</code> or <code>[footnotes /]</code> tag to position it anywhere you want. |
||||||
|
|
||||||
|
* The filter will take the text within the tag and move it to a footnote at the |
||||||
|
bottom of the page. In it's place it will place a number which is also a link to |
||||||
|
the footnote. Footnotes supports both <code>[fn]square brackets[/fn]</code> and <code><fn>angle brackets</fn></code>. |
||||||
|
|
||||||
|
* You can also use a "value" attribute to a) set the numbering to start from the given value, or b) to set an arbitrary text string as label. |
||||||
|
|
||||||
|
Ex: |
||||||
|
|
||||||
|
[fn value="5"]This becomes footnote #5. Subsequent are #6, #7...[/fn] |
||||||
|
[fn value="*"]This footnote is assigned the label "*"[/fn] |
||||||
|
|
||||||
|
Using value="" you can have multiple references to the same footnote in the text body. |
||||||
|
|
||||||
|
[fn value="5"]This becomes footnote #5.[/fn] |
||||||
|
[fn value="5"]This is a reference to the same footnote #5, this text itself is discarded.[/fn] |
||||||
|
|
||||||
|
TROUBLESHOOTING & FAQ |
||||||
|
------------------------------ |
||||||
|
|
||||||
|
Q: When trying to install the Footnotes module, I get the message: Before you can use the FakeObjects module, you need to download the plugin from ckeditor.com and place it in /libraries/fakeobjects." |
||||||
|
|
||||||
|
A: To avoid this error message, please follow the guidelines in the Required modules and the Installation sections of this Readme file. Please mind that the Drupal 8.x-2.x branch of the Footnotes module only supports the CKEditor and does not support the TinyMCE. |
||||||
@ -0,0 +1,75 @@ |
|||||||
|
/* |
||||||
|
* CSS specific to Footnotes module. |
||||||
|
* |
||||||
|
* This is an alternative layout, it is not so nice but overcomes |
||||||
|
* the layout bugs on IE. http://drupal.org/node/166628 |
||||||
|
* To use this layout, just rename this file to footnotes.css. |
||||||
|
*/ |
||||||
|
|
||||||
|
/* 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: 0.5em; |
||||||
|
list-style-type: none; |
||||||
|
background: none; /* Garland theme sets a bullet via background image, this must be unset! See bug 861634 */ |
||||||
|
} |
||||||
|
.footnotes .footnote-label { |
||||||
|
vertical-align: top; |
||||||
|
position: relative; |
||||||
|
top: -0.35em; |
||||||
|
left: -0.35em; |
||||||
|
font-size: 0.8em; |
||||||
|
} |
||||||
|
/* 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; |
||||||
|
} |
||||||
|
/* |
||||||
|
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; |
||||||
|
} |
||||||
@ -0,0 +1,82 @@ |
|||||||
|
/* |
||||||
|
* CSS specific to 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; |
||||||
|
} |
||||||
@ -0,0 +1,60 @@ |
|||||||
|
/** |
||||||
|
* @file |
||||||
|
*/ |
||||||
|
|
||||||
|
function footnotesDialog(editor, isEdit) { |
||||||
|
return { |
||||||
|
title: Drupal.t("Footnotes Dialog"), |
||||||
|
minWidth: 500, |
||||||
|
minHeight: 50, |
||||||
|
contents: [ |
||||||
|
{ |
||||||
|
id: "info", |
||||||
|
label: Drupal.t("Add a footnote"), |
||||||
|
title: Drupal.t("Add a footnote"), |
||||||
|
elements: [ |
||||||
|
{ |
||||||
|
id: "footnote", |
||||||
|
type: "text", |
||||||
|
label: Drupal.t("Footnote text :"), |
||||||
|
setup(element) { |
||||||
|
if (isEdit) { |
||||||
|
this.setValue(element.getHtml()); |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: "value", |
||||||
|
type: "text", |
||||||
|
label: Drupal.t("Value :"), |
||||||
|
setup(element) { |
||||||
|
if (isEdit) { |
||||||
|
this.setValue(element.getAttribute("value")); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
onShow() { |
||||||
|
if (isEdit) { |
||||||
|
this.fakeObj = CKEDITOR.plugins.footnotes.getSelectedFootnote(editor); |
||||||
|
this.realObj = editor.restoreRealElement(this.fakeObj); |
||||||
|
} |
||||||
|
this.setupContent(this.realObj); |
||||||
|
}, |
||||||
|
onOk() { |
||||||
|
CKEDITOR.plugins.footnotes.createFootnote( |
||||||
|
editor, |
||||||
|
this.realObj, |
||||||
|
this.getValueOf("info", "footnote"), |
||||||
|
this.getValueOf("info", "value") |
||||||
|
); |
||||||
|
delete this.fakeObj; |
||||||
|
delete this.realObj; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
CKEDITOR.dialog.add("createfootnotes", editor => footnotesDialog(editor)); |
||||||
|
CKEDITOR.dialog.add("editfootnotes", editor => footnotesDialog(editor, 1)); |
||||||
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 263 B |
@ -0,0 +1,151 @@ |
|||||||
|
/** |
||||||
|
* @file |
||||||
|
* A CKeditor plugin to insert footnotes as in-place <fn> elements (consumed by Footnotes module in Drupal). |
||||||
|
* |
||||||
|
* This is a rather sophisticated plugin to show a dialog to insert |
||||||
|
* <fn> footnotes or edit existing ones. It produces and understands |
||||||
|
* the <fn>angle bracket</fn> variant and uses the fakeObjects API to |
||||||
|
* show a nice icon to the user, while producing proper <fn> tags when |
||||||
|
* the text is saved or View Source is pressed. |
||||||
|
* |
||||||
|
* If a text contains footnotes of the [fn]square bracket[/fn] variant, |
||||||
|
* they will be visible in the text and this plugin will not react to them. |
||||||
|
* |
||||||
|
* This plugin uses Drupal.t() to translate strings and will not as such |
||||||
|
* work outside of Drupal. (But removing those functions would be the only |
||||||
|
* change needed.) While being part of a Wysiwyg compatible module, it could |
||||||
|
* also be used together with the CKEditor module. |
||||||
|
* |
||||||
|
* Drupal Wysiwyg requirement: The first argument to CKEDITOR.plugins.add() |
||||||
|
* must be equal to the key used in $plugins[] in hook_wysiwyg_plugin(). |
||||||
|
*/ |
||||||
|
|
||||||
|
CKEDITOR.plugins.add("footnotes", { |
||||||
|
requires: ["fakeobjects", "dialog"], |
||||||
|
icons: "footnotes", |
||||||
|
onLoad() { |
||||||
|
const iconPath = `${window.location.origin + this.path}icons/fn_icon2.png`; |
||||||
|
CKEDITOR.addCss( |
||||||
|
`${".cke_footnote{background-image: url("}${CKEDITOR.getUrl( |
||||||
|
iconPath |
||||||
|
)});` +
|
||||||
|
`background - position: center center;` + |
||||||
|
`background - repeat: no - repeat;` + |
||||||
|
`width: 16px;` + |
||||||
|
`height: 16px;` + |
||||||
|
`}` |
||||||
|
); |
||||||
|
}, |
||||||
|
init(editor) { |
||||||
|
editor.addCommand( |
||||||
|
"createfootnotes", |
||||||
|
new CKEDITOR.dialogCommand("createfootnotes", { |
||||||
|
allowedContent: "fn[value]" |
||||||
|
}) |
||||||
|
); |
||||||
|
editor.addCommand( |
||||||
|
"editfootnotes", |
||||||
|
new CKEDITOR.dialogCommand("editfootnotes", { |
||||||
|
allowedContent: "fn[value]" |
||||||
|
}) |
||||||
|
); |
||||||
|
|
||||||
|
// Drupal Wysiwyg requirement: The first argument to editor.ui.addButton()
|
||||||
|
// must be equal to the key used in $plugins[<pluginName>]['buttons'][<key>]
|
||||||
|
// in hook_wysiwyg_plugin().
|
||||||
|
if (editor.ui.addButton) { |
||||||
|
editor.ui.addButton("footnotes", { |
||||||
|
label: Drupal.t("Add a footnote"), |
||||||
|
command: "createfootnotes", |
||||||
|
icon: "footnotes" |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
if (editor.addMenuItems) { |
||||||
|
editor.addMenuGroup("footnotes", 100); |
||||||
|
editor.addMenuItems({ |
||||||
|
footnotes: { |
||||||
|
label: Drupal.t("Edit footnote"), |
||||||
|
command: "editfootnotes", |
||||||
|
icon: "footnotes", |
||||||
|
group: "footnotes" |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
if (editor.contextMenu) { |
||||||
|
editor.contextMenu.addListener(element => { |
||||||
|
if (!element || element.data("cke-real-element-type") !== "fn") { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return { footnotes: CKEDITOR.TRISTATE_ON }; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
editor.on("doubleclick", evt => { |
||||||
|
if (CKEDITOR.plugins.footnotes.getSelectedFootnote(editor)) { |
||||||
|
evt.data.dialog = "editfootnotes"; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
CKEDITOR.dialog.add("createfootnotes", `${this.path}dialogs/footnotes.js`); |
||||||
|
CKEDITOR.dialog.add("editfootnotes", `${this.path}dialogs/footnotes.js`); |
||||||
|
}, |
||||||
|
afterInit(editor) { |
||||||
|
const { dataProcessor } = editor; |
||||||
|
const { dataFilter } = dataProcessor; |
||||||
|
|
||||||
|
if (dataFilter) { |
||||||
|
dataFilter.addRules({ |
||||||
|
elements: { |
||||||
|
fn(element) { |
||||||
|
return editor.createFakeParserElement( |
||||||
|
element, |
||||||
|
"cke_footnote", |
||||||
|
"hiddenfield", |
||||||
|
false |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
CKEDITOR.plugins.footnotes = { |
||||||
|
createFootnote(editor, origElement, text, value) { |
||||||
|
let realElement; |
||||||
|
if (!origElement) { |
||||||
|
realElement = CKEDITOR.dom.element.createFromHtml("<fn></fn>"); |
||||||
|
} else { |
||||||
|
realElement = origElement; |
||||||
|
} |
||||||
|
|
||||||
|
if (text && text.length > 0) { |
||||||
|
realElement.setHtml(text); |
||||||
|
} |
||||||
|
if (value && value.length > 0) { |
||||||
|
realElement.setAttribute("value", value); |
||||||
|
} |
||||||
|
|
||||||
|
const fakeElement = editor.createFakeElement( |
||||||
|
realElement, |
||||||
|
"cke_footnote", |
||||||
|
"hiddenfield", |
||||||
|
false |
||||||
|
); |
||||||
|
editor.insertElement(fakeElement); |
||||||
|
}, |
||||||
|
|
||||||
|
getSelectedFootnote(editor) { |
||||||
|
const selection = editor.getSelection(); |
||||||
|
const element = selection.getSelectedElement(); |
||||||
|
const seltype = selection.getType(); |
||||||
|
|
||||||
|
if ( |
||||||
|
seltype === CKEDITOR.SELECTION_ELEMENT && |
||||||
|
element.data("cke-real-element-type") === "hiddenfield" |
||||||
|
) { |
||||||
|
return element; |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
bibcite_footnotes_footnote_picker: |
||||||
|
ckeditor5: |
||||||
|
plugins: |
||||||
|
- footnotepicker2.FootnotePicker2 |
||||||
|
drupal: |
||||||
|
label: 'Footnote/Citation' |
||||||
|
library: bibcite_footnotes/footnote_picker |
||||||
|
admin_library: bibcite_footnotes/footnote_picker |
||||||
|
toolbar_items: |
||||||
|
footnotePicker2: |
||||||
|
label: 'Footnote' |
||||||
|
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path d="M6.5 4.5c-.6 0-1 .4-1 1v4.2c0 .6.4 1 1 1h.8v1.7H6.2c-.6 0-1 .4-1 1v.4h3.3c.6 0 1-.4 1-1V10c0-.5-.3-.9-.7-1.1.4-.3.7-.7.7-1.3V5.5c0-.6-.4-1-1-1H6.5z"/><path d="M11 6h7v1.4h-7V6zm0 3h7v1.4h-7V9zm0 3h5v1.4h-5V12z"/></svg>' |
||||||
|
elements: |
||||||
|
- <fn value page reference> |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
name: 'BibCite Footnotes' |
||||||
|
type: module |
||||||
|
description: 'Inline footnote links for BibCite References with CKEditor 4 and CKEditor 5 support.' |
||||||
|
core_version_requirement: ^9.5 || ^10 || ^11 |
||||||
|
package: 'Bibliography & Citation' |
||||||
|
dependencies: |
||||||
|
- bibcite:bibcite |
||||||
|
- bibcite:bibcite_entity |
||||||
|
- drupal:editor |
||||||
|
- drupal:filter |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
reference_footnote: |
||||||
|
version: VERSION |
||||||
|
css: |
||||||
|
theme: |
||||||
|
css/reference_footnote.css: {} |
||||||
|
dependencies: |
||||||
|
- core/jquery |
||||||
|
- core/drupalSettings |
||||||
|
|
||||||
|
replace_citations: |
||||||
|
version: VERSION |
||||||
|
js: |
||||||
|
js/replace_citations.js: {} |
||||||
|
dependencies: |
||||||
|
- core/jquery |
||||||
|
- core/drupalSettings |
||||||
|
|
||||||
|
footnote_picker: |
||||||
|
css: |
||||||
|
theme: |
||||||
|
css/footnote_picker.admin.css: {} |
||||||
|
css/footnote_picker.dialog.css: {} |
||||||
|
js: |
||||||
|
js/ckeditor5_plugins/footnotepicker2/build/footnotepicker2.js: {} |
||||||
|
dependencies: |
||||||
|
- core/ckeditor5 |
||||||
|
- core/drupal |
||||||
|
- core/drupal.dialog |
||||||
|
- core/jquery |
||||||
@ -0,0 +1,366 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* @file |
||||||
|
* Contains bibcite_footnotes.module. |
||||||
|
*/ |
||||||
|
|
||||||
|
use Drupal\Core\Field\EntityReferenceFieldItemListInterface; |
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
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; |
||||||
|
use Drupal\field\Entity\FieldConfig; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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::service('extension.list.module')->getPath('bibcite_footnotes') . '/templates', |
||||||
|
'template' => 'bibcite-footnote-list', |
||||||
|
'variables' => [ |
||||||
|
'notes' => [], |
||||||
|
'note_type' => [], |
||||||
|
'config' => [], |
||||||
|
], |
||||||
|
], |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Implements hook_help(). |
||||||
|
*/ |
||||||
|
function bibcite_footnotes_help($route_name, RouteMatchInterface $route_match) { |
||||||
|
switch ($route_name) { |
||||||
|
// Main module help for the bibcite_footnotes module. |
||||||
|
case 'help.page.bibcite_footnotes': |
||||||
|
$output = ''; |
||||||
|
$output .= '<h3>' . t('About') . '</h3>'; |
||||||
|
$output .= '<p>' . t('Inline footnote links for BibCite References') . '</p>'; |
||||||
|
return $output; |
||||||
|
|
||||||
|
default: |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Implementation of hook_preprocess_bibcite_footnote_list(). |
||||||
|
* |
||||||
|
* Gatehrs all notes and prints out Notes and References as separate lists. |
||||||
|
* |
||||||
|
* @param $variables Theme variables. |
||||||
|
* |
||||||
|
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException |
||||||
|
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException |
||||||
|
*/ |
||||||
|
function bibcite_footnotes_preprocess_bibcite_footnote_list(&$variables) { |
||||||
|
$config = $variables['config']; |
||||||
|
$footnotes = $variables['notes']; |
||||||
|
|
||||||
|
$notes = [ |
||||||
|
'#theme' => 'item_list', |
||||||
|
'#list_type' => 'ul', |
||||||
|
'#title' => $variables['note_type'] == $config['notes_section_label'], |
||||||
|
'#attributes' => ['class' => 'footnotes'], |
||||||
|
'#wrapper_attributes' => ['class' => 'container'], |
||||||
|
]; |
||||||
|
|
||||||
|
$notes['#attached']['library'][] = 'bibcite_footnotes/reference_footnote'; |
||||||
|
$dont_show_backlink_text = $config['reference_dont_show_backlink_text']; |
||||||
|
|
||||||
|
$citation_tools = new CitationTools(); |
||||||
|
if (empty($footnotes)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
foreach ($footnotes as $fn) { |
||||||
|
$item = [ |
||||||
|
'#id' => $fn['fn_id'], |
||||||
|
'#wrapper_attributes' => [ |
||||||
|
'class' => ['footnote'], |
||||||
|
], |
||||||
|
]; |
||||||
|
$build = []; |
||||||
|
$reference_entity_id = $fn['reference']; |
||||||
|
|
||||||
|
$footnote_link_text = ($dont_show_backlink_text) ? '^' : $fn['value']; |
||||||
|
if (!is_array($fn['ref_id'])) { |
||||||
|
$url = Url::fromUserInput('#' . $fn['ref_id'], ['attributes' => ['id' => $fn['fn_id'], 'class' => 'footnote-link']]); |
||||||
|
$link = Link::fromTextAndUrl(($footnote_link_text), $url)->toRenderable(); |
||||||
|
$build['footnote-link'] = $link; |
||||||
|
$override_page_in_citation = $fn['page'] ? $fn['page'] : FALSE; |
||||||
|
} |
||||||
|
else { |
||||||
|
// Output footnote that has more than one reference to it in the body. |
||||||
|
// The only difference is to insert backlinks to all references. |
||||||
|
// Helper: we need to enumerate a, b, c... |
||||||
|
$abc = str_split("abcdefghijklmnopqrstuvwxyz"); |
||||||
|
$i = 0; |
||||||
|
|
||||||
|
$url = Url::fromUserInput('#' . $fn['ref_id'][0], ['attributes' => ['id' => $fn['fn_id'], 'class' => 'footnote-link']]); |
||||||
|
$build['footnote-link'] = Link::fromTextAndUrl($footnote_link_text, $url)->toRenderable(); |
||||||
|
|
||||||
|
foreach ($fn['ref_id'] as $ref) { |
||||||
|
$url = Url::fromUserInput( '#' . $ref, ['attributes' => ['id' => $fn['fn_id'], 'class' => 'footnote-multi']]); |
||||||
|
$build[] = Link::fromTextAndUrl($abc[$i], $url)->toRenderable(); |
||||||
|
|
||||||
|
$i++; |
||||||
|
} |
||||||
|
$override_page_in_citation = FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
if (isset($fn['ibid']) && $fn['ibid']) { |
||||||
|
$build['footnote-text'] = [ |
||||||
|
'#type' => 'markup', |
||||||
|
'#markup' => ' <span class="endnote-text"><span class="ibid">' |
||||||
|
. t('Ibid') . '.</span>' |
||||||
|
. (!empty($fn['page']) ? ', ' . $fn['page'] : '') |
||||||
|
. '</span>', |
||||||
|
]; |
||||||
|
} |
||||||
|
else if (!empty($fn['text'])) { |
||||||
|
$build['footnote-link'] = Link::fromTextAndUrl($footnote_link_text, $url)->toRenderable(); |
||||||
|
$build['footnote-text'] = [ |
||||||
|
'#type' => 'markup', |
||||||
|
'#markup' => ' <span class="endnote-text">' . $fn['text'] . '</span>', |
||||||
|
]; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if (!empty($reference_entity_id)) { |
||||||
|
if (empty($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; |
||||||
|
} |
||||||
|
|
||||||
|
//$render = render($build); |
||||||
|
$citation_build_render = \Drupal::service('renderer')->render($citation_build); |
||||||
|
$item['sort'] = trim(strip_tags($citation_build_render)); |
||||||
|
} |
||||||
|
|
||||||
|
// $item['#markup'] = $render; |
||||||
|
$notes['#items'][] = $build; |
||||||
|
$reference_entity_id = FALSE; |
||||||
|
$render = FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
$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); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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'] = '<h2>Hello!</h2>'; |
||||||
|
$fn = $variables['fn']['fn']; |
||||||
|
// TODO: Make a more formal way to denote inline citations. |
||||||
|
|
||||||
|
$class = 'see-footnote'; |
||||||
|
// Generate the hover text |
||||||
|
$citation_tools = new CitationTools(); |
||||||
|
$citation_entity_id = $fn['reference']; |
||||||
|
$citation_data = $citation_tools->getRenderableReference($citation_entity_id); |
||||||
|
|
||||||
|
// Citation contains a page reference, so construct parenthetical footnote. |
||||||
|
// @TODO: Make it explicit. |
||||||
|
if (!empty($fn['page'] && !is_numeric($fn['value']))) { |
||||||
|
$fn['value'] = "({$fn['value']}, {$fn['page']})"; |
||||||
|
$citation_data['#data']['page'] = $fn['page']; |
||||||
|
$class .= '-inline'; |
||||||
|
} |
||||||
|
$citation_data_render = \Drupal::service('renderer')->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]]); |
||||||
|
|
||||||
|
|
||||||
|
$link = Link::fromTextAndUrl($fn['value'], $url)->toRenderable(); |
||||||
|
unset($variables['fn']['fn']); |
||||||
|
$variables['fn']['fn'] = $link; |
||||||
|
// $variables['fn']['fn'][] = $link; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Implementation of hook_inline_entity_form_entity_form_alter(). |
||||||
|
* |
||||||
|
* Force all entities to be saved once they are created. |
||||||
|
* |
||||||
|
* @TODO: Add cleanup batch task for orphaned References. |
||||||
|
* |
||||||
|
* @param $form |
||||||
|
* @param \Drupal\Core\Form\FormStateInterface $form_state |
||||||
|
*/ |
||||||
|
function bibcite_footnotes_inline_entity_form_entity_form_alter(&$form, FormStateInterface &$form_state) { |
||||||
|
$form['#save_entity'] = TRUE; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Implementation of hook_form_node_form_alter(). |
||||||
|
* |
||||||
|
* This function populates the Reference list drop-down in the CKEditor drop-down. |
||||||
|
* It gathers all referenced entities of type bibcite_reference and puts them |
||||||
|
* into a JavaScript setting under drupalSettings.bibcite_footnotes.references. |
||||||
|
* |
||||||
|
* @param $form |
||||||
|
* @param \Drupal\Core\Form\FormStateInterface $form_state |
||||||
|
*/ |
||||||
|
function bibcite_footnotes_form_node_form_alter(&$form, FormStateInterface &$form_state) { |
||||||
|
$ief = $form_state->get('inline_entity_form'); |
||||||
|
if ($ief) { |
||||||
|
// Inline entity storage uses hashes to separate out the field instances. |
||||||
|
$bibcite_references = []; |
||||||
|
foreach ($ief as $ief_instance) { |
||||||
|
/** |
||||||
|
* @var FieldConfig $field_config |
||||||
|
*/ |
||||||
|
if ($field_config = $ief_instance['instance']) { |
||||||
|
|
||||||
|
// Check if this is a bibcite_reference field type. |
||||||
|
if ($field_config->getSetting('handler') == 'default:bibcite_reference') { |
||||||
|
$field_name = $field_config->get('field_name'); |
||||||
|
if (!empty($ief_instance['entities'])) { |
||||||
|
|
||||||
|
foreach ($ief_instance['entities'] as $entity_wrapper) { |
||||||
|
/** |
||||||
|
* @var \Drupal\core\Entity\EntityInterface $entity |
||||||
|
*/ |
||||||
|
$entity = $entity_wrapper['entity']; |
||||||
|
if ($entity->getEntityTypeId() == 'bibcite_reference') { |
||||||
|
list($citation_text, $citation_key) = bibcite_footnotes_get_ckeditor_select_item($entity); |
||||||
|
$bibcite_references[] = [$citation_text, $citation_key]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$triggering_element = $form_state->getTriggeringElement(); |
||||||
|
if (!$triggering_element) { |
||||||
|
$form['#attached']['drupalSettings']['bibcite_footnotes']['references'] = $bibcite_references; |
||||||
|
} |
||||||
|
else { |
||||||
|
$form[$triggering_element['#parents'][0]]['widget']['#attached']['library'][] = 'bibcite_footnotes/replace_citations'; |
||||||
|
$form[$triggering_element['#parents'][0]]['widget']['#attached']['drupalSettings']['bibcite_footnotes']['references'] = $bibcite_references; |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
// Not using Inline Entity Form, get the references if they exist |
||||||
|
$entity = $form_state->getFormObject()->getEntity(); |
||||||
|
$fields = $entity->getFields(); |
||||||
|
$reference_field = FALSE; |
||||||
|
foreach ($fields as $field) { |
||||||
|
$type = $field->getFieldDefinition(); |
||||||
|
if ($type->getType() == 'entity_reference') { |
||||||
|
if ($type->getSetting('handler') == 'default:bibcite_reference') { |
||||||
|
$referenced_entities = $field->referencedEntities(); |
||||||
|
$bibcite_references = []; |
||||||
|
foreach ($referenced_entities as $referenced_entity) { |
||||||
|
list($citation_text, $citation_key) = bibcite_footnotes_get_ckeditor_select_item($referenced_entity); |
||||||
|
$bibcite_references[] = [$citation_text, $citation_key]; |
||||||
|
} |
||||||
|
$form['#attached']['drupalSettings']['bibcite_footnotes']['references'] = $bibcite_references; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Construct an item to go into the CKEditor Reference drop-down list. |
||||||
|
* |
||||||
|
* @param $entity |
||||||
|
* |
||||||
|
* @return array |
||||||
|
* The form of the array is ["Citation string", [reference entity id]. |
||||||
|
*/ |
||||||
|
function bibcite_footnotes_get_ckeditor_select_item($entity): array { |
||||||
|
$serializer = \Drupal::service('serializer'); |
||||||
|
|
||||||
|
$data = $serializer->normalize($entity, 'csl'); |
||||||
|
$build = ['#theme' => 'bibcite_citation', '#data' => $data]; |
||||||
|
$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. |
||||||
|
$citation_key = $entity->id->first()->getValue()['value']; |
||||||
|
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; |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
Description |
||||||
|
----------- |
||||||
|
|
||||||
|
Example content type and text format to demonstrate a fully-configured Reference Footnotes setup. |
||||||
@ -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: ^9.5 || ^10 || ^11 |
||||||
|
package: 'Bibliography & Citation' |
||||||
|
dependencies: |
||||||
|
- bibcite_footnotes:bibcite_footnotes |
||||||
|
- bibcite:bibcite_import |
||||||
|
- bibcite:bibcite_bibtex |
||||||
|
- bibcite:bibcite_endnote |
||||||
|
- bibcite:bibcite_ris |
||||||
|
- bibcite:bibcite_marc |
||||||
|
- inline_entity_form:inline_entity_form |
||||||
@ -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: { } |
||||||
@ -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: { } |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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' |
||||||
@ -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 |
||||||
@ -0,0 +1,38 @@ |
|||||||
|
{ |
||||||
|
"name": "drupal/footnotes", |
||||||
|
"description": "Add automatically numbered footnotes to your posts.", |
||||||
|
"type": "drupal-module", |
||||||
|
"homepage": "https://drupal.org/project/footnotes", |
||||||
|
"authors": [ |
||||||
|
{ |
||||||
|
"name": "Andrii Aleksandrov (id.aleks)", |
||||||
|
"homepage": "https://www.drupal.org/u/idaleks", |
||||||
|
"role": "Maintainer" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Oleksandr Dekhteruk (pifagor)", |
||||||
|
"homepage": "https://www.drupal.org/u/pifagor", |
||||||
|
"role": "Maintainer" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Fernando Conceição (yukare)", |
||||||
|
"homepage": "https://www.drupal.org/u/yukare", |
||||||
|
"role": "Maintainer" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Henrik Ingo (hingo)", |
||||||
|
"homepage": "https://www.drupal.org/u/hingo", |
||||||
|
"role": "Maintainer" |
||||||
|
} |
||||||
|
], |
||||||
|
"support": { |
||||||
|
"issues": "https://drupal.org/project/issues/footnotes", |
||||||
|
"source": "https://cgit.drupalcode.org/footnotes" |
||||||
|
}, |
||||||
|
"license": "GPL-2.0+", |
||||||
|
"minimum-stability": "dev", |
||||||
|
"require": { |
||||||
|
"drupal/core": ">=8.6", |
||||||
|
"drupal/fakeobjects": "^1.0" |
||||||
|
} |
||||||
|
} |
||||||
@ -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 |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
filter_settings.filter_reference_footnotes: |
||||||
|
type: mapping |
||||||
|
label: 'Reference Footnotes filter settings' |
||||||
|
mapping: |
||||||
|
footnotes_collapse: |
||||||
|
type: boolean |
||||||
|
label: 'Collapse identical footnotes' |
||||||
|
footnotes_ibid: |
||||||
|
type: boolean |
||||||
|
label: 'Use Ibid.' |
||||||
|
notes_section_label: |
||||||
|
type: label |
||||||
|
label: 'Notes section heading label' |
||||||
|
reference_dont_show_backlink_text: |
||||||
|
type: boolean |
||||||
|
label: "Don't show note value text in reference list" |
||||||
|
works_cited_sort_by: |
||||||
|
type: string |
||||||
|
label: 'Sort works cited by' |
||||||
@ -0,0 +1,95 @@ |
|||||||
|
|
||||||
|
.footnote-list .footnotes .footnote-link, |
||||||
|
.footnote-list .footnotes .footnote-multi { |
||||||
|
float: left; |
||||||
|
} |
||||||
|
.footnote-list .footnotes .footnote-link::after, |
||||||
|
.footnote-list .footnotes .footnote-multi::after { |
||||||
|
content: " "; |
||||||
|
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; |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
name: Footnotes |
||||||
|
description: 'Add automatically numbered footnotes to your posts.' |
||||||
|
|
||||||
|
type: module |
||||||
|
core_version_requirement: ^8 || ^9 |
||||||
|
|
||||||
|
dependencies: |
||||||
|
- 'fakeobjects:fakeobjects' |
||||||
|
test_dependencies: |
||||||
|
- 'fakeobjects:fakeobjects' |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* @file |
||||||
|
* Install, update and uninstall functions for the Footnotes module. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** |
||||||
|
* Implements hook_requirements(). |
||||||
|
*/ |
||||||
|
function footnotes_requirements($phase) { |
||||||
|
if ($phase != 'runtime') { |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
// Check if fakeobjects module is enabled and properly configured. |
||||||
|
$fakeobjects_exist = \Drupal::moduleHandler()->moduleExists('fakeobjects'); |
||||||
|
if ($fakeobjects_exist) { |
||||||
|
$fakeobjects_requirements = fakeobjects_requirements($phase); |
||||||
|
if ($fakeobjects_requirements['fakeobjects']['severity'] === REQUIREMENT_OK) { |
||||||
|
$requirements['footnotes'] = [ |
||||||
|
'title' => t('Footnotes'), |
||||||
|
'value' => t('Footnotes requirements are OK.'), |
||||||
|
'severity' => REQUIREMENT_OK, |
||||||
|
]; |
||||||
|
} |
||||||
|
else { |
||||||
|
$requirements['footnotes'] = [ |
||||||
|
'title' => t('Footnotes'), |
||||||
|
'value' => t('Footnotes requirements are not properly configured. Please check Fakeobjects module requirements.'), |
||||||
|
'severity' => REQUIREMENT_ERROR, |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
||||||
|
else { |
||||||
|
$requirements['footnotes'] = [ |
||||||
|
'title' => t('Footnotes'), |
||||||
|
'value' => t("<a href=':href'>Fakeobjects module</a> isn't installed/enabled.", [':href' => 'https://www.drupal.org/project/fakeobjects']), |
||||||
|
'severity' => REQUIREMENT_ERROR, |
||||||
|
'description' => t('Footnotes module has a dependency on Fakeobjects module. Ensure that Fakeobjects module is enabled and configured.'), |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
return $requirements; |
||||||
|
} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
footnotes: |
||||||
|
version: VERSION |
||||||
|
css: |
||||||
|
component: |
||||||
|
assets/css/footnotes.css: {} |
||||||
@ -0,0 +1,92 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
/** |
||||||
|
* @file |
||||||
|
* This file contains the hooks for Footnotes module. |
||||||
|
* |
||||||
|
* The Footnotes module is a filter that can be used to insert |
||||||
|
* automatically numbered footnotes into Drupal texts. |
||||||
|
*/ |
||||||
|
|
||||||
|
use Drupal\Core\Routing\RouteMatchInterface; |
||||||
|
use Drupal\Core\Url; |
||||||
|
|
||||||
|
/** |
||||||
|
* Implements hook_help(). |
||||||
|
*/ |
||||||
|
function footnotes_help($route_name, RouteMatchInterface $route_match) { |
||||||
|
switch ($route_name) { |
||||||
|
case 'help.page.footnotes': |
||||||
|
return t("Insert automatically numbered footnotes using <fn> or [fn] tags. Enable the footnotes text filter <a href=':href'>here</a>", [ |
||||||
|
':href' => Url::fromRoute('filter.admin_overview')->toString(), |
||||||
|
]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Implements hook_theme(). |
||||||
|
* |
||||||
|
* Thanks to emfabric for this implementation. http://drupal.org/node/221156 |
||||||
|
*/ |
||||||
|
function footnotes_theme() { |
||||||
|
return [ |
||||||
|
'footnote_link' => [ |
||||||
|
'variables' => [ |
||||||
|
'fn' => NULL, |
||||||
|
], |
||||||
|
], |
||||||
|
'footnote_list' => [ |
||||||
|
'variables' => [ |
||||||
|
'footnotes'=> NULL, |
||||||
|
], |
||||||
|
], |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Helper for other filters, check if Footnotes is present in your filter chain. |
||||||
|
* |
||||||
|
* Note: Due to changes in Filter API, the arguments to this function have |
||||||
|
* changed in Drupal 7. |
||||||
|
* |
||||||
|
* Other filters may leverage the Footnotes functionality in a simple way: |
||||||
|
* by outputting markup with <fn>...</fn> tags within. |
||||||
|
* |
||||||
|
* This creates a dependency, the Footnotes filter must be present later in |
||||||
|
* "Input format". By calling this helper function the other filters that |
||||||
|
* depend on Footnotes may check whether Footnotes is present later in the chain |
||||||
|
* of filters in the current Input format. |
||||||
|
* |
||||||
|
* If this function returns true, the caller may depend on Footnotes. Function |
||||||
|
* returns false if caller may not depend on Footnotes. |
||||||
|
* |
||||||
|
* You should also put "dependencies = footnotes" in your module.info file. |
||||||
|
* |
||||||
|
* Example usage: |
||||||
|
* <code> |
||||||
|
* _filter_example_process( $text, $filter, $format ) { |
||||||
|
* ... |
||||||
|
* if(footnotes_is_footnotes_later($format, $filter)) { |
||||||
|
* //output markup which may include [fn] tags |
||||||
|
* } |
||||||
|
* else { |
||||||
|
* // must make do without footnotes features |
||||||
|
* // can also emit warning/error that user should install and configure |
||||||
|
* // footnotes module |
||||||
|
* } |
||||||
|
* ... |
||||||
|
* } |
||||||
|
* </code> |
||||||
|
* |
||||||
|
* @param object $format |
||||||
|
* The text format object caller is part of. |
||||||
|
* @param object $caller_filter |
||||||
|
* The filter object representing the caller (in this text format). |
||||||
|
* |
||||||
|
* @return True |
||||||
|
* If Footnotes is present after $caller in $format. |
||||||
|
*/ |
||||||
|
function footnotes_is_footnotes_later($format, $caller_filter) { |
||||||
|
return $format['filter_footnotes']['weight'] > $caller_filter['weight']; |
||||||
|
} |
||||||
@ -0,0 +1,116 @@ |
|||||||
|
(function() { |
||||||
|
function referenceFootnotesDialog( editor, isEdit ) { |
||||||
|
return { |
||||||
|
title : Drupal.t('Reference Footnotes Dialog'), |
||||||
|
minWidth : 500, |
||||||
|
minHeight : 50, |
||||||
|
contents : [ |
||||||
|
{ |
||||||
|
id: 'info', |
||||||
|
label: Drupal.t('Add a reference footnote'), |
||||||
|
title: Drupal.t('Add a reference footnote'), |
||||||
|
elements: |
||||||
|
[ |
||||||
|
{ |
||||||
|
id: 'reference', |
||||||
|
type: 'select', |
||||||
|
items: [[" - None - ", 0]].concat(typeof(drupalSettings.bibcite_footnotes) !== 'undefined' ? drupalSettings.bibcite_footnotes.references : []), |
||||||
|
label: Drupal.t('Reference Footnote item:'), |
||||||
|
setup: function (element) { |
||||||
|
if (isEdit) |
||||||
|
this.setValue(element.getAttribute('reference')); |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 'footnote', |
||||||
|
type: 'textarea', |
||||||
|
label: Drupal.t('Or add free-form footnote text :'), |
||||||
|
setup: function (element) { |
||||||
|
if (isEdit) { |
||||||
|
var markup = element.getHtml(); |
||||||
|
|
||||||
|
this.setValue(markup); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 'html-help', |
||||||
|
type: 'html', |
||||||
|
html: 'HTML tags can be used, e.g., <strong>, <em>, <a href="...">', |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 'page', |
||||||
|
type: 'text', |
||||||
|
labelLayout: 'horizontal', |
||||||
|
label: Drupal.t('Page(s):'), |
||||||
|
style: 'float:left:width:50px', |
||||||
|
setup: function (element) { |
||||||
|
if (isEdit) { |
||||||
|
this.setValue(element.getAttribute('page')); |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
id: 'value', |
||||||
|
type: 'text', |
||||||
|
label: Drupal.t('Value :'), |
||||||
|
labelLayout: 'horizontal', |
||||||
|
style: 'float:left;width:200px;', |
||||||
|
setup: function (element) { |
||||||
|
if (isEdit) |
||||||
|
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', |
||||||
|
} |
||||||
|
], |
||||||
|
} |
||||||
|
], |
||||||
|
onShow : function() { |
||||||
|
if (isEdit) { |
||||||
|
this.fakeObj = CKEDITOR.plugins.reference_footnotes.getSelectedFootnote(editor); |
||||||
|
this.realObj = editor.restoreRealElement(this.fakeObj); |
||||||
|
} |
||||||
|
var select = this.parts.contents.$.getElementsByTagName('select'); |
||||||
|
var selectBox = select.item(0); |
||||||
|
// Remove all but the default 'None' item from teh list.
|
||||||
|
var i; |
||||||
|
for (i = selectBox.options.length - 1; i >= 1; i--) { |
||||||
|
selectBox.remove(i) |
||||||
|
} |
||||||
|
|
||||||
|
// Re-add buttons from the current state of Settings.
|
||||||
|
if (typeof (drupalSettings.bibcite_footnotes) !== 'undefined') { |
||||||
|
|
||||||
|
drupalSettings.bibcite_footnotes.references.forEach(function (reference) { |
||||||
|
var newReference = document.createElement('option'); |
||||||
|
newReference.text = reference[0]; |
||||||
|
newReference.setAttribute("value", reference[1]); |
||||||
|
selectBox.add(newReference); |
||||||
|
}); |
||||||
|
} |
||||||
|
this.setupContent( this.realObj ); |
||||||
|
}, |
||||||
|
onOk : function() { |
||||||
|
var referenceNote = this.getValueOf('info', 'reference'); |
||||||
|
var textNote = this.getValueOf('info', 'footnote'); |
||||||
|
var page = this.getValueOf('info', 'page'); |
||||||
|
CKEDITOR.plugins.reference_footnotes.createFootnote( editor, this.realObj, textNote, this.getValueOf('info', 'value'), referenceNote, page); |
||||||
|
delete this.fakeObj; |
||||||
|
delete this.realObj; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
CKEDITOR.dialog.add( 'createreferencefootnotes', function( editor ) { |
||||||
|
return referenceFootnotesDialog( editor ); |
||||||
|
}); |
||||||
|
CKEDITOR.dialog.add( 'editreferencefootnotes', function( editor ) { |
||||||
|
return referenceFootnotesDialog( editor, 1 ); |
||||||
|
}); |
||||||
|
})(); |
||||||
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 906 B |
|
After Width: | Height: | Size: 265 B |
|
After Width: | Height: | Size: 443 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 263 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 225 B |
|
After Width: | Height: | Size: 813 B |
@ -0,0 +1,143 @@ |
|||||||
|
(function() { |
||||||
|
CKEDITOR.plugins.add( 'reference_footnotes', |
||||||
|
{ |
||||||
|
requires : [ 'fakeobjects','dialog' ], |
||||||
|
icons: 'reference_footnotes', |
||||||
|
onLoad: function() { |
||||||
|
var icon_path = window.location.origin + this.path + 'images/fn_icon2.png'; |
||||||
|
var ref_icon_path = window.location.origin + this.path + 'images/fn_icon3.png'; |
||||||
|
CKEDITOR.addCss( |
||||||
|
'.cke_reference_footnote' + |
||||||
|
'{' + |
||||||
|
'background-image: url(' + CKEDITOR.getUrl( ref_icon_path ) + ');' + |
||||||
|
'background-position: center center;' + |
||||||
|
'background-repeat: no-repeat;' + |
||||||
|
'width: 16px;' + |
||||||
|
'height: 16px;' + |
||||||
|
'}' + |
||||||
|
'.cke_footnote' + |
||||||
|
'{' + |
||||||
|
'background-image: url(' + CKEDITOR.getUrl( icon_path ) + ');' + |
||||||
|
'background-position: center center;' + |
||||||
|
'background-repeat: no-repeat;' + |
||||||
|
'width: 16px;' + |
||||||
|
'height: 16px;' + |
||||||
|
'}' |
||||||
|
|
||||||
|
); |
||||||
|
}, |
||||||
|
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 ) |
||||||
|
{ |
||||||
|
|
||||||
|
editor.addCommand('createreferencefootnotes', new CKEDITOR.dialogCommand('createreferencefootnotes', { |
||||||
|
allowedContent: 'fn[value]' |
||||||
|
})); |
||||||
|
editor.addCommand('editreferencefootnotes', new CKEDITOR.dialogCommand('editreferencefootnotes', { |
||||||
|
allowedContent: 'fn[value]' |
||||||
|
})); |
||||||
|
|
||||||
|
// Drupal Wysiwyg requirement: The first argument to editor.ui.addButton()
|
||||||
|
// must be equal to the key used in $plugins[<pluginName>]['buttons'][<key>]
|
||||||
|
// in hook_wysiwyg_plugin().
|
||||||
|
editor.ui.addButton && editor.ui.addButton( 'reference_footnotes', { |
||||||
|
label: Drupal.t('Add a reference footnote'), |
||||||
|
command: 'createreferencefootnotes', |
||||||
|
icon: 'reference_footnotes' |
||||||
|
}); |
||||||
|
|
||||||
|
if (editor.addMenuItems) { |
||||||
|
editor.addMenuGroup('reference_footnotes', 100); |
||||||
|
editor.addMenuItems({ |
||||||
|
footnotes: { |
||||||
|
label: Drupal.t('Edit footnote'), |
||||||
|
command: 'editreferencefootnotes', |
||||||
|
icon: 'reference_footnotes', |
||||||
|
group: 'reference_footnotes' |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
if (editor.contextMenu) { |
||||||
|
editor.contextMenu.addListener( function( element, selection ) { |
||||||
|
if ( !element || element.data('cke-real-element-type') != 'fn' ) |
||||||
|
return null; |
||||||
|
|
||||||
|
return { footnotes: CKEDITOR.TRISTATE_ON }; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
editor.on( 'doubleclick', function( evt ) { |
||||||
|
if ( CKEDITOR.plugins.reference_footnotes.getSelectedFootnote( editor ) ) |
||||||
|
{ |
||||||
|
evt.data.dialog = 'editreferencefootnotes'; |
||||||
|
} |
||||||
|
}, null, null, 1); // Ensure this event fires after the 'footnotes' event so we can decide which dialog to show.
|
||||||
|
|
||||||
|
CKEDITOR.dialog.add( 'createreferencefootnotes', this.path + 'dialogs/footnotes.js' ); |
||||||
|
CKEDITOR.dialog.add( 'editreferencefootnotes', this.path + 'dialogs/footnotes.js' ); |
||||||
|
}, |
||||||
|
afterInit : function( editor ) { |
||||||
|
var dataProcessor = editor.dataProcessor, |
||||||
|
dataFilter = dataProcessor && dataProcessor.dataFilter; |
||||||
|
|
||||||
|
if (dataFilter) { |
||||||
|
dataFilter.addRules({ |
||||||
|
elements: { |
||||||
|
fn: function(element ) { |
||||||
|
|
||||||
|
return editor.createFakeParserElement(element, 'cke_reference_footnote', 'hiddenfield', false); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
})(); |
||||||
|
|
||||||
|
CKEDITOR.plugins.reference_footnotes = { |
||||||
|
createFootnote: function( editor, origElement, text, value, reference, page) { |
||||||
|
if (!origElement) { |
||||||
|
var realElement = CKEDITOR.dom.element.createFromHtml('<fn></fn>'); |
||||||
|
} |
||||||
|
else { |
||||||
|
realElement = origElement; |
||||||
|
} |
||||||
|
|
||||||
|
if (text && text.length > 0 ) { |
||||||
|
realElement.setHtml(text); |
||||||
|
} |
||||||
|
realElement.setAttribute('value',value); |
||||||
|
if (page && page.length > 0) { |
||||||
|
realElement.setAttribute('page', page); |
||||||
|
} |
||||||
|
if (reference && reference.length > 0) { |
||||||
|
realElement.setAttribute('reference', reference); |
||||||
|
} |
||||||
|
|
||||||
|
var fakeElement = editor.createFakeElement( realElement , 'cke_reference_footnote', 'hiddenfield', false ); |
||||||
|
editor.insertElement(fakeElement); |
||||||
|
}, |
||||||
|
|
||||||
|
getSelectedFootnote: function( editor ) { |
||||||
|
var selection = editor.getSelection(); |
||||||
|
var element = selection.getSelectedElement(); |
||||||
|
var seltype = selection.getType(); |
||||||
|
|
||||||
|
if ( seltype == CKEDITOR.SELECTION_ELEMENT && element.data('cke-real-element-type') == 'hiddenfield') { |
||||||
|
return element; |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
/** |
||||||
|
* @file |
||||||
|
* Provides JavaScript additions to entity embed dialog. |
||||||
|
* |
||||||
|
* This file provides popup windows for previewing embedded entities from the |
||||||
|
* embed dialog. |
||||||
|
*/ |
||||||
|
|
||||||
|
(function ($, Drupal) { |
||||||
|
|
||||||
|
"use strict"; |
||||||
|
|
||||||
|
Drupal.behaviors.bibciteFootnotesReplaceCitations = { |
||||||
|
attach: function attach(context, settings) { |
||||||
|
/* |
||||||
|
if (CKEDITOR.instances) { |
||||||
|
for (var instance in CKEDITOR.instances) { |
||||||
|
var editor = CKEDITOR.instances[instance]; |
||||||
|
var config = editor.config; |
||||||
|
var name = editor.name; |
||||||
|
editor.destroy(); |
||||||
|
CKEDITOR.replace(name, config); |
||||||
|
} |
||||||
|
} |
||||||
|
*/ |
||||||
|
} |
||||||
|
} |
||||||
|
})(jQuery, Drupal); |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* Created by PhpStorm. |
||||||
|
* User: aoneill |
||||||
|
* Date: 2018-10-30 |
||||||
|
* Time: 2:36 PM |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace Drupal\bibcite_footnotes; |
||||||
|
|
||||||
|
|
||||||
|
class CitationTools { |
||||||
|
|
||||||
|
public function getRenderableReference($reference_entity) { |
||||||
|
if (is_numeric($reference_entity)) { |
||||||
|
$reference_entity = \Drupal::entityTypeManager() |
||||||
|
->getStorage('bibcite_reference') |
||||||
|
->load($reference_entity); |
||||||
|
} |
||||||
|
$serializer = \Drupal::service('serializer'); |
||||||
|
$data = $serializer->normalize($reference_entity, 'csl'); |
||||||
|
return ['#theme' => 'bibcite_citation', '#data' => $data]; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,76 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\bibcite_footnotes\Plugin\BibCiteProcessor; |
||||||
|
|
||||||
|
//use AcademicPuma\CiteProc\CiteProc; |
||||||
|
use Seboettg\CiteProc\CiteProc; |
||||||
|
use Drupal\Core\Config\ConfigFactoryInterface; |
||||||
|
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; |
||||||
|
use Drupal\bibcite\Plugin\BibCiteProcessorBase; |
||||||
|
use Drupal\bibcite\Plugin\BibCiteProcessorInterface; |
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Defines a style provider based on citeproc-php library. |
||||||
|
* |
||||||
|
* Extended to support inline citation links based on the CSL. |
||||||
|
* |
||||||
|
* @BibCiteProcessor( |
||||||
|
* id = "citeproc-php", |
||||||
|
* label = @Translation("Citeproc PHP"), |
||||||
|
* ) |
||||||
|
*/ |
||||||
|
class CiteprocPhpInline extends BibCiteProcessorBase implements BibCiteProcessorInterface, ContainerFactoryPluginInterface { |
||||||
|
|
||||||
|
/** |
||||||
|
* Config factory service. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Config\ConfigFactoryInterface |
||||||
|
*/ |
||||||
|
protected $configFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { |
||||||
|
return new static( |
||||||
|
$configuration, |
||||||
|
$plugin_id, |
||||||
|
$plugin_definition, |
||||||
|
$container->get('config.factory') |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $config_factory) { |
||||||
|
parent::__construct($configuration, $plugin_id, $plugin_definition); |
||||||
|
|
||||||
|
$this->configFactory = $config_factory; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getDescription() { |
||||||
|
return $this->t('Render citation by citeproc-php library'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function render($data, $csl, $lang) { |
||||||
|
$cite_proc = new CiteProc($csl, $lang); |
||||||
|
$mode = !empty($data['mode']) ? $data['mode'] : NULL; |
||||||
|
|
||||||
|
if (!$data instanceof \stdClass) { |
||||||
|
$data = json_decode(json_encode($data)); |
||||||
|
} |
||||||
|
if (!empty($data)) { |
||||||
|
return $cite_proc->render($data, $mode); |
||||||
|
} |
||||||
|
return ''; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\footnotes\Plugin\CKEditorPlugin; |
||||||
|
|
||||||
|
use Drupal\ckeditor\CKEditorPluginBase; |
||||||
|
use Drupal\editor\Entity\Editor; |
||||||
|
use Drupal\Core\StringTranslation\StringTranslationTrait; |
||||||
|
|
||||||
|
/** |
||||||
|
* Defines the "Footnotes" plugin. |
||||||
|
* |
||||||
|
* @CKEditorPlugin( |
||||||
|
* id = "footnotes", |
||||||
|
* label = @Translation("FootnotesButton") |
||||||
|
* ) |
||||||
|
*/ |
||||||
|
class Footnotes extends CKEditorPluginBase { |
||||||
|
|
||||||
|
use StringTranslationTrait; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getDependencies(Editor $editor) { |
||||||
|
return ['fakeobjects']; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getFile() { |
||||||
|
return drupal_get_path('module', 'footnotes') . '/assets/js/ckeditor/plugin.js'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getButtons() { |
||||||
|
return [ |
||||||
|
'footnotes' => [ |
||||||
|
'label' => $this->t('Footnotes'), |
||||||
|
'image' => drupal_get_path('module', 'footnotes') . '/assets/js/ckeditor/icons/footnotes.png', |
||||||
|
], |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getConfig(Editor $editor) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,78 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\bibcite_footnotes\Plugin\CKEditorPlugin; |
||||||
|
|
||||||
|
use Drupal\ckeditor\CKEditorPluginBase; |
||||||
|
use Drupal\editor\Entity\Editor; |
||||||
|
|
||||||
|
/** |
||||||
|
* Defines the "reference_footnotes" plugin. |
||||||
|
* |
||||||
|
* NOTE: The plugin ID ('id' key) corresponds to the CKEditor plugin name. |
||||||
|
* It is the first argument of the CKEDITOR.plugins.add() function in the |
||||||
|
* plugin.js file. |
||||||
|
* |
||||||
|
* @CKEditorPlugin( |
||||||
|
* id = "reference_footnotes", |
||||||
|
* label = @Translation("Reference Footnotes") |
||||||
|
* ) |
||||||
|
*/ |
||||||
|
class ReferenceFootnotes extends CKEditorPluginBase { |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
* |
||||||
|
* NOTE: The keys of the returned array corresponds to the CKEditor button |
||||||
|
* names. They are the first argument of the editor.ui.addButton() or |
||||||
|
* editor.ui.addRichCombo() functions in the plugin.js file. |
||||||
|
*/ |
||||||
|
public function getButtons() { |
||||||
|
// Make sure that the path to the image matches the file structure of |
||||||
|
// the CKEditor plugin you are implementing. |
||||||
|
return [ |
||||||
|
'reference_footnotes' => [ |
||||||
|
'label' => t('Reference Footnotes'), |
||||||
|
'image' => \Drupal::service('extension.list.module')->getPath('bibcite_footnotes') . |
||||||
|
'/js/plugins/reference_footnotes/images/icon.png', |
||||||
|
], |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getFile() { |
||||||
|
// Make sure that the path to the plugin.js matches the file structure of |
||||||
|
// the CKEditor plugin you are implementing. |
||||||
|
return \Drupal::service('extension.list.module')->getPath('bibcite_footnotes') . '/js/plugins/reference_footnotes/plugin.js'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function isInternal() { |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getDependencies(Editor $editor) { |
||||||
|
return ['fakeobjects']; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getLibraries(Editor $editor) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function getConfig(Editor $editor) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,378 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\footnotes\Plugin\Filter; |
||||||
|
|
||||||
|
use Drupal\Component\Utility\Xss; |
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
use Drupal\Core\StringTranslation\StringTranslationTrait; |
||||||
|
use Drupal\filter\FilterProcessResult; |
||||||
|
use Drupal\filter\Plugin\FilterBase; |
||||||
|
|
||||||
|
/** |
||||||
|
* Provides a base filter for Footnotes filter. |
||||||
|
* |
||||||
|
* @Filter( |
||||||
|
* id = "filter_footnotes", |
||||||
|
* module = "footnotes", |
||||||
|
* title = @Translation("Footnotes filter"), |
||||||
|
* description = @Translation("You can insert footnotes directly into texts."), |
||||||
|
* type = \Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE, |
||||||
|
* cache = FALSE, |
||||||
|
* settings = { |
||||||
|
* "footnotes_collapse" = FALSE, |
||||||
|
* "footnotes_html" = FALSE |
||||||
|
* }, |
||||||
|
* weight = 0 |
||||||
|
* ) |
||||||
|
*/ |
||||||
|
class FootnotesFilter extends FilterBase { |
||||||
|
|
||||||
|
use StringTranslationTrait; |
||||||
|
|
||||||
|
/** |
||||||
|
* The renderer. |
||||||
|
* |
||||||
|
* @var \Drupal\Core\Render\RendererInterface |
||||||
|
*/ |
||||||
|
protected $renderer; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a FootnotesFilter object. |
||||||
|
* |
||||||
|
* @param array $configuration |
||||||
|
* A configuration array containing information about the plugin instance. |
||||||
|
* @param string $plugin_id |
||||||
|
* The plugin_id for the plugin instance. |
||||||
|
* @param array $plugin_definition |
||||||
|
* The plugin implementation definition. |
||||||
|
*/ |
||||||
|
public function __construct(array $configuration, $plugin_id, array $plugin_definition) { |
||||||
|
parent::__construct($configuration, $plugin_id, $plugin_definition); |
||||||
|
$this->renderer = \Drupal::service('renderer'); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the tips for the filter. |
||||||
|
* |
||||||
|
* @param bool $long |
||||||
|
* If get the long or short tip. |
||||||
|
* |
||||||
|
* @return string |
||||||
|
* The tip to show for the user. |
||||||
|
*/ |
||||||
|
public function tips($long = FALSE) { |
||||||
|
if ($long) { |
||||||
|
return $this->t('You can insert footnotes directly into texts with <code>[fn]This text becomes a footnote.[/fn]</code>. This will be replaced with a running number (the footnote reference) and the text within the [fn] tags will be moved to the bottom of the page (the footnote). See <a href=":link">Footnotes Readme page</a> for additional usage options.', [':link' => 'http://drupal.org/project/footnotes">']); |
||||||
|
} |
||||||
|
else { |
||||||
|
return $this->t('Use [fn]...[/fn] (or <fn>...</fn>) to insert automatically numbered footnotes.'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public function process($text, $langcode) { |
||||||
|
// Supporting both [fn] and <fn> now. Thanks to fletchgqc |
||||||
|
// http://drupal.org/node/268026. |
||||||
|
// 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); |
||||||
|
} |
||||||
|
$text = preg_replace('|\[fn([^\]]*)\]|', '<fn$1>', $text); |
||||||
|
$text = preg_replace('|\[/fn\]|', '</fn>', $text); |
||||||
|
$text = preg_replace('|\[footnotes([^\]]*)\]|', '<footnotes$1>', $text); |
||||||
|
|
||||||
|
// 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>'; |
||||||
|
} |
||||||
|
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); |
||||||
|
} |
||||||
|
elseif (!empty($footer)) { |
||||||
|
$text .= "\n\n" . $footer; |
||||||
|
} |
||||||
|
$result = new FilterProcessResult($text); |
||||||
|
$result->setAttachments([ |
||||||
|
'library' => [ |
||||||
|
'footnotes/footnotes', |
||||||
|
], |
||||||
|
]); |
||||||
|
return $result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper function called from preg_replace_callback() above. |
||||||
|
* |
||||||
|
* Uses static vars to temporarily store footnotes found. |
||||||
|
* This is not threadsafe, but PHP isn't. |
||||||
|
* |
||||||
|
* @param mixed $matches |
||||||
|
* Elements from array: |
||||||
|
* - 0: complete matched string. |
||||||
|
* - 1: tag name. |
||||||
|
* - 2: tag attributes. |
||||||
|
* - 3: tag content. |
||||||
|
* @param string $op |
||||||
|
* Operation. |
||||||
|
* |
||||||
|
* @return string |
||||||
|
* Return the string processed by geshi library. |
||||||
|
*/ |
||||||
|
protected function replaceCallback($matches, $op = '') { |
||||||
|
static $opt_collapse = 0; |
||||||
|
static $opt_html = 0; |
||||||
|
static $n = 0; |
||||||
|
static $store_matches = []; |
||||||
|
static $used_values = []; |
||||||
|
$str = ''; |
||||||
|
|
||||||
|
if ($op == 'prepare') { |
||||||
|
// In the 'prepare' case, the first argument contains the options to use. |
||||||
|
// The name 'matches' is incorrect, we just use the variable anyway. |
||||||
|
$opt_collapse = $matches['footnotes_collapse']; |
||||||
|
$opt_html = $matches['footnotes_html']; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
if ($op == 'output footer') { |
||||||
|
if (count($store_matches) > 0) { |
||||||
|
// 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 |
||||||
|
$markup = [ |
||||||
|
'#theme' => 'footnote_list', |
||||||
|
'#footnotes' => $store_matches, |
||||||
|
]; |
||||||
|
$str = $this->renderer->render($markup); |
||||||
|
} |
||||||
|
// Reset the static variables so they can be used again next time. |
||||||
|
$n = 0; |
||||||
|
$store_matches = []; |
||||||
|
$used_values = []; |
||||||
|
|
||||||
|
return $str; |
||||||
|
} |
||||||
|
|
||||||
|
// Default op: act as called by preg_replace_callback() |
||||||
|
// Random string used to ensure footnote id's are unique, even |
||||||
|
// when contents of multiple nodes reside on same page. |
||||||
|
// (fixes http://drupal.org/node/194558). |
||||||
|
$randstr = $this->randstr(); |
||||||
|
|
||||||
|
$value = ''; |
||||||
|
// Did the pattern match anything in the <fn> tag? |
||||||
|
if ($matches[1]) { |
||||||
|
// See if value attribute can parsed, either well-formed in quotes eg |
||||||
|
// <fn value="3">. |
||||||
|
if (preg_match('|value=["\'](.*?)["\']|', $matches[1], $value_match)) { |
||||||
|
$value = $value_match[1]; |
||||||
|
// Or without quotes eg <fn value=8>. |
||||||
|
} |
||||||
|
elseif (preg_match('|value=(\S*)|', $matches[1], $value_match)) { |
||||||
|
$value = $value_match[1]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ($value) { |
||||||
|
// A value label was found. If it is numeric, record it in $n so further |
||||||
|
// notes can increment from there. |
||||||
|
// After adding support for multiple references to same footnote in the |
||||||
|
// body (http://drupal.org/node/636808) also must check that $n is |
||||||
|
// monotonously increasing. |
||||||
|
if (is_numeric($value) && $n < $value) { |
||||||
|
$n = $value; |
||||||
|
} |
||||||
|
} |
||||||
|
elseif ($opt_collapse and $value_existing = $this->findFootnote($matches[2], $store_matches)) { |
||||||
|
// An identical footnote already exists. Set value to the previously |
||||||
|
// existing value. |
||||||
|
$value = $value_existing; |
||||||
|
} |
||||||
|
else { |
||||||
|
// No value label, either a plain <fn> or unparsable attributes. Increment |
||||||
|
// the footnote counter, set label equal to it. |
||||||
|
$n++; |
||||||
|
$value = $n; |
||||||
|
} |
||||||
|
|
||||||
|
// Remove illegal characters from $value so it can be used as an HTML id |
||||||
|
// attribute. |
||||||
|
$value_id = preg_replace('|[^\w\-]|', '', $value); |
||||||
|
|
||||||
|
// Create a sanitized version of $text that is suitable for using as HTML |
||||||
|
// attribute value. (In particular, as the title attribute to the footnote |
||||||
|
// link). |
||||||
|
$allowed_tags = []; |
||||||
|
$text_clean = Xss::filter($matches['2'], $allowed_tags); |
||||||
|
// HTML attribute cannot contain quotes. |
||||||
|
$text_clean = str_replace('"', """, $text_clean); |
||||||
|
// Remove newlines. Browsers don't support them anyway and they'll confuse |
||||||
|
// line break converter in filter.module. |
||||||
|
$text_clean = str_replace("\n", " ", $text_clean); |
||||||
|
$text_clean = str_replace("\r", "", $text_clean); |
||||||
|
|
||||||
|
// Create a footnote item as an array. |
||||||
|
$fn = [ |
||||||
|
'value' => $value, |
||||||
|
'text' => $opt_html ? html_entity_decode($matches[2]) : $matches[2], |
||||||
|
'text_clean' => $text_clean, |
||||||
|
'fn_id' => 'footnote' . $value_id . '_' . $randstr, |
||||||
|
'ref_id' => 'footnoteref' . $value_id . '_' . $randstr, |
||||||
|
]; |
||||||
|
|
||||||
|
// We now allow to repeat the footnote value label, in which case the link |
||||||
|
// to the previously existing footnote is returned. Content of the current |
||||||
|
// footnote is ignored. See http://drupal.org/node/636808 . |
||||||
|
if (!in_array($value, $used_values)) { |
||||||
|
// This is the normal case, add the footnote to $store_matches. |
||||||
|
// Store the footnote item. |
||||||
|
array_push($store_matches, $fn); |
||||||
|
array_push($used_values, $value); |
||||||
|
} |
||||||
|
else { |
||||||
|
// A footnote with the same label already exists. |
||||||
|
// Use the text and id from the first footnote with this value. |
||||||
|
// Any text in this footnote is discarded. |
||||||
|
$i = array_search($value, $used_values); |
||||||
|
$fn['text'] = $store_matches[$i]['text']; |
||||||
|
$fn['text_clean'] = $store_matches[$i]['text_clean']; |
||||||
|
$fn['fn_id'] = $store_matches[$i]['fn_id']; |
||||||
|
// Push the new ref_id into the first occurrence of this footnote label |
||||||
|
// The stored footnote thus holds a list of ref_id's rather than just one |
||||||
|
// id. |
||||||
|
$ref_array = is_array($store_matches[$i]['ref_id']) ? $store_matches[$i]['ref_id'] : [$store_matches[$i]['ref_id']]; |
||||||
|
array_push($ref_array, $fn['ref_id']); |
||||||
|
$store_matches[$i]['ref_id'] = $ref_array; |
||||||
|
} |
||||||
|
|
||||||
|
// Return the item themed into a footnote link. |
||||||
|
// Drupal 7 requires we use "render element" which just introduces a wrapper |
||||||
|
// around the old array. |
||||||
|
$fn = [ |
||||||
|
'#theme' => 'footnote_link', |
||||||
|
'#fn' => $fn, |
||||||
|
]; |
||||||
|
$result = $this->renderer->render($fn); |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create the settings form for the filter. |
||||||
|
* |
||||||
|
* @param array $form |
||||||
|
* A minimally prepopulated form array. |
||||||
|
* @param \Drupal\Core\Form\FormStateInterface $form_state |
||||||
|
* The state of the (entire) configuration form. |
||||||
|
* |
||||||
|
* @return array |
||||||
|
* The $form array with additional form elements for the settings of |
||||||
|
* this filter. The submitted form values should match $this->settings. |
||||||
|
* |
||||||
|
* @todo Add validation of submited form values, it already exists for |
||||||
|
* drupal 7, must update it only. |
||||||
|
*/ |
||||||
|
public function settingsForm(array $form, FormStateInterface $form_state) { |
||||||
|
$settings['footnotes_collapse'] = [ |
||||||
|
'#type' => 'checkbox', |
||||||
|
'#title' => $this->t('Collapse footnotes with identical content'), |
||||||
|
'#default_value' => $this->settings['footnotes_collapse'], |
||||||
|
'#description' => $this->t('If two footnotes have the exact same content, they will be collapsed into one as if using the same value="" attribute.'), |
||||||
|
]; |
||||||
|
$settings['footnotes_html'] = [ |
||||||
|
'#type' => 'checkbox', |
||||||
|
'#title' => $this->t('Handle footnote text as HTML'), |
||||||
|
'#default_value' => $this->settings['footnotes_html'], |
||||||
|
'#description' => $this->t('If not checked, a HTML tag in the footnote text will be shown as-is to the user.'), |
||||||
|
]; |
||||||
|
return $settings; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
private function findFootnote($text, array &$store_matches) { |
||||||
|
if (!empty($store_matches)) { |
||||||
|
foreach ($store_matches as &$fn) { |
||||||
|
if ($fn['text'] == $text) { |
||||||
|
return $fn['value']; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,300 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Drupal\bibcite_footnotes\Plugin\Filter; |
||||||
|
|
||||||
|
use Drupal\Component\Utility\Xss; |
||||||
|
use Drupal\Core\Config\Config; |
||||||
|
use Drupal\Core\Config\ConfigFactoryInterface; |
||||||
|
use Drupal\Core\Config\ImmutableConfig; |
||||||
|
use Drupal\Core\Form\FormStateInterface; |
||||||
|
use Drupal\Core\Render\RendererInterface; |
||||||
|
use Drupal\filter\FilterProcessResult; |
||||||
|
use Drupal\filter\Plugin\FilterBase; |
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
||||||
|
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reference Footnotes filter. |
||||||
|
* |
||||||
|
* @Filter( |
||||||
|
* id = "filter_reference_footnotes", |
||||||
|
* title = @Translation("Reference Footnotes filter"), |
||||||
|
* description = @Translation("You can insert footnotes directly into texts."), |
||||||
|
* type = \Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE, |
||||||
|
* cache = FALSE, |
||||||
|
* settings = { |
||||||
|
* "footnotes_collapse" = FALSE, |
||||||
|
* "footnotes_ibid" = FALSE, |
||||||
|
* "notes_section_label" = "Notes", |
||||||
|
* "reference_dont_show_backlink_text" = FALSE, |
||||||
|
* "works_cited_sort_by" = "weight" |
||||||
|
* }, |
||||||
|
* weight = 0 |
||||||
|
* ) |
||||||
|
*/ |
||||||
|
final class ReferenceFootnotesFilter extends FilterBase implements ContainerFactoryPluginInterface { |
||||||
|
|
||||||
|
protected RendererInterface $renderer; |
||||||
|
|
||||||
|
protected ImmutableConfig $config; |
||||||
|
|
||||||
|
protected Config $configEditable; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
array $configuration, |
||||||
|
string $plugin_id, |
||||||
|
array $plugin_definition, |
||||||
|
RendererInterface $renderer, |
||||||
|
ConfigFactoryInterface $configFactory, |
||||||
|
) { |
||||||
|
parent::__construct($configuration, $plugin_id, $plugin_definition); |
||||||
|
|
||||||
|
$this->renderer = $renderer; |
||||||
|
$this->config = $configFactory->get('reference_footnotes.settings'); |
||||||
|
$this->configEditable = $configFactory->getEditable('reference_footnotes.settings'); |
||||||
|
} |
||||||
|
|
||||||
|
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self { |
||||||
|
return new self( |
||||||
|
$configuration, |
||||||
|
(string) $plugin_id, |
||||||
|
$plugin_definition, |
||||||
|
$container->get('renderer'), |
||||||
|
$container->get('config.factory'), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function settingsForm(array $form, FormStateInterface $form_state): array { |
||||||
|
$settings['footnotes_collapse'] = [ |
||||||
|
'#type' => 'checkbox', |
||||||
|
'#title' => $this->t('Collapse reference footnotes with identical content'), |
||||||
|
'#default_value' => (bool) ($this->settings['footnotes_collapse'] ?? FALSE), |
||||||
|
'#description' => $this->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' => (bool) ($this->settings['footnotes_ibid'] ?? FALSE), |
||||||
|
]; |
||||||
|
|
||||||
|
$settings['notes_section_label'] = [ |
||||||
|
'#type' => 'textfield', |
||||||
|
'#title' => $this->t('Notes section heading label'), |
||||||
|
'#default_value' => (string) ($this->settings['notes_section_label'] ?? 'Notes'), |
||||||
|
]; |
||||||
|
|
||||||
|
$settings['reference_dont_show_backlink_text'] = [ |
||||||
|
'#type' => 'checkbox', |
||||||
|
'#title' => $this->t("Don't show note 'value' text in reference list."), |
||||||
|
'#description' => $this->t("Suitable for MLA-style citations, like (Smith, 33-22)"), |
||||||
|
'#default_value' => (bool) ($this->settings['reference_dont_show_backlink_text'] ?? FALSE), |
||||||
|
]; |
||||||
|
|
||||||
|
$settings['works_cited_sort_by'] = [ |
||||||
|
'#type' => 'select', |
||||||
|
'#title' => $this->t('Sort Works Cited list by'), |
||||||
|
'#options' => [ |
||||||
|
'weight' => $this->t('Manually'), |
||||||
|
'alphabetical' => $this->t('Alphabetically'), |
||||||
|
], |
||||||
|
'#default_value' => (string) ($this->settings['works_cited_sort_by'] ?? 'weight'), |
||||||
|
]; |
||||||
|
|
||||||
|
return $settings; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper function called from preg_replace_callback(). |
||||||
|
* |
||||||
|
* Uses static vars to temporarily store footnotes found. |
||||||
|
* This is not threadsafe, but PHP isn't. |
||||||
|
*/ |
||||||
|
protected function replaceCallback(array $matches, string $op = ''): string|int { |
||||||
|
static $opt_collapse = 0; |
||||||
|
static $n = 0; |
||||||
|
static $store_matches = []; |
||||||
|
static $used_values = []; |
||||||
|
|
||||||
|
if ($op === 'prepare') { |
||||||
|
// In the 'prepare' case, $matches contains the options to use. |
||||||
|
$opt_collapse = !empty($matches['footnotes_collapse']); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
if ($op === 'output footer') { |
||||||
|
$str = ''; |
||||||
|
|
||||||
|
if (count($store_matches) > 0) { |
||||||
|
if (!empty($this->settings['footnotes_ibid'])) { |
||||||
|
$this->ibidemify($store_matches); |
||||||
|
} |
||||||
|
|
||||||
|
$markup = [ |
||||||
|
'#theme' => 'bibcite_footnote_list', |
||||||
|
'#notes' => $store_matches, |
||||||
|
'#config' => $this->settings, |
||||||
|
]; |
||||||
|
$str = (string) $this->renderer->render($markup, FALSE); |
||||||
|
} |
||||||
|
|
||||||
|
// Reset static variables. |
||||||
|
$n = 0; |
||||||
|
$store_matches = []; |
||||||
|
$used_values = []; |
||||||
|
|
||||||
|
return $str; |
||||||
|
} |
||||||
|
|
||||||
|
// Default op: act as called by preg_replace_callback(). |
||||||
|
$randstr = $this->randstr(); |
||||||
|
|
||||||
|
$value = $this->extractAttribute($matches, 'value'); |
||||||
|
$page = $this->extractAttribute($matches, 'page'); |
||||||
|
$reference = $this->extractAttribute($matches, 'reference'); |
||||||
|
|
||||||
|
if ($value !== '') { |
||||||
|
if (is_numeric($value) && $n < (int) $value) { |
||||||
|
$n = (int) $value; |
||||||
|
} |
||||||
|
} |
||||||
|
elseif ($opt_collapse && ($value_existing = $this->findFootnote($matches[2], $reference, $store_matches)) !== FALSE) { |
||||||
|
$value = (string) $value_existing; |
||||||
|
} |
||||||
|
else { |
||||||
|
$n++; |
||||||
|
$value = (string) $n; |
||||||
|
} |
||||||
|
|
||||||
|
$value_id = preg_replace('|[^\w\-]|', '', $value) ?? ''; |
||||||
|
|
||||||
|
// Sanitize content for title attribute. |
||||||
|
$allowed_tags = []; |
||||||
|
$text_clean = Xss::filter($matches[2], $allowed_tags); |
||||||
|
$text_clean = str_replace('"', '"', $text_clean); |
||||||
|
$text_clean = str_replace("\n", ' ', $text_clean); |
||||||
|
$text_clean = str_replace("\r", '', $text_clean); |
||||||
|
|
||||||
|
$fn = [ |
||||||
|
'value' => $value, |
||||||
|
'text' => $matches[2], |
||||||
|
'text_clean' => $text_clean, |
||||||
|
'page' => $page, |
||||||
|
'reference' => $reference, |
||||||
|
'fn_id' => 'footnote' . $value_id . '_' . $randstr, |
||||||
|
'ref_id' => 'footnoteref' . $value_id . '_' . $randstr, |
||||||
|
]; |
||||||
|
|
||||||
|
if (!in_array($value, $used_values, TRUE)) { |
||||||
|
$store_matches[] = $fn; |
||||||
|
$used_values[] = $value; |
||||||
|
} |
||||||
|
else { |
||||||
|
$i = array_search($value, $used_values, TRUE); |
||||||
|
$fn['text'] = $store_matches[$i]['text']; |
||||||
|
$fn['text_clean'] = $store_matches[$i]['text_clean']; |
||||||
|
$fn['fn_id'] = $store_matches[$i]['fn_id']; |
||||||
|
|
||||||
|
$ref_array = is_array($store_matches[$i]['ref_id']) ? $store_matches[$i]['ref_id'] : [$store_matches[$i]['ref_id']]; |
||||||
|
$ref_array[] = $fn['ref_id']; |
||||||
|
$store_matches[$i]['ref_id'] = $ref_array; |
||||||
|
} |
||||||
|
|
||||||
|
$render_array = [ |
||||||
|
'#theme' => 'bibcite_footnote_link', |
||||||
|
'fn' => $fn, |
||||||
|
]; |
||||||
|
|
||||||
|
return (string) $this->renderer->render($render_array, FALSE); |
||||||
|
} |
||||||
|
|
||||||
|
private function findFootnote(string $text, string $reference, array &$store_matches): string|false { |
||||||
|
foreach ($store_matches as $fn) { |
||||||
|
if (($fn['text'] ?? NULL) === $text && ($fn['reference'] ?? NULL) === $reference) { |
||||||
|
return (string) $fn['value']; |
||||||
|
} |
||||||
|
} |
||||||
|
return FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
protected function extractAttribute(array $matches, string $attribute): string { |
||||||
|
$value = ''; |
||||||
|
if (!empty($matches[1])) { |
||||||
|
if (preg_match('|' . preg_quote($attribute, '|') . '=["\'](.*?)["\']|', $matches[1], $value_match)) { |
||||||
|
$value = $value_match[1]; |
||||||
|
} |
||||||
|
elseif (preg_match('|' . preg_quote($attribute, '|') . '=(\S*)|', $matches[1], $value_match)) { |
||||||
|
$value = $value_match[1]; |
||||||
|
} |
||||||
|
} |
||||||
|
return $value; |
||||||
|
} |
||||||
|
|
||||||
|
protected function ibidemify(array &$footnotes): void { |
||||||
|
$prev_reference_id = FALSE; |
||||||
|
foreach ($footnotes as $index => $fn) { |
||||||
|
if ($prev_reference_id && ($fn['reference'] ?? NULL) === $prev_reference_id) { |
||||||
|
$footnotes[$index]['ibid'] = TRUE; |
||||||
|
continue; |
||||||
|
} |
||||||
|
$prev_reference_id = $fn['reference'] ?? FALSE; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function process($text, $langcode): FilterProcessResult { |
||||||
|
if (is_array($text)) { |
||||||
|
$text = implode('', $text); |
||||||
|
} |
||||||
|
|
||||||
|
$text = preg_replace('|\[fn([^\]]*)\]|', '<fn$1>', (string) $text); |
||||||
|
$text = preg_replace('|\[/fn\]|', '</fn>', (string) $text); |
||||||
|
$text = preg_replace('|\[footnotes([^\]]*)\]|', '<footnotes$1>', (string) $text); |
||||||
|
|
||||||
|
$foo = []; |
||||||
|
$open_tags = preg_match_all("|<fn([^>]*)>|", $text, $foo); |
||||||
|
$close_tags = preg_match_all("|</fn>|", $text, $foo); |
||||||
|
|
||||||
|
if ($open_tags === $close_tags + 1) { |
||||||
|
$text .= '</fn>'; |
||||||
|
} |
||||||
|
elseif ($open_tags > $close_tags + 1) { |
||||||
|
// Keep behavior, but avoid translating inside trigger_error message. |
||||||
|
trigger_error('You have unclosed fn tags. This is invalid and will produce unpredictable results.'); |
||||||
|
} |
||||||
|
|
||||||
|
// Let the callback know which options to use. |
||||||
|
$this->replaceCallback($this->settings, 'prepare'); |
||||||
|
|
||||||
|
$pattern = '|<fn([^>]*)>(.*?)</fn>|s'; |
||||||
|
$text = preg_replace_callback($pattern, [$this, 'replaceCallback'], $text); |
||||||
|
|
||||||
|
$footer = $this->replaceCallback([], 'output footer'); |
||||||
|
$pattern = '|(<footnotes([^\]]*)>)|'; |
||||||
|
|
||||||
|
if (preg_match($pattern, $text) > 0) { |
||||||
|
$text = preg_replace($pattern, (string) $footer, $text, 1); |
||||||
|
} |
||||||
|
elseif (!empty($footer)) { |
||||||
|
$text .= "\n\n" . $footer; |
||||||
|
} |
||||||
|
|
||||||
|
$result = new FilterProcessResult((string) $text); |
||||||
|
$result->setAttachments([ |
||||||
|
'library' => ['footnotes/footnotes'], |
||||||
|
]); |
||||||
|
return $result; |
||||||
|
} |
||||||
|
|
||||||
|
public function randstr(): string { |
||||||
|
$chars = 'abcdefghijklmnopqrstuwxyz1234567890'; |
||||||
|
$str = ''; |
||||||
|
for ($i = 0; $i < 7; $i++) { |
||||||
|
$n = rand(0, strlen($chars) - 1); |
||||||
|
$str .= substr($chars, $n, 1); |
||||||
|
} |
||||||
|
return $str; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
{# |
||||||
|
/** |
||||||
|
* @file footnote-link.html.twig |
||||||
|
* Default theme implementation to present an inline reference footnote link. |
||||||
|
* |
||||||
|
* Available variables: |
||||||
|
* - fn: Components of the footnote |
||||||
|
* - link: Rendered link. |
||||||
|
* - attributes: HTML attributes for the container element. |
||||||
|
* |
||||||
|
* @see template_preprocess_footnote_link(). |
||||||
|
* |
||||||
|
* @ingroup themeable |
||||||
|
*/ |
||||||
|
#} |
||||||
|
{{ fn }} |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
{# |
||||||
|
/** |
||||||
|
* @file footnote-list.html.twig |
||||||
|
* Default theme implementation to present a list of reference footnotes. |
||||||
|
* |
||||||
|
* Available variables: |
||||||
|
* - footnotess: A list of footnotes |
||||||
|
* - notes: A list of free-form notes |
||||||
|
* - references: A list of citations |
||||||
|
* - attributes: HTML attributes for the container element. |
||||||
|
* |
||||||
|
* @see template_preprocess_footnote_list(). |
||||||
|
* |
||||||
|
* @ingroup themeable |
||||||
|
*/ |
||||||
|
#} |
||||||
|
|
||||||
|
<div{{ attributes.addClass('footnote-list') }}> |
||||||
|
{{ notes }} |
||||||
|
{{ references }} |
||||||
|
</div> |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
{# footnotes/footnote-link.html.twig #} |
||||||
|
<a class="see-footnote" id="{{ fn.ref_id }}" title="{{ fn.text_clean }}" href="#{{ fn.fn_id }}">{{ fn.value }}</a> |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
{# footnotes/footnote-list.html.twig #} |
||||||
|
<ul class="footnotes"> |
||||||
|
{% for fn in footnotes %} |
||||||
|
{% if fn.ref_id is iterable %} |
||||||
|
{# |
||||||
|
// Output footnote that has more than one reference to it in the body. |
||||||
|
// The only difference is to insert backlinks to all references. |
||||||
|
// Helper: we need to enumerate a, b, c... |
||||||
|
#} |
||||||
|
{% set abc = "abcdefghijklmnopqrstuvwxyz"|split('') %} |
||||||
|
{% set i = 0 %} |
||||||
|
<li class="footnote" id="{{ fn.fn_id }}"><a href="#{{ fn.ref_id.0 }}" class="footnote-label">{{ fn.value }}</a> |
||||||
|
{% for ref in fn.ref_id %} |
||||||
|
<a class="footnote-multi" href="#{{ ref }}">{{ attribute(abc, i) }}</a> |
||||||
|
{% set i = i + 1 %} |
||||||
|
{% endfor %} |
||||||
|
{{ fn.text|raw }}</li> |
||||||
|
{% else %} |
||||||
|
{# Output normal footnote. #} |
||||||
|
<li class="footnote" id="{{ fn.fn_id }}"><a class="footnote-label" href="#{{ fn.ref_id }}">{{ fn.value }}</a>{{ fn.text|raw }}</li> |
||||||
|
{% endif %} |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
@ -0,0 +1,168 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\Tests\footnotes\Functional; |
||||||
|
|
||||||
|
use Drupal\Core\Session\AccountInterface; |
||||||
|
use Drupal\Core\StringTranslation\StringTranslationTrait; |
||||||
|
use Drupal\Tests\BrowserTestBase; |
||||||
|
|
||||||
|
/** |
||||||
|
* Contains Footnotes Filter plugin functionality tests. |
||||||
|
* |
||||||
|
* @group footnotes |
||||||
|
*/ |
||||||
|
class FootnotesFilterPluginTest extends BrowserTestBase { |
||||||
|
|
||||||
|
use StringTranslationTrait; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
protected static $modules = [ |
||||||
|
'fakeobjects', |
||||||
|
'footnotes', |
||||||
|
'node', |
||||||
|
]; |
||||||
|
|
||||||
|
/** |
||||||
|
* An user with permissions to proper permissions. |
||||||
|
* |
||||||
|
* @var \Drupal\user\UserInterface |
||||||
|
*/ |
||||||
|
protected $adminUser; |
||||||
|
|
||||||
|
/** |
||||||
|
* Text format name. |
||||||
|
* |
||||||
|
* @var string |
||||||
|
*/ |
||||||
|
protected $formatName; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
protected function setUp() { |
||||||
|
parent::setUp(); |
||||||
|
|
||||||
|
// Create a filter admin user. |
||||||
|
$permissions = [ |
||||||
|
'administer filters', |
||||||
|
'administer nodes', |
||||||
|
'access administration pages', |
||||||
|
'administer site configuration', |
||||||
|
]; |
||||||
|
$this->adminUser = $this->drupalCreateUser($permissions); |
||||||
|
$this->formatName = strtolower($this->randomMachineName()); |
||||||
|
|
||||||
|
$this->drupalLogin($this->adminUser); |
||||||
|
$this->createTextFormat(); |
||||||
|
$this->drupalCreateContentType(['type' => 'page']); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests CKEditor Filter plugin functionality. |
||||||
|
*/ |
||||||
|
public function testDefaultFunctionality() { |
||||||
|
// Verify a title with HTML entities is properly escaped. |
||||||
|
$text1 = 'This is the note one.'; |
||||||
|
$note1 = '[fn]' . $text1 . '[/fn]'; |
||||||
|
$text2 = 'And this is the note two.'; |
||||||
|
$note2 = "<fn>$text2</fn>"; |
||||||
|
|
||||||
|
$body = '<p>' . $this->randomMachineName(100) . $note1 . '</p><p>' . |
||||||
|
$this->randomMachineName(100) . $note2 . '</p>'; |
||||||
|
|
||||||
|
// Create a node. |
||||||
|
$node = $this->drupalCreateNode([ |
||||||
|
'title' => $this->randomString(), |
||||||
|
'body' => [ |
||||||
|
0 => [ |
||||||
|
'value' => $body, |
||||||
|
'format' => $this->formatName, |
||||||
|
], |
||||||
|
], |
||||||
|
]); |
||||||
|
|
||||||
|
$this->drupalGet('node/' . $node->id()); |
||||||
|
|
||||||
|
// Footnote with [fn]. |
||||||
|
$this->assertNoRaw($note1); |
||||||
|
$this->assertText($text1); |
||||||
|
|
||||||
|
// Footnote with <fn>. |
||||||
|
$this->assertNoRaw($note2); |
||||||
|
$this->assertText($text2); |
||||||
|
|
||||||
|
// Css file: |
||||||
|
$this->assertRaw('/assets/css/footnotes.css'); |
||||||
|
// @todo currently additional settings doesn't work as expected. |
||||||
|
// So we don't check additional settings for now. |
||||||
|
// $this->createTextFormat(TRUE); |
||||||
|
$text1 = 'This is the note one.'; |
||||||
|
$note1 = "[fn value='1']{$text1}[/fn]"; |
||||||
|
$text2 = 'And this is the note two.'; |
||||||
|
$note2 = "<fn value='1'>{$text2}</fn>"; |
||||||
|
|
||||||
|
$body = '<p>' . $this->randomMachineName(100) . $note1 . '</p><p>' . |
||||||
|
$this->randomMachineName(100) . $note2 . '</p>'; |
||||||
|
|
||||||
|
// Create a node. |
||||||
|
$node = $this->drupalCreateNode([ |
||||||
|
'title' => $this->randomString(), |
||||||
|
'body' => [ |
||||||
|
0 => [ |
||||||
|
'value' => $body, |
||||||
|
'format' => $this->formatName, |
||||||
|
], |
||||||
|
], |
||||||
|
]); |
||||||
|
|
||||||
|
$this->drupalGet('node/' . $node->id()); |
||||||
|
|
||||||
|
// Footnote with [fn]. |
||||||
|
$this->assertNoRaw($note1); |
||||||
|
$this->assertText($text1); |
||||||
|
|
||||||
|
// Elements with the same value should be collapsed. |
||||||
|
// @todo This should work only if footnotes_collapse setting is enabled. |
||||||
|
$this->assertNoRaw($note2); |
||||||
|
$this->assertNoText($text2); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new text format. |
||||||
|
* |
||||||
|
* @param bool $additional_settings |
||||||
|
* Indicates if filter settings should be enabled. |
||||||
|
*/ |
||||||
|
protected function createTextFormat($additional_settings = FALSE) { |
||||||
|
$button_groups = json_encode([ |
||||||
|
[ |
||||||
|
[ |
||||||
|
'name' => 'Tools', |
||||||
|
'items' => ['Source', 'footnotes'], |
||||||
|
], |
||||||
|
], |
||||||
|
]); |
||||||
|
|
||||||
|
$edit = [ |
||||||
|
'format' => $this->formatName, |
||||||
|
'name' => $this->formatName, |
||||||
|
'roles[' . AccountInterface::AUTHENTICATED_ROLE . ']' => TRUE, |
||||||
|
'editor[editor]' => 'ckeditor', |
||||||
|
'filters[filter_footnotes][status]' => TRUE, |
||||||
|
]; |
||||||
|
$this->drupalGet("admin/config/content/formats/add"); |
||||||
|
// Keep the "CKEditor" editor selected and click the "Configure" button. |
||||||
|
$this->drupalPostForm(NULL, $edit, 'editor_configure'); |
||||||
|
$edit['editor[settings][toolbar][button_groups]'] = $button_groups; |
||||||
|
$edit['filters[filter_footnotes][settings][footnotes_collapse]'] = $button_groups; |
||||||
|
if ($additional_settings) { |
||||||
|
$edit['filters[filter_footnotes][settings][footnotes_collapse]'] = 1; |
||||||
|
$edit['filters[filter_footnotes][settings][footnotes_html]'] = 1; |
||||||
|
} |
||||||
|
$this->drupalPostForm(NULL, $edit, $this->t('Save configuration')); |
||||||
|
$this->assertText($this->t('Added text format @format.', ['@format' => $this->formatName])); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,176 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\Tests\footnotes\FunctionalJavascript; |
||||||
|
|
||||||
|
use Drupal\Core\Entity\Entity\EntityFormDisplay; |
||||||
|
use Drupal\Core\StringTranslation\StringTranslationTrait; |
||||||
|
use Drupal\editor\Entity\Editor; |
||||||
|
use Drupal\field\Entity\FieldConfig; |
||||||
|
use Drupal\field\Entity\FieldStorageConfig; |
||||||
|
use Drupal\filter\Entity\FilterFormat; |
||||||
|
use Drupal\FunctionalJavascriptTests\WebDriverTestBase; |
||||||
|
use Drupal\node\Entity\NodeType; |
||||||
|
use Drupal\Tests\ckeditor\Traits\CKEditorTestTrait; |
||||||
|
|
||||||
|
/** |
||||||
|
* Contains Footnotes CKEditor plugin functionality tests. |
||||||
|
* |
||||||
|
* @group footnotes |
||||||
|
*/ |
||||||
|
class FootnotesCkeditorPluginTest extends WebDriverTestBase |
||||||
|
{ |
||||||
|
|
||||||
|
use StringTranslationTrait; |
||||||
|
use CKEditorTestTrait; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
protected $defaultTheme = 'stark'; |
||||||
|
|
||||||
|
/** |
||||||
|
* The account. |
||||||
|
* |
||||||
|
* @var \Drupal\user\UserInterface |
||||||
|
*/ |
||||||
|
protected $account; |
||||||
|
|
||||||
|
/** |
||||||
|
* The FilterFormat config entity used for testing. |
||||||
|
* |
||||||
|
* @var \Drupal\filter\FilterFormatInterface |
||||||
|
*/ |
||||||
|
protected $filterFormat; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
public static $modules = ['node', 'ckeditor', 'filter', 'ckeditor_test', 'fakeobjects', 'footnotes']; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@inheritdoc} |
||||||
|
*/ |
||||||
|
protected function setUp() |
||||||
|
{ |
||||||
|
parent::setUp(); |
||||||
|
|
||||||
|
// Create a text format and associate CKEditor. |
||||||
|
$this->filterFormat = FilterFormat::create([ |
||||||
|
'format' => 'filtered_html', |
||||||
|
'name' => 'Filtered HTML', |
||||||
|
'filters' => [ |
||||||
|
'filter_footnotes' => [ |
||||||
|
'status' => TRUE, |
||||||
|
'settings' => [ |
||||||
|
'footnotes_collapse' => 0, |
||||||
|
'footnotes_html' => 0 |
||||||
|
], |
||||||
|
], |
||||||
|
], |
||||||
|
]); |
||||||
|
$this->filterFormat->save(); |
||||||
|
|
||||||
|
Editor::create([ |
||||||
|
'format' => 'filtered_html', |
||||||
|
'editor' => 'ckeditor', |
||||||
|
'settings' => [ |
||||||
|
'toolbar' => [ |
||||||
|
'rows' => [ |
||||||
|
[ |
||||||
|
[ |
||||||
|
'name' => 'All the things', |
||||||
|
'items' => [ |
||||||
|
'Source', |
||||||
|
'Bold', |
||||||
|
'Italic', |
||||||
|
'footnotes', |
||||||
|
], |
||||||
|
], |
||||||
|
], |
||||||
|
], |
||||||
|
], |
||||||
|
], |
||||||
|
])->save(); |
||||||
|
|
||||||
|
// Create a node type for testing. |
||||||
|
NodeType::create(['type' => 'page', 'name' => 'page'])->save(); |
||||||
|
|
||||||
|
$field_storage = FieldStorageConfig::loadByName('node', 'body'); |
||||||
|
|
||||||
|
// Create a body field instance for the 'page' node type. |
||||||
|
FieldConfig::create([ |
||||||
|
'field_storage' => $field_storage, |
||||||
|
'bundle' => 'page', |
||||||
|
'label' => 'Body', |
||||||
|
'settings' => ['display_summary' => TRUE], |
||||||
|
'required' => TRUE, |
||||||
|
])->save(); |
||||||
|
|
||||||
|
// Assign widget settings for the 'default' form mode. |
||||||
|
EntityFormDisplay::create([ |
||||||
|
'targetEntityType' => 'node', |
||||||
|
'bundle' => 'page', |
||||||
|
'mode' => 'default', |
||||||
|
'status' => TRUE, |
||||||
|
])->setComponent('body', ['type' => 'text_textarea_with_summary']) |
||||||
|
->save(); |
||||||
|
|
||||||
|
$this->account = $this->drupalCreateUser([ |
||||||
|
'administer nodes', |
||||||
|
'create page content', |
||||||
|
'use text format filtered_html', |
||||||
|
]); |
||||||
|
$this->drupalLogin($this->account); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests CKEditor plugin functionality for body field. |
||||||
|
*/ |
||||||
|
public function testUi() |
||||||
|
{ |
||||||
|
$session = $this->getSession(); |
||||||
|
$assert_session = $this->assertSession(); |
||||||
|
|
||||||
|
$this->drupalGet("node/add/page"); |
||||||
|
$page = $session->getPage(); |
||||||
|
|
||||||
|
$this->waitForEditor(); |
||||||
|
$this->pressEditorButton('footnotes'); |
||||||
|
$this->assertNotEmpty( |
||||||
|
$assert_session->waitForElementVisible('css', '.cke_1.cke_editor_edit-body-0-value_dialog') |
||||||
|
); |
||||||
|
$assert_session->elementTextContains('css', 'table.cke_dialog .cke_dialog_title', $this->t('Footnotes Dialog')); |
||||||
|
$assert_session->elementTextContains('css', '.cke_dialog_page_contents table tr:first-child', $this->t('Footnote text :')); |
||||||
|
$assert_session->elementTextContains('css', '.cke_dialog_page_contents table tr:last-child', $this->t('Value :')); |
||||||
|
$page->find('css', 'a.cke_dialog_ui_button_cancel')->click(); |
||||||
|
|
||||||
|
$this->assertEmpty($assert_session->elementExists('css', '.cke_1.cke_editor_edit-body-0-value_dialog')->isVisible()); |
||||||
|
|
||||||
|
$texts = ['Text one.', 'Text two.', 'Text tree', 'Text four', 'Text five']; |
||||||
|
foreach ($texts as $key => $value) { |
||||||
|
$this->pressEditorButton('footnotes'); |
||||||
|
$this->assertNotEmpty( |
||||||
|
$assert_session->waitForElementVisible('css', '.cke_1.cke_editor_edit-body-0-value_dialog') |
||||||
|
); |
||||||
|
$assert_session->elementExists('css', '.cke_dialog_page_contents table tr:last-child input')->setValue($key); |
||||||
|
$assert_session->elementExists('css', '.cke_dialog_page_contents table tr:first-child input')->setValue($value); |
||||||
|
$page->find('css', 'a.cke_dialog_ui_button_ok')->click(); |
||||||
|
|
||||||
|
$this->assertEmpty($assert_session->elementExists('css', '.cke_1.cke_editor_edit-body-0-value_dialog')->isVisible()); |
||||||
|
} |
||||||
|
$this->pressEditorButton('source'); |
||||||
|
$body_value = $assert_session->elementExists('css', '.cke .cke_contents .cke_source')->getValue(); |
||||||
|
|
||||||
|
$body_value = str_replace(["\r\n", "\r", "\n"], "", $body_value); |
||||||
|
$body_value = trim($body_value); |
||||||
|
|
||||||
|
$expected_value = '<p>'; |
||||||
|
foreach ($texts as $key => $value) { |
||||||
|
$expected_value .= '<fn value="' . $key . '">' . $value . '</fn>'; |
||||||
|
} |
||||||
|
$expected_value .= '</p>'; |
||||||
|
|
||||||
|
$this->assertEqual($body_value, $expected_value, $this->t('String, formed by CKEditor, is correct.')); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||