Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d555a596e | ||
|
|
d485286289 | ||
|
|
97dd8cdf97 | ||
|
|
97ec3584d8 | ||
|
|
e9a00903a9 | ||
|
|
9c6fe22012 | ||
|
|
741849397e | ||
|
|
679e1f09ad | ||
|
|
162ab2b949 | ||
|
|
c0b6dc6b75 | ||
|
|
848f03f4a4 | ||
|
|
ac821854ce | ||
|
|
1ae73c1c12 | ||
|
|
07a138cfe6 | ||
|
|
1d3a3dd6fd | ||
|
|
b33159e3d0 |
@@ -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,63 @@
|
||||
|
||||
-- SUMMARY --
|
||||
|
||||
The Synonyms module enriches content Entities with the notion of synonyms.
|
||||
Currently the module provides the following functionality:
|
||||
* support of synonyms through Fields both base and attached ones. Any field,
|
||||
for which synonyms provider exists, can be enabled as source of synonyms.
|
||||
* synonyms-friendly autocomplete and select widgets for entity reference field
|
||||
type.
|
||||
* integration with Views: Synonyms module provides a few filters and contextual
|
||||
filters that allow filtering not only by entity name but also by one of its
|
||||
synonyms.
|
||||
* 2 form elements are provided for developers: synonyms-friendly entity
|
||||
autocomplete and select.
|
||||
|
||||
-- REQUIREMENTS --
|
||||
|
||||
The Synonyms module requires only Drupal core.
|
||||
|
||||
-- SUPPORTED SYNONYMS PROVIDERS --
|
||||
|
||||
Module ships with ability to provide synonyms from the following field types:
|
||||
* "Text" field type
|
||||
* "Entity Reference" field type
|
||||
* "Number" field type
|
||||
* "Float" field type
|
||||
* "Decimal" field type
|
||||
* "Email" field type
|
||||
* "Telephone" field type
|
||||
|
||||
Worth mentioning here: this list is easily extended further by implementing new
|
||||
synonyms providers in your code. Refer to Synonyms documentation for more
|
||||
details on how to accomplish it.
|
||||
|
||||
-- GRANULATION WITHIN SYNONYMS BEHAVIOR --
|
||||
|
||||
In order to achieve greater flexibility, this module introduced additional
|
||||
granularity into what "synonyms" mean. This granularity is expressed via
|
||||
"synonyms behavior" idea whatsoever. Therefore each synonyms behavior may have
|
||||
its own synonyms provider than you can enable and configure through admin UI of
|
||||
the Synonyms module. For example, field "Typos" can be part of autocomplete
|
||||
behavior, while field "Other spellings" can be part of select behavior.
|
||||
Currently the following synonym behaviors are recognized (other modules actually
|
||||
can extend this list):
|
||||
* Autocomplete - whether synonyms from this provider should participate in
|
||||
autocomplete suggestions. This module ships with synonyms friendly
|
||||
autocomplete widget and the autocomplete suggestions will be filled in with
|
||||
the synonyms from enabled providers for "autocomplete" behavior
|
||||
* Select - whether synonyms from this provider should be included in the
|
||||
synonyms friendly select widget.
|
||||
|
||||
That way on the Synonyms configuration page you will get to add/remove certain
|
||||
synonym providers for certain synonym behaviors. Also, each behavior and
|
||||
provider may have its own settings that you get to configure too.
|
||||
|
||||
-- INSTALLATION --
|
||||
|
||||
* Install as usual
|
||||
|
||||
-- CONFIGURATION --
|
||||
|
||||
* You can configure synonyms of all eligible entity types by going to Admin ->
|
||||
Structure -> Synonyms (/admin/structure/synonyms)
|
||||
@@ -0,0 +1,22 @@
|
||||
synonyms.synonym.*:
|
||||
type: config_entity
|
||||
label: 'Synonym configuration'
|
||||
mapping:
|
||||
id:
|
||||
type: string
|
||||
label: 'ID'
|
||||
provider_plugin:
|
||||
type: string
|
||||
label: 'Controlled provider plugin'
|
||||
base_provider_plugin:
|
||||
type: string
|
||||
label: 'Base ID of the controlled provider plugin'
|
||||
provider_configuration:
|
||||
type: synonyms.provider.[%parent.base_provider_plugin]
|
||||
label: 'Configuration of provider plugin'
|
||||
behavior:
|
||||
type: string
|
||||
label: 'Controlled behavior service'
|
||||
behavior_configuration:
|
||||
type: synonyms.[%parent.behavior]
|
||||
label: 'Configuration of behavior service'
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
synonyms.synonyms.behavior.select:
|
||||
type: mapping
|
||||
label: 'Settings of select synonyms behavior'
|
||||
mapping:
|
||||
wording:
|
||||
type: string
|
||||
label: 'Wording for select entry'
|
||||
|
||||
synonyms.synonyms.behavior.autocomplete:
|
||||
type: mapping
|
||||
label: 'Settings of autocomplete synonyms behavior'
|
||||
mapping:
|
||||
wording:
|
||||
type: string
|
||||
label: 'Wording for autocomplete suggestion'
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
synonyms.provider.field:
|
||||
type: mapping
|
||||
label: 'Settings of field synonyms provider'
|
||||
mapping:
|
||||
|
||||
synonyms.provider.base_field:
|
||||
type: mapping
|
||||
label: 'Settings of base field synonyms provider'
|
||||
mapping:
|
||||
|
||||
synonyms.provider.entityreference_field:
|
||||
type: mapping
|
||||
label: 'Settings of entity reference field synonyms provider'
|
||||
mapping:
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Annotation for synonyms provider plugin instance.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class SynonymsProvider extends Plugin {
|
||||
|
||||
/**
|
||||
* Machine readable name of this plugin,
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* Human readable name of this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* Synonyms behavior service ID into which this plugin provides synonyms.
|
||||
*
|
||||
* Synonyms behaviors are the services with the tag 'synonyms_behavior'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $synonyms_behavior_service;
|
||||
|
||||
/**
|
||||
* Entity type which is controlled by ths plugin.
|
||||
*
|
||||
* Entity type into which this plugin provides synonyms.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $controlled_entity_type;
|
||||
|
||||
/**
|
||||
* Bundle which is controlled by this plugin.
|
||||
*
|
||||
* Bundle into which this plugin provides synonyms. If the entity type does
|
||||
* not support bundles, just put here the entity type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $controlled_bundle;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\synonyms\SynonymsService\Behavior\AutocompleteService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Synonyms friendly entity autocomplete controller.
|
||||
*/
|
||||
class EntityAutocomplete extends ControllerBase {
|
||||
|
||||
/**
|
||||
* @var AutocompleteService
|
||||
*/
|
||||
protected $autocompleteService;
|
||||
|
||||
/**
|
||||
* EntityAutocomplete constructor.
|
||||
*/
|
||||
public function __construct(AutocompleteService $autocomplete_service) {
|
||||
$this->autocompleteService = $autocomplete_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('synonyms.behavior.autocomplete')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing callback: provide suggestions for autocomplete form element.
|
||||
*/
|
||||
public function autocomplete(Request $request, EntityTypeInterface $target_type, $token) {
|
||||
$matches = [];
|
||||
|
||||
if ($input = $request->get('q')) {
|
||||
$input = Tags::explode($input);
|
||||
$input = array_pop($input);
|
||||
foreach ($this->autocompleteService->autocompleteLookup($input, $token) as $k => $suggestion) {
|
||||
$key = $suggestion['entity_label'] . ' (' . $suggestion['entity_id'] . ')';
|
||||
$key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(Html::decodeEntities(strip_tags($key)))));
|
||||
|
||||
// Names containing commas or quotes must be wrapped in quotes.
|
||||
$key = Tags::encode($key);
|
||||
|
||||
$matches[] = [
|
||||
'value' => $key,
|
||||
'label' => $suggestion['wording'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return new JsonResponse($matches);
|
||||
}
|
||||
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Controller;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\synonyms\SynonymsService\Behavior\SynonymsBehaviorInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Controller for admin UI of the module.
|
||||
*/
|
||||
class SynonymConfigController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* SynonymConfigController constructor.
|
||||
*/
|
||||
public function __construct(EntityTypeBundleInfoInterface $entity_type_bundle_info, BehaviorService $behavior_service) {
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->behaviorService = $behavior_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('synonyms.behaviors')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing callback: show overview table of synonyms configuration.
|
||||
*/
|
||||
public function overview() {
|
||||
$render = [
|
||||
'#type' => 'table',
|
||||
'#header' => [
|
||||
$this->t('Entity type'),
|
||||
$this->t('Bundle'),
|
||||
$this->t('Manage'),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->entityTypeManager()->getDefinitions() as $entity_type) {
|
||||
if ($entity_type instanceof ContentEntityTypeInterface) {
|
||||
foreach ($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) {
|
||||
$links = [];
|
||||
foreach ($this->behaviorService->getBehaviorServices() as $service_id => $service) {
|
||||
$count = count($this->behaviorService->getSynonymConfigEntities($service_id, $entity_type->id(), $bundle));
|
||||
|
||||
if ($count > 0) {
|
||||
$title = $this->t('@behavior (@count)', [
|
||||
'@behavior' => $service['service']->getTitle(),
|
||||
'@count' => $count,
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$title = $service['service']->getTitle();
|
||||
}
|
||||
|
||||
$links[$service_id] = [
|
||||
'title' => $title,
|
||||
'url' => Url::fromRoute('entity.synonym.overview.entity_type.bundle.behavior', [
|
||||
'synonyms_entity_type' => $entity_type->id(),
|
||||
'bundle' => $bundle,
|
||||
'synonyms_behavior_service' => $service_id,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
$render[] = [
|
||||
['#markup' => Html::escape($entity_type->getLabel())],
|
||||
['#markup' => $bundle == $entity_type->id() ? '' : Html::escape($bundle_info['label'])],
|
||||
['#type' => 'operations', '#links' => $links],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $render;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing callback to overview a particular entity type and behavior service.
|
||||
*/
|
||||
public function overviewEntityTypeBundle(EntityTypeInterface $synonyms_entity_type, $bundle, SynonymsBehaviorInterface $synonyms_behavior_service) {
|
||||
$table = [
|
||||
'#type' => 'table',
|
||||
'#header' => [
|
||||
$this->t('Provider'),
|
||||
$this->t('Operations'),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($this->entityTypeManager()->getStorage('synonym')->loadMultiple() as $synonym_config) {
|
||||
$provider_instance = $synonym_config->getProviderPluginInstance();
|
||||
$provider_definition = $provider_instance->getPluginDefinition();
|
||||
if ($provider_definition['controlled_entity_type'] == $synonyms_entity_type->id() && $provider_definition['controlled_bundle'] == $bundle && $this->behaviorService->getBehaviorService($provider_definition['synonyms_behavior_service'])['service'] == $synonyms_behavior_service) {
|
||||
$table[] = [
|
||||
['#markup' => Html::escape($synonym_config->label())],
|
||||
[
|
||||
'#type' => 'operations',
|
||||
'#links' => $this->entityTypeManager()->getListBuilder($synonym_config->getEntityTypeId())->getOperations($synonym_config),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Title callback for 'entity.synonym.overview.entity_type.bundle.behavior'.
|
||||
*/
|
||||
public function overviewEntityTypeBundleTitle(EntityTypeInterface $synonyms_entity_type, $bundle, SynonymsBehaviorInterface $synonyms_behavior_service) {
|
||||
if ($synonyms_entity_type->id() == $bundle) {
|
||||
return $this->t('Manage @behavior of @entity_type', [
|
||||
'@behavior' => $synonyms_behavior_service->getTitle(),
|
||||
'@entity_type' => $synonyms_entity_type->getLabel(),
|
||||
]);
|
||||
}
|
||||
|
||||
$bundle_info = $this->entityTypeBundleInfo->getBundleInfo($synonyms_entity_type->id());
|
||||
|
||||
return $this->t('Manage @behavior of @entity_type @bundle', [
|
||||
'@behavior' => $synonyms_behavior_service->getTitle(),
|
||||
'@entity_type' => $synonyms_entity_type->getLabel(),
|
||||
'@bundle' => $bundle_info[$bundle]['label'],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Element;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\Textfield;
|
||||
use Drupal\Core\Site\Settings;
|
||||
|
||||
/**
|
||||
* Form element for synonyms-friendly entity autocomplete.
|
||||
*
|
||||
* @FormElement("synonyms_entity_autocomplete")
|
||||
*/
|
||||
class SynonymsEntityAutocomplete extends Textfield {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$info = parent::getInfo();
|
||||
|
||||
// Target entity type for suggestions.
|
||||
$info['#target_type'] = NULL;
|
||||
|
||||
// String or array of allowed target bundles. If omitted, all bundles will
|
||||
// be included in autocomplete suggestions.
|
||||
$info['#target_bundles'] = NULL;
|
||||
|
||||
// Default maximum amount of provided suggestions.
|
||||
$info['#suggestion_size'] = 10;
|
||||
|
||||
// Whether to suggest same entity at most once (in the case, when more than
|
||||
// 1 synonym triggers inclusion of that entity).
|
||||
$info['#suggest_only_unique'] = FALSE;
|
||||
|
||||
// Operator to match keyword. Allowed values are:
|
||||
// - CONTAINS
|
||||
// - STARTS_WITH
|
||||
$info['#match'] = 'CONTAINS';
|
||||
|
||||
array_unshift($info['#process'], [get_class($this), 'elementSynonymsEntityAutocomplete']);
|
||||
$info['#element_validate'][] = [get_class($this), 'validateEntityAutocomplete'];
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
$entities = NULL;
|
||||
if ($input === FALSE) {
|
||||
if (isset($element['#default_value'])) {
|
||||
$entities = [];
|
||||
foreach ($element['#default_value'] as $entity) {
|
||||
$entities[] = $entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Potentially the #value is set directly, so it contains the 'target_id'
|
||||
// array structure instead of a string.
|
||||
elseif ($input !== FALSE && is_array($input)) {
|
||||
$entity_ids = array_map(function(array $item) {
|
||||
return $item['target_id'];
|
||||
}, $input);
|
||||
$entities = \Drupal::entityTypeManager()->getStorage($element['#target_type'])->loadMultiple($entity_ids);
|
||||
}
|
||||
|
||||
if (is_array($entities)) {
|
||||
$value = [];
|
||||
foreach ($entities as $entity) {
|
||||
$value[] = $entity->label() . ' (' . $entity->id() . ')';
|
||||
}
|
||||
return Tags::implode($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element process callback for 'synonyms_entity_autocomplete' type.
|
||||
*/
|
||||
public static function elementSynonymsEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
|
||||
$data = [
|
||||
'target_type' => $element['#target_type'],
|
||||
'target_bundles' => $element['#target_bundles'],
|
||||
'suggestion_size' => $element['#suggestion_size'],
|
||||
'suggest_only_unique' => $element['#suggest_only_unique'],
|
||||
'match' => $element['#match'],
|
||||
];
|
||||
$token = Crypt::hmacBase64(serialize($data), Settings::getHashSalt());
|
||||
$key_value_storage = \Drupal::keyValue('synonyms_entity_autocomplete');
|
||||
|
||||
$key_value_storage->setIfNotExists($token, $data);
|
||||
|
||||
$element['#autocomplete_route_name'] = 'synonyms.entity_autocomplete';
|
||||
$element['#autocomplete_route_parameters'] = [
|
||||
'target_type' => $element['#target_type'],
|
||||
'token' => $token,
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validation handler for synonyms_entity_autocomplete elements.
|
||||
*/
|
||||
public static function validateEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
|
||||
$tokens = Tags::explode($form_state->getValue($element['#parents']));
|
||||
$value = [];
|
||||
|
||||
$autocomplete_service = \Drupal::getContainer()->get('synonyms.behavior.autocomplete');
|
||||
foreach ($tokens as $token) {
|
||||
$entity_id = self::extractEntityIdFromAutocompleteInput($token);
|
||||
if (!$entity_id) {
|
||||
$lookup = $autocomplete_service->autocompleteLookup($token, $element['#autocomplete_route_parameters']['token']);
|
||||
$lookup = array_shift($lookup);
|
||||
if ($lookup) {
|
||||
$entity_id = $lookup['entity_id'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity_id) {
|
||||
$value[] = ['target_id' => $entity_id];
|
||||
}
|
||||
}
|
||||
$form_state->setValueForElement($element, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the entity ID from the autocompletion result.
|
||||
*
|
||||
* @param string $input
|
||||
* The input coming from the autocompletion result.
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function extractEntityIdFromAutocompleteInput($input) {
|
||||
$match = NULL;
|
||||
|
||||
// Take "label (entity id)', match the ID from parenthesis when it's a
|
||||
// number.
|
||||
if (preg_match("/.+\s\((\d+)\)/", $input, $matches)) {
|
||||
$match = $matches[1];
|
||||
}
|
||||
return $match;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Element;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\Element\Select;
|
||||
|
||||
/**
|
||||
* Form element for synonyms-friendly entity select.
|
||||
*
|
||||
* @FormElement("synonyms_entity_select")
|
||||
*/
|
||||
class SynonymsEntitySelect extends Select {
|
||||
|
||||
/**
|
||||
* Delimiter to use when separating entity ID and its synonym.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DELIMITER = ':';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$info = parent::getInfo();
|
||||
|
||||
// Target entity type.
|
||||
$info['#target_type'] = NULL;
|
||||
|
||||
// Which SelectionInterface plugin to use for retrieving referenceable
|
||||
// entities.
|
||||
$info['#handler'] = 'default';
|
||||
|
||||
// Array of settings for the selection handler.
|
||||
$info['#handler_settings'] = [];
|
||||
|
||||
// Put actual value under this key in the associative array.
|
||||
$info['#key_column'] = 'target_id';
|
||||
|
||||
array_unshift($info['#process'], [get_class($this), 'elementSynonymsEntitySelect']);
|
||||
$info['#element_validate'][] = [get_class($this), 'validateEntitySelect'];
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
|
||||
$return = parent::valueCallback($element, $input, $form_state);
|
||||
|
||||
if (is_null($return) && isset($element['#default_value'])) {
|
||||
$return = $element['#default_value'];
|
||||
}
|
||||
|
||||
// Force default value (entity ID(-s)) to be strings. Otherwise we are
|
||||
// hitting the situation when all synonyms are highlighted as selected.
|
||||
// This code snippet explains the problem:
|
||||
// $a = [25];
|
||||
// $k = '25:25';
|
||||
// in_array($k, $a); // Yields TRUE, because PHP seems to compare int to int
|
||||
// and not string-wise.
|
||||
if (is_array($return)) {
|
||||
$return = array_map(function($item) {
|
||||
return (string) $item;
|
||||
}, $return);
|
||||
}
|
||||
elseif (!is_null($return)) {
|
||||
$return = (string) $return;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element process callback for 'synonyms_entity_select' type.
|
||||
*/
|
||||
public static function elementSynonymsEntitySelect(array &$element, FormStateInterface $form_state, array &$complete_form) {
|
||||
$options = [];
|
||||
|
||||
$selection = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance([
|
||||
'target_type' => $element['#target_type'],
|
||||
'handler' => $element['#handler'],
|
||||
'handler_settings' => $element['#handler_settings'],
|
||||
'entity' => NULL,
|
||||
]);
|
||||
|
||||
$bundle_info = \Drupal::getContainer()->get('entity_type.bundle.info')->getBundleInfo($element['#target_type']);
|
||||
|
||||
$referenceable_entities = $selection->getReferenceableEntities();
|
||||
$entities = [];
|
||||
|
||||
foreach ($referenceable_entities as $bundle_entity_ids) {
|
||||
$entities = array_merge($entities, array_keys($bundle_entity_ids));
|
||||
}
|
||||
|
||||
if (!empty($entities)) {
|
||||
$entities = \Drupal::entityTypeManager()->getStorage($element['#target_type'])->loadMultiple($entities);
|
||||
}
|
||||
|
||||
foreach ($referenceable_entities as $bundle => $entity_ids) {
|
||||
$key = (string) $bundle_info[$bundle]['label'];
|
||||
$options[$key] = [];
|
||||
|
||||
$synonyms = \Drupal::getContainer()->get('synonyms.behavior.select')->getSynonymsMultiple(array_intersect_key($entities, $entity_ids));
|
||||
|
||||
foreach ($entity_ids as $entity_id => $label) {
|
||||
$options[$key][$entity_id] = $label;
|
||||
|
||||
foreach ($synonyms[$entity_id] as $synonym) {
|
||||
$options[$key][$entity_id . self::DELIMITER . $synonym['synonym']] = $synonym['wording'];
|
||||
}
|
||||
}
|
||||
|
||||
asort($options[$key]);
|
||||
}
|
||||
|
||||
if (count($options) == 1) {
|
||||
// Strip away the bundle optgroup if it's the only bundle.
|
||||
$options = reset($options);
|
||||
}
|
||||
|
||||
$element['#options'] = $options;
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validation handler for synonyms_entity_select elements.
|
||||
*/
|
||||
public static function validateEntitySelect(array &$element, FormStateInterface $form_state, array &$complete_form) {
|
||||
$value = $form_state->getValue($element['#parents']);
|
||||
if (!isset($element['#multiple']) || !$element['#multiple']) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
$unique = [];
|
||||
foreach ($value as $v) {
|
||||
if ($v !== '') {
|
||||
if (!is_numeric($v)) {
|
||||
$v = explode(self::DELIMITER, $v, 2)[0];
|
||||
}
|
||||
$unique[$v] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$items = [];
|
||||
foreach ($unique as $v) {
|
||||
$items[] = [
|
||||
$element['#key_column'] => $v,
|
||||
];
|
||||
}
|
||||
|
||||
if (!isset($element['#multiple']) || !$element['#multiple']) {
|
||||
$items = reset($items);
|
||||
}
|
||||
|
||||
$form_state->setValueForElement($element, $items);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Entity;
|
||||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
use Drupal\synonyms\SynonymProviderPluginCollection;
|
||||
use Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
/**
|
||||
* Synonym configuration entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "synonym",
|
||||
* label = @Translation("Synonym configuration"),
|
||||
* handlers = {
|
||||
* "list_builder" = "Drupal\Core\Config\Entity\ConfigEntityListBuilder",
|
||||
* "form" = {
|
||||
* "add" = "Drupal\synonyms\Form\SynonymForm",
|
||||
* "edit" = "Drupal\synonyms\Form\SynonymForm",
|
||||
* "delete" = "Drupal\synonyms\Form\SynonymDeleteForm"
|
||||
* }
|
||||
* },
|
||||
* config_prefix = "synonym",
|
||||
* admin_permission = "administer synonyms",
|
||||
* entity_keys = {
|
||||
* "id" = "id"
|
||||
* },
|
||||
* links = {
|
||||
* "edit-form" = "/admin/structure/synonyms/{synonym}",
|
||||
* "delete-form" = "/admin/structure/synonyms/{synonym}/delete"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class Synonym extends ConfigEntityBase implements SynonymInterface {
|
||||
|
||||
/**
|
||||
* Plugin ID that corresponds to this config entry.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $provider_plugin;
|
||||
|
||||
/**
|
||||
* Base plugin ID that corresponds to this config entry.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $base_provider_plugin;
|
||||
|
||||
/**
|
||||
* Plugin configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $provider_configuration = [];
|
||||
|
||||
/**
|
||||
* Controlled behavior service.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $behavior;
|
||||
|
||||
/**
|
||||
* Behavior configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $behavior_configuration = [];
|
||||
|
||||
/**
|
||||
* The plugin collection that stores synonym provider plugins.
|
||||
*
|
||||
* @var \Drupal\synonyms\SynonymProviderPluginCollection
|
||||
*/
|
||||
protected $pluginCollection;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function label() {
|
||||
return $this->getProviderPluginInstance()->getPluginDefinition()['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProviderPluginInstance() {
|
||||
return $this->getPluginCollection()->get($this->getProviderPlugin());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProviderPlugin() {
|
||||
return $this->provider_plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setProviderPlugin($plugin) {
|
||||
$this->provider_plugin = $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProviderConfiguration() {
|
||||
if ($this->getProviderPluginInstance() instanceof ConfigurablePluginInterface) {
|
||||
return $this->getPluginCollection()->get($this->getProviderPlugin())->getConfiguration();
|
||||
}
|
||||
return $this->provider_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setProviderConfiguration(array $provider_configuration) {
|
||||
if ($this->getProviderPluginInstance() instanceof ConfigurablePluginInterface) {
|
||||
$this->getProviderPluginInstance()->setConfiguration($provider_configuration);
|
||||
}
|
||||
$this->provider_configuration = $provider_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBehaviorConfiguration() {
|
||||
return $this->behavior_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setBehaviorConfiguration(array $behavior_configuration) {
|
||||
$this->behavior_configuration = $behavior_configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin collections used by this entity.
|
||||
*
|
||||
* @return SynonymProviderPluginCollection[]
|
||||
* An array of plugin collections, keyed by the property name they use to
|
||||
* store their configuration.
|
||||
*/
|
||||
public function getPluginCollections() {
|
||||
return ['provider_configuration' => $this->getPluginCollection()];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postLoad(EntityStorageInterface $storage, array &$entities) {
|
||||
parent::postLoad($storage, $entities);
|
||||
foreach ($entities as $entity) {
|
||||
$entity->addCacheTags([self::cacheTagConstruct($entity->get('behavior'), $entity->getProviderPluginInstance()->getPluginDefinition()['controlled_entity_type'], $entity->getProviderPluginInstance()->getPluginDefinition()['controlled_bundle'])]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
parent::preSave($storage);
|
||||
|
||||
// Update the "static" properties. We keep them only to be able to leverage
|
||||
// Schema API through them.
|
||||
$this->base_provider_plugin = $this->getProviderPluginInstance()->getBaseId();
|
||||
$this->behavior = $this->getProviderPluginInstance()->getBehaviorService();
|
||||
|
||||
// Make sure we have appropriate cache tags in this entity. If it was just
|
||||
// created and runs its first save it might not have it set up yet.
|
||||
$this->addCacheTags([self::cacheTagConstruct($this->behavior, $this->getProviderPluginInstance()->getPluginDefinition()['controlled_entity_type'], $this->getProviderPluginInstance()->getPluginDefinition()['controlled_bundle'])]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a cache tag.
|
||||
*
|
||||
* Construct a cache tag that represents synonyms config for a given behavior,
|
||||
* entity type, and bundle.
|
||||
*
|
||||
* @param string $behavior
|
||||
* Synonyms behavior whose cache tag is requested
|
||||
* @param string $entity_type
|
||||
* Entity type whose cache tag is requested
|
||||
* @param string $bundle
|
||||
* Bundle whose cache tag is requested
|
||||
*
|
||||
* @return string
|
||||
* Cache tag
|
||||
*/
|
||||
public static function cacheTagConstruct($behavior, $entity_type, $bundle) {
|
||||
return 'synonyms:' . $behavior . '.' . $entity_type . '.' . $bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the creation of entity's LazyPluginCollection.
|
||||
*
|
||||
* @return SynonymProviderPluginCollection
|
||||
* The entity's plugin collection.
|
||||
*/
|
||||
protected function getPluginCollection() {
|
||||
if (!$this->pluginCollection && $this->getProviderPlugin()) {
|
||||
$this->pluginCollection = new SynonymProviderPluginCollection(\Drupal::service('plugin.manager.synonyms_provider'), $this->getProviderPlugin(), $this->provider_configuration);
|
||||
}
|
||||
return $this->pluginCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function invalidateTagsOnSave($update) {
|
||||
parent::invalidateTagsOnSave($update);
|
||||
Cache::invalidateTags($this->cacheTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static function invalidateTagsOnDelete(EntityTypeInterface $entity_type, array $entities) {
|
||||
$cacheability_metadata = new CacheableMetadata();
|
||||
foreach ($entities as $entity) {
|
||||
$cacheability_metadata->addCacheableDependency($entity);
|
||||
}
|
||||
Cache::invalidateTags($cacheability_metadata->getCacheTags());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Delete form for synonym config entity.
|
||||
*/
|
||||
class SynonymDeleteForm extends EntityConfirmFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to delete synonym configuration %name?', ['%name' => $this->entity->label()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.synonym.overview');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->entity->delete();
|
||||
drupal_set_message($this->t('Synonym configuration %label has been deleted.', ['%label' => $this->entity->label()]));
|
||||
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\SubformState;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
use Drupal\synonyms\SynonymsProviderPluginManager;
|
||||
use Drupal\synonyms\SynonymsService\Behavior\SynonymsBehaviorConfigurableInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Entity form for 'synonym' config entity type.
|
||||
*/
|
||||
class SynonymForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* @var SynonymInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $entityQuery;
|
||||
|
||||
/**
|
||||
* Entity type manager.
|
||||
*
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Entity type bundle info.
|
||||
*
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* @var SynonymsProviderPluginManager
|
||||
*/
|
||||
protected $synonymsProviderPluginManager;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorServices;
|
||||
|
||||
/**
|
||||
* Entity type that is being edited/added.
|
||||
*
|
||||
* @var EntityTypeInterface
|
||||
*/
|
||||
protected $controlledEntityType;
|
||||
|
||||
/**
|
||||
* Bundle that is being edited/added.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $controlledBundle;
|
||||
|
||||
/**
|
||||
* Service ID that is being edited/added.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $behaviorServiceId;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
public function __construct(QueryFactory $entity_query, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, SynonymsProviderPluginManager $synonyms_provider_plugin_manager, BehaviorService $behavior_services, ContainerInterface $container) {
|
||||
$this->entityQuery = $entity_query;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->synonymsProviderPluginManager = $synonyms_provider_plugin_manager;
|
||||
$this->behaviorServices = $behavior_services;
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.query'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('plugin.manager.synonyms_provider'),
|
||||
$container->get('synonyms.behaviors'),
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function init(FormStateInterface $form_state) {
|
||||
parent::init($form_state);
|
||||
|
||||
if ($this->entity->isNew()) {
|
||||
$this->controlledEntityType = $this->getRequest()->get('synonyms_entity_type')->id();
|
||||
$this->controlledBundle = $this->getRequest()->get('bundle');
|
||||
$this->behaviorServiceId = $this->getRouteMatch()->getRawParameter('synonyms_behavior_service');
|
||||
}
|
||||
else {
|
||||
$plugin_definition = $this->entity->getProviderPluginInstance()->getPluginDefinition();
|
||||
$this->controlledEntityType = $plugin_definition['controlled_entity_type'];
|
||||
$this->controlledBundle = $plugin_definition['controlled_bundle'];
|
||||
$this->behaviorServiceId = $plugin_definition['synonyms_behavior_service'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
$class = get_class($this);
|
||||
|
||||
$provider_plugin = $this->entity->getProviderPlugin();
|
||||
if ($form_state->getValue('provider_plugin')) {
|
||||
$provider_plugin = $form_state->getValue('provider_plugin');
|
||||
}
|
||||
|
||||
$form['id'] = [
|
||||
'#type' => 'value',
|
||||
'#value' => str_replace(':', '.', $provider_plugin),
|
||||
];
|
||||
|
||||
$options = [];
|
||||
foreach ($this->synonymsProviderPluginManager->getDefinitions() as $plugin_id => $plugin) {
|
||||
if ($plugin['controlled_entity_type'] == $this->controlledEntityType && $plugin['controlled_bundle'] == $this->controlledBundle && $plugin['synonyms_behavior_service'] == $this->behaviorServiceId) {
|
||||
$options[$plugin_id] = $plugin['label'];
|
||||
}
|
||||
}
|
||||
|
||||
$form['provider_plugin'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Synonyms provider'),
|
||||
'#description' => $this->t('Select what synonyms provider it should represent.'),
|
||||
'#required' => TRUE,
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->entity->getProviderPlugin(),
|
||||
'#ajax' => [
|
||||
'wrapper' => 'synonyms-entity-configuration-ajax-wrapper',
|
||||
'event' => 'change',
|
||||
'callback' => [$class, 'ajaxForm'],
|
||||
],
|
||||
];
|
||||
|
||||
$form['ajax_wrapper'] = [
|
||||
'#prefix' => '<div id="synonyms-entity-configuration-ajax-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
];
|
||||
|
||||
$form['ajax_wrapper']['provider_configuration'] = [
|
||||
'#tree' => TRUE,
|
||||
'#title' => $this->t('Provider settings'),
|
||||
'#open' => TRUE,
|
||||
];
|
||||
|
||||
$form['ajax_wrapper']['behavior_configuration'] = [
|
||||
'#tree' => TRUE,
|
||||
'#title' => $this->t('Behavior settings'),
|
||||
'#open' => TRUE,
|
||||
];
|
||||
|
||||
if ($provider_plugin) {
|
||||
$provider_plugin_instance = $this->entity->getProviderPluginInstance();
|
||||
|
||||
if ($provider_plugin_instance instanceof PluginFormInterface) {
|
||||
$form['ajax_wrapper']['provider_configuration']['#type'] = 'details';
|
||||
$form['ajax_wrapper']['provider_configuration'] += $provider_plugin_instance->buildConfigurationForm($form['ajax_wrapper']['provider_configuration'], $form_state);
|
||||
}
|
||||
|
||||
$behavior_service_instance = $provider_plugin_instance->getBehaviorServiceInstance();
|
||||
if ($behavior_service_instance instanceof SynonymsBehaviorConfigurableInterface) {
|
||||
$form['ajax_wrapper']['behavior_configuration']['#type'] = 'details';
|
||||
$form['ajax_wrapper']['behavior_configuration'] += $behavior_service_instance->buildConfigurationForm($form['ajax_wrapper']['behavior_configuration'], $form_state, $this->entity->getBehaviorConfiguration(), $this->entity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
|
||||
if ($this->entity->getProviderPluginInstance() instanceof PluginFormInterface) {
|
||||
$this->entity->getProviderPluginInstance()->validateConfigurationForm($form['ajax_wrapper']['provider_configuration'], $this->getSubFormState('provider_configuration', $form, $form_state));
|
||||
}
|
||||
|
||||
if ($this->entity->getProviderPluginInstance()->getBehaviorServiceInstance() instanceof SynonymsBehaviorConfigurableInterface) {
|
||||
$this->entity->getProviderPluginInstance()->getBehaviorServiceInstance()->validateConfigurationForm($form['ajax_wrapper']['behavior_configuration'], $this->getSubFormState('behavior_configuration', $form, $form_state), $this->entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
if ($this->entity->getProviderPluginInstance() instanceof PluginFormInterface) {
|
||||
$this->entity->getProviderPluginInstance()->submitConfigurationForm($form['ajax_wrapper']['provider_configuration'], $this->getSubFormState('provider_configuration', $form, $form_state));
|
||||
}
|
||||
|
||||
if ($this->entity->getProviderPluginInstance()->getBehaviorServiceInstance() instanceof SynonymsBehaviorConfigurableInterface) {
|
||||
$this->entity->setBehaviorConfiguration($this->entity->getProviderPluginInstance()->getBehaviorServiceInstance()->submitConfigurationForm($form['ajax_wrapper']['behavior_configuration'], $this->getSubFormState('behavior_configuration', $form, $form_state), $this->entity));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$status = $this->entity->save();
|
||||
|
||||
if ($status) {
|
||||
drupal_set_message($this->t('Saved the %label synonym configuration.', [
|
||||
'%label' => $this->entity->label(),
|
||||
]));
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('The %label synonym configuration was not saved.', [
|
||||
'%label' => $this->entity->label(),
|
||||
]), 'error');
|
||||
}
|
||||
|
||||
$form_state->setRedirect('entity.synonym.overview');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether entity with such ID already exists.
|
||||
*
|
||||
* @param string $id
|
||||
* Entity ID to check
|
||||
*
|
||||
* @return bool
|
||||
* Whether entity with such ID already exists.
|
||||
*/
|
||||
public function exist($id) {
|
||||
$entity = $this->entityQuery->get('synonym')
|
||||
->condition('id', $id)
|
||||
->execute();
|
||||
return (bool) $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax callback.
|
||||
*/
|
||||
public static function ajaxForm(array &$form, FormStateInterface $form_state, Request $request) {
|
||||
return $form['ajax_wrapper'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Supportive method to create sub-form-states.
|
||||
*
|
||||
* @param string $element_name
|
||||
* Name of the nested form element for which to create a sub form state
|
||||
* @param array $form
|
||||
* Full form array
|
||||
* @param FormStateInterface $form_state
|
||||
* Full form state out of which to create sub form state
|
||||
*
|
||||
* @return SubformState
|
||||
* Sub form state object generated based on the input arguments
|
||||
*/
|
||||
protected function getSubFormState($element_name, array $form, FormStateInterface $form_state) {
|
||||
return SubformState::createForSubform($form['ajax_wrapper'][$element_name], $form, $form_state);
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\ParamConverter;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\ParamConverter\ParamConverterInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Param converter service for entity types.
|
||||
*/
|
||||
class EntityTypeParamConverter implements ParamConverterInterface {
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* EntityTypeParamConverter constructor.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert($value, $definition, $name, array $defaults) {
|
||||
return $this->entityTypeManager->getDefinition($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($definition, $name, Route $route) {
|
||||
return (!empty($definition['type']) && $definition['type'] == 'synonyms_entity_type');
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\ParamConverter;
|
||||
|
||||
use Drupal\Core\ParamConverter\ParamConverterInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Param converter for synonyms behavior service.
|
||||
*/
|
||||
class SynonymsBehaviorServiceParamConverter implements ParamConverterInterface {
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* SynonymsBehaviorServiceParamConverter constructor.
|
||||
*/
|
||||
public function __construct(BehaviorService $behavior_service) {
|
||||
$this->behaviorService = $behavior_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert($value, $definition, $name, array $defaults) {
|
||||
return $this->behaviorService->getBehaviorService($value)['service'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($definition, $name, Route $route) {
|
||||
return (!empty($definition['type']) && $definition['type'] == 'synonyms_behavior_service');
|
||||
}
|
||||
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\ContentEntityType;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Derivative for entity_reference synonyms provider plugin.
|
||||
*/
|
||||
class EntityReferenceField extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Entity type manager.
|
||||
*
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* Entity type bundle info.
|
||||
*
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
function __construct(EntityTypeManagerInterface $entity_type_manager, BehaviorService $behavior_service, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityFieldManagerInterface $entity_field_manager) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('synonyms.behaviors'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('entity_field.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$implemented_interfaces = new \ReflectionClass($base_plugin_definition['class']);
|
||||
$implemented_interfaces = $implemented_interfaces->getInterfaceNames();
|
||||
|
||||
foreach ($this->behaviorService->getBehaviorServices() as $service_id => $behavior) {
|
||||
$required_interfaces = $behavior['required_interfaces'];
|
||||
|
||||
$diff = array_diff($required_interfaces, $implemented_interfaces);
|
||||
if (empty($diff)) {
|
||||
// This plugin has implemented all required interfaces for this
|
||||
// behavior.
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $entity_type) {
|
||||
if ($entity_type instanceof ContentEntityType) {
|
||||
foreach ($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) {
|
||||
$fields = $this->entityFieldManager->getFieldDefinitions($entity_type->id(), $bundle);
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if ($field->getType() == 'entity_reference') {
|
||||
$derivative_name = implode('_', [
|
||||
$service_id,
|
||||
$entity_type->id(),
|
||||
$bundle,
|
||||
$field->getName(),
|
||||
]);
|
||||
|
||||
$this->derivatives[$derivative_name] = $base_plugin_definition;
|
||||
$this->derivatives[$derivative_name]['label'] = $this->t('@behavior on @field', [
|
||||
'@behavior' => $behavior['service']->getTitle(),
|
||||
'@field' => $field->getLabel(),
|
||||
]);
|
||||
$this->derivatives[$derivative_name]['synonyms_behavior_service'] = $service_id;
|
||||
$this->derivatives[$derivative_name]['controlled_entity_type'] = $entity_type->id();
|
||||
$this->derivatives[$derivative_name]['controlled_bundle'] = $bundle;
|
||||
$this->derivatives[$derivative_name]['field'] = $field->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\ContentEntityType;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Drupal\synonyms\SynonymsService\FieldTypeToSynonyms;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Derivative for synonyms provider plugins.
|
||||
*/
|
||||
class Field extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Entity type manager.
|
||||
*
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* Entity type bundle info.
|
||||
*
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var FieldTypeToSynonyms
|
||||
*/
|
||||
protected $fieldTypeToSynonyms;
|
||||
|
||||
function __construct(EntityTypeManagerInterface $entity_type_manager, BehaviorService $behavior_service, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityFieldManagerInterface $entity_field_manager, FieldTypeToSynonyms $field_type_to_synonyms) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->fieldTypeToSynonyms = $field_type_to_synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('synonyms.behaviors'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('synonyms.provider.field_type_to_synonyms')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$implemented_interfaces = new \ReflectionClass($base_plugin_definition['class']);
|
||||
$implemented_interfaces = $implemented_interfaces->getInterfaceNames();
|
||||
|
||||
$field_type_to_property_map = $this->fieldTypeToSynonyms->getSimpleFieldTypeToPropertyMap();
|
||||
foreach ($this->behaviorService->getBehaviorServices() as $service_id => $behavior) {
|
||||
$required_interfaces = $behavior['required_interfaces'];
|
||||
|
||||
$diff = array_diff($required_interfaces, $implemented_interfaces);
|
||||
if (empty($diff)) {
|
||||
// This plugin has implemented all required interfaces for this
|
||||
// behavior.
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $entity_type) {
|
||||
if ($entity_type instanceof ContentEntityType) {
|
||||
foreach ($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) {
|
||||
$base_fields = $this->entityFieldManager->getBaseFieldDefinitions($entity_type->id());
|
||||
$fields = $this->entityFieldManager->getFieldDefinitions($entity_type->id(), $bundle);
|
||||
|
||||
switch ($base_plugin_definition['id']) {
|
||||
case 'base_field':
|
||||
$fields = $base_fields;
|
||||
break;
|
||||
|
||||
case 'field':
|
||||
$fields = array_diff_key($fields, $base_fields);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if ($field->getName() != 'synonyms' && isset($field_type_to_property_map[$field->getType()])) {
|
||||
$derivative_name = implode('_', [
|
||||
$service_id,
|
||||
$entity_type->id(),
|
||||
$bundle,
|
||||
$field->getName(),
|
||||
]);
|
||||
|
||||
$this->derivatives[$derivative_name] = $base_plugin_definition;
|
||||
$this->derivatives[$derivative_name]['label'] = $this->t('@behavior on @field', [
|
||||
'@behavior' => $behavior['service']->getTitle(),
|
||||
'@field' => $field->getLabel(),
|
||||
]);
|
||||
$this->derivatives[$derivative_name]['synonyms_behavior_service'] = $service_id;
|
||||
$this->derivatives[$derivative_name]['controlled_entity_type'] = $entity_type->id();
|
||||
$this->derivatives[$derivative_name]['controlled_bundle'] = $bundle;
|
||||
$this->derivatives[$derivative_name]['field'] = $field->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides synonyms argument validator plugin definitions for all entity types.
|
||||
*/
|
||||
class ViewsSynonymsEntityArgumentValidator extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The base plugin ID this derivative is for.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $basePluginId;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs an ViewsSynonymsEntityArgumentValidator object.
|
||||
*/
|
||||
public function __construct($base_plugin_id, EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) {
|
||||
$this->basePluginId = $base_plugin_id;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->stringTranslation = $string_translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$base_plugin_id,
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
$entity_types = $this->entityTypeManager->getDefinitions();
|
||||
$this->derivatives = [];
|
||||
foreach ($entity_types as $entity_type_id => $entity_type) {
|
||||
if ($entity_type instanceof ContentEntityTypeInterface) {
|
||||
$this->derivatives[$entity_type_id] = [
|
||||
'id' => 'synonyms_entity:' . $entity_type_id,
|
||||
'provider' => 'synonyms',
|
||||
'title' => $this->t('Synonyms of @entity_type', [
|
||||
'@entity_type' => $entity_type->getLowercaseLabel(),
|
||||
]),
|
||||
'help' => $this->t('Validate @label', ['@label' => $entity_type->getLabel()]),
|
||||
'entity_type' => $entity_type_id,
|
||||
'class' => $base_plugin_definition['class'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'synonyms friendly autocomplete' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "synonyms_autocomplete",
|
||||
* label = @Translation("Synonyms-friendly autocomplete"),
|
||||
* description = @Translation("An autocomplete with entities and their synonyms."),
|
||||
* field_types = {
|
||||
* "entity_reference"
|
||||
* },
|
||||
* multiple_values = TRUE
|
||||
* )
|
||||
*/
|
||||
class EntityReferenceSynonymsAutocomplete extends WidgetBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, BehaviorService $behavior_service) {
|
||||
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
|
||||
|
||||
$this->behaviorService = $behavior_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$configuration['field_definition'],
|
||||
$configuration['settings'],
|
||||
$configuration['third_party_settings'],
|
||||
$container->get('synonyms.behaviors')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultSettings() {
|
||||
return [
|
||||
'suggestion_size' => 10,
|
||||
'suggest_only_unique' => FALSE,
|
||||
'match' => 'CONTAINS',
|
||||
] + parent::defaultSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state) {
|
||||
$elements = parent::settingsForm($form, $form_state);
|
||||
|
||||
$elements['suggestion_size'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Suggestions Size'),
|
||||
'#description' => $this->t('Please, enter how many suggested entities to show in the autocomplete textfield.'),
|
||||
'#required' => TRUE,
|
||||
'#default_value' => $this->getSetting('suggestion_size'),
|
||||
];
|
||||
|
||||
$elements['suggest_only_unique'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Suggest only one entry per entity'),
|
||||
'#description' => t('If you want to include only name or a single synonym, suggesting a particular entity, while disregarding all ongoing ones, please, tick this checkbox on.'),
|
||||
'#default_value' => $this->getSetting('suggest_only_unique'),
|
||||
];
|
||||
|
||||
$elements['match'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Match operator'),
|
||||
'#description' => $this->t('Choose how to match the keyword against existing data.'),
|
||||
'#options' => $this->getMatchOperatorOptions(),
|
||||
'#default_value' => $this->getSetting('match'),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = parent::settingsSummary();
|
||||
|
||||
$summary[] = $this->t('Suggestion size: @size', [
|
||||
'@size' => $this->getSetting('suggestion_size'),
|
||||
]);
|
||||
|
||||
$summary[] = $this->t('Only unique: @unique', [
|
||||
'@unique' => $this->getSetting('suggest_only_unique') ? $this->t('Yes') : $this->t('No'),
|
||||
]);
|
||||
|
||||
$summary[] = $this->t('Match: @match', [
|
||||
'@match' => $this->getMatchOperatorOptions()[$this->getSetting('match')],
|
||||
]);
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$default_value = [];
|
||||
foreach ($items as $item) {
|
||||
if ($item->entity) {
|
||||
$default_value[] = $item->entity;
|
||||
}
|
||||
}
|
||||
$element += [
|
||||
'#type' => 'synonyms_entity_autocomplete',
|
||||
'#target_type' => $this->getFieldSetting('target_type'),
|
||||
'#target_bundles' => $this->getFieldSetting('handler_settings')['target_bundles'],
|
||||
'#suggestion_size' => $this->getSetting('suggestion_size'),
|
||||
'#suggest_only_unique' => $this->getSetting('suggest_only_unique'),
|
||||
'#match' => $this->getSetting('match'),
|
||||
'#default_value' => $default_value,
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the options for the match operator.
|
||||
*
|
||||
* @return array
|
||||
* List of options.
|
||||
*/
|
||||
protected function getMatchOperatorOptions() {
|
||||
return [
|
||||
'STARTS_WITH' => t('Starts with'),
|
||||
'CONTAINS' => t('Contains'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'synonyms friendly select' widget.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "synonyms_select",
|
||||
* label = @Translation("Synonyms-friendly select"),
|
||||
* description = @Translation("A dropdown with entities and their synonyms."),
|
||||
* field_types = {
|
||||
* "entity_reference"
|
||||
* },
|
||||
* multiple_values = TRUE
|
||||
* )
|
||||
*/
|
||||
class EntityReferenceSynonymsSelect extends WidgetBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$default_value = [];
|
||||
foreach ($items as $item) {
|
||||
if ($item->{$this->getKeyColumn()}) {
|
||||
$default_value[] = $item->{$this->getKeyColumn()};
|
||||
}
|
||||
}
|
||||
|
||||
$element += [
|
||||
'#type' => 'synonyms_entity_select',
|
||||
'#key_column' => $this->getKeyColumn(),
|
||||
'#target_type' => $this->getFieldSetting('target_type'),
|
||||
'#handler' => $this->fieldDefinition->getSetting('handler'),
|
||||
'#handler_settings' => $this->fieldDefinition->getSetting('handler_settings') ?: [],
|
||||
'#multiple' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple(),
|
||||
'#default_value' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple() ? $default_value : reset($default_value),
|
||||
];
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of the column that is managed by this widget in the field
|
||||
* .
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyColumn() {
|
||||
return $this->fieldDefinition->getFieldStorageDefinition()->getPropertyNames()[0];
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Synonyms\Provider;
|
||||
|
||||
use Drupal\Component\Plugin\PluginBase;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsProviderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Good starting point for a synonyms provider plugin.
|
||||
*/
|
||||
abstract class AbstractProvider extends PluginBase implements SynonymsProviderInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, ContainerInterface $container) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBehaviorService() {
|
||||
return $this->getPluginDefinition()['synonyms_behavior_service'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBehaviorServiceInstance() {
|
||||
return $this->container->get($this->getPluginDefinition()['synonyms_behavior_service']);
|
||||
}
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Synonyms\Provider;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetTrait;
|
||||
use Drupal\synonyms\SynonymsService\FieldTypeToSynonyms;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provide synonyms from base fields.
|
||||
*
|
||||
* @SynonymsProvider(
|
||||
* id = "base_field",
|
||||
* deriver = "Drupal\synonyms\Plugin\Derivative\Field"
|
||||
* )
|
||||
*/
|
||||
class BaseField extends AbstractProvider implements SynonymsGetProviderInterface, SynonymsFindProviderInterface, SynonymsFormatWordingProviderInterface {
|
||||
|
||||
use SynonymsGetTrait, SynonymsFindTrait, SynonymsFormatWordingTrait;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var FieldTypeToSynonyms
|
||||
*/
|
||||
protected $fieldTypeToSynonyms;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, FieldTypeToSynonyms $field_type_to_synonyms, EntityTypeManagerInterface $entity_type_manager, Connection $database, ContainerInterface $container) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $container);
|
||||
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->fieldTypeToSynonyms = $field_type_to_synonyms;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('synonyms.provider.field_type_to_synonyms'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('database'),
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []) {
|
||||
$map = $this->fieldTypeToSynonyms->getSimpleFieldTypeToPropertyMap();
|
||||
$field_type = $entity->getFieldDefinition($this->getPluginDefinition()['field'])->getType();
|
||||
|
||||
$synonyms = [];
|
||||
|
||||
if (isset($map[$field_type])) {
|
||||
foreach ($entity->get($this->getPluginDefinition()['field']) as $item) {
|
||||
$synonyms[] = $item->{$map[$field_type]};
|
||||
}
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function synonymsFind(ConditionInterface $condition) {
|
||||
$entity_type_definition = $this->entityTypeManager->getDefinition($this->getPluginDefinition()['controlled_entity_type']);
|
||||
$entity_keys = $entity_type_definition->getKeys();
|
||||
|
||||
$query = $this->database->select($entity_type_definition->getDataTable(), 'base');
|
||||
$query->addField('base', $entity_keys['id'], 'entity_id');
|
||||
$query->addField('base', $this->getPluginDefinition()['field'], 'synonym');
|
||||
|
||||
if ($this->getPluginDefinition()['controlled_entity_type'] != $this->getPluginDefinition()['controlled_bundle'] && isset($entity_keys['bundle']) && $entity_keys['bundle']) {
|
||||
$query->condition($entity_keys['bundle'], $this->getPluginDefinition()['controlled_bundle']);
|
||||
}
|
||||
|
||||
$this->synonymsFindProcessCondition($condition, $this->getPluginDefinition()['field'], $entity_keys['id']);
|
||||
$query->condition($condition);
|
||||
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle'])[$this->getPluginDefinition()['field']];
|
||||
return [
|
||||
$field->getConfigDependencyKey() => [$field->getConfigDependencyName()],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Synonyms\Provider;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryBase;
|
||||
use Drupal\Core\Entity\Query\Sql\Condition;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provide synonyms from entity reference field type.
|
||||
*
|
||||
* @SynonymsProvider(
|
||||
* id = "entityreference_field",
|
||||
* deriver = "Drupal\synonyms\Plugin\Derivative\EntityReferenceField"
|
||||
* )
|
||||
*/
|
||||
class EntityReferenceField extends AbstractProvider implements SynonymsGetProviderInterface, SynonymsFindProviderInterface, SynonymsFormatWordingProviderInterface {
|
||||
|
||||
use SynonymsGetTrait, SynonymsFindTrait, SynonymsFormatWordingTrait;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $entityQueryFactory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, Connection $database, QueryFactory $entity_query_factory, ContainerInterface $container) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $container);
|
||||
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->database = $database;
|
||||
$this->entityQueryFactory = $entity_query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('database'),
|
||||
$container->get('entity.query'),
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []) {
|
||||
$synonyms = [];
|
||||
|
||||
foreach ($entity->get($this->getPluginDefinition()['field']) as $item) {
|
||||
$synonyms[] = $item->entity->label();
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function synonymsFind(ConditionInterface $condition) {
|
||||
$entity_type_definition = $this->entityTypeManager->getDefinition($this->getPluginDefinition()['controlled_entity_type']);
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle']);
|
||||
$field = $field[$this->getPluginDefinition()['field']];
|
||||
|
||||
$query = new FieldQuery($entity_type_definition, 'AND', $this->database, QueryBase::getNamespaces($this->entityQueryFactory->get($entity_type_definition->id(), 'AND')));
|
||||
|
||||
if ($entity_type_definition->hasKey('bundle')) {
|
||||
$query->condition($entity_type_definition->getKey('bundle'), $this->getPluginDefinition()['controlled_bundle']);
|
||||
}
|
||||
|
||||
$target_entity_type_definition = $this->entityTypeManager->getDefinition($field->getSetting('target_type'));
|
||||
$label_column = $target_entity_type_definition->getKey('label');
|
||||
|
||||
// Some hacks.
|
||||
if (!$label_column && $target_entity_type_definition->id() == 'user') {
|
||||
$label_column = 'name';
|
||||
}
|
||||
|
||||
if (!$label_column) {
|
||||
// No label column is defined on target entity type, there's nothing we
|
||||
// can do.
|
||||
return [];
|
||||
}
|
||||
|
||||
$synonym_column = $field->getName() . '.entity.' . $label_column;
|
||||
$this->synonymsFindProcessCondition($condition, $synonym_column, $entity_type_definition->getKey('id'));
|
||||
|
||||
$conditions_array = $condition->conditions();
|
||||
$entity_condition = new Condition($conditions_array['#conjunction'], $query);
|
||||
|
||||
// We will insert a dummy condition for a synonym column just to have the
|
||||
// actual table.column data on the synonym column.
|
||||
$hash = md5($synonym_column);
|
||||
$query->condition($synonym_column, $hash);
|
||||
|
||||
unset($conditions_array['#conjunction']);
|
||||
foreach ($conditions_array as $v) {
|
||||
$entity_condition->condition($v['field'], $v['value'], $v['operator']);
|
||||
}
|
||||
|
||||
$query->condition($entity_condition);
|
||||
|
||||
// We need run the entity query in order to force it into building a normal
|
||||
// SQL query.
|
||||
$query->execute();
|
||||
|
||||
// Now let's get "demapped" normal SQL query that will tell us explicitly
|
||||
// where all entity properties/fields are stored among the SQL tables. Such
|
||||
// data is what we need and we do not want to demap it manually.
|
||||
$sql_query = $query->getSqlQuery();
|
||||
|
||||
// Swap the entity_id column into the alias "entity_id" as we are supposed
|
||||
// to do in this method implementation.
|
||||
$select_fields = &$sql_query->getFields();
|
||||
$select_fields = ['entity_id' => $select_fields[$entity_type_definition->getKey('id')]];
|
||||
$select_fields['entity_id']['alias'] = 'entity_id';
|
||||
|
||||
// We need some dummy extra condition to force them into re-compilation.
|
||||
$sql_query->where('1 = 1');
|
||||
$conditions_array = &$sql_query->conditions();
|
||||
foreach ($conditions_array as $k => $condition_array) {
|
||||
if (isset($condition_array['value']) && $condition_array['value'] == $hash) {
|
||||
list($table_alias, $column) = explode('.', $condition_array['field']);
|
||||
unset($conditions_array[$k]);
|
||||
|
||||
// Also unsetting the table aliased by this condition.
|
||||
$tables = &$sql_query->getTables();
|
||||
$real_table = $tables[$table_alias]['table'];
|
||||
foreach ($tables as $k2 => $v2) {
|
||||
if ($k2 != $table_alias && $real_table == $v2['table']) {
|
||||
unset($tables[$table_alias]);
|
||||
$table_alias = $k2;
|
||||
}
|
||||
}
|
||||
|
||||
$sql_query->addField($table_alias, $column, 'synonym');
|
||||
break;
|
||||
}
|
||||
}
|
||||
$result = $sql_query->execute();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle'])[$this->getPluginDefinition()['field']];
|
||||
return [
|
||||
$field->getConfigDependencyKey() => [$field->getConfigDependencyName()],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\Synonyms\Provider;
|
||||
|
||||
use Drupal\Component\Plugin\DependentPluginInterface;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryBase;
|
||||
use Drupal\Core\Entity\Query\Sql\Condition;
|
||||
use Drupal\Core\Entity\Query\Sql\Query;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingTrait;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetTrait;
|
||||
use Drupal\synonyms\SynonymsService\FieldTypeToSynonyms;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provide synonyms from attached simple fields.
|
||||
*
|
||||
* @SynonymsProvider(
|
||||
* id = "field",
|
||||
* deriver = "Drupal\synonyms\Plugin\Derivative\Field"
|
||||
* )
|
||||
*/
|
||||
class Field extends AbstractProvider implements SynonymsGetProviderInterface, SynonymsFindProviderInterface, SynonymsFormatWordingProviderInterface, DependentPluginInterface {
|
||||
|
||||
use SynonymsGetTrait, SynonymsFindTrait, SynonymsFormatWordingTrait;
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var FieldTypeToSynonyms
|
||||
*/
|
||||
protected $fieldTypeToSynonyms;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $entityQueryFactory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityFieldManagerInterface $entity_field_manager, FieldTypeToSynonyms $field_type_to_synonyms, EntityTypeManagerInterface $entity_type_manager, Connection $database, QueryFactory $entity_query_factory, ContainerInterface $container) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $container);
|
||||
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->fieldTypeToSynonyms = $field_type_to_synonyms;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->database = $database;
|
||||
$this->entityQueryFactory = $entity_query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('synonyms.provider.field_type_to_synonyms'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('database'),
|
||||
$container->get('entity.query'),
|
||||
$container
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []) {
|
||||
$map = $this->fieldTypeToSynonyms->getSimpleFieldTypeToPropertyMap();
|
||||
$field_type = $entity->getFieldDefinition($this->getPluginDefinition()['field'])->getType();
|
||||
|
||||
$synonyms = [];
|
||||
|
||||
if (isset($map[$field_type])) {
|
||||
foreach ($entity->get($this->getPluginDefinition()['field']) as $item) {
|
||||
$synonyms[] = $item->{$map[$field_type]};
|
||||
}
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function synonymsFind(ConditionInterface $condition) {
|
||||
$entity_type_definition = $this->entityTypeManager->getDefinition($this->getPluginDefinition()['controlled_entity_type']);
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle']);
|
||||
$field = $field[$this->getPluginDefinition()['field']];
|
||||
$field_property = $this->fieldTypeToSynonyms->getSimpleFieldTypeToPropertyMap();
|
||||
if (isset($field_property[$field->getType()])) {
|
||||
$field_property = $field_property[$field->getType()];
|
||||
}
|
||||
else {
|
||||
return [];
|
||||
}
|
||||
|
||||
$query = new FieldQuery($entity_type_definition, 'AND', $this->database, QueryBase::getNamespaces($this->entityQueryFactory->get($entity_type_definition->id(), 'AND')));
|
||||
|
||||
|
||||
if ($entity_type_definition->hasKey('bundle')) {
|
||||
$query->condition($entity_type_definition->getKey('bundle'), $this->getPluginDefinition()['controlled_bundle']);
|
||||
}
|
||||
|
||||
$synonym_column = $field->getName() . '.' . $field_property;
|
||||
$this->synonymsFindProcessCondition($condition, $synonym_column, $entity_type_definition->getKey('id'));
|
||||
|
||||
$conditions_array = $condition->conditions();
|
||||
$entity_condition = new Condition($conditions_array['#conjunction'], $query);
|
||||
|
||||
// We will insert a dummy condition for a synonym column just to have the
|
||||
// actual table.column data on the synonym column.
|
||||
$hash = md5($synonym_column);
|
||||
$query->condition($synonym_column, $hash);
|
||||
|
||||
unset($conditions_array['#conjunction']);
|
||||
foreach ($conditions_array as $v) {
|
||||
$entity_condition->condition($v['field'], $v['value'], $v['operator']);
|
||||
}
|
||||
|
||||
$query->condition($entity_condition);
|
||||
|
||||
// We need run the entity query in order to force it into building a normal
|
||||
// SQL query.
|
||||
$query->execute();
|
||||
|
||||
// Now let's get "demapped" normal SQL query that will tell us explicitly
|
||||
// where all entity properties/fields are stored among the SQL tables. Such
|
||||
// data is what we need and we do not want to demap it manually.
|
||||
$sql_query = $query->getSqlQuery();
|
||||
|
||||
// Swap the entity_id column into the alias "entity_id" as we are supposed
|
||||
// to do in this method implementation.
|
||||
$select_fields = &$sql_query->getFields();
|
||||
$select_fields = ['entity_id' => $select_fields[$entity_type_definition->getKey('id')]];
|
||||
$select_fields['entity_id']['alias'] = 'entity_id';
|
||||
|
||||
// We need some dummy extra condition to force them into re-compilation.
|
||||
$sql_query->where('1 = 1');
|
||||
$conditions_array = &$sql_query->conditions();
|
||||
foreach ($conditions_array as $k => $condition_array) {
|
||||
if (isset($condition_array['value']) && $condition_array['value'] == $hash) {
|
||||
list($table_alias, $column) = explode('.', $condition_array['field']);
|
||||
unset($conditions_array[$k]);
|
||||
|
||||
// Also unsetting the table aliased by this condition.
|
||||
$tables = &$sql_query->getTables();
|
||||
$real_table = $tables[$table_alias]['table'];
|
||||
foreach ($tables as $k2 => $v2) {
|
||||
if ($k2 != $table_alias && $real_table == $v2['table']) {
|
||||
unset($tables[$table_alias]);
|
||||
$table_alias = $k2;
|
||||
}
|
||||
}
|
||||
|
||||
$sql_query->addField($table_alias, $column, 'synonym');
|
||||
break;
|
||||
}
|
||||
}
|
||||
$result = $sql_query->execute();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$field = $this->entityFieldManager->getFieldDefinitions($this->getPluginDefinition()['controlled_entity_type'], $this->getPluginDefinition()['controlled_bundle'])[$this->getPluginDefinition()['field']];
|
||||
return [
|
||||
$field->getConfigDependencyKey() => [$field->getConfigDependencyName()],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hacked implementation of Entity query.
|
||||
*/
|
||||
class FieldQuery extends Query {
|
||||
|
||||
/**
|
||||
* We need to be able to extract SQL query object.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\SelectInterface
|
||||
*/
|
||||
public function getSqlQuery() {
|
||||
return $this->sqlQuery;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin;
|
||||
|
||||
use Drupal\Core\Field\FieldItemList;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
|
||||
/**
|
||||
* Field item list of "synonyms" computed base field.
|
||||
*/
|
||||
class SynonymsFieldItemList extends FieldItemList {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue($include_computed = FALSE) {
|
||||
$synonyms = [];
|
||||
|
||||
$entity = $this->getEntity();
|
||||
|
||||
$behavior_service = \Drupal::getContainer()->get('synonyms.behaviors');
|
||||
|
||||
$services = $behavior_service->getBehaviorServicesWithInterface(SynonymsGetProviderInterface::class);
|
||||
foreach ($services as $service_id => $service) {
|
||||
foreach ($behavior_service->getSynonymConfigEntities($service_id, $entity->getEntityTypeId(), $entity->bundle()) as $synonym_config) {
|
||||
$synonyms = array_merge($synonyms, $synonym_config->getProviderPluginInstance()->getSynonyms($entity, $synonym_config->getBehaviorConfiguration()));
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($synonyms);
|
||||
}
|
||||
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\views\argument_validator;
|
||||
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
use Drupal\views\Plugin\views\argument_validator\Entity;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Synonyms-friendly entity validator.
|
||||
*
|
||||
* @ViewsArgumentValidator(
|
||||
* id = "synonyms_entity",
|
||||
* deriver = "Drupal\synonyms\Plugin\Derivative\ViewsSynonymsEntityArgumentValidator"
|
||||
* )
|
||||
*/
|
||||
class SynonymsEntity extends Entity {
|
||||
|
||||
protected $multipleCapable = FALSE;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $entityQuery;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, EntityTypeManagerInterface $entity_type_manager, QueryFactory $entity_query, BehaviorService $behavior_service) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_manager);
|
||||
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityQuery = $entity_query;
|
||||
$this->behaviorService = $behavior_service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_definition,
|
||||
$plugin_definition,
|
||||
$container->get('entity.manager'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity.query'),
|
||||
$container->get('synonyms.behaviors')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
|
||||
$form['transform'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Transform dashes in URL to spaces.'),
|
||||
'#default_value' => $this->options['transform'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateArgument($argument) {
|
||||
if ($this->options['transform']) {
|
||||
$argument = str_replace('-', ' ', $argument);
|
||||
}
|
||||
|
||||
$entity_type = $this->entityTypeManager->getDefinition($this->definition['entity_type']);
|
||||
|
||||
if ($entity_type->hasKey('label') || $entity_type->id() == 'user') {
|
||||
$query = $this->entityQuery->get($entity_type->id());
|
||||
|
||||
// User entity type does not declare its label, while it does have one.
|
||||
$label_column = $entity_type->id() == 'user' ? 'name' : $entity_type->getKey('label');
|
||||
$query->condition($label_column, $argument, '=');
|
||||
|
||||
if ($entity_type->hasKey('bundle') && !empty($this->options['bundles'])) {
|
||||
$query->condition($entity_type->getKey('bundle'), $this->options['bundles'], 'IN');
|
||||
}
|
||||
|
||||
$result = $query->execute();
|
||||
if (!empty($result)) {
|
||||
$entities = $this->entityTypeManager->getStorage($entity_type->id())->loadMultiple($result);
|
||||
foreach ($entities as $entity) {
|
||||
if ($this->validateEntity($entity)) {
|
||||
$this->argument->argument = $entity->id();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We've fallen through with search by entity name, now it's time to search
|
||||
// by synonyms.
|
||||
$condition = new Condition('AND');
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER, $argument, '=');
|
||||
|
||||
foreach ($this->behaviorService->getBehaviorServicesWithInterface(SynonymsFindProviderInterface::class) as $service_id => $service) {
|
||||
foreach ($this->behaviorService->getSynonymConfigEntities($service_id, $entity_type->id(), empty($this->options['bundles']) ? NULL : $this->options['bundles']) as $synonym_config) {
|
||||
foreach ($synonym_config->getProviderPluginInstance()->synonymsFind(clone $condition) as $synonym) {
|
||||
$entity = $this->entityTypeManager->getStorage($entity_type->id())->load($synonym->entity_id);
|
||||
if ($this->validateEntity($entity)) {
|
||||
$this->argument->argument = $entity->id();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['transform'] = ['default' => FALSE];
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\Plugin\views\filter;
|
||||
|
||||
use Drupal\Core\Entity\Entity;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Plugin\views\filter\FilterPluginBase;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Filter entity by its name or one of its synonyms.
|
||||
*
|
||||
* @ViewsFilter("synonyms_entity")
|
||||
*/
|
||||
class SynonymsEntity extends FilterPluginBase {
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_type.bundle.info')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
|
||||
$options['widget'] = [
|
||||
'default' => 'autocomplete',
|
||||
];
|
||||
|
||||
$options['target_bundles'] = [
|
||||
'default' => NULL,
|
||||
];
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
|
||||
parent::init($view, $display, $options);
|
||||
|
||||
switch ($this->options['widget']) {
|
||||
case 'autocomplete':
|
||||
$this->operator = 'IN';
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
$this->operator = !$this->isExposed() || $this->options['expose']['multiple'] ? 'IN' : '=';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildExtraOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildExtraOptionsForm($form, $form_state);
|
||||
|
||||
$form['widget'] = [
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Widget'),
|
||||
'#options' => [
|
||||
'autocomplete' => $this->t('Autocomplete'),
|
||||
'select' => $this->t('Select'),
|
||||
],
|
||||
'#default_value' => $this->options['widget'],
|
||||
'#required' => TRUE,
|
||||
'#description' => $this->t('Choose what widget to use in order to specify entity.'),
|
||||
];
|
||||
|
||||
if ($this->entityTypeManager->getDefinition($this->definition['entity_type'])->hasKey('bundle')) {
|
||||
$options = array_map(function($item) {
|
||||
return $item['label'];
|
||||
}, $this->entityTypeBundleInfo->getBundleInfo($this->definition['entity_type']));
|
||||
|
||||
$form['target_bundles'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Bundles'),
|
||||
'#description' => $this->t('Limit the possible values down to specific subset of bundles. Leave empty to have no filter by bundles.'),
|
||||
'#default_value' => $this->options['target_bundles'],
|
||||
'#options' => $options,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitExtraOptionsForm($form, FormStateInterface $form_state) {
|
||||
parent::submitExtraOptionsForm($form, $form_state);
|
||||
|
||||
if (isset($form['target_bundles'])) {
|
||||
$target_bundles = array_values(array_filter($form_state->getValue($form['target_bundles']['#parents'])));
|
||||
if (empty($target_bundles)) {
|
||||
$target_bundles = NULL;
|
||||
}
|
||||
$form_state->setValueForElement($form['target_bundles'], $target_bundles);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasExtraOptions() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateExposed(&$form, FormStateInterface $form_state) {
|
||||
parent::validateExposed($form, $form_state);
|
||||
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
|
||||
$target_ids = $form_state->getValue($identifier);
|
||||
switch ($this->operator) {
|
||||
case 'IN':
|
||||
$target_ids = array_map(function($item) {
|
||||
return $item['target_id'];
|
||||
}, $target_ids);
|
||||
break;
|
||||
|
||||
case '=':
|
||||
$target_ids = $target_ids['target_id'];
|
||||
break;
|
||||
}
|
||||
|
||||
$form_state->setValue($identifier, $target_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function adminSummary() {
|
||||
if ($this->isExposed()) {
|
||||
return $this->t('Exposed');
|
||||
}
|
||||
|
||||
$labels = [];
|
||||
|
||||
if (!empty($this->value)) {
|
||||
foreach ($this->getEntityStorage()->loadMultiple($this->value) as $entity) {
|
||||
$labels[] = $entity->label();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->operator . ' ' . implode(', ', $labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function valueForm(&$form, FormStateInterface $form_state) {
|
||||
if (!empty($this->value)) {
|
||||
$default_value = $this->getEntityStorage()->loadMultiple($this->value);
|
||||
}
|
||||
else {
|
||||
$default_value = [];
|
||||
}
|
||||
|
||||
switch ($this->options['widget']) {
|
||||
case 'autocomplete':
|
||||
$form['value'] = [
|
||||
'#type' => 'synonyms_entity_autocomplete',
|
||||
'#title' => $this->t('Entity'),
|
||||
'#target_type' => $this->definition['entity_type'],
|
||||
'#target_bundles' => $this->options['target_bundles'],
|
||||
'#default_value' => $default_value,
|
||||
];
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
$form['value'] = [
|
||||
'#type' => 'synonyms_entity_select',
|
||||
'#title' => $this->t('Entity'),
|
||||
'#target_type' => $this->definition['entity_type'],
|
||||
'#handler_settings' => [
|
||||
'target_bundles' => $this->options['target_bundles'],
|
||||
],
|
||||
'#default_value' => array_keys($default_value),
|
||||
'#multiple' => TRUE,
|
||||
'#empty_option' => '',
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function valueSubmit($form, FormStateInterface $form_state) {
|
||||
parent::valueSubmit($form, $form_state);
|
||||
|
||||
$target_ids = array_map(function($item) {
|
||||
return $item['target_id'];
|
||||
}, $form_state->getValue($form['value']['#parents']));
|
||||
|
||||
$form_state->setValueForElement($form['value'], $target_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entity storage of the entity type this filter is set up to use.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected function getEntityStorage() {
|
||||
return $this->entityTypeManager->getStorage($this->definition['entity_type']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsProviderInterface;
|
||||
|
||||
/**
|
||||
* Interface of synonyms configuration entity.
|
||||
*/
|
||||
interface SynonymInterface extends ConfigEntityInterface, EntityWithPluginCollectionInterface {
|
||||
|
||||
/**
|
||||
* Get ID of the synonyms provider plugin that is set up in this entity.
|
||||
*
|
||||
* @return string
|
||||
* Plugin ID of synonyms provider that corresponds to this configuration
|
||||
* entity
|
||||
*/
|
||||
public function getProviderPlugin();
|
||||
|
||||
/**
|
||||
* Get instance of the synonyms provider plugin that is set up in this entity.
|
||||
*
|
||||
* @return SynonymsProviderInterface
|
||||
* Initiated synonyms provider instance that corresponds to this
|
||||
* configuration entity
|
||||
*/
|
||||
public function getProviderPluginInstance();
|
||||
|
||||
/**
|
||||
* Set the synonyms provider plugin to use in this entity.
|
||||
*
|
||||
* @param string $plugin
|
||||
* Synonyms provider plugin ID to set in this configuration entity
|
||||
*/
|
||||
public function setProviderPlugin($plugin);
|
||||
|
||||
/**
|
||||
* Get synonyms provider plugin configuration from this entity.
|
||||
*
|
||||
* @return array
|
||||
* Array of synonyms provider plugin configuration
|
||||
*/
|
||||
public function getProviderConfiguration();
|
||||
|
||||
/**
|
||||
* Set synonyms provider plugin configuration for this entity.
|
||||
*
|
||||
* @param array $provider_configuration
|
||||
* Array of synonyms provider plugin configuration to set
|
||||
*/
|
||||
public function setProviderConfiguration(array $provider_configuration);
|
||||
|
||||
/**
|
||||
* Get synonyms behavior configuration from this entity.
|
||||
*
|
||||
* @return array
|
||||
* Array of synonyms behavior configuration
|
||||
*/
|
||||
public function getBehaviorConfiguration();
|
||||
|
||||
/**
|
||||
* Set synonyms behavior configuration for this entity.
|
||||
*
|
||||
* @param array $behavior_configuration
|
||||
* Array of synonyms behavior configuration to set
|
||||
*/
|
||||
public function setBehaviorConfiguration(array $behavior_configuration);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms;
|
||||
|
||||
use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
|
||||
|
||||
/**
|
||||
* Provides a collection of synonym provider plugins.
|
||||
*/
|
||||
class SynonymProviderPluginCollection extends DefaultSingleLazyPluginCollection {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Drupal\synonyms\SynonymsProviderInterface\SynonymsProviderInterface
|
||||
*/
|
||||
public function &get($instance_id) {
|
||||
return parent::get($instance_id);
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
|
||||
/**
|
||||
* Interface to look up entities by synonyms they have.
|
||||
*/
|
||||
interface SynonymsFindProviderInterface extends SynonymsProviderInterface {
|
||||
|
||||
/**
|
||||
* Constant which denotes placeholder of a synonym column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COLUMN_SYNONYM_PLACEHOLDER = '***SYNONYM_COLUMN***';
|
||||
|
||||
/**
|
||||
* Constant which denotes placeholder of an entity ID column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COLUMN_ENTITY_ID_PLACEHOLDER = '***ENTITY_ID***';
|
||||
|
||||
/**
|
||||
* Look up entities by their synonyms within a behavior implementation.
|
||||
*
|
||||
* You are provided with a SQL condition that you should apply to the storage
|
||||
* of synonyms within the provided behavior implementation. And then return
|
||||
* result: what entities are matched by the provided condition through what
|
||||
* synonyms.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\ConditionInterface $condition
|
||||
* Condition that defines what to search for. Apart from normal SQL
|
||||
* conditions as known in Drupal, it may contain the following placeholders:
|
||||
* - SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER: to denote
|
||||
* synonyms column which you should replace with the actual column name
|
||||
* where the synonyms data for your provider is stored in plain text.
|
||||
* - SynonymsFindProviderInterface::COLUMN_ENTITY_ID_PLACEHOLDER: to denote
|
||||
* column that holds entity ID. You are supposed to replace this
|
||||
* placeholder with actual column name that holds entity ID in your case.
|
||||
* For ease of work with these placeholders, you may use the
|
||||
* SynonymsFindTrait and then just invoke the
|
||||
* $this->synonymsFindProcessCondition() method, so you won't have to worry
|
||||
* much about it
|
||||
*
|
||||
* @return \Traversable
|
||||
* Traversable result set of found synonyms and entity IDs to which those
|
||||
* belong. Each element in the result set should be an object and should
|
||||
* have the following structure:
|
||||
* - synonym: (string) Synonym that was found and which satisfies the
|
||||
* provided condition
|
||||
* - entity_id: (int) ID of the entity to which the found synonym belongs
|
||||
*/
|
||||
public function synonymsFind(ConditionInterface $condition);
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
|
||||
/**
|
||||
* Supportive trait to find synonyms.
|
||||
*/
|
||||
trait SynonymsFindTrait {
|
||||
|
||||
/**
|
||||
* Supportive method to process $condition argument in synonymsFind().
|
||||
*
|
||||
* This method will swap SynonymsFindProviderInterface::COLUMN_* to real
|
||||
* column names in $condition for you, so you do not have to worry about
|
||||
* internal processing of $condition object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\ConditionInterface $condition
|
||||
* Condition to be processed
|
||||
* @param string $synonym_column
|
||||
* Actual name of the column where synonyms are kept in text
|
||||
* @param string $entity_id_column
|
||||
* Actual name of the column where entity_ids are kept
|
||||
*/
|
||||
public function synonymsFindProcessCondition(ConditionInterface $condition, $synonym_column, $entity_id_column) {
|
||||
$condition_array = &$condition->conditions();
|
||||
foreach ($condition_array as &$v) {
|
||||
if (is_array($v) && isset($v['field'])) {
|
||||
if ($v['field'] instanceof ConditionInterface) {
|
||||
// Recursively process this condition too.
|
||||
$this->synonymsFindProcessCondition($v['field'], $synonym_column, $entity_id_column);
|
||||
}
|
||||
else {
|
||||
$replace = [
|
||||
SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER => $synonym_column,
|
||||
SynonymsFindProviderInterface::COLUMN_ENTITY_ID_PLACEHOLDER => $entity_id_column,
|
||||
];
|
||||
$v['field'] = str_replace(array_keys($replace), array_values($replace), $v['field']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
|
||||
/**
|
||||
* Interface to format a synonym into some kind of wording.
|
||||
*/
|
||||
interface SynonymsFormatWordingProviderInterface {
|
||||
|
||||
/**
|
||||
* Format a synonym into wording as requested by configuration.
|
||||
*
|
||||
* @param string $synonym
|
||||
* Synonym that should be formatted
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity to which this synonym belongs
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which it all happens
|
||||
*
|
||||
* @return string
|
||||
* Formatted wording
|
||||
*/
|
||||
public function synonymFormatWording($synonym, ContentEntityInterface $entity, SynonymInterface $synonym_config);
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* Array of supported tokens in wording. Keys are the tokens whereas
|
||||
* corresponding values are explanations about what each token means
|
||||
*/
|
||||
public function formatWordingAvailableTokens();
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
|
||||
/**
|
||||
* Trait to format wording of a synonym.
|
||||
*/
|
||||
trait SynonymsFormatWordingTrait {
|
||||
|
||||
/**
|
||||
* Format a synonym into wording as requested by configuration.
|
||||
*
|
||||
* @param string $synonym
|
||||
* Synonym that should be formatted
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity to which this synonym belongs
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which it all happens
|
||||
*
|
||||
* @return string
|
||||
* Formatted wording
|
||||
*/
|
||||
public function synonymFormatWording($synonym, ContentEntityInterface $entity, SynonymInterface $synonym_config) {
|
||||
// TODO: maybe we should use tokens replacement here? But then it would mean
|
||||
// an extra dependency on the tokens module. Is it worth it? For now let's
|
||||
// use stupid str_replace() and incorporate tokens only if user base really
|
||||
// asks for it.
|
||||
$map = [
|
||||
'@synonym' => $synonym,
|
||||
'@entity_label' => $entity->label(),
|
||||
];
|
||||
return str_replace(array_keys($map), array_values($map), $synonym_config->getBehaviorConfiguration()['wording']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* Array of supported tokens in wording. Keys are the tokens whereas
|
||||
* corresponding values are explanations about what each token means
|
||||
*/
|
||||
public function formatWordingAvailableTokens() {
|
||||
return [
|
||||
'@synonym' => $this->t('actual synonym value'),
|
||||
'@entity_label' => $this->t('actual label of the entity this synonym belongs to'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Component\Plugin\ConfigurablePluginInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
use Drupal\synonyms\SynonymsBehavior\SynonymsBehaviorInterface;
|
||||
|
||||
/**
|
||||
* Interface to extract (get) synonyms from an entity.
|
||||
*/
|
||||
interface SynonymsGetProviderInterface extends SynonymsProviderInterface {
|
||||
|
||||
/**
|
||||
* Fetch synonyms from an entity.
|
||||
*
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity whose synonyms should be fetched
|
||||
* @param array $behavior_configuration
|
||||
* Configuration of the synonyms behavior that this provider should obey to
|
||||
*
|
||||
* @return string[]
|
||||
* Array of extracted synonyms
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []);
|
||||
|
||||
/**
|
||||
* Fetch synonyms from multiple entities at once.
|
||||
*
|
||||
* @param ContentEntityInterface[] $entities
|
||||
* Array of entities whose synonyms should be fetched. The array will be
|
||||
* keyed by entity ID and all provided entities will be of the same entity
|
||||
* type and bundle
|
||||
* @param array $behavior_configuration
|
||||
* Configuration of the synonyms behavior that this provider should obey to
|
||||
*
|
||||
* @return array
|
||||
* Array of extracted synonyms. It must be keyed by entity ID and each sub
|
||||
* array should represent a list of synonyms that were extracted from the
|
||||
* corresponding entity
|
||||
*/
|
||||
public function getSynonymsMultiple(array $entities, array $behavior_configuration = []);
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
|
||||
/**
|
||||
* Trait to extract synonyms from an entity.
|
||||
*/
|
||||
trait SynonymsGetTrait {
|
||||
|
||||
/**
|
||||
* Fetch synonyms from multiple entities at once.
|
||||
*
|
||||
* @param ContentEntityInterface[] $entities
|
||||
* Array of entities whose synonyms should be fetched. They array will be
|
||||
* keyed by entity ID and all provided entities will be of the same entity
|
||||
* type and bundle
|
||||
* @param array $behavior_configuration
|
||||
* Configuration of the synonyms behavior that this provider should obey to
|
||||
*
|
||||
* @return array
|
||||
* Array of extracted synonyms. It must be keyed by entity ID and each sub
|
||||
* array should represent a list of synonyms that were extracted from the
|
||||
* corresponding entity
|
||||
*/
|
||||
public function getSynonymsMultiple(array $entities, array $behavior_configuration = []) {
|
||||
$synonyms = [];
|
||||
|
||||
foreach ($entities as $entity_id => $entity) {
|
||||
$synonyms[$entity_id] = $this->getSynonyms($entity, $behavior_configuration);
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch synonyms from an entity.
|
||||
*
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity whose synonyms should be fetched
|
||||
* @param array $behavior_configuration
|
||||
* Configuration of the synonyms behavior that this provider should obey to
|
||||
*
|
||||
* @return string[]
|
||||
* Array of extracted synonyms
|
||||
*/
|
||||
abstract public function getSynonyms(ContentEntityInterface $entity, array $behavior_configuration = []);
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsProviderInterface;
|
||||
|
||||
use Drupal\synonyms\SynonymsService\Behavior\SynonymsBehaviorInterface;
|
||||
|
||||
/**
|
||||
* Most generic synonyms provider interface. All providers must implement it.
|
||||
*/
|
||||
interface SynonymsProviderInterface {
|
||||
|
||||
/**
|
||||
* Fetch behavior service which corresponds to this provider.
|
||||
*
|
||||
* @return string
|
||||
* Name of synonyms behavior service which corresponds to this provider
|
||||
*/
|
||||
public function getBehaviorService();
|
||||
|
||||
/**
|
||||
* Fetch behavior service instance which corresponds to this provider.
|
||||
*
|
||||
* @return SynonymsBehaviorInterface
|
||||
*/
|
||||
public function getBehaviorServiceInstance();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\synonyms\Annotation\SynonymsProvider;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsProviderInterface;
|
||||
|
||||
/**
|
||||
* Plugin manager for Synonyms provider plugin type.
|
||||
*/
|
||||
class SynonymsProviderPluginManager extends DefaultPluginManager {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct(
|
||||
'Plugin/Synonyms/Provider',
|
||||
$namespaces,
|
||||
$module_handler,
|
||||
SynonymsProviderInterface::class,
|
||||
SynonymsProvider::class
|
||||
);
|
||||
$this->alterInfo('synonyms_provider_info');
|
||||
$this->setCacheBackend($cache_backend, 'synonyms_provider_info_plugins');
|
||||
}
|
||||
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService\Behavior;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
|
||||
/**
|
||||
* Synonyms behavior service for autocomplete.
|
||||
*/
|
||||
class AutocompleteService implements SynonymsBehaviorConfigurableInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* @var KeyValueStoreInterface
|
||||
*/
|
||||
protected $keyValue;
|
||||
|
||||
/**
|
||||
* The entity reference selection handler plugin manager.
|
||||
*
|
||||
* @var SelectionPluginManagerInterface
|
||||
*/
|
||||
protected $selectionManager;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* @var RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
public function __construct(KeyValueFactoryInterface $key_value, SelectionPluginManagerInterface $selection_plugin_manager, BehaviorService $behavior_service, Connection $database, EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer) {
|
||||
$this->keyValue = $key_value->get('synonyms_entity_autocomplete');
|
||||
$this->selectionManager = $selection_plugin_manager;
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->database = $database;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state, array $configuration, SynonymInterface $synonym_config) {
|
||||
$replacements = [
|
||||
'#theme' => 'item_list',
|
||||
'#list_type' => 'ul',
|
||||
'#items' => [],
|
||||
];
|
||||
foreach ($synonym_config->getProviderPluginInstance()->formatWordingAvailableTokens() as $token => $token_info) {
|
||||
$replacements['#items'][] = Html::escape($token) . ': ' . $token_info;
|
||||
}
|
||||
|
||||
$replacements = $this->renderer->renderRoot($replacements);
|
||||
|
||||
$form['wording'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Wording for autocomplete suggestion'),
|
||||
'#default_value' => $configuration['wording'],
|
||||
'#description' => $this->t('Specify the wording with which the autocomplete suggestion should be presented. Available replacement tokens are: @replacements', [
|
||||
'@replacements' => $replacements,
|
||||
]),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config) {
|
||||
return [
|
||||
'wording' => $form_state->getValue('wording'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Autocomplete');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRequiredInterfaces() {
|
||||
return [
|
||||
SynonymsGetProviderInterface::class,
|
||||
SynonymsFindProviderInterface::class,
|
||||
SynonymsFormatWordingProviderInterface::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute synonym-friendly lookup of entities by a given keyword.
|
||||
*
|
||||
* @param string $keyword
|
||||
* Keyword to search for
|
||||
* @param string $key_value_key
|
||||
* Key under which additional settings about the lookup are stored in
|
||||
* key-value storage
|
||||
*
|
||||
* @return array
|
||||
* Array of looked up suggestions. Each array will have the following
|
||||
* structure:
|
||||
* - entity_id: (int) ID of the entity which this entry represents
|
||||
* - entity_label: (string) Label of the entity which this entry represents
|
||||
* - wording: (string) Wording with which this entry should be shown to the
|
||||
* end user on the UI
|
||||
*/
|
||||
public function autocompleteLookup($keyword, $key_value_key) {
|
||||
$suggestions = [];
|
||||
|
||||
if ($this->keyValue->has($key_value_key)) {
|
||||
$settings = $this->keyValue->get($key_value_key);
|
||||
|
||||
$suggested_entity_ids = [];
|
||||
|
||||
$target_bundles = $settings['target_bundles'];
|
||||
$handler_settings = [];
|
||||
if (!empty($target_bundles)) {
|
||||
$handler_settings['target_bundles'] = $target_bundles;
|
||||
}
|
||||
elseif (!$this->entityTypeManager->getDefinition($settings['target_type'])->hasKey('bundle')) {
|
||||
$target_bundles = [$settings['target_type']];
|
||||
}
|
||||
|
||||
$options = [
|
||||
'target_type' => $settings['target_type'],
|
||||
'handler' => 'default',
|
||||
'handler_settings' => $handler_settings,
|
||||
];
|
||||
$handler = $this->selectionManager->getInstance($options);
|
||||
|
||||
foreach ($handler->getReferenceableEntities($keyword, $settings['match'], $settings['suggestion_size']) as $suggested_entities) {
|
||||
foreach ($suggested_entities as $entity_id => $entity_label) {
|
||||
$suggestions[] = [
|
||||
'entity_id' => $entity_id,
|
||||
'entity_label' => $entity_label,
|
||||
'wording' => $entity_label,
|
||||
];
|
||||
if ($settings['suggest_only_unique']) {
|
||||
$suggested_entity_ids[] = $entity_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($suggestions) < $settings['suggestion_size']) {
|
||||
foreach ($this->behaviorService->getSynonymConfigEntities('synonyms.behavior.autocomplete', $settings['target_type'], $target_bundles) as $behavior_service) {
|
||||
$plugin_instance = $behavior_service->getProviderPluginInstance();
|
||||
|
||||
$condition = new Condition('AND');
|
||||
switch ($settings['match']) {
|
||||
case 'CONTAINS':
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER, '%' . $this->database->escapeLike($keyword) . '%', 'LIKE');
|
||||
break;
|
||||
|
||||
case 'STARTS_WITH':
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER, $this->database->escapeLike($keyword) . '%', 'LIKE');
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($suggested_entity_ids)) {
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_ENTITY_ID_PLACEHOLDER, $suggested_entity_ids, 'NOT IN');
|
||||
}
|
||||
|
||||
|
||||
foreach ($plugin_instance->synonymsFind($condition) as $row) {
|
||||
if (!in_array($row->entity_id, $suggested_entity_ids)) {
|
||||
$suggestions[] = [
|
||||
'entity_id' => $row->entity_id,
|
||||
'entity_label' => NULL,
|
||||
'synonym' => $row->synonym,
|
||||
'synonym_config_entity' => $behavior_service,
|
||||
'wording' => NULL,
|
||||
];
|
||||
}
|
||||
|
||||
if ($settings['suggest_only_unique']) {
|
||||
$suggested_entity_ids[] = $row->entity_id;
|
||||
}
|
||||
|
||||
if (count($suggestions) == $settings['suggestion_size']) {
|
||||
break(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
foreach ($suggestions as $suggestion) {
|
||||
if (!$suggestion['entity_label']) {
|
||||
$ids[] = $suggestion['entity_id'];
|
||||
}
|
||||
}
|
||||
$ids = array_unique($ids);
|
||||
|
||||
if (!empty($ids)) {
|
||||
$entities = $this->entityTypeManager->getStorage($settings['target_type'])
|
||||
->loadMultiple($ids);
|
||||
|
||||
foreach ($suggestions as $k => $suggestion) {
|
||||
if (!$suggestion['entity_label']) {
|
||||
$suggestions[$k]['entity_label'] = $entities[$suggestion['entity_id']]->label();
|
||||
$suggestions[$k]['wording'] = $suggestion['synonym_config_entity']->getProviderPluginInstance()->synonymFormatWording($suggestion['synonym'], $entities[$suggestion['entity_id']], $suggestion['synonym_config_entity']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService\Behavior;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFormatWordingProviderInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
|
||||
/**
|
||||
* Synonyms behavior service for select widget.
|
||||
*/
|
||||
class SelectService implements SynonymsBehaviorConfigurableInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* @var RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* SelectService constructor.
|
||||
*/
|
||||
public function __construct(BehaviorService $behavior_service, RendererInterface $renderer) {
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state, array $configuration, SynonymInterface $synonym_config) {
|
||||
$replacements = [
|
||||
'#theme' => 'item_list',
|
||||
'#list_type' => 'ul',
|
||||
'#items' => [],
|
||||
];
|
||||
foreach ($synonym_config->getProviderPluginInstance()->formatWordingAvailableTokens() as $token => $token_info) {
|
||||
$replacements['#items'][] = Html::escape($token) . ': ' . $token_info;
|
||||
}
|
||||
|
||||
$replacements = $this->renderer->renderRoot($replacements);
|
||||
|
||||
$form['wording'] = [
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Wording for select entry'),
|
||||
'#default_value' => $configuration['wording'],
|
||||
'#description' => $this->t('Specify the wording with which the select entry should be presented. Available replacement tokens are: @replacements', [
|
||||
'@replacements' => $replacements,
|
||||
]),
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config) {
|
||||
return [
|
||||
'wording' => $form_state->getValue('wording'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Select');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRequiredInterfaces() {
|
||||
return [
|
||||
SynonymsGetProviderInterface::class,
|
||||
SynonymsFormatWordingProviderInterface::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a list of synonyms from an entity.
|
||||
*
|
||||
* @param ContentEntityInterface $entity
|
||||
* Entity from which to extract the synonyms
|
||||
*
|
||||
* @return array
|
||||
* Array of synonyms. Each sub array will have the following structure:
|
||||
* - synonym: (string) Synonym itself
|
||||
* - wording: (string) Formatted wording with which this synonym should be
|
||||
* presented to the end user
|
||||
*/
|
||||
public function getSynonyms(ContentEntityInterface $entity) {
|
||||
$synonyms = $this->getSynonymsMultiple([$entity->id() => $entity]);
|
||||
return $synonyms[$entity->id()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a list of synonyms from multiple entities.
|
||||
*
|
||||
* @param ContentEntityInterface[] $entities
|
||||
* Array of entities from which to extract the synonyms. It should be keyed
|
||||
* by entity ID and may only contain entities of the same type and bundle
|
||||
*
|
||||
* @return array
|
||||
* Array of synonyms. The returned array will be keyed by entity ID and the
|
||||
* inner array will have the following structure:
|
||||
* - synonym: (string) Synonym itself
|
||||
* - wording: (string) Formatted wording with which this synonym should be
|
||||
* presented to the end user
|
||||
*/
|
||||
public function getSynonymsMultiple(array $entities) {
|
||||
if (empty($entities)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$synonym_configs = $this->behaviorService->getSynonymConfigEntities('synonyms.behavior.select', reset($entities)->getEntityTypeId(), reset($entities)->bundle());
|
||||
|
||||
$synonyms = [];
|
||||
foreach ($entities as $entity) {
|
||||
$synonyms[$entity->id()] = [];
|
||||
}
|
||||
|
||||
foreach ($synonym_configs as $synonym_config) {
|
||||
foreach ($synonym_config->getProviderPluginInstance()->getSynonymsMultiple($entities, $synonym_config->getBehaviorConfiguration()) as $entity_id => $entity_synonyms) {
|
||||
foreach ($entity_synonyms as $entity_synonym) {
|
||||
$synonyms[$entity_id][] = [
|
||||
'synonym' => $entity_synonym,
|
||||
'wording' => $synonym_config->getProviderPluginInstance()->synonymFormatWording($entity_synonym, $entities[$entity_id], $synonym_config),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService\Behavior;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\synonyms\SynonymInterface;
|
||||
|
||||
/**
|
||||
* Interface of a configurable synonyms behavior.
|
||||
*/
|
||||
interface SynonymsBehaviorConfigurableInterface extends SynonymsBehaviorInterface {
|
||||
|
||||
/**
|
||||
* Build configuration form.
|
||||
*
|
||||
* @param array $form
|
||||
* Form into which your configuration form will be embedded. You are
|
||||
* supposed to extend this array with additional configuration form elements
|
||||
* that your behavior needs
|
||||
* @param FormStateInterface $form_state
|
||||
* Form state object that corresponds to this form
|
||||
* @param array $configuration
|
||||
* Array of existing configuration for your behavior. Normally you would use
|
||||
* it as a source of default values for your configuration form elements
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which the form is being built
|
||||
*
|
||||
* @return array
|
||||
* Extended $form that includes the form elements required for configuration
|
||||
* of your behavior
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state, array $configuration, SynonymInterface $synonym_config);
|
||||
|
||||
/**
|
||||
* Validate submitted values into your configuration form.
|
||||
*
|
||||
* @param array $form
|
||||
* Your configuration form as it was built in
|
||||
* static::buildConfigurationForm()
|
||||
* @param FormStateInterface $form_state
|
||||
* Form state that corresponds to this form. You should rise form validation
|
||||
* errors on this form state, should you discover any in user input
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which the form is being built
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config);
|
||||
|
||||
/**
|
||||
* Process submitted values and generate new configuration.
|
||||
*
|
||||
* @param array $form
|
||||
* Your configuration form as it was built in
|
||||
* static::buildConfigurationForm()
|
||||
* @param FormStateInterface $form_state
|
||||
* Form state that corresponds to this form
|
||||
* @param SynonymInterface $synonym_config
|
||||
* Synonym config entity in the context of which the form is being built
|
||||
*
|
||||
* @return array
|
||||
* Array of new behavior configuration that corresponds to the submitted
|
||||
* values in the form
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state, SynonymInterface $synonym_config);
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService\Behavior;
|
||||
|
||||
/**
|
||||
* Interface of a synonyms behavior. All behaviors must implement it.
|
||||
*/
|
||||
interface SynonymsBehaviorInterface {
|
||||
|
||||
/**
|
||||
* Get human readable title of this behavior.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle();
|
||||
|
||||
/**
|
||||
* Get a list of interfaces required from synonyms provider plugins.
|
||||
*
|
||||
* Get a list of PHP interfaces a synonyms provider plugin must implement in
|
||||
* order to support this behavior.
|
||||
*
|
||||
* @return array
|
||||
* Array of PHP interfaces a synonyms provider plugin must implement in
|
||||
* order to support this behavior
|
||||
*/
|
||||
public function getRequiredInterfaces();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\synonyms\Entity\Synonym;
|
||||
use Drupal\synonyms\SynonymsService\Behavior\SynonymsBehaviorInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Collect all known synonyms behavior services.
|
||||
*
|
||||
* Collect all known synonyms behavior services during dependency injection
|
||||
* container compilation.
|
||||
*/
|
||||
class BehaviorService implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $behaviorServices;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->behaviorServices = [];
|
||||
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new discovered behavior service.
|
||||
*
|
||||
* @param SynonymsBehaviorInterface $behavior_service
|
||||
* Behavior service object that was discovered and should be added into the
|
||||
* list of known ones
|
||||
* @param string $id
|
||||
* Service ID that corresponds to this behavior service
|
||||
*/
|
||||
public function addBehaviorService(SynonymsBehaviorInterface $behavior_service, $id) {
|
||||
if (!isset($this->behaviorServices[$id])) {
|
||||
$this->behaviorServices[$id] = [
|
||||
'service' => $behavior_service,
|
||||
'required_interfaces' => [],
|
||||
];
|
||||
}
|
||||
$this->behaviorServices[$id]['required_interfaces'] = $behavior_service->getRequiredInterfaces();
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of known synonyms behavior service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBehaviorServices() {
|
||||
return $this->behaviorServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a synonyms behavior service.
|
||||
*
|
||||
* @param string $behavior_service_id
|
||||
* ID of the service to retrieve
|
||||
*
|
||||
* @return array
|
||||
* Array of information about the behavior service. The array will have the
|
||||
* following structure:
|
||||
* - service: (SynonymsBehaviorInterface) Initiated behavior service
|
||||
* - required_interfaces: (array) Array of PHP interfaces it requires
|
||||
*/
|
||||
public function getBehaviorService($behavior_service_id) {
|
||||
return isset($this->behaviorServices[$behavior_service_id]) ? $this->behaviorServices[$behavior_service_id] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of behavior services that require a given interface.
|
||||
*
|
||||
* @param string $interface
|
||||
* Full name of the interface
|
||||
*
|
||||
* @return array
|
||||
* The return structure of this method is identical of the return structure
|
||||
* of static::getBehaviorServices()
|
||||
*/
|
||||
public function getBehaviorServicesWithInterface($interface) {
|
||||
$return = [];
|
||||
|
||||
foreach ($this->getBehaviorServices() as $service_id => $service) {
|
||||
if (in_array($interface, $service['required_interfaces'])) {
|
||||
$return[$service_id] = $service;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of enabled synonym providers for a requested synonyms behavior.
|
||||
*
|
||||
* @param string $synonyms_behavior
|
||||
* ID of the synonyms behavior services whose enabled providers should be
|
||||
* returned
|
||||
* @param string $entity_type
|
||||
* Entity type for which to conduct the search
|
||||
* @param string|array $bundle
|
||||
* Single bundle or an array of them for which to conduct the search. If
|
||||
* null is given, then no restrictions are applied on bundle level
|
||||
*
|
||||
* @return Synonym[]
|
||||
*/
|
||||
public function getSynonymConfigEntities($synonyms_behavior, $entity_type, $bundle) {
|
||||
$entities = [];
|
||||
|
||||
if (is_scalar($bundle) && !is_null($bundle)) {
|
||||
$bundle = [$bundle];
|
||||
}
|
||||
|
||||
foreach ($this->entityTypeManager->getStorage('synonym')->loadMultiple() as $entity) {
|
||||
$provider_instance = $entity->getProviderPluginInstance();
|
||||
$provider_definition = $provider_instance->getPluginDefinition();
|
||||
if ($provider_definition['synonyms_behavior_service'] == $synonyms_behavior && $provider_definition['controlled_entity_type'] == $entity_type && (!is_array($bundle) || in_array($provider_definition['controlled_bundle'], $bundle))) {
|
||||
$entities[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService;
|
||||
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
|
||||
/**
|
||||
* Service to look up an entity by its name or synonym.
|
||||
*/
|
||||
class EntityGet {
|
||||
|
||||
/**
|
||||
* @var SynonymsFind
|
||||
*/
|
||||
protected $synonymsFindService;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $queryFactory;
|
||||
|
||||
public function __construct(SynonymsFind $synonyms_find_service, EntityTypeManagerInterface $entity_type_manager, QueryFactory $query_factory) {
|
||||
$this->synonymsFindService = $synonyms_find_service;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->queryFactory = $query_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try finding an entity by its name or synonym.
|
||||
*
|
||||
* @param EntityTypeInterface $entity_type
|
||||
* What entity type is being searched
|
||||
* @param string $name
|
||||
* The look up keyword (the supposed name or synonym)
|
||||
* @param string $bundle
|
||||
* Optionally limit the search within a specific bundle name of the provided
|
||||
* entity type
|
||||
*
|
||||
* @return int
|
||||
* ID of the looked up entity. If such entity was not found, then 0 is
|
||||
* returned
|
||||
*/
|
||||
public function entityGetBySynonym(EntityTypeInterface $entity_type, $name, $bundle = NULL) {
|
||||
if ($entity_type->id() == 'user' || $entity_type->hasKey('label')) {
|
||||
$label_column = $entity_type->id() == 'user' ? 'name' : $entity_type->getKey('label');
|
||||
$query = $this->queryFactory->get($entity_type->id());
|
||||
$query->condition($label_column, $name);
|
||||
if ($entity_type->hasKey('bundle') && $bundle) {
|
||||
$query->condition($entity_type->getKey('bundle'), $bundle);
|
||||
}
|
||||
|
||||
$result = $query->execute();
|
||||
$result = reset($result);
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
$condition = new Condition('AND');
|
||||
$condition->condition(SynonymsFindProviderInterface::COLUMN_SYNONYM_PLACEHOLDER, $name);
|
||||
|
||||
$synonyms_found = $this->synonymsFindService->synonymsFind($condition, $entity_type, $bundle, '*');
|
||||
if (isset($synonyms_found[0]->entity_id)) {
|
||||
return $synonyms_found[0]->entity_id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService;
|
||||
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
|
||||
/**
|
||||
* Service to map known field types to how synonyms are encoded in them.
|
||||
*/
|
||||
class FieldTypeToSynonyms {
|
||||
|
||||
/**
|
||||
* @var ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* FieldTypeToSynonyms constructor.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map field types to the properties within them where synonyms are stored.
|
||||
*
|
||||
* @return array
|
||||
* Map where keys are simple field types and values are the properties where
|
||||
* the corresponding field type keeps the synonyms
|
||||
*/
|
||||
public function getSimpleFieldTypeToPropertyMap() {
|
||||
$map = [
|
||||
'integer' => 'value',
|
||||
'float' => 'value',
|
||||
'decimal' => 'value',
|
||||
'string' => 'value',
|
||||
'email' => 'value',
|
||||
'telephone' => 'value',
|
||||
];
|
||||
$this->moduleHandler->alter('synonyms_field_type_to_synonym', $map);
|
||||
return $map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms\SynonymsService;
|
||||
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsFindProviderInterface;
|
||||
|
||||
/**
|
||||
* Service that allows to look up entities by their synonyms.
|
||||
*/
|
||||
class SynonymsFind {
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* @var EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* SynonymsFind constructor.
|
||||
*/
|
||||
public function __construct(BehaviorService $behavior_service, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup entity IDs by the $condition.
|
||||
*
|
||||
* @param Condition $condition
|
||||
* Condition which defines what to search for
|
||||
* @param EntityTypeInterface $entity_type
|
||||
* Entity type within which to search
|
||||
* @param string|array $bundle
|
||||
* Either single bundle string or array of such within which to search. NULL
|
||||
* stands for no filtering by bundle, i.e. searching among all bundles
|
||||
* @param string|array $service_id
|
||||
* Either a single behavior service ID or an array of them within which to
|
||||
* execute the lookup. You may also use the wildcard * to search among all
|
||||
* supported behaviors
|
||||
*
|
||||
* @return array
|
||||
* Array of looked up synonyms/entities. Each element in this array will be
|
||||
* an object with the following structure:
|
||||
* - synonym: (string) synonym that was looked up
|
||||
* - entity_id: (int) ID of the entity which this synonym belongs to
|
||||
*/
|
||||
public function synonymsFind(Condition $condition, EntityTypeInterface $entity_type, $bundle = NULL, $service_id = 'synonyms.behavior.autocomplete') {
|
||||
if (!$entity_type->getKey('bundle')) {
|
||||
$bundle = $entity_type->id();
|
||||
}
|
||||
|
||||
$lookup = [];
|
||||
|
||||
if ($service_id == '*') {
|
||||
$service_id = array_keys($this->behaviorService->getBehaviorServicesWithInterface(SynonymsFindProviderInterface::class));
|
||||
}
|
||||
|
||||
if (!is_array($service_id)) {
|
||||
$service_id = [$service_id];
|
||||
}
|
||||
|
||||
if (is_null($bundle)) {
|
||||
$bundle = array_keys($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()));
|
||||
}
|
||||
|
||||
foreach ($service_id as $service_id_value) {
|
||||
foreach ($this->behaviorService->getSynonymConfigEntities($service_id_value, $entity_type->id(), $bundle) as $synonym_config) {
|
||||
foreach ($synonym_config->getProviderPluginInstance()->synonymsFind(clone $condition) as $synonym) {
|
||||
$lookup[] = $synonym;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $lookup;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Documentation for Synonyms module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hook to alter known simple field types that contain potential synonyms data.
|
||||
*
|
||||
* The simple field types (those defined by $map) are used for synonyms
|
||||
* providing through Drupal\synonyms\Plugin\Synonyms\Provider\Field or
|
||||
* Drupal\synonyms\Plugin\Synonyms\Provider\BaseField plugins depending whether
|
||||
* it is an attached field or base one correspondingly. Both synonyms providers
|
||||
* plugin simply take a specific column/value from the field and return it as a
|
||||
* synonym.
|
||||
*
|
||||
* @param array $map
|
||||
* Array of known simple field types eligible for synonyms providing through
|
||||
* the 2 plugins. Keys are field types whereas corresponding values are field
|
||||
* columns that contain synonyms. You are encouraged to alter $map in order to
|
||||
* add/remove known field types per your business needs
|
||||
*/
|
||||
function hook_synonyms_field_type_to_synonym_alter(&$map) {
|
||||
// Let's assume our module provides some additional field type and we want
|
||||
// that field type to be eligible for synonyms providing through the 2 simple
|
||||
// plugins. And let's suppose the synonyms are actually stored in 'value'
|
||||
// column within our custom field type.
|
||||
$map['the_field_type_my_module_provides'] = 'value';
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
name: Synonyms
|
||||
description: Provides synonyms feature for content entities.
|
||||
package: Synonyms
|
||||
type: module
|
||||
# core: 8.x
|
||||
configure: entity.synonym.overview
|
||||
dependencies:
|
||||
- system (>=8.2)
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-03-11
|
||||
version: '8.x-1.0-alpha1+4-dev'
|
||||
core: '8.x'
|
||||
project: 'synonyms'
|
||||
datestamp: 1520765586
|
||||
@@ -0,0 +1,5 @@
|
||||
entity.synonym.entity_type.bundle.behavior.add_form:
|
||||
route_name: 'entity.synonym.entity_type.bundle.behavior.add_form'
|
||||
title: 'Add synonym configuration'
|
||||
appears_on:
|
||||
- entity.synonym.overview.entity_type.bundle.behavior
|
||||
@@ -0,0 +1,5 @@
|
||||
synonyms.configure:
|
||||
title: 'Synonyms'
|
||||
route_name: entity.synonym.overview
|
||||
description: 'Manage entity synonyms.'
|
||||
parent: system.admin_structure
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide synonyms feature for content entities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_base_field_info().
|
||||
*/
|
||||
function synonyms_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
|
||||
if ($entity_type instanceof \Drupal\Core\Entity\ContentEntityTypeInterface) {
|
||||
$fields = [];
|
||||
|
||||
$fields['synonyms'] = \Drupal\Core\Field\BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Entity synonyms'))
|
||||
->setDescription(t('A list of known entity synonyms.'))
|
||||
->setComputed(TRUE)
|
||||
->setClass('\Drupal\synonyms\Plugin\SynonymsFieldItemList');
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
function synonyms_views_data() {
|
||||
$data = [];
|
||||
|
||||
foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type) {
|
||||
if ($entity_type instanceof \Drupal\Core\Entity\ContentEntityTypeInterface && $entity_type->getBaseTable() && $entity_type->getKey('id')) {
|
||||
$data[$entity_type->getBaseTable()]['synonyms'] = [
|
||||
'title' => t('Synonyms of @entity_type', [
|
||||
'@entity_type' => $entity_type->getLowercaseLabel(),
|
||||
]),
|
||||
'filter' => [
|
||||
'id' => 'synonyms_entity',
|
||||
'real field' => $entity_type->getKey('id'),
|
||||
'entity_type' => $entity_type->id(),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
administer synonyms:
|
||||
title: 'Administer synonyms'
|
||||
description: 'Administer synonyms of entities.'
|
||||
|
||||
access synonyms entity autocomplete:
|
||||
title: 'Access synonyms entity autocomplete'
|
||||
description: 'Access synonyms-friendly entity autocomplete'
|
||||
@@ -0,0 +1,63 @@
|
||||
entity.synonym.overview:
|
||||
path: '/admin/structure/synonyms'
|
||||
defaults:
|
||||
_controller: 'Drupal\synonyms\Controller\SynonymConfigController::overview'
|
||||
_title: 'Synonyms'
|
||||
requirements:
|
||||
_permission: 'administer synonyms'
|
||||
|
||||
entity.synonym.overview.entity_type.bundle.behavior:
|
||||
path: '/admin/structure/synonyms/{synonyms_entity_type}/{bundle}/{synonyms_behavior_service}'
|
||||
options:
|
||||
parameters:
|
||||
synonyms_entity_type:
|
||||
type: synonyms_entity_type
|
||||
synonyms_behavior_service:
|
||||
type: synonyms_behavior_service
|
||||
defaults:
|
||||
_controller: 'Drupal\synonyms\Controller\SynonymConfigController::overviewEntityTypeBundle'
|
||||
_title: 'Synonyms'
|
||||
_title_callback: 'Drupal\synonyms\Controller\SynonymConfigController::overviewEntityTypeBundleTitle'
|
||||
requirements:
|
||||
_permission: 'administer synonyms'
|
||||
|
||||
entity.synonym.entity_type.bundle.behavior.add_form:
|
||||
path: '/admin/structure/synonyms/{synonyms_entity_type}/{bundle}/{synonyms_behavior_service}/add'
|
||||
options:
|
||||
parameters:
|
||||
synonyms_entity_type:
|
||||
type: synonyms_entity_type
|
||||
synonyms_behavior_service:
|
||||
type: synonyms_behavior_service
|
||||
defaults:
|
||||
_entity_form: 'synonym.add'
|
||||
_title: 'Add synonym configuration'
|
||||
requirements:
|
||||
_permission: 'administer synonyms'
|
||||
|
||||
entity.synonym.edit_form:
|
||||
path: '/admin/structure/synonyms/{synonym}'
|
||||
defaults:
|
||||
_entity_form: 'synonym.edit'
|
||||
_title: 'Edit synonym configuration'
|
||||
requirements:
|
||||
_permission: 'administer synonyms'
|
||||
|
||||
entity.synonym.delete_form:
|
||||
path: '/admin/structure/synonyms/{synonym}/delete'
|
||||
defaults:
|
||||
_entity_form: 'synonym.delete'
|
||||
_title: 'Delete synonym configuration'
|
||||
requirements:
|
||||
_permission: 'administer synonyms'
|
||||
|
||||
synonyms.entity_autocomplete:
|
||||
path: '/synonyms/entity/autocomplete/{target_type}/{token}'
|
||||
options:
|
||||
parameters:
|
||||
target_type:
|
||||
type: synonyms_entity_type
|
||||
defaults:
|
||||
_controller: 'Drupal\synonyms\Controller\EntityAutocomplete::autocomplete'
|
||||
requirements:
|
||||
_permission: 'access synonyms entity autocomplete'
|
||||
@@ -0,0 +1,46 @@
|
||||
services:
|
||||
plugin.manager.synonyms_provider:
|
||||
class: Drupal\synonyms\SynonymsProviderPluginManager
|
||||
parent: default_plugin_manager
|
||||
|
||||
synonyms.behaviors:
|
||||
class: Drupal\synonyms\SynonymsService\BehaviorService
|
||||
tags:
|
||||
- { name: service_collector, tag: synonyms_behavior, call: addBehaviorService }
|
||||
arguments: [ '@entity_type.manager' ]
|
||||
|
||||
synonyms.provider.field_type_to_synonyms:
|
||||
class: Drupal\synonyms\SynonymsService\FieldTypeToSynonyms
|
||||
arguments: [ '@module_handler' ]
|
||||
|
||||
synonyms.synonyms_find:
|
||||
class: Drupal\synonyms\SynonymsService\SynonymsFind
|
||||
arguments: [ '@synonyms.behaviors', '@entity_type.bundle.info' ]
|
||||
|
||||
synonyms.synonyms_entity_get:
|
||||
class: Drupal\synonyms\SynonymsService\EntityGet
|
||||
arguments: [ '@synonyms.synonyms_find', '@entity_type.manager', '@entity.query' ]
|
||||
|
||||
synonyms.behavior.autocomplete:
|
||||
class: Drupal\synonyms\SynonymsService\Behavior\AutocompleteService
|
||||
arguments: [ '@keyvalue', '@plugin.manager.entity_reference_selection', '@synonyms.behaviors', '@database', '@entity_type.manager', '@renderer' ]
|
||||
tags:
|
||||
- { name: synonyms_behavior }
|
||||
|
||||
synonyms.behavior.select:
|
||||
class: Drupal\synonyms\SynonymsService\Behavior\SelectService
|
||||
arguments: [ '@synonyms.behaviors', '@renderer' ]
|
||||
tags:
|
||||
- { name: synonyms_behavior }
|
||||
|
||||
synonyms.param_converter.entity_type:
|
||||
class: Drupal\synonyms\ParamConverter\EntityTypeParamConverter
|
||||
tags:
|
||||
- { name: paramconverter }
|
||||
arguments: [ '@entity_type.manager' ]
|
||||
|
||||
synonyms.param_converter.synonyms_behavior:
|
||||
class: Drupal\synonyms\ParamConverter\SynonymsBehaviorServiceParamConverter
|
||||
tags:
|
||||
- { name: paramconverter }
|
||||
arguments: [ '@synonyms.behaviors' ]
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\synonyms_search\SynonymsService\Behavior;
|
||||
|
||||
use Drupal\Component\Datetime\TimeInterface;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\Query\QueryFactory;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\synonyms\Entity\Synonym;
|
||||
use Drupal\synonyms\SynonymsProviderInterface\SynonymsGetProviderInterface;
|
||||
use Drupal\synonyms\SynonymsService\Behavior\SynonymsBehaviorInterface;
|
||||
use Drupal\synonyms\SynonymsService\BehaviorService;
|
||||
|
||||
/**
|
||||
* Expose synonyms of referenced entities to core Search index.
|
||||
*/
|
||||
class SearchService implements SynonymsBehaviorInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Behavior name this class represents.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const behavior = 'synonyms.behavior.search';
|
||||
|
||||
/**
|
||||
* @var EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var BehaviorService
|
||||
*/
|
||||
protected $behaviorService;
|
||||
|
||||
/**
|
||||
* @var QueryFactory
|
||||
*/
|
||||
protected $queryFactory;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* @var TimeInterface
|
||||
*/
|
||||
protected $time;
|
||||
|
||||
/**
|
||||
* SearchService constructor.
|
||||
*/
|
||||
public function __construct(EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, BehaviorService $behavior_service, QueryFactory $query_factory, Connection $database, TimeInterface $time) {
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->behaviorService = $behavior_service;
|
||||
$this->queryFactory = $query_factory;
|
||||
$this->database = $database;
|
||||
$this->time = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_entity_view().
|
||||
*/
|
||||
public function entityView(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
|
||||
if ($entity instanceof ContentEntityInterface && $view_mode == 'search_index') {
|
||||
$synonyms = [];
|
||||
$cacheable_metadata = new CacheableMetadata();
|
||||
|
||||
$entity_references = array_filter($this->entityFieldManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()), function($item) {
|
||||
if ($item->getType() == 'entity_reference') {
|
||||
$target_entity_type = $this->entityTypeManager->getDefinition($item->getSetting('target_type'));
|
||||
return $target_entity_type instanceof ContentEntityTypeInterface;
|
||||
}
|
||||
return FALSE;
|
||||
});
|
||||
foreach ($entity_references as $entity_reference) {
|
||||
foreach ($entity->get($entity_reference->getName())->referencedEntities() as $target_entity) {
|
||||
$synonyms = array_merge($synonyms, $this->getEntitySynonyms($target_entity));
|
||||
$cacheable_metadata->addCacheableDependency($target_entity);
|
||||
|
||||
// Depend on the synonyms configs for this entity type + bundle.
|
||||
$cacheable_metadata->addCacheTags([Synonym::cacheTagConstruct(self::behavior, $target_entity->getEntityTypeId(), $target_entity->bundle())]);
|
||||
}
|
||||
}
|
||||
|
||||
$build['synonyms_search'] = [
|
||||
'#markup' => implode(', ', $synonyms),
|
||||
];
|
||||
$cacheable_metadata->applyTo($build['synonyms_search']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all search index dependent on a given entity for reindexing.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
||||
* Entity whose dependent search index should be marked for reindexing
|
||||
*/
|
||||
public function entityMarkForReindex(ContentEntityInterface $entity) {
|
||||
$this->entityMarkForReindexMultiple([$entity->id()], $entity->getEntityTypeId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all search index dependent on given entities for reindexing.
|
||||
*
|
||||
* @param array $entity_ids
|
||||
* Entity IDs whose dependent search index should be marked for reindexing
|
||||
* @param string $entity_type
|
||||
* Entity type of the give entity IDs
|
||||
*/
|
||||
public function entityMarkForReindexMultiple($entity_ids, $entity_type) {
|
||||
if (empty($entity_ids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$map = $this->entityFieldManager->getFieldMapByFieldType('entity_reference');
|
||||
foreach ($map as $host_entity_type => $fields) {
|
||||
foreach ($fields as $field_name => $field_info) {
|
||||
$field = FieldStorageConfig::loadByName($host_entity_type, $field_name);
|
||||
if ($field && $field->getSetting('target_type') == $entity_type) {
|
||||
$query = $this->queryFactory->get($host_entity_type, 'AND');
|
||||
$query->condition($field_name, $entity_ids, 'IN');
|
||||
$result = $query->execute();
|
||||
|
||||
// For the sake of performance we do a direct query on the
|
||||
// {search_dataset} table instead of using search_mark_for_reindex()
|
||||
// function.
|
||||
// TODO: is there any smarter way to generate search type rather then
|
||||
// adding suffix of '_search'?
|
||||
if (!empty($result)) {
|
||||
$this->database->update('search_dataset')
|
||||
->fields(['reindex' => $this->time->getRequestTime()])
|
||||
->condition('reindex', 0)
|
||||
->condition('type', $host_entity_type . '_search')
|
||||
->condition('sid', array_values($result), 'IN')
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->t('Search');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRequiredInterfaces() {
|
||||
return [
|
||||
SynonymsGetProviderInterface::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of synonyms from a given entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
||||
*
|
||||
* @return string[]
|
||||
* A list of synonyms of a given entity
|
||||
*/
|
||||
protected function getEntitySynonyms(ContentEntityInterface $entity) {
|
||||
$synonyms = [];
|
||||
|
||||
foreach ($this->behaviorService->getSynonymConfigEntities('synonyms.behavior.search', $entity->getEntityTypeId(), $entity->bundle()) as $synonym_config) {
|
||||
$synonyms = array_merge($synonyms, $synonym_config->getProviderPluginInstance()->getSynonyms($entity));
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
name: Synonyms Search
|
||||
description: Integrates Synonyms with core Search module.
|
||||
package: Synonyms
|
||||
type: module
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- synonyms
|
||||
- search
|
||||
|
||||
# Information added by Drupal.org packaging script on 2018-03-11
|
||||
version: '8.x-1.0-alpha1+4-dev'
|
||||
core: '8.x'
|
||||
project: 'synonyms'
|
||||
datestamp: 1520765586
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Integrates Synonyms with core Search module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_view().
|
||||
*/
|
||||
function synonyms_search_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
|
||||
\Drupal::service('synonyms.behavior.search')->entityView($build, $entity, $display, $view_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_update().
|
||||
*/
|
||||
function synonyms_search_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
|
||||
if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface) {
|
||||
\Drupal::service('synonyms.behavior.search')->entityMarkForReindex($entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_delete().
|
||||
*/
|
||||
function synonyms_search_entity_delete(Drupal\Core\Entity\EntityInterface $entity) {
|
||||
if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface) {
|
||||
\Drupal::service('synonyms.behavior.search')->entityMarkForReindex($entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_insert().
|
||||
*/
|
||||
function synonyms_search_synonym_insert(Drupal\Core\Entity\EntityInterface $entity) {
|
||||
synonyms_search_synonym_reindex($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_update().
|
||||
*/
|
||||
function synonyms_search_synonym_update(Drupal\Core\Entity\EntityInterface $entity) {
|
||||
synonyms_search_synonym_reindex($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_delete().
|
||||
*/
|
||||
function synonyms_search_synonym_delete(Drupal\Core\Entity\EntityInterface $entity) {
|
||||
synonyms_search_synonym_reindex($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark all search index dependent on a given synonym config for reindexing.
|
||||
*
|
||||
* @param \Drupal\synonyms\SynonymInterface $synonym
|
||||
* Synonym config whose dependent search index should be marked for reindexing
|
||||
*/
|
||||
function synonyms_search_synonym_reindex(\Drupal\synonyms\SynonymInterface $synonym) {
|
||||
if ($synonym->get('behavior') == 'synonyms.behavior.search') {
|
||||
$entity_type = \Drupal::entityTypeManager()->getDefinition($synonym->getProviderPluginInstance()->getPluginDefinition()['controlled_entity_type']);
|
||||
$bundle = $synonym->getProviderPluginInstance()->getPluginDefinition()['controlled_bundle'];
|
||||
$query = \Drupal::entityQuery($entity_type->id());
|
||||
$query->condition($entity_type->getKey('bundle'), $bundle);
|
||||
$result = $query->execute();
|
||||
\Drupal::service('synonyms.behavior.search')->entityMarkForReindexMultiple(array_values($result), $entity_type->id());
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
services:
|
||||
synonyms.behavior.search:
|
||||
class: Drupal\synonyms_search\SynonymsService\Behavior\SearchService
|
||||
arguments: [ '@entity_field.manager', '@entity_type.manager', '@synonyms.behaviors', '@entity.query', '@database', '@datetime.time' ]
|
||||
tags:
|
||||
- { name: synonyms_behavior }
|
||||
@@ -40,3 +40,17 @@ edlp_admin.add_entry:
|
||||
title: 'New Entry'
|
||||
appears_on:
|
||||
- view.entree_s_.page_1
|
||||
edlp_admin.add_genre:
|
||||
route_name: 'entity.taxonomy_term.add_form'
|
||||
route_parameters:
|
||||
taxonomy_vocabulary: 'genres'
|
||||
title: 'New Genre'
|
||||
appears_on:
|
||||
- view.entree_s_.page_2
|
||||
edlp_admin.add_langue:
|
||||
route_name: 'entity.taxonomy_term.add_form'
|
||||
route_parameters:
|
||||
taxonomy_vocabulary: 'langues'
|
||||
title: 'New Langue'
|
||||
appears_on:
|
||||
- view.entree_s_.page_3
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
|
||||
(function($,Drupal,drupalSettings){EdlpCorpus=function(){var _activated=true;var _$body=$('body');var _$container=_$body;var _$canvas=$('<canvas id="corpus-map">').appendTo(_$container);var _canvas=_$canvas[0];var _ctx=_canvas.getContext('2d');var _canvas_props={'margin_top':100,'margin_right':0,'margin_bottom':100,'margin_left':0};var _physics=new Physics();var _nodes=[];var _articles_nodes=[];var _no_articles_nodes=[];var _nodes_by_entries={};var _nodes_by_nid={};var _nodes_Nid_Id={};var _nodes_centered=[];var _nodes_centered_attractions=[];var _playlist=[];var _p_velocity_factor=0.5;var _mouse_in=true;var _m_pos={x:0,y:0};var _node_hover_id=-1;var _node_opened_id=-1;var _$entrees_block=$('#block-edlpentreesblock');var _$entrees_block_termlinks=$('a.term-link',_$entrees_block);var _$articles_link;var _node_pop_up;var _settings=drupalSettings.edlp_corpus;var _ecolors=_settings.colors;var _attracter,_repulser_top,_repulser_center,_repulser_bottom,_scrambler_TL,_scrambler_TR,_scrambler_BR,_scrambler_BL,_scrambler_CL,_scrambler_CR;function init(){initCanvas();if(_activated){loadCorpus();}else{_$canvas.addClass('de-activated');}};function initCanvas(){window.addEventListener('resize',onResizeCanvas,false);onResizeCanvas();};function onResizeCanvas(){_canvas.width=window.innerWidth;_canvas.height=window.innerHeight;for(var i=0;i<_nodes.length;i++){_nodes[i].onResizeCanvas();resizePhysics();}};function loadCorpus(){var ajax_path=_settings.load_corpus_ajax_url;var path=window.location.origin+Drupal.url(ajax_path);$.getJSON(path,{}).done(function(data){onCorpusLoaded(data);}).fail(function(jqxhr,textStatus,error){onCorpusLoadError(jqxhr,textStatus,error);});};function onCorpusLoadError(jqxhr,textStatus,error){void 0;};function onCorpusLoaded(data){initPhysics();buildNodes(data.nodes);initArtilesLink();initNodePopup();initEvents();startAnime();};function initPhysics(){_attracter=_physics.makeParticle(1000);_attracter.fixed=true;_repulser_top=_physics.makeParticle(100);_repulser_top.fixed=true;_repulser_center=_physics.makeParticle(100);_repulser_center.fixed=true;_repulser_bottom=_physics.makeParticle(100);_repulser_bottom.fixed=true;_scrambler_TL=_physics.makeParticle(100);_scrambler_TL.fixed=true;_scrambler_TR=_physics.makeParticle(100);_scrambler_TR.fixed=true;_scrambler_BR=_physics.makeParticle(100);_scrambler_BR.fixed=true;_scrambler_BL=_physics.makeParticle(100);_scrambler_BL.fixed=true;_scrambler_CR=_physics.makeParticle(100);_scrambler_CR.fixed=true;_scrambler_CL=_physics.makeParticle(100);_scrambler_CL.fixed=true;resizePhysics();};function resizePhysics(){_attracter.position={x:_canvas.width/2,y:_canvas.height/2};_repulser_top.position={x:_canvas.width/2,y:0};_repulser_center.position={x:_canvas.width/2,y:_canvas.height/2};_repulser_bottom.position={x:_canvas.width/2,y:_canvas.height};_scrambler_TL.position={x:-200,y:_canvas.height/40};_scrambler_BL.position={x:-200,y:_canvas.height-_canvas.height/40};_scrambler_TR.position={x:_canvas.width+200,y:_canvas.height/40};_scrambler_BR.position={x:_canvas.width+200,y:_canvas.height-_canvas.height/40};};function buildNodes(nodes){var d;for(var i in nodes){d=i<1?true:false;new Node(i,nodes[i],d);_playlist.push({'id':i,'nid':nodes[i].nid,'audio_url':nodes[i].audio_url});}};function Node(i,node,d){this.id=i;for(key in node)
|
||||
(function($,Drupal,drupalSettings){EdlpCorpus=function(){var _activated=true;var _$body=$('body');var _$container=_$body;var _$canvas=$('<canvas id="corpus-map">').appendTo(_$container);var _canvas=_$canvas[0];var _ctx=_canvas.getContext('2d');var _canvas_props={'margin_top':90,'margin_right':0,'margin_bottom':65,'margin_left':0};var _physics=new Physics();var _nodes=[];var _articles_nodes=[];var _no_articles_nodes=[];var _nodes_by_entries={};var _nodes_by_nid={};var _nodes_Nid_Id={};var _nodes_centered=[];var _nodes_centered_attractions=[];var _playlist=[];var _p_velocity_factor=0.5;var _mouse_in=true;var _m_pos={x:0,y:0};var _node_hover_id=-1;var _node_opened_id=-1;var _$entrees_block=$('#block-edlpentreesblock');var _$entrees_block_termlinks=$('a.term-link',_$entrees_block);var _$articles_link;var _node_pop_up;var _settings=drupalSettings.edlp_corpus;var _ecolors=_settings.colors;var _attracter,_repulser_top,_repulser_center,_repulser_bottom,_scrambler_TL,_scrambler_TR,_scrambler_BR,_scrambler_BL,_scrambler_CL,_scrambler_CR;function init(){initCanvas();if(_activated){loadCorpus();}else{_$canvas.addClass('de-activated');}};function initCanvas(){window.addEventListener('resize',onResizeCanvas,false);onResizeCanvas();};function onResizeCanvas(){_canvas.width=window.innerWidth;_canvas.height=window.innerHeight;for(var i=0;i<_nodes.length;i++){_nodes[i].onResizeCanvas();resizePhysics();}};function loadCorpus(){var ajax_path=_settings.load_corpus_ajax_url;var path=window.location.origin+Drupal.url(ajax_path);$.getJSON(path,{}).done(function(data){onCorpusLoaded(data);}).fail(function(jqxhr,textStatus,error){onCorpusLoadError(jqxhr,textStatus,error);});};function onCorpusLoadError(jqxhr,textStatus,error){void 0;};function onCorpusLoaded(data){initPhysics();buildNodes(data.nodes);initArtilesLink();initNodePopup();initEvents();checkPreOpenedEntry();startAnime();};function initPhysics(){_attracter=_physics.makeParticle(1000);_attracter.fixed=true;resizePhysics();};function resizePhysics(){_attracter.position={x:_canvas.width/2,y:_canvas.height/2};};function buildNodes(nodes){var d;for(var i in nodes){d=i<1?true:false;new Node(i,nodes[i],d);_playlist.push({'id':i,'nid':nodes[i].nid,'audio_url':nodes[i].audio_url});}};function Node(i,node,d){this.id=i;for(key in node)
|
||||
this[key]=node[key];_nodes.push(this);_nodes_by_nid[this.nid]=this;_nodes_Nid_Id[this.nid]=this.id;if(this.has_article==1){_articles_nodes.push(this);}else{_no_articles_nodes.push(this);}
|
||||
for(var j=0;j<this.entrees.length;j++){if(typeof _nodes_by_entries[this.entrees[j]]=='undefined')
|
||||
_nodes_by_entries[this.entrees[j]]=[];_nodes_by_entries[this.entrees[j]].push(this);}
|
||||
this.debug=d;this.mass=Math.max(1,this.entrees.length);this.velocity_threshold=0.01;switch(true){case this.entrees.length>3:this.r=10;break;case this.entrees.length>1&&this.entrees.length<=3:this.r=7;break;default:this.r=4;break;}
|
||||
this.e_color='e_col_'+this.entrees[Math.floor(Math.random(this.entrees.length))];this.hover=false;this.opened=false;this.faded=false;this.center=false;this.aside=false;this.scrambling=false;this.n_repulses={};if(typeof Node.initialized=="undefined"){Node.prototype.init=function(){this.calcWallLimits();this.x=this.wall_limits.left+Math.random()*(this.wall_limits.right-this.wall_limits.left);this.y=this.wall_limits.top+Math.random()*(this.wall_limits.bottom-this.wall_limits.top);this.initPhysics();};Node.prototype.initPhysics=function(){this.p=_physics.makeParticle(this.mass,this.x,this.y);this.p.velocity=new Physics.Vector((Math.random()-0.5)*_p_velocity_factor,(Math.random()-0.5)*_p_velocity_factor);this.attract=_physics.makeAttraction(_attracter,this.p,1000,_canvas.width*2);this.attract.on=false;this.repulse_top=_physics.makeAttraction(_repulser_top,this.p,-60,_canvas.height/6);this.repulse_center=_physics.makeAttraction(_repulser_center,this.p,-50,_canvas.height/6);this.repulse_bottom=_physics.makeAttraction(_repulser_bottom,this.p,-60,_canvas.height/6);this.repulse_top.on=false;this.repulse_center.on=false;this.repulse_bottom.on=false;this.scramble_TL=_physics.makeAttraction(_scrambler_TL,this.p,-100,_canvas.height/2.5);this.scramble_TR=_physics.makeAttraction(_scrambler_TR,this.p,-100,_canvas.height/2.5);this.scramble_BR=_physics.makeAttraction(_scrambler_BR,this.p,-100,_canvas.height/2.5);this.scramble_BL=_physics.makeAttraction(_scrambler_BL,this.p,-100,_canvas.height/2.5);this.scramble_TL.on=false;this.scramble_TR.on=false;this.scramble_BR.on=false;this.scramble_BL.on=false;};Node.prototype.calcWallLimits=function(){this.wall_limits={top:_canvas_props.margin_top+this.r,right:_canvas.width-_canvas_props.margin_right-this.r,bottom:_canvas.height-_canvas_props.margin_bottom-this.r,left:_canvas_props.margin_left+this.r}};Node.prototype.onResizeCanvas=function(){this.calcWallLimits();};Node.prototype.onUpdate=function(){if(this.hover||this.opened){this.p.fixed=true;}else{this.p.fixed=false;}
|
||||
this.e_color='e_col_'+this.entrees[Math.floor(Math.random(this.entrees.length))];this.hover=false;this.opened=false;this.faded=false;this.center=false;this.aside=false;this.scrambling=false;this.n_repulses={};if(typeof Node.initialized=="undefined"){Node.prototype.init=function(){this.calcWallLimits();this.x=this.wall_limits.left+Math.random()*(this.wall_limits.right-this.wall_limits.left);this.y=this.wall_limits.top+Math.random()*(this.wall_limits.bottom-this.wall_limits.top);this.ori_pos={x:this.x,y:this.y};this.initPhysics();};Node.prototype.initPhysics=function(){this.p=_physics.makeParticle(this.mass,this.x,this.y);this.p.velocity=new Physics.Vector((Math.random()-0.5)*_p_velocity_factor,(Math.random()-0.5)*_p_velocity_factor);this.ori_p=_physics.makeParticle(1000,this.ori_pos.x,this.ori_pos.y);this.ori_p.fixed=true;this.ori_p_attract=_physics.makeAttraction(this.ori_p,this.p,1000,_canvas.width*2);this.ori_p_attract.on=false;this.attract=_physics.makeAttraction(_attracter,this.p,1000,_canvas.width*2);this.attract.on=false;};Node.prototype.calcWallLimits=function(){this.wall_limits={top:_canvas_props.margin_top+this.r,right:_canvas.width-_canvas_props.margin_right-this.r,bottom:_canvas.height-_canvas_props.margin_bottom-this.r,left:_canvas_props.margin_left+this.r}};Node.prototype.onResizeCanvas=function(){this.calcWallLimits();this.shuffleOriP();};Node.prototype.shuffleOriP=function(){this.ori_pos.x=this.wall_limits.left+Math.random()*(this.wall_limits.right-this.wall_limits.left);this.ori_pos.y=this.wall_limits.top+Math.random()*(this.wall_limits.bottom-this.wall_limits.top);this.scramble();};Node.prototype.onUpdate=function(){if(this.hover||this.opened){this.p.fixed=true;}else{this.p.fixed=false;}
|
||||
if(this.center)
|
||||
this.limitEvolutionZone();if(!this.p.resting()){this.checkWallBouncing();this.updatePos();}
|
||||
this.limitEvolutionZone();if(!this.p.resting()){this.checkWallBouncing();this.updatePos();if(this.scrambling){this.checkOriPDist();}}
|
||||
if(_mouse_in&&!this.aside)
|
||||
this.checkMouse();this.draw();};Node.prototype.checkVelocityThreshold=function(){if(!this.centered||!this.aside){if(Math.abs(this.p.velocity.x)<this.velocity_threshold&&Math.abs(this.p.velocity.y)<this.velocity_threshold){this.p.velocity.multiplyScalar(0);}}};Node.prototype.checkWallBouncing=function(){switch(true){case this.x<this.wall_limits.left&&this.p.velocity.x<0:this.p.position.x=this.wall_limits.left;this.p.velocity.multiplyScalarXY(-1,1).multiplyScalar(0.75);break;case this.x>this.wall_limits.right&&this.p.velocity.x>0:this.p.position.x=this.wall_limits.right;this.p.velocity.multiplyScalarXY(-1,1).multiplyScalar(0.75);break;case this.y<this.wall_limits.top&&this.p.velocity.y<0:this.p.position.y=this.wall_limits.top;this.p.velocity.multiplyScalarXY(1,-1).multiplyScalar(0.75);break;case this.y>this.wall_limits.bottom&&this.p.velocity.y>0:this.p.position.y=this.wall_limits.bottom;this.p.velocity.multiplyScalarXY(1,-1).multiplyScalar(0.75);break;}};Node.prototype.updatePos=function(){this.x=this.p.position.x;this.y=this.p.position.y;};Node.prototype.checkMouse=function(){if(_m_pos.x>this.x-this.r&&_m_pos.x<this.x+this.r&&_m_pos.y>this.y-this.r&&_m_pos.y<this.y+this.r){if(_node_hover_id==-1||_node_hover_id!==this.id){this.hover=true;_node_hover_id=this.id;_node_pop_up.setNode(this);}}else{this.hover=false;if(_node_hover_id==this.id){_node_hover_id=-1;_node_pop_up.removeNode();}}};Node.prototype.open=function(){this.opened=true;_node_opened_id=this.id;};Node.prototype.close=function(){this.opened=false;};Node.prototype.fade=function(){this.faded=true;};Node.prototype.unFade=function(){this.faded=false;};Node.prototype.setCenteredOnEntree=function(tid){this.e_color='e_col_'+tid;this.setCentered();};Node.prototype.setCentered=function(){this.center=true;this.stopScrambling();this.unsetAside();this.attract.on=true;}
|
||||
Node.prototype.unsetCentered=function(){this.center=false;this.attract.on=false;}
|
||||
Node.prototype.limitEvolutionZone=function(){this.dist_to_attracter=this.p.distanceTo(_attracter);if(this.dist_to_attracter<_canvas.width/4){if(this.p.velocity.length()>0.4){this.p.velocity.multiplyScalar(0.995);}}
|
||||
if(Math.abs(_attracter.position.x-this.x)>_canvas.width/3||Math.abs(_attracter.position.y-this.y)>_canvas.height/3){this.attract.on=true;}else{this.attract.on=false;}}
|
||||
Node.prototype.setAside=function(){this.aside=true;this.stopScrambling();this.unsetCentered();this.repulse_top.on=true;this.repulse_center.on=true;this.repulse_bottom.on=true;}
|
||||
Node.prototype.unsetAside=function(){this.aside=false;this.repulse_top.on=false;this.repulse_center.on=false;this.repulse_bottom.on=false;}
|
||||
Node.prototype.scramble=function(){this.scrambling=true;this.unsetCentered();this.unsetAside();this.scramble_TL.on=true;this.scramble_TR.on=true;this.scramble_BR.on=true;this.scramble_BL.on=true;}
|
||||
Node.prototype.stopScrambling=function(){this.scrambling=false;this.scramble_TL.on=false;this.scramble_TR.on=false;this.scramble_BR.on=false;this.scramble_BL.on=false;}
|
||||
Node.prototype.setAside=function(){this.aside=true;this.stopScrambling();this.unsetCentered();this.ori_p.position.x=this.x<_canvas.width/2?this.wall_limits.left:this.wall_limits.right;this.ori_p_attract.on=true;}
|
||||
Node.prototype.unsetAside=function(){void 0;this.aside=false;this.ori_p_attract.on=false;this.ori_p.position.x=this.ori_pos.x;}
|
||||
Node.prototype.scramble=function(){this.scrambling=true;this.unsetCentered();this.unsetAside();this.ori_p.position.x=this.ori_pos.x;this.ori_p.position.y=this.ori_pos.y;this.ori_p_attract.on=true;}
|
||||
Node.prototype.checkOriPDist=function(){if(this.p.distanceTo(this.ori_p)<50){this.stopScrambling();}};Node.prototype.stopScrambling=function(){this.scrambling=false;this.ori_p_attract.on=false;}
|
||||
Node.prototype.draw=function(){_ctx.beginPath();_ctx.globalAlpha=this.faded?0.1:1;_ctx.fillStyle=_ecolors[this.e_color];_ctx.fillRect(this.x-this.r,this.y-this.r,this.r*2,this.r*2);if(this.opened){_ctx.lineWidth='1px';_ctx.strokeStyle='rgb(255,0,0)';_ctx.strokeRect(this.x-this.r-3,this.y-this.r-3,this.r*2+6,this.r*2+6);}
|
||||
_ctx.globalAlpha=1;_ctx.closePath();};Node.initialized=true;}
|
||||
this.init();};function checkParticulesCollisions(){var na,nb,ma,mb,w,h,dx,dy,makeup,newVelX1,newVelY1,newVelX2,newVelY2;for(var n=0,l=_nodes.length;n<l;n++){na=_nodes[n];ma=na.p.mass;if(na.scrambling)continue;for(var q=n+1;q<l;q++){nb=_nodes[q];mb=nb.p.mass;if((na.center&&nb.aside)||(na.aside&&nb.center))
|
||||
@@ -25,14 +25,12 @@ newVelY1=(ma-mb)/(ma+mb)*na.p.velocity.y+2*mb/(ma+mb)*nb.p.velocity.y;newVelY2=(
|
||||
newVelX1=(ma-mb)/(ma+mb)*na.p.velocity.x+2*mb/(ma+mb)*nb.p.velocity.x;newVelX2=(mb-ma)/(mb+ma)*nb.p.velocity.x+2*ma/(mb+ma)*na.p.velocity.x;na.p.velocity.x=newVelX1;nb.p.velocity.x=newVelX2;}
|
||||
na.p.velocity.multiplyScalar(0.90);nb.p.velocity.multiplyScalar(0.90);}}};function openNodeByNid(nid){closeNode();if(typeof _nodes_Nid_Id[nid]!='undefined'){_node_opened_id=_nodes_Nid_Id[nid];_nodes[_nodes_Nid_Id[nid]].open();}}
|
||||
function closeNode(){if(_node_opened_id!=-1){_nodes[_node_opened_id].close();_node_opened_id=-1;}}
|
||||
function createNodesRepulsions(){purgeNodesRepulsions();for(var n=0;n<_nodes_centered.length;n++){for(var q=n+1;q<_nodes_centered.length;q++){_physics.makeAttraction(_nodes_centered[n].p,_nodes_centered[q].p,-0.4,10);}}};function purgeNodesRepulsions(){for(var a=_physics.attractions.length-1;a>=0;a--){if(Math.abs(_physics.attractions[a].constant)<1){_physics.attractions.splice(a,1);}else{break;}}};function filterEntree(t){shutDownArticles();_nodes_centered=[];for(var n=0;n<_nodes.length;n++){if(_nodes[n].entrees.indexOf(t)==-1){_nodes[n].setAside();}else{_nodes[n].setCenteredOnEntree(t);_nodes_centered.push(_nodes[n]);}}
|
||||
function createNodesRepulsions(){purgeNodesRepulsions();for(var n=0;n<_nodes_centered.length;n++){for(var q=n+1;q<_nodes_centered.length;q++){_physics.makeAttraction(_nodes_centered[n].p,_nodes_centered[q].p,-0.4,10);}}};function purgeNodesRepulsions(){for(var a=_physics.attractions.length-1;a>=0;a--){if(Math.abs(_physics.attractions[a].constant)<1){_physics.attractions.splice(a,1);}else{break;}}};function toggleEntree(tid,bubbling){var $link=_$entrees_block_termlinks.filter('[tid="'+tid+'"]');var $li=$link.parents('li');var sys_path=$link.attr('data-drupal-link-system-path');var url=$link.attr('href');if(!$li.is('.opened')){_$entrees_block_termlinks.parents('li').removeClass('opened');$li.addClass('opened');filterEntree(tid);if(bubbling){_$body.trigger({'type':'open_entree','tid':tid,'url':url,'sys_path':sys_path});}}else if(bubbling){$li.removeClass('opened');scrambleCollection();_$body.trigger({'type':'close_entree','tid':tid});}};function filterEntree(t){shutDownArticles();_nodes_centered=[];for(var n=0;n<_nodes.length;n++){if(_nodes[n].entrees.indexOf(t)==-1){_nodes[n].setAside();}else{_nodes[n].setCenteredOnEntree(t);_nodes_centered.push(_nodes[n]);}}
|
||||
createNodesRepulsions();};function highlightEntries(){_$entrees_block_termlinks.removeClass('highlighted');var id=-1;if(_node_hover_id!=-1){id=_node_hover_id;}else if(_node_opened_id!=-1){id=_node_opened_id;}
|
||||
var entree;if(id!=-1){for(var i=0;i<_nodes[id].entrees.length;i++){entree=_nodes[id].entrees[i];_$entrees_block_termlinks.filter(function(){return $(this).attr('tid')==entree;}).addClass('highlighted');}}};function filterSearchResults(nids){shutDownArticles();_nodes_centered=[];for(var n=0;n<_nodes.length;n++){if(nids.indexOf(_nodes[n].nid)==-1){_nodes[n].setAside();}else{_nodes[n].setCentered();_nodes_centered.push(_nodes[n]);}}
|
||||
createNodesRepulsions();};function scrambleCollection(){for(var i=0;i<_nodes.length;i++){_nodes[i].scramble();}
|
||||
stopScrambling();};function stopScrambling(){for(var i=0;i<_nodes.length;i++){setTimeout(function(n){n.stopScrambling();}.bind(this,_nodes[i]),1000+Math.random()*4000);}};function closeAllEntries(){_$entrees_block_termlinks.each(function(index,el){if($(this).parents('li').is('.opened')){$(this).trigger('click');return false;}});};function initArtilesLink(){_$articles_link=$('<a>').html('Articles').attr("href","#articles").addClass('articles-link').on('click',onCLickedOnArticles);$('.item-list ul',_$entrees_block).append($('<li>').append($('<span class="oblique-wrapper">').append(_$articles_link)));};function onCLickedOnArticles(e){e.preventDefault();$(this).toggleClass('is-active');if($(this).is('.is-active')){filterArticles();closeAllEntries();}else{resetArticlesFilter();}
|
||||
createNodesRepulsions();};function scrambleCollection(){for(var i=0;i<_nodes.length;i++){_nodes[i].scramble();}};function closeAllEntries(){_$entrees_block_termlinks.each(function(index,el){if($(this).parents('li').is('.opened')){$(this).trigger('click');return false;}});};function initArtilesLink(){_$articles_link=$('<a>').html('Articles').attr("href","#articles").addClass('articles-link').on('click',onCLickedOnArticles);$('.item-list ul',_$entrees_block).append($('<li>').append($('<span class="oblique-wrapper">').append(_$articles_link)));};function onCLickedOnArticles(e){e.preventDefault();$(this).toggleClass('is-active');if($(this).is('.is-active')){filterArticles();closeAllEntries();}else{resetArticlesFilter();}
|
||||
return false;};function filterArticles(){for(var i=0;i<_no_articles_nodes.length;i++){_no_articles_nodes[i].fade();}};function resetArticlesFilter(){for(var i=0;i<_no_articles_nodes.length;i++){_no_articles_nodes[i].unFade();}};function shutDownArticles(){if(_$articles_link.is('.is-active'))
|
||||
_$articles_link.trigger('click');};function initEvents(){_$canvas.on('mousemove',function(event){event.preventDefault();_m_pos.x=event.originalEvent.clientX;_m_pos.y=event.originalEvent.clientY;}).on('mouseenter',function(event){_mouse_in=true;}).on('mouseout',function(event){_mouse_in=false;_node_pop_up.removeNode();}).on('click',function(event){if(event.target.tagName!="A"&&event.target.tagName!="INPUT"){event.preventDefault();if(_node_hover_id!=-1){var event={'type':'corpus-cliked-on-node','target_node':{'id':_node_hover_id,'nid':_nodes[_node_hover_id].nid,'audio_url':_nodes[_node_hover_id].audio_url},};_$canvas.trigger(event);}else{_$canvas.trigger('corpus-cliked-on-map');}}}).on('audio-node-opened',function(e){openNodeByNid(e.nid);}).on('audio-node-closed',function(e){closeNode();});_$entrees_block_termlinks.on('click',function(event){event.preventDefault();var tid=$(this).attr('tid');var $li=$(this).parents('li');if(!$li.is('.opened')){_$entrees_block_termlinks.parents('li').removeClass('opened');$li.addClass('opened');filterEntree(tid);_$body.trigger({'type':'open_entree','tid':tid});}else{$li.removeClass('opened');scrambleCollection();_$body.trigger({'type':'close_entree','tid':tid});}
|
||||
return false;});_$body.on('chutier-action-done',function(e){_nodes_by_nid[e.target_id].chutier_action=e.new_action;}).on('search-results-loaded',function(e){filterSearchResults(e.results);}).on('search-closed',function(e){scrambleCollection();});};function initNodePopup(){_node_pop_up=new NodePopUp();};function NodePopUp(){this.visible=false;this.node;this.$dom=$('<div>').addClass('node-popup').attr('pos','top-right').appendTo('body');this.$content=$('<div>').addClass('inner').appendTo(this.$dom);if(typeof NodePopUp.initialized=="undefined"){NodePopUp.prototype.setNode=function(n){this.node=n;this.setPositioning();this.setContent();};NodePopUp.prototype.setPositioning=function(){switch(true){case this.node.x>this.node.wall_limits.right-350&&this.node.y<this.node.wall_limits.top+200:this.$dom.attr('pos','bottom-left');break;case this.node.x>this.node.wall_limits.right-350:this.$dom.attr('pos','top-left');break;case this.node.y<this.node.wall_limits.top+200:this.$dom.attr('pos','bottom-right');break;default:this.$dom.attr('pos','top-right');}};NodePopUp.prototype.setContent=function(){this.$content.html('');var $entrees=$('<div>').addClass('entrees');for(var i=0;i<this.node.entrees.length;i++){var tid=this.node.entrees[i];$entrees.append($('<span>').addClass('entree').attr('tid',tid));}
|
||||
_$articles_link.trigger('click');};function initEvents(){void 0;_$canvas.on('mousemove',function(event){event.preventDefault();_m_pos.x=event.originalEvent.clientX;_m_pos.y=event.originalEvent.clientY;}).on('mouseenter',function(event){_mouse_in=true;}).on('mouseout',function(event){_mouse_in=false;_node_pop_up.removeNode();}).on('click',function(event){if(event.target.tagName!="A"&&event.target.tagName!="INPUT"){event.preventDefault();if(_node_hover_id!=-1){var event={'type':'corpus-cliked-on-node','target_node':{'id':_node_hover_id,'nid':_nodes[_node_hover_id].nid,'audio_url':_nodes[_node_hover_id].audio_url},};_$canvas.trigger(event);}else{_$canvas.trigger('corpus-cliked-on-map');}}}).on('audio-node-opened',function(e){openNodeByNid(e.nid);}).on('audio-node-closed',function(e){closeNode();}).on('open-entree',function(e){toggleEntree(e.tid);}).on('close-all-entree',function(e){closeAllEntries();});_$entrees_block_termlinks.on('click',function(event){event.preventDefault();toggleEntree($(this).attr('tid'),true);return false;});_$body.on('chutier-action-done',function(e){_nodes_by_nid[e.target_id].chutier_action=e.new_action;}).on('search-results-loaded',function(e){filterSearchResults(e.results);}).on('search-closed',function(e){scrambleCollection();}).on('new-content-not-entree-ajax-loaded',function(e){if(_$entrees_block_termlinks.parents('li.opened').length){_$entrees_block_termlinks.parents('li').removeClass('opened');scrambleCollection();}});};function checkPreOpenedEntry(){_$entrees_block.find('li.entree').each(function(index,el){var $li=$(this);if($('a.is-active',$li).length){$li.addClass('opened');filterEntree($li.attr('tid'));return false;}});};function initNodePopup(){_node_pop_up=new NodePopUp();};function NodePopUp(){this.visible=false;this.node;this.$dom=$('<div>').addClass('node-popup').attr('pos','top-right').appendTo('body');this.$content=$('<div>').addClass('inner').appendTo(this.$dom);if(typeof NodePopUp.initialized=="undefined"){NodePopUp.prototype.setNode=function(n){this.node=n;this.setPositioning();this.setContent();};NodePopUp.prototype.setPositioning=function(){switch(true){case this.node.x>this.node.wall_limits.right-350&&this.node.y<this.node.wall_limits.top+200:this.$dom.attr('pos','bottom-left');break;case this.node.x>this.node.wall_limits.right-350:this.$dom.attr('pos','top-left');break;case this.node.y<this.node.wall_limits.top+200:this.$dom.attr('pos','bottom-right');break;default:this.$dom.attr('pos','top-right');}};NodePopUp.prototype.setContent=function(){this.$content.html('');var $entrees=$('<div>').addClass('entrees');for(var i=0;i<this.node.entrees.length;i++){var tid=this.node.entrees[i];$entrees.append($('<span>').addClass('entree').attr('tid',tid));}
|
||||
var $chutier_action=$('<span>').addClass('chutier-icon').attr('action',this.node.chutier_action);this.$content.append($entrees).append('<h2 class="title">'+this.node.title+'</h2>').append('<section class="description">'+this.node.description+'</section>').append($chutier_action);};NodePopUp.prototype.removeNode=function(){this.node=false;};NodePopUp.prototype.draw=function(){if(this.node){this.$dom.css({'display':"block",'left':this.node.x+"px",'top':this.node.y+"px",});}else{this.$dom.css({'display':"none",});}};NodePopUp.initialized=true;}}
|
||||
function render(){_ctx.clearRect(0,0,_canvas.width,_canvas.height);checkParticulesCollisions();for(var i=0;i<_nodes.length;i++){_nodes[i].onUpdate();}
|
||||
_node_pop_up.draw();if(_node_hover_id!=-1){_canvas.style.cursor='pointer';}else{_canvas.style.cursor='auto';}
|
||||
|
||||
@@ -12,11 +12,7 @@
|
||||
|
||||
/**
|
||||
* depends on :
|
||||
*
|
||||
|
||||
* physics.js : http://jonobr1.com/Physics/#
|
||||
* EaselJS : https://createjs.com/docs/easeljs/modules/EaselJS.html
|
||||
*
|
||||
*/
|
||||
|
||||
(function($, Drupal, drupalSettings) {
|
||||
@@ -29,13 +25,12 @@
|
||||
var _canvas = _$canvas[0];
|
||||
var _ctx = _canvas.getContext('2d');
|
||||
var _canvas_props = {
|
||||
'margin_top':100,
|
||||
'margin_top':90,
|
||||
'margin_right':0,
|
||||
'margin_bottom':100,
|
||||
'margin_bottom':65,
|
||||
'margin_left':0
|
||||
};
|
||||
var _physics = new Physics();
|
||||
// var _stage = new createjs.Stage('corpus-map');
|
||||
var _nodes = [];
|
||||
var _articles_nodes = [];
|
||||
var _no_articles_nodes = [];
|
||||
@@ -47,8 +42,6 @@
|
||||
|
||||
var _playlist = [];
|
||||
|
||||
// var _particules = [];
|
||||
// var _base_radius = 3; // nodes radius (real radius, not diametre)
|
||||
var _p_velocity_factor = 0.5;
|
||||
var _mouse_in = true;
|
||||
var _m_pos = {x:0, y:0};
|
||||
@@ -60,9 +53,7 @@
|
||||
var _node_pop_up;
|
||||
// Colors depend on drupalSettings loaded by edlp_corpus.module
|
||||
var _settings = drupalSettings.edlp_corpus;
|
||||
// load_corpus_ajax_url
|
||||
var _ecolors = _settings.colors;
|
||||
//console.log('Corpus : _ecolors', _ecolors);
|
||||
// Physics
|
||||
var _attracter,
|
||||
_repulser_top,
|
||||
@@ -139,6 +130,7 @@
|
||||
initArtilesLink();
|
||||
initNodePopup();
|
||||
initEvents();
|
||||
checkPreOpenedEntry();
|
||||
startAnime();
|
||||
};
|
||||
|
||||
@@ -150,41 +142,12 @@
|
||||
function initPhysics(){
|
||||
_attracter = _physics.makeParticle(1000);
|
||||
_attracter.fixed = true;
|
||||
_repulser_top = _physics.makeParticle(100);
|
||||
_repulser_top.fixed = true;
|
||||
_repulser_center = _physics.makeParticle(100);
|
||||
_repulser_center.fixed = true;
|
||||
_repulser_bottom = _physics.makeParticle(100);
|
||||
_repulser_bottom.fixed = true;
|
||||
_scrambler_TL = _physics.makeParticle(100);
|
||||
_scrambler_TL.fixed = true;
|
||||
_scrambler_TR = _physics.makeParticle(100);
|
||||
_scrambler_TR.fixed = true;
|
||||
_scrambler_BR = _physics.makeParticle(100);
|
||||
_scrambler_BR.fixed = true;
|
||||
_scrambler_BL = _physics.makeParticle(100);
|
||||
_scrambler_BL.fixed = true;
|
||||
_scrambler_CR = _physics.makeParticle(100);
|
||||
_scrambler_CR.fixed = true;
|
||||
_scrambler_CL = _physics.makeParticle(100);
|
||||
_scrambler_CL.fixed = true;
|
||||
// move _attracter and _repulser on window resize
|
||||
// move _attracter on window resize
|
||||
resizePhysics();
|
||||
};
|
||||
function resizePhysics(){
|
||||
// attracters
|
||||
_attracter.position = {x:_canvas.width/2, y:_canvas.height/2};
|
||||
// repulsers
|
||||
_repulser_top.position = {x:_canvas.width/2, y:0};
|
||||
_repulser_center.position = {x:_canvas.width/2, y:_canvas.height/2};
|
||||
_repulser_bottom.position = {x:_canvas.width/2, y:_canvas.height};
|
||||
// scramblers
|
||||
_scrambler_TL.position = {x:-200, y:_canvas.height/40};
|
||||
_scrambler_BL.position = {x:-200, y:_canvas.height-_canvas.height/40};
|
||||
// _scrambler_CL.position = {x:-200, y:_canvas.height/2};
|
||||
_scrambler_TR.position = {x:_canvas.width+200, y:_canvas.height/40};
|
||||
_scrambler_BR.position = {x:_canvas.width+200, y:_canvas.height-_canvas.height/40};
|
||||
// _scrambler_CR.position = {x:_canvas.width+200, y:_canvas.height/2};
|
||||
};
|
||||
|
||||
// _ _ _
|
||||
@@ -273,40 +236,26 @@
|
||||
this.x = this.wall_limits.left + Math.random()*(this.wall_limits.right - this.wall_limits.left);
|
||||
this.y = this.wall_limits.top + Math.random()*(this.wall_limits.bottom - this.wall_limits.top);
|
||||
|
||||
this.ori_pos = {x:this.x,y:this.y};
|
||||
// TODO: don't forget to move ori_pos on window resize
|
||||
|
||||
this.initPhysics();
|
||||
|
||||
// if(this.id == '620'){
|
||||
// _node_pop_up.setNode(this);
|
||||
// }
|
||||
};
|
||||
|
||||
Node.prototype.initPhysics = function(){
|
||||
// particule
|
||||
this.p = _physics.makeParticle(this.mass, this.x, this.y);
|
||||
this.p.velocity = new Physics.Vector((Math.random()-0.5)*_p_velocity_factor, (Math.random()-0.5)*_p_velocity_factor);
|
||||
// origin position particule
|
||||
this.ori_p = _physics.makeParticle(1000, this.ori_pos.x, this.ori_pos.y);
|
||||
this.ori_p.fixed = true;
|
||||
// TODO: don't forget to move ori_p on window resize
|
||||
this.ori_p_attract = _physics.makeAttraction(this.ori_p, this.p, 1000, _canvas.width*2);
|
||||
this.ori_p_attract.on = false;
|
||||
// attracter
|
||||
this.attract = _physics.makeAttraction(_attracter, this.p, 1000, _canvas.width*2);
|
||||
this.attract.on = false;
|
||||
// repulsers
|
||||
this.repulse_top = _physics.makeAttraction(_repulser_top, this.p, -60, _canvas.height/6);
|
||||
this.repulse_center = _physics.makeAttraction(_repulser_center, this.p, -50, _canvas.height/6);
|
||||
this.repulse_bottom = _physics.makeAttraction(_repulser_bottom, this.p, -60, _canvas.height/6);
|
||||
this.repulse_top.on = false;
|
||||
this.repulse_center.on = false;
|
||||
this.repulse_bottom.on = false;
|
||||
// Scramblers
|
||||
this.scramble_TL = _physics.makeAttraction(_scrambler_TL, this.p, -100, _canvas.height/2.5);
|
||||
this.scramble_TR = _physics.makeAttraction(_scrambler_TR, this.p, -100, _canvas.height/2.5);
|
||||
this.scramble_BR = _physics.makeAttraction(_scrambler_BR, this.p, -100, _canvas.height/2.5);
|
||||
this.scramble_BL = _physics.makeAttraction(_scrambler_BL, this.p, -100, _canvas.height/2.5);
|
||||
// this.scramble_CR = _physics.makeAttraction(_scrambler_CR, this.p, -50, _canvas.height/4);
|
||||
// this.scramble_CL = _physics.makeAttraction(_scrambler_CL, this.p, -50, _canvas.height/4);
|
||||
this.scramble_TL.on = false;
|
||||
this.scramble_TR.on = false;
|
||||
this.scramble_BR.on = false;
|
||||
this.scramble_BL.on = false;
|
||||
// this.scramble_CR.on = false;
|
||||
// this.scramble_CL.on = false;
|
||||
};
|
||||
|
||||
Node.prototype.calcWallLimits = function(){
|
||||
@@ -320,9 +269,16 @@
|
||||
|
||||
Node.prototype.onResizeCanvas = function(){
|
||||
this.calcWallLimits();
|
||||
this.shuffleOriP();
|
||||
// TODO: when canvas is smaller what about nodes hors champs, they are coming back alone but it's too long
|
||||
};
|
||||
|
||||
Node.prototype.shuffleOriP = function(){
|
||||
this.ori_pos.x = this.wall_limits.left + Math.random()*(this.wall_limits.right - this.wall_limits.left);
|
||||
this.ori_pos.y = this.wall_limits.top + Math.random()*(this.wall_limits.bottom - this.wall_limits.top);
|
||||
this.scramble();
|
||||
};
|
||||
|
||||
Node.prototype.onUpdate = function(){
|
||||
|
||||
// this.p.velocity.multiplyScalar(0.99);
|
||||
@@ -339,7 +295,11 @@
|
||||
// this.checkVelocityThreshold();
|
||||
this.checkWallBouncing();
|
||||
this.updatePos();
|
||||
if(this.scrambling){
|
||||
this.checkOriPDist();
|
||||
}
|
||||
}
|
||||
|
||||
if(_mouse_in && !this.aside)
|
||||
this.checkMouse();
|
||||
|
||||
@@ -459,35 +419,37 @@
|
||||
// this.fade();
|
||||
this.stopScrambling();
|
||||
this.unsetCentered();
|
||||
this.repulse_top.on = true;
|
||||
this.repulse_center.on = true;
|
||||
this.repulse_bottom.on = true;
|
||||
this.ori_p.position.x = this.x < _canvas.width /2 ? this.wall_limits.left : this.wall_limits.right;
|
||||
this.ori_p_attract.on = true;
|
||||
}
|
||||
Node.prototype.unsetAside = function(){
|
||||
console.log('unsetAside');
|
||||
this.aside = false;
|
||||
this.ori_p_attract.on = false;
|
||||
this.ori_p.position.x = this.ori_pos.x;
|
||||
// this.unFade();
|
||||
this.repulse_top.on = false;
|
||||
this.repulse_center.on = false;
|
||||
this.repulse_bottom.on = false;
|
||||
}
|
||||
|
||||
Node.prototype.scramble = function(){
|
||||
this.scrambling = true;
|
||||
this.unsetCentered();
|
||||
this.unsetAside();
|
||||
this.scramble_TL.on = true;
|
||||
this.scramble_TR.on = true;
|
||||
this.scramble_BR.on = true;
|
||||
this.scramble_BL.on = true;
|
||||
// this.scramble_CR.on = true;
|
||||
// this.scramble_CL.on = true;
|
||||
this.ori_p.position.x = this.ori_pos.x;
|
||||
this.ori_p.position.y = this.ori_pos.y;
|
||||
this.ori_p_attract.on = true;
|
||||
}
|
||||
Node.prototype.checkOriPDist = function(){
|
||||
if(this.p.distanceTo(this.ori_p) < 50){
|
||||
this.stopScrambling();
|
||||
}
|
||||
};
|
||||
Node.prototype.stopScrambling = function(){
|
||||
this.scrambling = false;
|
||||
this.scramble_TL.on = false;
|
||||
this.scramble_TR.on = false;
|
||||
this.scramble_BR.on = false;
|
||||
this.scramble_BL.on = false;
|
||||
this.ori_p_attract.on = false;
|
||||
// this.scramble_TL.on = false;
|
||||
// this.scramble_TR.on = false;
|
||||
// this.scramble_BR.on = false;
|
||||
// this.scramble_BL.on = false;
|
||||
// this.scramble_CR.on = false;
|
||||
// this.scramble_CL.on = false;
|
||||
}
|
||||
@@ -654,6 +616,25 @@
|
||||
// | __|_ _| |_ _ _ ___ ___ ___
|
||||
// | _|| ' \ _| '_/ -_) -_|_-<
|
||||
// |___|_||_\__|_| \___\___/__/
|
||||
function toggleEntree(tid, bubbling){
|
||||
var $link = _$entrees_block_termlinks.filter('[tid="'+tid+'"]');
|
||||
var $li = $link.parents('li');
|
||||
var sys_path = $link.attr('data-drupal-link-system-path');
|
||||
var url = $link.attr('href');
|
||||
if(!$li.is('.opened')){
|
||||
_$entrees_block_termlinks.parents('li').removeClass('opened');
|
||||
$li.addClass('opened');
|
||||
filterEntree(tid);
|
||||
if(bubbling){
|
||||
_$body.trigger({'type':'open_entree', 'tid':tid, 'url':url, 'sys_path':sys_path});
|
||||
}
|
||||
// bubbling is true only when event is a real click on entree link
|
||||
}else if(bubbling){
|
||||
$li.removeClass('opened');
|
||||
scrambleCollection();
|
||||
_$body.trigger({'type':'close_entree', 'tid':tid});
|
||||
}
|
||||
};
|
||||
function filterEntree(t){
|
||||
shutDownArticles();
|
||||
|
||||
@@ -688,7 +669,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ___ _
|
||||
// / __| ___ __ _ _ _ __| |_
|
||||
// \__ \/ -_) _` | '_/ _| ' \
|
||||
@@ -709,26 +689,20 @@
|
||||
createNodesRepulsions();
|
||||
};
|
||||
|
||||
|
||||
// ___ _ _ _
|
||||
// / __| __ _ _ __ _ _ __ | |__| (_)_ _ __ _
|
||||
// \__ \/ _| '_/ _` | ' \| '_ \ | | ' \/ _` |
|
||||
// |___/\__|_| \__,_|_|_|_|_.__/_|_|_||_\__, |
|
||||
// |___/
|
||||
function scrambleCollection(){
|
||||
// console.log('scrambleCollection');
|
||||
for (var i = 0; i < _nodes.length; i++) {
|
||||
_nodes[i].scramble();
|
||||
}
|
||||
// setTimeout(stopScrambling, 2000);
|
||||
stopScrambling();
|
||||
};
|
||||
function stopScrambling(){
|
||||
for (var i = 0; i < _nodes.length; i++) {
|
||||
setTimeout(function(n){
|
||||
n.stopScrambling();
|
||||
}.bind(this, _nodes[i]), 1000 + Math.random()*4000);
|
||||
}
|
||||
// stopScrambling();
|
||||
};
|
||||
|
||||
function closeAllEntries(){
|
||||
_$entrees_block_termlinks.each(function(index, el) {
|
||||
if($(this).parents('li').is('.opened')){
|
||||
@@ -787,11 +761,13 @@
|
||||
if(_$articles_link.is('.is-active'))
|
||||
_$articles_link.trigger('click');
|
||||
};
|
||||
|
||||
// ___ _
|
||||
// | __|_ _____ _ _| |_ ___
|
||||
// | _|\ V / -_) ' \ _(_-<
|
||||
// |___|\_/\___|_||_\__/__/
|
||||
function initEvents(){
|
||||
console.log('Corpus, initEvents');
|
||||
_$canvas
|
||||
.on('mousemove', function(event) {
|
||||
event.preventDefault();
|
||||
@@ -836,22 +812,33 @@
|
||||
})
|
||||
.on('audio-node-closed', function(e){
|
||||
closeNode();
|
||||
})
|
||||
.on('open-entree', function(e){
|
||||
toggleEntree(e.tid);
|
||||
})
|
||||
.on('close-all-entree', function(e){
|
||||
closeAllEntries();
|
||||
});
|
||||
|
||||
_$entrees_block_termlinks.on('click', function(event) {
|
||||
// console.log('_$entrees_block_termlinks click', this);
|
||||
event.preventDefault();
|
||||
var tid = $(this).attr('tid');
|
||||
var $li = $(this).parents('li');
|
||||
if(!$li.is('.opened')){
|
||||
_$entrees_block_termlinks.parents('li').removeClass('opened');
|
||||
$li.addClass('opened');
|
||||
filterEntree(tid);
|
||||
_$body.trigger({'type':'open_entree', 'tid':tid});
|
||||
}else{
|
||||
$li.removeClass('opened');
|
||||
scrambleCollection();
|
||||
_$body.trigger({'type':'close_entree', 'tid':tid});
|
||||
}
|
||||
toggleEntree($(this).attr('tid'), true);
|
||||
// var $link = $(this);
|
||||
// var tid = $link.attr('tid');
|
||||
// var $li = $link.parents('li');
|
||||
// var sys_path = $link.attr('data-drupal-link-system-path');
|
||||
// var url = $link.attr('href');
|
||||
// if(!$li.is('.opened')){
|
||||
// _$entrees_block_termlinks.parents('li').removeClass('opened');
|
||||
// $li.addClass('opened');
|
||||
// toggleEntree(tid);
|
||||
// _$body.trigger({'type':'open_entree', 'tid':tid, 'url':url, 'sys_path':sys_path});
|
||||
// }else{
|
||||
// $li.removeClass('opened');
|
||||
// scrambleCollection();
|
||||
// _$body.trigger({'type':'close_entree', 'tid':tid});
|
||||
// }
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -866,9 +853,27 @@
|
||||
})
|
||||
.on('search-closed', function(e){
|
||||
scrambleCollection();
|
||||
})
|
||||
.on('new-content-not-entree-ajax-loaded', function(e){
|
||||
// close all entries only if entry already opened
|
||||
if(_$entrees_block_termlinks.parents('li.opened').length){
|
||||
_$entrees_block_termlinks.parents('li').removeClass('opened');
|
||||
scrambleCollection();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function checkPreOpenedEntry(){
|
||||
_$entrees_block.find('li.entree').each(function(index, el) {
|
||||
var $li = $(this);
|
||||
if($('a.is-active', $li).length){
|
||||
$li.addClass('opened');
|
||||
filterEntree($li.attr('tid'));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// _ _ _ ___ _ _
|
||||
// | \| |___ __| |___| _ \___ _ __| | | |_ __
|
||||
// | .` / _ \/ _` / -_) _/ _ \ '_ \ |_| | '_ \
|
||||
@@ -994,10 +999,8 @@
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function($) {
|
||||
var edlpcorpus = new EdlpCorpus();
|
||||
});
|
||||
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
|
||||
@@ -132,32 +132,45 @@ class EdlpSearchController extends ControllerBase {
|
||||
$query->keys($this->keys);
|
||||
}
|
||||
|
||||
// select the field in which search will be performed
|
||||
$query->setFulltextFields(array($this->field_name));
|
||||
|
||||
if(null !== $this->field_synonyms){
|
||||
// select the taxo field AND the synonym field in which search will be performed
|
||||
$query->setFulltextFields(array($this->field_name, $this->field_synonyms));
|
||||
}else{
|
||||
// OR select the unique taxo field in which search will be performed
|
||||
$query->setFulltextFields(array($this->field_name));
|
||||
}
|
||||
|
||||
$result = $query->execute();
|
||||
$items = $result->getResultItems();
|
||||
|
||||
$this->matches = [];
|
||||
foreach ($items as $item) {
|
||||
// get the field from item
|
||||
try {
|
||||
/** @var \Drupal\Core\Entity\EntityInterface $entity */
|
||||
$fields = $item->getField($this->field_name)->getValues();
|
||||
}catch (SearchApiException $e) {
|
||||
// if error on getinfg the field, skip item
|
||||
continue;
|
||||
}
|
||||
// if no fields returned, skip item
|
||||
if (!$fields) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get field content
|
||||
$field_text = $fields[0]->getText();
|
||||
|
||||
// if content already in matches, skip item
|
||||
if( in_array($field_text, $this->matches) ){
|
||||
continue;
|
||||
}
|
||||
|
||||
// add the item to the return list
|
||||
$this->matches[] = $field_text;
|
||||
|
||||
// do not return more than 14 items
|
||||
if(count($this->matches) > 14){
|
||||
break;
|
||||
}
|
||||
@@ -169,8 +182,12 @@ class EdlpSearchController extends ControllerBase {
|
||||
* getRequestArgs
|
||||
*/
|
||||
private function getRequestAutocompleteArgs(){
|
||||
// args are provided by form definition with autocomplete_route_parameters
|
||||
// dpm($this->request);
|
||||
$this->field_name = $this->request->query->get('field_name');
|
||||
// if($this->request->query->get('field_synonyms')){
|
||||
$this->field_synonyms = $this->request->query->get('field_synonyms');
|
||||
// }
|
||||
$this->keys = $this->request->query->get('q');
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class EdlpSearchForm extends FormBase {
|
||||
'genres' => [
|
||||
'#type' => 'textfield',
|
||||
'#autocomplete_route_name' => 'edlp_search.edlp_search_controller_autocomplete',
|
||||
'#autocomplete_route_parameters' => array('field_name' => 'field_genres'),
|
||||
'#autocomplete_route_parameters' => array('field_name' => 'field_genres', 'field_synonyms' => 'field_synonyms'),
|
||||
'#maxlength' => 20,
|
||||
'#size' => 15,
|
||||
'#weight' => '0',
|
||||
|
||||
@@ -1,11 +1,2 @@
|
||||
console.log('EDLP THEME HISTORY.js');
|
||||
// var edlp is provided by edlp_ajax.module file
|
||||
if(edlp.redirect){
|
||||
console.log('history redirect');
|
||||
window.localStorage.setItem('edlp_origin_path', edlp.sys_path.replace(/^\//, ''));
|
||||
window.localStorage.setItem('edlp_origin_url', window.location.pathname);
|
||||
// redirect to home
|
||||
window.location.replace(window.location.origin);
|
||||
}else{
|
||||
console.log('history do not redirect');
|
||||
}
|
||||
|
||||
void 0;if(edlp.redirect){void 0;window.localStorage.setItem('edlp_origin_path',edlp.sys_path.replace(/^\//,''));window.localStorage.setItem('edlp_origin_url',window.location.pathname);window.localStorage.setItem('edlp_origin_hash',window.location.hash);window.location.replace(window.location.origin);}else{void 0;}
|
||||
+40
-1080
File diff suppressed because it is too large
Load Diff
+1
-2710
File diff suppressed because one or more lines are too long
@@ -4,6 +4,7 @@ if(edlp.redirect){
|
||||
console.log('history redirect');
|
||||
window.localStorage.setItem('edlp_origin_path', edlp.sys_path.replace(/^\//, ''));
|
||||
window.localStorage.setItem('edlp_origin_url', window.location.pathname);
|
||||
window.localStorage.setItem('edlp_origin_hash', window.location.hash);
|
||||
// redirect to home
|
||||
window.location.replace(window.location.origin);
|
||||
}else{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
console.log('EDLP THEME MAIN.js');
|
||||
(function($, Drupal, drupalSettings) {
|
||||
|
||||
EdlpTheme = function(){
|
||||
var _ajax_settings = drupalSettings.edlp_ajax;
|
||||
var _$body = $('body');
|
||||
var _is_front = _$body.is('.path-frontpage');
|
||||
var _corpus_ready = false;
|
||||
var _$corpus_canvas;
|
||||
var _$row = $('main[role="main"]>.layout-content>.row');
|
||||
var _$ajaxLinks;
|
||||
@@ -19,6 +19,8 @@ console.log('EDLP THEME MAIN.js');
|
||||
function init(){
|
||||
console.log("EdlpTheme init()");
|
||||
|
||||
initAjaxLinks();
|
||||
|
||||
initHistory();
|
||||
|
||||
if(!drupalSettings.path.isFront)
|
||||
@@ -29,11 +31,6 @@ console.log('EDLP THEME MAIN.js');
|
||||
_audioPlayer = new AudioPlayer();
|
||||
_compoPlayer = new CompoPlayer();
|
||||
|
||||
initAjaxLinks();
|
||||
|
||||
// if (_$body.is('.path-productions')) initProductions();
|
||||
// if(_$body.is('.path-frontpage')) initHome();
|
||||
// initScrollbars();
|
||||
};
|
||||
|
||||
// ___ _
|
||||
@@ -59,7 +56,31 @@ console.log('EDLP THEME MAIN.js');
|
||||
_compoPlayer.newCompo();
|
||||
})
|
||||
.on('search-results-loaded', initAjaxLinks)
|
||||
.on('open_entree', closeAllModals)
|
||||
// do not close index or notice modale on entree click
|
||||
.on('open_entree', function(e){
|
||||
console.log('on_open_entree : e', e);
|
||||
closeAllModals();
|
||||
|
||||
// add body class for currently loaded content
|
||||
// var body_classes = [
|
||||
// 'path-'+sys_path.replace(/\//g, '-'),
|
||||
// 'entity-type-'+data.entity_type,
|
||||
// 'bundle-'+data.bundle,
|
||||
// 'view-mode-'+data.view_mode
|
||||
// ];
|
||||
_$body.removeClass();//.addClass(body_classes.join(' '));
|
||||
|
||||
// record new history state
|
||||
if(typeof e.url != 'undefined'){
|
||||
// var state = {
|
||||
// ajax_path:null,
|
||||
// sys_path:null,
|
||||
// entree_tid:e.tid
|
||||
// };
|
||||
var state = getSysPathState(e.sys_path);
|
||||
history.pushState(state, null, e.url);
|
||||
}
|
||||
})
|
||||
.on('close_entree', backToFrontPage);
|
||||
}
|
||||
|
||||
@@ -84,44 +105,55 @@ console.log('EDLP THEME MAIN.js');
|
||||
// / _ \ | / _` \ \ /
|
||||
// /_/ \_\/ \__,_/_\_\
|
||||
// |__/
|
||||
function parseAjaxSysPath(sys_path, view_mode){
|
||||
// console.log('Theme : parseAjaxSysPath', sys_path);
|
||||
var ajax_path = sys_path;
|
||||
function getSysPathState(sys_path, view_mode){
|
||||
// console.log('Theme : getSysPathState', sys_path);
|
||||
var state = {
|
||||
'sys_path':sys_path,
|
||||
'ajax_path': sys_path
|
||||
};
|
||||
|
||||
// convert node link to edlp_ajax_node module links
|
||||
var node_match = ajax_path.match(/^\/?(node\/\d+)$/g);
|
||||
var term_match = ajax_path.match(/^\/?(taxonomy\/term\/\d+)$/g);
|
||||
var node_match = state.ajax_path.match(/^\/?(node\/(\d+))$/i);
|
||||
console.log('node_match', node_match);
|
||||
var term_match = state.ajax_path.match(/^\/?(taxonomy\/term\/(\d+))$/i);
|
||||
console.log('term_match', term_match);
|
||||
if(node_match){
|
||||
ajax_path = _ajax_settings.entityjson_path+'/'+node_match[0];
|
||||
state.ajax_path = _ajax_settings.entityjson_path+'/'+node_match[1];
|
||||
state.node_nid = node_match[2];
|
||||
// check for viewmode attribute
|
||||
if(view_mode){
|
||||
ajax_path += '/'+view_mode;
|
||||
state.ajax_path += '/'+view_mode;
|
||||
}
|
||||
}else if(term_match){
|
||||
ajax_path = _ajax_settings.entityjson_path+'/'+term_match[0];
|
||||
ajax_path = ajax_path.replace(/taxonomy\/term/, 'taxonomy_term');
|
||||
// terms are always entrees, there's no other vocabulary links in front
|
||||
state.ajax_path = _ajax_settings.entityjson_path+'/'+term_match[1];
|
||||
state.ajax_path = state.ajax_path.replace(/taxonomy\/term/, 'taxonomy_term');
|
||||
state.entree_tid = term_match[2];
|
||||
// check for viewmode attribute
|
||||
if(view_mode){
|
||||
ajax_path += '/'+view_mode;
|
||||
state.ajax_path += '/'+view_mode;
|
||||
state.view_mode = view_mode;
|
||||
}else{
|
||||
state.ajax_path = null;
|
||||
}
|
||||
}else{
|
||||
// convert other link to ajax
|
||||
// TODO: we assume that other links (no node, no term) are all from own modules (e.g. productions) !! may not be true !!
|
||||
ajax_path += '/ajax'
|
||||
state.ajax_path += '/ajax'
|
||||
}
|
||||
return ajax_path;
|
||||
return state;
|
||||
};
|
||||
function ajaxLoadContent(url, sys_path, ajax_path, selector){
|
||||
console.log('ajaxLoadContent : url', url);
|
||||
function ajaxLoadContent(state){
|
||||
console.log('ajaxLoadContent : url', state.url);
|
||||
_$body.addClass('ajax-loading');
|
||||
|
||||
var path = window.location.origin + Drupal.url(ajax_path);
|
||||
var path = window.location.origin + Drupal.url(state.ajax_path);
|
||||
$.getJSON(path, {})
|
||||
.done(function(data){
|
||||
onAjaxLoaded(data, url, ajax_path, sys_path, selector);
|
||||
onAjaxLoaded(data, state);
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error){
|
||||
onAjaxLoadError(jqxhr, textStatus, error, sys_path);
|
||||
onAjaxLoadError(jqxhr, textStatus, error, state.sys_path);
|
||||
});
|
||||
};
|
||||
function onAjaxLoadError(jqxhr, textStatus, error, sys_path){
|
||||
@@ -129,22 +161,17 @@ console.log('EDLP THEME MAIN.js');
|
||||
$('.ajax-loading').removeClass('ajax-loading');
|
||||
_$body.removeClass('ajax-loading');
|
||||
};
|
||||
function onAjaxLoaded(data, url, ajax_path, sys_path, selector){
|
||||
console.log('ajax loaded url:'+url+' ajax_path:'+ajax_path+' sys_path:'+sys_path);
|
||||
function onAjaxLoaded(data, state){
|
||||
console.log('ajax loaded url:'+state.url+' ajax_path:'+state.ajax_path+' sys_path:'+state.sys_path);
|
||||
|
||||
// reset all style may been added by other pages (like masonry for productions)
|
||||
// and replace all content with newly loaded
|
||||
// TODO: build a system to replace or append contents (like studio + search)
|
||||
_$row.removeAttr('style').html(data.rendered);
|
||||
|
||||
// add close btn
|
||||
if(sys_path != 'productions'){
|
||||
addCloseBtnToCols();
|
||||
}
|
||||
|
||||
// add body class for currently loaded content
|
||||
var body_classes = [
|
||||
'path-'+sys_path.replace(/\//g, '-'),
|
||||
'path-'+state.sys_path.replace(/\//g, '-'),
|
||||
'entity-type-'+data.entity_type,
|
||||
'bundle-'+data.bundle,
|
||||
'view-mode-'+data.view_mode
|
||||
@@ -152,8 +179,9 @@ console.log('EDLP THEME MAIN.js');
|
||||
_$body.removeClass().addClass(body_classes.join(' '));
|
||||
|
||||
// id node add a generic path-node class to body
|
||||
m = sys_path.match(/^\/?(node\/\d+)$/g);
|
||||
if(m)
|
||||
// m = state.sys_path.match(/^\/?(node\/\d+)$/g);
|
||||
// if(m)
|
||||
if(state.node_nid)
|
||||
_$body.addClass('path-edlp-node');
|
||||
|
||||
// handle clicked link classes
|
||||
@@ -161,10 +189,14 @@ console.log('EDLP THEME MAIN.js');
|
||||
$('.is-active').removeClass('is-active');
|
||||
$('.is-active-trail').removeClass('is-active-trail');
|
||||
|
||||
if(typeof selector != 'undefined'){
|
||||
$('a[selector="'+selector+'"]').addClass('is-active');
|
||||
if(typeof state.selector != 'undefined'){
|
||||
// in case of entree link (actualy, selector is used only for entries links)
|
||||
console.log('selector', state.selector);
|
||||
$('a[selector="'+state.selector+'"]').addClass('is-active');
|
||||
}else{
|
||||
$('a[data-drupal-link-system-path="'+sys_path+'"]').addClass('is-active');
|
||||
$('a[data-drupal-link-system-path="'+state.sys_path+'"]').addClass('is-active');
|
||||
// as new content is not related to entree, we trigger close entree
|
||||
_$body.trigger({'type':'new-content-not-entree-ajax-loaded'});
|
||||
}
|
||||
|
||||
// if bundle page (productions) activate production links
|
||||
@@ -190,13 +222,17 @@ console.log('EDLP THEME MAIN.js');
|
||||
|
||||
// initScrollbars();
|
||||
|
||||
if(sys_path == "productions")
|
||||
if(state.sys_path == "productions"){
|
||||
initProductions();
|
||||
}else{
|
||||
addCloseModalBtnToCols();
|
||||
}
|
||||
|
||||
initAjaxLinks();
|
||||
|
||||
// trigger other modules behaviours
|
||||
_$body.trigger({'type':'new-content-ajax-loaded'});
|
||||
|
||||
// and call druapl behaviours
|
||||
Drupal.attachBehaviors(_$row[0]);
|
||||
|
||||
@@ -204,26 +240,27 @@ console.log('EDLP THEME MAIN.js');
|
||||
_$body.removeClass('ajax-loading');
|
||||
|
||||
|
||||
var state = {
|
||||
ajax_path:ajax_path,
|
||||
sys_path:sys_path,
|
||||
};
|
||||
// url is null means that we are loading content on popState event
|
||||
// so we don't record the state again
|
||||
if(url){
|
||||
if(state.url){
|
||||
// var state = {
|
||||
// ajax_path:ajax_path,
|
||||
// sys_path:sys_path,
|
||||
// };
|
||||
// console.log('url:'+url+' ; state',state);
|
||||
// console.log(window.location);
|
||||
// we can not pushestate with absolute url
|
||||
history.pushState(state, null, url);
|
||||
history.pushState(state, null, state.url);
|
||||
}
|
||||
};
|
||||
|
||||
function addCloseBtnToCols(){
|
||||
function addCloseModalBtnToCols(){
|
||||
$('.col', _$row).each(function(index, el) {
|
||||
|
||||
if($('span.close-col-btn', this).length)
|
||||
return true;
|
||||
|
||||
$(this).children('.wrapper').append($('<span>')
|
||||
$(this).children('.wrapper').prepend($('<span>')
|
||||
.addClass('close-col-btn')
|
||||
.on('click', function(e){
|
||||
// check for theme attribute and emmit event
|
||||
@@ -234,9 +271,10 @@ console.log('EDLP THEME MAIN.js');
|
||||
}
|
||||
// remove the col
|
||||
$col.remove();
|
||||
// if row is empty call closeAllModals()
|
||||
if(!$('.col', _$row).length){
|
||||
backToFrontPage();
|
||||
// if row is empty and we are not in productions call closeAllModals()
|
||||
if(!$('.col', _$row).length
|
||||
&& !_$body.is('.entity-type-node.bundle-page')){
|
||||
backToFrontPage();
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -253,41 +291,73 @@ console.log('EDLP THEME MAIN.js');
|
||||
window.addEventListener('popstate', onHistoryPopState);
|
||||
};
|
||||
function initFirstLoad(){
|
||||
console.log('theme : initFirstLoad()', window.location);
|
||||
console.log(document.cookie);
|
||||
var url = window.location.pathname;
|
||||
// var origin_path = getCookie('edlp_origin_path');
|
||||
// console.log('origin_path', origin_path);
|
||||
console.log('theme : initFirstLoad()');
|
||||
|
||||
var origin_sys_path = window.localStorage.getItem('edlp_origin_path');
|
||||
console.log('origin_sys_path', origin_sys_path);
|
||||
var origin_url = window.localStorage.getItem('edlp_origin_url');
|
||||
console.log('origin_url', origin_url);
|
||||
if(origin_sys_path){
|
||||
var origin_url = window.localStorage.getItem('edlp_origin_url');
|
||||
// origin_hash is used as viewmode for taxonomy term entrees load (index or notice)
|
||||
var origin_hash = window.localStorage.getItem('edlp_origin_hash');
|
||||
var view_mode = origin_hash.replace('#', '');
|
||||
if(view_mode){
|
||||
var $link = $('[href="'+origin_url+'"][viewmode="'+view_mode+'"]');
|
||||
var selector = $link.attr('selector') || null;
|
||||
if(selector){
|
||||
// in case of entree link (actualy, selector is used only for entries links)
|
||||
if(_corpus_ready){
|
||||
_$corpus_canvas.trigger({
|
||||
type:'open-entree',
|
||||
tid:$link.attr('tid')
|
||||
});
|
||||
}else{
|
||||
// else : EdlpCorpus will check when ready if entry item (notice or index) is already .is-active
|
||||
// .is-active class is added by onAjaxLoaded() (when content is loaded)
|
||||
// but what if corpus ready before onAjaxLoaded
|
||||
$('li.entree[tid="'+$link.attr('tid')+'"] a.term-link').addClass('is-active');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create history state
|
||||
var state = {
|
||||
ajax_path: parseAjaxSysPath(origin_sys_path),
|
||||
sys_path: origin_sys_path,
|
||||
};
|
||||
console.log('state', state);
|
||||
var state = getSysPathState(origin_sys_path, view_mode);
|
||||
|
||||
// only if not entree path
|
||||
if(state.ajax_path){
|
||||
// load content through ajax
|
||||
// ajaxLoadContent(null, state.sys_path, state.ajax_path, selector);
|
||||
ajaxLoadContent(state);
|
||||
}
|
||||
|
||||
// TODO what about entree alone (without notice or index)
|
||||
if(state.entree_tid){
|
||||
openEntree(state.entree_tid);
|
||||
}
|
||||
|
||||
// record history state
|
||||
history.replaceState(state, null, origin_url);
|
||||
// load content through ajax
|
||||
ajaxLoadContent(null, state.sys_path, state.ajax_path);
|
||||
history.replaceState(state, null, origin_url+origin_hash);
|
||||
|
||||
// reset the storage
|
||||
window.localStorage.removeItem("edlp_origin_path");
|
||||
window.localStorage.removeItem("edlp_origin_url");
|
||||
|
||||
}else{
|
||||
history.replaceState({home:true}, null, url);
|
||||
history.replaceState({home:true}, null, window.location.pathname);
|
||||
_$body.attr('booted', 'booted');
|
||||
}
|
||||
};
|
||||
function onHistoryPopState(e){
|
||||
//console.log('onPopState',e);
|
||||
console.log('onPopState',e.state);
|
||||
if(e.state.home){
|
||||
backToFrontPage();
|
||||
backToFrontPage(true);
|
||||
}else{
|
||||
ajaxLoadContent(null, e.state.sys_path, e.state.ajax_path)
|
||||
if(e.state.entree_tid){
|
||||
openEntree(e.state.entree_tid);
|
||||
}
|
||||
if(e.state.ajax_path){
|
||||
e.state.url = null;
|
||||
// ajaxLoadContent(null, e.state.sys_path, e.state.ajax_path)
|
||||
ajaxLoadContent(e.state);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -299,7 +369,8 @@ console.log('EDLP THEME MAIN.js');
|
||||
function initAjaxLinks(){
|
||||
// console.log('initAjaxLinks');
|
||||
|
||||
$('a', '#block-mainnavigation')
|
||||
$('a.site-name', '#block-edlptheme-branding')
|
||||
.add('a', '#block-mainnavigation')
|
||||
.add('a', '#block-footer.menu--footer')
|
||||
.add('a', '#block-productions')
|
||||
.add('a', 'article.node:not(.node--type-enregistrement) h2.node-title')
|
||||
@@ -338,7 +409,6 @@ console.log('EDLP THEME MAIN.js');
|
||||
}
|
||||
|
||||
// other links
|
||||
var url = $(this).attr('href');
|
||||
var sys_path = $(this).attr('data-drupal-link-system-path');
|
||||
|
||||
// front page
|
||||
@@ -349,15 +419,21 @@ console.log('EDLP THEME MAIN.js');
|
||||
}
|
||||
|
||||
var view_mode = $link.attr('viewmode');
|
||||
var ajax_path = parseAjaxSysPath(sys_path, view_mode);
|
||||
var state = getSysPathState(sys_path, view_mode);
|
||||
state.url = $(this).attr('href');
|
||||
|
||||
$link.addClass('ajax-loading');
|
||||
|
||||
if($link.is('[selector]')){
|
||||
var selector = $link.attr('selector');
|
||||
if(view_mode){
|
||||
state.url += "#"+view_mode;
|
||||
}
|
||||
|
||||
ajaxLoadContent(url, sys_path, ajax_path, selector);
|
||||
if($link.is('[selector]')){
|
||||
state.selector = $link.attr('selector');
|
||||
}
|
||||
|
||||
$link.addClass('ajax-loading');
|
||||
// ajaxLoadContent(url, sys_path, state.ajax_path, selector);
|
||||
ajaxLoadContent(state);
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -368,6 +444,7 @@ console.log('EDLP THEME MAIN.js');
|
||||
// |_|
|
||||
function onCorpusMapReady(e){
|
||||
//console.log('theme : onCorpusReady', e);
|
||||
_corpus_ready = true;
|
||||
_$corpus_canvas = $('canvas#corpus-map');
|
||||
_$corpus_canvas
|
||||
.on('corpus-cliked-on-map', function(e) {
|
||||
@@ -385,6 +462,24 @@ console.log('EDLP THEME MAIN.js');
|
||||
|
||||
_$body.attr('corpus-map', 'ready');
|
||||
}
|
||||
function openEntree(tid){
|
||||
if(tid){
|
||||
closeAllModals();
|
||||
|
||||
_$body.removeClass();//.addClass(body_classes.join(' '));
|
||||
|
||||
// open entree
|
||||
if(_corpus_ready){
|
||||
_$corpus_canvas.trigger({
|
||||
type:'open-entree',
|
||||
tid:tid
|
||||
});
|
||||
}else{
|
||||
// else : EdlpCorpus will check when ready if entry item (notice or index) is already .is-active
|
||||
$('li.entree[tid="'+tid+'"] a.term-link').addClass('is-active');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// _ _ _
|
||||
// /_\ _ _ __| (_)___
|
||||
@@ -976,16 +1071,20 @@ console.log('EDLP THEME MAIN.js');
|
||||
// | _| '_/ _ \ ' \ _| _/ _` / _` / -_)
|
||||
// |_||_| \___/_||_\__|_| \__,_\__, \___|
|
||||
// |___/
|
||||
function backToFrontPage(){
|
||||
function backToFrontPage(pop_state){
|
||||
closeAllModals();
|
||||
// assume we are going back to front page
|
||||
$('body').removeClass().addClass('path-frontpage');
|
||||
$('a[data-drupal-link-system-path="<front>"]').addClass('is-active');
|
||||
|
||||
// close entrees
|
||||
_$corpus_canvas.trigger({'type':'close-all-entree'});
|
||||
if(!pop_state){
|
||||
history.pushState({home:true}, null, window.location.origin);
|
||||
}
|
||||
}
|
||||
|
||||
function initHome(){
|
||||
addCloseBtnToCols();
|
||||
addCloseModalBtnToCols();
|
||||
// console.log('theme : initHome');
|
||||
// console.log('theme : initProductions');
|
||||
var $grid = $('.grid',_$row).masonry({
|
||||
@@ -1059,16 +1158,16 @@ console.log('EDLP THEME MAIN.js');
|
||||
// |_|
|
||||
|
||||
// https://plainjs.com/javascript/utilities/set-cookie-get-cookie-and-delete-cookie-5/
|
||||
function getCookie(name) {
|
||||
var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
|
||||
return v ? v[2] : null;
|
||||
}
|
||||
function setCookie(name, value, days) {
|
||||
var d = new Date;
|
||||
d.setTime(d.getTime() + 24*60*60*1000*days);
|
||||
document.cookie = name + "=" + value + ";path=/;expires=" + d.toGMTString();
|
||||
}
|
||||
function deleteCookie(name) { setCookie(name, '', -1); }
|
||||
// function getCookie(name) {
|
||||
// var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
|
||||
// return v ? v[2] : null;
|
||||
// }
|
||||
// function setCookie(name, value, days) {
|
||||
// var d = new Date;
|
||||
// d.setTime(d.getTime() + 24*60*60*1000*days);
|
||||
// document.cookie = name + "=" + value + ";path=/;expires=" + d.toGMTString();
|
||||
// }
|
||||
// function deleteCookie(name) { setCookie(name, '', -1); }
|
||||
|
||||
init();
|
||||
} // end EdlpTheme()
|
||||
@@ -1077,5 +1176,4 @@ console.log('EDLP THEME MAIN.js');
|
||||
var edlptheme = new EdlpTheme();
|
||||
});
|
||||
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
|
||||
@@ -266,12 +266,14 @@ main[role="main"]{
|
||||
// z-index: 10;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
position:absolute;
|
||||
top:0; right:0;
|
||||
padding:0!important; margin:0.5em;
|
||||
position:sticky;
|
||||
top:0.5em;
|
||||
padding:0!important;
|
||||
margin:0.5em;
|
||||
display: block;
|
||||
$s:10px;
|
||||
width:$s; height:$s;
|
||||
float: right;
|
||||
background-image: url(../img/close.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
@@ -1199,6 +1201,7 @@ footer{
|
||||
ul{
|
||||
margin:0;
|
||||
padding:0;
|
||||
white-space: nowrap;
|
||||
li{
|
||||
pointer-events: all;
|
||||
// @include oblique-list;
|
||||
|
||||
+2
-2
@@ -17,12 +17,12 @@
|
||||
{% if site_logo or site_name %}
|
||||
<h1>
|
||||
{% if site_logo %}
|
||||
<a href="{{ path('<front>') }}" title="{{ 'Home'|t }}" rel="home" class="site-logo">
|
||||
<a href="{{ path('<front>') }}" title="{{ 'Home'|t }}" rel="home" class="site-logo" data-drupal-link-system-path="<front>">
|
||||
<img src="{{ site_logo }}" alt="{{ 'Home'|t }}" />
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if site_name %}
|
||||
<a class="site-name" href="{{ path('<front>') }}" title="{{ 'Home'|t }}" rel="home">{{ site_name }}</a>
|
||||
<a class="site-name" href="{{ path('<front>') }}" title="{{ 'Home'|t }}" rel="home" data-drupal-link-system-path="<front>">{{ site_name }}</a>
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
uuid: 4864fda4-27e7-4375-932f-d5493d37236e
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- node.type.autre_son
|
||||
id: node.autre_son.menu_link
|
||||
field_name: menu_link
|
||||
entity_type: node
|
||||
bundle: autre_son
|
||||
label: 'Lien du menu'
|
||||
description: 'Computed menu link for the node (only available during node saving).'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
handler: default
|
||||
handler_settings: { }
|
||||
field_type: entity_reference
|
||||
@@ -0,0 +1,20 @@
|
||||
uuid: a2747f17-57c4-4f4c-8dfe-9f4fea51a414
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- node.type.fil
|
||||
id: node.fil.menu_link
|
||||
field_name: menu_link
|
||||
entity_type: node
|
||||
bundle: fil
|
||||
label: 'Lien du menu'
|
||||
description: 'Computed menu link for the node (only available during node saving).'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
handler: default
|
||||
handler_settings: { }
|
||||
field_type: entity_reference
|
||||
@@ -11,7 +11,7 @@ bundle: genres
|
||||
label: Name
|
||||
description: ''
|
||||
required: true
|
||||
translatable: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
|
||||
@@ -11,7 +11,7 @@ bundle: langues
|
||||
label: Name
|
||||
description: ''
|
||||
required: true
|
||||
translatable: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
uuid: 7a74202f-9c18-4376-b97e-cde0e86c42fe
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- path
|
||||
- user
|
||||
id: user.user.path
|
||||
field_name: path
|
||||
entity_type: user
|
||||
bundle: user
|
||||
label: 'Alias d''URL'
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: path
|
||||
+13
-12
@@ -19,6 +19,7 @@ dependencies:
|
||||
module:
|
||||
- field_group
|
||||
- file
|
||||
- synonyms
|
||||
- text
|
||||
- workflow
|
||||
third_party_settings:
|
||||
@@ -102,8 +103,8 @@ third_party_settings:
|
||||
group_taxonomy:
|
||||
children:
|
||||
- field_entrees
|
||||
- field_genres
|
||||
- field_langues
|
||||
- field_genres
|
||||
parent_name: group_onglets
|
||||
weight: 27
|
||||
format_type: tab
|
||||
@@ -144,25 +145,25 @@ content:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
type: entity_reference_autocomplete
|
||||
type: entity_reference_autocomplete_tags
|
||||
region: content
|
||||
field_genres:
|
||||
weight: 13
|
||||
settings:
|
||||
suggestion_size: 10
|
||||
suggest_only_unique: false
|
||||
match: CONTAINS
|
||||
third_party_settings: { }
|
||||
type: synonyms_autocomplete
|
||||
region: content
|
||||
field_langues:
|
||||
weight: 12
|
||||
settings:
|
||||
match_operator: CONTAINS
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
type: entity_reference_autocomplete
|
||||
region: content
|
||||
field_langues:
|
||||
weight: 13
|
||||
settings:
|
||||
match_operator: CONTAINS
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
type: entity_reference_autocomplete
|
||||
type: entity_reference_autocomplete_tags
|
||||
region: content
|
||||
field_nbr_locuteurs:
|
||||
weight: 16
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
uuid: dfdb11b9-c837-4adc-bd18-ad95a30d05f3
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.taxonomy_term.genres.field_synonyms
|
||||
- taxonomy.vocabulary.genres
|
||||
id: taxonomy_term.genres.default
|
||||
targetEntityType: taxonomy_term
|
||||
bundle: genres
|
||||
mode: default
|
||||
content:
|
||||
field_synonyms:
|
||||
weight: 2
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
type: string_textfield
|
||||
region: content
|
||||
langcode:
|
||||
type: language_select
|
||||
weight: 1
|
||||
region: content
|
||||
settings:
|
||||
include_locked: true
|
||||
third_party_settings: { }
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
description: true
|
||||
path: true
|
||||
@@ -0,0 +1,29 @@
|
||||
uuid: ad860d07-87dc-4803-abc7-41e1724a77e4
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- taxonomy.vocabulary.langues
|
||||
id: taxonomy_term.langues.default
|
||||
targetEntityType: taxonomy_term
|
||||
bundle: langues
|
||||
mode: default
|
||||
content:
|
||||
langcode:
|
||||
type: language_select
|
||||
weight: 1
|
||||
region: content
|
||||
settings:
|
||||
include_locked: true
|
||||
third_party_settings: { }
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
description: true
|
||||
path: true
|
||||
@@ -0,0 +1,31 @@
|
||||
uuid: 0845979e-b977-46c8-9f90-47c6543a355e
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.taxonomy_term.genres.field_synonyms
|
||||
- taxonomy.vocabulary.genres
|
||||
module:
|
||||
- text
|
||||
id: taxonomy_term.genres.default
|
||||
targetEntityType: taxonomy_term
|
||||
bundle: genres
|
||||
mode: default
|
||||
content:
|
||||
description:
|
||||
label: hidden
|
||||
type: text_default
|
||||
weight: 0
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
field_synonyms:
|
||||
weight: 1
|
||||
label: above
|
||||
settings:
|
||||
link_to_entity: false
|
||||
third_party_settings: { }
|
||||
type: string
|
||||
region: content
|
||||
hidden:
|
||||
langcode: true
|
||||
@@ -71,6 +71,7 @@ module:
|
||||
responsive_image: 0
|
||||
search_api: 0
|
||||
search_api_db: 0
|
||||
synonyms: 0
|
||||
system: 0
|
||||
taxonomy: 0
|
||||
text: 0
|
||||
|
||||
@@ -7,12 +7,6 @@ dependencies:
|
||||
- node.type.static
|
||||
module:
|
||||
- image
|
||||
third_party_settings:
|
||||
content_translation:
|
||||
translation_sync:
|
||||
alt: alt
|
||||
title: title
|
||||
file: '0'
|
||||
id: node.static.field_visuel
|
||||
field_name: field_visuel
|
||||
entity_type: node
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
uuid: 633eb359-7553-4848-b50c-1a2a9a249a24
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.taxonomy_term.field_synonyms
|
||||
- taxonomy.vocabulary.genres
|
||||
id: taxonomy_term.genres.field_synonyms
|
||||
field_name: field_synonyms
|
||||
entity_type: taxonomy_term
|
||||
bundle: genres
|
||||
label: Synonyms
|
||||
description: ''
|
||||
required: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: string
|
||||
@@ -0,0 +1,21 @@
|
||||
uuid: 5a2f08b1-bc6a-44f8-83a0-5a222f49901b
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- taxonomy
|
||||
id: taxonomy_term.field_synonyms
|
||||
field_name: field_synonyms
|
||||
entity_type: taxonomy_term
|
||||
type: string
|
||||
settings:
|
||||
max_length: 255
|
||||
is_ascii: false
|
||||
case_sensitive: false
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: -1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
@@ -8,11 +8,11 @@ dependencies:
|
||||
- content_translation
|
||||
third_party_settings:
|
||||
content_translation:
|
||||
enabled: false
|
||||
enabled: true
|
||||
bundle_settings:
|
||||
untranslatable_fields_hide: '0'
|
||||
untranslatable_fields_hide: '1'
|
||||
id: taxonomy_term.genres
|
||||
target_entity_type_id: taxonomy_term
|
||||
target_bundle: genres
|
||||
default_langcode: site_default
|
||||
language_alterable: false
|
||||
language_alterable: true
|
||||
|
||||
@@ -8,11 +8,11 @@ dependencies:
|
||||
- content_translation
|
||||
third_party_settings:
|
||||
content_translation:
|
||||
enabled: false
|
||||
enabled: true
|
||||
bundle_settings:
|
||||
untranslatable_fields_hide: '0'
|
||||
untranslatable_fields_hide: '1'
|
||||
id: taxonomy_term.langues
|
||||
target_entity_type_id: taxonomy_term
|
||||
target_bundle: langues
|
||||
default_langcode: site_default
|
||||
language_alterable: false
|
||||
language_alterable: true
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
label: 'Update URL alias'
|
||||
@@ -0,0 +1 @@
|
||||
label: 'Update URL alias'
|
||||
@@ -0,0 +1,23 @@
|
||||
uuid: c165ad8c-6dbf-4a3a-9c91-639595a3b980
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- ctools
|
||||
- taxonomy
|
||||
id: taxonomy_entrees
|
||||
label: 'Taxonomy entrees'
|
||||
type: 'canonical_entities:taxonomy_term'
|
||||
pattern: '[term:vocabulary]/[term:name]'
|
||||
selection_criteria:
|
||||
bfdafa2f-bf98-46de-ad4a-2bb5bc88de38:
|
||||
id: 'entity_bundle:taxonomy_term'
|
||||
bundles:
|
||||
entrees: entrees
|
||||
negate: false
|
||||
context_mapping:
|
||||
taxonomy_term: taxonomy_term
|
||||
uuid: bfdafa2f-bf98-46de-ad4a-2bb5bc88de38
|
||||
selection_logic: and
|
||||
weight: -5
|
||||
relationships: { }
|
||||
@@ -12,6 +12,7 @@ dependencies:
|
||||
- field.storage.node.field_workflow
|
||||
- field.storage.node.field_genres
|
||||
- field.storage.node.field_langues
|
||||
- field.storage.taxonomy_term.field_synonyms
|
||||
- search_api.server.edlp_db
|
||||
module:
|
||||
- taxonomy
|
||||
@@ -138,6 +139,17 @@ field_settings:
|
||||
- field.storage.node.field_langues
|
||||
module:
|
||||
- taxonomy
|
||||
field_synonyms:
|
||||
label: 'Genre(s) » Taxonomy term » Synonyms'
|
||||
datasource_id: 'entity:node'
|
||||
property_path: 'field_genres:entity:field_synonyms'
|
||||
type: text
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_genres
|
||||
- field.storage.taxonomy_term.field_synonyms
|
||||
module:
|
||||
- taxonomy
|
||||
datasource_settings:
|
||||
'entity:node':
|
||||
bundles:
|
||||
@@ -161,6 +173,7 @@ processor_settings:
|
||||
- name
|
||||
- field_genres
|
||||
- field_langues
|
||||
- field_synonyms
|
||||
spaces: ''
|
||||
overlap_cjk: 1
|
||||
minimum_word_size: '3'
|
||||
@@ -200,6 +213,7 @@ processor_settings:
|
||||
- name
|
||||
- field_genres
|
||||
- field_langues
|
||||
- field_synonyms
|
||||
stopwords:
|
||||
- a
|
||||
- an
|
||||
@@ -251,6 +265,7 @@ processor_settings:
|
||||
- field_workflow
|
||||
- field_genres
|
||||
- field_langues
|
||||
- field_synonyms
|
||||
weights:
|
||||
preprocess_index: -20
|
||||
preprocess_query: -20
|
||||
@@ -266,6 +281,7 @@ processor_settings:
|
||||
- field_workflow
|
||||
- field_genres
|
||||
- field_langues
|
||||
- field_synonyms
|
||||
weights:
|
||||
preprocess_index: -20
|
||||
preprocess_query: -20
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
uuid: f3bc2d60-f84d-401c-b265-f2dc9bce7d2a
|
||||
langcode: fr
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.taxonomy_term.genres.field_synonyms
|
||||
id: field.synonyms.behavior.autocomplete_taxonomy_term_genres_field_synonyms
|
||||
provider_plugin: 'field:synonyms.behavior.autocomplete_taxonomy_term_genres_field_synonyms'
|
||||
base_provider_plugin: field
|
||||
provider_configuration: { }
|
||||
behavior: synonyms.behavior.autocomplete
|
||||
behavior_configuration:
|
||||
wording: '@entity_label (@synonym)'
|
||||
@@ -75,7 +75,10 @@ permissions:
|
||||
- 'schedule corpus_documents workflow_transition'
|
||||
- 'schedule generique workflow_transition'
|
||||
- 'translate enregistrement node'
|
||||
- 'translate entrees taxonomy_term'
|
||||
- 'translate evenement node'
|
||||
- 'translate genres taxonomy_term'
|
||||
- 'translate langues taxonomy_term'
|
||||
- 'translate page node'
|
||||
- 'translate static node'
|
||||
- 'update content translations'
|
||||
|
||||
@@ -12,6 +12,7 @@ permissions:
|
||||
- 'access content'
|
||||
- 'access devel information'
|
||||
- 'access kint'
|
||||
- 'access synonyms entity autocomplete'
|
||||
- 'create corpus_documents workflow_transition'
|
||||
- 'create generique workflow_transition'
|
||||
- 'view published fil entities'
|
||||
|
||||
@@ -12,6 +12,7 @@ permissions:
|
||||
- 'access content'
|
||||
- 'access devel information'
|
||||
- 'access kint'
|
||||
- 'access synonyms entity autocomplete'
|
||||
- 'add composition entities'
|
||||
- 'add fil entities'
|
||||
- 'create corpus_documents workflow_transition'
|
||||
|
||||
@@ -80,6 +80,7 @@ permissions:
|
||||
- 'administer site configuration'
|
||||
- 'administer software updates'
|
||||
- 'administer studio'
|
||||
- 'administer synonyms'
|
||||
- 'administer taxonomy'
|
||||
- 'administer taxonomy_term display'
|
||||
- 'administer taxonomy_term fields'
|
||||
@@ -242,9 +243,12 @@ permissions:
|
||||
- 'synchronize configuration'
|
||||
- 'translate any entity'
|
||||
- 'translate enregistrement node'
|
||||
- 'translate entrees taxonomy_term'
|
||||
- 'translate evenement node'
|
||||
- 'translate fil node'
|
||||
- 'translate genres taxonomy_term'
|
||||
- 'translate interface'
|
||||
- 'translate langues taxonomy_term'
|
||||
- 'translate page node'
|
||||
- 'translate static node'
|
||||
- 'update content translations'
|
||||
|
||||
@@ -28,6 +28,8 @@ dependencies:
|
||||
- node.type.page
|
||||
- node.type.static
|
||||
- taxonomy.vocabulary.entrees
|
||||
- taxonomy.vocabulary.genres
|
||||
- taxonomy.vocabulary.langues
|
||||
- taxonomy.vocabulary.page_type
|
||||
- user.role.admin
|
||||
- user.role.collectionneur
|
||||
@@ -2172,6 +2174,104 @@ display:
|
||||
entity_type: node
|
||||
entity_field: langcode
|
||||
plugin_id: language
|
||||
field_genres_target_id:
|
||||
id: field_genres_target_id
|
||||
table: node__field_genres
|
||||
field: field_genres_target_id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: or
|
||||
value: { }
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: field_genres_target_id_op
|
||||
label: 'Genre(s) (field_genres)'
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: field_genres_target_id_op
|
||||
identifier: field_genres_target_id
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
collectionneur: '0'
|
||||
admin: '0'
|
||||
root: '0'
|
||||
invite: '0'
|
||||
user: '0'
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
reduce_duplicates: false
|
||||
type: textfield
|
||||
limit: true
|
||||
vid: genres
|
||||
hierarchy: false
|
||||
error_message: true
|
||||
plugin_id: taxonomy_index_tid
|
||||
field_langues_target_id:
|
||||
id: field_langues_target_id
|
||||
table: node__field_langues
|
||||
field: field_langues_target_id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: or
|
||||
value: { }
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: field_langues_target_id_op
|
||||
label: 'Langue(s) (field_langues)'
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: field_langues_target_id_op
|
||||
identifier: field_langues_target_id
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
collectionneur: '0'
|
||||
admin: '0'
|
||||
root: '0'
|
||||
invite: '0'
|
||||
user: '0'
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
reduce_duplicates: false
|
||||
type: textfield
|
||||
limit: true
|
||||
vid: langues
|
||||
hierarchy: false
|
||||
error_message: true
|
||||
plugin_id: taxonomy_index_tid
|
||||
defaults:
|
||||
filters: false
|
||||
filter_groups: false
|
||||
@@ -3101,6 +3201,7 @@ display:
|
||||
filters: false
|
||||
filter_groups: false
|
||||
fields: false
|
||||
title: false
|
||||
filters:
|
||||
status_extra:
|
||||
id: status_extra
|
||||
@@ -3655,6 +3756,7 @@ display:
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: changed
|
||||
title: Agenda
|
||||
display_plugin: page
|
||||
display_title: Agenda
|
||||
id: page_3
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,15 +6,15 @@ corpus_documents_double_fond:
|
||||
collectionneur:
|
||||
grant_view: true
|
||||
grant_update: true
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
admin:
|
||||
grant_view: true
|
||||
grant_update: true
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
root:
|
||||
grant_view: true
|
||||
grant_update: true
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
anonymous:
|
||||
grant_view: 0
|
||||
grant_update: 0
|
||||
@@ -35,11 +35,11 @@ corpus_documents_publie:
|
||||
admin:
|
||||
grant_view: 0
|
||||
grant_update: true
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
root:
|
||||
grant_view: 0
|
||||
grant_update: true
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
anonymous:
|
||||
grant_view: true
|
||||
grant_update: 0
|
||||
@@ -114,15 +114,15 @@ corpus_documents_sas:
|
||||
collectionneur:
|
||||
grant_view: true
|
||||
grant_update: 0
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
admin:
|
||||
grant_view: true
|
||||
grant_update: 0
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
root:
|
||||
grant_view: true
|
||||
grant_update: 0
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
anonymous:
|
||||
grant_view: 0
|
||||
grant_update: 0
|
||||
@@ -139,15 +139,15 @@ corpus_documents_masque:
|
||||
collectionneur:
|
||||
grant_view: true
|
||||
grant_update: true
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
admin:
|
||||
grant_view: true
|
||||
grant_update: true
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
root:
|
||||
grant_view: true
|
||||
grant_update: true
|
||||
grant_delete: 0
|
||||
grant_delete: true
|
||||
anonymous:
|
||||
grant_view: 0
|
||||
grant_update: 0
|
||||
|
||||
Reference in New Issue
Block a user