diff --git a/core/modules/block/src/BlockForm.php b/core/modules/block/src/BlockForm.php index 172186a84e..fe2227701a 100644 --- a/core/modules/block/src/BlockForm.php +++ b/core/modules/block/src/BlockForm.php @@ -288,6 +288,10 @@ protected function buildVisibilityInterface(array $form, FormStateInterface $for $form['language']['negate']['#type'] = 'value'; $form['language']['negate']['#value'] = $form['language']['negate']['#default_value']; } + if (isset($form['status_code'])) { + $form['status_code']['negate']['#type'] = 'value'; + $form['status_code']['negate']['#value'] = $form['status_code']['negate']['#default_value']; + } return $form; } diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index 3e61a0036b..fdd78b2e46 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -351,6 +351,14 @@ condition.plugin.request_path: pages: type: string +condition.plugin.error_page: + type: condition.plugin + mapping: + status_codes: + type: sequence + sequence: + type: string + condition.plugin.current_theme: type: condition.plugin mapping: diff --git a/core/modules/system/src/Plugin/Condition/ErrorPage.php b/core/modules/system/src/Plugin/Condition/ErrorPage.php new file mode 100644 index 0000000000..0820b2a316 --- /dev/null +++ b/core/modules/system/src/Plugin/Condition/ErrorPage.php @@ -0,0 +1,122 @@ +setRequestStack($container->get('request_stack')); + return $instance; + } + + public function setRequestStack(RequestStack $requestStack): void { + $this->requestStack = $requestStack; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return ['status_codes' => []] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $status_codes = [ + self::ACCESS_DENIED => $this->t('Access denied (@error_code)', ['@error_code' => self::ACCESS_DENIED]), + self::NOT_FOUND => $this->t('Page not found (@error_code)', ['@error_code' => self::NOT_FOUND]), + ]; + $form['status_codes'] = [ + '#type' => 'checkboxes', + '#title' => $this->t('Show on status code'), + '#options' => $status_codes, + '#default_value' => $this->configuration['status_codes'], + '#description' => $this->t('Select status codes to enforce. If none are selected, all status codes will be allowed.'), + ]; + return parent::buildConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $this->configuration['status_codes'] = array_filter($form_state->getValue('status_codes')); + parent::submitConfigurationForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function summary() { + $config = $this->configuration['status_codes']; + $status_codes = [self::ACCESS_DENIED, self::NOT_FOUND]; + $result = empty($config) ? $status_codes : $config; + $count = count($result); + $codes = implode(', ', $result); + if (!empty($this->configuration['negate'])) { + return $this->formatPlural($count, 'Request response code is not: @codes', 'Request response code is not one of the following: @codes', ['@codes' => $codes]); + } + return $this->formatPlural($count, 'Request response code is: @codes', 'Request response code is one of the following: @codes', ['@codes' => $codes]); + } + + /** + * {@inheritdoc} + */ + public function evaluate() { + $config = $this->configuration['status_codes']; + if (empty($config)) { + return TRUE; + } + $codes = array_combine($config, $config); + $exception = $this->requestStack->getCurrentRequest()->attributes->get('exception'); + return ($exception instanceof HttpExceptionInterface && isset($codes[$exception->getStatusCode()])); + } + + /** + * {@inheritdoc} + */ + public function getCacheContexts() { + $contexts = parent::getCacheContexts(); + $contexts[] = 'url.path'; + return $contexts; + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Plugin/Condition/ErrorPageTest.php b/core/tests/Drupal/KernelTests/Core/Plugin/Condition/ErrorPageTest.php new file mode 100644 index 0000000000..1ca3521cd9 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Plugin/Condition/ErrorPageTest.php @@ -0,0 +1,186 @@ +installSchema('system', ['sequences']); + $this->installConfig('system'); + + $this->pluginManager = $this->container->get('plugin.manager.condition'); + + // Set the test request stack in the container. + $this->requestStack = new RequestStack(); + $this->container->set('request_stack', $this->requestStack); + } + + /** + * Tests the request path condition. + * + * @dataProvider providerTestConditions + */ + public function testConditions(array $status_codes, bool $negate, string $response_code, bool $expected_execute) { + if ($response_code == '200') { + $request = Request::create('/my/valid/page'); + } + else { + $request = new Request(); + $request->attributes->set('exception', new HttpException((int) $response_code)); + } + $this->requestStack->push($request); + + /** @var \Drupal\system\Plugin\Condition\ErrorPage $condition */ + $condition = $this->pluginManager->createInstance('error_page'); + $condition->setConfig('status_codes', $status_codes); + $condition->setConfig('negate', $negate); + + $this->assertSame($expected_execute, $condition->execute()); + } + + /** + * Provides test data for testConditions. + */ + public function providerTestConditions() { + // Default values with 200 response code. + yield [ + 'status_codes' => [], + 'negate' => FALSE, + 'response_code' => '200', + 'expected_execute' => TRUE, + ]; + // Default values with 200 response code and negated enabled. + yield [ + 'status_codes' => [], + 'negate' => TRUE, + 'response_code' => '200', + 'expected_execute' => FALSE, + ]; + + // Default values with 403 response code. + yield [ + 'status_codes' => [], + 'negate' => FALSE, + 'response_code' => '403', + 'expected_execute' => TRUE, + ]; + // Default values with 403 response code and negated enabled. + yield [ + 'status_codes' => [], + 'negate' => TRUE, + 'response_code' => '403', + 'expected_execute' => FALSE, + ]; + + // 403 status code enabled with 200 response code. + yield [ + 'status_codes' => [403 => '403'], + 'negate' => FALSE, + 'response_code' => '200', + 'expected_execute' => FALSE, + ]; + // 403 status code enabled with 200 response code and negated enabled. + yield [ + 'status_codes' => [403 => '403'], + 'negate' => TRUE, + 'response_code' => '200', + 'expected_execute' => TRUE, + ]; + + // 403 status code enabled with 403 response code. + yield [ + 'status_codes' => [403 => '403'], + 'negate' => FALSE, + 'response_code' => '403', + 'expected_execute' => TRUE, + ]; + // 403 status code enabled with 403 response code and negated enabled. + yield [ + 'status_codes' => [403 => '403'], + 'negate' => TRUE, + 'response_code' => '403', + 'expected_execute' => FALSE, + ]; + + // 403 status code enabled with 404 response code. + yield [ + 'status_codes' => [403 => '403'], + 'negate' => FALSE, + 'response_code' => '404', + 'expected_execute' => FALSE, + ]; + // 403 status code enabled with 404 response code and negated enabled. + yield [ + 'status_codes' => [403 => '403'], + 'negate' => TRUE, + 'response_code' => '404', + 'expected_execute' => TRUE, + ]; + + // 403,404 status code enabled with 403 response code. + yield [ + 'status_codes' => [ + 403 => '403', + 404 => '404', + ], + 'negate' => FALSE, + 'response_code' => '403', + 'expected_execute' => TRUE, + ]; + // 403,404 status code enabled with 404 response code. + yield [ + 'status_codes' => [ + 403 => '403', + 404 => '404', + ], + 'negate' => FALSE, + 'response_code' => '404', + 'expected_execute' => TRUE, + ]; + + // 403,404 status code enabled with 200 response code. + yield [ + 'status_codes' => [ + 403 => '403', + 404 => '404', + ], + 'negate' => FALSE, + 'response_code' => '200', + 'expected_execute' => FALSE, + ]; + } + +}