Issue #2920309 by jhodgdon, Amber Himes Matz, alexpott, larowlan, andypost, vadim.hirbu, effulgentsia, diqidoq, jibran, webchick, catch, xjm, jhedstrom, Gábor Hojtsy, dawehner, Berdir, tim.plunkett, benjifisher, markcarver, yoroy, ckrina, amateescu, gnuget, webflo, Greg Boggs, yo30, vijaycs85, SenthilMohith, andrewmacpherson, EclipseGc, sandboxpl, MariaDenysyuk, tstoeckler, miro_dietiker, Mixologic, RoloDMonkey, timmillwood, Fabianx: Add experimental module for Help Topics
parent
8f430db9e6
commit
992f677a04
|
@ -231,6 +231,10 @@ Hypertext Application Language (HAL)
|
|||
Help
|
||||
- ?
|
||||
|
||||
Help Topics
|
||||
- Amber Matz 'Amber Himes Matz' https://www.drupal.org/u/amber-himes-matz
|
||||
- Andrey Postnikov 'andypost' https://www.drupal.org/u/andypost
|
||||
|
||||
Image
|
||||
- Claudiu Cristea 'claudiu.cristea' https://www.drupal.org/u/claudiu.cristea
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@
|
|||
"drupal/forum": "self.version",
|
||||
"drupal/hal": "self.version",
|
||||
"drupal/help": "self.version",
|
||||
"drupal/help_topics": "self.version",
|
||||
"drupal/history": "self.version",
|
||||
"drupal/image": "self.version",
|
||||
"drupal/inline_form_errors": "self.version",
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Help Topics module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Perform alterations on help topic definitions.
|
||||
*
|
||||
* @param array $info
|
||||
* Array of help topic plugin definitions keyed by their plugin ID.
|
||||
*/
|
||||
function hook_help_topics_info_alter(array &$info) {
|
||||
// Alter the help topic to be displayed on admin/help.
|
||||
$info['example.help_topic']['top_level'] = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
|
@ -0,0 +1,8 @@
|
|||
name: Help Topics
|
||||
type: module
|
||||
description: 'Displays help topics provided by themes and modules.'
|
||||
core: 8.x
|
||||
package: Core (Experimental)
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:help
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Displays help topics provided by modules and themes.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function help_topics_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.help_topics':
|
||||
$help_home = Url::fromRoute('help.main')->toString();
|
||||
$locale_help = (\Drupal::moduleHandler()->moduleExists('locale')) ? Url::fromRoute('help.page', ['name' => 'locale'])->toString() : '#';
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Help Topics module adds module- and theme-provided help topics to the module overviews from the core Help module. For more information, see the <a href=":online">online documentation for the Help Topics module</a>.', [':online' => 'https://www.drupal.org/modules/help_topics']) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Viewing help topics') . '</dt>';
|
||||
$output .= '<dd>' . t('The top-level help topics are listed on the main <a href=":help_page">Help page</a>. Links to other topics, including non-top-level help topics, can be found under the "Related" heading when viewing a topic page.', [':help_page' => $help_home]) . '</dd>';
|
||||
$output .= '<dt>' . t('Providing help topics') . '</dt>';
|
||||
$output .= '<dd>' . t("Modules and themes can provide help topics as Twig-file-based plugins in a project sub-directory called <em>help_topics</em>; plugin meta-data is provided in meta tags within each Twig file. Plugin-based help topics provided by modules and themes will automatically be updated when a module or theme is updated. Use the plugins in <em>core/modules/help_topics/help_topics</em> as a guide when writing and formatting a help topic plugin for your theme or module.") . '</dd>';
|
||||
$output .= '<dt>' . t('Translating help topics') . '</dt>';
|
||||
$output .= '<dd>' . t('The title and body text of help topics provided by contributed modules and themes are translatable using the <a href=":locale_help">Interface Translation module</a>. Topics provided by custom modules and themes are also translatable if they have been viewed at least once in a non-English language, which triggers putting their translatable text into the translation database.', [':locale_help' => $locale_help]) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return ['#markup' => $output];
|
||||
|
||||
case 'help_topics.help_topic':
|
||||
$help_home = Url::fromRoute('help.main')->toString();
|
||||
return '<p>' . t('See the <a href=":help_page">Help page</a> for more topics.', [
|
||||
':help_page' => $help_home,
|
||||
]) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function help_topics_theme() {
|
||||
return [
|
||||
'help_topic' => [
|
||||
'variables' => [
|
||||
'body' => [],
|
||||
'related' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
help_topics.help_topic:
|
||||
path: '/admin/help/topic/{id}'
|
||||
defaults:
|
||||
_controller: '\Drupal\help_topics\Controller\HelpTopicPluginController::viewHelpTopic'
|
||||
requirements:
|
||||
_permission: 'access administration pages'
|
|
@ -0,0 +1,17 @@
|
|||
services:
|
||||
help_topics.breadcrumb:
|
||||
class: Drupal\help_topics\HelpBreadcrumbBuilder
|
||||
arguments: ['@string_translation']
|
||||
tags:
|
||||
- { name: breadcrumb_builder, priority: 900 }
|
||||
public: false
|
||||
plugin.manager.help_topic:
|
||||
class: Drupal\help_topics\HelpTopicPluginManager
|
||||
arguments: ['@module_handler', '@theme_handler', '@cache.discovery']
|
||||
help_topics.twig.loader:
|
||||
class: Drupal\help_topics\HelpTopicTwigLoader
|
||||
arguments: ['@app.root', '@module_handler', '@theme_handler']
|
||||
# Lowest core priority because loading help topics is not the usual case.
|
||||
tags:
|
||||
- { name: twig.loader, priority: -200 }
|
||||
public: false
|
|
@ -0,0 +1,15 @@
|
|||
<meta name="help_topic:label" content="Changing basic site settings"/>
|
||||
<meta name="help_topic:top_level"/>
|
||||
<meta name="help_topic:related" content="user.security_account_settings"/>
|
||||
{% set regional_url = render_var(url('system.regional_settings')) %}
|
||||
{% set information_url = render_var(url('system.site_information_settings')) %}
|
||||
{% set datetime_url = render_var(url('entity.date_format.collection')) %}
|
||||
<p>{% trans %}The settings for your site are configured on various administrative pages, as follows:{% endtrans %}</p>
|
||||
<dl>
|
||||
<dt>{% trans %}Site name, slogan, and email address{% endtrans %}</dt>
|
||||
<dd>{% trans %}On the <a href="{{ information_url }}"><em>Basic site settings</em></a> page, which you can reach in the main <em>Manage</em> administrative menu, by navigating to <em>Configuration</em> > <em>System</em> > <em>Basic site settings</em>.{% endtrans %}</dd>
|
||||
<dt>{% trans %}Time zone and country{% endtrans %}</dt>
|
||||
<dd>{% trans %}On the <a href="{{ regional_url }}"><em>Regional settings</em></a> page, which you can reach in the main <em>Manage</em> administrative menu, by navigating to <em>Configuration</em> > <em>Regional and language</em> > <em>Regional settings</em>.{% endtrans %}</dd>
|
||||
<dt>{% trans %}Date and time formats{% endtrans %}</dt>
|
||||
<dd>{% trans %}On the <a href="{{ datetime_url }}"><em>Date and time formats</em></a> page, which you can reach in the main <em>Manage</em> administrative menu, by navigating to <em>Configuration</em> > <em>Regional and language</em> > <em>Date and time formats</em>.{% endtrans %}</dd>
|
||||
</dl>
|
|
@ -0,0 +1,10 @@
|
|||
<meta name="help_topic:label" content="Configuring error responses, including 403/404 pages"/>
|
||||
<meta name="help_topic:related" content="core.config_basic,core.maintenance"/>
|
||||
{% set log_settings_url = render_var(url('system.logging_settings')) %}
|
||||
{% set site_settings_url = render_var(url('system.site_information_settings')) %}
|
||||
<h2>{% trans %}Configuring 403/404 pages{% endtrans %}</h2>
|
||||
<p>{% trans %}The core software provides default responses for 403 response (Not Authorized: when someone tries to visit a page they do not have permission to see) and 404 response (Not Found: when someone tries to visit a page that does not exist). You can change what page is displayed for these responses on the <a href="{{ site_settings_url }}"><em>Basic site settings</em></a> page, which you can reach in the main <em>Manage</em> administrative menu, by navigating to <em>Configuration</em> > <em>System</em> > <em>Basic site settings</em>. Note that the pages you want to use must already exist as either system-provided pages or content that you have created.{% endtrans %}</p>
|
||||
<h2>{% trans %}Responding to software errors{% endtrans %}</h2>
|
||||
<p>{% trans %}Software errors on your site are logged, if you have a logging module installed (such as the core Database Logging module or the core Syslog module). You can configure whether or not error messages are also shown (to both administrators and other site visitors) on the <a href="{{ log_settings_url }}"><em>Logging and errors</em></a> configuration page, which you can reach in the main <em>Manage</em> administrative menu, by navigating to <em>Configuration</em> > <em>Development</em> > <em>Logging and errors</em>.{% endtrans %}</p>
|
||||
<h2>{% trans %}Viewing the site log{% endtrans %}</h2>
|
||||
<p>{% trans %}If you have the core Database Logging module installed, you can view recent error and informational messages by navigating in the main <em>Manage</em> administrative menu to <em>Reports</em> > <em>Recent log messages</em>. If you are using the core Syslog module for logging, error messages will be logged in your web server''s log files.{% endtrans %}</p>
|
|
@ -0,0 +1,3 @@
|
|||
<meta name="help_topic:label" content="Maintaining and troubleshooting your site"/>
|
||||
<meta name="help_topic:top_level"/>
|
||||
<p>{% trans %}The related topics listed here will help you keep your site running and troubleshoot problems.{% endtrans %}</p>
|
|
@ -0,0 +1,3 @@
|
|||
<meta name="help_topic:label" content="Defining navigation and URLs"/>
|
||||
<meta name="help_topic:top_level"/>
|
||||
<p>{% trans %}The related topics listed here describe how to set up various aspects of site navigation and URLs.{% endtrans %}</p>
|
|
@ -0,0 +1,4 @@
|
|||
<meta name="help_topic:label" content="Making your site secure"/>
|
||||
<meta name="help_topic:top_level"/>
|
||||
<meta name="help_topic:related" content="menu_ui.menu_overview"/>
|
||||
<p>{% trans %}The topics listed here will help you make and keep your site secure.{% endtrans %}</p>
|
|
@ -0,0 +1,7 @@
|
|||
<meta name="help_topic:label" content="Accessibility features"/>
|
||||
<meta name="help_topic:related" content="core.ui_components"/>
|
||||
<p>{% trans %}The following features of the administrative user interface may help administrative users with disabilities access your site:{% endtrans %}</p>
|
||||
<dl>
|
||||
<dt>{% trans %}Disabling drag-and-drop functionality{% endtrans %}</dt>
|
||||
<dd>{% trans %}The default drag-and-drop user interface for ordering tables in the administrative interface presents a challenge for some users, including users of screen readers and other assistive technology. The drag-and-drop interface can be disabled in a table by clicking a link labeled <em>Show row weights</em> above the table. The replacement interface allows users to order the table by choosing numerical weights instead of dragging table rows.{% endtrans %}</dd>
|
||||
</dl>
|
|
@ -0,0 +1,3 @@
|
|||
<meta name="help_topic:label" content="Using the administrative interface"/>
|
||||
<meta name="help_topic:top_level"/>
|
||||
<p>{% trans %}The related topics listed here describe various aspects of the administrative interface, and tell how to use them.{% endtrans %}</p>
|
|
@ -0,0 +1,11 @@
|
|||
<meta name="help_topic:label" content="Contextual links"/>
|
||||
<meta name="help_topic:related" content="core.ui_components"/>
|
||||
<h2>{% trans %}What are contextual links?{% endtrans %}</h2>
|
||||
<p>{% trans %}<em>Contextual links</em> give users with the <em>Use contextual links</em> permission quick access to administrative tasks related to areas of non-administrative pages. For example, if a page on your site displays a block, the block would have a contextual link that would allow users with permission to configure the block. If the block contains a menu or a view, it would also have a contextual link for editing the menu links or the view. Clicking a contextual link takes you to the related administrative page directly, without needing to navigate through the administrative menu system.{% endtrans %}</p>
|
||||
<h2>{% trans %}Displaying and using contextual links{% endtrans %}</h2>
|
||||
<p>{% trans %}If you have the core Contextual Links module installed, the contextual links related to an area on a page can be displayed by clicking the contextual links button in that area of the page. In most themes, this button looks like a pencil and is placed in the upper right corner of the page area (upper left for right-to-left languages); however, contextual links buttons are normally hidden. Here are two ways to make contextual links buttons visible:{% endtrans %}</p>
|
||||
<ul>
|
||||
<li>{% trans %}Hovering your mouse over an area on the page will temporarily make the contextual links button visible, if there is one for that area of the page. Also, in most themes, the page area that the contextual links pertain to will be outlined while you are hovering.{% endtrans %}</li>
|
||||
<li>{% trans %}If you have the core Toolbar module enabled, there will be a contextual links toggle button at the far right end of the toolbar (far left end for right-to-left languages; this button looks like a pencil in most themes). Clicking the toggle button will make all the individual contextual links buttons on the page visible; clicking the toggle button again will make them invisible.{% endtrans %}</li>
|
||||
</ul>
|
||||
<p>{% trans %}While the contextual links button for the area of interest is visible, click the button to display the list of links for that area. Click a link in the list to perform the task.{% endtrans %}</p>
|
|
@ -0,0 +1,6 @@
|
|||
<meta name="help_topic:label" content="Tours"/>
|
||||
<meta name="help_topic:related" content="core.ui_components"/>
|
||||
<h2>{% trans %}What are tours?{% endtrans %}</h2>
|
||||
<p>{% trans %}The core Tour module provides users with <em>tours</em>, which are guided tours of the administrative interface. Each tour starts on a particular administrative page, and consists of one or more <em>tips</em> that highlight elements of the page, guide you through a workflow, or explain key concepts. Users need <em>Access tour</em> permission to view tours, and JavaScript must be enabled in their browsers.{% endtrans %}</p>
|
||||
<h2>{% trans %}Viewing tours{% endtrans %}</h2>
|
||||
<p>{% trans %}If a tour is available on a page, and you have the core Toolbar module installed, a <em>Tour</em> button will appear on the right end of the toolbar (left end for right-to-left languages). Click this button to view the first tip of the tour; click the <em>Next</em> button to advance to the next tip, and <em>End tour</em> at the end to close the tour.{% endtrans %}</p>
|
|
@ -0,0 +1,9 @@
|
|||
<meta name="help_topic:label" content="Writing good help"/>
|
||||
<meta name="help_topic:top_level"/>
|
||||
<p>{% trans %}Here are some suggestions for how to make your help topics as useful as possible for readers:{% endtrans %}</p>
|
||||
<ul>
|
||||
<li>{% trans %}Choose short titles. If the topic describes a task, start with a verb in -ing form, like "Writing good help".{% endtrans %}</li>
|
||||
<li>{% trans %}Make your topics modular and short, using links to connect information together.{% endtrans %}</li>
|
||||
<li>{% trans %}Use headings and lists to organize your topics.{% endtrans %}</li>
|
||||
<li>{% trans %}Write in second person (you, your). When describing a task, use imperative mood (tell people directly what to do, such as "Enter information in the ABC field", rather than using words like "please" or more passive or declarative language like "the ABC field needs to be filled in").{% endtrans %}</li>
|
||||
</ul>
|
|
@ -0,0 +1,8 @@
|
|||
<meta name="help_topic:label" content="Shortcuts"/>
|
||||
<meta name="help_topic:related" content="core.ui_components"/>
|
||||
<h2>{% trans %}What are shortcuts?{% endtrans %}</h2>
|
||||
<p>{% trans %}<em>Shortcuts</em> are quick links to administrative pages; they are managed by the core Shortcut module. A site can have one or more <em>shortcut sets</em>, which can be shared by one or more users; each set contains one or more shortcuts. Users need <em>Use shortcuts</em> permission to view shortcuts; <em>Edit current shortcut set</em> permission to add, delete, or edit the shortcuts in the set assigned to them; and <em>Select any shortcut set</em> permission to select a different shortcut set when editing their user profile. There is also an <em>Administer shortcuts</em> permission, which allows an administrator to do any of these actions, and also permits assigning shortcut sets to other users.{% endtrans %}</p>
|
||||
<h2>{% trans %}Creating and deleting shortcuts{% endtrans %}</h2>
|
||||
<p>{% trans %}When viewing certain administrative pages, you will see a link that allows you to add the page to your current shortcut set. In the core Seven administrative theme, the link looks like a star, and is displayed next to the page title. If the page is already in your shortcut set, you will instead see a link that allows you to remove it.{% endtrans %}</p>
|
||||
<h2>{% trans %}Viewing and using shortcuts{% endtrans %}</h2>
|
||||
<p>{% trans %}If you have the core Toolbar module installed, click <em>Shortcuts</em> in the toolbar to display your shortcuts. Once they are displayed, click any link in the shortcut bar to go directly to the administrative page. If you are not using the Toolbar module, you can display shortcuts by placing the <em>Shortcuts</em> block in a region of your theme.{% endtrans %}</p>
|
|
@ -0,0 +1,10 @@
|
|||
<meta name="help_topic:label" content="Defining how user accounts are created"/>
|
||||
<meta name="help_topic:related" content="core.security"/>
|
||||
{% set account_settings_url = render_var(url('entity.user.admin_form')) %}
|
||||
<p>{% trans %}On the <a href="{{ account_settings_url }}"><em>Account settings</em></a> page, which you can reach from the <em>Manage</em> administrative menu, by navigating to <em>Configuration</em> > <em>People</em> > <em>Account settings</em> (requires the <em>Administer account settings</em> permission), you can configure several settings related to how user accounts are created:{% endtrans %}</p>
|
||||
<ul>
|
||||
<li>{% trans %}You can make it possible for new users to register themselves, with or without administrator approval. Or, you can make it so only administrators with <em>Administer users</em> permission can register new users.{% endtrans %}</li>
|
||||
<li>{% trans %}You can require email verification of new user accounts.{% endtrans %}</li>
|
||||
<li>{% trans %}You can enable or disable a password strength indicator, which is shown whenever passwords are being set up or changed.{% endtrans %}</li>
|
||||
<li>{% trans %}You can edit the email messages that are sent to users in conjunction with the user registration process.{% endtrans %}</li>
|
||||
</ul>
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics\Controller;
|
||||
|
||||
use Drupal\Component\Utility\SortArray;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\help_topics\HelpTopicPluginManagerInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Controller for help topic plugins.
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class HelpTopicPluginController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* The renderer service.
|
||||
*
|
||||
* @var \Drupal\Core\Render\RendererInterface
|
||||
*/
|
||||
protected $renderer;
|
||||
|
||||
/**
|
||||
* The Help Topic plugin manager.
|
||||
*
|
||||
* @var \Drupal\help_topics\HelpTopicPluginManagerInterface
|
||||
*/
|
||||
protected $helpTopicPluginManager;
|
||||
|
||||
/**
|
||||
* Constructs a HelpTopicPluginController object.
|
||||
*
|
||||
* @param \Drupal\help_topics\HelpTopicPluginManagerInterface $help_topic_plugin_manager
|
||||
* The help topic plugin manager service.
|
||||
* @param \Drupal\Core\Render\RendererInterface $renderer
|
||||
* The renderer service.
|
||||
*/
|
||||
public function __construct(HelpTopicPluginManagerInterface $help_topic_plugin_manager, RendererInterface $renderer) {
|
||||
$this->helpTopicPluginManager = $help_topic_plugin_manager;
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.help_topic'),
|
||||
$container->get('renderer')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a help topic page.
|
||||
*
|
||||
* @param string $id
|
||||
* The plugin ID. Maps to the {id} placeholder in the
|
||||
* help_topics.help_topic route.
|
||||
*
|
||||
* @return array
|
||||
* A render array with the contents of a help topic page.
|
||||
*/
|
||||
public function viewHelpTopic($id) {
|
||||
$build = [];
|
||||
|
||||
if (!$this->helpTopicPluginManager->hasDefinition($id)) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
/* @var \Drupal\help_topics\HelpTopicPluginInterface $help_topic */
|
||||
$help_topic = $this->helpTopicPluginManager->createInstance($id);
|
||||
|
||||
$build['#body'] = $help_topic->getBody();
|
||||
|
||||
$this->renderer->addCacheableDependency($build, $help_topic);
|
||||
|
||||
// Build the related topics section, starting with the list this topic
|
||||
// says are related.
|
||||
$links = [];
|
||||
|
||||
$related = $help_topic->getRelated();
|
||||
foreach ($related as $other_id) {
|
||||
if ($other_id !== $id) {
|
||||
/** @var \Drupal\help_topics\HelpTopicPluginInterface $topic */
|
||||
$topic = $this->helpTopicPluginManager->createInstance($other_id);
|
||||
$links[$other_id] = [
|
||||
'title' => $topic->getLabel(),
|
||||
'url' => Url::fromRoute('help_topics.help_topic', ['id' => $other_id]),
|
||||
];
|
||||
$this->renderer->addCacheableDependency($build, $topic);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($links)) {
|
||||
uasort($links, [SortArray::class, 'sortByTitleElement']);
|
||||
$build['#related'] = [
|
||||
'#theme' => 'links__related',
|
||||
'#heading' => [
|
||||
'text' => $this->t('Related topics'),
|
||||
'level' => 'h2',
|
||||
],
|
||||
'#links' => $links,
|
||||
];
|
||||
}
|
||||
|
||||
$build['#theme'] = 'help_topic';
|
||||
$build['#title'] = $help_topic->getLabel();
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics;
|
||||
|
||||
use Drupal\Core\Breadcrumb\Breadcrumb;
|
||||
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
|
||||
/**
|
||||
* Provides a breadcrumb builder for help topic pages.
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class HelpBreadcrumbBuilder implements BreadcrumbBuilderInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Constructs the HelpBreadcrumbBuilder.
|
||||
*
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The translation service.
|
||||
*/
|
||||
public function __construct(TranslationInterface $string_translation) {
|
||||
$this->stringTranslation = $string_translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies(RouteMatchInterface $route_match) {
|
||||
return $route_match->getRouteName() == 'help_topics.help_topic';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build(RouteMatchInterface $route_match) {
|
||||
$breadcrumb = new Breadcrumb();
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '<front>'));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Administration'), 'system.admin'));
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Help'), 'help.main'));
|
||||
|
||||
return $breadcrumb;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics;
|
||||
|
||||
use Drupal\Component\Discovery\DiscoveryException;
|
||||
use Drupal\Component\FileCache\FileCacheFactory;
|
||||
use Drupal\Component\FileSystem\RegexDirectoryIterator;
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
|
||||
use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
|
||||
/**
|
||||
* Discovers help topic plugins from Twig files in help_topics directories.
|
||||
*
|
||||
* @see \Drupal\help_topics\HelpTopicTwig
|
||||
* @see \Drupal\help_topics\HelpTopicTwigLoader
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class HelpTopicDiscovery implements DiscoveryInterface {
|
||||
|
||||
use DiscoveryTrait;
|
||||
|
||||
/**
|
||||
* Defines the key in the discovered data where the file path is stored.
|
||||
*/
|
||||
const FILE_KEY = '_discovered_file_path';
|
||||
|
||||
/**
|
||||
* An array of directories to scan, keyed by the provider.
|
||||
*
|
||||
* The value can either be a string or an array of strings. The string values
|
||||
* should be the path of a directory to scan.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $directories = [];
|
||||
|
||||
/**
|
||||
* Constructs a HelpTopicDiscovery object.
|
||||
*
|
||||
* @param array $directories
|
||||
* An array of directories to scan, keyed by the provider. The value can
|
||||
* either be a string or an array of strings. The string values should be
|
||||
* the path of a directory to scan.
|
||||
*/
|
||||
public function __construct(array $directories) {
|
||||
$this->directories = $directories;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinitions() {
|
||||
$plugins = $this->findAll();
|
||||
|
||||
// Flatten definitions into what's expected from plugins.
|
||||
$definitions = [];
|
||||
foreach ($plugins as $list) {
|
||||
foreach ($list as $id => $definition) {
|
||||
$definitions[$id] = $definition;
|
||||
}
|
||||
}
|
||||
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of discoverable items.
|
||||
*
|
||||
* @return array
|
||||
* An array of discovered data keyed by provider.
|
||||
*
|
||||
* @throws \Drupal\Component\Discovery\DiscoveryException
|
||||
* Exception thrown if there is a problem during discovery.
|
||||
*/
|
||||
public function findAll() {
|
||||
$all = [];
|
||||
|
||||
$files = $this->findFiles();
|
||||
|
||||
$file_cache = FileCacheFactory::get('help_topic_discovery:help_topics');
|
||||
|
||||
// Try to load from the file cache first.
|
||||
foreach ($file_cache->getMultiple(array_keys($files)) as $file => $data) {
|
||||
$all[$files[$file]][$data['id']] = $data;
|
||||
unset($files[$file]);
|
||||
}
|
||||
|
||||
// If there are files left that were not returned from the cache, load and
|
||||
// parse them now. This list was flipped above and is keyed by filename.
|
||||
if ($files) {
|
||||
foreach ($files as $file => $provider) {
|
||||
$plugin_id = substr(basename($file), 0, -10);
|
||||
// The plugin ID begins with provider.
|
||||
list($file_name_provider,) = explode('.', $plugin_id, 2);
|
||||
// Only the Help Topics module can provider help for other extensions.
|
||||
// @todo https://www.drupal.org/project/drupal/issues/3025577 Remove
|
||||
// help_topics special case once Help Topics is stable and core
|
||||
// modules can provide their own help topics.
|
||||
if ($provider !== 'help_topics' && $provider !== $file_name_provider) {
|
||||
throw new DiscoveryException("$file should begin with '$provider.'");
|
||||
}
|
||||
$data = [
|
||||
// The plugin ID is derived from the filename. The extension
|
||||
// '.html.twig' is removed
|
||||
'id' => $plugin_id,
|
||||
'provider' => $file_name_provider,
|
||||
'class' => HelpTopicTwig::class,
|
||||
static::FILE_KEY => $file,
|
||||
];
|
||||
|
||||
// Get the rest of the plugin definition from meta tags contained in the
|
||||
// help topic Twig file.
|
||||
foreach (get_meta_tags($file) as $key => $value) {
|
||||
$key = substr($key, 11);
|
||||
switch ($key) {
|
||||
case 'related':
|
||||
$data[$key] = array_map('trim', explode(',', $value));
|
||||
break;
|
||||
case 'top_level':
|
||||
$data[$key] = TRUE;
|
||||
if ($value !== '') {
|
||||
throw new DiscoveryException("$file contains invalid meta tag with name='help_topic:top_level', the 'content' property should not exist");
|
||||
}
|
||||
break;
|
||||
case 'label':
|
||||
$data[$key] = new TranslatableMarkup($value);
|
||||
break;
|
||||
default:
|
||||
throw new DiscoveryException("$file contains invalid meta tag with name='$key'");
|
||||
}
|
||||
}
|
||||
if (!isset($data['label'])) {
|
||||
throw new DiscoveryException("$file does not contain the required meta tag with name='help_topic:label'");
|
||||
}
|
||||
|
||||
$all[$provider][$data['id']] = $data;
|
||||
$file_cache->set($file, $data);
|
||||
}
|
||||
}
|
||||
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of providers keyed by file path.
|
||||
*
|
||||
* @return array
|
||||
* An array of providers keyed by file path.
|
||||
*/
|
||||
protected function findFiles() {
|
||||
$file_list = [];
|
||||
foreach ($this->directories as $provider => $directories) {
|
||||
$directories = (array) $directories;
|
||||
foreach ($directories as $directory) {
|
||||
if (is_dir($directory)) {
|
||||
/** @var \SplFileInfo $fileInfo */
|
||||
$iterator = new RegexDirectoryIterator($directory, '/\.html\.twig$/i');
|
||||
foreach ($iterator as $fileInfo) {
|
||||
$file_list[$fileInfo->getPathname()] = $provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $file_list;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics;
|
||||
|
||||
use Drupal\Core\Link;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Base class for help topic plugins.
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
abstract class HelpTopicPluginBase extends PluginBase implements HelpTopicPluginInterface {
|
||||
|
||||
/**
|
||||
* The name of the module or theme providing the help topic.
|
||||
*/
|
||||
public function getProvider() {
|
||||
return $this->pluginDefinition['provider'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->pluginDefinition['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTopLevel() {
|
||||
return $this->pluginDefinition['top_level'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRelated() {
|
||||
return $this->pluginDefinition['related'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toUrl(array $options = []) {
|
||||
return Url::fromRoute('help_topics.help_topic', ['id' => $this->getPluginId()], $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toLink($text = NULL, array $options = []) {
|
||||
if (!$text) {
|
||||
$text = $this->getLabel();
|
||||
}
|
||||
return Link::createFromRoute($text, 'help_topics.help_topic', ['id' => $this->getPluginId()], $options);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics;
|
||||
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\Component\Plugin\DerivativeInspectionInterface;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for help topic plugin classes.
|
||||
*
|
||||
* @see \Drupal\help_topics\HelpTopicPluginManager
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
interface HelpTopicPluginInterface extends PluginInspectionInterface, DerivativeInspectionInterface, CacheableDependencyInterface {
|
||||
|
||||
/**
|
||||
* Returns the label of the topic.
|
||||
*
|
||||
* @return string
|
||||
* The label of the topic.
|
||||
*/
|
||||
public function getLabel();
|
||||
|
||||
/**
|
||||
* Returns the body of the topic.
|
||||
*
|
||||
* @return array
|
||||
* A render array representing the body.
|
||||
*/
|
||||
public function getBody();
|
||||
|
||||
/**
|
||||
* Returns whether this is a top-level topic or not.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if this is a topic that should be displayed on the Help topics
|
||||
* list; FALSE if not.
|
||||
*/
|
||||
public function isTopLevel();
|
||||
|
||||
/**
|
||||
* Returns the IDs of related topics.
|
||||
*
|
||||
* @return string[]
|
||||
* Array of the IDs of related topics.
|
||||
*/
|
||||
public function getRelated();
|
||||
|
||||
/**
|
||||
* Returns the URL for viewing the help topic.
|
||||
*
|
||||
* @param array $options
|
||||
* (optional) See
|
||||
* \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute() for the
|
||||
* available options.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* A URL object containing the URL for viewing the help topic.
|
||||
*/
|
||||
public function toUrl(array $options = []);
|
||||
|
||||
/**
|
||||
* Returns a link for viewing the help topic.
|
||||
*
|
||||
* @param string|null $text
|
||||
* (optional) Link text to use for the link. If NULL, defaults to the
|
||||
* topic title.
|
||||
* @param array $options
|
||||
* (optional) See
|
||||
* \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute() for the
|
||||
* available options.
|
||||
*
|
||||
* @return \Drupal\Core\Link
|
||||
* A link object for viewing the topic.
|
||||
*/
|
||||
public function toLink($text = NULL, array $options = []);
|
||||
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
|
||||
/**
|
||||
* Provides the default help_topic manager.
|
||||
*
|
||||
* Modules and themes can provide help topics in .html.twig files called
|
||||
* provider.name_of_topic.html.twig inside the module or theme sub-directory
|
||||
* help_topics. The provider is validated to be the extension that provides the
|
||||
* help topic.
|
||||
*
|
||||
* The Twig file must contain a meta tag named 'help_topic:label'. It can also
|
||||
* contain meta tags named 'help_topic:top_level' and 'help_topic:related'. For
|
||||
* example:
|
||||
* @code
|
||||
* <!–– The label/title of the topic. -->
|
||||
* <meta name="help_topic:label" content="Configuring error responses, including 403/404 pages"/>
|
||||
*
|
||||
* <!–– Related help topics in a comma separated help topic ID list. -->
|
||||
* <meta name="help_topic:related" content="core.config_basic,core.maintenance"/>
|
||||
*
|
||||
* <!–– If present then the help topic will appear on admin/help. -->
|
||||
* <meta name="help_topic:top_level"/>
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\help_topics\HelpTopicDiscovery
|
||||
* @see \Drupal\help_topics\HelpTopicTwig
|
||||
* @see \Drupal\help_topics\HelpTopicTwigLoader
|
||||
* @see \Drupal\help_topics\HelpTopicPluginInterface
|
||||
* @see \Drupal\help_topics\HelpTopicPluginBase
|
||||
* @see hook_help_topics_info_alter()
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class HelpTopicPluginManager extends DefaultPluginManager implements HelpTopicPluginManagerInterface {
|
||||
|
||||
/**
|
||||
* Provides default values for all help topic plugins.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaults = [
|
||||
// The plugin ID.
|
||||
'id' => '',
|
||||
// The title of the help topic plugin.
|
||||
'label' => '',
|
||||
// Whether or not the topic should appear on the help topics list.
|
||||
'top_level' => '',
|
||||
// List of related topic machine names.
|
||||
'related' => [],
|
||||
// The class used to instantiate the plugin.
|
||||
'class' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* Constructs a new HelpTopicManager object.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* Cache backend instance to use.
|
||||
*/
|
||||
public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache_backend) {
|
||||
// Note that the parent construct is not called because this not use
|
||||
// annotated class discovery.
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->alterInfo('help_topics_info');
|
||||
// Use the 'config:core.extension' cache tag so the plugin cache is
|
||||
// invalidated on theme install and uninstall.
|
||||
$this->setCacheBackend($cache_backend, 'help_topics', ['config:core.extension']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDiscovery() {
|
||||
if (!isset($this->discovery)) {
|
||||
// We want to find help topic plugins in core, modules and themes in
|
||||
// a sub-directory called help_topics.
|
||||
$directories = array_merge(
|
||||
['core'],
|
||||
$this->moduleHandler->getModuleDirectories(),
|
||||
$this->themeHandler->getThemeDirectories()
|
||||
);
|
||||
|
||||
$directories = array_map(function ($dir) {
|
||||
return [$dir . '/help_topics'];
|
||||
}, $directories);
|
||||
|
||||
$this->discovery = new HelpTopicDiscovery($directories);
|
||||
}
|
||||
return $this->discovery;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function providerExists($provider) {
|
||||
return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function findDefinitions() {
|
||||
$definitions = parent::findDefinitions();
|
||||
|
||||
// At this point the plugin list only contains valid plugins. Ensure all
|
||||
// related plugins exist and the relationship is bi-directional. This
|
||||
// ensures topics are listed on their related topics.
|
||||
foreach ($definitions as $plugin_id => $plugin_definition) {
|
||||
foreach ($plugin_definition['related'] as $key => $related_id) {
|
||||
// If the related help topic does not exist it might be for a module
|
||||
// that is not installed. Remove it.
|
||||
// @todo Discuss this more as this could cause silent errors but it
|
||||
// offers useful functionality to relate to help topic provided by
|
||||
// extensions that are yet to be installed.
|
||||
if (!isset($definitions[$related_id])) {
|
||||
unset($definitions[$plugin_id]['related'][$key]);
|
||||
continue;
|
||||
}
|
||||
// Make the related relationship bi-directional.
|
||||
if (isset($definitions[$related_id]) && !in_array($plugin_id, $definitions[$related_id]['related'], TRUE)) {
|
||||
$definitions[$related_id]['related'][] = $plugin_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for managing help topics and storing their definitions.
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
interface HelpTopicPluginManagerInterface extends PluginManagerInterface {
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Template\TwigEnvironment;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Represents a help topic plugin whose definition comes from a Twig file.
|
||||
*
|
||||
* @see \Drupal\help_topics\HelpTopicDiscovery
|
||||
* @see \Drupal\help_topics\HelpTopicTwigLoader
|
||||
* @see \Drupal\help_topics\HelpTopicPluginManager
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class HelpTopicTwig extends HelpTopicPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The Twig environment.
|
||||
*
|
||||
* @var \Drupal\Core\Template\TwigEnvironment
|
||||
*/
|
||||
protected $twig;
|
||||
|
||||
/**
|
||||
* HelpTopicPluginBase constructor.
|
||||
*
|
||||
* @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\Template\TwigEnvironment $twig
|
||||
* The Twig environment.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, TwigEnvironment $twig) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->twig = $twig;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('twig')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBody() {
|
||||
return [
|
||||
// Note that #markup elements are automatically XSS admin filtered which
|
||||
// removes the meta tags from the rendered HTML.
|
||||
'#markup' => $this->twig->load('@help_topics/' . $this->getPluginId() . '.html.twig')->render(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return ['core.extension'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics;
|
||||
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
|
||||
/**
|
||||
* Loads help topic Twig files from the filesystem.
|
||||
*
|
||||
* This loader adds module and theme help topic paths to a help_topics namespace
|
||||
* to the Twig filesystem loader so that help_topics can be referenced, using
|
||||
* '@help-topic/pluginId.html.twig'.
|
||||
*
|
||||
* @see \Drupal\help_topics\HelpTopicDiscovery
|
||||
* @see \Drupal\help_topics\HelpTopicTwig
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class HelpTopicTwigLoader extends \Twig_Loader_Filesystem {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
const MAIN_NAMESPACE = 'help_topics';
|
||||
|
||||
/**
|
||||
* Constructs a new HelpTopicTwigLoader object.
|
||||
*
|
||||
* @param string $root_path
|
||||
* The root path.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler service.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler service.
|
||||
*/
|
||||
public function __construct($root_path, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
|
||||
parent::__construct([], $root_path);
|
||||
// Add help_topics directories for modules and themes in the 'help_topic'
|
||||
// namespace.
|
||||
array_map([$this, 'addExtension'], $module_handler->getModuleDirectories());
|
||||
array_map([$this, 'addExtension'], $theme_handler->getThemeDirectories());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an extensions help_topics directory to the Twig loader.
|
||||
*
|
||||
* @param $path
|
||||
* The path to the extension.
|
||||
*/
|
||||
protected function addExtension($path) {
|
||||
$path .= DIRECTORY_SEPARATOR . 'help_topics';
|
||||
if (is_dir($path)) {
|
||||
$this->cache = $this->errorCache = [];
|
||||
$this->paths[self::MAIN_NAMESPACE][] = rtrim($path, '/\\');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\help_topics\Plugin\HelpSection;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\help_topics\HelpTopicPluginInterface;
|
||||
use Drupal\help_topics\HelpTopicPluginManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\help\Plugin\HelpSection\HelpSectionPluginBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides the help topics list section for the help page.
|
||||
*
|
||||
* @HelpSection(
|
||||
* id = "help_topics",
|
||||
* title = @Translation("Topics"),
|
||||
* description = @Translation("Topics can be provided by modules or themes. Top-level help topics on your site:"),
|
||||
* permission = "access administration pages"
|
||||
* )
|
||||
*
|
||||
* @internal
|
||||
* Help Topic is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class HelpTopicSection extends HelpSectionPluginBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The plugin manager.
|
||||
*
|
||||
* @var \Drupal\help_topics\HelpTopicPluginManagerInterface
|
||||
*/
|
||||
protected $pluginManager;
|
||||
|
||||
/**
|
||||
* The top level help topic plugins.
|
||||
*
|
||||
* @var \Drupal\help_topics\HelpTopicPluginInterface[]
|
||||
*/
|
||||
protected $topLevelPlugins;
|
||||
|
||||
/**
|
||||
* The merged top level help topic plugins cache metadata.
|
||||
*/
|
||||
protected $cacheableMetadata;
|
||||
|
||||
/**
|
||||
* Constructs a HelpTopicSection 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\help_topics\HelpTopicPluginManagerInterface $plugin_manager
|
||||
* The help topic plugin manager service.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, HelpTopicPluginManagerInterface $plugin_manager) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->pluginManager = $plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('plugin.manager.help_topic')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return $this->getCacheMetadata()->getCacheTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return $this->getCacheMetadata()->getCacheContexts();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return $this->getCacheMetadata()->getCacheMaxAge();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function listTopics() {
|
||||
// Map the top level help topic plugins to a list of topic links.
|
||||
return array_map(function (HelpTopicPluginInterface $topic) {
|
||||
return $topic->toLink();
|
||||
}, $this->getPlugins());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the top level help topic plugins.
|
||||
*
|
||||
* @return \Drupal\help_topics\HelpTopicPluginInterface[]
|
||||
* The top level help topic plugins
|
||||
*/
|
||||
protected function getPlugins() {
|
||||
if (!isset($this->topLevelPlugins)) {
|
||||
$definitions = $this->pluginManager->getDefinitions();
|
||||
|
||||
// Get all the top level topics and merge their list cache tags.
|
||||
foreach ($definitions as $definition) {
|
||||
if ($definition['top_level']) {
|
||||
$this->topLevelPlugins[$definition['id']] = $this->pluginManager->createInstance($definition['id']);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the top level topics by label and, if the labels match, then by
|
||||
// plugin ID.
|
||||
usort($this->topLevelPlugins, function (HelpTopicPluginInterface $a, HelpTopicPluginInterface $b) {
|
||||
$a_label = (string) $a->getLabel();
|
||||
$b_label = (string) $b->getLabel();
|
||||
if ($a_label === $b_label) {
|
||||
return $a->getPluginId() < $b->getPluginId() ? -1 : 1;
|
||||
}
|
||||
return strnatcasecmp($a_label, $b_label);
|
||||
});
|
||||
}
|
||||
return $this->topLevelPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the merged CacheableMetadata for all the top level help topic plugins.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheableMetadata
|
||||
* The merged CacheableMetadata for all the top level help topic plugins.
|
||||
*/
|
||||
protected function getCacheMetadata() {
|
||||
if (!isset($this->cacheableMetadata)) {
|
||||
$this->cacheableMetadata = new CacheableMetadata();
|
||||
foreach ($this->getPlugins() as $plugin) {
|
||||
$this->cacheableMetadata->addCacheableDependency($plugin);
|
||||
}
|
||||
}
|
||||
return $this->cacheableMetadata;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation to display a help topic.
|
||||
*
|
||||
* Available variables:
|
||||
* - body: The body of the topic.
|
||||
* - related: List of related topic links.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
<article>
|
||||
{{ body }}
|
||||
{{ related }}
|
||||
</article>
|
|
@ -0,0 +1,3 @@
|
|||
<meta name="help_topic:label" content="Additional topic"/>
|
||||
<meta name="help_topic:related" content="help_topics_test.test"/>
|
||||
<p>{% trans %}This topic should get listed automatically on the Help test topic.{% endtrans %}</p>
|
|
@ -0,0 +1,2 @@
|
|||
<meta name="help_topic:label" content="Linked topic"/>
|
||||
<p>{% trans %}This topic is not supposed to be top-level.{% endtrans %}</p>
|
|
@ -0,0 +1,6 @@
|
|||
<meta name="help_topic:label" content="ABC Help Test module"/>
|
||||
<meta name="help_topic:top_level"/>
|
||||
<meta name="help_topic:related" content="help_topics_test.linked,does_not_exist.and_no_error"/>
|
||||
{% set help_topic_url = render_var(url('help_topics.help_topic', {id: 'help_topics.help_topic_writing'})) %}
|
||||
<p>{% trans %}This is a test. It should <a href="{{ help_topic_url }}">link to the writing good help topic</a>. Also there should be a related topic link below to the Help module topic page and the linked topic.{% endtrans %}</p>
|
||||
<p>{% trans %}Test translation.{% endtrans %}</p>
|
|
@ -0,0 +1,7 @@
|
|||
# The name of this module is deliberately different from its machine
|
||||
# name to test the presented order of help topics.
|
||||
name: 'ABC Help Test'
|
||||
type: module
|
||||
description: 'Support module for help testing.'
|
||||
package: Testing
|
||||
core: 8.x
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test module for help.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function help_topics_test_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.help_topics_test':
|
||||
return 'Some kind of non-empty output for testing';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_help_topics_info_alter().
|
||||
*/
|
||||
function help_topics_test_help_topics_info_alter(array &$info) {
|
||||
$info['help_topics_test.test']['top_level'] = \Drupal::state()->get('help_topics_test.test:top_level', TRUE);
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\help_topics\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\system\Functional\Menu\AssertBreadcrumbTrait;
|
||||
|
||||
/**
|
||||
* Verifies help topic display and user access to help based on permissions.
|
||||
*
|
||||
* @group help_topics
|
||||
*/
|
||||
class HelpTopicTest extends BrowserTestBase {
|
||||
use AssertBreadcrumbTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'help_topics_test',
|
||||
'help',
|
||||
'help_topics',
|
||||
'block',
|
||||
];
|
||||
|
||||
/**
|
||||
* The admin user that will be created.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* The anonymous user that will be created.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $anyUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// These tests rely on some markup from the 'Seven' theme and we test theme
|
||||
// provided help topics.
|
||||
\Drupal::service('theme_handler')->install(['seven', 'help_topics_test_theme']);
|
||||
\Drupal::service('config.factory')->getEditable('system.theme')->set('admin', 'seven')->save();
|
||||
|
||||
// Place various blocks.
|
||||
$settings = [
|
||||
'theme' => 'seven',
|
||||
'region' => 'help',
|
||||
];
|
||||
$this->placeBlock('help_block', $settings);
|
||||
$this->placeBlock('local_tasks_block', $settings);
|
||||
$this->placeBlock('local_actions_block', $settings);
|
||||
$this->placeBlock('page_title_block', $settings);
|
||||
$this->placeBlock('system_breadcrumb_block', $settings);
|
||||
|
||||
// Create users.
|
||||
$this->adminUser = $this->createUser([
|
||||
'access administration pages',
|
||||
'view the administration theme',
|
||||
'administer permissions',
|
||||
]);
|
||||
$this->anyUser = $this->createUser([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the main help page and individual pages for topics.
|
||||
*/
|
||||
public function testHelp() {
|
||||
$session = $this->assertSession();
|
||||
|
||||
// Log in the regular user.
|
||||
$this->drupalLogin($this->anyUser);
|
||||
$this->verifyHelp(403);
|
||||
|
||||
// Log in the admin user.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->verifyHelp();
|
||||
$this->verifyHelpLinks();
|
||||
$this->verifyBreadCrumb();
|
||||
|
||||
// Verify that help topics text appears on admin/help.
|
||||
$this->drupalGet('admin/help');
|
||||
$session->responseContains('<h2>Topics</h2>');
|
||||
$session->pageTextContains('Topics can be provided by modules or themes');
|
||||
|
||||
// Verify links for for help topics and order.
|
||||
$page_text = $this->getTextContent();
|
||||
$start = strpos($page_text, 'Topics can be provided');
|
||||
$pos = $start;
|
||||
foreach ($this->getTopicList() as $info) {
|
||||
$name = $info['name'];
|
||||
$session->linkExists($name);
|
||||
$new_pos = strpos($page_text, $name, $start);
|
||||
$this->assertTrue($new_pos > $pos, 'Order of ' . $name . ' is correct on page');
|
||||
$pos = $new_pos;
|
||||
}
|
||||
|
||||
// Ensure the plugin manager alter hook works as expected.
|
||||
$session->linkExists('ABC Help Test module');
|
||||
\Drupal::state()->set('help_topics_test.test:top_level', FALSE);
|
||||
\Drupal::service('plugin.manager.help_topic')->clearCachedDefinitions();
|
||||
$this->drupalGet('admin/help');
|
||||
$session->linkNotExists('ABC Help Test module');
|
||||
\Drupal::state()->set('help_topics_test.test:top_level', TRUE);
|
||||
\Drupal::service('plugin.manager.help_topic')->clearCachedDefinitions();
|
||||
$this->drupalGet('admin/help');
|
||||
|
||||
// Ensure all the expected links are present before uninstalling.
|
||||
$session->linkExists('ABC Help Test module');
|
||||
$session->linkExists('ABC Help Test');
|
||||
$session->linkExists('XYZ Help Test theme');
|
||||
|
||||
// Uninstall the test module and verify the topics are gone, after
|
||||
// reloading page.
|
||||
$this->container->get('module_installer')->uninstall(['help_topics_test']);
|
||||
$this->drupalGet('admin/help');
|
||||
$session->linkNotExists('ABC Help Test module');
|
||||
$session->linkNotExists('ABC Help Test');
|
||||
$session->linkExists('XYZ Help Test theme');
|
||||
|
||||
// Uninstall the test theme and verify the topic is gone.
|
||||
$this->container->get('theme_installer')->uninstall(['help_topics_test_theme']);
|
||||
$this->drupalGet('admin/help');
|
||||
$session->linkNotExists('XYZ Help Test theme');
|
||||
|
||||
// Verify the Help Topics provided by the Help Topics module for optional
|
||||
// extensions do not exist.
|
||||
$this->drupalGet('admin/help/topic/core.ui_components');
|
||||
$session->linkNotExists('Shortcuts');
|
||||
$session->linkExists('Accessibility features');
|
||||
$this->container->get('module_installer')->install(['shortcut']);
|
||||
$this->drupalGet('admin/help/topic/core.ui_components');
|
||||
$session->linkExists('Shortcuts');
|
||||
$session->linkExists('Accessibility features');
|
||||
$this->clickLink('Shortcuts');
|
||||
$session->pageTextContains('What are shortcuts?');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the logged in user has access to various help links and pages.
|
||||
*
|
||||
* @param int $response
|
||||
* (optional) The HTTP response code to test for. If it's 200 (default),
|
||||
* the test verifies the user sees the help; if it's not, it verifies they
|
||||
* are denied access.
|
||||
*/
|
||||
protected function verifyHelp($response = 200) {
|
||||
// Verify access to help topic pages.
|
||||
foreach ($this->getTopicList() as $topic => $info) {
|
||||
// View help topic page.
|
||||
$this->drupalGet('admin/help/topic/' . $topic);
|
||||
$session = $this->assertSession();
|
||||
$session->statusCodeEquals($response);
|
||||
if ($response == 200) {
|
||||
$name = $info['name'];
|
||||
$session->titleEquals($name . ' | Drupal');
|
||||
$session->responseContains('<h1 class="page-title">' . $name . '</h1>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies links on the test help topic page and other pages.
|
||||
*
|
||||
* Assumes an admin user is logged in.
|
||||
*/
|
||||
protected function verifyHelpLinks() {
|
||||
$session = $this->assertSession();
|
||||
// Verify links on the test top-level page.
|
||||
$page = 'admin/help/topic/help_topics_test.test';
|
||||
$links = [
|
||||
'link to the writing good help topic' => 'Writing good help',
|
||||
'Linked topic' => 'This topic is not supposed to be top-level',
|
||||
'Additional topic' => 'This topic should get listed automatically',
|
||||
];
|
||||
foreach ($links as $link_text => $page_text) {
|
||||
$this->drupalGet($page);
|
||||
$this->clickLink($link_text);
|
||||
$session->pageTextContains($page_text);
|
||||
}
|
||||
|
||||
// Verify theme provided help topics work and can be related.
|
||||
$this->drupalGet('admin/help/topic/help_topics_test_theme.test');
|
||||
$session->pageTextContains('This is a theme provided topic.');
|
||||
// Use the article element to provide a positive assertion to improve the
|
||||
// assertion that the help html does not contain meta tags.
|
||||
$this->assertContains('This is a theme provided topic.', $session->elementExists('css', 'article')->getText());
|
||||
// Ensure that meta tags containing plugin information do not appear on
|
||||
// topic pages
|
||||
$session->elementNotExists('css', 'article meta');
|
||||
$this->clickLink('Additional topic');
|
||||
$session->linkExists('XYZ Help Test theme');
|
||||
|
||||
// Verify that the non-top-level topics do not appear on the Help page.
|
||||
$this->drupalGet('admin/help');
|
||||
$session->linkNotExists('Linked topic');
|
||||
$session->linkNotExists('Additional topic');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of topic IDs to test.
|
||||
*
|
||||
* @return array
|
||||
* A list of topics to test, in the order in which they should appear. The
|
||||
* keys are the machine names of the topics. The values are arrays with the
|
||||
* following elements:
|
||||
* - name: Displayed name.
|
||||
*/
|
||||
protected function getTopicList() {
|
||||
return [
|
||||
'help_topics_test.test' => [
|
||||
'name' => 'ABC Help Test module',
|
||||
],
|
||||
'help_topics.help_topic_writing' => [
|
||||
'name' => 'Writing good help',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests breadcrumb on a help topic page.
|
||||
*/
|
||||
public function verifyBreadCrumb() {
|
||||
// Verify Help Topics administration breadcrumbs.
|
||||
$trail = [
|
||||
'' => 'Home',
|
||||
'admin' => t('Administration'),
|
||||
'admin/help' => t('Help'),
|
||||
];
|
||||
$this->assertBreadcrumb('admin/help/topic/help_topics_test.test', $trail);
|
||||
// Ensure we are on the expected help topic page.
|
||||
$this->assertSession()->pageTextContains('Also there should be a related topic link below to the Help module topic page and the linked topic.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\help_topics\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Verifies help topic display and user access to help based on permissions.
|
||||
*
|
||||
* @group help_topics
|
||||
*/
|
||||
class HelpTopicTranslationTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'help_topics_test',
|
||||
'help',
|
||||
'help_topics',
|
||||
'block',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// These tests rely on some markup from the 'Seven' theme.
|
||||
\Drupal::service('theme_handler')->install(['seven']);
|
||||
\Drupal::service('config.factory')->getEditable('system.theme')->set('admin', 'seven')->save();
|
||||
|
||||
// Place various blocks.
|
||||
$settings = [
|
||||
'theme' => 'seven',
|
||||
'region' => 'help',
|
||||
];
|
||||
$this->placeBlock('help_block', $settings);
|
||||
$this->placeBlock('local_tasks_block', $settings);
|
||||
$this->placeBlock('local_actions_block', $settings);
|
||||
$this->placeBlock('page_title_block', $settings);
|
||||
|
||||
// Create user.
|
||||
$this->drupalLogin($this->createUser([
|
||||
'access administration pages',
|
||||
'view the administration theme',
|
||||
'administer permissions',
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests help topic translations.
|
||||
*/
|
||||
public function testHelpTopicTranslations() {
|
||||
$session = $this->assertSession();
|
||||
|
||||
// Verify that help topic link is translated on admin/help.
|
||||
$this->drupalGet('admin/help');
|
||||
$session->linkExists('ABC-Hilfetestmodul');
|
||||
// Verify that help topic is translated.
|
||||
$this->drupalGet('admin/help/topic/help_topics_test.test');
|
||||
$session->pageTextContains('ABC-Hilfetestmodul');
|
||||
$session->pageTextContains('Übersetzung testen.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function installParameters() {
|
||||
$parameters = parent::installParameters();
|
||||
// Install in German. This will ensure the language and locale modules are
|
||||
// installed.
|
||||
$parameters['parameters']['langcode'] = 'de';
|
||||
// Create a po file so we don't attempt to download one from
|
||||
// localize.drupal.org and to have a test translation that will not change.
|
||||
\Drupal::service('file_system')->mkdir($this->publicFilesDirectory . '/translations', NULL, TRUE);
|
||||
$contents = <<<ENDPO
|
||||
msgid ""
|
||||
msgstr ""
|
||||
|
||||
msgid "ABC Help Test module"
|
||||
msgstr "ABC-Hilfetestmodul"
|
||||
|
||||
msgid "Test translation."
|
||||
msgstr "Übersetzung testen."
|
||||
|
||||
ENDPO;
|
||||
include_once $this->root . '/core/includes/install.core.inc';
|
||||
$version = _install_get_version_info(\Drupal::VERSION)['major'] . '.0.0';
|
||||
file_put_contents($this->publicFilesDirectory . "/translations/drupal-{$version}.de.po", $contents);
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\help_topics\Unit;
|
||||
|
||||
use Drupal\Component\Discovery\DiscoveryException;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\help_topics\HelpTopicDiscovery;
|
||||
use Drupal\help_topics\HelpTopicTwig;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\help_topics\HelpTopicDiscovery
|
||||
* @group help_topics
|
||||
*/
|
||||
class HelpTopicDiscoveryTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::findAll
|
||||
*/
|
||||
public function testDiscoveryExceptionProviderMismatch() {
|
||||
vfsStream::setup('root');
|
||||
vfsStream::create([
|
||||
'modules' => [
|
||||
'foo' => [
|
||||
'help_topics' => [
|
||||
// The content of the help topic does not matter.
|
||||
'test.topic.html.twig' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$discovery = new HelpTopicDiscovery(['foo' => vfsStream::url('root/modules/foo/help_topics')]);
|
||||
|
||||
$this->expectException(DiscoveryException::class);
|
||||
$this->expectExceptionMessage("vfs://root/modules/foo/help_topics/test.topic.html.twig should begin with 'foo.'");
|
||||
$discovery->getDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::findAll
|
||||
*/
|
||||
public function testDiscoveryExceptionMissingLabelMetaTag() {
|
||||
vfsStream::setup('root');
|
||||
|
||||
vfsStream::create([
|
||||
'modules' => [
|
||||
'test' => [
|
||||
'help_topics' => [
|
||||
// The content of the help topic does not matter.
|
||||
'test.topic.html.twig' => '',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$discovery = new HelpTopicDiscovery(['test' => vfsStream::url('root/modules/test/help_topics')]);
|
||||
|
||||
$this->expectException(DiscoveryException::class);
|
||||
$this->expectExceptionMessage("vfs://root/modules/test/help_topics/test.topic.html.twig does not contain the required meta tag with name='help_topic:label'");
|
||||
$discovery->getDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::findAll
|
||||
*/
|
||||
public function testDiscoveryExceptionInvalidMetaTag() {
|
||||
vfsStream::setup('root');
|
||||
// Note a blank line is required after the last meta tag otherwise the last
|
||||
// meta tag is not parsed.
|
||||
$topic_content = <<<EOF
|
||||
<meta name="help_topic:label" content="A label"/>
|
||||
<meta name="help_topic:foo" content="bar"/>
|
||||
|
||||
EOF;
|
||||
|
||||
vfsStream::create([
|
||||
'modules' => [
|
||||
'test' => [
|
||||
'help_topics' => [
|
||||
'test.topic.html.twig' => $topic_content,
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$discovery = new HelpTopicDiscovery(['test' => vfsStream::url('root/modules/test/help_topics')]);
|
||||
|
||||
$this->expectException(DiscoveryException::class);
|
||||
$this->expectExceptionMessage("vfs://root/modules/test/help_topics/test.topic.html.twig contains invalid meta tag with name='foo'");
|
||||
$discovery->getDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::findAll
|
||||
*/
|
||||
public function testDiscoveryExceptionInvalidTopLevel() {
|
||||
vfsStream::setup('root');
|
||||
// Note a blank line is required after the last meta tag otherwise the last
|
||||
// meta tag is not parsed.
|
||||
$topic_content = <<<EOF
|
||||
<meta name="help_topic:label" content="A label"/>
|
||||
<meta name="help_topic:top_level" content="bar"/>
|
||||
|
||||
EOF;
|
||||
|
||||
vfsStream::create([
|
||||
'modules' => [
|
||||
'test' => [
|
||||
'help_topics' => [
|
||||
'test.topic.html.twig' => $topic_content,
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$discovery = new HelpTopicDiscovery(['test' => vfsStream::url('root/modules/test/help_topics')]);
|
||||
|
||||
$this->expectException(DiscoveryException::class);
|
||||
$this->expectExceptionMessage("vfs://root/modules/test/help_topics/test.topic.html.twig contains invalid meta tag with name='help_topic:top_level', the 'content' property should not exist");
|
||||
$discovery->getDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::findAll
|
||||
*/
|
||||
public function testHelpTopicsExtensionProviderSpecialCase() {
|
||||
vfsStream::setup('root');
|
||||
$topic_content = <<<EOF
|
||||
<meta name="help_topic:label" content="Test"/>
|
||||
<h2>Test</h2>
|
||||
EOF;
|
||||
|
||||
vfsStream::create([
|
||||
'modules' => [
|
||||
'help_topics' => [
|
||||
'help_topics' => [
|
||||
'core.topic.html.twig' => $topic_content,
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$discovery = new HelpTopicDiscovery(['help_topics' => vfsStream::url('root/modules/help_topics/help_topics')]);
|
||||
$this->assertArrayHasKey('core.topic', $discovery->getDefinitions());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::findAll
|
||||
*/
|
||||
public function testHelpTopicsDefinition() {
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('string_translation', $this->getStringTranslationStub());
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
vfsStream::setup('root');
|
||||
$topic_content = <<<EOF
|
||||
<meta name="help_topic:label" content="Test"/>
|
||||
<meta name="help_topic:top_level"/>
|
||||
<meta name="help_topic:related" content="one, two ,three"/>
|
||||
<h2>Test</h2>
|
||||
EOF;
|
||||
|
||||
vfsStream::create([
|
||||
'modules' => [
|
||||
'foo' => [
|
||||
'help_topics' => [
|
||||
'foo.topic.html.twig' => $topic_content,
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$discovery = new HelpTopicDiscovery(['foo' => vfsStream::url('root/modules/foo/help_topics')]);
|
||||
$definition = $discovery->getDefinitions()['foo.topic'];
|
||||
$this->assertEquals('Test', $definition['label']);
|
||||
$this->assertInstanceOf(TranslatableMarkup::class, $definition['label']);
|
||||
$this->assertSame(TRUE, $definition['top_level']);
|
||||
// Each related plugin ID should be trimmed.
|
||||
$this->assertSame(['one', 'two', 'three'], $definition['related']);
|
||||
$this->assertSame('foo', $definition['provider']);
|
||||
$this->assertSame(HelpTopicTwig::class, $definition['class']);
|
||||
$this->assertSame(vfsStream::url('root/modules/foo/help_topics/foo.topic.html.twig'), $definition['_discovered_file_path']);
|
||||
$this->assertSame('foo.topic', $definition['id']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<meta name="help_topic:label" content="XYZ Help Test theme"/>
|
||||
<meta name="help_topic:top_level"/>
|
||||
<meta name="help_topic:related" content="help_topics_test.additional"/>
|
||||
<p>{% trans %}This is a theme provided topic.{% endtrans %}</p>
|
|
@ -0,0 +1,5 @@
|
|||
name: Test Help Topics
|
||||
type: theme
|
||||
description: A theme to test help topics.
|
||||
version: VERSION
|
||||
core: 8.x
|
Loading…
Reference in New Issue