diff --git a/sites/all/modules/contrib/taxonomy/synonyms/LICENSE.txt b/sites/all/modules/contrib/taxonomy/synonyms/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/LICENSE.txt @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/sites/all/modules/contrib/taxonomy/synonyms/README.txt b/sites/all/modules/contrib/taxonomy/synonyms/README.txt new file mode 100644 index 000000000..32139520b --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/README.txt @@ -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) diff --git a/sites/all/modules/contrib/taxonomy/synonyms/config/schema/synonym.schema.yml b/sites/all/modules/contrib/taxonomy/synonyms/config/schema/synonym.schema.yml new file mode 100644 index 000000000..80d8d4453 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/config/schema/synonym.schema.yml @@ -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' diff --git a/sites/all/modules/contrib/taxonomy/synonyms/config/schema/synonyms.behavior.schema.yml b/sites/all/modules/contrib/taxonomy/synonyms/config/schema/synonyms.behavior.schema.yml new file mode 100644 index 000000000..7f58a26d5 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/config/schema/synonyms.behavior.schema.yml @@ -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' diff --git a/sites/all/modules/contrib/taxonomy/synonyms/config/schema/synonyms.provider.schema.yml b/sites/all/modules/contrib/taxonomy/synonyms/config/schema/synonyms.provider.schema.yml new file mode 100644 index 000000000..d3c4fd194 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/config/schema/synonyms.provider.schema.yml @@ -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: diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Annotation/SynonymsProvider.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Annotation/SynonymsProvider.php new file mode 100644 index 000000000..2d0136d2e --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Annotation/SynonymsProvider.php @@ -0,0 +1,56 @@ +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); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Controller/SynonymConfigController.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Controller/SynonymConfigController.php new file mode 100644 index 000000000..7e4290225 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Controller/SynonymConfigController.php @@ -0,0 +1,149 @@ +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'], + ]); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Element/SynonymsEntityAutocomplete.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Element/SynonymsEntityAutocomplete.php new file mode 100644 index 000000000..c3e75ae09 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Element/SynonymsEntityAutocomplete.php @@ -0,0 +1,148 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Element/SynonymsEntitySelect.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Element/SynonymsEntitySelect.php new file mode 100644 index 000000000..d8ea1b45f --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Element/SynonymsEntitySelect.php @@ -0,0 +1,161 @@ +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); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Entity/Synonym.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Entity/Synonym.php new file mode 100644 index 000000000..8f78a8887 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Entity/Synonym.php @@ -0,0 +1,235 @@ +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()); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Form/SynonymDeleteForm.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Form/SynonymDeleteForm.php new file mode 100644 index 000000000..081b5b117 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Form/SynonymDeleteForm.php @@ -0,0 +1,45 @@ +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()); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Form/SynonymForm.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Form/SynonymForm.php new file mode 100644 index 000000000..0954ed998 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Form/SynonymForm.php @@ -0,0 +1,292 @@ +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' => '
', + '#suffix' => '
', + ]; + + $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); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/ParamConverter/EntityTypeParamConverter.php b/sites/all/modules/contrib/taxonomy/synonyms/src/ParamConverter/EntityTypeParamConverter.php new file mode 100644 index 000000000..127fabb2c --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/ParamConverter/EntityTypeParamConverter.php @@ -0,0 +1,40 @@ +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'); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/ParamConverter/SynonymsBehaviorServiceParamConverter.php b/sites/all/modules/contrib/taxonomy/synonyms/src/ParamConverter/SynonymsBehaviorServiceParamConverter.php new file mode 100644 index 000000000..189ec5213 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/ParamConverter/SynonymsBehaviorServiceParamConverter.php @@ -0,0 +1,40 @@ +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'); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Derivative/EntityReferenceField.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Derivative/EntityReferenceField.php new file mode 100644 index 000000000..c09d132dd --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Derivative/EntityReferenceField.php @@ -0,0 +1,113 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Derivative/Field.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Derivative/Field.php new file mode 100644 index 000000000..8e15cdf1d --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Derivative/Field.php @@ -0,0 +1,133 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Derivative/ViewsSynonymsEntityArgumentValidator.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Derivative/ViewsSynonymsEntityArgumentValidator.php new file mode 100644 index 000000000..4062a5b88 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Derivative/ViewsSynonymsEntityArgumentValidator.php @@ -0,0 +1,78 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Field/FieldWidget/EntityReferenceSynonymsAutocomplete.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Field/FieldWidget/EntityReferenceSynonymsAutocomplete.php new file mode 100644 index 000000000..f99ad2814 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Field/FieldWidget/EntityReferenceSynonymsAutocomplete.php @@ -0,0 +1,157 @@ +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'), + ]; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Field/FieldWidget/EntityReferenceSynonymsSelect.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Field/FieldWidget/EntityReferenceSynonymsSelect.php new file mode 100644 index 000000000..3f46aadc4 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Field/FieldWidget/EntityReferenceSynonymsSelect.php @@ -0,0 +1,56 @@ +{$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]; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/AbstractProvider.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/AbstractProvider.php new file mode 100644 index 000000000..a1c701df4 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/AbstractProvider.php @@ -0,0 +1,58 @@ +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']); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/BaseField.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/BaseField.php new file mode 100644 index 000000000..ff7b9d554 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/BaseField.php @@ -0,0 +1,128 @@ +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()], + ]; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/EntityReferenceField.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/EntityReferenceField.php new file mode 100644 index 000000000..a566396e4 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/EntityReferenceField.php @@ -0,0 +1,191 @@ +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()], + ]; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/Field.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/Field.php new file mode 100644 index 000000000..72ce06891 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/Synonyms/Provider/Field.php @@ -0,0 +1,216 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/SynonymsFieldItemList.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/SynonymsFieldItemList.php new file mode 100644 index 000000000..ca9c192f7 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/SynonymsFieldItemList.php @@ -0,0 +1,33 @@ +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); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/views/argument_validator/SynonymsEntity.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/views/argument_validator/SynonymsEntity.php new file mode 100644 index 000000000..72d218fcf --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/views/argument_validator/SynonymsEntity.php @@ -0,0 +1,144 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/views/filter/SynonymsEntity.php b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/views/filter/SynonymsEntity.php new file mode 100644 index 000000000..2bae0db8d --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/Plugin/views/filter/SynonymsEntity.php @@ -0,0 +1,243 @@ +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']); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymInterface.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymInterface.php new file mode 100644 index 000000000..79d5c40db --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymInterface.php @@ -0,0 +1,72 @@ +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); + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsFindTrait.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsFindTrait.php new file mode 100644 index 000000000..3b43cc857 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsFindTrait.php @@ -0,0 +1,45 @@ +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']); + } + } + } + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsFormatWordingProviderInterface.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsFormatWordingProviderInterface.php new file mode 100644 index 000000000..65756fa28 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsFormatWordingProviderInterface.php @@ -0,0 +1,35 @@ + $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'), + ]; + } + +} + diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsGetProviderInterface.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsGetProviderInterface.php new file mode 100644 index 000000000..91881e48d --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsGetProviderInterface.php @@ -0,0 +1,45 @@ + $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 = []); + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsProviderInterface.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsProviderInterface.php new file mode 100644 index 000000000..dc297bdbd --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsProviderInterface/SynonymsProviderInterface.php @@ -0,0 +1,27 @@ +alterInfo('synonyms_provider_info'); + $this->setCacheBackend($cache_backend, 'synonyms_provider_info_plugins'); + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/Behavior/AutocompleteService.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/Behavior/AutocompleteService.php new file mode 100644 index 000000000..7a87602a6 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/Behavior/AutocompleteService.php @@ -0,0 +1,250 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/Behavior/SelectService.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/Behavior/SelectService.php new file mode 100644 index 000000000..ca04eb0e4 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/Behavior/SelectService.php @@ -0,0 +1,157 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/Behavior/SynonymsBehaviorConfigurableInterface.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/Behavior/SynonymsBehaviorConfigurableInterface.php new file mode 100644 index 000000000..f5fcc59bd --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/Behavior/SynonymsBehaviorConfigurableInterface.php @@ -0,0 +1,65 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/EntityGet.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/EntityGet.php new file mode 100644 index 000000000..0638d07bd --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/EntityGet.php @@ -0,0 +1,79 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/FieldTypeToSynonyms.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/FieldTypeToSynonyms.php new file mode 100644 index 000000000..a1708509f --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/FieldTypeToSynonyms.php @@ -0,0 +1,44 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/SynonymsFind.php b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/SynonymsFind.php new file mode 100644 index 000000000..2246f2e77 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/src/SynonymsService/SynonymsFind.php @@ -0,0 +1,84 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms.api.php b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.api.php new file mode 100644 index 000000000..388659292 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.api.php @@ -0,0 +1,30 @@ +=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 diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms.links.action.yml b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.links.action.yml new file mode 100644 index 000000000..00f6cb2b1 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.links.action.yml @@ -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 diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms.links.menu.yml b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.links.menu.yml new file mode 100644 index 000000000..da3f4ed76 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.links.menu.yml @@ -0,0 +1,5 @@ +synonyms.configure: + title: 'Synonyms' + route_name: entity.synonym.overview + description: 'Manage entity synonyms.' + parent: system.admin_structure diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms.module b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.module new file mode 100644 index 000000000..4e0298593 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.module @@ -0,0 +1,47 @@ +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; +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms.permissions.yml b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.permissions.yml new file mode 100644 index 000000000..ace7fe85c --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.permissions.yml @@ -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' diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms.routing.yml b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.routing.yml new file mode 100644 index 000000000..76bb331c9 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.routing.yml @@ -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' diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms.services.yml b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.services.yml new file mode 100644 index 000000000..fb5224816 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms.services.yml @@ -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' ] diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/src/SynonymsService/Behavior/SearchService.php b/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/src/SynonymsService/Behavior/SearchService.php new file mode 100644 index 000000000..bf52c1a5a --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/src/SynonymsService/Behavior/SearchService.php @@ -0,0 +1,194 @@ +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; + } + +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/synonyms_search.info.yml b/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/synonyms_search.info.yml new file mode 100644 index 000000000..c3be36439 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/synonyms_search.info.yml @@ -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 diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/synonyms_search.module b/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/synonyms_search.module new file mode 100644 index 000000000..1b4d6b698 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/synonyms_search.module @@ -0,0 +1,69 @@ +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()); + } +} diff --git a/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/synonyms_search.services.yml b/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/synonyms_search.services.yml new file mode 100644 index 000000000..6bd2d8674 --- /dev/null +++ b/sites/all/modules/contrib/taxonomy/synonyms/synonyms_search/synonyms_search.services.yml @@ -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 } diff --git a/sites/default/config/sync/core.base_field_override.node.autre_son.menu_link.yml b/sites/default/config/sync/core.base_field_override.node.autre_son.menu_link.yml new file mode 100644 index 000000000..74bf1626b --- /dev/null +++ b/sites/default/config/sync/core.base_field_override.node.autre_son.menu_link.yml @@ -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 diff --git a/sites/default/config/sync/core.base_field_override.node.fil.menu_link.yml b/sites/default/config/sync/core.base_field_override.node.fil.menu_link.yml new file mode 100644 index 000000000..1bf091e3a --- /dev/null +++ b/sites/default/config/sync/core.base_field_override.node.fil.menu_link.yml @@ -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 diff --git a/sites/default/config/sync/core.base_field_override.taxonomy_term.genres.name.yml b/sites/default/config/sync/core.base_field_override.taxonomy_term.genres.name.yml index 9378d9679..edc6361b8 100644 --- a/sites/default/config/sync/core.base_field_override.taxonomy_term.genres.name.yml +++ b/sites/default/config/sync/core.base_field_override.taxonomy_term.genres.name.yml @@ -11,7 +11,7 @@ bundle: genres label: Name description: '' required: true -translatable: false +translatable: true default_value: { } default_value_callback: '' settings: { } diff --git a/sites/default/config/sync/core.base_field_override.taxonomy_term.langues.name.yml b/sites/default/config/sync/core.base_field_override.taxonomy_term.langues.name.yml index fab29b735..1d6e39d5e 100644 --- a/sites/default/config/sync/core.base_field_override.taxonomy_term.langues.name.yml +++ b/sites/default/config/sync/core.base_field_override.taxonomy_term.langues.name.yml @@ -11,7 +11,7 @@ bundle: langues label: Name description: '' required: true -translatable: false +translatable: true default_value: { } default_value_callback: '' settings: { } diff --git a/sites/default/config/sync/core.base_field_override.user.user.path.yml b/sites/default/config/sync/core.base_field_override.user.user.path.yml new file mode 100644 index 000000000..0b3c18e22 --- /dev/null +++ b/sites/default/config/sync/core.base_field_override.user.user.path.yml @@ -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 diff --git a/sites/default/config/sync/core.entity_form_display.node.enregistrement.default.yml b/sites/default/config/sync/core.entity_form_display.node.enregistrement.default.yml index 22ab40cf4..d6a51d6ac 100644 --- a/sites/default/config/sync/core.entity_form_display.node.enregistrement.default.yml +++ b/sites/default/config/sync/core.entity_form_display.node.enregistrement.default.yml @@ -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 diff --git a/sites/default/config/sync/core.entity_form_display.taxonomy_term.genres.default.yml b/sites/default/config/sync/core.entity_form_display.taxonomy_term.genres.default.yml new file mode 100644 index 000000000..790570bcd --- /dev/null +++ b/sites/default/config/sync/core.entity_form_display.taxonomy_term.genres.default.yml @@ -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 diff --git a/sites/default/config/sync/core.entity_form_display.taxonomy_term.langues.default.yml b/sites/default/config/sync/core.entity_form_display.taxonomy_term.langues.default.yml new file mode 100644 index 000000000..2e0002d27 --- /dev/null +++ b/sites/default/config/sync/core.entity_form_display.taxonomy_term.langues.default.yml @@ -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 diff --git a/sites/default/config/sync/core.entity_view_display.taxonomy_term.genres.default.yml b/sites/default/config/sync/core.entity_view_display.taxonomy_term.genres.default.yml new file mode 100644 index 000000000..4583f85d4 --- /dev/null +++ b/sites/default/config/sync/core.entity_view_display.taxonomy_term.genres.default.yml @@ -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 diff --git a/sites/default/config/sync/core.extension.yml b/sites/default/config/sync/core.extension.yml index 40d3ce8bb..5b92bac32 100644 --- a/sites/default/config/sync/core.extension.yml +++ b/sites/default/config/sync/core.extension.yml @@ -71,6 +71,7 @@ module: responsive_image: 0 search_api: 0 search_api_db: 0 + synonyms: 0 system: 0 taxonomy: 0 text: 0 diff --git a/sites/default/config/sync/field.field.node.static.field_visuel.yml b/sites/default/config/sync/field.field.node.static.field_visuel.yml index 3bde475e6..e4bbe518b 100644 --- a/sites/default/config/sync/field.field.node.static.field_visuel.yml +++ b/sites/default/config/sync/field.field.node.static.field_visuel.yml @@ -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 diff --git a/sites/default/config/sync/field.field.taxonomy_term.genres.field_synonyms.yml b/sites/default/config/sync/field.field.taxonomy_term.genres.field_synonyms.yml new file mode 100644 index 000000000..e4a830bde --- /dev/null +++ b/sites/default/config/sync/field.field.taxonomy_term.genres.field_synonyms.yml @@ -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 diff --git a/sites/default/config/sync/field.storage.taxonomy_term.field_synonyms.yml b/sites/default/config/sync/field.storage.taxonomy_term.field_synonyms.yml new file mode 100644 index 000000000..5ceccd23f --- /dev/null +++ b/sites/default/config/sync/field.storage.taxonomy_term.field_synonyms.yml @@ -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 diff --git a/sites/default/config/sync/language.content_settings.taxonomy_term.genres.yml b/sites/default/config/sync/language.content_settings.taxonomy_term.genres.yml index 48d53ce4b..ddb446af2 100644 --- a/sites/default/config/sync/language.content_settings.taxonomy_term.genres.yml +++ b/sites/default/config/sync/language.content_settings.taxonomy_term.genres.yml @@ -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 diff --git a/sites/default/config/sync/language.content_settings.taxonomy_term.langues.yml b/sites/default/config/sync/language.content_settings.taxonomy_term.langues.yml index 9c4bf35ad..42ded3f60 100644 --- a/sites/default/config/sync/language.content_settings.taxonomy_term.langues.yml +++ b/sites/default/config/sync/language.content_settings.taxonomy_term.langues.yml @@ -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 diff --git a/sites/default/config/sync/language/en/system.action.pathauto_update_alias_node.yml b/sites/default/config/sync/language/en/system.action.pathauto_update_alias_node.yml new file mode 100644 index 000000000..66d81861d --- /dev/null +++ b/sites/default/config/sync/language/en/system.action.pathauto_update_alias_node.yml @@ -0,0 +1 @@ +label: 'Update URL alias' diff --git a/sites/default/config/sync/language/en/system.action.pathauto_update_alias_user.yml b/sites/default/config/sync/language/en/system.action.pathauto_update_alias_user.yml new file mode 100644 index 000000000..66d81861d --- /dev/null +++ b/sites/default/config/sync/language/en/system.action.pathauto_update_alias_user.yml @@ -0,0 +1 @@ +label: 'Update URL alias' diff --git a/sites/default/config/sync/synonyms.synonym.field.synonyms.behavior.autocomplete_taxonomy_term_genres_field_synonyms.yml b/sites/default/config/sync/synonyms.synonym.field.synonyms.behavior.autocomplete_taxonomy_term_genres_field_synonyms.yml new file mode 100644 index 000000000..86401b86c --- /dev/null +++ b/sites/default/config/sync/synonyms.synonym.field.synonyms.behavior.autocomplete_taxonomy_term_genres_field_synonyms.yml @@ -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)' diff --git a/sites/default/config/sync/user.role.admin.yml b/sites/default/config/sync/user.role.admin.yml index e8e8fb0f5..c48b1601f 100644 --- a/sites/default/config/sync/user.role.admin.yml +++ b/sites/default/config/sync/user.role.admin.yml @@ -77,6 +77,8 @@ permissions: - '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' diff --git a/sites/default/config/sync/user.role.anonymous.yml b/sites/default/config/sync/user.role.anonymous.yml index a8c782a10..0343fa6ac 100644 --- a/sites/default/config/sync/user.role.anonymous.yml +++ b/sites/default/config/sync/user.role.anonymous.yml @@ -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' diff --git a/sites/default/config/sync/user.role.authenticated.yml b/sites/default/config/sync/user.role.authenticated.yml index 4de88227b..4d136dc37 100644 --- a/sites/default/config/sync/user.role.authenticated.yml +++ b/sites/default/config/sync/user.role.authenticated.yml @@ -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' diff --git a/sites/default/config/sync/user.role.root.yml b/sites/default/config/sync/user.role.root.yml index 3f25c94c9..b73059a02 100644 --- a/sites/default/config/sync/user.role.root.yml +++ b/sites/default/config/sync/user.role.root.yml @@ -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' @@ -245,7 +246,9 @@ permissions: - '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' diff --git a/sites/default/config/sync/views.view.content.yml b/sites/default/config/sync/views.view.content.yml index 94397dca8..70f14c2ec 100644 --- a/sites/default/config/sync/views.view.content.yml +++ b/sites/default/config/sync/views.view.content.yml @@ -979,7 +979,7 @@ display: path: admin/content/production menu: type: tab - title: Production + title: Content description: '' expanded: false parent: 'menu_link_content:8b5fed48-c008-4041-9bda-06f997582175' diff --git a/sites/default/config/sync/views.view.entree_s_.yml b/sites/default/config/sync/views.view.entree_s_.yml index b06cfce1a..a772d224c 100644 --- a/sites/default/config/sync/views.view.entree_s_.yml +++ b/sites/default/config/sync/views.view.entree_s_.yml @@ -5,8 +5,11 @@ dependencies: config: - field.storage.taxonomy_term.field_color - field.storage.taxonomy_term.field_notice + - field.storage.taxonomy_term.field_synonyms - field.storage.taxonomy_term.field_workflow - taxonomy.vocabulary.entrees + - taxonomy.vocabulary.genres + - taxonomy.vocabulary.langues - user.role.admin - user.role.collectionneur - user.role.root @@ -18,7 +21,7 @@ dependencies: - user - workflow id: entree_s_ -label: Entrée(s) +label: 'Admin Taxo' module: views description: '' tag: '' @@ -90,21 +93,10 @@ display: summary: '' description: '' columns: - field_color: field_color name: name - field_workflow: name - edit_taxonomy_term: name - operations: name - description__value: description__value - field_notice: field_notice + field_synonyms: field_synonyms + operations: operations info: - field_color: - sortable: false - default_sort_order: asc - align: '' - separator: '' - empty_column: false - responsive: '' name: sortable: false default_sort_order: asc @@ -112,16 +104,7 @@ display: separator: '
' empty_column: false responsive: '' - field_workflow: - sortable: false - default_sort_order: asc - align: '' - separator: '' - empty_column: false - responsive: '' - edit_taxonomy_term: - sortable: false - default_sort_order: asc + field_synonyms: align: '' separator: '' empty_column: false @@ -131,18 +114,6 @@ display: separator: '' empty_column: false responsive: '' - description__value: - align: '' - separator: '' - empty_column: false - responsive: '' - field_notice: - sortable: false - default_sort_order: asc - align: '' - separator: '' - empty_column: false - responsive: '' default: '-1' empty_table: false row: @@ -507,12 +478,42 @@ display: id: vid table: taxonomy_term_field_data field: vid + relationship: none + group_type: group + admin_label: '' + operator: in value: - entrees: entrees + genres: genres + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + 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: { } entity_type: taxonomy_term entity_field: vid plugin_id: bundle - group: 1 field_workflow_value: id: field_workflow_value table: taxonomy_term__field_workflow @@ -642,7 +643,7 @@ display: entity_field: langcode plugin_id: language sorts: { } - title: Entrée(s) + title: Langues header: result: id: result @@ -678,7 +679,7 @@ display: page_1: display_plugin: page id: page_1 - display_title: Page + display_title: Entrees position: 1 display_options: display_extenders: { } @@ -693,6 +694,183 @@ display: context: '0' menu_name: admin rendering_language: '***LANGUAGE_entity_translation***' + display_description: '' + filters: + vid: + id: vid + table: taxonomy_term_field_data + field: vid + relationship: none + group_type: group + admin_label: '' + operator: in + value: + entrees: entrees + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + 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: { } + entity_type: taxonomy_term + entity_field: vid + plugin_id: bundle + field_workflow_value: + id: field_workflow_value + table: taxonomy_term__field_workflow + field: field_workflow_value + relationship: none + group_type: group + admin_label: '' + operator: or + value: { } + group: 1 + exposed: true + expose: + operator_id: field_workflow_value_op + label: Workflow + description: '' + use_operator: false + operator: field_workflow_value_op + identifier: field_workflow_value + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + collectionneur: '0' + admin: '0' + root: '0' + invite: '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 + plugin_id: workflow_state + name: + id: name + table: taxonomy_term_field_data + field: name + relationship: none + group_type: group + admin_label: '' + operator: '=' + value: '' + group: 1 + exposed: true + expose: + operator_id: name_op + label: Name + description: '' + use_operator: false + operator: name_op + identifier: name + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + collectionneur: '0' + admin: '0' + root: '0' + invite: '0' + placeholder: '' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: taxonomy_term + entity_field: name + plugin_id: string + langcode: + id: langcode + table: taxonomy_term_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + operator: in + value: + '***LANGUAGE_language_interface***': '***LANGUAGE_language_interface***' + und: und + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + 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: { } + entity_type: taxonomy_term + entity_field: langcode + plugin_id: language + defaults: + filters: false + filter_groups: false + filter_groups: + operator: AND + groups: + 1: AND cache_metadata: max-age: -1 contexts: @@ -705,3 +883,636 @@ display: - 'config:field.storage.taxonomy_term.field_color' - 'config:field.storage.taxonomy_term.field_notice' - 'config:field.storage.taxonomy_term.field_workflow' + page_2: + display_plugin: page + id: page_2 + display_title: Genres + position: 1 + display_options: + display_extenders: { } + path: admin/content/genres + menu: + type: tab + title: Genres + description: '' + expanded: false + parent: 'menu_link_content:8b5fed48-c008-4041-9bda-06f997582175' + weight: 200 + context: '0' + menu_name: admin + rendering_language: '***LANGUAGE_entity_translation***' + display_description: '' + fields: + name: + id: name + table: taxonomy_term_field_data + field: name + entity_type: taxonomy_term + entity_field: name + alter: + alter_text: false + make_link: false + absolute: false + trim: false + word_boundary: false + ellipsis: false + strip_tags: false + html: false + hide_empty: false + empty_zero: false + type: string + settings: + link_to_entity: true + plugin_id: term_name + relationship: none + group_type: group + admin_label: '' + label: Name + exclude: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_alter_empty: true + click_sort_column: value + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + convert_spaces: false + field_synonyms: + id: field_synonyms + table: taxonomy_term__field_synonyms + field: field_synonyms + relationship: none + group_type: group + admin_label: '' + label: Synonyms + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: false + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + plugin_id: field + operations: + id: operations + table: taxonomy_term_data + field: operations + relationship: none + group_type: group + admin_label: '' + label: 'Liens d''actions' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: true + entity_type: taxonomy_term + plugin_id: entity_operations + defaults: + fields: false + filters: false + filter_groups: false + filters: + vid: + id: vid + table: taxonomy_term_field_data + field: vid + relationship: none + group_type: group + admin_label: '' + operator: in + value: + genres: genres + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + 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: { } + entity_type: taxonomy_term + entity_field: vid + plugin_id: bundle + name: + id: name + table: taxonomy_term_field_data + field: name + relationship: none + group_type: group + admin_label: '' + operator: '=' + value: '' + group: 1 + exposed: true + expose: + operator_id: name_op + label: Name + description: '' + use_operator: false + operator: name_op + identifier: name + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + collectionneur: '0' + admin: '0' + root: '0' + invite: '0' + user: '0' + placeholder: '' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: taxonomy_term + entity_field: name + plugin_id: string + langcode: + id: langcode + table: taxonomy_term_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + operator: in + value: + '***LANGUAGE_language_interface***': '***LANGUAGE_language_interface***' + und: und + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + 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: { } + entity_type: taxonomy_term + entity_field: langcode + plugin_id: language + field_synonyms_value: + id: field_synonyms_value + table: taxonomy_term__field_synonyms + field: field_synonyms_value + relationship: none + group_type: group + admin_label: '' + operator: contains + value: '' + group: 1 + exposed: true + expose: + operator_id: field_synonyms_value_op + label: 'Synonyms (field_synonyms)' + description: '' + use_operator: false + operator: field_synonyms_value_op + identifier: field_synonyms_value + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + collectionneur: '0' + admin: '0' + root: '0' + invite: '0' + user: '0' + placeholder: '' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + plugin_id: string + filter_groups: + operator: AND + groups: + 1: AND + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.roles + tags: + - 'config:field.storage.taxonomy_term.field_synonyms' + page_3: + display_plugin: page + id: page_3 + display_title: Langues + position: 1 + display_options: + display_extenders: { } + path: admin/content/langues + menu: + type: tab + title: Langues + description: '' + expanded: false + parent: 'menu_link_content:8b5fed48-c008-4041-9bda-06f997582175' + weight: 201 + context: '0' + menu_name: admin + rendering_language: '***LANGUAGE_entity_translation***' + display_description: '' + fields: + name: + id: name + table: taxonomy_term_field_data + field: name + entity_type: taxonomy_term + entity_field: name + alter: + alter_text: false + make_link: false + absolute: false + trim: false + word_boundary: false + ellipsis: false + strip_tags: false + html: false + hide_empty: false + empty_zero: false + type: string + settings: + link_to_entity: true + plugin_id: term_name + relationship: none + group_type: group + admin_label: '' + label: Name + exclude: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_alter_empty: true + click_sort_column: value + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + convert_spaces: false + operations: + id: operations + table: taxonomy_term_data + field: operations + relationship: none + group_type: group + admin_label: '' + label: 'Liens d''actions' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: true + entity_type: taxonomy_term + plugin_id: entity_operations + defaults: + fields: false + filters: false + filter_groups: false + filters: + vid: + id: vid + table: taxonomy_term_field_data + field: vid + relationship: none + group_type: group + admin_label: '' + operator: in + value: + langues: langues + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + 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: { } + entity_type: taxonomy_term + entity_field: vid + plugin_id: bundle + name: + id: name + table: taxonomy_term_field_data + field: name + relationship: none + group_type: group + admin_label: '' + operator: '=' + value: '' + group: 1 + exposed: true + expose: + operator_id: name_op + label: Name + description: '' + use_operator: false + operator: name_op + identifier: name + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + collectionneur: '0' + admin: '0' + root: '0' + invite: '0' + user: '0' + placeholder: '' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: taxonomy_term + entity_field: name + plugin_id: string + langcode: + id: langcode + table: taxonomy_term_field_data + field: langcode + relationship: none + group_type: group + admin_label: '' + operator: in + value: + '***LANGUAGE_language_interface***': '***LANGUAGE_language_interface***' + und: und + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + 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: { } + entity_type: taxonomy_term + entity_field: langcode + plugin_id: language + filter_groups: + operator: AND + groups: + 1: AND + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.roles + tags: { }