Commit 5a95baad authored by catch's avatar catch
Browse files

Issue #3090659 by jhodgdon, andypost, larowlan, pratik_kamble, ravi.shankar,...

Issue #3090659 by jhodgdon, andypost, larowlan, pratik_kamble, ravi.shankar, Fabianx, catch, Berdir, Charlie ChX Negyesi, daffie, Chi, dww: Make a way for help topics to generate links only if they work and are accessible
parent 209dcea4
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -23,3 +23,8 @@ services:
      - [setSearchManager, ['@?plugin.manager.search']]
    tags:
      - { name: plugin_manager_cache_clear }
  help_twig.extension:
    class: Drupal\help_topics\HelpTwigExtension
    arguments: ['@access_manager', '@plugin.manager.help_topic', '@string_translation']
    tags:
      - { name: twig.extension }
+7 −4
Original line number Diff line number Diff line
@@ -4,16 +4,19 @@ related:
  - block.overview
  - block.configure
---
{% set layout_url = render_var(url('block.admin_display')) %}
{% set configure = render_var(url('help.help_topic', {'id': 'block.configure'})) %}
{% set layout_link_text %}
{% trans %}Block layout{% endtrans %}
{% endset %}
{% set layout_link = render_var(help_route_link(layout_link_text, 'block.admin_display')) %}
{% set configure_topic = render_var(help_topic_link('block.configure')) %}
<h2>{% trans %}Goal{% endtrans %}</h2>
<p>{% trans %}Place a block into a theme's region. {% endtrans %}</p>
<h2>{% trans %}Steps{% endtrans %}</h2>
<ol>
  <li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Structure</em> &gt; <a href="{{ layout_url }}"><em>Block layout</em></a>.{% endtrans %}</li>
  <li>{% trans %}In the <em>Manage</em> administrative menu, navigate to <em>Structure</em> &gt; {{ layout_link }}.{% endtrans %}</li>
  <li>{% trans %}Click the name of the theme that you want to place the block in.{% endtrans %}</li>
  <li>{% trans %}Optionally, click <em>Demonstrate block regions</em> to see the regions of the theme.{% endtrans %}</li>
  <li>{% trans %}Find the region where you want the block, and click <em>Place block</em> in that region. A modal dialog will pop up.{% endtrans %}</li>
  <li>{% trans %}Find the block you want to place and click <em>Place block</em>. A <em>Configure block</em> modal dialog will pop up.{% endtrans %}</li>
  <li>{% trans %}Configure the block and click <em>Save block</em>; see <a href="{{ configure }}">Configuring a previously-placed block</a> for configuration details.{% endtrans %}</li>
  <li>{% trans %}Configure the block and click <em>Save block</em>; see {{ configure_topic }} for configuration details.{% endtrans %}</li>
</ol>
+164 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\help_topics;

use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Access\AccessManagerInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Url;
use Symfony\Component\Routing\Exception\InvalidParameterException;
use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

/**
 * Defines and registers Drupal Twig extensions for rendering help topics.
 */
class HelpTwigExtension extends AbstractExtension {

  use StringTranslationTrait;

  /**
   * The access manager.
   *
   * @var \Drupal\Core\Access\AccessManagerInterface
   */
  protected $accessManager;

  /**
   * The help topic plugin manager.
   *
   * @var \Drupal\help_topics\HelpTopicPluginManagerInterface
   */
  protected $pluginManager;

  /**
   * Constructs a \Drupal\help_topics\HelpTwigExtension.
   *
   * @param \Drupal\Core\Access\AccessManagerInterface $access_manager
   *   The access manager.
   * @param \Drupal\help_topics\HelpTopicPluginManagerInterface $plugin_manager
   *   The help topic plugin manager service.
   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
   *   The string translation service.
   */
  public function __construct(AccessManagerInterface $access_manager, HelpTopicPluginManagerInterface $plugin_manager, TranslationInterface $string_translation) {
    $this->accessManager = $access_manager;
    $this->pluginManager = $plugin_manager;
    $this->stringTranslation = $string_translation;
  }

  /**
   * {@inheritdoc}
   */
  public function getFunctions() {
    return [
      new TwigFunction('help_route_link', [$this, 'getRouteLink']),
      new TwigFunction('help_topic_link', [$this, 'getTopicLink']),
    ];
  }

  /**
   * Returns a link or plain text, given text, route name, and parameters.
   *
   * @param string $text
   *   The link text.
   * @param string $route
   *   The name of the route.
   * @param array $parameters
   *   (optional) An associative array of route parameter names and values.
   * @param array $options
   *   (optional) An associative array of additional options. The 'absolute'
   *   option is forced to be TRUE.
   *
   * @return array
   *   A render array with a generated absolute link to the given route. If
   *   the user does not have permission for the route, or an exception occurs,
   *   such as a missing route or missing parameters, the render array is for
   *   the link text as a plain string instead.
   *
   * @see \Drupal\Core\Template\TwigExtension::getUrl()
   */
  public function getRouteLink(string $text, string $route, array $parameters = [], array $options = []): array {
    assert($this->accessManager instanceof AccessManagerInterface, "The access manager hasn't been set up. Any configuration YAML file with a service directive dealing with the Twig configuration can cause this, most likely found in a recently installed or changed module.");

    $bubbles = new BubbleableMetadata();
    $bubbles->addCacheTags(['route_match']);

    try {
      $access_object = $this->accessManager->checkNamedRoute($route, $parameters, NULL, TRUE);
      $bubbles->addCacheableDependency($access_object);

      if ($access_object->isAllowed()) {
        $options['absolute'] = TRUE;
        $url = Url::fromRoute($route, $parameters, $options);
        // Generate the URL to check for parameter problems and collect
        // cache metadata.
        $generated = $url->toString(TRUE);
        $bubbles->addCacheableDependency($generated);
        $build = [
          '#title' => $text,
          '#type' => 'link',
          '#url' => $url,
        ];
      }
      else {
        // If the user doesn't have access, return the link text.
        $build = ['#markup' => $text];
      }
    }
    catch (RouteNotFoundException | MissingMandatoryParametersException | InvalidParameterException $e) {
      // If the route had one of these exceptions, return the link text.
      $build = ['#markup' => $text];
    }
    $bubbles->applyTo($build);
    return $build;
  }

  /**
   * Returns a link to a help topic, or the title of the topic.
   *
   * @param string $topic_id
   *   The help topic ID.
   *
   * @return array
   *   A render array with a generated absolute link to the given topic. If
   *   the user does not have permission to view the topic, or an exception
   *   occurs, such as the topic not being defined due to a module not being
   *   installed, a default string is returned.
   *
   * @see \Drupal\Core\Template\TwigExtension::getUrl()
   */
  public function getTopicLink(string $topic_id): array {
    assert($this->pluginManager instanceof HelpTopicPluginManagerInterface, "The plugin manager hasn't been set up. Any configuration YAML file with a service directive dealing with the Twig configuration can cause this, most likely found in a recently installed or changed module.");

    $bubbles = new BubbleableMetadata();
    $bubbles->addCacheableDependency($this->pluginManager);
    try {
      $plugin = $this->pluginManager->createInstance($topic_id);
    }
    catch (PluginNotFoundException $e) {
      // Not a topic.
      $plugin = FALSE;
    }

    if ($plugin) {
      $parameters = ['id' => $topic_id];
      $route = 'help.help_topic';
      $build = $this->getRouteLink($plugin->getLabel(), $route, $parameters);
      $bubbles->addCacheableDependency($plugin);
    }
    else {
      $build = [
        '#markup' => $this->t('Missing help topic'),
      ];
    }

    $bubbles->applyTo($build);
    return $build;
  }

}
+2 −2
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ related:
  - help_topics_test.linked
  - does_not_exist.and_no_error
---
{% set help_topic_url = render_var(url('help.help_topic', {id: 'help_topics_test.additional'})) %}
<p>{% trans %}This is a test. It should <a href="{{ help_topic_url }}">link to the additional topic</a>. Also there should be a related topic link below to the Help module topic page and the linked topic.{% endtrans %}</p>
{% set help_topic_link = render_var(help_topic_link('help_topics_test.test_urls')) %}
<p>{% trans %}This is a test. It should link to the URL test topic {{ help_topic_link }}. Also there should be a related topic link below to the Help module topic page and the linked topic.{% endtrans %}</p>
<p>{% trans %}Nonworditem totranslate.{% endtrans %}</p>
<p>{% trans %}Test translation.{% endtrans %}</p>
+19 −0
Original line number Diff line number Diff line
---
label: 'URL test topic'
top_level: true
---
{% set non_route_link = render_var(help_route_link('not a route', 'not_a_real_route')) %}
{% set missing_params_link = render_var(help_route_link('missing params', 'help_topics_test.test_route')) %}
{% set invalid_params_link = render_var(help_route_link('invalid params', 'help_topics_test.test_route', {'int_param': 'not_an_int'})) %}
{% set valid_link = render_var(help_route_link('valid link', 'help_topics_test.test_route', {'int_param': 2})) %}
{% set topic_link = render_var(help_topic_link('help_topics_test.additional')) %}
{% set not_a_topic = render_var(help_topic_link('not_a_topic')) %}
<p>{% trans %}This topic should be top-level. It is used to test URLs{% endtrans %}</p>
<ul>
  <li>{% trans %}Should not be a link: {{ non_route_link }}{% endtrans %}</li>
  <li>{% trans %}Should not be a link: {{ missing_params_link }}{% endtrans %}</li>
  <li>{% trans %}Should not be a link: {{ invalid_params_link }}{% endtrans %}</li>
  <li>{% trans %}Should be a link if user has access: {{ valid_link }}{% endtrans %}</li>
  <li>{% trans %}Should be a link: {{ topic_link }}{% endtrans %}</li>
  <li>{% trans %}Should not be a link: {{ not_a_topic }}{% endtrans %}</li>
</ul>
Loading