diff --git a/js/linkit.autocomplete.js b/js/linkit.autocomplete.js index 558278d..92b4457 100644 --- a/js/linkit.autocomplete.js +++ b/js/linkit.autocomplete.js @@ -78,6 +78,7 @@ $('input[name="attributes[data-entity-type]"]', $form).val(ui.item.entity_type_id); $('input[name="attributes[data-entity-uuid]"]', $form).val(ui.item.entity_uuid); $('input[name="attributes[data-entity-substitution]"]', $form).val(ui.item.substitution_id); + $('input[name="attributes[data-entity-substitution-extra]"]', $form).val(ui.item.substitution_extra); } event.target.value = ui.item.path; diff --git a/js/linkit.filter_html.admin.js b/js/linkit.filter_html.admin.js index 3807f6e..9e80565 100644 --- a/js/linkit.filter_html.admin.js +++ b/js/linkit.filter_html.admin.js @@ -42,7 +42,7 @@ rule.allowed.tags = ['a']; // Attributes. rule.required.attributes = ['data-entity-substitution', 'data-entity-type', 'data-entity-uuid', 'title']; - rule.allowed.attributes = ['data-entity-substitution', 'data-entity-type', 'data-entity-uuid', 'title']; + rule.allowed.attributes = ['data-entity-substitution-extra', 'data-entity-substitution', 'data-entity-type', 'data-entity-uuid', 'title']; linkitFeature.addHTMLRule(rule); return linkitFeature; diff --git a/linkit.module b/linkit.module index e9159be..b6a5307 100644 --- a/linkit.module +++ b/linkit.module @@ -101,6 +101,7 @@ function linkit_form_editor_link_dialog_alter(&$form, FormStateInterface $form_s 'data-entity-type', 'data-entity-uuid', 'data-entity-substitution', + 'data-entity-substitution-extra', ]; $form['attributes']["#weight"] = -100; @@ -130,6 +131,7 @@ function linkit_form_editor_link_dialog_submit(array &$form, FormStateInterface $form_state->unsetValue(['attributes', 'data-entity-type']); $form_state->unsetValue(['attributes', 'data-entity-uuid']); $form_state->unsetValue(['attributes', 'data-entity-substitution']); + $form_state->unsetValue(['attributes', 'data-entity-substitution-extra']); } $fields = [ @@ -137,6 +139,7 @@ function linkit_form_editor_link_dialog_submit(array &$form, FormStateInterface 'data-entity-type', 'data-entity-uuid', 'data-entity-substitution', + 'data-entity-substitution-extra', ]; foreach ($fields as $field_name) { diff --git a/src/Plugin/Filter/LinkitFilter.php b/src/Plugin/Filter/LinkitFilter.php index f272303..81be75f 100644 --- a/src/Plugin/Filter/LinkitFilter.php +++ b/src/Plugin/Filter/LinkitFilter.php @@ -112,7 +112,7 @@ class LinkitFilter extends FilterBase implements ContainerFactoryPluginInterface /** @var \Drupal\Core\GeneratedUrl $url */ $url = $this->substitutionManager ->createInstance($substitution_type) - ->getUrl($entity); + ->getUrl($entity, $element->getAttribute('data-entity-substitution-extra')); // Parse link href as url, extract query and fragment from it. $href_url = parse_url($element->getAttribute('href')); diff --git a/src/Plugin/Linkit/Matcher/ViewDisplayMatcher.php b/src/Plugin/Linkit/Matcher/ViewDisplayMatcher.php new file mode 100644 index 0000000..ef3cb60 --- /dev/null +++ b/src/Plugin/Linkit/Matcher/ViewDisplayMatcher.php @@ -0,0 +1,149 @@ +t('Include disabled: @include_disabled', [ + '@include_disabled' => $this->configuration['include_disabled'] ? $this->t('Yes') : $this->t('No'), + ]); + + return $summery; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'include_disabled' => FALSE, + ] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return parent::calculateDependencies() + [ + 'module' => ['views'], + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); + + $form['disabled_views'] = [ + '#type' => 'details', + '#title' => $this->t('Disabled views'), + '#open' => TRUE, + ]; + + $form['disabled_views']['include_disabled'] = [ + '#title' => $this->t('Include disabled views'), + '#type' => 'checkbox', + '#default_value' => $this->configuration['include_disabled'], + '#description' => $this->t('In order to see disabled views, users must also have permissions to do so.'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function execute($string) { + $suggestions = new SuggestionCollection(); + $string = trim($string, "\/ \t\n\r\0\x0B"); + + // Find appropriate activated view. + $query = $this->entityTypeManager->getStorage('view')->getQuery() + ->sort('label', 'ASC'); + + // Can be found via the label. + $conditionOnLabel = $query->andConditionGroup() + ->condition('label', $string, 'CONTAINS') + ->condition('display.*.display_options.path', '', '<>'); + // Or can be found via the path. + $conditionOnUrl = $query->andConditionGroup() + ->condition('display.*.display_options.path', $string, 'CONTAINS'); + $condition = $query->orConditionGroup() + ->condition($conditionOnLabel) + ->condition($conditionOnUrl); + $query->condition($condition); + + // Limit according to settings. + if (!$this->configuration['include_disabled']) { + $query->condition('status', TRUE); + } + if ($this->configuration['limit']) { + $query->range(0, $this->configuration['limit']); + } + + $this->addQueryTags($query); + $entity_ids = $query->execute(); + + // Build the suggestion associated with each views. + foreach (\Drupal::entityTypeManager()->getStorage('view')->loadMultiple($entity_ids) as $view_id => $view) { + // Check each display to see if it has a path and is enabled. + foreach ($view->get('display') as $display_id => $display) { + // If the key doesn't exist, enabled is assumed. + $enabled = !empty($display['display_options']['enabled']) || !array_key_exists('enabled', $display['display_options']); + if ($enabled && !empty($display['display_options']['path']) && strpos($display['display_options']['path'], '%') === FALSE) { + // Build the entity suggestion. + $suggestion = new AdvancedEntitySuggestion(); + $suggestion->setLabel($view->label() . ' (' . $display['display_title'] . ')') + ->setGroup($this->t('Views')) + ->setDescription($this->buildDescription($view)) + ->setEntityUuid($view->uuid()) + ->setEntityTypeId($view->getEntityTypeId()) + ->setSubstitutionId('view_display') + ->setSubstitutionExtra($display_id) + ->setPath(Url::fromRoute("view.$view_id.$display_id")->toString()); + $suggestions->addSuggestion($suggestion); + } + } + } + + return $suggestions; + } + + /** + * Builds the metadata string used in the suggestion. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The matched entity. + * + * @return string + * The metadata for this entity. + */ + protected function buildDescription(EntityInterface $entity) { + $description = \Drupal::token()->replace($this->configuration['metadata'], [$this->targetType => $entity->getExecutable()], ['clear' => TRUE]); + return LinkitXss::descriptionFilter($description); + } +} diff --git a/src/Plugin/Linkit/Substitution/Canonical.php b/src/Plugin/Linkit/Substitution/Canonical.php index deff892..e0fb196 100644 --- a/src/Plugin/Linkit/Substitution/Canonical.php +++ b/src/Plugin/Linkit/Substitution/Canonical.php @@ -20,7 +20,7 @@ class Canonical extends PluginBase implements SubstitutionInterface { /** * {@inheritdoc} */ - public function getUrl(EntityInterface $entity) { + public function getUrl(EntityInterface $entity, $extra = NULL) { return $entity->toUrl('canonical')->toString(TRUE); } diff --git a/src/Plugin/Linkit/Substitution/File.php b/src/Plugin/Linkit/Substitution/File.php index c441dfc..c5e34e2 100644 --- a/src/Plugin/Linkit/Substitution/File.php +++ b/src/Plugin/Linkit/Substitution/File.php @@ -21,7 +21,7 @@ class File extends PluginBase implements SubstitutionInterface { /** * {@inheritdoc} */ - public function getUrl(EntityInterface $entity) { + public function getUrl(EntityInterface $entity, $extra = NULL) { $url = new GeneratedUrl(); /** @var \Drupal\file\FileInterface $entity */ $url->setGeneratedUrl(file_create_url($entity->getFileUri())); diff --git a/src/Plugin/Linkit/Substitution/Media.php b/src/Plugin/Linkit/Substitution/Media.php index 9cdcb1c..66b3006 100644 --- a/src/Plugin/Linkit/Substitution/Media.php +++ b/src/Plugin/Linkit/Substitution/Media.php @@ -37,7 +37,7 @@ class Media extends PluginBase implements SubstitutionInterface, ContainerFactor /** * {@inheritdoc} */ - public function getUrl(EntityInterface $entity) { + public function getUrl(EntityInterface $entity, $extra = NULL) { $url = new GeneratedUrl(); if (!($entity instanceof MediaInterface)) { diff --git a/src/Plugin/Linkit/Substitution/ViewDisplay.php b/src/Plugin/Linkit/Substitution/ViewDisplay.php new file mode 100644 index 0000000..8897439 --- /dev/null +++ b/src/Plugin/Linkit/Substitution/ViewDisplay.php @@ -0,0 +1,40 @@ +setGeneratedUrl(Url::fromRoute("view.{$entity->id()}.$extra")->toString()); + $url->addCacheableDependency($entity); + return $url; + } + + /** + * {@inheritdoc} + */ + public static function isApplicable(EntityTypeInterface $entity_type) { + return $entity_type->id() === 'view'; + } + +} diff --git a/src/SubstitutionInterface.php b/src/SubstitutionInterface.php index c39ec5a..33c54ba 100644 --- a/src/SubstitutionInterface.php +++ b/src/SubstitutionInterface.php @@ -17,10 +17,15 @@ interface SubstitutionInterface extends PluginInspectionInterface { * @param \Drupal\Core\Entity\EntityInterface $entity * The entity to get a URL for. * + * @param string $extra + * Any string encoded extra parameter that could be relevant for suggestion retrieval. + * + * @see Drupal\linkit\Plugin\Linkit\Matcher\ViewMatcher + * * @return \Drupal\Core\GeneratedUrl * A url to replace. */ - public function getUrl(EntityInterface $entity); + public function getUrl(EntityInterface $entity, $extra = NULL); /** * Checks if this substitution plugin is applicable for the given entity type. diff --git a/src/Suggestion/AdvancedEntitySuggestion.php b/src/Suggestion/AdvancedEntitySuggestion.php new file mode 100644 index 0000000..c876b6f --- /dev/null +++ b/src/Suggestion/AdvancedEntitySuggestion.php @@ -0,0 +1,39 @@ +substitutionExtra = $substitution_extra; + return $this; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() { + return parent::jsonSerialize() + [ + 'substitution_extra' => $this->substitutionExtra, + ]; + } + +}