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"] = " "; - $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