added linkit contrib module
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) 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
|
||||
this service 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 make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. 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.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
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
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE 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.
|
||||
|
||||
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
|
||||
convey 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 2 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, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision 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, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This 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.
|
||||
@@ -0,0 +1,33 @@
|
||||
Linkit
|
||||
===========
|
||||
Linkit provides an **enriched linking experience for internal and external
|
||||
linking** with editors by using an autocomplete field. Linkit has by default
|
||||
support for nodes, users, taxonomy terms, files, comments and
|
||||
**basic support for all types of entities** that defines a canonical link
|
||||
template.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
* Normal module installation procedure. See
|
||||
https://www.drupal.org/documentation/install/modules-themes/modules-8
|
||||
|
||||
|
||||
Configuration
|
||||
------------
|
||||
|
||||
After the installation, you have to create a Linkit profile. The profile will
|
||||
contain information about which plugins to use.
|
||||
Profiles can be created at `/admin/config/content/linkit`
|
||||
|
||||
When you have created a profile, you need to enable the Linkit plugin on the
|
||||
text format you want to use. Formats are found at
|
||||
`admin/config/content/formats`.
|
||||
|
||||
|
||||
Plugins examples
|
||||
------------
|
||||
|
||||
There are plugin implementation examples in the linkit_test module bundled with
|
||||
Linkit core.
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "drupal/linkit",
|
||||
"description": "Linkit - Enriched linking experience",
|
||||
"type": "drupal-module",
|
||||
"homepage": "http://drupal.org/project/linkit",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Emil Stjerneman",
|
||||
"email": "emil@stjerneman.com",
|
||||
"homepage": "https://stjerneman.com",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "http://drupal.org/project/linkit",
|
||||
"source": "http://cgit.drupalcode.org/linkit"
|
||||
},
|
||||
"license": "GPL-2.0+"
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
name: linkit_result_thumbnail
|
||||
label: 'Linkit result thumbnail'
|
||||
effects:
|
||||
2943df29-38ea-459c-ba1d-290489bb1807:
|
||||
uuid: 2943df29-38ea-459c-ba1d-290489bb1807
|
||||
id: image_scale_and_crop
|
||||
weight: 1
|
||||
data:
|
||||
width: 50
|
||||
height: 50
|
||||
@@ -0,0 +1,131 @@
|
||||
# Schema for the configuration files of the Linkit module.
|
||||
|
||||
linkit.linkit_profile.*:
|
||||
type: config_entity
|
||||
mapping:
|
||||
label:
|
||||
type: label
|
||||
label: 'Name'
|
||||
id:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
attributes:
|
||||
type: sequence
|
||||
sequence:
|
||||
type: linkit.attribute
|
||||
matchers:
|
||||
type: sequence
|
||||
sequence:
|
||||
type: linkit.matcher
|
||||
|
||||
linkit.attribute:
|
||||
type: mapping
|
||||
mapping:
|
||||
id:
|
||||
type: string
|
||||
settings:
|
||||
type: linkit.attribute.[%parent.id]
|
||||
weight:
|
||||
type: integer
|
||||
|
||||
linkit.attribute.*:
|
||||
type: mapping
|
||||
label: 'Attribute settings'
|
||||
|
||||
# Plugin \Drupal\linkit\Plugin\Linkit\Attribute\Target
|
||||
linkit.attribute.target:
|
||||
type: linkit.attribute
|
||||
mapping:
|
||||
widget_type:
|
||||
type: string
|
||||
|
||||
# Plugin \Drupal\linkit\Plugin\Linkit\Attribute\Title
|
||||
linkit.attribute.title:
|
||||
type: linkit.attribute
|
||||
mapping:
|
||||
automatic_title:
|
||||
type: boolean
|
||||
|
||||
linkit.matcher:
|
||||
type: mapping
|
||||
mapping:
|
||||
id:
|
||||
type: string
|
||||
uuid:
|
||||
type: string
|
||||
settings:
|
||||
type: linkit.matcher.[%parent.id]
|
||||
weight:
|
||||
type: integer
|
||||
|
||||
linkit.matcher.*:
|
||||
type: mapping
|
||||
label: 'Matcher settings'
|
||||
|
||||
linkit.matcher.entity:
|
||||
type: mapping
|
||||
mapping:
|
||||
result_description:
|
||||
type: string
|
||||
bundles:
|
||||
type: sequence
|
||||
sequence:
|
||||
type: string
|
||||
nullable: true
|
||||
group_by_bundle:
|
||||
type: boolean
|
||||
|
||||
linkit.matcher.entity:*:
|
||||
type: linkit.matcher.entity
|
||||
|
||||
# Plugin \Drupal\linkit\Plugin\Linkit\Matcher\FileMatcher
|
||||
linkit.matcher.entity:file:
|
||||
type: linkit.matcher.entity
|
||||
mapping:
|
||||
images:
|
||||
type: mapping
|
||||
mapping:
|
||||
show_dimensions:
|
||||
type: boolean
|
||||
show_thumbnail:
|
||||
type: boolean
|
||||
thumbnail_image_style:
|
||||
type: string
|
||||
nullable: true
|
||||
|
||||
# Plugin \Drupal\linkit\Plugin\Linkit\Matcher\NodeMatcher
|
||||
linkit.matcher.entity:node:
|
||||
type: linkit.matcher.entity
|
||||
mapping:
|
||||
include_unpublished:
|
||||
type: boolean
|
||||
|
||||
# Plugin \Drupal\linkit\Plugin\Linkit\Matcher\UserMatcher
|
||||
linkit.matcher.entity:user:
|
||||
type: linkit.matcher.entity
|
||||
mapping:
|
||||
roles:
|
||||
type: sequence
|
||||
sequence:
|
||||
type: string
|
||||
nullable: true
|
||||
include_blocked:
|
||||
type: boolean
|
||||
|
||||
# Third party settings on behalf of the imce module.
|
||||
linkit.linkit_profile.*.third_party.imce:
|
||||
type: mapping
|
||||
mapping:
|
||||
use:
|
||||
type: boolean
|
||||
scheme:
|
||||
type: string
|
||||
|
||||
ckeditor.plugin.linkit:
|
||||
type: mapping
|
||||
label: 'Profile dropdown'
|
||||
mapping:
|
||||
linkit_profile:
|
||||
type: string
|
||||
label: 'List of profiles'
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @file
|
||||
* Stylesheet for the Linkit module admin GUI.
|
||||
*/
|
||||
|
||||
.linkit-plugin-summary {
|
||||
float: left;
|
||||
font-size: .9em;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.linkit-matcher-add-form tbody td:first-child,
|
||||
.linkit-attribute-add-form tbody td:first-child {
|
||||
width: 1%;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @file
|
||||
* Stylesheet for the Linkit module.
|
||||
*/
|
||||
.js input.form-linkit-autocomplete {
|
||||
background-image: url(/core/misc/throbber-inactive.png);
|
||||
background-position: 100% center; /* LTR */
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.js[dir="rtl"] input.form-linkit-autocomplete {
|
||||
background-position: 0% center;
|
||||
}
|
||||
.js input.form-linkit-autocomplete.ui-autocomplete-loading {
|
||||
background-image: url(/core/misc/throbber-active.gif);
|
||||
background-position: 100% center; /* LTR */
|
||||
}
|
||||
.js[dir="rtl"] input.form-linkit-autocomplete.ui-autocomplete-loading {
|
||||
background-position: 0% center;
|
||||
}
|
||||
|
||||
/* Override the default jQuery UI theme */
|
||||
.linkit-ui-autocomplete {
|
||||
max-height: 330px;
|
||||
overflow: auto;
|
||||
}
|
||||
.linkit-ui-autocomplete.ui-widget {
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
.linkit-ui-autocomplete.ui-menu .ui-menu-item {
|
||||
list-style: none;
|
||||
padding: 5px 7px;
|
||||
}
|
||||
|
||||
.linkit-ui-autocomplete .ui-menu-item.ui-state-focus {
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #bfbfbf;
|
||||
background: #0075ba;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.linkit-result:not(:last-of-type) {
|
||||
border-bottom: 1px solid #bfbfbf;
|
||||
}
|
||||
|
||||
.linkit-result--group {
|
||||
padding: 3px;
|
||||
background-color: #e7e7e7;
|
||||
border-bottom: 1px solid #bfbfbf;
|
||||
text-align: center;
|
||||
font-size: 0.9em;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.linkit-result--title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.linkit-result--description {
|
||||
display: block;
|
||||
font-size: 0.9em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.linkit-result--description img {
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @file
|
||||
* Title attribute functions.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, document) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var fieldName = '[name="attributes[title]"]';
|
||||
|
||||
/**
|
||||
* Automatically populate the title attribute.
|
||||
*/
|
||||
$(document).bind('linkit.autocomplete.select', function (triggerEvent, event, ui) {
|
||||
if (ui.item.hasOwnProperty('title')) {
|
||||
$('form.linkit-editor-dialog-form').find(fieldName).val(ui.item.title);
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery, Drupal, document);
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* @file
|
||||
* Linkit Autocomplete based on jQuery UI.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, _, document) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var autocomplete;
|
||||
|
||||
/**
|
||||
* JQuery UI autocomplete source callback.
|
||||
*
|
||||
* @param {object} request
|
||||
* @param {function} response
|
||||
*/
|
||||
function sourceData(request, response) {
|
||||
var elementId = this.element.attr('id');
|
||||
|
||||
if (!(elementId in autocomplete.cache)) {
|
||||
autocomplete.cache[elementId] = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} suggestions
|
||||
*/
|
||||
function showSuggestions(suggestions) {
|
||||
response(suggestions.matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the data object into an array and update autocomplete results.
|
||||
*
|
||||
* @param {object} data
|
||||
*/
|
||||
function sourceCallbackHandler(data) {
|
||||
autocomplete.cache[elementId][term] = data;
|
||||
showSuggestions(data);
|
||||
}
|
||||
|
||||
// Get the desired term and construct the autocomplete URL for it.
|
||||
var term = request.term;
|
||||
|
||||
// Check if the term is already cached.
|
||||
if (autocomplete.cache[elementId].hasOwnProperty(term)) {
|
||||
showSuggestions(autocomplete.cache[elementId][term]);
|
||||
}
|
||||
else {
|
||||
var options = $.extend({success: sourceCallbackHandler, data: {q: term}}, autocomplete.ajax);
|
||||
$.ajax(this.element.attr('data-autocomplete-path'), options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an autocomplete select event.
|
||||
*
|
||||
* @param {jQuery.Event} event
|
||||
* @param {object} ui
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
function selectHandler(event, ui) {
|
||||
if (ui.item.hasOwnProperty('path')) {
|
||||
event.target.value = ui.item.path;
|
||||
}
|
||||
$(document).trigger('linkit.autocomplete.select', [event, ui]);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override jQuery UI _renderItem function to output HTML by default.
|
||||
*
|
||||
* @param {object} ul
|
||||
* The <ul> element that the newly created <li> element must be appended to.
|
||||
* @param {object} item
|
||||
*
|
||||
* @return {object}
|
||||
*/
|
||||
function renderItem(ul, item) {
|
||||
var $line = $('<li>').addClass('linkit-result');
|
||||
$line.append($('<span>').html(item.title).addClass('linkit-result--title'));
|
||||
|
||||
if (item.description !== null) {
|
||||
$line.append($('<span>').html(item.description).addClass('linkit-result--description'));
|
||||
}
|
||||
|
||||
return $line.appendTo(ul);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override jQuery UI _renderMenu function to handle groups.
|
||||
*
|
||||
* @param {object} ul
|
||||
* An empty <ul> element to use as the widget's menu.
|
||||
* @param {array} items
|
||||
* An Array of items that match the user typed term.
|
||||
*/
|
||||
function renderMenu(ul, items) {
|
||||
var self = this.element.autocomplete('instance');
|
||||
|
||||
var grouped_items = _.groupBy(items, function (item) {
|
||||
return item.hasOwnProperty('group') ? item.group : '';
|
||||
});
|
||||
|
||||
$.each(grouped_items, function (group, items) {
|
||||
if (group.length) {
|
||||
ul.append('<li class="linkit-result--group">' + group + '</li>');
|
||||
}
|
||||
|
||||
$.each(items, function (index, item) {
|
||||
self._renderItemData(ul, item);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the autocomplete behavior to all required fields.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.linkit_autocomplete = {
|
||||
attach: function (context) {
|
||||
// Act on textfields with the "form-autocomplete" class.
|
||||
var $autocomplete = $(context).find('input.form-linkit-autocomplete').once('linkit-autocomplete');
|
||||
if ($autocomplete.length) {
|
||||
$.widget('custom.autocomplete', $.ui.autocomplete, {
|
||||
_create: function () {
|
||||
this._super();
|
||||
this.widget().menu('option', 'items', '> :not(.linkit-result--group)');
|
||||
},
|
||||
_renderMenu: autocomplete.options.renderMenu,
|
||||
_renderItem: autocomplete.options.renderItem
|
||||
});
|
||||
|
||||
// Use jQuery UI Autocomplete on the textfield.
|
||||
$autocomplete.autocomplete(autocomplete.options);
|
||||
$autocomplete.autocomplete('widget').addClass('linkit-ui-autocomplete');
|
||||
}
|
||||
},
|
||||
detach: function (context, settings, trigger) {
|
||||
if (trigger === 'unload') {
|
||||
$(context).find('input.form-linkit-autocomplete')
|
||||
.removeOnce('linkit-autocomplete')
|
||||
.autocomplete('destroy');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Autocomplete object implementation.
|
||||
*/
|
||||
autocomplete = {
|
||||
cache: {},
|
||||
options: {
|
||||
source: sourceData,
|
||||
renderItem: renderItem,
|
||||
renderMenu: renderMenu,
|
||||
select: selectHandler,
|
||||
minLength: 1
|
||||
},
|
||||
ajax: {
|
||||
dataType: 'json'
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, _, document);
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file
|
||||
* IMCE integration for Linkit.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
*
|
||||
* Need to be in the global namespace, otherwise the IMCE window will not show
|
||||
* the 'select' button in the toolbar.
|
||||
*/
|
||||
var linkitImce = window.linkitImce = {};
|
||||
|
||||
/**
|
||||
* Drupal behavior to handle imce linkit integration.
|
||||
*/
|
||||
Drupal.behaviors.linkitImce = {
|
||||
attach: function (context, settings) {
|
||||
var $link = $(context).find('.linkit-imce-open').once('linkit-imce-open');
|
||||
if ($link.length) {
|
||||
$link.bind('click', function (event) {
|
||||
event.preventDefault();
|
||||
window.open($(this).attr('href'), '', 'width=760,height=560,resizable=1');
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for imce sendto operation.
|
||||
*/
|
||||
linkitImce.sendto = function (file, win) {
|
||||
var imce = win.imce;
|
||||
var items = imce.getSelection();
|
||||
|
||||
if (imce.countSelection() > 1) {
|
||||
imce.setMessage(Drupal.t('You can only select one file.'));
|
||||
return;
|
||||
}
|
||||
var path = imce.getConf('root_url') + '/' + imce.getItemPath(items[0]);
|
||||
$('[data-drupal-selector="edit-attributes-href"]').val(path);
|
||||
win.close();
|
||||
};
|
||||
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @file
|
||||
* Some basic behaviors and utility functions for Linkit.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
*/
|
||||
Drupal.linkit = {};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 539 B |
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* @file
|
||||
* Linkit plugin.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
|
||||
(function ($, Drupal, drupalSettings, CKEDITOR) {
|
||||
|
||||
'use strict';
|
||||
|
||||
// Alter the dialog settings to make a bigger dialog.
|
||||
// $(window).on('dialog:beforecreate', function (event, dialog, $element, settings) {
|
||||
// settings.dialogClass = settings.dialogClass.replace('ui-dialog--narrow', '');
|
||||
// settings.width = 700;
|
||||
// });
|
||||
|
||||
CKEDITOR.plugins.add('linkit', {
|
||||
init: function (editor) {
|
||||
// Add the commands for link and unlink.
|
||||
editor.addCommand('linkit', {
|
||||
allowedContent: new CKEDITOR.style({
|
||||
element: 'a',
|
||||
attributes: {
|
||||
'!href': '',
|
||||
// @TODO: Read these dynamically from the profile.
|
||||
'accesskey': '',
|
||||
'id': '',
|
||||
'rel': '',
|
||||
'target': '',
|
||||
'title': ''
|
||||
}
|
||||
}),
|
||||
requiredContent: new CKEDITOR.style({
|
||||
element: 'a',
|
||||
attributes: {
|
||||
href: ''
|
||||
}
|
||||
}),
|
||||
modes: {wysiwyg: 1},
|
||||
canUndo: true,
|
||||
exec: function (editor) {
|
||||
var linkElement = getSelectedLink(editor);
|
||||
var linkDOMElement = null;
|
||||
|
||||
// Set existing values based on selected element.
|
||||
var existingValues = {};
|
||||
if (linkElement && linkElement.$) {
|
||||
linkDOMElement = linkElement.$;
|
||||
|
||||
// Populate an array with the link's current attributes.
|
||||
var attribute = null;
|
||||
var attributeName;
|
||||
for (var attrIndex = 0; attrIndex < linkDOMElement.attributes.length; attrIndex++) {
|
||||
attribute = linkDOMElement.attributes.item(attrIndex);
|
||||
attributeName = attribute.nodeName.toLowerCase();
|
||||
// Don't consider data-cke-saved- attributes; they're just there
|
||||
// to work around browser quirks.
|
||||
if (attributeName.substring(0, 15) === 'data-cke-saved-') {
|
||||
continue;
|
||||
}
|
||||
// Store the value for this attribute, unless there's a
|
||||
// data-cke-saved- alternative for it, which will contain the
|
||||
// quirk-free, original value.
|
||||
existingValues[attributeName] = linkElement.data('cke-saved-' + attributeName) || attribute.nodeValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare a save callback to be used upon saving the dialog.
|
||||
var saveCallback = function (returnValues) {
|
||||
editor.fire('saveSnapshot');
|
||||
|
||||
// Create a new link element if needed.
|
||||
if (!linkElement && returnValues.attributes.href) {
|
||||
var selection = editor.getSelection();
|
||||
var range = selection.getRanges(1)[0];
|
||||
|
||||
// Use link URL as text with a collapsed cursor.
|
||||
if (range.collapsed) {
|
||||
// Shorten mailto URLs to just the email address.
|
||||
var text = new CKEDITOR.dom.text(returnValues.attributes.href.replace(/^mailto:/, ''), editor.document);
|
||||
range.insertNode(text);
|
||||
range.selectNodeContents(text);
|
||||
}
|
||||
|
||||
// Create the new link by applying a style to the new text.
|
||||
var style = new CKEDITOR.style({element: 'a', attributes: returnValues.attributes});
|
||||
style.type = CKEDITOR.STYLE_INLINE;
|
||||
style.applyToRange(range);
|
||||
range.select();
|
||||
|
||||
// Set the link so individual properties may be set below.
|
||||
linkElement = getSelectedLink(editor);
|
||||
}
|
||||
// Update the link properties.
|
||||
else if (linkElement) {
|
||||
for (var attrName in returnValues.attributes) {
|
||||
if (returnValues.attributes.hasOwnProperty(attrName)) {
|
||||
// Update the property if a value is specified.
|
||||
if (returnValues.attributes[attrName].length > 0) {
|
||||
var value = returnValues.attributes[attrName];
|
||||
linkElement.data('cke-saved-' + attrName, value);
|
||||
linkElement.setAttribute(attrName, value);
|
||||
}
|
||||
// Delete the property if set to an empty string.
|
||||
else {
|
||||
linkElement.removeAttribute(attrName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save snapshot for undo support.
|
||||
editor.fire('saveSnapshot');
|
||||
};
|
||||
// Drupal.t() will not work inside CKEditor plugins because CKEditor
|
||||
// loads the JavaScript file instead of Drupal. Pull translated
|
||||
// strings from the plugin settings that are translated server-side.
|
||||
var dialogSettings = {
|
||||
title: linkElement ? editor.config.linkit_dialogTitleAdd : editor.config.linkit_dialogTitleEdit,
|
||||
dialogClass: 'editor-linkit-dialog'
|
||||
};
|
||||
|
||||
// Open the dialog for the edit form.
|
||||
Drupal.ckeditor.openDialog(editor, Drupal.url('linkit/dialog/linkit/' + editor.config.drupal.format), existingValues, saveCallback, dialogSettings);
|
||||
}
|
||||
});
|
||||
|
||||
// CTRL + L.
|
||||
editor.setKeystroke(CKEDITOR.CTRL + 76, 'linkit');
|
||||
|
||||
// Add buttons.
|
||||
if (editor.ui.addButton) {
|
||||
editor.ui.addButton('Linkit', {
|
||||
label: Drupal.t('Link'),
|
||||
command: 'linkit',
|
||||
icon: this.path + '/linkit.png'
|
||||
});
|
||||
}
|
||||
|
||||
editor.on('doubleclick', function (evt) {
|
||||
var element = getSelectedLink(editor) || evt.data.element;
|
||||
|
||||
if (!element.isReadOnly()) {
|
||||
if (element.is('a')) {
|
||||
editor.getSelection().selectElement(element);
|
||||
editor.getCommand('linkit').exec();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If the "menu" plugin is loaded, register the menu items.
|
||||
if (editor.addMenuItems) {
|
||||
editor.addMenuItems({
|
||||
linkit: {
|
||||
label: Drupal.t('Edit Link'),
|
||||
command: 'linkit',
|
||||
group: 'link',
|
||||
order: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If the "contextmenu" plugin is loaded, register the listeners.
|
||||
if (editor.contextMenu) {
|
||||
editor.contextMenu.addListener(function (element, selection) {
|
||||
if (!element || element.isReadOnly()) {
|
||||
return null;
|
||||
}
|
||||
var anchor = getSelectedLink(editor);
|
||||
if (!anchor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var menu = {};
|
||||
if (anchor.getAttribute('href') && anchor.getChildCount()) {
|
||||
menu = {
|
||||
linkit: CKEDITOR.TRISTATE_OFF
|
||||
};
|
||||
}
|
||||
return menu;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the surrounding link element of current selection.
|
||||
*
|
||||
* The following selection will all return the link element.
|
||||
*
|
||||
* @example
|
||||
* <a href="#">li^nk</a>
|
||||
* <a href="#">[link]</a>
|
||||
* text[<a href="#">link]</a>
|
||||
* <a href="#">li[nk</a>]
|
||||
* [<b><a href="#">li]nk</a></b>]
|
||||
* [<a href="#"><b>li]nk</b></a>
|
||||
*
|
||||
* @param {CKEDITOR.editor} editor
|
||||
* The CKEditor editor object
|
||||
*
|
||||
* @return {?HTMLElement}
|
||||
* The selected link element, or null.
|
||||
*
|
||||
*/
|
||||
function getSelectedLink(editor) {
|
||||
var selection = editor.getSelection();
|
||||
var selectedElement = selection.getSelectedElement();
|
||||
if (selectedElement && selectedElement.is('a')) {
|
||||
return selectedElement;
|
||||
}
|
||||
|
||||
var range = selection.getRanges(true)[0];
|
||||
|
||||
if (range) {
|
||||
range.shrink(CKEDITOR.SHRINK_TEXT);
|
||||
return editor.elementPath(range.getCommonAncestor()).contains('a', 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
})(jQuery, Drupal, drupalSettings, CKEDITOR);
|
||||
@@ -0,0 +1,12 @@
|
||||
name: Linkit
|
||||
description: Linkit
|
||||
package: Custom
|
||||
type: module
|
||||
# core: 8.x
|
||||
configure: entity.linkit_profile.collection
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-03-21
|
||||
version: '8.x-4.3'
|
||||
core: '8.x'
|
||||
project: 'linkit'
|
||||
datestamp: 1490127367
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Installation functions for Linkit module.
|
||||
*/
|
||||
@@ -0,0 +1,43 @@
|
||||
linkit.admin:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/linkit.admin.theme.css: {}
|
||||
|
||||
linkit.base:
|
||||
version: VERSION
|
||||
js:
|
||||
js/linkit.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
|
||||
linkit.autocomplete:
|
||||
version: VERSION
|
||||
js:
|
||||
js/autocomplete.js: {}
|
||||
css:
|
||||
component:
|
||||
css/linkit.autocomplete.css: {}
|
||||
dependencies:
|
||||
- linkit/linkit.base
|
||||
- core/drupal.ajax
|
||||
- core/jquery.ui.autocomplete
|
||||
- core/underscore
|
||||
|
||||
linkit.attribute.title:
|
||||
version: VERSION
|
||||
js:
|
||||
js/attribute/title.js: {}
|
||||
dependencies:
|
||||
- linkit/linkit.base
|
||||
- linkit/linkit.autocomplete
|
||||
|
||||
linkit.imce:
|
||||
version: VERSION
|
||||
js:
|
||||
js/linkit.imce.js: {}
|
||||
dependencies:
|
||||
- core/jquery.once
|
||||
- linkit/linkit.base
|
||||
@@ -0,0 +1,18 @@
|
||||
linkit_profile.add_form:
|
||||
route_name: entity.linkit_profile.add_form
|
||||
title: 'Add profile'
|
||||
appears_on:
|
||||
- entity.linkit_profile.collection
|
||||
|
||||
linkit.matcher.add:
|
||||
route_name: linkit.matcher.add
|
||||
title: 'Add matcher'
|
||||
appears_on:
|
||||
- linkit.matchers
|
||||
|
||||
linkit.attribute.add:
|
||||
route_name: linkit.attribute.add
|
||||
title: 'Add attribute'
|
||||
appears_on:
|
||||
- linkit.attributes
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
entity.linkit_profile.collection:
|
||||
title: 'Linkit'
|
||||
parent: system.admin_config_content
|
||||
description: 'Manage Linkit profiles.'
|
||||
route_name: entity.linkit_profile.collection
|
||||
@@ -0,0 +1,15 @@
|
||||
entity.linkit_profile.edit_form:
|
||||
route_name: entity.linkit_profile.edit_form
|
||||
title: 'Edit'
|
||||
base_route: entity.linkit_profile.edit_form
|
||||
weight: 0
|
||||
linkit.matchers:
|
||||
route_name: linkit.matchers
|
||||
title: 'Manage matchers'
|
||||
base_route: entity.linkit_profile.edit_form
|
||||
weight: 5
|
||||
linkit.attributes:
|
||||
route_name: linkit.attributes
|
||||
title: 'Manage attributes'
|
||||
base_route: entity.linkit_profile.edit_form
|
||||
weight: 10
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
*/
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function linkit_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'entity.linkit_profile.attributes':
|
||||
return '<p>' . t('Attributes are HTML attributes that will be attached to the insert plugin.') . '</p>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter() for linkit_profile_form on behalf
|
||||
* of the 'imce' module.
|
||||
*
|
||||
* Adds IMCE settings to the form.
|
||||
*
|
||||
* @see imce_form_linkit_profile_form_builder()
|
||||
*/
|
||||
function imce_form_linkit_profile_form_alter(&$form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\Linkit\ProfileInterface $linkit_profile */
|
||||
$linkit_profile = $form_state->getFormObject()->getEntity();
|
||||
|
||||
$form['imce'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('IMCE integration'),
|
||||
'#group' => 'additional_settings',
|
||||
);
|
||||
|
||||
$form['imce']['imce_use'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable IMCE File Browser in the editor dialog.'),
|
||||
'#default_value' => $linkit_profile->getThirdPartySetting('imce', 'use', FALSE),
|
||||
);
|
||||
|
||||
$scheme_options = \Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::READ_VISIBLE);
|
||||
$form['imce']['imce_scheme'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Scheme'),
|
||||
'#options' => $scheme_options,
|
||||
'#default_value' => $linkit_profile->getThirdPartySetting('imce', 'scheme', 'public'),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="imce_use"]' => ['checked' => TRUE],
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
$form['#entity_builders'][] = 'imce_form_linkit_profile_form_builder';
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity builder for the linkit profile form with imce options.
|
||||
*
|
||||
* @see imce_form_linkit_profile_form_alter().
|
||||
*/
|
||||
function imce_form_linkit_profile_form_builder($entity_type, ProfileInterface $linkit_profile, &$form, FormStateInterface $form_state) {
|
||||
$linkit_profile->setThirdPartySetting('imce', 'use', $form_state->getValue('imce_use'));
|
||||
$linkit_profile->setThirdPartySetting('imce', 'scheme', $form_state->getValue('imce_scheme'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter() for linkit_editor_dialog_form on
|
||||
* behalf of the 'imce' module.
|
||||
*
|
||||
* Adds a button to open the imce file browser if it is enabled.
|
||||
*/
|
||||
function imce_form_linkit_editor_dialog_form_alter(&$form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\Linkit\ProfileInterface $linkit_profile */
|
||||
$linkit_profile = $form_state->getFormObject()->getLinkitProfile();
|
||||
|
||||
if($linkit_profile->getThirdPartySetting('imce', 'use', FALSE)) {
|
||||
$form['imce-link'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => t('Open IMCE file browser'),
|
||||
'#url' => Url::fromRoute('imce.page', [
|
||||
'scheme' => $linkit_profile->getThirdPartySetting('imce', 'scheme', 'public'),
|
||||
]),
|
||||
'#options' => array(
|
||||
'query' => array(
|
||||
'sendto' => 'linkitImce.sendto',
|
||||
),
|
||||
),
|
||||
'#attributes' => [
|
||||
'class' => ['linkit-imce-open'],
|
||||
],
|
||||
'#attached' => [
|
||||
'library' => [
|
||||
'linkit/linkit.imce'
|
||||
],
|
||||
],
|
||||
'#weight' => 1,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
administer linkit profiles:
|
||||
title: 'Administer linkit profiles'
|
||||
@@ -0,0 +1,111 @@
|
||||
entity.linkit_profile.collection:
|
||||
path: '/admin/config/content/linkit'
|
||||
defaults:
|
||||
_entity_list: 'linkit_profile'
|
||||
_title: 'Linkit profiles'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
entity.linkit_profile.add_form:
|
||||
path: '/admin/config/content/linkit/add'
|
||||
defaults:
|
||||
_entity_form: linkit_profile.add
|
||||
_title: 'Add profile'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
entity.linkit_profile.edit_form:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}'
|
||||
defaults:
|
||||
_entity_form: linkit_profile.edit
|
||||
_title_callback: 'Drupal\linkit\Controller\LinkitController::profileTitle'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
entity.linkit_profile.delete_form:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}/delete'
|
||||
defaults:
|
||||
_entity_form: linkit_profile.delete
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
linkit.attributes:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}/attributes'
|
||||
defaults:
|
||||
_form: '\Drupal\linkit\Form\Attribute\OverviewForm'
|
||||
_title: 'Manage attributes'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
linkit.attribute.add:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}/attributes/add'
|
||||
defaults:
|
||||
_form: '\Drupal\linkit\Form\Attribute\AddForm'
|
||||
_title: 'Add attribute'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
linkit.attribute.delete:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}/attributes/{plugin_instance_id}/delete'
|
||||
defaults:
|
||||
_form: '\Drupal\linkit\Form\Attribute\DeleteForm'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
linkit.attribute.edit:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}/attributes/{plugin_instance_id}'
|
||||
defaults:
|
||||
_form: '\Drupal\linkit\Form\Attribute\EditForm'
|
||||
_title_callback: 'Drupal\linkit\Controller\LinkitController::attributeTitle'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
linkit.matchers:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}/matchers'
|
||||
defaults:
|
||||
_form: '\Drupal\linkit\Form\Matcher\OverviewForm'
|
||||
_title: 'Manage matchers'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
linkit.matcher.add:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}/matchers/add'
|
||||
defaults:
|
||||
_form: '\Drupal\linkit\Form\Matcher\AddForm'
|
||||
_title: 'Add matcher'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
linkit.matcher.delete:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}/matchers/{plugin_instance_id}/delete'
|
||||
defaults:
|
||||
_form: '\Drupal\linkit\Form\Matcher\DeleteForm'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
linkit.matcher.edit:
|
||||
path: '/admin/config/content/linkit/manage/{linkit_profile}/matchers/{plugin_instance_id}'
|
||||
defaults:
|
||||
_form: '\Drupal\linkit\Form\Matcher\EditForm'
|
||||
_title_callback: 'Drupal\linkit\Controller\LinkitController::matcherTitle'
|
||||
requirements:
|
||||
_permission: 'administer linkit profiles'
|
||||
|
||||
linkit.linkit_dialog:
|
||||
path: '/linkit/dialog/linkit/{filter_format}'
|
||||
defaults:
|
||||
_form: '\Drupal\linkit\Form\LinkitEditorDialog'
|
||||
_title: 'Add link'
|
||||
requirements:
|
||||
_entity_access: 'filter_format.use'
|
||||
options:
|
||||
_theme: ajax_base_page
|
||||
|
||||
linkit.autocomplete:
|
||||
path: '/linkit/autocomplete/{linkit_profile_id}'
|
||||
defaults:
|
||||
_controller: '\Drupal\linkit\Controller\AutocompleteController::autocomplete'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
||||
options:
|
||||
_theme: ajax_base_page
|
||||
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
plugin.manager.linkit.attribute:
|
||||
class: Drupal\linkit\AttributeManager
|
||||
parent: default_plugin_manager
|
||||
|
||||
plugin.manager.linkit.matcher:
|
||||
class: Drupal\linkit\MatcherManager
|
||||
parent: default_plugin_manager
|
||||
|
||||
linkit.result_manager:
|
||||
class: Drupal\linkit\ResultManager
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Annotation\Attribute.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines an attribute annotation object.
|
||||
*
|
||||
* Plugin Namespace: Plugin\Linkit\Attribute
|
||||
*
|
||||
* For a working example, see \Drupal\linkit\Plugin\Linkit\Attribute\Title
|
||||
*
|
||||
* @see \Drupal\linkit\AttributeInterface
|
||||
* @see \Drupal\linkit\AttributeBase
|
||||
* @see \Drupal\linkit\AttributeManager
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Attribute extends Plugin {
|
||||
|
||||
/**
|
||||
* The plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the attribute.
|
||||
*
|
||||
* The string should be wrapped in a @Translation().
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* The real HTML attribute name for this attribute.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $html_name;
|
||||
|
||||
/**
|
||||
* A brief description of the attribute.
|
||||
*
|
||||
* This will be shown when adding or configuring a profile.
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation (optional)
|
||||
*/
|
||||
public $description = '';
|
||||
|
||||
/**
|
||||
* A default weight for the attribute.
|
||||
*
|
||||
* @var int (optional)
|
||||
*/
|
||||
public $weight = 0;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Annotation\Matcher.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines a matcher annotation object.
|
||||
*
|
||||
* Plugin Namespace: Plugin\Linkit\Matcher
|
||||
*
|
||||
* @see \Drupal\linkit\MatcherInterface
|
||||
* @see \Drupal\linkit\MatcherBase
|
||||
* @see \Drupal\linkit\MatcherManager
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class Matcher extends Plugin {
|
||||
|
||||
/**
|
||||
* The plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the matcher.
|
||||
*
|
||||
* The string should be wrapped in a @Translation().
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* The entity type that is managed by this matcher.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\AttributeBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
|
||||
/**
|
||||
* Provides a base class for attribute plugins.
|
||||
*
|
||||
* @see \Drupal\linkit\Annotation\Attribute
|
||||
* @see \Drupal\linkit\AttributeBase
|
||||
* @see \Drupal\linkit\AttributeManager
|
||||
* @see plugin_api
|
||||
*/
|
||||
abstract class AttributeBase extends PluginBase implements AttributeInterface {
|
||||
|
||||
/**
|
||||
* The weight of the attribute compared to others in an attribute collection.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $weight = 0;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->setConfiguration($configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfiguration() {
|
||||
return [
|
||||
'id' => $this->getPluginId(),
|
||||
'weight' => $this->weight,
|
||||
'settings' => $this->configuration,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConfiguration(array $configuration) {
|
||||
$configuration += [
|
||||
'weight' => '0',
|
||||
'settings' => [],
|
||||
];
|
||||
$this->configuration = $configuration['settings'] + $this->defaultConfiguration();
|
||||
$this->weight = $configuration['weight'];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->pluginDefinition['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHtmlName() {
|
||||
return $this->pluginDefinition['html_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->pluginDefinition['description'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setWeight($weight) {
|
||||
$this->weight = $weight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\AttributeCollection.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Plugin\DefaultLazyPluginCollection;
|
||||
|
||||
/**
|
||||
* A collection of attribute plugins.
|
||||
*/
|
||||
class AttributeCollection extends DefaultLazyPluginCollection {
|
||||
|
||||
/**
|
||||
* All possible attribute IDs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $definitions;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Drupal\linkit\AttributeInterface
|
||||
*/
|
||||
public function &get($instance_id) {
|
||||
return parent::get($instance_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sortHelper($aID, $bID) {
|
||||
$a_weight = $this->get($aID)->getWeight();
|
||||
$b_weight = $this->get($bID)->getWeight();
|
||||
if ($a_weight == $b_weight) {
|
||||
return strnatcasecmp($this->get($aID)->getLabel(), $this->get($bID)->getLabel());
|
||||
}
|
||||
|
||||
return ($a_weight < $b_weight) ? -1 : 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\AttributeInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
|
||||
/**
|
||||
* Defines the interface for attributes plugins.
|
||||
*
|
||||
* @see \Drupal\linkit\Annotation\Attribute
|
||||
* @see \Drupal\linkit\AttributeBase
|
||||
* @see \Drupal\linkit\AttributeManager
|
||||
* @see plugin_api
|
||||
*/
|
||||
interface AttributeInterface extends PluginInspectionInterface, ConfigurablePluginInterface {
|
||||
|
||||
/**
|
||||
* Returns the attribute label.
|
||||
*
|
||||
* @return string
|
||||
* The attribute label.
|
||||
*/
|
||||
public function getLabel();
|
||||
|
||||
/**
|
||||
* Returns the attribute description.
|
||||
*
|
||||
* @return string
|
||||
* The attribute description.
|
||||
*/
|
||||
public function getDescription();
|
||||
|
||||
/**
|
||||
* Returns the attribute html name. This is the name of the attribute
|
||||
* that will be inserted in the <code><a></code> tag.
|
||||
*
|
||||
* @return string
|
||||
* The attribute html name.
|
||||
*/
|
||||
public function getHtmlName();
|
||||
|
||||
/**
|
||||
* Returns the weight of the attribute.
|
||||
*
|
||||
* @return int|string
|
||||
* Either the integer weight of the attribute or an empty string.
|
||||
*/
|
||||
public function getWeight();
|
||||
|
||||
/**
|
||||
* Sets the weight for this attribute.
|
||||
*
|
||||
* @param int $weight
|
||||
* The weight for this attribute.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWeight($weight);
|
||||
|
||||
/**
|
||||
* The form element structure for this attribute to be used in the dialog.
|
||||
*
|
||||
* @param mixed $default_value
|
||||
* The default value for the element. Used when editing an attribute in the
|
||||
* dialog.
|
||||
*
|
||||
* @return array
|
||||
* The form element.
|
||||
*/
|
||||
public function buildFormElement($default_value);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\AttributeManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
|
||||
/**
|
||||
* Manages attributes.
|
||||
*/
|
||||
class AttributeManager extends DefaultPluginManager {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct('Plugin/Linkit/Attribute', $namespaces, $module_handler, 'Drupal\linkit\AttributeInterface', 'Drupal\linkit\Annotation\Attribute');
|
||||
|
||||
$this->alterInfo('linkit_attribute');
|
||||
$this->setCacheBackend($cache_backend, 'linkit_attributes');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\ConfigurableAttributeBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
/**
|
||||
* Provides a base class for configurable attributes.
|
||||
*
|
||||
* @see plugin_api
|
||||
*/
|
||||
abstract class ConfigurableAttributeBase extends AttributeBase implements ConfigurableAttributeInterface {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\ConfigurableAttributeInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
|
||||
/**
|
||||
* Defines the interface for configurable attributes.
|
||||
*
|
||||
* @see \Drupal\linkit\Annotation\Attribute
|
||||
* @see \Drupal\linkit\ConfigurableAttributeBase
|
||||
* @see \Drupal\linkit\AttributeInterface
|
||||
* @see \Drupal\linkit\AttributeBase
|
||||
* @see \Drupal\linkit\AttributeManager
|
||||
* @see plugin_api
|
||||
*/
|
||||
interface ConfigurableAttributeInterface extends AttributeInterface, PluginFormInterface {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\ConfigurableMatcherBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
/**
|
||||
* Provides a base class for configurable matchers.
|
||||
*
|
||||
* @see plugin_api
|
||||
*/
|
||||
abstract class ConfigurableMatcherBase extends MatcherBase implements ConfigurableMatcherInterface {
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\ConfigurableMatcherInterface.
|
||||
*/
|
||||
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
|
||||
/**
|
||||
* Defines the interface for configurable matchers.
|
||||
*
|
||||
* @see \Drupal\linkit\Annotation\Matcher
|
||||
* @see \Drupal\linkit\ConfigurableMatcherBase
|
||||
* @see \Drupal\linkit\MatcherInterface
|
||||
* @see \Drupal\linkit\MatcherBase
|
||||
* @see \Drupal\linkit\MatcherManager
|
||||
* @see plugin_api
|
||||
*/
|
||||
interface ConfigurableMatcherInterface extends MatcherInterface, PluginFormInterface {
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Controller\AutocompleteController.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\linkit\ResultManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class AutocompleteController implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The linkit profile storage service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $linkitProfileStorage;
|
||||
|
||||
/**
|
||||
* The result manager.
|
||||
*
|
||||
* @var \Drupal\linkit\ResultManager
|
||||
*/
|
||||
protected $resultManager;
|
||||
|
||||
/**
|
||||
* The linkit profile.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* Constructs a EntityAutocompleteController object.
|
||||
*
|
||||
* @param ResultManager $resultManager
|
||||
* The result service.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $linkit_profile_storage
|
||||
* The linkit profile storage service.
|
||||
*/
|
||||
public function __construct(EntityStorageInterface $linkit_profile_storage, ResultManager $resultManager) {
|
||||
$this->linkitProfileStorage = $linkit_profile_storage;
|
||||
$this->resultManager = $resultManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager')->getStorage('linkit_profile'),
|
||||
$container->get('linkit.result_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback for linkit search autocompletion.
|
||||
*
|
||||
* Like other autocomplete functions, this function inspects the 'q' query
|
||||
* parameter for the string to use to search for suggestions.
|
||||
*
|
||||
* @param Request $request
|
||||
* The request.
|
||||
* @param $linkit_profile_id
|
||||
* The linkit profile id.
|
||||
* @return JsonResponse
|
||||
* A JSON response containing the autocomplete suggestions.
|
||||
*/
|
||||
public function autocomplete(Request $request, $linkit_profile_id) {
|
||||
$this->linkitProfile = $this->linkitProfileStorage->load($linkit_profile_id);
|
||||
$string = Unicode::strtolower($request->query->get('q'));
|
||||
|
||||
$matches = $this->resultManager->getResults($this->linkitProfile, $string);
|
||||
|
||||
$json_object = new \stdClass();
|
||||
$json_object->matches = $matches;
|
||||
|
||||
return new JsonResponse($json_object);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Controller\LinkitController.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Controller;
|
||||
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
|
||||
/**
|
||||
* Provides route responses for linkit.module.
|
||||
*/
|
||||
class LinkitController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
*
|
||||
* @param \Drupal\linkit\ProfileInterface $linkit_profile
|
||||
* The profile.
|
||||
*
|
||||
* @return string
|
||||
* The profile label as a render array.
|
||||
*/
|
||||
public function profileTitle(ProfileInterface $linkit_profile) {
|
||||
return $this->t('Edit %label profile', array('%label' => $linkit_profile->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
*
|
||||
* @param \Drupal\linkit\ProfileInterface $linkit_profile
|
||||
* The profile.
|
||||
* @param string $plugin_instance_id
|
||||
* The plugin instance id.
|
||||
*
|
||||
* @return string
|
||||
* The title for the matcher edit form.
|
||||
*/
|
||||
public function matcherTitle(ProfileInterface $linkit_profile, $plugin_instance_id) {
|
||||
/** @var \Drupal\linkit\MatcherInterface $matcher */
|
||||
$matcher = $linkit_profile->getMatcher($plugin_instance_id);
|
||||
return $this->t('Edit %label matcher', array('%label' => $matcher->getLabel()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Route title callback.
|
||||
*
|
||||
* @param \Drupal\linkit\ProfileInterface $linkit_profile
|
||||
* The profile.
|
||||
* @param string $plugin_instance_id
|
||||
* The plugin instance id.
|
||||
*
|
||||
* @return string
|
||||
* The title for the attribute edit form.
|
||||
*/
|
||||
public function attributeTitle(ProfileInterface $linkit_profile, $plugin_instance_id) {
|
||||
/** @var \Drupal\linkit\AttributeInterface $attribute */
|
||||
$attribute = $linkit_profile->getAttribute($plugin_instance_id);
|
||||
return $this->t('Edit %label attribute', array('%label' => $attribute->getLabel()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Element\Linkit.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\Element\FormElement;
|
||||
use Drupal\Core\Render\Element\Textfield;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides a form element for linkit.
|
||||
*
|
||||
* @FormElement("linkit")
|
||||
*/
|
||||
class Linkit extends FormElement {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return array(
|
||||
'#input' => TRUE,
|
||||
'#size' => 60,
|
||||
'#process' => array(
|
||||
array($class, 'processLinkitAutocomplete'),
|
||||
array($class, 'processGroup'),
|
||||
),
|
||||
'#pre_render' => array(
|
||||
array($class, 'preRenderLinkitElement'),
|
||||
array($class, 'preRenderGroup'),
|
||||
),
|
||||
'#theme' => 'input__textfield',
|
||||
'#theme_wrappers' => array('form_element'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
return Textfield::valueCallback($element, $input, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds linkit custom autocomplete functionality to elements.
|
||||
*
|
||||
* Instead of using the core autocomplete, we use our own.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Drupal\Core\Render\Element\FormElement::processAutocomplete
|
||||
*/
|
||||
public static function processLinkitAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) {
|
||||
$url = NULL;
|
||||
$access = FALSE;
|
||||
|
||||
if (!empty($element['#autocomplete_route_name'])) {
|
||||
$parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : array();
|
||||
$url = Url::fromRoute($element['#autocomplete_route_name'], $parameters)->toString(TRUE);
|
||||
/** @var \Drupal\Core\Access\AccessManagerInterface $access_manager */
|
||||
$access_manager = \Drupal::service('access_manager');
|
||||
$access = $access_manager->checkNamedRoute($element['#autocomplete_route_name'], $parameters, \Drupal::currentUser(), TRUE);
|
||||
}
|
||||
|
||||
if ($access) {
|
||||
$metadata = BubbleableMetadata::createFromRenderArray($element);
|
||||
if ($access->isAllowed()) {
|
||||
$element['#attributes']['class'][] = 'form-linkit-autocomplete';
|
||||
$metadata->addAttachments(['library' => ['linkit/linkit.autocomplete']]);
|
||||
// Provide a data attribute for the JavaScript behavior to bind to.
|
||||
$element['#attributes']['data-autocomplete-path'] = $url->getGeneratedUrl();
|
||||
$metadata = $metadata->merge($url);
|
||||
}
|
||||
$metadata
|
||||
->merge(BubbleableMetadata::createFromObject($access))
|
||||
->applyTo($element);
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a #type 'linkit' render element for input.html.twig.
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties of the element.
|
||||
* Properties used: #title, #value, #description, #size, #attributes.
|
||||
*
|
||||
* @return array
|
||||
* The $element with prepared variables ready for input.html.twig.
|
||||
*/
|
||||
public static function preRenderLinkitElement($element) {
|
||||
$element['#attributes']['type'] = 'text';
|
||||
Element::setAttributes($element, array('id', 'name', 'value', 'size'));
|
||||
static::setAttributes($element, array('form-text'));
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Entity\Profile.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
|
||||
use Drupal\linkit\AttributeCollection;
|
||||
use Drupal\linkit\MatcherCollection;
|
||||
use Drupal\linkit\MatcherInterface;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
|
||||
/**
|
||||
* Defines the linkit profile entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "linkit_profile",
|
||||
* label = @Translation("Linkit profile"),
|
||||
* handlers = {
|
||||
* "list_builder" = "Drupal\linkit\ProfileListBuilder",
|
||||
* "form" = {
|
||||
* "add" = "Drupal\linkit\Form\Profile\AddForm",
|
||||
* "edit" = "Drupal\linkit\Form\Profile\EditForm",
|
||||
* "delete" = "Drupal\Core\Entity\EntityDeleteForm"
|
||||
* }
|
||||
* },
|
||||
* admin_permission = "administer linkit profiles",
|
||||
* config_prefix = "linkit_profile",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label"
|
||||
* },
|
||||
* links = {
|
||||
* "collection" = "/admin/config/content/linkit",
|
||||
* "edit-form" = "/admin/config/content/linkit/manage/{linkit_profile}",
|
||||
* "delete-form" = "/admin/config/content/linkit/manage/{linkit_profile}/delete"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "label",
|
||||
* "description",
|
||||
* "attributes",
|
||||
* "matchers"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Profile extends ConfigEntityBase implements ProfileInterface, EntityWithPluginCollectionInterface {
|
||||
|
||||
/**
|
||||
* The ID of this profile.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The human-readable label of this profile.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* Description of this profile.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* Configured attribute for this profile.
|
||||
*
|
||||
* An associative array of attribute assigned to the profile, keyed by the
|
||||
* attribute id of each attribute and using the properties:
|
||||
* - id: The plugin ID of the attribute instance.
|
||||
* - status: (optional) A Boolean indicating whether the attribute is enabled
|
||||
* in the profile. Defaults to FALSE.
|
||||
* - weight: (optional) The weight of the attribute in the profile.
|
||||
* Defaults to 0.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* Holds the collection of attributes that are attached to this profile.
|
||||
*
|
||||
* @var \Drupal\linkit\AttributeCollection
|
||||
*/
|
||||
protected $attributeCollection;
|
||||
|
||||
/**
|
||||
* Configured matchers for this profile.
|
||||
*
|
||||
* An associative array of matchers assigned to the profile, keyed by the
|
||||
* matcher ID of each matcher and using the properties:
|
||||
* - id: The plugin ID of the matchers instance.
|
||||
* - status: (optional) A Boolean indicating whether the matchers is enabled
|
||||
* in the profile. Defaults to FALSE.
|
||||
* - weight: (optional) The weight of the matchers in the profile.
|
||||
* Defaults to 0.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $matchers = [];
|
||||
|
||||
/**
|
||||
* Holds the collection of matchers that are attached to this profile.
|
||||
*
|
||||
* @var \Drupal\linkit\MatcherCollection
|
||||
*/
|
||||
protected $matcherCollection;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->get('description');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDescription($description) {
|
||||
$this->set('description', trim($description));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttribute($attribute_id) {
|
||||
return $this->getAttributes()->get($attribute_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAttributes() {
|
||||
if (!$this->attributeCollection) {
|
||||
$this->attributeCollection = new AttributeCollection($this->getAttributeManager(), $this->attributes);
|
||||
$this->attributeCollection->sort();
|
||||
}
|
||||
return $this->attributeCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addAttribute(array $configuration) {
|
||||
$this->getAttributes()->addInstanceId($configuration['id'], $configuration);
|
||||
return $configuration['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeAttribute($attribute_id) {
|
||||
unset($this->attributes[$attribute_id]);
|
||||
$this->getAttributes()->removeInstanceId($attribute_id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAttributeConfig($attribute_id, array $configuration) {
|
||||
$this->attributes[$attribute_id] = $configuration;
|
||||
$this->getAttributes()->setInstanceConfiguration($attribute_id, $configuration);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMatcher($instance_id) {
|
||||
return $this->getMatchers()->get($instance_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMatchers() {
|
||||
if (!$this->matcherCollection) {
|
||||
$this->matcherCollection = new MatcherCollection($this->getMatcherManager(), $this->matchers);
|
||||
$this->matcherCollection->sort();
|
||||
}
|
||||
return $this->matcherCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addMatcher(array $configuration) {
|
||||
$configuration['uuid'] = $this->uuidGenerator()->generate();
|
||||
$this->getMatchers()->addInstanceId($configuration['uuid'], $configuration);
|
||||
return $configuration['uuid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeMatcher(MatcherInterface $matcher) {
|
||||
$this->getMatchers()->removeInstanceId($matcher->getUuid());
|
||||
$this->save();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMatcherConfig($instance_id, array $configuration) {
|
||||
$this->matchers[$instance_id] = $configuration;
|
||||
$this->getMatchers()->setInstanceConfiguration($instance_id, $configuration);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPluginCollections() {
|
||||
return array(
|
||||
'attributes' => $this->getAttributes(),
|
||||
'matchers' => $this->getMatchers(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attribute manager.
|
||||
*
|
||||
* @return \Drupal\Component\Plugin\PluginManagerInterface
|
||||
* The attribute manager.
|
||||
*/
|
||||
protected function getAttributeManager() {
|
||||
return \Drupal::service('plugin.manager.linkit.attribute');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matcher manager.
|
||||
*
|
||||
* @return \Drupal\Component\Plugin\PluginManagerInterface
|
||||
* The matcher manager.
|
||||
*/
|
||||
protected function getMatcherManager() {
|
||||
return \Drupal::service('plugin.manager.linkit.matcher');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Attribute\AddForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Attribute;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\linkit\AttributeManager;
|
||||
use Drupal\linkit\ConfigurableAttributeInterface;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form to apply attributes to a profile.
|
||||
*/
|
||||
class AddForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The profiles to which the attributes will be applied.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* The attribute manager.
|
||||
*
|
||||
* @var \Drupal\linkit\AttributeManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a new AddForm.
|
||||
*
|
||||
* @param \Drupal\linkit\AttributeManager $manager
|
||||
* The attribute manager.
|
||||
*/
|
||||
public function __construct(AttributeManager $manager) {
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.linkit.attribute')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return "linkit_attribute_add_form";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) {
|
||||
$this->linkitProfile = $linkit_profile;
|
||||
|
||||
$form['#attached']['library'][] = 'linkit/linkit.admin';
|
||||
$header = [
|
||||
'label' => $this->t('Attributes'),
|
||||
'description' => $this->t('Description'),
|
||||
];
|
||||
|
||||
$form['plugin'] = [
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $header,
|
||||
'#options' => $this->buildRows(),
|
||||
'#empty' => $this->t('No attributes available.'),
|
||||
'#multiple' => FALSE,
|
||||
];
|
||||
|
||||
$form['actions'] = ['#type' => 'actions'];
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save and continue'),
|
||||
'#submit' => ['::submitForm'],
|
||||
'#tableselect' => TRUE,
|
||||
'#button_type' => 'primary',
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
if (empty($form_state->getValue('plugin'))) {
|
||||
$form_state->setErrorByName('plugin', $this->t('No attribute selected.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->cleanValues();
|
||||
|
||||
/** @var \Drupal\linkit\AttributeInterface $plugin */
|
||||
$plugin = $this->manager->createInstance($form_state->getValue('plugin'));
|
||||
$plugin_id = $this->linkitProfile->addAttribute($plugin->getConfiguration());
|
||||
$this->linkitProfile->save();
|
||||
|
||||
$this->logger('linkit')->notice('Added %label attribute to the @profile profile.', [
|
||||
'%label' => $this->linkitProfile->getAttribute($plugin_id)->getLabel(),
|
||||
'@profile' => $this->linkitProfile->label(),
|
||||
]);
|
||||
|
||||
$is_configurable = $plugin instanceof ConfigurableAttributeInterface;
|
||||
if ($is_configurable) {
|
||||
$form_state->setRedirect('linkit.attribute.edit', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $plugin_id,
|
||||
]);
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('Added %label attribute.', ['%label' => $plugin->getLabel()]));
|
||||
|
||||
$form_state->setRedirect('linkit.attributes', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the table rows.
|
||||
*
|
||||
* Only attributes that is not already applied to the profile are shown.
|
||||
*
|
||||
* @return array
|
||||
* An array of table rows.
|
||||
*/
|
||||
private function buildRows() {
|
||||
$rows = [];
|
||||
|
||||
$applied_plugins = $this->linkitProfile->getAttributes()->getConfiguration();
|
||||
$all_plugins = $this->manager->getDefinitions();
|
||||
uasort($all_plugins, function ($a, $b) {
|
||||
return strnatcasecmp($a['label'], $b['label']);
|
||||
});
|
||||
foreach (array_diff_key($all_plugins, $applied_plugins) as $definition) {
|
||||
/** @var \Drupal\linkit\AttributeInterface $plugin */
|
||||
$plugin = $this->manager->createInstance($definition['id']);
|
||||
|
||||
$row = [
|
||||
'label' => (string) $plugin->getLabel(),
|
||||
'description' => (string) $plugin->getDescription(),
|
||||
];
|
||||
|
||||
$rows[$plugin->getPluginId()] = $row;
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Attribute\DeleteForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Attribute;
|
||||
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides a form to remove an attribute from a profile.
|
||||
*/
|
||||
class DeleteForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The profiles that the attribute is applied to.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* The attribute to be removed from the profile.
|
||||
*
|
||||
* @var \Drupal\linkit\AttributeInterface
|
||||
*/
|
||||
protected $linkitAttribute;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete the @plugin attribute from the %profile profile?', ['%profile' => $this->linkitProfile->label(), '@plugin' => $this->linkitAttribute->getLabel()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return Url::fromRoute('linkit.attributes', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'linkit_attribute_delete_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL, $plugin_instance_id = NULL) {
|
||||
$this->linkitProfile = $linkit_profile;
|
||||
|
||||
if (!$this->linkitProfile->getAttributes()->has($plugin_instance_id)) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$this->linkitAttribute = $this->linkitProfile->getAttribute($plugin_instance_id);
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
if ($this->linkitProfile->getAttributes()->has($this->linkitAttribute->getPluginId())) {
|
||||
$this->linkitProfile->removeAttribute($this->linkitAttribute->getPluginId());
|
||||
$this->linkitProfile->save();
|
||||
|
||||
drupal_set_message($this->t('The attribute %label has been deleted.', ['%label' => $this->linkitAttribute->getLabel()]));
|
||||
$this->logger('linkit')->notice('The attribute %label has been deleted in the @profile profile.', [
|
||||
'%label' => $this->linkitAttribute->getLabel(),
|
||||
'@profile' => $this->linkitProfile->label(),
|
||||
]);
|
||||
}
|
||||
|
||||
$form_state->setRedirect('linkit.attributes', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Attribute\EditForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Attribute;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
|
||||
/**
|
||||
* Provides an edit form for attributes.
|
||||
*/
|
||||
class EditForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The profiles to which the attributes will be applied.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* The attribute to edit.
|
||||
*
|
||||
* @var \Drupal\linkit\ConfigurableAttributeInterface
|
||||
*/
|
||||
protected $linkitAttribute;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'linkit_attribute_edit_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL, $plugin_instance_id = NULL) {
|
||||
$this->linkitProfile = $linkit_profile;
|
||||
$this->linkitAttribute = $this->linkitProfile->getAttribute($plugin_instance_id);
|
||||
$form['data'] = [
|
||||
'#tree' => true,
|
||||
];
|
||||
|
||||
$form['data'] += $this->linkitAttribute->buildConfigurationForm($form, $form_state);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save changes'),
|
||||
'#submit' => array('::submitForm'),
|
||||
'#button_type' => 'primary',
|
||||
);
|
||||
$form['actions']['delete'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Delete'),
|
||||
'#url' => Url::fromRoute('linkit.attribute.delete', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $this->linkitAttribute->getPluginId(),
|
||||
]),
|
||||
'#attributes' => [
|
||||
'class' => ['button', 'button--danger'],
|
||||
],
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->cleanValues();
|
||||
$plugin_data = (new FormState())->setValues($form_state->getValue('data'));
|
||||
$this->linkitAttribute->submitConfigurationForm($form, $plugin_data);
|
||||
$this->linkitProfile->save();
|
||||
|
||||
drupal_set_message($this->t('Saved %label configuration.', array('%label' => $this->linkitAttribute->getLabel())));
|
||||
$this->logger('linkit')->notice('The attribute %label has been updated in the @profile profile.', [
|
||||
'%label' => $this->linkitAttribute->getLabel(),
|
||||
'@profile' => $this->linkitProfile->label(),
|
||||
]);
|
||||
|
||||
$form_state->setRedirect('linkit.attributes', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Attribute\OverviewForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Attribute;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\AttributeManager;
|
||||
use Drupal\linkit\ConfigurableAttributeInterface;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides an overview form for attribute on a profile.
|
||||
*/
|
||||
class OverviewForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The profiles to which the attributes are applied to.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
private $linkitProfile;
|
||||
|
||||
/**
|
||||
* The attribute manager.
|
||||
*
|
||||
* @var \Drupal\linkit\AttributeManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a new OverviewForm.
|
||||
*
|
||||
* @param \Drupal\linkit\AttributeManager $manager
|
||||
* The attribute manager.
|
||||
*/
|
||||
public function __construct(AttributeManager $manager) {
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.linkit.attribute')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return "linkit_attribute_overview_form";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) {
|
||||
$this->linkitProfile = $linkit_profile;
|
||||
|
||||
$form['plugins'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => [
|
||||
$this->t('Attribute'),
|
||||
$this->t('Description'),
|
||||
$this->t('Weight'),
|
||||
$this->t('Operations'),
|
||||
],
|
||||
'#empty' => $this->t('No attributes added.'),
|
||||
'#tabledrag' => [
|
||||
[
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'plugin-order-weight',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->linkitProfile->getAttributes() as $plugin) {
|
||||
$key = $plugin->getPluginId();
|
||||
|
||||
$form['plugins'][$key]['#attributes']['class'][] = 'draggable';
|
||||
$form['plugins'][$key]['#weight'] = $plugin->getWeight();
|
||||
|
||||
$form['plugins'][$key]['label'] = [
|
||||
'#plain_text' => (string) $plugin->getLabel(),
|
||||
];
|
||||
|
||||
$form['plugins'][$key]['description'] = [
|
||||
'#plain_text' => (string) $plugin->getDescription(),
|
||||
];
|
||||
|
||||
$form['plugins'][$key]['weight'] = [
|
||||
'#type' => 'weight',
|
||||
'#title' => t('Weight for @title', ['@title' => (string) $plugin->getLabel()]),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $plugin->getWeight(),
|
||||
'#attributes' => ['class' => ['plugin-order-weight']],
|
||||
];
|
||||
|
||||
$form['plugins'][$key]['operations'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => [],
|
||||
];
|
||||
|
||||
$is_configurable = $plugin instanceof ConfigurableAttributeInterface;
|
||||
if ($is_configurable) {
|
||||
$form['plugins'][$key]['operations']['#links']['edit'] = [
|
||||
'title' => t('Edit'),
|
||||
'url' => Url::fromRoute('linkit.attribute.edit', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $key,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
$form['plugins'][$key]['operations']['#links']['delete'] = [
|
||||
'title' => t('Delete'),
|
||||
'url' => Url::fromRoute('linkit.attribute.delete', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $key,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
$form['actions'] = ['#type' => 'actions'];
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#button_type' => 'primary',
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
foreach ($form_state->getValue('plugins') as $id => $plugin_data) {
|
||||
if ($this->linkitProfile->getAttributes()->has($id)) {
|
||||
$this->linkitProfile->getAttribute($id)->setWeight($plugin_data['weight']);
|
||||
}
|
||||
}
|
||||
$this->linkitProfile->save();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\LinkitEditorDialog.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\HtmlCommand;
|
||||
use Drupal\editor\Ajax\EditorDialogSave;
|
||||
use Drupal\Core\Ajax\CloseModalDialogCommand;
|
||||
use Drupal\linkit\AttributeCollection;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a linkit dialog for text editors.
|
||||
*/
|
||||
class LinkitEditorDialog extends FormBase {
|
||||
|
||||
/**
|
||||
* The editor storage service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $editorStorage;
|
||||
|
||||
/**
|
||||
* The linkit profile storage service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $linkitProfileStorage;
|
||||
|
||||
/**
|
||||
* The linkit profile.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* Constructs a form object for linkit dialog.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $editor_storage
|
||||
* The editor storage service.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $linkit_profile_storage
|
||||
* The linkit profile storage service.
|
||||
*/
|
||||
public function __construct(EntityStorageInterface $editor_storage, EntityStorageInterface $linkit_profile_storage) {
|
||||
$this->editorStorage = $editor_storage;
|
||||
$this->linkitProfileStorage = $linkit_profile_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager')->getStorage('editor'),
|
||||
$container->get('entity.manager')->getStorage('linkit_profile')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'linkit_editor_dialog_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param \Drupal\filter\Entity\FilterFormat $filter_format
|
||||
* The filter format for which this dialog corresponds.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, FilterFormat $filter_format = NULL) {
|
||||
// The default values are set directly from \Drupal::request()->request,
|
||||
// provided by the editor plugin opening the dialog.
|
||||
$user_input = $form_state->getUserInput();
|
||||
$input = isset($user_input['editor_object']) ? $user_input['editor_object'] : [];
|
||||
|
||||
/** @var \Drupal\editor\EditorInterface $editor */
|
||||
$editor = $this->editorStorage->load($filter_format->id());
|
||||
$linkit_profile_id = $editor->getSettings()['plugins']['linkit']['linkit_profile'];
|
||||
$this->linkitProfile = $this->linkitProfileStorage->load($linkit_profile_id);
|
||||
|
||||
$form['#tree'] = TRUE;
|
||||
$form['#attached']['library'][] = 'editor/drupal.editor.dialog';
|
||||
$form['#prefix'] = '<div id="linkit-editor-dialog-form">';
|
||||
$form['#suffix'] = '</div>';
|
||||
|
||||
// Everything under the "attributes" key is merged directly into the
|
||||
// generated link tag's attributes.
|
||||
$form['attributes']['href'] = [
|
||||
'#title' => $this->t('Link'),
|
||||
'#type' => 'linkit',
|
||||
'#default_value' => isset($input['href']) ? $input['href'] : '',
|
||||
'#description' => $this->t('Start typing to find content or paste a URL.'),
|
||||
'#autocomplete_route_name' => 'linkit.autocomplete',
|
||||
'#autocomplete_route_parameters' => [
|
||||
'linkit_profile_id' => $linkit_profile_id
|
||||
],
|
||||
'#weight' => 0,
|
||||
];
|
||||
|
||||
$this->addAttributes($form, $form_state, $this->linkitProfile->getAttributes(), $input);
|
||||
|
||||
$form['actions'] = [
|
||||
'#type' => 'actions',
|
||||
];
|
||||
|
||||
$form['actions']['save_modal'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#submit' => [],
|
||||
'#ajax' => [
|
||||
'callback' => '::submitForm',
|
||||
'event' => 'click',
|
||||
],
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$attributes = array_filter($form_state->getValue('attributes'));
|
||||
$form_state->setValue('attributes', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$response = new AjaxResponse();
|
||||
|
||||
if ($form_state->getErrors()) {
|
||||
unset($form['#prefix'], $form['#suffix']);
|
||||
$form['status_messages'] = [
|
||||
'#type' => 'status_messages',
|
||||
'#weight' => -10,
|
||||
];
|
||||
$response->addCommand(new HtmlCommand('#linkit-editor-dialog-form', $form));
|
||||
}
|
||||
else {
|
||||
$response->addCommand(new EditorDialogSave($form_state->getValues()));
|
||||
$response->addCommand(new CloseModalDialogCommand());
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the attributes enabled on the current profile.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
* @param AttributeCollection $attributes
|
||||
* A collection of attributes for the current profile.
|
||||
* @param array $input
|
||||
* An array with the attribute values from the editor.
|
||||
*/
|
||||
private function addAttributes(array &$form, FormStateInterface &$form_state, AttributeCollection $attributes, array $input) {
|
||||
if ($attributes->count()) {
|
||||
$form['linkit_attributes'] = [
|
||||
'#type' => 'container',
|
||||
'#title' => $this->t('Attributes'),
|
||||
'#weight' => '10',
|
||||
];
|
||||
|
||||
/** @var \Drupal\linkit\AttributeInterface $plugin */
|
||||
foreach ($attributes as $plugin) {
|
||||
$plugin_name = $plugin->getHtmlName();
|
||||
|
||||
$default_value = isset($input[$plugin_name]) ? $input[$plugin_name] : '';
|
||||
$form['linkit_attributes'][$plugin_name] = $plugin->buildFormElement($default_value);
|
||||
$form['linkit_attributes'][$plugin_name] += [
|
||||
'#parents' => [
|
||||
'attributes', $plugin_name,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the linkit profile entity.
|
||||
*
|
||||
* @return \Drupal\linkit\ProfileInterface
|
||||
* The current linkit profile used by this form.
|
||||
*/
|
||||
public function getLinkitProfile() {
|
||||
return $this->linkitProfile;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Matcher\AddForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Matcher;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\linkit\ConfigurableMatcherInterface;
|
||||
use Drupal\linkit\MatcherManager;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form to apply matchers to a profile.
|
||||
*/
|
||||
class AddForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The profiles to which the matchers will be applied.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
|
||||
/**
|
||||
* The matcher manager.
|
||||
*
|
||||
* @var \Drupal\linkit\MatcherManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a new AddForm.
|
||||
*
|
||||
* @param \Drupal\linkit\MatcherManager $manager
|
||||
* The matcher manager.
|
||||
*/
|
||||
public function __construct(MatcherManager $manager) {
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.linkit.matcher')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return "linkit_matcher_add_form";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) {
|
||||
$this->linkitProfile = $linkit_profile;
|
||||
|
||||
$form['#attached']['library'][] = 'linkit/linkit.admin';
|
||||
$header = [
|
||||
'label' => $this->t('Matchers'),
|
||||
];
|
||||
|
||||
$form['plugin'] = [
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $header,
|
||||
'#options' => $this->buildRows(),
|
||||
'#empty' => $this->t('No matchers available.'),
|
||||
'#multiple' => FALSE,
|
||||
];
|
||||
|
||||
$form['actions'] = ['#type' => 'actions'];
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save and continue'),
|
||||
'#submit' => ['::submitForm'],
|
||||
'#tableselect' => TRUE,
|
||||
'#button_type' => 'primary',
|
||||
];
|
||||
|
||||
$options = [];
|
||||
foreach ($this->manager->getDefinitions() as $id => $plugin) {
|
||||
$options[$id] = $plugin['label'];
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
if (empty($form_state->getValue('plugin'))) {
|
||||
$form_state->setErrorByName('plugin', $this->t('No matcher selected.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->cleanValues();
|
||||
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance($form_state->getValue('plugin'));
|
||||
|
||||
$plugin_uuid = $this->linkitProfile->addMatcher($plugin->getConfiguration());
|
||||
$this->linkitProfile->save();
|
||||
|
||||
$this->logger('linkit')->notice('Added %label matcher to the @profile profile.', [
|
||||
'%label' => $this->linkitProfile->getMatcher($plugin_uuid)->getLabel(),
|
||||
'@profile' => $this->linkitProfile->label(),
|
||||
]);
|
||||
|
||||
$is_configurable = $plugin instanceof ConfigurableMatcherInterface;
|
||||
if ($is_configurable) {
|
||||
$form_state->setRedirect('linkit.matcher.edit', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $plugin_uuid,
|
||||
]);
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('Added %label matcher.', ['%label' => $plugin->getLabel()]));
|
||||
|
||||
$form_state->setRedirect('linkit.matchers', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the table rows.
|
||||
*
|
||||
* @return array
|
||||
* An array of table rows.
|
||||
*/
|
||||
private function buildRows() {
|
||||
$rows = [];
|
||||
$all_plugins = $this->manager->getDefinitions();
|
||||
uasort($all_plugins, function ($a, $b) {
|
||||
return strnatcasecmp($a['label'], $b['label']);
|
||||
});
|
||||
foreach ($all_plugins as $definition) {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance($definition['id']);
|
||||
$row = [
|
||||
'label' => $plugin->getLabel(),
|
||||
];
|
||||
$rows[$plugin->getPluginId()] = $row;
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Matcher\DeleteForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Matcher;
|
||||
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Provides a form to remove a matcher from a profile.
|
||||
*/
|
||||
class DeleteForm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The profiles that the matcher is applied to.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* The matcher to be removed from the profile.
|
||||
*
|
||||
* @var \Drupal\linkit\MatcherInterface
|
||||
*/
|
||||
protected $linkitMatcher;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete the @plugin matcher from the %profile profile?', ['%profile' => $this->linkitProfile->label(), '@plugin' => $this->linkitMatcher->getLabel()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return Url::fromRoute('linkit.matchers', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'linkit_matcher_delete_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL, $plugin_instance_id = NULL) {
|
||||
$this->linkitProfile = $linkit_profile;
|
||||
|
||||
if (!$this->linkitProfile->getMatchers()->has($plugin_instance_id)) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$this->linkitMatcher = $this->linkitProfile->getMatcher($plugin_instance_id);
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->linkitProfile->removeMatcher($this->linkitMatcher);
|
||||
|
||||
drupal_set_message($this->t('The matcher %label has been deleted.', ['%label' => $this->linkitMatcher->getLabel()]));
|
||||
$this->logger('linkit')->notice('The matcher %label has been deleted in the @profile profile.', [
|
||||
'%label' => $this->linkitMatcher->getLabel(),
|
||||
'@profile' => $this->linkitProfile->label(),
|
||||
]);
|
||||
|
||||
$form_state->setRedirect('linkit.matchers', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Matcher\EditForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Matcher;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
|
||||
/**
|
||||
* Provides an edit form for matchers.
|
||||
*/
|
||||
class EditForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The profiles to which the matchers will be applied.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* The matcher to edit.
|
||||
*
|
||||
* @var \Drupal\linkit\ConfigurableMatcherInterface
|
||||
*/
|
||||
protected $linkitMatcher;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'linkit_matcher_edit_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL, $plugin_instance_id = NULL) {
|
||||
$this->linkitProfile = $linkit_profile;
|
||||
$this->linkitMatcher = $this->linkitProfile->getMatcher($plugin_instance_id);
|
||||
|
||||
$form += $this->linkitMatcher->buildConfigurationForm($form, $form_state);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save changes'),
|
||||
'#submit' => array('::submitForm'),
|
||||
'#button_type' => 'primary',
|
||||
);
|
||||
$form['actions']['delete'] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Delete'),
|
||||
'#url' => Url::fromRoute('linkit.matcher.delete', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $this->linkitMatcher->getUuid(),
|
||||
]),
|
||||
'#attributes' => [
|
||||
'class' => ['button', 'button--danger'],
|
||||
],
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->cleanValues();
|
||||
$plugin_data = (new FormState())->setValues($form_state->getValues());
|
||||
$this->linkitMatcher->submitConfigurationForm($form, $plugin_data);
|
||||
$this->linkitProfile->save();
|
||||
|
||||
drupal_set_message($this->t('Saved %label configuration.', array('%label' => $this->linkitMatcher->getLabel())));
|
||||
$this->logger('linkit')->notice('The matcher %label has been updated in the @profile profile.', [
|
||||
'%label' => $this->linkitMatcher->getLabel(),
|
||||
'@profile' => $this->linkitProfile->label(),
|
||||
]);
|
||||
|
||||
$form_state->setRedirect('linkit.matchers', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Matcher\OverviewForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Matcher;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\ConfigurableMatcherInterface;
|
||||
use Drupal\linkit\MatcherManager;
|
||||
use Drupal\linkit\ProfileInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides an overview form for matchers on a profile.
|
||||
*/
|
||||
class OverviewForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The profiles to which the matchers are applied to.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
private $linkitProfile;
|
||||
|
||||
/**
|
||||
* The matcher manager.
|
||||
*
|
||||
* @var \Drupal\linkit\MatcherManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Constructs a new OverviewForm.
|
||||
*
|
||||
* @param \Drupal\linkit\MatcherManager $manager
|
||||
* The matcher manager.
|
||||
*/
|
||||
public function __construct(MatcherManager $manager) {
|
||||
$this->manager = $manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.linkit.matcher')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return "linkit_matcher_overview_form";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) {
|
||||
$this->linkitProfile = $linkit_profile;
|
||||
$form['#attached']['library'][] = 'linkit/linkit.admin';
|
||||
$form['plugins'] = [
|
||||
'#type' => 'table',
|
||||
'#header' => [
|
||||
[
|
||||
'data' => $this->t('Matcher'),
|
||||
'colspan' => 2
|
||||
],
|
||||
$this->t('Weight'),
|
||||
$this->t('Operations'),
|
||||
],
|
||||
'#empty' => $this->t('No matchers added.'),
|
||||
'#tabledrag' => [
|
||||
[
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'plugin-order-weight',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->linkitProfile->getMatchers() as $plugin) {
|
||||
$key = $plugin->getUuid();
|
||||
|
||||
$form['plugins'][$key]['#attributes']['class'][] = 'draggable';
|
||||
$form['plugins'][$key]['#weight'] = $plugin->getWeight();
|
||||
|
||||
$form['plugins'][$key]['label'] = [
|
||||
'#plain_text' => (string) $plugin->getLabel(),
|
||||
];
|
||||
|
||||
$form['plugins'][$key]['summary'] = [];
|
||||
|
||||
$summary = $plugin->getSummary();
|
||||
if (!empty($summary)) {
|
||||
$form['plugins'][$key]['summary'] = [
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<div class="linkit-plugin-summary">{{ summary|safe_join("<br />") }}</div>',
|
||||
'#context' => ['summary' => $summary],
|
||||
];
|
||||
}
|
||||
|
||||
$form['plugins'][$key]['weight'] = [
|
||||
'#type' => 'weight',
|
||||
'#title' => t('Weight for @title', ['@title' => (string) $plugin->getLabel()]),
|
||||
'#title_display' => 'invisible',
|
||||
'#default_value' => $plugin->getWeight(),
|
||||
'#attributes' => ['class' => ['plugin-order-weight']],
|
||||
];
|
||||
|
||||
$form['plugins'][$key]['operations'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => [],
|
||||
];
|
||||
|
||||
$is_configurable = $plugin instanceof ConfigurableMatcherInterface;
|
||||
if ($is_configurable) {
|
||||
$form['plugins'][$key]['operations']['#links']['edit'] = [
|
||||
'title' => t('Edit'),
|
||||
'url' => Url::fromRoute('linkit.matcher.edit', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $key,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
$form['plugins'][$key]['operations']['#links']['delete'] = [
|
||||
'title' => t('Delete'),
|
||||
'url' => Url::fromRoute('linkit.matcher.delete', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $key,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
$form['actions'] = ['#type' => 'actions'];
|
||||
$form['actions']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Save'),
|
||||
'#button_type' => 'primary',
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
foreach ($form_state->getValue('plugins') as $id => $plugin_data) {
|
||||
if ($this->linkitProfile->getMatchers()->has($id)) {
|
||||
$this->linkitProfile->getMatcher($id)->setWeight($plugin_data['weight']);
|
||||
}
|
||||
}
|
||||
$this->linkitProfile->save();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Profile\AddForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Profile;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Controller for profile addition forms.
|
||||
*
|
||||
* @see \Drupal\linkit\Profile\FormBase
|
||||
*/
|
||||
class AddForm extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Save and manage matchers');
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Profile\EditForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Profile;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides an edit form for profile.
|
||||
*
|
||||
* @see \Drupal\linkit\Profile\FormBase
|
||||
*/
|
||||
class EditForm extends FormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Update profile');
|
||||
$actions['delete']['#value'] = $this->t('Delete profile');
|
||||
return $actions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Form\Profile\FormBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Form\Profile;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Base form for profile add and edit forms.
|
||||
*/
|
||||
abstract class FormBase extends EntityForm {
|
||||
|
||||
/**
|
||||
* The entity being used by this form.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form['label'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Profile Name'),
|
||||
'#default_value' => $this->entity->label(),
|
||||
'#description' => $this->t('The human-readable name of this profile. This name must be unique.'),
|
||||
'#required' => TRUE,
|
||||
'#size' => 30,
|
||||
];
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'machine_name',
|
||||
'#default_value' => $this->entity->id(),
|
||||
'#machine_name' => [
|
||||
'exists' => ['\Drupal\linkit\Entity\Profile', 'load']
|
||||
],
|
||||
'#disabled' => !$this->entity->isNew(),
|
||||
];
|
||||
|
||||
$form['description'] = [
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Description'),
|
||||
'#default_value' => $this->entity->getDescription(),
|
||||
'#description' => $this->t('The text will be displayed on the <em>profile collection</em> page.'),
|
||||
];
|
||||
|
||||
$form['additional_settings'] = array(
|
||||
'#type' => 'vertical_tabs',
|
||||
'#weight' => 99,
|
||||
);
|
||||
|
||||
return parent::form($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$linkit_profile = $this->entity;
|
||||
|
||||
// Prevent leading and trailing spaces in linkit profile labels.
|
||||
$linkit_profile->set('label', trim($linkit_profile->label()));
|
||||
|
||||
$status = $linkit_profile->save();
|
||||
$edit_link = $this->entity->link($this->t('Edit'));
|
||||
switch ($status) {
|
||||
case SAVED_NEW:
|
||||
drupal_set_message($this->t('Created new profile %label.', ['%label' => $linkit_profile->label()]));
|
||||
$this->logger('linkit')->notice('Created new profile %label.', ['%label' => $linkit_profile->label(), 'link' => $edit_link]);
|
||||
$form_state->setRedirect('linkit.matchers', [
|
||||
'linkit_profile' => $linkit_profile->id(),
|
||||
]);
|
||||
break;
|
||||
|
||||
case SAVED_UPDATED:
|
||||
drupal_set_message($this->t('Updated profile %label.', ['%label' => $linkit_profile->label()]));
|
||||
$this->logger('linkit')->notice('Updated profile %label.', ['%label' => $linkit_profile->label(), 'link' => $edit_link]);
|
||||
$form_state->setRedirectUrl($linkit_profile->urlInfo('edit-form'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\MatcherBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
|
||||
/**
|
||||
* Provides a base class for matchers.
|
||||
*
|
||||
* @see plugin_api
|
||||
*/
|
||||
abstract class MatcherBase extends PluginBase implements MatcherInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The matcher ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $uuid;
|
||||
|
||||
/**
|
||||
* The weight of the matcher compared to others in a matcher collection.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $weight = 0;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->setConfiguration($configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUuid() {
|
||||
return $this->uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->pluginDefinition['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSummary() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setWeight($weight) {
|
||||
return $this->weight = $weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfiguration() {
|
||||
return [
|
||||
'uuid' => $this->getUuid(),
|
||||
'id' => $this->getPluginId(),
|
||||
'weight' => $this->getWeight(),
|
||||
'settings' => $this->configuration,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConfiguration(array $configuration) {
|
||||
$configuration += [
|
||||
'uuid' => '',
|
||||
'weight' => '0',
|
||||
'settings' => [],
|
||||
];
|
||||
$this->configuration = $configuration['settings'] + $this->defaultConfiguration();
|
||||
$this->uuid = $configuration['uuid'];
|
||||
$this->weight = $configuration['weight'];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\MatcherCollection.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Plugin\DefaultLazyPluginCollection;
|
||||
|
||||
/**
|
||||
* A collection of matchers.
|
||||
*/
|
||||
class MatcherCollection extends DefaultLazyPluginCollection {
|
||||
|
||||
/**
|
||||
* All possible matcher IDs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $definitions;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Drupal\linkit\MatcherInterface
|
||||
*/
|
||||
public function &get($instance_id) {
|
||||
return parent::get($instance_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function sortHelper($aID, $bID) {
|
||||
$a_weight = $this->get($aID)->getWeight();
|
||||
$b_weight = $this->get($bID)->getWeight();
|
||||
if ($a_weight == $b_weight) {
|
||||
return strnatcasecmp($this->get($aID)->getLabel(), $this->get($bID)->getLabel());
|
||||
}
|
||||
|
||||
return ($a_weight < $b_weight) ? -1 : 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\MatcherInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
|
||||
/**
|
||||
* Defines the interface for matchers.
|
||||
*
|
||||
* @see \Drupal\linkit\Annotation\Matcher
|
||||
* @see \Drupal\linkit\MatcherBase
|
||||
* @see \Drupal\linkit\MatcherManager
|
||||
* @see plugin_api
|
||||
*/
|
||||
interface MatcherInterface extends PluginInspectionInterface, ConfigurablePluginInterface {
|
||||
|
||||
/**
|
||||
* Returns the unique ID representing the matcher.
|
||||
*
|
||||
* @return string
|
||||
* The matcher ID.
|
||||
*/
|
||||
public function getUuid();
|
||||
|
||||
/**
|
||||
* Returns the matcher label.
|
||||
*
|
||||
* @return string
|
||||
* The matcher label.
|
||||
*/
|
||||
public function getLabel();
|
||||
|
||||
/**
|
||||
* Returns the summarized configuration of the matcher.
|
||||
*
|
||||
* @return array
|
||||
* An array of summarized configuration of the matcher.
|
||||
*/
|
||||
public function getSummary();
|
||||
|
||||
/**
|
||||
* Returns the weight of the matcher.
|
||||
*
|
||||
* @return int|string
|
||||
* Either the integer weight of the matcher, or an empty string.
|
||||
*/
|
||||
public function getWeight();
|
||||
|
||||
/**
|
||||
* Sets the weight for the matcher.
|
||||
*
|
||||
* @param int $weight
|
||||
* The weight for this matcher.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWeight($weight);
|
||||
|
||||
/**
|
||||
* Gets an array with search matches that will be presented in the autocomplete
|
||||
* widget.
|
||||
*
|
||||
* @param $string
|
||||
* The string that contains the text to search for.
|
||||
*
|
||||
* @return array
|
||||
* An array whose values are an associative array containing:
|
||||
* - title: A string to use as the search result label.
|
||||
* - description: (optional) A string with additional information about the
|
||||
* result item.
|
||||
* - path: The URL to the item.
|
||||
* - group: (optional) A string with the group name for the result item.
|
||||
* Best practice is to use the plugin name as group name.
|
||||
*/
|
||||
public function getMatches($string);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\MatcherManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
|
||||
/**
|
||||
* Manages matchers.
|
||||
*/
|
||||
class MatcherManager extends DefaultPluginManager {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct('Plugin/Linkit/Matcher', $namespaces, $module_handler, 'Drupal\linkit\MatcherInterface', 'Drupal\linkit\Annotation\Matcher');
|
||||
|
||||
$this->alterInfo('linkit_matcher');
|
||||
$this->setCacheBackend($cache_backend, 'linkit_matchers');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\MatcherTokensTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
/**
|
||||
* Provides friendly methods for matchers using tokens.
|
||||
*/
|
||||
trait MatcherTokensTrait {
|
||||
|
||||
/**
|
||||
* Inserts a form element with a list of available tokens.
|
||||
*
|
||||
* @param $form
|
||||
* The form array to append the token list to.
|
||||
* @param array $types
|
||||
* An array of token types to use.
|
||||
*/
|
||||
public function insertTokenList(&$form, array $types = array()) {
|
||||
if (\Drupal::moduleHandler()->moduleExists('token')) {
|
||||
// Add the token tree UI.
|
||||
$form['token_tree'] = array(
|
||||
'#theme' => 'token_tree_link',
|
||||
'#token_types' => $types,
|
||||
'#dialog' => TRUE,
|
||||
'#weight' => -90,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$token_items = array();
|
||||
foreach ($this->getAvailableTokens($types) as $type => $tokens) {
|
||||
foreach ($tokens as $name => $info) {
|
||||
$token_description = !empty($info['description']) ? $info['description'] : '';
|
||||
$token_items[$type . ':' . $name] = "[$type:$name]" . ' - ' . $info['name'] . ': ' . $token_description;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($token_items)) {
|
||||
$form['tokens'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Available tokens'),
|
||||
'#weight' => -90,
|
||||
);
|
||||
|
||||
$form['tokens']['list'] = array(
|
||||
'#theme' => 'item_list',
|
||||
'#items' => $token_items,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all available tokens.
|
||||
*
|
||||
* @param array $types
|
||||
* An array of token types to use.
|
||||
* @return array
|
||||
* An array with available tokens
|
||||
*/
|
||||
public function getAvailableTokens(array $types = array()) {
|
||||
$info = \Drupal::token()->getInfo();
|
||||
$available = array_intersect_key($info['tokens'], array_flip($types));
|
||||
return $available;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\CKEditorPlugin\Linkit.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\CKEditorPlugin;
|
||||
|
||||
use Drupal\ckeditor\CKEditorPluginBase;
|
||||
use Drupal\ckeditor\CKEditorPluginConfigurableInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines the "linkit" plugin.
|
||||
*
|
||||
* @CKEditorPlugin(
|
||||
* id = "linkit",
|
||||
* label = @Translation("Linkit"),
|
||||
* module = "linkit"
|
||||
* )
|
||||
*/
|
||||
class Linkit extends CKEditorPluginBase implements CKEditorPluginConfigurableInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The Linkit profile storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $linkitProfileStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $linkit_profile_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->linkitProfileStorage = $linkit_profile_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager')->getStorage('linkit_profile')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFile() {
|
||||
return drupal_get_path('module', 'linkit') . '/js/plugins/linkit/plugin.js';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfig(Editor $editor) {
|
||||
return array(
|
||||
'linkit_dialogTitleAdd' => t('Add link'),
|
||||
'linkit_dialogTitleEdit' => t('Edit link'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getButtons() {
|
||||
return array(
|
||||
'Linkit' => array(
|
||||
'label' => t('Linkit'),
|
||||
'image' => drupal_get_path('module', 'linkit') . '/js/plugins/linkit/linkit.png',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) {
|
||||
$settings = $editor->getSettings();
|
||||
|
||||
$all_profiles = $this->linkitProfileStorage->loadMultiple();
|
||||
|
||||
$options = array();
|
||||
foreach ($all_profiles as $profile) {
|
||||
$options[$profile->id()] = $profile->label();
|
||||
}
|
||||
|
||||
$form['linkit_profile'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Select a linkit profile'),
|
||||
'#options' => $options,
|
||||
'#default_value' => isset($settings['plugins']['linkit']) ? $settings['plugins']['linkit'] : '',
|
||||
'#empty_option' => $this->t('- Select profile -'),
|
||||
'#description' => $this->t('Select the linkit profile you wish to use with this text format.'),
|
||||
'#element_validate' => array(
|
||||
array($this, 'validateLinkitProfileSelection'),
|
||||
),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* #element_validate handler for the "linkit_profile" element in settingsForm().
|
||||
*/
|
||||
public function validateLinkitProfileSelection(array $element, FormStateInterface $form_state) {
|
||||
$toolbar_buttons = $form_state->getValue(array('editor', 'settings', 'toolbar', 'button_groups'));
|
||||
if (strpos($toolbar_buttons, '"Linkit"') !== FALSE && empty($element['#value'])) {
|
||||
$form_state->setError($element, t('Please select the linkit profile you wish to use.'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Derivative\EntityMatcherDeriver.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
* @see plugin_api
|
||||
*/
|
||||
class EntityMatcherDeriver extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Creates an EntityMatcherDeriver object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager) {
|
||||
$this->entityManager = $entity_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
$has_canonical = $entity_type->hasLinkTemplate('canonical');
|
||||
|
||||
if ($has_canonical) {
|
||||
$this->derivatives[$entity_type_id] = $base_plugin_definition;
|
||||
$this->derivatives[$entity_type_id]['id'] = $base_plugin_definition['id'] . ':' . $entity_type_id;
|
||||
$this->derivatives[$entity_type_id]['label'] = $entity_type->getLabel();
|
||||
$this->derivatives[$entity_type_id]['target_entity'] = $entity_type_id;
|
||||
$this->derivatives[$entity_type_id]['base_plugin_label'] = (string) $base_plugin_definition['label'];
|
||||
}
|
||||
}
|
||||
|
||||
return parent::getDerivativeDefinitions($base_plugin_definition);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Accesskey.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Attribute;
|
||||
|
||||
use Drupal\linkit\AttributeBase;
|
||||
|
||||
/**
|
||||
* Accesskey attribute.
|
||||
*
|
||||
* @Attribute(
|
||||
* id = "accesskey",
|
||||
* label = @Translation("Accesskey"),
|
||||
* html_name = "accesskey",
|
||||
* description = @Translation("Basic input field for the accesskey attribute.")
|
||||
* )
|
||||
*/
|
||||
class Accesskey extends AttributeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFormElement($default_value) {
|
||||
return [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Accesskey'),
|
||||
'#default_value' => $default_value,
|
||||
'#maxlength' => 255,
|
||||
'#size' => 40,
|
||||
'#placeholder' => t('The "accesskey" attribute value'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Clazz.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Attribute;
|
||||
|
||||
use Drupal\linkit\AttributeBase;
|
||||
|
||||
/**
|
||||
* Class attribute.
|
||||
*
|
||||
* @TODO: For now Drupal filter_html wont support class attributes with
|
||||
* wildcards.
|
||||
* See: \Drupal\filter\Plugin\Filter\FilterHtml::getHTMLRestrictions
|
||||
* See: core/modules/filter/filter.filter_html.admin.js
|
||||
*
|
||||
* @Attribute(
|
||||
* id = "class",
|
||||
* label = @Translation("Class"),
|
||||
* html_name = "class",
|
||||
* description = @Translation("Basic input field for the class attribute."),
|
||||
* )
|
||||
*/
|
||||
//class Clazz extends AttributeBase {
|
||||
//
|
||||
// /**
|
||||
// * {@inheritdoc}
|
||||
// */
|
||||
// public function buildFormElement($default_value) {
|
||||
// return [
|
||||
// '#type' => 'textfield',
|
||||
// '#title' => t('Class'),
|
||||
// '#maxlength' => 255,
|
||||
// '#size' => 40,
|
||||
// ];
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Id.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Attribute;
|
||||
|
||||
use Drupal\linkit\AttributeBase;
|
||||
|
||||
/**
|
||||
* Id attribute.
|
||||
*
|
||||
* @Attribute(
|
||||
* id = "id",
|
||||
* label = @Translation("Id"),
|
||||
* html_name = "id",
|
||||
* description = @Translation("Basic input field for the id attribute."),
|
||||
* )
|
||||
*/
|
||||
class Id extends AttributeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFormElement($default_value) {
|
||||
return [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Id'),
|
||||
'#default_value' => $default_value,
|
||||
'#maxlength' => 255,
|
||||
'#size' => 40,
|
||||
'#placeholder' => t('The "id" attribute value'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Relationship.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Attribute;
|
||||
|
||||
use Drupal\linkit\AttributeBase;
|
||||
|
||||
/**
|
||||
* Relationship attribute.
|
||||
*
|
||||
* @Attribute(
|
||||
* id = "relationship",
|
||||
* label = @Translation("Relationship"),
|
||||
* html_name = "rel",
|
||||
* description = @Translation("Basic input field for the relationship attribute."),
|
||||
* )
|
||||
*/
|
||||
class Relationship extends AttributeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFormElement($default_value) {
|
||||
return [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Relationship'),
|
||||
'#default_value' => $default_value,
|
||||
'#maxlength' => 255,
|
||||
'#size' => 40,
|
||||
'#placeholder' => t('The "rel" attribute value'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Target.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Attribute;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\linkit\ConfigurableAttributeBase;
|
||||
|
||||
/**
|
||||
* Target attribute.
|
||||
*
|
||||
* @Attribute(
|
||||
* id = "target",
|
||||
* label = @Translation("Target"),
|
||||
* html_name = "target"
|
||||
* )
|
||||
*/
|
||||
class Target extends ConfigurableAttributeBase {
|
||||
|
||||
const SELECT_LIST = 'select_list';
|
||||
const SIMPLE_CHECKBOX = 'simple_checkbox';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFormElement($default_value) {
|
||||
switch ($this->configuration['widget_type']) {
|
||||
case self::SELECT_LIST:
|
||||
return [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target'),
|
||||
'#options' => [
|
||||
'' => '',
|
||||
'_blank' => t('New window (_blank)'),
|
||||
'_top' => t('Top window (_top)'),
|
||||
'_self' => t('Same window (_self)'),
|
||||
'_parent' => t('Parent window (_parent)')
|
||||
],
|
||||
'#default_value' => $default_value,
|
||||
];
|
||||
case self::SIMPLE_CHECKBOX:
|
||||
return [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Open in new window'),
|
||||
'#default_value' => $default_value,
|
||||
'#return_value' => '_blank',
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return parent::defaultConfiguration() + [
|
||||
'widget_type' => self::SIMPLE_CHECKBOX,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form['widget_type'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Widget type'),
|
||||
'#default_value' => $this->configuration['widget_type'],
|
||||
'#options' => [
|
||||
self::SELECT_LIST => $this->t('Selectlist with predefined targets.'),
|
||||
self::SIMPLE_CHECKBOX => $this->t('Simple checkbox to allow links to be opened in a new browser window or tab.'),
|
||||
],
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->configuration['widget_type'] = $form_state->getValue('widget_type');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Title.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Attribute;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\linkit\ConfigurableAttributeBase;
|
||||
|
||||
/**
|
||||
* Title attribute.
|
||||
*
|
||||
* @Attribute(
|
||||
* id = "title",
|
||||
* label = @Translation("Title"),
|
||||
* html_name = "title",
|
||||
* description = @Translation("Basic input field for the title attribute.")
|
||||
* )
|
||||
*/
|
||||
class Title extends ConfigurableAttributeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFormElement($default_value) {
|
||||
$element = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Title'),
|
||||
'#default_value' => $default_value,
|
||||
'#maxlength' => 255,
|
||||
'#size' => 40,
|
||||
'#placeholder' => t('The "title" attribute value'),
|
||||
];
|
||||
|
||||
if ($this->configuration['automatic_title']) {
|
||||
$element['#attached']['library'][] = 'linkit/linkit.attribute.title';
|
||||
$element['#placeholder'] = t('The "title" attribute value (auto populated)');
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return parent::defaultConfiguration() + [
|
||||
'automatic_title' => FALSE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form['automatic_title'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Automatically populate title'),
|
||||
'#default_value' => $this->configuration['automatic_title'],
|
||||
'#description' => $this->t('Automatically populate the title attribute with the title from the match selection.'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->configuration['automatic_title'] = $form_state->getValue('automatic_title');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\EntityMatcher.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Matcher;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\linkit\ConfigurableMatcherBase;
|
||||
use Drupal\linkit\MatcherTokensTrait;
|
||||
use Drupal\linkit\Utility\LinkitXss;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* @Matcher(
|
||||
* id = "entity",
|
||||
* label = @Translation("Entity"),
|
||||
* deriver = "\Drupal\linkit\Plugin\Derivative\EntityMatcherDeriver"
|
||||
* )
|
||||
*/
|
||||
class EntityMatcher extends ConfigurableMatcherBase {
|
||||
|
||||
use MatcherTokensTrait;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The module handler service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* The target entity type id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $target_type;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
if (empty($plugin_definition['target_entity'])) {
|
||||
throw new \InvalidArgumentException("Missing required 'target_entity' property for a matcher.");
|
||||
}
|
||||
$this->database = $database;
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->currentUser = $current_user;
|
||||
$this->target_type = $plugin_definition['target_entity'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('database'),
|
||||
$container->get('entity.manager'),
|
||||
$container->get('module_handler'),
|
||||
$container->get('current_user')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSummary() {
|
||||
$summery = parent::getSummary();
|
||||
$entity_type = $this->entityManager->getDefinition($this->target_type);
|
||||
|
||||
$result_description = $this->configuration['result_description'];
|
||||
if (!empty($result_description)) {
|
||||
$summery[] = $this->t('Result description: @result_description', [
|
||||
'@result_description' => $result_description
|
||||
]);
|
||||
}
|
||||
|
||||
if ($entity_type->hasKey('bundle')) {
|
||||
$has_bundle_filter = !empty($this->configuration['bundles']);
|
||||
$bundles = [];
|
||||
|
||||
if ($has_bundle_filter) {
|
||||
$bundles_info = $this->entityManager->getBundleInfo($this->target_type);
|
||||
foreach ($this->configuration['bundles'] as $bundle) {
|
||||
$bundles[] = $bundles_info[$bundle]['label'];
|
||||
}
|
||||
}
|
||||
|
||||
$summery[] = $this->t('Bundle filter: @bundle_filter', [
|
||||
'@bundle_filter' => $has_bundle_filter ? implode(', ', $bundles) : t('None'),
|
||||
]);
|
||||
|
||||
$summery[] = $this->t('Group by bundle: @bundle_grouping', [
|
||||
'@bundle_grouping' => $this->configuration['group_by_bundle'] ? $this->t('Yes') : $this->t('No'),
|
||||
]);
|
||||
}
|
||||
|
||||
return $summery;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return parent::defaultConfiguration() + [
|
||||
'result_description' => '',
|
||||
'bundles' => [],
|
||||
'group_by_bundle' => FALSE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$entity_type = $this->entityManager->getDefinition($this->target_type);
|
||||
$form['result_description'] = [
|
||||
'#title' => $this->t('Result description'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $this->configuration['result_description'],
|
||||
'#size' => 120,
|
||||
'#maxlength' => 255,
|
||||
'#weight' => -100,
|
||||
];
|
||||
|
||||
$this->insertTokenList($form, [$this->target_type]);
|
||||
|
||||
// Filter the possible bundles to use if the entity has bundles.
|
||||
if ($entity_type->hasKey('bundle')) {
|
||||
$bundle_options = [];
|
||||
foreach ($this->entityManager->getBundleInfo($this->target_type) as $bundle_name => $bundle_info) {
|
||||
$bundle_options[$bundle_name] = $bundle_info['label'];
|
||||
}
|
||||
|
||||
$form['bundles'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Restrict to the selected bundles'),
|
||||
'#options' => $bundle_options,
|
||||
'#default_value' => $this->configuration['bundles'],
|
||||
'#description' => $this->t('If none of the checkboxes is checked, allow all bundles.'),
|
||||
'#element_validate' => [[get_class($this), 'elementValidateFilter']],
|
||||
'#weight' => -50,
|
||||
];
|
||||
|
||||
// Group the results by bundle.
|
||||
$form['group_by_bundle'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Group by bundle'),
|
||||
'#default_value' => $this->configuration['group_by_bundle'],
|
||||
'#weight' => -50,
|
||||
];
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->configuration['result_description'] = $form_state->getValue('result_description');
|
||||
$this->configuration['bundles'] = $form_state->getValue('bundles');
|
||||
$this->configuration['group_by_bundle'] = $form_state->getValue('group_by_bundle');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validation handler; Filters the #value property of an element.
|
||||
*/
|
||||
public static function elementValidateFilter(&$element, FormStateInterface $form_state) {
|
||||
$element['#value'] = array_filter($element['#value']);
|
||||
$form_state->setValueForElement($element, $element['#value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMatches($string) {
|
||||
$query = $this->buildEntityQuery($string);
|
||||
$result = $query->execute();
|
||||
|
||||
if (empty($result)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$matches = [];
|
||||
$entities = $this->entityManager->getStorage($this->target_type)->loadMultiple($result);
|
||||
|
||||
foreach ($entities as $entity_id => $entity) {
|
||||
// Check the access against the defined entity access handler.
|
||||
/** @var \Drupal\Core\Access\AccessResultInterface $access */
|
||||
$access = $entity->access('view', $this->currentUser, TRUE);
|
||||
if (!$access->isAllowed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$matches[] = [
|
||||
'title' => $this->buildLabel($entity),
|
||||
'description' => $this->buildDescription($entity),
|
||||
'path' => $this->buildPath($entity),
|
||||
'group' => $this->buildGroup($entity),
|
||||
];
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an EntityQuery to get entities.
|
||||
*
|
||||
* @param $match
|
||||
* Text to match the label against.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\Query\QueryInterface
|
||||
* The EntityQuery object with the basic conditions and sorting applied to
|
||||
* it.
|
||||
*/
|
||||
protected function buildEntityQuery($match) {
|
||||
$match = $this->database->escapeLike($match);
|
||||
|
||||
$entity_type = $this->entityManager->getDefinition($this->target_type);
|
||||
$query = $this->entityManager->getStorage($this->target_type)->getQuery();
|
||||
$label_key = $entity_type->getKey('label');
|
||||
|
||||
if ($label_key) {
|
||||
$query->condition($label_key, '%' . $match . '%', 'LIKE');
|
||||
$query->sort($label_key, 'ASC');
|
||||
}
|
||||
|
||||
// Bundle check.
|
||||
if (!empty($this->configuration['bundles']) && $bundle_key = $entity_type->getKey('bundle')) {
|
||||
$query->condition($bundle_key, $this->configuration['bundles'], 'IN');
|
||||
}
|
||||
|
||||
// Add tags to let other modules alter the query.
|
||||
$query->addTag('linkit_entity_autocomplete');
|
||||
$query->addTag('linkit_entity_' . $this->target_type . '_autocomplete');
|
||||
|
||||
// Add access tag for the query.
|
||||
$query->addTag('entity_access');
|
||||
$query->addTag($this->target_type . '_access');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the label string used in the match array.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The matched entity.
|
||||
*
|
||||
* @return string
|
||||
* The label for this entity.
|
||||
*/
|
||||
protected function buildLabel($entity) {
|
||||
return Html::escape($entity->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the description string used in the match array.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The matched entity.
|
||||
*
|
||||
* @return string
|
||||
* The description for this entity.
|
||||
*/
|
||||
protected function buildDescription($entity) {
|
||||
$description = \Drupal::token()->replace($this->configuration['result_description'], [$this->target_type => $entity], []);
|
||||
return LinkitXss::descriptionFilter($description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the path string used in the match array.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The matched entity.
|
||||
*
|
||||
* @return string
|
||||
* The URL for this entity.
|
||||
*/
|
||||
protected function buildPath($entity) {
|
||||
return $entity->toUrl()->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the group string used in the match array.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The matched entity.
|
||||
*
|
||||
* @return string
|
||||
* The match group for this entity.
|
||||
*/
|
||||
protected function buildGroup($entity) {
|
||||
$group = $entity->getEntityType()->getLabel();
|
||||
|
||||
// If the entities by this entity should be grouped by bundle, get the
|
||||
// name and append it to the group.
|
||||
if ($this->configuration['group_by_bundle']) {
|
||||
$bundles = $this->entityManager->getBundleInfo($entity->getEntityTypeId());
|
||||
$bundle_label = $bundles[$entity->bundle()]['label'];
|
||||
$group .= ' - ' . $bundle_label;
|
||||
}
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\FileMatcher.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Matcher;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\linkit\Utility\LinkitXss;
|
||||
|
||||
/**
|
||||
* @Matcher(
|
||||
* id = "entity:file",
|
||||
* target_entity = "file",
|
||||
* label = @Translation("File"),
|
||||
* provider = "file"
|
||||
* )
|
||||
*/
|
||||
class FileMatcher extends EntityMatcher {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSummary() {
|
||||
$summery = parent::getSummary();
|
||||
|
||||
$summery[] = $this->t('Show image dimensions: @show_image_dimensions', [
|
||||
'@show_image_dimensions' => $this->configuration['images']['show_dimensions'] ? $this->t('Yes') : $this->t('No'),
|
||||
]);
|
||||
|
||||
$summery[] = $this->t('Show image thumbnail: @show_image_thumbnail', [
|
||||
'@show_image_thumbnail' => $this->configuration['images']['show_thumbnail'] ? $this->t('Yes') : $this->t('No'),
|
||||
]);
|
||||
|
||||
if ($this->moduleHandler->moduleExists('image') && $this->configuration['images']['show_thumbnail']) {
|
||||
$image_style = ImageStyle::load($this->configuration['images']['thumbnail_image_style']);
|
||||
if (!is_null($image_style)) {
|
||||
$summery[] = $this->t('Thumbnail style: @thumbnail_style', [
|
||||
'@thumbnail_style' => $image_style->label(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $summery;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return parent::defaultConfiguration() + [
|
||||
'images' => [
|
||||
'show_dimensions' => FALSE,
|
||||
'show_thumbnail' => FALSE,
|
||||
'thumbnail_image_style' => 'linkit_result_thumbnail',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = parent::calculateDependencies() + [
|
||||
'module' => ['file'],
|
||||
];
|
||||
|
||||
if ($this->configuration['images']['show_thumbnail']) {
|
||||
$dependencies['module'][] = 'image';
|
||||
$dependencies['config'][] = 'image.style.' . $this->configuration['images']['thumbnail_image_style'];
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildConfigurationForm($form, $form_state);
|
||||
|
||||
$form['images'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => t('Image file settings'),
|
||||
'#description' => t('Extra settings for image files in the result.'),
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
$form['images']['show_dimensions'] = [
|
||||
'#title' => t('Show pixel dimensions'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => $this->configuration['images']['show_dimensions'],
|
||||
];
|
||||
|
||||
if ($this->moduleHandler->moduleExists('image')) {
|
||||
$form['images']['show_thumbnail'] = [
|
||||
'#title' => t('Show thumbnail'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => $this->configuration['images']['show_thumbnail'],
|
||||
];
|
||||
|
||||
$form['images']['thumbnail_image_style'] = [
|
||||
'#title' => t('Thumbnail image style'),
|
||||
'#type' => 'select',
|
||||
'#default_value' => $this->configuration['images']['thumbnail_image_style'],
|
||||
'#options' => image_style_options(FALSE),
|
||||
'#states' => [
|
||||
'visible' => [
|
||||
':input[name="images[show_thumbnail]"]' => ['checked' => TRUE],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitConfigurationForm($form, $form_state);
|
||||
|
||||
$values = $form_state->getValue('images');
|
||||
if (!$values['show_thumbnail']) {
|
||||
$values['thumbnail_image_style'] = NULL;
|
||||
}
|
||||
|
||||
$this->configuration['images'] = $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildEntityQuery($match) {
|
||||
$query = parent::buildEntityQuery($match);
|
||||
$query->condition('status', FILE_STATUS_PERMANENT);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildDescription($entity) {
|
||||
$description_array = array();
|
||||
|
||||
$description_array[] = parent::buildDescription($entity);
|
||||
|
||||
/** @var \Drupal\file\FileInterface $entity */
|
||||
$file = $entity->getFileUri();
|
||||
|
||||
/** @var \Drupal\Core\Image\ImageInterface $image */
|
||||
$image = \Drupal::service('image.factory')->get($file);
|
||||
if ($image->isValid()) {
|
||||
if ($this->configuration['images']['show_dimensions']) {
|
||||
$description_array[] = $image->getWidth() . 'x' . $image->getHeight() . 'px';
|
||||
}
|
||||
|
||||
if ($this->configuration['images']['show_thumbnail'] && $this->moduleHandler->moduleExists('image')) {
|
||||
$image_element = array(
|
||||
'#weight' => -10,
|
||||
'#theme' => 'image_style',
|
||||
'#style_name' => $this->configuration['images']['thumbnail_image_style'],
|
||||
'#uri' => $entity->getFileUri(),
|
||||
);
|
||||
|
||||
$description_array[] = (string) \Drupal::service('renderer')->render($image_element);
|
||||
}
|
||||
}
|
||||
|
||||
$description = implode('<br />' , $description_array);
|
||||
return LinkitXss::descriptionFilter($description);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* The file entity still uses url() even though it's deprecated in the
|
||||
* entity interface.
|
||||
*/
|
||||
protected function buildPath($entity) {
|
||||
/** @var \Drupal\file\FileInterface $entity */
|
||||
return file_url_transform_relative(file_create_url($entity->getFileUri()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\NodeMatcher.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Matcher;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* @Matcher(
|
||||
* id = "entity:node",
|
||||
* target_entity = "node",
|
||||
* label = @Translation("Content"),
|
||||
* provider = "node"
|
||||
* )
|
||||
*/
|
||||
class NodeMatcher extends EntityMatcher {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSummary() {
|
||||
$summery = parent::getSummary();
|
||||
|
||||
$summery[] = $this->t('Include unpublished: @include_unpublished', [
|
||||
'@include_unpublished' => $this->configuration['include_unpublished'] ? $this->t('Yes') : $this->t('No'),
|
||||
]);
|
||||
|
||||
return $summery;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return parent::defaultConfiguration() + [
|
||||
'include_unpublished' => FALSE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
return parent::calculateDependencies() + [
|
||||
'module' => ['node'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildConfigurationForm($form, $form_state);
|
||||
|
||||
$form['include_unpublished'] = [
|
||||
'#title' => t('Include unpublished nodes'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => $this->configuration['include_unpublished'],
|
||||
'#description' => t('In order to see unpublished nodes, the requesting user must also have permissions to do so.'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitConfigurationForm($form, $form_state);
|
||||
|
||||
$this->configuration['include_unpublished'] = $form_state->getValue('include_unpublished');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildEntityQuery($match) {
|
||||
$query = parent::buildEntityQuery($match);
|
||||
|
||||
$no_access = !$this->currentUser->hasPermission('bypass node access') && !count($this->moduleHandler->getImplementations('node_grants'));
|
||||
if ($this->configuration['include_unpublished'] !== TRUE || $no_access) {
|
||||
$query->condition('status', NODE_PUBLISHED);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\TermMatcher.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Matcher;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\linkit\Utility\LinkitXss;
|
||||
|
||||
/**
|
||||
* @Matcher(
|
||||
* id = "entity:taxonomy_term",
|
||||
* target_entity = "taxonomy_term",
|
||||
* label = @Translation("Taxonomy term"),
|
||||
* provider = "taxonomy"
|
||||
* )
|
||||
*/
|
||||
class TermMatcher extends EntityMatcher {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
return parent::calculateDependencies() + [
|
||||
'module' => ['taxonomy'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildConfigurationForm($form, $form_state);
|
||||
$this->insertTokenList($form, ['term']);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildDescription($entity) {
|
||||
$description = \Drupal::token()->replace($this->configuration['result_description'], ['term' => $entity], []);
|
||||
return LinkitXss::descriptionFilter($description);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\UserMatcher.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Plugin\Linkit\Matcher;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* @Matcher(
|
||||
* id = "entity:user",
|
||||
* target_entity = "user",
|
||||
* label = @Translation("User"),
|
||||
* provider = "user"
|
||||
* )
|
||||
*/
|
||||
class UserMatcher extends EntityMatcher {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSummary() {
|
||||
$summery = parent::getSummary();
|
||||
|
||||
$roles = !empty($this->configuration['roles']) ? $this->configuration['roles'] : ['None'];
|
||||
$summery[] = $this->t('Role filter: @role_filter', [
|
||||
'@role_filter' => implode(', ', $roles),
|
||||
]);
|
||||
|
||||
$summery[] = $this->t('Include blocked users: @include_blocked', [
|
||||
'@include_blocked' => $this->configuration['include_blocked'] ? $this->t('Yes') : $this->t('No'),
|
||||
]);
|
||||
|
||||
return $summery;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return parent::defaultConfiguration() + [
|
||||
'roles' => [],
|
||||
'include_blocked' => FALSE,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
return parent::calculateDependencies() + [
|
||||
'module' => ['user'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildConfigurationForm($form, $form_state);
|
||||
|
||||
$form['roles'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Restrict to the selected roles'),
|
||||
'#options' => array_diff_key(user_role_names(TRUE), array(RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID)),
|
||||
'#default_value' => $this->configuration['roles'],
|
||||
'#description' => $this->t('If none of the checkboxes is checked, allow all roles.'),
|
||||
'#element_validate' => [[get_class($this), 'elementValidateFilter']],
|
||||
);
|
||||
|
||||
$form['include_blocked'] = [
|
||||
'#title' => t('Include blocked user'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => $this->configuration['include_blocked'],
|
||||
'#description' => t('In order to see blocked users, the requesting user must also have permissions to do so.'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitConfigurationForm($form, $form_state);
|
||||
|
||||
$this->configuration['roles'] = $form_state->getValue('roles');
|
||||
$this->configuration['include_blocked'] = $form_state->getValue('include_blocked');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildEntityQuery($match) {
|
||||
$query = parent::buildEntityQuery($match);
|
||||
|
||||
$match = $this->database->escapeLike($match);
|
||||
// The user entity don't specify a label key so we have to do it instead.
|
||||
$query->condition('name', '%' . $match . '%', 'LIKE');
|
||||
|
||||
// Filter by role.
|
||||
if (!empty($this->configuration['roles'])) {
|
||||
$query->condition('roles', $this->configuration['roles'], 'IN');
|
||||
}
|
||||
|
||||
if ($this->configuration['include_blocked'] !== TRUE || !$this->currentUser->hasPermission('administer users')) {
|
||||
$query->condition('status', 1);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\ProfileInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a profile entity.
|
||||
*/
|
||||
interface ProfileInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Gets the profile description.
|
||||
*
|
||||
* @return string
|
||||
* The profile description.
|
||||
*/
|
||||
public function getDescription();
|
||||
|
||||
/**
|
||||
* Sets the profile description.
|
||||
*
|
||||
* @param string $description
|
||||
* The profile description.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDescription($description);
|
||||
|
||||
/**
|
||||
* Returns a specific attribute.
|
||||
*
|
||||
* @param string $attribute_id
|
||||
* The attribute ID.
|
||||
*
|
||||
* @return \Drupal\linkit\AttributeInterface
|
||||
* The attribute object.
|
||||
*/
|
||||
public function getAttribute($attribute_id);
|
||||
|
||||
/**
|
||||
* Returns the attributes for this profile.
|
||||
*
|
||||
* @return \Drupal\linkit\AttributeCollection|\Drupal\linkit\AttributeInterface[]
|
||||
* The attribute collection.
|
||||
*/
|
||||
public function getAttributes();
|
||||
|
||||
/**
|
||||
* Adds an attribute to this profile.
|
||||
*
|
||||
* @param array $configuration
|
||||
* An array of attribute configuration.
|
||||
*
|
||||
* @return String
|
||||
* The ID of the attribute.
|
||||
*/
|
||||
public function addAttribute(array $configuration);
|
||||
|
||||
/**
|
||||
* Removes an attribute from this profile.
|
||||
*
|
||||
* @param string $attribute_id
|
||||
* The attribute ID.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeAttribute($attribute_id);
|
||||
|
||||
/**
|
||||
* Sets the configuration for an attribute instance.
|
||||
*
|
||||
* @param string $attribute_id
|
||||
* The ID of the attribute to set the configuration for.
|
||||
* @param array $configuration
|
||||
* The attribute configuration to set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAttributeConfig($attribute_id, array $configuration);
|
||||
|
||||
/**
|
||||
* Returns a specific matcher.
|
||||
*
|
||||
* @param string $instance_id
|
||||
* The matcher instance ID.
|
||||
*
|
||||
* @return \Drupal\linkit\MatcherInterface
|
||||
* The matcher object.
|
||||
*/
|
||||
public function getMatcher($instance_id);
|
||||
|
||||
/**
|
||||
* Returns the matchers for this profile.
|
||||
*
|
||||
* @return \Drupal\linkit\MatcherCollection|\Drupal\linkit\MatcherInterface[]
|
||||
* The matcher collection.
|
||||
*/
|
||||
public function getMatchers();
|
||||
|
||||
/**
|
||||
* Adds a matcher to this profile.
|
||||
*
|
||||
* @param array $configuration
|
||||
* An array of matcher configuration.
|
||||
*
|
||||
* @return string
|
||||
* The instance ID of the matcher.
|
||||
*/
|
||||
public function addMatcher(array $configuration);
|
||||
|
||||
/**
|
||||
* Removes a matcher from this profile.
|
||||
*
|
||||
* @param \Drupal\linkit\MatcherInterface $matcher
|
||||
* The matcher object.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeMatcher(MatcherInterface $matcher);
|
||||
|
||||
/**
|
||||
* Sets the configuration for a matcher instance.
|
||||
*
|
||||
* @param string $instance_id
|
||||
* The instance ID of the matcher to set the configuration for.
|
||||
* @param array $configuration
|
||||
* The matcher configuration to set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setMatcherConfig($instance_id, array $configuration);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\ProfileListBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of profile entities.
|
||||
*
|
||||
* @see \Drupal\linkit\Entity\Profile
|
||||
*/
|
||||
class ProfileListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['title'] = t('Profile');
|
||||
$header['description'] = [
|
||||
'data' => t('Description'),
|
||||
'class' => [RESPONSIVE_PRIORITY_MEDIUM],
|
||||
];
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
/** @var \Drupal\linkit\ProfileInterface $linkitProfile */
|
||||
$linkitProfile = $entity;
|
||||
$row['label'] = $linkitProfile->label();
|
||||
$row['description']['data'] = ['#markup' => $linkitProfile->getDescription()];
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
|
||||
if (isset($operations['edit'])) {
|
||||
$operations['edit']['title'] = t('Edit profile');
|
||||
}
|
||||
|
||||
$operations['matchers'] = [
|
||||
'title' => t('Manage matchers'),
|
||||
'weight' => 10,
|
||||
'url' => Url::fromRoute('linkit.matchers', [
|
||||
'linkit_profile' => $entity->id()
|
||||
]),
|
||||
];
|
||||
|
||||
$operations['attributes'] = [
|
||||
'title' => t('Manage attributes'),
|
||||
'weight' => 20,
|
||||
'url' => Url::fromRoute('linkit.attributes', [
|
||||
'linkit_profile' => $entity->id()
|
||||
]),
|
||||
];
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\ResultManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit;
|
||||
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Result service to handle autocomplete matcher results.
|
||||
*/
|
||||
class ResultManager {
|
||||
|
||||
/**
|
||||
* Gets the results.
|
||||
*
|
||||
* @param ProfileInterface $linkitProfile
|
||||
* The linkit profile.
|
||||
* @param $search_string
|
||||
* The string ro use in the matchers.
|
||||
*
|
||||
* @return array
|
||||
* An array of matches.
|
||||
*/
|
||||
public function getResults(ProfileInterface $linkitProfile, $search_string) {
|
||||
$matches = array();
|
||||
|
||||
if (empty(trim($search_string))) {
|
||||
return [[
|
||||
'title' => t('No results'),
|
||||
]];
|
||||
}
|
||||
|
||||
// Special for link to front page.
|
||||
if (strpos($search_string, 'front') !== FALSE) {
|
||||
$matches[] = [
|
||||
'title' => t('Front page'),
|
||||
'description' => 'The front page for this site.',
|
||||
'path' => Url::fromRoute('<front>')->toString(),
|
||||
'group' => t('System'),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($linkitProfile->getMatchers() as $plugin) {
|
||||
$matches = array_merge($matches, $plugin->getMatches($search_string));
|
||||
}
|
||||
|
||||
// Check for an e-mail address then return an e-mail match and create a
|
||||
// mail-to link if appropriate.
|
||||
if (filter_var($search_string, FILTER_VALIDATE_EMAIL)) {
|
||||
$matches[] = [
|
||||
'title' => t('E-mail @email', ['@email' => $search_string]),
|
||||
'description' => t('Opens your mail client ready to e-mail @email', ['@email' => $search_string]),
|
||||
'path' => 'mailto:' . Html::escape($search_string),
|
||||
'group' => t('E-mail'),
|
||||
];
|
||||
}
|
||||
|
||||
// If there is still no matches, return a "no results" array.
|
||||
if (empty($matches)) {
|
||||
return [[
|
||||
'title' => t('No results'),
|
||||
]];
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Tests\AttributeCrudTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Tests;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\Entity\Profile;
|
||||
|
||||
/**
|
||||
* Tests adding, listing and deleting attributes on a profile.
|
||||
*
|
||||
* @group linkit
|
||||
*/
|
||||
class AttributeCrudTest extends LinkitTestBase {
|
||||
|
||||
/**
|
||||
* The attribute manager.
|
||||
*
|
||||
* @var \Drupal\linkit\AttributeManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* The linkit profile.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->manager = $this->container->get('plugin.manager.linkit.attribute');
|
||||
|
||||
$this->linkitProfile = $this->createProfile();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the overview page.
|
||||
*/
|
||||
function testOverview() {
|
||||
$this->drupalGet(Url::fromRoute('linkit.attributes', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
$this->assertText(t('No attributes added.'));
|
||||
|
||||
$this->assertLinkByHref(Url::fromRoute('linkit.attribute.add', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
])->toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding an attribute to a profile.
|
||||
*/
|
||||
function testAdd() {
|
||||
$this->drupalGet(Url::fromRoute('linkit.attribute.add', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
|
||||
$this->assertEqual(count($this->manager->getDefinitions()), count($this->xpath('//input[@type="radio"]')), 'All attributes are available.');
|
||||
|
||||
$edit = array();
|
||||
$edit['plugin'] = 'dummy_attribute';
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
|
||||
|
||||
$this->assertUrl(Url::fromRoute('linkit.attributes', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
|
||||
$this->assertEqual(1, count($this->xpath('//table/tbody/tr')), 'Attribute added.');
|
||||
$this->assertNoText(t('No attributes added.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding a configurable attribute to a profile.
|
||||
*/
|
||||
function testAddConfigurable() {
|
||||
$this->drupalGet(Url::fromRoute('linkit.attribute.add', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
|
||||
$this->assertEqual(count($this->manager->getDefinitions()), count($this->xpath('//input[@type="radio"]')), 'All attributes are available.');
|
||||
|
||||
$edit = array();
|
||||
$edit['plugin'] = 'configurable_dummy_attribute';
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
|
||||
|
||||
$this->assertUrl(Url::fromRoute('linkit.attribute.edit', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => 'configurable_dummy_attribute',
|
||||
]));
|
||||
|
||||
$this->drupalGet(Url::fromRoute('linkit.attributes', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
|
||||
$this->assertEqual(1, count($this->xpath('//table/tbody/tr')), 'Attribute added.');
|
||||
$this->assertNoText(t('No attributes added.'));
|
||||
|
||||
$plugin_url = Url::fromRoute('linkit.attribute.edit', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => 'configurable_dummy_attribute',
|
||||
]);
|
||||
|
||||
$this->assertLinkByHref($plugin_url->toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test delete an attribute from a profile.
|
||||
*/
|
||||
function testDelete() {
|
||||
/** @var \Drupal\linkit\AttributeInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('dummy_attribute');
|
||||
|
||||
$this->linkitProfile->addAttribute($plugin->getConfiguration());
|
||||
$this->linkitProfile->save();
|
||||
|
||||
// Try delete an attribute that is not attached to the profile.
|
||||
$this->drupalGet(Url::fromRoute('linkit.attribute.delete', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => 'doesntexists'
|
||||
]));
|
||||
$this->assertResponse('404');
|
||||
|
||||
// Go to the delete page, but press cancel.
|
||||
$this->drupalGet(Url::fromRoute('linkit.attribute.delete', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $plugin->getPluginId(),
|
||||
]));
|
||||
$this->clickLink(t('Cancel'));
|
||||
$this->assertUrl(Url::fromRoute('linkit.attributes', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
|
||||
// Delete the attribute from the profile.
|
||||
$this->drupalGet(Url::fromRoute('linkit.attribute.delete', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => 'dummy_attribute',
|
||||
]));
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Confirm'));
|
||||
$this->assertRaw(t('The attribute %plugin has been deleted.', ['%plugin' => $plugin->getLabel()]));
|
||||
$this->assertUrl(Url::fromRoute('linkit.attributes', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
$this->assertText(t('No attributes added.'));
|
||||
|
||||
/** @var \Drupal\linkit\Entity\Profile $updated_profile */
|
||||
$updated_profile = Profile::load($this->linkitProfile->id());
|
||||
$this->assertFalse($updated_profile->getAttributes()->has($plugin->getPluginId()), 'The attribute is deleted from the profile');
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Tests\Controllers\LinkitControllerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Tests\Controllers;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\Tests\LinkitTestBase;
|
||||
|
||||
|
||||
/**
|
||||
* Tests Linkit controller.
|
||||
*
|
||||
* @group linkit
|
||||
*/
|
||||
class LinkitControllerTest extends LinkitTestBase {
|
||||
|
||||
/**
|
||||
* The linkit profile.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->linkitProfile = $this->createProfile();
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the profile route title callback.
|
||||
*/
|
||||
function testProfileTitle() {
|
||||
$this->drupalGet(Url::fromRoute('entity.linkit_profile.edit_form', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
|
||||
$this->assertText('Edit ' . $this->linkitProfile->label() . ' profile');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the matcher route title callback.
|
||||
*/
|
||||
function testMatcherTitle() {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->container->get('plugin.manager.linkit.matcher')->createInstance('configurable_dummy_matcher');
|
||||
$matcher_uuid = $this->linkitProfile->addMatcher($plugin->getConfiguration());
|
||||
$this->linkitProfile->save();
|
||||
|
||||
$this->drupalGet(Url::fromRoute('linkit.matcher.edit', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $matcher_uuid,
|
||||
]));
|
||||
|
||||
$this->assertText('Edit ' . $plugin->getLabel() . ' matcher');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the attribute route title callback.
|
||||
*/
|
||||
function testAttributeTitle() {
|
||||
/** @var \Drupal\linkit\AttributeInterface $plugin */
|
||||
$plugin = $this->container->get('plugin.manager.linkit.attribute')->createInstance('configurable_dummy_attribute');
|
||||
$this->linkitProfile->addAttribute($plugin->getConfiguration());
|
||||
$this->linkitProfile->save();
|
||||
|
||||
$this->drupalGet(Url::fromRoute('linkit.attribute.edit', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $plugin->getPluginId(),
|
||||
]));
|
||||
$this->assertText('Edit ' . $plugin->getLabel() . ' attribute');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Tests\LinkitTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\linkit\Entity\Profile;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Sets up page and article content types.
|
||||
*/
|
||||
abstract class LinkitTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* Enable block module to get the local_actions_block to work.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['linkit', 'linkit_test', 'block'];
|
||||
|
||||
/**
|
||||
* A user with the 'administer linkit profiles' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* A user without the 'administer linkit profiles' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $baseUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->adminUser = $this->drupalCreateUser(['administer linkit profiles']);
|
||||
$this->baseUser = $this->drupalCreateUser();
|
||||
|
||||
$this->drupalPlaceBlock('page_title_block', ['region' => 'content']);
|
||||
$this->drupalPlaceBlock('local_tasks_block', ['region' => 'content']);
|
||||
$this->drupalPlaceBlock('local_actions_block', ['region' => 'content']);
|
||||
$this->drupalPlaceBlock('system_messages_block', ['region' => 'highlighted']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a profile based on default settings.
|
||||
*
|
||||
* @param array $settings
|
||||
* (optional) An associative array of settings for the profile, as used in
|
||||
* entity_create(). Override the defaults by specifying the key and value
|
||||
* in the array
|
||||
*
|
||||
* The following defaults are provided:
|
||||
* - label: Random string.
|
||||
*
|
||||
* @return \Drupal\linkit\ProfileInterface
|
||||
* The created profile entity.
|
||||
*/
|
||||
protected function createProfile(array $settings = []) {
|
||||
// Populate defaults array.
|
||||
$settings += [
|
||||
'id' => Unicode::strtolower($this->randomMachineName()),
|
||||
'label' => $this->randomMachineName(),
|
||||
];
|
||||
|
||||
$profile = Profile::create($settings);
|
||||
$profile->save();
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Tests\MatcherCrudTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Tests;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\linkit\Entity\Profile;
|
||||
|
||||
/**
|
||||
* Tests adding, listing, updating and deleting matchers on a profile.
|
||||
*
|
||||
* @group linkit
|
||||
*/
|
||||
class MatcherCrudTest extends LinkitTestBase {
|
||||
|
||||
/**
|
||||
* The attribute manager.
|
||||
*
|
||||
* @var \Drupal\linkit\MatcherManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* The linkit profile.
|
||||
*
|
||||
* @var \Drupal\linkit\ProfileInterface
|
||||
*/
|
||||
protected $linkitProfile;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->manager = $this->container->get('plugin.manager.linkit.matcher');
|
||||
|
||||
$this->linkitProfile = $this->createProfile();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the overview page.
|
||||
*/
|
||||
function testOverview() {
|
||||
$this->drupalGet(Url::fromRoute('linkit.matchers', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
$this->assertText(t('No matchers added.'));
|
||||
|
||||
$this->assertLinkByHref(Url::fromRoute('linkit.matcher.add', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
])->toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding a matcher to a profile.
|
||||
*/
|
||||
function testAdd() {
|
||||
$this->drupalGet(Url::fromRoute('linkit.matcher.add', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
|
||||
$edit = array();
|
||||
$edit['plugin'] = 'dummy_matcher';
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
|
||||
|
||||
// Load the saved profile.
|
||||
$this->linkitProfile = Profile::load($this->linkitProfile->id());
|
||||
|
||||
$matcher_ids = $this->linkitProfile->getMatchers()->getInstanceIds();
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->linkitProfile->getMatcher(current($matcher_ids));
|
||||
|
||||
$this->assertRaw(t('Added %label matcher.', ['%label' => $plugin->getLabel()]));
|
||||
$this->assertNoText(t('No matchers added.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding a configurable attribute to a profile.
|
||||
*/
|
||||
function testAddConfigurable() {
|
||||
$this->drupalGet(Url::fromRoute('linkit.matcher.add', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
|
||||
$edit = array();
|
||||
$edit['plugin'] = 'configurable_dummy_matcher';
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
|
||||
|
||||
// Load the saved profile.
|
||||
$this->linkitProfile = Profile::load($this->linkitProfile->id());
|
||||
|
||||
$matcher_ids = $this->linkitProfile->getMatchers()->getInstanceIds();
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->linkitProfile->getMatcher(current($matcher_ids));
|
||||
|
||||
$this->assertUrl(Url::fromRoute('linkit.matcher.edit', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
'plugin_instance_id' => $plugin->getUuid(),
|
||||
]));
|
||||
|
||||
$this->drupalGet(Url::fromRoute('linkit.matchers', [
|
||||
'linkit_profile' => $this->linkitProfile->id(),
|
||||
]));
|
||||
|
||||
$this->assertNoText(t('No matchers added.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test delete a matcher from a profile.
|
||||
*/
|
||||
function testDelete() {
|
||||
/** @var \Drupal\linkit\AttributeInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('dummy_matcher');
|
||||
|
||||
$profile = $this->createProfile();
|
||||
$plugin_uuid = $profile->addMatcher($plugin->getConfiguration());
|
||||
$profile->save();
|
||||
|
||||
// Try delete a matcher that is not attached to the profile.
|
||||
$this->drupalGet(Url::fromRoute('linkit.matcher.delete', [
|
||||
'linkit_profile' => $profile->id(),
|
||||
'plugin_instance_id' => 'doesntexists'
|
||||
]));
|
||||
$this->assertResponse('404');
|
||||
|
||||
// Go to the delete page, but press cancel.
|
||||
$this->drupalGet(Url::fromRoute('linkit.matcher.delete', [
|
||||
'linkit_profile' => $profile->id(),
|
||||
'plugin_instance_id' => $plugin_uuid,
|
||||
]));
|
||||
$this->clickLink(t('Cancel'));
|
||||
$this->assertUrl(Url::fromRoute('linkit.matchers', [
|
||||
'linkit_profile' => $profile->id(),
|
||||
]));
|
||||
|
||||
// Delete the matcher from the profile.
|
||||
$this->drupalGet(Url::fromRoute('linkit.matcher.delete', [
|
||||
'linkit_profile' => $profile->id(),
|
||||
'plugin_instance_id' => $plugin_uuid,
|
||||
]));
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Confirm'));
|
||||
$this->assertRaw(t('The matcher %plugin has been deleted.', ['%plugin' => $plugin->getLabel()]));
|
||||
$this->assertUrl(Url::fromRoute('linkit.matchers', [
|
||||
'linkit_profile' => $profile->id(),
|
||||
]));
|
||||
$this->assertText(t('No matchers added.'));
|
||||
|
||||
/** @var \Drupal\linkit\Entity\Profile $updated_profile */
|
||||
$updated_profile = Profile::load($profile->id());
|
||||
$this->assertFalse($updated_profile->getMatchers()->has($plugin_uuid), 'The user matcher is deleted from the profile');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Tests\Matchers\NodeMatcherTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Tests\Matchers;
|
||||
|
||||
use Drupal\linkit\Tests\LinkitTestBase;
|
||||
|
||||
/**
|
||||
* Tests node matcher.
|
||||
*
|
||||
* @group linkit
|
||||
*/
|
||||
class NodeMatcherTest extends LinkitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node'];
|
||||
|
||||
/**
|
||||
* The matcher manager.
|
||||
*
|
||||
* @var \Drupal\linkit\MatcherManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->manager = $this->container->get('plugin.manager.linkit.matcher');
|
||||
|
||||
$type1 = $this->drupalCreateContentType(['type' => 'test1', 'name' => 'Test1']);
|
||||
$type2 = $this->drupalCreateContentType(['type' => 'test2', 'name' => 'Test2']);
|
||||
|
||||
// Nodes with type 1.
|
||||
$this->drupalCreateNode(['title' => 'Lorem Ipsum 1', 'type' => $type1->id()]);
|
||||
$this->drupalCreateNode(['title' => 'Lorem Ipsum 2', 'type' => $type1->id()]);
|
||||
|
||||
// Nodes with type 1.
|
||||
$this->drupalCreateNode(['title' => 'Lorem Ipsum 3', 'type' => $type2->id()]);
|
||||
|
||||
// Unpublished node.
|
||||
$this->drupalCreateNode(['title' => 'Lorem unpublishd', 'type' => $type1->id(), 'status' => FALSE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node matcher.
|
||||
*/
|
||||
function testNodeMatcherWidthDefaultConfiguration() {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('entity:node', []);
|
||||
$matches = $plugin->getMatches('Lorem');
|
||||
$this->assertEqual(3, count($matches), 'Correct number of matches');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node matcher with bundle filer.
|
||||
*/
|
||||
function testNodeMatcherWidthBundleFiler() {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('entity:node', [
|
||||
'settings' => [
|
||||
'bundles' => [
|
||||
'test1' => 'test1'
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$matches = $plugin->getMatches('Lorem');
|
||||
$this->assertEqual(2, count($matches), 'Correct number of matches');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests node matcher with include unpublished setting activated.
|
||||
*/
|
||||
function testNodeMatcherWidthIncludeUnpublished() {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('entity:node', [
|
||||
'settings' => [
|
||||
'include_unpublished' => TRUE,
|
||||
],
|
||||
]);
|
||||
|
||||
// Test without permissions to see unpublished nodes.
|
||||
$matches = $plugin->getMatches('Lorem');
|
||||
$this->assertEqual(3, count($matches), 'Correct number of matches');
|
||||
|
||||
$account = $this->drupalCreateUser(['bypass node access']);
|
||||
$this->drupalLogin($account);
|
||||
|
||||
// Test with permissions to see unpublished nodes.
|
||||
$matches = $plugin->getMatches('Lorem');
|
||||
$this->assertEqual(4, count($matches), 'Correct number of matches');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Tests\Matchers\TermMatcherTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Tests\Matchers;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\linkit\Tests\LinkitTestBase;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests term matcher.
|
||||
*
|
||||
* @group linkit
|
||||
*/
|
||||
class TermMatcherTest extends LinkitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['taxonomy'];
|
||||
|
||||
/**
|
||||
* The matcher manager.
|
||||
*
|
||||
* @var \Drupal\linkit\MatcherManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* Creates and saves a vocabulary.
|
||||
*
|
||||
* @param string $name
|
||||
* The vocabulary name.
|
||||
*
|
||||
* @return Vocabulary The new vocabulary object.
|
||||
* The new vocabulary object.
|
||||
*/
|
||||
private function createVocabulary($name) {
|
||||
$vocabularyStorage = \Drupal::entityTypeManager()->getStorage('taxonomy_vocabulary');
|
||||
$vocabulary = $vocabularyStorage->create([
|
||||
'name' => $name,
|
||||
'description' => $name,
|
||||
'vid' => Unicode::strtolower($name),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$vocabulary->save();
|
||||
return $vocabulary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and saves a new term with in vocabulary $vid.
|
||||
*
|
||||
* @param \Drupal\taxonomy\Entity\Vocabulary $vocabulary
|
||||
* The vocabulary object.
|
||||
* @param array $values
|
||||
* (optional) An array of values to set, keyed by property name. If the
|
||||
* entity type has bundles, the bundle key has to be specified.
|
||||
*
|
||||
* @return \Drupal\taxonomy\Entity\Term
|
||||
* The new taxonomy term object.
|
||||
*/
|
||||
private function createTerm(Vocabulary $vocabulary, $values = array()) {
|
||||
$filter_formats = filter_formats();
|
||||
$format = array_pop($filter_formats);
|
||||
|
||||
$termStorage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
|
||||
$term = $termStorage->create($values + array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => array(
|
||||
'value' => $this->randomMachineName(),
|
||||
// Use the first available text format.
|
||||
'format' => $format->id(),
|
||||
),
|
||||
'vid' => $vocabulary->id(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
$term->save();
|
||||
return $term;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->manager = $this->container->get('plugin.manager.linkit.matcher');
|
||||
|
||||
$testing_vocabulary_1 = $this->createVocabulary('testing_vocabulary_1');
|
||||
$testing_vocabulary_2 = $this->createVocabulary('testing_vocabulary_2');
|
||||
|
||||
$this->createTerm($testing_vocabulary_1, ['name' => 'foo_bar']);
|
||||
$this->createTerm($testing_vocabulary_1, ['name' => 'foo_baz']);
|
||||
$this->createTerm($testing_vocabulary_1, ['name' => 'foo_foo']);
|
||||
$this->createTerm($testing_vocabulary_1, ['name' => 'bar']);
|
||||
$this->createTerm($testing_vocabulary_2, ['name' => 'foo_bar']);
|
||||
$this->createTerm($testing_vocabulary_2, ['name' => 'foo_baz']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests term matcher with default configuration.
|
||||
*/
|
||||
function testTermMatcherWidthDefaultConfiguration() {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('entity:taxonomy_term', []);
|
||||
$matches = $plugin->getMatches('foo');
|
||||
$this->assertEqual(5, count($matches), 'Correct number of matches');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests term matcher with bundle filer.
|
||||
*/
|
||||
function testTermMatcherWidthBundleFiler() {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('entity:taxonomy_term', [
|
||||
'settings' => [
|
||||
'bundles' => [
|
||||
'testing_vocabulary_1' => 'testing_vocabulary_1'
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$matches = $plugin->getMatches('foo');
|
||||
$this->assertEqual(3, count($matches), 'Correct number of matches');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Tests\Matchers\UserMatcherTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Tests\Matchers;
|
||||
|
||||
use Drupal\linkit\Tests\LinkitTestBase;
|
||||
|
||||
/**
|
||||
* Tests user matcher.
|
||||
*
|
||||
* @group linkit
|
||||
*/
|
||||
class UserMatcherTest extends LinkitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user'];
|
||||
|
||||
/**
|
||||
* The matcher manager.
|
||||
*
|
||||
* @var \Drupal\linkit\MatcherManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(['access user profiles']));
|
||||
$this->manager = $this->container->get('plugin.manager.linkit.matcher');
|
||||
|
||||
$custom_role = $this->drupalCreateRole(array(), 'custom_role', 'custom_role');
|
||||
$custom_role_admin = $this->drupalCreateRole(array(), 'custom_role_admin', 'custom_role_admin');
|
||||
|
||||
$this->drupalCreateUser([], 'lorem');
|
||||
$this->drupalCreateUser([], 'foo');
|
||||
|
||||
$account = $this->drupalCreateUser([], 'ipsumlorem');
|
||||
$account->addRole($custom_role);
|
||||
$account->save();
|
||||
|
||||
$account = $this->drupalCreateUser([], 'lorem_custom_role');
|
||||
$account->addRole($custom_role);
|
||||
$account->save();
|
||||
|
||||
$account = $this->drupalCreateUser([], 'lorem_custom_role_admin');
|
||||
$account->addRole($custom_role_admin);
|
||||
$account->save();
|
||||
|
||||
$account = $this->drupalCreateUser([], 'blocked_lorem');
|
||||
$account->block();
|
||||
$account->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user matcher.
|
||||
*/
|
||||
function testUserMatcherWidthDefaultConfiguration() {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('entity:user', []);
|
||||
$matches = $plugin->getMatches('Lorem');
|
||||
$this->assertEqual(4, count($matches), 'Correct number of matches');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user matcher with role filer.
|
||||
*/
|
||||
function testUserMatcherWidthRoleFiler() {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('entity:user', [
|
||||
'settings' => [
|
||||
'roles' => [
|
||||
'custom_role' => 'custom_role'
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$matches = $plugin->getMatches('Lorem');
|
||||
$this->assertEqual(2, count($matches), 'Correct number of matches');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user matcher with include blocked setting activated.
|
||||
*/
|
||||
function testUserMatcherWidthIncludeBlocked() {
|
||||
/** @var \Drupal\linkit\MatcherInterface $plugin */
|
||||
$plugin = $this->manager->createInstance('entity:user', [
|
||||
'settings' => [
|
||||
'include_blocked' => TRUE,
|
||||
],
|
||||
]);
|
||||
|
||||
// Test without permissions to see blocked users.
|
||||
$matches = $plugin->getMatches('blocked');
|
||||
$this->assertEqual(0, count($matches), 'Correct number of matches');
|
||||
|
||||
$account = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($account);
|
||||
|
||||
// Test with permissions to see blocked users.
|
||||
$matches = $plugin->getMatches('blocked');
|
||||
$this->assertEqual(1, count($matches), 'Correct number of matches');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Tests\ProfileCreationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Tests;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Tests creating, loading and deleting profiles.
|
||||
*
|
||||
* @group linkit
|
||||
*/
|
||||
class ProfileCrudTest extends LinkitTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the overview page.
|
||||
*/
|
||||
function testOverview() {
|
||||
// Verify that the profile collection page is not accessible for regular
|
||||
// users.
|
||||
$this->drupalLogin($this->baseUser);
|
||||
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
|
||||
$this->assertResponse(403);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Verify that the profile collection page is accessible for regular users.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$profiles = [];
|
||||
$profiles[] = $this->createProfile();
|
||||
$profiles[] = $this->createProfile();
|
||||
|
||||
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Assert that the 'Add profile' action exists.
|
||||
$this->assertLinkByHref(Url::fromRoute('entity.linkit_profile.add_form')->toString());
|
||||
|
||||
/** @var \Drupal\linkit\ProfileInterface $profile */
|
||||
foreach ($profiles as $profile) {
|
||||
$this->assertLinkByHref('admin/config/content/linkit/manage/' . $profile->id());
|
||||
$this->assertLinkByHref('admin/config/content/linkit/manage/' . $profile->id() . '/delete');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates profile.
|
||||
*/
|
||||
function testProfileCreation() {
|
||||
$this->drupalGet(Url::fromRoute('entity.linkit_profile.add_form'));
|
||||
$this->drupalGet('admin/config/content/linkit/add');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Create a profile.
|
||||
$edit = [];
|
||||
$edit['label'] = Unicode::strtolower($this->randomMachineName());
|
||||
$edit['id'] = Unicode::strtolower($this->randomMachineName());
|
||||
$edit['description'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and manage matchers'));
|
||||
|
||||
$this->assertRaw(t('Created new profile %label.', ['%label' => $edit['label']]));
|
||||
|
||||
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
|
||||
$this->assertText($edit['label'], 'Profile exists in the profile collection.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a profile.
|
||||
*/
|
||||
function testProfileUpdate() {
|
||||
$profile = $this->createProfile();
|
||||
$this->drupalGet(Url::fromRoute('entity.linkit_profile.edit_form', [
|
||||
'linkit_profile' => $profile->id(),
|
||||
]));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$id_field = $this->xpath('.//input[not(@disabled) and @name="id"]');
|
||||
|
||||
$this->assertTrue(empty($id_field), 'Machine name field is disabled.');
|
||||
$this->assertLinkByHref(Url::fromRoute('entity.linkit_profile.edit_form', [
|
||||
'linkit_profile' => $profile->id(),
|
||||
])->toString());
|
||||
$this->assertLinkByHref('admin/config/content/linkit/manage/' . $profile->id() . '/delete');
|
||||
|
||||
$edit = [];
|
||||
$edit['label'] = $this->randomMachineName();
|
||||
$edit['description'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm(NULL, $edit, t('Update profile'));
|
||||
|
||||
$this->assertRaw(t('Updated profile %label.', ['%label' => $edit['label']]));
|
||||
|
||||
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
|
||||
$this->assertText($edit['label'], 'Updated profile exists in the profile collection.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a profile.
|
||||
*/
|
||||
function testProfileDelete() {
|
||||
/** @var \Drupal\linkit\ProfileInterface $profile */
|
||||
$profile = $this->createProfile();
|
||||
$this->drupalGet(Url::fromRoute('entity.linkit_profile.delete_form', [
|
||||
'linkit_profile' => $profile->id(),
|
||||
]));
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
|
||||
$this->assertRaw(t('The linkit profile %label has been deleted.', ['%label' => $profile->label()]));
|
||||
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
|
||||
$this->assertNoText($profile->label(), 'Deleted profile does not exists in the profile collection.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit\Utility\LinkitXss.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit\Utility;
|
||||
|
||||
use Drupal\Component\Utility\Xss;
|
||||
|
||||
/**
|
||||
* Extends the default XSS protection to simplify it for Linkits needs.
|
||||
*/
|
||||
class LinkitXss extends Xss {
|
||||
|
||||
/**
|
||||
* Description filter helper.
|
||||
*
|
||||
* @param $string
|
||||
* The string with raw HTML in it. It will be stripped of everything that
|
||||
* can cause an XSS attack.
|
||||
*
|
||||
* @return string
|
||||
* An XSS safe version of $string, or an empty string if $string is not
|
||||
* valid UTF-8.
|
||||
*
|
||||
* @see \Drupal\Component\Utility\Xss::filter()
|
||||
*/
|
||||
public static function descriptionFilter($string) {
|
||||
return parent::filter($string, ['img'] + Xss::getHtmlTagList());
|
||||
}
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# Schema for the configuration files of the Linkit test module.
|
||||
|
||||
|
||||
# Plugin \Drupal\linkit_test\Plugin\Linkit\Attribute\ConfigurableDummyAttribute
|
||||
linkit.attribute.configurable_dummy_attribute:
|
||||
type: linkit.attribute
|
||||
mapping:
|
||||
dummy_setting:
|
||||
type: boolean
|
||||
|
||||
# Plugin \Drupal\linkit_test\Plugin\Linkit\Matcher\ConfigurableDummyMatcher
|
||||
linkit.matcher.configurable_dummy_matcher:
|
||||
type: linkit.matcher
|
||||
mapping:
|
||||
dummy_setting:
|
||||
type: boolean
|
||||
@@ -0,0 +1,16 @@
|
||||
name: 'Linkit test module'
|
||||
description: 'Support module for Linkit testing.'
|
||||
package: Testing
|
||||
type: module
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- linkit
|
||||
- field
|
||||
- text
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-03-21
|
||||
version: '8.x-4.3'
|
||||
core: '8.x'
|
||||
project: 'linkit'
|
||||
datestamp: 1490127367
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Support module for Linkit testing.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_linkit_attribute_alter().
|
||||
*/
|
||||
function linkit_test_linkit_attribute_alter(&$linkit_attribute_info) {
|
||||
if (isset($linkit_attribute_info['dummyattribute'])) {
|
||||
$linkit_attribute_info['dummyattribute']['description'] = t('Altered dummy description');
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit_test\Plugin\Linkit\Attribute\ConfigurableDummyAttribute.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit_test\Plugin\Linkit\Attribute;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\linkit\ConfigurableAttributeBase;
|
||||
|
||||
/**
|
||||
* Accesskey attribute.
|
||||
*
|
||||
* @Attribute(
|
||||
* id = "configurable_dummy_attribute",
|
||||
* label = @Translation("Configurable Dummy Attribute"),
|
||||
* html_name = "configurabledummyattribute",
|
||||
* description = @Translation("Configurable Dummy Attribute")
|
||||
* )
|
||||
*/
|
||||
class ConfigurableDummyAttribute extends ConfigurableAttributeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFormElement($default_value) {
|
||||
return [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('DummyAttribute'),
|
||||
'#default_value' => $default_value,
|
||||
'#maxlength' => 255,
|
||||
'#size' => 40,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return parent::defaultConfiguration() + [
|
||||
'dummy_setting' => FALSE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form['dummy_setting'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Dummy setting'),
|
||||
'#default_value' => $this->configuration['dummy_setting'],
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->configuration['dummy_setting'] = $form_state->getValue('dummy_setting');
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit_test\Plugin\Linkit\Attribute\DummyAttribute.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit_test\Plugin\Linkit\Attribute;
|
||||
|
||||
use Drupal\linkit\AttributeBase;
|
||||
|
||||
/**
|
||||
* Accesskey attribute.
|
||||
*
|
||||
* @Attribute(
|
||||
* id = "dummy_attribute",
|
||||
* label = @Translation("Dummy Attribute"),
|
||||
* html_name = "dummyattribute",
|
||||
* description = @Translation("Dummy Attribute")
|
||||
* )
|
||||
*/
|
||||
class DummyAttribute extends AttributeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFormElement($default_value) {
|
||||
return [
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('DummyAttribute'),
|
||||
'#default_value' => $default_value,
|
||||
'#maxlength' => 255,
|
||||
'#size' => 40,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit_test\Plugin\Linkit\Matcher\ConfigurableDummyMatcher.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit_test\Plugin\Linkit\Matcher;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\linkit\ConfigurableMatcherBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
|
||||
/**
|
||||
* @Matcher(
|
||||
* id = "configurable_dummy_matcher",
|
||||
* label = @Translation("Configurable Dummy Matcher"),
|
||||
* )
|
||||
*/
|
||||
class ConfigurableDummyMatcher extends ConfigurableMatcherBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return parent::defaultConfiguration() + [
|
||||
'dummy_setting' => FALSE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form['dummy_setting'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Dummy setting'),
|
||||
'#default_value' => $this->configuration['dummy_setting'],
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->configuration['dummy_setting'] = $form_state->getValue('dummy_setting');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMatches($string) {
|
||||
$matches[] = [
|
||||
'title' => 'Configurable Dummy Matcher title',
|
||||
'description' => 'Configurable Dummy Matcher description',
|
||||
'path' => 'http://example.com',
|
||||
'group' => 'Configurable Dummy Matcher',
|
||||
];
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\linkit_test\Plugin\Linkit\Matcher\DummyMatcher.
|
||||
*/
|
||||
|
||||
namespace Drupal\linkit_test\Plugin\Linkit\Matcher;
|
||||
|
||||
use Drupal\linkit\MatcherBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
|
||||
/**
|
||||
* @Matcher(
|
||||
* id = "dummy_matcher",
|
||||
* label = @Translation("Dummy Matcher"),
|
||||
* )
|
||||
*/
|
||||
class DummyMatcher extends MatcherBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMatches($string) {
|
||||
$matches[] = [
|
||||
'title' => 'DummyMatcher title',
|
||||
'description' => 'DummyMatcher description',
|
||||
'path' => 'http://example.com',
|
||||
'group' => 'DummyMatcher',
|
||||
];
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user