diff --git a/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php b/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php
index 89a7e76..76ab043 100644
--- a/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php
+++ b/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php
@@ -50,9 +50,8 @@ class Tour extends ConfigEntityBase {
    * The paths in which this tip can be displayed.
    *
    * @var array
-   *   An array of paths.
    */
-  protected $paths;
+  protected $paths = array();
 
   /**
    * Holds the collection of tips that are attached to this tour.
@@ -66,7 +65,7 @@ class Tour extends ConfigEntityBase {
    *
    * @var array
    */
-  protected $tips;
+  protected $tips = array();
 
   /**
    * Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::__construct();
diff --git a/core/modules/tour_ui/lib/Drupal/tour_ui/Routing/TourUIController.php b/core/modules/tour_ui/lib/Drupal/tour_ui/Routing/TourUIController.php
new file mode 100644
index 0000000..16a8d3c
--- /dev/null
+++ b/core/modules/tour_ui/lib/Drupal/tour_ui/Routing/TourUIController.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\tour_ui\Routing\TourUIController.
+ */
+
+namespace Drupal\tour_ui\Routing;
+
+use Drupal\Core\Entity\EntityManager;
+use Drupal\tour\Plugin\Core\Entity\Tour;
+
+/**
+ * @todo.
+ */
+class TourUIController {
+
+  /**
+   * @todo.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $manager;
+
+  /**
+   * @todo.
+   */
+  public function __construct(EntityManager $manager) {
+    $this->manager = $manager;
+  }
+
+  /**
+   * @todo.
+   */
+  public function listing() {
+    return $this->manager->getListController('tour')->render();
+  }
+
+  public function add() {
+    $tour = $this->manager->getStorageController('tour')->create(array());
+    return entity_get_form($tour);
+  }
+
+  public function edit(Tour $tour) {
+    return entity_get_form($tour);
+  }
+
+}
diff --git a/core/modules/tour_ui/lib/Drupal/tour_ui/Tests/TourUITest.php b/core/modules/tour_ui/lib/Drupal/tour_ui/Tests/TourUITest.php
new file mode 100644
index 0000000..54f6305
--- /dev/null
+++ b/core/modules/tour_ui/lib/Drupal/tour_ui/Tests/TourUITest.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\tour_ui\Tests\TourUITest.
+ */
+
+namespace Drupal\tour_ui\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests the Tour UI..
+ */
+class TourUITest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('tour_ui', 'tour_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Tour UI',
+      'description' => 'Tests the Tour UI.',
+      'group' => 'Tour',
+    );
+  }
+
+  /**
+   * Tests the listing and editing of a tour.
+   */
+  public function testUI() {
+    $this->drupalLogin($this->root_user);
+    $this->listTest();
+    $this->editTest();
+  }
+
+  /**
+   * Tests the listing of a tour.
+   */
+  protected function listTest() {
+    // Assert that all three test tours are shown.
+    $this->drupalGet('admin/config/user-interface/tour');
+    $elements = $this->xpath('//table/tbody/tr');
+    $this->assertEqual(count($elements), 3);
+
+    // The first column contains the title.
+    $elements = $this->xpath('//table/tbody/tr[contains(@class, :class)]/td[1]', array(':class' => 'tour-test-en'));
+    $this->assertIdentical((string) $elements[0], t('Tour test english'));
+    // The second column contains the paths.
+    $elements = $this->xpath('//table/tbody/tr[contains(@class, :class)]/td/a[contains(@href, :href)]', array(':class' => 'tour-test-en', ':href' => 'tour-test-1'));
+    $this->assertIdentical((string) $elements[0], '/tour-test-1');
+    // The third column contains the number of tips.
+    $elements = $this->xpath('//table/tbody/tr[contains(@class, :class)]/td[3]', array(':class' => 'tour-test-en'));
+    $this->assertIdentical((string) $elements[0], '2');
+  }
+
+  /**
+   * Tests the editing of a tour.
+   */
+  protected function editTest() {
+    // Create a new tour. Ensure that it comes before the test tours.
+    $edit = array(
+      'label' => 'a' . $this->randomString(),
+      'id' => strtolower($this->randomName()),
+    );
+    $this->drupalPost('admin/config/user-interface/tour/add', $edit, t('Save'));
+    $this->assertRaw(t('The %tour tour has been created.', array('%tour' => $edit['label'])));
+    $elements = $this->xpath('//table/tbody/tr');
+    $this->assertEqual(count($elements), 4);
+
+    // Edit and re-save an existing tour.
+    $this->clickLink(t('Edit'));
+    $this->assertTitle(t('Edit tour | @site-name', array('@site-name' => config('system.site')->get('name'))));
+    $this->drupalPost(NULL, array(), t('Save'));
+    $this->assertRaw(t('Updated the %tour tour', array('%tour' => $edit['label'])));
+
+    // Attempt to create a duplicate tour.
+    $this->drupalPost('admin/config/user-interface/tour/add', $edit, t('Save'));
+    $this->assertRaw(t('The machine-readable name is already in use. It must be unique.'));
+
+    // Delete a tour.
+    $this->drupalGet('admin/config/user-interface/tour/manage/' . $edit['id'] . '/edit');
+    $this->drupalPost(NULL, NULL, t('Delete'));
+    $this->assertRaw(t('Are you sure you want to delete the %tour tour?', array('%tour' => $edit['label'])));
+    $this->clickLink(t('Cancel'));
+    $this->clickLink(t('Delete'));
+    $this->drupalPost(NULL, NULL, t('Delete'));
+    $elements = $this->xpath('//table/tbody/tr');
+    $this->assertEqual(count($elements), 3);
+    $this->assertRaw(t('Deleted the %tour tour.', array('%tour' => $edit['label'])));
+  }
+
+}
diff --git a/core/modules/tour_ui/lib/Drupal/tour_ui/TourDeleteForm.php b/core/modules/tour_ui/lib/Drupal/tour_ui/TourDeleteForm.php
new file mode 100644
index 0000000..a1778e1
--- /dev/null
+++ b/core/modules/tour_ui/lib/Drupal/tour_ui/TourDeleteForm.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\tour_ui\TourDeleteForm.
+ */
+
+namespace Drupal\tour_ui;
+
+use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Builds the form to delete a tour.
+ */
+class TourDeleteForm implements FormInterface {
+
+  /**
+   * Stores the tour entity being deleted.
+   *
+   * @var \Drupal\Core\Entity\EntityInterface
+   */
+  protected $entity;
+
+  /**
+   * Creates a new instance of this form.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $tour
+   *   The tour being acted upon.
+   *
+   * @return array
+   *   The built form array.
+   */
+  public function getForm(EntityInterface $tour) {
+    $this->entity = $tour;
+    return drupal_get_form($this);
+  }
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::getFormID().
+   */
+  public function getFormID() {
+    return 'tour_ui_confirm_delete';
+  }
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::buildForm().
+   */
+  public function buildForm(array $form, array &$form_state) {
+    return confirm_form($form,
+      t('Are you sure you want to delete the %tour tour?', array('%tour' => $this->entity->label())),
+      'admin/config/user-interface/tour',
+      t('This action cannot be undone.'),
+      t('Delete'),
+      t('Cancel')
+    );
+  }
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::validateForm().
+   */
+  public function validateForm(array &$form, array &$form_state) {
+  }
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::submitForm().
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    $this->entity->delete();
+    $form_state['redirect'] = 'admin/config/user-interface/tour';
+    drupal_set_message(t('Deleted the %tour tour.', array('%tour' => $this->entity->label())));
+  }
+
+}
diff --git a/core/modules/tour_ui/lib/Drupal/tour_ui/TourFormController.php b/core/modules/tour_ui/lib/Drupal/tour_ui/TourFormController.php
new file mode 100644
index 0000000..dff55b2
--- /dev/null
+++ b/core/modules/tour_ui/lib/Drupal/tour_ui/TourFormController.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\tour_ui\TourFormController.
+ */
+
+namespace Drupal\tour_ui;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormController;
+
+/**
+ * Form controller for the tour entity edit forms.
+ */
+class TourFormController extends EntityFormController {
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityFormController::form().
+   */
+  public function form(array $form, array &$form_state, EntityInterface $entity) {
+    $form = parent::form($form, $form_state, $entity);
+
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Tour name'),
+      '#required' => TRUE,
+      '#default_value' => $entity->label(),
+    );
+    $form['id'] = array(
+      '#type' => 'machine_name',
+      '#machine_name' => array(
+        'exists' => '_tour_load',
+        'replace_pattern' => '[^a-z0-9-]+',
+        'replace' => '-',
+      ),
+      '#default_value' => $entity->id(),
+      '#disabled' => !$entity->isNew(),
+    );
+    $form['langcode'] = array(
+      '#type' => 'language_select',
+      '#title' => t('Language'),
+      '#languages' => LANGUAGE_ALL,
+      '#default_value' => $entity->langcode,
+    );
+    $form['paths'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Paths'),
+      '#default_value' => implode("\n", $entity->getPaths()),
+      '#rows' => 5,
+      '#description' => t('Provide a list of paths that this tour will be displayed on.'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityFormController::submit().
+   */
+  public function submit(array $form, array &$form_state) {
+    // Filter out invalid characters and convert to an array.
+    preg_replace('/(\r\n?|\n)/', '\n', $form_state['values']['paths']);
+    $form_state['values']['paths'] = explode("\n", $form_state['values']['paths']);
+    $form_state['values']['paths'] = array_map('trim', $form_state['values']['paths']);
+    $form_state['values']['paths'] = array_filter($form_state['values']['paths']);
+
+    $entity = parent::submit($form, $form_state);
+    $is_new = $entity->isNew();
+    $entity->save();
+
+    if ($is_new) {
+      drupal_set_message(t('The %tour tour has been created.', array('%tour' => $entity->label())));
+    }
+    else {
+      drupal_set_message(t('Updated the %tour tour.', array('%tour' => $entity->label())));
+    }
+
+    $form_state['redirect'] = 'admin/config/user-interface/tour';
+    return $entity;
+  }
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityFormController::delete().
+   */
+  public function delete(array $form, array &$form_state) {
+    $entity = $this->getEntity($form_state);
+    $form_state['redirect'] = 'admin/config/user-interface/tour/manage/' . $entity->id() . '/delete';
+  }
+
+}
diff --git a/core/modules/tour_ui/lib/Drupal/tour_ui/TourListController.php b/core/modules/tour_ui/lib/Drupal/tour_ui/TourListController.php
new file mode 100644
index 0000000..b0a0842
--- /dev/null
+++ b/core/modules/tour_ui/lib/Drupal/tour_ui/TourListController.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * Contains \Drupal\tour_ui\TourListController.
+ */
+
+namespace Drupal\tour_ui;
+
+use Drupal\Core\Config\Entity\ConfigEntityListController;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides a listing of tours.
+ */
+class TourListController extends ConfigEntityListController {
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityListController::buildHeader().
+   */
+  public function buildHeader() {
+    $row['label'] = t('Label');
+    $row['paths'] = t('Paths');
+    $row['tips'] = t('Number of tips');
+    $row['operations'] = t('Operations');
+    return $row;
+  }
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityListController::buildRow().
+   */
+  public function buildRow(EntityInterface $entity) {
+    $data['label'] = check_plain($entity->label());
+    // Include the paths this tour is used on.
+    $data['paths'] = implode('<br>', array_map(function($path) {
+      // If the path contains no wildcards, output it as a link.
+      if (strpos($path, '*') === FALSE) {
+        return l('/' . $path, $path);
+      }
+      return check_plain('/' . $path);
+    }, $entity->getPaths()));
+    // Count the number of tips.
+    $data['tips'] = count($entity->getTipList());
+    $data['operations']['data'] = $this->buildOperations($entity);
+    // Wrap the whole row so that the entity ID is used as a class.
+    return array(
+      'data' => $data,
+      'class' => array(
+        $entity->id(),
+      ),
+    );
+  }
+
+  /**
+   * Overrides \Drupal\Core\Entity\EntityListController::getOperations().
+   */
+  public function getOperations(EntityInterface $entity) {
+    $operations = parent::getOperations($entity);
+    // Tours do not support being enabled or disabled.
+    unset($operations['enable'], $operations['disable']);
+    return $operations;
+  }
+
+}
diff --git a/core/modules/tour_ui/lib/Drupal/tour_ui/TourUiBundle.php b/core/modules/tour_ui/lib/Drupal/tour_ui/TourUiBundle.php
new file mode 100644
index 0000000..719757b
--- /dev/null
+++ b/core/modules/tour_ui/lib/Drupal/tour_ui/TourUiBundle.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\tour_ui\TourUiBundle.
+ */
+
+namespace Drupal\tour_ui;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * Tour UI dependency injection container.
+ */
+class TourUiBundle extends Bundle {
+
+  /**
+   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    $container->register('tour_ui.controller', 'Drupal\tour_ui\Routing\TourUIController')
+      ->addArgument(new Reference('plugin.manager.entity'));
+  }
+
+}
diff --git a/core/modules/tour_ui/tour_ui.info b/core/modules/tour_ui/tour_ui.info
new file mode 100644
index 0000000..e52f5ed
--- /dev/null
+++ b/core/modules/tour_ui/tour_ui.info
@@ -0,0 +1,6 @@
+name = Tour UI
+description = Provides a UI to manage guided tours.
+package = Core
+version = VERSION
+core = 8.x
+dependencies[] = tour
diff --git a/core/modules/tour_ui/tour_ui.module b/core/modules/tour_ui/tour_ui.module
new file mode 100644
index 0000000..a941dc9
--- /dev/null
+++ b/core/modules/tour_ui/tour_ui.module
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @file
+ * @todo.
+ */
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Implements hook_entity_info().
+ */
+function tour_ui_entity_info(&$entity_info) {
+  $entity_info['tour']['list_controller_class'] = 'Drupal\tour_ui\TourListController';
+  $entity_info['tour']['form_controller_class']['default'] = 'Drupal\tour_ui\TourFormController';
+  $entity_info['tour']['uri_callback'] = 'tour_ui_uri';
+}
+
+/**
+ * Implements hook_menu().
+ */
+function tour_ui_menu() {
+  $items['admin/config/user-interface/tour'] = array(
+    'title' => 'Tour',
+    'description' => 'Add and modify guided tours.',
+    'page callback' => 'NOT_USED',
+    'access callback' => TRUE,
+  );
+  $items['admin/config/user-interface/tour/add'] = array(
+    'title' => 'Add tour',
+    'type' => MENU_LOCAL_ACTION,
+    'page callback' => 'NOT_USED',
+    'access callback' => TRUE,
+  );
+  $items['admin/config/user-interface/tour/manage/%/edit'] = array(
+    'title' => 'Edit tour',
+    'page callback' => 'NOT_USED',
+    'access callback' => TRUE,
+  );
+  $items['admin/config/user-interface/tour/manage/%/delete'] = array(
+    'title' => 'Delete tour',
+    'page callback' => 'NOT_USED',
+    'access callback' => TRUE,
+  );
+  return $items;
+}
+
+/**
+ * Callback for the entity URI.
+ */
+function tour_ui_uri(EntityInterface $entity) {
+  return array(
+    'path' => 'admin/config/user-interface/tour/manage/' . $entity->id,
+    'options' => array(
+      'entity_type' => $entity->entityType(),
+      'entity' => $entity,
+    ),
+  );
+}
+
+/**
+ * Callback for machine_name exists.
+ */
+function _tour_load($tour) {
+  return entity_load('tour', $tour);
+}
diff --git a/core/modules/tour_ui/tour_ui.routing.yml b/core/modules/tour_ui/tour_ui.routing.yml
new file mode 100644
index 0000000..e1d9bb5
--- /dev/null
+++ b/core/modules/tour_ui/tour_ui.routing.yml
@@ -0,0 +1,24 @@
+tour_ui_listing:
+  pattern: '/admin/config/user-interface/tour'
+  defaults:
+    _controller: 'tour_ui.controller:listing'
+  requirements:
+    _access: 'TRUE'
+tour_ui_add:
+  pattern: '/admin/config/user-interface/tour/add'
+  defaults:
+    _controller: 'tour_ui.controller:add'
+  requirements:
+    _access: 'TRUE'
+tour_ui_edit:
+  pattern: '/admin/config/user-interface/tour/manage/{tour}/edit'
+  defaults:
+    _controller: 'tour_ui.controller:edit'
+  requirements:
+    _access: 'TRUE'
+tour_ui_delete:
+  pattern: '/admin/config/user-interface/tour/manage/{tour}/delete'
+  defaults:
+    _controller: '\Drupal\tour_ui\TourDeleteForm::getForm'
+  requirements:
+    _access: 'TRUE'
