updated linkit contrib module to last beta version

This commit is contained in:
2018-04-17 14:53:21 +02:00
parent c0fbcb7706
commit 76ff40f8bb
136 changed files with 5138 additions and 3860 deletions
@@ -1,69 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Annotation\Attribute.
*/
namespace Drupal\linkit\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines an attribute annotation object.
*
* Plugin Namespace: Plugin\Linkit\Attribute
*
* For a working example, see \Drupal\linkit\Plugin\Linkit\Attribute\Title
*
* @see \Drupal\linkit\AttributeInterface
* @see \Drupal\linkit\AttributeBase
* @see \Drupal\linkit\AttributeManager
* @see plugin_api
*
* @Annotation
*/
class Attribute extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The human-readable name of the attribute.
*
* The string should be wrapped in a @Translation().
*
* @var \Drupal\Core\Annotation\Translation
*/
public $label;
/**
* The real HTML attribute name for this attribute.
*
* @var string
*/
public $html_name;
/**
* A brief description of the attribute.
*
* This will be shown when adding or configuring a profile.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation (optional)
*/
public $description = '';
/**
* A default weight for the attribute.
*
* @var int (optional)
*/
public $weight = 0;
}
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Annotation\Matcher.
*/
namespace Drupal\linkit\Annotation;
use Drupal\Component\Annotation\Plugin;
@@ -12,7 +7,7 @@ use Drupal\Component\Annotation\Plugin;
/**
* Defines a matcher annotation object.
*
* Plugin Namespace: Plugin\Linkit\Matcher
* Plugin Namespace: Plugin\Linkit\Matcher.
*
* @see \Drupal\linkit\MatcherInterface
* @see \Drupal\linkit\MatcherBase
@@ -39,11 +34,4 @@ class Matcher extends Plugin {
*/
public $label;
/**
* The entity type that is managed by this matcher.
*
* @var string
*/
public $entity_type;
}
@@ -0,0 +1,28 @@
<?php
namespace Drupal\linkit\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a substitution annotation object.
*
* @Annotation
*/
class Substitution extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The human-readable name of the substitution.
*
* @var \Drupal\Core\Annotation\Translation
*/
public $label;
}
@@ -1,112 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\AttributeBase.
*/
namespace Drupal\linkit;
use Drupal\Core\Plugin\PluginBase;
/**
* Provides a base class for attribute plugins.
*
* @see \Drupal\linkit\Annotation\Attribute
* @see \Drupal\linkit\AttributeBase
* @see \Drupal\linkit\AttributeManager
* @see plugin_api
*/
abstract class AttributeBase extends PluginBase implements AttributeInterface {
/**
* The weight of the attribute compared to others in an attribute collection.
*
* @var int
*/
protected $weight = 0;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->setConfiguration($configuration);
}
/**
* {@inheritdoc}
*/
public function getConfiguration() {
return [
'id' => $this->getPluginId(),
'weight' => $this->weight,
'settings' => $this->configuration,
];
}
/**
* {@inheritdoc}
*/
public function setConfiguration(array $configuration) {
$configuration += [
'weight' => '0',
'settings' => [],
];
$this->configuration = $configuration['settings'] + $this->defaultConfiguration();
$this->weight = $configuration['weight'];
return $this;
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [];
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
return [];
}
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->pluginDefinition['label'];
}
/**
* {@inheritdoc}
*/
public function getHtmlName() {
return $this->pluginDefinition['html_name'];
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->pluginDefinition['description'];
}
/**
* {@inheritdoc}
*/
public function getWeight() {
return $this->weight;
}
/**
* {@inheritdoc}
*/
public function setWeight($weight) {
$this->weight = $weight;
return $this;
}
}
@@ -1,46 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\AttributeCollection.
*/
namespace Drupal\linkit;
use Drupal\Core\Plugin\DefaultLazyPluginCollection;
/**
* A collection of attribute plugins.
*/
class AttributeCollection extends DefaultLazyPluginCollection {
/**
* All possible attribute IDs.
*
* @var array
*/
protected $definitions;
/**
* {@inheritdoc}
*
* @return \Drupal\linkit\AttributeInterface
*/
public function &get($instance_id) {
return parent::get($instance_id);
}
/**
* {@inheritdoc}
*/
public function sortHelper($aID, $bID) {
$a_weight = $this->get($aID)->getWeight();
$b_weight = $this->get($bID)->getWeight();
if ($a_weight == $b_weight) {
return strnatcasecmp($this->get($aID)->getLabel(), $this->get($bID)->getLabel());
}
return ($a_weight < $b_weight) ? -1 : 1;
}
}
@@ -1,78 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\AttributeInterface.
*/
namespace Drupal\linkit;
use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Component\Plugin\PluginInspectionInterface;
/**
* Defines the interface for attributes plugins.
*
* @see \Drupal\linkit\Annotation\Attribute
* @see \Drupal\linkit\AttributeBase
* @see \Drupal\linkit\AttributeManager
* @see plugin_api
*/
interface AttributeInterface extends PluginInspectionInterface, ConfigurablePluginInterface {
/**
* Returns the attribute label.
*
* @return string
* The attribute label.
*/
public function getLabel();
/**
* Returns the attribute description.
*
* @return string
* The attribute description.
*/
public function getDescription();
/**
* Returns the attribute html name. This is the name of the attribute
* that will be inserted in the <code>&lt;a&gt;</code> tag.
*
* @return string
* The attribute html name.
*/
public function getHtmlName();
/**
* Returns the weight of the attribute.
*
* @return int|string
* Either the integer weight of the attribute or an empty string.
*/
public function getWeight();
/**
* Sets the weight for this attribute.
*
* @param int $weight
* The weight for this attribute.
*
* @return $this
*/
public function setWeight($weight);
/**
* The form element structure for this attribute to be used in the dialog.
*
* @param mixed $default_value
* The default value for the element. Used when editing an attribute in the
* dialog.
*
* @return array
* The form element.
*/
public function buildFormElement($default_value);
}
@@ -1,29 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\AttributeManager.
*/
namespace Drupal\linkit;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Manages attributes.
*/
class AttributeManager extends DefaultPluginManager {
/**
* {@inheritdoc}
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct('Plugin/Linkit/Attribute', $namespaces, $module_handler, 'Drupal\linkit\AttributeInterface', 'Drupal\linkit\Annotation\Attribute');
$this->alterInfo('linkit_attribute');
$this->setCacheBackend($cache_backend, 'linkit_attributes');
}
}
@@ -1,16 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\ConfigurableAttributeBase.
*/
namespace Drupal\linkit;
/**
* Provides a base class for configurable attributes.
*
* @see plugin_api
*/
abstract class ConfigurableAttributeBase extends AttributeBase implements ConfigurableAttributeInterface {
}
@@ -1,23 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\ConfigurableAttributeInterface.
*/
namespace Drupal\linkit;
use Drupal\Core\Plugin\PluginFormInterface;
/**
* Defines the interface for configurable attributes.
*
* @see \Drupal\linkit\Annotation\Attribute
* @see \Drupal\linkit\ConfigurableAttributeBase
* @see \Drupal\linkit\AttributeInterface
* @see \Drupal\linkit\AttributeBase
* @see \Drupal\linkit\AttributeManager
* @see plugin_api
*/
interface ConfigurableAttributeInterface extends AttributeInterface, PluginFormInterface {
}
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\ConfigurableMatcherBase.
*/
namespace Drupal\linkit;
/**
@@ -1,11 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\ConfigurableMatcherInterface.
*/
namespace Drupal\linkit;
use Drupal\Core\Plugin\PluginFormInterface;
@@ -1,20 +1,18 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Controller\AutocompleteController.
*/
namespace Drupal\linkit\Controller;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\linkit\ResultManager;
use Drupal\linkit\SuggestionManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
/**
* Returns responses for linkit autocomplete routes.
*/
class AutocompleteController implements ContainerInjectionInterface {
/**
@@ -25,11 +23,11 @@ class AutocompleteController implements ContainerInjectionInterface {
protected $linkitProfileStorage;
/**
* The result manager.
* The suggestion manager.
*
* @var \Drupal\linkit\ResultManager
* @var \Drupal\linkit\SuggestionManager
*/
protected $resultManager;
protected $suggestionManager;
/**
* The linkit profile.
@@ -41,14 +39,14 @@ class AutocompleteController implements ContainerInjectionInterface {
/**
* Constructs a EntityAutocompleteController object.
*
* @param ResultManager $resultManager
* The result service.
* @param \Drupal\Core\Entity\EntityStorageInterface $linkit_profile_storage
* The linkit profile storage service.
* @param \Drupal\linkit\SuggestionManager $suggestionManager
* The suggestion service.
*/
public function __construct(EntityStorageInterface $linkit_profile_storage, ResultManager $resultManager) {
public function __construct(EntityStorageInterface $linkit_profile_storage, SuggestionManager $suggestionManager) {
$this->linkitProfileStorage = $linkit_profile_storage;
$this->resultManager = $resultManager;
$this->suggestionManager = $suggestionManager;
}
/**
@@ -57,7 +55,7 @@ class AutocompleteController implements ContainerInjectionInterface {
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('linkit_profile'),
$container->get('linkit.result_manager')
$container->get('linkit.suggestion_manager')
);
}
@@ -67,23 +65,31 @@ class AutocompleteController implements ContainerInjectionInterface {
* Like other autocomplete functions, this function inspects the 'q' query
* parameter for the string to use to search for suggestions.
*
* @param Request $request
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
* @param $linkit_profile_id
* @param string $linkit_profile_id
* The linkit profile id.
* @return JsonResponse
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
* A JSON response containing the autocomplete suggestions.
*/
public function autocomplete(Request $request, $linkit_profile_id) {
$this->linkitProfile = $this->linkitProfileStorage->load($linkit_profile_id);
$string = Unicode::strtolower($request->query->get('q'));
$string = $request->query->get('q');
$matches = $this->resultManager->getResults($this->linkitProfile, $string);
$suggestionCollection = $this->suggestionManager->getSuggestions($this->linkitProfile, Unicode::strtolower($string));
$json_object = new \stdClass();
$json_object->matches = $matches;
/*
* If there are no suggestions from the matcher plugins, we have to add a
* special suggestion that have the same path as the given string so users
* can select it and use it anyway. This is a common use case with external
* links.
*/
if (!count($suggestionCollection->getSuggestions()) && !empty($string)) {
$suggestionCollection = $this->suggestionManager->addUnscathedSuggestion($suggestionCollection, $string);
}
return new JsonResponse($json_object);
return new JsonResponse($suggestionCollection);
}
}
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Controller\LinkitController.
*/
namespace Drupal\linkit\Controller;
use Drupal\Core\Controller\ControllerBase;
@@ -25,7 +20,7 @@ class LinkitController extends ControllerBase {
* The profile label as a render array.
*/
public function profileTitle(ProfileInterface $linkit_profile) {
return $this->t('Edit %label profile', array('%label' => $linkit_profile->label()));
return $this->t('Edit %label profile', ['%label' => $linkit_profile->label()]);
}
/**
@@ -42,24 +37,7 @@ class LinkitController extends ControllerBase {
public function matcherTitle(ProfileInterface $linkit_profile, $plugin_instance_id) {
/** @var \Drupal\linkit\MatcherInterface $matcher */
$matcher = $linkit_profile->getMatcher($plugin_instance_id);
return $this->t('Edit %label matcher', array('%label' => $matcher->getLabel()));
}
/**
* Route title callback.
*
* @param \Drupal\linkit\ProfileInterface $linkit_profile
* The profile.
* @param string $plugin_instance_id
* The plugin instance id.
*
* @return string
* The title for the attribute edit form.
*/
public function attributeTitle(ProfileInterface $linkit_profile, $plugin_instance_id) {
/** @var \Drupal\linkit\AttributeInterface $attribute */
$attribute = $linkit_profile->getAttribute($plugin_instance_id);
return $this->t('Edit %label attribute', array('%label' => $attribute->getLabel()));
return $this->t('Edit %label matcher', ['%label' => $matcher->getLabel()]);
}
}
@@ -1,15 +1,9 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Element\Linkit.
*/
namespace Drupal\linkit\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\FormElement;
use Drupal\Core\Render\Element\Textfield;
use Drupal\Core\Url;
@@ -26,20 +20,20 @@ class Linkit extends FormElement {
*/
public function getInfo() {
$class = get_class($this);
return array(
return [
'#input' => TRUE,
'#size' => 60,
'#process' => array(
array($class, 'processLinkitAutocomplete'),
array($class, 'processGroup'),
),
'#pre_render' => array(
array($class, 'preRenderLinkitElement'),
array($class, 'preRenderGroup'),
),
'#process' => [
[$class, 'processLinkitAutocomplete'],
[$class, 'processGroup'],
],
'#pre_render' => [
[$class, 'preRenderLinkitElement'],
[$class, 'preRenderGroup'],
],
'#theme' => 'input__textfield',
'#theme_wrappers' => array('form_element'),
);
'#theme_wrappers' => ['form_element'],
];
}
/**
@@ -63,7 +57,7 @@ class Linkit extends FormElement {
$access = FALSE;
if (!empty($element['#autocomplete_route_name'])) {
$parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : array();
$parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : [];
$url = Url::fromRoute($element['#autocomplete_route_name'], $parameters)->toString(TRUE);
/** @var \Drupal\Core\Access\AccessManagerInterface $access_manager */
$access_manager = \Drupal::service('access_manager');
@@ -88,21 +82,10 @@ class Linkit extends FormElement {
}
/**
* Prepares a #type 'linkit' render element for input.html.twig.
*
* @param array $element
* An associative array containing the properties of the element.
* Properties used: #title, #value, #description, #size, #attributes.
*
* @return array
* The $element with prepared variables ready for input.html.twig.
* {@inheritdoc}
*/
public static function preRenderLinkitElement($element) {
$element['#attributes']['type'] = 'text';
Element::setAttributes($element, array('id', 'name', 'value', 'size'));
static::setAttributes($element, array('form-text'));
return $element;
return Textfield::preRenderTextfield($element);
}
}
@@ -1,15 +1,9 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Entity\Profile.
*/
namespace Drupal\linkit\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
use Drupal\linkit\AttributeCollection;
use Drupal\linkit\MatcherCollection;
use Drupal\linkit\MatcherInterface;
use Drupal\linkit\ProfileInterface;
@@ -43,7 +37,6 @@ use Drupal\linkit\ProfileInterface;
* "id",
* "label",
* "description",
* "attributes",
* "matchers"
* }
* )
@@ -71,28 +64,6 @@ class Profile extends ConfigEntityBase implements ProfileInterface, EntityWithPl
*/
protected $description;
/**
* Configured attribute for this profile.
*
* An associative array of attribute assigned to the profile, keyed by the
* attribute id of each attribute and using the properties:
* - id: The plugin ID of the attribute instance.
* - status: (optional) A Boolean indicating whether the attribute is enabled
* in the profile. Defaults to FALSE.
* - weight: (optional) The weight of the attribute in the profile.
* Defaults to 0.
*
* @var array
*/
protected $attributes = [];
/**
* Holds the collection of attributes that are attached to this profile.
*
* @var \Drupal\linkit\AttributeCollection
*/
protected $attributeCollection;
/**
* Configured matchers for this profile.
*
@@ -130,50 +101,6 @@ class Profile extends ConfigEntityBase implements ProfileInterface, EntityWithPl
return $this;
}
/**
* {@inheritdoc}
*/
public function getAttribute($attribute_id) {
return $this->getAttributes()->get($attribute_id);
}
/**
* {@inheritdoc}
*/
public function getAttributes() {
if (!$this->attributeCollection) {
$this->attributeCollection = new AttributeCollection($this->getAttributeManager(), $this->attributes);
$this->attributeCollection->sort();
}
return $this->attributeCollection;
}
/**
* {@inheritdoc}
*/
public function addAttribute(array $configuration) {
$this->getAttributes()->addInstanceId($configuration['id'], $configuration);
return $configuration['id'];
}
/**
* {@inheritdoc}
*/
public function removeAttribute($attribute_id) {
unset($this->attributes[$attribute_id]);
$this->getAttributes()->removeInstanceId($attribute_id);
return $this;
}
/**
* {@inheritdoc}
*/
public function setAttributeConfig($attribute_id, array $configuration) {
$this->attributes[$attribute_id] = $configuration;
$this->getAttributes()->setInstanceConfiguration($attribute_id, $configuration);
return $this;
}
/**
* {@inheritdoc}
*/
@@ -186,7 +113,7 @@ class Profile extends ConfigEntityBase implements ProfileInterface, EntityWithPl
*/
public function getMatchers() {
if (!$this->matcherCollection) {
$this->matcherCollection = new MatcherCollection($this->getMatcherManager(), $this->matchers);
$this->matcherCollection = new MatcherCollection(\Drupal::service('plugin.manager.linkit.matcher'), $this->matchers);
$this->matcherCollection->sort();
}
return $this->matcherCollection;
@@ -223,30 +150,9 @@ class Profile extends ConfigEntityBase implements ProfileInterface, EntityWithPl
* {@inheritdoc}
*/
public function getPluginCollections() {
return array(
'attributes' => $this->getAttributes(),
return [
'matchers' => $this->getMatchers(),
);
}
/**
* Returns the attribute manager.
*
* @return \Drupal\Component\Plugin\PluginManagerInterface
* The attribute manager.
*/
protected function getAttributeManager() {
return \Drupal::service('plugin.manager.linkit.attribute');
}
/**
* Returns the matcher manager.
*
* @return \Drupal\Component\Plugin\PluginManagerInterface
* The matcher manager.
*/
protected function getMatcherManager() {
return \Drupal::service('plugin.manager.linkit.matcher');
];
}
}
@@ -1,166 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Attribute\AddForm.
*/
namespace Drupal\linkit\Form\Attribute;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\linkit\AttributeManager;
use Drupal\linkit\ConfigurableAttributeInterface;
use Drupal\linkit\ProfileInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form to apply attributes to a profile.
*/
class AddForm extends FormBase {
/**
* The profiles to which the attributes will be applied.
*
* @var \Drupal\linkit\ProfileInterface
*/
protected $linkitProfile;
/**
* The attribute manager.
*
* @var \Drupal\linkit\AttributeManager
*/
protected $manager;
/**
* Constructs a new AddForm.
*
* @param \Drupal\linkit\AttributeManager $manager
* The attribute manager.
*/
public function __construct(AttributeManager $manager) {
$this->manager = $manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.linkit.attribute')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return "linkit_attribute_add_form";
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) {
$this->linkitProfile = $linkit_profile;
$form['#attached']['library'][] = 'linkit/linkit.admin';
$header = [
'label' => $this->t('Attributes'),
'description' => $this->t('Description'),
];
$form['plugin'] = [
'#type' => 'tableselect',
'#header' => $header,
'#options' => $this->buildRows(),
'#empty' => $this->t('No attributes available.'),
'#multiple' => FALSE,
];
$form['actions'] = ['#type' => 'actions'];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Save and continue'),
'#submit' => ['::submitForm'],
'#tableselect' => TRUE,
'#button_type' => 'primary',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (empty($form_state->getValue('plugin'))) {
$form_state->setErrorByName('plugin', $this->t('No attribute selected.'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$form_state->cleanValues();
/** @var \Drupal\linkit\AttributeInterface $plugin */
$plugin = $this->manager->createInstance($form_state->getValue('plugin'));
$plugin_id = $this->linkitProfile->addAttribute($plugin->getConfiguration());
$this->linkitProfile->save();
$this->logger('linkit')->notice('Added %label attribute to the @profile profile.', [
'%label' => $this->linkitProfile->getAttribute($plugin_id)->getLabel(),
'@profile' => $this->linkitProfile->label(),
]);
$is_configurable = $plugin instanceof ConfigurableAttributeInterface;
if ($is_configurable) {
$form_state->setRedirect('linkit.attribute.edit', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $plugin_id,
]);
}
else {
drupal_set_message($this->t('Added %label attribute.', ['%label' => $plugin->getLabel()]));
$form_state->setRedirect('linkit.attributes', [
'linkit_profile' => $this->linkitProfile->id(),
]);
}
}
/**
* Builds the table rows.
*
* Only attributes that is not already applied to the profile are shown.
*
* @return array
* An array of table rows.
*/
private function buildRows() {
$rows = [];
$applied_plugins = $this->linkitProfile->getAttributes()->getConfiguration();
$all_plugins = $this->manager->getDefinitions();
uasort($all_plugins, function ($a, $b) {
return strnatcasecmp($a['label'], $b['label']);
});
foreach (array_diff_key($all_plugins, $applied_plugins) as $definition) {
/** @var \Drupal\linkit\AttributeInterface $plugin */
$plugin = $this->manager->createInstance($definition['id']);
$row = [
'label' => (string) $plugin->getLabel(),
'description' => (string) $plugin->getDescription(),
];
$rows[$plugin->getPluginId()] = $row;
}
return $rows;
}
}
@@ -1,92 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Attribute\DeleteForm.
*/
namespace Drupal\linkit\Form\Attribute;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\linkit\ProfileInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides a form to remove an attribute from a profile.
*/
class DeleteForm extends ConfirmFormBase {
/**
* The profiles that the attribute is applied to.
*
* @var \Drupal\linkit\ProfileInterface
*/
protected $linkitProfile;
/**
* The attribute to be removed from the profile.
*
* @var \Drupal\linkit\AttributeInterface
*/
protected $linkitAttribute;
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete the @plugin attribute from the %profile profile?', ['%profile' => $this->linkitProfile->label(), '@plugin' => $this->linkitAttribute->getLabel()]);
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return Url::fromRoute('linkit.attributes', [
'linkit_profile' => $this->linkitProfile->id(),
]);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'linkit_attribute_delete_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL, $plugin_instance_id = NULL) {
$this->linkitProfile = $linkit_profile;
if (!$this->linkitProfile->getAttributes()->has($plugin_instance_id)) {
throw new NotFoundHttpException();
}
$this->linkitAttribute = $this->linkitProfile->getAttribute($plugin_instance_id);
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
if ($this->linkitProfile->getAttributes()->has($this->linkitAttribute->getPluginId())) {
$this->linkitProfile->removeAttribute($this->linkitAttribute->getPluginId());
$this->linkitProfile->save();
drupal_set_message($this->t('The attribute %label has been deleted.', ['%label' => $this->linkitAttribute->getLabel()]));
$this->logger('linkit')->notice('The attribute %label has been deleted in the @profile profile.', [
'%label' => $this->linkitAttribute->getLabel(),
'@profile' => $this->linkitProfile->label(),
]);
}
$form_state->setRedirect('linkit.attributes', [
'linkit_profile' => $this->linkitProfile->id(),
]);
}
}
@@ -1,96 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Attribute\EditForm.
*/
namespace Drupal\linkit\Form\Attribute;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\linkit\ProfileInterface;
/**
* Provides an edit form for attributes.
*/
class EditForm extends FormBase {
/**
* The profiles to which the attributes will be applied.
*
* @var \Drupal\linkit\ProfileInterface
*/
protected $linkitProfile;
/**
* The attribute to edit.
*
* @var \Drupal\linkit\ConfigurableAttributeInterface
*/
protected $linkitAttribute;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'linkit_attribute_edit_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL, $plugin_instance_id = NULL) {
$this->linkitProfile = $linkit_profile;
$this->linkitAttribute = $this->linkitProfile->getAttribute($plugin_instance_id);
$form['data'] = [
'#tree' => true,
];
$form['data'] += $this->linkitAttribute->buildConfigurationForm($form, $form_state);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Save changes'),
'#submit' => array('::submitForm'),
'#button_type' => 'primary',
);
$form['actions']['delete'] = array(
'#type' => 'link',
'#title' => $this->t('Delete'),
'#url' => Url::fromRoute('linkit.attribute.delete', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $this->linkitAttribute->getPluginId(),
]),
'#attributes' => [
'class' => ['button', 'button--danger'],
],
);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$form_state->cleanValues();
$plugin_data = (new FormState())->setValues($form_state->getValue('data'));
$this->linkitAttribute->submitConfigurationForm($form, $plugin_data);
$this->linkitProfile->save();
drupal_set_message($this->t('Saved %label configuration.', array('%label' => $this->linkitAttribute->getLabel())));
$this->logger('linkit')->notice('The attribute %label has been updated in the @profile profile.', [
'%label' => $this->linkitAttribute->getLabel(),
'@profile' => $this->linkitProfile->label(),
]);
$form_state->setRedirect('linkit.attributes', [
'linkit_profile' => $this->linkitProfile->id(),
]);
}
}
@@ -1,156 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Attribute\OverviewForm.
*/
namespace Drupal\linkit\Form\Attribute;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\linkit\AttributeManager;
use Drupal\linkit\ConfigurableAttributeInterface;
use Drupal\linkit\ProfileInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides an overview form for attribute on a profile.
*/
class OverviewForm extends FormBase {
/**
* The profiles to which the attributes are applied to.
*
* @var \Drupal\linkit\ProfileInterface
*/
private $linkitProfile;
/**
* The attribute manager.
*
* @var \Drupal\linkit\AttributeManager
*/
protected $manager;
/**
* Constructs a new OverviewForm.
*
* @param \Drupal\linkit\AttributeManager $manager
* The attribute manager.
*/
public function __construct(AttributeManager $manager) {
$this->manager = $manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.linkit.attribute')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return "linkit_attribute_overview_form";
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ProfileInterface $linkit_profile = NULL) {
$this->linkitProfile = $linkit_profile;
$form['plugins'] = [
'#type' => 'table',
'#header' => [
$this->t('Attribute'),
$this->t('Description'),
$this->t('Weight'),
$this->t('Operations'),
],
'#empty' => $this->t('No attributes added.'),
'#tabledrag' => [
[
'action' => 'order',
'relationship' => 'sibling',
'group' => 'plugin-order-weight',
],
],
];
foreach ($this->linkitProfile->getAttributes() as $plugin) {
$key = $plugin->getPluginId();
$form['plugins'][$key]['#attributes']['class'][] = 'draggable';
$form['plugins'][$key]['#weight'] = $plugin->getWeight();
$form['plugins'][$key]['label'] = [
'#plain_text' => (string) $plugin->getLabel(),
];
$form['plugins'][$key]['description'] = [
'#plain_text' => (string) $plugin->getDescription(),
];
$form['plugins'][$key]['weight'] = [
'#type' => 'weight',
'#title' => t('Weight for @title', ['@title' => (string) $plugin->getLabel()]),
'#title_display' => 'invisible',
'#default_value' => $plugin->getWeight(),
'#attributes' => ['class' => ['plugin-order-weight']],
];
$form['plugins'][$key]['operations'] = [
'#type' => 'operations',
'#links' => [],
];
$is_configurable = $plugin instanceof ConfigurableAttributeInterface;
if ($is_configurable) {
$form['plugins'][$key]['operations']['#links']['edit'] = [
'title' => t('Edit'),
'url' => Url::fromRoute('linkit.attribute.edit', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $key,
]),
];
}
$form['plugins'][$key]['operations']['#links']['delete'] = [
'title' => t('Delete'),
'url' => Url::fromRoute('linkit.attribute.delete', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $key,
]),
];
}
$form['actions'] = ['#type' => 'actions'];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Save'),
'#button_type' => 'primary',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
foreach ($form_state->getValue('plugins') as $id => $plugin_data) {
if ($this->linkitProfile->getAttributes()->has($id)) {
$this->linkitProfile->getAttribute($id)->setWeight($plugin_data['weight']);
}
}
$this->linkitProfile->save();
}
}
@@ -1,207 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\LinkitEditorDialog.
*/
namespace Drupal\linkit\Form;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\filter\Entity\FilterFormat;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\editor\Ajax\EditorDialogSave;
use Drupal\Core\Ajax\CloseModalDialogCommand;
use Drupal\linkit\AttributeCollection;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a linkit dialog for text editors.
*/
class LinkitEditorDialog extends FormBase {
/**
* The editor storage service.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $editorStorage;
/**
* The linkit profile storage service.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $linkitProfileStorage;
/**
* The linkit profile.
*
* @var \Drupal\linkit\ProfileInterface
*/
protected $linkitProfile;
/**
* Constructs a form object for linkit dialog.
*
* @param \Drupal\Core\Entity\EntityStorageInterface $editor_storage
* The editor storage service.
* @param \Drupal\Core\Entity\EntityStorageInterface $linkit_profile_storage
* The linkit profile storage service.
*/
public function __construct(EntityStorageInterface $editor_storage, EntityStorageInterface $linkit_profile_storage) {
$this->editorStorage = $editor_storage;
$this->linkitProfileStorage = $linkit_profile_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('editor'),
$container->get('entity.manager')->getStorage('linkit_profile')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'linkit_editor_dialog_form';
}
/**
* {@inheritdoc}
*
* @param \Drupal\filter\Entity\FilterFormat $filter_format
* The filter format for which this dialog corresponds.
*/
public function buildForm(array $form, FormStateInterface $form_state, FilterFormat $filter_format = NULL) {
// The default values are set directly from \Drupal::request()->request,
// provided by the editor plugin opening the dialog.
$user_input = $form_state->getUserInput();
$input = isset($user_input['editor_object']) ? $user_input['editor_object'] : [];
/** @var \Drupal\editor\EditorInterface $editor */
$editor = $this->editorStorage->load($filter_format->id());
$linkit_profile_id = $editor->getSettings()['plugins']['linkit']['linkit_profile'];
$this->linkitProfile = $this->linkitProfileStorage->load($linkit_profile_id);
$form['#tree'] = TRUE;
$form['#attached']['library'][] = 'editor/drupal.editor.dialog';
$form['#prefix'] = '<div id="linkit-editor-dialog-form">';
$form['#suffix'] = '</div>';
// Everything under the "attributes" key is merged directly into the
// generated link tag's attributes.
$form['attributes']['href'] = [
'#title' => $this->t('Link'),
'#type' => 'linkit',
'#default_value' => isset($input['href']) ? $input['href'] : '',
'#description' => $this->t('Start typing to find content or paste a URL.'),
'#autocomplete_route_name' => 'linkit.autocomplete',
'#autocomplete_route_parameters' => [
'linkit_profile_id' => $linkit_profile_id
],
'#weight' => 0,
];
$this->addAttributes($form, $form_state, $this->linkitProfile->getAttributes(), $input);
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['save_modal'] = [
'#type' => 'submit',
'#value' => $this->t('Save'),
'#submit' => [],
'#ajax' => [
'callback' => '::submitForm',
'event' => 'click',
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$attributes = array_filter($form_state->getValue('attributes'));
$form_state->setValue('attributes', $attributes);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
if ($form_state->getErrors()) {
unset($form['#prefix'], $form['#suffix']);
$form['status_messages'] = [
'#type' => 'status_messages',
'#weight' => -10,
];
$response->addCommand(new HtmlCommand('#linkit-editor-dialog-form', $form));
}
else {
$response->addCommand(new EditorDialogSave($form_state->getValues()));
$response->addCommand(new CloseModalDialogCommand());
}
return $response;
}
/**
* Adds the attributes enabled on the current profile.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param AttributeCollection $attributes
* A collection of attributes for the current profile.
* @param array $input
* An array with the attribute values from the editor.
*/
private function addAttributes(array &$form, FormStateInterface &$form_state, AttributeCollection $attributes, array $input) {
if ($attributes->count()) {
$form['linkit_attributes'] = [
'#type' => 'container',
'#title' => $this->t('Attributes'),
'#weight' => '10',
];
/** @var \Drupal\linkit\AttributeInterface $plugin */
foreach ($attributes as $plugin) {
$plugin_name = $plugin->getHtmlName();
$default_value = isset($input[$plugin_name]) ? $input[$plugin_name] : '';
$form['linkit_attributes'][$plugin_name] = $plugin->buildFormElement($default_value);
$form['linkit_attributes'][$plugin_name] += [
'#parents' => [
'attributes', $plugin_name,
],
];
}
}
}
/**
* Gets the linkit profile entity.
*
* @return \Drupal\linkit\ProfileInterface
* The current linkit profile used by this form.
*/
public function getLinkitProfile() {
return $this->linkitProfile;
}
}
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Matcher\AddForm.
*/
namespace Drupal\linkit\Form\Matcher;
use Drupal\Core\Form\FormBase;
@@ -88,11 +83,6 @@ class AddForm extends FormBase {
'#button_type' => 'primary',
];
$options = [];
foreach ($this->manager->getDefinitions() as $id => $plugin) {
$options[$id] = $plugin['label'];
}
return $form;
}
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Matcher\DeleteForm.
*/
namespace Drupal\linkit\Form\Matcher;
use Drupal\Core\Form\ConfirmFormBase;
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Matcher\EditForm.
*/
namespace Drupal\linkit\Form\Matcher;
use Drupal\Core\Form\FormBase;
@@ -14,7 +9,7 @@ use Drupal\Core\Url;
use Drupal\linkit\ProfileInterface;
/**
* Provides an edit form for matchers.
* Provides an edit form for matchers.
*/
class EditForm extends FormBase {
@@ -48,14 +43,14 @@ class EditForm extends FormBase {
$form += $this->linkitMatcher->buildConfigurationForm($form, $form_state);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
$form['actions'] = ['#type' => 'actions'];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Save changes'),
'#submit' => array('::submitForm'),
'#submit' => ['::submitForm'],
'#button_type' => 'primary',
);
$form['actions']['delete'] = array(
];
$form['actions']['delete'] = [
'#type' => 'link',
'#title' => $this->t('Delete'),
'#url' => Url::fromRoute('linkit.matcher.delete', [
@@ -65,7 +60,7 @@ class EditForm extends FormBase {
'#attributes' => [
'class' => ['button', 'button--danger'],
],
);
];
return $form;
}
@@ -79,7 +74,7 @@ class EditForm extends FormBase {
$this->linkitMatcher->submitConfigurationForm($form, $plugin_data);
$this->linkitProfile->save();
drupal_set_message($this->t('Saved %label configuration.', array('%label' => $this->linkitMatcher->getLabel())));
drupal_set_message($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(),
@@ -89,4 +84,5 @@ class EditForm extends FormBase {
'linkit_profile' => $this->linkitProfile->id(),
]);
}
}
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Matcher\OverviewForm.
*/
namespace Drupal\linkit\Form\Matcher;
use Drupal\Core\Form\FormBase;
@@ -70,8 +65,8 @@ class OverviewForm extends FormBase {
'#type' => 'table',
'#header' => [
[
'data' => $this->t('Matcher'),
'colspan' => 2
'data' => $this->t('Matchers'),
'colspan' => 2,
],
$this->t('Weight'),
$this->t('Operations'),
@@ -109,7 +104,7 @@ class OverviewForm extends FormBase {
$form['plugins'][$key]['weight'] = [
'#type' => 'weight',
'#title' => t('Weight for @title', ['@title' => (string) $plugin->getLabel()]),
'#title' => $this->t('Weight for @title', ['@title' => (string) $plugin->getLabel()]),
'#title_display' => 'invisible',
'#default_value' => $plugin->getWeight(),
'#attributes' => ['class' => ['plugin-order-weight']],
@@ -123,18 +118,18 @@ class OverviewForm extends FormBase {
$is_configurable = $plugin instanceof ConfigurableMatcherInterface;
if ($is_configurable) {
$form['plugins'][$key]['operations']['#links']['edit'] = [
'title' => t('Edit'),
'title' => $this->t('Edit'),
'url' => Url::fromRoute('linkit.matcher.edit', [
'linkit_profile' => $this->linkitProfile->id(),
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $key,
]),
];
}
$form['plugins'][$key]['operations']['#links']['delete'] = [
'title' => t('Delete'),
'title' => $this->t('Delete'),
'url' => Url::fromRoute('linkit.matcher.delete', [
'linkit_profile' => $this->linkitProfile->id(),
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $key,
]),
];
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Profile\AddForm.
*/
namespace Drupal\linkit\Form\Profile;
use Drupal\Core\Form\FormStateInterface;
@@ -1,16 +1,11 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Profile\EditForm.
*/
namespace Drupal\linkit\Form\Profile;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides an edit form for profile.
* Provides an edit form for profile.
*
* @see \Drupal\linkit\Profile\FormBase
*/
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Form\Profile\FormBase.
*/
namespace Drupal\linkit\Form\Profile;
use Drupal\Core\Entity\EntityForm;
@@ -39,7 +34,7 @@ abstract class FormBase extends EntityForm {
'#type' => 'machine_name',
'#default_value' => $this->entity->id(),
'#machine_name' => [
'exists' => ['\Drupal\linkit\Entity\Profile', 'load']
'exists' => ['\Drupal\linkit\Entity\Profile', 'load'],
],
'#disabled' => !$this->entity->isNew(),
];
@@ -51,10 +46,10 @@ abstract class FormBase extends EntityForm {
'#description' => $this->t('The text will be displayed on the <em>profile collection</em> page.'),
];
$form['additional_settings'] = array(
$form['additional_settings'] = [
'#type' => 'vertical_tabs',
'#weight' => 99,
);
];
return parent::form($form, $form_state);
}
@@ -69,7 +64,7 @@ abstract class FormBase extends EntityForm {
$linkit_profile->set('label', trim($linkit_profile->label()));
$status = $linkit_profile->save();
$edit_link = $this->entity->link($this->t('Edit'));
$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()]));
@@ -82,7 +77,7 @@ abstract class FormBase extends EntityForm {
case SAVED_UPDATED:
drupal_set_message($this->t('Updated profile %label.', ['%label' => $linkit_profile->label()]));
$this->logger('linkit')->notice('Updated profile %label.', ['%label' => $linkit_profile->label(), 'link' => $edit_link]);
$form_state->setRedirectUrl($linkit_profile->urlInfo('edit-form'));
$form_state->setRedirectUrl($linkit_profile->toUrl('edit-form'));
break;
}
}
@@ -1,14 +1,10 @@
<?php
/**
* @file
* Contains \Drupal\linkit\MatcherBase.
*/
namespace Drupal\linkit;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a base class for matchers.
@@ -40,6 +36,17 @@ abstract class MatcherBase extends PluginBase implements MatcherInterface, Conta
$this->setConfiguration($configuration);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition
);
}
/**
* {@inheritdoc}
*/
@@ -58,7 +65,7 @@ abstract class MatcherBase extends PluginBase implements MatcherInterface, Conta
* {@inheritdoc}
*/
public function getSummary() {
return array();
return [];
}
/**
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\MatcherCollection.
*/
namespace Drupal\linkit;
use Drupal\Core\Plugin\DefaultLazyPluginCollection;
@@ -21,15 +16,6 @@ class MatcherCollection extends DefaultLazyPluginCollection {
*/
protected $definitions;
/**
* {@inheritdoc}
*
* @return \Drupal\linkit\MatcherInterface
*/
public function &get($instance_id) {
return parent::get($instance_id);
}
/**
* {@inheritdoc}
*/
@@ -1,15 +1,9 @@
<?php
/**
* @file
* Contains \Drupal\linkit\MatcherInterface.
*/
namespace Drupal\linkit;
use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Component\Plugin\PluginInspectionInterface;
use Drupal\Core\Plugin\PluginFormInterface;
/**
* Defines the interface for matchers.
@@ -64,21 +58,14 @@ interface MatcherInterface extends PluginInspectionInterface, ConfigurablePlugin
public function setWeight($weight);
/**
* Gets an array with search matches that will be presented in the autocomplete
* widget.
* Executes the matcher.
*
* @param $string
* @param string $string
* The string that contains the text to search for.
*
* @return array
* An array whose values are an associative array containing:
* - title: A string to use as the search result label.
* - description: (optional) A string with additional information about the
* result item.
* - path: The URL to the item.
* - group: (optional) A string with the group name for the result item.
* Best practice is to use the plugin name as group name.
* @return \Drupal\linkit\Suggestion\SuggestionCollection
* A suggestion collection.
*/
public function getMatches($string);
public function execute($string);
}
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\MatcherManager.
*/
namespace Drupal\linkit;
use Drupal\Core\Cache\CacheBackendInterface;
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\MatcherTokensTrait.
*/
namespace Drupal\linkit;
/**
@@ -15,23 +10,23 @@ trait MatcherTokensTrait {
/**
* Inserts a form element with a list of available tokens.
*
* @param $form
* @param array $form
* The form array to append the token list to.
* @param array $types
* An array of token types to use.
*/
public function insertTokenList(&$form, array $types = array()) {
public function insertTokenList(array &$form, array $types = []) {
if (\Drupal::moduleHandler()->moduleExists('token')) {
// Add the token tree UI.
$form['token_tree'] = array(
$form['metadata']['token_tree'] = [
'#theme' => 'token_tree_link',
'#token_types' => $types,
'#dialog' => TRUE,
'#weight' => -90,
);
'#weight' => 10,
];
}
else {
$token_items = array();
$token_items = [];
foreach ($this->getAvailableTokens($types) as $type => $tokens) {
foreach ($tokens as $name => $info) {
$token_description = !empty($info['description']) ? $info['description'] : '';
@@ -40,16 +35,16 @@ trait MatcherTokensTrait {
}
if (count($token_items)) {
$form['tokens'] = array(
$form['metadata']['tokens'] = [
'#type' => 'details',
'#title' => t('Available tokens'),
'#weight' => -90,
);
'#weight' => 10,
];
$form['tokens']['list'] = array(
$form['metadata']['tokens']['list'] = [
'#theme' => 'item_list',
'#items' => $token_items,
);
];
}
}
}
@@ -59,10 +54,11 @@ trait MatcherTokensTrait {
*
* @param array $types
* An array of token types to use.
*
* @return array
* An array with available tokens
*/
public function getAvailableTokens(array $types = array()) {
public function getAvailableTokens(array $types = []) {
$info = \Drupal::token()->getInfo();
$available = array_intersect_key($info['tokens'], array_flip($types));
return $available;
@@ -1,123 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\CKEditorPlugin\Linkit.
*/
namespace Drupal\linkit\Plugin\CKEditorPlugin;
use Drupal\ckeditor\CKEditorPluginBase;
use Drupal\ckeditor\CKEditorPluginConfigurableInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\editor\Entity\Editor;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the "linkit" plugin.
*
* @CKEditorPlugin(
* id = "linkit",
* label = @Translation("Linkit"),
* module = "linkit"
* )
*/
class Linkit extends CKEditorPluginBase implements CKEditorPluginConfigurableInterface, ContainerFactoryPluginInterface {
/**
* The Linkit profile storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $linkitProfileStorage;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $linkit_profile_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->linkitProfileStorage = $linkit_profile_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')->getStorage('linkit_profile')
);
}
/**
* {@inheritdoc}
*/
public function getFile() {
return drupal_get_path('module', 'linkit') . '/js/plugins/linkit/plugin.js';
}
/**
* {@inheritdoc}
*/
public function getConfig(Editor $editor) {
return array(
'linkit_dialogTitleAdd' => t('Add link'),
'linkit_dialogTitleEdit' => t('Edit link'),
);
}
/**
* {@inheritdoc}
*/
public function getButtons() {
return array(
'Linkit' => array(
'label' => t('Linkit'),
'image' => drupal_get_path('module', 'linkit') . '/js/plugins/linkit/linkit.png',
),
);
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) {
$settings = $editor->getSettings();
$all_profiles = $this->linkitProfileStorage->loadMultiple();
$options = array();
foreach ($all_profiles as $profile) {
$options[$profile->id()] = $profile->label();
}
$form['linkit_profile'] = array(
'#type' => 'select',
'#title' => t('Select a linkit profile'),
'#options' => $options,
'#default_value' => isset($settings['plugins']['linkit']) ? $settings['plugins']['linkit'] : '',
'#empty_option' => $this->t('- Select profile -'),
'#description' => $this->t('Select the linkit profile you wish to use with this text format.'),
'#element_validate' => array(
array($this, 'validateLinkitProfileSelection'),
),
);
return $form;
}
/**
* #element_validate handler for the "linkit_profile" element in settingsForm().
*/
public function validateLinkitProfileSelection(array $element, FormStateInterface $form_state) {
$toolbar_buttons = $form_state->getValue(array('editor', 'settings', 'toolbar', 'button_groups'));
if (strpos($toolbar_buttons, '"Linkit"') !== FALSE && empty($element['#value'])) {
$form_state->setError($element, t('Please select the linkit profile you wish to use.'));
}
}
}
@@ -0,0 +1,111 @@
<?php
namespace Drupal\linkit\Plugin\CKEditorPlugin;
use Drupal\ckeditor\CKEditorPluginConfigurableInterface;
use Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalLink;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\editor\Entity\Editor;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Adds a settings form to select a Linkit profile on the default link plugin.
*/
class LinkitDrupalLink extends DrupalLink implements CKEditorPluginConfigurableInterface, ContainerFactoryPluginInterface {
/**
* The Linkit profile storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $linkitProfileStorage;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $linkit_profile_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->linkitProfileStorage = $linkit_profile_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')->getStorage('linkit_profile')
);
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) {
$settings = $editor->getSettings();
$all_profiles = $this->linkitProfileStorage->loadMultiple();
$options = [];
foreach ($all_profiles as $profile) {
$options[$profile->id()] = $profile->label();
}
$form['linkit_enabled'] = [
'#type' => 'checkbox',
'#title' => $this->t('Linkit enabled'),
'#default_value' => isset($settings['plugins']['drupallink']['linkit_enabled']) ? $settings['plugins']['drupallink']['linkit_enabled'] : '',
'#description' => $this->t('Enable Linkit for this text format.'),
];
$form['linkit_profile'] = [
'#type' => 'select',
'#title' => $this->t('Linkit profile'),
'#options' => $options,
'#default_value' => isset($settings['plugins']['drupallink']['linkit_profile']) ? $settings['plugins']['drupallink']['linkit_profile'] : '',
'#empty_option' => $this->t('- Select -'),
'#description' => $this->t('Select the Linkit profile you wish to use with this text format.'),
'#states' => [
'invisible' => [
'input[data-drupal-selector="edit-editor-settings-plugins-drupallink-linkit-enabled"]' => ['checked' => FALSE],
],
],
'#element_validate' => [
[$this, 'validateLinkitProfileSelection'],
],
];
return $form;
}
/**
* Linkit profile select validation.
*
* #element_validate callback for the "linkit_profile" element.
*
* @param array $element
* An associative array containing the properties and children of the
* generic form element.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form for the form this element belongs to.
*
* @see \Drupal\Core\Render\Element\FormElement::processPattern()
*/
public function validateLinkitProfileSelection(array $element, FormStateInterface $form_state) {
$values = $form_state->getValue([
'editor',
'settings',
'plugins',
'drupallink',
]);
$enabled = isset($values['linkit_enabled']) && $values['linkit_enabled'] === 1;
if ($enabled && empty(trim($values['linkit_profile']))) {
$form_state->setError($element, $this->t('Please select the Linkit profile you wish to use.'));
}
}
}
@@ -1,39 +1,35 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Derivative\EntityMatcherDeriver.
*/
namespace Drupal\linkit\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides matchers based on active entities.
*
* @see plugin_api
*/
class EntityMatcherDeriver extends DeriverBase implements ContainerDeriverInterface {
/**
* The entity manager.
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityManager;
protected $entityTypeManager;
/**
* Creates an EntityMatcherDeriver object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(EntityManagerInterface $entity_manager) {
$this->entityManager = $entity_manager;
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
@@ -41,7 +37,7 @@ class EntityMatcherDeriver extends DeriverBase implements ContainerDeriverInterf
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container->get('entity.manager')
$container->get('entity_type.manager')
);
}
@@ -49,10 +45,13 @@ class EntityMatcherDeriver extends DeriverBase implements ContainerDeriverInterf
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
$has_canonical = $entity_type->hasLinkTemplate('canonical');
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
$canonical = $entity_type->getLinkTemplate('canonical');
$edit_form = $entity_type->getLinkTemplate('edit-form');
if ($has_canonical) {
// 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)) {
$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();
@@ -0,0 +1,156 @@
<?php
namespace Drupal\linkit\Plugin\Filter;
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
use Drupal\linkit\SubstitutionManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a Linkit filter.
*
* @Filter(
* id = "linkit",
* title = @Translation("Linkit URL converter"),
* description = @Translation("Updates links inserted by Linkit to point to entity URL aliases."),
* settings = {
* "title" = TRUE,
* },
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
* )
*/
class LinkitFilter extends FilterBase implements ContainerFactoryPluginInterface {
/**
* The entity repository.
*
* @var \Drupal\Core\Entity\EntityRepositoryInterface
*/
protected $entityRepository;
/**
* The substitution manager.
*
* @var \Drupal\linkit\SubstitutionManagerInterface
*/
protected $substitutionManager;
/**
* Constructs a LinkitFilter 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\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);
$this->entityRepository = $entity_repository;
$this->substitutionManager = $substitution_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.repository'),
$container->get('plugin.manager.linkit.substitution')
);
}
/**
* {@inheritdoc}
*/
public function process($text, $langcode) {
$result = new FilterProcessResult($text);
if (strpos($text, 'data-entity-type') !== FALSE && strpos($text, 'data-entity-uuid') !== FALSE) {
$dom = Html::load($text);
$xpath = new \DOMXPath($dom);
foreach ($xpath->query('//a[@data-entity-type and @data-entity-uuid]') as $element) {
/** @var \DOMElement $element */
try {
// Load the appropriate translation of the linked entity.
$entity_type = $element->getAttribute('data-entity-type');
$uuid = $element->getAttribute('data-entity-uuid');
// Make the substitution optional, for backwards compatibility,
// maintaining the previous hard-coded direct file link assumptions,
// for content created before the substitution feature.
if (!$substitution_type = $element->getAttribute('data-entity-substitution')) {
$substitution_type = $entity_type === 'file' ? 'file' : SubstitutionManagerInterface::DEFAULT_SUBSTITUTION;
}
$entity = $this->entityRepository->loadEntityByUuid($entity_type, $uuid);
if ($entity) {
$entity = $this->entityRepository->getTranslationFromContext($entity, $langcode);
/** @var \Drupal\Core\GeneratedUrl $url */
$url = $this->substitutionManager
->createInstance($substitution_type)
->getUrl($entity);
$element->setAttribute('href', $url->getGeneratedUrl());
$access = $entity->access('view', NULL, TRUE);
// Set the appropriate title attribute.
if ($this->settings['title'] && !$access->isForbidden() && !$element->getAttribute('title')) {
$element->setAttribute('title', $entity->label());
}
// The processed text now depends on:
$result
// - the linked entity access for the current user.
->addCacheableDependency($access)
// - the generated URL (which has undergone path & route
// processing)
->addCacheableDependency($url)
// - the linked entity (whose URL and title may change)
->addCacheableDependency($entity);
}
}
catch (\Exception $e) {
watchdog_exception('linkit_filter', $e);
}
}
$result->setProcessedText(Html::serialize($dom));
}
return $result;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form['title'] = [
'#type' => 'checkbox',
'#title' => $this->t('Automatically set the <code>title</code> attribute to that of the (translated) referenced content'),
'#default_value' => $this->settings['title'],
'#attached' => [
'library' => ['linkit/linkit.filter_html.admin'],
],
];
return $form;
}
}
@@ -1,38 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Accesskey.
*/
namespace Drupal\linkit\Plugin\Linkit\Attribute;
use Drupal\linkit\AttributeBase;
/**
* Accesskey attribute.
*
* @Attribute(
* id = "accesskey",
* label = @Translation("Accesskey"),
* html_name = "accesskey",
* description = @Translation("Basic input field for the accesskey attribute.")
* )
*/
class Accesskey extends AttributeBase {
/**
* {@inheritdoc}
*/
public function buildFormElement($default_value) {
return [
'#type' => 'textfield',
'#title' => t('Accesskey'),
'#default_value' => $default_value,
'#maxlength' => 255,
'#size' => 40,
'#placeholder' => t('The "accesskey" attribute value'),
];
}
}
@@ -1,41 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Clazz.
*/
namespace Drupal\linkit\Plugin\Linkit\Attribute;
use Drupal\linkit\AttributeBase;
/**
* Class attribute.
*
* @TODO: For now Drupal filter_html wont support class attributes with
* wildcards.
* See: \Drupal\filter\Plugin\Filter\FilterHtml::getHTMLRestrictions
* See: core/modules/filter/filter.filter_html.admin.js
*
* @Attribute(
* id = "class",
* label = @Translation("Class"),
* html_name = "class",
* description = @Translation("Basic input field for the class attribute."),
* )
*/
//class Clazz extends AttributeBase {
//
// /**
// * {@inheritdoc}
// */
// public function buildFormElement($default_value) {
// return [
// '#type' => 'textfield',
// '#title' => t('Class'),
// '#maxlength' => 255,
// '#size' => 40,
// ];
// }
//
//}
@@ -1,38 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Id.
*/
namespace Drupal\linkit\Plugin\Linkit\Attribute;
use Drupal\linkit\AttributeBase;
/**
* Id attribute.
*
* @Attribute(
* id = "id",
* label = @Translation("Id"),
* html_name = "id",
* description = @Translation("Basic input field for the id attribute."),
* )
*/
class Id extends AttributeBase {
/**
* {@inheritdoc}
*/
public function buildFormElement($default_value) {
return [
'#type' => 'textfield',
'#title' => t('Id'),
'#default_value' => $default_value,
'#maxlength' => 255,
'#size' => 40,
'#placeholder' => t('The "id" attribute value'),
];
}
}
@@ -1,38 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Relationship.
*/
namespace Drupal\linkit\Plugin\Linkit\Attribute;
use Drupal\linkit\AttributeBase;
/**
* Relationship attribute.
*
* @Attribute(
* id = "relationship",
* label = @Translation("Relationship"),
* html_name = "rel",
* description = @Translation("Basic input field for the relationship attribute."),
* )
*/
class Relationship extends AttributeBase {
/**
* {@inheritdoc}
*/
public function buildFormElement($default_value) {
return [
'#type' => 'textfield',
'#title' => t('Relationship'),
'#default_value' => $default_value,
'#maxlength' => 255,
'#size' => 40,
'#placeholder' => t('The "rel" attribute value'),
];
}
}
@@ -1,96 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Target.
*/
namespace Drupal\linkit\Plugin\Linkit\Attribute;
use Drupal\Core\Form\FormStateInterface;
use Drupal\linkit\ConfigurableAttributeBase;
/**
* Target attribute.
*
* @Attribute(
* id = "target",
* label = @Translation("Target"),
* html_name = "target"
* )
*/
class Target extends ConfigurableAttributeBase {
const SELECT_LIST = 'select_list';
const SIMPLE_CHECKBOX = 'simple_checkbox';
/**
* {@inheritdoc}
*/
public function buildFormElement($default_value) {
switch ($this->configuration['widget_type']) {
case self::SELECT_LIST:
return [
'#type' => 'select',
'#title' => t('Target'),
'#options' => [
'' => '',
'_blank' => t('New window (_blank)'),
'_top' => t('Top window (_top)'),
'_self' => t('Same window (_self)'),
'_parent' => t('Parent window (_parent)')
],
'#default_value' => $default_value,
];
case self::SIMPLE_CHECKBOX:
return [
'#type' => 'checkbox',
'#title' => t('Open in new window'),
'#default_value' => $default_value,
'#return_value' => '_blank',
];
}
return [];
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
'widget_type' => self::SIMPLE_CHECKBOX,
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['widget_type'] = [
'#type' => 'radios',
'#title' => $this->t('Widget type'),
'#default_value' => $this->configuration['widget_type'],
'#options' => [
self::SELECT_LIST => $this->t('Selectlist with predefined targets.'),
self::SIMPLE_CHECKBOX => $this->t('Simple checkbox to allow links to be opened in a new browser window or tab.'),
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['widget_type'] = $form_state->getValue('widget_type');
}
}
@@ -1,82 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Attribute\Title.
*/
namespace Drupal\linkit\Plugin\Linkit\Attribute;
use Drupal\Core\Form\FormStateInterface;
use Drupal\linkit\ConfigurableAttributeBase;
/**
* Title attribute.
*
* @Attribute(
* id = "title",
* label = @Translation("Title"),
* html_name = "title",
* description = @Translation("Basic input field for the title attribute.")
* )
*/
class Title extends ConfigurableAttributeBase {
/**
* {@inheritdoc}
*/
public function buildFormElement($default_value) {
$element = [
'#type' => 'textfield',
'#title' => t('Title'),
'#default_value' => $default_value,
'#maxlength' => 255,
'#size' => 40,
'#placeholder' => t('The "title" attribute value'),
];
if ($this->configuration['automatic_title']) {
$element['#attached']['library'][] = 'linkit/linkit.attribute.title';
$element['#placeholder'] = t('The "title" attribute value (auto populated)');
}
return $element;
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
'automatic_title' => FALSE,
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['automatic_title'] = [
'#type' => 'checkbox',
'#title' => $this->t('Automatically populate title'),
'#default_value' => $this->configuration['automatic_title'],
'#description' => $this->t('Automatically populate the title attribute with the title from the match selection.'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['automatic_title'] = $form_state->getValue('automatic_title');
}
}
@@ -0,0 +1,38 @@
<?php
namespace Drupal\linkit\Plugin\Linkit\Matcher;
/**
* Provides specific linkit matchers for contact forms.
*
* @Matcher(
* id = "entity:contact_form",
* label = @Translation("Contact form"),
* target_entity = "contact_form",
* provider = "contact"
* )
*/
class ContactFormMatcher extends EntityMatcher {
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
return parent::calculateDependencies() + [
'module' => ['contact'],
];
}
/**
* {@inheritdoc}
*/
protected function buildEntityQuery($search_string) {
$query = parent::buildEntityQuery($search_string);
// Remove the personal contact form from the suggestion list.
$query->condition('id', 'personal', '<>');
return $query;
}
}
@@ -0,0 +1,40 @@
<?php
namespace Drupal\linkit\Plugin\Linkit\Matcher;
use Drupal\Component\Utility\Html;
use Drupal\linkit\MatcherBase;
use Drupal\linkit\Suggestion\DescriptionSuggestion;
use Drupal\linkit\Suggestion\SuggestionCollection;
/**
* Provides specific linkit matchers for emails.
*
* @Matcher(
* id = "email",
* label = @Translation("Email"),
* )
*/
class EmailMatcher extends MatcherBase {
/**
* {@inheritdoc}
*/
public function execute($string) {
$suggestions = new SuggestionCollection();
// 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]))
->setPath('mailto:' . Html::escape($string))
->setGroup($this->t('E-mail'))
->setDescription($this->t('Opens your mail client ready to e-mail @email', ['@email' => $string]));
$suggestions->addSuggestion($suggestion);
}
return $suggestions;
}
}
@@ -1,24 +1,31 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\EntityMatcher.
*/
namespace Drupal\linkit\Plugin\Linkit\Matcher;
use Drupal\Component\Utility\Html;
use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\linkit\ConfigurableMatcherBase;
use Drupal\linkit\MatcherTokensTrait;
use Drupal\linkit\SubstitutionManagerInterface;
use Drupal\linkit\Suggestion\EntitySuggestion;
use Drupal\linkit\Suggestion\SuggestionCollection;
use Drupal\linkit\Utility\LinkitXss;
use Exception;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides default linkit matchers for all entity types.
*
* @Matcher(
* id = "entity",
* label = @Translation("Entity"),
@@ -37,11 +44,25 @@ class EntityMatcher extends ConfigurableMatcherBase {
protected $database;
/**
* The entity manager.
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityManager;
protected $entityTypeManager;
/**
* The entity type bundle info.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $entityTypeBundleInfo;
/**
* The entity repository.
*
* @var \Drupal\Core\Entity\EntityRepositoryInterface
*/
protected $entityRepository;
/**
* The module handler service.
@@ -58,26 +79,36 @@ class EntityMatcher extends ConfigurableMatcherBase {
protected $currentUser;
/**
* The target entity type id
* The target entity type ID.
*
* @var string
*/
protected $target_type;
protected $targetType;
/**
* The substitution manager.
*
* @var \Drupal\linkit\SubstitutionManagerInterface
*/
protected $substitutionManager;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityRepositoryInterface $entity_repository, ModuleHandlerInterface $module_handler, AccountInterface $current_user, SubstitutionManagerInterface $substitution_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
if (empty($plugin_definition['target_entity'])) {
throw new \InvalidArgumentException("Missing required 'target_entity' property for a matcher.");
}
$this->database = $database;
$this->entityManager = $entity_manager;
$this->entityTypeManager = $entity_type_manager;
$this->entityTypeBundleInfo = $entity_type_bundle_info;
$this->entityRepository = $entity_repository;
$this->moduleHandler = $module_handler;
$this->currentUser = $current_user;
$this->target_type = $plugin_definition['target_entity'];
$this->targetType = $plugin_definition['target_entity'];
$this->substitutionManager = $substitution_manager;
}
/**
@@ -89,9 +120,12 @@ class EntityMatcher extends ConfigurableMatcherBase {
$plugin_id,
$plugin_definition,
$container->get('database'),
$container->get('entity.manager'),
$container->get('entity_type.manager'),
$container->get('entity_type.bundle.info'),
$container->get('entity.repository'),
$container->get('module_handler'),
$container->get('current_user')
$container->get('current_user'),
$container->get('plugin.manager.linkit.substitution')
);
}
@@ -100,12 +134,12 @@ class EntityMatcher extends ConfigurableMatcherBase {
*/
public function getSummary() {
$summery = parent::getSummary();
$entity_type = $this->entityManager->getDefinition($this->target_type);
$entity_type = $this->entityTypeManager->getDefinition($this->targetType);
$result_description = $this->configuration['result_description'];
if (!empty($result_description)) {
$summery[] = $this->t('Result description: @result_description', [
'@result_description' => $result_description
$metadata = $this->configuration['metadata'];
if (!empty($metadata)) {
$summery[] = $this->t('Metadata: @metadata', [
'@metadata' => $metadata,
]);
}
@@ -114,7 +148,7 @@ class EntityMatcher extends ConfigurableMatcherBase {
$bundles = [];
if ($has_bundle_filter) {
$bundles_info = $this->entityManager->getBundleInfo($this->target_type);
$bundles_info = $this->entityTypeBundleInfo->getBundleInfo($this->targetType);
foreach ($this->configuration['bundles'] as $bundle) {
$bundles[] = $bundles_info[$bundle]['label'];
}
@@ -136,55 +170,93 @@ class EntityMatcher extends ConfigurableMatcherBase {
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
'result_description' => '',
return [
'metadata' => '',
'bundles' => [],
'group_by_bundle' => FALSE,
];
'substitution_type' => SubstitutionManagerInterface::DEFAULT_SUBSTITUTION,
] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$entity_type = $this->entityManager->getDefinition($this->target_type);
$form['result_description'] = [
'#title' => $this->t('Result description'),
'#type' => 'textfield',
'#default_value' => $this->configuration['result_description'],
'#size' => 120,
'#maxlength' => 255,
$entity_type = $this->entityTypeManager->getDefinition($this->targetType);
$form['metadata'] = [
'#type' => 'details',
'#title' => $this->t('Suggestion metadata'),
'#open' => TRUE,
'#weight' => -100,
];
$this->insertTokenList($form, [$this->target_type]);
$form['metadata']['metadata'] = [
'#title' => $this->t('Metadata'),
'#type' => 'textfield',
'#default_value' => $this->configuration['metadata'],
'#description' => $this->t('Metadata is shown together with each suggestion in the suggestion list.'),
'#size' => 120,
'#maxlength' => 255,
'#weight' => 0,
];
$this->insertTokenList($form, [$this->targetType]);
// Filter the possible bundles to use if the entity has bundles.
if ($entity_type->hasKey('bundle')) {
$bundle_options = [];
foreach ($this->entityManager->getBundleInfo($this->target_type) as $bundle_name => $bundle_info) {
foreach ($this->entityTypeBundleInfo->getBundleInfo($this->targetType) as $bundle_name => $bundle_info) {
$bundle_options[$bundle_name] = $bundle_info['label'];
}
$form['bundles'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Restrict to the selected bundles'),
'#options' => $bundle_options,
'#default_value' => $this->configuration['bundles'],
'#description' => $this->t('If none of the checkboxes is checked, allow all bundles.'),
'#element_validate' => [[get_class($this), 'elementValidateFilter']],
'#weight' => -50,
$form['bundle_restrictions'] = [
'#type' => 'details',
'#title' => $this->t('Bundle restrictions'),
'#open' => TRUE,
'#weight' => -90,
];
// Group the results by bundle.
$form['group_by_bundle'] = [
$form['bundle_restrictions']['bundles'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Restrict suggestions to the selected bundles'),
'#options' => $bundle_options,
'#default_value' => $this->configuration['bundles'],
'#description' => $this->t('If none of the checkboxes is checked, all bundles are allowed.'),
'#element_validate' => [[get_class($this), 'elementValidateFilter']],
];
$form['bundle_grouping'] = [
'#type' => 'details',
'#title' => $this->t('Bundle grouping'),
'#open' => TRUE,
];
// Group the suggestions by bundle.
$form['bundle_grouping']['group_by_bundle'] = [
'#type' => 'checkbox',
'#title' => $this->t('Group by bundle'),
'#default_value' => $this->configuration['group_by_bundle'],
'#weight' => -50,
'#description' => $this->t('Group suggestions by their bundle.'),
];
}
$substitution_options = $this->substitutionManager->getApplicablePluginsOptionList($this->targetType);
$form['substitution'] = [
'#type' => 'details',
'#title' => $this->t('URL substitution'),
'#open' => TRUE,
'#weight' => 100,
'#access' => count($substitution_options) !== 1,
];
$form['substitution']['substitution_type'] = [
'#title' => $this->t('Substitution Type'),
'#type' => 'select',
'#default_value' => $this->configuration['substitution_type'],
'#options' => $substitution_options,
'#description' => $this->t('Configure how the selected entity should be transformed into a URL for insertion.'),
];
return $form;
}
@@ -198,9 +270,10 @@ class EntityMatcher extends ConfigurableMatcherBase {
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['result_description'] = $form_state->getValue('result_description');
$this->configuration['metadata'] = $form_state->getValue('metadata');
$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');
}
/**
@@ -214,55 +287,64 @@ class EntityMatcher extends ConfigurableMatcherBase {
/**
* {@inheritdoc}
*/
public function getMatches($string) {
public function execute($string) {
$suggestions = new SuggestionCollection();
$query = $this->buildEntityQuery($string);
$result = $query->execute();
$query_result = $query->execute();
$url_results = $this->findEntityIdByUrl($string);
$result = array_merge($query_result, $url_results);
// If no results, return an empty suggestion collection.
if (empty($result)) {
return [];
return $suggestions;
}
$matches = [];
$entities = $this->entityManager->getStorage($this->target_type)->loadMultiple($result);
$entities = $this->entityTypeManager->getStorage($this->targetType)->loadMultiple($result);
foreach ($entities as $entity_id => $entity) {
foreach ($entities as $entity) {
// Check the access against the defined entity access handler.
/** @var \Drupal\Core\Access\AccessResultInterface $access */
$access = $entity->access('view', $this->currentUser, TRUE);
if (!$access->isAllowed()) {
continue;
}
$matches[] = [
'title' => $this->buildLabel($entity),
'description' => $this->buildDescription($entity),
'path' => $this->buildPath($entity),
'group' => $this->buildGroup($entity),
];
$entity = $this->entityRepository->getTranslationFromContext($entity);
$suggestion = $this->createSuggestion($entity);
$suggestions->addSuggestion($suggestion);
}
return $matches;
return $suggestions;
}
/**
* Builds an EntityQuery to get entities.
*
* @param $match
* @param string $search_string
* Text to match the label against.
*
* @return \Drupal\Core\Entity\Query\QueryInterface
* The EntityQuery object with the basic conditions and sorting applied to
* it.
*/
protected function buildEntityQuery($match) {
$match = $this->database->escapeLike($match);
protected function buildEntityQuery($search_string) {
$search_string = $this->database->escapeLike($search_string);
$entity_type = $this->entityManager->getDefinition($this->target_type);
$query = $this->entityManager->getStorage($this->target_type)->getQuery();
$entity_type = $this->entityTypeManager->getDefinition($this->targetType);
$query = $this->entityTypeManager->getStorage($this->targetType)->getQuery();
$label_key = $entity_type->getKey('label');
if ($label_key) {
$query->condition($label_key, '%' . $match . '%', 'LIKE');
// For configuration entities, the condition needs to be CONTAINS as
// the matcher does not support LIKE.
if ($entity_type instanceof ConfigEntityTypeInterface) {
$query->condition($label_key, $search_string, 'CONTAINS');
}
else {
$query->condition($label_key, '%' . $search_string . '%', 'LIKE');
}
$query->sort($label_key, 'ASC');
}
@@ -271,19 +353,51 @@ class EntityMatcher extends ConfigurableMatcherBase {
$query->condition($bundle_key, $this->configuration['bundles'], 'IN');
}
// Add tags to let other modules alter the query.
$query->addTag('linkit_entity_autocomplete');
$query->addTag('linkit_entity_' . $this->target_type . '_autocomplete');
// Add access tag for the query.
$query->addTag('entity_access');
$query->addTag($this->target_type . '_access');
$this->addQueryTags($query);
return $query;
}
/**
* Builds the label string used in the match array.
* Adds query tags to the query.
*
* @param \Drupal\Core\Entity\Query\QueryInterface $query
* A query to add tags to.
*/
protected function addQueryTags(QueryInterface $query) {
// Add tags to let other modules alter the query.
$query->addTag('linkit_entity_autocomplete');
$query->addTag('linkit_entity_' . $this->targetType . '_autocomplete');
// Add access tag for the query.
$query->addTag('entity_access');
$query->addTag($this->targetType . '_access');
}
/**
* Creates a suggestion.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The matched entity.
*
* @return \Drupal\linkit\Suggestion\EntitySuggestion
* A suggestion object with populated entity data.
*/
protected function createSuggestion(EntityInterface $entity) {
$suggestion = new EntitySuggestion();
$suggestion->setLabel($this->buildLabel($entity))
->setGroup($this->buildGroup($entity))
->setDescription($this->buildDescription($entity))
->setEntityUuid($entity->uuid())
->setEntityTypeId($entity->getEntityTypeId())
->setSubstitutionId($this->configuration['substitution_type'])
->setPath($this->buildPath($entity));
return $suggestion;
}
/**
* Builds the label string used in the suggestion.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The matched entity.
@@ -291,39 +405,26 @@ class EntityMatcher extends ConfigurableMatcherBase {
* @return string
* The label for this entity.
*/
protected function buildLabel($entity) {
protected function buildLabel(EntityInterface $entity) {
return Html::escape($entity->label());
}
/**
* Builds the description string used in the match array.
* Builds the metadata string used in the suggestion.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The matched entity.
*
* @return string
* The description for this entity.
* The metadata for this entity.
*/
protected function buildDescription($entity) {
$description = \Drupal::token()->replace($this->configuration['result_description'], [$this->target_type => $entity], []);
protected function buildDescription(EntityInterface $entity) {
$description = \Drupal::token()->replace($this->configuration['metadata'], [$this->targetType => $entity], ['clear' => TRUE]);
return LinkitXss::descriptionFilter($description);
}
/**
* Builds the path string used in the match array.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The matched entity.
*
* @return string
* The URL for this entity.
*/
protected function buildPath($entity) {
return $entity->toUrl()->toString();
}
/**
* Builds the group string used in the match array.
* Builds the group string used in the suggestion.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The matched entity.
@@ -331,13 +432,13 @@ class EntityMatcher extends ConfigurableMatcherBase {
* @return string
* The match group for this entity.
*/
protected function buildGroup($entity) {
protected function buildGroup(EntityInterface $entity) {
$group = $entity->getEntityType()->getLabel();
// If the entities by this entity should be grouped by bundle, get the
// name and append it to the group.
if ($this->configuration['group_by_bundle']) {
$bundles = $this->entityManager->getBundleInfo($entity->getEntityTypeId());
$bundles = $this->entityTypeBundleInfo->getBundleInfo($entity->getEntityTypeId());
$bundle_label = $bundles[$entity->bundle()]['label'];
$group .= ' - ' . $bundle_label;
}
@@ -345,4 +446,43 @@ class EntityMatcher extends ConfigurableMatcherBase {
return $group;
}
/**
* Builds the path string used in the suggestion.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The matched entity.
*
* @return string
* The path for this entity.
*/
protected function buildPath(EntityInterface $entity) {
return $entity->toUrl('canonical', ['path_processing' => FALSE])->toString();
}
/**
* Finds entity id from the given input.
*
* @param string $user_input
* The string to url parse.
*
* @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 = [];
try {
$params = Url::fromUserInput($user_input)->getRouteParameters();
if (key($params) === $this->targetType) {
$result = [end($params)];
}
}
catch (Exception $e) {
// Do nothing.
}
return $result;
}
}
@@ -1,21 +1,19 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\FileMatcher.
*/
namespace Drupal\linkit\Plugin\Linkit\Matcher;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\linkit\Utility\LinkitXss;
/**
* Provides specific linkit matchers for the file entity type.
*
* @Matcher(
* id = "entity:file",
* target_entity = "file",
* label = @Translation("File"),
* target_entity = "file",
* provider = "file"
* )
*/
@@ -25,39 +23,48 @@ class FileMatcher extends EntityMatcher {
* {@inheritdoc}
*/
public function getSummary() {
$summery = parent::getSummary();
$summary = parent::getSummary();
$summery[] = $this->t('Show image dimensions: @show_image_dimensions', [
if (!empty($this->configuration['file_extensions'])) {
$summary[] = $this->t('Limit matches to the following file extensions: @file_extensions', [
'@file_extensions' => str_replace(' ', ', ', $this->configuration['file_extensions']),
]);
}
$summary[] = $this->t('Show image dimensions: @show_image_dimensions', [
'@show_image_dimensions' => $this->configuration['images']['show_dimensions'] ? $this->t('Yes') : $this->t('No'),
]);
$summery[] = $this->t('Show image thumbnail: @show_image_thumbnail', [
$summary[] = $this->t('Show image thumbnail: @show_image_thumbnail', [
'@show_image_thumbnail' => $this->configuration['images']['show_thumbnail'] ? $this->t('Yes') : $this->t('No'),
]);
if ($this->moduleHandler->moduleExists('image') && $this->configuration['images']['show_thumbnail']) {
$image_style = ImageStyle::load($this->configuration['images']['thumbnail_image_style']);
if (!is_null($image_style)) {
$summery[] = $this->t('Thumbnail style: @thumbnail_style', [
'@thumbnail_style' => $image_style->label(),
if (!is_null($image_style)) {
$summary[] = $this->t('Thumbnail style: @thumbnail_style', [
'@thumbnail_style' => $image_style->label(),
]);
}
}
return $summery;
return $summary;
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
return [
'file_extensions' => '',
'file_status' => FILE_STATUS_PERMANENT,
'images' => [
'show_dimensions' => FALSE,
'show_thumbnail' => FALSE,
'thumbnail_image_style' => 'linkit_result_thumbnail',
],
];
'substitution_type' => 'file',
] + parent::defaultConfiguration();
}
/**
@@ -82,28 +89,46 @@ class FileMatcher extends EntityMatcher {
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$form['images'] = array(
$form['extensions'] = [
'#type' => 'details',
'#title' => t('Image file settings'),
'#description' => t('Extra settings for image files in the result.'),
'#title' => $this->t('File extensions'),
'#open' => TRUE,
'#weight' => -100,
];
$file_extensions = str_replace(' ', ', ', $this->configuration['file_extensions']);
$form['extensions']['file_extensions'] = [
'#type' => 'textfield',
'#title' => $this->t('Allowed file extensions'),
'#default_value' => $file_extensions,
'#description' => $this->t('Separate extensions with a space or comma and do not include the leading dot.'),
'#element_validate' => [['\Drupal\file\Plugin\Field\FieldType\FileItem', 'validateExtensions']],
'#maxlength' => 256,
];
$form['images'] = [
'#type' => 'details',
'#title' => $this->t('Image file settings'),
'#description' => $this->t('Extra settings for image files in the result.'),
'#open' => TRUE,
'#tree' => TRUE,
);
];
$form['images']['show_dimensions'] = [
'#title' => t('Show pixel dimensions'),
'#title' => $this->t('Show pixel dimensions'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['images']['show_dimensions'],
];
if ($this->moduleHandler->moduleExists('image')) {
$form['images']['show_thumbnail'] = [
'#title' => t('Show thumbnail'),
'#title' => $this->t('Show thumbnail'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['images']['show_thumbnail'],
];
$form['images']['thumbnail_image_style'] = [
'#title' => t('Thumbnail image style'),
'#title' => $this->t('Thumbnail image style'),
'#type' => 'select',
'#default_value' => $this->configuration['images']['thumbnail_image_style'],
'#options' => image_style_options(FALSE),
@@ -124,6 +149,8 @@ class FileMatcher extends EntityMatcher {
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::submitConfigurationForm($form, $form_state);
$this->configuration['file_extensions'] = $form_state->getValue('file_extensions');
$values = $form_state->getValue('images');
if (!$values['show_thumbnail']) {
$values['thumbnail_image_style'] = NULL;
@@ -135,9 +162,19 @@ class FileMatcher extends EntityMatcher {
/**
* {@inheritdoc}
*/
protected function buildEntityQuery($match) {
$query = parent::buildEntityQuery($match);
$query->condition('status', FILE_STATUS_PERMANENT);
protected function buildEntityQuery($search_string) {
$query = parent::buildEntityQuery($search_string);
$query->condition('status', $this->configuration['file_status']);
if (!empty($this->configuration['file_extensions'])) {
$file_extensions = explode(' ', $this->configuration['file_extensions']);
$group = $query->orConditionGroup();
foreach ($file_extensions as $file_extension) {
$group->condition('filename', '%\.' . $this->database->escapeLike($file_extension), 'LIKE');
}
$query->condition($group);
}
return $query;
}
@@ -145,8 +182,8 @@ class FileMatcher extends EntityMatcher {
/**
* {@inheritdoc}
*/
protected function buildDescription($entity) {
$description_array = array();
protected function buildDescription(EntityInterface $entity) {
$description_array = [];
$description_array[] = parent::buildDescription($entity);
@@ -161,29 +198,27 @@ class FileMatcher extends EntityMatcher {
}
if ($this->configuration['images']['show_thumbnail'] && $this->moduleHandler->moduleExists('image')) {
$image_element = array(
$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);
}
}
$description = implode('<br />' , $description_array);
$description = implode('<br />', $description_array);
return LinkitXss::descriptionFilter($description);
}
/**
* {@inheritdoc}
*
* The file entity still uses url() even though it's deprecated in the
* entity interface.
*/
protected function buildPath($entity) {
protected function buildPath(EntityInterface $entity) {
/** @var \Drupal\file\FileInterface $entity */
return file_url_transform_relative(file_create_url($entity->getFileUri()));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Drupal\linkit\Plugin\Linkit\Matcher;
use Drupal\Core\Url;
use Drupal\linkit\MatcherBase;
use Drupal\linkit\Suggestion\DescriptionSuggestion;
use Drupal\linkit\Suggestion\SuggestionCollection;
/**
* Provides specific linkit matchers for the front page.
*
* @Matcher(
* id = "front_page",
* label = @Translation("Front page"),
* )
*/
class FrontPageMatcher extends MatcherBase {
/**
* {@inheritdoc}
*/
public function execute($string) {
$suggestions = new SuggestionCollection();
// Special for link to front page.
if (strpos($string, 'front') !== FALSE) {
$suggestion = new DescriptionSuggestion();
$suggestion->setLabel($this->t('Front page'))
->setPath(Url::fromRoute('<front>')->toString())
->setGroup($this->t('System'))
->setDescription($this->t('The front page for this site.'));
$suggestions->addSuggestion($suggestion);
}
return $suggestions;
}
}
@@ -1,19 +1,16 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\NodeMatcher.
*/
namespace Drupal\linkit\Plugin\Linkit\Matcher;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides specific linkit matchers for the node entity type.
*
* @Matcher(
* id = "entity:node",
* target_entity = "node",
* label = @Translation("Content"),
* target_entity = "node",
* provider = "node"
* )
*/
@@ -36,9 +33,9 @@ class NodeMatcher extends EntityMatcher {
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
return [
'include_unpublished' => FALSE,
];
] + parent::defaultConfiguration();
}
/**
@@ -56,11 +53,17 @@ class NodeMatcher extends EntityMatcher {
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$form['include_unpublished'] = [
'#title' => t('Include unpublished nodes'),
$form['unpublished_nodes'] = [
'#type' => 'details',
'#title' => $this->t('Unpublished nodes'),
'#open' => TRUE,
];
$form['unpublished_nodes']['include_unpublished'] = [
'#title' => $this->t('Include unpublished nodes'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['include_unpublished'],
'#description' => t('In order to see unpublished nodes, the requesting user must also have permissions to do so.'),
'#description' => $this->t('In order to see unpublished nodes, users must also have permissions to do so.'),
];
return $form;
@@ -78,8 +81,8 @@ class NodeMatcher extends EntityMatcher {
/**
* {@inheritdoc}
*/
protected function buildEntityQuery($match) {
$query = parent::buildEntityQuery($match);
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) {
@@ -1,20 +1,18 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\TermMatcher.
*/
namespace Drupal\linkit\Plugin\Linkit\Matcher;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\linkit\Utility\LinkitXss;
/**
* Provides specific linkit matchers for the taxonomy_term entity type.
*
* @Matcher(
* id = "entity:taxonomy_term",
* target_entity = "taxonomy_term",
* label = @Translation("Taxonomy term"),
* target_entity = "taxonomy_term",
* provider = "taxonomy"
* )
*/
@@ -41,8 +39,8 @@ class TermMatcher extends EntityMatcher {
/**
* {@inheritdoc}
*/
protected function buildDescription($entity) {
$description = \Drupal::token()->replace($this->configuration['result_description'], ['term' => $entity], []);
protected function buildDescription(EntityInterface $entity) {
$description = \Drupal::token()->replace($this->configuration['metadata'], ['term' => $entity], []);
return LinkitXss::descriptionFilter($description);
}
@@ -1,19 +1,17 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Plugin\Linkit\Matcher\UserMatcher.
*/
namespace Drupal\linkit\Plugin\Linkit\Matcher;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\RoleInterface;
/**
* Provides specific linkit matchers for the user entity type.
*
* @Matcher(
* id = "entity:user",
* target_entity = "user",
* label = @Translation("User"),
* target_entity = "user",
* provider = "user"
* )
*/
@@ -41,13 +39,12 @@ class UserMatcher extends EntityMatcher {
* {@inheritdoc}
*/
public function defaultConfiguration() {
return parent::defaultConfiguration() + [
return [
'roles' => [],
'include_blocked' => FALSE,
];
] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
@@ -63,20 +60,33 @@ class UserMatcher extends EntityMatcher {
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$form['roles'] = array(
$form['role_restrictions'] = [
'#type' => 'details',
'#title' => $this->t('Role restrictions'),
'#open' => TRUE,
'#weight' => -90,
];
$form['role_restrictions']['roles'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Restrict to the selected roles'),
'#options' => array_diff_key(user_role_names(TRUE), array(RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID)),
'#default_value' => $this->configuration['roles'],
'#description' => $this->t('If none of the checkboxes is checked, allow all roles.'),
'#options' => array_diff_key(user_role_names(TRUE), [RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID]),
'#default_value' => $this->configuration['roles'],
'#description' => $this->t('If none of the checkboxes is checked, all roles are allowed.'),
'#element_validate' => [[get_class($this), 'elementValidateFilter']],
);
];
$form['include_blocked'] = [
'#title' => t('Include blocked user'),
$form['blocked_users'] = [
'#type' => 'details',
'#title' => $this->t('Blocked users'),
'#open' => TRUE,
];
$form['blocked_users']['include_blocked'] = [
'#title' => $this->t('Include blocked user'),
'#type' => 'checkbox',
'#default_value' => $this->configuration['include_blocked'],
'#description' => t('In order to see blocked users, the requesting user must also have permissions to do so.'),
'#description' => $this->t('In order to see blocked users, users must have permissions to do so.'),
];
return $form;
@@ -95,12 +105,12 @@ class UserMatcher extends EntityMatcher {
/**
* {@inheritdoc}
*/
protected function buildEntityQuery($match) {
$query = parent::buildEntityQuery($match);
protected function buildEntityQuery($search_string) {
$query = parent::buildEntityQuery($search_string);
$match = $this->database->escapeLike($match);
$search_string = $this->database->escapeLike($search_string);
// The user entity don't specify a label key so we have to do it instead.
$query->condition('name', '%' . $match . '%', 'LIKE');
$query->condition('name', '%' . $search_string . '%', 'LIKE');
// Filter by role.
if (!empty($this->configuration['roles'])) {
@@ -0,0 +1,34 @@
<?php
namespace Drupal\linkit\Plugin\Linkit\Substitution;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\linkit\SubstitutionInterface;
/**
* A substitution plugin for the canonical URL of an entity.
*
* @Substitution(
* id = "canonical",
* label = @Translation("Canonical URL"),
* )
*/
class Canonical extends PluginBase implements SubstitutionInterface {
/**
* {@inheritdoc}
*/
public function getUrl(EntityInterface $entity) {
return $entity->toUrl('canonical')->toString(TRUE);
}
/**
* {@inheritdoc}
*/
public static function isApplicable(EntityTypeInterface $entity_type) {
return $entity_type->hasLinkTemplate('canonical');
}
}
@@ -0,0 +1,39 @@
<?php
namespace Drupal\linkit\Plugin\Linkit\Substitution;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\GeneratedUrl;
use Drupal\linkit\SubstitutionInterface;
/**
* A substitution plugin for the URL to a file.
*
* @Substitution(
* id = "file",
* label = @Translation("Direct File URL"),
* )
*/
class File extends PluginBase implements SubstitutionInterface {
/**
* {@inheritdoc}
*/
public function getUrl(EntityInterface $entity) {
$url = new GeneratedUrl();
/** @var \Drupal\file\FileInterface $entity */
$url->setGeneratedUrl(file_create_url($entity->getFileUri()));
$url->addCacheableDependency($entity);
return $url;
}
/**
* {@inheritdoc}
*/
public static function isApplicable(EntityTypeInterface $entity_type) {
return $entity_type->isSubclassOf('Drupal\file\FileInterface');
}
}
@@ -0,0 +1,100 @@
<?php
namespace Drupal\linkit\Plugin\Linkit\Substitution;
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\linkit\SubstitutionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* A substitution plugin for the URL to a file.
*
* @Substitution(
* id = "media",
* label = @Translation("Direct URL to media file entity"),
* )
*/
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}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager')
);
}
/**
* 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.
*/
public function getUrl(EntityInterface $entity) {
$url = new GeneratedUrl();
/** @var \Drupal\media\MediaTypeInterface $media_type */
$media_type = $entity->get('bundle')->entity;
// Default to the canonical URL if the bundle doesn't have a source field.
if (empty($media_type->getSource()->getConfiguration()['source_field'])) {
return $entity->toUrl('canonical')->toString(TRUE);
}
$source_field = $media_type->getSource()->getConfiguration()['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.
*/
public static function isApplicable(EntityTypeInterface $entity_type) {
return $entity_type->entityClassImplements('Drupal\media\MediaInterface');
}
}
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\ProfileInterface.
*/
namespace Drupal\linkit;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
@@ -32,58 +27,6 @@ interface ProfileInterface extends ConfigEntityInterface {
*/
public function setDescription($description);
/**
* Returns a specific attribute.
*
* @param string $attribute_id
* The attribute ID.
*
* @return \Drupal\linkit\AttributeInterface
* The attribute object.
*/
public function getAttribute($attribute_id);
/**
* Returns the attributes for this profile.
*
* @return \Drupal\linkit\AttributeCollection|\Drupal\linkit\AttributeInterface[]
* The attribute collection.
*/
public function getAttributes();
/**
* Adds an attribute to this profile.
*
* @param array $configuration
* An array of attribute configuration.
*
* @return String
* The ID of the attribute.
*/
public function addAttribute(array $configuration);
/**
* Removes an attribute from this profile.
*
* @param string $attribute_id
* The attribute ID.
*
* @return $this
*/
public function removeAttribute($attribute_id);
/**
* Sets the configuration for an attribute instance.
*
* @param string $attribute_id
* The ID of the attribute to set the configuration for.
* @param array $configuration
* The attribute configuration to set.
*
* @return $this
*/
public function setAttributeConfig($attribute_id, array $configuration);
/**
* Returns a specific matcher.
*
@@ -118,7 +61,7 @@ interface ProfileInterface extends ConfigEntityInterface {
* Removes a matcher from this profile.
*
* @param \Drupal\linkit\MatcherInterface $matcher
* The matcher object.
* The matcher object.
*
* @return $this
*/
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\ProfileListBuilder.
*/
namespace Drupal\linkit;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
@@ -22,11 +17,11 @@ class ProfileListBuilder extends ConfigEntityListBuilder {
* {@inheritdoc}
*/
public function buildHeader() {
$header['title'] = t('Profile');
$header['description'] = [
'data' => t('Description'),
'class' => [RESPONSIVE_PRIORITY_MEDIUM],
];
$header['title'] = t('Profile');
$header['description'] = [
'data' => t('Description'),
'class' => [RESPONSIVE_PRIORITY_MEDIUM],
];
return $header + parent::buildHeader();
}
@@ -55,15 +50,7 @@ class ProfileListBuilder extends ConfigEntityListBuilder {
'title' => t('Manage matchers'),
'weight' => 10,
'url' => Url::fromRoute('linkit.matchers', [
'linkit_profile' => $entity->id()
]),
];
$operations['attributes'] = [
'title' => t('Manage attributes'),
'weight' => 20,
'url' => Url::fromRoute('linkit.attributes', [
'linkit_profile' => $entity->id()
'linkit_profile' => $entity->id(),
]),
];
@@ -1,74 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\ResultManager.
*/
namespace Drupal\linkit;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
/**
* Result service to handle autocomplete matcher results.
*/
class ResultManager {
/**
* Gets the results.
*
* @param ProfileInterface $linkitProfile
* The linkit profile.
* @param $search_string
* The string ro use in the matchers.
*
* @return array
* An array of matches.
*/
public function getResults(ProfileInterface $linkitProfile, $search_string) {
$matches = array();
if (empty(trim($search_string))) {
return [[
'title' => t('No results'),
]];
}
// Special for link to front page.
if (strpos($search_string, 'front') !== FALSE) {
$matches[] = [
'title' => t('Front page'),
'description' => 'The front page for this site.',
'path' => Url::fromRoute('<front>')->toString(),
'group' => t('System'),
];
}
foreach ($linkitProfile->getMatchers() as $plugin) {
$matches = array_merge($matches, $plugin->getMatches($search_string));
}
// Check for an e-mail address then return an e-mail match and create a
// mail-to link if appropriate.
if (filter_var($search_string, FILTER_VALIDATE_EMAIL)) {
$matches[] = [
'title' => t('E-mail @email', ['@email' => $search_string]),
'description' => t('Opens your mail client ready to e-mail @email', ['@email' => $search_string]),
'path' => 'mailto:' . Html::escape($search_string),
'group' => t('E-mail'),
];
}
// If there is still no matches, return a "no results" array.
if (empty($matches)) {
return [[
'title' => t('No results'),
]];
}
return $matches;
}
}
@@ -0,0 +1,36 @@
<?php
namespace Drupal\linkit;
use Drupal\Component\Plugin\PluginInspectionInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Interface for substitution plugins.
*/
interface SubstitutionInterface extends PluginInspectionInterface {
/**
* 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.
*/
public function getUrl(EntityInterface $entity);
/**
* 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.
*/
public static function isApplicable(EntityTypeInterface $entity_type);
}
@@ -0,0 +1,77 @@
<?php
namespace Drupal\linkit;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* A plugin manager for the substitution plugins.
*/
class SubstitutionManager extends DefaultPluginManager implements SubstitutionManagerInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs the SubstitutionManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct('Plugin/Linkit/Substitution', $namespaces, $module_handler, 'Drupal\linkit\SubstitutionInterface', 'Drupal\linkit\Annotation\Substitution');
$this->alterInfo('linkit_substitution');
$this->setCacheBackend($cache_backend, 'linkit_substitution');
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public function getApplicablePluginsOptionList($entity_type_id) {
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
$options = [];
foreach ($this->filterPlugins($this->getDefinitions(), $entity_type) as $id => $definition) {
$options[$id] = $definition['label'];
}
return $options;
}
/**
* Filter the list of plugins by their applicability.
*
* @param array $definitions
* An array of plugin definitions.
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type to get applicable plugins for.
*
* @return array
* The definitions appropriate for the given entity type.
*
* @see SubstitutionInterface::isApplicable()
*/
protected function filterPlugins(array $definitions, EntityTypeInterface $entity_type) {
return array_filter($definitions, function ($definition) use ($entity_type) {
/** @var \Drupal\linkit\SubstitutionInterface $class */
$class = $definition['class'];
return $class::isApplicable($entity_type);
});
}
}
@@ -0,0 +1,28 @@
<?php
namespace Drupal\linkit;
use Drupal\Component\Plugin\PluginManagerInterface;
/**
* An interface for the substitution manager.
*/
interface SubstitutionManagerInterface extends PluginManagerInterface {
/**
* Get the default substitution.
*/
const DEFAULT_SUBSTITUTION = 'canonical';
/**
* Get a form API options list for the entity ID.
*
* @param string $entity_type_id
* The entity type ID.
*
* @return array
* An options list.
*/
public function getApplicablePluginsOptionList($entity_type_id);
}
@@ -0,0 +1,44 @@
<?php
namespace Drupal\linkit\Suggestion;
/**
* Defines a linkit suggestion with description.
*/
class DescriptionSuggestion extends SimpleSuggestion implements SuggestionDescriptionInterface {
/**
* The suggestion description.
*
* A string with additional information about the suggestion.
*
* @var string
*/
protected $description;
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->description;
}
/**
* {@inheritdoc}
*/
public function setDescription($description) {
$this->description = $description;
return $this;
}
/**
* {@inheritdoc}
*/
public function jsonSerialize() {
return parent::jsonSerialize() + [
'description' => $this->getDescription(),
];
}
}
@@ -0,0 +1,81 @@
<?php
namespace Drupal\linkit\Suggestion;
/**
* Defines a entity suggestion.
*/
class EntitySuggestion extends DescriptionSuggestion {
/**
* The entity uuid.
*
* @var string
*/
protected $entityUuid;
/**
* The entity type id.
*
* @var string
*/
protected $entityTypeId;
/**
* The substitution id.
*
* @var string
*/
protected $substitutionId;
/**
* Sets the entity uuid.
*
* @param string $entity_uuid
* The entity uuid.
*
* @return $this
*/
public function setEntityUuid($entity_uuid) {
$this->entityUuid = $entity_uuid;
return $this;
}
/**
* Sets the entity type id.
*
* @param string $entity_type_id
* The entity type id.
*
* @return $this
*/
public function setEntityTypeId($entity_type_id) {
$this->entityTypeId = $entity_type_id;
return $this;
}
/**
* Sets the substitution id.
*
* @param string $substitution_id
* The substitution id.
*
* @return $this
*/
public function setSubstitutionId($substitution_id) {
$this->substitutionId = $substitution_id;
return $this;
}
/**
* {@inheritdoc}
*/
public function jsonSerialize() {
return parent::jsonSerialize() + [
'entity_uuid' => $this->entityUuid,
'entity_type_id' => $this->entityTypeId,
'substitution_id' => $this->substitutionId,
];
}
}
@@ -0,0 +1,87 @@
<?php
namespace Drupal\linkit\Suggestion;
/**
* Defines a simple suggestion.
*/
class SimpleSuggestion implements SuggestionInterface {
/**
* The suggestion label.
*
* @var string
*/
protected $label;
/**
* The suggestion path.
*
* @var string
*/
protected $path;
/**
* The suggestion group.
*
* @var string
*/
protected $group;
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->label;
}
/**
* {@inheritdoc}
*/
public function setLabel($label) {
$this->label = $label;
return $this;
}
/**
* {@inheritdoc}
*/
public function getPath() {
return $this->path;
}
/**
* {@inheritdoc}
*/
public function setPath($path) {
$this->path = $path;
return $this;
}
/**
* {@inheritdoc}
*/
public function getGroup() {
return $this->group;
}
/**
* {@inheritdoc}
*/
public function setGroup($group) {
$this->group = $group;
return $this;
}
/**
* {@inheritdoc}
*/
public function jsonSerialize() {
return [
'label' => $this->getLabel(),
'path' => $this->getPath(),
'group' => $this->getGroup(),
];
}
}
@@ -0,0 +1,56 @@
<?php
namespace Drupal\linkit\Suggestion;
/**
* Defines a suggestion collection used to avoid JSON Hijacking.
*/
class SuggestionCollection implements \JsonSerializable {
/**
* An array of suggestions.
*
* @var array
*/
protected $suggestions = [];
/**
* Returns all suggestions in the collection.
*
* @return \Drupal\linkit\Suggestion\SuggestionInterface[]
* All suggestions in the collection.
*/
public function getSuggestions() {
return $this->suggestions;
}
/**
* Adds a suggestion to this collection.
*
* @param \Drupal\linkit\Suggestion\SuggestionInterface $suggestion
* The suggestion to add to the collection.
*/
public function addSuggestion(SuggestionInterface $suggestion) {
$this->suggestions[] = $suggestion;
}
/**
* Adds a collection of suggestions to the this collection.
*
* @param \Drupal\linkit\Suggestion\SuggestionCollection $suggestionCollection
* A collection of suggestions.
*/
public function addSuggestions(SuggestionCollection $suggestionCollection) {
$this->suggestions = array_merge($this->suggestions, $suggestionCollection->getSuggestions());
}
/**
* {@inheritdoc}
*/
public function jsonSerialize() {
return [
'suggestions' => $this->suggestions,
];
}
}
@@ -0,0 +1,28 @@
<?php
namespace Drupal\linkit\Suggestion;
/**
* Defines the interface for suggestions that have a description.
*/
interface SuggestionDescriptionInterface {
/**
* Gets the suggestion description.
*
* @return string
* The suggestion description.
*/
public function getDescription();
/**
* Sets the suggestion description.
*
* @param string $description
* The suggestion description.
*
* @return $this
*/
public function setDescription($description);
}
@@ -0,0 +1,64 @@
<?php
namespace Drupal\linkit\Suggestion;
/**
* Defines the interface for suggestions.
*/
interface SuggestionInterface extends \JsonSerializable {
/**
* Gets the suggestion label.
*
* @return string
* The suggestion label.
*/
public function getLabel();
/**
* Sets the suggestion label.
*
* @param string $label
* The suggestion label to set.
*
* @return $this
*/
public function setLabel($label);
/**
* Gets the suggestion path.
*
* @return string
* The suggestion path.
*/
public function getPath();
/**
* Sets the suggestion path.
*
* @param string $path
* The suggestion path to set.
*
* @return $this
*/
public function setPath($path);
/**
* Gets the suggestion group.
*
* @return string
* The suggestion group.
*/
public function getGroup();
/**
* Sets the suggestion group.
*
* @param string $group
* The suggestion group to set.
*
* @return $this
*/
public function setGroup($group);
}
@@ -0,0 +1,63 @@
<?php
namespace Drupal\linkit;
use Drupal\Component\Utility\Html;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\linkit\Suggestion\DescriptionSuggestion;
use Drupal\linkit\Suggestion\SuggestionCollection;
/**
* Suggestion service to handle autocomplete suggestions.
*/
class SuggestionManager {
use StringTranslationTrait;
/**
* Gets the suggestions.
*
* @param ProfileInterface $linkitProfile
* The linkit profile.
* @param string $search_string
* The string ro use in the matchers.
*
* @return \Drupal\linkit\Suggestion\SuggestionCollection
* A suggestion collection.
*/
public function getSuggestions(ProfileInterface $linkitProfile, $search_string) {
$suggestions = new SuggestionCollection();
if (empty(trim($search_string))) {
return $suggestions;
}
foreach ($linkitProfile->getMatchers() as $plugin) {
$suggestions->addSuggestions($plugin->execute($search_string));
}
return $suggestions;
}
/**
* Adds an unscathed suggestion to the given suggestion collection.
*
* @param \Drupal\linkit\Suggestion\SuggestionCollection $suggestionCollection
* A suggestion collection to add the unscathed suggestion to.
* @param string $search_string
* The string ro use in the matchers.
*
* @return \Drupal\linkit\Suggestion\SuggestionCollection
* A suggestion collection.
*/
public function addUnscathedSuggestion(SuggestionCollection $suggestionCollection, $search_string) {
$suggestion = new DescriptionSuggestion();
$suggestion->setLabel(Html::escape($search_string))
->setGroup($this->t('No results'))
->setDescription($this->t('Linkit could not find any suggestions. This URL will be used as is.'))
->setPath($search_string);
$suggestionCollection->addSuggestion($suggestion);
return $suggestionCollection;
}
}
@@ -1,160 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Tests\AttributeCrudTest.
*/
namespace Drupal\linkit\Tests;
use Drupal\Core\Url;
use Drupal\linkit\Entity\Profile;
/**
* Tests adding, listing and deleting attributes on a profile.
*
* @group linkit
*/
class AttributeCrudTest extends LinkitTestBase {
/**
* The attribute manager.
*
* @var \Drupal\linkit\AttributeManager
*/
protected $manager;
/**
* The linkit profile.
*
* @var \Drupal\linkit\ProfileInterface
*/
protected $linkitProfile;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->manager = $this->container->get('plugin.manager.linkit.attribute');
$this->linkitProfile = $this->createProfile();
$this->drupalLogin($this->adminUser);
}
/**
* Test the overview page.
*/
function testOverview() {
$this->drupalGet(Url::fromRoute('linkit.attributes', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$this->assertText(t('No attributes added.'));
$this->assertLinkByHref(Url::fromRoute('linkit.attribute.add', [
'linkit_profile' => $this->linkitProfile->id(),
])->toString());
}
/**
* Test adding an attribute to a profile.
*/
function testAdd() {
$this->drupalGet(Url::fromRoute('linkit.attribute.add', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$this->assertEqual(count($this->manager->getDefinitions()), count($this->xpath('//input[@type="radio"]')), 'All attributes are available.');
$edit = array();
$edit['plugin'] = 'dummy_attribute';
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
$this->assertUrl(Url::fromRoute('linkit.attributes', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$this->assertEqual(1, count($this->xpath('//table/tbody/tr')), 'Attribute added.');
$this->assertNoText(t('No attributes added.'));
}
/**
* Test adding a configurable attribute to a profile.
*/
function testAddConfigurable() {
$this->drupalGet(Url::fromRoute('linkit.attribute.add', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$this->assertEqual(count($this->manager->getDefinitions()), count($this->xpath('//input[@type="radio"]')), 'All attributes are available.');
$edit = array();
$edit['plugin'] = 'configurable_dummy_attribute';
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
$this->assertUrl(Url::fromRoute('linkit.attribute.edit', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => 'configurable_dummy_attribute',
]));
$this->drupalGet(Url::fromRoute('linkit.attributes', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$this->assertEqual(1, count($this->xpath('//table/tbody/tr')), 'Attribute added.');
$this->assertNoText(t('No attributes added.'));
$plugin_url = Url::fromRoute('linkit.attribute.edit', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => 'configurable_dummy_attribute',
]);
$this->assertLinkByHref($plugin_url->toString());
}
/**
* Test delete an attribute from a profile.
*/
function testDelete() {
/** @var \Drupal\linkit\AttributeInterface $plugin */
$plugin = $this->manager->createInstance('dummy_attribute');
$this->linkitProfile->addAttribute($plugin->getConfiguration());
$this->linkitProfile->save();
// Try delete an attribute that is not attached to the profile.
$this->drupalGet(Url::fromRoute('linkit.attribute.delete', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => 'doesntexists'
]));
$this->assertResponse('404');
// Go to the delete page, but press cancel.
$this->drupalGet(Url::fromRoute('linkit.attribute.delete', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $plugin->getPluginId(),
]));
$this->clickLink(t('Cancel'));
$this->assertUrl(Url::fromRoute('linkit.attributes', [
'linkit_profile' => $this->linkitProfile->id(),
]));
// Delete the attribute from the profile.
$this->drupalGet(Url::fromRoute('linkit.attribute.delete', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => 'dummy_attribute',
]));
$this->drupalPostForm(NULL, [], t('Confirm'));
$this->assertRaw(t('The attribute %plugin has been deleted.', ['%plugin' => $plugin->getLabel()]));
$this->assertUrl(Url::fromRoute('linkit.attributes', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$this->assertText(t('No attributes added.'));
/** @var \Drupal\linkit\Entity\Profile $updated_profile */
$updated_profile = Profile::load($this->linkitProfile->id());
$this->assertFalse($updated_profile->getAttributes()->has($plugin->getPluginId()), 'The attribute is deleted from the profile');
}
}
@@ -1,83 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Tests\Controllers\LinkitControllerTest.
*/
namespace Drupal\linkit\Tests\Controllers;
use Drupal\Core\Url;
use Drupal\linkit\Tests\LinkitTestBase;
/**
* Tests Linkit controller.
*
* @group linkit
*/
class LinkitControllerTest extends LinkitTestBase {
/**
* The linkit profile.
*
* @var \Drupal\linkit\ProfileInterface
*/
protected $linkitProfile;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->linkitProfile = $this->createProfile();
$this->drupalLogin($this->adminUser);
}
/**
* Tests the profile route title callback.
*/
function testProfileTitle() {
$this->drupalGet(Url::fromRoute('entity.linkit_profile.edit_form', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$this->assertText('Edit ' . $this->linkitProfile->label() . ' profile');
}
/**
* Tests the matcher route title callback.
*/
function testMatcherTitle() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->container->get('plugin.manager.linkit.matcher')->createInstance('configurable_dummy_matcher');
$matcher_uuid = $this->linkitProfile->addMatcher($plugin->getConfiguration());
$this->linkitProfile->save();
$this->drupalGet(Url::fromRoute('linkit.matcher.edit', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $matcher_uuid,
]));
$this->assertText('Edit ' . $plugin->getLabel() . ' matcher');
}
/**
* Tests the attribute route title callback.
*/
function testAttributeTitle() {
/** @var \Drupal\linkit\AttributeInterface $plugin */
$plugin = $this->container->get('plugin.manager.linkit.attribute')->createInstance('configurable_dummy_attribute');
$this->linkitProfile->addAttribute($plugin->getConfiguration());
$this->linkitProfile->save();
$this->drupalGet(Url::fromRoute('linkit.attribute.edit', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $plugin->getPluginId(),
]));
$this->assertText('Edit ' . $plugin->getLabel() . ' attribute');
}
}
@@ -1,83 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Tests\LinkitTestBase.
*/
namespace Drupal\linkit\Tests;
use Drupal\Component\Utility\Unicode;
use Drupal\linkit\Entity\Profile;
use Drupal\simpletest\WebTestBase;
/**
* Sets up page and article content types.
*/
abstract class LinkitTestBase extends WebTestBase {
/**
* Modules to enable.
*
* Enable block module to get the local_actions_block to work.
*
* @var array
*/
public static $modules = ['linkit', 'linkit_test', 'block'];
/**
* A user with the 'administer linkit profiles' permission.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* A user without the 'administer linkit profiles' permission.
*
* @var \Drupal\user\UserInterface
*/
protected $baseUser;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(['administer linkit profiles']);
$this->baseUser = $this->drupalCreateUser();
$this->drupalPlaceBlock('page_title_block', ['region' => 'content']);
$this->drupalPlaceBlock('local_tasks_block', ['region' => 'content']);
$this->drupalPlaceBlock('local_actions_block', ['region' => 'content']);
$this->drupalPlaceBlock('system_messages_block', ['region' => 'highlighted']);
}
/**
* Creates a profile based on default settings.
*
* @param array $settings
* (optional) An associative array of settings for the profile, as used in
* entity_create(). Override the defaults by specifying the key and value
* in the array
*
* The following defaults are provided:
* - label: Random string.
*
* @return \Drupal\linkit\ProfileInterface
* The created profile entity.
*/
protected function createProfile(array $settings = []) {
// Populate defaults array.
$settings += [
'id' => Unicode::strtolower($this->randomMachineName()),
'label' => $this->randomMachineName(),
];
$profile = Profile::create($settings);
$profile->save();
return $profile;
}
}
@@ -1,158 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Tests\MatcherCrudTest.
*/
namespace Drupal\linkit\Tests;
use Drupal\Core\Url;
use Drupal\linkit\Entity\Profile;
/**
* Tests adding, listing, updating and deleting matchers on a profile.
*
* @group linkit
*/
class MatcherCrudTest extends LinkitTestBase {
/**
* The attribute manager.
*
* @var \Drupal\linkit\MatcherManager
*/
protected $manager;
/**
* The linkit profile.
*
* @var \Drupal\linkit\ProfileInterface
*/
protected $linkitProfile;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->manager = $this->container->get('plugin.manager.linkit.matcher');
$this->linkitProfile = $this->createProfile();
$this->drupalLogin($this->adminUser);
}
/**
* Test the overview page.
*/
function testOverview() {
$this->drupalGet(Url::fromRoute('linkit.matchers', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$this->assertText(t('No matchers added.'));
$this->assertLinkByHref(Url::fromRoute('linkit.matcher.add', [
'linkit_profile' => $this->linkitProfile->id(),
])->toString());
}
/**
* Test adding a matcher to a profile.
*/
function testAdd() {
$this->drupalGet(Url::fromRoute('linkit.matcher.add', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$edit = array();
$edit['plugin'] = 'dummy_matcher';
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
// Load the saved profile.
$this->linkitProfile = Profile::load($this->linkitProfile->id());
$matcher_ids = $this->linkitProfile->getMatchers()->getInstanceIds();
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->linkitProfile->getMatcher(current($matcher_ids));
$this->assertRaw(t('Added %label matcher.', ['%label' => $plugin->getLabel()]));
$this->assertNoText(t('No matchers added.'));
}
/**
* Test adding a configurable attribute to a profile.
*/
function testAddConfigurable() {
$this->drupalGet(Url::fromRoute('linkit.matcher.add', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$edit = array();
$edit['plugin'] = 'configurable_dummy_matcher';
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
// Load the saved profile.
$this->linkitProfile = Profile::load($this->linkitProfile->id());
$matcher_ids = $this->linkitProfile->getMatchers()->getInstanceIds();
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->linkitProfile->getMatcher(current($matcher_ids));
$this->assertUrl(Url::fromRoute('linkit.matcher.edit', [
'linkit_profile' => $this->linkitProfile->id(),
'plugin_instance_id' => $plugin->getUuid(),
]));
$this->drupalGet(Url::fromRoute('linkit.matchers', [
'linkit_profile' => $this->linkitProfile->id(),
]));
$this->assertNoText(t('No matchers added.'));
}
/**
* Test delete a matcher from a profile.
*/
function testDelete() {
/** @var \Drupal\linkit\AttributeInterface $plugin */
$plugin = $this->manager->createInstance('dummy_matcher');
$profile = $this->createProfile();
$plugin_uuid = $profile->addMatcher($plugin->getConfiguration());
$profile->save();
// Try delete a matcher that is not attached to the profile.
$this->drupalGet(Url::fromRoute('linkit.matcher.delete', [
'linkit_profile' => $profile->id(),
'plugin_instance_id' => 'doesntexists'
]));
$this->assertResponse('404');
// Go to the delete page, but press cancel.
$this->drupalGet(Url::fromRoute('linkit.matcher.delete', [
'linkit_profile' => $profile->id(),
'plugin_instance_id' => $plugin_uuid,
]));
$this->clickLink(t('Cancel'));
$this->assertUrl(Url::fromRoute('linkit.matchers', [
'linkit_profile' => $profile->id(),
]));
// Delete the matcher from the profile.
$this->drupalGet(Url::fromRoute('linkit.matcher.delete', [
'linkit_profile' => $profile->id(),
'plugin_instance_id' => $plugin_uuid,
]));
$this->drupalPostForm(NULL, [], t('Confirm'));
$this->assertRaw(t('The matcher %plugin has been deleted.', ['%plugin' => $plugin->getLabel()]));
$this->assertUrl(Url::fromRoute('linkit.matchers', [
'linkit_profile' => $profile->id(),
]));
$this->assertText(t('No matchers added.'));
/** @var \Drupal\linkit\Entity\Profile $updated_profile */
$updated_profile = Profile::load($profile->id());
$this->assertFalse($updated_profile->getMatchers()->has($plugin_uuid), 'The user matcher is deleted from the profile');
}
}
@@ -1,105 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Tests\Matchers\NodeMatcherTest.
*/
namespace Drupal\linkit\Tests\Matchers;
use Drupal\linkit\Tests\LinkitTestBase;
/**
* Tests node matcher.
*
* @group linkit
*/
class NodeMatcherTest extends LinkitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node'];
/**
* The matcher manager.
*
* @var \Drupal\linkit\MatcherManager
*/
protected $manager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->adminUser);
$this->manager = $this->container->get('plugin.manager.linkit.matcher');
$type1 = $this->drupalCreateContentType(['type' => 'test1', 'name' => 'Test1']);
$type2 = $this->drupalCreateContentType(['type' => 'test2', 'name' => 'Test2']);
// Nodes with type 1.
$this->drupalCreateNode(['title' => 'Lorem Ipsum 1', 'type' => $type1->id()]);
$this->drupalCreateNode(['title' => 'Lorem Ipsum 2', 'type' => $type1->id()]);
// Nodes with type 1.
$this->drupalCreateNode(['title' => 'Lorem Ipsum 3', 'type' => $type2->id()]);
// Unpublished node.
$this->drupalCreateNode(['title' => 'Lorem unpublishd', 'type' => $type1->id(), 'status' => FALSE]);
}
/**
* Tests node matcher.
*/
function testNodeMatcherWidthDefaultConfiguration() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:node', []);
$matches = $plugin->getMatches('Lorem');
$this->assertEqual(3, count($matches), 'Correct number of matches');
}
/**
* Tests node matcher with bundle filer.
*/
function testNodeMatcherWidthBundleFiler() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:node', [
'settings' => [
'bundles' => [
'test1' => 'test1'
],
],
]);
$matches = $plugin->getMatches('Lorem');
$this->assertEqual(2, count($matches), 'Correct number of matches');
}
/**
* Tests node matcher with include unpublished setting activated.
*/
function testNodeMatcherWidthIncludeUnpublished() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:node', [
'settings' => [
'include_unpublished' => TRUE,
],
]);
// Test without permissions to see unpublished nodes.
$matches = $plugin->getMatches('Lorem');
$this->assertEqual(3, count($matches), 'Correct number of matches');
$account = $this->drupalCreateUser(['bypass node access']);
$this->drupalLogin($account);
// Test with permissions to see unpublished nodes.
$matches = $plugin->getMatches('Lorem');
$this->assertEqual(4, count($matches), 'Correct number of matches');
}
}
@@ -1,134 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Tests\Matchers\TermMatcherTest.
*/
namespace Drupal\linkit\Tests\Matchers;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Language\LanguageInterface;
use Drupal\linkit\Tests\LinkitTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests term matcher.
*
* @group linkit
*/
class TermMatcherTest extends LinkitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['taxonomy'];
/**
* The matcher manager.
*
* @var \Drupal\linkit\MatcherManager
*/
protected $manager;
/**
* Creates and saves a vocabulary.
*
* @param string $name
* The vocabulary name.
*
* @return Vocabulary The new vocabulary object.
* The new vocabulary object.
*/
private function createVocabulary($name) {
$vocabularyStorage = \Drupal::entityTypeManager()->getStorage('taxonomy_vocabulary');
$vocabulary = $vocabularyStorage->create([
'name' => $name,
'description' => $name,
'vid' => Unicode::strtolower($name),
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
]);
$vocabulary->save();
return $vocabulary;
}
/**
* Creates and saves a new term with in vocabulary $vid.
*
* @param \Drupal\taxonomy\Entity\Vocabulary $vocabulary
* The vocabulary object.
* @param array $values
* (optional) An array of values to set, keyed by property name. If the
* entity type has bundles, the bundle key has to be specified.
*
* @return \Drupal\taxonomy\Entity\Term
* The new taxonomy term object.
*/
private function createTerm(Vocabulary $vocabulary, $values = array()) {
$filter_formats = filter_formats();
$format = array_pop($filter_formats);
$termStorage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$term = $termStorage->create($values + array(
'name' => $this->randomMachineName(),
'description' => array(
'value' => $this->randomMachineName(),
// Use the first available text format.
'format' => $format->id(),
),
'vid' => $vocabulary->id(),
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
));
$term->save();
return $term;
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->adminUser);
$this->manager = $this->container->get('plugin.manager.linkit.matcher');
$testing_vocabulary_1 = $this->createVocabulary('testing_vocabulary_1');
$testing_vocabulary_2 = $this->createVocabulary('testing_vocabulary_2');
$this->createTerm($testing_vocabulary_1, ['name' => 'foo_bar']);
$this->createTerm($testing_vocabulary_1, ['name' => 'foo_baz']);
$this->createTerm($testing_vocabulary_1, ['name' => 'foo_foo']);
$this->createTerm($testing_vocabulary_1, ['name' => 'bar']);
$this->createTerm($testing_vocabulary_2, ['name' => 'foo_bar']);
$this->createTerm($testing_vocabulary_2, ['name' => 'foo_baz']);
}
/**
* Tests term matcher with default configuration.
*/
function testTermMatcherWidthDefaultConfiguration() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:taxonomy_term', []);
$matches = $plugin->getMatches('foo');
$this->assertEqual(5, count($matches), 'Correct number of matches');
}
/**
* Tests term matcher with bundle filer.
*/
function testTermMatcherWidthBundleFiler() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:taxonomy_term', [
'settings' => [
'bundles' => [
'testing_vocabulary_1' => 'testing_vocabulary_1'
],
],
]);
$matches = $plugin->getMatches('foo');
$this->assertEqual(3, count($matches), 'Correct number of matches');
}
}
@@ -1,114 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Tests\Matchers\UserMatcherTest.
*/
namespace Drupal\linkit\Tests\Matchers;
use Drupal\linkit\Tests\LinkitTestBase;
/**
* Tests user matcher.
*
* @group linkit
*/
class UserMatcherTest extends LinkitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user'];
/**
* The matcher manager.
*
* @var \Drupal\linkit\MatcherManager
*/
protected $manager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser(['access user profiles']));
$this->manager = $this->container->get('plugin.manager.linkit.matcher');
$custom_role = $this->drupalCreateRole(array(), 'custom_role', 'custom_role');
$custom_role_admin = $this->drupalCreateRole(array(), 'custom_role_admin', 'custom_role_admin');
$this->drupalCreateUser([], 'lorem');
$this->drupalCreateUser([], 'foo');
$account = $this->drupalCreateUser([], 'ipsumlorem');
$account->addRole($custom_role);
$account->save();
$account = $this->drupalCreateUser([], 'lorem_custom_role');
$account->addRole($custom_role);
$account->save();
$account = $this->drupalCreateUser([], 'lorem_custom_role_admin');
$account->addRole($custom_role_admin);
$account->save();
$account = $this->drupalCreateUser([], 'blocked_lorem');
$account->block();
$account->save();
}
/**
* Tests user matcher.
*/
function testUserMatcherWidthDefaultConfiguration() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:user', []);
$matches = $plugin->getMatches('Lorem');
$this->assertEqual(4, count($matches), 'Correct number of matches');
}
/**
* Tests user matcher with role filer.
*/
function testUserMatcherWidthRoleFiler() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:user', [
'settings' => [
'roles' => [
'custom_role' => 'custom_role'
],
],
]);
$matches = $plugin->getMatches('Lorem');
$this->assertEqual(2, count($matches), 'Correct number of matches');
}
/**
* Tests user matcher with include blocked setting activated.
*/
function testUserMatcherWidthIncludeBlocked() {
/** @var \Drupal\linkit\MatcherInterface $plugin */
$plugin = $this->manager->createInstance('entity:user', [
'settings' => [
'include_blocked' => TRUE,
],
]);
// Test without permissions to see blocked users.
$matches = $plugin->getMatches('blocked');
$this->assertEqual(0, count($matches), 'Correct number of matches');
$account = $this->drupalCreateUser(['administer users']);
$this->drupalLogin($account);
// Test with permissions to see blocked users.
$matches = $plugin->getMatches('blocked');
$this->assertEqual(1, count($matches), 'Correct number of matches');
}
}
@@ -0,0 +1,44 @@
<?php
namespace Drupal\linkit\Tests;
use Drupal\Component\Utility\Unicode;
use Drupal\linkit\Entity\Profile;
/**
* Provides methods to create profiles based on default settings.
*
* This trait is meant to be used only by test classes.
*/
trait ProfileCreationTrait {
/**
* Creates a profile based on default settings.
*
* @param array $settings
* (optional) An associative array of settings for the profile, as used in
* entity_create(). Override the defaults by specifying the key and value
* in the array.
* The following defaults are provided:
* - id: Random string.
* - label: Random string.
*
* @return \Drupal\linkit\ProfileInterface
* The created profile entity.
*/
protected function createProfile(array $settings = []) {
// Populate defaults array.
$settings += [
'id' => Unicode::strtolower($this->randomMachineName()),
'label' => $this->randomMachineName(),
];
$profile = Profile::create($settings);
$profile->save();
$profile = Profile::load($profile->id());
return $profile;
}
}
@@ -1,125 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Tests\ProfileCreationTest.
*/
namespace Drupal\linkit\Tests;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Url;
/**
* Tests creating, loading and deleting profiles.
*
* @group linkit
*/
class ProfileCrudTest extends LinkitTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->adminUser);
}
/**
* Test the overview page.
*/
function testOverview() {
// Verify that the profile collection page is not accessible for regular
// users.
$this->drupalLogin($this->baseUser);
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
$this->assertResponse(403);
$this->drupalLogout();
// Verify that the profile collection page is accessible for regular users.
$this->drupalLogin($this->adminUser);
$profiles = [];
$profiles[] = $this->createProfile();
$profiles[] = $this->createProfile();
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
$this->assertResponse(200);
// Assert that the 'Add profile' action exists.
$this->assertLinkByHref(Url::fromRoute('entity.linkit_profile.add_form')->toString());
/** @var \Drupal\linkit\ProfileInterface $profile */
foreach ($profiles as $profile) {
$this->assertLinkByHref('admin/config/content/linkit/manage/' . $profile->id());
$this->assertLinkByHref('admin/config/content/linkit/manage/' . $profile->id() . '/delete');
}
}
/**
* Creates profile.
*/
function testProfileCreation() {
$this->drupalGet(Url::fromRoute('entity.linkit_profile.add_form'));
$this->drupalGet('admin/config/content/linkit/add');
$this->assertResponse(200);
// Create a profile.
$edit = [];
$edit['label'] = Unicode::strtolower($this->randomMachineName());
$edit['id'] = Unicode::strtolower($this->randomMachineName());
$edit['description'] = $this->randomMachineName(16);
$this->drupalPostForm(NULL, $edit, t('Save and manage matchers'));
$this->assertRaw(t('Created new profile %label.', ['%label' => $edit['label']]));
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
$this->assertText($edit['label'], 'Profile exists in the profile collection.');
}
/**
* Updates a profile.
*/
function testProfileUpdate() {
$profile = $this->createProfile();
$this->drupalGet(Url::fromRoute('entity.linkit_profile.edit_form', [
'linkit_profile' => $profile->id(),
]));
$this->assertResponse(200);
$id_field = $this->xpath('.//input[not(@disabled) and @name="id"]');
$this->assertTrue(empty($id_field), 'Machine name field is disabled.');
$this->assertLinkByHref(Url::fromRoute('entity.linkit_profile.edit_form', [
'linkit_profile' => $profile->id(),
])->toString());
$this->assertLinkByHref('admin/config/content/linkit/manage/' . $profile->id() . '/delete');
$edit = [];
$edit['label'] = $this->randomMachineName();
$edit['description'] = $this->randomMachineName(16);
$this->drupalPostForm(NULL, $edit, t('Update profile'));
$this->assertRaw(t('Updated profile %label.', ['%label' => $edit['label']]));
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
$this->assertText($edit['label'], 'Updated profile exists in the profile collection.');
}
/**
* Delete a profile.
*/
function testProfileDelete() {
/** @var \Drupal\linkit\ProfileInterface $profile */
$profile = $this->createProfile();
$this->drupalGet(Url::fromRoute('entity.linkit_profile.delete_form', [
'linkit_profile' => $profile->id(),
]));
$this->drupalPostForm(NULL, [], t('Delete'));
$this->assertRaw(t('The linkit profile %label has been deleted.', ['%label' => $profile->label()]));
$this->drupalGet(Url::fromRoute('entity.linkit_profile.collection'));
$this->assertNoText($profile->label(), 'Deleted profile does not exists in the profile collection.');
}
}
@@ -0,0 +1,152 @@
<?php
namespace Drupal\linkit\Tests\Update;
use Drupal\filter\Entity\FilterFormat;
use Drupal\system\Tests\Update\UpdatePathTestBase;
/**
* Tests Linkit upgrade paths.
*
* @group Update
*/
class LinkitUpdateTest extends UpdatePathTestBase {
/**
* The config factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->configFactory = $this->container->get('config.factory');
}
/**
* Set database dump files to be used.
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../tests/fixtures/update/drupal-8.linkit-enabled.standard.php.gz',
__DIR__ . '/../../../tests/fixtures/update/linkit-additions.php',
];
}
/**
* Tests linkit_update_8500().
*
* @see linkit_update_8500()
*/
public function testLinkitUpdate8500() {
$editor = $this->configFactory->get('editor.editor.format_1');
$this->assertTrue($editor->get('settings.plugins.linkit'), 'We got old linkit settings in the editor configuration.');
$format_1_linkit_profile = $editor->get('settings.plugins.linkit.linkit_profile');
$editor = $this->configFactory->get('editor.editor.format_2');
$this->assertTrue($editor->get('settings.plugins.linkit'), 'We got old linkit settings in the editor configuration.');
$format_2_linkit_profile = $editor->get('settings.plugins.linkit.linkit_profile');
$editor = $this->configFactory->get('editor.editor.format_3');
$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');
$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.');
$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->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.');
$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->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.');
$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->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.');
$format = $this->configFactory->get('filter.format.format_1');
$this->assertNotNull($format->get('filters.linkit'), 'Linkit filter is enabled.');
$this->assertTrue($format->get('filters.linkit.weight') < $format->get('filters.filter_html.weight'), 'Linkit filter is running before filter_html.');
$format = $this->configFactory->get('filter.format.format_2');
$this->assertNotNull($format->get('filters.linkit'), 'Linkit filter is enabled.');
$format = $this->configFactory->get('filter.format.format_3');
$this->assertNotNull($format->get('filters.linkit'), 'Linkit filter is enabled.');
$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']));
$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');
}
}
@@ -1,10 +1,5 @@
<?php
/**
* @file
* Contains \Drupal\linkit\Utility\LinkitXss.
*/
namespace Drupal\linkit\Utility;
use Drupal\Component\Utility\Xss;
@@ -17,7 +12,7 @@ class LinkitXss extends Xss {
/**
* Description filter helper.
*
* @param $string
* @param string $string
* The string with raw HTML in it. It will be stripped of everything that
* can cause an XSS attack.
*