Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 6.0.x
  • 6.1.x
  • 6.x-1.x
  • 7.x
  • 7.x-1.x
  • 7.x-2.x
  • 7.x-3.x
  • 8.x-4.x
  • 8.x-5.x
  • 6.0.0
  • 6.0.0-beta1
  • 6.0.0-beta2
  • 6.0.0-beta3
  • 6.0.0-beta4
  • 6.0.0-rc1
  • 6.0.0-rc2
  • 6.0.1
  • 6.0.2
  • 6.1.0
  • 6.1.0-rc1
  • 6.1.0-rc2
  • 6.1.1
  • 6.1.2
  • 6.1.3
  • 6.1.4
  • 6.1.5
  • 6.1.6
  • 6.1.7
  • 6.1.8
  • 6.x-1.0
  • 6.x-1.0-alpha1
  • 6.x-1.0-beta1
  • 6.x-1.1
  • 6.x-1.10
  • 6.x-1.11
  • 6.x-1.12
  • 6.x-1.2
  • 6.x-1.3
  • 6.x-1.4
  • 6.x-1.5
  • 6.x-1.6
  • 6.x-1.7
  • 6.x-1.8
  • 6.x-1.9
  • 7.0.0
  • 7.0.0-alpha1
  • 7.0.0-alpha2
  • 7.0.1
  • 7.0.2
  • 7.0.3
  • 7.0.4
  • 7.0.5
  • 7.0.6
  • 7.0.7
  • 7.x-1.0
  • 7.x-1.1
  • 7.x-1.2
  • 7.x-1.3
  • 7.x-2.0
  • 7.x-2.0-beta1
  • 7.x-2.0-beta2
  • 7.x-2.1
  • 7.x-2.2
  • 7.x-2.3
  • 7.x-2.4
  • 7.x-2.5
  • 7.x-2.6
  • 7.x-2.7
  • 7.x-3.0
  • 7.x-3.1
  • 7.x-3.2
  • 7.x-3.3
  • 7.x-3.4
  • 7.x-3.5
  • 7.x-3.6
  • 7.x-3.7
  • 7.x-3.8
  • 8.x-4.0
  • 8.x-4.1
  • 8.x-4.2
  • 8.x-4.3
  • 8.x-4.4
  • 8.x-5.0-alpha1
  • 8.x-5.0-beta1
  • 8.x-5.0-beta10
  • 8.x-5.0-beta11
  • 8.x-5.0-beta12
  • 8.x-5.0-beta13
  • 8.x-5.0-beta2
  • 8.x-5.0-beta3
  • 8.x-5.0-beta4
  • 8.x-5.0-beta5
  • 8.x-5.0-beta6
  • 8.x-5.0-beta7
  • 8.x-5.0-beta8
  • 8.x-5.0-beta9
96 results

Target

Select target project
  • project/linkit
  • issue/linkit-3184082
  • issue/linkit-2712951
  • issue/linkit-3188748
  • issue/linkit-2886455
  • issue/linkit-3209603
  • issue/linkit-3049946
  • issue/linkit-2946234
  • issue/linkit-3240789
  • issue/linkit-3241565
  • issue/linkit-3232190
  • issue/linkit-3292870
  • issue/linkit-3304416
  • issue/linkit-3301151
  • issue/linkit-3300020
  • issue/linkit-3288339
  • issue/linkit-3288338
  • issue/linkit-3326426
  • issue/linkit-3204338
  • issue/linkit-3330975
  • issue/linkit-2981543
  • issue/linkit-3346274
  • issue/linkit-3347035
  • issue/linkit-3187270
  • issue/linkit-2877535
  • issue/linkit-3303942
  • issue/linkit-3351631
  • issue/linkit-3353476
  • issue/linkit-3353971
  • issue/linkit-3356715
  • issue/linkit-3355806
  • issue/linkit-2829173
  • issue/linkit-3360915
  • issue/linkit-3362180
  • issue/linkit-3094710
  • issue/linkit-3358023
  • issue/linkit-3362578
  • issue/linkit-3273630
  • issue/linkit-3383865
  • issue/linkit-3399995
  • issue/linkit-3078075
  • issue/linkit-3375163
  • issue/linkit-3379928
  • issue/linkit-3388193
  • issue/linkit-3389847
  • issue/linkit-3391649
  • issue/linkit-3364963
  • issue/linkit-3398071
  • issue/linkit-3412457
  • issue/linkit-3420280
  • issue/linkit-3396049
  • issue/linkit-3422951
  • issue/linkit-3422683
  • issue/linkit-3423516
  • issue/linkit-3423212
  • issue/linkit-3350477
  • issue/linkit-3222939
  • issue/linkit-3425830
  • issue/linkit-3442127
  • issue/linkit-3366401
  • issue/linkit-3428696
  • issue/linkit-3412195
  • issue/linkit-3438460
  • issue/linkit-3441789
  • issue/linkit-3443812
  • issue/linkit-3022261
  • issue/linkit-3441909
  • issue/linkit-3388565
  • issue/linkit-3450369
  • issue/linkit-3447669
  • issue/linkit-3465302
  • issue/linkit-3456396
  • issue/linkit-3468081
  • issue/linkit-3041045
  • issue/linkit-3472411
  • issue/linkit-3472501
  • issue/linkit-3472672
  • issue/linkit-3366761
  • issue/linkit-3476264
  • issue/linkit-3476222
  • issue/linkit-3373153
  • issue/linkit-3480382
  • issue/linkit-3155619
  • issue/linkit-3481475
  • issue/linkit-3495652
  • issue/linkit-3484874
  • issue/linkit-3485328
  • issue/linkit-3485843
  • issue/linkit-3498223
  • issue/linkit-3498711
  • issue/linkit-3500351
  • issue/linkit-3393201
  • issue/linkit-2997546
  • issue/linkit-3223919
  • issue/linkit-3519747
  • issue/linkit-3520443
  • issue/linkit-3520550
  • issue/linkit-3522476
  • issue/linkit-3088314
  • issue/linkit-3525299
  • issue/linkit-3525437
  • issue/linkit-3526037
  • issue/linkit-3529093
  • issue/linkit-2693769
  • issue/linkit-3531024
  • issue/linkit-3531463
  • issue/linkit-3532420
  • issue/linkit-3532814
  • issue/linkit-3510657
  • issue/linkit-3447685
  • issue/linkit-3523049
  • issue/linkit-3504398
  • issue/linkit-3348695
  • issue/linkit-3153482
  • issue/linkit-3381766
  • issue/linkit-3359540
  • issue/linkit-3534671
  • issue/linkit-3535098
  • issue/linkit-3447133
  • issue/linkit-3436733
  • issue/linkit-3535364
  • issue/linkit-2962821
  • issue/linkit-3386818
  • issue/linkit-3535479
  • issue/linkit-3536067
  • issue/linkit-3055954
  • issue/linkit-3537304
127 results
Select Git revision
  • 6.0.x
  • 6.1.x
  • 6.x-1.x
  • 7.x
  • 7.x-1.x
  • 7.x-2.x
  • 7.x-3.x
  • 8.x-4.x
  • 8.x-5.x
  • 6.0.0
  • 6.0.0-beta1
  • 6.0.0-beta2
  • 6.0.0-beta3
  • 6.0.0-beta4
  • 6.0.0-rc1
  • 6.0.0-rc2
  • 6.0.1
  • 6.0.2
  • 6.1.0
  • 6.1.0-rc1
  • 6.1.0-rc2
  • 6.1.1
  • 6.1.2
  • 6.1.3
  • 6.1.4
  • 6.1.5
  • 6.1.6
  • 6.1.7
  • 6.1.8
  • 6.x-1.0
  • 6.x-1.0-alpha1
  • 6.x-1.0-beta1
  • 6.x-1.1
  • 6.x-1.10
  • 6.x-1.11
  • 6.x-1.12
  • 6.x-1.2
  • 6.x-1.3
  • 6.x-1.4
  • 6.x-1.5
  • 6.x-1.6
  • 6.x-1.7
  • 6.x-1.8
  • 6.x-1.9
  • 7.0.0
  • 7.0.0-alpha1
  • 7.0.0-alpha2
  • 7.0.1
  • 7.0.2
  • 7.0.3
  • 7.0.4
  • 7.0.5
  • 7.0.6
  • 7.0.7
  • 7.x-1.0
  • 7.x-1.1
  • 7.x-1.2
  • 7.x-1.3
  • 7.x-2.0
  • 7.x-2.0-beta1
  • 7.x-2.0-beta2
  • 7.x-2.1
  • 7.x-2.2
  • 7.x-2.3
  • 7.x-2.4
  • 7.x-2.5
  • 7.x-2.6
  • 7.x-2.7
  • 7.x-3.0
  • 7.x-3.1
  • 7.x-3.2
  • 7.x-3.3
  • 7.x-3.4
  • 7.x-3.5
  • 7.x-3.6
  • 7.x-3.7
  • 7.x-3.8
  • 8.x-4.0
  • 8.x-4.1
  • 8.x-4.2
  • 8.x-4.3
  • 8.x-4.4
  • 8.x-5.0-alpha1
  • 8.x-5.0-beta1
  • 8.x-5.0-beta10
  • 8.x-5.0-beta11
  • 8.x-5.0-beta12
  • 8.x-5.0-beta13
  • 8.x-5.0-beta2
  • 8.x-5.0-beta3
  • 8.x-5.0-beta4
  • 8.x-5.0-beta5
  • 8.x-5.0-beta6
  • 8.x-5.0-beta7
  • 8.x-5.0-beta8
  • 8.x-5.0-beta9
96 results
Show changes

Commits on Source 6

......@@ -98,6 +98,9 @@ filter_settings.linkit:
title:
type: boolean
label: 'Automatically set the "title" attribute'
media_substitution:
type: string
label: 'Media entity URL substitution'
# Plugin \Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalLink
# Linkit alters the plugin to save Linkit specific information used by the
......
......@@ -28,10 +28,13 @@
max-height: calc((100vh - 80px) / 2);
}
.linkit-ui-autocomplete.ui-widget {
:not(.ck) > .linkit-ui-autocomplete.ui-widget {
position: absolute;
max-width: inherit;
font-size: 0.9em;
&.ck-reset_all-excluded {
white-space: break-spaces;
}
}
.linkit-ui-autocomplete.ui-menu .linkit-result-line-wrapper {
......@@ -89,3 +92,9 @@
.linkit-result-line--description img {
display: block;
}
/* Required to accommodate CKEditor v45 UI changes. See #3532814 */
.ck.ck-form.ck-link-form.ck-vertical-form {
width: fit-content;
min-width: 340px;
}
.linkit-widget-container .fieldset__wrapper,
.linkit-widget-container .fieldset-wrapper {
display: flex;
flex-flow: row wrap;
column-gap: var(--space-l, 10px);
> * {
width: 100%;
/* Fix none collapsing margins (caused by flex) */
margin-bottom: 0;
}
> .form-item--linkit-widget-uri,
> .form-item--linkit-widget-title {
flex: 1 1 auto;
width: calc(50% - var(--space-l, 10px));
min-width: 20em;
input {
width: 100%;
}
}
/* Claro specific fixes */
.claro-autocomplete {
width: 100%;
}
}
......@@ -3,6 +3,11 @@ linkit.admin:
theme:
css/linkit.admin.theme.css: {}
linkit.widget:
css:
theme:
css/linkit.widget.theme.css: {}
linkit.base:
js:
js/linkit.js: {}
......
......@@ -60,7 +60,7 @@ class AutocompleteController implements ContainerInjectionInterface {
// The erroneous variable name $linkit_profile_id is to avoid BC breaks.
$string = $request->query->get('q');
$suggestionCollection = $this->suggestionManager->getSuggestions($linkit_profile_id, mb_strtolower($string));
$suggestionCollection = $this->suggestionManager->getSuggestions($linkit_profile_id, $string);
/*
* If there are no suggestions from the matcher plugins, we have to add a
......
......@@ -130,8 +130,14 @@ class LinkitWidget extends LinkWidget {
$item = $items[$delta];
$uri = $item->uri ?? NULL;
try {
// Try to fetch entity information from the URI.
$default_allowed = !$item->isEmpty() && ($this->currentUser->hasPermission('link to any page') || $item->getUrl()->access());
}
catch (\InvalidArgumentException $e) {
// Make sure we render the form if InvalidArgumentException is thrown.
}
if (!empty($item->options['data-entity-type']) && !empty($item->options['data-entity-uuid'])) {
$entity = $this->entityRepository->loadEntityByUuid($item->options['data-entity-type'], $item->options['data-entity-uuid']);
}
......@@ -165,8 +171,13 @@ class LinkitWidget extends LinkWidget {
'linkit_profile_id' => $this->getSetting('linkit_profile'),
];
// Add a class to the title field.
// Add class to the URI fields item wrapper.
$element['uri']['#wrapper_attributes']['class'][] = 'form-item--linkit-widget-uri';
// Add a class to the title field and its item wrapper.
$element['title']['#attributes']['class'][] = 'linkit-widget-title';
$element['title']['#wrapper_attributes']['class'][] = 'form-item--linkit-widget-title';
if ($this->getSetting('linkit_auto_link_text')) {
$element['title']['#attributes']['data-linkit-widget-title-autofill-enabled'] = TRUE;
}
......@@ -189,6 +200,10 @@ class LinkitWidget extends LinkWidget {
'#default_value' => $entity ? ($entity->getEntityTypeId() === 'file' ? 'file' : 'canonical') : '',
];
// Add custom css for the widget representation:
$element['#attached']['library'][] = 'linkit/linkit.widget';
// Add a custom class to the parent container:
$element['#attributes']['class'][] = 'linkit-widget-container';
return $element;
}
......
......@@ -22,6 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* description = @Translation("Updates links inserted by Linkit to point to entity URL aliases."),
* settings = {
* "title" = TRUE,
* "media_substitution" = "metadata",
* },
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
* )
......@@ -116,6 +117,12 @@ class LinkitFilter extends FilterBase implements ContainerFactoryPluginInterface
if (!$substitution_type = $element->getAttribute('data-entity-substitution')) {
$substitution_type = $entity_type === 'file' ? 'file' : SubstitutionManagerInterface::DEFAULT_SUBSTITUTION;
}
if ($entity_type === 'media') {
$substitution_options = $this->substitutionManager->getApplicablePluginsOptionList('media');
if (in_array($this->settings['media_substitution'], array_keys($substitution_options))) {
$substitution_type = $this->settings['media_substitution'];
}
}
$entity = $this->entityRepository->loadEntityByUuid($entity_type, $uuid);
if ($entity) {
......@@ -188,6 +195,17 @@ class LinkitFilter extends FilterBase implements ContainerFactoryPluginInterface
'#title' => $this->t('Automatically set the <code>title</code> attribute to that of the (translated) referenced content'),
'#default_value' => $this->settings['title'],
];
if (\Drupal::moduleHandler()->moduleExists('media')) {
$substitution_options = $this->substitutionManager->getApplicablePluginsOptionList('media');
$form['media_substitution'] = [
'#type' => 'select',
'#title' => $this->t('Media entity URL substitution'),
'#options' => [
'metadata' => $this->t('Use metadata from when the link was created'),
] + $substitution_options,
'#default_value' => $this->settings['media_substitution'] ?? 'metadata',
];
}
return $form;
}
......
......@@ -3,6 +3,7 @@
namespace Drupal\linkit\Plugin\Linkit\Matcher;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Query\QueryInterface;
......@@ -104,6 +105,13 @@ class EntityMatcher extends ConfigurableMatcherBase {
*/
protected $configFactory;
/**
* The request context.
*
* @var \Drupal\Core\Routing\RequestContext
*/
protected $requestContext;
/**
* {@inheritdoc}
*/
......@@ -123,6 +131,7 @@ class EntityMatcher extends ConfigurableMatcherBase {
$instance->substitutionManager = $container->get('plugin.manager.linkit.substitution');
$instance->token = $container->get('token');
$instance->configFactory = $container->get('config.factory');
$instance->requestContext = $container->get('router.request_context');
return $instance;
}
......@@ -523,18 +532,31 @@ class EntityMatcher extends ConfigurableMatcherBase {
*
* @param string $user_input
* The string to url parse.
* @param string $base_url
* The site base url. Typically this is only used for testing.
*
* @return array
* An array with an entity id if the input can be parsed as an internal url
* and a match is found, otherwise an empty array.
*/
protected function findEntityIdByUrl($user_input) {
$result = [];
public function findEntityIdByUrl($user_input, $base_url = '') {
if (empty($base_url)) {
$base_url = $this->requestContext->getCompleteBaseUrl();
}
$is_absolute_local_url = UrlHelper::isExternal($user_input)
&& UrlHelper::isValid($user_input, TRUE)
&& UrlHelper::externalIsLocal($user_input, $base_url);
if ($is_absolute_local_url) {
// The link points to this domain. Make it relative so it can be
// matched in Url::fromUserInput().
$user_input = substr($user_input, strlen($base_url));
}
$result = [];
try {
$params = Url::fromUserInput($user_input)->getRouteParameters();
if (key($params) === $this->targetType) {
$result = [end($params)];
if (!empty($params[$this->targetType])) {
$result = [$params[$this->targetType]];
}
}
catch (\Exception $e) {
......
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Drupal\Tests\linkit\Kernel\Matchers;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\linkit\Kernel\LinkitKernelTestBase;
......@@ -20,7 +21,15 @@ class NodeMatcherTest extends LinkitKernelTestBase {
*
* @var array
*/
protected static $modules = ['field', 'node', 'content_moderation', 'workflows'];
protected static $modules = [
'field',
'node',
'content_moderation',
'workflows',
'language',
'path',
'path_alias',
];
/**
* The matcher manager.
......@@ -37,7 +46,7 @@ class NodeMatcherTest extends LinkitKernelTestBase {
$this->installEntitySchema('node');
$this->installSchema('node', ['node_access']);
$this->installConfig(['field', 'node']);
$this->installConfig(['field', 'node', 'language']);
$this->manager = $this->container->get('plugin.manager.linkit.matcher');
......@@ -182,4 +191,86 @@ class NodeMatcherTest extends LinkitKernelTestBase {
}
}
/**
* Test node matches generated from an absolute URL input.
*/
public function testNodeMatcherFromAbsoluteUrl() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:node');
/** @var \Drupal\node\NodeInterface[] $nodes */
$nodes = $this->container->get('entity_type.manager')->getStorage('node')->loadByProperties(['title' => 'Lorem Ipsum 1']);
$node = reset($nodes);
$suggestions = $plugin->execute($node->toUrl()->setAbsolute()->toString());
$this->assertEquals(1, count($suggestions->getSuggestions()));
// Check that URLs that don't match the drupal base URL do not get matched.
$external_domain_url = $node->toUrl(NULL, ['base_url' => 'https://example.com'])->setAbsolute()->toString();
$this->assertEquals(0, count($plugin->execute($external_domain_url)->getSuggestions()));
$base_url = $this->container->get('router.request_context')->getCompleteBaseUrl();
$non_drupal_base_path = $node->toUrl(NULL, ['base_url' => $base_url . '/some-other-sub-path'])->setAbsolute()->toString();
$this->assertEquals(0, count($plugin->execute($non_drupal_base_path)->getSuggestions()));
}
/**
* Test node matches generated from an absolute URL input.
*/
public function testNodeMatcherFromAbsoluteUrlWithLanguagePrefix() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:node');
$langcode = 'nl';
ConfigurableLanguage::createFromLangcode($langcode)->save();
\Drupal::configFactory()->getEditable('language.negotiation')
->set('url.prefixes.nl', $langcode)
->save();
// In order to reflect the changes for a multilingual site in the container
// we have to rebuild it.
\Drupal::service('kernel')->rebuildContainer();
/** @var \Drupal\node\NodeInterface[] $nodes */
$nodes = $this->container->get('entity_type.manager')->getStorage('node')->loadByProperties(['title' => 'Lorem Ipsum 1']);
$node = reset($nodes);
$translation = $node->addTranslation($langcode, $node->toArray());
$translation->save();
$translated_url = $translation->toUrl()->setAbsolute()->toString();
// Make sure the translated URL contains our prefix.
$this->assertStringContainsString('/' . $langcode . '/', (string) $translated_url);
$suggestions = $plugin->execute($translated_url);
$this->assertEquals(1, count($suggestions->getSuggestions()));
$base_urls = [
'https://domainname/',
'https://domainname/prefix-a/',
];
foreach ($base_urls as $base_url) {
// Test that <domainname>/<internal-path> returns a positive match.
$url = $node->toUrl(NULL, ['base_url' => $base_url])->setAbsolute()->toString();
$matches = $plugin->findEntityIdByUrl($url, $base_url);
$this->assertEquals(1, count($matches));
// Test that <domainname>/<langcode>/<internal-path> returns a positive
// match.
$translated_url = $translation->toUrl(NULL, ['base_url' => $base_url])->setAbsolute()->toString();
$matches = $plugin->findEntityIdByUrl($translated_url, $base_url);
$this->assertEquals(1, count($matches));
}
$node->set('path', '/test-path');
$node->save();
$translation->set('path', '/test-pad');
$translation->save();
foreach ($base_urls as $base_url) {
// Test that <domainname>/<alias> returns a positive match.
$url = $node->toUrl(NULL, ['base_url' => $base_url])->setAbsolute()->toString();
$matches = $plugin->findEntityIdByUrl($url, $base_url);
$this->assertEquals(1, count($matches));
// Test that <domainname>/<langcode>/<alias> returns a positive match.
$translated_url = $translation->toUrl(NULL, ['base_url' => $base_url])->setAbsolute()->toString();
$matches = $plugin->findEntityIdByUrl($translated_url, $base_url);
$this->assertEquals([], $matches);
}
}
}