From 1c0f9b15f0e836e97f85382aff7d6294cb2b6031 Mon Sep 17 00:00:00 2001
From: damiankloip <damiankloip@1037976.no-reply.drupal.org>
Date: Tue, 11 Sep 2012 11:55:31 -0400
Subject: [PATCH] Issue #1760284 by damiankloip, tim.plunkett: Copy the new
 core Config Entity List API into Views.

---
 css/views-admin.theme.css                     |   4 +
 includes/admin.inc                            |  40 +-
 .../views/Plugin/ctools/export_ui/ViewsUI.php | 477 ------------------
 lib/Drupal/views/Tests/ModuleTest.php         |  12 -
 lib/Drupal/views/Tests/UI/SettingsTest.php    |   4 +-
 lib/Drupal/views/Tests/UpgradeTestCase.php    | 123 -----
 lib/Drupal/views/Tests/Wizard/BasicTest.php   |   3 +-
 lib/Drupal/views/View.php                     |  49 ++
 lib/Drupal/views/ViewListController.php       | 174 +++++++
 lib/Drupal/views/ViewStorage.php              |   2 +
 lib/Drupal/views/ViewStorageController.php    |   2 -
 views.install                                 |  18 -
 views.module                                  |  71 +--
 views_ui.info                                 |   1 +
 views_ui.module                               | 105 +---
 .../EntityListControllerBase.php              | 177 +++++++
 .../EntityListControllerInterface.php         |  84 +++
 .../Tests/ConfigEntityListingTest.php         |  70 +++
 .../ConfigTestListController.php              |  55 ++
 .../views_ui_listing_test.info                |   5 +
 .../views_ui_listing_test.module              |   1 +
 views_ui_listing/views_ui_listing.info        |   4 +
 views_ui_listing/views_ui_listing.module      |  85 ++++
 23 files changed, 741 insertions(+), 825 deletions(-)
 delete mode 100644 lib/Drupal/views/Plugin/ctools/export_ui/ViewsUI.php
 create mode 100644 lib/Drupal/views/ViewListController.php
 create mode 100644 views_ui_listing/lib/Drupal/views_ui_listing/EntityListControllerBase.php
 create mode 100644 views_ui_listing/lib/Drupal/views_ui_listing/EntityListControllerInterface.php
 create mode 100644 views_ui_listing/lib/Drupal/views_ui_listing/Tests/ConfigEntityListingTest.php
 create mode 100644 views_ui_listing/tests/views_ui_listing_test/lib/Drupal/views_ui_listing_test/ConfigTestListController.php
 create mode 100644 views_ui_listing/tests/views_ui_listing_test/views_ui_listing_test.info
 create mode 100644 views_ui_listing/tests/views_ui_listing_test/views_ui_listing_test.module
 create mode 100644 views_ui_listing/views_ui_listing.info
 create mode 100644 views_ui_listing/views_ui_listing.module

diff --git a/css/views-admin.theme.css b/css/views-admin.theme.css
index 49dd8057e75c..be85d6a60ec5 100644
--- a/css/views-admin.theme.css
+++ b/css/views-admin.theme.css
@@ -235,6 +235,10 @@ input.form-radio {
   font-weight: bold;
 }
 
+tr.views-ui-list-disabled td {
+  color: #999;
+}
+
 /* @end */
 
 /* @group Messages */
diff --git a/includes/admin.inc b/includes/admin.inc
index 380e1886553c..a76855700250 100644
--- a/includes/admin.inc
+++ b/includes/admin.inc
@@ -771,36 +771,22 @@ function views_ui_taxonomy_autocomplete_validate($element, &$form_state) {
 }
 
 /**
- * Theme function; returns basic administrative information about a view.
- *
- * TODO: template + preprocess
+ * Implements hook_preprocess_HOOK() for theme_views_ui_view_info().
  */
-function theme_views_ui_view_info($variables) {
-  $view = $variables['view'];
-  $title = $view->getHumanName();
+function template_preprocess_views_ui_view_info(&$variables) {
+  $variables['title'] = $variables['view']->getHumanName();
 
-  $displays = _views_ui_get_displays_list($view);
-  $displays = empty($displays) ? t('None') : format_plural(count($displays), 'Display', 'Displays') . ': ' . '<em>' . implode(', ', $displays) . '</em>';
-
-  switch ($view->type) {
-    case t('Default'):
-    default:
-      $type = t('In code');
-      break;
-
-    case t('Normal'):
-      $type = t('In database');
-      break;
-
-    case t('Overridden'):
-      $type = t('Database overriding code');
-  }
+  $displays = $variables['view']->getDisplaysList();
+  $variables['displays'] = empty($displays) ? t('None') : format_plural(count($displays), 'Display', 'Displays') . ': ' . '<em>' . implode(', ', $displays) . '</em>';
+}
 
+/**
+ * Returns basic administrative information about a view.
+ */
+function theme_views_ui_view_info($variables) {
   $output = '';
-  $output .= '<div class="views-ui-view-title">' . $title . "</div>\n";
-  $output .= '<div class="views-ui-view-displays">' . $displays . "</div>\n";
-  $output .= '<div class="views-ui-view-storage">' . $type . "</div>\n";
-  $output .= '<div class="views-ui-view-base">' . t('Type') . ': ' . $variables['base'] . "</div>\n";
+  $output .= '<div class="views-ui-view-title">' . $variables['title'] . "</div>\n";
+  $output .= '<div class="views-ui-view-displays">' . $variables['displays'] . "</div>\n";
   return $output;
 }
 
@@ -946,7 +932,7 @@ function views_ui_edit_form($form, &$form_state, $view, $display_id = NULL) {
     $view->fixMissingRelationships();
   }
 
-  $form['#attached']['js'][] = ctools_attach_js('collapsible-div');
+  $form['#attached']['js'][] = drupal_get_path('module', 'ctools') . '/js/collapsible-div.js';
 
   $form['#tree'] = TRUE;
   // @todo When more functionality is added to this form, cloning here may be
diff --git a/lib/Drupal/views/Plugin/ctools/export_ui/ViewsUI.php b/lib/Drupal/views/Plugin/ctools/export_ui/ViewsUI.php
deleted file mode 100644
index 23cdd7de4845..000000000000
--- a/lib/Drupal/views/Plugin/ctools/export_ui/ViewsUI.php
+++ /dev/null
@@ -1,477 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains the CTools Export UI integration code.
- *
- * Note that this is only a partial integration.
- */
-
-namespace Drupal\views\Plugin\ctools\export_ui;
-
-use Drupal\ctools\Plugin\ctools\export_ui\ExportUIPluginBase;
-use Drupal\Core\Annotation\Plugin;
-use Drupal\Core\Annotation\Translation;
-
-/**
- * CTools Export UI class handler for Views UI.
- *
- * @Plugin(
- *   id = "views_ui",
- *   name = "views_ui",
- *   schema = "views_view",
- *   module = "views",
- *   access = "administer views",
- *   menu = {
- *     "menu_item" = "views",
- *     "menu_title" = "Views",
- *     "menu_description" = "Manage customized lists of content.",
- *   },
- *   title_singular = @Translation("view"),
- *   title_singular_proper = @Translation("View"),
- *   title_plural = @Translation("views"),
- *   title_plural_proper = @Translation("Views"),
- *   strings = {
- *     "confirmation" = {
- *       "revert" = {
- *         "information" = @Translation("This action will permanently remove any customizations made to this view."),
- *         "success" = @Translation("The view has been reverted.")
- *       },
- *       "delete" = {
- *         "information" = @Translation("This action will permanently remove the view from your database."),
- *         "success" = @Translation("The view has been deleted.")
- *       },
- *     },
- *   }
- * )
- */
-class ViewsUI extends ExportUIPluginBase {
-
-  function init($plugin) {
-    // We modify the plugin info here so that we take the defaults and
-    // twiddle, rather than completely override them.
-
-    // Reset the edit path to match what we're really using.
-    $plugin['menu']['items']['edit']['path'] = 'view/%ctools_export_ui/edit';
-    $plugin['menu']['items']['clone']['path'] = 'view/%ctools_export_ui/clone';
-    $plugin['menu']['items']['clone']['type'] = MENU_VISIBLE_IN_BREADCRUMB;
-    $plugin['menu']['items']['enable']['path'] = 'view/%ctools_export_ui/enable';
-    $plugin['menu']['items']['disable']['path'] = 'view/%ctools_export_ui/disable';
-    $plugin['menu']['items']['delete']['path'] = 'view/%ctools_export_ui/delete';
-    $plugin['menu']['items']['delete']['type'] = MENU_VISIBLE_IN_BREADCRUMB;
-    $plugin['menu']['items']['revert']['path'] = 'view/%ctools_export_ui/revert';
-    $plugin['menu']['items']['revert']['type'] = MENU_VISIBLE_IN_BREADCRUMB;
-
-    $prefix_count = count(explode('/', $plugin['menu']['menu_prefix']));
-    $plugin['menu']['items']['add-template'] = array(
-      'path' => 'template/%/add',
-      'title' => 'Add from template',
-      'page callback' => 'ctools_export_ui_switcher_page',
-      'page arguments' => array($plugin['name'], 'add_template', $prefix_count + 2),
-      'load arguments' => array($plugin['name']),
-      'access callback' => 'ctools_export_ui_task_access',
-      'access arguments' => array($plugin['name'], 'add_template', $prefix_count + 2),
-      'type' => MENU_CALLBACK,
-    );
-
-    return parent::init($plugin);
-  }
-
-  function hook_menu(&$items) {
-    // We are using our own 'edit' still, rather than having edit on this
-    // object (maybe in the future) so unset the edit callbacks:
-
-    // Store this so we can put them back as sometimes they're needed
-    // again laster:
-    $stored_items = $this->plugin['menu']['items'];
-    // We leave these to make sure the operations still exist in the plugin so
-    // that the path finder.
-    unset($this->plugin['menu']['items']['edit']);
-    unset($this->plugin['menu']['items']['add']);
-    unset($this->plugin['menu']['items']['import']);
-    unset($this->plugin['menu']['items']['edit callback']);
-
-    parent::hook_menu($items);
-
-    $this->plugin['menu']['items'] = $stored_items;
-  }
-
-  function load_item($item_name) {
-    return views_ui_cache_load($item_name);
-  }
-
-  function list_form(&$form, &$form_state) {
-    $row_class = 'container-inline';
-    if (!config('views.settings')->get('ui.show.listing_filters')) {
-      $row_class .= " element-invisible";
-    }
-
-    views_include('admin');
-
-    parent::list_form($form, $form_state);
-
-    // ctools only has two rows. We want four.
-    // That's why we create our own structure.
-    $form['bottom row']['submit']['#attributes']['class'][] = 'js-hide';
-    $form['first row'] = array(
-      '#prefix' => '<div class="' . $row_class . ' ctools-export-ui-row ctools-export-ui-first-row clearfix">',
-      '#suffix' => '</div>',
-      'search' => $form['top row']['search'],
-      'submit' => $form['bottom row']['submit'],
-      'reset' => $form['bottom row']['reset'],
-    );
-    $form['second row'] = array(
-      '#prefix' => '<div class="' . $row_class . ' ctools-export-ui-row ctools-export-ui-second-row clearfix">',
-      '#suffix' => '</div>',
-      'storage' => $form['top row']['storage'],
-      'disabled' => $form['top row']['disabled'],
-    );
-    $form['third row'] = array(
-      '#prefix' => '<div class="' . $row_class . ' ctools-export-ui-row ctools-export-ui-third-row clearfix element-hidden">',
-      '#suffix' => '</div>',
-      'order' => $form['bottom row']['order'],
-      'sort' => $form['bottom row']['sort'],
-    );
-    unset($form['top row']);
-    unset($form['bottom row']);
-
-    // Modify the look and contents of existing form elements.
-    $form['second row']['storage']['#title'] = '';
-    $form['second row']['storage']['#options'] = array(
-      'all' => t('All storage'),
-      t('Normal') => t('In database'),
-      t('Default') => t('In code'),
-      t('Overridden') => t('Database overriding code'),
-    );
-    $form['second row']['disabled']['#title'] = '';
-    $form['second row']['disabled']['#options']['all'] = t('All status');
-    $form['third row']['sort']['#title'] = '';
-
-    // And finally, add our own.
-    $this->bases = array();
-    foreach (views_fetch_base_tables() as $table => $info) {
-      $this->bases[$table] = $info['title'];
-    }
-
-    $form['second row']['base'] = array(
-      '#type' => 'select',
-      '#options' => array_merge(array('all' => t('All types')), $this->bases),
-      '#default_value' => 'all',
-      '#weight' => -1,
-    );
-
-    $tags = array();
-    if (isset($form_state['object']->items)) {
-      foreach ($form_state['object']->items as $name => $view) {
-        if (!empty($view->tag)) {
-          $view_tags = drupal_explode_tags($view->tag);
-          foreach ($view_tags as $tag) {
-            $tags[$tag] = $tag;
-          }
-        }
-      }
-    }
-    asort($tags);
-
-    $form['second row']['tag'] = array(
-      '#type' => 'select',
-      '#title' => t('Filter'),
-      '#options' => array_merge(array('all' => t('All tags')), array('none' => t('No tags')), $tags),
-      '#default_value' => 'all',
-      '#weight' => -9,
-    );
-
-    $displays = array();
-    foreach (views_get_plugin_definitions('display') as $id => $info) {
-      if (!empty($info['admin'])) {
-        $displays[$id] = $info['admin'];
-      }
-    }
-    asort($displays);
-
-    $form['second row']['display'] = array(
-      '#type' => 'select',
-      '#options' => array_merge(array('all' => t('All displays')), $displays),
-      '#default_value' => 'all',
-      '#weight' => -1,
-    );
-  }
-
-  function list_filter($form_state, $view) {
-    // Don't filter by tags if all is set up.
-    if ($form_state['values']['tag'] != 'all') {
-      // If none is selected check whether the view has a tag.
-      if ($form_state['values']['tag'] == 'none') {
-        return !empty($view->tag);
-      }
-      else {
-        // Check whether the tag can be found in the views tag.
-        return strpos($view->tag, $form_state['values']['tag']) === FALSE;
-      }
-    }
-    if ($form_state['values']['base'] != 'all' && $form_state['values']['base'] != $view->base_table) {
-      return TRUE;
-    }
-
-    return parent::list_filter($form_state, $view);
-  }
-
-  function list_sort_options() {
-    return array(
-      'disabled' => t('Enabled, name'),
-      'name' => t('Name'),
-      'path' => t('Path'),
-      'tag' => t('Tag'),
-      'storage' => t('Storage'),
-    );
-  }
-
-  function list_build_row($view, &$form_state, $operations) {
-    if (!empty($view->human_name)) {
-      $title = $view->human_name;
-    }
-    else {
-      $title = $view->getTitle();
-      if (empty($title)) {
-        $title = $view->name;
-      }
-    }
-
-    $paths = _views_ui_get_paths($view);
-    $paths = implode(", ", $paths);
-
-    $base = !empty($this->bases[$view->base_table]) ? $this->bases[$view->base_table] : t('Broken');
-
-    $info = theme('views_ui_view_info', array('view' => $view, 'base' => $base));
-
-    // Reorder the operations so that enable is the default action for a templatic views
-    if (!empty($operations['enable'])) {
-      $operations = array('enable' => $operations['enable']) + $operations;
-    }
-
-    // Set up sorting
-    switch ($form_state['values']['order']) {
-      case 'disabled':
-        $this->sorts[$view->name] = strtolower(empty($view->disabled) . $title);
-        break;
-      case 'name':
-        $this->sorts[$view->name] = strtolower($title);
-        break;
-      case 'path':
-        $this->sorts[$view->name] = strtolower($paths);
-        break;
-      case 'tag':
-        $this->sorts[$view->name] = strtolower($view->tag);
-        break;
-      case 'storage':
-        $this->sorts[$view->name] = strtolower($view->type . $title);
-        break;
-    }
-
-    $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline'))));
-
-    $this->rows[$view->name] = array(
-      'data' => array(
-        array('data' => $info, 'class' => array('views-ui-name')),
-        array('data' => check_plain($view->description), 'class' => array('views-ui-description')),
-        array('data' => check_plain($view->tag), 'class' => array('views-ui-tag')),
-        array('data' => $paths, 'class' => array('views-ui-path')),
-        array('data' => $ops, 'class' => array('views-ui-operations')),
-      ),
-      'title' => t('Machine name: ') . check_plain($view->name),
-      'class' => array(!empty($view->disabled) ? 'ctools-export-ui-disabled' : 'ctools-export-ui-enabled'),
-    );
-  }
-
-  function list_render(&$form_state) {
-    views_include('admin');
-    views_ui_add_admin_css();
-    drupal_add_library('system', 'jquery.bbq');
-    views_add_js('views-list');
-
-    $this->active = $form_state['values']['order'];
-    $this->order = $form_state['values']['sort'];
-
-    $query = tablesort_get_query_parameters();
-
-    $header = array(
-      $this->tablesort_link(t('View name'), 'name', 'views-ui-name'),
-      array('data' => t('Description'), 'class' => array('views-ui-description')),
-      $this->tablesort_link(t('Tag'), 'tag', 'views-ui-tag'),
-      $this->tablesort_link(t('Path'), 'path', 'views-ui-path'),
-      array('data' => t('Operations'), 'class' => array('views-ui-operations')),
-    );
-
-    $table = array(
-      'header' => $header,
-      'rows' => $this->rows,
-      'empty' => t('No views match the search criteria.'),
-      'attributes' => array('id' => 'ctools-export-ui-list-items'),
-    );
-    return theme('table', $table);
-  }
-
-  function tablesort_link($label, $field, $class) {
-    $title = t('sort by @s', array('@s' => $label));
-    $initial = 'asc';
-
-    if ($this->active == $field) {
-      $initial = ($this->order == 'asc') ? 'desc' : 'asc';
-      $label .= theme('tablesort_indicator', array('style' => $initial));
-    }
-
-    $query['order'] = $field;
-    $query['sort'] = $initial;
-    $link_options = array(
-      'html' => TRUE,
-      'attributes' => array('title' => $title),
-      'query' => $query,
-    );
-    $link = l($label, current_path(), $link_options);
-    if ($this->active == $field) {
-      $class .= ' active';
-    }
-
-    return array('data' => $link, 'class' => $class);
-  }
-
-  function clone_page($js, $input, $item, $step = NULL) {
-    drupal_set_title($this->get_page_title('clone', $item));
-
-    $name = $item->{$this->plugin['export']['key']};
-
-    $form_state = array(
-      'plugin' => $this->plugin,
-      'object' => &$this,
-      'ajax' => $js,
-      'item' => $item,
-      'op' => 'add',
-      'form type' => 'clone',
-      'original name' => $name,
-      'rerender' => TRUE,
-      'no_redirect' => TRUE,
-      'step' => $step,
-      // Store these in case additional args are needed.
-      'function args' => func_get_args(),
-    );
-
-    $output = drupal_build_form('views_ui_clone_form', $form_state);
-    if (!empty($form_state['executed'])) {
-      $item->name = $form_state['values']['name'];
-      $item->human_name = $form_state['values']['human_name'];
-      $item->vid = NULL;
-      views_ui_cache_set($item);
-
-      drupal_goto(ctools_export_ui_plugin_menu_path($this->plugin, 'edit', $item->name));
-    }
-
-    return $output;
-  }
-
-  function add_template_page($js, $input, $name, $step = NULL) {
-    $templates = views_get_all_templates();
-
-    if (empty($templates[$name])) {
-      return MENU_NOT_FOUND;
-    }
-
-    $template = $templates[$name];
-
-    // The template description probably describes the template, not the
-    // view that will be created from it, but users aren't that likely to
-    // touch it.
-    if (!empty($template->description)) {
-      unset($template->description);
-    }
-
-    $template->is_template = TRUE;
-    $template->type = t('Default');
-
-    $output = $this->clone_page($js, $input, $template, $step);
-    drupal_set_title(t('Create view from template @template', array('@template' => $template->getHumanName())));
-    return $output;
-  }
-
-  function set_item_state($state, $js, $input, $item) {
-    if (!$state) {
-      $item->enable();
-    }
-    else {
-      $item->disable();
-    }
-    $item->save();
-    menu_router_rebuild();
-
-    if (!$js) {
-      drupal_goto(ctools_export_ui_plugin_base_path($this->plugin));
-    }
-    else {
-      return $this->list_page($js, $input);
-    }
-  }
-
-  function list_page($js, $input) {
-    // wrap output in a div for CSS
-    $output = parent::list_page($js, $input);
-    if (is_string($output)) {
-      $output = '<div id="views-ui-list-page">' . $output . '</div>';
-      return $output;
-    }
-  }
-}
-
-/**
- * Form callback to edit an exportable item using the wizard
- *
- * This simply loads the object defined in the plugin and hands it off.
- */
-function views_ui_clone_form($form, &$form_state) {
-  $counter = 1;
-
-  if (!isset($form_state['item'])) {
-    $view = views_get_view($form_state['original name']);
-  }
-  else {
-    $view = $form_state['item'];
-  }
-  do {
-    if (empty($form_state['item']->is_template)) {
-      $name = format_plural($counter, 'Clone of', 'Clone @count of') . ' ' . $view->getHumanName();
-    }
-    else {
-      $name = $view->getHumanName();
-      if ($counter > 1) {
-        $name .= ' ' . $counter;
-      }
-    }
-    $counter++;
-    $machine_name = preg_replace('/[^a-z0-9_]+/', '_', drupal_strtolower($name));
-  } while (ctools_export_crud_load($form_state['plugin']['schema'], $machine_name));
-
-  $form['human_name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('View name'),
-    '#default_value' => $name,
-    '#size' => 32,
-    '#maxlength' => 255,
-  );
-
-  $form['name'] = array(
-    '#title' => t('View name'),
-    '#type' => 'machine_name',
-    '#required' => TRUE,
-    '#maxlength' => 128,
-    '#size' => 128,
-    '#machine_name' => array(
-      'exists' => 'ctools_export_ui_edit_name_exists',
-      'source' => array('human_name'),
-    ),
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Continue'),
-  );
-
-  return $form;
-}
diff --git a/lib/Drupal/views/Tests/ModuleTest.php b/lib/Drupal/views/Tests/ModuleTest.php
index 80047abb9fda..ede122e01eda 100644
--- a/lib/Drupal/views/Tests/ModuleTest.php
+++ b/lib/Drupal/views/Tests/ModuleTest.php
@@ -91,18 +91,6 @@ public function test_views_trim_text() {
     }
   }
 
-  /**
-   * Tests the dynamic includes of templates via module feature.
-   */
-  function testModuleTemplates() {
-    $existing = array();
-    $type = array();
-    $theme = array();
-    $path = array();
-    $registry = views_theme($existing, $type, $theme, $path);
-    $this->assertTrue(isset($registry['views_view__frontpage']));
-  }
-
   /**
    * Tests the views_get_handler method.
    */
diff --git a/lib/Drupal/views/Tests/UI/SettingsTest.php b/lib/Drupal/views/Tests/UI/SettingsTest.php
index 32a6dd4eabe7..f590e84533ab 100644
--- a/lib/Drupal/views/Tests/UI/SettingsTest.php
+++ b/lib/Drupal/views/Tests/UI/SettingsTest.php
@@ -29,7 +29,8 @@ public static function getInfo() {
 
   /**
    * Tests the settings for the views listing page.
-   */
+   *
+   * @todo Decide if this functionality should be implemented.
   function testViewsListing() {
     $this->drupalLogin($this->adminUser);
 
@@ -51,6 +52,7 @@ function testViewsListing() {
 
     $this->assertNoFieldByXPath("//div[contains(@class, 'ctools-export-ui-row')][contains(@class, 'element-invisible')]");
   }
+   */
 
   /**
    * Tests the settings for the edit ui.
diff --git a/lib/Drupal/views/Tests/UpgradeTestCase.php b/lib/Drupal/views/Tests/UpgradeTestCase.php
index 8f11b09008d8..6aedec1e66e3 100644
--- a/lib/Drupal/views/Tests/UpgradeTestCase.php
+++ b/lib/Drupal/views/Tests/UpgradeTestCase.php
@@ -100,127 +100,4 @@ public function testMovedTo() {
     $this->assertEqual('views_test_data', $view->field['id']->table);
   }
 
-  /**
-   * Tests a import via ui.
-   *
-   * To ensure the general functionality, the recent comments view from drupal6
-   * is used.
-   */
-  public function testUpgradeImport() {
-    $admin_user = $this->drupalCreateUser(array('administer views', 'administer site configuration', 'use PHP for settings'));
-    $this->drupalLogin($admin_user);
-    $edit = array(
-      'view' => $this->viewUpgradeImport(),
-    );
-    $this->drupalPost('admin/structure/views/import', $edit, t('Import'));
-
-    $this->assertText('Recent comments');
-  }
-
-  /**
-   * @todo When we know if we are having import, we can either remove or
-   * update this.
-   */
-  protected function viewUpgradeImport() {
-    $import = '
-      $view = new Drupal\views\View(array(), "view");
-      $view->name = "comments_recent";
-      $view->description = "Contains a block and a page to list recent comments; the block will automatically link to the page, which displays the comment body as well as a link to the node.";
-      $view->tag = "default";
-      $view->base_table = "comments";
-      $view->human_name = "";
-      $view->core = 8;
-      $view->api_version = "3.0";
-      $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
-
-      /* Display: Defaults */
-      $handler = $view->newDisplay("default", "Defaults", "default");
-      $handler->display->display_options["title"] = "Recent comments";
-      $handler->display->display_options["use_more"] = TRUE;
-      $handler->display->display_options["access"]["type"] = "none";
-      $handler->display->display_options["cache"]["type"] = "none";
-      $handler->display->display_options["query"]["type"] = "views_query";
-      $handler->display->display_options["exposed_form"]["type"] = "basic";
-      $handler->display->display_options["pager"]["type"] = "some";
-      $handler->display->display_options["pager"]["options"]["items_per_page"] = 5;
-      $handler->display->display_options["style_plugin"] = "html_list";
-      $handler->display->display_options["row_plugin"] = "fields";
-      /* Relationship: Comment: Node */
-      $handler->display->display_options["relationships"]["nid"]["id"] = "nid";
-      $handler->display->display_options["relationships"]["nid"]["table"] = "comments";
-      $handler->display->display_options["relationships"]["nid"]["field"] = "nid";
-      /* Field: Comment: Title */
-      $handler->display->display_options["fields"]["subject"]["id"] = "subject";
-      $handler->display->display_options["fields"]["subject"]["table"] = "comments";
-      $handler->display->display_options["fields"]["subject"]["field"] = "subject";
-      $handler->display->display_options["fields"]["subject"]["label"] = "";
-      $handler->display->display_options["fields"]["subject"]["link_to_comment"] = 1;
-      /* Field: Comment: Post date */
-      $handler->display->display_options["fields"]["timestamp"]["id"] = "timestamp";
-      $handler->display->display_options["fields"]["timestamp"]["table"] = "comments";
-      $handler->display->display_options["fields"]["timestamp"]["field"] = "timestamp";
-      $handler->display->display_options["fields"]["timestamp"]["label"] = "";
-      $handler->display->display_options["fields"]["timestamp"]["date_format"] = "time ago";
-      /* Sort criterion: Comment: Post date */
-      $handler->display->display_options["sorts"]["timestamp"]["id"] = "timestamp";
-      $handler->display->display_options["sorts"]["timestamp"]["table"] = "comments";
-      $handler->display->display_options["sorts"]["timestamp"]["field"] = "timestamp";
-      $handler->display->display_options["sorts"]["timestamp"]["order"] = "DESC";
-      /* Filter: Node: Published or admin */
-      $handler->display->display_options["filters"]["status_extra"]["id"] = "status_extra";
-      $handler->display->display_options["filters"]["status_extra"]["table"] = "node";
-      $handler->display->display_options["filters"]["status_extra"]["field"] = "status_extra";
-      $handler->display->display_options["filters"]["status_extra"]["relationship"] = "nid";
-      $handler->display->display_options["filters"]["status_extra"]["group"] = 0;
-      $handler->display->display_options["filters"]["status_extra"]["expose"]["operator"] = FALSE;
-
-      /* Display: Page */
-      $handler = $view->newDisplay("page", "Page", "page");
-      $handler->display->display_options["defaults"]["items_per_page"] = FALSE;
-      $handler->display->display_options["defaults"]["style_plugin"] = FALSE;
-      $handler->display->display_options["style_plugin"] = "html_list";
-      $handler->display->display_options["defaults"]["style_options"] = FALSE;
-      $handler->display->display_options["defaults"]["row_plugin"] = FALSE;
-      $handler->display->display_options["row_plugin"] = "fields";
-      $handler->display->display_options["row_options"]["inline"] = array(
-        "title" => "title",
-        "timestamp" => "timestamp",
-      );
-      $handler->display->display_options["row_options"]["separator"] = "&nbsp;";
-      $handler->display->display_options["defaults"]["row_options"] = FALSE;
-      $handler->display->display_options["defaults"]["fields"] = FALSE;
-      /* Field: Node: Title */
-      $handler->display->display_options["fields"]["title"]["id"] = "title";
-      $handler->display->display_options["fields"]["title"]["table"] = "node";
-      $handler->display->display_options["fields"]["title"]["field"] = "title";
-      $handler->display->display_options["fields"]["title"]["relationship"] = "nid";
-      $handler->display->display_options["fields"]["title"]["label"] = "Reply to";
-      $handler->display->display_options["fields"]["title"]["link_to_node"] = 1;
-      /* Field: Comment: Post date */
-      $handler->display->display_options["fields"]["timestamp"]["id"] = "timestamp";
-      $handler->display->display_options["fields"]["timestamp"]["table"] = "comments";
-      $handler->display->display_options["fields"]["timestamp"]["field"] = "timestamp";
-      $handler->display->display_options["fields"]["timestamp"]["label"] = "";
-      $handler->display->display_options["fields"]["timestamp"]["date_format"] = "time ago";
-      /* Field: Comment: Title */
-      $handler->display->display_options["fields"]["subject"]["id"] = "subject";
-      $handler->display->display_options["fields"]["subject"]["table"] = "comments";
-      $handler->display->display_options["fields"]["subject"]["field"] = "subject";
-      $handler->display->display_options["fields"]["subject"]["label"] = "";
-      $handler->display->display_options["fields"]["subject"]["link_to_comment"] = 1;
-      /* Field: Comment: Body */
-      $handler->display->display_options["fields"]["comment"]["id"] = "comment";
-      $handler->display->display_options["fields"]["comment"]["table"] = "comments";
-      $handler->display->display_options["fields"]["comment"]["field"] = "comment";
-      $handler->display->display_options["fields"]["comment"]["label"] = "";
-      $handler->display->display_options["path"] = "comments/recent";
-
-      /* Display: Block */
-      $handler = $view->newDisplay("block", "Block", "block");
-      $handler->display->display_options["block_description"] = "Recent comments view"
-;';
-
-      return $import;
-  }
-
 }
diff --git a/lib/Drupal/views/Tests/Wizard/BasicTest.php b/lib/Drupal/views/Tests/Wizard/BasicTest.php
index 41ebdce85732..530f00c950a4 100644
--- a/lib/Drupal/views/Tests/Wizard/BasicTest.php
+++ b/lib/Drupal/views/Tests/Wizard/BasicTest.php
@@ -38,7 +38,8 @@ function testViewsWizardAndListing() {
     $this->assertText(t('Your view was saved. You may edit it from the list below.'));
     $this->assertText($view1['human_name']);
     $this->assertText($view1['description']);
-    foreach (array('delete', 'clone', 'edit') as $operation) {
+    // @todo For now, clone is being left to config.module to solve.
+    foreach (array('delete', 'edit') as $operation) {
       $this->assertLinkByHref(url('admin/structure/views/view/' . $view1['name'] . '/' . $operation));
     }
 
diff --git a/lib/Drupal/views/View.php b/lib/Drupal/views/View.php
index 4da2da810c88..fcba53e5ac82 100644
--- a/lib/Drupal/views/View.php
+++ b/lib/Drupal/views/View.php
@@ -2152,4 +2152,53 @@ public static function viewsHandlerTypes() {
     return $retval;
   }
 
+  /**
+   * Gets a list of paths assigned to the view.
+   *
+   * @return array
+   *   An array of paths for this view.
+   */
+  public function getPaths() {
+    $all_paths = array();
+    if (empty($this->display)) {
+      $all_paths[] = t('Edit this view to add a display.');
+    }
+    else {
+      $this->initDisplay();   // Make sure all the handlers are set up
+      foreach ($this->display as $display) {
+        if (!empty($display->handler) && $display->handler->hasPath()) {
+          $path = $display->handler->getOption('path');
+          if ($this->isEnabled() && strpos($path, '%') === FALSE) {
+            $all_paths[] = l('/' . $path, $path);
+          }
+          else {
+            $all_paths[] = check_plain('/' . $path);
+          }
+        }
+      }
+    }
+
+    return array_unique($all_paths);
+  }
+
+  /**
+   * Gets a list of displays included in the view.
+   *
+   * @return array
+   *   An array of display types that this view includes.
+   */
+  function getDisplaysList() {
+    $this->initDisplay();
+
+    $displays = array();
+    foreach ($this->display as $display) {
+      if (!empty($display->handler->definition['admin'])) {
+        $displays[$display->handler->definition['admin']] = TRUE;
+      }
+    }
+
+    ksort($displays);
+    return array_keys($displays);
+  }
+
 }
diff --git a/lib/Drupal/views/ViewListController.php b/lib/Drupal/views/ViewListController.php
new file mode 100644
index 000000000000..af0bc02d50a9
--- /dev/null
+++ b/lib/Drupal/views/ViewListController.php
@@ -0,0 +1,174 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\ViewListController.
+ */
+
+namespace Drupal\views;
+
+use Drupal\views_ui_listing\EntityListControllerBase;
+use Drupal\entity\EntityInterface;
+
+/**
+ * Provides a listing of Views.
+ */
+class ViewListController extends EntityListControllerBase {
+
+  public function __construct($entity_type, $entity_info = FALSE) {
+    parent::__construct($entity_type, $entity_info);
+  }
+
+  /**
+   * Overrides Drupal\views_ui_listing\EntityListControllerBase::hookMenu();
+   */
+  public function hookMenu() {
+    // Find the path and the number of path arguments.
+    $path = $this->entityInfo['list path'];
+    $path_count = count(explode('/', $path));
+
+    $items = parent::hookMenu();
+    // Override the access callback.
+    // @todo Probably won't need to specify user access.
+    $items[$path]['title'] = 'Views';
+    $items[$path]['description'] = 'Manage customized lists of content.';
+    $items[$path]['access callback'] = 'user_access';
+    $items[$path]['access arguments'] = array('administer views');
+
+    // Set up the base for AJAX callbacks.
+    $ajax_base = array(
+      'page callback' => 'views_ui_listing_ajax_callback',
+      'page arguments' => array($this, $path_count + 1, $path_count + 2),
+      'access callback' => 'user_access',
+      'access arguments' => array('administer views'),
+      'type' => MENU_CALLBACK,
+    );
+
+    // Add an enable link.
+    $items["$path/view/%views_ui_view/enable"] = array(
+      'title' => 'Enable a view',
+    ) + $ajax_base;
+    // Add a disable link.
+    $items["$path/view/%views_ui_view/disable"] = array(
+      'title' => 'Disable a view',
+    ) + $ajax_base;
+
+    return $items;
+  }
+
+  /**
+   * Overrides Drupal\views_ui_listing\EntityListControllerBase::getList();
+   */
+  public function getList() {
+    $list = parent::getList();
+    uasort($list, function ($a, $b) {
+      $a_enabled = $a->isEnabled();
+      $b_enabled = $b->isEnabled();
+      if ($a_enabled != $b_enabled) {
+        return $a_enabled < $b_enabled;
+      }
+      return $a->id() > $b->id();
+    });
+    return $list;
+  }
+
+  /**
+   * Overrides Drupal\views_ui_listing\EntityListControllerBase::getRowData();
+   */
+  public function getRowData(EntityInterface $view) {
+    $operations = $this->buildActionLinks($view);
+    $operations['#theme'] = 'links__ctools_dropbutton';
+    return array(
+      'data' => array(
+        'view_name' => theme('views_ui_view_info', array('view' => $view)),
+        'description' => $view->description,
+        'tag' => $view->tag,
+        'path' => implode(', ', $view->getPaths()),
+        'operations' => drupal_render($operations),
+      ),
+      'title' => t('Machine name: ') . $view->id(),
+      'class' => array($view->isEnabled() ? 'views-ui-list-enabled' : 'views-ui-list-disabled'),
+    );
+  }
+
+  /**
+   * Overrides Drupal\views_ui_listing\EntityListControllerBase::getRowData();
+   */
+  public function getHeaderData() {
+    return array(
+      'view_name' => array(
+        'data' => t('View name'),
+        'class' => array('views-ui-name'),
+      ),
+      'description' => array(
+        'data' => t('Description'),
+        'class' => array('views-ui-description'),
+      ),
+      'tag' => array(
+        'data' => t('Tag'),
+        'class' => array('views-ui-tag'),
+      ),
+      'path' => array(
+        'data' => t('Path'),
+        'class' => array('views-ui-path'),
+      ),
+      'actions' => array(
+        'data' => t('Operations'),
+        'class' => array('views-ui-operations'),
+      ),
+    );
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::defineActionLinks();
+   */
+  public function defineActionLinks(EntityInterface $view) {
+    $path = $this->entityInfo['list path'] . '/view/' . $view->id();
+    $enabled = $view->isEnabled();
+
+    if (!$enabled) {
+      $definition['enable'] = array(
+        'title' => t('Enable'),
+        'ajax' => TRUE,
+        'token' => TRUE,
+        'href' => "$path/enable",
+      );
+    }
+    $definition['edit'] = array(
+      'title' => t('Edit'),
+      'href' => "$path/edit",
+    );
+    if ($enabled) {
+      $definition['disable'] = array(
+        'title' => t('Disable'),
+        'ajax' => TRUE,
+        'token' => TRUE,
+        'href' => "$path/disable",
+      );
+    }
+    // This property doesn't exist yet.
+    if (!empty($view->overridden)) {
+      $definition['revert'] = array(
+        'title' => t('Revert'),
+        'href' => "$path/revert",
+      );
+    }
+    else {
+      $definition['delete'] = array(
+        'title' => t('Delete'),
+        'href' => "$path/delete",
+      );
+    }
+    return $definition;
+  }
+
+  /**
+   * Overrides Drupal\views_ui_listing\EntityListControllerBase::renderList();
+   */
+  public function renderList() {
+    $list = parent::renderList();
+    $list['#attached']['css'] = views_ui_get_admin_css();
+    return $list;
+  }
+
+}
diff --git a/lib/Drupal/views/ViewStorage.php b/lib/Drupal/views/ViewStorage.php
index 4524ca0247fd..f1f9accde69f 100644
--- a/lib/Drupal/views/ViewStorage.php
+++ b/lib/Drupal/views/ViewStorage.php
@@ -26,6 +26,7 @@ public function id() {
    */
   public function enable() {
     $this->disabled = FALSE;
+    $this->save();
   }
 
   /**
@@ -33,6 +34,7 @@ public function enable() {
    */
   public function disable() {
     $this->disabled = TRUE;
+    $this->save();
   }
 
   /**
diff --git a/lib/Drupal/views/ViewStorageController.php b/lib/Drupal/views/ViewStorageController.php
index 2bdb3a8ca4fe..8c9a61b29036 100644
--- a/lib/Drupal/views/ViewStorageController.php
+++ b/lib/Drupal/views/ViewStorageController.php
@@ -36,8 +36,6 @@ protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
         }
         $entity->{$this->uuidKey} = $this->uuidFactory->generate();
       }
-      // @todo This property is left in for CTools export UI.
-      $entity->type = t('Normal');
       $this->attachDisplays($entity);
     }
   }
diff --git a/views.install b/views.install
index 9f5355e8415b..4a8dc9a3c346 100644
--- a/views.install
+++ b/views.install
@@ -20,29 +20,12 @@ function views_install() {
 function views_schema() {
   $schema['views_view'] = array(
     'description' => 'Stores the general data for a view.',
-    'export' => array(
-      'identifier' => 'view',
-      'bulk export' => TRUE,
-      'primary key' => 'vid',
-      'admin_title' => 'human_name',
-      'admin_description' => 'description',
-      'object' => 'Drupal\views\View',
-      'load all callback' => 'views_get_all_views',
-      'load callback' => 'views_storage_load',
-      'save callback' => 'views_storage_save',
-      'status callback' => 'views_storage_status',
-      // CRUD callbacks
-      'create callback' => 'views_new_view',
-      'save callback' => 'views_save_view',
-      'delete callback' => 'views_delete_view',
-    ),
     'fields' => array(
       'vid' => array(
         'type' => 'serial',
         'unsigned' => TRUE,
         'not null' => TRUE,
         'description' => 'The view ID of the field, defined by the database.',
-        'no export' => TRUE,
       ),
       'name' => array(
         'type' => 'varchar',
@@ -95,7 +78,6 @@ function views_schema() {
         'not null' => TRUE,
         'default' => 0,
         'description' => 'The view this display is attached to.',
-        'no export' => TRUE,
       ),
       'id' => array(
         'type' => 'varchar',
diff --git a/views.module b/views.module
index 77ef90c46a05..d5c69cd8f23b 100644
--- a/views.module
+++ b/views.module
@@ -89,6 +89,8 @@ function views_entity_info() {
       'label' => t('View'),
       'entity class' => 'Drupal\views\View',
       'controller class' => 'Drupal\views\ViewStorageController',
+      'list controller class' => 'Drupal\views\ViewListController',
+      'list path' => 'admin/structure/views',
       'form controller class' => array(
         'default' => 'Drupal\node\NodeFormController',
       ),
@@ -246,12 +248,6 @@ function views_theme($existing, $type, $theme, $path) {
     'variables' => array('more_url' => NULL, 'link_text' => 'more', 'view' => NULL),
   );
 
-  // Add theme suggestions which are part of modules.
-  foreach (views_get_module_apis() as $info) {
-    if (isset($info['template path'])) {
-      $hooks += _views_find_module_templates($hooks, $info['template path']);
-    }
-  }
   return $hooks;
 }
 
@@ -1185,26 +1181,10 @@ function views_module_include($api, $reset = FALSE) {
   }
 
   // @todo Replace with http://drupal.org/node/1760284.
-  ctools_include('plugins');
+  module_load_include('inc', 'ctools', 'includes/plugins');
   return ctools_plugin_api_include('views', $api, views_api_minimum_version(), views_api_version());
 }
 
-/**
- * Get a list of modules that support the current views API.
- */
-function views_get_module_apis($api = 'views', $reset = FALSE) {
-  if ($reset) {
-    $cache = &drupal_static('ctools_plugin_api_info');
-    if (isset($cache['views']['views'])) {
-      unset($cache['views']['views']);
-    }
-  }
-
-  // @todo Replace with http://drupal.org/node/1760284.
-  ctools_include('plugins');
-  return ctools_plugin_api_info('views', $api, views_api_minimum_version(), views_api_version());
-}
-
 /**
  * Include views .css files.
  */
@@ -1585,51 +1565,6 @@ function views_get_all_views($reset = FALSE) {
   return entity_get_controller('view')->load();
 }
 
-/**
- * Loads a view with the storage controller.
- *
- * @param string $id
- *   The view name to load.
- *
- * @return Drupal\views\View
- *   The view which is loaded.
- */
-function views_storage_load($id) {
-  $result = entity_get_controller('view')->load(array($id));
-  return reset($result);
-}
-
-/**
- * Saves a view with the storage controller.
- *
- * @param Drupal\views\View $view
- *   The view which is saved.
- *
- * @return int
- *   SAVED_NEW or SAVED_UPDATED is returned depending on the operation
- *   performed.
- */
-function views_storage_save(View $view) {
-  return entity_get_controller('view')->save($view);
-}
-
-/**
- * Toggles the view status between enabled and disabled.
- *
- * @param Drupal\views\View $view
- *   The view which gets a different status.
- * @param bool $status
- *   Whether to enable or disable the view.
- */
-function views_storage_status(View $view, $status) {
-  if (!$status) {
-    $view->enable();
-  }
-  else {
-    $view->disable();
-  }
-}
-
 /**
  * Returns an array of all enabled views, as fully loaded $view objects.
  */
diff --git a/views_ui.info b/views_ui.info
index c9eebf4192d9..5a9d77245bbe 100644
--- a/views_ui.info
+++ b/views_ui.info
@@ -4,3 +4,4 @@ package = Views
 core = 8.x
 configure = admin/structure/views
 dependencies[] = views
+dependencies[] = views_ui_listing
diff --git a/views_ui.module b/views_ui.module
index f502e950d427..dc8246c67f60 100644
--- a/views_ui.module
+++ b/views_ui.module
@@ -27,6 +27,7 @@ function views_ui_menu() {
     'type' => MENU_LOCAL_ACTION,
   ) + $base;
 
+  /*
   // Top-level Views module pages (not tied to a particular View).
   $items['admin/structure/views/add-template'] = array(
     'title' => 'Add view from template',
@@ -34,6 +35,7 @@ function views_ui_menu() {
     // Don't show a local action link if there aren't any templates.
     'type' => views_get_all_templates() ? MENU_LOCAL_ACTION : MENU_VISIBLE_IN_BREADCRUMB,
   ) + $base;
+  */
 
   $items['admin/structure/views/import'] = array(
     'title' => 'Import',
@@ -348,19 +350,6 @@ function views_ui_cache_set(&$view) {
   views_temp_store()->set($view->name, $view);
 }
 
-/**
- * Specialized menu callback to load a view that is only a default
- * view.
- */
-function views_ui_default_load($name) {
-  $view = views_get_view($name);
-  if ($view->type == t('Default')) {
-    return $view;
-  }
-
-  return FALSE;
-}
-
 /**
  * Theme preprocess for views-view.tpl.php.
  */
@@ -531,17 +520,6 @@ function views_ui_view_preview_section_rows_links($view) {
   return $links;
 }
 
-/**
- * Implments hook_ctools_plugin_directory().
- *
- * Views UI provides wizard plugins on behalf of core base tables.
- */
-function views_ui_ctools_plugin_directory($module, $plugin) {
-  if ($module == 'views_ui' || ($module == 'ctools' && $plugin == 'export_ui')) {
-    return 'plugins/' . $plugin;
-  }
-}
-
 /**
  * Fetch metadata on a specific views ui wizard plugin.
  *
@@ -611,18 +589,6 @@ function views_ui_views_wizard_defaults() {
   );
 }
 
-/**
- * Inform CTools that the Views wizard plugin can have child plugins.
- */
-function views_ui_ctools_plugin_type() {
-  return array(
-    'views_wizard' => array(
-      'child plugins' => TRUE,
-      'defaults' => views_ui_views_wizard_defaults(),
-    ),
-  );
-}
-
 function views_ui_get_form_wizard_instance($wizard) {
   return views_get_plugin('wizard', $wizard['name']);
 }
@@ -742,66 +708,6 @@ function views_ui_ajax_get_form($form_id) {
     return $callback($form, $form_state);
   }
 }
-// @todo move these when we can
-
-/**
- * Helper function to get a list of paths assigned to a view.
- *
- * @param $view
- *   The view.
- *
- * @return
- *   An array of links to this view's display paths.
- */
-function _views_ui_get_paths($view) {
-  $all_paths = array();
-  if (empty($view->display)) {
-    $all_paths[] = t('Edit this view to add a display.');
-  }
-  else {
-    $view->initDisplay();   // Make sure all the handlers are set up
-    foreach ($view->display as $display) {
-      if (!empty($display->handler) && $display->handler->hasPath()) {
-        $one_path = $display->handler->getOption('path');
-        if (empty($path_sort)) {
-          $path_sort = strtolower($one_path);
-        }
-        if (empty($view->disabled) && strpos($one_path, '%') === FALSE) {
-          $all_paths[] = l('/' . $one_path, $one_path);
-        }
-        else {
-          $all_paths[] = check_plain('/' . $one_path);
-        }
-      }
-    }
-  }
-
-  return array_unique($all_paths);
-}
-
-/**
- * Helper function to get a list of displays included in a view.
- *
- * @param $view
- *   The view.
- *
- * @return
- *   An array of display types that this view includes.
- */
-function _views_ui_get_displays_list($view) {
-  $displays = array();
-  foreach ($view->display as $display) {
-    if (!empty($display->handler->definition['admin'])) {
-      $displays[$display->handler->definition['admin']] = TRUE;
-    }
-  }
-
-  if ($displays) {
-    ksort($displays);
-    $displays = array_keys($displays);
-  }
-  return $displays;
-}
 
 /**
  * This is part of a patch to address a jQueryUI bug.  The bug is responsible
@@ -840,3 +746,10 @@ function views_ui_truncate($string, $length) {
 
   return $string;
 }
+
+/**
+ * Magic load function. Wrapper to load a view.
+ */
+function views_ui_view_load($name) {
+  return views_get_view($name);
+}
diff --git a/views_ui_listing/lib/Drupal/views_ui_listing/EntityListControllerBase.php b/views_ui_listing/lib/Drupal/views_ui_listing/EntityListControllerBase.php
new file mode 100644
index 000000000000..6b3ff993e614
--- /dev/null
+++ b/views_ui_listing/lib/Drupal/views_ui_listing/EntityListControllerBase.php
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * Definition of Drupal\views_ui_listing\EntityListControllerBase.
+ */
+
+namespace Drupal\views_ui_listing;
+
+use Drupal\entity\EntityInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+
+/**
+ * Abstract base class for config entity listing plugins.
+ */
+abstract class EntityListControllerBase implements EntityListControllerInterface {
+
+  /**
+   * The Config storage controller class.
+   *
+   * @var Drupal\config\ConfigStorageController
+   */
+  protected $storage;
+
+  /**
+   * The Config entity type.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * The Config entity info.
+   *
+   * @var array
+   */
+  protected $entityInfo;
+
+  /**
+   * If ajax links are used on the listing page.
+   *
+   * @var bool
+   */
+  protected $usesAJAX;
+
+  public function __construct($entity_type, $entity_info = FALSE) {
+    $this->entityType = $entity_type;
+    $this->storage = entity_get_controller($entity_type);
+    if (!$entity_info) {
+      $entity_info = entity_get_info($entity_type);
+    }
+    $this->entityInfo = $entity_info;
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::getList();
+   */
+  public function getList() {
+    return $this->storage->load();
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::getStorageController();
+   */
+  public function getStorageController() {
+    return $this->storage;
+  }
+
+  public function getPath() {
+    return $this->entityInfo['list path'];
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::hookMenu();
+   */
+  public function hookMenu() {
+    $items = array();
+    $items[$this->entityInfo['list path']] = array(
+      'page callback' => 'views_ui_listing_entity_listing_page',
+      'page arguments' => array($this->entityType),
+      // @todo Add a proper access callback here.
+      'access callback' => TRUE,
+    );
+    return $items;
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::getRowData();
+   */
+  public function getRowData(EntityInterface $entity) {
+    $row = array();
+
+    $row['id'] = $entity->id();
+    $row['label'] = $entity->label();
+    $actions = $this->buildActionLinks($entity);
+    $row['actions'] = drupal_render($actions);
+
+    return $row;
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::getHeaderData();
+   */
+  public function getHeaderData() {
+    $row = array();
+    $row['id'] = t('ID');
+    $row['label'] = t('Label');
+    $row['actions'] = t('Actions');
+    return $row;
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::buildActionLinks();
+   */
+  public function buildActionLinks(EntityInterface $entity) {
+    $links = array();
+
+    foreach ($this->defineActionLinks($entity) as $definition) {
+      $attributes = array();
+
+      if (!empty($definition['ajax'])) {
+        $attributes['class'][] = 'use-ajax';
+        // Set this to true if we haven't already.
+        if (!isset($this->usesAJAX)) {
+          $this->usesAJAX = TRUE;
+        }
+      }
+
+      $links[] = array(
+        'title' => $definition['title'],
+        'href' => $definition['href'],
+        'attributes' => $attributes,
+      );
+    }
+
+    return array(
+      '#theme' => 'links',
+      '#links' => $links,
+    );
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::renderList();
+   */
+  public function renderList() {
+    $rows = array();
+
+    foreach ($this->getList() as $entity) {
+      $rows[] = $this->getRowData($entity);
+    }
+
+    // Add core AJAX library if we need to.
+    if (!empty($this->usesAJAX)) {
+      drupal_add_library('system', 'drupal.ajax');
+    }
+
+    return array(
+      '#theme' => 'table',
+      '#header' => $this->getHeaderData(),
+      '#rows' => $rows,
+      '#attributes' => array(
+        'id' => 'config-entity-listing',
+      ),
+    );
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::renderList();
+   */
+  public function renderListAJAX() {
+    $list = $this->renderList();
+    $commands = array();
+    $commands[] = ajax_command_replace('#config-entity-listing', drupal_render($list));
+
+    return new JsonResponse(ajax_render($commands));
+  }
+
+}
diff --git a/views_ui_listing/lib/Drupal/views_ui_listing/EntityListControllerInterface.php b/views_ui_listing/lib/Drupal/views_ui_listing/EntityListControllerInterface.php
new file mode 100644
index 000000000000..41bc9ddbfd03
--- /dev/null
+++ b/views_ui_listing/lib/Drupal/views_ui_listing/EntityListControllerInterface.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views_ui_listing\EntityListControllerInterface.
+ */
+
+namespace Drupal\views_ui_listing;
+
+use Drupal\entity\EntityInterface;
+
+/**
+ * Defines an interface for Configuration entity listing plugins.
+ */
+interface EntityListControllerInterface {
+
+  /*
+   * Returns a list of all available config entites of this type.
+   */
+  public function getList();
+
+  /**
+   * Gets the ConfigEntityController.
+   *
+   * @todo Put in correct namespace and docs here.
+   */
+  public function getStorageController();
+
+  /**
+   * Gets the hook_menu array item.
+   *
+   * @todo Put in correct docs here.
+   */
+  public function hookMenu();
+
+  /**
+   * Builds an array of data for each row.
+   *
+   * @param EntityInterface $entity
+   *
+   * @return array
+   *   An array of fields to use for this entity.
+   */
+  public function getRowData(EntityInterface $entity);
+
+  /**
+   * Builds the header row.
+   *
+   * @return array
+   *   An array of header strings.
+   */
+  public function getHeaderData();
+
+  /**
+   * Renders the list page markup to be output.
+   *
+   * @return string
+   *   The output markup for the listing page.
+   */
+  public function renderList();
+
+  /**
+   * Returns the list page as JSON.
+   *
+   * @return Symfony\Component\HttpFoundation\JsonResponse
+   *   AJAX commands to render the list.
+   */
+  public function renderListAJAX();
+
+  /**
+   * Renders a list of action links.
+   *
+   * @return array
+   */
+  public function buildActionLinks(EntityInterface $entity);
+
+  /**
+   * Provides an array of information to render action links.
+   *
+   * @return array
+   */
+  public function defineActionLinks(EntityInterface $entity);
+
+}
diff --git a/views_ui_listing/lib/Drupal/views_ui_listing/Tests/ConfigEntityListingTest.php b/views_ui_listing/lib/Drupal/views_ui_listing/Tests/ConfigEntityListingTest.php
new file mode 100644
index 000000000000..b2aeae377323
--- /dev/null
+++ b/views_ui_listing/lib/Drupal/views_ui_listing/Tests/ConfigEntityListingTest.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views_ui_listing\Tests\ConfigEntityListingTest.
+ */
+
+namespace Drupal\views_ui_listing\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\config\ConfigEntityBase;
+
+/**
+ * Tests configuration entities.
+ */
+class ConfigEntityListingTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('views_ui_listing_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Views configuration entity listing',
+      'description' => 'Tests configuration entity listing plugins.',
+      'group' => 'Views',
+    );
+  }
+
+  /**
+   * Tests basic listing plugin functionilty.
+   */
+  function testListingPlugin() {
+    $controller = views_ui_listing_get_list_controller('config_test');
+
+    // Get a list of Config entities.
+    $list = $controller->getList();
+    $this->assertEqual(count($list), 1, 'Correct number of plugins found.');
+    $this->assertTrue(!empty($list['default']), '"Default" config entity key found in list.');
+    $this->assertTrue($list['default'] instanceof ConfigEntityBase, '"Default" config entity is an instance of ConfigEntityBase');
+  }
+
+  /**
+   * Tests the listing UI.
+   */
+  function testListingUI() {
+    $page = $this->drupalGet('config-listing-test');
+
+    // Test that the page exists.
+    $this->assertText('Config test', 'Config test listing page title found.');
+
+    // Check we have the default id and label on the page too.
+    $this->assertText('default', '"default" ID found.');
+    $this->assertText('Default', '"Default" label found');
+
+    // Check each link.
+    foreach (array('edit', 'add', 'delete') as $link) {
+      $this->drupalSetContent($page);
+      $this->assertLink($link);
+      $this->clickLink($link);
+      $this->assertResponse(200);
+    }
+
+    // @todo Test AJAX links.
+  }
+
+}
diff --git a/views_ui_listing/tests/views_ui_listing_test/lib/Drupal/views_ui_listing_test/ConfigTestListController.php b/views_ui_listing/tests/views_ui_listing_test/lib/Drupal/views_ui_listing_test/ConfigTestListController.php
new file mode 100644
index 000000000000..05432cff900d
--- /dev/null
+++ b/views_ui_listing/tests/views_ui_listing_test/lib/Drupal/views_ui_listing_test/ConfigTestListController.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Definition of Drupal\views_ui_listing_test\ConfigTestListController.
+ */
+
+namespace Drupal\views_ui_listing_test;
+
+use Drupal\views_ui_listing\EntityListControllerBase;
+use Drupal\entity\EntityInterface;
+
+/**
+ * Views config entity listing controller.
+ */
+class ConfigTestListController extends EntityListControllerBase {
+
+  /**
+   * Overrides Drupal\views_ui_listing\EntityListControllerBase::hookMenu();
+   */
+  public function hookMenu() {
+    $path = $this->entityInfo['list path'];
+
+    $items = parent::hookMenu();
+    $items[$path]['title'] = 'Config test';
+    $items[$path]['description'] = 'Config test listing page.';
+    return $items;
+  }
+
+  /**
+   * Implements Drupal\views_ui_listing\EntityListControllerInterface::actionLinkMappings().
+   */
+  public function defineActionLinks(EntityInterface $entity) {
+    $id = $entity->id();
+
+    // @todo Add AJAX link to test.
+    return array(
+      'edit' => array(
+        'title' => 'edit',
+        'href' => "admin/structure/config_test/manage/$id/edit",
+        'ajax' => FALSE,
+      ),
+      'add' => array(
+        'title' => 'add',
+        'href' => "admin/structure/config_test/add",
+        'ajax' => FALSE,
+      ),
+      'delete' => array(
+        'title' => 'delete',
+        'href' => "admin/structure/config_test/manage/$id/delete",
+        'ajax' => FALSE,
+      ),
+    );
+  }
+
+}
diff --git a/views_ui_listing/tests/views_ui_listing_test/views_ui_listing_test.info b/views_ui_listing/tests/views_ui_listing_test/views_ui_listing_test.info
new file mode 100644
index 000000000000..703ba6ce930a
--- /dev/null
+++ b/views_ui_listing/tests/views_ui_listing_test/views_ui_listing_test.info
@@ -0,0 +1,5 @@
+name = Views UI listing test module
+package = Views
+dependencies[] = views_ui
+dependencies[] = config_test
+hidden = TRUE
diff --git a/views_ui_listing/tests/views_ui_listing_test/views_ui_listing_test.module b/views_ui_listing/tests/views_ui_listing_test/views_ui_listing_test.module
new file mode 100644
index 000000000000..b3d9bbc7f371
--- /dev/null
+++ b/views_ui_listing/tests/views_ui_listing_test/views_ui_listing_test.module
@@ -0,0 +1 @@
+<?php
diff --git a/views_ui_listing/views_ui_listing.info b/views_ui_listing/views_ui_listing.info
new file mode 100644
index 000000000000..3fb1baa9e6e0
--- /dev/null
+++ b/views_ui_listing/views_ui_listing.info
@@ -0,0 +1,4 @@
+name = Views UI Listing
+description = temporary replacement for export UI.
+package = Views
+core = 8.x
diff --git a/views_ui_listing/views_ui_listing.module b/views_ui_listing/views_ui_listing.module
new file mode 100644
index 000000000000..b5b7bedc9af3
--- /dev/null
+++ b/views_ui_listing/views_ui_listing.module
@@ -0,0 +1,85 @@
+<?php
+
+use Drupal\views_ui_listing\EntityListControllerInterface;
+use Drupal\config\ConfigEntityInterface;
+
+/**
+ * Implements hook_menu().
+ */
+function views_ui_listing_menu() {
+  $items = array();
+  foreach (entity_get_info() as $entity_type => $type_info) {
+    if (isset($type_info['list controller class'])) {
+      $controller = views_ui_listing_get_list_controller($entity_type);
+      $items += $controller->hookMenu();
+    }
+  }
+  return $items;
+}
+
+/**
+ * Gets the entity list controller class for an entity type.
+ *
+ * @return Drupal\entity\EntityListControllerInterface
+ */
+function views_ui_listing_get_list_controller($entity_type) {
+  $controllers = &drupal_static(__FUNCTION__, array());
+  if (!isset($controllers[$entity_type])) {
+    $type_info = entity_get_info($entity_type);
+    $class = $type_info['list controller class'];
+    $controllers[$entity_type] = new $class($entity_type);
+  }
+  return $controllers[$entity_type];
+}
+
+/**
+ * Page callback: Displays a config listing page.
+ *
+ * @param string $entity_id
+ *   The entity type.
+ *
+ * @return string
+ *   The page markup for the page.
+ */
+function views_ui_listing_entity_listing_page($entity_id) {
+  $controller = views_ui_listing_get_list_controller($entity_id);
+  return $controller->renderList();
+}
+
+/**
+ * Page callback: Calls a method on a config entity and reloads the listing page.
+ *
+ * @param Drupal\views_ui_listing\EntityListControllerInterface $controller
+ *   The list controller for this entity.
+ * @param Drupal\config\ConfigEntityInterface $entity
+ *   The config entity being acted upon.
+ * @param string $op
+ *   The action to perform, e.g., 'enable' or 'disable'.
+ *
+ * @return mixed
+ *   Either returns the listing page as JSON, or calls drupal_goto() to
+ *   redirect back to the listing page.
+ */
+function views_ui_listing_ajax_callback(EntityListControllerInterface $controller, ConfigEntityInterface $entity, $op) {
+  // Perform the operation.
+  $entity->$op();
+
+  // If the request is via AJAX, return the rendered list as JSON.
+  if (drupal_container()->get('request')->request->get('js')) {
+    return $controller->renderListAJAX();
+  }
+  // Otherwise, redirect back to the page.
+  else {
+    drupal_goto($controller->getPath());
+  }
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function views_ui_listing_entity_info_alter(&$entity_info) {
+  if (isset($entity_info['config_test'])) {
+    $entity_info['config_test']['list path'] = 'config-listing-test';
+    $entity_info['config_test']['list controller class'] = 'Drupal\views_ui_listing_test\ConfigTestListController';
+  }
+}
-- 
GitLab