diff --git a/sites/all/modules/contrib/fields/linkit/LICENSE.txt b/sites/all/modules/contrib/fields/linkit/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/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/fields/linkit/README.md b/sites/all/modules/contrib/fields/linkit/README.md new file mode 100644 index 000000000..7f610ba71 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/README.md @@ -0,0 +1,33 @@ +Linkit +=========== +Linkit provides an **enriched linking experience for internal and external +linking** with editors by using an autocomplete field. Linkit has by default +support for nodes, users, taxonomy terms, files, comments and +**basic support for all types of entities** that defines a canonical link +template. + + +Installation +------------ + +* Normal module installation procedure. See + https://www.drupal.org/documentation/install/modules-themes/modules-8 + + +Configuration +------------ + +After the installation, you have to create a Linkit profile. The profile will +contain information about which plugins to use. +Profiles can be created at `/admin/config/content/linkit` + +When you have created a profile, you need to enable the Linkit plugin on the +text format you want to use. Formats are found at +`admin/config/content/formats`. + + +Plugins examples +------------ + +There are plugin implementation examples in the linkit_test module bundled with +Linkit core. diff --git a/sites/all/modules/contrib/fields/linkit/composer.json b/sites/all/modules/contrib/fields/linkit/composer.json new file mode 100644 index 000000000..c922ba235 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/composer.json @@ -0,0 +1,19 @@ +{ + "name": "drupal/linkit", + "description": "Linkit - Enriched linking experience", + "type": "drupal-module", + "homepage": "http://drupal.org/project/linkit", + "authors": [ + { + "name": "Emil Stjerneman", + "email": "emil@stjerneman.com", + "homepage": "https://stjerneman.com", + "role": "Maintainer" + } + ], + "support": { + "issues": "http://drupal.org/project/linkit", + "source": "http://cgit.drupalcode.org/linkit" + }, + "license": "GPL-2.0+" +} diff --git a/sites/all/modules/contrib/fields/linkit/config/optional/image.style.linkit_result_thumbnail.yml b/sites/all/modules/contrib/fields/linkit/config/optional/image.style.linkit_result_thumbnail.yml new file mode 100644 index 000000000..69792fe03 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/config/optional/image.style.linkit_result_thumbnail.yml @@ -0,0 +1,13 @@ +langcode: en +status: true +dependencies: { } +name: linkit_result_thumbnail +label: 'Linkit result thumbnail' +effects: + 2943df29-38ea-459c-ba1d-290489bb1807: + uuid: 2943df29-38ea-459c-ba1d-290489bb1807 + id: image_scale_and_crop + weight: 1 + data: + width: 50 + height: 50 diff --git a/sites/all/modules/contrib/fields/linkit/config/schema/linkit.schema.yml b/sites/all/modules/contrib/fields/linkit/config/schema/linkit.schema.yml new file mode 100644 index 000000000..1e63f0262 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/config/schema/linkit.schema.yml @@ -0,0 +1,131 @@ +# Schema for the configuration files of the Linkit module. + +linkit.linkit_profile.*: + type: config_entity + mapping: + label: + type: label + label: 'Name' + id: + type: string + description: + type: string + attributes: + type: sequence + sequence: + type: linkit.attribute + matchers: + type: sequence + sequence: + type: linkit.matcher + +linkit.attribute: + type: mapping + mapping: + id: + type: string + settings: + type: linkit.attribute.[%parent.id] + weight: + type: integer + +linkit.attribute.*: + type: mapping + label: 'Attribute settings' + +# Plugin \Drupal\linkit\Plugin\Linkit\Attribute\Target +linkit.attribute.target: + type: linkit.attribute + mapping: + widget_type: + type: string + +# Plugin \Drupal\linkit\Plugin\Linkit\Attribute\Title +linkit.attribute.title: + type: linkit.attribute + mapping: + automatic_title: + type: boolean + +linkit.matcher: + type: mapping + mapping: + id: + type: string + uuid: + type: string + settings: + type: linkit.matcher.[%parent.id] + weight: + type: integer + +linkit.matcher.*: + type: mapping + label: 'Matcher settings' + +linkit.matcher.entity: + type: mapping + mapping: + result_description: + type: string + bundles: + type: sequence + sequence: + type: string + nullable: true + group_by_bundle: + type: boolean + +linkit.matcher.entity:*: + type: linkit.matcher.entity + +# Plugin \Drupal\linkit\Plugin\Linkit\Matcher\FileMatcher +linkit.matcher.entity:file: + type: linkit.matcher.entity + mapping: + images: + type: mapping + mapping: + show_dimensions: + type: boolean + show_thumbnail: + type: boolean + thumbnail_image_style: + type: string + nullable: true + +# Plugin \Drupal\linkit\Plugin\Linkit\Matcher\NodeMatcher +linkit.matcher.entity:node: + type: linkit.matcher.entity + mapping: + include_unpublished: + type: boolean + +# Plugin \Drupal\linkit\Plugin\Linkit\Matcher\UserMatcher +linkit.matcher.entity:user: + type: linkit.matcher.entity + mapping: + roles: + type: sequence + sequence: + type: string + nullable: true + include_blocked: + type: boolean + +# Third party settings on behalf of the imce module. +linkit.linkit_profile.*.third_party.imce: + type: mapping + mapping: + use: + type: boolean + scheme: + type: string + +ckeditor.plugin.linkit: + type: mapping + label: 'Profile dropdown' + mapping: + linkit_profile: + type: string + label: 'List of profiles' diff --git a/sites/all/modules/contrib/fields/linkit/css/linkit.admin.theme.css b/sites/all/modules/contrib/fields/linkit/css/linkit.admin.theme.css new file mode 100644 index 000000000..c4afd11b9 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/css/linkit.admin.theme.css @@ -0,0 +1,15 @@ +/** + * @file + * Stylesheet for the Linkit module admin GUI. + */ + +.linkit-plugin-summary { + float: left; + font-size: .9em; + line-height: 1.2em; +} + +.linkit-matcher-add-form tbody td:first-child, +.linkit-attribute-add-form tbody td:first-child { + width: 1%; +} diff --git a/sites/all/modules/contrib/fields/linkit/css/linkit.autocomplete.css b/sites/all/modules/contrib/fields/linkit/css/linkit.autocomplete.css new file mode 100644 index 000000000..806e98a2e --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/css/linkit.autocomplete.css @@ -0,0 +1,69 @@ +/** + * @file + * Stylesheet for the Linkit module. + */ +.js input.form-linkit-autocomplete { + background-image: url(/core/misc/throbber-inactive.png); + background-position: 100% center; /* LTR */ + background-repeat: no-repeat; +} +.js[dir="rtl"] input.form-linkit-autocomplete { + background-position: 0% center; +} +.js input.form-linkit-autocomplete.ui-autocomplete-loading { + background-image: url(/core/misc/throbber-active.gif); + background-position: 100% center; /* LTR */ +} +.js[dir="rtl"] input.form-linkit-autocomplete.ui-autocomplete-loading { + background-position: 0% center; +} + +/* Override the default jQuery UI theme */ +.linkit-ui-autocomplete { + max-height: 330px; + overflow: auto; +} +.linkit-ui-autocomplete.ui-widget { + font-size: .9em; +} + +.linkit-ui-autocomplete.ui-menu .ui-menu-item { + list-style: none; + padding: 5px 7px; +} + +.linkit-ui-autocomplete .ui-menu-item.ui-state-focus { + margin: 0; + border: 0; + border-bottom: 1px solid #bfbfbf; + background: #0075ba; + color: #fff; +} + +.linkit-result:not(:last-of-type) { + border-bottom: 1px solid #bfbfbf; +} + +.linkit-result--group { + padding: 3px; + background-color: #e7e7e7; + border-bottom: 1px solid #bfbfbf; + text-align: center; + font-size: 0.9em; + font-weight: 600; + color: #555; +} + +.linkit-result--title { + font-weight: 600; +} + +.linkit-result--description { + display: block; + font-size: 0.9em; + line-height: 1.3; +} + +.linkit-result--description img { + display: block; +} diff --git a/sites/all/modules/contrib/fields/linkit/js/attribute/title.js b/sites/all/modules/contrib/fields/linkit/js/attribute/title.js new file mode 100644 index 000000000..a2bb7a134 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/js/attribute/title.js @@ -0,0 +1,21 @@ +/** + * @file + * Title attribute functions. + */ + +(function ($, Drupal, document) { + + 'use strict'; + + var fieldName = '[name="attributes[title]"]'; + + /** + * Automatically populate the title attribute. + */ + $(document).bind('linkit.autocomplete.select', function (triggerEvent, event, ui) { + if (ui.item.hasOwnProperty('title')) { + $('form.linkit-editor-dialog-form').find(fieldName).val(ui.item.title); + } + }); + +})(jQuery, Drupal, document); diff --git a/sites/all/modules/contrib/fields/linkit/js/autocomplete.js b/sites/all/modules/contrib/fields/linkit/js/autocomplete.js new file mode 100644 index 000000000..34e6e11a8 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/js/autocomplete.js @@ -0,0 +1,167 @@ +/** + * @file + * Linkit Autocomplete based on jQuery UI. + */ + +(function ($, Drupal, _, document) { + + 'use strict'; + + var autocomplete; + + /** + * JQuery UI autocomplete source callback. + * + * @param {object} request + * @param {function} response + */ + function sourceData(request, response) { + var elementId = this.element.attr('id'); + + if (!(elementId in autocomplete.cache)) { + autocomplete.cache[elementId] = {}; + } + + /** + * @param {object} suggestions + */ + function showSuggestions(suggestions) { + response(suggestions.matches); + } + + /** + * Transforms the data object into an array and update autocomplete results. + * + * @param {object} data + */ + function sourceCallbackHandler(data) { + autocomplete.cache[elementId][term] = data; + showSuggestions(data); + } + + // Get the desired term and construct the autocomplete URL for it. + var term = request.term; + + // Check if the term is already cached. + if (autocomplete.cache[elementId].hasOwnProperty(term)) { + showSuggestions(autocomplete.cache[elementId][term]); + } + else { + var options = $.extend({success: sourceCallbackHandler, data: {q: term}}, autocomplete.ajax); + $.ajax(this.element.attr('data-autocomplete-path'), options); + } + } + + /** + * Handles an autocomplete select event. + * + * @param {jQuery.Event} event + * @param {object} ui + * + * @return {boolean} + */ + function selectHandler(event, ui) { + if (ui.item.hasOwnProperty('path')) { + event.target.value = ui.item.path; + } + $(document).trigger('linkit.autocomplete.select', [event, ui]); + return false; + } + + /** + * Override jQuery UI _renderItem function to output HTML by default. + * + * @param {object} ul + * The
    element that the newly created
  • element must be appended to. + * @param {object} item + * + * @return {object} + */ + function renderItem(ul, item) { + var $line = $('
  • ').addClass('linkit-result'); + $line.append($('').html(item.title).addClass('linkit-result--title')); + + if (item.description !== null) { + $line.append($('').html(item.description).addClass('linkit-result--description')); + } + + return $line.appendTo(ul); + } + + /** + * Override jQuery UI _renderMenu function to handle groups. + * + * @param {object} ul + * An empty
      element to use as the widget's menu. + * @param {array} items + * An Array of items that match the user typed term. + */ + function renderMenu(ul, items) { + var self = this.element.autocomplete('instance'); + + var grouped_items = _.groupBy(items, function (item) { + return item.hasOwnProperty('group') ? item.group : ''; + }); + + $.each(grouped_items, function (group, items) { + if (group.length) { + ul.append('
    • ' + group + '
    • '); + } + + $.each(items, function (index, item) { + self._renderItemData(ul, item); + }); + }); + } + + /** + * Attaches the autocomplete behavior to all required fields. + * + * @type {Drupal~behavior} + */ + Drupal.behaviors.linkit_autocomplete = { + attach: function (context) { + // Act on textfields with the "form-autocomplete" class. + var $autocomplete = $(context).find('input.form-linkit-autocomplete').once('linkit-autocomplete'); + if ($autocomplete.length) { + $.widget('custom.autocomplete', $.ui.autocomplete, { + _create: function () { + this._super(); + this.widget().menu('option', 'items', '> :not(.linkit-result--group)'); + }, + _renderMenu: autocomplete.options.renderMenu, + _renderItem: autocomplete.options.renderItem + }); + + // Use jQuery UI Autocomplete on the textfield. + $autocomplete.autocomplete(autocomplete.options); + $autocomplete.autocomplete('widget').addClass('linkit-ui-autocomplete'); + } + }, + detach: function (context, settings, trigger) { + if (trigger === 'unload') { + $(context).find('input.form-linkit-autocomplete') + .removeOnce('linkit-autocomplete') + .autocomplete('destroy'); + } + } + }; + + /** + * Autocomplete object implementation. + */ + autocomplete = { + cache: {}, + options: { + source: sourceData, + renderItem: renderItem, + renderMenu: renderMenu, + select: selectHandler, + minLength: 1 + }, + ajax: { + dataType: 'json' + } + }; + +})(jQuery, Drupal, _, document); diff --git a/sites/all/modules/contrib/fields/linkit/js/linkit.imce.js b/sites/all/modules/contrib/fields/linkit/js/linkit.imce.js new file mode 100644 index 000000000..3682483de --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/js/linkit.imce.js @@ -0,0 +1,52 @@ +/** + * @file + * IMCE integration for Linkit. + */ + +(function ($, Drupal, drupalSettings) { + + 'use strict'; + + /** + * @namespace + * + * Need to be in the global namespace, otherwise the IMCE window will not show + * the 'select' button in the toolbar. + */ + var linkitImce = window.linkitImce = {}; + + /** + * Drupal behavior to handle imce linkit integration. + */ + Drupal.behaviors.linkitImce = { + attach: function (context, settings) { + var $link = $(context).find('.linkit-imce-open').once('linkit-imce-open'); + if ($link.length) { + $link.bind('click', function (event) { + event.preventDefault(); + window.open($(this).attr('href'), '', 'width=760,height=560,resizable=1'); + }); + } + } + }; + + /** + * Handler for imce sendto operation. + */ + linkitImce.sendto = function (file, win) { + var imce = win.imce; + var items = imce.getSelection(); + + if (imce.countSelection() > 1) { + imce.setMessage(Drupal.t('You can only select one file.')); + return; + } + var path = imce.getConf('root_url') + '/' + imce.getItemPath(items[0]); + $('[data-drupal-selector="edit-attributes-href"]').val(path); + win.close(); + }; + + +})(jQuery, Drupal, drupalSettings); + + diff --git a/sites/all/modules/contrib/fields/linkit/js/linkit.js b/sites/all/modules/contrib/fields/linkit/js/linkit.js new file mode 100644 index 000000000..0e5a051a6 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/js/linkit.js @@ -0,0 +1,15 @@ +/** + * @file + * Some basic behaviors and utility functions for Linkit. + */ + +(function ($, Drupal, drupalSettings) { + + 'use strict'; + + /** + * @namespace + */ + Drupal.linkit = {}; + +})(jQuery, Drupal, drupalSettings); diff --git a/sites/all/modules/contrib/fields/linkit/js/plugins/linkit/linkit.png b/sites/all/modules/contrib/fields/linkit/js/plugins/linkit/linkit.png new file mode 100644 index 000000000..90fc72ee7 Binary files /dev/null and b/sites/all/modules/contrib/fields/linkit/js/plugins/linkit/linkit.png differ diff --git a/sites/all/modules/contrib/fields/linkit/js/plugins/linkit/plugin.js b/sites/all/modules/contrib/fields/linkit/js/plugins/linkit/plugin.js new file mode 100644 index 000000000..e3fd0ce02 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/js/plugins/linkit/plugin.js @@ -0,0 +1,223 @@ +/** + * @file + * Linkit plugin. + * + * @ignore + */ + +(function ($, Drupal, drupalSettings, CKEDITOR) { + + 'use strict'; + + // Alter the dialog settings to make a bigger dialog. + // $(window).on('dialog:beforecreate', function (event, dialog, $element, settings) { + // settings.dialogClass = settings.dialogClass.replace('ui-dialog--narrow', ''); + // settings.width = 700; + // }); + + CKEDITOR.plugins.add('linkit', { + init: function (editor) { + // Add the commands for link and unlink. + editor.addCommand('linkit', { + allowedContent: new CKEDITOR.style({ + element: 'a', + attributes: { + '!href': '', + // @TODO: Read these dynamically from the profile. + 'accesskey': '', + 'id': '', + 'rel': '', + 'target': '', + 'title': '' + } + }), + requiredContent: new CKEDITOR.style({ + element: 'a', + attributes: { + href: '' + } + }), + modes: {wysiwyg: 1}, + canUndo: true, + exec: function (editor) { + var linkElement = getSelectedLink(editor); + var linkDOMElement = null; + + // Set existing values based on selected element. + var existingValues = {}; + if (linkElement && linkElement.$) { + linkDOMElement = linkElement.$; + + // Populate an array with the link's current attributes. + var attribute = null; + var attributeName; + for (var attrIndex = 0; attrIndex < linkDOMElement.attributes.length; attrIndex++) { + attribute = linkDOMElement.attributes.item(attrIndex); + attributeName = attribute.nodeName.toLowerCase(); + // Don't consider data-cke-saved- attributes; they're just there + // to work around browser quirks. + if (attributeName.substring(0, 15) === 'data-cke-saved-') { + continue; + } + // Store the value for this attribute, unless there's a + // data-cke-saved- alternative for it, which will contain the + // quirk-free, original value. + existingValues[attributeName] = linkElement.data('cke-saved-' + attributeName) || attribute.nodeValue; + } + } + + // Prepare a save callback to be used upon saving the dialog. + var saveCallback = function (returnValues) { + editor.fire('saveSnapshot'); + + // Create a new link element if needed. + if (!linkElement && returnValues.attributes.href) { + var selection = editor.getSelection(); + var range = selection.getRanges(1)[0]; + + // Use link URL as text with a collapsed cursor. + if (range.collapsed) { + // Shorten mailto URLs to just the email address. + var text = new CKEDITOR.dom.text(returnValues.attributes.href.replace(/^mailto:/, ''), editor.document); + range.insertNode(text); + range.selectNodeContents(text); + } + + // Create the new link by applying a style to the new text. + var style = new CKEDITOR.style({element: 'a', attributes: returnValues.attributes}); + style.type = CKEDITOR.STYLE_INLINE; + style.applyToRange(range); + range.select(); + + // Set the link so individual properties may be set below. + linkElement = getSelectedLink(editor); + } + // Update the link properties. + else if (linkElement) { + for (var attrName in returnValues.attributes) { + if (returnValues.attributes.hasOwnProperty(attrName)) { + // Update the property if a value is specified. + if (returnValues.attributes[attrName].length > 0) { + var value = returnValues.attributes[attrName]; + linkElement.data('cke-saved-' + attrName, value); + linkElement.setAttribute(attrName, value); + } + // Delete the property if set to an empty string. + else { + linkElement.removeAttribute(attrName); + } + } + } + } + + // Save snapshot for undo support. + editor.fire('saveSnapshot'); + }; + // Drupal.t() will not work inside CKEditor plugins because CKEditor + // loads the JavaScript file instead of Drupal. Pull translated + // strings from the plugin settings that are translated server-side. + var dialogSettings = { + title: linkElement ? editor.config.linkit_dialogTitleAdd : editor.config.linkit_dialogTitleEdit, + dialogClass: 'editor-linkit-dialog' + }; + + // Open the dialog for the edit form. + Drupal.ckeditor.openDialog(editor, Drupal.url('linkit/dialog/linkit/' + editor.config.drupal.format), existingValues, saveCallback, dialogSettings); + } + }); + + // CTRL + L. + editor.setKeystroke(CKEDITOR.CTRL + 76, 'linkit'); + + // Add buttons. + if (editor.ui.addButton) { + editor.ui.addButton('Linkit', { + label: Drupal.t('Link'), + command: 'linkit', + icon: this.path + '/linkit.png' + }); + } + + editor.on('doubleclick', function (evt) { + var element = getSelectedLink(editor) || evt.data.element; + + if (!element.isReadOnly()) { + if (element.is('a')) { + editor.getSelection().selectElement(element); + editor.getCommand('linkit').exec(); + } + } + }); + + // If the "menu" plugin is loaded, register the menu items. + if (editor.addMenuItems) { + editor.addMenuItems({ + linkit: { + label: Drupal.t('Edit Link'), + command: 'linkit', + group: 'link', + order: 1 + } + }); + } + + // If the "contextmenu" plugin is loaded, register the listeners. + if (editor.contextMenu) { + editor.contextMenu.addListener(function (element, selection) { + if (!element || element.isReadOnly()) { + return null; + } + var anchor = getSelectedLink(editor); + if (!anchor) { + return null; + } + + var menu = {}; + if (anchor.getAttribute('href') && anchor.getChildCount()) { + menu = { + linkit: CKEDITOR.TRISTATE_OFF + }; + } + return menu; + }); + } + } + }); + + /** + * Get the surrounding link element of current selection. + * + * The following selection will all return the link element. + * + * @example + * li^nk + * [link] + * text[link] + * li[nk] + * [li]nk] + * [li]nk + * + * @param {CKEDITOR.editor} editor + * The CKEditor editor object + * + * @return {?HTMLElement} + * The selected link element, or null. + * + */ + function getSelectedLink(editor) { + var selection = editor.getSelection(); + var selectedElement = selection.getSelectedElement(); + if (selectedElement && selectedElement.is('a')) { + return selectedElement; + } + + var range = selection.getRanges(true)[0]; + + if (range) { + range.shrink(CKEDITOR.SHRINK_TEXT); + return editor.elementPath(range.getCommonAncestor()).contains('a', 1); + } + return null; + } + +})(jQuery, Drupal, drupalSettings, CKEDITOR); diff --git a/sites/all/modules/contrib/fields/linkit/linkit.info.yml b/sites/all/modules/contrib/fields/linkit/linkit.info.yml new file mode 100644 index 000000000..0a1f31ca7 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/linkit.info.yml @@ -0,0 +1,12 @@ +name: Linkit +description: Linkit +package: Custom +type: module +# core: 8.x +configure: entity.linkit_profile.collection + +# Information added by Drupal.org packaging script on 2017-03-21 +version: '8.x-4.3' +core: '8.x' +project: 'linkit' +datestamp: 1490127367 diff --git a/sites/all/modules/contrib/fields/linkit/linkit.install b/sites/all/modules/contrib/fields/linkit/linkit.install new file mode 100644 index 000000000..0f3bacf74 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/linkit.install @@ -0,0 +1,6 @@ +' . t('Attributes are HTML attributes that will be attached to the insert plugin.') . '

      '; + break; + } +} + + +/** + * Implements hook_form_BASE_FORM_ID_alter() for linkit_profile_form on behalf + * of the 'imce' module. + * + * Adds IMCE settings to the form. + * + * @see imce_form_linkit_profile_form_builder() + */ +function imce_form_linkit_profile_form_alter(&$form, FormStateInterface $form_state) { + /** @var \Drupal\Linkit\ProfileInterface $linkit_profile */ + $linkit_profile = $form_state->getFormObject()->getEntity(); + + $form['imce'] = array( + '#type' => 'details', + '#title' => t('IMCE integration'), + '#group' => 'additional_settings', + ); + + $form['imce']['imce_use'] = array( + '#type' => 'checkbox', + '#title' => t('Enable IMCE File Browser in the editor dialog.'), + '#default_value' => $linkit_profile->getThirdPartySetting('imce', 'use', FALSE), + ); + + $scheme_options = \Drupal::service('stream_wrapper_manager')->getNames(StreamWrapperInterface::READ_VISIBLE); + $form['imce']['imce_scheme'] = array( + '#type' => 'radios', + '#title' => t('Scheme'), + '#options' => $scheme_options, + '#default_value' => $linkit_profile->getThirdPartySetting('imce', 'scheme', 'public'), + '#states' => [ + 'visible' => [ + ':input[name="imce_use"]' => ['checked' => TRUE], + ], + ], + ); + + $form['#entity_builders'][] = 'imce_form_linkit_profile_form_builder'; +} + +/** + * Entity builder for the linkit profile form with imce options. + * + * @see imce_form_linkit_profile_form_alter(). + */ +function imce_form_linkit_profile_form_builder($entity_type, ProfileInterface $linkit_profile, &$form, FormStateInterface $form_state) { + $linkit_profile->setThirdPartySetting('imce', 'use', $form_state->getValue('imce_use')); + $linkit_profile->setThirdPartySetting('imce', 'scheme', $form_state->getValue('imce_scheme')); +} + +/** + * Implements hook_form_BASE_FORM_ID_alter() for linkit_editor_dialog_form on + * behalf of the 'imce' module. + * + * Adds a button to open the imce file browser if it is enabled. + */ +function imce_form_linkit_editor_dialog_form_alter(&$form, FormStateInterface $form_state) { + /** @var \Drupal\Linkit\ProfileInterface $linkit_profile */ + $linkit_profile = $form_state->getFormObject()->getLinkitProfile(); + + if($linkit_profile->getThirdPartySetting('imce', 'use', FALSE)) { + $form['imce-link'] = [ + '#type' => 'link', + '#title' => t('Open IMCE file browser'), + '#url' => Url::fromRoute('imce.page', [ + 'scheme' => $linkit_profile->getThirdPartySetting('imce', 'scheme', 'public'), + ]), + '#options' => array( + 'query' => array( + 'sendto' => 'linkitImce.sendto', + ), + ), + '#attributes' => [ + 'class' => ['linkit-imce-open'], + ], + '#attached' => [ + 'library' => [ + 'linkit/linkit.imce' + ], + ], + '#weight' => 1, + ]; + } +} diff --git a/sites/all/modules/contrib/fields/linkit/linkit.permissions.yml b/sites/all/modules/contrib/fields/linkit/linkit.permissions.yml new file mode 100644 index 000000000..e56ef4f4a --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/linkit.permissions.yml @@ -0,0 +1,2 @@ +administer linkit profiles: + title: 'Administer linkit profiles' \ No newline at end of file diff --git a/sites/all/modules/contrib/fields/linkit/linkit.routing.yml b/sites/all/modules/contrib/fields/linkit/linkit.routing.yml new file mode 100644 index 000000000..035952c0a --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/linkit.routing.yml @@ -0,0 +1,111 @@ +entity.linkit_profile.collection: + path: '/admin/config/content/linkit' + defaults: + _entity_list: 'linkit_profile' + _title: 'Linkit profiles' + requirements: + _permission: 'administer linkit profiles' + +entity.linkit_profile.add_form: + path: '/admin/config/content/linkit/add' + defaults: + _entity_form: linkit_profile.add + _title: 'Add profile' + requirements: + _permission: 'administer linkit profiles' + +entity.linkit_profile.edit_form: + path: '/admin/config/content/linkit/manage/{linkit_profile}' + defaults: + _entity_form: linkit_profile.edit + _title_callback: 'Drupal\linkit\Controller\LinkitController::profileTitle' + requirements: + _permission: 'administer linkit profiles' + +entity.linkit_profile.delete_form: + path: '/admin/config/content/linkit/manage/{linkit_profile}/delete' + defaults: + _entity_form: linkit_profile.delete + requirements: + _permission: 'administer linkit profiles' + +linkit.attributes: + path: '/admin/config/content/linkit/manage/{linkit_profile}/attributes' + defaults: + _form: '\Drupal\linkit\Form\Attribute\OverviewForm' + _title: 'Manage attributes' + requirements: + _permission: 'administer linkit profiles' + +linkit.attribute.add: + path: '/admin/config/content/linkit/manage/{linkit_profile}/attributes/add' + defaults: + _form: '\Drupal\linkit\Form\Attribute\AddForm' + _title: 'Add attribute' + requirements: + _permission: 'administer linkit profiles' + +linkit.attribute.delete: + path: '/admin/config/content/linkit/manage/{linkit_profile}/attributes/{plugin_instance_id}/delete' + defaults: + _form: '\Drupal\linkit\Form\Attribute\DeleteForm' + requirements: + _permission: 'administer linkit profiles' + +linkit.attribute.edit: + path: '/admin/config/content/linkit/manage/{linkit_profile}/attributes/{plugin_instance_id}' + defaults: + _form: '\Drupal\linkit\Form\Attribute\EditForm' + _title_callback: 'Drupal\linkit\Controller\LinkitController::attributeTitle' + requirements: + _permission: 'administer linkit profiles' + +linkit.matchers: + path: '/admin/config/content/linkit/manage/{linkit_profile}/matchers' + defaults: + _form: '\Drupal\linkit\Form\Matcher\OverviewForm' + _title: 'Manage matchers' + requirements: + _permission: 'administer linkit profiles' + +linkit.matcher.add: + path: '/admin/config/content/linkit/manage/{linkit_profile}/matchers/add' + defaults: + _form: '\Drupal\linkit\Form\Matcher\AddForm' + _title: 'Add matcher' + requirements: + _permission: 'administer linkit profiles' + +linkit.matcher.delete: + path: '/admin/config/content/linkit/manage/{linkit_profile}/matchers/{plugin_instance_id}/delete' + defaults: + _form: '\Drupal\linkit\Form\Matcher\DeleteForm' + requirements: + _permission: 'administer linkit profiles' + +linkit.matcher.edit: + path: '/admin/config/content/linkit/manage/{linkit_profile}/matchers/{plugin_instance_id}' + defaults: + _form: '\Drupal\linkit\Form\Matcher\EditForm' + _title_callback: 'Drupal\linkit\Controller\LinkitController::matcherTitle' + requirements: + _permission: 'administer linkit profiles' + +linkit.linkit_dialog: + path: '/linkit/dialog/linkit/{filter_format}' + defaults: + _form: '\Drupal\linkit\Form\LinkitEditorDialog' + _title: 'Add link' + requirements: + _entity_access: 'filter_format.use' + options: + _theme: ajax_base_page + +linkit.autocomplete: + path: '/linkit/autocomplete/{linkit_profile_id}' + defaults: + _controller: '\Drupal\linkit\Controller\AutocompleteController::autocomplete' + requirements: + _access: 'TRUE' + options: + _theme: ajax_base_page diff --git a/sites/all/modules/contrib/fields/linkit/linkit.services.yml b/sites/all/modules/contrib/fields/linkit/linkit.services.yml new file mode 100644 index 000000000..1deeb8679 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/linkit.services.yml @@ -0,0 +1,11 @@ +services: + plugin.manager.linkit.attribute: + class: Drupal\linkit\AttributeManager + parent: default_plugin_manager + + plugin.manager.linkit.matcher: + class: Drupal\linkit\MatcherManager + parent: default_plugin_manager + + linkit.result_manager: + class: Drupal\linkit\ResultManager diff --git a/sites/all/modules/contrib/fields/linkit/src/Annotation/Attribute.php b/sites/all/modules/contrib/fields/linkit/src/Annotation/Attribute.php new file mode 100644 index 000000000..d22b34bcd --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Annotation/Attribute.php @@ -0,0 +1,69 @@ +setConfiguration($configuration); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return [ + 'id' => $this->getPluginId(), + 'weight' => $this->weight, + 'settings' => $this->configuration, + ]; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $configuration += [ + 'weight' => '0', + 'settings' => [], + ]; + $this->configuration = $configuration['settings'] + $this->defaultConfiguration(); + $this->weight = $configuration['weight']; + return $this; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return []; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return []; + } + + /** + * {@inheritdoc} + */ + public function getLabel() { + return $this->pluginDefinition['label']; + } + + /** + * {@inheritdoc} + */ + public function getHtmlName() { + return $this->pluginDefinition['html_name']; + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return $this->pluginDefinition['description']; + } + + /** + * {@inheritdoc} + */ + public function getWeight() { + return $this->weight; + } + + /** + * {@inheritdoc} + */ + public function setWeight($weight) { + $this->weight = $weight; + return $this; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/AttributeCollection.php b/sites/all/modules/contrib/fields/linkit/src/AttributeCollection.php new file mode 100644 index 000000000..3b4d88b40 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/AttributeCollection.php @@ -0,0 +1,46 @@ +get($aID)->getWeight(); + $b_weight = $this->get($bID)->getWeight(); + if ($a_weight == $b_weight) { + return strnatcasecmp($this->get($aID)->getLabel(), $this->get($bID)->getLabel()); + } + + return ($a_weight < $b_weight) ? -1 : 1; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/AttributeInterface.php b/sites/all/modules/contrib/fields/linkit/src/AttributeInterface.php new file mode 100644 index 000000000..d3e865ba6 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/AttributeInterface.php @@ -0,0 +1,78 @@ +<a> tag. + * + * @return string + * The attribute html name. + */ + public function getHtmlName(); + + /** + * Returns the weight of the attribute. + * + * @return int|string + * Either the integer weight of the attribute or an empty string. + */ + public function getWeight(); + + /** + * Sets the weight for this attribute. + * + * @param int $weight + * The weight for this attribute. + * + * @return $this + */ + public function setWeight($weight); + + /** + * The form element structure for this attribute to be used in the dialog. + * + * @param mixed $default_value + * The default value for the element. Used when editing an attribute in the + * dialog. + * + * @return array + * The form element. + */ + public function buildFormElement($default_value); + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/AttributeManager.php b/sites/all/modules/contrib/fields/linkit/src/AttributeManager.php new file mode 100644 index 000000000..99d5a3b9f --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/AttributeManager.php @@ -0,0 +1,29 @@ +alterInfo('linkit_attribute'); + $this->setCacheBackend($cache_backend, 'linkit_attributes'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/ConfigurableAttributeBase.php b/sites/all/modules/contrib/fields/linkit/src/ConfigurableAttributeBase.php new file mode 100644 index 000000000..14ba4415c --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/ConfigurableAttributeBase.php @@ -0,0 +1,16 @@ +linkitProfileStorage = $linkit_profile_storage; + $this->resultManager = $resultManager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity.manager')->getStorage('linkit_profile'), + $container->get('linkit.result_manager') + ); + } + + /** + * Menu callback for linkit search autocompletion. + * + * Like other autocomplete functions, this function inspects the 'q' query + * parameter for the string to use to search for suggestions. + * + * @param Request $request + * The request. + * @param $linkit_profile_id + * The linkit profile id. + * @return JsonResponse + * A JSON response containing the autocomplete suggestions. + */ + public function autocomplete(Request $request, $linkit_profile_id) { + $this->linkitProfile = $this->linkitProfileStorage->load($linkit_profile_id); + $string = Unicode::strtolower($request->query->get('q')); + + $matches = $this->resultManager->getResults($this->linkitProfile, $string); + + $json_object = new \stdClass(); + $json_object->matches = $matches; + + return new JsonResponse($json_object); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Controller/LinkitController.php b/sites/all/modules/contrib/fields/linkit/src/Controller/LinkitController.php new file mode 100644 index 000000000..d23e7d336 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Controller/LinkitController.php @@ -0,0 +1,65 @@ +t('Edit %label profile', array('%label' => $linkit_profile->label())); + } + + /** + * Route title callback. + * + * @param \Drupal\linkit\ProfileInterface $linkit_profile + * The profile. + * @param string $plugin_instance_id + * The plugin instance id. + * + * @return string + * The title for the matcher edit form. + */ + public function matcherTitle(ProfileInterface $linkit_profile, $plugin_instance_id) { + /** @var \Drupal\linkit\MatcherInterface $matcher */ + $matcher = $linkit_profile->getMatcher($plugin_instance_id); + return $this->t('Edit %label matcher', array('%label' => $matcher->getLabel())); + } + + /** + * Route title callback. + * + * @param \Drupal\linkit\ProfileInterface $linkit_profile + * The profile. + * @param string $plugin_instance_id + * The plugin instance id. + * + * @return string + * The title for the attribute edit form. + */ + public function attributeTitle(ProfileInterface $linkit_profile, $plugin_instance_id) { + /** @var \Drupal\linkit\AttributeInterface $attribute */ + $attribute = $linkit_profile->getAttribute($plugin_instance_id); + return $this->t('Edit %label attribute', array('%label' => $attribute->getLabel())); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Element/Linkit.php b/sites/all/modules/contrib/fields/linkit/src/Element/Linkit.php new file mode 100644 index 000000000..25e9a0f4f --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Element/Linkit.php @@ -0,0 +1,108 @@ + TRUE, + '#size' => 60, + '#process' => array( + array($class, 'processLinkitAutocomplete'), + array($class, 'processGroup'), + ), + '#pre_render' => array( + array($class, 'preRenderLinkitElement'), + array($class, 'preRenderGroup'), + ), + '#theme' => 'input__textfield', + '#theme_wrappers' => array('form_element'), + ); + } + + /** + * {@inheritdoc} + */ + public static function valueCallback(&$element, $input, FormStateInterface $form_state) { + return Textfield::valueCallback($element, $input, $form_state); + } + + /** + * Adds linkit custom autocomplete functionality to elements. + * + * Instead of using the core autocomplete, we use our own. + * + * {@inheritdoc} + * + * @see \Drupal\Core\Render\Element\FormElement::processAutocomplete + */ + public static function processLinkitAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) { + $url = NULL; + $access = FALSE; + + if (!empty($element['#autocomplete_route_name'])) { + $parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : array(); + $url = Url::fromRoute($element['#autocomplete_route_name'], $parameters)->toString(TRUE); + /** @var \Drupal\Core\Access\AccessManagerInterface $access_manager */ + $access_manager = \Drupal::service('access_manager'); + $access = $access_manager->checkNamedRoute($element['#autocomplete_route_name'], $parameters, \Drupal::currentUser(), TRUE); + } + + if ($access) { + $metadata = BubbleableMetadata::createFromRenderArray($element); + if ($access->isAllowed()) { + $element['#attributes']['class'][] = 'form-linkit-autocomplete'; + $metadata->addAttachments(['library' => ['linkit/linkit.autocomplete']]); + // Provide a data attribute for the JavaScript behavior to bind to. + $element['#attributes']['data-autocomplete-path'] = $url->getGeneratedUrl(); + $metadata = $metadata->merge($url); + } + $metadata + ->merge(BubbleableMetadata::createFromObject($access)) + ->applyTo($element); + } + + return $element; + } + + /** + * Prepares a #type 'linkit' render element for input.html.twig. + * + * @param array $element + * An associative array containing the properties of the element. + * Properties used: #title, #value, #description, #size, #attributes. + * + * @return array + * The $element with prepared variables ready for input.html.twig. + */ + public static function preRenderLinkitElement($element) { + $element['#attributes']['type'] = 'text'; + Element::setAttributes($element, array('id', 'name', 'value', 'size')); + static::setAttributes($element, array('form-text')); + + return $element; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Entity/Profile.php b/sites/all/modules/contrib/fields/linkit/src/Entity/Profile.php new file mode 100644 index 000000000..785ff6365 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Entity/Profile.php @@ -0,0 +1,252 @@ +get('description'); + } + + /** + * {@inheritdoc} + */ + public function setDescription($description) { + $this->set('description', trim($description)); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getAttribute($attribute_id) { + return $this->getAttributes()->get($attribute_id); + } + + /** + * {@inheritdoc} + */ + public function getAttributes() { + if (!$this->attributeCollection) { + $this->attributeCollection = new AttributeCollection($this->getAttributeManager(), $this->attributes); + $this->attributeCollection->sort(); + } + return $this->attributeCollection; + } + + /** + * {@inheritdoc} + */ + public function addAttribute(array $configuration) { + $this->getAttributes()->addInstanceId($configuration['id'], $configuration); + return $configuration['id']; + } + + /** + * {@inheritdoc} + */ + public function removeAttribute($attribute_id) { + unset($this->attributes[$attribute_id]); + $this->getAttributes()->removeInstanceId($attribute_id); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setAttributeConfig($attribute_id, array $configuration) { + $this->attributes[$attribute_id] = $configuration; + $this->getAttributes()->setInstanceConfiguration($attribute_id, $configuration); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getMatcher($instance_id) { + return $this->getMatchers()->get($instance_id); + } + + /** + * {@inheritdoc} + */ + public function getMatchers() { + if (!$this->matcherCollection) { + $this->matcherCollection = new MatcherCollection($this->getMatcherManager(), $this->matchers); + $this->matcherCollection->sort(); + } + return $this->matcherCollection; + } + + /** + * {@inheritdoc} + */ + public function addMatcher(array $configuration) { + $configuration['uuid'] = $this->uuidGenerator()->generate(); + $this->getMatchers()->addInstanceId($configuration['uuid'], $configuration); + return $configuration['uuid']; + } + + /** + * {@inheritdoc} + */ + public function removeMatcher(MatcherInterface $matcher) { + $this->getMatchers()->removeInstanceId($matcher->getUuid()); + $this->save(); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setMatcherConfig($instance_id, array $configuration) { + $this->matchers[$instance_id] = $configuration; + $this->getMatchers()->setInstanceConfiguration($instance_id, $configuration); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getPluginCollections() { + return array( + 'attributes' => $this->getAttributes(), + 'matchers' => $this->getMatchers(), + ); + } + + /** + * Returns the attribute manager. + * + * @return \Drupal\Component\Plugin\PluginManagerInterface + * The attribute manager. + */ + protected function getAttributeManager() { + return \Drupal::service('plugin.manager.linkit.attribute'); + } + + /** + * Returns the matcher manager. + * + * @return \Drupal\Component\Plugin\PluginManagerInterface + * The matcher manager. + */ + protected function getMatcherManager() { + return \Drupal::service('plugin.manager.linkit.matcher'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/AddForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/AddForm.php new file mode 100644 index 000000000..059ef512e --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/AddForm.php @@ -0,0 +1,166 @@ +manager = $manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.linkit.attribute') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return "linkit_attribute_add_form"; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) { + $this->linkitProfile = $linkit_profile; + + $form['#attached']['library'][] = 'linkit/linkit.admin'; + $header = [ + 'label' => $this->t('Attributes'), + 'description' => $this->t('Description'), + ]; + + $form['plugin'] = [ + '#type' => 'tableselect', + '#header' => $header, + '#options' => $this->buildRows(), + '#empty' => $this->t('No attributes available.'), + '#multiple' => FALSE, + ]; + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Save and continue'), + '#submit' => ['::submitForm'], + '#tableselect' => TRUE, + '#button_type' => 'primary', + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + if (empty($form_state->getValue('plugin'))) { + $form_state->setErrorByName('plugin', $this->t('No attribute selected.')); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $form_state->cleanValues(); + + /** @var \Drupal\linkit\AttributeInterface $plugin */ + $plugin = $this->manager->createInstance($form_state->getValue('plugin')); + $plugin_id = $this->linkitProfile->addAttribute($plugin->getConfiguration()); + $this->linkitProfile->save(); + + $this->logger('linkit')->notice('Added %label attribute to the @profile profile.', [ + '%label' => $this->linkitProfile->getAttribute($plugin_id)->getLabel(), + '@profile' => $this->linkitProfile->label(), + ]); + + $is_configurable = $plugin instanceof ConfigurableAttributeInterface; + if ($is_configurable) { + $form_state->setRedirect('linkit.attribute.edit', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $plugin_id, + ]); + } + else { + drupal_set_message($this->t('Added %label attribute.', ['%label' => $plugin->getLabel()])); + + $form_state->setRedirect('linkit.attributes', [ + 'linkit_profile' => $this->linkitProfile->id(), + ]); + } + } + + /** + * Builds the table rows. + * + * Only attributes that is not already applied to the profile are shown. + * + * @return array + * An array of table rows. + */ + private function buildRows() { + $rows = []; + + $applied_plugins = $this->linkitProfile->getAttributes()->getConfiguration(); + $all_plugins = $this->manager->getDefinitions(); + uasort($all_plugins, function ($a, $b) { + return strnatcasecmp($a['label'], $b['label']); + }); + foreach (array_diff_key($all_plugins, $applied_plugins) as $definition) { + /** @var \Drupal\linkit\AttributeInterface $plugin */ + $plugin = $this->manager->createInstance($definition['id']); + + $row = [ + 'label' => (string) $plugin->getLabel(), + 'description' => (string) $plugin->getDescription(), + ]; + + $rows[$plugin->getPluginId()] = $row; + } + + return $rows; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/DeleteForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/DeleteForm.php new file mode 100644 index 000000000..d31e44b01 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/DeleteForm.php @@ -0,0 +1,92 @@ +t('Are you sure you want to delete the @plugin attribute from the %profile profile?', ['%profile' => $this->linkitProfile->label(), '@plugin' => $this->linkitAttribute->getLabel()]); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return Url::fromRoute('linkit.attributes', [ + 'linkit_profile' => $this->linkitProfile->id(), + ]); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'linkit_attribute_delete_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL, $plugin_instance_id = NULL) { + $this->linkitProfile = $linkit_profile; + + if (!$this->linkitProfile->getAttributes()->has($plugin_instance_id)) { + throw new NotFoundHttpException(); + } + + $this->linkitAttribute = $this->linkitProfile->getAttribute($plugin_instance_id); + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + if ($this->linkitProfile->getAttributes()->has($this->linkitAttribute->getPluginId())) { + $this->linkitProfile->removeAttribute($this->linkitAttribute->getPluginId()); + $this->linkitProfile->save(); + + drupal_set_message($this->t('The attribute %label has been deleted.', ['%label' => $this->linkitAttribute->getLabel()])); + $this->logger('linkit')->notice('The attribute %label has been deleted in the @profile profile.', [ + '%label' => $this->linkitAttribute->getLabel(), + '@profile' => $this->linkitProfile->label(), + ]); + } + + $form_state->setRedirect('linkit.attributes', [ + 'linkit_profile' => $this->linkitProfile->id(), + ]); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/EditForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/EditForm.php new file mode 100644 index 000000000..0b76601fb --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/EditForm.php @@ -0,0 +1,96 @@ +linkitProfile = $linkit_profile; + $this->linkitAttribute = $this->linkitProfile->getAttribute($plugin_instance_id); + $form['data'] = [ + '#tree' => true, + ]; + + $form['data'] += $this->linkitAttribute->buildConfigurationForm($form, $form_state); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Save changes'), + '#submit' => array('::submitForm'), + '#button_type' => 'primary', + ); + $form['actions']['delete'] = array( + '#type' => 'link', + '#title' => $this->t('Delete'), + '#url' => Url::fromRoute('linkit.attribute.delete', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $this->linkitAttribute->getPluginId(), + ]), + '#attributes' => [ + 'class' => ['button', 'button--danger'], + ], + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $form_state->cleanValues(); + $plugin_data = (new FormState())->setValues($form_state->getValue('data')); + $this->linkitAttribute->submitConfigurationForm($form, $plugin_data); + $this->linkitProfile->save(); + + drupal_set_message($this->t('Saved %label configuration.', array('%label' => $this->linkitAttribute->getLabel()))); + $this->logger('linkit')->notice('The attribute %label has been updated in the @profile profile.', [ + '%label' => $this->linkitAttribute->getLabel(), + '@profile' => $this->linkitProfile->label(), + ]); + + $form_state->setRedirect('linkit.attributes', [ + 'linkit_profile' => $this->linkitProfile->id(), + ]); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/OverviewForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/OverviewForm.php new file mode 100644 index 000000000..267dd61d5 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Attribute/OverviewForm.php @@ -0,0 +1,156 @@ +manager = $manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.linkit.attribute') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return "linkit_attribute_overview_form"; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) { + $this->linkitProfile = $linkit_profile; + + $form['plugins'] = [ + '#type' => 'table', + '#header' => [ + $this->t('Attribute'), + $this->t('Description'), + $this->t('Weight'), + $this->t('Operations'), + ], + '#empty' => $this->t('No attributes added.'), + '#tabledrag' => [ + [ + 'action' => 'order', + 'relationship' => 'sibling', + 'group' => 'plugin-order-weight', + ], + ], + ]; + + foreach ($this->linkitProfile->getAttributes() as $plugin) { + $key = $plugin->getPluginId(); + + $form['plugins'][$key]['#attributes']['class'][] = 'draggable'; + $form['plugins'][$key]['#weight'] = $plugin->getWeight(); + + $form['plugins'][$key]['label'] = [ + '#plain_text' => (string) $plugin->getLabel(), + ]; + + $form['plugins'][$key]['description'] = [ + '#plain_text' => (string) $plugin->getDescription(), + ]; + + $form['plugins'][$key]['weight'] = [ + '#type' => 'weight', + '#title' => t('Weight for @title', ['@title' => (string) $plugin->getLabel()]), + '#title_display' => 'invisible', + '#default_value' => $plugin->getWeight(), + '#attributes' => ['class' => ['plugin-order-weight']], + ]; + + $form['plugins'][$key]['operations'] = [ + '#type' => 'operations', + '#links' => [], + ]; + + $is_configurable = $plugin instanceof ConfigurableAttributeInterface; + if ($is_configurable) { + $form['plugins'][$key]['operations']['#links']['edit'] = [ + 'title' => t('Edit'), + 'url' => Url::fromRoute('linkit.attribute.edit', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $key, + ]), + ]; + } + + $form['plugins'][$key]['operations']['#links']['delete'] = [ + 'title' => t('Delete'), + 'url' => Url::fromRoute('linkit.attribute.delete', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $key, + ]), + ]; + } + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Save'), + '#button_type' => 'primary', + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + foreach ($form_state->getValue('plugins') as $id => $plugin_data) { + if ($this->linkitProfile->getAttributes()->has($id)) { + $this->linkitProfile->getAttribute($id)->setWeight($plugin_data['weight']); + } + } + $this->linkitProfile->save(); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/LinkitEditorDialog.php b/sites/all/modules/contrib/fields/linkit/src/Form/LinkitEditorDialog.php new file mode 100644 index 000000000..8f3b8f571 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/LinkitEditorDialog.php @@ -0,0 +1,207 @@ +editorStorage = $editor_storage; + $this->linkitProfileStorage = $linkit_profile_storage; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity.manager')->getStorage('editor'), + $container->get('entity.manager')->getStorage('linkit_profile') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'linkit_editor_dialog_form'; + } + + /** + * {@inheritdoc} + * + * @param \Drupal\filter\Entity\FilterFormat $filter_format + * The filter format for which this dialog corresponds. + */ + public function buildForm(array $form, FormStateInterface $form_state, FilterFormat $filter_format = NULL) { + // The default values are set directly from \Drupal::request()->request, + // provided by the editor plugin opening the dialog. + $user_input = $form_state->getUserInput(); + $input = isset($user_input['editor_object']) ? $user_input['editor_object'] : []; + + /** @var \Drupal\editor\EditorInterface $editor */ + $editor = $this->editorStorage->load($filter_format->id()); + $linkit_profile_id = $editor->getSettings()['plugins']['linkit']['linkit_profile']; + $this->linkitProfile = $this->linkitProfileStorage->load($linkit_profile_id); + + $form['#tree'] = TRUE; + $form['#attached']['library'][] = 'editor/drupal.editor.dialog'; + $form['#prefix'] = '
      '; + $form['#suffix'] = '
      '; + + // Everything under the "attributes" key is merged directly into the + // generated link tag's attributes. + $form['attributes']['href'] = [ + '#title' => $this->t('Link'), + '#type' => 'linkit', + '#default_value' => isset($input['href']) ? $input['href'] : '', + '#description' => $this->t('Start typing to find content or paste a URL.'), + '#autocomplete_route_name' => 'linkit.autocomplete', + '#autocomplete_route_parameters' => [ + 'linkit_profile_id' => $linkit_profile_id + ], + '#weight' => 0, + ]; + + $this->addAttributes($form, $form_state, $this->linkitProfile->getAttributes(), $input); + + $form['actions'] = [ + '#type' => 'actions', + ]; + + $form['actions']['save_modal'] = [ + '#type' => 'submit', + '#value' => $this->t('Save'), + '#submit' => [], + '#ajax' => [ + 'callback' => '::submitForm', + 'event' => 'click', + ], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + $attributes = array_filter($form_state->getValue('attributes')); + $form_state->setValue('attributes', $attributes); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $response = new AjaxResponse(); + + if ($form_state->getErrors()) { + unset($form['#prefix'], $form['#suffix']); + $form['status_messages'] = [ + '#type' => 'status_messages', + '#weight' => -10, + ]; + $response->addCommand(new HtmlCommand('#linkit-editor-dialog-form', $form)); + } + else { + $response->addCommand(new EditorDialogSave($form_state->getValues())); + $response->addCommand(new CloseModalDialogCommand()); + } + + return $response; + } + + /** + * Adds the attributes enabled on the current profile. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param AttributeCollection $attributes + * A collection of attributes for the current profile. + * @param array $input + * An array with the attribute values from the editor. + */ + private function addAttributes(array &$form, FormStateInterface &$form_state, AttributeCollection $attributes, array $input) { + if ($attributes->count()) { + $form['linkit_attributes'] = [ + '#type' => 'container', + '#title' => $this->t('Attributes'), + '#weight' => '10', + ]; + + /** @var \Drupal\linkit\AttributeInterface $plugin */ + foreach ($attributes as $plugin) { + $plugin_name = $plugin->getHtmlName(); + + $default_value = isset($input[$plugin_name]) ? $input[$plugin_name] : ''; + $form['linkit_attributes'][$plugin_name] = $plugin->buildFormElement($default_value); + $form['linkit_attributes'][$plugin_name] += [ + '#parents' => [ + 'attributes', $plugin_name, + ], + ]; + } + } + } + + /** + * Gets the linkit profile entity. + * + * @return \Drupal\linkit\ProfileInterface + * The current linkit profile used by this form. + */ + public function getLinkitProfile() { + return $this->linkitProfile; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/AddForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/AddForm.php new file mode 100644 index 000000000..0acad7329 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/AddForm.php @@ -0,0 +1,165 @@ +manager = $manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.linkit.matcher') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return "linkit_matcher_add_form"; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) { + $this->linkitProfile = $linkit_profile; + + $form['#attached']['library'][] = 'linkit/linkit.admin'; + $header = [ + 'label' => $this->t('Matchers'), + ]; + + $form['plugin'] = [ + '#type' => 'tableselect', + '#header' => $header, + '#options' => $this->buildRows(), + '#empty' => $this->t('No matchers available.'), + '#multiple' => FALSE, + ]; + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Save and continue'), + '#submit' => ['::submitForm'], + '#tableselect' => TRUE, + '#button_type' => 'primary', + ]; + + $options = []; + foreach ($this->manager->getDefinitions() as $id => $plugin) { + $options[$id] = $plugin['label']; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + if (empty($form_state->getValue('plugin'))) { + $form_state->setErrorByName('plugin', $this->t('No matcher selected.')); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $form_state->cleanValues(); + + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance($form_state->getValue('plugin')); + + $plugin_uuid = $this->linkitProfile->addMatcher($plugin->getConfiguration()); + $this->linkitProfile->save(); + + $this->logger('linkit')->notice('Added %label matcher to the @profile profile.', [ + '%label' => $this->linkitProfile->getMatcher($plugin_uuid)->getLabel(), + '@profile' => $this->linkitProfile->label(), + ]); + + $is_configurable = $plugin instanceof ConfigurableMatcherInterface; + if ($is_configurable) { + $form_state->setRedirect('linkit.matcher.edit', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $plugin_uuid, + ]); + } + else { + drupal_set_message($this->t('Added %label matcher.', ['%label' => $plugin->getLabel()])); + + $form_state->setRedirect('linkit.matchers', [ + 'linkit_profile' => $this->linkitProfile->id(), + ]); + } + } + + /** + * Builds the table rows. + * + * @return array + * An array of table rows. + */ + private function buildRows() { + $rows = []; + $all_plugins = $this->manager->getDefinitions(); + uasort($all_plugins, function ($a, $b) { + return strnatcasecmp($a['label'], $b['label']); + }); + foreach ($all_plugins as $definition) { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance($definition['id']); + $row = [ + 'label' => $plugin->getLabel(), + ]; + $rows[$plugin->getPluginId()] = $row; + } + + return $rows; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/DeleteForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/DeleteForm.php new file mode 100644 index 000000000..bff47370f --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/DeleteForm.php @@ -0,0 +1,90 @@ +t('Are you sure you want to delete the @plugin matcher from the %profile profile?', ['%profile' => $this->linkitProfile->label(), '@plugin' => $this->linkitMatcher->getLabel()]); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return Url::fromRoute('linkit.matchers', [ + 'linkit_profile' => $this->linkitProfile->id(), + ]); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'linkit_matcher_delete_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL, $plugin_instance_id = NULL) { + $this->linkitProfile = $linkit_profile; + + if (!$this->linkitProfile->getMatchers()->has($plugin_instance_id)) { + throw new NotFoundHttpException(); + } + + $this->linkitMatcher = $this->linkitProfile->getMatcher($plugin_instance_id); + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $this->linkitProfile->removeMatcher($this->linkitMatcher); + + drupal_set_message($this->t('The matcher %label has been deleted.', ['%label' => $this->linkitMatcher->getLabel()])); + $this->logger('linkit')->notice('The matcher %label has been deleted in the @profile profile.', [ + '%label' => $this->linkitMatcher->getLabel(), + '@profile' => $this->linkitProfile->label(), + ]); + + $form_state->setRedirect('linkit.matchers', [ + 'linkit_profile' => $this->linkitProfile->id(), + ]); + + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/EditForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/EditForm.php new file mode 100644 index 000000000..193d3263d --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/EditForm.php @@ -0,0 +1,92 @@ +linkitProfile = $linkit_profile; + $this->linkitMatcher = $this->linkitProfile->getMatcher($plugin_instance_id); + + $form += $this->linkitMatcher->buildConfigurationForm($form, $form_state); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Save changes'), + '#submit' => array('::submitForm'), + '#button_type' => 'primary', + ); + $form['actions']['delete'] = array( + '#type' => 'link', + '#title' => $this->t('Delete'), + '#url' => Url::fromRoute('linkit.matcher.delete', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $this->linkitMatcher->getUuid(), + ]), + '#attributes' => [ + 'class' => ['button', 'button--danger'], + ], + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $form_state->cleanValues(); + $plugin_data = (new FormState())->setValues($form_state->getValues()); + $this->linkitMatcher->submitConfigurationForm($form, $plugin_data); + $this->linkitProfile->save(); + + drupal_set_message($this->t('Saved %label configuration.', array('%label' => $this->linkitMatcher->getLabel()))); + $this->logger('linkit')->notice('The matcher %label has been updated in the @profile profile.', [ + '%label' => $this->linkitMatcher->getLabel(), + '@profile' => $this->linkitProfile->label(), + ]); + + $form_state->setRedirect('linkit.matchers', [ + 'linkit_profile' => $this->linkitProfile->id(), + ]); + } +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/OverviewForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/OverviewForm.php new file mode 100644 index 000000000..1bffe2d7f --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Matcher/OverviewForm.php @@ -0,0 +1,165 @@ +manager = $manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.linkit.matcher') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return "linkit_matcher_overview_form"; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) { + $this->linkitProfile = $linkit_profile; + $form['#attached']['library'][] = 'linkit/linkit.admin'; + $form['plugins'] = [ + '#type' => 'table', + '#header' => [ + [ + 'data' => $this->t('Matcher'), + 'colspan' => 2 + ], + $this->t('Weight'), + $this->t('Operations'), + ], + '#empty' => $this->t('No matchers added.'), + '#tabledrag' => [ + [ + 'action' => 'order', + 'relationship' => 'sibling', + 'group' => 'plugin-order-weight', + ], + ], + ]; + + foreach ($this->linkitProfile->getMatchers() as $plugin) { + $key = $plugin->getUuid(); + + $form['plugins'][$key]['#attributes']['class'][] = 'draggable'; + $form['plugins'][$key]['#weight'] = $plugin->getWeight(); + + $form['plugins'][$key]['label'] = [ + '#plain_text' => (string) $plugin->getLabel(), + ]; + + $form['plugins'][$key]['summary'] = []; + + $summary = $plugin->getSummary(); + if (!empty($summary)) { + $form['plugins'][$key]['summary'] = [ + '#type' => 'inline_template', + '#template' => '
      {{ summary|safe_join("
      ") }}
      ', + '#context' => ['summary' => $summary], + ]; + } + + $form['plugins'][$key]['weight'] = [ + '#type' => 'weight', + '#title' => t('Weight for @title', ['@title' => (string) $plugin->getLabel()]), + '#title_display' => 'invisible', + '#default_value' => $plugin->getWeight(), + '#attributes' => ['class' => ['plugin-order-weight']], + ]; + + $form['plugins'][$key]['operations'] = [ + '#type' => 'operations', + '#links' => [], + ]; + + $is_configurable = $plugin instanceof ConfigurableMatcherInterface; + if ($is_configurable) { + $form['plugins'][$key]['operations']['#links']['edit'] = [ + 'title' => t('Edit'), + 'url' => Url::fromRoute('linkit.matcher.edit', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $key, + ]), + ]; + } + + $form['plugins'][$key]['operations']['#links']['delete'] = [ + 'title' => t('Delete'), + 'url' => Url::fromRoute('linkit.matcher.delete', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $key, + ]), + ]; + } + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Save'), + '#button_type' => 'primary', + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + foreach ($form_state->getValue('plugins') as $id => $plugin_data) { + if ($this->linkitProfile->getMatchers()->has($id)) { + $this->linkitProfile->getMatcher($id)->setWeight($plugin_data['weight']); + } + } + $this->linkitProfile->save(); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Profile/AddForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Profile/AddForm.php new file mode 100644 index 000000000..b43d69c81 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Profile/AddForm.php @@ -0,0 +1,28 @@ +t('Save and manage matchers'); + return $actions; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Profile/EditForm.php b/sites/all/modules/contrib/fields/linkit/src/Form/Profile/EditForm.php new file mode 100644 index 000000000..6fb8eb0c2 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Profile/EditForm.php @@ -0,0 +1,29 @@ +t('Update profile'); + $actions['delete']['#value'] = $this->t('Delete profile'); + return $actions; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Form/Profile/FormBase.php b/sites/all/modules/contrib/fields/linkit/src/Form/Profile/FormBase.php new file mode 100644 index 000000000..52f2d940a --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Form/Profile/FormBase.php @@ -0,0 +1,90 @@ + 'textfield', + '#title' => $this->t('Profile Name'), + '#default_value' => $this->entity->label(), + '#description' => $this->t('The human-readable name of this profile. This name must be unique.'), + '#required' => TRUE, + '#size' => 30, + ]; + + $form['id'] = [ + '#type' => 'machine_name', + '#default_value' => $this->entity->id(), + '#machine_name' => [ + 'exists' => ['\Drupal\linkit\Entity\Profile', 'load'] + ], + '#disabled' => !$this->entity->isNew(), + ]; + + $form['description'] = [ + '#type' => 'textarea', + '#title' => $this->t('Description'), + '#default_value' => $this->entity->getDescription(), + '#description' => $this->t('The text will be displayed on the profile collection page.'), + ]; + + $form['additional_settings'] = array( + '#type' => 'vertical_tabs', + '#weight' => 99, + ); + + return parent::form($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + $linkit_profile = $this->entity; + + // Prevent leading and trailing spaces in linkit profile labels. + $linkit_profile->set('label', trim($linkit_profile->label())); + + $status = $linkit_profile->save(); + $edit_link = $this->entity->link($this->t('Edit')); + switch ($status) { + case SAVED_NEW: + drupal_set_message($this->t('Created new profile %label.', ['%label' => $linkit_profile->label()])); + $this->logger('linkit')->notice('Created new profile %label.', ['%label' => $linkit_profile->label(), 'link' => $edit_link]); + $form_state->setRedirect('linkit.matchers', [ + 'linkit_profile' => $linkit_profile->id(), + ]); + break; + + case SAVED_UPDATED: + drupal_set_message($this->t('Updated profile %label.', ['%label' => $linkit_profile->label()])); + $this->logger('linkit')->notice('Updated profile %label.', ['%label' => $linkit_profile->label(), 'link' => $edit_link]); + $form_state->setRedirectUrl($linkit_profile->urlInfo('edit-form')); + break; + } + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/MatcherBase.php b/sites/all/modules/contrib/fields/linkit/src/MatcherBase.php new file mode 100644 index 000000000..6932e2b71 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/MatcherBase.php @@ -0,0 +1,119 @@ +setConfiguration($configuration); + } + + /** + * {@inheritdoc} + */ + public function getUuid() { + return $this->uuid; + } + + /** + * {@inheritdoc} + */ + public function getLabel() { + return $this->pluginDefinition['label']; + } + + /** + * {@inheritdoc} + */ + public function getSummary() { + return array(); + } + + /** + * {@inheritdoc} + */ + public function getWeight() { + return $this->weight; + } + + /** + * {@inheritdoc} + */ + public function setWeight($weight) { + return $this->weight = $weight; + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return [ + 'uuid' => $this->getUuid(), + 'id' => $this->getPluginId(), + 'weight' => $this->getWeight(), + 'settings' => $this->configuration, + ]; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $configuration += [ + 'uuid' => '', + 'weight' => '0', + 'settings' => [], + ]; + $this->configuration = $configuration['settings'] + $this->defaultConfiguration(); + $this->uuid = $configuration['uuid']; + $this->weight = $configuration['weight']; + return $this; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return []; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return []; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/MatcherCollection.php b/sites/all/modules/contrib/fields/linkit/src/MatcherCollection.php new file mode 100644 index 000000000..b7124c2f3 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/MatcherCollection.php @@ -0,0 +1,46 @@ +get($aID)->getWeight(); + $b_weight = $this->get($bID)->getWeight(); + if ($a_weight == $b_weight) { + return strnatcasecmp($this->get($aID)->getLabel(), $this->get($bID)->getLabel()); + } + + return ($a_weight < $b_weight) ? -1 : 1; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/MatcherInterface.php b/sites/all/modules/contrib/fields/linkit/src/MatcherInterface.php new file mode 100644 index 000000000..ef5d98103 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/MatcherInterface.php @@ -0,0 +1,84 @@ +alterInfo('linkit_matcher'); + $this->setCacheBackend($cache_backend, 'linkit_matchers'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/MatcherTokensTrait.php b/sites/all/modules/contrib/fields/linkit/src/MatcherTokensTrait.php new file mode 100644 index 000000000..69b010c4e --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/MatcherTokensTrait.php @@ -0,0 +1,71 @@ +moduleExists('token')) { + // Add the token tree UI. + $form['token_tree'] = array( + '#theme' => 'token_tree_link', + '#token_types' => $types, + '#dialog' => TRUE, + '#weight' => -90, + ); + } + else { + $token_items = array(); + foreach ($this->getAvailableTokens($types) as $type => $tokens) { + foreach ($tokens as $name => $info) { + $token_description = !empty($info['description']) ? $info['description'] : ''; + $token_items[$type . ':' . $name] = "[$type:$name]" . ' - ' . $info['name'] . ': ' . $token_description; + } + } + + if (count($token_items)) { + $form['tokens'] = array( + '#type' => 'details', + '#title' => t('Available tokens'), + '#weight' => -90, + ); + + $form['tokens']['list'] = array( + '#theme' => 'item_list', + '#items' => $token_items, + ); + } + } + } + + /** + * Gets all available tokens. + * + * @param array $types + * An array of token types to use. + * @return array + * An array with available tokens + */ + public function getAvailableTokens(array $types = array()) { + $info = \Drupal::token()->getInfo(); + $available = array_intersect_key($info['tokens'], array_flip($types)); + return $available; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/CKEditorPlugin/Linkit.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/CKEditorPlugin/Linkit.php new file mode 100644 index 000000000..54056a525 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/CKEditorPlugin/Linkit.php @@ -0,0 +1,123 @@ +linkitProfileStorage = $linkit_profile_storage; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity.manager')->getStorage('linkit_profile') + ); + } + + /** + * {@inheritdoc} + */ + public function getFile() { + return drupal_get_path('module', 'linkit') . '/js/plugins/linkit/plugin.js'; + } + + /** + * {@inheritdoc} + */ + public function getConfig(Editor $editor) { + return array( + 'linkit_dialogTitleAdd' => t('Add link'), + 'linkit_dialogTitleEdit' => t('Edit link'), + ); + } + + /** + * {@inheritdoc} + */ + public function getButtons() { + return array( + 'Linkit' => array( + 'label' => t('Linkit'), + 'image' => drupal_get_path('module', 'linkit') . '/js/plugins/linkit/linkit.png', + ), + ); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) { + $settings = $editor->getSettings(); + + $all_profiles = $this->linkitProfileStorage->loadMultiple(); + + $options = array(); + foreach ($all_profiles as $profile) { + $options[$profile->id()] = $profile->label(); + } + + $form['linkit_profile'] = array( + '#type' => 'select', + '#title' => t('Select a linkit profile'), + '#options' => $options, + '#default_value' => isset($settings['plugins']['linkit']) ? $settings['plugins']['linkit'] : '', + '#empty_option' => $this->t('- Select profile -'), + '#description' => $this->t('Select the linkit profile you wish to use with this text format.'), + '#element_validate' => array( + array($this, 'validateLinkitProfileSelection'), + ), + ); + + return $form; + } + + /** + * #element_validate handler for the "linkit_profile" element in settingsForm(). + */ + public function validateLinkitProfileSelection(array $element, FormStateInterface $form_state) { + $toolbar_buttons = $form_state->getValue(array('editor', 'settings', 'toolbar', 'button_groups')); + if (strpos($toolbar_buttons, '"Linkit"') !== FALSE && empty($element['#value'])) { + $form_state->setError($element, t('Please select the linkit profile you wish to use.')); + } + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Derivative/EntityMatcherDeriver.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Derivative/EntityMatcherDeriver.php new file mode 100644 index 000000000..b5fcf5020 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Derivative/EntityMatcherDeriver.php @@ -0,0 +1,67 @@ +entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $container->get('entity.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) { + $has_canonical = $entity_type->hasLinkTemplate('canonical'); + + if ($has_canonical) { + $this->derivatives[$entity_type_id] = $base_plugin_definition; + $this->derivatives[$entity_type_id]['id'] = $base_plugin_definition['id'] . ':' . $entity_type_id; + $this->derivatives[$entity_type_id]['label'] = $entity_type->getLabel(); + $this->derivatives[$entity_type_id]['target_entity'] = $entity_type_id; + $this->derivatives[$entity_type_id]['base_plugin_label'] = (string) $base_plugin_definition['label']; + } + } + + return parent::getDerivativeDefinitions($base_plugin_definition); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Accesskey.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Accesskey.php new file mode 100644 index 000000000..e0a609f1b --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Accesskey.php @@ -0,0 +1,38 @@ + 'textfield', + '#title' => t('Accesskey'), + '#default_value' => $default_value, + '#maxlength' => 255, + '#size' => 40, + '#placeholder' => t('The "accesskey" attribute value'), + ]; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Clazz.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Clazz.php new file mode 100644 index 000000000..03787e7b4 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Clazz.php @@ -0,0 +1,41 @@ + 'textfield', +// '#title' => t('Class'), +// '#maxlength' => 255, +// '#size' => 40, +// ]; +// } +// +//} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Id.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Id.php new file mode 100644 index 000000000..817b1ba09 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Id.php @@ -0,0 +1,38 @@ + 'textfield', + '#title' => t('Id'), + '#default_value' => $default_value, + '#maxlength' => 255, + '#size' => 40, + '#placeholder' => t('The "id" attribute value'), + ]; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Relationship.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Relationship.php new file mode 100644 index 000000000..0c3286767 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Relationship.php @@ -0,0 +1,38 @@ + 'textfield', + '#title' => t('Relationship'), + '#default_value' => $default_value, + '#maxlength' => 255, + '#size' => 40, + '#placeholder' => t('The "rel" attribute value'), + ]; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Target.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Target.php new file mode 100644 index 000000000..ce4833757 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Target.php @@ -0,0 +1,96 @@ +configuration['widget_type']) { + case self::SELECT_LIST: + return [ + '#type' => 'select', + '#title' => t('Target'), + '#options' => [ + '' => '', + '_blank' => t('New window (_blank)'), + '_top' => t('Top window (_top)'), + '_self' => t('Same window (_self)'), + '_parent' => t('Parent window (_parent)') + ], + '#default_value' => $default_value, + ]; + case self::SIMPLE_CHECKBOX: + return [ + '#type' => 'checkbox', + '#title' => t('Open in new window'), + '#default_value' => $default_value, + '#return_value' => '_blank', + ]; + } + + return []; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'widget_type' => self::SIMPLE_CHECKBOX, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form['widget_type'] = [ + '#type' => 'radios', + '#title' => $this->t('Widget type'), + '#default_value' => $this->configuration['widget_type'], + '#options' => [ + self::SELECT_LIST => $this->t('Selectlist with predefined targets.'), + self::SIMPLE_CHECKBOX => $this->t('Simple checkbox to allow links to be opened in a new browser window or tab.'), + ], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $this->configuration['widget_type'] = $form_state->getValue('widget_type'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Title.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Title.php new file mode 100644 index 000000000..6cd8f3e4b --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Attribute/Title.php @@ -0,0 +1,82 @@ + 'textfield', + '#title' => t('Title'), + '#default_value' => $default_value, + '#maxlength' => 255, + '#size' => 40, + '#placeholder' => t('The "title" attribute value'), + ]; + + if ($this->configuration['automatic_title']) { + $element['#attached']['library'][] = 'linkit/linkit.attribute.title'; + $element['#placeholder'] = t('The "title" attribute value (auto populated)'); + } + + return $element; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'automatic_title' => FALSE, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form['automatic_title'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Automatically populate title'), + '#default_value' => $this->configuration['automatic_title'], + '#description' => $this->t('Automatically populate the title attribute with the title from the match selection.'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $this->configuration['automatic_title'] = $form_state->getValue('automatic_title'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php new file mode 100644 index 000000000..33838206a --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php @@ -0,0 +1,348 @@ +database = $database; + $this->entityManager = $entity_manager; + $this->moduleHandler = $module_handler; + $this->currentUser = $current_user; + $this->target_type = $plugin_definition['target_entity']; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('database'), + $container->get('entity.manager'), + $container->get('module_handler'), + $container->get('current_user') + ); + } + + /** + * {@inheritdoc} + */ + public function getSummary() { + $summery = parent::getSummary(); + $entity_type = $this->entityManager->getDefinition($this->target_type); + + $result_description = $this->configuration['result_description']; + if (!empty($result_description)) { + $summery[] = $this->t('Result description: @result_description', [ + '@result_description' => $result_description + ]); + } + + if ($entity_type->hasKey('bundle')) { + $has_bundle_filter = !empty($this->configuration['bundles']); + $bundles = []; + + if ($has_bundle_filter) { + $bundles_info = $this->entityManager->getBundleInfo($this->target_type); + foreach ($this->configuration['bundles'] as $bundle) { + $bundles[] = $bundles_info[$bundle]['label']; + } + } + + $summery[] = $this->t('Bundle filter: @bundle_filter', [ + '@bundle_filter' => $has_bundle_filter ? implode(', ', $bundles) : t('None'), + ]); + + $summery[] = $this->t('Group by bundle: @bundle_grouping', [ + '@bundle_grouping' => $this->configuration['group_by_bundle'] ? $this->t('Yes') : $this->t('No'), + ]); + } + + return $summery; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'result_description' => '', + 'bundles' => [], + 'group_by_bundle' => FALSE, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $entity_type = $this->entityManager->getDefinition($this->target_type); + $form['result_description'] = [ + '#title' => $this->t('Result description'), + '#type' => 'textfield', + '#default_value' => $this->configuration['result_description'], + '#size' => 120, + '#maxlength' => 255, + '#weight' => -100, + ]; + + $this->insertTokenList($form, [$this->target_type]); + + // Filter the possible bundles to use if the entity has bundles. + if ($entity_type->hasKey('bundle')) { + $bundle_options = []; + foreach ($this->entityManager->getBundleInfo($this->target_type) as $bundle_name => $bundle_info) { + $bundle_options[$bundle_name] = $bundle_info['label']; + } + + $form['bundles'] = [ + '#type' => 'checkboxes', + '#title' => $this->t('Restrict to the selected bundles'), + '#options' => $bundle_options, + '#default_value' => $this->configuration['bundles'], + '#description' => $this->t('If none of the checkboxes is checked, allow all bundles.'), + '#element_validate' => [[get_class($this), 'elementValidateFilter']], + '#weight' => -50, + ]; + + // Group the results by bundle. + $form['group_by_bundle'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Group by bundle'), + '#default_value' => $this->configuration['group_by_bundle'], + '#weight' => -50, + ]; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $this->configuration['result_description'] = $form_state->getValue('result_description'); + $this->configuration['bundles'] = $form_state->getValue('bundles'); + $this->configuration['group_by_bundle'] = $form_state->getValue('group_by_bundle'); + } + + /** + * Form element validation handler; Filters the #value property of an element. + */ + public static function elementValidateFilter(&$element, FormStateInterface $form_state) { + $element['#value'] = array_filter($element['#value']); + $form_state->setValueForElement($element, $element['#value']); + } + + /** + * {@inheritdoc} + */ + public function getMatches($string) { + $query = $this->buildEntityQuery($string); + $result = $query->execute(); + + if (empty($result)) { + return []; + } + + $matches = []; + $entities = $this->entityManager->getStorage($this->target_type)->loadMultiple($result); + + foreach ($entities as $entity_id => $entity) { + // Check the access against the defined entity access handler. + /** @var \Drupal\Core\Access\AccessResultInterface $access */ + $access = $entity->access('view', $this->currentUser, TRUE); + if (!$access->isAllowed()) { + continue; + } + + $matches[] = [ + 'title' => $this->buildLabel($entity), + 'description' => $this->buildDescription($entity), + 'path' => $this->buildPath($entity), + 'group' => $this->buildGroup($entity), + ]; + } + + return $matches; + } + + /** + * Builds an EntityQuery to get entities. + * + * @param $match + * Text to match the label against. + * + * @return \Drupal\Core\Entity\Query\QueryInterface + * The EntityQuery object with the basic conditions and sorting applied to + * it. + */ + protected function buildEntityQuery($match) { + $match = $this->database->escapeLike($match); + + $entity_type = $this->entityManager->getDefinition($this->target_type); + $query = $this->entityManager->getStorage($this->target_type)->getQuery(); + $label_key = $entity_type->getKey('label'); + + if ($label_key) { + $query->condition($label_key, '%' . $match . '%', 'LIKE'); + $query->sort($label_key, 'ASC'); + } + + // Bundle check. + if (!empty($this->configuration['bundles']) && $bundle_key = $entity_type->getKey('bundle')) { + $query->condition($bundle_key, $this->configuration['bundles'], 'IN'); + } + + // Add tags to let other modules alter the query. + $query->addTag('linkit_entity_autocomplete'); + $query->addTag('linkit_entity_' . $this->target_type . '_autocomplete'); + + // Add access tag for the query. + $query->addTag('entity_access'); + $query->addTag($this->target_type . '_access'); + + return $query; + } + + /** + * Builds the label string used in the match array. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The matched entity. + * + * @return string + * The label for this entity. + */ + protected function buildLabel($entity) { + return Html::escape($entity->label()); + } + + /** + * Builds the description string used in the match array. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The matched entity. + * + * @return string + * The description for this entity. + */ + protected function buildDescription($entity) { + $description = \Drupal::token()->replace($this->configuration['result_description'], [$this->target_type => $entity], []); + return LinkitXss::descriptionFilter($description); + } + + /** + * Builds the path string used in the match array. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The matched entity. + * + * @return string + * The URL for this entity. + */ + protected function buildPath($entity) { + return $entity->toUrl()->toString(); + } + + /** + * Builds the group string used in the match array. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The matched entity. + * + * @return string + * The match group for this entity. + */ + protected function buildGroup($entity) { + $group = $entity->getEntityType()->getLabel(); + + // If the entities by this entity should be grouped by bundle, get the + // name and append it to the group. + if ($this->configuration['group_by_bundle']) { + $bundles = $this->entityManager->getBundleInfo($entity->getEntityTypeId()); + $bundle_label = $bundles[$entity->bundle()]['label']; + $group .= ' - ' . $bundle_label; + } + + return $group; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/FileMatcher.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/FileMatcher.php new file mode 100644 index 000000000..cb39ccfe6 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/FileMatcher.php @@ -0,0 +1,189 @@ +t('Show image dimensions: @show_image_dimensions', [ + '@show_image_dimensions' => $this->configuration['images']['show_dimensions'] ? $this->t('Yes') : $this->t('No'), + ]); + + $summery[] = $this->t('Show image thumbnail: @show_image_thumbnail', [ + '@show_image_thumbnail' => $this->configuration['images']['show_thumbnail'] ? $this->t('Yes') : $this->t('No'), + ]); + + if ($this->moduleHandler->moduleExists('image') && $this->configuration['images']['show_thumbnail']) { + $image_style = ImageStyle::load($this->configuration['images']['thumbnail_image_style']); + if (!is_null($image_style)) { + $summery[] = $this->t('Thumbnail style: @thumbnail_style', [ + '@thumbnail_style' => $image_style->label(), + ]); + } + } + + return $summery; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'images' => [ + 'show_dimensions' => FALSE, + 'show_thumbnail' => FALSE, + 'thumbnail_image_style' => 'linkit_result_thumbnail', + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + $dependencies = parent::calculateDependencies() + [ + 'module' => ['file'], + ]; + + if ($this->configuration['images']['show_thumbnail']) { + $dependencies['module'][] = 'image'; + $dependencies['config'][] = 'image.style.' . $this->configuration['images']['thumbnail_image_style']; + } + + return $dependencies; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + $form['images'] = array( + '#type' => 'details', + '#title' => t('Image file settings'), + '#description' => t('Extra settings for image files in the result.'), + '#tree' => TRUE, + ); + + $form['images']['show_dimensions'] = [ + '#title' => t('Show pixel dimensions'), + '#type' => 'checkbox', + '#default_value' => $this->configuration['images']['show_dimensions'], + ]; + + if ($this->moduleHandler->moduleExists('image')) { + $form['images']['show_thumbnail'] = [ + '#title' => t('Show thumbnail'), + '#type' => 'checkbox', + '#default_value' => $this->configuration['images']['show_thumbnail'], + ]; + + $form['images']['thumbnail_image_style'] = [ + '#title' => t('Thumbnail image style'), + '#type' => 'select', + '#default_value' => $this->configuration['images']['thumbnail_image_style'], + '#options' => image_style_options(FALSE), + '#states' => [ + 'visible' => [ + ':input[name="images[show_thumbnail]"]' => ['checked' => TRUE], + ], + ], + ]; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + parent::submitConfigurationForm($form, $form_state); + + $values = $form_state->getValue('images'); + if (!$values['show_thumbnail']) { + $values['thumbnail_image_style'] = NULL; + } + + $this->configuration['images'] = $values; + } + + /** + * {@inheritdoc} + */ + protected function buildEntityQuery($match) { + $query = parent::buildEntityQuery($match); + $query->condition('status', FILE_STATUS_PERMANENT); + + return $query; + } + + /** + * {@inheritdoc} + */ + protected function buildDescription($entity) { + $description_array = array(); + + $description_array[] = parent::buildDescription($entity); + + /** @var \Drupal\file\FileInterface $entity */ + $file = $entity->getFileUri(); + + /** @var \Drupal\Core\Image\ImageInterface $image */ + $image = \Drupal::service('image.factory')->get($file); + if ($image->isValid()) { + if ($this->configuration['images']['show_dimensions']) { + $description_array[] = $image->getWidth() . 'x' . $image->getHeight() . 'px'; + } + + if ($this->configuration['images']['show_thumbnail'] && $this->moduleHandler->moduleExists('image')) { + $image_element = array( + '#weight' => -10, + '#theme' => 'image_style', + '#style_name' => $this->configuration['images']['thumbnail_image_style'], + '#uri' => $entity->getFileUri(), + ); + + $description_array[] = (string) \Drupal::service('renderer')->render($image_element); + } + } + + $description = implode('
      ' , $description_array); + return LinkitXss::descriptionFilter($description); + } + + /** + * {@inheritdoc} + * + * The file entity still uses url() even though it's deprecated in the + * entity interface. + */ + protected function buildPath($entity) { + /** @var \Drupal\file\FileInterface $entity */ + return file_url_transform_relative(file_create_url($entity->getFileUri())); + } +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/NodeMatcher.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/NodeMatcher.php new file mode 100644 index 000000000..4beb6f2a5 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/NodeMatcher.php @@ -0,0 +1,92 @@ +t('Include unpublished: @include_unpublished', [ + '@include_unpublished' => $this->configuration['include_unpublished'] ? $this->t('Yes') : $this->t('No'), + ]); + + return $summery; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'include_unpublished' => FALSE, + ]; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return parent::calculateDependencies() + [ + 'module' => ['node'], + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + $form['include_unpublished'] = [ + '#title' => t('Include unpublished nodes'), + '#type' => 'checkbox', + '#default_value' => $this->configuration['include_unpublished'], + '#description' => t('In order to see unpublished nodes, the requesting user must also have permissions to do so.'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + parent::submitConfigurationForm($form, $form_state); + + $this->configuration['include_unpublished'] = $form_state->getValue('include_unpublished'); + } + + /** + * {@inheritdoc} + */ + protected function buildEntityQuery($match) { + $query = parent::buildEntityQuery($match); + + $no_access = !$this->currentUser->hasPermission('bypass node access') && !count($this->moduleHandler->getImplementations('node_grants')); + if ($this->configuration['include_unpublished'] !== TRUE || $no_access) { + $query->condition('status', NODE_PUBLISHED); + } + + return $query; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/TermMatcher.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/TermMatcher.php new file mode 100644 index 000000000..513b1dccc --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/TermMatcher.php @@ -0,0 +1,49 @@ + ['taxonomy'], + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + $this->insertTokenList($form, ['term']); + return $form; + } + + /** + * {@inheritdoc} + */ + protected function buildDescription($entity) { + $description = \Drupal::token()->replace($this->configuration['result_description'], ['term' => $entity], []); + return LinkitXss::descriptionFilter($description); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/UserMatcher.php b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/UserMatcher.php new file mode 100644 index 000000000..94a462a93 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Plugin/Linkit/Matcher/UserMatcher.php @@ -0,0 +1,117 @@ +configuration['roles']) ? $this->configuration['roles'] : ['None']; + $summery[] = $this->t('Role filter: @role_filter', [ + '@role_filter' => implode(', ', $roles), + ]); + + $summery[] = $this->t('Include blocked users: @include_blocked', [ + '@include_blocked' => $this->configuration['include_blocked'] ? $this->t('Yes') : $this->t('No'), + ]); + + return $summery; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'roles' => [], + 'include_blocked' => FALSE, + ]; + } + + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return parent::calculateDependencies() + [ + 'module' => ['user'], + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + $form['roles'] = array( + '#type' => 'checkboxes', + '#title' => $this->t('Restrict to the selected roles'), + '#options' => array_diff_key(user_role_names(TRUE), array(RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID)), + '#default_value' => $this->configuration['roles'], + '#description' => $this->t('If none of the checkboxes is checked, allow all roles.'), + '#element_validate' => [[get_class($this), 'elementValidateFilter']], + ); + + $form['include_blocked'] = [ + '#title' => t('Include blocked user'), + '#type' => 'checkbox', + '#default_value' => $this->configuration['include_blocked'], + '#description' => t('In order to see blocked users, the requesting user must also have permissions to do so.'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + parent::submitConfigurationForm($form, $form_state); + + $this->configuration['roles'] = $form_state->getValue('roles'); + $this->configuration['include_blocked'] = $form_state->getValue('include_blocked'); + } + + /** + * {@inheritdoc} + */ + protected function buildEntityQuery($match) { + $query = parent::buildEntityQuery($match); + + $match = $this->database->escapeLike($match); + // The user entity don't specify a label key so we have to do it instead. + $query->condition('name', '%' . $match . '%', 'LIKE'); + + // Filter by role. + if (!empty($this->configuration['roles'])) { + $query->condition('roles', $this->configuration['roles'], 'IN'); + } + + if ($this->configuration['include_blocked'] !== TRUE || !$this->currentUser->hasPermission('administer users')) { + $query->condition('status', 1); + } + + return $query; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/ProfileInterface.php b/sites/all/modules/contrib/fields/linkit/src/ProfileInterface.php new file mode 100644 index 000000000..fae8275fe --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/ProfileInterface.php @@ -0,0 +1,139 @@ + t('Description'), + 'class' => [RESPONSIVE_PRIORITY_MEDIUM], + ]; + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + /** @var \Drupal\linkit\ProfileInterface $linkitProfile */ + $linkitProfile = $entity; + $row['label'] = $linkitProfile->label(); + $row['description']['data'] = ['#markup' => $linkitProfile->getDescription()]; + return $row + parent::buildRow($entity); + } + + /** + * {@inheritdoc} + */ + public function getDefaultOperations(EntityInterface $entity) { + $operations = parent::getDefaultOperations($entity); + + if (isset($operations['edit'])) { + $operations['edit']['title'] = t('Edit profile'); + } + + $operations['matchers'] = [ + 'title' => t('Manage matchers'), + 'weight' => 10, + 'url' => Url::fromRoute('linkit.matchers', [ + 'linkit_profile' => $entity->id() + ]), + ]; + + $operations['attributes'] = [ + 'title' => t('Manage attributes'), + 'weight' => 20, + 'url' => Url::fromRoute('linkit.attributes', [ + 'linkit_profile' => $entity->id() + ]), + ]; + + return $operations; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/ResultManager.php b/sites/all/modules/contrib/fields/linkit/src/ResultManager.php new file mode 100644 index 000000000..bd4274454 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/ResultManager.php @@ -0,0 +1,74 @@ + t('No results'), + ]]; + } + + // Special for link to front page. + if (strpos($search_string, 'front') !== FALSE) { + $matches[] = [ + 'title' => t('Front page'), + 'description' => 'The front page for this site.', + 'path' => Url::fromRoute('')->toString(), + 'group' => t('System'), + ]; + } + + foreach ($linkitProfile->getMatchers() as $plugin) { + $matches = array_merge($matches, $plugin->getMatches($search_string)); + } + + // Check for an e-mail address then return an e-mail match and create a + // mail-to link if appropriate. + if (filter_var($search_string, FILTER_VALIDATE_EMAIL)) { + $matches[] = [ + 'title' => t('E-mail @email', ['@email' => $search_string]), + 'description' => t('Opens your mail client ready to e-mail @email', ['@email' => $search_string]), + 'path' => 'mailto:' . Html::escape($search_string), + 'group' => t('E-mail'), + ]; + } + + // If there is still no matches, return a "no results" array. + if (empty($matches)) { + return [[ + 'title' => t('No results'), + ]]; + } + + return $matches; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Tests/AttributeCrudTest.php b/sites/all/modules/contrib/fields/linkit/src/Tests/AttributeCrudTest.php new file mode 100644 index 000000000..5132ff5fb --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Tests/AttributeCrudTest.php @@ -0,0 +1,160 @@ +manager = $this->container->get('plugin.manager.linkit.attribute'); + + $this->linkitProfile = $this->createProfile(); + $this->drupalLogin($this->adminUser); + } + + /** + * Test the overview page. + */ + function testOverview() { + $this->drupalGet(Url::fromRoute('linkit.attributes', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + $this->assertText(t('No attributes added.')); + + $this->assertLinkByHref(Url::fromRoute('linkit.attribute.add', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])->toString()); + } + + /** + * Test adding an attribute to a profile. + */ + function testAdd() { + $this->drupalGet(Url::fromRoute('linkit.attribute.add', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + + $this->assertEqual(count($this->manager->getDefinitions()), count($this->xpath('//input[@type="radio"]')), 'All attributes are available.'); + + $edit = array(); + $edit['plugin'] = 'dummy_attribute'; + $this->drupalPostForm(NULL, $edit, t('Save and continue')); + + $this->assertUrl(Url::fromRoute('linkit.attributes', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + + $this->assertEqual(1, count($this->xpath('//table/tbody/tr')), 'Attribute added.'); + $this->assertNoText(t('No attributes added.')); + } + + /** + * Test adding a configurable attribute to a profile. + */ + function testAddConfigurable() { + $this->drupalGet(Url::fromRoute('linkit.attribute.add', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + + $this->assertEqual(count($this->manager->getDefinitions()), count($this->xpath('//input[@type="radio"]')), 'All attributes are available.'); + + $edit = array(); + $edit['plugin'] = 'configurable_dummy_attribute'; + $this->drupalPostForm(NULL, $edit, t('Save and continue')); + + $this->assertUrl(Url::fromRoute('linkit.attribute.edit', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => 'configurable_dummy_attribute', + ])); + + $this->drupalGet(Url::fromRoute('linkit.attributes', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + + $this->assertEqual(1, count($this->xpath('//table/tbody/tr')), 'Attribute added.'); + $this->assertNoText(t('No attributes added.')); + + $plugin_url = Url::fromRoute('linkit.attribute.edit', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => 'configurable_dummy_attribute', + ]); + + $this->assertLinkByHref($plugin_url->toString()); + } + + /** + * Test delete an attribute from a profile. + */ + function testDelete() { + /** @var \Drupal\linkit\AttributeInterface $plugin */ + $plugin = $this->manager->createInstance('dummy_attribute'); + + $this->linkitProfile->addAttribute($plugin->getConfiguration()); + $this->linkitProfile->save(); + + // Try delete an attribute that is not attached to the profile. + $this->drupalGet(Url::fromRoute('linkit.attribute.delete', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => 'doesntexists' + ])); + $this->assertResponse('404'); + + // Go to the delete page, but press cancel. + $this->drupalGet(Url::fromRoute('linkit.attribute.delete', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $plugin->getPluginId(), + ])); + $this->clickLink(t('Cancel')); + $this->assertUrl(Url::fromRoute('linkit.attributes', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + + // Delete the attribute from the profile. + $this->drupalGet(Url::fromRoute('linkit.attribute.delete', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => 'dummy_attribute', + ])); + + $this->drupalPostForm(NULL, [], t('Confirm')); + $this->assertRaw(t('The attribute %plugin has been deleted.', ['%plugin' => $plugin->getLabel()])); + $this->assertUrl(Url::fromRoute('linkit.attributes', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + $this->assertText(t('No attributes added.')); + + /** @var \Drupal\linkit\Entity\Profile $updated_profile */ + $updated_profile = Profile::load($this->linkitProfile->id()); + $this->assertFalse($updated_profile->getAttributes()->has($plugin->getPluginId()), 'The attribute is deleted from the profile'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Tests/Controllers/LinkitControllerTest.php b/sites/all/modules/contrib/fields/linkit/src/Tests/Controllers/LinkitControllerTest.php new file mode 100644 index 000000000..f7acbe8fc --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Tests/Controllers/LinkitControllerTest.php @@ -0,0 +1,83 @@ +linkitProfile = $this->createProfile(); + + $this->drupalLogin($this->adminUser); + } + + /** + * Tests the profile route title callback. + */ + function testProfileTitle() { + $this->drupalGet(Url::fromRoute('entity.linkit_profile.edit_form', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + + $this->assertText('Edit ' . $this->linkitProfile->label() . ' profile'); + } + + /** + * Tests the matcher route title callback. + */ + function testMatcherTitle() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->container->get('plugin.manager.linkit.matcher')->createInstance('configurable_dummy_matcher'); + $matcher_uuid = $this->linkitProfile->addMatcher($plugin->getConfiguration()); + $this->linkitProfile->save(); + + $this->drupalGet(Url::fromRoute('linkit.matcher.edit', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $matcher_uuid, + ])); + + $this->assertText('Edit ' . $plugin->getLabel() . ' matcher'); + } + + /** + * Tests the attribute route title callback. + */ + function testAttributeTitle() { + /** @var \Drupal\linkit\AttributeInterface $plugin */ + $plugin = $this->container->get('plugin.manager.linkit.attribute')->createInstance('configurable_dummy_attribute'); + $this->linkitProfile->addAttribute($plugin->getConfiguration()); + $this->linkitProfile->save(); + + $this->drupalGet(Url::fromRoute('linkit.attribute.edit', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $plugin->getPluginId(), + ])); + $this->assertText('Edit ' . $plugin->getLabel() . ' attribute'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Tests/LinkitTestBase.php b/sites/all/modules/contrib/fields/linkit/src/Tests/LinkitTestBase.php new file mode 100644 index 000000000..196dece20 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Tests/LinkitTestBase.php @@ -0,0 +1,83 @@ +adminUser = $this->drupalCreateUser(['administer linkit profiles']); + $this->baseUser = $this->drupalCreateUser(); + + $this->drupalPlaceBlock('page_title_block', ['region' => 'content']); + $this->drupalPlaceBlock('local_tasks_block', ['region' => 'content']); + $this->drupalPlaceBlock('local_actions_block', ['region' => 'content']); + $this->drupalPlaceBlock('system_messages_block', ['region' => 'highlighted']); + } + + /** + * Creates a profile based on default settings. + * + * @param array $settings + * (optional) An associative array of settings for the profile, as used in + * entity_create(). Override the defaults by specifying the key and value + * in the array + * + * The following defaults are provided: + * - label: Random string. + * + * @return \Drupal\linkit\ProfileInterface + * The created profile entity. + */ + protected function createProfile(array $settings = []) { + // Populate defaults array. + $settings += [ + 'id' => Unicode::strtolower($this->randomMachineName()), + 'label' => $this->randomMachineName(), + ]; + + $profile = Profile::create($settings); + $profile->save(); + + return $profile; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Tests/MatcherCrudTest.php b/sites/all/modules/contrib/fields/linkit/src/Tests/MatcherCrudTest.php new file mode 100644 index 000000000..528c378b2 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Tests/MatcherCrudTest.php @@ -0,0 +1,158 @@ +manager = $this->container->get('plugin.manager.linkit.matcher'); + + $this->linkitProfile = $this->createProfile(); + $this->drupalLogin($this->adminUser); + } + + /** + * Test the overview page. + */ + function testOverview() { + $this->drupalGet(Url::fromRoute('linkit.matchers', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + $this->assertText(t('No matchers added.')); + + $this->assertLinkByHref(Url::fromRoute('linkit.matcher.add', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])->toString()); + } + + /** + * Test adding a matcher to a profile. + */ + function testAdd() { + $this->drupalGet(Url::fromRoute('linkit.matcher.add', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + + $edit = array(); + $edit['plugin'] = 'dummy_matcher'; + $this->drupalPostForm(NULL, $edit, t('Save and continue')); + + // Load the saved profile. + $this->linkitProfile = Profile::load($this->linkitProfile->id()); + + $matcher_ids = $this->linkitProfile->getMatchers()->getInstanceIds(); + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->linkitProfile->getMatcher(current($matcher_ids)); + + $this->assertRaw(t('Added %label matcher.', ['%label' => $plugin->getLabel()])); + $this->assertNoText(t('No matchers added.')); + } + + /** + * Test adding a configurable attribute to a profile. + */ + function testAddConfigurable() { + $this->drupalGet(Url::fromRoute('linkit.matcher.add', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + + $edit = array(); + $edit['plugin'] = 'configurable_dummy_matcher'; + $this->drupalPostForm(NULL, $edit, t('Save and continue')); + + // Load the saved profile. + $this->linkitProfile = Profile::load($this->linkitProfile->id()); + + $matcher_ids = $this->linkitProfile->getMatchers()->getInstanceIds(); + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->linkitProfile->getMatcher(current($matcher_ids)); + + $this->assertUrl(Url::fromRoute('linkit.matcher.edit', [ + 'linkit_profile' => $this->linkitProfile->id(), + 'plugin_instance_id' => $plugin->getUuid(), + ])); + + $this->drupalGet(Url::fromRoute('linkit.matchers', [ + 'linkit_profile' => $this->linkitProfile->id(), + ])); + + $this->assertNoText(t('No matchers added.')); + } + + /** + * Test delete a matcher from a profile. + */ + function testDelete() { + /** @var \Drupal\linkit\AttributeInterface $plugin */ + $plugin = $this->manager->createInstance('dummy_matcher'); + + $profile = $this->createProfile(); + $plugin_uuid = $profile->addMatcher($plugin->getConfiguration()); + $profile->save(); + + // Try delete a matcher that is not attached to the profile. + $this->drupalGet(Url::fromRoute('linkit.matcher.delete', [ + 'linkit_profile' => $profile->id(), + 'plugin_instance_id' => 'doesntexists' + ])); + $this->assertResponse('404'); + + // Go to the delete page, but press cancel. + $this->drupalGet(Url::fromRoute('linkit.matcher.delete', [ + 'linkit_profile' => $profile->id(), + 'plugin_instance_id' => $plugin_uuid, + ])); + $this->clickLink(t('Cancel')); + $this->assertUrl(Url::fromRoute('linkit.matchers', [ + 'linkit_profile' => $profile->id(), + ])); + + // Delete the matcher from the profile. + $this->drupalGet(Url::fromRoute('linkit.matcher.delete', [ + 'linkit_profile' => $profile->id(), + 'plugin_instance_id' => $plugin_uuid, + ])); + + $this->drupalPostForm(NULL, [], t('Confirm')); + $this->assertRaw(t('The matcher %plugin has been deleted.', ['%plugin' => $plugin->getLabel()])); + $this->assertUrl(Url::fromRoute('linkit.matchers', [ + 'linkit_profile' => $profile->id(), + ])); + $this->assertText(t('No matchers added.')); + + /** @var \Drupal\linkit\Entity\Profile $updated_profile */ + $updated_profile = Profile::load($profile->id()); + $this->assertFalse($updated_profile->getMatchers()->has($plugin_uuid), 'The user matcher is deleted from the profile'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Tests/Matchers/NodeMatcherTest.php b/sites/all/modules/contrib/fields/linkit/src/Tests/Matchers/NodeMatcherTest.php new file mode 100644 index 000000000..9db476232 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Tests/Matchers/NodeMatcherTest.php @@ -0,0 +1,105 @@ +drupalLogin($this->adminUser); + $this->manager = $this->container->get('plugin.manager.linkit.matcher'); + + $type1 = $this->drupalCreateContentType(['type' => 'test1', 'name' => 'Test1']); + $type2 = $this->drupalCreateContentType(['type' => 'test2', 'name' => 'Test2']); + + // Nodes with type 1. + $this->drupalCreateNode(['title' => 'Lorem Ipsum 1', 'type' => $type1->id()]); + $this->drupalCreateNode(['title' => 'Lorem Ipsum 2', 'type' => $type1->id()]); + + // Nodes with type 1. + $this->drupalCreateNode(['title' => 'Lorem Ipsum 3', 'type' => $type2->id()]); + + // Unpublished node. + $this->drupalCreateNode(['title' => 'Lorem unpublishd', 'type' => $type1->id(), 'status' => FALSE]); + } + + /** + * Tests node matcher. + */ + function testNodeMatcherWidthDefaultConfiguration() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance('entity:node', []); + $matches = $plugin->getMatches('Lorem'); + $this->assertEqual(3, count($matches), 'Correct number of matches'); + } + + /** + * Tests node matcher with bundle filer. + */ + function testNodeMatcherWidthBundleFiler() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance('entity:node', [ + 'settings' => [ + 'bundles' => [ + 'test1' => 'test1' + ], + ], + ]); + + $matches = $plugin->getMatches('Lorem'); + $this->assertEqual(2, count($matches), 'Correct number of matches'); + } + + /** + * Tests node matcher with include unpublished setting activated. + */ + function testNodeMatcherWidthIncludeUnpublished() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance('entity:node', [ + 'settings' => [ + 'include_unpublished' => TRUE, + ], + ]); + + // Test without permissions to see unpublished nodes. + $matches = $plugin->getMatches('Lorem'); + $this->assertEqual(3, count($matches), 'Correct number of matches'); + + $account = $this->drupalCreateUser(['bypass node access']); + $this->drupalLogin($account); + + // Test with permissions to see unpublished nodes. + $matches = $plugin->getMatches('Lorem'); + $this->assertEqual(4, count($matches), 'Correct number of matches'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Tests/Matchers/TermMatcherTest.php b/sites/all/modules/contrib/fields/linkit/src/Tests/Matchers/TermMatcherTest.php new file mode 100644 index 000000000..d28a484a2 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Tests/Matchers/TermMatcherTest.php @@ -0,0 +1,134 @@ +getStorage('taxonomy_vocabulary'); + $vocabulary = $vocabularyStorage->create([ + 'name' => $name, + 'description' => $name, + 'vid' => Unicode::strtolower($name), + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + ]); + $vocabulary->save(); + return $vocabulary; + } + + /** + * Creates and saves a new term with in vocabulary $vid. + * + * @param \Drupal\taxonomy\Entity\Vocabulary $vocabulary + * The vocabulary object. + * @param array $values + * (optional) An array of values to set, keyed by property name. If the + * entity type has bundles, the bundle key has to be specified. + * + * @return \Drupal\taxonomy\Entity\Term + * The new taxonomy term object. + */ + private function createTerm(Vocabulary $vocabulary, $values = array()) { + $filter_formats = filter_formats(); + $format = array_pop($filter_formats); + + $termStorage = \Drupal::entityTypeManager()->getStorage('taxonomy_term'); + $term = $termStorage->create($values + array( + 'name' => $this->randomMachineName(), + 'description' => array( + 'value' => $this->randomMachineName(), + // Use the first available text format. + 'format' => $format->id(), + ), + 'vid' => $vocabulary->id(), + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + )); + $term->save(); + return $term; + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + $this->drupalLogin($this->adminUser); + $this->manager = $this->container->get('plugin.manager.linkit.matcher'); + + $testing_vocabulary_1 = $this->createVocabulary('testing_vocabulary_1'); + $testing_vocabulary_2 = $this->createVocabulary('testing_vocabulary_2'); + + $this->createTerm($testing_vocabulary_1, ['name' => 'foo_bar']); + $this->createTerm($testing_vocabulary_1, ['name' => 'foo_baz']); + $this->createTerm($testing_vocabulary_1, ['name' => 'foo_foo']); + $this->createTerm($testing_vocabulary_1, ['name' => 'bar']); + $this->createTerm($testing_vocabulary_2, ['name' => 'foo_bar']); + $this->createTerm($testing_vocabulary_2, ['name' => 'foo_baz']); + } + + /** + * Tests term matcher with default configuration. + */ + function testTermMatcherWidthDefaultConfiguration() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance('entity:taxonomy_term', []); + $matches = $plugin->getMatches('foo'); + $this->assertEqual(5, count($matches), 'Correct number of matches'); + } + + /** + * Tests term matcher with bundle filer. + */ + function testTermMatcherWidthBundleFiler() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance('entity:taxonomy_term', [ + 'settings' => [ + 'bundles' => [ + 'testing_vocabulary_1' => 'testing_vocabulary_1' + ], + ], + ]); + + $matches = $plugin->getMatches('foo'); + $this->assertEqual(3, count($matches), 'Correct number of matches'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Tests/Matchers/UserMatcherTest.php b/sites/all/modules/contrib/fields/linkit/src/Tests/Matchers/UserMatcherTest.php new file mode 100644 index 000000000..6150a1119 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Tests/Matchers/UserMatcherTest.php @@ -0,0 +1,114 @@ +drupalLogin($this->drupalCreateUser(['access user profiles'])); + $this->manager = $this->container->get('plugin.manager.linkit.matcher'); + + $custom_role = $this->drupalCreateRole(array(), 'custom_role', 'custom_role'); + $custom_role_admin = $this->drupalCreateRole(array(), 'custom_role_admin', 'custom_role_admin'); + + $this->drupalCreateUser([], 'lorem'); + $this->drupalCreateUser([], 'foo'); + + $account = $this->drupalCreateUser([], 'ipsumlorem'); + $account->addRole($custom_role); + $account->save(); + + $account = $this->drupalCreateUser([], 'lorem_custom_role'); + $account->addRole($custom_role); + $account->save(); + + $account = $this->drupalCreateUser([], 'lorem_custom_role_admin'); + $account->addRole($custom_role_admin); + $account->save(); + + $account = $this->drupalCreateUser([], 'blocked_lorem'); + $account->block(); + $account->save(); + } + + /** + * Tests user matcher. + */ + function testUserMatcherWidthDefaultConfiguration() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance('entity:user', []); + $matches = $plugin->getMatches('Lorem'); + $this->assertEqual(4, count($matches), 'Correct number of matches'); + } + + /** + * Tests user matcher with role filer. + */ + function testUserMatcherWidthRoleFiler() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance('entity:user', [ + 'settings' => [ + 'roles' => [ + 'custom_role' => 'custom_role' + ], + ], + ]); + + $matches = $plugin->getMatches('Lorem'); + $this->assertEqual(2, count($matches), 'Correct number of matches'); + } + + /** + * Tests user matcher with include blocked setting activated. + */ + function testUserMatcherWidthIncludeBlocked() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance('entity:user', [ + 'settings' => [ + 'include_blocked' => TRUE, + ], + ]); + + // Test without permissions to see blocked users. + $matches = $plugin->getMatches('blocked'); + $this->assertEqual(0, count($matches), 'Correct number of matches'); + + $account = $this->drupalCreateUser(['administer users']); + $this->drupalLogin($account); + + // Test with permissions to see blocked users. + $matches = $plugin->getMatches('blocked'); + $this->assertEqual(1, count($matches), 'Correct number of matches'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Tests/ProfileCrudTest.php b/sites/all/modules/contrib/fields/linkit/src/Tests/ProfileCrudTest.php new file mode 100644 index 000000000..93af4e9ce --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Tests/ProfileCrudTest.php @@ -0,0 +1,125 @@ +drupalLogin($this->adminUser); + } + + /** + * Test the overview page. + */ + function testOverview() { + // Verify that the profile collection page is not accessible for regular + // users. + $this->drupalLogin($this->baseUser); + $this->drupalGet(Url::fromRoute('entity.linkit_profile.collection')); + $this->assertResponse(403); + $this->drupalLogout(); + + // Verify that the profile collection page is accessible for regular users. + $this->drupalLogin($this->adminUser); + + $profiles = []; + $profiles[] = $this->createProfile(); + $profiles[] = $this->createProfile(); + + $this->drupalGet(Url::fromRoute('entity.linkit_profile.collection')); + $this->assertResponse(200); + + // Assert that the 'Add profile' action exists. + $this->assertLinkByHref(Url::fromRoute('entity.linkit_profile.add_form')->toString()); + + /** @var \Drupal\linkit\ProfileInterface $profile */ + foreach ($profiles as $profile) { + $this->assertLinkByHref('admin/config/content/linkit/manage/' . $profile->id()); + $this->assertLinkByHref('admin/config/content/linkit/manage/' . $profile->id() . '/delete'); + } + } + + /** + * Creates profile. + */ + function testProfileCreation() { + $this->drupalGet(Url::fromRoute('entity.linkit_profile.add_form')); + $this->drupalGet('admin/config/content/linkit/add'); + $this->assertResponse(200); + + // Create a profile. + $edit = []; + $edit['label'] = Unicode::strtolower($this->randomMachineName()); + $edit['id'] = Unicode::strtolower($this->randomMachineName()); + $edit['description'] = $this->randomMachineName(16); + $this->drupalPostForm(NULL, $edit, t('Save and manage matchers')); + + $this->assertRaw(t('Created new profile %label.', ['%label' => $edit['label']])); + + $this->drupalGet(Url::fromRoute('entity.linkit_profile.collection')); + $this->assertText($edit['label'], 'Profile exists in the profile collection.'); + } + + /** + * Updates a profile. + */ + function testProfileUpdate() { + $profile = $this->createProfile(); + $this->drupalGet(Url::fromRoute('entity.linkit_profile.edit_form', [ + 'linkit_profile' => $profile->id(), + ])); + $this->assertResponse(200); + + $id_field = $this->xpath('.//input[not(@disabled) and @name="id"]'); + + $this->assertTrue(empty($id_field), 'Machine name field is disabled.'); + $this->assertLinkByHref(Url::fromRoute('entity.linkit_profile.edit_form', [ + 'linkit_profile' => $profile->id(), + ])->toString()); + $this->assertLinkByHref('admin/config/content/linkit/manage/' . $profile->id() . '/delete'); + + $edit = []; + $edit['label'] = $this->randomMachineName(); + $edit['description'] = $this->randomMachineName(16); + $this->drupalPostForm(NULL, $edit, t('Update profile')); + + $this->assertRaw(t('Updated profile %label.', ['%label' => $edit['label']])); + + $this->drupalGet(Url::fromRoute('entity.linkit_profile.collection')); + $this->assertText($edit['label'], 'Updated profile exists in the profile collection.'); + } + + /** + * Delete a profile. + */ + function testProfileDelete() { + /** @var \Drupal\linkit\ProfileInterface $profile */ + $profile = $this->createProfile(); + $this->drupalGet(Url::fromRoute('entity.linkit_profile.delete_form', [ + 'linkit_profile' => $profile->id(), + ])); + $this->drupalPostForm(NULL, [], t('Delete')); + + $this->assertRaw(t('The linkit profile %label has been deleted.', ['%label' => $profile->label()])); + $this->drupalGet(Url::fromRoute('entity.linkit_profile.collection')); + $this->assertNoText($profile->label(), 'Deleted profile does not exists in the profile collection.'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/src/Utility/LinkitXss.php b/sites/all/modules/contrib/fields/linkit/src/Utility/LinkitXss.php new file mode 100644 index 000000000..1c8b5c890 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/src/Utility/LinkitXss.php @@ -0,0 +1,34 @@ + 'textfield', + '#title' => t('DummyAttribute'), + '#default_value' => $default_value, + '#maxlength' => 255, + '#size' => 40, + ]; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return parent::defaultConfiguration() + [ + 'dummy_setting' => FALSE, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form['dummy_setting'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Dummy setting'), + '#default_value' => $this->configuration['dummy_setting'], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $this->configuration['dummy_setting'] = $form_state->getValue('dummy_setting'); + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/tests/linkit_test/src/Plugin/Linkit/Attribute/DummyAttribute.php b/sites/all/modules/contrib/fields/linkit/tests/linkit_test/src/Plugin/Linkit/Attribute/DummyAttribute.php new file mode 100644 index 000000000..82581674c --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/tests/linkit_test/src/Plugin/Linkit/Attribute/DummyAttribute.php @@ -0,0 +1,37 @@ + 'textfield', + '#title' => t('DummyAttribute'), + '#default_value' => $default_value, + '#maxlength' => 255, + '#size' => 40, + ]; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/tests/linkit_test/src/Plugin/Linkit/Matcher/ConfigurableDummyMatcher.php b/sites/all/modules/contrib/fields/linkit/tests/linkit_test/src/Plugin/Linkit/Matcher/ConfigurableDummyMatcher.php new file mode 100644 index 000000000..8bcc60b16 --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/tests/linkit_test/src/Plugin/Linkit/Matcher/ConfigurableDummyMatcher.php @@ -0,0 +1,83 @@ + FALSE, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form['dummy_setting'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Dummy setting'), + '#default_value' => $this->configuration['dummy_setting'], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $this->configuration['dummy_setting'] = $form_state->getValue('dummy_setting'); + } + + /** + * {@inheritdoc} + */ + public function getMatches($string) { + $matches[] = [ + 'title' => 'Configurable Dummy Matcher title', + 'description' => 'Configurable Dummy Matcher description', + 'path' => 'http://example.com', + 'group' => 'Configurable Dummy Matcher', + ]; + + return $matches; + } + +} diff --git a/sites/all/modules/contrib/fields/linkit/tests/linkit_test/src/Plugin/Linkit/Matcher/DummyMatcher.php b/sites/all/modules/contrib/fields/linkit/tests/linkit_test/src/Plugin/Linkit/Matcher/DummyMatcher.php new file mode 100644 index 000000000..242e7145c --- /dev/null +++ b/sites/all/modules/contrib/fields/linkit/tests/linkit_test/src/Plugin/Linkit/Matcher/DummyMatcher.php @@ -0,0 +1,47 @@ + 'DummyMatcher title', + 'description' => 'DummyMatcher description', + 'path' => 'http://example.com', + 'group' => 'DummyMatcher', + ]; + + return $matches; + } + +}