diff --git a/composer.json b/composer.json index dc412307bfe8d0c390c4dbf6a15317e2fe1aae34..38f012d652dc735056c335bda0198e6a10e62db1 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", diff --git a/composer.lock b/composer.lock index da901bfdf95e1dc4598036af547bc84dfca9a4e4..eda49faf36df0f9564a869ef95775b209b9010bc 100644 --- a/composer.lock +++ b/composer.lock @@ -6785,29 +6785,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" @@ -6816,7 +6813,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 13babe0caff786d3eb9e8e3ed2de4bf792b64e65..cde3c58e0054f230a35c848faa68279d24486379 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -6992,30 +6992,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" @@ -7025,7 +7022,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 b4f147895e65dd233ae60cd398fd322d2320a2bd..bae3099f88e23aa4803dd217662f344dd0692542 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 6a1c65f55aec035bc014ab1e9a823ead21609a8e..7579931ef8ab0170816d6fdc726eac2930be88c8 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 1f3ddef96980c4e0f50b240ed4be17ab018d3494..8de3bef31f106d1d5c0d8d235db31df97a5f1f26 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 3ee9a287dc49acbd042fc1206bd1ed071782be59..647d2f1305e3682a5ae42a969965c174e3a375d4 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 75777dee4b65203231239af7fe5c9dc80f238f15..2a07e97cfaafd5f9de13fa1e688e9f4342b95ade 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 1fbcea4167bd98cf624cfb8693f3e94be29d9181..fa92a99dc4a12075a821fc1bb3cda090ab72d07e 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 09190e7cf7d22a40356f3711bae13f04163f0308..88869bcf20cb370e2d2299d8eb448ff004de6bee 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 b90d166e1cf84b47117a023438c267c19834c510..cdcc5ffe73d253605adff65e2f5d86ebc662d4be 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 bc18f93e1e53dd9d813818b6d677540ea80ab9ea..dcc8320c55f651afc5065b469236dd473ff04fb1 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 a00331188cd45b061bcb7a1de0af9f3d4b4982d4..4684e08af7f30615c9ad9592f0da9661945e9178 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 27bc56de54325241db67a477c0bfba040e7bc980..a310df1cdee2866b9e1cbf8b2b4bcee0973e555b 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 98003ebaeda3ba8ffc668c6463db652889ed4ee2..b1dd268db6fb4c8a6abf1a0ff2219a2437dffb81 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 2842aeb14c1572f0c5a5b1df418852d92024adcb..0000000000000000000000000000000000000000 --- 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 89a14d2dc525e5128d72aefb5f6fb036847852e2..eb966b91b7f543d4a6c15c7c5110abacee5dcec4 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 088cbba47a7a982e7282f2fbc8563ac8ee1e214e..2045bf885796f65fafe4031aec8a570beef52269 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 ef021b553e24eb7d72b0a64e7c321931fce4edb1..d133b77b5d4709b4f73c4f8c8e9596d1cb8fcf77 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 0000000000000000000000000000000000000000..f83a7c63c2e7da5d4c231e7d54b5be6ec8f8ff2d --- /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 6ed6d6774b1cea9f9716bbd7099961a3a5ed3d09..c25818e4bc165ad83c1eea736ec6ba7533f566a0 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 aeb66d45354fadfae540f2f970ef8f84a75f59d3..eda49f417f2b5540fccd016e9463ec96753ddcba 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 f60b329bf111ccd96e475ce5713f124549501234..a8699ed9477fc309a592f0915526ae9d999f634d 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 bc949b81fd11ef308d9c167c750d6c704235faaf..b2ebbf303d1c9c0aa3bdf141ee87e06b9f7ed7b4 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 1bf5b9a870ec5e9b7ffef195bcf9b0905eee1cb3..46f1a6903e6c4869d235c957107e028280b405e2 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 d50791f377f2419279a164b6643886be1ad3a1a1..f3c59c3ac80f47582a3a2c4f6bbf1783342e67a4 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 89453ab9d8a5588daf49b66fead5f1a8f4057860..24cc37c0185c46aed151eeb450e17b34eed1e1cc 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 ffe98ce05c47213f6b5c945d85e9f9b7358760d9..e9bc05892f3af99eecbf75eb8109cd2798823776 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 6120ae19b36e4fb0c34736a30927deb99e60ef9a..50bf69b074424d0d3d13d046ae04a94d1ca50db3 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 a1e6eb15d65bbe755a1f6ce15d8c1215c30cc773..687cbe66792686e76434a253973d46639a90706d 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 31a8ee693f77cdd4de61758548a803047b881c9a..536d678eb94fba40f8242a8aebb0fa67f251cf06 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 bf4d4f9022f3b8b35d8202d25ed5d617d7677ebd..5d5bae2da5c30c4c830c91f3f7439af6452d0476 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 743bd9392565e231a893b34112343c82d380125b..578acb00b391b0c14372496d1deb59722d4e0ad9 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 cb5d4c56dd54f1bd12c3af2d4a5260133d590806..c1796c1fc778092ef2e0bd418810f28425866963 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 87095defff75593152752ce047b74f417ed74703..f3e35f8cdff64c9b6d48ef7bf5bd7bec988cc87a 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 800e022a568b2b3be2bfa0d0fe5ab45ceddfd5fa..274ba1e12a8781574ebe0e8f9376c215dc51798e 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 f2793f3e862d35c5aa4aeb8fbb6c64f98153c3be..2482862df99c5aa929b3a81c4eec979aaf404bee 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 eeee993d9c4c321eb094a75839a0b139aa06882d..7d0d9ec79a5ddeea03b1fd27fd8f14fa59895957 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 79c79467cc922e253322b51bb822214f938ed582..fe4c43e3a8af5ba7b93dee06651e99be1f7054ca 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 702e99158023ef648b2ea1dee64e600a0738dce7..1b4f4534f29ddb1503c496cd45fbd5573b94c196 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 029973dcc15c7420a39a4770bb345889056d1861..1cfe0caa8c945f738090c7756495654c1b85dd6f 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 8b7f579727f266b3faaf66345a63175bc321c7c1..305a465a6bb10d59cf81a4aad6e139c602a6bb07 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 7ef400de268c96147be4f8b0af4f6f799b9c74f3..3c36366187f6c725fe71437af28e41d104f838a3 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 53a0efc66a5f7bfd400acb961be481b1b1c8acf3..4c7e94eb505104804a3330f0e8c246f27835eb9a 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 0a43411167b0115575c79819d7153b0e7f0b372b..a07f2181c7f532312aeb715114799ff360ec0571 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 5d354a07d232a9efa33ecc1a5515ca1d83e0ef99..96a4595ae094bdac6b80db65ae58e8687d5351b2 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 3ffd4b64edd513b324899edf2952d745dde8ef92..25fcf3fadc98289e289ea1fdfc868b2d896b7175 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 236c927f6349c5724c3b2f5d63e559fbfd872ceb..ff1ccf8bd99cf6178dadb064d29ac9bf054e0d67 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 67b066614c9ee8127e33aa1f98f0ca30c14597bc..0000000000000000000000000000000000000000 --- 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 e6f9c47588326fdec6bdecc1cd63fcc7a42c4dd5..ca04d67d18b47b14744a342e7518ea3f2e85467f 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 20510761775f43ca7538ee0e2d9890cfbb4423c4..3652ee835d98bd510dc389786d4204eb7e934c29 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 ae9368886da3ef7c78775e1a1d38295a594228ba..6fa229622f491013246c773fc69d56d98e7b5821 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 75e1f99871aaabe83eb8458340acb17578b80a0d..6f806d67196d49ca63f0cb42981a37f992f5987c 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 0000000000000000000000000000000000000000..1bb379bc548b42fa89fefc4ae0a7eb8cc56f77d2 --- /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 6d9a72791893370b16188adb0b4c0a5e6daf14ce..372576cab609f8a878bc578a86e5c2ffe8c5ba34 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 0000000000000000000000000000000000000000..51fb2abde8effe1e261a3bc54a7405ed30db9a1c --- /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 0000000000000000000000000000000000000000..ae8895e54e18342a6a362336263f117319cc33fa --- /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 9383adcd1ad2d343b0e4bfd25d310641e8cc34da..c3eebbc788cea85a77749006be8552ef7e2bc8e4 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 0000000000000000000000000000000000000000..a5f37a8381f829873fddf8ea35d6e2e5309a8889 --- /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 469e5ccd5c65efc22980ecea1700466271e8b62f..15134cdcc1f44b098528c51418ae5ff618fe2758 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 6d982f7c1f5da9bdb49e76de1805a03a26dbc102..a379b12e2513bb8e0c24cb73906d3fbb38d85d6a 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 8a5c593e6b44073a5c70642699d470c90c902665..0000000000000000000000000000000000000000 --- 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 35f82332323dd7266858cf45e8eaf58cad0f3f63..73aba863838d9383f1678cfb0ce26f14a2b4278b 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 e664e95bab49756366a8dd9c1b893a3f5c9c534f..3f384698cb64afec13e9c02aa7539951735b19d4 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 87d3c313584e30bfa1b068ad405f53f61666a3fd..69a6682cf6e9f21f7dde7f482f97cb6a9f3d416d 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')