diff --git a/config/schema/footnotes.schema.yml b/config/schema/footnotes.schema.yml index 03761bc..1064faa 100644 --- a/config/schema/footnotes.schema.yml +++ b/config/schema/footnotes.schema.yml @@ -14,6 +14,12 @@ filter_settings.filter_footnotes: footnotes_footer_disable: type: boolean label: 'Disable output of the footnotes footer' + footnotes_node_bundles_individual: + type: sequence + label: 'Node bundles to always display individual footnotes' + sequence: + type: string + label: 'Node types' block.settings.footnotes_group: type: block_settings diff --git a/src/Plugin/Filter/FootnotesFilter.php b/src/Plugin/Filter/FootnotesFilter.php index 18aee06..afabf75 100644 --- a/src/Plugin/Filter/FootnotesFilter.php +++ b/src/Plugin/Filter/FootnotesFilter.php @@ -7,14 +7,17 @@ use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Random; +use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Render\RendererInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; use Drupal\footnotes\FootnotesDialog; use Drupal\footnotes\FootnotesGroup; +use Drupal\node\NodeInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -32,6 +35,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * "footnotes_css" = TRUE, * "footnotes_dialog" = FALSE, * "footnotes_footer_disable" = FALSE, + * "footnotes_node_bundles_individual" = NULL, * }, * weight = 0 * ) @@ -54,6 +58,20 @@ class FootnotesFilter extends FilterBase implements ContainerFactoryPluginInterf */ protected array $storedFootnotes = []; + /** + * The route match service. + * + * @var \Drupal\Core\Routing\RouteMatchInterface + */ + protected RouteMatchInterface $routeMatch; + + /** + * The entity type bundle info service. + * + * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface + */ + protected EntityTypeBundleInfoInterface $entityTypeBundleInfo; + /** * Constructs a MediaEmbed object. * @@ -77,8 +95,12 @@ class FootnotesFilter extends FilterBase implements ContainerFactoryPluginInterf protected RendererInterface $renderer, protected FootnotesGroup $footnotesGroup, protected FootnotesDialog $footnotesDialog, + protected RouteMatchInterface $route_match, + protected EntityTypeBundleInfoInterface $bundle_info ) { parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->routeMatch = $route_match; + $this->entityTypeBundleInfo = $bundle_info; } /** @@ -91,7 +113,9 @@ class FootnotesFilter extends FilterBase implements ContainerFactoryPluginInterf $plugin_definition, $container->get('renderer'), $container->get('footnotes.group'), - $container->get('footnotes.dialog') + $container->get('footnotes.dialog'), + $container->get('current_route_match'), + $container->get('entity_type.bundle.info') ); } @@ -157,8 +181,10 @@ class FootnotesFilter extends FilterBase implements ContainerFactoryPluginInterf // Save the updated text. $processed_text = Html::serialize($dom); + /** @var NodeInterface $entity */ + $entity = $this->routeMatch->getParameter('node'); // Output or store the footnotes. - if ($this->settings['footnotes_footer_disable']) { + if ($this->settings['footnotes_footer_disable'] && !in_array($entity->bundle(), $this->settings['footnotes_node_bundles_individual'])) { // Store the footnotes in the footnotes group so the site builder // can output them elsewhere. @@ -457,9 +483,37 @@ class FootnotesFilter extends FilterBase implements ContainerFactoryPluginInterf '#default_value' => $this->settings['footnotes_footer_disable'] ?? FALSE, '#description' => $this->t("If disabled, the footnotes will be grouped together. They can be output using the Footnotes Group block, or anywhere using Twig Tweak {{ drupal_block('footnotes_group') }}. Note that you must clear the cache in order for changes to this setting to take effect."), ]; + // Get all 'node' bundles. + $bundles = $this->entityTypeBundleInfo->getBundleInfo('node'); + $bundle_options = array_map(function ($item) { + return $item['label']; + }, $bundles); + + $settings['footnotes_node_bundles_individual'] = [ + '#title' => $this->t('Node bundles to always display individual footnotes'), + '#type' => 'checkboxes', + '#options' => $bundle_options, + '#default_value' => $this->settings['footnotes_node_bundles_individual'] ?? [], + '#description' => $this->t('Select the content types (node bundles) where individual footnotes should still be displayed below each text field, even when the "Disable output of the footnotes footer" option is enabled.'), + '#element_validate' => [[static::class, 'validateOptions']], + ]; return $settings; } + /** + * Form element validation handler. + * + * @param array $element + * The allowed_view_modes form element. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The form state. + */ + public static function validateOptions(array &$element, FormStateInterface $form_state) { + // Filters the #value property so only selected values appear in the + // config. + $form_state->setValueForElement($element, array_filter($element['#value'])); + } + /** * Duplicate of MediaEmbed::replaceNodeContent(). *