diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkAccessCheck.php b/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkAccessCheck.php
deleted file mode 100644
index 85e48b2..0000000
--- a/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkAccessCheck.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\shortcut\Access\LinkAccessCheck.
- */
-
-namespace Drupal\shortcut\Access;
-
-use Drupal\Core\Access\StaticAccessCheckInterface;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * Provides an access check for shortcut link delete routes.
- */
-class LinkAccessCheck implements StaticAccessCheckInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function appliesTo() {
-    return array('_access_shortcut_link');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function access(Route $route, Request $request) {
-    $menu_link = $request->attributes->get('menu_link');
-    $set_name = str_replace('shortcut-', '', $menu_link['menu_name']);
-    if ($shortcut_set = shortcut_set_load($set_name)) {
-      return shortcut_set_edit_access($shortcut_set) ? static::ALLOW : static::DENY;
-    }
-    return static::DENY;
-  }
-
-}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutController.php b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutController.php
new file mode 100644
index 0000000..a7b0d42
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutController.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Controller\ShortcutController.
+ */
+
+namespace Drupal\shortcut\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\shortcut\ShortcutSetInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides route responses for taxonomy.module.
+ */
+class ShortcutController extends ControllerBase {
+
+  /**
+   * Returns a rendered edit form to create a new shortcut associated to the
+   * given shortcut set.
+   *
+   * @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
+   *   The shortcut set this shortcut will be added to.
+   *
+   * @return array
+   *   The shortcut add form.
+   */
+  public function addForm(ShortcutSetInterface $shortcut_set) {
+    $shortcut = $this->entityManager()->getStorageController('shortcut')->create(array('shortcut_set' => $shortcut_set->id()));
+    if ($this->moduleHandler()->moduleExists('language')) {
+      $shortcut->langcode = language_get_default_langcode('shortcut', $shortcut_set->id());
+    }
+    return $this->entityManager()->getForm($shortcut, 'add');
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php
index 7123ae5..9bea543 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php
@@ -34,21 +34,33 @@ class ShortcutSetController extends ControllerBase {
    */
   public function addShortcutLinkInline(ShortcutSetInterface $shortcut_set, Request $request) {
     $token = $request->query->get('token');
-    $link = $request->query->get('link');
-    if (isset($token) && drupal_valid_token($token, 'shortcut-add-link') && shortcut_valid_link($link)) {
-      $item = menu_get_item($link);
-      $title = ($item && $item['title']) ? $item['title'] : $link;
-      $link = array(
-        'link_title' => $title,
-        'link_path' => $link,
-      );
-      $this->moduleHandler()->loadInclude('shortcut', 'admin.inc');
-      shortcut_admin_add_link($link, $shortcut_set);
-      if ($shortcut_set->save() == SAVED_UPDATED) {
-        drupal_set_message(t('Added a shortcut for %title.', array('%title' => $link['link_title'])));
+    $link_path = $request->query->get('link');
+    if (isset($token) && drupal_valid_token($token, 'shortcut-add-link') && shortcut_valid_link($link_path)) {
+      // Normalize the path in case it is an alias.
+      $link_path = $this->container()->get('path.alias_manager')->getSystemPath($link_path);
+      if (empty($link_path)) {
+        $link_path = '<front>';
+      }
+
+      $result = $this->container()->get('entity.query')->get('menu_link')
+        ->condition('link_path', $link_path, '=')
+        ->execute();
+
+      if (!empty($result)) {
+        $menu_link = $this->entityManager->getStorageController('menu_link')->load(reset($result));
+
+        $shortcut = $this->entityManager->getStorageController('shortcut')->create(array(
+          'title' => $menu_link->label(),
+          'menu_link' => $menu_link,
+        ));
+        $shortcut->save();
+
+        $shortcut_set->addShortcut($shortcut)->save();
+
+        drupal_set_message(t('Added a shortcut for %title.', array('%title' => $menu_link->label())));
       }
       else {
-        drupal_set_message(t('Unable to add a shortcut for %title.', array('%title' => $link['link_title'])));
+        drupal_set_message(t('Unable to add a shortcut for %path.', array('%path' => $link_path)));
       }
       return $this->redirect('<front>');
     }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
new file mode 100644
index 0000000..95d18b5
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
@@ -0,0 +1,163 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Plugin\Core\Entity\Shortcut.
+ */
+
+namespace Drupal\shortcut\Entity;
+
+use Drupal\Core\Entity\EntityNG;
+use Drupal\Core\Entity\EntityStorageControllerInterface;
+
+/**
+ * Defines the shortcut entity class.
+ *
+ * @EntityType(
+ *   id = "shortcut",
+ *   label = @Translation("Shortcut link"),
+ *   module = "shortcut",
+ *   controllers = {
+ *     "storage" = "Drupal\Core\Entity\DatabaseStorageControllerNG",
+ *     "access" = "Drupal\shortcut\ShortcutAccessController",
+ *     "form" = {
+ *       "default" = "Drupal\shortcut\ShortcutFormController",
+ *       "add" = "Drupal\shortcut\ShortcutFormController",
+ *       "delete" = "Drupal\shortcut\Form\ShortcutDeleteForm"
+ *     },
+ *     "translation" = "Drupal\content_translation\ContentTranslationControllerNG"
+ *   },
+ *   base_table = "shortcut",
+ *   data_table = "shortcut_property_data",
+ *   translatable = TRUE,
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid",
+ *     "bundle" = "shortcut_set",
+ *     "label" = "title"
+ *   }
+ * )
+ */
+class Shortcut extends EntityNG {
+
+  /**
+   * The entity ID.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $id;
+
+  /**
+   * The entity UUID.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $uuid;
+
+  /**
+   * The bundle of the shortcut.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $shortcut_set;
+
+  /**
+   * The label of the shortcut.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $title;
+
+  /**
+   * The weight of the shortcut.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $weight;
+
+  /**
+   * The referenced menu link.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $menu_link;
+
+  /**
+   * Initialize the object. Invoked upon construction and wake up.
+   */
+  protected function init() {
+    parent::init();
+
+    // We unset all defined properties, so magic getters apply.
+    unset($this->id);
+    unset($this->uuid);
+    unset($this->shortcut_set);
+    unset($this->title);
+    unset($this->weight);
+    unset($this->menu_link);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
+    parent::preCreate($storage_controller, $values);
+
+    if (!isset($values['shortcut_set'])) {
+      $values['shortcut_set'] = 'default';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions($entity_type) {
+    $properties['id'] = array(
+      'label' => t('ID'),
+      'description' => t('The ID of the shortcut.'),
+      'type' => 'integer_field',
+      'read-only' => TRUE,
+    );
+    $properties['uuid'] = array(
+      'label' => t('UUID'),
+      'description' => t('The UUID of the shortcut.'),
+      'type' => 'uuid_field',
+    );
+    $properties['shortcut_set'] = array(
+      'label' => t('Shortcut set'),
+      'description' => t('The bundle of the shortcut.'),
+      'type' => 'string_field',
+      'required' => TRUE,
+    );
+    $properties['title'] = array(
+      'label' => t('Title'),
+      'description' => t('The name of the shortcut.'),
+      'type' => 'string_field',
+      'translatable' => TRUE,
+    );
+    $properties['weight'] = array(
+      'label' => t('Weight'),
+      'description' => t('Weight among shortcuts in the same shortcut set.'),
+      'type' => 'integer_field',
+    );
+    $properties['menu_link'] = array(
+      'label' => t('Menu link'),
+      'description' => t('The referenced menu link.'),
+      'type' => 'entity_reference_field',
+      'settings' => array('target_type' => 'menu_link'),
+      'required' => TRUE,
+    );
+    $properties['langcode'] = array(
+      'label' => t('Language code'),
+      'description' => t('The language code of the shortcut.'),
+      'type' => 'language_field',
+    );
+    $properties['default_langcode'] = array(
+      'label' => t('Default language'),
+      'description' => t('Flag to indicate whether this is the default language.'),
+      'type' => 'boolean_field',
+    );
+    return $properties;
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/ShortcutSet.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/ShortcutSet.php
index b52fccb..014dd8a 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Entity/ShortcutSet.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/ShortcutSet.php
@@ -8,9 +8,8 @@
 namespace Drupal\shortcut\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
-use Drupal\Core\Entity\Annotation\EntityType;
-use Drupal\Core\Annotation\Translation;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\shortcut\ShortcutInterface;
 use Drupal\shortcut\ShortcutSetInterface;
 
 /**
@@ -83,18 +82,12 @@ public function postCreate(EntityStorageControllerInterface $storage_controller)
     if (!$this->getOriginalID()) {
       // Save a new shortcut set with links copied from the user's default set.
       $default_set = shortcut_default_set();
-      // Generate a name to have no collisions with menu.
-      // Size of menu_name is 32 so id could be 23 = 32 - strlen('shortcut-').
-      $id = substr($this->id(), 0, 23);
-      $this->set('id', $id);
-      if ($default_set->id() != $id) {
-        foreach ($default_set->links as $link) {
-          $link = $link->createDuplicate();
-          $link->enforceIsNew();
-          $link->menu_name = $id;
-          $link->save();
-          $this->links[$link->uuid()] = $link;
-        }
+      foreach ($default_set->links as $shortcut) {
+        $shortcut = $shortcut->createDuplicate();
+        $shortcut->enforceIsNew();
+        $shortcut->shortcut_set->value = $this->id();
+        $shortcut->save();
+        $this->links[$shortcut->uuid()] = $shortcut;
       }
     }
   }
@@ -118,13 +111,8 @@ public function postSave(EntityStorageControllerInterface $storage_controller, $
     parent::postSave($storage_controller, $update);
 
     foreach ($this->links as $uuid) {
-      if ($menu_link = entity_load_by_uuid('menu_link', $uuid)) {
-        // Do not specifically associate these links with the shortcut module,
-        // since other modules may make them editable via the menu system.
-        // However, we do need to specify the correct menu name.
-        $menu_link->menu_name = 'shortcut-' . $this->id();
-        $menu_link->plid = 0;
-        $menu_link->save();
+      if ($shortcut = entity_load_by_uuid('shortcut', $uuid)) {
+        $shortcut->save();
       }
     }
   }
@@ -137,9 +125,44 @@ public static function preDelete(EntityStorageControllerInterface $storage_contr
 
     foreach ($entities as $entity) {
       $storage_controller->deleteAssignedShortcutSets($entity);
-      // Next, delete the menu links for this set.
-      menu_delete_links('shortcut-' . $entity->id());
+
+      // Next, delete the shortcuts for this set.
+      $shortcut_ids = \Drupal::entityQuery('shortcut')
+        ->condition('shortcut_set', $entity->id(), '=')
+        ->execute();
+
+      $controller = \Drupal::entityManager()->getStorageController('shortcut');
+      $entities = $controller->loadMultiple($shortcut_ids);
+      $controller->delete($entities);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addShortcut(ShortcutInterface $shortcut) {
+    $this->links[$shortcut->uuid()] = $shortcut;
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetLinkWeights() {
+    $weight = -50;
+    foreach ($this->links as $link) {
+      $link->weight->value = ++$weight;
     }
+
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getShortcuts() {
+    return \Drupal::entityManager()->getStorageController('shortcut')->loadByProperties(array('shortcut_set' => $this->id()));
   }
 
 }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/LinkDelete.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/LinkDelete.php
deleted file mode 100644
index a0012dc..0000000
--- a/core/modules/shortcut/lib/Drupal/shortcut/Form/LinkDelete.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\shortcut\Form\LinkDelete.
- */
-
-namespace Drupal\shortcut\Form;
-
-use Drupal\Core\Form\ConfirmFormBase;
-use Drupal\menu_link\Entity\MenuLink;
-
-/**
- * Builds the shortcut link deletion form.
- */
-class LinkDelete extends ConfirmFormBase {
-
-  /**
-   * The menu link to delete.
-   *
-   * @var \Drupal\menu_link\Entity\MenuLink
-   */
-  protected $menuLink;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormID() {
-    return 'shortcut_link_delete';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQuestion() {
-    return t('Are you sure you want to delete the shortcut %title?', array('%title' => $this->menuLink->link_title));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCancelRoute() {
-    return array(
-      'route_name' => 'shortcut.set_customize',
-      'route_parameters' => array(
-        'shortcut_set' => str_replace('shortcut-', '', $this->menuLink->menu_name),
-      ),
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfirmText() {
-    return t('Delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, array &$form_state, MenuLink $menu_link = NULL) {
-    $this->menuLink = $menu_link;
-
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, array &$form_state) {
-    menu_link_delete($this->menuLink->mlid);
-    $set_name = str_replace('shortcut-', '' , $this->menuLink->menu_name);
-    $form_state['redirect'] = 'admin/config/user-interface/shortcut/manage/' . $set_name;
-    drupal_set_message(t('The shortcut %title has been deleted.', array('%title' => $this->menuLink->link_title)));
-  }
-
-}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
index 29da4a8..334bf56 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
@@ -8,6 +8,7 @@
 namespace Drupal\shortcut\Form;
 
 use Drupal\Core\Entity\EntityFormController;
+use Drupal\Core\Entity\EntityManager;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -16,6 +17,32 @@
 class SetCustomize extends EntityFormController {
 
   /**
+   * The shortcut storage controller.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageControllerInterface
+   */
+  protected $storageController;
+
+  /**
+   * Constructs a SetCustomize object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(EntityManager $entity_manager) {
+    $this->storageController = $entity_manager->getStorageController('shortcut');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.manager')
+    );
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function form(array $form, array &$form_state) {
@@ -28,35 +55,36 @@ public function form(array $form, array &$form_state) {
     $form['shortcuts']['links'] = array(
       '#type' => 'table',
       '#header' => array(t('Name'), t('Weight'), t('Operations')),
-      '#empty' => t('No shortcuts available. @link', array('@link' => l(t('Add a shortcut'), 'admin/config/user-interface/shortcut/' . $this->entity->id() . '/add-link'))),
+      '#empty' => $this->t('No shortcuts available. <a href="@link">Add a shortcut</a>', array('@link' => $this->urlGenerator()->generateFromRoute('shortcut.link_add', array('shortcut_set' => $this->entity->id())))),
       '#attributes' => array('id' => 'shortcuts'),
       '#tabledrag' => array(
         array('order', 'sibling', 'shortcut-weight'),
       ),
     );
 
-    foreach ($this->entity->links as $link) {
-      $mlid = $link->id();
-      $form['shortcuts']['links'][$mlid]['#attributes']['class'][] = 'draggable';
-      $form['shortcuts']['links'][$mlid]['name']['#markup'] = l($link->link_title, $link->link_path);
-      $form['shortcuts']['links'][$mlid]['#weight'] = $link->weight;
-      $form['shortcuts']['links'][$mlid]['weight'] = array(
+    $shortcuts = $this->storageController->loadByProperties(array('shortcut_set' => $this->entity->id()));
+    foreach ($shortcuts as $shortcut) {
+      $id = $shortcut->id();
+      $form['shortcuts']['links'][$id]['#attributes']['class'][] = 'draggable';
+      $form['shortcuts']['links'][$id]['name']['#markup'] = l($shortcut->title->value, $shortcut->menu_link->entity['link_path']);
+      $form['shortcuts']['links'][$id]['#weight'] = $shortcut->weight->value;
+      $form['shortcuts']['links'][$id]['weight'] = array(
         '#type' => 'weight',
-        '#title' => t('Weight for @title', array('@title' => $link->link_title)),
+        '#title' => t('Weight for @title', array('@title' => $shortcut->title->value)),
         '#title_display' => 'invisible',
-        '#default_value' => $link->weight,
+        '#default_value' => $shortcut->weight->value,
         '#attributes' => array('class' => array('shortcut-weight')),
       );
 
       $links['edit'] = array(
         'title' => t('Edit'),
-        'href' => "admin/config/user-interface/shortcut/link/$mlid",
+        'href' => "admin/config/user-interface/shortcut/link/$id",
       );
       $links['delete'] = array(
         'title' => t('Delete'),
-        'href' => "admin/config/user-interface/shortcut/link/$mlid/delete",
+        'href' => "admin/config/user-interface/shortcut/link/$id/delete",
       );
-      $form['shortcuts']['links'][$mlid]['operations'] = array(
+      $form['shortcuts']['links'][$id]['operations'] = array(
         '#type' => 'operations',
         '#links' => $links,
       );
@@ -74,7 +102,7 @@ protected function actions(array $form, array &$form_state) {
     return array(
       'submit' => array(
         '#value' => t('Save changes'),
-        '#access' => !empty($this->entity->links),
+        '#access' => (bool) element_get_visible_children($form['shortcuts']['links']),
         '#submit' => array(
           array($this, 'submit'),
           array($this, 'save'),
@@ -87,9 +115,10 @@ protected function actions(array $form, array &$form_state) {
    * {@inheritdoc}
    */
   public function save(array $form, array &$form_state) {
-    foreach ($this->entity->links as $link) {
-      $link->weight = $form_state['values']['shortcuts']['links'][$link->mlid]['weight'];
-      $link->save();
+    $shortcuts = $this->storageController->loadByProperties(array('shortcut_set' => $this->entity->id()));
+    foreach ($shortcuts as $shortcut) {
+      $shortcut->weight->value = $form_state['values']['shortcuts']['links'][$shortcut->id()]['weight'];
+      $shortcut->save();
     }
     drupal_set_message(t('The shortcut set has been updated.'));
   }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutDeleteForm.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutDeleteForm.php
new file mode 100644
index 0000000..aba6ee2
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutDeleteForm.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Form\ShortcutDeleteForm.
+ */
+
+namespace Drupal\shortcut\Form;
+
+use Drupal\Core\Entity\EntityNGConfirmFormBase;
+
+/**
+ * Builds the shortcut link deletion form.
+ */
+class ShortcutDeleteForm extends EntityNGConfirmFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'shortcut_confirm_delete';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return t('Are you sure you want to delete the shortcut %title?', array('%title' => $this->entity->title->value));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelRoute() {
+    return array(
+      'route_name' => 'shortcut.set_customize',
+      'route_parameters' => array(
+        'shortcut_set' => $this->entity->bundle(),
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    $this->entity->delete();
+    $form_state['redirect'] = 'admin/config/user-interface/shortcut/manage/' . $this->entity->bundle();
+    drupal_set_message(t('The shortcut %title has been deleted.', array('%title' => $this->entity->title->value)));
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php
new file mode 100644
index 0000000..3742897
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\ShortcutAccessController.
+ */
+
+namespace Drupal\shortcut;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityAccessController;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Defines the access controller for the test entity type.
+ */
+class ShortcutAccessController extends EntityAccessController {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
+    if ($shortcut_set = shortcut_set_load($entity->bundle())) {
+      return shortcut_set_edit_access($shortcut_set, $account);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
+    if ($shortcut_set = shortcut_set_load($entity_bundle)) {
+      return shortcut_set_edit_access($shortcut_set, $account);
+    }
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutFormController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutFormController.php
new file mode 100644
index 0000000..298956c
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutFormController.php
@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\ShortcutFormController.
+ */
+
+namespace Drupal\shortcut;
+
+use Drupal\Core\Entity\EntityControllerInterface;
+use Drupal\Core\Entity\EntityFormControllerNG;
+use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Path\AliasManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Form controller for the shortcut entity forms.
+ */
+class ShortcutFormController extends EntityFormControllerNG {
+
+  /**
+   * The path alias manager.
+   *
+   * @var \Drupal\Core\Path\AliasManagerInterface
+   */
+  protected $aliasManager;
+
+  /**
+   * The shortcut_set storage controller.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageControllerInterface
+   */
+  protected $storageController;
+
+  /**
+   * The entity query factory.
+   *
+   * @var \Drupal\Core\Entity\Query\QueryFactory
+   */
+  protected $entityQueryFactory;
+
+  /**
+   * Constructs a new action form.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface
+   *   The module handler service.
+   * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
+   *   The path alias manager.
+   * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
+   *   The action storage controller.
+   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query_factory
+   *   The entity query factory.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler, AliasManagerInterface $alias_manager, EntityStorageControllerInterface $storage_controller, QueryFactory $entity_query_factory) {
+    $this->moduleHandler = $module_handler;
+    $this->aliasManager = $alias_manager;
+    $this->storageController = $storage_controller;
+    $this->entityQueryFactory = $entity_query_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('module_handler'),
+      $container->get('path.alias_manager'),
+      $container->get('entity.manager')->getStorageController('shortcut_set'),
+      $container->get('entity.query')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, array &$form_state) {
+    $form = parent::form($form, $form_state);
+    $entity = $this->entity;
+
+    $form['title'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Name'),
+      '#default_value' => $entity->title->value,
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#required' => TRUE,
+      '#weight' => -10,
+    );
+
+    $form['link_path'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Path'),
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#field_prefix' => url(NULL, array('absolute' => TRUE)),
+      '#default_value' => (!$entity->isNew()) ? $entity->menu_link->entity->link_path : NULL,
+    );
+
+    $form['langcode'] = array(
+      '#title' => t('Language'),
+      '#type' => 'language_select',
+      '#default_value' => $entity->getUntranslated()->language()->id,
+      '#languages' => Language::STATE_ALL,
+    );
+
+    $form['shortcut_set'] = array(
+      '#type' => 'value',
+      '#value' => $entity->bundle(),
+    );
+
+    $form['menu_link'] = array(
+      '#type' => 'value',
+      '#value' => $entity->menu_link->target_id,
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate(array $form, array &$form_state) {
+    if (!shortcut_valid_link($form_state['values']['link_path'])) {
+      form_set_error('link_path', t('The link must correspond to a valid path on the site.'));
+    }
+    else {
+      // Normalize the path in case it is an alias.
+      $link_path = $this->aliasManager->getSystemPath($form_state['values']['link_path']);
+      if (empty($link_path)) {
+        $link_path = '<front>';
+      }
+
+      $result = $this->entityQueryFactory->get('menu_link')
+        ->condition('link_path', $link_path, '=')
+        ->execute();
+
+      if (!empty($result)) {
+        form_set_value($form['menu_link'], reset($result), $form_state);
+      }
+      else {
+        form_set_error('link_path', t('A menu link corresponding to this path was not found.'));
+      }
+    }
+
+    parent::validate($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, array &$form_state) {
+    $entity = $this->entity;
+    $entity->save();
+
+    if ($entity->isNew()) {
+      $shortcut_set = $this->storageController->load($entity->bundle());
+      $shortcut_set->addShortcut($entity)->save();
+
+      $message = t('The shortcut %link has been updated.', array('%link' => $entity->title->value));
+    }
+    else {
+      $message = t('Added a shortcut for %title.', array('%title' => $entity->title->value));
+    }
+    drupal_set_message($message);
+
+    $form_state['redirect'] = 'admin/config/user-interface/shortcut/manage/' . $entity->bundle();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete(array $form, array &$form_state) {
+    $form_state['redirect'] = array('admin/config/user-interface/shortcut/link/' . $this->entity->id() . '/delete');
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutInterface.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutInterface.php
new file mode 100644
index 0000000..ecc29fc
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutInterface.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\ShortcutInterface.
+ */
+
+namespace Drupal\shortcut;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+
+/**
+ * Provides an interface defining a shortcut entity.
+ */
+interface ShortcutInterface extends ContentEntityInterface {
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetInterface.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetInterface.php
index 5fa358a..a9b71df 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetInterface.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetInterface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\shortcut\Entity\ShortcutSetInterface.
+ * Contains \Drupal\shortcut\ShortcutSetInterface.
  */
 
 namespace Drupal\shortcut;
@@ -14,4 +14,36 @@
  */
 interface ShortcutSetInterface extends ConfigEntityInterface {
 
+  /**
+   * Adds a link to the end of a shortcut set, keeping within a prescribed
+   * limit.
+   *
+   * @param \Drupal\shortcut\ShortcutInterface
+   *   The shortcut that is being added.
+   *
+   * @return \Drupal\shortcut\ShortcutSetInterface
+   *   The shortcut set.
+   */
+  public function addShortcut(ShortcutInterface $shortcut);
+
+  /**
+   * Resets the link weights in a shortcut set to match their current order.
+   *
+   * This function can be used, for example, when a new shortcut link is added
+   * to the set. If the link is added to the end of the array and this function
+   * is called, it will force that link to display at the end of the list.
+   *
+   * @return \Drupal\shortcut\ShortcutSetInterface
+   *   The shortcut set.
+   */
+  public function resetLinkWeights();
+
+  /**
+   * Returns all the shortcuts from a shortcut set.
+   *
+   * @return \Drupal\shortcut\ShortcutInterface[]
+   *   An array of shortcut entities.
+   */
+  public function getShortcuts();
+
 }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetListController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetListController.php
index 6215a87..179992c 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetListController.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetListController.php
@@ -31,7 +31,7 @@ public function getOperations(EntityInterface $entity) {
     $uri = $entity->uri();
 
     if (isset($operations['edit'])) {
-      $operations['edit']['title'] = t('Edit menu');
+      $operations['edit']['title'] = t('Edit shortcut set');
       $operations['edit']['href'] = $uri['path'] . '/edit';
     }
 
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetStorageController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetStorageController.php
index 2f17850..c978827 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetStorageController.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetStorageController.php
@@ -7,8 +7,14 @@
 
 namespace Drupal\shortcut;
 
+use Drupal\Component\Uuid\UuidInterface;
+use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Config\Entity\ConfigStorageController;
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Entity\EntityManager;
 use Drupal\shortcut\ShortcutSetInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Defines a storage controller for shortcut entities.
@@ -16,15 +22,60 @@
 class ShortcutSetStorageController extends ConfigStorageController implements ShortcutSetStorageControllerInterface {
 
   /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a FieldStorageController 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\Component\Uuid\UuidInterface $uuid_service
+   *   The UUID service.
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *   The entity manager.
+   */
+  public function __construct($entity_type, array $entity_info, ConfigFactory $config_factory, StorageInterface $config_storage, QueryFactory $entity_query_factory, UuidInterface $uuid_service, EntityManager $entity_manager) {
+    parent::__construct($entity_type, $entity_info, $config_factory, $config_storage, $entity_query_factory, $uuid_service);
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * {@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('uuid'),
+      $container->get('entity.manager')
+    );
+  }
+
+  /**
    * Overrides \Drupal\config\ConfigStorageController::attachLoad().
    */
   protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
     parent::attachLoad($queried_entities, $revision_id);
 
     foreach ($queried_entities as $id => $entity) {
-      $links = menu_load_links('shortcut-' . $id);
-      foreach ($links as $menu_link) {
-        $entity->links[$menu_link->uuid()] = $menu_link;
+      $shortcuts = $this->entityManager->getStorageController('shortcut')->loadByProperties(array('shortcut_set' => $id));
+      foreach ($shortcuts as $shortcut) {
+        $entity->links[$shortcut->uuid()] = $shortcut;
       }
     }
   }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php
index bd2d194..b5cd685 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php
@@ -55,8 +55,8 @@ function testShortcutLinkAdd() {
     foreach ($test_cases as $test) {
       $title = $this->randomName();
       $form_data = array(
-        'shortcut_link[link_title]' => $title,
-        'shortcut_link[link_path]'  => $test['path'],
+        'title' => $title,
+        'link_path'  => $test['path'],
       );
       $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
       $this->assertResponse(200);
@@ -76,9 +76,10 @@ function testShortcutQuickLink() {
     \Drupal::config('system.theme')->set('admin', 'seven')->save();
     $this->container->get('config.factory')->get('node.settings')->set('use_admin_theme', '1')->save();
 
-    $link = reset($this->set->links);
+    $shortcuts = $this->set->getShortcuts();
+    $shortcut = reset($shortcuts);
 
-    $this->drupalGet($link->link_path);
+    $this->drupalGet($shortcut->menu_link->entity->link_path);
     $this->assertRaw(t('Remove from %title shortcuts', array('%title' => $this->set->label())), '"Add to shortcuts" link properly switched to "Remove from shortcuts".');
   }
 
@@ -91,10 +92,11 @@ function testShortcutLinkRename() {
     // Attempt to rename shortcut link.
     $new_link_name = $this->randomName();
 
-    $link = reset($set->links);
-    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $link->mlid, array('shortcut_link[link_title]' => $new_link_name, 'shortcut_link[link_path]' => $link->link_path), t('Save'));
+    $shortcuts = $set->getShortcuts();
+    $shortcut = reset($shortcuts);
+    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title' => $new_link_name, 'link_path' => $shortcut->menu_link->entity->link_path), t('Save'));
     $saved_set = shortcut_set_load($set->id());
-    $titles = $this->getShortcutInformation($saved_set, 'link_title');
+    $titles = $this->getShortcutInformation($saved_set, 'title');
     $this->assertTrue(in_array($new_link_name, $titles), 'Shortcut renamed: ' . $new_link_name);
     $this->assertLink($new_link_name, 0, 'Renamed shortcut link appears on the page.');
   }
@@ -108,8 +110,9 @@ function testShortcutLinkChangePath() {
     // Tests changing a shortcut path.
     $new_link_path = 'admin/config';
 
-    $link = reset($set->links);
-    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $link->mlid, array('shortcut_link[link_title]' => $link->link_title, 'shortcut_link[link_path]' => $new_link_path), t('Save'));
+    $shortcuts = $set->getShortcuts();
+    $shortcut = reset($shortcuts);
+    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title' => $shortcut->title->value, 'link_path' => $new_link_path), t('Save'));
     $saved_set = shortcut_set_load($set->id());
     $paths = $this->getShortcutInformation($saved_set, 'link_path');
     $this->assertTrue(in_array($new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path);
@@ -122,16 +125,15 @@ function testShortcutLinkChangePath() {
   function testShortcutLinkDelete() {
     $set = $this->set;
 
-    $link = reset($set->links);
-    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $link->mlid . '/delete', array(), 'Delete');
+    $shortcuts = $set->getShortcuts();
+    $shortcut = reset($shortcuts);
+    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id() . '/delete', array(), 'Delete');
     $saved_set = shortcut_set_load($set->id());
-    $mlids = $this->getShortcutInformation($saved_set, 'mlid');
-    $this->assertFalse(in_array($link->mlid, $mlids), 'Successfully deleted a shortcut.');
+    $ids = $this->getShortcutInformation($saved_set, 'id');
+    $this->assertFalse(in_array($shortcut->id(), $ids), 'Successfully deleted a shortcut.');
 
-    // Delete all the remaining shortcut menu links.
-    foreach (array_filter($mlids) as $mlid) {
-      menu_link_delete($mlid);
-    }
+    // Delete all the remaining shortcut links.
+    entity_delete_multiple('shortcut', array_filter($ids));
 
     // Get the front page to check that no exceptions occur.
     $this->drupalGet('');
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutSetsTest.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutSetsTest.php
index 470227a..06493ec 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutSetsTest.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutSetsTest.php
@@ -89,23 +89,6 @@ function testShortcutSetSwitchNoSetName() {
   }
 
   /**
-   * Tests that save() correctly updates existing links.
-   */
-  function testShortcutSetSave() {
-    $set = $this->set;
-    $old_mlids = $this->getShortcutInformation($set, 'mlid');
-
-    $menu_link = $this->generateShortcutLink('admin', $this->randomName());
-    $menu_link->save();
-    $set->links[$menu_link->uuid()] = $menu_link;
-    $set->save();
-    $saved_set = shortcut_set_load($set->id());
-
-    $new_mlids = $this->getShortcutInformation($saved_set, 'mlid');
-    $this->assertTrue(count(array_intersect($old_mlids, $new_mlids)) == count($old_mlids), 'Shortcut::save() did not inadvertently change existing mlids.');
-  }
-
-  /**
    * Tests renaming a shortcut set.
    */
   function testShortcutSetRename() {
@@ -113,7 +96,7 @@ function testShortcutSetRename() {
 
     $new_label = $this->randomName();
     $this->drupalGet('admin/config/user-interface/shortcut');
-    $this->clickLink(t('Edit menu'));
+    $this->clickLink(t('Edit shortcut set'));
     $this->drupalPostForm(NULL, array('label' => $new_label), t('Save'));
     $set = shortcut_set_load($set->id());
     $this->assertTrue($set->label() == $new_label, 'Shortcut set has been successfully renamed.');
@@ -168,7 +151,7 @@ function testShortcutSetDeleteDefault() {
    */
   function testShortcutSetCreateWithSetName() {
     $random_name = $this->randomName();
-    $new_set = $this->generateShortcutSet($random_name, $random_name, TRUE);
+    $new_set = $this->generateShortcutSet($random_name, $random_name);
     $sets = entity_load_multiple('shortcut_set');
     $this->assertTrue(isset($sets[$random_name]), 'Successfully created a shortcut set with a defined set name.');
     $this->drupalGet('user/' . $this->admin_user->id() . '/shortcuts');
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php
index 06088ef..1347441 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\shortcut\Tests;
 
+use Drupal\shortcut\ShortcutSetInterface;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -38,6 +39,8 @@
 
   /**
    * Site-wide default shortcut set.
+   *
+   * @var \Drupal\shortcut\ShortcutSetInterface
    */
   protected $set;
 
@@ -51,21 +54,34 @@ function setUp() {
 
       // Populate the default shortcut set.
       $shortcut_set = shortcut_set_load('default');
-      $menu_link = entity_create('menu_link', array(
-        'link_path' => 'node/add',
-        'link_title' => t('Add content'),
-        'weight' => -20,
-      ));
-      $menu_link->save();
-      $shortcut_set->links[$menu_link->uuid()] = $menu_link;
-      $menu_item = entity_create('menu_link', array(
-        'link_path' => 'admin/content',
-        'link_title' => t('All content'),
-        'weight' => -19,
-      ));
-      $menu_item->save();
-      $shortcut_set->links[$menu_item->uuid()] = $menu_item;
-      $shortcut_set->save();
+
+      $result = \Drupal::entityQuery('menu_link')
+        ->condition('link_path', 'node/add', '=')
+        ->execute();
+
+      if (!empty($result)) {
+        $shortcut = entity_create('shortcut', array(
+          'set' => $shortcut_set->id(),
+          'title' => t('Add content'),
+          'weight' => -20,
+          'menu_link' => reset($result),
+        ));
+        $shortcut->save();
+      }
+
+      $result = \Drupal::entityQuery('menu_link')
+        ->condition('link_path', 'admin/content', '=')
+        ->execute();
+
+      if (!empty($result)) {
+        $shortcut = entity_create('shortcut', array(
+          'set' => $shortcut_set->id(),
+          'title' => t('All content'),
+          'weight' => -19,
+          'menu_link' => reset($result),
+        ));
+        $shortcut->save();
+      }
     }
 
     // Create users.
@@ -84,53 +100,40 @@ function setUp() {
   /**
    * Creates a generic shortcut set.
    */
-  function generateShortcutSet($label = '', $id = NULL, $default_links = TRUE) {
+  function generateShortcutSet($label = '', $id = NULL) {
     $set = entity_create('shortcut_set', array(
       'id' => isset($id) ? $id : strtolower($this->randomName()),
       'label' => empty($label) ? $this->randomString() : $label,
     ));
-    if ($default_links) {
-      $menu_link = $this->generateShortcutLink('node/add');
-      $set->links[$menu_link->uuid()] = $menu_link;
-      $menu_link = $this->generateShortcutLink('admin/content');
-      $set->links[$menu_link->uuid()] = $menu_link;
-    }
     $set->save();
     return $set;
   }
 
   /**
-   * Creates a generic shortcut link.
-   */
-  function generateShortcutLink($path, $title = '') {
-    $link = entity_create('menu_link', array(
-      'link_path' => $path,
-      'link_title' => !empty($title) ? $title : $this->randomName(),
-    ));
-    $link->save();
-
-    return $link;
-  }
-
-  /**
    * Extracts information from shortcut set links.
    *
-   * @param object $set
+   * @param \Drupal\shortcut\ShortcutSetInterface $set
    *   The shortcut set object to extract information from.
    * @param string $key
    *   The array key indicating what information to extract from each link:
+   *    - 'title': Extract link titles.
    *    - 'link_path': Extract link paths.
-   *    - 'link_title': Extract link titles.
-   *    - 'mlid': Extract the menu link item ID numbers.
+   *    - 'id': Extract the shortcut ID.
    *
    * @return array
    *   Array of the requested information from each link.
    */
-  function getShortcutInformation($set, $key) {
+  function getShortcutInformation(ShortcutSetInterface $set, $key) {
     $info = array();
-    foreach ($set->links as $uuid => $link) {
-      $info[] = $link->{$key};
+    foreach ($set->getShortcuts() as $shortcut) {
+      if ($key == 'link_path') {
+        $info[] = $shortcut->menu_link->entity->link_path;
+      }
+      else {
+        $info[] = $shortcut->{$key}->value;
+      }
     }
     return $info;
   }
+
 }
diff --git a/core/modules/shortcut/shortcut.admin.inc b/core/modules/shortcut/shortcut.admin.inc
index 2d6b49d..a865c99 100644
--- a/core/modules/shortcut/shortcut.admin.inc
+++ b/core/modules/shortcut/shortcut.admin.inc
@@ -5,9 +5,6 @@
  * Administrative page callbacks for the shortcut module.
  */
 
-use Symfony\Component\HttpFoundation\RedirectResponse;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-
 /**
  * Form callback: builds the form for switching shortcut sets.
  *
@@ -173,182 +170,3 @@ function shortcut_set_switch_submit($form, &$form_state) {
   // Assign the shortcut set to the provided user account.
   shortcut_set_assign_user($set, $account);
 }
-
-/**
- * Form callback: builds the form for adding a new shortcut link.
- *
- * @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 $shortcut_set Drupal\shortcut\Entity\Shortcut
- *   An object representing the shortcut set to which the link will be added.
- *
- * @return
- *   An array representing the form definition.
- *
- * @ingroup forms
- * @see shortcut_link_edit_validate()
- * @see shortcut_link_add_submit()
- *
- * @deprecated Use \Drupal\shortcut\Form\ShortcutForm::add()
- */
-function shortcut_link_add($form, &$form_state, $shortcut_set) {
-  drupal_set_title(t('Add new shortcut'));
-  $form['shortcut_set'] = array(
-    '#type' => 'value',
-    '#value' => $shortcut_set,
-  );
-  $form += _shortcut_link_form_elements();
-  return $form;
-}
-
-/**
- * Form callback: builds the form for editing a shortcut link.
- *
- * @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 $shortcut_link
- *   An array representing the link that is being edited.
- *
- * @return
- *   An array representing the form definition.
- *
- * @ingroup forms
- * @see shortcut_link_edit_validate()
- * @see shortcut_link_edit_submit()
- *
- * @deprecated Use \Drupal\shortcut\Form\ShortcutForm::edit()
- */
-function shortcut_link_edit($form, &$form_state, $shortcut_link) {
-  drupal_set_title(t('Editing @shortcut', array('@shortcut' => $shortcut_link['link_title'])));
-  $form['original_shortcut_link'] = array(
-    '#type' => 'value',
-    '#value' => $shortcut_link,
-  );
-  $form += _shortcut_link_form_elements($shortcut_link);
-  return $form;
-}
-
-/**
- * Helper function for building a form for adding or editing shortcut links.
- *
- * @param $shortcut_link
- *   (optional) An array representing the shortcut link that will be edited. If
- *   not provided, a new link will be created.
- *
- * @return
- *   An array of form elements.
- */
-function _shortcut_link_form_elements($shortcut_link = NULL) {
-  if (!isset($shortcut_link)) {
-    $shortcut_link = entity_create('menu_link', array(
-      'link_title' => '',
-      'link_path' => ''
-    ));
-  }
-  else {
-    $shortcut_link['link_path'] = ($shortcut_link['link_path'] == '<front>') ? '' : \Drupal::service('path.alias_manager')->getPathAlias($shortcut_link['link_path']);
-  }
-
-  $form['shortcut_link']['#tree'] = TRUE;
-  $form['shortcut_link']['link_title'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Name'),
-    '#size' => 40,
-    '#maxlength' => 255,
-    '#default_value' => $shortcut_link['link_title'],
-    '#required' => TRUE,
-  );
-
-  $form['shortcut_link']['link_path'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Path'),
-    '#size' => 40,
-    '#maxlength' => 255,
-    '#field_prefix' => url(NULL, array('absolute' => TRUE)),
-    '#default_value' => $shortcut_link['link_path'],
-  );
-
-  $form['#validate'][] = 'shortcut_link_edit_validate';
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * Validation handler for the shortcut link add and edit forms.
- */
-function shortcut_link_edit_validate($form, &$form_state) {
-  if (!shortcut_valid_link($form_state['values']['shortcut_link']['link_path'])) {
-    form_set_error('shortcut_link][link_path', t('The link must correspond to a valid path on the site.'));
-  }
-}
-
-/**
- * Submit handler for shortcut_link_edit().
- */
-function shortcut_link_edit_submit($form, &$form_state) {
-  // Normalize the path in case it is an alias.
-  $shortcut_path = \Drupal::service('path.alias_manager')->getSystemPath($form_state['values']['shortcut_link']['link_path']);
-  if (empty($shortcut_path)) {
-    $shortcut_path = '<front>';
-  }
-  $form_state['values']['shortcut_link']['link_path'] = $shortcut_path;
-
-  $shortcut_link = $form_state['values']['original_shortcut_link'];
-  foreach ($form_state['values']['shortcut_link'] as $key => $value) {
-   $shortcut_link[$key] = $value;
-  }
-
-  menu_link_save($shortcut_link);
-  $set_name = str_replace('shortcut-', '' , $shortcut_link['menu_name']);
-  $form_state['redirect'] = 'admin/config/user-interface/shortcut/manage/' . $set_name;
-  drupal_set_message(t('The shortcut %link has been updated.', array('%link' => $shortcut_link['link_title'])));
-}
-
-/**
- * Submit handler for shortcut_link_add().
- */
-function shortcut_link_add_submit($form, &$form_state) {
-  // Add the shortcut link to the set.
-  $shortcut_set = $form_state['values']['shortcut_set'];
-  $shortcut_link = $form_state['values']['shortcut_link'];
-  $shortcut_link['menu_name'] = $shortcut_set->id();
-  shortcut_admin_add_link($shortcut_link, $shortcut_set);
-  $shortcut_set->save();
-  $form_state['redirect'] = 'admin/config/user-interface/shortcut/manage/' . $shortcut_set->id();
-  drupal_set_message(t('Added a shortcut for %title.', array('%title' => $shortcut_link['link_title'])));
-}
-
-/**
- * Adds a link to the end of a shortcut set, keeping within a prescribed limit.
- *
- * @param $link
- *   An array representing a shortcut link.
- * @param $shortcut_set Drupal\shortcut\Entity\Shortcut
- *   An object representing the shortcut set which the link will be added to.
- *   The links in the shortcut set will be re-weighted so that the new link is
- *   at the end, and some existing links may be disabled (if the $limit
- *   parameter is provided).
- */
-function shortcut_admin_add_link($shortcut_link, &$shortcut_set) {
-  // Normalize the path in case it is an alias.
-  $shortcut_link['link_path'] = \Drupal::service('path.alias_manager')->getSystemPath($shortcut_link['link_path']);
-  if (empty($shortcut_link['link_path'])) {
-    $shortcut_link['link_path'] = '<front>';
-  }
-  $menu_link = entity_create('menu_link', $shortcut_link);
-  $menu_link->save();
-
-  // Add the link to the end of the list.
-  $shortcut_set->links[$menu_link->uuid()] = $menu_link;
-  shortcut_set_reset_link_weights($shortcut_set);
-}
diff --git a/core/modules/shortcut/shortcut.install b/core/modules/shortcut/shortcut.install
index 420d8c2..f811981 100644
--- a/core/modules/shortcut/shortcut.install
+++ b/core/modules/shortcut/shortcut.install
@@ -22,6 +22,98 @@ function shortcut_uninstall() {
  * Implements hook_schema().
  */
 function shortcut_schema() {
+  $schema['shortcut'] = array(
+    'description' => 'Stores shortcut items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique shortcut ID.',
+      ),
+      'uuid' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+        'description' => 'Unique Key: Universally unique identifier for this shortcut.',
+      ),
+      'shortcut_set' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The bundle of the shortcut.',
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode of the original variant of this shortcut.',
+      ),
+      'weight' => array(
+        'description' => 'Weight among shortcuts in the same shortcut set.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'menu_link' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => 'The {menu_links}.mlid of the associated menu link.',
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+    'foreign keys' => array(
+      'menu_links' => array(
+        'table' => 'menu_links',
+        'columns' => array('menu_link' => 'mlid'),
+      ),
+    ),
+  );
+
+  $schema['shortcut_property_data'] = array(
+    'description' => 'Stores shortcut properties.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {shortcut}.id of the shortcut.',
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode of this variant of this shortcut.',
+      ),
+      'default_langcode' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
+      ),
+      'title' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => FALSE,
+        'description' => 'The title of the shortcut.',
+      ),
+    ),
+    'foreign keys' => array(
+      'shortcut' => array(
+        'table' => 'shortcut',
+        'columns' => array('id' => 'id'),
+      ),
+    ),
+    'primary key' => array('id', 'langcode'),
+  );
+
   $schema['shortcut_set_users'] = array(
     'description' => 'Maps users to shortcut sets.',
     'fields' => array(
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 8ca0472..ace8e5d 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -5,8 +5,8 @@
  * Allows users to manage customizable lists of shortcut links.
  */
 
-use Drupal\shortcut\Entity\Shortcut;
 use Symfony\Component\HttpFoundation\Request;
+use Drupal\shortcut\ShortcutSetInterface;
 
 /**
  * Implements hook_help().
@@ -113,7 +113,7 @@ function shortcut_menu() {
   );
   $items['admin/config/user-interface/shortcut/manage/%shortcut_set/add-link'] = array(
     'title' => 'Add shortcut',
-    'route_name' => 'shortcut.link_edit',
+    'route_name' => 'shortcut.link_add',
     'type' => MENU_LOCAL_ACTION,
   );
   $items['admin/config/user-interface/shortcut/link/%menu_link'] = array(
@@ -146,15 +146,15 @@ function shortcut_admin_paths() {
 /**
  * Access callback for editing a shortcut set.
  *
- * @param $shortcut_set Drupal\shortcut\Entity\Shortcut
+ * @param Drupal\shortcut\ShortcutSetInterface $shortcut_set
  *   (optional) The shortcut set to be edited. If not set, the current user's
  *   shortcut set will be used.
  *
- * @return
+ * @return bool
  *   TRUE if the current user has access to edit the shortcut set, FALSE
  *   otherwise.
  */
-function shortcut_set_edit_access($shortcut_set = NULL) {
+function shortcut_set_edit_access(ShortcutSetInterface $shortcut_set = NULL) {
   $account = \Drupal::currentUser();
   // Sufficiently-privileged users can edit their currently displayed shortcut
   // set, but not other sets. Shortcut administrators can edit any set.
@@ -175,7 +175,7 @@ function shortcut_set_edit_access($shortcut_set = NULL) {
  *   permissions will be checked for switching the logged-in user's own
  *   shortcut set.
  *
- * @return
+ * @return bool
  *   TRUE if the current user has access to switch the shortcut set of the
  *   provided account, FALSE otherwise.
  */
@@ -202,41 +202,26 @@ function shortcut_set_switch_access($account = NULL) {
 }
 
 /**
- * Access callback for editing a link in a shortcut set.
- */
-function shortcut_link_access($menu_link) {
-  // The link must belong to a shortcut set that the current user has access
-  // to edit.
-  $set_name = str_replace('shortcut-', '', $menu_link['menu_name']);
-  if ($shortcut_set = shortcut_set_load($set_name)) {
-    return shortcut_set_edit_access($shortcut_set);
-  }
-  return FALSE;
-}
-
-/**
  * Implements hook_menu_link_delete().
  */
 function shortcut_menu_link_delete($menu_link) {
-  // If the deleted menu link was in a shortcut set, remove it.
-  if (strpos($menu_link->menu_name, 'shortcut-') === 0) {
-    $shortcut = entity_load('shortcut_set', str_replace('shortcut-', '', $menu_link->menu_name));
-    unset($shortcut->links[$menu_link->uuid]);
-    $shortcut->save();
+  // Delete all shortcuts that point to the deleted menu link.
+  $shortcut_controller = \Drupal::entityManager()->getStorageController('shortcut');
+  if ($shortcuts = $shortcut_controller->loadByProperties(array('menu_link' => $menu_link->id()))) {
+    $shortcut_controller->delete($shortcuts);
   }
 }
 
 /**
  * Loads the data for a shortcut set.
  *
- * @param $id
+ * @param string $id
  *   The machine-name of the shortcut set to load.
  *
- * @return object
+ * @return \Drupal\shortcut\ShortcutSetInterface|null
  *   If the shortcut set exists, an object containing the following properties:
  *   - 'id': The internal name of the shortcut set.
  *   - 'label': The title of the shortcut set.
- *   - 'links': An array of links associated with this shortcut set.
  *   If the shortcut set does not exist, the function returns NULL.
  */
 function shortcut_set_load($id) {
@@ -244,24 +229,6 @@ function shortcut_set_load($id) {
 }
 
 /**
- * Resets the link weights in a shortcut set to match their current order.
- *
- * This function can be used, for example, when a new shortcut link is added to
- * the set. If the link is added to the end of the array and this function is
- * called, it will force that link to display at the end of the list.
- *
- * @param object $shortcut_set
- *   An object representing a shortcut set. The link weights of the passed-in
- *   object will be reset as described above.
- */
-function shortcut_set_reset_link_weights(&$shortcut_set) {
-  $weight = -50;
-  foreach ($shortcut_set->links as $menu_link) {
-    $menu_link->weight = ++$weight;
-  }
-}
-
-/**
  * Assigns a user to a particular shortcut set.
  *
  * @param $shortcut_set Drupal\shortcut\Entity\Shortcut
@@ -410,10 +377,11 @@ function shortcut_valid_link($path) {
 /**
  * Returns an array of shortcut links, suitable for rendering.
  *
- * @param $shortcut_set Drupal\shortcut\Entity\Shortcut
+ * @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
  *   (optional) An object representing the set whose links will be displayed.
  *   If not provided, the user's current set will be displayed.
- * @return
+ *
+ * @return \Drupal\shortcut\ShortcutInterface[]
  *   An array of shortcut links, in the format returned by the menu system.
  *
  * @see menu_tree()
@@ -457,9 +425,11 @@ function shortcut_preprocess_page(&$variables) {
     $shortcut_set = shortcut_current_displayed_set();
 
     // Check if $link is already a shortcut and set $link_mode accordingly.
-    foreach ($shortcut_set->links as $shortcut) {
-      if ($link == $shortcut['link_path']) {
-        $mlid = $shortcut['mlid'];
+    $shortcuts = \Drupal::entityManager()->getStorageController('shortcut')->loadByProperties(array('shortcut_set' => $shortcut_set->id()));
+    foreach ($shortcuts as $shortcut) {
+      if ($link == $shortcut->menu_link->entity['link_path']) {
+        $mlid = $shortcut->menu_link->target_id;
+        $shortcut_id = $shortcut->id();
         break;
       }
     }
@@ -473,7 +443,7 @@ function shortcut_preprocess_page(&$variables) {
     else {
       $query['mlid'] = $mlid;
       $link_text = shortcut_set_switch_access() ? t('Remove from %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->label())) : t('Remove from shortcuts');
-      $link_path = 'admin/config/user-interface/shortcut/link/' . $mlid . '/delete';
+      $link_path = 'admin/config/user-interface/shortcut/link/' . $shortcut_id . '/delete';
     }
 
     if (theme_get_setting('shortcut_module_link')) {
diff --git a/core/modules/shortcut/shortcut.routing.yml b/core/modules/shortcut/shortcut.routing.yml
index 15549fd..800e997 100644
--- a/core/modules/shortcut/shortcut.routing.yml
+++ b/core/modules/shortcut/shortcut.routing.yml
@@ -1,10 +1,3 @@
-shortcut.link_delete:
-  path: '/admin/config/user-interface/shortcut/link/{menu_link}/delete'
-  defaults:
-    _form: 'Drupal\shortcut\Form\LinkDelete'
-  requirements:
-    _access_shortcut_link: 'TRUE'
-
 shortcut.set_delete:
   path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/delete'
   defaults:
@@ -51,18 +44,23 @@ shortcut.set_customize:
 shortcut.link_add:
   path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/add-link'
   defaults:
-    _content: '\Drupal\shortcut\Form\ShortcutForm::add'
-    _title: 'Add Shortcut'
+    _content: '\Drupal\shortcut\Controller\ShortcutController::addForm'
   requirements:
-    _access_shortcut_set_edit: 'TRUE'
+    _entity_create_access: 'shortcut:{shortcut_set}'
 
 shortcut.link_edit:
-  path: '/admin/config/user-interface/shortcut/link/{menu_link}'
+  path: '/admin/config/user-interface/shortcut/link/{shortcut}'
+  defaults:
+    _entity_form: 'shortcut.default'
+  requirements:
+    _entity_access: 'shortcut.update'
+
+shortcut.link_delete:
+  path: '/admin/config/user-interface/shortcut/link/{shortcut}/delete'
   defaults:
-    _content: '\Drupal\shortcut\Form\ShortcutForm::edit'
-    _title: 'Add Shortcut'
+    _entity_form: 'shortcut.delete'
   requirements:
-    _access_shortcut_link: 'TRUE'
+    _entity_access: 'shortcut.delete'
 
 shortcut.overview:
   path: 'user/{user}/shortcuts'
diff --git a/core/modules/shortcut/shortcut.services.yml b/core/modules/shortcut/shortcut.services.yml
deleted file mode 100644
index 439a0e0..0000000
--- a/core/modules/shortcut/shortcut.services.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-services:
-  access_check.shortcut.link:
-    class: Drupal\shortcut\Access\LinkAccessCheck
-    tags:
-      - { name: access_check }
-
-  access_check.shortcut.shortcut_set_edit:
-    class: Drupal\shortcut\Access\ShortcutSetEditAccessCheck
-    tags:
-      - { name: access_check }
-
-  access_check.shortcut.shortcut_set_switch:
-    class: Drupal\shortcut\Access\ShortcutSetSwitchAccessCheck
-    tags:
-      - { name: access_check }
diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install
index acf8871..dad5604 100644
--- a/core/profiles/standard/standard.install
+++ b/core/profiles/standard/standard.install
@@ -64,23 +64,34 @@ function standard_install() {
 
   // Populate the default shortcut set.
   $shortcut_set = shortcut_set_load('default');
-  $menu_link = entity_create('menu_link', array(
-    'link_path' => 'node/add',
-    'link_title' => t('Add content'),
-    'weight' => -20,
-  ));
-  $menu_link->save();
-  $shortcut_set->links[$menu_link->uuid()] = $menu_link;
 
-  $menu_link = entity_create('menu_link', array(
-    'link_path' => 'admin/content',
-    'link_title' => t('All content'),
-    'weight' => -19,
-  ));
-  $menu_link->save();
-  $shortcut_set->links[$menu_link->uuid()] = $menu_link;
+  $result = \Drupal::entityQuery('menu_link')
+    ->condition('link_path', 'node/add', '=')
+    ->execute();
+
+  if (!empty($result)) {
+    $shortcut = entity_create('shortcut', array(
+      'set' => $shortcut_set->id(),
+      'title' => t('Add content'),
+      'weight' => -20,
+      'menu_link' => reset($result),
+    ));
+    $shortcut->save();
+  }
+
+  $result = \Drupal::entityQuery('menu_link')
+    ->condition('link_path', 'admin/content', '=')
+    ->execute();
 
-  $shortcut_set->save();
+  if (!empty($result)) {
+    $shortcut = entity_create('shortcut', array(
+      'set' => $shortcut_set->id(),
+      'title' => t('All content'),
+      'weight' => -19,
+      'menu_link' => reset($result),
+    ));
+    $shortcut->save();
+  }
 
   // Enable the admin theme.
   theme_enable(array('seven'));
