diff --git a/composer.json b/composer.json index ec50d64342087712c4ba4c88dee9e6414b915b4c..2f545b2350ceca5d867c4748bcfbfb26a247dcfd 100644 --- a/composer.json +++ b/composer.json @@ -135,7 +135,7 @@ "drupal/inline_entity_form": "1.0-rc1", "drupal/libraries": "^3.0@alpha", "drupal/link_attributes": "1.0", - "drupal/linkit": "5.0-beta6", + "drupal/linkit": "5.0-beta10", "drupal/magnific_popup": "1.3", "drupal/mathjax": "^2.7", "drupal/media_entity_browser": "2.0-alpha1", @@ -298,6 +298,9 @@ }, "drupal/smtp": { "2781157": "https://www.drupal.org/files/issues/2018-11-07/2781157-n10.patch" + }, + "drupal/linkit": { + "2712951": "https://www.drupal.org/files/issues/2019-11-27/linkit_for_link_field-2712951-140.patch" } } }, diff --git a/composer.lock b/composer.lock index 4c6822df3af44d756906f4898680a905997b7f9e..38fb2fbb4d62c4103d60cfd04fc6cfb2252ce7d6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0152be0b23fe32d326005f2420b7c6a8", + "content-hash": "80a5fefd459bde7852dfd2301ce78a0e", "packages": [ { "name": "alchemy/zippy", @@ -5441,17 +5441,17 @@ }, { "name": "drupal/linkit", - "version": "5.0.0-beta6", + "version": "5.0.0-beta10", "source": { "type": "git", "url": "https://git.drupalcode.org/project/linkit.git", - "reference": "8.x-5.0-beta6" + "reference": "8.x-5.0-beta10" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/linkit-8.x-5.0-beta6.zip", - "reference": "8.x-5.0-beta6", - "shasum": "ae237702c341b0ee77560fbf4dcb08a77e2da434" + "url": "https://ftp.drupal.org/files/projects/linkit-8.x-5.0-beta10.zip", + "reference": "8.x-5.0-beta10", + "shasum": "9980707514ec63fd1bdc27eebd4af243efda97a6" }, "require": { "drupal/core": "~8.0" @@ -5465,8 +5465,8 @@ "dev-5.x": "5.x-dev" }, "drupal": { - "version": "8.x-5.0-beta6", - "datestamp": "1520552585", + "version": "8.x-5.0-beta10", + "datestamp": "1573783085", "security-coverage": { "status": "not-covered", "message": "Beta releases are not covered by Drupal security advisories." diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 4efb47c236f3c3c43f5e026edc783080178c8d02..7508cad86ed1dba316f08775ea266007e4eafec4 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -5608,18 +5608,18 @@ }, { "name": "drupal/linkit", - "version": "5.0.0-beta6", - "version_normalized": "5.0.0.0-beta6", + "version": "5.0.0-beta10", + "version_normalized": "5.0.0.0-beta10", "source": { "type": "git", "url": "https://git.drupalcode.org/project/linkit.git", - "reference": "8.x-5.0-beta6" + "reference": "8.x-5.0-beta10" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/linkit-8.x-5.0-beta6.zip", - "reference": "8.x-5.0-beta6", - "shasum": "ae237702c341b0ee77560fbf4dcb08a77e2da434" + "url": "https://ftp.drupal.org/files/projects/linkit-8.x-5.0-beta10.zip", + "reference": "8.x-5.0-beta10", + "shasum": "9980707514ec63fd1bdc27eebd4af243efda97a6" }, "require": { "drupal/core": "~8.0" @@ -5633,12 +5633,15 @@ "dev-5.x": "5.x-dev" }, "drupal": { - "version": "8.x-5.0-beta6", - "datestamp": "1520552585", + "version": "8.x-5.0-beta10", + "datestamp": "1573783085", "security-coverage": { "status": "not-covered", "message": "Beta releases are not covered by Drupal security advisories." } + }, + "patches_applied": { + "2712951": "https://www.drupal.org/files/issues/2019-11-27/linkit_for_link_field-2712951-140.patch" } }, "installation-source": "dist", diff --git a/web/modules/linkit/PATCHES.txt b/web/modules/linkit/PATCHES.txt new file mode 100644 index 0000000000000000000000000000000000000000..2c2692ba419ab1ae53ae6423c71d699d21bbbad6 --- /dev/null +++ b/web/modules/linkit/PATCHES.txt @@ -0,0 +1,7 @@ +This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches) +Patches applied to this directory: + +2712951 +Source: https://www.drupal.org/files/issues/2019-11-27/linkit_for_link_field-2712951-140.patch + + diff --git a/web/modules/linkit/README.md b/web/modules/linkit/README.md index 687ce4b3f202a2f5746cf894796c584824d49847..b4ecac50c56cccc2c6b58195b8a1aa7df7c2c392 100644 --- a/web/modules/linkit/README.md +++ b/web/modules/linkit/README.md @@ -1,13 +1,24 @@ -Linkit -=========== +INTRODUCTION +------------ 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. + * For a full description of the module, visit the project page: + https://www.drupal.org/project/linkit + + * To submit bug reports and feature suggestions, or track changes: + https://www.drupal.org/project/issues/linkit + +REQUIREMENTS +------------ + +No special requirements + -Installation +INSTALLATION ------------ * Normal module installation procedure. See @@ -37,8 +48,8 @@ If automatic titles is enabled in the Linkit filter settings, and `<a href data-entity-type data-entity-uuid title>`). -Configuration ------------- +CONFIGURATION +------------- A default Linkit profile will have been installed as a step in the module installation process. The profile will contain information about @@ -47,8 +58,15 @@ which plugins to use. You can create additional profiles at `/admin/config/content/linkit`. -Plugins examples ------------- +PLUGIN EXAMPLES +--------------- There are plugin implementation examples in the linkit_test module bundled with Linkit core. + + +MAINTAINERS +----------- + +Current maintainers: + * Emil Stjerneman (anon) - https://www.drupal.org/user/464598 diff --git a/web/modules/linkit/config/schema/linkit.schema.yml b/web/modules/linkit/config/schema/linkit.schema.yml index 882a2f1cb84fdaeb2434ef20af2efc154ca06726..b8d487cbd0d1851e115a1b30dd5ddec1ddb2ac7e 100644 --- a/web/modules/linkit/config/schema/linkit.schema.yml +++ b/web/modules/linkit/config/schema/linkit.schema.yml @@ -45,6 +45,8 @@ linkit.matcher.entity: type: boolean substitution_type: type: string + limit: + type: integer linkit.matcher.entity:*: type: linkit.matcher.entity @@ -110,3 +112,21 @@ ckeditor.plugin.drupallink: linkit_profile: type: string label: 'Linkit profile' + +# Schema for the Linkit widget. +field.widget.settings.linkit: + type: field.widget.settings.link_default + label: 'Linkit widget settings' + mapping: + linkit_profile: + type: string + label: 'Linkit profile' + +# Schema for the Linkit formatter. +field.formatter.settings.linkit: + type: field.formatter.settings.link + label: 'Linkit format settings' + mapping: + linkit_profile: + type: string + label: 'Linkit profile' diff --git a/web/modules/linkit/css/linkit.autocomplete.css b/web/modules/linkit/css/linkit.autocomplete.css index 8e47730400c707a4b603548fcbfc7a9d1c08f0ec..4159a495f94a7967384c64bee1f9784c370c4aec 100644 --- a/web/modules/linkit/css/linkit.autocomplete.css +++ b/web/modules/linkit/css/linkit.autocomplete.css @@ -3,7 +3,7 @@ * Stylesheet for the Linkit module. */ .js input.form-linkit-autocomplete { - background-image: url(../../../core/misc/throbber-inactive.png); + background-image: url(../images/throbber-inactive.png); background-position: 100% center; /* LTR */ background-repeat: no-repeat; } @@ -13,7 +13,7 @@ } .js input.form-linkit-autocomplete.ui-autocomplete-loading { - background-image: url(../../../core/misc/throbber-active.gif); + background-image: url(../images/throbber-active.gif); background-position: 100% center; /* LTR */ } @@ -23,7 +23,7 @@ /* Override the default jQuery UI theme */ .linkit-ui-autocomplete { - max-height: 330px; + max-height: calc((100vh - 80px)/2); overflow: auto; } @@ -31,24 +31,27 @@ font-size: .9em; } -.linkit-ui-autocomplete.ui-menu .ui-menu-item { - list-style: none; +.linkit-ui-autocomplete.ui-menu .linkit-result-line-wrapper { + margin: 0; padding: 5px 7px; } -.linkit-ui-autocomplete .ui-menu-item.ui-state-focus { - margin: 0; +.linkit-ui-autocomplete.ui-menu .linkit-result-line-wrapper.ui-state-focus, +.linkit-ui-autocomplete.ui-menu .linkit-result-line-wrapper.ui-state-active { border: 0; border-bottom: 1px solid #bfbfbf; background: #0075ba; color: #fff; } -.linkit-result:not(:last-of-type) { +.linkit-result-line:not(:last-of-type) { border-bottom: 1px solid #bfbfbf; } -.linkit-result--group { +.ui-menu .linkit-result-line--group.ui-menu-divider { + margin: 0; + height: auto; + line-height: inherit; padding: 3px; background-color: #e7e7e7; border-bottom: 1px solid #bfbfbf; @@ -58,16 +61,17 @@ color: #555; } -.linkit-result--title { +.linkit-result-line--title { + display: block; font-weight: 600; } -.linkit-result--description { +.linkit-result-line--description { display: block; font-size: 0.9em; line-height: 1.3; } -.linkit-result--description img { +.linkit-result-line--description img { display: block; } diff --git a/web/modules/linkit/images/throbber-active.gif b/web/modules/linkit/images/throbber-active.gif new file mode 100644 index 0000000000000000000000000000000000000000..abf5c5d5b0e72ad28b5c8848af2a75dffad799c3 --- /dev/null +++ b/web/modules/linkit/images/throbber-active.gif @@ -0,0 +1,5 @@ +GIF89a� ���{�������������{Ƶ����������Z��5�ʋ����k�����F��s�������������������������������������������!�NETSCAPE2.0���!���,����� ��a�'��4ɨ� ���bD���@��#�@�+�!2$��lԈ8$M"�eP9�٧��P ��°�tIK�<����y�J�� .=4�0v� ')+!�!���,����� ��J�'�H�|�����~��E���K���pD��C%aD �p��5o���!R�� U� +s�FQb�H^2�N���pk�b��^!�!���,����� ��B�'�Ϙ�(���z�P:��$!��|�( �@�R����`,ETk�*�R^+�؉��-5& +�^��lN���!���,����� ��M�'��h��b���;�x���2*I�Oĕ:}�$�f@�"hd�@GW�A�U0Q�W�Cd�p�D�1~�׃�(�x�3*�!���,����� ��M�'�H�|�����~��E�2.髳�.�=���H�����������`���!f*�"�H^����K�O|�R���+�!���,����� ��C�'�Ϙ�(���z�з��j��|Ѻ��U��n(F�%z�e�1"��Z��ލ;Cl�T��S!�!���,����� ��H�'���h��b���;�x��͖����G���C�)QM�!�Ԑ$h��h��-���"����`9���i� +�!���,����� ��J�'���|�����~��E�2.髳�.�=�Ñ[|~��Cd[~l U�&��r"���," � fh��%�*��K�t!�!���,����� ��D�'�Ϙ�(���z�з��j��|Ѻ��Bt�}b��wCA=�h�jE ��G�k���慍�W�xЩ�!���,����� ��M�'��h��b���;�x���2�#��(*(��i����`XQM)���z-V8T"���l�׃���x'4*�!���,����� ��N�'�H�|�����~��E�2.�+��3G�H ��!q�����>� �RH$��J*B�x��n��> +�G�V)�<ߕ�!���,����� ��C�'�Ϙ�(���z��8�c���KԈ;�d��>��&�H̔�� �n�R2u��R�0��-EVmWW!�; \ No newline at end of file diff --git a/web/modules/linkit/images/throbber-inactive.png b/web/modules/linkit/images/throbber-inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..befbb9f0a1ec779fbce224f57e8cc2c87328fcb4 --- /dev/null +++ b/web/modules/linkit/images/throbber-inactive.png @@ -0,0 +1,3 @@ +�PNG + +��� IHDR������ ���Oy����bKGD������������� pHYs����������tIME�4h�7�����IDAT(ϝ�!�� �����DV�?�?��V"+#�#�S�i���E2��fYJU������!�{��z��Zk�9CD��#�i`ߟ���3��AD(�@U!"PU�֚s��Br�o��<_nTնm�9�¯s������0�[��pj������@fv��(��J��@D��Bݿ҇;쩺���2E��]vΊ>!.�3þ������.����t��9�k��sN��ؔҠ������'�������IEND�B`� \ No newline at end of file diff --git a/web/modules/linkit/js/autocomplete.js b/web/modules/linkit/js/linkit.autocomplete.js similarity index 58% rename from web/modules/linkit/js/autocomplete.js rename to web/modules/linkit/js/linkit.autocomplete.js index 44afac0fdafd06caf00a92af5d16e54f07a94942..b2ccca311d0b6a330fbd650a2ee2d27c40c70065 100644 --- a/web/modules/linkit/js/autocomplete.js +++ b/web/modules/linkit/js/linkit.autocomplete.js @@ -44,9 +44,9 @@ } else { var options = $.extend({ - success: sourceCallbackHandler, - data: {q: term} - }, autocomplete.ajax); + success: sourceCallbackHandler, + data: {q: term} + }, autocomplete.ajax); $.ajax(this.element.attr('data-autocomplete-path'), options); } } @@ -63,22 +63,36 @@ * False to prevent further handlers. */ function selectHandler(event, ui) { - var $form = $(event.target).closest('form'); + var $context = $(event.target).closest('form,fieldset,tr'); if (!ui.item.path) { throw 'Missing path param.' + JSON.stringify(ui.item); } - $('input[name="href_dirty_check"]', $form).val(ui.item.path); + $('input[name="href_dirty_check"]', $context).val(ui.item.path); if (ui.item.entity_type_id || ui.item.entity_uuid || ui.item.substitution_id) { if (!ui.item.entity_type_id || !ui.item.entity_uuid || !ui.item.substitution_id) { throw 'Missing path param.' + JSON.stringify(ui.item); } - - $('input[name="attributes[data-entity-type]"]', $form).val(ui.item.entity_type_id); - $('input[name="attributes[data-entity-uuid]"]', $form).val(ui.item.entity_uuid); - $('input[name="attributes[data-entity-substitution]"]', $form).val(ui.item.substitution_id); + } + $('input[name="attributes[href]"], input[name$="[attributes][href]"]', $context).val(ui.item.path); + $('input[name="attributes[data-entity-type]"], input[name$="[attributes][data-entity-type]"]', $context).val(ui.item.entity_type_id); + $('input[name="attributes[data-entity-uuid]"], input[name$="[attributes][data-entity-uuid]"]', $context).val(ui.item.entity_uuid); + $('input[name="attributes[data-entity-substitution]"], input[name$="[attributes][data-entity-substitution]"]', $context).val(ui.item.substitution_id); + + if (ui.item.label) { + // Automatically set the link title. + var $linkTitle = $(event.target).closest('.form-item').siblings('.form-type-textfield').find('.linkit-widget-title'); + if ($linkTitle.length > 0) { + if (!$linkTitle.val() || $linkTitle.hasClass('link-widget-title--auto')) { + // Set value to the label. + $linkTitle.val(ui.item.label); + + // Flag title as being automatically set. + $linkTitle.addClass('link-widget-title--auto'); + } + } } event.target.value = ui.item.path; @@ -98,14 +112,14 @@ * jQuery collection of the ul element. */ function renderItem(ul, item) { - var $line = $('<li>').addClass('linkit-result'); - $line.append($('<span>').html(item.label).addClass('linkit-result--title')); + var $line = $('<li>').addClass('linkit-result-line'); + var $wrapper = $('<div>').addClass('linkit-result-line-wrapper'); + $wrapper.append($('<span>').html(item.label).addClass('linkit-result-line--title')); if (item.hasOwnProperty('description')) { - $line.append($('<span>').html(item.description).addClass('linkit-result--description')); + $wrapper.append($('<span>').html(item.description).addClass('linkit-result-line--description')); } - - return $line.appendTo(ul); + return $line.append($wrapper).appendTo(ul); } /** @@ -125,7 +139,7 @@ $.each(grouped_items, function (group, items) { if (group.length) { - ul.append('<li class="linkit-result--group">' + group + '</li>'); + ul.append('<li class="linkit-result-line--group ui-menu-divider">' + group + '</li>'); } $.each(items, function (index, item) { @@ -134,6 +148,16 @@ }); } + function focusHandler() { + return false; + } + + function searchHandler(event) { + var options = autocomplete.options; + + return !options.isComposing; + } + /** * Attaches the autocomplete behavior to all required fields. * @@ -149,10 +173,10 @@ // Act on textfields with the "form-linkit-autocomplete" class. var $autocomplete = $(context).find('input.form-linkit-autocomplete').once('linkit-autocomplete'); if ($autocomplete.length) { - $.widget('custom.autocomplete', $.ui.autocomplete, { + $.widget('ui.autocomplete', $.ui.autocomplete, { _create: function () { this._super(); - this.widget().menu('option', 'items', '> :not(.linkit-result--group)'); + this.widget().menu('option', 'items', '> :not(.linkit-result-line--group)'); }, _renderMenu: autocomplete.options.renderMenu, _renderItem: autocomplete.options.renderItem @@ -160,10 +184,37 @@ // Use jQuery UI Autocomplete on the textfield. $autocomplete.autocomplete(autocomplete.options); - $autocomplete.autocomplete('widget').addClass('linkit-ui-autocomplete'); $autocomplete.click(function () { - $autocomplete.autocomplete('search', $autocomplete.val()); + var $this = $(this); + $this.autocomplete('search', $this.val()); + }); + + // Process each item. + $autocomplete.each(function () { + var $uri = $(this); + $uri.autocomplete('widget').addClass('linkit-ui-autocomplete'); + + $uri.closest('.form-item').siblings('.form-type-textfield').find('.linkit-widget-title') + .each(function() { + // Set automatic title flag if title is the same as uri text. + var $title = $(this); + var uriValue = $uri.val(); + if (uriValue && uriValue === $title.val()) { + $title.addClass('link-widget-title--auto'); + } + }) + .change(function () { + // Remove automatic title flag. + $(this).removeClass('link-widget-title--auto'); + }); + }); + + $autocomplete.on('compositionstart.autocomplete', function () { + autocomplete.options.isComposing = true; + }); + $autocomplete.on('compositionend.autocomplete', function () { + autocomplete.options.isComposing = false; }); } }, @@ -183,10 +234,13 @@ cache: {}, options: { source: sourceData, + focus: focusHandler, + search: searchHandler, + select: selectHandler, renderItem: renderItem, renderMenu: renderMenu, - select: selectHandler, - minLength: 1 + minLength: 1, + isComposing: false }, ajax: { dataType: 'json' diff --git a/web/modules/linkit/linkit.info.yml b/web/modules/linkit/linkit.info.yml index 7cc51ac9c6cce3d239819bf16a8ea12b5e1680d1..b3fa92e817cfd08dff6606eb7621ec6b7e17b382 100644 --- a/web/modules/linkit/linkit.info.yml +++ b/web/modules/linkit/linkit.info.yml @@ -1,14 +1,13 @@ name: Linkit type: module description: 'Provides an easy interface for internal and external linking with wysiwyg editors.' -package: Custom -# core: 8.x +package: User interface +core: 8.x configure: entity.linkit_profile.collection test_dependencies: - - imce + - imce:imce -# Information added by Drupal.org packaging script on 2017-09-13 -version: '8.x-5.0-beta6' -core: '8.x' +# Information added by Drupal.org packaging script on 2019-11-15 +version: '8.x-5.0-beta10' project: 'linkit' -datestamp: 1505341914 +datestamp: 1573783087 diff --git a/web/modules/linkit/linkit.libraries.yml b/web/modules/linkit/linkit.libraries.yml index 6f3bce370cb03e8150e9e4bd25be45fea1b7ef64..9e00dd3101bd8c7cc809f7b118b0175fb3c999c0 100644 --- a/web/modules/linkit/linkit.libraries.yml +++ b/web/modules/linkit/linkit.libraries.yml @@ -16,7 +16,7 @@ linkit.base: linkit.autocomplete: version: VERSION js: - js/autocomplete.js: {} + js/linkit.autocomplete.js: {} css: component: css/linkit.autocomplete.css: {} diff --git a/web/modules/linkit/linkit.module b/web/modules/linkit/linkit.module index affeaebf4de8fcd966fd65b909f220d1e047c1e1..14d14258b1715e30a033b0b1295b409a03f2aa4b 100644 --- a/web/modules/linkit/linkit.module +++ b/web/modules/linkit/linkit.module @@ -133,7 +133,6 @@ function linkit_form_editor_link_dialog_submit(array &$form, FormStateInterface } $fields = [ - 'href', 'data-entity-type', 'data-entity-uuid', 'data-entity-substitution', @@ -150,4 +149,5 @@ function linkit_form_editor_link_dialog_submit(array &$form, FormStateInterface } } } + } diff --git a/web/modules/linkit/linkit.routing.yml b/web/modules/linkit/linkit.routing.yml index b81eabd465e1132a8c618223163662671d580043..6a0d68d19f67bf05d1bf7a44a8f3d70eb32be4e4 100644 --- a/web/modules/linkit/linkit.routing.yml +++ b/web/modules/linkit/linkit.routing.yml @@ -65,6 +65,7 @@ linkit.autocomplete: defaults: _controller: '\Drupal\linkit\Controller\AutocompleteController::autocomplete' requirements: + # Access is handled by the matchers. _access: 'TRUE' options: _theme: ajax_base_page diff --git a/web/modules/linkit/src/Annotation/Matcher.php b/web/modules/linkit/src/Annotation/Matcher.php index 349c4342b1862da28811bdcdb6218c9f3430c60b..6892db077d8cb0d13ecc5438d26100e6cb5177b0 100644 --- a/web/modules/linkit/src/Annotation/Matcher.php +++ b/web/modules/linkit/src/Annotation/Matcher.php @@ -28,8 +28,6 @@ class Matcher extends Plugin { /** * The human-readable name of the matcher. * - * The string should be wrapped in a @Translation(). - * * @var \Drupal\Core\Annotation\Translation */ public $label; diff --git a/web/modules/linkit/src/Controller/AutocompleteController.php b/web/modules/linkit/src/Controller/AutocompleteController.php index 1e6e5bd1774845535a3cc27da21697dc19b2bdab..cbb4d2ea88963c3480a95832bb3edf961eadcf31 100644 --- a/web/modules/linkit/src/Controller/AutocompleteController.php +++ b/web/modules/linkit/src/Controller/AutocompleteController.php @@ -2,7 +2,6 @@ namespace Drupal\linkit\Controller; -use Drupal\Component\Utility\Unicode; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\linkit\SuggestionManager; @@ -77,7 +76,7 @@ public function autocomplete(Request $request, $linkit_profile_id) { $this->linkitProfile = $this->linkitProfileStorage->load($linkit_profile_id); $string = $request->query->get('q'); - $suggestionCollection = $this->suggestionManager->getSuggestions($this->linkitProfile, Unicode::strtolower($string)); + $suggestionCollection = $this->suggestionManager->getSuggestions($this->linkitProfile, mb_strtolower($string)); /* * If there are no suggestions from the matcher plugins, we have to add a diff --git a/web/modules/linkit/src/Element/Linkit.php b/web/modules/linkit/src/Element/Linkit.php index 5e533c514fe46be6fda2c0a573d365832f875e20..2bb7963f62a7462fb8e27fcea20c6163ab05739e 100644 --- a/web/modules/linkit/src/Element/Linkit.php +++ b/web/modules/linkit/src/Element/Linkit.php @@ -26,6 +26,7 @@ public function getInfo() { '#process' => [ [$class, 'processLinkitAutocomplete'], [$class, 'processGroup'], + [$class, 'processAjaxForm'], ], '#pre_render' => [ [$class, 'preRenderLinkitElement'], diff --git a/web/modules/linkit/src/Entity/Profile.php b/web/modules/linkit/src/Entity/Profile.php index 651242f83037939cb7c6bfbe150eba6bfe33597e..070e98472ab8b23a7285e7a04f3b3f2bad65d48f 100644 --- a/web/modules/linkit/src/Entity/Profile.php +++ b/web/modules/linkit/src/Entity/Profile.php @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityWithPluginCollectionInterface; use Drupal\linkit\MatcherCollection; use Drupal\linkit\MatcherInterface; +use Drupal\linkit\Plugin\Linkit\Matcher\EntityMatcher; use Drupal\linkit\ProfileInterface; /** @@ -108,6 +109,19 @@ public function getMatcher($instance_id) { return $this->getMatchers()->get($instance_id); } + /** + * {@inheritdoc} + */ + public function getMatcherByEntityType($entity_type_id) { + foreach ($this->getMatchers() as $matcher) { + if ($matcher instanceof EntityMatcher && $matcher->getPluginDefinition()['target_entity'] === $entity_type_id) { + return $matcher; + } + } + + return NULL; + } + /** * {@inheritdoc} */ diff --git a/web/modules/linkit/src/Form/Matcher/AddForm.php b/web/modules/linkit/src/Form/Matcher/AddForm.php index fdc80f077091c0256150beb78f5b2dc848c1b9c4..5007d0475bedeb8924c527aa8c2be85b723a1055 100644 --- a/web/modules/linkit/src/Form/Matcher/AddForm.php +++ b/web/modules/linkit/src/Form/Matcher/AddForm.php @@ -120,7 +120,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { ]); } else { - drupal_set_message($this->t('Added %label matcher.', ['%label' => $plugin->getLabel()])); + $this->messenger()->addMessage($this->t('Added %label matcher.', ['%label' => $plugin->getLabel()])); $form_state->setRedirect('linkit.matchers', [ 'linkit_profile' => $this->linkitProfile->id(), diff --git a/web/modules/linkit/src/Form/Matcher/DeleteForm.php b/web/modules/linkit/src/Form/Matcher/DeleteForm.php index e09b038e0614db3788a22a4f6208391f9365d8cd..e2f14ecb2e5fda091a491e9893385e8ca27614df 100644 --- a/web/modules/linkit/src/Form/Matcher/DeleteForm.php +++ b/web/modules/linkit/src/Form/Matcher/DeleteForm.php @@ -70,7 +70,7 @@ public function buildForm(array $form, FormStateInterface $form_state, ProfileIn 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->messenger()->addMessage($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(), diff --git a/web/modules/linkit/src/Form/Matcher/EditForm.php b/web/modules/linkit/src/Form/Matcher/EditForm.php index ef32d4a95b6b39673e70e680d6520d5ff2d7d042..d847d4e57cb2a379db4479bbfdaf0dc0c11e74b1 100644 --- a/web/modules/linkit/src/Form/Matcher/EditForm.php +++ b/web/modules/linkit/src/Form/Matcher/EditForm.php @@ -74,7 +74,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $this->linkitMatcher->submitConfigurationForm($form, $plugin_data); $this->linkitProfile->save(); - drupal_set_message($this->t('Saved %label configuration.', ['%label' => $this->linkitMatcher->getLabel()])); + $this->messenger()->addMessage($this->t('Saved %label configuration.', ['%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(), diff --git a/web/modules/linkit/src/Form/Profile/AddForm.php b/web/modules/linkit/src/Form/Profile/AddForm.php index 10d8105818f8cee8d05f4101d5ffb8fe7f652a85..eab67d9167027133f213e98474d36290ac241b3c 100644 --- a/web/modules/linkit/src/Form/Profile/AddForm.php +++ b/web/modules/linkit/src/Form/Profile/AddForm.php @@ -7,7 +7,7 @@ /** * Controller for profile addition forms. * - * @see \Drupal\linkit\Profile\FormBase + * @see \Drupal\linkit\Form\Profile\FormBase */ class AddForm extends FormBase { diff --git a/web/modules/linkit/src/Form/Profile/EditForm.php b/web/modules/linkit/src/Form/Profile/EditForm.php index e3e4dfae9f850e3a1aabad865a3a7f81554b9fd9..6a2784de7259f0c4246d6eb5207e0cfd5c2cdf04 100644 --- a/web/modules/linkit/src/Form/Profile/EditForm.php +++ b/web/modules/linkit/src/Form/Profile/EditForm.php @@ -7,7 +7,7 @@ /** * Provides an edit form for profile. * - * @see \Drupal\linkit\Profile\FormBase + * @see \Drupal\linkit\Form\Profile\FormBase */ class EditForm extends FormBase { diff --git a/web/modules/linkit/src/Form/Profile/FormBase.php b/web/modules/linkit/src/Form/Profile/FormBase.php index 7670a42a98b707edb26f02d238c92ae36698e9dc..d11e33c0c0763b057ca5ad826af8744c5540179a 100644 --- a/web/modules/linkit/src/Form/Profile/FormBase.php +++ b/web/modules/linkit/src/Form/Profile/FormBase.php @@ -67,7 +67,7 @@ public function save(array $form, FormStateInterface $form_state) { $edit_link = $this->entity->toLink($this->t('Edit'), 'edit-form')->toString(); switch ($status) { case SAVED_NEW: - drupal_set_message($this->t('Created new profile %label.', ['%label' => $linkit_profile->label()])); + $this->messenger()->addMessage($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(), @@ -75,7 +75,7 @@ public function save(array $form, FormStateInterface $form_state) { break; case SAVED_UPDATED: - drupal_set_message($this->t('Updated profile %label.', ['%label' => $linkit_profile->label()])); + $this->messenger()->addMessage($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->toUrl('edit-form')); break; diff --git a/web/modules/linkit/src/MatcherCollection.php b/web/modules/linkit/src/MatcherCollection.php index ac223b479cb6e70442f5b5845dbe20a1cae9a5c1..c397bc3383ae0a8fb33ee6abbef4ed9ae13fd426 100644 --- a/web/modules/linkit/src/MatcherCollection.php +++ b/web/modules/linkit/src/MatcherCollection.php @@ -16,16 +16,6 @@ class MatcherCollection extends DefaultLazyPluginCollection { */ protected $definitions; - /** - * {@inheritdoc} - * - * @return \Drupal\linkit\MatcherInterface - * A matcher plugin. - */ - public function &get($instance_id) { - return parent::get($instance_id); - } - /** * {@inheritdoc} */ diff --git a/web/modules/linkit/src/MatcherInterface.php b/web/modules/linkit/src/MatcherInterface.php index 4bb4988901768d96cda1f1a095d77254ea915738..e2be93a4a243cd630659786fe1bc3134991fa0c5 100644 --- a/web/modules/linkit/src/MatcherInterface.php +++ b/web/modules/linkit/src/MatcherInterface.php @@ -2,7 +2,8 @@ namespace Drupal\linkit; -use Drupal\Component\Plugin\ConfigurablePluginInterface; +use Drupal\Component\Plugin\ConfigurableInterface; +use Drupal\Component\Plugin\DependentPluginInterface; use Drupal\Component\Plugin\PluginInspectionInterface; /** @@ -13,7 +14,7 @@ * @see \Drupal\linkit\MatcherManager * @see plugin_api */ -interface MatcherInterface extends PluginInspectionInterface, ConfigurablePluginInterface { +interface MatcherInterface extends PluginInspectionInterface, ConfigurableInterface, DependentPluginInterface { /** * Returns the unique ID representing the matcher. diff --git a/web/modules/linkit/src/Plugin/Derivative/EntityMatcherDeriver.php b/web/modules/linkit/src/Plugin/Derivative/EntityMatcherDeriver.php index a51e129286f51db9d66bbe733439d39213e46736..3e6f68988749d291eeeb41903f17c3e041b8e047 100644 --- a/web/modules/linkit/src/Plugin/Derivative/EntityMatcherDeriver.php +++ b/web/modules/linkit/src/Plugin/Derivative/EntityMatcherDeriver.php @@ -51,7 +51,7 @@ public function getDerivativeDefinitions($base_plugin_definition) { // Only entities that has a distinct canonical URI that is not the same // as the edit-form URI will be derived. - if ($entity_type instanceof ContentEntityTypeInterface && $canonical && ($canonical !== $edit_form)) { + if (($entity_type instanceof ContentEntityTypeInterface && $canonical && $canonical !== $edit_form) || $entity_type_id === 'media') { $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(); diff --git a/web/modules/linkit/src/Plugin/Field/FieldFormatter/LinkitFormatter.php b/web/modules/linkit/src/Plugin/Field/FieldFormatter/LinkitFormatter.php new file mode 100644 index 0000000000000000000000000000000000000000..775da51423b9a224168f660d9a9b15d035c9055b --- /dev/null +++ b/web/modules/linkit/src/Plugin/Field/FieldFormatter/LinkitFormatter.php @@ -0,0 +1,188 @@ +<?php + +namespace Drupal\linkit\Plugin\Field\FieldFormatter; + +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Path\PathValidatorInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\link\LinkItemInterface; +use Drupal\link\Plugin\Field\FieldFormatter\LinkFormatter; +use Drupal\linkit\Entity\Profile; +use Drupal\linkit\SubstitutionManagerInterface; +use Drupal\linkit\Utility\LinkitHelper; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Plugin implementation of the 'linkit' formatter. + * + * @FieldFormatter( + * id = "linkit", + * label = @Translation("Linkit"), + * field_types = { + * "link" + * } + * ) + */ +class LinkitFormatter extends LinkFormatter implements ContainerFactoryPluginInterface { + + /** + * The substitution manager. + * + * @var \Drupal\linkit\SubstitutionManagerInterface + */ + protected $substitutionManager; + + /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $plugin_id, + $plugin_definition, + $configuration['field_definition'], + $configuration['settings'], + $configuration['label'], + $configuration['view_mode'], + $configuration['third_party_settings'], + $container->get('path.validator'), + $container->get('plugin.manager.linkit.substitution'), + $container->get('entity_type.manager') + ); + } + + /** + * Constructs a new LinkitFormatter. + * + * @param string $plugin_id + * The plugin_id for the formatter. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition + * The definition of the field to which the formatter is associated. + * @param array $settings + * The formatter settings. + * @param string $label + * The formatter label display setting. + * @param string $view_mode + * The view mode. + * @param array $third_party_settings + * Third party settings. + * @param \Drupal\Core\Path\PathValidatorInterface $path_validator + * The path validator service. + * @param \Drupal\linkit\SubstitutionManagerInterface $substitution_manager + * The substitution manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + */ + public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, PathValidatorInterface $path_validator, SubstitutionManagerInterface $substitution_manager, EntityTypeManagerInterface $entity_type_manager) { + parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings, $path_validator); + $this->substitutionManager = $substitution_manager; + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function defaultSettings() { + return [ + 'linkit_profile' => 'default', + ] + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $elements = parent::settingsForm($form, $form_state); + + $linkit_profiles = $this->entityTypeManager->getStorage('linkit_profile')->loadMultiple(); + + $options = []; + foreach ($linkit_profiles as $linkit_profile) { + $options[$linkit_profile->id()] = $linkit_profile->label(); + } + + $elements['linkit_profile'] = [ + '#type' => 'select', + '#title' => $this->t('Linkit profile'), + '#description' => $this->t('Must be the same as the profile selected on the form display for this field.'), + '#options' => $options, + '#default_value' => $this->getSetting('linkit_profile'), + ]; + + return $elements; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = parent::settingsSummary(); + + $linkit_profile_id = $this->getSetting('linkit_profile'); + $linkit_profile = $this->entityTypeManager->getStorage('linkit_profile')->load($linkit_profile_id); + + if ($linkit_profile) { + $summary[] = $this->t('Linkit profile: @linkit_profile', ['@linkit_profile' => $linkit_profile->label()]); + } + + return $summary; + } + + /** + * {@inheritdoc} + */ + public function viewElements(FieldItemListInterface $items, $langcode) { + $elements = parent::viewElements($items, $langcode); + + // Loop over the elements and substitute the URL. + foreach ($elements as $delta => &$item) { + /** @var \Drupal\link\LinkItemInterface $link_item */ + $link_item = $items->get($delta); + $substituted_url = $this->getSubstitutedUrl($link_item); + // Convert generated URL into a URL object. + if ($substituted_url && ($url = \Drupal::pathValidator()->getUrlIfValid($substituted_url->getGeneratedUrl()))) { + $item['#url'] = $url; + } + } + + return $elements; + } + + /** + * Returns a substitution URL for the given linked item. + * + * In case the items links to an entity use a substituted/generated URL. + * + * @param \Drupal\link\LinkItemInterface $item + * The link item. + * + * @return \Drupal\Core\GeneratedUrl|null + * The substitution URL, or NULL if not able to retrieve it from the item. + */ + protected function getSubstitutedUrl(LinkItemInterface $item) { + if (parse_url($item->uri, PHP_URL_SCHEME) == 'entity') { + if ($entity = LinkitHelper::getEntityFromUri($item->uri)) { + $profile = Profile::load($this->getSettings()['linkit_profile']); + + /** @var \\Drupal\linkit\Plugin\Linkit\Matcher\EntityMatcher $matcher */ + $matcher = $profile->getMatcherByEntityType($entity->getEntityTypeId()); + $substitution_type = $matcher ? $matcher->getConfiguration()['settings']['substitution_type'] : SubstitutionManagerInterface::DEFAULT_SUBSTITUTION; + return $this->substitutionManager->createInstance($substitution_type)->getUrl($entity); + } + } + + return NULL; + } + +} diff --git a/web/modules/linkit/src/Plugin/Field/FieldWidget/LinkitWidget.php b/web/modules/linkit/src/Plugin/Field/FieldWidget/LinkitWidget.php new file mode 100644 index 0000000000000000000000000000000000000000..d2f7823bfb14d1f967ff55df606a3eb42d451c6a --- /dev/null +++ b/web/modules/linkit/src/Plugin/Field/FieldWidget/LinkitWidget.php @@ -0,0 +1,178 @@ +<?php + +namespace Drupal\linkit\Plugin\Field\FieldWidget; + +use Drupal\Core\Url; +use Drupal\link\Plugin\Field\FieldWidget\LinkWidget; +use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\linkit\Utility\LinkitHelper; + +/** + * Plugin implementation of the 'linkit' widget. + * + * @FieldWidget( + * id = "linkit", + * label = @Translation("Linkit"), + * field_types = { + * "link" + * } + * ) + */ +class LinkitWidget extends LinkWidget { + + /** + * {@inheritdoc} + */ + public static function defaultSettings() { + return [ + 'linkit_profile' => 'default', + ] + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ + public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { + $item = $items[$delta]; + $uri = $item->uri; + $uri_scheme = parse_url($uri, PHP_URL_SCHEME); + if (!empty($uri) && empty($uri_scheme)) { + $uri = LinkitHelper::uriFromUserInput($uri); + $uri_scheme = parse_url($uri, PHP_URL_SCHEME); + } + $uri_as_url = !empty($uri) ? Url::fromUri($uri)->toString() : ''; + $linkit_profile_id = $this->getSetting('linkit_profile'); + + // The current field value could have been entered by a different user. + // However, if it is inaccessible to the current user, do not display it + // to them. + $default_allowed = !$item->isEmpty() && (\Drupal::currentUser()->hasPermission('link to any page') || $item->getUrl()->access()); + + if ($default_allowed && $uri_scheme == 'entity') { + $entity = LinkitHelper::getEntityFromUri($uri); + } + + $element['uri'] = [ + '#type' => 'linkit', + '#title' => $this->t('URL'), + '#placeholder' => $this->getSetting('placeholder_url'), + '#default_value' => $default_allowed ? $uri_as_url : NULL, + '#maxlength' => 2048, + '#required' => $element['#required'], + '#description' => $this->t('Start typing to find content or paste a URL and click on the suggestion below.'), + '#autocomplete_route_name' => 'linkit.autocomplete', + '#autocomplete_route_parameters' => [ + 'linkit_profile_id' => $linkit_profile_id, + ], + '#error_no_message' => TRUE, + ]; + + $element['attributes']['href'] = [ + '#type' => 'hidden', + '#default_value' => $default_allowed ? $uri : '', + ]; + + $element['attributes']['data-entity-type'] = [ + '#type' => 'hidden', + '#default_value' => $default_allowed && isset($entity) ? $entity->getEntityTypeId() : '', + ]; + + $element['attributes']['data-entity-uuid'] = [ + '#type' => 'hidden', + '#default_value' => $default_allowed && isset($entity) ? $entity->uuid() : '', + ]; + + $element['attributes']['data-entity-substitution'] = [ + '#type' => 'hidden', + '#default_value' => $default_allowed && isset($entity) ? $entity->getEntityTypeId() == 'file' ? 'file' : 'canonical' : '', + ]; + + $element['title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Link text'), + '#placeholder' => $this->getSetting('placeholder_title'), + '#default_value' => isset($items[$delta]->title) ? $items[$delta]->title : NULL, + '#maxlength' => 255, + '#access' => $this->getFieldSetting('title') != DRUPAL_DISABLED, + '#attributes' => [ + 'class' => ['linkit-widget-title'], + ], + '#error_no_message' => TRUE, + ]; + // Post-process the title field to make it conditionally required if URL is + // non-empty. Omit the validation on the field edit form, since the field + // settings cannot be saved otherwise. + if (!$this->isDefaultValueWidget($form_state) && $this->getFieldSetting('title') == DRUPAL_REQUIRED) { + $element['#element_validate'][] = [get_called_class(), 'validateTitleElement']; + } + + // If cardinality is 1, ensure a proper label is output for the field. + if ($this->fieldDefinition->getFieldStorageDefinition()->getCardinality() == 1) { + // If the link title is disabled, use the field definition label as the + // title of the 'uri' element. + if ($this->getFieldSetting('title') == DRUPAL_DISABLED) { + $element['uri']['#title'] = $element['#title']; + } + // Otherwise wrap everything in a details element. + else { + $element += [ + '#type' => 'fieldset', + ]; + } + } + + return $element; + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $elements = parent::settingsForm($form, $form_state); + + $linkit_profiles = \Drupal::entityTypeManager()->getStorage('linkit_profile')->loadMultiple(); + + $options = []; + foreach ($linkit_profiles as $linkit_profile) { + $options[$linkit_profile->id()] = $linkit_profile->label(); + } + + $elements['linkit_profile'] = [ + '#type' => 'select', + '#title' => $this->t('Linkit profile'), + '#options' => $options, + '#default_value' => $this->getSetting('linkit_profile'), + ]; + + return $elements; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = parent::settingsSummary(); + + $linkit_profile_id = $this->getSetting('linkit_profile'); + $linkit_profile = \Drupal::entityTypeManager()->getStorage('linkit_profile')->load($linkit_profile_id); + + if ($linkit_profile) { + $summary[] = $this->t('Linkit profile: @linkit_profile', ['@linkit_profile' => $linkit_profile->label()]); + } + + return $summary; + } + + /** + * {@inheritdoc} + */ + public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { + foreach ($values as &$value) { + $value['uri'] = LinkitHelper::uriFromUserInput($value['uri']); + $value += ['options' => []]; + } + return $values; + } + +} diff --git a/web/modules/linkit/src/Plugin/Filter/LinkitFilter.php b/web/modules/linkit/src/Plugin/Filter/LinkitFilter.php index 12d5a1c6a717e1178ea23911f1e71805d4522fb4..11f3e0512449f89ad3d5213238ddef1025574663 100644 --- a/web/modules/linkit/src/Plugin/Filter/LinkitFilter.php +++ b/web/modules/linkit/src/Plugin/Filter/LinkitFilter.php @@ -51,6 +51,8 @@ class LinkitFilter extends FilterBase implements ContainerFactoryPluginInterface * The plugin implementation definition. * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository * The entity repository service. + * @param \Drupal\linkit\SubstitutionManagerInterface $substitution_manager + * The substitution manager. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityRepositoryInterface $entity_repository, SubstitutionManagerInterface $substitution_manager) { parent::__construct($configuration, $plugin_id, $plugin_definition); diff --git a/web/modules/linkit/src/Plugin/Linkit/Matcher/EmailMatcher.php b/web/modules/linkit/src/Plugin/Linkit/Matcher/EmailMatcher.php index c9513d94d04f7cd0bb58273b5c252a7c2525145b..d6ea3613069e0f6446c58f7598968097ffcb3f7b 100644 --- a/web/modules/linkit/src/Plugin/Linkit/Matcher/EmailMatcher.php +++ b/web/modules/linkit/src/Plugin/Linkit/Matcher/EmailMatcher.php @@ -23,11 +23,14 @@ class EmailMatcher extends MatcherBase { public function execute($string) { $suggestions = new SuggestionCollection(); + // Strip the mailto: prefix to match only the e-mail part of the string. + $string = str_replace('mailto:', '', $string); + // Check for an e-mail address then return an e-mail match and create a // mail-to link if appropriate. if (filter_var($string, FILTER_VALIDATE_EMAIL)) { $suggestion = new DescriptionSuggestion(); - $suggestion->setLabel($this->t('E-mail @email', ['@email' => $string])) + $suggestion->setLabel($string) ->setPath('mailto:' . Html::escape($string)) ->setGroup($this->t('E-mail')) ->setDescription($this->t('Opens your mail client ready to e-mail @email', ['@email' => $string])); diff --git a/web/modules/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php b/web/modules/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php index 5b7901f72abe0b5f9b47dc983feed3e725ab8242..84c9a44bbaa15f471476d32c066ea04c8c5683a5 100644 --- a/web/modules/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php +++ b/web/modules/linkit/src/Plugin/Linkit/Matcher/EntityMatcher.php @@ -36,6 +36,11 @@ class EntityMatcher extends ConfigurableMatcherBase { use MatcherTokensTrait; + /** + * The default limit for matches. + */ + const DEFAULT_LIMIT = 100; + /** * The database connection. * @@ -161,6 +166,12 @@ public function getSummary() { $summery[] = $this->t('Group by bundle: @bundle_grouping', [ '@bundle_grouping' => $this->configuration['group_by_bundle'] ? $this->t('Yes') : $this->t('No'), ]); + + if (!empty($this->configuration['limit'])) { + $summery[] = $this->t('Limit: @limit', [ + '@limit' => $this->configuration['limit'], + ]); + } } return $summery; @@ -175,6 +186,7 @@ public function defaultConfiguration() { 'bundles' => [], 'group_by_bundle' => FALSE, 'substitution_type' => SubstitutionManagerInterface::DEFAULT_SUBSTITUTION, + 'limit' => static::DEFAULT_LIMIT, ] + parent::defaultConfiguration(); } @@ -257,6 +269,25 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#description' => $this->t('Configure how the selected entity should be transformed into a URL for insertion.'), ]; + $form['limit'] = [ + '#type' => 'details', + '#title' => $this->t('Limit'), + '#open' => TRUE, + ]; + + $form['limit']['limit'] = [ + '#type' => 'select', + '#options' => [ + 0 => $this->t('Unlimited'), + 20 => 20, + 50 => 50, + 100 => 100, + 200 => 200, + ], + '#title' => $this->t('Limit search results'), + '#description' => $this->t('Limit the amount of results displayed when searching.'), + '#default_value' => $this->configuration['limit'], + ]; return $form; } @@ -274,6 +305,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s $this->configuration['bundles'] = $form_state->getValue('bundles'); $this->configuration['group_by_bundle'] = $form_state->getValue('group_by_bundle'); $this->configuration['substitution_type'] = $form_state->getValue('substitution_type'); + $this->configuration['limit'] = $form_state->getValue('limit'); } /** @@ -305,12 +337,19 @@ public function execute($string) { // 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; } $entity = $this->entityRepository->getTranslationFromContext($entity); $suggestion = $this->createSuggestion($entity); + if ($query = parse_url($string, PHP_URL_QUERY)) { + $suggestion->setPath($suggestion->getPath() . '?' . $query); + } + if ($fragment = parse_url($string, PHP_URL_FRAGMENT)) { + $suggestion->setPath($suggestion->getPath() . '#' . $fragment); + } $suggestions->addSuggestion($suggestion); } @@ -352,6 +391,9 @@ protected function buildEntityQuery($search_string) { $query->condition($bundle_key, $this->configuration['bundles'], 'IN'); } + if ($this->configuration['limit']) { + $query->range(0, $this->configuration['limit']); + } $this->addQueryTags($query); return $query; @@ -415,7 +457,7 @@ protected function buildLabel(EntityInterface $entity) { * The matched entity. * * @return string - * The metadata for this entity. + * The metadata for this entity. */ protected function buildDescription(EntityInterface $entity) { $description = \Drupal::token()->replace($this->configuration['metadata'], [$this->targetType => $entity], ['clear' => TRUE]); diff --git a/web/modules/linkit/src/Plugin/Linkit/Matcher/FileMatcher.php b/web/modules/linkit/src/Plugin/Linkit/Matcher/FileMatcher.php index 28252a2fd641e96ac83d04ea4e53b7bd05b4d4e2..9d05089982a2c81c44be38f99e6b9777a33ae35d 100644 --- a/web/modules/linkit/src/Plugin/Linkit/Matcher/FileMatcher.php +++ b/web/modules/linkit/src/Plugin/Linkit/Matcher/FileMatcher.php @@ -190,22 +190,30 @@ protected function buildDescription(EntityInterface $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 = [ - '#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); + if ($this->configuration['images']['show_dimensions'] || $this->configuration['images']['show_thumbnail']) { + $image_factory = \Drupal::service('image.factory'); + $supported_extensions = $image_factory->getSupportedExtensions(); + + // Check if the file extension is supported by the image toolkit. + if (empty(file_validate_extensions($entity, implode(' ', $supported_extensions)))) { + /** @var \Drupal\Core\Image\ImageInterface $image */ + $image = $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 = [ + '#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); + } + } } } diff --git a/web/modules/linkit/src/Plugin/Linkit/Matcher/FrontPageMatcher.php b/web/modules/linkit/src/Plugin/Linkit/Matcher/FrontPageMatcher.php index dd3ca15eaac87e86e15ac64a880bdfe848517988..90347b6ed61aaa68c8710e0422338ed9d8d1a0a6 100644 --- a/web/modules/linkit/src/Plugin/Linkit/Matcher/FrontPageMatcher.php +++ b/web/modules/linkit/src/Plugin/Linkit/Matcher/FrontPageMatcher.php @@ -2,10 +2,10 @@ namespace Drupal\linkit\Plugin\Linkit\Matcher; -use Drupal\Core\Url; use Drupal\linkit\MatcherBase; use Drupal\linkit\Suggestion\DescriptionSuggestion; use Drupal\linkit\Suggestion\SuggestionCollection; +use Drupal\linkit\Utility\LinkitHelper; /** * Provides specific linkit matchers for the front page. @@ -22,12 +22,18 @@ class FrontPageMatcher extends MatcherBase { */ public function execute($string) { $suggestions = new SuggestionCollection(); + $front_path = '/'; + $query_and_fragment = LinkitHelper::getQueryAndFragment($string); + + if (!empty($query_and_fragment)) { + $string = substr($string, 0, strpos($string, $query_and_fragment)); + } // Special for link to front page. - if (strpos($string, 'front') !== FALSE) { + if (strpos($string, 'front') !== FALSE || $string == $front_path) { $suggestion = new DescriptionSuggestion(); $suggestion->setLabel($this->t('Front page')) - ->setPath(Url::fromRoute('<front>')->toString()) + ->setPath($front_path . $query_and_fragment) ->setGroup($this->t('System')) ->setDescription($this->t('The front page for this site.')); diff --git a/web/modules/linkit/src/Plugin/Linkit/Matcher/NodeMatcher.php b/web/modules/linkit/src/Plugin/Linkit/Matcher/NodeMatcher.php index 3a1424cc8a78cd8ab85b400db980e4e1358c1443..f40c7c006e049a7fbaeb92b579ccdbbbd004f9e1 100644 --- a/web/modules/linkit/src/Plugin/Linkit/Matcher/NodeMatcher.php +++ b/web/modules/linkit/src/Plugin/Linkit/Matcher/NodeMatcher.php @@ -3,6 +3,7 @@ namespace Drupal\linkit\Plugin\Linkit\Matcher; use Drupal\Core\Form\FormStateInterface; +use Drupal\node\NodeInterface; /** * Provides specific linkit matchers for the node entity type. @@ -84,9 +85,27 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s protected function buildEntityQuery($search_string) { $query = parent::buildEntityQuery($search_string); - $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); + if ($this->configuration['include_unpublished'] == FALSE) { + $query->condition('status', NodeInterface::PUBLISHED); + } + elseif (count($this->moduleHandler->getImplementations('node_grants')) === 0) { + if (($this->currentUser->hasPermission('bypass node access') || $this->currentUser->hasPermission('view any unpublished content'))) { + // User can see all content, no check necessary. + } + elseif ($this->currentUser->hasPermission('view own unpublished content')) { + // Users with "view own unpublished content" can see only their own. + if ($this->configuration['include_unpublished'] == TRUE) { + $or_condition = $query + ->orConditionGroup() + ->condition('status', NodeInterface::PUBLISHED) + ->condition('uid', $this->currentUser->id()); + $query->condition($or_condition); + } + } + } + else { + // All other users should only get published results. + $query->condition('status', NodeInterface::PUBLISHED); } return $query; diff --git a/web/modules/linkit/src/Plugin/Linkit/Substitution/Media.php b/web/modules/linkit/src/Plugin/Linkit/Substitution/Media.php index a7e02953dd5415ea2bd0cd7dfdc4f9d90d836c04..9cdcb1cead2718470835d9950fec152046f5b880 100644 --- a/web/modules/linkit/src/Plugin/Linkit/Substitution/Media.php +++ b/web/modules/linkit/src/Plugin/Linkit/Substitution/Media.php @@ -5,10 +5,11 @@ use Drupal\Component\Plugin\PluginBase; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Entity\EntityTypeManager; use Drupal\Core\GeneratedUrl; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\file\FileInterface; use Drupal\linkit\SubstitutionInterface; +use Drupal\media\MediaInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -21,30 +22,6 @@ */ class Media extends PluginBase implements SubstitutionInterface, ContainerFactoryPluginInterface { - /** - * The entity type manager. - * - * @var \Drupal\Core\Entity\EntityTypeManager - */ - protected $entityTypeManager; - - /** - * Constructs a Media object. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager - * The entity type manager. - */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManager $entity_type_manager) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->entityTypeManager = $entity_type_manager; - } - /** * {@inheritdoc} */ @@ -58,43 +35,38 @@ public static function create(ContainerInterface $container, array $configuratio } /** - * Get the URL associated with a given entity. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity to get a URL for. - * - * @return \Drupal\Core\GeneratedUrl - * A url to replace. + * {@inheritdoc} */ public function getUrl(EntityInterface $entity) { $url = new GeneratedUrl(); - /** @var \Drupal\media_entity\MediaBundleInterface $media_bundle */ - $media_bundle = $this->entityTypeManager->getStorage('media_bundle')->load($entity->bundle()); - // Default to the canonical URL if the bundle doesn't have a source field. - if (empty($media_bundle->getTypeConfiguration()['source_field'])) { + if (!($entity instanceof MediaInterface)) { + return $url; + } + + $source_field = $entity->getSource()->getSourceFieldDefinition($entity->get('bundle')->entity); + if ($source_field && $entity->hasField($source_field->getName()) && $entity->get($source_field->getName())->entity instanceof FileInterface) { + /** @var \Drupal\file\FileInterface $file */ + $file = $entity->get($source_field->getName())->entity; + $url->setGeneratedUrl(file_create_url($file->getFileUri())); + $url->addCacheableDependency($entity); + return $url; + } + + // If available, fall back to the canonical URL if the bundle doesn't have + // a file source field. + if ($entity->getEntityType()->getLinkTemplate('canonical') != $entity->getEntityType()->getLinkTemplate('edit-form')) { return $entity->toUrl('canonical')->toString(TRUE); } - $source_field = $media_bundle->getTypeConfiguration()['source_field']; - /** @var \Drupal\file\FileInterface $file */ - $file = $entity->{$source_field}->entity; - $url->setGeneratedUrl(file_create_url($file->getFileUri())); - $url->addCacheableDependency($entity); return $url; } /** - * Checks if this substitution plugin is applicable for the given entity type. - * - * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type - * An entity type object. - * - * @return bool - * If the plugin is applicable. + * {@inheritdoc} */ public static function isApplicable(EntityTypeInterface $entity_type) { - return $entity_type->entityClassImplements('Drupal\media_entity\MediaInterface'); + return $entity_type->entityClassImplements('Drupal\media\MediaInterface'); } } diff --git a/web/modules/linkit/src/ProfileInterface.php b/web/modules/linkit/src/ProfileInterface.php index de8c5da84d476dccca5a8c58a0d0f5046ee1dafc..3d2e8cd0148d4a6cffdf2a3bffefed7a0c4323d7 100644 --- a/web/modules/linkit/src/ProfileInterface.php +++ b/web/modules/linkit/src/ProfileInterface.php @@ -38,6 +38,17 @@ public function setDescription($description); */ public function getMatcher($instance_id); + /** + * Returns the first enabled matcher for the given entity type ID. + * + * @param string $entity_type_id + * The entity type ID. + * + * @return \Drupal\linkit\Plugin\Linkit\Matcher\EntityMatcher|null + * An entity matcher instance or null if not found. + */ + public function getMatcherByEntityType($entity_type_id); + /** * Returns the matchers for this profile. * diff --git a/web/modules/linkit/src/Tests/ProfileCreationTrait.php b/web/modules/linkit/src/Tests/ProfileCreationTrait.php index e662aaf1412f8dd11aa08d19368e84fdd618aa9f..2343035689e58c5a712464ed0b39fe6deffbf40a 100644 --- a/web/modules/linkit/src/Tests/ProfileCreationTrait.php +++ b/web/modules/linkit/src/Tests/ProfileCreationTrait.php @@ -2,7 +2,6 @@ namespace Drupal\linkit\Tests; -use Drupal\Component\Utility\Unicode; use Drupal\linkit\Entity\Profile; /** @@ -29,7 +28,7 @@ trait ProfileCreationTrait { protected function createProfile(array $settings = []) { // Populate defaults array. $settings += [ - 'id' => Unicode::strtolower($this->randomMachineName()), + 'id' => mb_strtolower($this->randomMachineName()), 'label' => $this->randomMachineName(), ]; diff --git a/web/modules/linkit/src/Utility/LinkitHelper.php b/web/modules/linkit/src/Utility/LinkitHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..56e9fab5bcb955600b9742127e78d5472f84e5c9 --- /dev/null +++ b/web/modules/linkit/src/Utility/LinkitHelper.php @@ -0,0 +1,192 @@ +<?php + +namespace Drupal\linkit\Utility; + +use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Url; + +/** + * Provides helper to operate on URIs. + */ +class LinkitHelper { + + /** + * Load the entity referenced by an entity scheme uri. + * + * @param string $uri + * An internal uri string representing an entity path, such as + * "entity:node/23". + * + * @return \Drupal\Core\Entity\EntityInterface|null + * The most appropriate translation of the entity that matches the given + * uri, or NULL if could not match any entity. + */ + public static function getEntityFromUri($uri) { + // Stripe out potential query and fragment from the uri. + $uri = strtok(strtok($uri, "?"), "#"); + list($entity_type, $entity_id) = explode('/', substr($uri, 7), 2); + $entity_manager = \Drupal::entityTypeManager(); + if ($entity_manager->getDefinition($entity_type, FALSE)) { + if ($entity = $entity_manager->getStorage($entity_type)->load($entity_id)) { + return \Drupal::service('entity.repository')->getTranslationFromContext($entity); + } + } + + return NULL; + } + + /** + * Returns a processed uri with a proper scheme (if applicable). + * + * Turns the internal links into uri strings. + * + * @param string $input + * The raw (or processed) input. + * + * @return string|null + * The uri string or null if the input is empty. + */ + public static function uriFromUserInput($input) { + if (empty($input)) { + return NULL; + } + + $host = parse_url($input, PHP_URL_HOST); + $scheme = parse_url($input, PHP_URL_SCHEME); + + if ($scheme == 'mailto') { + return $input; + } + + if (UrlHelper::isExternal($input)) { + if (UrlHelper::externalIsLocal($input, \Drupal::request()->getSchemeAndHttpHost())) { + // The link points to this domain. Make it relative to perform an entity + // lookup. + $host_end = strpos($input, $host) + strlen($host); + $input = substr($input, $host_end); + } + else { + // This link is really external. + return $input; + } + } + + // Make sure the URI starts with a slash, otherwise the Url's factory + // methods will throw exceptions. + $starts_with_a_slash = strpos($input, '/') === 0; + $is_front = substr($input, 0, 7) === '<front>'; + if (!$scheme && !$is_front && !$starts_with_a_slash) { + $input = "/$input"; + } + + $entity = self::getEntityFromUserInput($input); + if ($entity) { + return 'entity:' . $entity->getEntityTypeId() . '/' . $entity->id() . static::getQueryAndFragment($input); + } + + // It's a relative link. If it's a file, store it as `base:`. Otherwise it's + // most likely internal. + $public_files_dir = \Drupal::service('stream_wrapper_manager') + ->getViaScheme('public') + ->getDirectoryPath(); + if (strpos($input, "/$public_files_dir") === 0) { + return "base:$input"; + } + else { + return "internal:$input"; + } + } + + /** + * Tries to convert an uri into an entity in multiple ways. + * + * @param string $input + * A uri or a path. + * + * @return \Drupal\Core\Entity\EntityInterface|null + * The entity if found, null otherwise. + */ + public static function getEntityFromUserInput($input) { + $scheme = parse_url($input, PHP_URL_SCHEME); + + // Check if it's an entity URI (e.g. entity:node/1). + if ($scheme === 'entity' && ($entity = static::getEntityFromUri($input))) { + return $entity; + } + + // If not, it can be a path pointing to an entity. + if (!$scheme) { + // Which can be hidden behind an alias in any of the site's languages. + $input = 'internal:' . static::getPathByAlias($input); + } + + try { + $route_name = Url::fromUri($input)->getRouteName(); + if ($route_name != "entity.node.edit_form" && $route_name != "node.add") { + $params = Url::fromUri($input)->getRouteParameters(); + $possibly_an_entity_type = key($params); + $entity = \Drupal::entityTypeManager() + ->getStorage($possibly_an_entity_type) + ->load($params[$possibly_an_entity_type]); + return \Drupal::service('entity.repository') + ->getTranslationFromContext($entity); + } + } + catch (\Exception $e) { + // Or not. + } + + return NULL; + } + + /** + * Tries to translate the given raw url path into an internal one. + * + * @param string $input + * Raw URL string consisting of a path and, optionally, query and fragment. + * + * @return string + * The internal path if any matched. The input string otherwise. + */ + public static function getPathByAlias($input) { + $prefixes = \Drupal::config('language.negotiation')->get('url.prefixes'); + /** @var \Drupal\Core\Path\AliasManagerInterface $path_alias_manager */ + $path_alias_manager = \Drupal::service('path.alias_manager'); + /** @var \Drupal\Core\Language\LanguageManagerInterface $language_manager */ + $language_manager = \Drupal::service('language_manager'); + + foreach ($language_manager->getLanguages() as $language) { + $prefix = $prefixes[$language->getId()]; + // Strip the language prefix. + $initial_path = parse_url($input, PHP_URL_PATH); + $path_without_prefix = preg_replace("/^\/$prefix\//", '/', $initial_path); + $path_resolved = $path_alias_manager->getPathByAlias($path_without_prefix, $language->getId()); + if ($path_resolved !== $path_without_prefix) { + return $path_resolved . static::getQueryAndFragment($input); + } + } + + return $input; + } + + /** + * Returns the query and fragment part of a given URL string. + * + * @param string $input + * An arbitrary URL. + * + * @return string + * The query and fragment parts or an empty string. + */ + public static function getQueryAndFragment($input) { + $result = ''; + if ($query = parse_url($input, PHP_URL_QUERY)) { + $result .= "?$query"; + } + if ($fragment = parse_url($input, PHP_URL_FRAGMENT)) { + $result .= "#$fragment"; + } + return $result; + } + +} diff --git a/web/modules/linkit/tests/fixtures/update/linkit-additions.php b/web/modules/linkit/tests/fixtures/update/linkit-additions.php index 07a60f148de409a1a1c869214d4ee4f54baa8f12..e0ffb5ae53cdb88255d8543ec746244b67021176 100644 --- a/web/modules/linkit/tests/fixtures/update/linkit-additions.php +++ b/web/modules/linkit/tests/fixtures/update/linkit-additions.php @@ -16,7 +16,6 @@ // Configuration for linkit profiles. $configs = []; $configs[] = Yaml::decode(file_get_contents(__DIR__ . '/linkit.linkit_profile.test_profile.yml')); -$configs[] = Yaml::decode(file_get_contents(__DIR__ . '/linkit.linkit_profile.test_profile_with_imce.yml')); foreach ($configs as $config) { $connection->insert('config') ->fields([ diff --git a/web/modules/linkit/tests/fixtures/update/linkit.linkit_profile.test_profile.yml b/web/modules/linkit/tests/fixtures/update/linkit.linkit_profile.test_profile.yml index bee4523d0cfbc3e4ac785165bc109d1ab351a3f5..462cc683691ea831bd64d2804febbbd8b4713094 100644 --- a/web/modules/linkit/tests/fixtures/update/linkit.linkit_profile.test_profile.yml +++ b/web/modules/linkit/tests/fixtures/update/linkit.linkit_profile.test_profile.yml @@ -5,6 +5,11 @@ dependencies: module: - file - node + - imce +third_party_settings: + imce: + use: 1 + scheme: public id: test_profile label: 'Test profile' description: 'This is a test profile' diff --git a/web/modules/linkit/tests/fixtures/update/linkit.linkit_profile.test_profile_with_imce.yml b/web/modules/linkit/tests/fixtures/update/linkit.linkit_profile.test_profile_with_imce.yml deleted file mode 100644 index c906d2f8ccbfe53b1f5ce077a3e128fe9e54e706..0000000000000000000000000000000000000000 --- a/web/modules/linkit/tests/fixtures/update/linkit.linkit_profile.test_profile_with_imce.yml +++ /dev/null @@ -1,24 +0,0 @@ -uuid: 3cfc827f-07ff-456e-86db-a831e3770a03 -langcode: en -status: true -dependencies: - module: - - imce - - node -third_party_settings: - imce: - use: 1 - scheme: public -id: test_profile_imce -label: 'Test profile imce' -description: 'This is a test profile with imce settings' -matchers: - 556010a3-e317-48b3-b4ed-854c10f4b950: - uuid: 556010a3-e317-48b3-b4ed-854c10f4b950 - id: 'entity:node' - weight: 0 - settings: - result_description: 'by [node:author] | [node:created:medium]' - bundles: { } - group_by_bundle: false - include_unpublished: false diff --git a/web/modules/linkit/tests/linkit_media_test/config/schema/linkit_media_test.schema.yml b/web/modules/linkit/tests/linkit_media_test/config/schema/linkit_media_test.schema.yml deleted file mode 100644 index 2e13885b5ac350c26e297a1d7edb96962630a3d6..0000000000000000000000000000000000000000 --- a/web/modules/linkit/tests/linkit_media_test/config/schema/linkit_media_test.schema.yml +++ /dev/null @@ -1,7 +0,0 @@ -media_entity.bundle.type.test_type: - type: mapping - label: 'Test type configuration' - mapping: - source_field: - type: string - label: 'Field with source information' diff --git a/web/modules/linkit/tests/linkit_media_test/config/schema/linkit_test.schema.yml b/web/modules/linkit/tests/linkit_media_test/config/schema/linkit_test.schema.yml deleted file mode 100644 index ce29e8fcd5c02ce464239f12d06d66fb3e089338..0000000000000000000000000000000000000000 --- a/web/modules/linkit/tests/linkit_media_test/config/schema/linkit_test.schema.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Schema for the configuration files of the Linkit test module. - -# Plugin \Drupal\linkit_test\Plugin\Linkit\Matcher\ConfigurableDummyMatcher -linkit.matcher.configurable_dummy_matcher: - type: linkit.matcher - mapping: - dummy_setting: - type: boolean diff --git a/web/modules/linkit/tests/linkit_media_test/linkit_media_test.info.yml b/web/modules/linkit/tests/linkit_media_test/linkit_media_test.info.yml deleted file mode 100644 index 423f72a1abcc2bb263054686f54e0657b40b25e4..0000000000000000000000000000000000000000 --- a/web/modules/linkit/tests/linkit_media_test/linkit_media_test.info.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: 'Linkit media test module' -description: 'Support module for Linkit media testing.' -package: Testing -type: module -# core: 8.x -dependencies: - - linkit - - field - - text - - media_entity - -# Information added by Drupal.org packaging script on 2017-09-13 -version: '8.x-5.0-beta6' -core: '8.x' -project: 'linkit' -datestamp: 1505341914 diff --git a/web/modules/linkit/tests/linkit_media_test/src/Plugin/MediaEntity/Type/TestType.php b/web/modules/linkit/tests/linkit_media_test/src/Plugin/MediaEntity/Type/TestType.php deleted file mode 100644 index a14b5cb501c3b61334a64e871ec83f6a19c64eb0..0000000000000000000000000000000000000000 --- a/web/modules/linkit/tests/linkit_media_test/src/Plugin/MediaEntity/Type/TestType.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -namespace Drupal\linkit_media_test\Plugin\MediaEntity\Type; - -use Drupal\media_entity\Plugin\MediaEntity\Type\Generic; - -/** - * Provides generic media type. - * - * @MediaType( - * id = "test_type", - * label = @Translation("Test type"), - * description = @Translation("Test media type.") - * ) - */ -class TestType extends Generic { - - /** - * {@inheritdoc} - */ - public function defaultConfiguration() { - return [ - 'source_field' => 'field_media_file', - ]; - } - -} diff --git a/web/modules/linkit/tests/linkit_test/linkit_test.info.yml b/web/modules/linkit/tests/linkit_test/linkit_test.info.yml index 1a5b3d653a5dae0a36e06bd1d74a254b2071e8ea..f28ce10679e5de23a39892741ddb2c505d434e4e 100644 --- a/web/modules/linkit/tests/linkit_test/linkit_test.info.yml +++ b/web/modules/linkit/tests/linkit_test/linkit_test.info.yml @@ -3,14 +3,13 @@ description: 'Support module for Linkit testing.' package: Testing type: module # version: VERSION -# core: 8.x +core: 8.x dependencies: - - linkit - - field - - text + - linkit:linkit + - drupal:field + - drupal:text -# Information added by Drupal.org packaging script on 2017-09-13 -version: '8.x-5.0-beta6' -core: '8.x' +# Information added by Drupal.org packaging script on 2019-11-15 +version: '8.x-5.0-beta10' project: 'linkit' -datestamp: 1505341914 +datestamp: 1573783087 diff --git a/web/modules/linkit/src/Tests/Update/LinkitUpdateTest.php b/web/modules/linkit/tests/src/Functional/LinkitUpdateTest.php similarity index 64% rename from web/modules/linkit/src/Tests/Update/LinkitUpdateTest.php rename to web/modules/linkit/tests/src/Functional/LinkitUpdateTest.php index 38de523e4f6cf4d2e31ae4701159bfd258c24952..54567fc6418272c7edb64f66f69191b53367a7d5 100644 --- a/web/modules/linkit/src/Tests/Update/LinkitUpdateTest.php +++ b/web/modules/linkit/tests/src/Functional/LinkitUpdateTest.php @@ -1,9 +1,9 @@ <?php -namespace Drupal\linkit\Tests\Update; +namespace Drupal\Tests\linkit\Functional; use Drupal\filter\Entity\FilterFormat; -use Drupal\system\Tests\Update\UpdatePathTestBase; +use Drupal\FunctionalTests\Update\UpdatePathTestBase; /** * Tests Linkit upgrade paths. @@ -38,9 +38,7 @@ protected function setDatabaseDumpFiles() { } /** - * Tests linkit_update_8500(). - * - * @see linkit_update_8500() + * Tests linkit_update_X. */ public function testLinkitUpdate8500() { $editor = $this->configFactory->get('editor.editor.format_1'); @@ -55,29 +53,40 @@ public function testLinkitUpdate8500() { $this->assertTrue($editor->get('settings.plugins.linkit'), 'We got old linkit settings in the editor configuration.'); $format_3_linkit_profile = $editor->get('settings.plugins.linkit.linkit_profile'); + $test_profile = $this->configFactory->get('linkit.linkit_profile.test_profile'); + $this->assertNotNull($test_profile->get('matchers.fc48c807-2a9c-44eb-b86b-7e134c1aa252.settings.result_description'), 'Profile have result_description'); + $this->assertNotNull($test_profile->get('third_party_settings.imce.use'), 'Profile have imce use'); + $this->assertNotNull($test_profile->get('third_party_settings.imce.scheme'), 'Profile have imce scheme'); + $this->runUpdates(); $test_profile = $this->configFactory->get('linkit.linkit_profile.test_profile'); - $this->assertEqual(NULL, $test_profile->get('attributes'), 'Attributes are deleted from the profile.'); + $this->assertEquals(NULL, $test_profile->get('attributes'), 'Attributes are deleted from the profile.'); + $this->assertEquals('canonical', $test_profile->get('matchers.fc48c807-2a9c-44eb-b86b-7e134c1aa252.settings.substitution_type'), 'Content matcher has a substitution type of canonical.'); + $this->assertEquals('file', $test_profile->get('matchers.b8d6d672-6377-493f-b492-3cc69511cf17.settings.substitution_type'), 'File matcher has a substitution type of file.'); + $this->assertNull($test_profile->get('matchers.fc48c807-2a9c-44eb-b86b-7e134c1aa252.settings.result_description'), 'Profile does not have result_description'); + $this->assertNotNull($test_profile->get('matchers.fc48c807-2a9c-44eb-b86b-7e134c1aa252.settings.metadata'), 'Profile have metadata'); + $this->assertNull($test_profile->get('third_party_settings.imce.use'), 'Profile does not have imce use'); + $this->assertNull($test_profile->get('third_party_settings.imce.scheme'), 'Profile does not have imce scheme'); $editor = $this->configFactory->get('editor.editor.format_1'); $this->assertNull($editor->get('settings.plugins.linkit'), 'Old linkit settings in the editor configuration is removed.'); - $this->assertEqual($editor->get('settings.toolbar.rows.0.1.items.0'), 'DrupalLink', 'Drupal link plugin is in the toolbar.'); - $this->assertNotEqual($editor->get('settings.toolbar.rows.0.1.items.1'), 'Linkit', 'Linkit plugin is removed from the toolbar.'); + $this->assertEquals($editor->get('settings.toolbar.rows.0.1.items.0'), 'DrupalLink', 'Drupal link plugin is in the toolbar.'); + $this->assertNotEquals($editor->get('settings.toolbar.rows.0.1.items.1'), 'Linkit', 'Linkit plugin is removed from the toolbar.'); $this->assertTrue($editor->get('settings.plugins.drupallink.linkit_enabled'), 'Drupal link plugin has linkit enabled.'); - $this->assertEqual($editor->get('settings.plugins.drupallink.linkit_profile'), $format_1_linkit_profile, 'Drupal link plugin uses the same profile as the old linkit plugin.'); + $this->assertEquals($editor->get('settings.plugins.drupallink.linkit_profile'), $format_1_linkit_profile, 'Drupal link plugin uses the same profile as the old linkit plugin.'); $editor = $this->configFactory->get('editor.editor.format_2'); $this->assertNull($editor->get('settings.plugins.linkit'), 'Old linkit settings in the editor configuration is removed.'); - $this->assertEqual($editor->get('settings.toolbar.rows.0.1.items.0'), 'DrupalLink', 'Drupal link plugin is in the toolbar.'); + $this->assertEquals($editor->get('settings.toolbar.rows.0.1.items.0'), 'DrupalLink', 'Drupal link plugin is in the toolbar.'); $this->assertTrue($editor->get('settings.plugins.drupallink.linkit_enabled'), 'Drupal link plugin has linkit enabled.'); - $this->assertEqual($editor->get('settings.plugins.drupallink.linkit_profile'), $format_2_linkit_profile, 'Drupal link plugin uses the same profile as the old linkit plugin.'); + $this->assertEquals($editor->get('settings.plugins.drupallink.linkit_profile'), $format_2_linkit_profile, 'Drupal link plugin uses the same profile as the old linkit plugin.'); $editor = $this->configFactory->get('editor.editor.format_3'); $this->assertNull($editor->get('settings.plugins.linkit'), 'Old linkit settings in the editor configuration is removed.'); - $this->assertEqual($editor->get('settings.toolbar.rows.0.0.items.0'), 'DrupalLink', 'Drupal link plugin is in the toolbar.'); + $this->assertEquals($editor->get('settings.toolbar.rows.0.0.items.0'), 'DrupalLink', 'Drupal link plugin is in the toolbar.'); $this->assertTrue($editor->get('settings.plugins.drupallink.linkit_enabled'), 'Drupal link plugin has linkit enabled.'); - $this->assertEqual($editor->get('settings.plugins.drupallink.linkit_profile'), $format_3_linkit_profile, 'Drupal link plugin uses the same profile as the old linkit plugin.'); + $this->assertEquals($editor->get('settings.plugins.drupallink.linkit_profile'), $format_3_linkit_profile, 'Drupal link plugin uses the same profile as the old linkit plugin.'); $format = $this->configFactory->get('filter.format.format_1'); $this->assertNotNull($format->get('filters.linkit'), 'Linkit filter is enabled.'); @@ -92,61 +101,11 @@ public function testLinkitUpdate8500() { $htmlRestrictions = FilterFormat::load('format_1')->getHtmlRestrictions(); $this->assertTrue(array_key_exists("data-entity-type", $htmlRestrictions['allowed']['a'])); $this->assertTrue(array_key_exists("data-entity-uuid", $htmlRestrictions['allowed']['a'])); + $this->assertTrue(array_key_exists("data-entity-substitution", $htmlRestrictions['allowed']['a'])); $htmlRestrictions = FilterFormat::load('format_3')->getHtmlRestrictions(); $this->assertTrue(array_key_exists("data-entity-type", $htmlRestrictions['allowed']['a'])); $this->assertTrue(array_key_exists("data-entity-uuid", $htmlRestrictions['allowed']['a'])); } - /** - * Tests linkit_update_8501(). - * - * @see linkit_update_8501() - */ - public function testLinkitUpdate8501() { - $this->runUpdates(); - - $test_profile = $this->configFactory->get('linkit.linkit_profile.test_profile'); - $this->assertEqual('canonical', $test_profile->get('matchers.fc48c807-2a9c-44eb-b86b-7e134c1aa252.settings.substitution_type'), 'Content matcher has a substitution type of canonical.'); - $this->assertEqual('file', $test_profile->get('matchers.b8d6d672-6377-493f-b492-3cc69511cf17.settings.substitution_type'), 'File matcher has a substitution type of file.'); - - $htmlRestrictions = FilterFormat::load('format_1')->getHtmlRestrictions(); - $this->assertTrue(array_key_exists("data-entity-type", $htmlRestrictions['allowed']['a'])); - $this->assertTrue(array_key_exists("data-entity-uuid", $htmlRestrictions['allowed']['a'])); - $this->assertTrue(array_key_exists("data-entity-substitution", $htmlRestrictions['allowed']['a'])); - } - - /** - * Tests linkit_update_8502(). - * - * @see linkit_update_8502() - */ - public function testLinkitUpdate8502() { - $test_profile = $this->configFactory->get('linkit.linkit_profile.test_profile'); - $this->assertNotNull($test_profile->get('matchers.fc48c807-2a9c-44eb-b86b-7e134c1aa252.settings.result_description'), 'Profile have result_description'); - - $this->runUpdates(); - - $test_profile = $this->configFactory->get('linkit.linkit_profile.test_profile'); - $this->assertNull($test_profile->get('matchers.fc48c807-2a9c-44eb-b86b-7e134c1aa252.settings.result_description'), 'Profile does not have result_description'); - $this->assertNotNull($test_profile->get('matchers.fc48c807-2a9c-44eb-b86b-7e134c1aa252.settings.metadata'), 'Profile have metadata'); - } - - /** - * Tests linkit_update_8503(). - * - * @see linkit_update_8503() - */ - public function testLinkitUpdate8503() { - $test_profile = $this->configFactory->get('linkit.linkit_profile.test_profile_imce'); - $this->assertNotNull($test_profile->get('third_party_settings.imce.use'), 'Profile have imce use'); - $this->assertNotNull($test_profile->get('third_party_settings.imce.scheme'), 'Profile have imce scheme'); - - $this->runUpdates(); - - $test_profile = $this->configFactory->get('linkit.linkit_profile.test_profile_imce'); - $this->assertNull($test_profile->get('third_party_settings.imce.use'), 'Profile does not have imce use'); - $this->assertNull($test_profile->get('third_party_settings.imce.scheme'), 'Profile does not have imce scheme'); - } - } diff --git a/web/modules/linkit/tests/src/Functional/MatcherAdminTest.php b/web/modules/linkit/tests/src/Functional/MatcherAdminTest.php index 2cf52ec1d7ba4b272dd1d84e813625fc4af851f6..217afb6043ed087b7b0a4b6c96b35487ff9a1df3 100644 --- a/web/modules/linkit/tests/src/Functional/MatcherAdminTest.php +++ b/web/modules/linkit/tests/src/Functional/MatcherAdminTest.php @@ -45,7 +45,7 @@ public function testOverview() { $this->drupalLogin($this->adminUser); $this->drupalGet('/admin/config/content/linkit/manage/' . $this->linkitProfile->id() . '/matchers'); - $this->assertSession()->pageTextContains(t('No matchers added.')); + $this->assertSession()->pageTextContains('No matchers added.'); // Make sure the 'Add matcher' action link is present. $this->assertSession()->linkByHrefExists('/admin/config/content/linkit/manage/' . $this->linkitProfile->id() . '/matchers/add'); @@ -62,17 +62,10 @@ public function testAdd() { // Create matcher. $edit = []; $edit['plugin'] = 'dummy_matcher'; - $this->submitForm($edit, t('Save and continue')); + $this->submitForm($edit, 'Save and continue'); - // Reload the 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->assertSession()->responseContains(t('Added %label matcher.', ['%label' => $plugin->getLabel()])); - $this->assertSession()->pageTextNotContains(t('No matchers added.')); + $this->assertSession()->pageTextContains('Added Dummy Matcher matcher.'); + $this->assertSession()->pageTextNotContains('No matchers added.'); } /** @@ -86,7 +79,7 @@ public function testAddConfigurable() { // Create configurable matcher. $edit = []; $edit['plugin'] = 'configurable_dummy_matcher'; - $this->submitForm($edit, t('Save and continue')); + $this->submitForm($edit, 'Save and continue'); // Reload the profile. $this->linkitProfile = Profile::load($this->linkitProfile->id()); @@ -98,7 +91,7 @@ public function testAddConfigurable() { $this->assertSession()->addressEquals('/admin/config/content/linkit/manage/' . $this->linkitProfile->id() . '/matchers/' . $plugin->getUuid()); $this->drupalGet('/admin/config/content/linkit/manage/' . $this->linkitProfile->id() . '/matchers'); - $this->assertSession()->pageTextNotContains(t('No matchers added.')); + $this->assertSession()->pageTextNotContains('No matchers added.'); } /** @@ -126,9 +119,9 @@ public function testDelete() { $this->drupalGet('/admin/config/content/linkit/manage/' . $this->linkitProfile->id() . '/matchers/' . $plugin_uuid . '/delete'); $this->submitForm([], t('Confirm')); - $this->assertSession()->responseContains(t('The matcher %plugin has been deleted.', ['%plugin' => $plugin->getLabel()])); + $this->assertSession()->pageTextContains('The matcher Dummy Matcher has been deleted.'); $this->assertSession()->addressEquals('/admin/config/content/linkit/manage/' . $this->linkitProfile->id() . '/matchers'); - $this->assertSession()->pageTextContains(t('No matchers added.')); + $this->assertSession()->pageTextContains('No matchers added.'); /** @var \Drupal\linkit\Entity\Profile $updated_profile */ $updated_profile = Profile::load($this->linkitProfile->id()); diff --git a/web/modules/linkit/tests/src/Functional/ProfileAdminTest.php b/web/modules/linkit/tests/src/Functional/ProfileAdminTest.php index 3b840b9ab4447b9911aa950e3036eedb5915838f..91620a44d05c4a06c1c3b8ab0dc5056edc068c57 100644 --- a/web/modules/linkit/tests/src/Functional/ProfileAdminTest.php +++ b/web/modules/linkit/tests/src/Functional/ProfileAdminTest.php @@ -2,7 +2,6 @@ namespace Drupal\Tests\linkit\Functional; -use Drupal\Component\Utility\Unicode; use Drupal\linkit\Tests\ProfileCreationTrait; /** @@ -62,13 +61,13 @@ public function testProfileCreation() { // Create a profile. $edit = []; - $edit['label'] = Unicode::strtolower($this->randomMachineName()); - $edit['id'] = Unicode::strtolower($this->randomMachineName()); + $edit['label'] = mb_strtolower($this->randomMachineName()); + $edit['id'] = mb_strtolower($this->randomMachineName()); $edit['description'] = $this->randomMachineName(16); - $this->submitForm($edit, t('Save and manage matchers')); + $this->submitForm($edit, 'Save and manage matchers'); // Make sure that the new profile was saved properly. - $this->assertSession()->responseContains(t('Created new profile %label.', ['%label' => $edit['label']])); + $this->assertSession()->pageTextContains('Created new profile ' . $edit['label']); $this->drupalGet('/admin/config/content/linkit'); $this->assertSession()->pageTextContains($edit['label']); } @@ -96,10 +95,10 @@ public function testProfileUpdate() { $edit = []; $edit['label'] = $this->randomMachineName(); $edit['description'] = $this->randomMachineName(16); - $this->submitForm($edit, t('Update profile')); + $this->submitForm($edit, 'Update profile'); // Make sure that the profile was updated properly. - $this->assertSession()->responseContains(t('Updated profile %label.', ['%label' => $edit['label']])); + $this->assertSession()->pageTextContains('Updated profile ' . $edit['label']); $this->drupalGet('/admin/config/content/linkit'); $this->assertSession()->pageTextContains($edit['label']); } @@ -117,12 +116,12 @@ public function testProfileDelete() { $this->assertSession()->statusCodeEquals(200); // Delete the profile. - $this->submitForm([], t('Delete')); + $this->submitForm([], 'Delete'); // Make sure that the profile was deleted properly. - $this->assertSession()->responseContains(t('The linkit profile %label has been deleted.', ['%label' => $profile->label()])); + $this->assertSession()->pageTextContains('The linkit profile ' . $profile->label() . ' has been deleted.'); $this->drupalGet('/admin/config/content/linkit'); - $this->assertSession()->responseNotContains($profile->label()); + $this->assertSession()->pageTextNotContains($profile->label()); } } diff --git a/web/modules/linkit/tests/src/FunctionalJavascript/LinkFieldTest.php b/web/modules/linkit/tests/src/FunctionalJavascript/LinkFieldTest.php new file mode 100644 index 0000000000000000000000000000000000000000..de5343892e5a31a55598b54a50a4d97e394b4dce --- /dev/null +++ b/web/modules/linkit/tests/src/FunctionalJavascript/LinkFieldTest.php @@ -0,0 +1,334 @@ +<?php + +namespace Drupal\Tests\linkit\FunctionalJavascript; + +use Drupal\entity_test\Entity\EntityTestMul; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; +use Drupal\linkit\Tests\ProfileCreationTrait; +use Drupal\node\Entity\NodeType; + +/** + * Tests the widget and formatter for Link fields. + * + * @group linkit + */ +class LinkFieldTest extends WebDriverTestBase { + + use ProfileCreationTrait; + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'node', + 'language', + 'field_ui', + 'entity_test', + 'link', + 'linkit', + ]; + + /** + * A linkit profile. + * + * @var \Drupal\linkit\ProfileInterface + */ + protected $linkitProfile; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $matcherManager = $this->container->get('plugin.manager.linkit.matcher'); + /** @var \Drupal\linkit\MatcherInterface $plugin */ + + $this->linkitProfile = $this->createProfile(); + $plugin = $matcherManager->createInstance('entity:entity_test_mul'); + $this->linkitProfile->addMatcher($plugin->getConfiguration()); + $plugin = $matcherManager->createInstance('email'); + $this->linkitProfile->addMatcher($plugin->getConfiguration()); + $plugin = $matcherManager->createInstance('front_page'); + $this->linkitProfile->addMatcher($plugin->getConfiguration()); + $this->linkitProfile->save(); + + // Create a node type for testing. + NodeType::create(['type' => 'page', 'name' => 'page'])->save(); + + // Create a link field. + $storage = FieldStorageConfig::create([ + 'field_name' => 'field_test_link', + 'entity_type' => 'node', + 'type' => 'link', + ]); + $storage->save(); + FieldConfig::create([ + 'bundle' => 'page', + 'entity_type' => 'node', + 'field_name' => 'field_test_link', + ])->save(); + + // Define our widget and formatter for this field. + entity_get_form_display('node', 'page', 'default') + ->setComponent('field_test_link', [ + 'type' => 'linkit', + ]) + ->save(); + entity_get_display('node', 'page', 'default') + ->setComponent('field_test_link', [ + 'type' => 'linkit', + ]) + ->save(); + + $account = $this->drupalCreateUser([ + 'administer node fields', + 'administer node display', + 'administer nodes', + 'bypass node access', + 'view test entity', + ]); + + $this->drupalLogin($account); + } + + /** + * Test the "linkit" widget and formatter. + */ + public function testLinkFieldWidgetAndFormatter() { + $session = $this->getSession(); + $assert_session = $this->assertSession(); + $page = $session->getPage(); + + // Create a test entity to be used as target. + /** @var \Drupal\Core\Entity\EntityInterface $entity */ + $entity = EntityTestMul::create(['name' => 'Foo']); + $entity->save(); + + // Test the widget behavior. + $this->drupalGet('node/add/page'); + + $assert_session->elementContains('css', '#edit-field-test-link-wrapper', 'Start typing to find content or paste a URL and click on the suggestion below.'); + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('f'); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + + // With the default profile no results are found. + $autocomplete_results_wrapper = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete'); + $this->assertTrue($autocomplete_results_wrapper->isVisible()); + $result_description = $assert_session->elementExists('css', 'li.linkit-result-line .linkit-result-line--description', $autocomplete_results_wrapper); + $this->assertEquals('Linkit could not find any suggestions. This URL will be used as is.', $result_description->getText()); + + // Set the widget to use our profile and try again. + entity_get_form_display('node', 'page', 'default') + ->setComponent('field_test_link', [ + 'type' => 'linkit', + 'settings' => [ + 'linkit_profile' => $this->linkitProfile->id(), + ], + ]) + ->save(); + $this->drupalGet('node/add/page'); + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('f'); + $session->getDriver()->keyDown($uri_input->getXpath(), 'o'); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + // Check that the URL input field value shows the entity path. + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $this->assertEquals($entity->toUrl()->toString(), $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals('Foo', $title_input->getValue()); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 1'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 1 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals('Foo', $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains("/entity_test_mul/manage/{$entity->id()}", $href_value); + + // Test internal entity targets with anchors. + /** @var \Drupal\Core\Entity\EntityInterface $entity */ + $entity2 = EntityTestMul::create(['name' => 'Anchored Entity']); + $entity2->save(); + + // Test the widget behavior. + $this->drupalGet('node/add/page'); + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('Anchored'); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + // Check that the URL input field value shows the entity path. + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $this->assertEquals($entity2->toUrl()->toString(), $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals('Anchored Entity', $title_input->getValue()); + + // Add an anchor to the URL field. + $url_input->setValue($entity2->toUrl()->toString() . '#with-anchor'); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 2'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 2 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals('Anchored Entity', $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains("/entity_test_mul/manage/{$entity2->id()}#with-anchor", $href_value); + + // Test external URLs. + $this->drupalGet('node/add/page'); + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('https://google.com#foobar'); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $autocomplete_results_wrapper = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete'); + $this->assertTrue($autocomplete_results_wrapper->isVisible()); + $result_description = $assert_session->elementExists('css', 'li.linkit-result-line .linkit-result-line--description', $autocomplete_results_wrapper); + $this->assertEquals('Linkit could not find any suggestions. This URL will be used as is.', $result_description->getText()); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + // Set a manual value for the title. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $title_input->setValue('This is google'); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 3'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 3 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals('This is google', $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains('https://google.com#foobar', $href_value); + + // Test emails. + $this->drupalGet('node/add/page'); + + $email = 'drupal@example.com'; + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue($email); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $this->assertEquals('mailto:' . $email, $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals($email, $title_input->getValue()); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 4'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 4 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals($email, $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains('mailto:' . $email, $href_value); + + // Test internal host. + $this->drupalGet('node/add/page'); + + $url = \Drupal::request()->getSchemeAndHttpHost() . '/node/1'; + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue($url); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $this->assertEquals($url, $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals($url, $title_input->getValue()); + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 5'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 5 has been created'); + + // Check that we are viewing the node, and the formatter displays what we + // expect Should display the relative url without the host. + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals($url, $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains('/node/1', $href_value); + + // Test front page. + $this->drupalGet('node/add/page'); + + $widget_wrapper = $assert_session->elementExists('css', '#edit-field-test-link-wrapper'); + $uri_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + $uri_input->setValue('<front>'); + $session->getDriver()->keyDown($uri_input->getXpath(), ' '); + $assert_session->waitOnAutocomplete(); + $first_result = $assert_session->elementExists('css', 'ul.linkit-ui-autocomplete li.linkit-result-line span.linkit-result-line--title'); + $first_result->click(); + $assert_session->assertWaitOnAjaxRequest(); + + $url_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][uri]"]', $widget_wrapper); + + $this->assertEquals('/', $url_input->getValue()); + // Check that the title was populated automatically. + $title_input = $assert_session->elementExists('css', 'input[name="field_test_link[0][title]"]', $widget_wrapper); + $this->assertEquals('Front page', $title_input->getValue()); + + // Give the node a title and save the page. + $page->fillField('title[0][value]', 'Host test node 6'); + $page->pressButton('Save'); + $assert_session->pageTextContains('Host test node 6 has been created'); + + $field_wrapper = $assert_session->elementExists('css', '.field--type-link.field--name-field-test-link'); + $link_element = $assert_session->elementExists('css', 'a', $field_wrapper); + $this->assertEquals('Front page', $link_element->getText()); + $href_value = $link_element->getAttribute('href'); + $this->assertContains('/', $href_value); + } + +} diff --git a/web/modules/linkit/tests/src/FunctionalJavascript/LinkitDialogTest.php b/web/modules/linkit/tests/src/FunctionalJavascript/LinkitDialogTest.php index ac7c616f68e8c2b574ced0bb7c6dee845c41ef20..8d9ad93ff2fca1b978e39fc25ec2cad6af2a7ee1 100644 --- a/web/modules/linkit/tests/src/FunctionalJavascript/LinkitDialogTest.php +++ b/web/modules/linkit/tests/src/FunctionalJavascript/LinkitDialogTest.php @@ -8,7 +8,7 @@ use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\filter\Entity\FilterFormat; -use Drupal\FunctionalJavascriptTests\JavascriptTestBase; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\linkit\Tests\ProfileCreationTrait; use Drupal\node\Entity\NodeType; @@ -18,7 +18,7 @@ * * @group linkit */ -class LinkitDialogTest extends JavascriptTestBase { +class LinkitDialogTest extends WebDriverTestBase { use ProfileCreationTrait; @@ -175,23 +175,21 @@ public function testLinkDialog() { $this->assertFalse($autocomplete_container->isVisible()); // Trigger a keydown event to active a autocomplete search. - $href_field->keyDown('f'); + $href_field->setValue('f'); + $this->getSession()->getDriver()->keyDown($href_field->getXpath(), ' '); // Wait for the results to load. - $this->getSession()->wait(5000, "jQuery('.linkit-result.ui-menu-item').length > 0"); + $this->getSession()->wait(5000, "jQuery('.linkit-result-line.ui-menu-item').length > 0"); // Make sure the autocomplete result container is visible. $this->assertTrue($autocomplete_container->isVisible()); // Find all the autocomplete results. - $results = $page->findAll('css', '.linkit-result.ui-menu-item'); + $results = $page->findAll('css', '.linkit-result-line.ui-menu-item'); $this->assertEquals(1, count($results), 'Found autocomplete result'); // Find the first result and click it. - $page->find('xpath', '(//li[contains(@class, "linkit-result") and contains(@class, "ui-menu-item")])[1]')->click(); - - // Make sure the linkit field field is populated with the node url. - $this->assertEquals($entity->toUrl()->toString(), $href_field->getValue(), 'The href field is populated with the node url.'); + $page->find('xpath', '//li[contains(@class, "linkit-result-line") and contains(@class, "ui-menu-item")][1]')->click(); // Make sure all other fields are populated. $this->assertEqualsWithJs('attributes[data-entity-type]', $entity->getEntityTypeId()); @@ -200,7 +198,7 @@ public function testLinkDialog() { $this->assertEqualsWithJs('href_dirty_check', $entity->toUrl()->toString()); // Save the dialog input. - $page->find('css', '.editor-link-dialog')->find('css', '.button.form-submit span')->click(); + $this->click('.editor-link-dialog button:contains("Save")'); // Wait for the dialog to close. $web_assert->assertWaitOnAjaxRequest(); @@ -247,7 +245,7 @@ public function testLinkDialog() { $href_field->setValue('http://example.com'); // Save the dialog input. - $page->find('css', '.editor-link-dialog')->find('css', '.button.form-submit span')->click(); + $this->click('.editor-link-dialog button:contains("Save")'); // Wait for the dialog to close. $web_assert->assertWaitOnAjaxRequest(); diff --git a/web/modules/linkit/tests/src/FunctionalJavascript/LinkitFormatAdminTest.php b/web/modules/linkit/tests/src/FunctionalJavascript/LinkitFormatAdminTest.php index 7d2d79c64c6904210b352eecea61f47e5a73b3cd..26ca56c723bb47b17fe71d0f1ed385c158322a6d 100644 --- a/web/modules/linkit/tests/src/FunctionalJavascript/LinkitFormatAdminTest.php +++ b/web/modules/linkit/tests/src/FunctionalJavascript/LinkitFormatAdminTest.php @@ -2,14 +2,14 @@ namespace Drupal\Tests\linkit\FunctionalJavascript; -use Drupal\FunctionalJavascriptTests\JavascriptTestBase; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; /** * Tests the linkit alterations on the text format forms. * * @group linkit */ -class LinkitFormatAdminTest extends JavascriptTestBase { +class LinkitFormatAdminTest extends WebDriverTestBase { /** * Modules to enable. @@ -39,7 +39,6 @@ public function testToggleLinkitFilter() { // Go to add filter page. $this->drupalGet('admin/config/content/formats/add'); - $this->assertSession()->statusCodeEquals(200); // Enable the 'Limit allowed HTML tags and correct faulty HTML' filter. $page->findField('filters[filter_html][status]')->check(); diff --git a/web/modules/linkit/tests/src/Kernel/LinkitAutocompleteTest.php b/web/modules/linkit/tests/src/Kernel/LinkitAutocompleteTest.php index 5c167b869563c5a7a8c23e34c6ebc82bfc6b6773..c42721bb4cf1f2d960cf62461ead4f27f38a78c3 100644 --- a/web/modules/linkit/tests/src/Kernel/LinkitAutocompleteTest.php +++ b/web/modules/linkit/tests/src/Kernel/LinkitAutocompleteTest.php @@ -2,10 +2,8 @@ namespace Drupal\Tests\linkit\Kernel; -use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\Html; -use Drupal\Component\Utility\Unicode; use Drupal\entity_test\Entity\EntityTest; use Drupal\entity_test\Entity\EntityTestMul; use Drupal\language\Entity\ConfigurableLanguage; @@ -111,7 +109,7 @@ public function testAutocompletionEmail() { $email = 'drupal@example.com'; $data = $this->getAutocompleteResult($email); - $this->assertSame((string) new FormattableMarkup('E-mail @email', ['@email' => $email]), $data[0]['label'], 'Autocomplete returned email suggestion.'); + $this->assertSame($email, $data[0]['label'], 'Autocomplete returned email suggestion.'); $this->assertSame('mailto:' . $email, $data[0]['path'], 'Autocomplete returned email suggestion with an mailto href.'); } @@ -153,6 +151,29 @@ public function testAutocompletion() { $this->assertEmpty(count($data), 'Autocomplete did not return any suggestions.'); } + /** + * Tests autocompletion with results limit. + */ + public function testAutocompletionWithLimit() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->matcherManager->createInstance('entity:entity_test'); + $configuration = $plugin->getConfiguration(); + $configuration['settings']['limit'] = 2; + + $this->linkitProfile->addMatcher($configuration); + $this->linkitProfile->save(); + + $entity_1 = EntityTest::create(['name' => 'foo 1']); + $entity_1->save(); + $entity_2 = EntityTest::create(['name' => 'foo 2']); + $entity_2->save(); + $entity_3 = EntityTest::create(['name' => 'foo 3']); + $entity_3->save(); + + $data = $this->getAutocompleteResult('foo'); + $this->assertTrue(count($data) == 2, 'Autocomplete returned the expected amount of suggestions.'); + } + /** * Tests autocompletion with translated entities. */ @@ -221,7 +242,7 @@ protected function getAutocompleteResult($input) { protected function createProfile(array $settings = []) { // Populate defaults array. $settings += [ - 'id' => Unicode::strtolower($this->randomMachineName()), + 'id' => mb_strtolower($this->randomMachineName()), 'label' => $this->randomMachineName(), ]; diff --git a/web/modules/linkit/tests/src/Kernel/LinkitEditorLinkDialogTest.php b/web/modules/linkit/tests/src/Kernel/LinkitEditorLinkDialogTest.php index 0a1963b44e8954cc92cd0b053be2d029c09d8241..42504fcdb6456686f405c5f45d2c641fe59e0667 100644 --- a/web/modules/linkit/tests/src/Kernel/LinkitEditorLinkDialogTest.php +++ b/web/modules/linkit/tests/src/Kernel/LinkitEditorLinkDialogTest.php @@ -128,7 +128,7 @@ public function testAdd() { $form_state->setValue(['attributes', 'href'], 'https://example.com/'); $form_state->setValue('href_dirty_check', ''); - $form_state->setValue(['attributes', 'data-entity-type'], $this->randomString()); + $form_state->setValue(['attributes', 'data-entity-type'], $entity->getEntityTypeId()); $form_state->setValue(['attributes', 'data-entity-uuid'], $this->randomString()); $form_state->setValue(['attributes', 'data-entity-substitution'], $this->randomString()); $form_builder->submitForm($form_object, $form_state); diff --git a/web/modules/linkit/tests/src/Kernel/LinkitFilterEntityTest.php b/web/modules/linkit/tests/src/Kernel/LinkitFilterEntityTest.php index e93af40a2387d984c9574cb0db3fc5e1010fdd62..bd2db531aa28c29db3d7a1c532159c17e9dac607 100644 --- a/web/modules/linkit/tests/src/Kernel/LinkitFilterEntityTest.php +++ b/web/modules/linkit/tests/src/Kernel/LinkitFilterEntityTest.php @@ -74,7 +74,7 @@ public function testFilterEntityAccess() { */ public function testFilterEntityTranslations() { // Create an entity and add translations to that. - /** @var EntityTestMul $entity */ + /** @var \Drupal\entity_test\Entity\EntityTestMul $entity */ $entity = EntityTestMul::create(['name' => $this->randomMachineName()]); $entity->addTranslation('sv', ['name' => $this->randomMachineName(), 'langcode' => 'sv']); $entity->addTranslation('da', ['name' => $this->randomMachineName(), 'langcode' => 'da']); diff --git a/web/modules/linkit/tests/src/Kernel/LinkitKernelTestBase.php b/web/modules/linkit/tests/src/Kernel/LinkitKernelTestBase.php index 737a0bb1f4e5972ea95d688ce919318029cc24f6..6de3134f71c37825eb5d6fe4d2e25e74d5c26050 100644 --- a/web/modules/linkit/tests/src/Kernel/LinkitKernelTestBase.php +++ b/web/modules/linkit/tests/src/Kernel/LinkitKernelTestBase.php @@ -30,7 +30,6 @@ abstract class LinkitKernelTestBase extends KernelTestBase { */ protected function setUp() { parent::setUp(); - $this->installSchema('system', 'router'); $this->installSchema('system', 'sequences'); $this->installEntitySchema('user'); $this->installConfig(['filter']); @@ -51,7 +50,7 @@ protected function createUser(array $values = [], array $permissions = []) { if ($permissions) { // Create a new role and apply permissions to it. $role = Role::create([ - 'id' => strtolower($this->randomMachineName(8)), + 'id' => mb_strtolower($this->randomMachineName(8)), 'label' => $this->randomMachineName(8), ]); $role->save(); diff --git a/web/modules/linkit/tests/src/Kernel/Matchers/FileMatcherTest.php b/web/modules/linkit/tests/src/Kernel/Matchers/FileMatcherTest.php index e890b465b12442b4eae69be6c5483e9563d84c31..4999aae1ed639fa3f586ea402e0c0d900e0f5e47 100644 --- a/web/modules/linkit/tests/src/Kernel/Matchers/FileMatcherTest.php +++ b/web/modules/linkit/tests/src/Kernel/Matchers/FileMatcherTest.php @@ -38,7 +38,7 @@ protected function setUp() { $this->manager = $this->container->get('plugin.manager.linkit.matcher'); - // Linkit doesn't case about the actual resource, only the entity. + // Linkit doesn't care about the actual resource, only the entity. foreach (['gif', 'jpg', 'png'] as $ext) { $file = File::create([ 'uid' => 1, @@ -49,6 +49,9 @@ protected function setUp() { ]); $file->save(); } + + // Create user 1 who has special permissions. + \Drupal::currentUser()->setAccount($this->createUser(['uid' => 1])); } /** diff --git a/web/modules/linkit/tests/src/Kernel/Matchers/MediaMatcherTest.php b/web/modules/linkit/tests/src/Kernel/Matchers/MediaMatcherTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ba9d648011a58136612869c19f233901b35ed27b --- /dev/null +++ b/web/modules/linkit/tests/src/Kernel/Matchers/MediaMatcherTest.php @@ -0,0 +1,91 @@ +<?php + +namespace Drupal\Tests\linkit\Kernel\Matchers; + +use Drupal\file\Entity\File; +use Drupal\media\Entity\Media; +use Drupal\media\Entity\MediaType; +use Drupal\Tests\linkit\Kernel\LinkitKernelTestBase; + +/** + * Tests media matcher. + * + * @group linkit + */ +class MediaMatcherTest extends LinkitKernelTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = ['file_test', 'file', 'media', 'image', 'field']; + + /** + * The matcher manager. + * + * @var \Drupal\linkit\MatcherManager + */ + protected $manager; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->installEntitySchema('file'); + $this->installEntitySchema('media'); + $this->installSchema('system', ['key_value_expire']); + $this->installSchema('file', ['file_usage']); + + $this->manager = $this->container->get('plugin.manager.linkit.matcher'); + + // Set up media bundle and fields. + $media_type = MediaType::create([ + 'label' => 'test', + 'id' => 'test', + 'description' => 'Test type.', + 'source' => 'file', + ]); + $media_type->save(); + $source_field = $media_type->getSource()->createSourceField($media_type); + $source_field->getFieldStorageDefinition()->save(); + $source_field->save(); + $media_type->set('source_configuration', [ + 'source_field' => $source_field->getName(), + ])->save(); + + // Linkit doesn't care about the actual resource, only the entity. + foreach (['gif', 'jpg', 'png'] as $ext) { + $file = File::create([ + 'uid' => 1, + 'filename' => 'image-test.' . $ext, + 'uri' => 'public://image-test.' . $ext, + 'filemime' => 'text/plain', + 'status' => FILE_STATUS_PERMANENT, + ]); + $file->save(); + + $media = Media::create([ + 'bundle' => 'test', + $source_field->getName() => ['target_id' => $file->id()], + ]); + $media->save(); + } + + // Create user 1 who has special permissions. + \Drupal::currentUser()->setAccount($this->createUser(['uid' => 1])); + } + + /** + * Tests media matcher. + */ + public function testMediaMatcherWithDefaultConfiguration() { + /** @var \Drupal\linkit\MatcherInterface $plugin */ + $plugin = $this->manager->createInstance('entity:media', []); + $suggestions = $plugin->execute('image-test'); + $this->assertEquals(3, count($suggestions->getSuggestions()), 'Correct number of suggestions.'); + } + +} diff --git a/web/modules/linkit/tests/src/Kernel/Matchers/NodeMatcherTest.php b/web/modules/linkit/tests/src/Kernel/Matchers/NodeMatcherTest.php index 8783a4f9762ea601eb826dffd1eeb9dc08c674c7..0ab55506edb60546da7e1672e204e020c322fa80 100644 --- a/web/modules/linkit/tests/src/Kernel/Matchers/NodeMatcherTest.php +++ b/web/modules/linkit/tests/src/Kernel/Matchers/NodeMatcherTest.php @@ -18,7 +18,7 @@ class NodeMatcherTest extends LinkitKernelTestBase { * * @var array */ - public static $modules = ['field', 'node']; + public static $modules = ['field', 'node', 'content_moderation', 'workflows']; /** * The matcher manager. @@ -34,6 +34,7 @@ protected function setUp() { parent::setUp(); $this->installEntitySchema('node'); + $this->installSchema('node', ['node_access']); $this->installConfig(['field', 'node']); $this->manager = $this->container->get('plugin.manager.linkit.matcher'); @@ -74,7 +75,7 @@ protected function setUp() { ]); $node->save(); - // Unpublished node. + // Unpublished nodes. $node = Node::create([ 'title' => 'Lorem unpublishd', 'type' => $type1->id(), @@ -82,6 +83,13 @@ protected function setUp() { ]); $node->save(); + $node = Node::create([ + 'title' => 'Lorem unpublishd 2', + 'type' => $type2->id(), + 'status' => FALSE, + ]); + $node->save(); + // Set the current user to someone that is not the node owner. \Drupal::currentUser()->setAccount($this->createUser([], ['access content'])); } @@ -133,7 +141,22 @@ public function testNodeMatcherWidthIncludeUnpublished() { // Test with permissions to see unpublished nodes. $suggestions = $plugin->execute('Lorem'); + $this->assertEquals(5, count($suggestions->getSuggestions()), 'Correct number of suggestions'); + + // Test with permissions to see own unpublished nodes. + \Drupal::currentUser()->setAccount($this->createUser([], ['access content', 'view own unpublished content'])); + $nodes = $this->container->get('entity_type.manager')->getStorage('node')->loadByProperties(['title' => 'Lorem unpublishd']); + $node4 = reset($nodes); + /** @var \Drupal\node\NodeInterface $node4 */ + $node4->setOwnerId(\Drupal::currentUser()->id()); + $node4->save(); + $suggestions = $plugin->execute('Lorem'); $this->assertEquals(4, count($suggestions->getSuggestions()), 'Correct number of suggestions'); + + // Test with permissions to see any unpublished nodes. + \Drupal::currentUser()->setAccount($this->createUser([], ['access content', 'view any unpublished content'])); + $suggestions = $plugin->execute('Lorem'); + $this->assertEquals(5, count($suggestions->getSuggestions()), 'Correct number of suggestions'); } /** diff --git a/web/modules/linkit/tests/src/Kernel/Matchers/TermMatcherTest.php b/web/modules/linkit/tests/src/Kernel/Matchers/TermMatcherTest.php index 22f2c446e68db8d1560413dc2619e8e6784a4332..4e04d0fde9bca5eef604cb2762c009546bbd4550 100644 --- a/web/modules/linkit/tests/src/Kernel/Matchers/TermMatcherTest.php +++ b/web/modules/linkit/tests/src/Kernel/Matchers/TermMatcherTest.php @@ -2,7 +2,6 @@ namespace Drupal\Tests\linkit\Kernel\Matchers; -use Drupal\Component\Utility\Unicode; use Drupal\Core\Language\LanguageInterface; use Drupal\taxonomy\VocabularyInterface; use Drupal\Tests\linkit\Kernel\LinkitKernelTestBase; @@ -118,7 +117,7 @@ private function createVocabulary($name) { $vocabulary = $vocabularyStorage->create([ 'name' => $name, 'description' => $name, - 'vid' => Unicode::strtolower($name), + 'vid' => mb_strtolower($name), 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, ]); $vocabulary->save(); diff --git a/web/modules/linkit/tests/src/Kernel/SubstitutionPluginTest.php b/web/modules/linkit/tests/src/Kernel/SubstitutionPluginTest.php index 12a182df330f822989af651eaec16c0b870634eb..5608ba2bce41a6436947a6928451e077e00286da 100644 --- a/web/modules/linkit/tests/src/Kernel/SubstitutionPluginTest.php +++ b/web/modules/linkit/tests/src/Kernel/SubstitutionPluginTest.php @@ -3,21 +3,18 @@ namespace Drupal\Tests\linkit\Kernel; use Drupal\entity_test\Entity\EntityTest; -use Drupal\field\Entity\FieldConfig; -use Drupal\field\Entity\FieldStorageConfig; use Drupal\file\Entity\File; use Drupal\linkit\Plugin\Linkit\Substitution\Canonical as CanonicalSubstitutionPlugin; use Drupal\linkit\Plugin\Linkit\Substitution\File as FileSubstitutionPlugin; use Drupal\linkit\Plugin\Linkit\Substitution\Media as MediaSubstitutionPlugin; -use Drupal\media_entity\Entity\Media; -use Drupal\media_entity\Entity\MediaBundle; +use Drupal\media\Entity\Media; +use Drupal\media\Entity\MediaType; +use Drupal\Core\DependencyInjection\ContainerBuilder; /** * Tests the substitution plugins. * * @group linkit - * - * @requires module media_entity */ class SubstitutionPluginTest extends LinkitKernelTestBase { @@ -43,10 +40,10 @@ class SubstitutionPluginTest extends LinkitKernelTestBase { public static $modules = [ 'file', 'entity_test', - 'media_entity', + 'media', + 'media_test_source', 'image', 'field', - 'linkit_media_test', ]; /** @@ -60,10 +57,44 @@ public function setUp() { $this->installEntitySchema('file'); $this->installEntitySchema('entity_test'); $this->installEntitySchema('media'); - $this->installEntitySchema('media_bundle'); + $this->installEntitySchema('media_type'); $this->installEntitySchema('field_storage_config'); $this->installEntitySchema('field_config'); $this->installSchema('file', ['file_usage']); + $this->installConfig(['media']); + \Drupal::entityTypeManager()->clearCachedDefinitions(); + + unset($GLOBALS['config']['system.file']); + \Drupal::configFactory()->getEditable('system.file')->set('default_scheme', 'public')->save(); + } + + /** + * {@inheritdoc} + */ + public function register(ContainerBuilder $container) { + parent::register($container); + + $container->register('stream_wrapper.public', 'Drupal\Core\StreamWrapper\PublicStream') + ->addTag('stream_wrapper', ['scheme' => 'public']); + } + + /** + * {@inheritdoc} + */ + protected function setUpFilesystem() { + $public_file_directory = $this->siteDirectory . '/files'; + + require_once 'core/includes/file.inc'; + + mkdir($this->siteDirectory, 0775); + mkdir($this->siteDirectory . '/files', 0775); + mkdir($this->siteDirectory . '/files/config/' . CONFIG_SYNC_DIRECTORY, 0775, TRUE); + + $this->setSetting('file_public_path', $public_file_directory); + + $GLOBALS['config_directories'] = [ + CONFIG_SYNC_DIRECTORY => $this->siteDirectory . '/files/config/sync', + ]; } /** @@ -109,27 +140,20 @@ public function testCanonicalSubstitution() { */ public function testMediaSubstitution() { // Set up media bundle and fields. - MediaBundle::create([ + $media_type = MediaType::create([ 'label' => 'test', 'id' => 'test', - 'description' => 'test bundle.', - 'type' => 'test_type', - ])->save(); - FieldStorageConfig::create([ - 'field_name' => 'field_media_file', - 'entity_type' => 'media', - 'type' => 'file', - 'settings' => [], - ])->save(); - FieldConfig::create([ - 'entity_type' => 'media', - 'bundle' => 'test', - 'field_name' => 'field_media_file', - 'label' => 'Media field', - 'settings' => [ - 'file_extensions' => 'txt', - ], + 'description' => 'Test type.', + 'source' => 'file', + ]); + $media_type->save(); + $source_field = $media_type->getSource()->createSourceField($media_type); + $source_field->getFieldStorageDefinition()->save(); + $source_field->save(); + $media_type->set('source_configuration', [ + 'source_field' => $source_field->getName(), ])->save(); + $file = File::create([ 'uid' => 1, 'filename' => 'druplicon.txt', @@ -138,14 +162,21 @@ public function testMediaSubstitution() { 'status' => FILE_STATUS_PERMANENT, ]); $file->save(); + $media = Media::create([ 'bundle' => 'test', - 'field_media_file' => ['target_id' => $file->id()], + $source_field->getName() => ['target_id' => $file->id()], ]); $media->save(); $media_substitution = $this->substitutionManager->createInstance('media'); - $this->assertEquals($GLOBALS['base_url'] . '/' . $this->siteDirectory . '/files/druplicon.txt', $media_substitution->getUrl($media)->getGeneratedUrl()); + $expected = $GLOBALS['base_url'] . '/' . $this->siteDirectory . '/files/druplicon.txt'; + $this->assertEquals($expected, $media_substitution->getUrl($media)->getGeneratedUrl()); + + // Ensure the url is identical when media entities have a standalone URL + // enabled. + \Drupal::configFactory()->getEditable('media.settings')->set('standalone_url', TRUE)->save(); + $this->assertEquals($expected, $media_substitution->getUrl($media)->getGeneratedUrl()); $entity_type = $this->entityTypeManager->getDefinition('media'); $this->assertTrue(MediaSubstitutionPlugin::isApplicable($entity_type), 'The entity type Media is applicable the media substitution.'); @@ -154,4 +185,37 @@ public function testMediaSubstitution() { $this->assertFalse(MediaSubstitutionPlugin::isApplicable($entity_type), 'The entity type File is not applicable the media substitution.'); } + /** + * Test the media substitution when there is no supported source field. + */ + public function testMediaSubstitutionWithoutFileSource() { + // Set up media bundle and fields. + $media_type = MediaType::create([ + 'label' => 'test', + 'id' => 'test', + 'description' => 'Test type.', + 'source' => 'test', + ]); + $media_type->save(); + $source_field = $media_type->getSource()->createSourceField($media_type); + $source_field->getFieldStorageDefinition()->save(); + $source_field->save(); + $media_type->set('source_configuration', [ + 'source_field' => $source_field->getName(), + ])->save(); + + $media = Media::create([ + 'bundle' => 'test', + $source_field->getName() => ['value' => 'foobar'], + ]); + $media->save(); + + $media_substitution = $this->substitutionManager->createInstance('media'); + $this->assertEquals('', $media_substitution->getUrl($media)->getGeneratedUrl()); + + $this->config('media.settings')->set('standalone_url', TRUE)->save(); + \Drupal::entityTypeManager()->clearCachedDefinitions(); + $this->assertEquals('/media/' . $media->id(), $media_substitution->getUrl($media)->getGeneratedUrl()); + } + }