Commit d0fcf487 authored by anon's avatar anon Committed by anon

Issue #1893300 by anon: Show link info in LinkIt dialog

parent c9d390e0
......@@ -26,7 +26,12 @@
* @param {object} suggestions
*/
function showSuggestions(suggestions) {
response(suggestions.matches);
if (suggestions.matches.length === 0) {
response([{title: Drupal.t('No results')}]);
}
else {
response(suggestions.matches);
}
}
/**
......@@ -63,11 +68,26 @@
function selectHandler(event, ui) {
if (ui.item.hasOwnProperty('path')) {
event.target.value = ui.item.path;
if (ui.item.hasOwnProperty('title')) {
$('.linkit-link-information > span').text(ui.item.title);
}
}
$(document).trigger('linkit.autocomplete.select', [event, ui]);
return false;
}
/**
* Handles an autocomplete response event.
*
* @param {jQuery.Event} event
* @param {object} ui
*/
function response(event, ui) {
if (ui.content.length !== 0) {
$('.linkit-link-information > span').text(event.target.value);
}
}
/**
* Override jQuery UI _renderItem function to output HTML by default.
*
......@@ -157,6 +177,7 @@
renderItem: renderItem,
renderMenu: renderMenu,
select: selectHandler,
response: response,
minLength: 1
},
ajax: {
......
......@@ -21,10 +21,8 @@ function linkit_ckeditor_plugin_info_alter(array &$plugins) {
* Implements hook_form_FORM_ID_alter().
*/
function linkit_form_editor_link_dialog_alter(&$form, FormStateInterface $form_state, $form_id) {
$user_input = $form_state->getUserInput();
/** @var Drupal\filter\Entity\FilterFormat $filter_format */
$filter_format = $form_state->getBuildInfo()['args'][0];
$input = isset($user_input['editor_object']) ? $user_input['editor_object'] : [];
/** @var \Drupal\Core\Entity\EntityStorageInterface $editorStorage */
$editorStorage = Drupal::service('entity.manager')->getStorage('editor');
......@@ -42,12 +40,51 @@ function linkit_form_editor_link_dialog_alter(&$form, FormStateInterface $form_s
/** @var \Drupal\linkit\Entity\Profile $linkit_profile */
$linkit_profile = Drupal::entityTypeManager()->getStorage('linkit_profile')->load($linkit_profile_id);
$user_input = $form_state->getUserInput();
$input = isset($user_input['editor_object']) ? $user_input['editor_object'] : [];
$data_entity_type = isset($input['data-entity-type']) ? $input['data-entity-type'] : '';
$data_entity_uuid = isset($input['data-entity-uuid']) ? $input['data-entity-uuid'] : '';
// If the filter_html filter is activated, or any other filters using
// XSS:filter(), it will remove 'entity:' from the href as it thinks it's a
// bad protocol. We therefor have to restore the URI again when editing a
// link. It is possible given that data-entity-type and data-entity-uuid is
// set on the link element.
try {
if (!empty($data_entity_type) && !empty($data_entity_uuid)) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = \Drupal::service('entity.repository')
->loadEntityByUuid($data_entity_type, $data_entity_uuid);
$href = 'entity:' . $entity->getEntityTypeId() . '/' . $entity->id();
$access = !$entity->access('view', NULL, TRUE)->isForbidden();
}
}
catch (Exception $exception) {
// Do nothing, this is handled in the finally block.
}
finally {
// If the href is not set, the data- attributes might not exists, or the
// href is external. In that case, use the given href.
if (!isset($href)) {
$href = isset($input['href']) ? $input['href'] : '';
}
}
$form['link-information'] = [
'#type' => 'inline_template',
'#template' => '<div class="form-item linkit-link-information"><strong>{% trans %}Links to:{% endtrans %}</strong> <span>{{ link_target }}</span></div>',
'#context' => [
'link_target' => !empty($entity) && !empty($access) && $access ? $entity->label() : $href,
],
'#weight' => 2,
];
// Everything under the "attributes" key is merged directly into the
// generated link tag's attributes.
$form['attributes']['href'] = [
'#title' => t('Link'),
'#type' => 'linkit',
'#default_value' => isset($input['href']) ? $input['href'] : '',
'#default_value' => $href,
'#description' => t('Start typing to find content or paste a URL.'),
'#autocomplete_route_name' => 'linkit.autocomplete',
'#autocomplete_route_parameters' => [
......@@ -77,7 +114,7 @@ function linkit_form_editor_link_dialog_alter(&$form, FormStateInterface $form_s
'linkit/linkit.imce',
],
],
'#weight' => 1,
'#weight' => 10,
];
}
......@@ -93,7 +130,8 @@ function linkit_form_editor_link_dialog_validate(array &$form, FormStateInterfac
// Check if the 'href' attribute contains a entity: URI.
$href = $form_state->getValue(['attributes', 'href']);
$uri_parts = parse_url($href);
if ($uri_parts['scheme'] !== 'entity') {
if (!$uri_parts || !isset($uri_parts['scheme']) || $uri_parts['scheme'] !== 'entity') {
$form_state->setValue(['attributes', 'data-entity-type'], '');
$form_state->setValue(['attributes', 'data-entity-uuid'], '');
return;
......@@ -102,20 +140,24 @@ function linkit_form_editor_link_dialog_validate(array &$form, FormStateInterfac
// Parse the entity: URI into an entity type ID and entity ID.
list($entity_type_id, $entity_id) = explode('/', $uri_parts['path'], 2);
// Check if the given entity type exists, to prevent the entity load method
// to throw exceptions.
$definition = \Drupal::entityTypeManager()->getDefinition($entity_type_id, FALSE);
if (is_null($definition)) {
$form_state->setError($form['attributes']['href'], t('Invalid URI'));
return;
}
// Load the entity and populate the data-entity-type and data-entity-uuid
// attributes as expected by filters.
// @see \Drupal\editor\Plugin\Filter\EditorFileReference
// @see \Drupal\linkit\Plugin\Filter\LinkitFilter
try {
if (!$entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id)) {
$form_state->setError($form['attributes']['href'], t('Invalid URI'));
}
else {
$form_state->setValue(['attributes', 'data-entity-type'], $entity_type_id);
$form_state->setValue(['attributes', 'data-entity-uuid'], $entity->uuid());
}
$entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
if (!empty($entity)) {
$form_state->setValue(['attributes', 'data-entity-type'], $entity->getEntityTypeId());
$form_state->setValue(['attributes', 'data-entity-uuid'], $entity->uuid());
}
catch (Exception $exception) {
else {
$form_state->setError($form['attributes']['href'], t('Invalid URI'));
}
......
......@@ -148,6 +148,11 @@ class LinkitDialogTest extends JavascriptTestBase {
$autocomplete_container = $page->find('css', 'ul.linkit-ui-autocomplete');
$this->assertFalse($autocomplete_container->isVisible());
// Make sure the link information is empty.
$javascript = "(function (){ return jQuery('.linkit-link-information > span').text(); })()";
$link_information = $session->evaluateScript($javascript);
$this->assertEquals('', $link_information, 'Link information is empty');
// Trigger a keydown event to active a autocomplete search.
$input_field->keyDown('f');
......@@ -168,6 +173,11 @@ class LinkitDialogTest extends JavascriptTestBase {
// Make sure the href field is populated with the node uri.
$this->assertEquals('entity:' . $this->demoEntity->getEntityTypeId() . '/' . $this->demoEntity->id(), $input_field->getValue(), 'The href field is populated with the node uri');
// Make sure the link information is populated.
$javascript = "(function (){ return jQuery('.linkit-link-information > span').text(); })()";
$link_information = $session->evaluateScript($javascript);
$this->assertEquals($this->demoEntity->label(), $link_information, 'Link information is populated');
// Save the dialog input.
$button = $page->find('css', '.editor-link-dialog')->find('css', '.button.form-submit span');
$button->click();
......@@ -179,14 +189,14 @@ class LinkitDialogTest extends JavascriptTestBase {
// have a name.
foreach (['data-entity-type' => $this->demoEntity->getEntityTypeId(), 'data-entity-uuid' => $this->demoEntity->uuid()] as $attr => $value) {
$javascript = <<<JS
(function($){
(function(){
var iframes = document.getElementsByClassName('cke_wysiwyg_frame');
if (iframes.length) {
var doc = iframes[0].contentDocument || iframes[0].contentWindow.document;
var link = doc.getElementsByTagName('a')[0];
return link.getAttribute("$attr");
}
})(jQuery)
})()
JS;
$link_attr = $session->evaluateScript($javascript);
$this->assertNotNull($link_attr);
......
<?php
namespace Drupal\Tests\linkit\Kernel;
use Drupal\Core\Form\FormState;
use Drupal\editor\Entity\Editor;
use Drupal\editor\Form\EditorLinkDialog;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\filter\Entity\FilterFormat;
use Drupal\linkit\Tests\ProfileCreationTrait;
/**
* Tests EditorLinkDialog validation and conversion functionality.
*
* @group linkit
*/
class LinkitEditorLinkDialogTest extends LinkitKernelTestBase {
use ProfileCreationTrait;
/**
* The linkit profile.
*
* @var \Drupal\linkit\ProfileInterface
*/
protected $linkitProfile;
/**
* Filter format for testing.
*
* @var \Drupal\filter\FilterFormatInterface
*/
protected $format;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['editor', 'ckeditor', 'entity_test'];
/**
* Sets up the test.
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test');
// Create a profile.
$this->linkitProfile = $this->createProfile();
/** @var \Drupal\linkit\MatcherManager $matcherManager */
$matcherManager = $this->container->get('plugin.manager.linkit.matcher');
// Add the entity_test matcher to the profile.
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $matcherManager->createInstance('entity:entity_test');
$this->linkitProfile->addMatcher($plugin->getConfiguration());
$this->linkitProfile->save();
// Add a text format.
$this->format = FilterFormat::create([
'format' => 'filtered_html',
'name' => 'Filtered HTML',
'weight' => 0,
'filters' => [],
]);
$this->format->save();
// Set up editor.
$editor = Editor::create([
'format' => 'filtered_html',
'editor' => 'ckeditor',
]);
$editor->setSettings([
'plugins' => [
'drupallink' => [
'linkit_enabled' => TRUE,
'linkit_profile' => $this->linkitProfile->id(),
],
],
]);
$editor->save();
}
/**
* Tests adding a link.
*/
public function testAdd() {
$entity_label = $this->randomString();
$entity = EntityTest::create(['name' => $entity_label]);
$entity->save();
$form_object = new EditorLinkDialog();
$input = [
'editor_object' => [],
'dialogOptions' => [
'title' => 'Add Link',
'dialogClass' => 'editor-link-dialog',
'autoResize' => 'true',
],
'_drupal_ajax' => '1',
'ajax_page_state' => [
'theme' => 'bartik',
'theme_token' => 'some-token',
'libraries' => '',
],
];
$form_state = (new FormState())
->setRequestMethod('POST')
->setUserInput($input)
->addBuildInfo('args', [$this->format]);
/** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
$form_builder = $this->container->get('form_builder');
$form_id = $form_builder->getFormId($form_object, $form_state);
$form = $form_builder->retrieveForm($form_id, $form_state);
$form_builder->prepareForm($form_id, $form, $form_state);
$form_builder->processForm($form_id, $form, $form_state);
$this->assertEquals('linkit.autocomplete', $form['attributes']['href']['#autocomplete_route_name'], 'Linkit is enabled on the href field.');
$this->assertEquals('', $form['attributes']['href']['#default_value'], 'The href attribute is empty.');
$this->assertEquals('', $form['link-information']['#context']['link_target'], 'Link information is empty.');
$form_state->setValue(['attributes', 'href'], 'entity:missing_entity/1');
$form_builder->submitForm($form_object, $form_state);
$this->assertNotEmpty($form_state->getErrors(), 'Got validation errors for none existing entity type.');
$form_state->setValue(['attributes', 'href'], 'url_without_schema');
$form_builder->submitForm($form_object, $form_state);
$this->assertEmpty($form_state->getErrors(), 'Got no validation errors for url without schema.');
$this->assertEquals('', $form_state->getValue(['attributes', 'data-entity-type']));
$this->assertEquals('', $form_state->getValue(['attributes', 'data-entity-uuid']));
$form_state->setValue(['attributes', 'href'], 'entity:entity_test/1');
$form_builder->submitForm($form_object, $form_state);
$this->assertEmpty($form_state->getErrors(), 'Got no validation errors for correct URI.');
$this->assertEquals($entity->getEntityTypeId(), $form_state->getValue(['attributes', 'data-entity-type']), 'Attribute "data-entity-type" exists and has the correct value.');
$this->assertEquals($entity->uuid(), $form_state->getValue(['attributes', 'data-entity-uuid']), 'Attribute "data-entity-uuid" exists and has the correct value.');
}
/**
* Tests editing a link with data attributes.
*/
public function testEditWithDataAttributes() {
$entity_label = $this->randomString();
$entity = EntityTest::create(['name' => $entity_label]);
$entity->save();
$entity_no_access = EntityTest::create(['name' => 'forbid_access']);
$entity_no_access->save();
$form_object = new EditorLinkDialog();
$input = [
'editor_object' => [
'href' => 'entity:entity_test/1',
'data-entity-type' => $entity->getEntityTypeId(),
'data-entity-uuid' => $entity->uuid(),
],
'dialogOptions' => [
'title' => 'Edit Link',
'dialogClass' => 'editor-link-dialog',
'autoResize' => 'true',
],
'_drupal_ajax' => '1',
'ajax_page_state' => [
'theme' => 'bartik',
'theme_token' => 'some-token',
'libraries' => '',
],
];
$form_state = (new FormState())
->setRequestMethod('POST')
->setUserInput($input)
->addBuildInfo('args', [$this->format]);
/** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
$form_builder = $this->container->get('form_builder');
$form_id = $form_builder->getFormId($form_object, $form_state);
$form = $form_builder->retrieveForm($form_id, $form_state);
$form_builder->prepareForm($form_id, $form, $form_state);
$form_builder->processForm($form_id, $form, $form_state);
$this->assertEquals('linkit.autocomplete', $form['attributes']['href']['#autocomplete_route_name'], 'Linkit is enabled on the href field.');
$this->assertEquals('entity:entity_test/1', $form['attributes']['href']['#default_value'], 'The href attribute is empty.');
$this->assertEquals($entity->label(), $form['link-information']['#context']['link_target'], 'Link information is empty.');
// Make sure the dialog don't display entity labels for inaccessible
// entities.
$input = [
'editor_object' => [
'href' => 'entity:entity_test/2',
'data-entity-type' => $entity_no_access->getEntityTypeId(),
'data-entity-uuid' => $entity_no_access->uuid(),
],
'dialogOptions' => [
'title' => 'Edit Link',
'dialogClass' => 'editor-link-dialog',
'autoResize' => 'true',
],
'_drupal_ajax' => '1',
'ajax_page_state' => [
'theme' => 'bartik',
'theme_token' => 'some-token',
'libraries' => '',
],
];
$form_state = (new FormState())
->setRequestMethod('POST')
->setUserInput($input)
->addBuildInfo('args', [$this->format]);
/** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
$form_builder = $this->container->get('form_builder');
$form_id = $form_builder->getFormId($form_object, $form_state);
$form = $form_builder->retrieveForm($form_id, $form_state);
$form_builder->prepareForm($form_id, $form, $form_state);
$form_builder->processForm($form_id, $form, $form_state);
$this->assertEquals('linkit.autocomplete', $form['attributes']['href']['#autocomplete_route_name'], 'Linkit is enabled on the href field.');
$this->assertEquals('entity:entity_test/2', $form['attributes']['href']['#default_value'], 'The href attribute is empty.');
$this->assertEquals('entity:entity_test/2', $form['link-information']['#context']['link_target'], 'Link information is empty.');
}
/**
* Tests editing a link without data attributes.
*/
public function testEditWithoutDataAttributes() {
$form_object = new EditorLinkDialog();
$input = [
'editor_object' => [
'href' => 'http://example.com/',
],
'dialogOptions' => [
'title' => 'Edit Link',
'dialogClass' => 'editor-link-dialog',
'autoResize' => 'true',
],
'_drupal_ajax' => '1',
'ajax_page_state' => [
'theme' => 'bartik',
'theme_token' => 'some-token',
'libraries' => '',
],
];
$form_state = (new FormState())
->setRequestMethod('POST')
->setUserInput($input)
->addBuildInfo('args', [$this->format]);
/** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
$form_builder = $this->container->get('form_builder');
$form_id = $form_builder->getFormId($form_object, $form_state);
$form = $form_builder->retrieveForm($form_id, $form_state);
$form_builder->prepareForm($form_id, $form, $form_state);
$form_builder->processForm($form_id, $form, $form_state);
$this->assertEquals('linkit.autocomplete', $form['attributes']['href']['#autocomplete_route_name'], 'Linkit is enabled on the href field.');
$this->assertEquals('http://example.com/', $form['attributes']['href']['#default_value'], 'The href attribute is empty.');
$this->assertEquals('http://example.com/', $form['link-information']['#context']['link_target'], 'Link information is empty.');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment