From 450a7ae9f4817157a2185e6e562f8236ef013ad4 Mon Sep 17 00:00:00 2001 From: Brian Canini <canini.16@osu.edu> Date: Thu, 18 Jun 2020 14:41:59 -0400 Subject: [PATCH] Updating drupal/redirect (1.3.0 => 1.6.0) --- composer.json | 4 +- composer.lock | 23 +- vendor/composer/installed.json | 23 +- web/modules/redirect/composer.json | 5 +- .../config/install/views.view.redirect.yml | 9 +- .../redirect/migrations/d6_path_redirect.yml | 3 +- .../redirect/migrations/d7_path_redirect.yml | 1 + .../install/views.view.redirect_404.yml | 59 +++ .../redirect_404/redirect_404.info.yml | 14 +- .../modules/redirect_404/redirect_404.install | 102 ++++ .../modules/redirect_404/redirect_404.module | 8 + .../redirect_404/redirect_404.views.inc | 12 + .../src/Controller/Fix404IgnoreController.php | 2 +- .../src/RedirectNotFoundStorageInterface.php | 5 + .../src/SqlRedirectNotFoundStorage.php | 13 +- .../src/Tests/Redirect404TestBase.php | 144 ------ .../Fix404RedirectUILanguageTest.php | 10 +- .../src/Functional}/Fix404RedirectUITest.php | 21 +- .../Redirect404LogSuppressorTest.php | 12 +- .../src/Functional/Redirect404TestBase.php | 89 ++++ .../src/Kernel/Fix404RedirectCronJobTest.php | 74 ++- .../redirect_domain/redirect_domain.info.yml | 12 +- .../redirect_domain/redirect_domain.module | 13 + .../DomainRedirectRequestSubscriber.php | 11 +- .../src/Form/RedirectDomainForm.php | 3 +- .../RedirectDomainUITest.php | 31 +- .../DomainRedirectRequestSubscriberTest.php | 21 +- web/modules/redirect/redirect.drush.inc | 18 +- web/modules/redirect/redirect.generate.inc | 58 +-- web/modules/redirect/redirect.info.yml | 10 +- web/modules/redirect/redirect.install | 16 +- web/modules/redirect/redirect.module | 236 +++------- web/modules/redirect/redirect.services.yml | 4 +- web/modules/redirect/src/Entity/Redirect.php | 36 +- .../RedirectRequestSubscriber.php | 26 +- .../RouteNormalizerRequestSubscriber.php | 2 +- .../src/Exception/RedirectLoopException.php | 42 +- .../redirect/src/Form/RedirectDeleteForm.php | 4 +- .../src/Form/RedirectDeleteMultipleForm.php | 8 +- .../redirect/src/Form/RedirectForm.php | 32 +- .../src/Form/RedirectSettingsForm.php | 38 +- .../src/Plugin/Action/DeleteRedirect.php | 8 +- .../RedirectSourceFormatter.php | 6 +- .../Field/FieldType/RedirectSourceItem.php | 22 +- .../FieldWidget/RedirectSourceWidget.php | 29 +- .../Constraint/SourceLinkTypeConstraint.php | 8 +- .../Plugin/migrate/source/PathRedirect.php | 8 +- .../redirect/src/RedirectRepository.php | 12 +- .../redirect/src/Tests/RedirectUITest.php | 436 ------------------ .../redirect/tests/fixtures/drupal6.php | 116 ++--- .../redirect/tests/fixtures/drupal7.php | 162 +++---- .../src/Functional}/AssertRedirectTrait.php | 38 +- .../src/Functional}/GlobalRedirectTest.php | 177 ++++--- .../src/Functional/RedirectNodeFormTest.php | 100 ++++ .../Functional}/RedirectUILanguageTest.php | 37 +- .../tests/src/Functional/RedirectUITest.php | 274 +++++++++++ .../RedirectJavascriptTest.php | 285 ++++++++++++ .../Kernel/Migrate/d6/PathRedirectTest.php | 5 +- .../Migrate/d7/PathRedirectSourceTest.php | 54 +++ .../Kernel/Migrate/d7/PathRedirectTest.php | 4 +- .../tests/src/Kernel/RedirectAPITest.php | 113 +++-- .../src/Unit/Migrate/d7/PathRedirectTest.php | 54 --- .../tests/src/Unit/RedirectCheckerTest.php | 2 +- .../Unit/RedirectRequestSubscriberTest.php | 28 +- .../RouteNormalizerRequestSubscriberTest.php | 8 +- 65 files changed, 1843 insertions(+), 1397 deletions(-) delete mode 100644 web/modules/redirect/modules/redirect_404/src/Tests/Redirect404TestBase.php rename web/modules/redirect/modules/redirect_404/{src/Tests => tests/src/Functional}/Fix404RedirectUILanguageTest.php (95%) rename web/modules/redirect/modules/redirect_404/{src/Tests => tests/src/Functional}/Fix404RedirectUITest.php (88%) rename web/modules/redirect/modules/redirect_404/{src/Tests => tests/src/Functional}/Redirect404LogSuppressorTest.php (74%) create mode 100644 web/modules/redirect/modules/redirect_404/tests/src/Functional/Redirect404TestBase.php rename web/modules/redirect/modules/redirect_domain/{src/Tests => tests/src/FunctionalJavascript}/RedirectDomainUITest.php (63%) delete mode 100644 web/modules/redirect/src/Tests/RedirectUITest.php rename web/modules/redirect/{src/Tests => tests/src/Functional}/AssertRedirectTrait.php (51%) rename web/modules/redirect/{src/Tests => tests/src/Functional}/GlobalRedirectTest.php (61%) create mode 100644 web/modules/redirect/tests/src/Functional/RedirectNodeFormTest.php rename web/modules/redirect/{src/Tests => tests/src/Functional}/RedirectUILanguageTest.php (73%) create mode 100644 web/modules/redirect/tests/src/Functional/RedirectUITest.php create mode 100644 web/modules/redirect/tests/src/FunctionalJavascript/RedirectJavascriptTest.php create mode 100644 web/modules/redirect/tests/src/Kernel/Migrate/d7/PathRedirectSourceTest.php delete mode 100644 web/modules/redirect/tests/src/Unit/Migrate/d7/PathRedirectTest.php diff --git a/composer.json b/composer.json index 84550d4e53..9904bb653c 100644 --- a/composer.json +++ b/composer.json @@ -153,7 +153,7 @@ "drupal/realname": "1.0.0-rc2", "drupal/rebuild_cache_access": "1.7", "drupal/recaptcha": "2.4", - "drupal/redirect": "1.3", + "drupal/redirect": "1.6", "drupal/redis": "1.0", "drupal/roleassign": "1.0.0-alpha2", "drupal/scheduler": "1.3", @@ -321,4 +321,4 @@ "php": "7.0.8" } } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index e72247e4d8..b352ce7f17 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "49d5961f8a3140652419db70da74c6d9", + "content-hash": "f696b1cc6952abfd681e8544036af676", "packages": [ { "name": "alchemy/zippy", @@ -6797,29 +6797,26 @@ }, { "name": "drupal/redirect", - "version": "1.3.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/redirect.git", - "reference": "8.x-1.3" + "reference": "8.x-1.6" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/redirect-8.x-1.3.zip", - "reference": "8.x-1.3", - "shasum": "3f9620d186e25f36ac56755979932b8ea965b8c7" + "url": "https://ftp.drupal.org/files/projects/redirect-8.x-1.6.zip", + "reference": "8.x-1.6", + "shasum": "f848e001deac8425ae57d4b9397087c491d37294" }, "require": { - "drupal/core": "~8" + "drupal/core": "^8.8 || ^9" }, "type": "drupal-module", "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - }, "drupal": { - "version": "8.x-1.3", - "datestamp": "1539682684", + "version": "8.x-1.6", + "datestamp": "1589312204", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -6828,7 +6825,7 @@ }, "notification-url": "https://packages.drupal.org/8/downloads", "license": [ - "GPL-2.0+" + "GPL-2.0-or-later" ], "authors": [ { diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index e114ec1040..e1f34011f2 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -7004,30 +7004,27 @@ }, { "name": "drupal/redirect", - "version": "1.3.0", - "version_normalized": "1.3.0.0", + "version": "1.6.0", + "version_normalized": "1.6.0.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/redirect.git", - "reference": "8.x-1.3" + "reference": "8.x-1.6" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/redirect-8.x-1.3.zip", - "reference": "8.x-1.3", - "shasum": "3f9620d186e25f36ac56755979932b8ea965b8c7" + "url": "https://ftp.drupal.org/files/projects/redirect-8.x-1.6.zip", + "reference": "8.x-1.6", + "shasum": "f848e001deac8425ae57d4b9397087c491d37294" }, "require": { - "drupal/core": "~8" + "drupal/core": "^8.8 || ^9" }, "type": "drupal-module", "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - }, "drupal": { - "version": "8.x-1.3", - "datestamp": "1539682684", + "version": "8.x-1.6", + "datestamp": "1589312204", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -7037,7 +7034,7 @@ "installation-source": "dist", "notification-url": "https://packages.drupal.org/8/downloads", "license": [ - "GPL-2.0+" + "GPL-2.0-or-later" ], "authors": [ { diff --git a/web/modules/redirect/composer.json b/web/modules/redirect/composer.json index b4f147895e..bae3099f88 100644 --- a/web/modules/redirect/composer.json +++ b/web/modules/redirect/composer.json @@ -2,9 +2,8 @@ "name": "drupal/redirect", "description": "Allows users to redirect from old URLs to new URLs.", "type": "drupal-module", - "license": "GPL-2.0+", - "minimum-stability": "dev", + "license": "GPL-2.0-or-later", "require": { - "drupal/core": "~8" + "drupal/core": "^8.8 || ^9" } } diff --git a/web/modules/redirect/config/install/views.view.redirect.yml b/web/modules/redirect/config/install/views.view.redirect.yml index 6a1c65f55a..7579931ef8 100644 --- a/web/modules/redirect/config/install/views.view.redirect.yml +++ b/web/modules/redirect/config/install/views.view.redirect.yml @@ -12,7 +12,6 @@ description: 'List of redirects' tag: '' base_table: redirect base_field: rid -core: 8.x display: default: display_plugin: default @@ -356,6 +355,8 @@ display: authenticated: authenticated anonymous: '0' administrator: '0' + operator_limit_selection: false + operator_list: { } is_grouped: false group_info: label: '' @@ -396,6 +397,8 @@ display: authenticated: authenticated anonymous: '0' administrator: '0' + operator_limit_selection: false + operator_list: { } is_grouped: false group_info: label: '' @@ -439,6 +442,8 @@ display: authenticated: authenticated anonymous: '0' administrator: '0' + operator_limit_selection: false + operator_list: { } is_grouped: true group_info: label: 'Status code' @@ -529,6 +534,8 @@ display: anonymous: '0' administrator: '0' reduce: false + operator_limit_selection: false + operator_list: { } is_grouped: false group_info: label: '' diff --git a/web/modules/redirect/migrations/d6_path_redirect.yml b/web/modules/redirect/migrations/d6_path_redirect.yml index 1f3ddef969..8de3bef31f 100644 --- a/web/modules/redirect/migrations/d6_path_redirect.yml +++ b/web/modules/redirect/migrations/d6_path_redirect.yml @@ -2,6 +2,7 @@ id: d6_path_redirect label: Path Redirect migration_tags: - Drupal 6 + - Content source: plugin: d6_path_redirect process: @@ -18,4 +19,4 @@ process: default_value: und status_code: type destination: - plugin: entity:redirect \ No newline at end of file + plugin: entity:redirect diff --git a/web/modules/redirect/migrations/d7_path_redirect.yml b/web/modules/redirect/migrations/d7_path_redirect.yml index 3ee9a287dc..647d2f1305 100644 --- a/web/modules/redirect/migrations/d7_path_redirect.yml +++ b/web/modules/redirect/migrations/d7_path_redirect.yml @@ -2,6 +2,7 @@ id: d7_path_redirect label: Path Redirect migration_tags: - Drupal 7 + - Content source: plugin: d7_path_redirect process: diff --git a/web/modules/redirect/modules/redirect_404/config/install/views.view.redirect_404.yml b/web/modules/redirect/modules/redirect_404/config/install/views.view.redirect_404.yml index 75777dee4b..2a07e97cfa 100644 --- a/web/modules/redirect/modules/redirect_404/config/install/views.view.redirect_404.yml +++ b/web/modules/redirect/modules/redirect_404/config/install/views.view.redirect_404.yml @@ -76,6 +76,7 @@ display: columns: path: path count: count + daily_count: daily_count timestamp: timestamp info: path: @@ -92,6 +93,13 @@ display: separator: '' empty_column: false responsive: '' + daily_count: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: '' timestamp: sortable: true default_sort_order: desc @@ -206,6 +214,57 @@ display: format: unserialized key: '' plugin_id: serialized + daily_count: + id: daily_count + table: redirect_404 + field: daily_count + relationship: none + group_type: group + admin_label: '' + label: 'Daily count' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + format: unserialized + key: '' + plugin_id: serialized timestamp: id: timestamp table: redirect_404 diff --git a/web/modules/redirect/modules/redirect_404/redirect_404.info.yml b/web/modules/redirect/modules/redirect_404/redirect_404.info.yml index 1fbcea4167..fa92a99dc4 100644 --- a/web/modules/redirect/modules/redirect_404/redirect_404.info.yml +++ b/web/modules/redirect/modules/redirect_404/redirect_404.info.yml @@ -1,14 +1,14 @@ name: 'Redirect 404' type: module description: 'Logs 404 errors and allows users to create redirects for often requested but missing pages.' -# core: 8.x +core_version_requirement: ^8.8 || ^9 +configure: redirect_404.fix_404 dependencies: - - redirect - - views + - redirect:redirect + - drupal:views -# Information added by Drupal.org packaging script on 2018-10-16 -version: '8.x-1.3' -core: '8.x' +# Information added by Drupal.org packaging script on 2020-05-12 +version: '8.x-1.6' project: 'redirect' -datestamp: 1539682690 +datestamp: 1589312206 diff --git a/web/modules/redirect/modules/redirect_404/redirect_404.install b/web/modules/redirect/modules/redirect_404/redirect_404.install index 09190e7cf7..88869bcf20 100644 --- a/web/modules/redirect/modules/redirect_404/redirect_404.install +++ b/web/modules/redirect/modules/redirect_404/redirect_404.install @@ -35,6 +35,13 @@ function redirect_404_schema() { 'not null' => TRUE, 'default' => 0, ], + 'daily_count' => [ + 'description' => 'The number of requests with that path and language in a day.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ], 'timestamp' => [ 'description' => 'The timestamp of the last request with that path and language.', 'type' => 'int', @@ -60,3 +67,98 @@ function redirect_404_schema() { function redirect_404_update_8101() { \Drupal::database()->schema()->dropField('redirect_404', 'relevancy'); } + +/** + * Add daily count field to redirect_404 table and view. + */ +function redirect_404_update_8102() { + \Drupal::database()->schema()->addField('redirect_404', 'daily_count', [ + 'description' => 'The number of requests with that path and language in a day.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ]); + + $view_config = \Drupal::configFactory()->getEditable('views.view.redirect_404'); + + if (!$view_config->isNew()) { + $columns_key = 'display.default.display_options.style.options.columns'; + $columns = $view_config->get($columns_key); + $columns['daily_count'] = 'daily_count'; + $view_config->set($columns_key, $columns); + + $info_key = 'display.default.display_options.style.options.info'; + $info = $view_config->get($info_key); + $info['daily_count'] = [ + 'sortable' => TRUE, + 'default_sort_order' => 'desc', + 'align' => '', + 'separator' => '', + 'empty_column' => FALSE, + 'responsive' => '', + ]; + $view_config->set($info_key, $info); + + $fields = $view_config->get('display.default.display_options.fields'); + + $daily_count = [ + 'id' => 'daily_count', + 'table' => 'redirect_404', + 'field' => 'daily_count', + 'relationship' => 'none', + 'group_type' => 'group', + 'admin_label' => '', + 'label' => 'Daily count', + 'exclude' => FALSE, + 'alter' => [ + 'alter_text' => FALSE, + 'text' => '', + 'make_link' => FALSE, + 'path' => '', + 'absolute' => FALSE, + 'external' => FALSE, + 'replace_spaces' => FALSE, + 'path_case' => 'none', + 'trim_whitespace' => FALSE, + 'alt' => '', + 'rel' => '', + 'link_class' => '', + 'prefix' => '', + 'suffix' => '', + 'target' => '', + 'nl2br' => FALSE, + 'max_length' => 0, + 'word_boundary' => TRUE, + 'ellipsis' => TRUE, + 'more_link' => FALSE, + 'more_link_text' => '', + 'more_link_path' => '', + 'strip_tags' => FALSE, + 'trim' => FALSE, + 'preserve_tags' => '', + 'html' => FALSE, + ], + 'element_type' => '', + 'element_class' => '', + 'element_label_type' => '', + 'element_label_class' => '', + 'element_label_colon' => TRUE, + 'element_wrapper_type' => '', + 'element_wrapper_class' => '', + 'element_default_classes' => TRUE, + 'empty' => '', + 'hide_empty' => FALSE, + 'empty_zero' => FALSE, + 'hide_alter_empty' => TRUE, + 'format' => 'unserialized', + 'key' => '', + 'plugin_id' => 'serialized', + ]; + + $count_index = array_search('count', array_keys($fields)); + $fields = array_slice($fields, 0, $count_index + 1) + ['daily_count' => $daily_count] + array_slice($fields, $count_index); + + $view_config->set('display.default.display_options.fields', $fields)->save(); + } +} diff --git a/web/modules/redirect/modules/redirect_404/redirect_404.module b/web/modules/redirect/modules/redirect_404/redirect_404.module index b90d166e1c..cdcc5ffe73 100644 --- a/web/modules/redirect/modules/redirect_404/redirect_404.module +++ b/web/modules/redirect/modules/redirect_404/redirect_404.module @@ -18,6 +18,14 @@ function redirect_404_cron() { /** @var \Drupal\redirect_404\SqlRedirectNotFoundStorage $redirect_storage */ $redirect_storage = \Drupal::service('redirect.not_found_storage'); $redirect_storage->purgeOldRequests(); + + $last_daily_reset = \Drupal::state()->get('redirect_404.last_daily_reset', 0); + + if (date('d', $last_daily_reset) != date('d')) { + $redirect_storage->resetDailyCount(); + + \Drupal::state()->set('redirect_404.last_daily_reset', \Drupal::time()->getRequestTime()); + } } /** diff --git a/web/modules/redirect/modules/redirect_404/redirect_404.views.inc b/web/modules/redirect/modules/redirect_404/redirect_404.views.inc index bc18f93e1e..dcc8320c55 100644 --- a/web/modules/redirect/modules/redirect_404/redirect_404.views.inc +++ b/web/modules/redirect/modules/redirect_404/redirect_404.views.inc @@ -60,6 +60,18 @@ function redirect_404_views_data() { ], ]; + $data['redirect_404']['daily_count'] = [ + 'title' => t('Daily count'), + 'help' => t('The number of requests with that path and language in a day.'), + 'field' => [ + 'id' => 'numeric', + 'click sortable' => TRUE, + ], + 'filter' => [ + 'id' => 'numeric', + ], + ]; + $data['redirect_404']['timestamp'] = [ 'title' => t('Timestamp'), 'help' => t('The timestamp of the last request with that path and language.'), diff --git a/web/modules/redirect/modules/redirect_404/src/Controller/Fix404IgnoreController.php b/web/modules/redirect/modules/redirect_404/src/Controller/Fix404IgnoreController.php index a00331188c..4684e08af7 100644 --- a/web/modules/redirect/modules/redirect_404/src/Controller/Fix404IgnoreController.php +++ b/web/modules/redirect/modules/redirect_404/src/Controller/Fix404IgnoreController.php @@ -67,7 +67,7 @@ public function ignorePath(Request $request) { if (empty($ignored_paths) || !strpos($path, $ignored_paths)) { $this->redirectStorage->resolveLogRequest($path, $langcode); - drupal_set_message($this->t('Resolved the path %path in the database. Please check the ignored list and save the settings.', [ + $this->messenger()->addMessage($this->t('Resolved the path %path in the database. Please check the ignored list and save the settings.', [ '%path' => $path, ])); } diff --git a/web/modules/redirect/modules/redirect_404/src/RedirectNotFoundStorageInterface.php b/web/modules/redirect/modules/redirect_404/src/RedirectNotFoundStorageInterface.php index 27bc56de54..a310df1cde 100644 --- a/web/modules/redirect/modules/redirect_404/src/RedirectNotFoundStorageInterface.php +++ b/web/modules/redirect/modules/redirect_404/src/RedirectNotFoundStorageInterface.php @@ -50,4 +50,9 @@ public function listRequests(array $header = [], $search = NULL); */ public function purgeOldRequests(); + /** + * Resets the daily counts of 404 request logs. + */ + public function resetDailyCount(); + } diff --git a/web/modules/redirect/modules/redirect_404/src/SqlRedirectNotFoundStorage.php b/web/modules/redirect/modules/redirect_404/src/SqlRedirectNotFoundStorage.php index 98003ebaed..b1dd268db6 100644 --- a/web/modules/redirect/modules/redirect_404/src/SqlRedirectNotFoundStorage.php +++ b/web/modules/redirect/modules/redirect_404/src/SqlRedirectNotFoundStorage.php @@ -67,9 +67,11 @@ public function logRequest($path, $langcode) { ->key('path', $path) ->key('langcode', $langcode) ->expression('count', 'count + 1') + ->expression('daily_count', 'daily_count + 1') ->fields([ - 'timestamp' => REQUEST_TIME, + 'timestamp' => \Drupal::time()->getRequestTime(), 'count' => 1, + 'daily_count' => 1, 'resolved' => 0, ]) ->execute(); @@ -159,4 +161,13 @@ public function listRequests(array $header = [], $search = NULL) { return $results; } + /** + * {@inheritdoc} + */ + public function resetDailyCount() { + $this->database->update('redirect_404') + ->fields(['daily_count' => 0]) + ->execute(); + } + } diff --git a/web/modules/redirect/modules/redirect_404/src/Tests/Redirect404TestBase.php b/web/modules/redirect/modules/redirect_404/src/Tests/Redirect404TestBase.php deleted file mode 100644 index 2842aeb14c..0000000000 --- a/web/modules/redirect/modules/redirect_404/src/Tests/Redirect404TestBase.php +++ /dev/null @@ -1,144 +0,0 @@ -<?php - -namespace Drupal\redirect_404\Tests; - -use Drupal\Component\Render\FormattableMarkup; -use Drupal\simpletest\WebTestBase; - -/** - * This class provides methods specifically for testing redirect 404 paths. - */ -abstract class Redirect404TestBase extends WebTestBase { - - /** - * Modules to enable. - * - * @var array - */ - public static $modules = [ - 'redirect_404', - 'node', - 'path', - ]; - - /** - * Permissions for the admin user. - * - * @var array - */ - protected $adminPermissions = [ - 'administer redirects', - 'administer redirect settings', - 'access content', - 'bypass node access', - 'create url aliases', - 'administer url aliases', - ]; - - /** - * A user with administrative permissions. - * - * @var \Drupal\user\UserInterface - */ - protected $adminUser; - - /** - * {@inheritdoc} - */ - public function setUp() { - parent::setUp(); - - // Create an admin user. - $this->adminUser = $this->drupalCreateUser($this->adminPermissions); - $this->drupalLogin($this->adminUser); - - $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']); - } - - /** - * Passes if the language of the 404 path IS found on the loaded page. - * - * Because assertText() checks also in the Language select options, this - * specific assertion in the redirect 404 table body is needed. - * - * @param string $language - * The language to assert in the redirect 404 table body. - * @param string $body - * (optional) The table body xpath where to assert the language. Defaults - * to '//table/tbody'. - * @param string $message - * (optional) A message to display with the assertion. Do not translate - * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed - * variables in the message text, not t(). If left blank, a default message - * will be displayed. - * - * @return bool - * TRUE on pass, FALSE on fail. - */ - protected function assertLanguageInTableBody($language, $body = '//table/tbody', $message = '') { - return $this->assertLanguageInTableBodyHelper($language, $body, $message, FALSE); - } - - /** - * Passes if the language of the 404 path is NOT found on the loaded page. - * - * Because assertText() checks also in the Language select options, this - * specific assertion in the redirect 404 table body is needed. - * - * @param string $language - * The language to assert in the redirect 404 table body. - * @param string $body - * (optional) The table body xpath where to assert the language. Defaults - * to '//table/tbody'. - * @param string $message - * (optional) A message to display with the assertion. Do not translate - * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed - * variables in the message text, not t(). If left blank, a default message - * will be displayed. - * - * @return bool - * TRUE on pass, FALSE on fail. - */ - protected function assertNoLanguageInTableBody($language, $body = '//table/tbody', $message = '') { - return $this->assertLanguageInTableBodyHelper($language, $body, $message, TRUE); - } - - /** - * Helper for assertLanguageInTableBody and assertNoLanguageInTableBody. - * - * @param array $language - * The language to assert in the redirect 404 table body. - * @param string $body - * (optional) The table body xpath where to assert the language. Defaults - * to '//table/tbody'. - * @param string $message - * (optional) A message to display with the assertion. Do not translate - * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed - * variables in the message text, not t(). If left blank, a default message - * will be displayed. - * @param bool $not_exists - * (optional) TRUE if this language should not exist, FALSE if it should. - * Defaults to TRUE. - * - * @return bool - * TRUE on pass, FALSE on fail. - */ - protected function assertLanguageInTableBodyHelper($language, $body = '//table/tbody', $message = '', $not_exists = TRUE) { - if (!$message) { - if (!$not_exists) { - $message = new FormattableMarkup('Language "@language" found in 404 table.', ['@language' => $language]); - } - else { - $message = new FormattableMarkup('Language "@language" not found in 404 table.', ['@language' => $language]); - } - } - - if ($not_exists) { - return $this->assertFalse(strpos($this->xpath($body)[0]->asXML(), $language), $message); - } - else { - return $this->assertTrue(strpos($this->xpath($body)[0]->asXML(), $language), $message); - } - } - -} diff --git a/web/modules/redirect/modules/redirect_404/src/Tests/Fix404RedirectUILanguageTest.php b/web/modules/redirect/modules/redirect_404/tests/src/Functional/Fix404RedirectUILanguageTest.php similarity index 95% rename from web/modules/redirect/modules/redirect_404/src/Tests/Fix404RedirectUILanguageTest.php rename to web/modules/redirect/modules/redirect_404/tests/src/Functional/Fix404RedirectUILanguageTest.php index 89a14d2dc5..eb966b91b7 100644 --- a/web/modules/redirect/modules/redirect_404/src/Tests/Fix404RedirectUILanguageTest.php +++ b/web/modules/redirect/modules/redirect_404/tests/src/Functional/Fix404RedirectUILanguageTest.php @@ -1,12 +1,12 @@ <?php -namespace Drupal\redirect_404\Tests; +namespace Drupal\Tests\redirect_404\Functional; use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Url; use Drupal\language\Entity\ConfigurableLanguage; -use Drupal\redirect\Tests\AssertRedirectTrait; +use Drupal\Tests\redirect\Functional\AssertRedirectTrait; /** * UI tests for redirect_404 module with language and content translation. @@ -105,7 +105,7 @@ public function testFix404RedirectList() { $this->assertUrl('admin/config/search/redirect/404'); $this->assertText('There are no 404 errors to fix.'); // Check if the redirect works as expected. - $this->assertRedirect('fr/testing', 'fr/node', 'HTTP/1.1 301 Moved Permanently'); + $this->assertRedirect('fr/testing', 'fr/node', 301); // Test removing a redirect assignment, visit again the non existing page. $this->drupalGet('admin/config/search/redirect'); @@ -136,7 +136,7 @@ public function testFix404RedirectList() { $this->drupalGet('admin/config/search/redirect'); $this->assertLanguageInTableBody('Spanish'); // Check if the redirect works as expected. - $this->assertRedirect('es/testing', 'es/node', 'HTTP/1.1 301 Moved Permanently'); + $this->assertRedirect('es/testing', 'es/node', 301); // Visit multiple non existing pages to test the Redirect 404 View. $this->drupalGet('testing1'); @@ -197,7 +197,7 @@ public function testFix404RedirectList() { $this->assertLanguageInTableBody('Spanish'); $this->assertLanguageInTableBody('English'); // Check if the redirect works as expected. - $this->assertRedirect('/testing1', '/node', 'HTTP/1.1 301 Moved Permanently'); + $this->assertRedirect('/testing1', '/node', 301); } } diff --git a/web/modules/redirect/modules/redirect_404/src/Tests/Fix404RedirectUITest.php b/web/modules/redirect/modules/redirect_404/tests/src/Functional/Fix404RedirectUITest.php similarity index 88% rename from web/modules/redirect/modules/redirect_404/src/Tests/Fix404RedirectUITest.php rename to web/modules/redirect/modules/redirect_404/tests/src/Functional/Fix404RedirectUITest.php index 088cbba47a..2045bf8857 100644 --- a/web/modules/redirect/modules/redirect_404/src/Tests/Fix404RedirectUITest.php +++ b/web/modules/redirect/modules/redirect_404/tests/src/Functional/Fix404RedirectUITest.php @@ -1,6 +1,6 @@ <?php -namespace Drupal\redirect_404\Tests; +namespace Drupal\Tests\redirect_404\Functional; use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Url; @@ -158,10 +158,9 @@ public function testIgnorePages() { $this->clickLink('Ignore'); $this->assertUrl('admin/config/search/redirect/settings?ignore=' . $path_to_ignore . $destination); $this->assertText('Resolved the path ' . $path_to_ignore . ' in the database. Please check the ignored list and save the settings.'); - $xpath = $this->xpath('//*[@id="edit-ignore-pages"]')[0]->asXML(); - $this->assertTrue(strpos($xpath, $node_to_ignore), $node_to_ignore . " in 'Path to ignore' found"); - $this->assertTrue(strpos($xpath, $terms_to_ignore), $terms_to_ignore . " in 'Path to ignore' found"); - $this->assertTrue(strpos($xpath, $path_to_ignore), $path_to_ignore . " in 'Path to ignore' found"); + $this->assertSession()->elementContains('css', '#edit-ignore-pages', $node_to_ignore); + $this->assertSession()->elementContains('css', '#edit-ignore-pages', $terms_to_ignore); + $this->assertSession()->elementContains('css', '#edit-ignore-pages', $path_to_ignore); // Save the path with wildcard, but omitting the leading slash. $nodes_to_ignore = 'node/*'; @@ -169,18 +168,18 @@ public function testIgnorePages() { $this->drupalPostForm(NULL, $edit, 'Save configuration'); // Should redirect to 'Fix 404'. Check the 404 entry is not shown anymore. $this->assertUrl('admin/config/search/redirect/404'); - $this->assertText('Configuration was saved.'); + $this->assertText('The configuration options have been saved.'); $this->assertNoText('node/' . $node2->id() . '/test'); $this->assertText('There are no 404 errors to fix.'); // Go back to the settings to check the 'Path to ignore' configurations. $this->drupalGet('admin/config/search/redirect/settings'); - $xpath = $this->xpath('//*[@id="edit-ignore-pages"]')[0]->asXML(); + $xpath = $this->xpath('//*[@id="edit-ignore-pages"]')[0]->getHtml(); // Check that the new page to ignore has been saved with leading slash. - $this->assertTrue(strpos($xpath, '/' . $nodes_to_ignore), '/' . $nodes_to_ignore . " in 'Path to ignore' found"); - $this->assertTrue(strpos($xpath, $terms_to_ignore), $terms_to_ignore . " in 'Path to ignore' found"); - $this->assertFalse(strpos($xpath, $node_to_ignore), $node_to_ignore . " in 'Path to ignore' found"); - $this->assertFalse(strpos($xpath, $path_to_ignore), $path_to_ignore . " in 'Path to ignore' found"); + $this->assertSession()->elementContains('css', '#edit-ignore-pages', '/'. $nodes_to_ignore); + $this->assertSession()->elementContains('css', '#edit-ignore-pages', $terms_to_ignore); + $this->assertSession()->elementNotContains('css', '#edit-ignore-pages', $node_to_ignore); + $this->assertSession()->elementNotContains('css', '#edit-ignore-pages', $path_to_ignore); } } diff --git a/web/modules/redirect/modules/redirect_404/src/Tests/Redirect404LogSuppressorTest.php b/web/modules/redirect/modules/redirect_404/tests/src/Functional/Redirect404LogSuppressorTest.php similarity index 74% rename from web/modules/redirect/modules/redirect_404/src/Tests/Redirect404LogSuppressorTest.php rename to web/modules/redirect/modules/redirect_404/tests/src/Functional/Redirect404LogSuppressorTest.php index ef021b553e..d133b77b5d 100644 --- a/web/modules/redirect/modules/redirect_404/src/Tests/Redirect404LogSuppressorTest.php +++ b/web/modules/redirect/modules/redirect_404/tests/src/Functional/Redirect404LogSuppressorTest.php @@ -1,6 +1,8 @@ <?php -namespace Drupal\redirect_404\Tests; +namespace Drupal\Tests\redirect_404\Functional; + +use Drupal\Core\Database\Database; /** * Tests suppressing 404 logs if the suppress_404 setting is enabled. @@ -56,8 +58,8 @@ public function testSuppress404Events() { $this->assertResponse(403); // Assert the events are logged in the dblog reports. - $this->assertEqual(db_query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'page not found'")->fetchField(), 1); - $this->assertEqual(db_query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'access denied'")->fetchField(), 1); + $this->assertEqual(Database::getConnection()->query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'page not found'")->fetchField(), 1); + $this->assertEqual(Database::getConnection()->query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'access denied'")->fetchField(), 1); // Login as admin and enable suppress_404 to avoid logging the 404 event. $this->drupalLogin($this->adminUser); @@ -73,8 +75,8 @@ public function testSuppress404Events() { // Assert only the new access denied event is logged now. $this->drupalLogin($this->adminUser); - $this->assertEqual(db_query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'page not found'")->fetchField(), 1); - $this->assertEqual(db_query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'access denied'")->fetchField(), 2); + $this->assertEqual(Database::getConnection()->query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'page not found'")->fetchField(), 1); + $this->assertEqual(Database::getConnection()->query("SELECT COUNT(*) FROM {watchdog} WHERE type = 'access denied'")->fetchField(), 2); } } diff --git a/web/modules/redirect/modules/redirect_404/tests/src/Functional/Redirect404TestBase.php b/web/modules/redirect/modules/redirect_404/tests/src/Functional/Redirect404TestBase.php new file mode 100644 index 0000000000..f83a7c63c2 --- /dev/null +++ b/web/modules/redirect/modules/redirect_404/tests/src/Functional/Redirect404TestBase.php @@ -0,0 +1,89 @@ +<?php + +namespace Drupal\Tests\redirect_404\Functional; + +use Drupal\Component\Render\FormattableMarkup; +use Drupal\Tests\BrowserTestBase; + +/** + * This class provides methods specifically for testing redirect 404 paths. + */ +abstract class Redirect404TestBase extends BrowserTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = [ + 'redirect_404', + 'node', + 'path', + ]; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * Permissions for the admin user. + * + * @var array + */ + protected $adminPermissions = [ + 'administer redirects', + 'administer redirect settings', + 'access content', + 'bypass node access', + 'create url aliases', + 'administer url aliases', + ]; + + /** + * A user with administrative permissions. + * + * @var \Drupal\user\UserInterface + */ + protected $adminUser; + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + + // Create an admin user. + $this->adminUser = $this->drupalCreateUser($this->adminPermissions); + $this->drupalLogin($this->adminUser); + + $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']); + } + + /** + * Passes if the language of the 404 path IS found on the loaded page. + * + * Because assertText() checks also in the Language select options, this + * specific assertion in the redirect 404 table body is needed. + * + * @param string $language + * The language to assert in the redirect 404 table body. + */ + protected function assertLanguageInTableBody($language) { + $this->assertSession()->elementContains('css', 'table tbody', $language); + } + + /** + * Passes if the language of the 404 path is NOT found on the loaded page. + * + * Because assertText() checks also in the Language select options, this + * specific assertion in the redirect 404 table body is needed. + * + * @param string $language + * The language to assert in the redirect 404 table body. + */ + protected function assertNoLanguageInTableBody($language) { + $this->assertSession()->elementNotContains('css', 'table tbody', $language); + } + +} diff --git a/web/modules/redirect/modules/redirect_404/tests/src/Kernel/Fix404RedirectCronJobTest.php b/web/modules/redirect/modules/redirect_404/tests/src/Kernel/Fix404RedirectCronJobTest.php index 6ed6d6774b..c25818e4bc 100644 --- a/web/modules/redirect/modules/redirect_404/tests/src/Kernel/Fix404RedirectCronJobTest.php +++ b/web/modules/redirect/modules/redirect_404/tests/src/Kernel/Fix404RedirectCronJobTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\redirect_404\Kernel; use Drupal\KernelTests\KernelTestBase; +use Drupal\Core\Database\Database; /** * Tests the clean up cron job for redirect_404. @@ -26,12 +27,12 @@ protected function setUp() { $this->installSchema('redirect_404', 'redirect_404'); // Insert some records in the test table with a given count and timestamp. - $this->insert404Row('/test1', 12, strtotime('now')); - $this->insert404Row('/test2', 5, strtotime('-1 hour')); - $this->insert404Row('/test3', 315, strtotime('-1 week')); - $this->insert404Row('/test4', 300, strtotime('-1 month')); - $this->insert404Row('/test5', 1557, strtotime('-1 week')); - $this->insert404Row('/test6', 1, strtotime('-1 day')); + $this->insert404Row('/test1', 12, 5, strtotime('now')); + $this->insert404Row('/test2', 5, 3, strtotime('-1 hour')); + $this->insert404Row('/test3', 315, 0, strtotime('-1 week')); + $this->insert404Row('/test4', 300, 0, strtotime('-1 month')); + $this->insert404Row('/test5', 1557, 0, strtotime('-1 week')); + $this->insert404Row('/test6', 1, 0, strtotime('-1 day')); } /** @@ -45,13 +46,13 @@ function testRedirect404CronJob() { ->save(); // Check that there are 6 rows in the redirect_404 table. - $result = db_query("SELECT COUNT(*) FROM {redirect_404}")->fetchField(); + $result = Database::getConnection()->query("SELECT COUNT(*) FROM {redirect_404}")->fetchField(); $this->assertEquals(6, $result); // Run cron to drop 3 rows from the redirect_404 test table. redirect_404_cron(); - $result = db_query("SELECT COUNT(*) FROM {redirect_404}")->fetchField(); + $result = Database::getConnection()->query("SELECT COUNT(*) FROM {redirect_404}")->fetchField(); $this->assertEquals(3, $result); // Check there are only 3 rows with more count in the redirect_404 table. @@ -85,13 +86,13 @@ function testRedirect404CronJobKeepAllButOne() { ->save(); // Check that there are 6 rows in the redirect_404 table. - $result = db_query("SELECT COUNT(*) FROM {redirect_404}")->fetchField(); + $result = Database::getConnection()->query("SELECT COUNT(*) FROM {redirect_404}")->fetchField(); $this->assertEquals(6, $result); // Run cron to drop just 1 row from the redirect_404 test table. redirect_404_cron(); - $result = db_query("SELECT COUNT(*) FROM {redirect_404}")->fetchField(); + $result = Database::getConnection()->query("SELECT COUNT(*) FROM {redirect_404}")->fetchField(); $this->assertEquals(5, $result); // Check only the row with least count has been removed from the table. @@ -114,6 +115,34 @@ function testRedirect404CronJobKeepAllButOne() { } } + /** + * Tests resetting the daily counts in the redirect_404 table. + */ + function testRedirect404CronJobDailyCountReset() { + // Check that there are 2 rows with daily count value bigger than 0. + $result = \Drupal::database()->query("SELECT COUNT(*) FROM {redirect_404} WHERE daily_count > 0") + ->fetchField(); + $this->assertEquals(2, $result); + + // Run cron to reset the daily counts in the redirect_404 test table. + redirect_404_cron(); + + $result = \Drupal::database()->query("SELECT COUNT(*) FROM {redirect_404} WHERE daily_count > 0") + ->fetchField(); + $this->assertEquals(0, $result); + + // Add new row with daily count value. + $this->insert404Row('/test7', 2, 2, time()); + + redirect_404_cron(); + + // Check if the row exists and the daily count isn't reset after cron run. + $this->assert404Row('/test7'); + $result = \Drupal::database()->query("SELECT COUNT(*) FROM {redirect_404} WHERE daily_count > 0") + ->fetchField(); + $this->assertEquals(1, $result); + } + /** * Inserts a 404 request log in the redirect_404 test table. * @@ -121,21 +150,24 @@ function testRedirect404CronJobKeepAllButOne() { * The path of the request. * @param int $count * (optional) The visits count of the request. + * @param int $daily_count + * (optional) The visits count of the request for a day. * @param int $timestamp * (optional) The timestamp of the last visited request. * @param string $langcode * (optional) The langcode of the request. */ - protected function insert404Row($path, $count = 1, $timestamp = 0, $langcode = 'en') { - db_insert('redirect_404') - ->fields([ - 'path' => $path, - 'langcode' => $langcode, - 'count' => $count, - 'timestamp' => $timestamp, - 'resolved' => 0, - ]) - ->execute(); + protected function insert404Row($path, $count = 1, $daily_count = 0, $timestamp = 0, $langcode = 'en') { + \Drupal::database()->insert('redirect_404') + ->fields([ + 'path' => $path, + 'langcode' => $langcode, + 'count' => $count, + 'daily_count' => $daily_count, + 'timestamp' => $timestamp, + 'resolved' => 0, + ]) + ->execute(); } /** @@ -174,7 +206,7 @@ protected function assertNo404Row($path, $langcode = 'en') { * table, FALSE if it should. Defaults to TRUE. */ protected function assert404RowHelper($path, $langcode = 'en', $not_exists = TRUE) { - $result = db_select('redirect_404', 'r404') + $result = Database::getConnection()->select('redirect_404', 'r404') ->fields('r404', ['path']) ->condition('path', $path) ->condition('langcode', $langcode) diff --git a/web/modules/redirect/modules/redirect_domain/redirect_domain.info.yml b/web/modules/redirect/modules/redirect_domain/redirect_domain.info.yml index aeb66d4535..eda49f417f 100644 --- a/web/modules/redirect/modules/redirect_domain/redirect_domain.info.yml +++ b/web/modules/redirect/modules/redirect_domain/redirect_domain.info.yml @@ -1,13 +1,13 @@ name: 'Redirect Domain' type: module description: 'Allows users to redirect between domains.' -# core: 8.x +core_version_requirement: ^8.8 || ^9 +configure: redirect_domain.domain_list dependencies: - - redirect + - redirect:redirect -# Information added by Drupal.org packaging script on 2018-10-16 -version: '8.x-1.3' -core: '8.x' +# Information added by Drupal.org packaging script on 2020-05-12 +version: '8.x-1.6' project: 'redirect' -datestamp: 1539682690 +datestamp: 1589312206 diff --git a/web/modules/redirect/modules/redirect_domain/redirect_domain.module b/web/modules/redirect/modules/redirect_domain/redirect_domain.module index f60b329bf1..a8699ed947 100644 --- a/web/modules/redirect/modules/redirect_domain/redirect_domain.module +++ b/web/modules/redirect/modules/redirect_domain/redirect_domain.module @@ -28,6 +28,19 @@ function redirect_domain_help($route_name, RouteMatchInterface $route_match) { $output .= '<li>' . t('Request: example.com/redirect => Response: redirected.com/example-path') . '</li>'; $output .= '<li>' . t('Request: foo.com/any-path => Response: bar.com') . '</li>'; $output .= '</ul>'; + $output .= '<h5>' . t('Precedence') . '</h5>'; + $output .= t('Top-down precedence is used. This means the first matching redirect that is found will be taken. For example, assume these redirect rules:') . '</p>'; + $output .= '<ul>'; + $output .= '<li>' . t('foo.com/some/path => somedomain.com/path') . '</li>'; + $output .= '<li>' . t('foo.com/* => wildcardredirect.com') . '</li>'; + $output .= '<li>' . t('foo.com/other/path => otherdomain.com/path') . '</li>'; + $output .= '</ul>'; + $output .= '<p>' . t('The following redirects would actually occur:') . '</p>'; + $output .= '<ul>'; + $output .= '<li>' . t('foo.com/some/path => somedomain.com/path') . '</li>'; + $output .= '<li>' . t('foo.com/other/path => wildcardredirect.com') . '</li>'; + $output .= '</ul>'; + return $output; } } diff --git a/web/modules/redirect/modules/redirect_domain/src/EventSubscriber/DomainRedirectRequestSubscriber.php b/web/modules/redirect/modules/redirect_domain/src/EventSubscriber/DomainRedirectRequestSubscriber.php index bc949b81fd..b2ebbf303d 100644 --- a/web/modules/redirect/modules/redirect_domain/src/EventSubscriber/DomainRedirectRequestSubscriber.php +++ b/web/modules/redirect/modules/redirect_domain/src/EventSubscriber/DomainRedirectRequestSubscriber.php @@ -80,9 +80,16 @@ public function onKernelRequestCheckDomainRedirect(GetResponseEvent $event) { $protocol = $request->getScheme() . '://'; $destination = NULL; + // Prior to being saved the source domain value has any periods replaced + // with a colon, which makes it suitable for use as a key. In order to + // match against those values the current hostname must be similarly + // converted. + // @see \Drupal\redirect_domain\Form\RedirectDomainForm::submitForm() + $converted_host = str_replace('.', ':', $host); + // Checks if there is a redirect domain in the configuration. - if (isset($domains[str_replace('.', ':', $host)])) { - foreach ($domains[str_replace('.', ':', $host)] as $item) { + if (isset($domains[$converted_host])) { + foreach ($domains[$converted_host] as $item) { if ($this->pathMatcher->matchPath($path, $item['sub_path'])) { $destination = $item['destination']; break; diff --git a/web/modules/redirect/modules/redirect_domain/src/Form/RedirectDomainForm.php b/web/modules/redirect/modules/redirect_domain/src/Form/RedirectDomainForm.php index 1bf5b9a870..46f1a6903e 100644 --- a/web/modules/redirect/modules/redirect_domain/src/Form/RedirectDomainForm.php +++ b/web/modules/redirect/modules/redirect_domain/src/Form/RedirectDomainForm.php @@ -150,6 +150,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { foreach ($redirects as $redirect) { if (!empty($redirect['from']) && !empty($redirect['destination'])) { // Replace '.' with ':' for an eligible key. + // @see \Drupal\redirect_domain\EventSubscriber\DomainRedirectRequestSubscriber::onKernelRequestCheckDomainRedirect() $redirect['from'] = str_replace('.', ':', $redirect['from']); $domain_redirects[$redirect['from']][] = [ 'sub_path' => '/' . ltrim($redirect['sub_path'], '/'), @@ -160,6 +161,6 @@ public function submitForm(array &$form, FormStateInterface $form_state) { } $domain_config->set('domain_redirects', $domain_redirects); $domain_config->save(); - drupal_set_message($this->t('The domain redirects have been saved.')); + $this->messenger()->addMessage($this->t('The domain redirects have been saved.')); } } diff --git a/web/modules/redirect/modules/redirect_domain/src/Tests/RedirectDomainUITest.php b/web/modules/redirect/modules/redirect_domain/tests/src/FunctionalJavascript/RedirectDomainUITest.php similarity index 63% rename from web/modules/redirect/modules/redirect_domain/src/Tests/RedirectDomainUITest.php rename to web/modules/redirect/modules/redirect_domain/tests/src/FunctionalJavascript/RedirectDomainUITest.php index d50791f377..f3c59c3ac8 100644 --- a/web/modules/redirect/modules/redirect_domain/src/Tests/RedirectDomainUITest.php +++ b/web/modules/redirect/modules/redirect_domain/tests/src/FunctionalJavascript/RedirectDomainUITest.php @@ -1,15 +1,15 @@ <?php -namespace Drupal\redirect_domain\Tests; +namespace Drupal\Tests\redirect_domain\FunctionalJavascript; -use Drupal\simpletest\WebTestBase; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; /** * Tests the UI for domain redirect. * * @group redirect_domain */ -class RedirectDomainUITest extends WebTestBase { +class RedirectDomainUITest extends WebDriverTestBase { /** * Modules to enable. @@ -20,6 +20,11 @@ class RedirectDomainUITest extends WebTestBase { 'redirect_domain', ]; + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + /** * Tests domain redirect. */ @@ -38,18 +43,18 @@ public function testDomainRedirect() { $this->assertFieldByName('redirects[0][destination]'); // Add another field for new domain redirect. - $this->drupalPostAjaxForm(NULL, [], ['op' => t('Add another')]); + $page = $this->getSession()->getPage(); + $page->pressButton('Add another'); + $this->assertSession()->assertWaitOnAjaxRequest(); // Add two new domain redirects. - $edit = [ - 'redirects[0][from]' => 'foo.example.org', - 'redirects[0][sub_path]' => '//sub-path', - 'redirects[0][destination]' => 'www.example.org/foo', - 'redirects[1][from]' => 'bar.example.org', - 'redirects[1][sub_path]' => '', - 'redirects[1][destination]' => 'www.example.org/bar', - ]; - $this->drupalPostForm(NULL, $edit, t('Save')); + $page->fillField('redirects[0][from]', 'foo.example.org'); + $page->fillField('redirects[0][sub_path]', '//sub-path'); + $page->fillField('redirects[0][destination]', 'www.example.org/foo'); + $page->fillField('redirects[1][from]', 'bar.example.org'); + $page->fillField('redirects[1][sub_path]', ''); + $page->fillField('redirects[1][destination]', 'www.example.org/bar'); + $page->pressButton('Save'); // Check the new domain redirects. $this->assertFieldByName('redirects[0][from]', 'foo.example.org'); diff --git a/web/modules/redirect/modules/redirect_domain/tests/src/Unit/DomainRedirectRequestSubscriberTest.php b/web/modules/redirect/modules/redirect_domain/tests/src/Unit/DomainRedirectRequestSubscriberTest.php index 89453ab9d8..24cc37c018 100644 --- a/web/modules/redirect/modules/redirect_domain/tests/src/Unit/DomainRedirectRequestSubscriberTest.php +++ b/web/modules/redirect/modules/redirect_domain/tests/src/Unit/DomainRedirectRequestSubscriberTest.php @@ -53,6 +53,20 @@ public function testDomainRedirect($request_url, $response_url) { 'destination' => 'redirected.com/redirect', ], ], + 'wildcardtest:com' => [ + [ + 'sub_path' => '/some/path', + 'destination' => 'somedomain.com/path', + ], + [ + 'sub_path' => '/*', + 'destination' => 'wildcardredirect.com', + ], + [ + 'sub_path' => '/other/path', + 'destination' => 'otherdomain.com/path', + ], + ], ], ], 'redirect.settings' => [ @@ -75,7 +89,7 @@ public function testDomainRedirect($request_url, $response_url) { $config_factory = $this->getConfigFactoryStub($data); // Create a mock path matcher. - $route_match = $this->getMock(RouteMatchInterface::class); + $route_match = $this->createMock(RouteMatchInterface::class); $path_matcher = new PathMatcher($config_factory, $route_match); $subscriber = new DomainRedirectRequestSubscriber( @@ -122,7 +136,7 @@ protected function getGetResponseEventStub($path_info, $query_string) { $http_kernel = $this->getMockBuilder(HttpKernelInterface::class) ->getMock(); - return new GetResponseEvent($http_kernel, $request, 'test'); + return new GetResponseEvent($http_kernel, $request, HttpKernelInterface::MASTER_REQUEST); } /** @@ -139,6 +153,9 @@ public function providerDomains() { $datasets[] = ['http://nonexisting.com', NULL]; $datasets[] = ['http://simpleexample.com/wrongpath', NULL]; $datasets[] = ['http://foo.com/fixedredirect', 'http://bar.com/fixedredirect']; + $datasets[] = ['http://wildcardtest.com/some/path', 'http://somedomain.com/path']; + $datasets[] = ['http://wildcardtest.com/other/path', 'http://wildcardredirect.com']; + $datasets[] = ['http://wildcardtest.com/does-not-exist', 'http://wildcardredirect.com']; return $datasets; } } diff --git a/web/modules/redirect/redirect.drush.inc b/web/modules/redirect/redirect.drush.inc index ffe98ce05c..e9bc05892f 100644 --- a/web/modules/redirect/redirect.drush.inc +++ b/web/modules/redirect/redirect.drush.inc @@ -4,21 +4,21 @@ * @file * Drush integration for the redirect module. */ - +use Drupal\Component\Utility\Environment; /** * Implements hook_drush_command(). */ function redirect_drush_command() { - $items['generate-redirects'] = array( + $items['generate-redirects'] = [ 'description' => 'Create redirects.', - 'drupal dependencies' => array('devel_generate'), - 'arguments' => array( + 'drupal dependencies' => ['devel_generate'], + 'arguments' => [ 'count' => 'Number of redirects to generate.', - ), - 'options' => array( + ], + 'options' => [ 'delete' => 'Delete all redirects before generating new ones.', - ), - ); + ], + ]; return $items; } @@ -53,7 +53,7 @@ function redirect_run_unprogressive_batch() { } // Attempt to increase the execution time. - drupal_set_time_limit(240); + Environment::setTimeLimit(240); // Build the batch array. $batch = call_user_func_array($batch_callback, $args); diff --git a/web/modules/redirect/redirect.generate.inc b/web/modules/redirect/redirect.generate.inc index 6120ae19b3..50bf69b074 100644 --- a/web/modules/redirect/redirect.generate.inc +++ b/web/modules/redirect/redirect.generate.inc @@ -7,7 +7,9 @@ use Drupal\Component\Utility\Random; use Drupal\devel_generate\DevelGenerateBase; +use Drupal\node\NodeInterface; use Drupal\redirect\Entity\Redirect; +use Drupal\Core\Database\Database; /** * @file @@ -15,21 +17,21 @@ */ function redirect_generate_form() { - $form['count'] = array( + $form['count'] = [ '#type' => 'textfield', '#title' => t('How many URL redirects would you like to generate?'), '#default_value' => 50, '#size' => 4, - ); - $form['delete'] = array( + ]; + $form['delete'] = [ '#type' => 'checkbox', '#title' => t('Delete all URL redirects before generating new URL redirects.'), '#default_value' => FALSE, - ); - $form['submit'] = array( + ]; + $form['submit'] = [ '#type' => 'submit', '#value' => t('Generate'), - ); + ]; return $form; } @@ -42,28 +44,28 @@ function redirect_generate_form_submit(&$form, &$form_state) { function redirect_generate_redirects_batch_info($count, $delete = FALSE) { if ($delete) { - $operations[] = array('redirect_generate_batch_delete', array()); + $operations[] = ['redirect_generate_batch_delete', []]; } - $operations[] = array('redirect_generate_batch_generate', array($count)); + $operations[] = ['redirect_generate_batch_generate', [$count]]; - return array( + return [ 'operations' => $operations, 'finished' => 'redirect_generate_batch_finished', 'file' => drupal_get_path('module', 'redirect') . '/redirect.generate.inc', - ); + ]; } function redirect_generate_batch_delete(array &$context) { if (empty($context['sandbox'])) { - $context['sandbox'] = array(); + $context['sandbox'] = []; $context['sandbox']['progress'] = 0; $context['sandbox']['current_rid'] = 0; - $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT rid) FROM {redirect}')->fetchField(); + $context['sandbox']['max'] = Database::getConnection()->query('SELECT COUNT(DISTINCT rid) FROM {redirect}')->fetchField(); } $limit = 20; - $rids = db_query_range("SELECT rid FROM {redirect} WHERE rid > :rid ORDER BY rid", 0, $limit, array(':rid' => $context['sandbox']['current_rid']))->fetchCol(); + $rids = Database::getConnection()->queryRange("SELECT rid FROM {redirect} WHERE rid > :rid ORDER BY rid", 0, $limit, [':rid' => $context['sandbox']['current_rid']])->fetchCol(); foreach (redirect_repository()->loadMultiple($rids) as $redirect) { $redirect->delete(); } @@ -71,7 +73,7 @@ function redirect_generate_batch_delete(array &$context) { // Update our progress information. $context['sandbox']['progress'] += count($rids); $context['sandbox']['current_rid'] = end($rids); - $context['message'] = t('Deleted URL redirect @rid.', array('@rid' => end($rids))); + $context['message'] = t('Deleted URL redirect @rid.', ['@rid' => end($rids)]); // Inform the batch engine that we are not finished, // and provide an estimation of the completion level we reached. @@ -82,12 +84,12 @@ function redirect_generate_batch_delete(array &$context) { function redirect_generate_batch_generate($num, array &$context) { if (empty($context['sandbox'])) { - $context['sandbox'] = array(); + $context['sandbox'] = []; $context['sandbox']['progress'] = 0; $context['sandbox']['max'] = $num; $query = \Drupal::database()->select('node', 'n'); $query->addField('n', 'nid'); - $query->condition('n.status', NODE_PUBLISHED); + $query->condition('n.status', NodeInterface::PUBLISHED); $query->addTag('node_access'); $context['sandbox']['nids'] = $query->execute()->fetchAllKeyed(0, 0); } @@ -96,7 +98,7 @@ function redirect_generate_batch_generate($num, array &$context) { $limit = 20; $types = array_keys(redirect_status_code_options()); - $languages = \Drupal::moduleHandler()->moduleExists('locale') ? array_keys(\Drupal::languageManager()->getLanguages()) : array(); + $languages = \Drupal::moduleHandler()->moduleExists('locale') ? array_keys(\Drupal::languageManager()->getLanguages()) : []; for ($i = 0; $i < min($limit, $context['sandbox']['max'] - $context['sandbox']['progress']); $i++) { $rand = mt_rand(0, 100); @@ -104,8 +106,8 @@ function redirect_generate_batch_generate($num, array &$context) { $redirect = Redirect::create(); $source = _redirect_generate_url(); - $source_options = array(); - $redirect_options = array(); + $source_options = []; + $redirect_options = []; if ($context['sandbox']['nids'] && $rand >= 40) { $redirect_target = 'node/' . array_rand($context['sandbox']['nids']); @@ -128,7 +130,7 @@ function redirect_generate_batch_generate($num, array &$context) { $redirect->setLanguage($languages[array_rand($languages)]); } - $query = array(); + $query = []; if ($rand <= 30) { $query = _redirect_generate_querystring(); } @@ -141,10 +143,10 @@ function redirect_generate_batch_generate($num, array &$context) { if (mt_rand(0, 1)) { $query = \Drupal::database(); $query->update('redirect') - ->fields(array( + ->fields([ 'count' => mt_rand(1, 500), - 'access' => mt_rand(REQUEST_TIME - 31536000, REQUEST_TIME), - )) + 'access' => mt_rand(Drupal::time()->getRequestTime() - 31536000, Drupal::time()->getRequestTime()), + ]) ->condition('rid', $redirect->id()) ->execute(); } @@ -165,20 +167,20 @@ function redirect_generate_batch_generate($num, array &$context) { function redirect_generate_batch_finished($success, $results, $operations) { if ($success) { - drupal_set_message(\Drupal::translation()->formatPlural(count($results), 'One URL redirect created.', '@count URL redirects created.')); + \Drupal::messenger()->addMessage(\Drupal::translation()->formatPlural(count($results), 'One URL redirect created.', '@count URL redirects created.')); } else { // An error occurred. // $operations contains the operations that remained unprocessed. $error_operation = reset($operations); - drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE)))); + \Drupal::messenger()->addMessage(t('An error occurred while processing @operation with arguments : @args', ['@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE)])); } } function _redirect_generate_url($external = FALSE, $max_levels = 2) { - $url = array(); + $url = []; if ($external) { - $tlds = array('com', 'net', 'org'); + $tlds = ['com', 'net', 'org']; $url[] = 'http://www.example.'. $tlds[array_rand($tlds)]; } $max_levels = mt_rand($external ? 0 : 1, $max_levels); @@ -189,6 +191,6 @@ function _redirect_generate_url($external = FALSE, $max_levels = 2) { } function _redirect_generate_querystring() { - $query = array(DevelGenerateBase::generateWord(mt_rand(1, 3)) => DevelGenerateBase::generateWord(mt_rand(2, 4))); + $query = [DevelGenerateBase::generateWord(mt_rand(1, 3)) => DevelGenerateBase::generateWord(mt_rand(2, 4))]; return $query; } diff --git a/web/modules/redirect/redirect.info.yml b/web/modules/redirect/redirect.info.yml index a1e6eb15d6..687cbe6679 100644 --- a/web/modules/redirect/redirect.info.yml +++ b/web/modules/redirect/redirect.info.yml @@ -1,15 +1,15 @@ name: Redirect type: module description: Allows users to redirect from old URLs to new URLs. -# core: 8.x +core_version_requirement: ^8.8 || ^9 configure: redirect.settings dependencies: + - drupal:path_alias - drupal:link - drupal:views -# Information added by Drupal.org packaging script on 2018-10-16 -version: '8.x-1.3' -core: '8.x' +# Information added by Drupal.org packaging script on 2020-05-12 +version: '8.x-1.6' project: 'redirect' -datestamp: 1539682690 +datestamp: 1589312206 diff --git a/web/modules/redirect/redirect.install b/web/modules/redirect/redirect.install index 31a8ee693f..536d678eb9 100644 --- a/web/modules/redirect/redirect.install +++ b/web/modules/redirect/redirect.install @@ -5,14 +5,14 @@ * Update hooks for the Redirect module. */ -use Drupal\redirect\Entity\Redirect; +use Drupal\Core\Config\FileStorage; +use Drupal\Core\Config\InstallStorage; use Drupal\Core\Database\Database; +use Drupal\redirect\Entity\Redirect; use Drupal\system\Entity\Action; +use Drupal\user\Entity\Role; use Drupal\views\Entity\View; use Symfony\Component\Yaml\Yaml; -use Drupal\Core\Config\InstallStorage; -use Drupal\Core\Config\FileStorage; -use Drupal\user\Entity\Role; /** * Rehash redirects to account for case insensitivity. @@ -23,7 +23,7 @@ function redirect_update_8100(&$sandbox) { $sandbox['progress'] = 0; $sandbox['current_rid'] = 0; // Note, because MySQL can treat `foo = LOWER(foo)`, all records must be checked. - $sandbox['max'] = db_query('SELECT COUNT(1) FROM {redirect}')->fetchField(); + $sandbox['max'] = Database::getConnection()->query('SELECT COUNT(1) FROM {redirect}')->fetchField(); } $result = \Drupal::database()->select('redirect', 'r') @@ -68,10 +68,10 @@ function redirect_update_8101() { // Update the last installed field definition and field schema. /** @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface $key_value_store */ - \Drupal::entityManager()->clearCachedFieldDefinitions(); + \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); $key_value_store_definition = \Drupal::keyValue('entity.definitions.installed'); $storage_definitions = $key_value_store_definition->get('redirect.field_storage_definitions'); - $storage_definitions['hash'] = $storage_definition = \Drupal::entityManager() + $storage_definitions['hash'] = $storage_definition = \Drupal::service('entity_field.manager') ->getFieldStorageDefinitions('redirect')['hash']; $key_value_store_definition->set('redirect.field_storage_definitions', $storage_definitions); @@ -145,7 +145,7 @@ function redirect_update_8103() { // Only create if the redirect view doesn't exist and views is enabled. if (!View::load('redirect') && \Drupal::moduleHandler()->moduleExists('views')) { $config_path = drupal_get_path('module', 'redirect') . '/config/install/views.view.redirect.yml'; - $data = Yaml::parse($config_path); + $data = Yaml::parse(file_get_contents($config_path)); \Drupal::configFactory()->getEditable('views.view.redirect')->setData($data)->save(TRUE); $message = 'The new redirect view has been created.'; } diff --git a/web/modules/redirect/redirect.module b/web/modules/redirect/redirect.module index bf4d4f9022..5d5bae2da5 100644 --- a/web/modules/redirect/redirect.module +++ b/web/modules/redirect/redirect.module @@ -5,30 +5,21 @@ * The redirect module. */ -/** - * @defgroup redirect_api Redirection API - * @{ - * Functions related to URL redirects. - * - * @} End of "defgroup redirect_api". - */ -use Drupal\Component\Utility\UrlHelper; -use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Field\FieldItemList; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\Language; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; -use Drupal\Core\Site\Settings; +use Drupal\path_alias\PathAliasInterface; use Drupal\redirect\Entity\Redirect; use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Drupal\Core\Database\Query\Condition; /** * Implements hook_hook_info(). */ function redirect_hook_info() { - $hooks = array( + $hooks = [ 'redirect_load', 'redirect_load_by_source_alter', 'redirect_access', @@ -39,9 +30,9 @@ function redirect_hook_info() { 'redirect_update', 'redirect_delete', 'redirect_alter', - ); + ]; - return array_fill_keys($hooks, array('group' => 'redirect')); + return array_fill_keys($hooks, ['group' => 'redirect']); } /** @@ -93,63 +84,39 @@ function redirect_entity_delete(EntityInterface $entity) { } /** - * Implements hook_path_update(). - * - * Will create redirect from the old path alias to the new one. + * Implements hook_ENTITY_TYPE_update() for path_alias. */ -function redirect_path_update(array $path) { - if (!\Drupal::config('redirect.settings')->get('auto_redirect')) { +function redirect_path_alias_update(PathAliasInterface $path_alias) { + $config = \Drupal::config('redirect.settings'); + if (!$config->get('auto_redirect')) { return; } - $original_path = $path['original']; + + /** @var \Drupal\path_alias\PathAliasInterface $original_path_alias */ + $original_path_alias = $path_alias->original; // Delete all redirects having the same source as this alias. - redirect_delete_by_path($path['alias'], $path['langcode'], FALSE); - if ($original_path['alias'] != $path['alias']) { - if (!redirect_repository()->findMatchingRedirect($original_path['alias'], array(), $original_path['langcode'])) { + redirect_delete_by_path($path_alias->getAlias(), $path_alias->language()->getId(), FALSE); + + // Create redirect from the old path alias to the new one. + if ($original_path_alias->getAlias() != $path_alias->getAlias()) { + if (!redirect_repository()->findMatchingRedirect($original_path_alias->getAlias(), [], $original_path_alias->language()->getId())) { $redirect = Redirect::create(); - $redirect->setSource($original_path['alias']); - $redirect->setRedirect($path['source']); - $redirect->setLanguage($original_path['langcode']); - $redirect->setStatusCode(\Drupal::config('redirect.settings')->get('default_status_code')); + $redirect->setSource($original_path_alias->getAlias()); + $redirect->setRedirect($path_alias->getPath()); + $redirect->setLanguage($original_path_alias->language()->getId()); + $redirect->setStatusCode($config->get('default_status_code')); $redirect->save(); } } } /** - * Implements hook_path_insert(). + * Implements hook_ENTITY_TYPE_insert() for path_alias. */ -function redirect_path_insert(array $path) { +function redirect_path_alias_insert(PathAliasInterface $path_alias) { // Delete all redirects having the same source as this alias. - redirect_delete_by_path($path['alias'], $path['langcode'], FALSE); -} - -/** - * Implements hook_path_delete(). - */ -function redirect_path_delete($path) { - if (!\Drupal::config('redirect.settings')->get('auto_redirect')) { - return; - } - elseif (isset($path['redirect']) && !$path['redirect']) { - return; - } - elseif (empty($path)) { - // @todo Remove this condition and allow $path to use an array type hint - // when http://drupal.org/node/1025904 is fixed. - return; - } - - // Redirect from a deleted alias to the system path. - //if (!redirect_load_by_source($path['alias'], $path['language'])) { - // $redirect = new stdClass(); - // redirect_create($redirect); - // $redirect->source = $path['alias']; - // $redirect->redirect = $path['source']; - // $redirect->language = $path['language']; - // redirect_save($redirect); - //} + redirect_delete_by_path($path_alias->getAlias(), $path_alias->language()->getId(), FALSE); } /** @@ -162,23 +129,23 @@ function redirect_path_delete($path) { function redirect_page_build(&$page) { if (redirect_is_current_page_404() && \Drupal::currentUser()->hasPermission('administer redirects')) { if (!isset($page['content']['system_main']['actions'])) { - $page['content']['system_main']['actions'] = array( + $page['content']['system_main']['actions'] = [ '#theme' => 'links', - '#links' => array(), - '#attributes' => array('class' => array('action-links')), + '#links' => [], + '#attributes' => ['class' => ['action-links']], '#weight' => -100, - ); + ]; } // We cannot simply use current_path() because if a 404 path is set, then // that value overrides whatever is in $_GET['q']. The // drupal_deliver_html_page() function thankfully puts the original current // path into $_GET['destination']. - $destination = drupal_get_destination(); - $page['content']['system_main']['actions']['#links']['add_redirect'] = array( + $destination = \Drupal::destination()->getAsArray(); + $page['content']['system_main']['actions']['#links']['add_redirect'] = [ 'title' => t('Add URL redirect from this page to another location'), 'href' => 'admin/config/search/redirect/add', - 'query' => array('source' => $destination['destination']) + drupal_get_destination(), - ); + 'query' => ['source' => $destination['destination']] + \Drupal::destination()->getAsArray(), + ]; } } @@ -214,7 +181,7 @@ function redirect_delete_by_path($path, $langcode = NULL, $match_subpaths_and_re $database = \Drupal::database(); $query = $database->select('redirect'); $query->addField('redirect', 'rid'); - $query_or = db_or(); + $query_or = new Condition('OR'); $query_or->condition('redirect_source__path', $database->escapeLike($path), 'LIKE'); if ($match_subpaths_and_redirect) { $query_or->condition('redirect_source__path', $database->escapeLike($path . '/') . '%', 'LIKE'); @@ -257,62 +224,8 @@ function redirect_sort_recursive(&$array, $callback = 'sort') { return $result; } -/** - * Build the URL of a redirect for display purposes only. - */ -function redirect_url($path, array $options = array(), $clean_url = NULL) { - // @todo - deal with removal of clean_url config. See - // https://drupal.org/node/1659580 - if (!isset($clean_url)) { - //$clean_url = variable_get('clean_url', 0); - } - - if ($path == '') { - $path = '<front>'; - } - - if (!isset($options['alter']) || !empty($options['alter'])) { - \Drupal::moduleHandler()->alter('redirect_url', $path, $options); - } - - // The base_url might be rewritten from the language rewrite in domain mode. - if (!isset($options['base_url'])) { - // @todo - is this correct? See https://drupal.org/node/1798832. - if (isset($options['https']) && Settings::get('mixed_mode_sessions', FALSE)) { - if ($options['https'] === TRUE) { - $options['base_url'] = $GLOBALS['base_secure_url']; - $options['absolute'] = TRUE; - } - elseif ($options['https'] === FALSE) { - $options['base_url'] = $GLOBALS['base_insecure_url']; - $options['absolute'] = TRUE; - } - } - else { - $options['base_url'] = $GLOBALS['base_url']; - } - } - - if (empty($options['absolute']) || url_is_external($path)) { - $url = $path; - } - else { - $url = $options['base_url'] . base_path() . $path; - } - - if (isset($options['query'])) { - $url .= $clean_url ? '?' : '&'; - $url .= UrlHelper::buildQuery($options['query']); - } - if (isset($options['fragment'])) { - $url .= '#' . $options['fragment']; - } - - return $url; -} - function redirect_status_code_options($code = NULL) { - $codes = array( + $codes = [ 300 => t('300 Multiple Choices'), 301 => t('301 Moved Permanently'), 302 => t('302 Found'), @@ -320,7 +233,7 @@ function redirect_status_code_options($code = NULL) { 304 => t('304 Not Modified'), 305 => t('305 Use Proxy'), 307 => t('307 Temporary Redirect'), - ); + ]; return isset($codes[$code]) ? $codes[$code] : $codes; } @@ -336,71 +249,17 @@ function redirect_is_current_page_404() { return drupal_get_http_header('Status') == '404 Not Found'; } -/** - * uasort callback; Compare redirects based on language neutrality and rids. - */ -function _redirect_uasort($a, $b) { - $a_weight = isset($a->weight) ? $a->weight : 0; - $b_weight = isset($b->weight) ? $b->weight : 0; - if ($a_weight != $b_weight) { - // First sort by weight (case sensitivity). - return $a_weight > $b_weight; - } - elseif ($a->language != $b->language) { - // Then sort by language specific over language neutral. - return $a->language == Language::LANGCODE_NOT_SPECIFIED; - } - elseif (!empty($a->source_options['query']) != !empty($b->source_options['query'])) { - // Then sort by redirects that do not have query strings over ones that do. - return empty($a->source_options['query']); - } - else { - // Lastly sort by the highest redirect ID. - return $a->rid < $b->rid; - } -} - /** * Implements hook_form_FORM_ID_alter() on behalf of locale.module. */ function locale_form_redirect_edit_form_alter(array &$form, FormStateInterface $form_state) { - $form['language'] = array( + $form['language'] = [ '#type' => 'select', '#title' => t('Language'), - '#options' => array(Language::LANGCODE_NOT_SPECIFIED => t('All languages')) + \Drupal::languageManager()->getLanguages(), + '#options' => [Language::LANGCODE_NOT_SPECIFIED => t('All languages')] + \Drupal::languageManager()->getLanguages(), '#default_value' => $form['language']['#value'], '#description' => t('A redirect set for a specific language will always be used when requesting this page in that language, and takes precedence over redirects set for <em>All languages</em>.'), - ); -} - -/** - * Fetch an array of redirect bulk operations. - * - * @see hook_redirect_operations() - * @see hook_redirect_operations_alter() - */ -function redirect_get_redirect_operations() { - $operations = &drupal_static(__FUNCTION__); - - if (!isset($operations)) { - $operations = \Drupal::moduleHandler()->invokeAll('redirect_operations'); - \Drupal::moduleHandler()->alter('redirect_operations', $operations); - } - - return $operations; -} - -/** - * Implements hook_redirect_operations(). - */ -function redirect_redirect_operations() { - $operations['delete'] = array( - 'action' => t('Delete'), - 'action_past' => t('Deleted'), - 'callback' => 'redirect_delete_multiple', - 'confirm' => TRUE, - ); - return $operations; + ]; } /** @@ -450,7 +309,7 @@ function redirect_form_node_form_alter(&$form, FormStateInterface $form_state, $ // Assemble the rows for the table. $rows = []; /** @var \Drupal\Core\Entity\EntityListBuilder $list_builder */ - $list_builder = \Drupal::service('entity.manager')->getListBuilder('redirect'); + $list_builder = \Drupal::service('entity_type.manager')->getListBuilder('redirect'); /** @var \Drupal\redirect\Entity\Redirect[] $redirects */ foreach ($redirects as $redirect) { $row = []; @@ -500,5 +359,22 @@ function redirect_form_node_form_alter(&$form, FormStateInterface $form_state, $ '#suffix' => '</p>', ]; } + + $form['url_redirects']['actions'] = [ + '#theme' => 'links', + '#links' => [], + '#attributes' => ['class' => ['action-links']], + ]; + $form['url_redirects']['actions']['#links']['add'] = [ + 'title' => t('Add URL redirect'), + 'url' => Url::fromRoute('redirect.add', [ + 'redirect' => $node->toUrl()->getInternalPath(), + 'destination' => \Drupal::destination()->get(), + ]), + 'attributes' => [ + 'class' => 'button', + 'target' => '_blank', + ], + ]; } } diff --git a/web/modules/redirect/redirect.services.yml b/web/modules/redirect/redirect.services.yml index 743bd93925..578acb00b3 100644 --- a/web/modules/redirect/redirect.services.yml +++ b/web/modules/redirect/redirect.services.yml @@ -3,7 +3,7 @@ parameters: services: redirect.repository: class: Drupal\redirect\RedirectRepository - arguments: ['@entity.manager', '@database', '@config.factory'] + arguments: ['@entity_type.manager', '@database', '@config.factory'] tags: - { name: backend_overridable } redirect.checker: @@ -11,7 +11,7 @@ services: arguments: ['@config.factory', '@state', '@access_manager', '@current_user', '@router.route_provider'] redirect.request_subscriber: class: Drupal\redirect\EventSubscriber\RedirectRequestSubscriber - arguments: ['@redirect.repository', '@language_manager', '@config.factory', '@path.alias_manager', '@module_handler', '@entity.manager', '@redirect.checker', '@router.request_context', '@path_processor_manager'] + arguments: ['@redirect.repository', '@language_manager', '@config.factory', '@path_alias.manager', '@module_handler', '@entity_type.manager', '@redirect.checker', '@router.request_context', '@path_processor_manager'] tags: - { name: event_subscriber } redirect.settings_cache_tag: diff --git a/web/modules/redirect/src/Entity/Redirect.php b/web/modules/redirect/src/Entity/Redirect.php index cb5d4c56dd..c1796c1fc7 100644 --- a/web/modules/redirect/src/Entity/Redirect.php +++ b/web/modules/redirect/src/Entity/Redirect.php @@ -60,10 +60,10 @@ class Redirect extends ContentEntityBase { * Base 64 hash. */ public static function generateHash($source_path, array $source_query, $language) { - $hash = array( + $hash = [ 'source' => mb_strtolower($source_path), 'language' => $language, - ); + ]; if (!empty($source_query)) { $hash['source_query'] = $source_query; @@ -76,9 +76,9 @@ public static function generateHash($source_path, array $source_query, $language * {@inheritdoc} */ public static function preCreate(EntityStorageInterface $storage_controller, array &$values) { - $values += array( + $values += [ 'type' => 'redirect', - ); + ]; } /** @@ -148,7 +148,7 @@ public function getCreated() { * @param array $query * Query arguments. */ - public function setSource($path, array $query = array()) { + public function setSource($path, array $query = []) { $this->redirect_source->set(0, ['path' => ltrim($path, '/'), 'query' => $query]); } @@ -204,7 +204,7 @@ public function getRedirect() { * @param array $options * The source url options. */ - public function setRedirect($url, array $query = array(), array $options = array()) { + public function setRedirect($url, array $query = [], array $options = []) { $uri = $url . ($query ? '?' . UrlHelper::buildQuery($query) : ''); $external = UrlHelper::isValid($url, TRUE); $uri = ($external ? $url : 'internal:/' . ltrim($uri, '/')); @@ -284,42 +284,42 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setLabel(t('User ID')) ->setDescription(t('The user ID of the node author.')) ->setDefaultValueCallback('\Drupal\redirect\Entity\Redirect::getCurrentUserId') - ->setSettings(array( + ->setSettings([ 'target_type' => 'user', - )); + ]); $fields['redirect_source'] = BaseFieldDefinition::create('redirect_source') ->setLabel(t('From')) - ->setDescription(t("Enter an internal Drupal path or path alias to redirect (e.g. %example1 or %example2). Fragment anchors (e.g. %anchor) are <strong>not</strong> allowed.", array('%example1' => 'node/123', '%example2' => 'taxonomy/term/123', '%anchor' => '#anchor'))) + ->setDescription(t("Enter an internal Drupal path or path alias to redirect (e.g. %example1 or %example2). Fragment anchors (e.g. %anchor) are <strong>not</strong> allowed.", ['%example1' => 'node/123', '%example2' => 'taxonomy/term/123', '%anchor' => '#anchor'])) ->setRequired(TRUE) ->setTranslatable(FALSE) - ->setDisplayOptions('form', array( + ->setDisplayOptions('form', [ 'type' => 'redirect_link', 'weight' => -5, - )) + ]) ->setDisplayConfigurable('form', TRUE); $fields['redirect_redirect'] = BaseFieldDefinition::create('link') ->setLabel(t('To')) ->setRequired(TRUE) ->setTranslatable(FALSE) - ->setSettings(array( + ->setSettings([ 'link_type' => LinkItemInterface::LINK_GENERIC, 'title' => DRUPAL_DISABLED - )) - ->setDisplayOptions('form', array( + ]) + ->setDisplayOptions('form', [ 'type' => 'link', 'weight' => -4, - )) + ]) ->setDisplayConfigurable('form', TRUE); $fields['language'] = BaseFieldDefinition::create('language') ->setLabel(t('Language')) ->setDescription(t('The redirect language.')) - ->setDisplayOptions('form', array( + ->setDisplayOptions('form', [ 'type' => 'language_select', 'weight' => 2, - )); + ]); $fields['status_code'] = BaseFieldDefinition::create('integer') ->setLabel(t('Status code')) @@ -341,7 +341,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { * An array of default values. */ public static function getCurrentUserId() { - return array(\Drupal::currentUser()->id()); + return [\Drupal::currentUser()->id()]; } } diff --git a/web/modules/redirect/src/EventSubscriber/RedirectRequestSubscriber.php b/web/modules/redirect/src/EventSubscriber/RedirectRequestSubscriber.php index 87095defff..f3e35f8cdf 100644 --- a/web/modules/redirect/src/EventSubscriber/RedirectRequestSubscriber.php +++ b/web/modules/redirect/src/EventSubscriber/RedirectRequestSubscriber.php @@ -4,19 +4,17 @@ use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageManagerInterface; -use Drupal\Core\Logger\RfcLogLevel; -use Drupal\Core\Path\AliasManagerInterface; use Drupal\Core\PathProcessor\InboundPathProcessorInterface; use Drupal\Core\Routing\TrustedRedirectResponse; use Drupal\Core\Url; +use Drupal\path_alias\AliasManagerInterface; use Drupal\redirect\Exception\RedirectLoopException; use Drupal\redirect\RedirectChecker; use Drupal\redirect\RedirectRepository; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -41,7 +39,7 @@ class RedirectRequestSubscriber implements EventSubscriberInterface { protected $config; /** - * @var \Drupal\Core\Path\AliasManager + * @var \Drupal\path_alias\AliasManagerInterface */ protected $aliasManager; @@ -51,9 +49,9 @@ class RedirectRequestSubscriber implements EventSubscriberInterface { protected $moduleHandler; /** - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * @var \Drupal\redirect\RedirectChecker @@ -81,24 +79,24 @@ class RedirectRequestSubscriber implements EventSubscriberInterface { * The language manager service. * @param \Drupal\Core\Config\ConfigFactoryInterface $config * The config. - * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager + * @param \Drupal\path_alias\AliasManagerInterface $alias_manager * The alias manager service. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler service. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager service. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. * @param \Drupal\redirect\RedirectChecker $checker * The redirect checker service. * @param \Symfony\Component\Routing\RequestContext * Request context. */ - public function __construct(RedirectRepository $redirect_repository, LanguageManagerInterface $language_manager, ConfigFactoryInterface $config, AliasManagerInterface $alias_manager, ModuleHandlerInterface $module_handler, EntityManagerInterface $entity_manager, RedirectChecker $checker, RequestContext $context, InboundPathProcessorInterface $path_processor) { + public function __construct(RedirectRepository $redirect_repository, LanguageManagerInterface $language_manager, ConfigFactoryInterface $config, AliasManagerInterface $alias_manager, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager, RedirectChecker $checker, RequestContext $context, InboundPathProcessorInterface $path_processor) { $this->redirectRepository = $redirect_repository; $this->languageManager = $language_manager; $this->config = $config->get('redirect.settings'); $this->aliasManager = $alias_manager; $this->moduleHandler = $module_handler; - $this->entityManager = $entity_manager; + $this->entityTypeManager = $entity_type_manager; $this->checker = $checker; $this->context = $context; $this->pathProcessor = $path_processor; @@ -147,7 +145,7 @@ public function onKernelRequestCheckRedirect(GetResponseEvent $event) { $redirect = $this->redirectRepository->findMatchingRedirect($path, $request_query, $this->languageManager->getCurrentLanguage()->getId()); } catch (RedirectLoopException $e) { - \Drupal::logger('redirect')->warning($e->getMessage()); + \Drupal::logger('redirect')->warning('Redirect loop identified at %path for redirect %rid', ['%path' => $e->getPath(), '%rid' => $e->getRedirectId()]); $response = new Response(); $response->setStatusCode(503); $response->setContent('Service unavailable'); @@ -204,7 +202,7 @@ public static function getSubscribedEvents() { // This needs to run before RouterListener::onKernelRequest(), which has // a priority of 32. Otherwise, that aborts the request if no matching // route is found. - $events[KernelEvents::REQUEST][] = array('onKernelRequestCheckRedirect', 33); + $events[KernelEvents::REQUEST][] = ['onKernelRequestCheckRedirect', 33]; return $events; } diff --git a/web/modules/redirect/src/EventSubscriber/RouteNormalizerRequestSubscriber.php b/web/modules/redirect/src/EventSubscriber/RouteNormalizerRequestSubscriber.php index 800e022a56..274ba1e12a 100644 --- a/web/modules/redirect/src/EventSubscriber/RouteNormalizerRequestSubscriber.php +++ b/web/modules/redirect/src/EventSubscriber/RouteNormalizerRequestSubscriber.php @@ -136,7 +136,7 @@ public function onKernelRequestRedirect(GetResponseEvent $event) { * {@inheritdoc} */ static function getSubscribedEvents() { - $events[KernelEvents::REQUEST][] = array('onKernelRequestRedirect', 30); + $events[KernelEvents::REQUEST][] = ['onKernelRequestRedirect', 30]; return $events; } diff --git a/web/modules/redirect/src/Exception/RedirectLoopException.php b/web/modules/redirect/src/Exception/RedirectLoopException.php index f2793f3e86..2482862df9 100644 --- a/web/modules/redirect/src/Exception/RedirectLoopException.php +++ b/web/modules/redirect/src/Exception/RedirectLoopException.php @@ -2,13 +2,27 @@ namespace Drupal\redirect\Exception; -use Drupal\Component\Utility\SafeMarkup; +use Drupal\Component\Render\FormattableMarkup; /** * Exception for when a redirect loop is detected. */ class RedirectLoopException extends \RuntimeException { + /** + * The looping path. + * + * @var string + */ + protected $path; + + /** + * The redirect ID. + * + * @var int + */ + protected $rid; + /** * Formats a redirect loop exception message. * @@ -18,7 +32,31 @@ class RedirectLoopException extends \RuntimeException { * The redirect ID that is involved in a loop. */ public function __construct($path, $rid) { - parent::__construct(SafeMarkup::format('Redirect loop identified at %path for redirect %rid', ['%path' => $path, '%rid' => $rid])); + $message = new FormattableMarkup('Redirect loop identified at %path for redirect %rid', ['%path' => $path, '%rid' => $rid]); + $this->path = $path; + $this->rid = $rid; + parent::__construct($message); + } + + /** + * Returns the looping path. + * + * @return string + * The path. + */ + public function getPath() { + return $this->path; + } + + /** + * Returns the redirect ID. + * + * @return int + * The redirect ID. + * + */ + public function getRedirectId() { + return $this->rid; } } diff --git a/web/modules/redirect/src/Form/RedirectDeleteForm.php b/web/modules/redirect/src/Form/RedirectDeleteForm.php index eeee993d9c..7d0d9ec79a 100644 --- a/web/modules/redirect/src/Form/RedirectDeleteForm.php +++ b/web/modules/redirect/src/Form/RedirectDeleteForm.php @@ -12,7 +12,7 @@ class RedirectDeleteForm extends ContentEntityConfirmFormBase { * {@inheritdoc} */ public function getQuestion() { - return $this->t('Are you sure you want to delete the URL redirect from %source to %redirect?', array('%source' => $this->entity->getSourceUrl(), '%redirect' => $this->entity->getRedirectUrl()->toString())); + return $this->t('Are you sure you want to delete the URL redirect from %source to %redirect?', ['%source' => $this->entity->getSourceUrl(), '%redirect' => $this->entity->getRedirectUrl()->toString()]); } /** @@ -34,7 +34,7 @@ public function getConfirmText() { */ public function submitForm(array &$form, FormStateInterface $form_state) { $this->entity->delete(); - drupal_set_message($this->t('The redirect %redirect has been deleted.', array('%redirect' => $this->entity->getRedirectUrl()->toString()))); + $this->messenger()->addMessage($this->t('The redirect %redirect has been deleted.', ['%redirect' => $this->entity->getRedirectUrl()->toString()])); $form_state->setRedirect('redirect.list'); } diff --git a/web/modules/redirect/src/Form/RedirectDeleteMultipleForm.php b/web/modules/redirect/src/Form/RedirectDeleteMultipleForm.php index 79c79467cc..fe4c43e3a8 100644 --- a/web/modules/redirect/src/Form/RedirectDeleteMultipleForm.php +++ b/web/modules/redirect/src/Form/RedirectDeleteMultipleForm.php @@ -6,7 +6,7 @@ use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Drupal\Core\Session\AccountInterface; @@ -48,7 +48,7 @@ class RedirectDeleteMultipleForm extends ConfirmFormBase { /** * Constructs a RedirectDeleteMultiple form object. * - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. @@ -69,7 +69,7 @@ public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityT */ public static function create(ContainerInterface $container) { return new static( - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('entity_type.manager'), $container->get('current_user'), $container->get('string_translation') @@ -132,7 +132,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $this->privateTempStoreFactory->get('redirect_multiple_delete_confirm')->delete($this->currentUser->id()); $count = count($this->redirects); $this->logger('redirect')->notice('Deleted @count redirects.', ['@count' => $count]); - drupal_set_message($this->stringTranslation->formatPlural($count, 'Deleted 1 redirect.', 'Deleted @count redirects.')); + $this->messenger()->addMessage($this->stringTranslation->formatPlural($count, 'Deleted 1 redirect.', 'Deleted @count redirects.')); } $form_state->setRedirect('redirect.list'); } diff --git a/web/modules/redirect/src/Form/RedirectForm.php b/web/modules/redirect/src/Form/RedirectForm.php index 702e991580..1b4f4534f2 100644 --- a/web/modules/redirect/src/Form/RedirectForm.php +++ b/web/modules/redirect/src/Form/RedirectForm.php @@ -24,13 +24,13 @@ protected function prepareEntity() { // To pass in the query set parameters into GET as follows: // source_query[key1]=value1&source_query[key2]=value2 - $source_query = array(); + $source_query = []; if ($this->getRequest()->get('source_query')) { $source_query = $this->getRequest()->get('source_query'); } - $redirect_options = array(); - $redirect_query = array(); + $redirect_options = []; + $redirect_query = []; if ($this->getRequest()->get('redirect_options')) { $redirect_options = $this->getRequest()->get('redirect_options'); if (isset($redirect_options['query'])) { @@ -50,7 +50,7 @@ protected function prepareEntity() { $redirect->setRedirect($redirect_url, $redirect_query, $redirect_options); } catch (MatchingRouteNotFoundException $e) { - drupal_set_message($this->t('Invalid redirect URL %url provided.', array('%url' => $redirect_url)), 'warning'); + $this->messenger()->addMessage($this->t('Invalid redirect URL %url provided.', ['%url' => $redirect_url]), 'warning'); } } @@ -76,13 +76,13 @@ public function form(array $form, FormStateInterface $form_state) { $default_code = $redirect->getStatusCode() ? $redirect->getStatusCode() : \Drupal::config('redirect.settings')->get('default_status_code'); - $form['status_code'] = array( + $form['status_code'] = [ '#type' => 'select', '#title' => $this->t('Redirect status'), - '#description' => $this->t('You can find more information about HTTP redirect status codes at <a href="@status-codes">@status-codes</a>.', array('@status-codes' => 'http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection')), + '#description' => $this->t('You can find more information about HTTP redirect status codes at <a href="@status-codes">@status-codes</a>.', ['@status-codes' => 'http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection']), '#default_value' => $default_code, '#options' => redirect_status_code_options(), - ); + ]; return $form; } @@ -92,8 +92,12 @@ public function form(array $form, FormStateInterface $form_state) { */ public function validateForm(array &$form, FormStateInterface $form_state) { parent::validateForm($form, $form_state); - $source = $form_state->getValue(array('redirect_source', 0)); - $redirect = $form_state->getValue(array('redirect_redirect', 0)); + $source = $form_state->getValue(['redirect_source', 0]); + // Trim any trailing spaces from source url, leaving leading space as is. + // leading space is still a valid candidate to add for 301 source url. + $source['path'] = rtrim($source['path']); + $form_state->setValue('redirect_source', [$source]); + $redirect = $form_state->getValue(['redirect_redirect', 0]); if ($source['path'] == '<front>') { $form_state->setErrorByName('redirect_source', $this->t('It is not allowed to create a redirect from the front page.')); @@ -126,17 +130,17 @@ public function validateForm(array &$form, FormStateInterface $form_state) { $hash = Redirect::generateHash($path, $query, $form_state->getValue('language')[0]['value']); // Search for duplicate. - $redirects = \Drupal::entityManager() + $redirects = \Drupal::entityTypeManager() ->getStorage('redirect') - ->loadByProperties(array('hash' => $hash)); + ->loadByProperties(['hash' => $hash]); if (!empty($redirects)) { $redirect = array_shift($redirects); if ($this->entity->isNew() || $redirect->id() != $this->entity->id()) { $form_state->setErrorByName('redirect_source', $this->t('The source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', - array( + [ '%source' => $source['path'], - '@edit-page' => $redirect->url('edit-form')))); + '@edit-page' => $redirect->toUrl('edit-form')->toString()])); } } } @@ -146,7 +150,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) { */ public function save(array $form, FormStateInterface $form_state) { $this->entity->save(); - drupal_set_message($this->t('The redirect has been saved.')); + $this->messenger()->addMessage($this->t('The redirect has been saved.')); $form_state->setRedirect('redirect.list'); } } diff --git a/web/modules/redirect/src/Form/RedirectSettingsForm.php b/web/modules/redirect/src/Form/RedirectSettingsForm.php index 029973dcc1..1cfe0caa8c 100644 --- a/web/modules/redirect/src/Form/RedirectSettingsForm.php +++ b/web/modules/redirect/src/Form/RedirectSettingsForm.php @@ -25,53 +25,53 @@ protected function getEditableConfigNames() { */ public function buildForm(array $form, FormStateInterface $form_state) { $config = $this->config('redirect.settings'); - $form['redirect_auto_redirect'] = array( + $form['redirect_auto_redirect'] = [ '#type' => 'checkbox', '#title' => $this->t('Automatically create redirects when URL aliases are changed.'), '#default_value' => $config->get('auto_redirect'), '#disabled' => !\Drupal::moduleHandler()->moduleExists('path'), - ); - $form['redirect_passthrough_querystring'] = array( + ]; + $form['redirect_passthrough_querystring'] = [ '#type' => 'checkbox', '#title' => $this->t('Retain query string through redirect.'), '#default_value' => $config->get('passthrough_querystring'), - '#description' => $this->t('For example, given a redirect from %source to %redirect, if a user visits %sourcequery they would be redirected to %redirectquery. The query strings in the redirection will always take precedence over the current query string.', array('%source' => 'source-path', '%redirect' => 'node?a=apples', '%sourcequery' => 'source-path?a=alligators&b=bananas', '%redirectquery' => 'node?a=apples&b=bananas')), - ); - $form['redirect_warning'] = array( + '#description' => $this->t('For example, given a redirect from %source to %redirect, if a user visits %sourcequery they would be redirected to %redirectquery. The query strings in the redirection will always take precedence over the current query string.', ['%source' => 'source-path', '%redirect' => 'node?a=apples', '%sourcequery' => 'source-path?a=alligators&b=bananas', '%redirectquery' => 'node?a=apples&b=bananas']), + ]; + $form['redirect_warning'] = [ '#type' => 'checkbox', '#title' => $this->t('Display a warning message to users when they are redirected.'), '#default_value' => $config->get('warning'), '#access' => FALSE, - ); - $form['redirect_default_status_code'] = array( + ]; + $form['redirect_default_status_code'] = [ '#type' => 'select', '#title' => $this->t('Default redirect status'), - '#description' => $this->t('You can find more information about HTTP redirect status codes at <a href="@status-codes">@status-codes</a>.', array('@status-codes' => 'http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection')), + '#description' => $this->t('You can find more information about HTTP redirect status codes at <a href="@status-codes">@status-codes</a>.', ['@status-codes' => 'http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection']), '#options' => redirect_status_code_options(), '#default_value' => $config->get('default_status_code'), - ); - $form['globals'] = array( + ]; + $form['globals'] = [ '#type' => 'fieldset', '#title' => $this->t('Global redirects'), '#description' => $this->t('(formerly Global Redirect features)'), - ); - $form['globals']['redirect_route_normalizer_enabled'] = array( + ]; + $form['globals']['redirect_route_normalizer_enabled'] = [ '#type' => 'checkbox', '#title' => $this->t('Enforce clean and canonical URLs.'), '#description' => $this->t('Enabling this will automatically redirect to the canonical URL of any page. That includes redirecting to an alias if existing, removing trailing slashes, ensure the language prefix is set and similar clean-up.'), '#default_value' => $config->get('route_normalizer_enabled'), - ); - $form['globals']['redirect_ignore_admin_path'] = array( + ]; + $form['globals']['redirect_ignore_admin_path'] = [ '#type' => 'checkbox', '#title' => $this->t('Ignore redirections on admin paths.'), '#default_value' => $config->get('ignore_admin_path'), - ); - $form['globals']['redirect_access_check'] = array( + ]; + $form['globals']['redirect_access_check'] = [ '#type' => 'checkbox', '#title' => $this->t('Check access to the redirected page'), '#description' => $this->t('This helps to stop redirection on protected pages and avoids giving away <em>secret</em> URL\'s. <strong>By default this feature is disabled to avoid any unexpected behavior</strong>'), '#default_value' => $config->get('access_check'), - ); + ]; return parent::buildForm($form, $form_state); } @@ -87,7 +87,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { } } $config->save(); - drupal_set_message($this->t('Configuration was saved.')); + parent::submitForm($form, $form_state); } } diff --git a/web/modules/redirect/src/Plugin/Action/DeleteRedirect.php b/web/modules/redirect/src/Plugin/Action/DeleteRedirect.php index 8b7f579727..305a465a6b 100644 --- a/web/modules/redirect/src/Plugin/Action/DeleteRedirect.php +++ b/web/modules/redirect/src/Plugin/Action/DeleteRedirect.php @@ -5,7 +5,7 @@ use Drupal\Core\Action\ActionBase; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\user\PrivateTempStoreFactory; +use Drupal\Core\TempStore\PrivateTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -23,7 +23,7 @@ class DeleteRedirect extends ActionBase implements ContainerFactoryPluginInterfa /** * The tempstore object. * - * @var \Drupal\user\SharedTempStore + * @var \Drupal\Core\TempStore\SharedTempStore */ protected $privateTempStore; @@ -43,7 +43,7 @@ class DeleteRedirect extends ActionBase implements ContainerFactoryPluginInterfa * The plugin ID for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory + * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory * The tempstore factory. * @param AccountInterface $current_user * Current user. @@ -63,7 +63,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('user.private_tempstore'), + $container->get('tempstore.private'), $container->get('current_user') ); } diff --git a/web/modules/redirect/src/Plugin/Field/FieldFormatter/RedirectSourceFormatter.php b/web/modules/redirect/src/Plugin/Field/FieldFormatter/RedirectSourceFormatter.php index 7ef400de26..3c36366187 100644 --- a/web/modules/redirect/src/Plugin/Field/FieldFormatter/RedirectSourceFormatter.php +++ b/web/modules/redirect/src/Plugin/Field/FieldFormatter/RedirectSourceFormatter.php @@ -22,12 +22,12 @@ class RedirectSourceFormatter extends FormatterBase { * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { - $elements = array(); + $elements = []; foreach ($items as $delta => $item) { - $elements[$delta] = array( + $elements[$delta] = [ '#markup' => urldecode($item->getUrl()->toString()), - ); + ]; } return $elements; diff --git a/web/modules/redirect/src/Plugin/Field/FieldType/RedirectSourceItem.php b/web/modules/redirect/src/Plugin/Field/FieldType/RedirectSourceItem.php index 53a0efc66a..4c7e94eb50 100644 --- a/web/modules/redirect/src/Plugin/Field/FieldType/RedirectSourceItem.php +++ b/web/modules/redirect/src/Plugin/Field/FieldType/RedirectSourceItem.php @@ -41,24 +41,24 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel * {@inheritdoc} */ public static function schema(FieldStorageDefinitionInterface $field_definition) { - return array( - 'columns' => array( - 'path' => array( + return [ + 'columns' => [ + 'path' => [ 'description' => 'The source path', 'type' => 'varchar', 'length' => 2048, - ), - 'query' => array( + ], + 'query' => [ 'description' => 'Serialized array of path queries', 'type' => 'blob', 'size' => 'big', 'serialize' => TRUE, - ), - ), - 'indexes' => array( - 'path' => array(array('path', 50)), - ), - ); + ], + ], + 'indexes' => [ + 'path' => [['path', 50]], + ], + ]; } /** diff --git a/web/modules/redirect/src/Plugin/Field/FieldWidget/RedirectSourceWidget.php b/web/modules/redirect/src/Plugin/Field/FieldWidget/RedirectSourceWidget.php index 0a43411167..a07f2181c7 100644 --- a/web/modules/redirect/src/Plugin/Field/FieldWidget/RedirectSourceWidget.php +++ b/web/modules/redirect/src/Plugin/Field/FieldWidget/RedirectSourceWidget.php @@ -2,11 +2,13 @@ namespace Drupal\redirect\Plugin\Field\FieldWidget; +use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\WidgetBase; use Drupal\Core\Url; use Drupal\Core\Form\FormStateInterface; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; /** @@ -37,25 +39,25 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen if ($items[$delta]->query) { $default_url_value .= '?' . http_build_query($items[$delta]->query); } - $element['path'] = array( + $element['path'] = [ '#type' => 'textfield', '#title' => $this->t('Path'), '#placeholder' => $this->getSetting('placeholder_url'), '#default_value' => $default_url_value, '#maxlength' => 2048, '#required' => $element['#required'], - '#field_prefix' => Url::fromRoute('<front>', array(), array('absolute' => TRUE))->toString(), - '#attributes' => array('data-disable-refocus' => 'true'), - ); + '#field_prefix' => Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(), + '#attributes' => ['data-disable-refocus' => 'true'], + ]; // If creating new URL add checks. if ($items->getEntity()->isNew()) { - $element['status_box'] = array( + $element['status_box'] = [ '#prefix' => '<div id="redirect-link-status">', '#suffix' => '</div>', - ); + ]; - $source_path = $form_state->getValue(array('redirect_source', 0, 'path')); + $source_path = $form_state->getValue(['redirect_source', 0, 'path']); if ($source_path) { $source_path = trim($source_path); @@ -63,13 +65,16 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen // @todo - Hmm... exception driven logic. Find a better way how to // determine if we have a valid path. try { - \Drupal::service('router')->match('/' . $form_state->getValue(array('redirect_source', 0, 'path'))); + \Drupal::service('router')->match('/' . $form_state->getValue(['redirect_source', 0, 'path'])); $element['status_box'][]['#markup'] = '<div class="messages messages--warning">' . $this->t('The source path %path is likely a valid path. It is preferred to <a href="@url-alias">create URL aliases</a> for existing paths rather than redirects.', - array('%path' => $source_path, '@url-alias' => Url::fromRoute('path.admin_add')->toString())) . '</div>'; + ['%path' => $source_path, '@url-alias' => Url::fromRoute('entity.path_alias.add_form')->toString()]) . '</div>'; } catch (ResourceNotFoundException $e) { // Do nothing, expected behaviour. } + catch (AccessDeniedHttpException $e) { + // @todo Source lookup results in an access denied, deny access? + } // Warning about the path being already redirected. $parsed_url = UrlHelper::parse($source_path); @@ -80,15 +85,15 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $redirects = $repository->findBySourcePath($path); if (!empty($redirects)) { $redirect = array_shift($redirects); - $element['status_box'][]['#markup'] = '<div class="messages messages--warning">' . $this->t('The base source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => $source_path, '@edit-page' => $redirect->url('edit-form'))) . '</div>'; + $element['status_box'][]['#markup'] = '<div class="messages messages--warning">' . $this->t('The base source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', ['%source' => $source_path, '@edit-page' => $redirect->toUrl('edit-form')->toString()]) . '</div>'; } } } - $element['path']['#ajax'] = array( + $element['path']['#ajax'] = [ 'callback' => 'redirect_source_link_get_status_messages', 'wrapper' => 'redirect-link-status', - ); + ]; } return $element; diff --git a/web/modules/redirect/src/Plugin/Validation/Constraint/SourceLinkTypeConstraint.php b/web/modules/redirect/src/Plugin/Validation/Constraint/SourceLinkTypeConstraint.php index 5d354a07d2..96a4595ae0 100644 --- a/web/modules/redirect/src/Plugin/Validation/Constraint/SourceLinkTypeConstraint.php +++ b/web/modules/redirect/src/Plugin/Validation/Constraint/SourceLinkTypeConstraint.php @@ -9,8 +9,8 @@ use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidatorInterface; -use Symfony\Component\Validator\ExecutionContextInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\Validator\Context\ExecutionContextInterface; /** * Validation constraint for links receiving data allowed by its settings. @@ -22,10 +22,10 @@ */ class SourceLinkTypeConstraint extends Constraint implements ConstraintValidatorInterface { - public $message = 'The URL %url is not valid magor.'; + public $message = 'The URL %url is not valid.'; /** - * @var \Symfony\Component\Validator\ExecutionContextInterface + * @var \Symfony\Component\Validator\Context\ExecutionContextInterface */ protected $context; @@ -84,7 +84,7 @@ public function validate($value, Constraint $constraint) { } } if (!$url_is_valid) { - $this->context->addViolation($this->message, array('%url' => $url_string)); + $this->context->addViolation($this->message, ['%url' => $url_string]); } } } diff --git a/web/modules/redirect/src/Plugin/migrate/source/PathRedirect.php b/web/modules/redirect/src/Plugin/migrate/source/PathRedirect.php index 3ffd4b64ed..25fcf3fadc 100644 --- a/web/modules/redirect/src/Plugin/migrate/source/PathRedirect.php +++ b/web/modules/redirect/src/Plugin/migrate/source/PathRedirect.php @@ -20,7 +20,7 @@ class PathRedirect extends DrupalSqlBase { public function query() { // Select path redirects. $query = $this->select('path_redirect', 'p') - ->fields('p', array( + ->fields('p', [ 'rid', 'source', 'redirect', @@ -29,7 +29,7 @@ public function query() { 'language', 'type', 'last_used', - )); + ]); return $query; } @@ -38,7 +38,7 @@ public function query() { * {@inheritdoc} */ public function fields() { - $fields = array( + $fields = [ 'rid' => $this->t('Redirect ID'), 'source' => $this->t('Source'), 'redirect' => $this->t('Redirect'), @@ -47,7 +47,7 @@ public function fields() { 'language' => $this->t('Language'), 'type' => $this->t('Type'), 'last_used' => $this->t('Last Used'), - ); + ]; return $fields; } diff --git a/web/modules/redirect/src/RedirectRepository.php b/web/modules/redirect/src/RedirectRepository.php index 236c927f63..ff1ccf8bd9 100644 --- a/web/modules/redirect/src/RedirectRepository.php +++ b/web/modules/redirect/src/RedirectRepository.php @@ -4,7 +4,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Database\Connection; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\Language; use Drupal\redirect\Entity\Redirect; use Drupal\redirect\Exception\RedirectLoopException; @@ -12,7 +12,7 @@ class RedirectRepository { /** - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ protected $manager; @@ -36,12 +36,12 @@ class RedirectRepository { /** * Constructs a \Drupal\redirect\EventSubscriber\RedirectRequestSubscriber object. * - * @param \Drupal\Core\Entity\EntityManagerInterface $manager - * The entity manager service. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $manager + * The entity type manager. * @param \Drupal\Core\Database\Connection $connection * The database connection. */ - public function __construct(EntityManagerInterface $manager, Connection $connection, ConfigFactoryInterface $config_factory) { + public function __construct(EntityTypeManagerInterface $manager, Connection $connection, ConfigFactoryInterface $config_factory) { $this->manager = $manager; $this->connection = $connection; $this->config = $config_factory->get('redirect.settings'); @@ -99,6 +99,8 @@ public function findMatchingRedirect($source_path, array $query = [], $language return $redirect; } + // Reset found redirects. + $this->foundRedirects = []; return NULL; } diff --git a/web/modules/redirect/src/Tests/RedirectUITest.php b/web/modules/redirect/src/Tests/RedirectUITest.php deleted file mode 100644 index 67b066614c..0000000000 --- a/web/modules/redirect/src/Tests/RedirectUITest.php +++ /dev/null @@ -1,436 +0,0 @@ -<?php - -namespace Drupal\redirect\Tests; - -use Drupal\Component\Utility\SafeMarkup; -use Drupal\Core\Language\Language; -use Drupal\Core\Logger\RfcLogLevel; -use Drupal\Core\Url; -use Drupal\simpletest\WebTestBase; - -/** - * UI tests for redirect module. - * - * @group redirect - */ -class RedirectUITest extends WebTestBase { - - use AssertRedirectTrait; - - /** - * @var \Drupal\Core\Session\AccountInterface - */ - protected $adminUser; - - /** - * @var \Drupal\redirect\RedirectRepository - */ - protected $repository; - - /** - * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage - */ - protected $storage; - - /** - * {@inheritdoc} - */ - public static $modules = ['redirect', 'node', 'path', 'dblog', 'views', 'taxonomy']; - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); - $this->adminUser = $this->drupalCreateUser(array( - 'administer redirects', - 'administer redirect settings', - 'access content', - 'bypass node access', - 'create url aliases', - 'administer taxonomy', - 'administer url aliases', - )); - - $this->repository = \Drupal::service('redirect.repository'); - - $this->storage = $this->container->get('entity.manager')->getStorage('redirect'); - } - - /** - * Test the redirect UI. - */ - public function testRedirectUI() { - $this->drupalLogin($this->adminUser); - - // Test populating the redirect form with predefined values. - $this->drupalGet('admin/config/search/redirect/add', array('query' => array( - 'source' => 'non-existing', - 'source_query' => array('key' => 'val', 'key1' => 'val1'), - 'redirect' => 'node', - 'redirect_options' => array('query' => array('key' => 'val', 'key1' => 'val1')), - ))); - $this->assertFieldByName('redirect_source[0][path]', 'non-existing?key=val&key1=val1'); - $this->assertFieldByName('redirect_redirect[0][uri]', '/node?key=val&key1=val1'); - - // Test creating a new redirect via UI. - $this->drupalPostForm('admin/config/search/redirect/add', array( - 'redirect_source[0][path]' => 'non-existing', - 'redirect_redirect[0][uri]' => '/node', - ), t('Save')); - - // Try to find the redirect we just created. - $redirect = $this->repository->findMatchingRedirect('non-existing'); - $this->assertEqual($redirect->getSourceUrl(), Url::fromUri('base:non-existing')->toString()); - $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:node')->toString()); - - // After adding the redirect we should end up in the list. Check if the - // redirect is listed. - $this->assertUrl('admin/config/search/redirect'); - $this->assertText('non-existing'); - $this->assertLink(Url::fromUri('base:node')->toString()); - $this->assertText(t('Not specified')); - - // Test the edit form and update action. - $this->clickLink(t('Edit')); - $this->assertFieldByName('redirect_source[0][path]', 'non-existing'); - $this->assertFieldByName('redirect_redirect[0][uri]', '/node'); - $this->assertFieldByName('status_code', $redirect->getStatusCode()); - - // Append a query string to see if we handle query data properly. - $this->drupalPostForm(NULL, array( - 'redirect_source[0][path]' => 'non-existing?key=value', - ), t('Save')); - - // Check the location after update and check if the value has been updated - // in the list. - $this->assertUrl('admin/config/search/redirect'); - $this->assertText('non-existing?key=value'); - - // The path field should not contain the query string and therefore we - // should be able to load the redirect using only the url part without - // query. - $this->storage->resetCache(); - $redirects = $this->repository->findBySourcePath('non-existing'); - $redirect = array_shift($redirects); - $this->assertEqual($redirect->getSourceUrl(), Url::fromUri('base:non-existing', ['query' => ['key' => 'value']])->toString()); - - // Test the source url hints. - // The hint about an existing base path. - $this->drupalPostAjaxForm('admin/config/search/redirect/add', array( - 'redirect_source[0][path]' => 'non-existing?key=value', - ), 'redirect_source[0][path]'); - $this->assertRaw(t('The base source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', - array('%source' => 'non-existing?key=value', '@edit-page' => $redirect->url('edit-form')))); - - // The hint about a valid path. - $this->drupalPostAjaxForm('admin/config/search/redirect/add', array( - 'redirect_source[0][path]' => 'node', - ), 'redirect_source[0][path]'); - $this->assertRaw(t('The source path %path is likely a valid path. It is preferred to <a href="@url-alias">create URL aliases</a> for existing paths rather than redirects.', - array('%path' => 'node', '@url-alias' => Url::fromRoute('path.admin_add')->toString()))); - - // Test validation. - // Duplicate redirect. - $this->drupalPostForm('admin/config/search/redirect/add', array( - 'redirect_source[0][path]' => 'non-existing?key=value', - 'redirect_redirect[0][uri]' => '/node', - ), t('Save')); - $this->assertRaw(t('The source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', - array('%source' => 'non-existing?key=value', '@edit-page' => $redirect->url('edit-form')))); - - // Redirecting to itself. - $this->drupalPostForm('admin/config/search/redirect/add', array( - 'redirect_source[0][path]' => 'node', - 'redirect_redirect[0][uri]' => '/node', - ), t('Save')); - $this->assertRaw(t('You are attempting to redirect the page to itself. This will result in an infinite loop.')); - - // Redirecting the front page. - $this->drupalPostForm('admin/config/search/redirect/add', array( - 'redirect_source[0][path]' => '<front>', - 'redirect_redirect[0][uri]' => '/node', - ), t('Save')); - $this->assertRaw(t('It is not allowed to create a redirect from the front page.')); - - // Redirecting a url with fragment. - $this->drupalPostForm('admin/config/search/redirect/add', array( - 'redirect_source[0][path]' => 'page-to-redirect#content', - 'redirect_redirect[0][uri]' => '/node', - ), t('Save')); - $this->assertRaw(t('The anchor fragments are not allowed.')); - - // Adding path that starts with / - $this->drupalPostForm('admin/config/search/redirect/add', array( - 'redirect_source[0][path]' => '/page-to-redirect', - 'redirect_redirect[0][uri]' => '/node', - ), t('Save')); - $this->assertRaw(t('The url to redirect from should not start with a forward slash (/).')); - - // Test filters. - // Add a new redirect. - $this->drupalPostForm('admin/config/search/redirect/add', array( - 'redirect_source[0][path]' => 'test27', - 'redirect_redirect[0][uri]' => '/node', - ), t('Save')); - - // Filter with non existing value. - $this->drupalGet('admin/config/search/redirect', array( - 'query' => array( - 'status_code' => '3', - ), - )); - - $rows = $this->xpath('//tbody/tr'); - // Check if the list has no rows. - $this->assertTrue(count($rows) == 0); - - // Filter with existing values. - $this->drupalGet('admin/config/search/redirect', array( - 'query' => array( - 'redirect_source__path' => 'test', - 'status_code' => '2', - ), - )); - - $rows = $this->xpath('//tbody/tr'); - // Check if the list has 1 row. - $this->assertTrue(count($rows) == 1); - - $this->drupalGet('admin/config/search/redirect', array( - 'query' => array( - 'redirect_redirect__uri' => 'nod', - ), - )); - - $rows = $this->xpath('//tbody/tr'); - // Check if the list has 2 rows. - $this->assertTrue(count($rows) == 2); - - // Test the plural form of the bulk delete action. - $this->drupalGet('admin/config/search/redirect'); - $edit = [ - 'redirect_bulk_form[0]' => TRUE, - 'redirect_bulk_form[1]' => TRUE, - ]; - $this->drupalPostForm(NULL, $edit, t('Apply to selected items')); - $this->assertText('Are you sure you want to delete these redirects?'); - $this->clickLink('Cancel'); - - // Test the delete action. - $this->clickLink(t('Delete')); - $this->assertRaw(t('Are you sure you want to delete the URL redirect from %source to %redirect?', - array('%source' => Url::fromUri('base:non-existing', ['query' => ['key' => 'value']])->toString(), '%redirect' => Url::fromUri('base:node')->toString()))); - $this->drupalPostForm(NULL, array(), t('Delete')); - $this->assertUrl('admin/config/search/redirect'); - - // Test the bulk delete action. - $this->drupalPostForm(NULL, ['redirect_bulk_form[0]' => TRUE], t('Apply to selected items')); - $this->assertText('Are you sure you want to delete this redirect?'); - $this->assertText('test27'); - $this->drupalPostForm(NULL, [], t('Delete')); - - $this->assertText(t('There is no redirect yet.')); - } - - /** - * Tests redirects being automatically created upon path alias change. - */ - public function testAutomaticRedirects() { - $this->drupalLogin($this->adminUser); - - // Create a node and update its path alias which should result in a redirect - // being automatically created from the old alias to the new one. - $node = $this->drupalCreateNode(array( - 'type' => 'article', - 'langcode' => Language::LANGCODE_NOT_SPECIFIED, - 'path' => array('alias' => '/node_test_alias'), - )); - $this->drupalGet('node/' . $node->id() . '/edit'); - $this->assertText(t('No URL redirects available.')); - $this->drupalPostForm('node/' . $node->id() . '/edit', array('path[0][alias]' => '/node_test_alias_updated'), t('Save')); - - $redirect = $this->repository->findMatchingRedirect('node_test_alias', array(), Language::LANGCODE_NOT_SPECIFIED); - $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:node_test_alias_updated')->toString()); - // Test if the automatically created redirect works. - $this->assertRedirect('node_test_alias', 'node_test_alias_updated'); - - // Test that changing the path back deletes the first redirect, creates - // a new one and does not result in a loop. - $this->drupalPostForm('node/' . $node->id() . '/edit', array('path[0][alias]' => '/node_test_alias'), t('Save')); - $redirect = $this->repository->findMatchingRedirect('node_test_alias', array(), Language::LANGCODE_NOT_SPECIFIED); - $this->assertTrue(empty($redirect)); - - \Drupal::service('path.alias_manager')->cacheClear(); - $redirect = $this->repository->findMatchingRedirect('node_test_alias_updated', array(), Language::LANGCODE_NOT_SPECIFIED); - - $this->drupalGet('node/' . $node->id() . '/edit'); - $this->assertText($redirect->getSourcePathWithQuery()); - $this->assertLinkByHref(Url::fromRoute('entity.redirect.edit_form', ['redirect' => $redirect->id()])->toString()); - $this->assertLinkByHref(Url::fromRoute('entity.redirect.delete_form', ['redirect' => $redirect->id()])->toString()); - - $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:node_test_alias')->toString()); - // Test if the automatically created redirect works. - $this->assertRedirect('node_test_alias_updated', 'node_test_alias'); - - // Test that the redirect will be deleted upon node deletion. - $this->drupalPostForm('node/' . $node->id() . '/delete', array(), t('Delete')); - $redirect = $this->repository->findMatchingRedirect('node_test_alias_updated', array(), Language::LANGCODE_NOT_SPECIFIED); - $this->assertTrue(empty($redirect)); - - // Create a term and update its path alias and check if we have a redirect - // from the previous path alias to the new one. - $term = $this->createTerm($this->createVocabulary()); - $this->drupalPostForm('taxonomy/term/' . $term->id() . '/edit', array('path[0][alias]' => '/term_test_alias_updated'), t('Save')); - $redirect = $this->repository->findMatchingRedirect('term_test_alias'); - $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:term_test_alias_updated')->toString()); - // Test if the automatically created redirect works. - $this->assertRedirect('term_test_alias', 'term_test_alias_updated'); - - // Test the path alias update via the admin path form. - $this->drupalPostForm('admin/config/search/path/add', array( - 'source' => '/node', - 'alias' => '/aaa_path_alias', - ), t('Save')); - // Note that here we rely on fact that we land on the path alias list page - // and the default sort is by the alias, which implies that the first edit - // link leads to the edit page of the aaa_path_alias. - $this->clickLink(t('Edit')); - $this->drupalPostForm(NULL, array('alias' => '/aaa_path_alias_updated'), t('Save')); - $redirect = $this->repository->findMatchingRedirect('aaa_path_alias', array(), 'en'); - $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:aaa_path_alias_updated')->toString()); - // Test if the automatically created redirect works. - $this->assertRedirect('aaa_path_alias', 'aaa_path_alias_updated'); - - // Test the automatically created redirect shows up in the form correctly. - $this->drupalGet('admin/config/search/redirect/edit/' . $redirect->id()); - $this->assertFieldByName('redirect_source[0][path]', 'aaa_path_alias'); - $this->assertFieldByName('redirect_redirect[0][uri]', '/node'); - } - - /** - * Test the redirect loop protection and logging. - */ - function testRedirectLoop() { - // Redirect loop redirection only works when page caching is disabled. - \Drupal::service('module_installer')->uninstall(['page_cache']); - - /** @var \Drupal\redirect\Entity\Redirect $redirect1 */ - $redirect1 = $this->storage->create(); - $redirect1->setSource('node'); - $redirect1->setRedirect('admin'); - $redirect1->setStatusCode(301); - $redirect1->save(); - - /** @var \Drupal\redirect\Entity\Redirect $redirect2 */ - $redirect2 = $this->storage->create(); - $redirect2->setSource('admin'); - $redirect2->setRedirect('node'); - $redirect2->setStatusCode(301); - $redirect2->save(); - - $this->maximumRedirects = 10; - $this->drupalGet('node'); - $this->assertText('Service unavailable'); - $this->assertResponse(503); - - $log = db_select('watchdog')->fields('watchdog')->condition('type', 'redirect')->execute()->fetchAll(); - if (count($log) == 0) { - $this->fail('Redirect loop has not been logged'); - } - else { - $log = reset($log); - $this->assertEqual($log->severity, RfcLogLevel::WARNING); - $this->assertEqual(SafeMarkup::format($log->message, unserialize($log->variables)), - SafeMarkup::format('Redirect loop identified at %path for redirect %id', array('%path' => '/node', '%id' => $redirect1->id()))); - } - } - - /** - * Returns a new vocabulary with random properties. - */ - function createVocabulary() { - // Create a vocabulary. - $vocabulary = entity_create('taxonomy_vocabulary', array( - 'name' => $this->randomMachineName(), - 'description' => $this->randomMachineName(), - 'vid' => mb_strtolower($this->randomMachineName()), - 'langcode' => Language::LANGCODE_NOT_SPECIFIED, - 'weight' => mt_rand(0, 10), - )); - $vocabulary->save(); - return $vocabulary; - } - - /** - * Returns a new term with random properties in vocabulary $vid. - */ - function createTerm($vocabulary) { - $filter_formats = filter_formats(); - $format = array_pop($filter_formats); - $term = entity_create('taxonomy_term', array( - 'name' => $this->randomMachineName(), - 'description' => array( - 'value' => $this->randomMachineName(), - // Use the first available text format. - 'format' => $format->id(), - ), - 'vid' => $vocabulary->id(), - 'langcode' => Language::LANGCODE_NOT_SPECIFIED, - 'path' => array('alias' => '/term_test_alias'), - )); - $term->save(); - return $term; - } - - /** - * Test cache tags. - * - * @todo Not sure this belongs in a UI test, but a full web test is needed. - */ - public function testCacheTags() { - /** @var \Drupal\redirect\Entity\Redirect $redirect1 */ - $redirect1 = $this->storage->create(); - $redirect1->setSource('test-redirect'); - $redirect1->setRedirect('node'); - $redirect1->setStatusCode(301); - $redirect1->save(); - - $this->assertRedirect('test-redirect', 'node'); - $headers = $this->drupalGetHeaders(TRUE); - // Note, self::assertCacheTag() cannot be used here since it only looks at - // the final set of headers. - $expected = 'http_response ' . implode(' ', $redirect1->getCacheTags()); - $this->assertEqual($expected, $headers[0]['x-drupal-cache-tags'], 'Redirect cache tags properly set.'); - - // First request should be a cache MISS. - $this->assertEqual($headers[0]['x-drupal-cache'], 'MISS', 'First request to the redirect was not cached.'); - - // Second request should be cached. - $this->assertRedirect('test-redirect', 'node'); - $headers = $this->drupalGetHeaders(TRUE); - $this->assertEqual($headers[0]['x-drupal-cache'], 'HIT', 'The second request to the redirect was cached.'); - - // Ensure that the redirect has been cleared from cache when deleted. - $redirect1->delete(); - $this->drupalGet('test-redirect'); - $this->assertResponse(404, 'Deleted redirect properly clears the internal page cache.'); - } - - /** - * Test external destinations. - */ - public function testExternal() { - $redirect = $this->storage->create(); - $redirect->setSource('a-path'); - // @todo Redirect::setRedirect() assumes that all redirects are internal. - $redirect->redirect_redirect->set(0, ['uri' => 'https://www.example.org']); - $redirect->setStatusCode(301); - $redirect->save(); - $this->assertRedirect('a-path', 'https://www.example.org'); - $this->drupalLogin($this->adminUser); - } - -} diff --git a/web/modules/redirect/tests/fixtures/drupal6.php b/web/modules/redirect/tests/fixtures/drupal6.php index e6f9c47588..ca04d67d18 100644 --- a/web/modules/redirect/tests/fixtures/drupal6.php +++ b/web/modules/redirect/tests/fixtures/drupal6.php @@ -8,59 +8,59 @@ $connection = Database::getConnection(); -$connection->schema()->createTable('path_redirect', array( - 'fields' => array( - 'rid' => array( +$connection->schema()->createTable('path_redirect', [ + 'fields' => [ + 'rid' => [ 'type' => 'serial', 'not null' => TRUE, 'size' => 'normal', - ), - 'source' => array( + ], + 'source' => [ 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, - ), - 'redirect' => array( + ], + 'redirect' => [ 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, - ), - 'query' => array( + ], + 'query' => [ 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, - ), - 'fragment' => array( + ], + 'fragment' => [ 'type' => 'varchar', 'length' => 50, 'not null' => FALSE, - ), - 'language' => array( + ], + 'language' => [ 'type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '', - ), - 'type' => array( + ], + 'type' => [ 'type' => 'int', 'size' => 'small', 'not null' => TRUE, - ), - 'last_used' => array( + ], + 'last_used' => [ 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, - ), - ), - 'primary key' => array('rid'), - 'unique keys' => array('source_language' => array('source', 'language')), + ], + ], + 'primary key' => ['rid'], + 'unique keys' => ['source_language' => ['source', 'language']], 'mysql_character_set' => 'utf8', -)); +]); $connection->insert('path_redirect') - ->fields(array( + ->fields([ 'rid', 'source', 'redirect', @@ -69,8 +69,8 @@ 'language', 'type', 'last_used', - )) - ->values(array( + ]) + ->values([ 'rid' => 5, 'source' => 'test/source/url', 'redirect' => 'test/redirect/url', @@ -79,8 +79,8 @@ 'language' => '', 'type' => 301, 'last_used' => 1449497138, - )) - ->values(array( + ]) + ->values([ 'rid' => 7, 'source' => 'test/source/url2', 'redirect' => 'http://test/external/redirect/url', @@ -89,79 +89,79 @@ 'language' => 'en', 'type' => 302, 'last_used' => 1449497139, - )) + ]) ->execute(); -$connection->schema()->createTable('system', array( - 'fields' => array( - 'filename' => array( +$connection->schema()->createTable('system', [ + 'fields' => [ + 'filename' => [ 'type' => 'varchar', 'not null' => TRUE, 'length' => '255', 'default' => '', - ), - 'name' => array( + ], + 'name' => [ 'type' => 'varchar', 'not null' => TRUE, 'length' => '255', 'default' => '', - ), - 'type' => array( + ], + 'type' => [ 'type' => 'varchar', 'not null' => TRUE, 'length' => '255', 'default' => '', - ), - 'owner' => array( + ], + 'owner' => [ 'type' => 'varchar', 'not null' => TRUE, 'length' => '255', 'default' => '', - ), - 'status' => array( + ], + 'status' => [ 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '0', - ), - 'throttle' => array( + ], + 'throttle' => [ 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '0', - ), - 'bootstrap' => array( + ], + 'bootstrap' => [ 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '0', - ), - 'schema_version' => array( + ], + 'schema_version' => [ 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '-1', - ), - 'weight' => array( + ], + 'weight' => [ 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '0', - ), - 'info' => array( + ], + 'info' => [ 'type' => 'text', 'not null' => FALSE, 'size' => 'normal', - ), - ), - 'primary key' => array( + ], + ], + 'primary key' => [ 'filename', - ), + ], 'mysql_character_set' => 'utf8', -)); +]); $connection->insert('system') - ->fields(array( + ->fields([ 'filename', 'name', 'type', @@ -171,8 +171,8 @@ 'schema_version', 'weight', 'info', - )) - ->values(array( + ]) + ->values([ 'filename' => 'modules/contrib/path_redirect/path_redirect.module', 'name' => 'path_redirect', 'type' => 'module', @@ -182,5 +182,5 @@ 'schema_version' => '7000', 'weight' => '0', 'info' => 'a:10:{s:4:"name";s:13:"Path Redirect";s:11:"description";s:51:"Allows users to redirect from old URLs to new URLs.";s:7:"package";s:5:"Other";s:7:"version";s:3:"6.0";s:4:"core";s:3:"6.x";s:7:"project";s:13:"path_redirect";s:9:"datestamp";s:10:"1347989995";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', - )) + ]) ->execute(); diff --git a/web/modules/redirect/tests/fixtures/drupal7.php b/web/modules/redirect/tests/fixtures/drupal7.php index 2051076177..3652ee835d 100644 --- a/web/modules/redirect/tests/fixtures/drupal7.php +++ b/web/modules/redirect/tests/fixtures/drupal7.php @@ -8,76 +8,76 @@ $connection = Database::getConnection(); -$connection->schema()->createTable('redirect', array( - 'fields' => array( - 'rid' => array( +$connection->schema()->createTable('redirect', [ + 'fields' => [ + 'rid' => [ 'type' => 'serial', 'not null' => TRUE, 'size' => 'normal', - ), - 'hash' => array( + ], + 'hash' => [ 'type' => 'varchar', 'length' => 64, 'not null' => TRUE, - ), - 'type' => array( + ], + 'type' => [ 'type' => 'varchar', 'length' => 64, 'not null' => TRUE, - ), - 'uid' => array( + ], + 'uid' => [ 'type' => 'int', 'not null' => TRUE, - ), - 'source' => array( + ], + 'source' => [ 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, - ), - 'source_options' => array( + ], + 'source_options' => [ 'type' => 'text', 'not null' => TRUE, - ), - 'redirect' => array( + ], + 'redirect' => [ 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, - ), - 'redirect_options' => array( + ], + 'redirect_options' => [ 'type' => 'text', 'not null' => TRUE, - ), - 'language' => array( + ], + 'language' => [ 'type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '', - ), - 'status_code' => array( + ], + 'status_code' => [ 'type' => 'int', 'size' => 'small', 'not null' => TRUE, - ), - 'count' => array( + ], + 'count' => [ 'type' => 'int', 'not null' => TRUE, - ), - 'access' => array( + ], + 'access' => [ 'type' => 'int', 'not null' => TRUE, - ), - ), - 'primary key' => array('rid'), - 'unique keys' => array( - 'source_language' => array('source', 'language'), - 'expires' => array('type', 'access') - ), + ], + ], + 'primary key' => ['rid'], + 'unique keys' => [ + 'source_language' => ['source', 'language'], + 'expires' => ['type', 'access'] + ], 'mysql_character_set' => 'utf8', -)); +]); $connection->insert('redirect') - ->fields(array( + ->fields([ 'rid', 'hash', 'type', @@ -90,8 +90,8 @@ 'status_code', 'count', 'access', - )) - ->values(array( + ]) + ->values([ 'rid' => 5, 'hash' => 'MwmDbnA65ag646gtEdLqmAqTbF0qQerse63RkQmJK_Y', 'type' => 'redirect', @@ -104,8 +104,8 @@ 'status_code' => 301, 'count' => 2518, 'access' => 1449497138, - )) - ->values(array( + ]) + ->values([ 'rid' => 7, 'hash' => 'GvD5bBB71W8qBvp9I9hHmbSoqZfTvUz0mIkEWjlP8M4', 'type' => 'redirect', @@ -118,104 +118,104 @@ 'status_code' => 0, 'count' => 419, 'access' => 1449497139, - )) + ]) ->execute(); -$connection->schema()->createTable('variable', array( - 'fields' => array( - 'name' => array( +$connection->schema()->createTable('variable', [ + 'fields' => [ + 'name' => [ 'type' => 'varchar', 'not null' => TRUE, 'length' => '128', 'default' => '', - ), - 'value' => array( + ], + 'value' => [ 'type' => 'blob', 'not null' => TRUE, 'size' => 'normal', - ), - ), - 'primary key' => array( + ], + ], + 'primary key' => [ 'name', - ), + ], 'mysql_character_set' => 'utf8', -)); +]); $connection->insert('variable') -->fields(array( +->fields([ 'name', 'value', -)) -->values(array( +]) +->values([ 'name' => 'redirect_default_status_code', 'value' => 's:3:"307";', -)) +]) ->execute(); -$connection->schema()->createTable('system', array( - 'fields' => array( - 'filename' => array( +$connection->schema()->createTable('system', [ + 'fields' => [ + 'filename' => [ 'type' => 'varchar', 'not null' => TRUE, 'length' => '255', 'default' => '', - ), - 'name' => array( + ], + 'name' => [ 'type' => 'varchar', 'not null' => TRUE, 'length' => '255', 'default' => '', - ), - 'type' => array( + ], + 'type' => [ 'type' => 'varchar', 'not null' => TRUE, 'length' => '12', 'default' => '', - ), - 'owner' => array( + ], + 'owner' => [ 'type' => 'varchar', 'not null' => TRUE, 'length' => '255', 'default' => '', - ), - 'status' => array( + ], + 'status' => [ 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '0', - ), - 'bootstrap' => array( + ], + 'bootstrap' => [ 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '0', - ), - 'schema_version' => array( + ], + 'schema_version' => [ 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '-1', - ), - 'weight' => array( + ], + 'weight' => [ 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '0', - ), - 'info' => array( + ], + 'info' => [ 'type' => 'blob', 'not null' => FALSE, 'size' => 'normal', - ), - ), - 'primary key' => array( + ], + ], + 'primary key' => [ 'filename', - ), + ], 'mysql_character_set' => 'utf8', -)); +]); $connection->insert('system') -->fields(array( +->fields([ 'filename', 'name', 'type', @@ -225,8 +225,8 @@ 'schema_version', 'weight', 'info', -)) -->values(array( +]) +->values([ 'filename' => 'modules/contrib/redirect/redirect.module', 'name' => 'redirect', 'type' => 'module', @@ -236,5 +236,5 @@ 'schema_version' => '7000', 'weight' => '0', 'info' => 'a:13:{s:4:"name";s:8:"Redirect";s:11:"description";s:51:"Allows users to redirect from old URLs to new URLs.";s:4:"core";s:3:"7.x";s:5:"files";a:11:{i:0;s:15:"redirect.module";i:1;s:18:"redirect.admin.inc";i:2;s:16:"redirect.install";i:3;s:13:"redirect.test";i:4;s:24:"views/redirect.views.inc";i:5;s:47:"views/redirect_handler_filter_redirect_type.inc";i:6;s:48:"views/redirect_handler_field_redirect_source.inc";i:7;s:50:"views/redirect_handler_field_redirect_redirect.inc";i:8;s:52:"views/redirect_handler_field_redirect_operations.inc";i:9;s:51:"views/redirect_handler_field_redirect_link_edit.inc";i:10;s:53:"views/redirect_handler_field_redirect_link_delete.inc";}s:9:"configure";s:37:"admin/config/search/redirect/settings";s:7:"version";s:11:"7.x-1.0-rc1";s:7:"project";s:8:"redirect";s:9:"datestamp";s:10:"1347989995";s:5:"mtime";i:1347989995;s:12:"dependencies";a:0:{}s:7:"package";s:5:"Other";s:3:"php";s:5:"5.2.4";s:9:"bootstrap";i:0;}', -)) +]) ->execute(); diff --git a/web/modules/redirect/src/Tests/AssertRedirectTrait.php b/web/modules/redirect/tests/src/Functional/AssertRedirectTrait.php similarity index 51% rename from web/modules/redirect/src/Tests/AssertRedirectTrait.php rename to web/modules/redirect/tests/src/Functional/AssertRedirectTrait.php index ae9368886d..6fa229622f 100644 --- a/web/modules/redirect/src/Tests/AssertRedirectTrait.php +++ b/web/modules/redirect/tests/src/Functional/AssertRedirectTrait.php @@ -1,9 +1,9 @@ <?php -namespace Drupal\redirect\Tests; +namespace Drupal\Tests\redirect\Functional; -use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Url; +use GuzzleHttp\Exception\ClientException; /** * Asserts the redirect from a given path to the expected destination path. @@ -18,19 +18,30 @@ trait AssertRedirectTrait { * @param $expected_ending_url * The path where we expect it to redirect. If NULL value provided, no * redirect is expected. - * @param string $expected_ending_status + * @param int $expected_ending_status * The status we expect to get with the first request. + * @param string $method + * The HTTP METHOD to use. + * + * @return \Psr\Http\Message\ResponseInterface + * The HTTP response. */ - public function assertRedirect($path, $expected_ending_url, $expected_ending_status = 'HTTP/1.1 301 Moved Permanently') { - $this->drupalHead($path); - $headers = $this->drupalGetHeaders(TRUE); + public function assertRedirect($path, $expected_ending_url, $expected_ending_status = 301, $method = 'GET') { + $client = $this->getHttpClient(); + /** @var \Psr\Http\Message\ResponseInterface $response */ + $url = $this->getAbsoluteUrl($path); + try { + $response = $client->request($method, $url, ['allow_redirects' => false]); + } catch (ClientException $e) { + $this->assertEquals($expected_ending_status, $e->getResponse()->getStatusCode()); + return $e->getResponse(); + } - $ending_url = isset($headers[0]['location']) ? $headers[0]['location'] : NULL; - $message = SafeMarkup::format('Testing redirect from %from to %to. Ending url: %url', [ - '%from' => $path, - '%to' => $expected_ending_url, - '%url' => $ending_url, - ]); + $this->assertEquals($expected_ending_status, $response->getStatusCode()); + + $ending_url = $response->getHeader('location'); + $ending_url = $ending_url ? $ending_url[0] : NULL; + $message = "Testing redirect from $path to $expected_ending_url. Ending url: $ending_url"; if ($expected_ending_url == '<front>') { $expected_ending_url = Url::fromUri('base:')->setAbsolute()->toString(); @@ -46,8 +57,7 @@ public function assertRedirect($path, $expected_ending_url, $expected_ending_sta } $this->assertEqual($expected_ending_url, $ending_url, $message); - - $this->assertEqual($headers[0][':status'], $expected_ending_status); + return $response; } } diff --git a/web/modules/redirect/src/Tests/GlobalRedirectTest.php b/web/modules/redirect/tests/src/Functional/GlobalRedirectTest.php similarity index 61% rename from web/modules/redirect/src/Tests/GlobalRedirectTest.php rename to web/modules/redirect/tests/src/Functional/GlobalRedirectTest.php index 75e1f99871..6f806d6719 100644 --- a/web/modules/redirect/src/Tests/GlobalRedirectTest.php +++ b/web/modules/redirect/tests/src/Functional/GlobalRedirectTest.php @@ -1,18 +1,24 @@ <?php -namespace Drupal\redirect\Tests; +namespace Drupal\Tests\redirect\Functional; -use Drupal\Component\Utility\SafeMarkup; +use Behat\Mink\Driver\GoutteDriver; +use Drupal\Core\Cache\Cache; use Drupal\Core\Language\Language; -use Drupal\simpletest\WebTestBase; use Drupal\language\Entity\ConfigurableLanguage; +use Drupal\taxonomy\Entity\Term; +use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Global redirect test cases. * * @group redirect */ -class GlobalRedirectTest extends WebTestBase { +class GlobalRedirectTest extends BrowserTestBase { + + use PathAliasTestTrait; /** * Modules to enable. @@ -60,6 +66,31 @@ class GlobalRedirectTest extends WebTestBase { */ protected $node; + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + protected $minkDefaultDriverClass = GoutteDriver::class; + + /** + * {@inheritdoc} + */ + protected function initMink() { + $session = parent::initMink(); + + /** @var \Behat\Mink\Driver\GoutteDriver $driver */ + $driver = $session->getDriver(); + // Since we are testing low-level redirect stuff, the HTTP client should + // NOT automatically follow redirects sent by the server. + $driver->getClient()->followRedirects(FALSE); + + return $session; + } + /** * {@inheritdoc} */ @@ -99,10 +130,10 @@ protected function setUp() { // Create an alias for the create story path - this is used in the // "redirect with permissions testing" test. - \Drupal::service('path.alias_storage')->save('/admin/config/system/site-information', '/site-info'); + $this->createPathAlias('/admin/config/system/site-information', '/site-info'); // Create a taxonomy term for the forum. - $term = entity_create('taxonomy_term', [ + $term = Term::create([ 'name' => 'Test Forum Term', 'vid' => 'forums', 'langcode' => Language::LANGCODE_NOT_SPECIFIED, @@ -111,13 +142,13 @@ protected function setUp() { $this->forumTerm = $term; // Create another taxonomy vocabulary with a term. - $vocab = entity_create('taxonomy_vocabulary', [ + $vocab = Vocabulary::create([ 'name' => 'test vocab', 'vid' => 'test-vocab', 'langcode' => Language::LANGCODE_NOT_SPECIFIED, ]); $vocab->save(); - $term = entity_create('taxonomy_term', [ + $term = Term::create([ 'name' => 'Test Term', 'vid' => $vocab->id(), 'langcode' => Language::LANGCODE_NOT_SPECIFIED, @@ -132,14 +163,12 @@ protected function setUp() { * Will test the redirects. */ public function testRedirects() { - - // First test that the good stuff can be switched off. + // First, test that redirects can be disabled. $this->config->set('route_normalizer_enabled', FALSE)->save(); - $this->assertRedirect('index.php/node/' . $this->node->id(), NULL, 'HTTP/1.1 200 OK'); - $this->assertRedirect('index.php/test-node', NULL, 'HTTP/1.1 200 OK'); - $this->assertRedirect('test-node/', NULL, 'HTTP/1.1 200 OK'); - $this->assertRedirect('Test-node/', NULL, 'HTTP/1.1 200 OK'); - + $this->assertNoRedirect('index.php/node/' . $this->node->id()); + $this->assertNoRedirect('index.php/test-node'); + $this->assertNoRedirect('test-node/'); + $this->assertNoRedirect('Test-node/'); $this->config->set('route_normalizer_enabled', TRUE)->save(); // Test alias normalization. @@ -155,20 +184,35 @@ public function testRedirects() { // Test front page redirects. $this->config('system.site')->set('page.front', '/node')->save(); - $this->assertRedirect('node', '<front>'); + $this->assertRedirect('node', '/'); // Test front page redirects with an alias. - \Drupal::service('path.alias_storage')->save('/node', '/node-alias'); - $this->assertRedirect('node-alias', '<front>'); - - // Test post request. - $this->drupalPost('Test-node', 'application/json', array()); + $this->createPathAlias('/node', '/node-alias'); + $this->assertRedirect('node-alias', '/'); + + // Test a POST request. It should stay on the same path and not try to + // redirect. Because Mink does not provide methods to do plain POSTs, we + // need to use the underlying Guzzle HTTP client directly. + /** @var \Behat\Mink\Driver\GoutteDriver $driver */ + $driver = $this->getSession()->getDriver(); + $response = $driver->getClient() + ->getClient() + ->post($this->getAbsoluteUrl('Test-node'), [ + // Do not follow redirects. This way, we can assert that the server did + // not even _try_ to redirect us + 'allow_redirects' => FALSE, + 'headers' => [ + 'Accept' => 'application/json', + ], + ]); // Does not do a redirect, stays in the same path. - $this->assertEqual(basename($this->getUrl()), 'Test-node'); + $this->assertSame(200, $response->getStatusCode()); + $this->assertEmpty($response->getHeader('Location')); + $this->assertStringNotContainsString('http-equiv="refresh', (string) $response->getBody()); // Test the access checking. $this->config->set('access_check', TRUE)->save(); - $this->assertRedirect('admin/config/system/site-information', NULL, 'HTTP/1.1 403 Forbidden'); + $this->assertNoRedirect('admin/config/system/site-information', 403); $this->config->set('access_check', FALSE)->save(); // @todo - here it seems that the access check runs prior to our redirecting @@ -179,11 +223,14 @@ public function testRedirects() { $this->assertRedirect('Test-node?&foo&.bar=baz', 'test-node?&foo&.bar=baz'); // Test alias normalization with trailing ?. - $this->assertRedirect('test-node?', 'test-node'); + // @todo \GuzzleHttp\Psr7\Uri strips away the trailing ?, this should + // actually be a redirect but can't be tested with Guzzle. Improve in + // https://www.drupal.org/project/redirect/issues/3119503. + $this->assertNoRedirect('test-node?'); $this->assertRedirect('Test-node?', 'test-node'); // Test alias normalization still works without trailing ?. - $this->assertRedirect('test-node', NULL, 'HTTP/1.1 200 OK'); + $this->assertNoRedirect('test-node'); $this->assertRedirect('Test-node', 'test-node'); // Login as user with admin privileges. @@ -197,7 +244,7 @@ public function testRedirects() { $this->assertRedirect('Test-node', 'test-node'); $this->config->set('ignore_admin_path', TRUE)->save(); - $this->assertRedirect('admin/config/system/site-information', NULL, 'HTTP/1.1 200 OK'); + $this->assertNoRedirect('admin/config/system/site-information'); // Test alias normalization again with ignore_admin_path true. $this->assertRedirect('Test-node', 'test-node'); @@ -223,7 +270,7 @@ public function testLanguageRedirects() { 'language_configuration[content_translation]' => TRUE, ]; $this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type')); - $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Page')), 'Basic page content type has been updated.'); + $this->assertRaw(t('The content type %type has been updated.', ['%type' => 'Page'])); $spanish_node = $this->drupalCreateNode([ 'type' => 'page', @@ -237,60 +284,54 @@ public function testLanguageRedirects() { } /** - * Asserts the redirect from $path to the $expected_ending_url. + * Visits a path and asserts that it is a redirect. * * @param string $path * The request path. - * @param $expected_ending_url + * @param string $expected_destination * The path where we expect it to redirect. If NULL value provided, no * redirect is expected. - * @param string $expected_ending_status + * @param int $status_code * The status we expect to get with the first request. + * + * @throws \Behat\Mink\Exception\ExpectationException */ - public function assertRedirect($path, $expected_ending_url, $expected_ending_status = 'HTTP/1.1 301 Moved Permanently') { - $this->drupalHead($GLOBALS['base_url'] . '/' . $path); - $headers = $this->drupalGetHeaders(TRUE); - - $ending_url = isset($headers[0]['location']) ? $headers[0]['location'] : NULL; - $message = SafeMarkup::format('Testing redirect from %from to %to. Ending url: %url', array( - '%from' => $path, - '%to' => $expected_ending_url, - '%url' => $ending_url, - )); - - - if ($expected_ending_url == '<front>') { - $expected_ending_url = $GLOBALS['base_url'] . '/'; - } - elseif (!empty($expected_ending_url)) { - $expected_ending_url = $GLOBALS['base_url'] . '/' . $expected_ending_url; - } - else { - $expected_ending_url = NULL; - } - - $this->assertEqual($expected_ending_url, $ending_url); - - $this->assertEqual($headers[0][':status'], $expected_ending_status); - } - - /** - * @inheritdoc} - */ - protected function drupalHead($path, array $options = [], array $headers = []) { + public function assertRedirect($path, $expected_destination, $status_code = 301) { // Always just use getAbsolutePath() so that generating the link does not // alter special requests. $url = $this->getAbsoluteUrl($path); - $out = $this->curlExec([CURLOPT_NOBODY => TRUE, CURLOPT_URL => $url, CURLOPT_HTTPHEADER => $headers]); + $this->getSession()->visit($url); + // Ensure that any changes to variables in the other thread are picked up. $this->refreshVariables(); - if ($this->dumpHeaders) { - $this->verbose('GET request to: ' . $path . - '<hr />Ending URL: ' . $this->getUrl() . - '<hr />Headers: <pre>' . Html::escape(var_export(array_map('trim', $this->headers), TRUE)) . '</pre>'); - } + $assert_session = $this->assertSession(); + $assert_session->responseHeaderEquals('Location', $this->getAbsoluteUrl($expected_destination)); + $assert_session->statusCodeEquals($status_code); + } + + /** + * Visits a path and asserts that it is NOT a redirect. + * + * @param string $path + * The path to visit. + * @param int $status_code + * (optional) The expected HTTP status code. Defaults to 200. + * + * @throws \Behat\Mink\Exception\ExpectationException + */ + protected function assertNoRedirect($path, $status_code = 200) { + $url = $this->getAbsoluteUrl($path); + $this->getSession()->visit($url); + + $assert_session = $this->assertSession(); + $assert_session->statusCodeEquals($status_code); + $assert_session->responseHeaderEquals('Location', NULL); + $assert_session->responseNotContains('http-equiv="refresh'); + $assert_session->addressEquals($path); - return $out; + // Ensure that any changes to variables in the other thread are picked up. + $this->refreshVariables(); } + } diff --git a/web/modules/redirect/tests/src/Functional/RedirectNodeFormTest.php b/web/modules/redirect/tests/src/Functional/RedirectNodeFormTest.php new file mode 100644 index 0000000000..1bb379bc54 --- /dev/null +++ b/web/modules/redirect/tests/src/Functional/RedirectNodeFormTest.php @@ -0,0 +1,100 @@ +<?php + +namespace Drupal\Tests\redirect\Functional; + +use Drupal\Tests\BrowserTestBase; + +/** + * Test the redirect functionality on node forms. + * + * @group redirect + */ +class RedirectNodeFormTest extends BrowserTestBase { + + /** + * A normal logged in user. + * + * @var \Drupal\user\UserInterface + */ + protected $webUser; + + /** + * A user with permission to bypass content access checks, and add redirects. + * + * @var \Drupal\user\UserInterface + */ + protected $adminUser; + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = ['node', 'redirect']; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + // Create Basic page node type. + if ($this->profile != 'standard') { + $this->drupalCreateContentType([ + 'type' => 'page', + 'name' => 'Basic page', + 'display_submitted' => FALSE, + ]); + } + + $this->webUser = $this->drupalCreateUser([ + 'access content', + 'create page content', + 'edit own page content', + ]); + + $this->adminUser = $this->drupalCreateUser([ + 'access content', + 'administer redirects', + 'bypass node access', + ]); + } + + /** + * Test redirect functionality on the node edit form. + */ + public function testNodeForm() { + // Login as a regular user. + $this->drupalLogin($this->webUser); + + // Create "Basic page" content with title. + $settings = [ + 'title' => $this->randomMachineName(8), + ]; + $node = $this->drupalCreateNode($settings); + + // Load the node edit form. + $this->drupalGet('node/' . $node->id() . '/edit'); + + // Make sure the redirect add button is not visible to this regular user. + $this->assertNoRaw('Add URL redirect'); + + // Now edit the same node as an admin user. + $this->drupalLogin($this->adminUser); + $this->drupalGet('node/' . $node->id() . '/edit'); + + // Make sure the redirect add button is visible for the admin user. + $this->assertRaw('Add URL redirect'); + + // Make sure the link works as expected. + $this->clickLink('Add URL redirect'); + $this->assertUrl('admin/config/search/redirect/add'); + $this->assertFieldsByValue($this->xpath("//input[@id = 'edit-redirect-redirect-0-uri']"), '/node/' . $node->id(), 'To: field correctly pre-filled.'); + } + +} diff --git a/web/modules/redirect/src/Tests/RedirectUILanguageTest.php b/web/modules/redirect/tests/src/Functional/RedirectUILanguageTest.php similarity index 73% rename from web/modules/redirect/src/Tests/RedirectUILanguageTest.php rename to web/modules/redirect/tests/src/Functional/RedirectUILanguageTest.php index 6d9a727918..372576cab6 100644 --- a/web/modules/redirect/src/Tests/RedirectUILanguageTest.php +++ b/web/modules/redirect/tests/src/Functional/RedirectUILanguageTest.php @@ -1,6 +1,7 @@ <?php -namespace Drupal\redirect\Tests; +namespace Drupal\Tests\redirect\Functional; + use Drupal\language\Entity\ConfigurableLanguage; /** @@ -48,27 +49,27 @@ public function testLanguageSpecificRedirects() { $this->assertOptionByText('edit-language-0-value', '- All languages -'); // Add a redirect for english. - $this->drupalPostForm('admin/config/search/redirect/add', array( + $this->drupalPostForm('admin/config/search/redirect/add', [ 'redirect_source[0][path]' => 'langpath', 'redirect_redirect[0][uri]' => '/user', 'language[0][value]' => 'en', - ), t('Save')); + ], t('Save')); // Add a redirect for germany. - $this->drupalPostForm('admin/config/search/redirect/add', array( + $this->drupalPostForm('admin/config/search/redirect/add', [ 'redirect_source[0][path]' => 'langpath', 'redirect_redirect[0][uri]' => '<front>', 'language[0][value]' => 'de', - ), t('Save')); + ], t('Save')); // Check redirect for english. - $this->assertRedirect('langpath', '/user', 'HTTP/1.1 301 Moved Permanently'); + $this->assertRedirect('langpath', '/user', 301); // Check redirect for germany. - $this->assertRedirect('de/langpath', '/de', 'HTTP/1.1 301 Moved Permanently'); + $this->assertRedirect('de/langpath', '/de', 301); // Check no redirect for spanish. - $this->assertRedirect('es/langpath', NULL, 'HTTP/1.1 404 Not Found'); + $this->assertRedirect('es/langpath', NULL, 404); } /** @@ -78,17 +79,17 @@ public function testUndefinedLangugageRedirects() { $this->drupalLogin($this->adminUser); // Add a redirect for english. - $this->drupalPostForm('admin/config/search/redirect/add', array( + $this->drupalPostForm('admin/config/search/redirect/add', [ 'redirect_source[0][path]' => 'langpath', 'redirect_redirect[0][uri]' => '/user', 'language[0][value]' => 'und', - ), t('Save')); + ], t('Save')); // Check redirect for english. - $this->assertRedirect('langpath', '/user', 'HTTP/1.1 301 Moved Permanently'); + $this->assertRedirect('langpath', '/user', 301); // Check redirect for spanish. - $this->assertRedirect('es/langpath', '/es/user', 'HTTP/1.1 301 Moved Permanently'); + $this->assertRedirect('es/langpath', '/es/user', 301); } /** @@ -98,17 +99,17 @@ public function testEditRedirectLanguage() { $this->drupalLogin($this->adminUser); // Add a redirect for english. - $this->drupalPostForm('admin/config/search/redirect/add', array( + $this->drupalPostForm('admin/config/search/redirect/add', [ 'redirect_source[0][path]' => 'langpath', 'redirect_redirect[0][uri]' => '/user', 'language[0][value]' => 'en', - ), t('Save')); + ], t('Save')); // Check redirect for english. - $this->assertRedirect('langpath', '/user', 'HTTP/1.1 301 Moved Permanently'); + $this->assertRedirect('langpath', '/user', 301); // Check that redirect for Germany is not working. - $this->assertRedirect('de/langpath', NULL, 'HTTP/1.1 404 Not Found'); + $this->assertRedirect('de/langpath', NULL, 404); // Edit the redirect and change the language. $this->drupalGet('admin/config/search/redirect'); @@ -116,10 +117,10 @@ public function testEditRedirectLanguage() { $this->drupalPostForm(NULL, ['language[0][value]' => 'de'], t('Save')); // Check redirect for english is NOT working now. - $this->assertRedirect('langpath', NULL, 'HTTP/1.1 404 Not Found'); + $this->assertRedirect('langpath', NULL, 404); // Check that redirect for Germany is now working. - $this->assertRedirect('de/langpath', '/de/user', 'HTTP/1.1 301 Moved Permanently'); + $this->assertRedirect('de/langpath', '/de/user', 301); } } diff --git a/web/modules/redirect/tests/src/Functional/RedirectUITest.php b/web/modules/redirect/tests/src/Functional/RedirectUITest.php new file mode 100644 index 0000000000..51fb2abde8 --- /dev/null +++ b/web/modules/redirect/tests/src/Functional/RedirectUITest.php @@ -0,0 +1,274 @@ +<?php + +namespace Drupal\Tests\redirect\Functional; + +use Drupal\Component\Render\FormattableMarkup; +use Drupal\Core\Language\Language; +use Drupal\Core\Logger\RfcLogLevel; +use Drupal\Core\Url; +use Drupal\taxonomy\Entity\Term; +use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\Tests\BrowserTestBase; + +/** + * UI tests for redirect module. + * + * @group redirect + */ +class RedirectUITest extends BrowserTestBase { + + use AssertRedirectTrait; + + /** + * @var \Drupal\Core\Session\AccountInterface + */ + protected $adminUser; + + /** + * @var \Drupal\redirect\RedirectRepository + */ + protected $repository; + + /** + * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage + */ + protected $storage; + + /** + * {@inheritdoc} + */ + public static $modules = ['redirect', 'node', 'path', 'dblog', 'views', 'taxonomy']; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + $this->adminUser = $this->drupalCreateUser([ + 'administer redirects', + 'administer redirect settings', + 'access content', + 'bypass node access', + 'create url aliases', + 'administer taxonomy', + 'administer url aliases', + ]); + + $this->repository = \Drupal::service('redirect.repository'); + + $this->storage = \Drupal::entityTypeManager()->getStorage('redirect'); + } + + /** + * Tests redirects being automatically created upon path alias change. + */ + public function testAutomaticRedirects() { + $this->drupalLogin($this->adminUser); + + // Create a node and update its path alias which should result in a redirect + // being automatically created from the old alias to the new one. + $node = $this->drupalCreateNode([ + 'type' => 'article', + 'langcode' => Language::LANGCODE_NOT_SPECIFIED, + 'path' => ['alias' => '/node_test_alias'], + ]); + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->assertText(t('No URL redirects available.')); + $this->drupalPostForm('node/' . $node->id() . '/edit', ['path[0][alias]' => '/node_test_alias_updated'], t('Save')); + + $redirect = $this->repository->findMatchingRedirect('node_test_alias', [], Language::LANGCODE_NOT_SPECIFIED); + $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:node_test_alias_updated')->toString()); + // Test if the automatically created redirect works. + $this->assertRedirect('node_test_alias', 'node_test_alias_updated'); + + // Test that changing the path back deletes the first redirect, creates + // a new one and does not result in a loop. + $this->drupalPostForm('node/' . $node->id() . '/edit', ['path[0][alias]' => '/node_test_alias'], t('Save')); + $redirect = $this->repository->findMatchingRedirect('node_test_alias', [], Language::LANGCODE_NOT_SPECIFIED); + $this->assertTrue(empty($redirect)); + + \Drupal::service('path_alias.manager')->cacheClear(); + $redirect = $this->repository->findMatchingRedirect('node_test_alias_updated', [], Language::LANGCODE_NOT_SPECIFIED); + + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->assertText($redirect->getSourcePathWithQuery()); + $this->assertLinkByHref(Url::fromRoute('entity.redirect.edit_form', ['redirect' => $redirect->id()])->toString()); + $this->assertLinkByHref(Url::fromRoute('entity.redirect.delete_form', ['redirect' => $redirect->id()])->toString()); + + $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:node_test_alias')->toString()); + // Test if the automatically created redirect works. + $this->assertRedirect('node_test_alias_updated', 'node_test_alias'); + + // Test that the redirect will be deleted upon node deletion. + $this->drupalPostForm('node/' . $node->id() . '/delete', [], t('Delete')); + $redirect = $this->repository->findMatchingRedirect('node_test_alias_updated', [], Language::LANGCODE_NOT_SPECIFIED); + $this->assertTrue(empty($redirect)); + + // Create a term and update its path alias and check if we have a redirect + // from the previous path alias to the new one. + $term = $this->createTerm($this->createVocabulary()); + $this->drupalPostForm('taxonomy/term/' . $term->id() . '/edit', ['path[0][alias]' => '/term_test_alias_updated'], t('Save')); + $redirect = $this->repository->findMatchingRedirect('term_test_alias'); + $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:term_test_alias_updated')->toString()); + // Test if the automatically created redirect works. + $this->assertRedirect('term_test_alias', 'term_test_alias_updated'); + + if (version_compare(\Drupal::VERSION, '8.8', '>=')) { + $path_field = 'path[0][value]'; + $alias_field = 'alias[0][value]'; + } + else { + $path_field = 'source'; + $alias_field = 'alias'; + } + + // Test the path alias update via the admin path form. + $this->drupalPostForm('admin/config/search/path/add', [ + $path_field => '/node', + $alias_field => '/aaa_path_alias', + ], t('Save')); + // Note that here we rely on fact that we land on the path alias list page + // and the default sort is by the alias, which implies that the first edit + // link leads to the edit page of the aaa_path_alias. + $this->clickLink(t('Edit')); + $this->drupalPostForm(NULL, [$alias_field => '/aaa_path_alias_updated'], t('Save')); + $redirect = $this->repository->findMatchingRedirect('aaa_path_alias', [], 'en'); + $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:aaa_path_alias_updated')->toString()); + // Test if the automatically created redirect works. + $this->assertRedirect('aaa_path_alias', 'aaa_path_alias_updated'); + + // Test the automatically created redirect shows up in the form correctly. + $this->drupalGet('admin/config/search/redirect/edit/' . $redirect->id()); + $this->assertFieldByName('redirect_source[0][path]', 'aaa_path_alias'); + $this->assertFieldByName('redirect_redirect[0][uri]', '/node'); + } + + /** + * Test the redirect loop protection and logging. + */ + function testRedirectLoop() { + // Redirect loop redirection only works when page caching is disabled. + \Drupal::service('module_installer')->uninstall(['page_cache']); + + /** @var \Drupal\redirect\Entity\Redirect $redirect1 */ + $redirect1 = $this->storage->create(); + $redirect1->setSource('node'); + $redirect1->setRedirect('admin'); + $redirect1->setStatusCode(301); + $redirect1->save(); + + /** @var \Drupal\redirect\Entity\Redirect $redirect2 */ + $redirect2 = $this->storage->create(); + $redirect2->setSource('admin'); + $redirect2->setRedirect('node'); + $redirect2->setStatusCode(301); + $redirect2->save(); + + $this->maximumRedirects = 10; + $this->drupalGet('node'); + $this->assertText('Service unavailable'); + $this->assertResponse(503); + + $log = \Drupal::database()->select('watchdog')->fields('watchdog')->condition('type', 'redirect')->execute()->fetchAll(); + if (count($log) == 0) { + $this->fail('Redirect loop has not been logged'); + } + else { + $log = reset($log); + $this->assertEquals(RfcLogLevel::WARNING, $log->severity); + $this->assertEquals('Redirect loop identified at %path for redirect %rid', $log->message); + $this->assertEquals(['%path' => '/node', '%rid' => $redirect1->id()], unserialize($log->variables)); + } + } + + /** + * Returns a new vocabulary with random properties. + */ + function createVocabulary() { + // Create a vocabulary. + $vocabulary = Vocabulary::create([ + 'name' => $this->randomMachineName(), + 'description' => $this->randomMachineName(), + 'vid' => mb_strtolower($this->randomMachineName()), + 'langcode' => Language::LANGCODE_NOT_SPECIFIED, + 'weight' => mt_rand(0, 10), + ]); + $vocabulary->save(); + return $vocabulary; + } + + /** + * Returns a new term with random properties in vocabulary $vid. + */ + function createTerm($vocabulary) { + $filter_formats = filter_formats(); + $format = array_pop($filter_formats); + $term = Term::create([ + 'name' => $this->randomMachineName(), + 'description' => [ + 'value' => $this->randomMachineName(), + // Use the first available text format. + 'format' => $format->id(), + ], + 'vid' => $vocabulary->id(), + 'langcode' => Language::LANGCODE_NOT_SPECIFIED, + 'path' => ['alias' => '/term_test_alias'], + ]); + $term->save(); + return $term; + } + + /** + * Test cache tags. + * + * @todo Not sure this belongs in a UI test, but a full web test is needed. + */ + public function testCacheTags() { + /** @var \Drupal\redirect\Entity\Redirect $redirect1 */ + $redirect1 = $this->storage->create(); + $redirect1->setSource('test-redirect'); + $redirect1->setRedirect('node'); + $redirect1->setStatusCode(301); + $redirect1->save(); + + $response = $this->assertRedirect('test-redirect', 'node'); + // Note, self::assertCacheTag() cannot be used here since it only looks at + // the final set of headers. + $expected = 'http_response ' . implode(' ', $redirect1->getCacheTags()); + $this->assertEqual($expected, $response->getHeader('x-drupal-cache-tags')[0], 'Redirect cache tags properly set.'); + + // First request should be a cache MISS. + $this->assertEqual($response->getHeader('x-drupal-cache')[0], 'MISS', 'First request to the redirect was not cached.'); + + // Second request should be cached. + $response = $this->assertRedirect('test-redirect', 'node'); + $this->assertEqual($response->getHeader('x-drupal-cache')[0], 'HIT', 'The second request to the redirect was cached.'); + + // Ensure that the redirect has been cleared from cache when deleted. + $redirect1->delete(); + $this->drupalGet('test-redirect'); + $this->assertResponse(404, 'Deleted redirect properly clears the internal page cache.'); + } + + /** + * Test external destinations. + */ + public function testExternal() { + $redirect = $this->storage->create(); + $redirect->setSource('a-path'); + // @todo Redirect::setRedirect() assumes that all redirects are internal. + $redirect->redirect_redirect->set(0, ['uri' => 'https://www.example.org']); + $redirect->setStatusCode(301); + $redirect->save(); + $this->assertRedirect('a-path', 'https://www.example.org'); + $this->drupalLogin($this->adminUser); + } + +} diff --git a/web/modules/redirect/tests/src/FunctionalJavascript/RedirectJavascriptTest.php b/web/modules/redirect/tests/src/FunctionalJavascript/RedirectJavascriptTest.php new file mode 100644 index 0000000000..ae8895e54e --- /dev/null +++ b/web/modules/redirect/tests/src/FunctionalJavascript/RedirectJavascriptTest.php @@ -0,0 +1,285 @@ +<?php + +namespace Drupal\Tests\redirect\FunctionalJavascript; + +use Drupal\Core\Url; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; + +/** + * UI tests for redirect module. + * + * @group redirect + */ +class RedirectJavascriptTest extends WebDriverTestBase { + + /** + * @var \Drupal\Core\Session\AccountInterface + */ + protected $adminUser; + + /** + * @var \Drupal\redirect\RedirectRepository + */ + protected $repository; + + /** + * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage + */ + protected $storage; + + /** + * {@inheritdoc} + */ + public static $modules = ['redirect', 'node', 'path', 'dblog', 'views', 'taxonomy']; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + $this->adminUser = $this->drupalCreateUser( + [ + 'administer redirects', + 'administer redirect settings', + 'access content', + 'bypass node access', + 'create url aliases', + 'administer taxonomy', + 'administer url aliases', + ] + ); + + $this->repository = \Drupal::service('redirect.repository'); + + $this->storage = $this->container->get('entity_type.manager')->getStorage('redirect'); + } + + /** + * Test the redirect UI. + */ + public function testRedirectUI() { + $this->drupalLogin($this->adminUser); + + // Test populating the redirect form with predefined values. + $this->drupalGet( + 'admin/config/search/redirect/add', [ + 'query' => [ + 'source' => 'non-existing', + 'source_query' => ['key' => 'val', 'key1' => 'val1'], + 'redirect' => 'node', + 'redirect_options' => ['query' => ['key' => 'val', 'key1' => 'val1']], + ] + ] + ); + $this->assertFieldByName('redirect_source[0][path]', 'non-existing?key=val&key1=val1'); + $this->assertFieldByName('redirect_redirect[0][uri]', '/node?key=val&key1=val1'); + + $this->drupalGet('admin/config/search/redirect/add'); + $page = $this->getSession()->getPage(); + $page->fillField('redirect_source[0][path]', 'non-existing'); + $page->fillField('redirect_redirect[0][uri]', '/node'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $page->pressButton('Save'); + + // Try to find the redirect we just created. + $redirect = $this->repository->findMatchingRedirect('non-existing'); + $this->assertEqual($redirect->getSourceUrl(), Url::fromUri('base:non-existing')->toString()); + $this->assertEqual($redirect->getRedirectUrl()->toString(), Url::fromUri('base:node')->toString()); + + // After adding the redirect we should end up in the list. Check if the + // redirect is listed. + $this->assertUrl('admin/config/search/redirect'); + $this->assertSession()->pageTextContains('non-existing'); + $this->assertLink(Url::fromUri('base:node')->toString()); + $this->assertSession()->pageTextContains(t('Not specified')); + + // Test the edit form and update action. + $this->clickLink(t('Edit')); + $this->assertFieldByName('redirect_source[0][path]', 'non-existing'); + $this->assertFieldByName('redirect_redirect[0][uri]', '/node'); + $this->assertFieldByName('status_code', $redirect->getStatusCode()); + + // Append a query string to see if we handle query data properly. + $this->drupalPostForm( + NULL, [ + 'redirect_source[0][path]' => 'non-existing?key=value', + ], t('Save') + ); + + // Check the location after update and check if the value has been updated + // in the list. + $this->assertUrl('admin/config/search/redirect'); + $this->assertSession()->pageTextContains('non-existing?key=value'); + + // The path field should not contain the query string and therefore we + // should be able to load the redirect using only the url part without + // query. + $this->storage->resetCache(); + $redirects = $this->repository->findBySourcePath('non-existing'); + $redirect = array_shift($redirects); + $this->assertEqual($redirect->getSourceUrl(), Url::fromUri('base:non-existing', ['query' => ['key' => 'value']])->toString()); + + // Test the source url hints. + // The hint about an existing base path. + $this->drupalGet('admin/config/search/redirect/add'); + $page->fillField('redirect_source[0][path]', 'non-existing?key=value'); + $page->fillField('redirect_redirect[0][uri]', ''); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertRaw( + t( + 'The base source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', + ['%source' => 'non-existing?key=value', '@edit-page' => $redirect->toUrl('edit-form')->toString()] + ) + ); + + // The hint about a valid path. + $this->drupalGet('admin/config/search/redirect/add'); + $page->fillField('redirect_source[0][path]', 'node'); + $page->fillField('redirect_redirect[0][uri]', ''); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertRaw( + t( + 'The source path %path is likely a valid path. It is preferred to <a href="@url-alias">create URL aliases</a> for existing paths rather than redirects.', + ['%path' => 'node', '@url-alias' => Url::fromRoute('entity.path_alias.add_form')->toString()] + ) + ); + + // Test validation. + // Duplicate redirect. + $this->drupalGet('admin/config/search/redirect/add'); + $page = $this->getSession()->getPage(); + $page->fillField('redirect_source[0][path]', 'non-existing?key=value'); + $page->fillField('redirect_redirect[0][uri]', '/node'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $page->pressButton('Save'); + $this->assertRaw( + t( + 'The source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', + ['%source' => 'non-existing?key=value', '@edit-page' => $redirect->toUrl('edit-form')->toString()] + ) + ); + + // Redirecting to itself. + $this->drupalGet('admin/config/search/redirect/add'); + $page = $this->getSession()->getPage(); + $page->fillField('redirect_source[0][path]', 'node'); + $page->fillField('redirect_redirect[0][uri]', '/node'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $page->pressButton('Save'); + $this->assertRaw(t('You are attempting to redirect the page to itself. This will result in an infinite loop.')); + + // Redirecting the front page. + $this->drupalGet('admin/config/search/redirect/add'); + $page = $this->getSession()->getPage(); + $page->fillField('redirect_source[0][path]', '<front>'); + $page->fillField('redirect_redirect[0][uri]', '/node'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $page->pressButton('Save'); + $this->assertRaw(t('It is not allowed to create a redirect from the front page.')); + + // Redirecting a url with fragment. + $this->drupalGet('admin/config/search/redirect/add'); + $page = $this->getSession()->getPage(); + $page->fillField('redirect_source[0][path]', 'page-to-redirect#content'); + $page->fillField('redirect_redirect[0][uri]', '/node'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $page->pressButton('Save'); + $this->assertRaw(t('The anchor fragments are not allowed.')); + + // Adding path that starts with / + $this->drupalGet('admin/config/search/redirect/add'); + $page = $this->getSession()->getPage(); + $page->fillField('redirect_source[0][path]', '/page-to-redirect'); + $page->fillField('redirect_redirect[0][uri]', '/node'); + // Wait on ajax is unpredictable, wait for one second. + $this->assertSession()->assertWaitOnAjaxRequest(); + $page->pressButton('Save'); + $this->assertRaw(t('The url to redirect from should not start with a forward slash (/).')); + + // Test filters. + // Add a new redirect. + $this->drupalGet('admin/config/search/redirect/add'); + $page = $this->getSession()->getPage(); + $page->fillField('redirect_source[0][path]', 'test27'); + $page->fillField('redirect_redirect[0][uri]', '/node'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $page->pressButton('Save'); + + // Filter with non existing value. + $this->drupalGet( + 'admin/config/search/redirect', [ + 'query' => [ + 'status_code' => '3', + ], + ] + ); + + $rows = $this->xpath('//tbody/tr'); + // Check if the list has no rows. + $this->assertTrue(count($rows) == 0); + + // Filter with existing values. + $this->drupalGet( + 'admin/config/search/redirect', [ + 'query' => [ + 'redirect_source__path' => 'test', + 'status_code' => '2', + ], + ] + ); + + $rows = $this->xpath('//tbody/tr'); + // Check if the list has 1 row. + $this->assertTrue(count($rows) == 1); + + $this->drupalGet( + 'admin/config/search/redirect', [ + 'query' => [ + 'redirect_redirect__uri' => 'nod', + ], + ] + ); + + $rows = $this->xpath('//tbody/tr'); + // Check if the list has 2 rows. + $this->assertTrue(count($rows) == 2); + + // Test the plural form of the bulk delete action. + $this->drupalGet('admin/config/search/redirect'); + $edit = [ + 'redirect_bulk_form[0]' => TRUE, + 'redirect_bulk_form[1]' => TRUE, + ]; + $this->drupalPostForm(NULL, $edit, t('Apply to selected items')); + $this->assertSession()->pageTextContains('Are you sure you want to delete these redirects?'); + $this->clickLink('Cancel'); + + // Test the delete action. + $page->find('css', '.dropbutton-toggle button')->press(); + $this->clickLink(t('Delete')); + $this->assertRaw( + t( + 'Are you sure you want to delete the URL redirect from %source to %redirect?', + ['%source' => Url::fromUri('base:non-existing', ['query' => ['key' => 'value']])->toString(), '%redirect' => Url::fromUri('base:node')->toString()] + ) + ); + $this->drupalPostForm(NULL, [], t('Delete')); + $this->assertUrl('admin/config/search/redirect'); + + // Test the bulk delete action. + $this->drupalPostForm(NULL, ['redirect_bulk_form[0]' => TRUE], t('Apply to selected items')); + $this->assertSession()->pageTextContains('Are you sure you want to delete this redirect?'); + $this->assertSession()->pageTextContains('test27'); + $this->drupalPostForm(NULL, [], t('Delete')); + + $this->assertSession()->pageTextContains(t('There is no redirect yet.')); + } + +} diff --git a/web/modules/redirect/tests/src/Kernel/Migrate/d6/PathRedirectTest.php b/web/modules/redirect/tests/src/Kernel/Migrate/d6/PathRedirectTest.php index 9383adcd1a..c3eebbc788 100644 --- a/web/modules/redirect/tests/src/Kernel/Migrate/d6/PathRedirectTest.php +++ b/web/modules/redirect/tests/src/Kernel/Migrate/d6/PathRedirectTest.php @@ -16,14 +16,13 @@ class PathRedirectTest extends MigrateDrupalTestBase { /** * {@inheritdoc} */ - public static $modules = array('redirect', 'link'); + public static $modules = ['redirect', 'link', 'path_alias']; /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); - $this->installSchema('system', array('router')); $this->installEntitySchema('redirect'); $this->loadFixture( __DIR__ . '/../../../../../tests/fixtures/drupal6.php'); @@ -39,7 +38,7 @@ public function testPathRedirect() { $redirect = Redirect::load(5); $this->assertSame($this->getMigration('d6_path_redirect') ->getIdMap() - ->lookupDestinationID(array(5)), array($redirect->id())); + ->lookupDestinationIds([5]), [[$redirect->id()]]); $this->assertSame("/test/source/url", $redirect->getSourceUrl()); $this->assertSame("base:test/redirect/url", $redirect->getRedirectUrl()->toUriString()); diff --git a/web/modules/redirect/tests/src/Kernel/Migrate/d7/PathRedirectSourceTest.php b/web/modules/redirect/tests/src/Kernel/Migrate/d7/PathRedirectSourceTest.php new file mode 100644 index 0000000000..a5f37a8381 --- /dev/null +++ b/web/modules/redirect/tests/src/Kernel/Migrate/d7/PathRedirectSourceTest.php @@ -0,0 +1,54 @@ +<?php + +namespace Drupal\Tests\redirect\Kernel\Migrate\d7; + +use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase; + +/** + * Tests D7 redirect source plugin. + * + * @group redirect + * @covers Drupal\redirect\Plugin\migrate\source\d7\PathRedirect + */ +class PathRedirectSourceTest extends MigrateSqlSourceTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = ['redirect', 'link', 'migrate_drupal', 'path_alias']; + + /** + * {@inheritdoc} + */ + public function providerSource() { + $tests = []; + $tests[0]['source_data']['variable'] = [ + [ + 'name' => 'redirect_default_status_code', + 'value' => 's:3:"307";', + ] + ]; + + $tests[0]['source_data']['redirect'] = [ + [ + 'rid' => 5, + 'hash' => 'MwmDbnA65ag646gtEdLqmAqTbF0qQerse63RkQmJK_Y', + 'type' => 'redirect', + 'uid' => 5, + 'source' => 'test/source/url', + 'source_options' => '', + 'redirect' => 'test/redirect/url', + 'redirect_options' => '', + 'language' => 'und', + 'status_code' => 301, + 'count' => 2518, + 'access' => 1449497138, + ], + ]; + // The expected results are identical to the source data. + $tests[0]['expected_data'] = $tests[0]['source_data']['redirect']; + + return $tests; + } + +} diff --git a/web/modules/redirect/tests/src/Kernel/Migrate/d7/PathRedirectTest.php b/web/modules/redirect/tests/src/Kernel/Migrate/d7/PathRedirectTest.php index 469e5ccd5c..15134cdcc1 100644 --- a/web/modules/redirect/tests/src/Kernel/Migrate/d7/PathRedirectTest.php +++ b/web/modules/redirect/tests/src/Kernel/Migrate/d7/PathRedirectTest.php @@ -21,7 +21,7 @@ class PathRedirectTest extends MigrateDrupalTestBase { /** * {@inheritdoc} */ - public static $modules = array('redirect', 'link'); + public static $modules = ['redirect', 'link', 'path_alias']; /** * {@inheritdoc} @@ -51,7 +51,7 @@ protected function assertEntity($id, $source_url, $redirect_url, $status_code) { $redirect = Redirect::load($id); $this->assertSame($this->getMigration('d7_path_redirect') ->getIdMap() - ->lookupDestinationID([$id]), [$redirect->id()]); + ->lookupDestinationIds([$id]), [[$redirect->id()]]); $this->assertSame($source_url, $redirect->getSourceUrl()); $this->assertSame($redirect_url, $redirect->getRedirectUrl() ->toUriString()); diff --git a/web/modules/redirect/tests/src/Kernel/RedirectAPITest.php b/web/modules/redirect/tests/src/Kernel/RedirectAPITest.php index 6d982f7c1f..a379b12e25 100644 --- a/web/modules/redirect/tests/src/Kernel/RedirectAPITest.php +++ b/web/modules/redirect/tests/src/Kernel/RedirectAPITest.php @@ -16,16 +16,18 @@ class RedirectAPITest extends KernelTestBase { /** + * The redirect storage. + * * @var \Drupal\Core\Entity\EntityStorageInterface */ - protected $controller; + protected $storage; /** * Modules to enable. * * @var array */ - public static $modules = array('redirect', 'link', 'field', 'system', 'user', 'language', 'views'); + public static $modules = ['redirect', 'link', 'field', 'system', 'user', 'language', 'views', 'path_alias']; /** * {@inheritdoc} @@ -35,13 +37,12 @@ public function setUp() { $this->installEntitySchema('redirect'); $this->installEntitySchema('user'); - $this->installSchema('system', ['router']); - $this->installConfig(array('redirect')); + $this->installConfig(['redirect']); $language = ConfigurableLanguage::createFromLangcode('de'); $language->save(); - $this->controller = $this->container->get('entity.manager')->getStorage('redirect'); + $this->storage = $this->container->get('entity_type.manager')->getStorage('redirect'); } /** @@ -50,38 +51,38 @@ public function setUp() { public function testRedirectEntity() { // Create a redirect and test if hash has been generated correctly. /** @var \Drupal\redirect\Entity\Redirect $redirect */ - $redirect = $this->controller->create(); - $redirect->setSource('some-url', array('key' => 'val')); + $redirect = $this->storage->create(); + $redirect->setSource('some-url', ['key' => 'val']); $redirect->setRedirect('node'); $redirect->save(); - $this->assertEquals(Redirect::generateHash('some-url', array('key' => 'val'), Language::LANGCODE_NOT_SPECIFIED), $redirect->getHash()); + $this->assertEquals(Redirect::generateHash('some-url', ['key' => 'val'], Language::LANGCODE_NOT_SPECIFIED), $redirect->getHash()); // Update the redirect source query and check if hash has been updated as // expected. - $redirect->setSource('some-url', array('key1' => 'val1')); + $redirect->setSource('some-url', ['key1' => 'val1']); $redirect->save(); - $this->assertEqual(Redirect::generateHash('some-url', array('key1' => 'val1'), Language::LANGCODE_NOT_SPECIFIED), $redirect->getHash()); + $this->assertEquals(Redirect::generateHash('some-url', ['key1' => 'val1'], Language::LANGCODE_NOT_SPECIFIED), $redirect->getHash()); // Update the redirect source path and check if hash has been updated as // expected. - $redirect->setSource('another-url', array('key1' => 'val1')); + $redirect->setSource('another-url', ['key1' => 'val1']); $redirect->save(); - $this->assertEqual(Redirect::generateHash('another-url', array('key1' => 'val1'), Language::LANGCODE_NOT_SPECIFIED), $redirect->getHash()); + $this->assertEquals(Redirect::generateHash('another-url', ['key1' => 'val1'], Language::LANGCODE_NOT_SPECIFIED), $redirect->getHash()); // Update the redirect language and check if hash has been updated as // expected. $redirect->setLanguage('de'); $redirect->save(); - $this->assertEqual(Redirect::generateHash('another-url', array('key1' => 'val1'), 'de'), $redirect->getHash()); + $this->assertEquals(Redirect::generateHash('another-url', ['key1' => 'val1'], 'de'), $redirect->getHash()); // Create a few more redirects to test the select. for ($i = 0; $i < 5; $i++) { - $redirect = $this->controller->create(); + $redirect = $this->storage->create(); $redirect->setSource($this->randomMachineName()); $redirect->save(); } /** @var \Drupal\redirect\RedirectRepository $repository */ $repository = \Drupal::service('redirect.repository'); - $redirect = $repository->findMatchingRedirect('another-url', array('key1' => 'val1'), 'de'); + $redirect = $repository->findMatchingRedirect('another-url', ['key1' => 'val1'], 'de'); if (!empty($redirect)) { - $this->assertEqual($redirect->getSourceUrl(), '/another-url?key1=val1'); + $this->assertEquals($redirect->getSourceUrl(), '/another-url?key1=val1'); } else { $this->fail(t('Failed to find matching redirect.')); @@ -91,20 +92,20 @@ public function testRedirectEntity() { $redirects = $repository->findBySourcePath('another-url'); $redirect = array_shift($redirects); if (!empty($redirect)) { - $this->assertEqual($redirect->getSourceUrl(), '/another-url?key1=val1'); + $this->assertEquals($redirect->getSourceUrl(), '/another-url?key1=val1'); } else { $this->fail(t('Failed to find redirect by source path.')); } // Test passthrough_querystring. - $redirect = $this->controller->create(); + $redirect = $this->storage->create(); $redirect->setSource('a-different-url'); $redirect->setRedirect('node'); $redirect->save(); $redirect = $repository->findMatchingRedirect('a-different-url', ['key1' => 'val1'], 'de'); if (!empty($redirect)) { - $this->assertEqual($redirect->getSourceUrl(), '/a-different-url'); + $this->assertEquals($redirect->getSourceUrl(), '/a-different-url'); } else { $this->fail('Failed to find redirect by source path with query string.'); @@ -113,26 +114,26 @@ public function testRedirectEntity() { // Add another redirect to the same path, with a query. This should always // be found before the source without a query set. /** @var \Drupal\redirect\Entity\Redirect $new_redirect */ - $new_redirect = $this->controller->create(); + $new_redirect = $this->storage->create(); $new_redirect->setSource('a-different-url', ['foo' => 'bar']); $new_redirect->setRedirect('node'); $new_redirect->save(); $found = $repository->findMatchingRedirect('a-different-url', ['foo' => 'bar'], 'de'); if (!empty($found)) { - $this->assertEqual($found->getSourceUrl(), '/a-different-url?foo=bar'); + $this->assertEquals($found->getSourceUrl(), '/a-different-url?foo=bar'); } else { $this->fail('Failed to find a redirect by source path with query string.'); } // Add a redirect to an external URL. - $external_redirect = $this->controller->create(); + $external_redirect = $this->storage->create(); $external_redirect->setSource('google'); $external_redirect->setRedirect('https://google.com'); $external_redirect->save(); $found = $repository->findMatchingRedirect('google'); if (!empty($found)) { - $this->assertEqual($found->getRedirectUrl()->toString(), 'https://google.com'); + $this->assertEquals($found->getRedirectUrl()->toString(), 'https://google.com'); } else { $this->fail('Failed to find a redirect for google.'); @@ -140,21 +141,21 @@ public function testRedirectEntity() { // Hashes should be case-insensitive since the source paths are. /** @var \Drupal\redirect\Entity\Redirect $redirect */ - $redirect = $this->controller->create(); + $redirect = $this->storage->create(); $redirect->setSource('Case-Sensitive-Path'); $redirect->setRedirect('node'); $redirect->save(); $found = $repository->findBySourcePath('case-sensitive-path'); if (!empty($found)) { $found = reset($found); - $this->assertEqual($found->getSourceUrl(), '/Case-Sensitive-Path'); + $this->assertEquals($found->getSourceUrl(), '/Case-Sensitive-Path'); } else { $this->fail('findBySourcePath is case sensitive'); } $found = $repository->findMatchingRedirect('case-sensitive-path'); if (!empty($found)) { - $this->assertEqual($found->getSourceUrl(), '/Case-Sensitive-Path'); + $this->assertEquals($found->getSourceUrl(), '/Case-Sensitive-Path'); } else { $this->fail('findMatchingRedirect is case sensitive.'); @@ -165,7 +166,7 @@ public function testRedirectEntity() { * Test slash is removed from source path in findMatchingRedirect. */ public function testDuplicateRedirectEntry() { - $redirect = $this->controller->create(); + $redirect = $this->storage->create(); $redirect->setSource('/foo/foo', []); $redirect->setRedirect('foo'); $redirect->save(); @@ -182,13 +183,13 @@ public function testDuplicateRedirectEntry() { * Test redirect_sort_recursive(). */ public function testSortRecursive() { - $test_cases = array( - array( - 'input' => array('b' => 'aa', 'c' => array('c2' => 'aa', 'c1' => 'aa'), 'a' => 'aa'), - 'expected' => array('a' => 'aa', 'b' => 'aa', 'c' => array('c1' => 'aa', 'c2' => 'aa')), + $test_cases = [ + [ + 'input' => ['b' => 'aa', 'c' => ['c2' => 'aa', 'c1' => 'aa'], 'a' => 'aa'], + 'expected' => ['a' => 'aa', 'b' => 'aa', 'c' => ['c1' => 'aa', 'c2' => 'aa']], 'callback' => 'ksort', - ), - ); + ], + ]; foreach ($test_cases as $index => $test_case) { $output = $test_case['input']; redirect_sort_recursive($output, $test_case['callback']); @@ -202,17 +203,17 @@ public function testSortRecursive() { public function testLoopDetection() { // Add a chained redirect that isn't a loop. /** @var \Drupal\redirect\Entity\Redirect $one */ - $one = $this->controller->create(); + $one = $this->storage->create(); $one->setSource('my-path'); $one->setRedirect('node'); $one->save(); /** @var \Drupal\redirect\Entity\Redirect $two */ - $two = $this->controller->create(); + $two = $this->storage->create(); $two->setSource('second-path'); $two->setRedirect('my-path'); $two->save(); /** @var \Drupal\redirect\Entity\Redirect $three */ - $three = $this->controller->create(); + $three = $this->storage->create(); $three->setSource('third-path'); $three->setRedirect('second-path'); $three->save(); @@ -221,7 +222,7 @@ public function testLoopDetection() { $repository = \Drupal::service('redirect.repository'); $found = $repository->findMatchingRedirect('third-path'); if (!empty($found)) { - $this->assertEqual($found->getRedirectUrl()->toString(), '/node', 'Chained redirects properly resolved in findMatchingRedirect.'); + $this->assertEquals($found->getRedirectUrl()->toString(), '/node', 'Chained redirects properly resolved in findMatchingRedirect.'); } else { $this->fail('Failed to resolve a chained redirect.'); @@ -239,6 +240,32 @@ public function testLoopDetection() { } } + /** + * Test loop detection reset. + */ + public function testLoopDetectionReset() { + // Add a chained redirect that isn't a loop. + /** @var \Drupal\redirect\Entity\Redirect $source */ + $source = $this->storage->create(); + $source->setSource('source-redirect'); + $source->setRedirect('target'); + $source->save(); + + /** @var \Drupal\redirect\Entity\Redirect $target */ + $target = $this->storage->create(); + $target->setSource('target'); + $target->setRedirect('second-target'); + $target->save(); + + /** @var \Drupal\redirect\RedirectRepository $repository */ + $repository = \Drupal::service('redirect.repository'); + $found = $repository->findMatchingRedirect('target'); + $this->assertEquals($target->id(), $found->id()); + + $found = $repository->findMatchingRedirect('source-redirect'); + $this->assertEquals($target->id(), $found->id()); + } + /** * Test redirect_parse_url(). */ @@ -261,7 +288,7 @@ public function testParseURL() { public function testMultilanguageCases() { // Add a redirect for english. /** @var \Drupal\redirect\Entity\Redirect $en_redirect */ - $en_redirect = $this->controller->create(); + $en_redirect = $this->storage->create(); $en_redirect->setSource('langpath'); $en_redirect->setRedirect('/about'); $en_redirect->setLanguage('en'); @@ -269,7 +296,7 @@ public function testMultilanguageCases() { // Add a redirect for germany. /** @var \Drupal\redirect\Entity\Redirect $en_redirect */ - $en_redirect = $this->controller->create(); + $en_redirect = $this->storage->create(); $en_redirect->setSource('langpath'); $en_redirect->setRedirect('node'); $en_redirect->setLanguage('de'); @@ -281,8 +308,8 @@ public function testMultilanguageCases() { $found = $repository->findBySourcePath('langpath'); if (!empty($found)) { - $this->assertEqual($found[1]->getRedirectUrl()->toString(), '/about', 'Multilingual redirect resolved properly.'); - $this->assertEqual($found[1]->get('language')[0]->value, 'en', 'Multilingual redirect resolved properly.'); + $this->assertEquals($found[1]->getRedirectUrl()->toString(), '/about', 'Multilingual redirect resolved properly.'); + $this->assertEquals($found[1]->get('language')[0]->value, 'en', 'Multilingual redirect resolved properly.'); } else { $this->fail('Failed to resolve the multilingual redirect.'); @@ -294,8 +321,8 @@ public function testMultilanguageCases() { $repository = \Drupal::service('redirect.repository'); $found = $repository->findBySourcePath('langpath'); if (!empty($found)) { - $this->assertEqual($found[2]->getRedirectUrl()->toString(), '/node', 'Multilingual redirect resolved properly.'); - $this->assertEqual($found[2]->get('language')[0]->value, 'de', 'Multilingual redirect resolved properly.'); + $this->assertEquals($found[2]->getRedirectUrl()->toString(), '/node', 'Multilingual redirect resolved properly.'); + $this->assertEquals($found[2]->get('language')[0]->value, 'de', 'Multilingual redirect resolved properly.'); } else { $this->fail('Failed to resolve the multilingual redirect.'); diff --git a/web/modules/redirect/tests/src/Unit/Migrate/d7/PathRedirectTest.php b/web/modules/redirect/tests/src/Unit/Migrate/d7/PathRedirectTest.php deleted file mode 100644 index 8a5c593e6b..0000000000 --- a/web/modules/redirect/tests/src/Unit/Migrate/d7/PathRedirectTest.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -namespace Drupal\Tests\redirect\Unit\Migrate\d7; - -use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase; - -/** - * Tests D7 redirect source plugin. - * - * @group redirect - */ -class PathRedirectTest extends MigrateSqlSourceTestCase { - - const PLUGIN_CLASS = 'Drupal\redirect\Plugin\migrate\source\d7\PathRedirect'; - - protected $migrationConfiguration = [ - 'id' => 'test', - 'source' => [ - 'plugin' => 'd7_path_redirect', - ], - ]; - - protected $expectedResults = [ - [ - 'rid' => 5, - 'hash' => 'MwmDbnA65ag646gtEdLqmAqTbF0qQerse63RkQmJK_Y', - 'type' => 'redirect', - 'uid' => 5, - 'source' => 'test/source/url', - 'source_options' => '', - 'redirect' => 'test/redirect/url', - 'redirect_options' => '', - 'language' => 'und', - 'status_code' => 301, - 'count' => 2518, - 'access' => 1449497138, - ], - ]; - - /** - * {@inheritdoc} - */ - protected function setUp() { - $this->databaseContents['variable'] = [ - [ - 'name' => 'redirect_default_status_code', - 'value' => 's:3:"307";', - ] - ]; - $this->databaseContents['redirect'] = $this->expectedResults; - parent::setUp(); - } - -} diff --git a/web/modules/redirect/tests/src/Unit/RedirectCheckerTest.php b/web/modules/redirect/tests/src/Unit/RedirectCheckerTest.php index 35f8233232..73aba86383 100644 --- a/web/modules/redirect/tests/src/Unit/RedirectCheckerTest.php +++ b/web/modules/redirect/tests/src/Unit/RedirectCheckerTest.php @@ -21,7 +21,7 @@ class RedirectCheckerTest extends UnitTestCase { */ public function testCanRedirect() { - $config = array('redirect.settings' => array('ignore_admin_path' => FALSE, 'access_check' => TRUE)); + $config = ['redirect.settings' => ['ignore_admin_path' => FALSE, 'access_check' => TRUE]]; $state = $this->getMockBuilder('Drupal\Core\State\StateInterface') ->getMock(); diff --git a/web/modules/redirect/tests/src/Unit/RedirectRequestSubscriberTest.php b/web/modules/redirect/tests/src/Unit/RedirectRequestSubscriberTest.php index e664e95bab..3f384698cb 100644 --- a/web/modules/redirect/tests/src/Unit/RedirectRequestSubscriberTest.php +++ b/web/modules/redirect/tests/src/Unit/RedirectRequestSubscriberTest.php @@ -2,7 +2,10 @@ namespace Drupal\Tests\redirect\Unit; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\Language; +use Drupal\path_alias\AliasManagerInterface; use Drupal\redirect\EventSubscriber\RedirectRequestSubscriber; use Drupal\Tests\UnitTestCase; use PHPUnit_Framework_MockObject_MockObject; @@ -11,13 +14,14 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; /** * Tests the redirect logic. * * @group redirect * - * @coversDefaultClass Drupal\redirect\EventSubscriber\RedirectRequestSubscriber + * @coversDefaultClass \Drupal\redirect\EventSubscriber\RedirectRequestSubscriber */ class RedirectRequestSubscriberTest extends UnitTestCase { @@ -136,7 +140,7 @@ protected function callOnKernelRequestCheckRedirect($redirect, $request_uri, $re ->method('canRedirect') ->will($this->returnValue(TRUE)); - $context = $this->getMock('Symfony\Component\Routing\RequestContext'); + $context = $this->createMock('Symfony\Component\Routing\RequestContext'); $inbound_path_processor = $this->getMockBuilder('Drupal\Core\PathProcessor\InboundPathProcessorInterface') ->disableOriginalConstructor() @@ -156,21 +160,17 @@ protected function callOnKernelRequestCheckRedirect($redirect, $request_uri, $re return $path; }); - $alias_manager = $this->getMockBuilder('Drupal\Core\Path\AliasManager') - ->disableOriginalConstructor() - ->getMock(); - $module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandlerInterface') - ->getMock(); - $entity_manager = $this->getMockBuilder('Drupal\Core\Entity\EntityManagerInterface') - ->getMock(); + $alias_manager = $this->createMock(AliasManagerInterface::class); + $module_handler = $this->createMock(ModuleHandlerInterface::class); + $entity_type_manager = $this->createMock(EntityTypeManagerInterface::class); $subscriber = new RedirectRequestSubscriber( $this->getRedirectRepositoryStub('findMatchingRedirect', $redirect), $this->getLanguageManagerStub(), - $this->getConfigFactoryStub(array('redirect.settings' => array('passthrough_querystring' => $retain_query))), + $this->getConfigFactoryStub(['redirect.settings' => ['passthrough_querystring' => $retain_query]]), $alias_manager, $module_handler, - $entity_manager, + $entity_type_manager, $checker, $context, $inbound_path_processor @@ -255,7 +255,7 @@ protected function getRedirectStub($url, $status_code = 301) { * @return \Symfony\Component\HttpKernel\Event\PostResponseEvent * The post response event object. */ - protected function getPostResponseEvent($headers = array()) { + protected function getPostResponseEvent($headers = []) { $http_kernel = $this->getMockBuilder('\Symfony\Component\HttpKernel\HttpKernelInterface') ->getMock(); $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request') @@ -280,7 +280,7 @@ protected function getGetResponseEventStub($path_info, $query_string) { $http_kernel = $this->getMockBuilder('\Symfony\Component\HttpKernel\HttpKernelInterface') ->getMock(); - return new GetResponseEvent($http_kernel, $request, 'test'); + return new GetResponseEvent($http_kernel, $request, HttpKernelInterface::MASTER_REQUEST); } /** @@ -293,7 +293,7 @@ protected function getLanguageManagerStub() { ->getMock(); $language_manager->expects($this->any()) ->method('getCurrentLanguage') - ->will($this->returnValue(new Language(array('id' => 'en')))); + ->will($this->returnValue(new Language(['id' => 'en']))); return $language_manager; } diff --git a/web/modules/redirect/tests/src/Unit/RouteNormalizerRequestSubscriberTest.php b/web/modules/redirect/tests/src/Unit/RouteNormalizerRequestSubscriberTest.php index 87d3c31358..69a6682cf6 100644 --- a/web/modules/redirect/tests/src/Unit/RouteNormalizerRequestSubscriberTest.php +++ b/web/modules/redirect/tests/src/Unit/RouteNormalizerRequestSubscriberTest.php @@ -24,7 +24,7 @@ class RouteNormalizerRequestSubscriberTest extends UnitTestCase { protected function setUp() { parent::setUp(); - $kill_switch = $this->getMock('\Drupal\Core\PageCache\ResponsePolicy\KillSwitch'); + $kill_switch = $this->createMock('\Drupal\Core\PageCache\ResponsePolicy\KillSwitch'); $kill_switch->expects($this->any()) ->method('trigger') ->withAnyParameters() @@ -136,7 +136,7 @@ protected function getSubscriber($request_uri, $enabled = TRUE, $call_expected = * @param bool $call_expected * If true, we expect generateFromRoute() to be called once. * - * @return \Drupal\Core\Routing\UrlGeneratorInterface|PHPUnit_Framework_MockObject_MockObject + * @return \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit\Framework\MockObject\MockObject */ protected function getUrlGeneratorStub($request_uri, $call_expected = TRUE) { $url_generator = $this->getMockBuilder('\Drupal\Core\Routing\UrlGeneratorInterface') @@ -159,7 +159,7 @@ protected function getUrlGeneratorStub($request_uri, $call_expected = TRUE) { * @param bool $call_expected * If true, we expect isFrontPage() to be called once. * - * @return \Drupal\Core\Path\PathMatcherInterface|PHPUnit_Framework_MockObject_MockObject + * @return \Drupal\Core\Path\PathMatcherInterface|\PHPUnit\Framework\MockObject\MockObject */ protected function getPathMatcherStub($call_expected = TRUE) { $path_matcher = $this->getMockBuilder('\Drupal\Core\Path\PathMatcherInterface') @@ -180,7 +180,7 @@ protected function getPathMatcherStub($call_expected = TRUE) { * @param bool $call_expected * If true, we expect canRedirect() to be called once. * - * @return \Drupal\redirect\RedirectChecker|PHPUnit_Framework_MockObject_MockObject + * @return \Drupal\redirect\RedirectChecker|\PHPUnit\Framework\MockObject\MockObject */ protected function getRedirectCheckerStub($call_expected = TRUE) { $redirect_checker = $this->getMockBuilder('\Drupal\redirect\RedirectChecker') -- GitLab