From 18ddb0044c8b935a7a2037de0189cc85435c3d2b Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Thu, 7 Aug 2014 21:43:27 +0100
Subject: [PATCH] Issue #2315807 by tim.plunkett: Remove support for path-based
 form redirects.

---
 core/includes/batch.inc                       | 16 ++++---
 core/includes/form.inc                        |  2 +-
 core/lib/Drupal/Core/Annotation/Action.php    |  5 +--
 core/lib/Drupal/Core/Form/FormState.php       | 34 ++++----------
 .../Drupal/Core/Form/FormStateInterface.php   |  4 --
 core/lib/Drupal/Core/Form/FormSubmitter.php   | 39 +++-------------
 .../Core/Form/FormSubmitterInterface.php      | 39 ++++------------
 .../src/ContentTranslationHandler.php         | 16 ++++---
 core/modules/field_ui/src/FieldOverview.php   |  8 +---
 core/modules/field_ui/src/FieldUI.php         |  6 ++-
 .../src/Form/FieldInstanceEditForm.php        |  7 +--
 .../src/Form/FieldStorageEditForm.php         |  7 +--
 core/modules/file/file.module                 |  2 +-
 .../node/src/Plugin/Action/DeleteNode.php     |  2 +-
 core/modules/path/src/Form/DeleteForm.php     |  2 +-
 .../src/Plugin/views/field/BulkForm.php       |  4 +-
 core/modules/system/system.module             | 20 ++++++---
 .../ajax_test/src/Form/AjaxTestDialogForm.php |  2 +-
 .../src/Form/FormTestCheckboxesZeroForm.php   |  2 +-
 .../src/Form/FormTestRedirectForm.php         |  7 ++-
 .../taxonomy/src/Form/OverviewTerms.php       |  1 -
 core/modules/taxonomy/src/TermForm.php        |  4 --
 .../update/src/Form/UpdateManagerInstall.php  |  2 +-
 core/modules/update/src/Form/UpdateReady.php  |  2 +-
 .../user/src/Plugin/Action/CancelUser.php     |  2 +-
 core/modules/user/src/RegisterForm.php        |  6 ---
 .../exposed_form/ExposedFormPluginBase.php    |  1 -
 core/modules/views_ui/src/ViewEditForm.php    | 37 +++++++++------
 .../Tests/Core/Form/FormBuilderTest.php       |  2 +-
 .../Drupal/Tests/Core/Form/FormStateTest.php  |  8 +---
 .../Tests/Core/Form/FormSubmitterTest.php     | 45 +++++--------------
 31 files changed, 118 insertions(+), 216 deletions(-)

diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index 11362208c91a..e1565a556611 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -16,9 +16,11 @@
 
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Timer;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Batch\Percentage;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Page\DefaultHtmlPageRenderer;
+use Drupal\Core\Url;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -446,13 +448,15 @@ function _batch_finished() {
     if (!isset($_batch['form_state'])) {
       $_batch['form_state'] = new FormState();
     }
-    if (!isset($_batch['form_state']['redirect'])) {
-      if (isset($_batch['redirect'])) {
-        $_batch['form_state']['redirect'] = $_batch['redirect'];
-      }
-      else {
-        $_batch['form_state']['redirect'] = $_batch['source_url'];
+    if ($_batch['form_state']->getRedirect() === NULL) {
+      $redirect = $_batch['batch_redirect'] ?: $_batch['source_url'];
+      $options = UrlHelper::parse($redirect);
+      if (!UrlHelper::isExternal($options['path'])) {
+        $options['path'] = $GLOBALS['base_url'] . '/' . $options['path'];
       }
+      $redirect = Url::createFromPath($options['path']);
+      $redirect->setOptions($options);
+      $_batch['form_state']->setRedirectUrl($redirect);
     }
 
     // Use \Drupal\Core\Form\FormSubmitterInterface::redirectForm() to handle
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 5a24e985a7a5..1b4a086f584b 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -3066,7 +3066,7 @@ function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = NU
       'url' => $url,
       'url_options' => array(),
       'source_url' => current_path(),
-      'redirect' => $redirect,
+      'batch_redirect' => $redirect,
       'theme' => $GLOBALS['theme_key'],
       'redirect_callback' => $redirect_callback,
     );
diff --git a/core/lib/Drupal/Core/Annotation/Action.php b/core/lib/Drupal/Core/Annotation/Action.php
index 0e4fd737e39e..5d0937dd4097 100644
--- a/core/lib/Drupal/Core/Annotation/Action.php
+++ b/core/lib/Drupal/Core/Annotation/Action.php
@@ -42,14 +42,13 @@ class Action extends Plugin {
   public $label;
 
   /**
-   * The path for a confirmation form for this action.
+   * The route name for a confirmation form for this action.
    *
-   * @todo Change this to accept a route.
    * @todo Provide a more generic way to allow an action to be confirmed first.
    *
    * @var string (optional)
    */
-  public $confirm_form_path = '';
+  public $confirm_form_route_name = '';
 
   /**
    * The entity type the action can apply to.
diff --git a/core/lib/Drupal/Core/Form/FormState.php b/core/lib/Drupal/Core/Form/FormState.php
index 5733d5a4bad9..5e2be94ef80b 100644
--- a/core/lib/Drupal/Core/Form/FormState.php
+++ b/core/lib/Drupal/Core/Form/FormState.php
@@ -111,33 +111,25 @@ class FormState implements FormStateInterface, \ArrayAccess {
    * Used when a form needs to return some kind of a
    * \Symfony\Component\HttpFoundation\Response object, e.g., a
    * \Symfony\Component\HttpFoundation\BinaryFileResponse when triggering a
-   * file download. If you use the $form_state['redirect'] key, it will be used
-   * to build a \Symfony\Component\HttpFoundation\RedirectResponse and will
-   * populate this key.
+   * file download. If you use self::setRedirect() or self::setRedirectUrl(),
+   * it will be used to build a
+   * \Symfony\Component\HttpFoundation\RedirectResponse and will populate this
+   * key.
    *
    * @var \Symfony\Component\HttpFoundation\Response|null
    */
   protected $response;
 
   /**
-   * Used to redirect the form on submission. It may either be a  string
-   * containing the destination URL, or an array of arguments compatible with
-   * url(). See url() for complete information.
+   * Used to redirect the form on submission.
    *
-   * This property is uncacheable.
-   *
-   * @var string|array|null
-   */
-  protected $redirect;
-
-  /**
-   * Used for route-based redirects.
+   * @see self::getRedirect()
    *
    * This property is uncacheable.
    *
-   * @var \Drupal\Core\Url|array
+   * @var \Drupal\Core\Url|\Symfony\Component\HttpFoundation\RedirectResponse|null
    */
-  protected $redirect_route;
+  protected $redirect;
 
   /**
    * If set to TRUE the form will NOT perform a redirect, even if
@@ -611,7 +603,7 @@ public function setRedirect($route_name, array $route_parameters = array(), arra
    * {@inheritdoc}
    */
   public function setRedirectUrl(Url $url) {
-    $this->set('redirect_route', $url);
+    $this->set('redirect', $url);
     return $this;
   }
 
@@ -633,14 +625,6 @@ public function getRedirect() {
       return FALSE;
     }
 
-    // Check for a route-based redirection.
-    if ($redirect_route = $this->get('redirect_route')) {
-      $redirect_route->setAbsolute();
-      return $redirect_route;
-    }
-
-    // @todo Remove once all redirects are converted away from paths in
-    //   https://www.drupal.org/node/2315807.
     return $this->get('redirect');
   }
 
diff --git a/core/lib/Drupal/Core/Form/FormStateInterface.php b/core/lib/Drupal/Core/Form/FormStateInterface.php
index c7868cbefecf..a24d28dc0df2 100644
--- a/core/lib/Drupal/Core/Form/FormStateInterface.php
+++ b/core/lib/Drupal/Core/Form/FormStateInterface.php
@@ -127,10 +127,6 @@ public function setRedirectUrl(Url $url);
    *   The value will be one of the following:
    *   - A fully prepared \Symfony\Component\HttpFoundation\RedirectResponse.
    *   - An instance of \Drupal\Core\Url to use for the redirect.
-   *   - A numerically-indexed array where the first value is the path to use
-   *     for the redirect, and the optional second value is an array of options
-   *     for generating the URL from the path.
-   *   - The path to use for the redirect.
    *   - NULL, to signify that no redirect was specified and that the current
    *     path should be used for the redirect.
    *   - FALSE, to signify that no redirect should take place.
diff --git a/core/lib/Drupal/Core/Form/FormSubmitter.php b/core/lib/Drupal/Core/Form/FormSubmitter.php
index fb447a2974f1..e99727e60404 100644
--- a/core/lib/Drupal/Core/Form/FormSubmitter.php
+++ b/core/lib/Drupal/Core/Form/FormSubmitter.php
@@ -127,10 +127,6 @@ public function executeSubmitHandlers(&$form, FormStateInterface &$form_state) {
    * {@inheritdoc}
    */
   public function redirectForm(FormStateInterface $form_state) {
-    // According to RFC 7231, 303 See Other status code must be used to redirect
-    // user agent (and not default 302 Found).
-    // @see http://tools.ietf.org/html/rfc7231#section-6.4.4
-    $status_code = Response::HTTP_SEE_OTHER;
     $redirect = $form_state->getRedirect();
 
     // Allow using redirect responses directly if needed.
@@ -141,35 +137,7 @@ public function redirectForm(FormStateInterface $form_state) {
     $url = NULL;
     // Check for a route-based redirection.
     if ($redirect instanceof Url) {
-      $url = $redirect->toString();
-    }
-    // An array contains the path to use for the redirect, as well as options to
-    // use for generating the URL.
-    elseif (is_array($redirect)) {
-      if (isset($redirect[1])) {
-        $options = $redirect[1];
-      }
-      else {
-        $options = array();
-      }
-      // Redirections should always use absolute URLs.
-      $options['absolute'] = TRUE;
-      if (isset($redirect[2])) {
-        $status_code = $redirect[2];
-      }
-      $url = $this->urlGenerator->generateFromPath($redirect[0], $options);
-    }
-    // A string represents the path to use for the redirect.
-    elseif (is_string($redirect)) {
-      // This function can be called from the installer, which guarantees
-      // that $redirect will always be a string, so catch that case here
-      // and use the appropriate redirect function.
-      if ($this->drupalInstallationAttempted()) {
-        install_goto($redirect);
-      }
-      else {
-        $url = $this->urlGenerator->generateFromPath($redirect, array('absolute' => TRUE));
-      }
+      $url = $redirect->setAbsolute()->toString();
     }
     // If no redirect was specified, redirect to the current path.
     elseif ($redirect === NULL) {
@@ -183,7 +151,10 @@ public function redirectForm(FormStateInterface $form_state) {
     }
 
     if ($url) {
-      return new RedirectResponse($url, $status_code);
+      // According to RFC 7231, 303 See Other status code must be used to redirect
+      // user agent (and not default 302 Found).
+      // @see http://tools.ietf.org/html/rfc7231#section-6.4.4
+      return new RedirectResponse($url, Response::HTTP_SEE_OTHER);
     }
   }
 
diff --git a/core/lib/Drupal/Core/Form/FormSubmitterInterface.php b/core/lib/Drupal/Core/Form/FormSubmitterInterface.php
index 8a79fe0791d2..15ce4d212462 100644
--- a/core/lib/Drupal/Core/Form/FormSubmitterInterface.php
+++ b/core/lib/Drupal/Core/Form/FormSubmitterInterface.php
@@ -49,21 +49,18 @@ public function executeSubmitHandlers(&$form, FormStateInterface &$form_state);
    * destination should be, based on the $form_state and the 'destination'
    * query string in the request URL, and redirects the user there.
    *
-   * Usually (for exceptions, see below) $form_state['redirect'] determines
-   * where to redirect the user. This can be set either to a string (the path to
-   * redirect to), or an array of arguments for url(). If
-   * $form_state['redirect'] is missing, the user is usually (again, see below
-   * for exceptions) redirected back to the page they came from, where they
-   * should see a fresh, unpopulated copy of the form.
+   * The result of \Drupal\Core\Form|FormStateInterface::getRedirect()
+   * determines where to redirect the user. See the possible return values
+   * listed there. If the result is FALSE, then the user will not be redirected.
    *
-   * Here is an example of how to set up a form to redirect to the path 'node':
+   * Here is an example of how to set up a form to redirect to the path 'user':
    * @code
-   * $form_state->set('redirect', 'node');
+   * $form_state->setRedirect('user.page');
    * @endcode
    * And here is an example of how to redirect to 'node/123?foo=bar#baz':
    * @code
-   * $form_state->set('redirect', array(
-   *   'node/123',
+   * $form_state->setRedirect('node.view',
+   *   array('node' => 123),
    *   array(
    *     'query' => array(
    *       'foo' => 'bar',
@@ -73,27 +70,7 @@ public function executeSubmitHandlers(&$form, FormStateInterface &$form_state);
    * ));
    * @endcode
    *
-   * There are several exceptions to the "usual" behavior described above:
-   * - If $form_state['programmed'] is TRUE, the form submission was usually
-   *   invoked via self::submitForm(), so any redirection would break the script
-   *   that invoked self::submitForm() and no redirection is done.
-   * - If $form_state['rebuild'] is TRUE, the form is being rebuilt, and no
-   *   redirection is done.
-   * - If $form_state['no_redirect'] is TRUE, redirection is disabled. This is
-   *   set, for instance, by \Drupal\system\FormAjaxController::getForm() to
-   *   prevent redirection in Ajax callbacks. $form_state['no_redirect'] should
-   *   never be set or altered by form builder functions or form validation
-   *   or submit handlers.
-   * - If $form_state['redirect'] is set to FALSE, redirection is disabled.
-   * - If none of the above conditions has prevented redirection, then the
-   *   redirect is accomplished by returning a RedirectResponse, passing in the
-   *   value of $form_state['redirect'] if it is set, or the current path if it
-   *   is not. RedirectResponse preferentially uses the value of
-   *   \Drupal::request->query->get('destination') (the 'destination' URL query
-   *   string) if it is present, so this will override any values set by
-   *   $form_state['redirect'].
-   *
-   * @param $form_state
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
    *   The current state of the form.
    *
    * @return \Symfony\Component\HttpFoundation\RedirectResponse|null
diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php
index d7601e73d64a..b1b7cc079b3e 100644
--- a/core/modules/content_translation/src/ContentTranslationHandler.php
+++ b/core/modules/content_translation/src/ContentTranslationHandler.php
@@ -435,8 +435,12 @@ public function entityFormSourceChange($form, FormStateInterface $form_state) {
     $entity = $form_controller->getEntity();
     $source = $form_state['values']['source_langcode']['source'];
 
-    $path = $entity->getSystemPath('drupal:content-translation-overview');
-    $form_state['redirect'] = $path . '/add/' . $source . '/' . $form_controller->getFormLangcode($form_state);
+    $entity_type_id = $entity->getEntityTypeId();
+    $form_state->setRedirect('content_translation.translation_add_' . $entity_type_id, array(
+      $entity_type_id => $entity->id(),
+      'source' => $source,
+      'target' => $form_controller->getFormLangcode($form_state),
+    ));
     $languages = language_list();
     drupal_set_message(t('Source language set to: %language', array('%language' => $languages[$source]->name)));
   }
@@ -462,9 +466,11 @@ function entityFormDelete($form, FormStateInterface $form_state) {
   function entityFormDeleteTranslation($form, FormStateInterface $form_state) {
     $form_controller = content_translation_form_controller($form_state);
     $entity = $form_controller->getEntity();
-    $path = $entity->getSystemPath('drupal:content-translation-overview');
-    $form_langcode = $form_controller->getFormLangcode($form_state);
-    $form_state['redirect'] = $path . '/delete/' . $form_langcode;
+    $entity_type_id = $entity->getEntityTypeId();
+    $form_state->setRedirect('content_translation.delete_' . $entity_type_id, array(
+      $entity_type_id => $entity->id(),
+      'language' => $form_controller->getFormLangcode($form_state),
+    ));
   }
 
   /**
diff --git a/core/modules/field_ui/src/FieldOverview.php b/core/modules/field_ui/src/FieldOverview.php
index 4cea5072b8c4..04c9805aa84c 100644
--- a/core/modules/field_ui/src/FieldOverview.php
+++ b/core/modules/field_ui/src/FieldOverview.php
@@ -478,13 +478,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     if ($destinations) {
       $destination = drupal_get_destination();
       $destinations[] = $destination['destination'];
-      $next_destination = FieldUI::getNextDestination($destinations, $form_state);
-      if (isset($next_destination['route_name'])) {
-        $form_state->setRedirect($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']);
-      }
-      else {
-        $form_state['redirect'] = $next_destination;
-      }
+      $form_state->setRedirectUrl(FieldUI::getNextDestination($destinations, $form_state));
     }
     elseif (!$error) {
       drupal_set_message($this->t('Your settings have been saved.'));
diff --git a/core/modules/field_ui/src/FieldUI.php b/core/modules/field_ui/src/FieldUI.php
index 77773985e399..bd1801064085 100644
--- a/core/modules/field_ui/src/FieldUI.php
+++ b/core/modules/field_ui/src/FieldUI.php
@@ -41,7 +41,7 @@ public static function getOverviewRouteInfo($entity_type_id, $bundle) {
    * @param array $destinations
    *   An array of destinations to redirect to.
    *
-   * @return array
+   * @return \Drupal\Core\Url
    *   The next destination to redirect to.
    */
   public static function getNextDestination(array $destinations) {
@@ -51,13 +51,15 @@ public static function getNextDestination(array $destinations) {
       $next_destination += array(
         'route_parameters' => array(),
       );
+      $next_destination = new Url($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']);
     }
     else {
       $options = UrlHelper::parse($next_destination);
       if ($destinations) {
         $options['query']['destinations'] = $destinations;
       }
-      $next_destination = array($options['path'], $options);
+      $next_destination = Url::createFromPath($options['path']);
+      $next_destination->setOptions($options);
     }
     return $next_destination;
   }
diff --git a/core/modules/field_ui/src/Form/FieldInstanceEditForm.php b/core/modules/field_ui/src/Form/FieldInstanceEditForm.php
index 3aacd3ce2c74..08bb37bb8bb2 100644
--- a/core/modules/field_ui/src/Form/FieldInstanceEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldInstanceEditForm.php
@@ -195,12 +195,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $request = $this->getRequest();
     if (($destinations = $request->query->get('destinations')) && $next_destination = FieldUI::getNextDestination($destinations)) {
       $request->query->remove('destinations');
-      if (isset($next_destination['route_name'])) {
-        $form_state->setRedirect($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']);
-      }
-      else {
-        $form_state['redirect'] = $next_destination;
-      }
+      $form_state->setRedirectUrl($next_destination);
     }
     else {
       $form_state->setRedirectUrl(FieldUI::getOverviewRouteInfo($this->instance->entity_type, $this->instance->bundle));
diff --git a/core/modules/field_ui/src/Form/FieldStorageEditForm.php b/core/modules/field_ui/src/Form/FieldStorageEditForm.php
index d7d0aa34f910..81306703e95d 100644
--- a/core/modules/field_ui/src/Form/FieldStorageEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldStorageEditForm.php
@@ -198,12 +198,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       $request = $this->getRequest();
       if (($destinations = $request->query->get('destinations')) && $next_destination = FieldUI::getNextDestination($destinations)) {
         $request->query->remove('destinations');
-        if (isset($next_destination['route_name'])) {
-          $form_state->setRedirect($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']);
-        }
-        else {
-          $form_state['redirect'] = $next_destination;
-        }
+        $form_state->setRedirectUrl($next_destination);
       }
       else {
         $form_state->setRedirectUrl(FieldUI::getOverviewRouteInfo($this->instance->entity_type, $this->instance->bundle));
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index a48a7a78d78a..a71340dd21ea 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -1418,7 +1418,7 @@ function file_managed_file_submit($form, FormStateInterface $form_state) {
   // Set the form to rebuild so that $form is correctly updated in response to
   // processing the file removal. Since this function did not change $form_state
   // if the upload button was clicked, a rebuild isn't necessary in that
-  // situation and setting $form_state['redirect'] to FALSE would suffice.
+  // situation and setting $form_state['no_redirect'] to TRUE would suffice.
   // However, we choose to always rebuild, to keep the form processing workflow
   // consistent between the two buttons.
   $form_state['rebuild'] = TRUE;
diff --git a/core/modules/node/src/Plugin/Action/DeleteNode.php b/core/modules/node/src/Plugin/Action/DeleteNode.php
index f2793570e237..e71b58150655 100644
--- a/core/modules/node/src/Plugin/Action/DeleteNode.php
+++ b/core/modules/node/src/Plugin/Action/DeleteNode.php
@@ -19,7 +19,7 @@
  *   id = "node_delete_action",
  *   label = @Translation("Delete selected content"),
  *   type = "node",
- *   confirm_form_path = "admin/content/node/delete"
+ *   confirm_form_route_name = "node.multiple_delete_confirm"
  * )
  */
 class DeleteNode extends ActionBase implements ContainerFactoryPluginInterface {
diff --git a/core/modules/path/src/Form/DeleteForm.php b/core/modules/path/src/Form/DeleteForm.php
index b253490719cb..80251dc38dea 100644
--- a/core/modules/path/src/Form/DeleteForm.php
+++ b/core/modules/path/src/Form/DeleteForm.php
@@ -89,7 +89,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $pid = NU
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->aliasStorage->delete(array('pid' => $this->pathAlias['pid']));
 
-    $form_state['redirect'] = 'admin/config/search/path';
+    $form_state->setRedirect('path.admin_overview');
   }
 
 }
diff --git a/core/modules/system/src/Plugin/views/field/BulkForm.php b/core/modules/system/src/Plugin/views/field/BulkForm.php
index 9cf68363e0d7..c910ba1c5c11 100644
--- a/core/modules/system/src/Plugin/views/field/BulkForm.php
+++ b/core/modules/system/src/Plugin/views/field/BulkForm.php
@@ -263,8 +263,8 @@ public function viewsFormSubmit(&$form, FormStateInterface $form_state) {
       $action->execute($entities);
 
       $operation_definition = $action->getPluginDefinition();
-      if (!empty($operation_definition['confirm_form_path'])) {
-        $form_state['redirect'] = $operation_definition['confirm_form_path'];
+      if (!empty($operation_definition['confirm_form_route_name'])) {
+        $form_state->setRedirect($operation_definition['confirm_form_route_name']);
       }
 
       $count = count(array_filter($form_state['values'][$this->options['id']]));
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 976fcc55fbae..cad83e532653 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -14,6 +14,7 @@
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Menu\MenuTreeParameters;
 use Drupal\block\BlockPluginInterface;
+use Drupal\Core\Url;
 use Drupal\user\UserInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use GuzzleHttp\Exception\RequestException;
@@ -790,7 +791,7 @@ function _system_themes_access($theme) {
  * using the URL from system_authorized_get_url(). Redirecting yourself is
  * necessary when your authorized operation is being triggered by a form
  * submit handler, since calling redirecting in a submit handler is a bad
- * idea, and you should instead set $form_state['redirect'].
+ * idea, and you should instead use $form_state->setRedirect().
  *
  * Once the SESSION is setup for the operation and the user is redirected to
  * authorize.php, they will be prompted for their connection credentials (core
@@ -817,14 +818,14 @@ function _system_themes_access($theme) {
  * not to assume any code exists. Example (system_authorized_run()):
  * @code
  *   system_authorized_init($callback, $file, $arguments, $page_title);
- *   return new RedirectResponse(system_authorized_get_url());
+ *   return new RedirectResponse(system_authorized_get_url()->toString());
  * @endcode
  * Example (update_manager_install_form_submit()):
  * @code
  *  system_authorized_init('update_authorize_run_install',
  *    drupal_get_path('module', 'update') . '/update.authorize.inc',
  *    $arguments, t('Update manager'));
- *  $form_state['redirect'] = system_authorized_get_url();
+ *  $form_state->setRedirectUrl(system_authorized_get_url());
  * @endcode
  *
  * @param $callback
@@ -863,7 +864,7 @@ function system_authorized_init($callback, $file, $arguments = array(), $page_ti
  *
  * @param array $options
  *   Optional array of options to pass to url().
- * @return
+ * @return \Drupal\Core\Url
  *   The full URL to authorize.php, using HTTPS if available.
  *
  * @see system_authorized_init()
@@ -873,7 +874,10 @@ function system_authorized_get_url(array $options = array()) {
   // Force HTTPS if available, regardless of what the caller specifies.
   $options['https'] = TRUE;
   // Prefix with $base_url so url() treats it as an external link.
-  return url($base_url . '/core/authorize.php', $options);
+  $url = Url::createFromPath($base_url . '/core/authorize.php');
+  $url_options = $url->getOptions();
+  $url->setOptions($options + $url_options);
+  return $url;
 }
 
 /**
@@ -881,6 +885,8 @@ function system_authorized_get_url(array $options = array()) {
  *
  * @param array $options
  *   Optional array of options to pass to url().
+ *
+ * @return \Drupal\Core\Url
  */
 function system_authorized_batch_processing_url(array $options = array()) {
   $options['query'] = array('batch' => '1');
@@ -894,7 +900,7 @@ function system_authorized_batch_processing_url(array $options = array()) {
  */
 function system_authorized_run($callback, $file, $arguments = array(), $page_title = NULL) {
   system_authorized_init($callback, $file, $arguments, $page_title);
-  return new RedirectResponse(system_authorized_get_url());
+  return new RedirectResponse(system_authorized_get_url()->toString());
 }
 
 /**
@@ -905,7 +911,7 @@ function system_authorized_run($callback, $file, $arguments = array(), $page_tit
 function system_authorized_batch_process() {
   $finish_url = system_authorized_get_url();
   $process_url = system_authorized_batch_processing_url();
-  return batch_process($finish_url, $process_url);
+  return batch_process($finish_url->toString(), $process_url->toString());
 }
 
 /**
diff --git a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
index 5f1181720146..22dc4ee616c6 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php
@@ -68,7 +68,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $form_state['redirect'] = 'ajax-test/dialog-contents';
+    $form_state->setRedirect('ajax_test.dialog_contents');
   }
 
 
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestCheckboxesZeroForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestCheckboxesZeroForm.php
index 70efd25fba71..557fd647fb2a 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestCheckboxesZeroForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestCheckboxesZeroForm.php
@@ -60,7 +60,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       $form_state->setResponse(new JsonResponse($form_state['values']));
     }
     else {
-      $form_state['redirect'] = FALSE;
+      $form_state['no_redirect'] = TRUE;
     }
   }
 
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
index 0adc985f5d02..1c6437d5d0d8 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 
 /**
  * Form builder to detect form redirect.
@@ -52,10 +53,12 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     if (!empty($form_state['values']['redirection'])) {
-      $form_state['redirect'] = !empty($form_state['values']['destination']) ? $form_state['values']['destination'] : NULL;
+      if (!empty($form_state['values']['destination'])) {
+        $form_state->setRedirectUrl(Url::createFromPath($GLOBALS['base_url'] . '/' . $form_state['values']['destination']));
+      }
     }
     else {
-      $form_state['redirect'] = FALSE;
+      $form_state['no_redirect'] = TRUE;
     }
   }
 
diff --git a/core/modules/taxonomy/src/Form/OverviewTerms.php b/core/modules/taxonomy/src/Form/OverviewTerms.php
index fd6606ea676f..88addad40bba 100644
--- a/core/modules/taxonomy/src/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/src/Form/OverviewTerms.php
@@ -341,7 +341,6 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
         '#submit' => array(array($this, 'submitReset')),
         '#value' => $this->t('Reset to alphabetical'),
       );
-      $form_state['redirect'] = array(current_path(), ($page ? array('query' => array('page' => $page)) : array()));
     }
 
     return $form;
diff --git a/core/modules/taxonomy/src/TermForm.php b/core/modules/taxonomy/src/TermForm.php
index fa2443ee2704..1af2453f84b3 100644
--- a/core/modules/taxonomy/src/TermForm.php
+++ b/core/modules/taxonomy/src/TermForm.php
@@ -98,10 +98,6 @@ public function form(array $form, FormStateInterface $form_state) {
       '#value' => $term->id(),
     );
 
-    if ($term->isNew()) {
-      $form_state['redirect'] = current_path();
-    }
-
     return parent::form($form, $form_state, $term);
   }
 
diff --git a/core/modules/update/src/Form/UpdateManagerInstall.php b/core/modules/update/src/Form/UpdateManagerInstall.php
index 5e56c668f3c6..58a49a0495ba 100644
--- a/core/modules/update/src/Form/UpdateManagerInstall.php
+++ b/core/modules/update/src/Form/UpdateManagerInstall.php
@@ -216,7 +216,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     // whatever FileTransfer object authorize.php creates for us.
     else {
       system_authorized_init('update_authorize_run_install', drupal_get_path('module', 'update') . '/update.authorize.inc', $arguments, $this->t('Update manager'));
-      $form_state['redirect'] = system_authorized_get_url();
+      $form_state->setRedirectUrl(system_authorized_get_url());
     }
   }
 
diff --git a/core/modules/update/src/Form/UpdateReady.php b/core/modules/update/src/Form/UpdateReady.php
index 942164a29460..71bb44176bf6 100644
--- a/core/modules/update/src/Form/UpdateReady.php
+++ b/core/modules/update/src/Form/UpdateReady.php
@@ -141,7 +141,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       // whatever FileTransfer object authorize.php creates for us.
       else {
         system_authorized_init('update_authorize_run_update', drupal_get_path('module', 'update') . '/update.authorize.inc', array($updates), $this->t('Update manager'));
-        $form_state['redirect'] = system_authorized_get_url();
+        $form_state->setRedirectUrl(system_authorized_get_url());
       }
     }
   }
diff --git a/core/modules/user/src/Plugin/Action/CancelUser.php b/core/modules/user/src/Plugin/Action/CancelUser.php
index 4246543b91ad..6c0e392b0d55 100644
--- a/core/modules/user/src/Plugin/Action/CancelUser.php
+++ b/core/modules/user/src/Plugin/Action/CancelUser.php
@@ -19,7 +19,7 @@
  *   id = "user_cancel_user_action",
  *   label = @Translation("Cancel the selected user accounts"),
  *   type = "user",
- *   confirm_form_path = "admin/people/cancel"
+ *   confirm_form_route_name = "user.multiple_cancel_confirm"
  * )
  */
 class CancelUser extends ActionBase implements ContainerFactoryPluginInterface {
diff --git a/core/modules/user/src/RegisterForm.php b/core/modules/user/src/RegisterForm.php
index f5c31765850c..5a6bfb63630b 100644
--- a/core/modules/user/src/RegisterForm.php
+++ b/core/modules/user/src/RegisterForm.php
@@ -60,12 +60,6 @@ public function form(array $form, FormStateInterface $form_state) {
     // Start with the default user account fields.
     $form = parent::form($form, $form_state, $account);
 
-    if ($admin) {
-      // Redirect back to page which initiated the create request; usually
-      // admin/people/create.
-      $form_state['redirect'] = current_path();
-    }
-
     return $form;
   }
 
diff --git a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
index 276a4dd2d0c7..62e80890a8dd 100644
--- a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
+++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
@@ -325,7 +325,6 @@ public function resetForm(&$form, FormStateInterface $form_state) {
       $this->view->exposed_data = array();
     }
 
-    $form_state['redirect'] = current_path();
     $form_state['values'] = array();
   }
 
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index 92f22bed8f8a..6d42b52ba593 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views_ui;
 
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\HtmlCommand;
@@ -16,6 +17,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Url;
 use Drupal\user\TempStoreFactory;
 use Drupal\views\Views;
 use Symfony\Component\HttpFoundation\RequestStack;
@@ -324,7 +326,10 @@ public function submit(array $form, FormStateInterface $form_state) {
           $query->remove('destination');
         }
       }
-      $form_state['redirect'] = $destination;
+      if (!UrlHelper::isExternal($destination)) {
+        $destination = $GLOBALS['base_url'] . '/' . $destination;
+      }
+      $form_state->setRedirectUrl(Url::createFromPath($destination));
     }
 
     $view->save();
@@ -778,22 +783,28 @@ public function renderDisplayTop(ViewUI $view) {
    * should not yet redirect to the destination.
    */
   public function submitDelayDestination($form, FormStateInterface $form_state) {
-    $query = $this->requestStack->getCurrentRequest()->query;
-    // @todo: Revisit this when http://drupal.org/node/1668866 is in.
-    $destination = $query->get('destination');
-    if (isset($destination) && $form_state['redirect'] !== FALSE) {
-      if (!isset($form_state['redirect'])) {
-        $form_state['redirect'] = current_path();
+    $request = $this->requestStack->getCurrentRequest();
+    $destination = $request->query->get('destination');
+
+    $redirect = $form_state->getRedirect();
+    // If there is a destination, and redirects are not explicitly disabled, add
+    // the destination as a query string to the redirect and suppress it for the
+    // current request.
+    if (isset($destination) && $redirect !== FALSE) {
+      // Create a valid redirect if one does not exist already.
+      if (!($redirect instanceof Url)) {
+        $redirect = Url::createFromRequest($request);
       }
-      if (is_string($form_state['redirect'])) {
-        $form_state['redirect'] = array($form_state['redirect']);
-      }
-      $options = isset($form_state['redirect'][1]) ? $form_state['redirect'][1] : array();
+
+      // Add the current destination to the redirect unless one exists already.
+      $options = $redirect->getOptions();
       if (!isset($options['query']['destination'])) {
         $options['query']['destination'] = $destination;
+        $redirect->setOptions($options);
       }
-      $form_state['redirect'][1] = $options;
-      $query->remove('destination');
+
+      $form_state->setRedirectUrl($redirect);
+      $request->query->remove('destination');
     }
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index 144ed65e3806..e071ca4dbf93 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -177,7 +177,7 @@ public function testHandleRedirectWithResponse() {
       ->will($this->returnCallback(function ($form, FormStateInterface $form_state) use ($response, $redirect) {
         // Set both the response and the redirect.
         $form_state->setResponse($response);
-        $form_state['redirect'] = $redirect;
+        $form_state->set('redirect', $redirect);
       }));
 
     $form_state = new FormState();
diff --git a/core/tests/Drupal/Tests/Core/Form/FormStateTest.php b/core/tests/Drupal/Tests/Core/Form/FormStateTest.php
index a193af45ee56..41882304cbaa 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormStateTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormStateTest.php
@@ -42,20 +42,14 @@ public function providerTestGetRedirect() {
     $data = array();
     $data[] = array(array(), NULL);
 
-    $data[] = array(array('redirect' => 'foo'), 'foo');
-    $data[] = array(array('redirect' => array('foo')), array('foo'));
-    $data[] = array(array('redirect' => array('bar', array('query' => array('foo' => 'baz')))), array('bar', array('query' => array('foo' => 'baz'))));
-    $data[] = array(array('redirect' => array('baz', array(), 301)), array('baz', array(), 301));
-
     $redirect = new RedirectResponse('/example');
     $data[] = array(array('redirect' => $redirect), $redirect);
 
-    $data[] = array(array('redirect_route' => new Url('test_route_b', array('key' => 'value'))), new Url('test_route_b', array('key' => 'value'), array('absolute' => TRUE)));
+    $data[] = array(array('redirect' => new Url('test_route_b', array('key' => 'value'))), new Url('test_route_b', array('key' => 'value')));
 
     $data[] = array(array('programmed' => TRUE), NULL);
     $data[] = array(array('rebuild' => TRUE), NULL);
     $data[] = array(array('no_redirect' => TRUE), NULL);
-    $data[] = array(array('redirect' => FALSE), NULL);
 
     return $data;
   }
diff --git a/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php b/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php
index 0bdc009f26a6..e76e9a6f4806 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php
@@ -98,57 +98,34 @@ public function providerTestHandleFormSubmissionWithResponses() {
   }
 
   /**
-   * Tests the redirectForm() method when a redirect is expected.
+   * Tests the redirectForm() method when the redirect is NULL.
    *
    * @covers ::redirectForm
-   *
-   * @dataProvider providerTestRedirectWithResult
    */
-  public function testRedirectWithResult($redirect_value, $result, $status = 303) {
+  public function testRedirectWithNull() {
     $form_submitter = $this->getFormSubmitter();
     $this->urlGenerator->expects($this->once())
       ->method('generateFromPath')
-      ->will($this->returnValueMap(array(
-          array(NULL, array('query' => array(), 'absolute' => TRUE), '<front>'),
-          array('foo', array('absolute' => TRUE), 'foo'),
-          array('bar', array('query' => array('foo' => 'baz'), 'absolute' => TRUE), 'bar'),
-          array('baz', array('absolute' => TRUE), 'baz'),
-        ))
-      );
+      ->with(NULL, array('query' => array(), 'absolute' => TRUE))
+      ->willReturn('<front>');
 
     $form_state = $this->getMock('Drupal\Core\Form\FormStateInterface');
     $form_state->expects($this->once())
       ->method('getRedirect')
-      ->willReturn($redirect_value);
+      ->willReturn(NULL);
     $redirect = $form_submitter->redirectForm($form_state);
-    $this->assertSame($result, $redirect->getTargetUrl());
-    $this->assertSame($status, $redirect->getStatusCode());
-  }
-
-  /**
-   * Provides test data for testing the redirectForm() method with a redirect.
-   *
-   * @return array
-   *   Returns some test data.
-   */
-  public function providerTestRedirectWithResult() {
-    return array(
-      array(NULL, '<front>'),
-      array('foo', 'foo'),
-      array(array('foo'), 'foo'),
-      array(array('bar', array('query' => array('foo' => 'baz'))), 'bar'),
-      array(array('baz', array(), 301), 'baz', 301),
-    );
+    $this->assertSame('<front>', $redirect->getTargetUrl());
+    $this->assertSame(303, $redirect->getStatusCode());
   }
 
   /**
-   * Tests the redirectForm() with redirect_route when a redirect is expected.
+   * Tests redirectForm() when a redirect is a Url object.
    *
    * @covers ::redirectForm
    *
-   * @dataProvider providerTestRedirectWithRouteWithResult
+   * @dataProvider providerTestRedirectWithUrl
    */
-  public function testRedirectWithRouteWithResult($redirect_value, $result, $status = 303) {
+  public function testRedirectWithUrl(Url $redirect_value, $result, $status = 303) {
     $container = new ContainerBuilder();
     $container->set('url_generator', $this->urlGenerator);
     \Drupal::setContainer($container);
@@ -176,7 +153,7 @@ public function testRedirectWithRouteWithResult($redirect_value, $result, $statu
    * @return array
    *   Returns some test data.
    */
-  public function providerTestRedirectWithRouteWithResult() {
+  public function providerTestRedirectWithUrl() {
     return array(
       array(new Url('test_route_a', array(), array('absolute' => TRUE)), 'test-route'),
       array(new Url('test_route_b', array('key' => 'value'), array('absolute' => TRUE)), 'test-route/value'),
-- 
GitLab