diff --git a/.cspell-project-words.txt b/.cspell-project-words.txt new file mode 100644 index 0000000000000000000000000000000000000000..3ec8f6258e5e6740596b117862a0ce6398e30598 --- /dev/null +++ b/.cspell-project-words.txt @@ -0,0 +1,6 @@ +contenu +rockowitz +selectmenu +spécial +strto +tocs diff --git a/css/toc.tree.css b/css/toc.tree.css index f2928f7ffa28097da330c659ca4eed3ffc32aebc..cdda65502aca75ac5c23f643bc2823eb3239b276 100755 --- a/css/toc.tree.css +++ b/css/toc.tree.css @@ -7,7 +7,7 @@ * Add border to tree when displayed within a node */ .node .toc-tree { - border: 1px solid #ccc; - padding: 0 1em; margin: 1em 0; + padding: 0 1em; + border: 1px solid #ccc; } diff --git a/js/toc.menu.js b/js/toc.menu.js index cff6edc0db3376e6c3343d10ce9a7ce4ba037d83..0f4bddceec2b0f73909b842fbd4d4590350cb4be 100644 --- a/js/toc.menu.js +++ b/js/toc.menu.js @@ -4,19 +4,15 @@ */ (function ($, Drupal) { - - "use strict"; - Drupal.behaviors.tocMenu = { - attach: function (context) { + attach(context) { $('form.toc-menu > select', context).change(function () { - var value = $(this).val(); + const value = this.value; if (value) { - location.hash = value; + window.location.hash = value; } this.selectedIndex = 0; }); - } + }, }; - })(jQuery, Drupal); diff --git a/js/toc_type.js b/js/toc_type.js index 0ff1e0b5201ed5b5a93ea30bf62a7cdc114737e8..1b998c4c5855943e23a719cca92544b5c561483a 100644 --- a/js/toc_type.js +++ b/js/toc_type.js @@ -4,25 +4,37 @@ */ (function ($, Drupal, once) { - - "use strict"; - - Drupal.behaviors.tocTypeOptions = { - attach: function (context) { - $(once('.js-toc-type-options-header-min, .js-toc-type-options-header-max', context)).change(toggleHeaders); - } - }; - toggleHeaders(); - function toggleHeaders() { - var min = $('.js-toc-type-options-header-min').val(); - var max = $('.js-toc-type-options-header-max').val(); + const min = parseInt( + document.querySelector('.js-toc-type-options-header-min')?.value || 1, + 10, + ); + const max = parseInt( + document.querySelector('.js-toc-type-options-header-max')?.value || 6, + 10, + ); - for (var i = 1; i <= 6; i++) { - // Having to use the id instead of $('.js-toc-type-options-header-h' + i). - var $header = $('details[id$="-headers-h' + i + '"]'); - (i >= min && i <= max) ? $header.show() : $header.hide(); + for (let i = 1; i <= 6; i++) { + const $header = $(`details[id$="-headers-h${i}"]`); + if (i >= min && i <= max) { + $header.show(); + } else { + $header.hide(); + } } } + Drupal.behaviors.tocTypeOptions = { + attach(context) { + once( + 'toc-type-header-change', + '.js-toc-type-options-header-min, .js-toc-type-options-header-max', + context, + ).forEach((el) => { + el.addEventListener('change', toggleHeaders); + }); + + toggleHeaders(); + }, + }; })(jQuery, Drupal, once); diff --git a/modules/toc_api_example/toc_api_example.info.yml b/modules/toc_api_example/toc_api_example.info.yml index 00007a77166b30b597e23421ef11486af2a18022..ec98eae78d1366de045250ffa0aaeedeb7c43e9e 100644 --- a/modules/toc_api_example/toc_api_example.info.yml +++ b/modules/toc_api_example/toc_api_example.info.yml @@ -2,6 +2,6 @@ name: 'TOC API example' type: module description: 'Example of a custom implementation of the TOC API that adds a table of contents to specified content types.' package: 'Example modules' -core_version_requirement: ^9.4 || ^10.0 +core_version_requirement: ^9.5 || ^10.2 || ^11.0 dependencies: - toc_api:toc_api diff --git a/modules/toc_api_example/toc_api_example.module b/modules/toc_api_example/toc_api_example.module index eea6f91dae97c47a78f3136c837da5e8927e6f83..31c4211601369f7907903f5424d2891eb43bb88d 100755 --- a/modules/toc_api_example/toc_api_example.module +++ b/modules/toc_api_example/toc_api_example.module @@ -2,24 +2,25 @@ /** * @file - * Example of a custom implementation of the TOC API that adds a table of contents to specified content types. + * Example of a custom implementation of the TOC API. */ -use Drupal\node\NodeInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; +use Drupal\node\NodeInterface; use Drupal\toc_api\Entity\TocType; /** * Implements hook_node_view(). */ -function toc_api_example_node_view(array &$build, NodeInterface $node, EntityViewDisplayInterface $display, $view_mode) { - // Add TOC to 'page' and 'article' content types that are being viewed as a full (page) with a body field. +function toc_api_example_node_view(array &$build, NodeInterface $node, EntityViewDisplayInterface $display, string $view_mode): void { + // Add TOC to 'page' and 'article' content types that are being viewed as a + // full (page) with a body field. if (in_array($node->getType(), ['page', 'article']) && $view_mode == 'full' && isset($build['body'][0])) { // Get the completely render (and filtered) body value. $body = (string) \Drupal::service('renderer')->render($build['body'][0]); // Get 'default' TOC type options. - /** @var \Drupal\toc_api\TocTypeInterface $toc_type */ + /** @var \Drupal\toc_api\TocTypeInterface|null $toc_type */ $toc_type = TocType::load('default'); $options = ($toc_type) ? $toc_type->getOptions() : []; diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000000000000000000000000000000000000..ec1ca4cd641a5db26ba483b9395e941671ac7bc1 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + level: 6 + ignoreErrors: + - identifier: missingType.iterableValue diff --git a/src/Entity/TocType.php b/src/Entity/TocType.php index 4f143724d32920637279a4d8c4f6754fd9147c8d..db59b0dedd86cfd46540985993763b4c8dc3bad1 100644 --- a/src/Entity/TocType.php +++ b/src/Entity/TocType.php @@ -64,9 +64,9 @@ class TocType extends ConfigEntityBase implements TocTypeInterface { /** * The TOC type options. * - * @var array + * @var array|null */ - protected $options; + protected $options = NULL; /** * {@inheritdoc} diff --git a/src/Plugin/Block/TocBlockBase.php b/src/Plugin/Block/TocBlockBase.php index 9b06dc92b810a8759c202db9a04d716a462a6097..b13c58e8507407c754474d7ba9ffa2740cf7dc16 100755 --- a/src/Plugin/Block/TocBlockBase.php +++ b/src/Plugin/Block/TocBlockBase.php @@ -3,15 +3,19 @@ namespace Drupal\toc_api\Plugin\Block; use Drupal\Core\Access\AccessResult; +use Drupal\Core\Access\AccessResultInterface; use Drupal\Core\Block\BlockBase; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\node\NodeInterface; +use Drupal\toc_api\TocInterface; +use Drupal\toc_api\TocManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Provides a base TOC block which displays the current TOC module's TOC in a - * block. + * Provides a base block which displays the current TOC module's TOC in a block. */ abstract class TocBlockBase extends BlockBase implements ContainerFactoryPluginInterface { @@ -20,7 +24,21 @@ abstract class TocBlockBase extends BlockBase implements ContainerFactoryPluginI * * @var \Drupal\Core\Routing\RouteMatchInterface */ - protected $routeMatch; + protected RouteMatchInterface $routeMatch; + + /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected EntityTypeManagerInterface $entityTypeManager; + + /** + * The TOC manager. + * + * @var \Drupal\toc_api\TocManagerInterface + */ + protected TocManagerInterface $tocManager; /** * Creates a LocalActionsBlock instance. @@ -33,34 +51,38 @@ abstract class TocBlockBase extends BlockBase implements ContainerFactoryPluginI * The plugin implementation definition. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match * The route match. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + * @param \Drupal\toc_api\TocManagerInterface $toc_manager + * The TOC manager. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) { + final public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match, EntityTypeManagerInterface $entity_type_manager, TocManagerInterface $toc_manager) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->routeMatch = $route_match; + $this->entityTypeManager = $entity_type_manager; + $this->tocManager = $toc_manager; } /** * {@inheritdoc} */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { return new static( $configuration, $plugin_id, $plugin_definition, - $container->get('current_route_match') + $container->get('current_route_match'), + $container->get('entity_type.manager'), + $container->get('toc_api.manager') ); } /** * {@inheritdoc} */ - public function build() { + public function build(): array { $toc = $this->getCurrentToc(); - if (empty($toc)) { - return []; - } - // Build the TOC. $options = $toc->getOptions(); $build = [ @@ -82,12 +104,9 @@ abstract class TocBlockBase extends BlockBase implements ContainerFactoryPluginI * @return \Drupal\toc_api\TocInterface * A TOC object. */ - protected function getCurrentToc() { - /** @var \Drupal\toc_api\TocManagerInterface $toc_manager */ - $toc_manager = \Drupal::service('toc_api.manager'); - + protected function getCurrentToc(): TocInterface { // Get the new TOC instance using the module name. - return $toc_manager->getToc($this->getCurrentTocId()); + return $this->tocManager->getToc($this->getCurrentTocId()); } /** @@ -99,21 +118,21 @@ abstract class TocBlockBase extends BlockBase implements ContainerFactoryPluginI * @return string * The current TOC block's plugin ID. */ - protected function getCurrentTocId() { + protected function getCurrentTocId(): string { return $this->pluginId; } /** * {@inheritdoc} */ - public function getCacheContexts() { + public function getCacheContexts(): array { return ['route']; } /** * {@inheritdoc} */ - public function getCacheTags() { + public function getCacheTags(): array { $node = $this->getCurrentNode(); return ($node) ? ['node:' . $node->id()] : []; } @@ -122,14 +141,15 @@ abstract class TocBlockBase extends BlockBase implements ContainerFactoryPluginI * Load the node associated with the current request. * * @return \Drupal\node\NodeInterface|null - * A node entity, or NULL if no node is not found. + * A node entity, or NULL if no node is found. */ - protected function getCurrentNode() { + protected function getCurrentNode(): ?NodeInterface { switch ($this->routeMatch->getRouteName()) { // Look at the request's node revision. case 'node.revision_show': - return node_revision_load($this->routeMatch - ->getParameter('node_revision')); + $node_storage = $this->entityTypeManager->getStorage('node'); + $node = $node_storage->loadRevision($this->routeMatch->getParameter('node_revision')); + return ($node instanceof NodeInterface) ? $node : NULL; // Look at the request's node preview. case 'entity.node.preview': @@ -146,14 +166,10 @@ abstract class TocBlockBase extends BlockBase implements ContainerFactoryPluginI /** * {@inheritdoc} */ - protected function blockAccess(AccountInterface $account) { - $this->getCurrentTocId(); - /** @var \Drupal\toc_api\TocManagerInterface $toc_manager */ - $toc_manager = \Drupal::service('toc_api.manager'); - + protected function blockAccess(AccountInterface $account): AccessResultInterface { // Get the new TOC instance and see if it is visible and should be // displayed in a block. - $toc = $toc_manager->getToc($this->getCurrentTocId()); + $toc = $this->tocManager->getToc($this->getCurrentTocId()); if (!$toc || !$toc->isVisible() || !$toc->isBlock()) { return AccessResult::forbidden(); diff --git a/src/Toc.php b/src/Toc.php index 4a0d1e7a9ba1804d8c4d4a8056fdd3e1aa7dcce7..3c7256410d592139b667d9dae6798e8909ab4281 100644 --- a/src/Toc.php +++ b/src/Toc.php @@ -72,9 +72,9 @@ class Toc implements TocInterface { /** * The number of root headers found in the content. * - * @var int + * @var int|null */ - protected $headerCount; + protected $headerCount = NULL; /** * The source content with unique header ids. @@ -100,9 +100,9 @@ class Toc implements TocInterface { /** * The toc represent as a tree. * - * @var array + * @var array|null */ - protected $tree; + protected $tree = NULL; /** * Existing in the source content IDs. @@ -192,16 +192,12 @@ class Toc implements TocInterface { $this->allowedTags = $this->formatter()->convertAllowedTagsToArray($this->options['header_allowed_tags']); $this->initialize(); - - // DEBUG: - // dsm($this->getIndex()); - // dsm($this->getTree()); } /** * Initializes the table of content index and ensure unique header ids. */ - protected function initialize() { + protected function initialize(): void { $this->index = []; // Setup an empty array of keys to track the index's keys. @@ -340,7 +336,7 @@ class Toc implements TocInterface { '#markup' => $header_html, '#allowed_tags' => $this->getAllowedTags(), ], - 'url' => Url::fromRoute('<none>', NULL, [ + 'url' => Url::fromRoute('<none>', [], [ 'fragment' => $header_id, ]), ]; @@ -356,6 +352,7 @@ class Toc implements TocInterface { * The TOC formatter */ protected function formatter() { + // @phpstan-ignore-next-line return \Drupal::service('toc_api.formatter'); } @@ -398,7 +395,8 @@ class Toc implements TocInterface { * {@inheritdoc} */ public function getHeaderCount() { - if (!isset($this->headerCount)) { + if ($this->headerCount === NULL) { + $this->headerCount = 0; foreach ($this->index as $item) { if (empty($item['parent'])) { $this->headerCount++; @@ -419,7 +417,7 @@ class Toc implements TocInterface { * {@inheritdoc} */ public function getTree() { - if (!isset($this->tree)) { + if ($this->tree === NULL) { $this->tree = [ 'title' => $this->options['title'], ]; @@ -445,7 +443,7 @@ class Toc implements TocInterface { * @param array $children * An array of keys to be associative to the parent header item. */ - protected function buildTree(array &$item, array $children) { + protected function buildTree(array &$item, array $children): void { $item['below_type'] = ''; $item['below'] = []; foreach ($children as $key) { @@ -498,8 +496,6 @@ class Toc implements TocInterface { * * @param \DOMDocument $dom * DOM object. - * - * @return void */ protected function collectExistingIds(\DOMDocument $dom): void { $xpath = new \DOMXPath($dom); @@ -518,8 +514,6 @@ class Toc implements TocInterface { * * @param \DOMDocument $dom * DOM object. - * - * @return void */ protected function useIdsOnly(\DOMDocument $dom): void { $xpath = new \DOMXPath($dom); diff --git a/src/TocBuilder.php b/src/TocBuilder.php index 4050680dcacd777c65d7b41340e1467bbfa6a8e7..5556512894f35c2adc9e2460153288e26abfc411 100644 --- a/src/TocBuilder.php +++ b/src/TocBuilder.php @@ -4,11 +4,14 @@ namespace Drupal\toc_api; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Xss; +use Drupal\Core\Render\Markup; use Drupal\Core\Render\RendererInterface; /** - * Defines a service that builds and renders a table of contents and update an - * HTML document's headers. + * Defines a service to build TOC. + * + * This service builds and renders a table of contents and update an HTML + * document's headers. */ class TocBuilder implements TocBuilderInterface { @@ -150,10 +153,13 @@ class TocBuilder implements TocBuilderInterface { */ public function renderToc(TocInterface $toc) { if (!$toc->isVisible()) { - return ''; + return Markup::create(''); } $build = $this->buildToc($toc); + if (empty($build)) { + return Markup::create(''); + } return $this->renderer->render($build); } diff --git a/src/TocBuilderInterface.php b/src/TocBuilderInterface.php index fdd0c1d88da47d13f4898b11f35bb0db6f5396bd..cfc02bd946b6c28c83fcde6c68c5a73718253f8b 100644 --- a/src/TocBuilderInterface.php +++ b/src/TocBuilderInterface.php @@ -26,8 +26,8 @@ interface TocBuilderInterface { * A TOC object. * * @return array - * A render array containing the table of content's body content with bookmarked, typed, and custom - * headers with back to top links. + * A render array containing the table of content's body content with + * bookmarked, typed, and custom headers with back to top links. */ public function buildContent(TocInterface $toc); diff --git a/src/TocFormatter.php b/src/TocFormatter.php index 3a30b302dce0d47ddd680c565589271e8625463a..1550948c0d694e5231eb1db8a27f7cff637b4367 100644 --- a/src/TocFormatter.php +++ b/src/TocFormatter.php @@ -3,7 +3,7 @@ namespace Drupal\toc_api; /** - * Defines a service for formatting a table of content's headers, numbering, and ids.. + * Defines a service for formatting a TOC headers, numbering, and ids. */ class TocFormatter implements TocFormatterInterface { @@ -12,7 +12,90 @@ class TocFormatter implements TocFormatterInterface { */ public function convertStringToId($text) { // Replace accents with their counterparts. - $text = strtr($text, ['Š' => 'S', 'š' => 's', 'Đ' => 'Dj', 'đ' => 'dj', 'Ž' => 'Z', 'ž' => 'z', 'Č' => 'C', 'č' => 'c', 'Ć' => 'C', 'ć' => 'c', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'A', 'Ç' => 'C', 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ý' => 'Y', 'Þ' => 'B', 'ß' => 'Ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'a', 'ç' => 'c', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'o', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ý' => 'y', 'þ' => 'b', 'ÿ' => 'y', 'Ŕ' => 'R', 'ŕ' => 'r', 'Ā' => 'A', 'Ē' => 'E', 'Ī' => 'I', 'Ō' => 'O', 'Ū' => 'U', 'ā' => 'a', 'ē' => 'e', 'ī' => 'i', 'ō' => 'o', 'ū' => 'u']); + $text = strtr($text, [ + 'Š' => 'S', + 'š' => 's', + 'Đ' => 'Dj', + 'đ' => 'dj', + 'Ž' => 'Z', + 'ž' => 'z', + 'Č' => 'C', + 'č' => 'c', + 'Ć' => 'C', + 'ć' => 'c', + 'À' => 'A', + 'Á' => 'A', + 'Â' => 'A', + 'Ã' => 'A', + 'Ä' => 'A', + 'Å' => 'A', + 'Æ' => 'A', + 'Ç' => 'C', + 'È' => 'E', + 'É' => 'E', + 'Ê' => 'E', + 'Ë' => 'E', + 'Ì' => 'I', + 'Í' => 'I', + 'Î' => 'I', + 'Ï' => 'I', + 'Ñ' => 'N', + 'Ò' => 'O', + 'Ó' => 'O', + 'Ô' => 'O', + 'Õ' => 'O', + 'Ö' => 'O', + 'Ø' => 'O', + 'Ù' => 'U', + 'Ú' => 'U', + 'Û' => 'U', + 'Ü' => 'U', + 'Ý' => 'Y', + 'Þ' => 'B', + 'ß' => 'Ss', + 'à' => 'a', + 'á' => 'a', + 'â' => 'a', + 'ã' => 'a', + 'ä' => 'a', + 'å' => 'a', + 'æ' => 'a', + 'ç' => 'c', + 'è' => 'e', + 'é' => 'e', + 'ê' => 'e', + 'ë' => 'e', + 'ì' => 'i', + 'í' => 'i', + 'î' => 'i', + 'ï' => 'i', + 'ð' => 'o', + 'ñ' => 'n', + 'ò' => 'o', + 'ó' => 'o', + 'ô' => 'o', + 'õ' => 'o', + 'ö' => 'o', + 'ø' => 'o', + 'ù' => 'u', + 'ú' => 'u', + 'û' => 'u', + 'ý' => 'y', + 'þ' => 'b', + 'ÿ' => 'y', + 'Ŕ' => 'R', + 'ŕ' => 'r', + 'Ā' => 'A', + 'Ē' => 'E', + 'Ī' => 'I', + 'Ō' => 'O', + 'Ū' => 'U', + 'ā' => 'a', + 'ē' => 'e', + 'ī' => 'i', + 'ō' => 'o', + 'ū' => 'u', + ]); // Lowercase. $text = strtolower($text); @@ -40,6 +123,10 @@ class TocFormatter implements TocFormatterInterface { * {@inheritdoc} */ public function convertNumberToListTypeValue($number, $type) { + if ($type === NULL) { + $type = ''; + } + $case_func = NULL; // Check if type should upper or lower cased. if (preg_match('/^(upper|lower)-(.+)$/', (string) $type, $match)) { @@ -72,7 +159,21 @@ class TocFormatter implements TocFormatterInterface { * {@inheritdoc} */ public function convertNumberToRomanNumeral($number) { - $roman_numerals = ['M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, 'C' => 100, 'XC' => 90, 'L' => 50, 'XL' => 40, 'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1]; + $roman_numerals = [ + 'M' => 1000, + 'CM' => 900, + 'D' => 500, + 'CD' => 400, + 'C' => 100, + 'XC' => 90, + 'L' => 50, + 'XL' => 40, + 'X' => 10, + 'IX' => 9, + 'V' => 5, + 'IV' => 4, + 'I' => 1, + ]; $result = ''; foreach ($roman_numerals as $roman_numeral => $roman_number) { $matches = intval($number / $roman_number); @@ -86,7 +187,34 @@ class TocFormatter implements TocFormatterInterface { * {@inheritdoc} */ public function convertNumberToLetter($number) { - static $letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; + static $letters = [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + ]; return $letters[(($number - 1) % 26)]; } diff --git a/src/TocFormatterInterface.php b/src/TocFormatterInterface.php index f2757816f41d273e9b34bbee8a4e6d94fba08f19..6387b96bd2d8bade48e2eb667afbb400b161b1b0 100644 --- a/src/TocFormatterInterface.php +++ b/src/TocFormatterInterface.php @@ -38,7 +38,7 @@ interface TocFormatterInterface { * * @param int $number * A number. - * @param string $type + * @param string|null $type * The HTML5 list-style-type. * * @return string diff --git a/src/TocInterface.php b/src/TocInterface.php index a16130c7baee1e1e2667678c02653f8f83b936c4..95f34bb6af356fe50945b9a031c359544e024d19 100644 --- a/src/TocInterface.php +++ b/src/TocInterface.php @@ -50,7 +50,9 @@ interface TocInterface { /** * Returns a hierarchical array of headers. * - * @return array + * @todo update the description text. + * + * @return int * An hierarchical array of headers. */ public function getHeaderCount(); diff --git a/src/TocManager.php b/src/TocManager.php index cdc7396117cd598c6dcc4fe3116572013da2e5fc..adb927f72eadc86311d7f813ed3b765f626c9bda 100644 --- a/src/TocManager.php +++ b/src/TocManager.php @@ -38,10 +38,11 @@ class TocManager implements TocManagerInterface { /** * {@inheritdoc} */ - public function create($id, $source, array $options = []) { + public function create(string $id, string $source, array $options = []): TocInterface { // Merge default TOC type options with passed options. - /** @var \Drupal\toc_api\TocTypeInterface $default_toc */ - if ($default_toc = TocType::load('default')) { + /** @var \Drupal\toc_api\TocTypeInterface|null $default_toc */ + $default_toc = TocType::load('default'); + if ($default_toc !== NULL) { $options = NestedArray::mergeDeep($default_toc->getOptions(), $options); } @@ -53,14 +54,14 @@ class TocManager implements TocManagerInterface { /** * {@inheritdoc} */ - public function getToc($id) { + public function getToc(string $id): ?TocInterface { return (isset($this->tocs[$id])) ? $this->tocs[$id] : NULL; } /** * {@inheritdoc} */ - public function reset($id = NULL) { + public function reset(?string $id = NULL): void { if ($id === NULL) { $this->tocs = []; } diff --git a/src/TocManagerInterface.php b/src/TocManagerInterface.php index 901048db76fbeab31afcdf92b76ad1d84e133bf5..9bbd8fa6cb9f1ec4d1e932816b285cd4a5893b31 100644 --- a/src/TocManagerInterface.php +++ b/src/TocManagerInterface.php @@ -23,7 +23,7 @@ interface TocManagerInterface { * @return \Drupal\toc_api\TocInterface * A new TOC object. */ - public function create($id, $source, array $options = []); + public function create(string $id, string $source, array $options = []): TocInterface; /** * Get the current TOC instance. @@ -32,10 +32,10 @@ interface TocManagerInterface { * ID used to track the TOC object's instance. Typically, the ID can be * the TOC implementation's module name. * - * @return \Drupal\toc_api\TocInterface + * @return \Drupal\toc_api\TocInterface|null * The current TOC instance. */ - public function getToc($id); + public function getToc(string $id): ?TocInterface; /** * Reset the current TOC instance. @@ -44,6 +44,6 @@ interface TocManagerInterface { * ID used to track the TOC object's instance. Typically, the ID can be * the TOC implementation's module name. */ - public function reset($id = NULL); + public function reset(?string $id = NULL): void; } diff --git a/src/TocTypeForm.php b/src/TocTypeForm.php index 4dbe5634dd3440f8999a673da397138da5782e9d..14a30f095324b533e35359586cd575474ab71d3f 100644 --- a/src/TocTypeForm.php +++ b/src/TocTypeForm.php @@ -48,13 +48,14 @@ class TocTypeForm extends EntityForm { * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity query factory. - * * @param \Drupal\Core\Theme\Registry $theme_registry * The theme registry. * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager * The theme manager. + * @param \Drupal\Core\Theme\ThemeInitializationInterface $theme_initialization + * The theme initialization logic. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, Registry $theme_registry, ThemeManagerInterface $theme_manager, ThemeInitializationInterface $theme_initialization) { + final public function __construct(EntityTypeManagerInterface $entity_type_manager, Registry $theme_registry, ThemeManagerInterface $theme_manager, ThemeInitializationInterface $theme_initialization) { $this->entityTypeManager = $entity_type_manager; $this->themeRegistry = $theme_registry; $this->themeManager = $theme_manager; @@ -64,7 +65,7 @@ class TocTypeForm extends EntityForm { /** * {@inheritdoc} */ - public static function create(ContainerInterface $container) { + public static function create(ContainerInterface $container): static { return new static( $container->get('entity_type.manager'), $container->get('theme.registry'), @@ -78,6 +79,9 @@ class TocTypeForm extends EntityForm { */ public function buildForm(array $form, FormStateInterface $form_state) { $toc_type = $this->entity; + if (!($toc_type instanceof TocTypeInterface)) { + return $form; + } $options = $toc_type->getOptions(); // An associative array of HTML header tags keyed by level. @@ -309,8 +313,7 @@ class TocTypeForm extends EntityForm { $form['options']['numbering']['headers'][$header_tag]['custom'] = [ '#title' => $this->t('Customize @tag numbering', [ '@tag' => $header_tag, - ] - ), + ]), '#type' => 'checkbox', '#default_value' => $header_options['custom'], '#attributes' => ['class' => ["js-toc-type-options-headers-$header_tag-custom"]], @@ -367,7 +370,7 @@ class TocTypeForm extends EntityForm { /** * {@inheritdoc} */ - public function submitForm(array &$form, FormStateInterface $form_state) { + public function submitForm(array &$form, FormStateInterface $form_state): void { $values = $form_state->getValues(); $options = $values['general'] + $values['header'] + $values['top'] + $values['numbering']; @@ -405,6 +408,7 @@ class TocTypeForm extends EntityForm { $this->messenger()->addMessage($this->t('Table of contents type %label saved.', ['%label' => $toc_type->label()])); $form_state->setRedirect('entity.toc_type.collection'); + return parent::save($form, $form_state); } /** @@ -418,13 +422,12 @@ class TocTypeForm extends EntityForm { */ public function exists($id) { return (bool) $this->entityTypeManager - ->get('toc_type') - ->condition('id', $id) - ->execute(); + ->getStorage('toc_type') + ->load($id); } /** - * Get TOC templates from the theme registry for the default theme as an associative array of options. + * Get TOC templates from the theme registry for the default theme. * * @return array * TOC templates as an associative array of options. diff --git a/src/TocTypeListBuilder.php b/src/TocTypeListBuilder.php index e95ee205c878d7c6a33b47a54aebbb0e460d6dc1..fc9976ceb2fec3ee49f563ef9dfd4f58521ccbb2 100644 --- a/src/TocTypeListBuilder.php +++ b/src/TocTypeListBuilder.php @@ -26,6 +26,9 @@ class TocTypeListBuilder extends ConfigEntityListBuilder { * {@inheritdoc} */ public function buildRow(EntityInterface $entity) { + if (!($entity instanceof TocTypeInterface)) { + return parent::buildRow($entity); + } $options = $entity->getOptions(); $row['label'] = $entity->label(); diff --git a/templates/toc-menu.html.twig b/templates/toc-menu.html.twig index 65b970a39c356e70e5d6e341e8d0e8f928b30969..92646f0a9c0f3283ef424a4ecae5ff3394055723 100644 --- a/templates/toc-menu.html.twig +++ b/templates/toc-menu.html.twig @@ -9,7 +9,7 @@ * - index: List of header items. Each header item contains: * - prefix: The header option prefix. Either indentation or the complete path. * - title: The header title. - * - url: The header fragrment (ie hash) URL, instance of \Drupal\Core\Url. + * - url: The header fragment (ie hash) URL, instance of \Drupal\Core\Url. * * @ingroup themeable */ diff --git a/templates/toc-tree.html.twig b/templates/toc-tree.html.twig index e582d62423087f9c06382d6c39f685b4aacef703..09d591474e009851f8d9cebedbde6bd34278400e 100644 --- a/templates/toc-tree.html.twig +++ b/templates/toc-tree.html.twig @@ -13,7 +13,7 @@ * - attributes: HTML attributes for the table of contents or list item. * - below: The table of contents child items. * - title: The table of contents or header title. - * - url: The header fragrment (ie hash) URL, instance of \Drupal\Core\Url. + * - url: The header fragment (ie hash) URL, instance of \Drupal\Core\Url. * * @ingroup themeable */ diff --git a/tests/src/Kernel/TocBuilderTest.php b/tests/src/Kernel/TocBuilderTest.php index cdf6bf01f0e093e33b3e32208b119ddc0a58abab..7cf81dea567279d718f4df05ab2a70c0b1dae7dc 100644 --- a/tests/src/Kernel/TocBuilderTest.php +++ b/tests/src/Kernel/TocBuilderTest.php @@ -20,6 +20,8 @@ class TocBuilderTest extends KernelTestBase { protected static $modules = ['toc_api']; /** + * The TOC manager. + * * @var \Drupal\toc_api\TocManagerInterface */ protected TocManagerInterface $manager; @@ -40,7 +42,7 @@ class TocBuilderTest extends KernelTestBase { * * @todo Convert to unit test and include in TocTest, once that is fixed. */ - public function testDuplicateIds() { + public function testDuplicateIds(): void { $toc_source = <<<HTML <p><a name="documents"></a></p> <h2>Documents</h2> diff --git a/tests/src/Kernel/TocManagerTest.php b/tests/src/Kernel/TocManagerTest.php index 0f30ed7119f990e36fa3fc0032f2691824251546..6c1f8ce912135fae6ff4d864053a6c1a22789d13 100755 --- a/tests/src/Kernel/TocManagerTest.php +++ b/tests/src/Kernel/TocManagerTest.php @@ -15,21 +15,41 @@ class TocManagerTest extends KernelTestBase { /** * Modules to enable. * - * @var array + * @var string[] */ - public static $modules = ['system', 'toc_api']; + protected static $modules = ['system', 'toc_api']; + + /** + * The default theme. + * + * @var string + */ + protected $defaultTheme = 'stark'; + + /** + * The content. + * + * @var string + */ + protected string $contentToc; /** * Empty test to avoid error about there being no tests. * * Remove when re-enabling disabledTestRender(). + * + * @return void + * No return value. */ - public function testRender() { - $this->assertNull(NULL); + public function testRender(): void { + $this->markTestSkipped('Temporarily skipping until real tests are added.'); } /** * Tests TOC rendering. Temporarily disabled. + * + * @return mixed + * Could return full content (toc tables) or NULL */ public function disabledTestRender() { /** @var \Drupal\Core\Render\RendererInterface $renderer */ @@ -42,16 +62,16 @@ class TocManagerTest extends KernelTestBase { // Create render toc and content functions with context. $render_toc = function ($toc) use ($toc_builder, $renderer) { return $renderer->executeInRenderContext(new RenderContext(), function () use ($toc_builder, $toc) { - $content = $toc_builder->renderToc($toc); - $this->content = $content; - return $content; + $contentToc = $toc_builder->renderToc($toc); + $this->content = $contentToc; + return $contentToc; }); }; $render_content = function ($toc) use ($toc_builder, $renderer) { return $renderer->executeInRenderContext(new RenderContext(), function () use ($toc_builder, $toc) { - $content = $toc_builder->renderContent($toc); - $this->content = $content; - return $content; + $contentToc = $toc_builder->renderContent($toc); + $this->content = $contentToc; + return $contentToc; }); }; @@ -65,14 +85,14 @@ class TocManagerTest extends KernelTestBase { // Check default TOC options. $toc = $toc_manager->create('toc_test', '<h2>header 2</h2><h3>header 3</h3><h4>header 4</h4><h4>header 4</h4><h2>header 2</h2>', []); $render_toc($toc); - $this->assertNotFalse($this->cssSelect('.toc-desktop.toc.toc-tree'), 'Toc tree exists'); + $this->assertNotEmpty($this->cssSelect('.toc-desktop.toc.toc-tree'), 'Toc tree exists'); $this->assertRaw('<h3>Table of Contents</h3>'); $this->assertRaw('<a href="#header-2">header 2</a>'); $this->assertRaw('<a href="#header-3">header 3</a>'); $this->assertRaw('<a href="#header-4">header 4</a>'); $this->assertRaw('<a href="#header-4-01">header 4</a>'); $this->assertRaw('<a href="#header-2-01">header 2</a>'); - $this->assertNotFalse($this->cssSelect('.toc-mobile.toc.toc-menu'), 'Toc menu exists'); + $this->assertNotEmpty($this->cssSelect('.toc-mobile.toc.toc-menu'), 'Toc menu exists'); $this->assertRaw('<option value="">Table of Contents</option>'); $this->assertRaw('<option value="#header-2">1) header 2</option>'); $this->assertRaw('<option value="#header-3">1.1) header 3</option>'); @@ -80,8 +100,8 @@ class TocManagerTest extends KernelTestBase { $this->assertRaw('<option value="#header-4-01">1.1.2) header 4</option>'); $this->assertRaw('<option value="#header-2-01">2) header 2</option>'); $render_content($toc); - $this->assertPattern('|<a href="#top" class="back-to-top">Back to top</a>\s+<h2|s', 'Back to top before h2'); - $this->assertPattern('|<a href="#top" class="back-to-top">Back to top</a>\s+$|s', 'Back to top at the bottom'); + $this->assertMatchesRegularExpression('|<a href="#top" class="back-to-top">Back to top</a>\s+<h2|s', 'Back to top before h2'); + $this->assertMatchesRegularExpression('|<a href="#top" class="back-to-top">Back to top</a>\s+$|s', 'Back to top at the bottom'); $this->assertRaw('<a href="#top" class="back-to-top">Back to top</a>'); $this->assertRaw('<h2 id="header-2"><span>1) </span>header 2</h2>'); $this->assertRaw('<h3 id="header-3"><span>1.1) </span>header 3</h3>'); @@ -99,7 +119,11 @@ class TocManagerTest extends KernelTestBase { ]; $toc = $toc_manager->create('toc_test', '<h2>header 2</h2><h3>header 3</h3><h4>header 4</h4><h4>header 4</h4><h2>header 2</h2>', $options); $render_toc($toc); - $this->assertPattern('|<ol class="decimal">.*?<ol class="lower-alpha">.*?<ol class="lower-roman">|s'); + $this->assertMatchesRegularExpression('|<ol class="decimal">.*?<ol class="lower-alpha">.*?<ol class="lower-roman">|s', + // Contenu à tester. + $this->content, + 'Failed asserting that the HTML structure matches the expected TOC format.' + ); $render_content($toc); $this->assertRaw('<h2 id="header-2"><span>1) </span>header 2</h2>'); $this->assertRaw('<h3 id="header-3"><span>1.a) </span>header 3</h3>'); @@ -125,11 +149,12 @@ class TocManagerTest extends KernelTestBase { ]; $toc = $toc_manager->create('toc_test', '<h2>header 2</h2><h3>header 3</h3><h4>header 4</h4><h4>header 4</h4><h2>header 2</h2>', $options); $render_content($toc); - $this->assertNoPattern('|<a href="#top" class="back-to-top">TOP</a>\s+<h2|s', 'No back to top before h2'); - $this->assertNoPattern('|<a href="#top" class="back-to-top">TOP</a>\s+$|s', 'No back to top at the bottom'); - $this->assertPattern('|<a href="#top" class="back-to-top">TOP</a>\s+<h3 id="header-3"><span>1.1\) </span>header 3</h3>|s', 'Back to top before h3'); - $this->assertPattern('|<a href="#top" class="back-to-top">TOP</a>\s+<h4 id="header-4"><span>1.1.1\) </span>header 4</h4>|s', 'Back to top before first h4'); - $this->assertPattern('|<a href="#top" class="back-to-top">TOP</a>\s+<h4 id="header-4-01"><span>1.1.2\) </span>header 4</h4>|s', 'Back to top before second h4'); + + $this->assertNoPatternLocal('|<a href="#top" class="back-to-top">TOP</a>\s+<h2|s', 'No back to top before h2'); + $this->assertNoPatternLocal('|<a href="#top" class="back-to-top">TOP</a>\s+$|s', 'No back to top at the bottom'); + $this->assertMatchesRegularExpression('|<a href="#top" class="back-to-top">TOP</a>\s+<h3 id="header-3"><span>1.1\) </span>header 3</h3>|s', 'Back to top before h3'); + $this->assertMatchesRegularExpression('|<a href="#top" class="back-to-top">TOP</a>\s+<h4 id="header-4"><span>1.1.1\) </span>header 4</h4>|s', 'Back to top before first h4'); + $this->assertMatchesRegularExpression('|<a href="#top" class="back-to-top">TOP</a>\s+<h4 id="header-4-01"><span>1.1.2\) </span>header 4</h4>|s', 'Back to top before second h4'); // Check list style type = 'none' and menu indent. $options = [ @@ -140,7 +165,7 @@ class TocManagerTest extends KernelTestBase { $this->assertRaw('<ol class="none">'); $render_content($toc); - // Check unorder list when type = FALSE. + // Check unordered list when type = FALSE. $options = [ 'number_path' => FALSE, 'default' => [ @@ -162,4 +187,25 @@ class TocManagerTest extends KernelTestBase { $this->assertRaw('<h4 id="header-4-01">header 4</h4>'); } + /** + * Wrapper to handle assertRaw equivalent in PHPUnit. + */ + protected function assertRaw(mixed $needle, mixed $message = ''): void { + $this->assertStringContainsString($needle, $this->content, $message); + } + + /** + * Wrapper to handle assertNoRaw equivalent in PHPUnit. + */ + protected function assertNoRaw(mixed $needle, mixed $message = ''): void { + $this->assertStringNotContainsString($needle, $this->content, $message); + } + + /** + * Wrapper to handle assertNoPattern equivalent in PHPUnit. + */ + protected function assertNoPatternLocal(mixed $pattern, mixed $message = ''): void { + $this->assertDoesNotMatchRegularExpression($pattern, $this->content, $message); + } + } diff --git a/tests/src/Unit/TocFormatterTest.php b/tests/src/Unit/TocFormatterTest.php index 7ddfab24870360c9a37ddc28fd6cdd3cac617493..c5bbac2268d474b3747e2dd04ba361c1bc11c39f 100755 --- a/tests/src/Unit/TocFormatterTest.php +++ b/tests/src/Unit/TocFormatterTest.php @@ -31,7 +31,7 @@ class TocFormatterTest extends UnitTestCase { } /** - * Tests converting string to valid HTML id with TocFormatter::convertStringToId(). + * Tests converting string to valid HTML id. * * @param string $string * The string to run through $this->formatter->convertStringToId(). @@ -42,7 +42,7 @@ class TocFormatterTest extends UnitTestCase { * * @dataProvider providerConvertStringToId */ - public function testConvertStringToId($string, $expected) { + public function testConvertStringToId($string, $expected): void { $result = $this->formatter->convertStringToId($string); $this->assertEquals($expected, $result); } @@ -52,7 +52,7 @@ class TocFormatterTest extends UnitTestCase { * * @see testConvertStringToId() */ - public function providerConvertStringToId() { + public static function providerConvertStringToId(): array { $tests[] = ['One', 'one']; $tests[] = ['One two', 'one-two']; $tests[] = ['One ! two', 'one-two']; @@ -62,7 +62,7 @@ class TocFormatterTest extends UnitTestCase { } /** - * Tests converting number to list style type with TocFormatter::convertNumberToListTypeValue(). + * Tests converting number to list style type. * * @param int $number * The number to run through TocFormatter::convertNumberToListTypeValue(). @@ -75,7 +75,7 @@ class TocFormatterTest extends UnitTestCase { * * @dataProvider providerConvertNumberToListTypeValue */ - public function testConvertNumberToListTypeValue($number, $type, $expected) { + public function testConvertNumberToListTypeValue($number, $type, $expected): void { $result = $this->formatter->convertNumberToListTypeValue($number, $type); $this->assertEquals($expected, $result); } @@ -85,7 +85,7 @@ class TocFormatterTest extends UnitTestCase { * * @see testConvertNumberToListTypeValue() */ - public function providerConvertNumberToListTypeValue() { + public static function providerConvertNumberToListTypeValue(): array { $tests[] = [1, NULL, 1]; $tests[] = [1, 'decimal', '1']; $tests[] = [1, 'random', '1']; @@ -108,7 +108,7 @@ class TocFormatterTest extends UnitTestCase { } /** - * Tests converting header keys to list style type values with TocFormatter::convertHeaderKeysToValues(). + * Tests converting header keys to list style type values. * * @param array $keys * The array to run through TocFormatter::convertHeaderKeysToValues(). @@ -121,7 +121,7 @@ class TocFormatterTest extends UnitTestCase { * * @dataProvider providerConvertHeaderKeysToValues */ - public function testConvertHeaderKeysToValues(array $keys, array $options, $expected) { + public function testConvertHeaderKeysToValues(array $keys, array $options, $expected): void { $result = $this->formatter->convertHeaderKeysToValues($keys, $options); $this->assertEquals($expected, $result); } @@ -131,7 +131,7 @@ class TocFormatterTest extends UnitTestCase { * * @see testConvertHeaderKeysToValues() */ - public function providerConvertHeaderKeysToValues() { + public static function providerConvertHeaderKeysToValues(): array { $options = [ 'number_path_truncate' => TRUE, 'headers' => [ @@ -145,7 +145,11 @@ class TocFormatterTest extends UnitTestCase { $tests[] = [['h1' => 2, 'h2' => 2, 'h3' => 0], $options, ['h1' => '2', 'h2' => 'b']]; $tests[] = [['h1' => 2, 'h2' => 2, 'h3' => 0, 'h4' => 0], $options, ['h1' => '2', 'h2' => 'b']]; $tests[] = [['h1' => 2, 'h2' => 0, 'h3' => 2], $options, ['h1' => '2', 'h2' => '0', 'h3' => 'ii']]; - $tests[] = [['h1' => 2, 'h2' => 2, 'h3' => 0], ['number_path_truncate' => FALSE] + $options, ['h1' => '2', 'h2' => 'b', 'h3' => '0']]; + $tests[] = [ + ['h1' => 2, 'h2' => 2, 'h3' => 0], + ['number_path_truncate' => FALSE] + $options, + ['h1' => '2', 'h2' => 'b', 'h3' => '0'], + ]; return $tests; } diff --git a/tests/src/Unit/TocTest.php b/tests/src/Unit/TocTest.php index 14f3fa0379cfb4ecb5c4d7d6e8850190656e229f..8de7592d264635d5932e6f1411a6f74b8b693baf 100755 --- a/tests/src/Unit/TocTest.php +++ b/tests/src/Unit/TocTest.php @@ -39,6 +39,9 @@ class TocTest extends UnitTestCase { * Tests parsing headers and creating a table of contents index. * * @see Toc::getIndex() + * + * @return void + * Performs some internal tests, but doesn't return anything. */ public function testIndex() { // Check default index. This covers type, tag, level, indent, keys, parent, @@ -300,7 +303,7 @@ class TocTest extends UnitTestCase { // Check missing parent. $toc = new Toc('<h2>header 2</h2><h4>header 4</h4><h4>header 4</h4><h2>header 2</h2>', []); - $this->assertArraySubset([ + $result = $this->assertArraySubset([ '1.0.0' => [ 'parent' => NULL, 'children' => [ @@ -327,6 +330,9 @@ class TocTest extends UnitTestCase { * Tests converting table of contents index to hierarchical tree. * * @see Toc::getTree() + * + * @return void + * Performs some internal tests, but doesn't return anything. */ public function testTree() { // Check parent child relationship. @@ -419,11 +425,14 @@ class TocTest extends UnitTestCase { * Tests converting table of contents index to hierarchical tree. * * @see Toc::getContent() + * + * @return void + * Runs a few render tests internally, no return value. */ public function testContent() { // Check update content ids. $toc = new Toc('<h2>header 2</h2><h3 id="three" class="custom">header 3</h3><h4 id="four">header 4</h4><h4 id="four">header 4</h4><h2>header 2</h2>', []); - + $content = $toc->getContent(); $this->assertStringContainsString('<h2 id="header-2">', $content); $this->assertStringContainsString('<h3 id="three-01" class="custom">', $content); @@ -435,6 +444,9 @@ class TocTest extends UnitTestCase { * Tests converting table of contents index to hierarchical tree. * * @see Toc::getHeaderCount() + * + * @return void + * Runs a few render tests internally, no return value. */ public function testHeaderCount() { // Check TOC is hidden. @@ -451,20 +463,27 @@ class TocTest extends UnitTestCase { /** * Recursively asserts that the expected items are set in the tested ToC. * - * @param $expected + * @param array $expected * An array of expected values, may contain further nested arrays. - * @param $actual + * @param array $actual * The object to test. + * + * @return bool|string + * Runs a few render tests internally, no return value. */ protected function assertArraySubset($expected, $actual) { + $assert_result = ""; + foreach ($expected as $key => $value) { if (is_array($value)) { - $this->assertArraySubset($value, $actual[$key]); + $assert_result = $this->assertArraySubset($value, $actual[$key]); } else { $this->assertSame($value, $actual[$key]); } } + + return $assert_result; } /** @@ -474,6 +493,9 @@ class TocTest extends UnitTestCase { * A TOC index or tree. * @param string $method * The TOC method to tests. + * + * @return void + * Runs a few render tests internally, no return value. */ protected function dumpArraySubset(array $array, $method) { $this->dumpArraySubsetUnset($array); @@ -489,6 +511,9 @@ class TocTest extends UnitTestCase { * * @param array $array * A TOC index or tree. + * + * @return void + * Runs a few tests internally, no return value. */ protected function dumpArraySubsetUnset(array &$array) { foreach ($array as &$value) { diff --git a/toc_api.info.yml b/toc_api.info.yml index 31ce058b00005768baab96aaeaabc22f77d85b3a..4fd7db1accec1b1dd02497b3464202b5440f9a53 100644 --- a/toc_api.info.yml +++ b/toc_api.info.yml @@ -2,5 +2,5 @@ name: 'TOC API' type: module description: 'API for building a a hierarchical table of contents from header tags.' package: 'API' -core_version_requirement: ^9.4 || ^10.0 +core_version_requirement: ^9.5 || ^10.2 || ^11.0 configure: entity.toc_type.collection diff --git a/toc_api.install b/toc_api.install index 51a86fc88cc82f749d459248fdc39790ea5ff5e5..fcf2f83ffc8cea8b8abf1177321765549cd1479f 100644 --- a/toc_api.install +++ b/toc_api.install @@ -8,10 +8,11 @@ /** * Add header_exclude_xpath to existing TOCs. */ -function toc_api_update_8001() { +function toc_api_update_8001(): void { $toc_types = \Drupal::entityQuery('toc_type')->execute(); foreach ($toc_types as $type) { + /** @var \Drupal\toc_api\TocTypeInterface $entity */ $entity = \Drupal::entityTypeManager()->getStorage('toc_type')->load($type); $options = $entity->getOptions(); diff --git a/toc_api.module b/toc_api.module index 62aa9c33b15789abe86c8d8beaf380bc8a86e49d..e9fe635da2b98837970e9a478a1634be20c119f3 100755 --- a/toc_api.module +++ b/toc_api.module @@ -11,7 +11,7 @@ use Drupal\Core\Template\Attribute; /** * Implements hook_help(). */ -function toc_api_help($route_name, RouteMatchInterface $route_match) { +function toc_api_help(string $route_name, RouteMatchInterface $route_match): ?string { switch ($route_name) { case 'help.page.toc_api': $output = '<h3>' . t('About') . '</h3>'; @@ -29,7 +29,7 @@ function toc_api_help($route_name, RouteMatchInterface $route_match) { /** * Implements hook_theme(). */ -function toc_api_theme() { +function toc_api_theme(): array { return [ 'toc_header' => [ 'variables' => ['toc' => NULL, 'item' => NULL, 'attributes' => []], @@ -62,7 +62,7 @@ function toc_api_theme() { * - toc: A TOC (table of contents) object. * - item: A table of contents header item. */ -function template_preprocess_toc_header(&$variables) { +function template_preprocess_toc_header(array &$variables): void { /** @var \Drupal\toc_api\TocInterface $toc */ $toc = $variables['toc']; @@ -73,7 +73,13 @@ function template_preprocess_toc_header(&$variables) { $options = $toc->getOptions(); $variables['options'] = $options; $variables['header_options'] = $options['headers'][$item['tag']]; - $variables['header_options']['display_number'] = in_array($variables['header_options']['number_type'], ['decimal', 'lower-alpha', 'upper-alpha', 'lower-roman', 'upper-roman']); + $variables['header_options']['display_number'] = in_array($variables['header_options']['number_type'], [ + 'decimal', + 'lower-alpha', + 'upper-alpha', + 'lower-roman', + 'upper-roman', + ]); $variables['attributes']['id'] = $variables['id']; $variables['attributes'] = new Attribute($variables['attributes']); @@ -90,7 +96,7 @@ function template_preprocess_toc_header(&$variables) { * - item: A table of contents header item. * - attributes: Attributes to be added to back to top link. */ -function template_preprocess_toc_back_to_top(&$variables) { +function template_preprocess_toc_back_to_top(array &$variables): void { /** @var \Drupal\toc_api\TocInterface $toc */ $toc = $variables['toc']; @@ -111,7 +117,7 @@ function template_preprocess_toc_back_to_top(&$variables) { * - toc: A TOC (table of contents) object. * - attributes: Attributes to be added to back to top link. */ -function template_preprocess_toc_tree(&$variables) { +function template_preprocess_toc_tree(array &$variables): void { /** @var \Drupal\toc_api\TocInterface $toc */ $toc = $variables['toc']; @@ -139,7 +145,7 @@ function template_preprocess_toc_tree(&$variables) { * - toc: A TOC (table of contents) object. * - attributes: Attributes to be added to back to top link. */ -function template_preprocess_toc_menu(&$variables) { +function template_preprocess_toc_menu(array &$variables): void { /** @var \Drupal\toc_api\TocInterface $toc */ $toc = $variables['toc']; @@ -177,7 +183,7 @@ function template_preprocess_toc_menu(&$variables) { * - toc: A TOC (table of contents) object. * - attributes: Attributes to be added to back to top link. */ -function template_preprocess_toc_responsive(&$variables) { +function template_preprocess_toc_responsive(array &$variables): void { $variables['attributes'] = new Attribute($variables['attributes']); $variables['desktop'] = [ @@ -209,7 +215,7 @@ function template_preprocess_toc_responsive(&$variables) { * - toc: A TOC (table of contents) object. * - attributes: Attributes to be added to back to top link. */ -function template_preprocess_toc_default(&$variables) { +function template_preprocess_toc_default(array &$variables): void { $variables['attributes'] = new Attribute($variables['attributes']); $variables['toc_default'] = [