From afdee270cb12cb1b290668222279fbd8cbbe2d06 Mon Sep 17 00:00:00 2001
From: Dave Long <dave@longwaveconsulting.com>
Date: Mon, 22 Jul 2024 18:16:33 +0100
Subject: [PATCH] Issue #2502637 by bnjmnm, shumer, smustgrave, cilefen, Wim
 Leers, legolasbo, quietone, Xano, nikitagupta, DanielVeza, vegantriathlete,
 gaurav-mathur, priyanka.sahni, alexpott, dww, rkoller, dqd, swentel,
 eltermann, Alan D., ZenDoodles, David_Rothstein, sun, welly, pameeela:
 Disabled text formats can't be seen in the GUI

---
 core/modules/filter/filter.routing.yml        |  8 ++
 .../filter/src/Element/ProcessedText.php      |  5 ++
 .../filter/src/Entity/FilterFormat.php        |  7 +-
 .../src/FilterFormatAccessControlHandler.php  |  2 +-
 .../filter/src/FilterFormatListBuilder.php    | 29 ++++---
 .../filter/src/Form/FilterDisableForm.php     |  5 +-
 .../filter/src/Form/FilterEnableForm.php      | 54 ++++++++++++
 .../tests/src/Functional/FilterAdminTest.php  | 84 +++++++++++++++++++
 8 files changed, 179 insertions(+), 15 deletions(-)
 create mode 100644 core/modules/filter/src/Form/FilterEnableForm.php

diff --git a/core/modules/filter/filter.routing.yml b/core/modules/filter/filter.routing.yml
index 5b56da7089f0..799722cc0dcd 100644
--- a/core/modules/filter/filter.routing.yml
+++ b/core/modules/filter/filter.routing.yml
@@ -45,3 +45,11 @@ entity.filter_format.disable:
     _title: 'Disable text format'
   requirements:
     _entity_access: 'filter_format.disable'
+
+entity.filter_format.enable:
+  path: '/admin/config/content/formats/manage/{filter_format}/enable'
+  defaults:
+    _entity_form: 'filter_format.enable'
+    _title: 'Enable text format'
+  requirements:
+    _entity_access: 'filter_format.enable'
diff --git a/core/modules/filter/src/Element/ProcessedText.php b/core/modules/filter/src/Element/ProcessedText.php
index 7ec8f662458f..a9505799b687 100644
--- a/core/modules/filter/src/Element/ProcessedText.php
+++ b/core/modules/filter/src/Element/ProcessedText.php
@@ -85,6 +85,11 @@ public static function preRenderText($element) {
       $message = !$format ? 'Missing text format: %format.' : 'Disabled text format: %format.';
       static::logger('filter')->alert($message, ['%format' => $format_id]);
       $element['#markup'] = '';
+      // Associate the disabled text format's cache tag, to ensure re-enabling
+      // the text format invalidates the appropriate render cache items.
+      if ($format !== NULL) {
+        $element['#cache']['tags'] = Cache::mergeTags($element['#cache']['tags'] ?? [], $format->getCacheTags());
+      }
       return $element;
     }
 
diff --git a/core/modules/filter/src/Entity/FilterFormat.php b/core/modules/filter/src/Entity/FilterFormat.php
index 699b6a99d4a4..dab96a0e9e12 100644
--- a/core/modules/filter/src/Entity/FilterFormat.php
+++ b/core/modules/filter/src/Entity/FilterFormat.php
@@ -3,6 +3,7 @@
 namespace Drupal\filter\Entity;
 
 use Drupal\Component\Plugin\PluginInspectionInterface;
+
 use Drupal\Core\Config\Action\Attribute\ActionMethod;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
@@ -30,7 +31,8 @@
  *     "form" = {
  *       "add" = "Drupal\filter\FilterFormatAddForm",
  *       "edit" = "Drupal\filter\FilterFormatEditForm",
- *       "disable" = "Drupal\filter\Form\FilterDisableForm"
+ *       "disable" = "Drupal\filter\Form\FilterDisableForm",
+ *       "enable" = "Drupal\filter\Form\FilterEnableForm",
  *     },
  *     "list_builder" = "Drupal\filter\FilterFormatListBuilder",
  *     "access" = "Drupal\filter\FilterFormatAccessControlHandler",
@@ -45,7 +47,8 @@
  *   },
  *   links = {
  *     "edit-form" = "/admin/config/content/formats/manage/{filter_format}",
- *     "disable" = "/admin/config/content/formats/manage/{filter_format}/disable"
+ *     "disable" = "/admin/config/content/formats/manage/{filter_format}/disable",
+ *     "enable" = "/admin/config/content/formats/manage/{filter_format}/enable",
  *   },
  *   config_export = {
  *     "name",
diff --git a/core/modules/filter/src/FilterFormatAccessControlHandler.php b/core/modules/filter/src/FilterFormatAccessControlHandler.php
index 2dcb6dc7d1ef..faaed4f5c516 100644
--- a/core/modules/filter/src/FilterFormatAccessControlHandler.php
+++ b/core/modules/filter/src/FilterFormatAccessControlHandler.php
@@ -41,7 +41,7 @@ protected function checkAccess(EntityInterface $filter_format, $operation, Accou
       return AccessResult::forbidden();
     }
 
-    if (in_array($operation, ['disable', 'update', 'view'])) {
+    if (in_array($operation, ['disable', 'update', 'view', 'enable'])) {
       return parent::checkAccess($filter_format, $operation, $account);
     }
 
diff --git a/core/modules/filter/src/FilterFormatListBuilder.php b/core/modules/filter/src/FilterFormatListBuilder.php
index d63ea6c9fc47..1aacdd87404d 100644
--- a/core/modules/filter/src/FilterFormatListBuilder.php
+++ b/core/modules/filter/src/FilterFormatListBuilder.php
@@ -75,22 +75,13 @@ public function getFormId() {
     return 'filter_admin_overview';
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function load() {
-    // Only list enabled filters.
-    return array_filter(parent::load(), function ($entity) {
-      return $entity->status();
-    });
-  }
-
   /**
    * {@inheritdoc}
    */
   public function buildHeader() {
     $header['label'] = $this->t('Name');
     $header['roles'] = $this->t('Roles');
+    $header['status'] = $this->t('Status');
     return $header + parent::buildHeader();
   }
 
@@ -124,7 +115,13 @@ public function buildRow(EntityInterface $entity) {
         '#context' => ['list_style' => 'comma-list'],
       ];
     }
-
+    if ($entity->status()) {
+      $status = $this->t('Enabled');
+    }
+    else {
+      $status = $this->t('Disabled');
+    }
+    $row['status']['#markup'] = $status;
     return $row + parent::buildRow($entity);
   }
 
@@ -143,6 +140,16 @@ public function getDefaultOperations(EntityInterface $entity) {
       unset($operations['disable']);
     }
 
+    // Remove disable and edit operations for disabled formats.
+    if (!$entity->status()) {
+      if (isset($operations['disable'])) {
+        unset($operations['disable']);
+      }
+      if (isset($operations['edit'])) {
+        unset($operations['edit']);
+      }
+    }
+
     return $operations;
   }
 
diff --git a/core/modules/filter/src/Form/FilterDisableForm.php b/core/modules/filter/src/Form/FilterDisableForm.php
index 0c213a26666e..594f06057113 100644
--- a/core/modules/filter/src/Form/FilterDisableForm.php
+++ b/core/modules/filter/src/Form/FilterDisableForm.php
@@ -38,7 +38,10 @@ public function getConfirmText() {
    * {@inheritdoc}
    */
   public function getDescription() {
-    return $this->t('Disabled text formats are completely removed from the administrative interface, and any content stored with that format will not be displayed. This action cannot be undone.');
+    return $this->t(
+      'Any content saved with the %format text format will not be displayed on the site until it is resaved with an enabled text format.',
+      ['%format' => $this->entity->label()],
+    );
   }
 
   /**
diff --git a/core/modules/filter/src/Form/FilterEnableForm.php b/core/modules/filter/src/Form/FilterEnableForm.php
new file mode 100644
index 000000000000..2dbe7f9c2c9c
--- /dev/null
+++ b/core/modules/filter/src/Form/FilterEnableForm.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\filter\Form;
+
+use Drupal\Core\Entity\EntityConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\Url;
+
+/**
+ * Provides the filter format enable form.
+ */
+class FilterEnableForm extends EntityConfirmFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion(): TranslatableMarkup {
+    return $this->t('Are you sure you want to enable the text format %format?', ['%format' => $this->entity->label()]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl(): Url {
+    return new Url('filter.admin_overview');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText(): TranslatableMarkup {
+    return $this->t('Enable');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription(): TranslatableMarkup {
+    return $this->t('This will make the %format format available.', ['%format' => $this->entity->label()]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state): void {
+    $this->entity->enable()->save();
+    $this->messenger()->addMessage($this->t('Enabled text format %format.', ['%format' => $this->entity->label()]));
+    $form_state->setRedirectUrl($this->getCancelUrl());
+  }
+
+}
diff --git a/core/modules/filter/tests/src/Functional/FilterAdminTest.php b/core/modules/filter/tests/src/Functional/FilterAdminTest.php
index e35db530f0c3..2b501f1d921f 100644
--- a/core/modules/filter/tests/src/Functional/FilterAdminTest.php
+++ b/core/modules/filter/tests/src/Functional/FilterAdminTest.php
@@ -478,4 +478,88 @@ public function testDisabledFormat(): void {
     $this->assertSession()->pageTextContains(sprintf('Missing text format: %s.', $format_id));
   }
 
+  /**
+   * Tests enabling and disabling of filters.
+   */
+  public function testFilterEnableAndDisable(): void {
+    $filter_test = FilterFormat::create([
+      'format' => 'filter_test',
+      'name' => 'Filter test',
+      'filters' => [
+        'filter_html' => [
+          'status' => TRUE,
+          'weight' => -10,
+          'settings' => [
+            'allowed_html' => '<p> <br> <strong> <a> <em> <h4>',
+          ],
+        ],
+      ],
+    ]);
+    $filter_test->save();
+
+    // Create a node type and add a standard body field.
+    $node_type = NodeType::create([
+      'type' => $this->randomMachineName(),
+      'name' => $this->randomString(),
+    ]);
+    $node_type->save();
+    node_add_body_field($node_type, $this->randomString());
+
+    // Create a new node of the new node type.
+    $title = $this->randomString();
+    $node = Node::create([
+      'type' => $node_type->id(),
+      'title' => $title,
+    ]);
+    $body_value = 'I belong to a filter that might be shut off!';
+    $node->body->value = $body_value;
+    $node->body->format = 'filter_test';
+    $node->save();
+
+    // Confirm the body field using the filter test is visible.
+    $this->drupalGet($node->toUrl());
+    $this->assertSession()->pageTextContains($title);
+    $this->assertSession()->pageTextContains($body_value);
+
+    $this->drupalGet('admin/config/content/formats');
+
+    // Verify filter_test links.
+    $this->assertSession()->linkByHrefExists('/admin/config/content/formats/manage/filter_test/disable');
+    $this->assertSession()->linkByHrefNotExists('/admin/config/content/formats/manage/filter_test/enable');
+
+    // Test the configure link appears for Filter test.
+    $this->assertSession()->elementExists('xpath', '//a[contains(@href, "/admin/config/content/formats/manage/filter_test") and text()="Configure"]');
+
+    // Disable 'Filter test'.
+    $this->getSession()->getPage()->find('css', '[href*="/admin/config/content/formats/manage/filter_test/disable"]')->click();
+    $this->assertSession()->pageTextContains('Are you sure you want to disable the text format Filter test?');
+    $this->getSession()->getPage()->find('css', '#edit-submit')->click();
+
+    // Verify filter_test links after filter_test is disabled.
+    $this->assertSession()->linkByHrefExists('/admin/config/content/formats/manage/filter_test/enable');
+    $this->assertSession()->linkByHrefNotExists('/admin/config/content/formats/manage/filter_test/disable');
+
+    // Test the configure link doesn't appear for Filter test.
+    $this->assertSession()->elementNotExists('xpath', '//a[contains(@href, "/admin/config/content/formats/manage/filter_test") and text()="Configure"]');
+
+    // Confirm the field using the now-disabled filter is not visible.
+    $this->drupalGet($node->toUrl());
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextContains($title);
+    $this->assertSession()->pageTextNotContains($body_value);
+
+    // Re-enable the filter that we disabled.
+    $this->drupalGet('admin/config/content/formats');
+    $this->getSession()->getPage()->find('css', '[href*="/admin/config/content/formats/manage/filter_test/enable"]')->click();
+    $this->assertSession()->pageTextContains('Are you sure you want to enable the text format Filter test?');
+    $this->getSession()->getPage()->find('css', '#edit-submit')->click();
+
+    // Confirm the presence of enable/disable operations has updated properly.
+    $this->assertSession()->linkByHrefExists('/admin/config/content/formats/manage/filter_test/disable');
+    $this->assertSession()->linkByHrefNotExists('/admin/config/content/formats/manage/filter_test/enable');
+
+    $this->drupalGet($node->toUrl());
+    $this->assertSession()->pageTextContains($body_value);
+  }
+
 }
-- 
GitLab