diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Access/SetSwitchAccessCheck.php b/core/modules/shortcut/lib/Drupal/shortcut/Access/SetSwitchAccessCheck.php
new file mode 100644
index 0000000..9177033
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Access/SetSwitchAccessCheck.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Access\SetSwitchAccessCheck.
+ */
+
+namespace Drupal\shortcut\Access;
+
+use Drupal\Core\Access\AccessCheckInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Defines a access checker for switching the shortcut set of a user account.
+ */
+class SetSwitchAccessCheck implements AccessCheckInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies(Route $route) {
+    return array_key_exists('_shortcut_set_switch', $route->getRequirements());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    $global_account = $request->attributes->get('account');
+    $account = $request->attributes->get('_account');
+
+    if (user_access('administer shortcuts', $global_account)) {
+      // Administrators can switch anyone's shortcut set.
+      return static::ALLOW;
+    }
+
+    if (!user_access('switch shortcut sets', $global_account)) {
+      // The user has no permission to switch anyone's shortcut set.
+      return static::DENY;
+    }
+
+    if (!isset($account) || $global_account->id() == $account->id()) {
+      // Users with the 'switch shortcut sets' permission can switch their own
+      // shortcuts sets.
+      return static::ALLOW;
+    }
+
+    return static::DENY;
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/SetSwitch.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetSwitch.php
new file mode 100644
index 0000000..0409cd2
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetSwitch.php
@@ -0,0 +1,233 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Form\SetSwitch.
+ */
+
+namespace Drupal\shortcut\Form;
+
+use Drupal\Core\Controller\ControllerInterface;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Form\FormInterface;
+use Drupal\Component\Utility\String;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\user\UserInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Builds the shortcut set switch form.
+ */
+class SetSwitch implements ControllerInterface, FormInterface {
+
+  /**
+   * The account the shortcut set is for.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $account;
+
+  /**
+   * The request object.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * The shortcut storage controller.
+   *
+   * @var \Drupal\shortcut\ShortcutStorageControllerInterface
+   */
+  protected $storageController;
+
+  /**
+   * The user storage controller.
+   *
+   * @var \Drupal\user\UserStorageControllerInterface
+   */
+  protected $userStorageController;
+
+  /**
+   * Constructs a SetSwitch object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(EntityManager $entity_manager) {
+    $this->storageController = $entity_manager->getStorageController('shortcut');
+    $this->userStorageController = $entity_manager->getStorageController('user');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.entity')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'shortcut_set_switch';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state, UserInterface $_account = NULL, Request $request = NULL) {
+    global $user;
+    $this->request = $request;
+    $this->account = $this->userStorageController->load($_account->id())->getBCEntity();
+
+    // Prepare the list of shortcut sets.
+    $options = array_map(function ($set) {
+      return String::checkPlain($set->label());
+    }, $this->storageController->loadMultiple());
+
+    $current_set = shortcut_current_displayed_set($this->account);
+
+    // Only administrators can add shortcut sets.
+    $add_access = user_access('administer shortcuts');
+    if ($add_access) {
+      $options['new'] = t('New set');
+    }
+
+    $account_is_user = $this->account->id() == $user->uid;
+    if (count($options) > 1) {
+      $form['set'] = array(
+        '#type' => 'radios',
+        '#title' => $account_is_user ? t('Choose a set of shortcuts to use') : t('Choose a set of shortcuts for this user'),
+        '#options' => $options,
+        '#default_value' => $current_set->id(),
+      );
+
+      $form['label'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Label'),
+        '#title_display' => 'invisible',
+        '#description' => t('The new set is created by copying items from your default shortcut set.'),
+        '#access' => $add_access,
+      );
+      $form['id'] = array(
+        '#type' => 'machine_name',
+        '#machine_name' => array(
+          'exists' => array($this, 'exists'),
+          'replace_pattern' => '[^a-z0-9-]+',
+          'replace' => '-',
+        ),
+        // This ID could be used for menu name.
+        '#maxlength' => 23,
+        '#states' => array(
+          'required' => array(
+            ':input[name="set"]' => array('value' => 'new'),
+          ),
+        ),
+        '#required' => FALSE,
+      );
+
+      if (!$account_is_user) {
+        $default_set = $this->storageController->getDefaultSet($this->account->getBCEntity());
+        $form['new']['#description'] = t('The new set is created by copying items from the %default set.', array('%default' => $default_set->label()));
+      }
+
+      $form['#attached'] = array(
+        'library' => array(array('shortcut', 'drupal.shortcut.admin')),
+      );
+
+      $form['actions'] = array('#type' => 'actions');
+      $form['actions']['submit'] = array(
+        '#type' => 'submit',
+        '#value' => t('Change set'),
+      );
+    }
+    else {
+      // There is only 1 option, so output a message in the $form array.
+      $form['info'] = array(
+        '#markup' => '<p>' . t('You are currently using the %set-name shortcut set.', array('%set-name' => $current_set->label())) . '</p>',
+      );
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, array &$form_state) {
+    if ($form_state['values']['set'] == 'new') {
+      // Check to prevent creating a shortcut set with an empty title.
+      if (trim($form_state['values']['label']) == '') {
+        form_set_error('new', t('The new set label is required.'));
+      }
+      // Check to prevent a duplicate title.
+      if (shortcut_set_title_exists($form_state['values']['label'])) {
+        form_set_error('label', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['label'])));
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    global $user;
+
+    $account_is_user = $this->account->id() == $user->uid;
+    if ($form_state['values']['set'] == 'new') {
+      // Save a new shortcut set with links copied from the user's default set.
+      $default_set = $this->storageController->getDefaultSet($this->account);
+      $set = $this->storageController->create(array(
+        'id' => $form_state['values']['id'],
+        'label' => $form_state['values']['label'],
+        'links' => $default_set->links,
+      ));
+      $set->save();
+      $replacements = array(
+        '%user' => $this->account->label(),
+        '%set_name' => $set->label(),
+        '@switch-url' => url($this->request->attributes->get('system_path')),
+      );
+      if ($account_is_user) {
+        // Only administrators can create new shortcut sets, so we know they have
+        // access to switch back.
+        drupal_set_message(t('You are now using the new %set_name shortcut set. You can edit it from this page or <a href="@switch-url">switch back to a different one.</a>', $replacements));
+      }
+      else {
+        drupal_set_message(t('%user is now using a new shortcut set called %set_name. You can edit it from this page.', $replacements));
+      }
+      $form_state['redirect'] = 'admin/config/user-interface/shortcut/manage/' . $set->id();
+    }
+    else {
+      // Switch to a different shortcut set.
+      $set = $this->storageController->load($form_state['values']['set']);
+      $replacements = array(
+        '%user' => $this->account->label(),
+        '%set_name' => $set->label(),
+      );
+      drupal_set_message($account_is_user ? t('You are now using the %set_name shortcut set.', $replacements) : t('%user is now using the %set_name shortcut set.', $replacements));
+    }
+
+    // Assign the shortcut set to the provided user account.
+    $this->storageController->assignUser($set, $this->account);
+  }
+
+  /**
+   * Determines if a shortcut set exists already.
+   *
+   * @param string $id
+   *   The set ID to check.
+   *
+   * @return bool
+   *   TRUE if the shortcut set exists, FALSE otherwise.
+   */
+  public function exists($id) {
+    $sets = $this->storageController->loadMultiple(array($id));
+    return !empty($sets[$id]);
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutStorageController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutStorageController.php
index a7150b1..9f8b360 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutStorageController.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutStorageController.php
@@ -7,8 +7,15 @@
 
 namespace Drupal\shortcut;
 
+use Drupal\Component\Uuid\Uuid;
+use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Config\Entity\ConfigStorageController;
+use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Session\AccountInterface;
 use Drupal\shortcut\Plugin\Core\Entity\Shortcut;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Defines a storage controller for shortcut entities.
@@ -16,6 +23,50 @@
 class ShortcutStorageController extends ConfigStorageController implements ShortcutStorageControllerInterface {
 
   /**
+   * The module handler.
+   *
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructs a ShortcutStorageController object.
+   *
+   * @param string $entity_type
+   *   The entity type for which the instance is created.
+   * @param array $entity_info
+   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The config factory service.
+   * @param \Drupal\Core\Config\StorageInterface $config_storage
+   *   The config storage service.
+   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query_factory
+   *   The entity query factory.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler;
+   */
+  public function __construct($entity_type, array $entity_info, ConfigFactory $config_factory, StorageInterface $config_storage, QueryFactory $entity_query_factory, ModuleHandlerInterface $module_handler) {
+    parent::__construct($entity_type, $entity_info, $config_factory, $config_storage, $entity_query_factory);
+
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+    return new static(
+      $entity_type,
+      $entity_info,
+      $container->get('config.factory'),
+      $container->get('config.storage'),
+      $container->get('entity.query'),
+      $container->get('module_handler')
+    );
+  }
+
+  /**
    * Overrides \Drupal\config\ConfigStorageController::attachLoad().
    */
   protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
@@ -45,7 +96,7 @@ public function deleteAssignedShortcutSets(Shortcut $entity) {
    */
   public function assignUser($shortcut_set, $account) {
     db_merge('shortcut_set_users')
-      ->key(array('uid' => $account->uid))
+      ->key(array('uid' => $account->id()))
       ->fields(array('set_name' => $shortcut_set->id()))
       ->execute();
     drupal_static_reset('shortcut_current_displayed_set');
@@ -56,7 +107,7 @@ public function assignUser($shortcut_set, $account) {
    */
   public function unassignUser($account) {
     $deleted = db_delete('shortcut_set_users')
-      ->condition('uid', $account->uid)
+      ->condition('uid', $account->id())
       ->execute();
     return (bool) $deleted;
   }
@@ -77,4 +128,25 @@ public function getAssignedToUser($account) {
   public function countAssignedUsers(Shortcut $shortcut) {
     return db_query('SELECT COUNT(*) FROM {shortcut_set_users} WHERE set_name = :name', array(':name' => $shortcut->id()))->fetchField();
   }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  function getDefaultSet(AccountInterface $account) {
+    // Allow modules to return a default shortcut set name. Since we can only
+    // have one, we allow the last module which returns a valid result to take
+    // precedence. If no module returns a valid set, fall back on the site-wide
+    // default, which is the lowest-numbered shortcut set.
+    $suggestions = array_reverse($this->moduleHandler->invokeAll('shortcut_default_set', $account));
+    $suggestions[] = 'default';
+    foreach ($suggestions as $name) {
+      if ($shortcut_set = $this->load($name)) {
+        break;
+      }
+    }
+
+    return $shortcut_set;
+  }
+
 }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutStorageControllerInterface.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutStorageControllerInterface.php
index e951620..9f3cebd 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutStorageControllerInterface.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutStorageControllerInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\shortcut;
 
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Session\AccountInterface;
 use Drupal\shortcut\Plugin\Core\Entity\Shortcut;
 
 /**
@@ -69,4 +70,16 @@ public function getAssignedToUser($account);
    *   The number of users who have this set assigned to them.
    */
   public function countAssignedUsers(Shortcut $shortcut);
+
+  /**
+   * Gets the default shortcut set for a given user account.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user account whose default shortcut set will be returned.
+   *
+   * @return
+   *   An object representing the default shortcut set.
+   */
+  public function getDefaultSet(AccountInterface $account);
+
 }
diff --git a/core/modules/shortcut/shortcut.admin.inc b/core/modules/shortcut/shortcut.admin.inc
index a75c4e1..88860ec 100644
--- a/core/modules/shortcut/shortcut.admin.inc
+++ b/core/modules/shortcut/shortcut.admin.inc
@@ -8,168 +8,6 @@
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 
-/**
- * Form callback: builds the form for switching shortcut sets.
- *
- * @param $form
- *   An associative array containing the structure of the form.
- * @param $form_state
- *   An associative array containing the current state of the form.
- * @param $account
- *   (optional) The user account whose shortcuts will be switched. Defaults to
- *   the current logged-in user.
- *
- * @return
- *   An array representing the form definition.
- *
- * @ingroup forms
- * @see shortcut_set_switch_validate()
- * @see shortcut_set_switch_submit()
- */
-function shortcut_set_switch($form, &$form_state, $account = NULL) {
-  global $user;
-  if (!isset($account)) {
-    $account = $user;
-  }
-
-  // Prepare the list of shortcut sets.
-  $sets = entity_load_multiple('shortcut');
-  $current_set = shortcut_current_displayed_set($account);
-
-  $options = array();
-  foreach ($sets as $name => $set) {
-    $options[$name] = check_plain($set->label());
-  }
-
-  // Only administrators can add shortcut sets.
-  $add_access = user_access('administer shortcuts');
-  if ($add_access) {
-    $options['new'] = t('New set');
-  }
-
-  if (count($options) > 1) {
-    $form['account'] = array(
-      '#type' => 'value',
-      '#value' => $account,
-    );
-
-    $form['set'] = array(
-      '#type' => 'radios',
-      '#title' => $user->uid == $account->uid ? t('Choose a set of shortcuts to use') : t('Choose a set of shortcuts for this user'),
-      '#options' => $options,
-      '#default_value' => $current_set->id(),
-    );
-
-    $form['label'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Label'),
-      '#title_display' => 'invisible',
-      '#description' => t('The new set is created by copying items from your default shortcut set.'),
-      '#access' => $add_access,
-    );
-    $form['id'] = array(
-      '#type' => 'machine_name',
-      '#machine_name' => array(
-        'exists' => 'shortcut_set_load',
-        'source' => array('label'),
-        'replace_pattern' => '[^a-z0-9-]+',
-        'replace' => '-',
-      ),
-      // This id could be used for menu name.
-      '#maxlength' => 23,
-      '#states' => array(
-        'required' => array(
-          ':input[name="set"]' => array('value' => 'new'),
-        ),
-      ),
-      '#required' => FALSE,
-    );
-
-    if ($user->uid != $account->uid) {
-      $default_set = shortcut_default_set($account);
-      $form['new']['#description'] = t('The new set is created by copying items from the %default set.', array('%default' => $default_set->label()));
-    }
-
-    $form['#attached'] = array(
-      'library' => array(array('shortcut', 'drupal.shortcut.admin')),
-    );
-
-    $form['actions'] = array('#type' => 'actions');
-    $form['actions']['submit'] = array(
-      '#type' => 'submit',
-      '#value' => t('Change set'),
-    );
-  }
-  else {
-    // There is only 1 option, so output a message in the $form array.
-    $form['info'] = array(
-      '#markup' => '<p>' . t('You are currently using the %set-name shortcut set.', array('%set-name' => $current_set->label())) . '</p>',
-    );
-  }
-
-  return $form;
-}
-
-/**
- * Validation handler for shortcut_set_switch().
- */
-function shortcut_set_switch_validate($form, &$form_state) {
-  if ($form_state['values']['set'] == 'new') {
-    // Check to prevent creating a shortcut set with an empty title.
-    if (trim($form_state['values']['label']) == '') {
-      form_set_error('new', t('The new set label is required.'));
-    }
-    // Check to prevent a duplicate title.
-    if (shortcut_set_title_exists($form_state['values']['label'])) {
-      form_set_error('label', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['label'])));
-    }
-  }
-}
-
-/**
- * Submit handler for shortcut_set_switch().
- */
-function shortcut_set_switch_submit($form, &$form_state) {
-  global $user;
-  $account = $form_state['values']['account'];
-
-  if ($form_state['values']['set'] == 'new') {
-    // Save a new shortcut set with links copied from the user's default set.
-    $default_set = shortcut_default_set($account);
-    $set = entity_create('shortcut', array(
-      'id' => $form_state['values']['id'],
-      'label' => $form_state['values']['label'],
-      'links' => $default_set->links,
-    ));
-    $set->save();
-    $replacements = array(
-      '%user' => $account->name,
-      '%set_name' => $set->label(),
-      '@switch-url' => url(current_path()),
-    );
-    if ($account->uid == $user->uid) {
-      // Only administrators can create new shortcut sets, so we know they have
-      // access to switch back.
-      drupal_set_message(t('You are now using the new %set_name shortcut set. You can edit it from this page or <a href="@switch-url">switch back to a different one.</a>', $replacements));
-    }
-    else {
-      drupal_set_message(t('%user is now using a new shortcut set called %set_name. You can edit it from this page.', $replacements));
-    }
-    $form_state['redirect'] = 'admin/config/user-interface/shortcut/manage/' . $set->id();
-  }
-  else {
-    // Switch to a different shortcut set.
-    $set = shortcut_set_load($form_state['values']['set']);
-    $replacements = array(
-      '%user' => $account->name,
-      '%set_name' => $set->label(),
-    );
-    drupal_set_message($account->uid == $user->uid ? t('You are now using the %set_name shortcut set.', $replacements) : t('%user is now using the %set_name shortcut set.', $replacements));
-  }
-
-  // Assign the shortcut set to the provided user account.
-  shortcut_set_assign_user($set, $account);
-}
 
 /**
  * Page callback: provides the shortcut set creation form.
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index c58bf8a..00dae2b 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -139,12 +139,8 @@ function shortcut_menu() {
   );
   $items['user/%user/shortcuts'] = array(
     'title' => 'Shortcuts',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('shortcut_set_switch', 1),
-    'access callback' => 'shortcut_set_switch_access',
-    'access arguments' => array(1),
+    'route_name' => 'shortcut_set_switch',
     'type' => MENU_LOCAL_TASK,
-    'file' => 'shortcut.admin.inc',
   );
 
   return $items;
@@ -183,12 +179,12 @@ function shortcut_set_edit_access($shortcut_set = NULL) {
   return FALSE;
 }
 
-/**
+ /**
  * Access callback for switching the shortcut set assigned to a user account.
  *
  * @param object $account
  *   (optional) The user account whose shortcuts will be switched. If not set,
- *   permissions will be checked for switching the logged-in user's own
+ *   permissions will be checked for switching the loggedin user's own
  *   shortcut set.
  *
  * @return
diff --git a/core/modules/shortcut/shortcut.routing.yml b/core/modules/shortcut/shortcut.routing.yml
index dbd1376..ca4cef0 100644
--- a/core/modules/shortcut/shortcut.routing.yml
+++ b/core/modules/shortcut/shortcut.routing.yml
@@ -26,6 +26,16 @@ shortcut_set_edit:
   requirements:
     _entity_access: 'shortcut.update'
 
+shortcut_set_switch:
+  pattern: '/user/{_account}/shortcuts'
+  defaults:
+    _form: 'Drupal\shortcut\Form\SetSwitch'
+  options:
+    converters:
+      _account: 'user'
+  requirements:
+    _shortcut_set_switch: 'TRUE'
+
 shortcut_link_add_inline:
   pattern: '/admin/config/user-interface/shortcut/manage/{shortcut}/add-link-inline'
   defaults:
diff --git a/core/modules/shortcut/shortcut.services.yml b/core/modules/shortcut/shortcut.services.yml
index bb95f49..c306354 100644
--- a/core/modules/shortcut/shortcut.services.yml
+++ b/core/modules/shortcut/shortcut.services.yml
@@ -3,3 +3,7 @@ services:
     class: Drupal\shortcut\Access\LinkDeleteAccessCheck
     tags:
       - { name: access_check }
+  access_check.shortcut.switch:
+    class: Drupal\shortcut\Access\SetSwitchAccessCheck
+    tags:
+      - { name: access_check }
