Skip to content
Snippets Groups Projects
Commit 7002e6ba authored by Alex Pott's avatar Alex Pott
Browse files

Issue #1800998 by dawehner, swentel: Use route system instead of hook_menu() in Views.

parent 3cdae055
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
Showing
with 749 additions and 132 deletions
......@@ -774,7 +774,7 @@ function _menu_translate(&$router_item, $map, $to_arg = FALSE) {
// Attempt to match this path to provide a fully built request to the
// acccess checker.
try {
$request->attributes->add(Drupal::service('router')->matchRequest($request));
$request->attributes->add(Drupal::service('router.dynamic')->matchRequest($request));
$router_item['access'] = Drupal::service('access_manager')->check($route, $request);
}
catch (NotFoundHttpException $e) {
......
......@@ -10,6 +10,7 @@
use Drupal\Core\Entity\DatabaseStorageController;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Symfony\Component\HttpFoundation\Request;
/**
* Controller class for menu links.
......@@ -216,6 +217,36 @@ protected function preSave(EntityInterface $entity) {
$entity->router_path = _menu_find_router_path($entity->link_path);
}
}
// Find the route_name.
if (!isset($entity->route_name)) {
$entity->route_name = $this->findRouteName($entity->link_path);
}
}
/**
* Returns the route_name matching a URL.
*
* @param string $link_path
* The link path to find a route name for.
*
* @return string
* The route name.
*/
protected function findRouteName($link_path) {
// Look up the route_name used for the given path.
$request = Request::create('/' . $link_path);
$request->attributes->set('system_path', $link_path);
try {
// Use router.dynamic instead of router, because router will call the
// legacy router which will call hook_menu() and you will get back to
// this method.
$result = \Drupal::service('router.dynamic')->matchRequest($request);
return isset($result['_route']) ? $result['_route'] : '';
}
catch (\Exception $e) {
return '';
}
}
/**
......
......@@ -72,10 +72,6 @@ function testNodeRSSContent() {
// viewing node.
$this->drupalGet("node/$node->nid");
$this->assertNoText($rss_only_content, 'Node content designed for RSS does not appear when viewing node.');
// Check that the node feed page does not try to interpret additional path
// components as arguments for node_feed() and returns default content.
$this->drupalGet('rss.xml/' . $this->randomName() . '/' . $this->randomName());
$this->assertText($rss_only_content, 'Ignore page arguments when delivering rss.xml.');
}
}
<?php
/**
* @file
* Contains \Drupal\node\Tests\Views\NodeIntegrationTest.
*/
namespace Drupal\node\Tests\Views;
class NodeIntegrationTest extends NodeTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = array('test_node_view');
public static function getInfo() {
return array(
'name' => 'Node: Views data',
'description' => 'Tests the node integration into views.',
'group' => 'Views module integration',
);
}
/**
* Tests basic node view with a node type argument.
*/
public function testNodeViewTypeArgument() {
// Create two content types with three nodes each.
$types = array();
$all_nids = array();
for ($i = 0; $i < 2; $i++) {
$type = $this->drupalCreateContentType();
$types[] = $type;
for ($j = 0; $j < 5; $j++) {
// Ensure the right order of the nodes.
$node = $this->drupalCreateNode(array('type' => $type->type, 'created' => REQUEST_TIME - ($i * 5 + $j)));
$nodes[$type->type][$node->id()] = $node;
$all_nids[] = $node->id();
}
}
$this->drupalGet('test-node-view');
$this->assertResponse(404);
$this->drupalGet('test-node-view/all');
$this->assertResponse(200);
$this->assertNids($all_nids);
foreach ($types as $type) {
$this->drupalGet("test-node-view/{$type->type}");
$this->assertNids(array_keys($nodes[$type->type]));
}
}
/**
* Ensures that a list of nodes appear on the page.
*
* @param array $expected_nids
* An array of node IDs.
*/
protected function assertNids(array $expected_nids = array()) {
$result = $this->xpath('//span[@class="field-content"]');
$nids = array();
foreach ($result as $element) {
$nids[] = (int) $element;
}
$this->assertEqual($nids, $expected_nids);
}
}
base_field: nid
base_table: node
core: 8.x
description: ''
status: '1'
display:
page_1:
display_plugin: page
id: page_1
display_title: Page
position: ''
display_options:
path: test-node-view
default:
display_plugin: default
id: default
display_title: Master
position: ''
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: none
options: { }
query:
type: views_query
options:
disable_sql_rewrite: '0'
distinct: '0'
slave: '0'
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: '0'
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: '1'
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: full
options:
items_per_page: '10'
offset: '0'
id: '0'
total_pages: ''
expose:
items_per_page: '0'
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 20, 40, 60'
items_per_page_options_all: '0'
items_per_page_options_all_label: '- All -'
offset: '0'
offset_label: Offset
tags:
previous: ' previous'
next: 'next ›'
first: '« first'
last: 'last »'
quantity: '9'
style:
type: default
row:
type: fields
fields:
nid:
id: nid
table: node
field: nid
relationship: none
group_type: group
admin_label: ''
label: Nid
exclude: '0'
alter:
alter_text: '0'
text: ''
make_link: '0'
path: ''
absolute: '0'
external: '0'
replace_spaces: '0'
path_case: none
trim_whitespace: '0'
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: '0'
max_length: ''
word_boundary: '1'
ellipsis: '1'
more_link: '0'
more_link_text: ''
more_link_path: ''
strip_tags: '0'
trim: '0'
preserve_tags: ''
html: '0'
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: '1'
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: '1'
empty: ''
hide_empty: '0'
empty_zero: '0'
hide_alter_empty: '1'
link_to_node: '0'
plugin_id: node
filters:
status:
value: '1'
table: node
field: status
id: status
expose:
operator: '0'
group: '1'
sorts:
created:
id: created
table: node
field: created
order: DESC
relationship: none
group_type: group
admin_label: ''
exposed: '0'
expose:
label: ''
granularity: second
title: test_node_view
header: { }
footer: { }
empty: { }
relationships: { }
arguments:
type:
id: type
table: node
field: type
relationship: none
group_type: group
admin_label: ''
default_action: 'not found'
exception:
value: all
title_enable: '0'
title: All
title_enable: '0'
title: ''
breadcrumb_enable: '0'
breadcrumb: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: '0'
summary_options:
base_path: ''
count: '1'
items_per_page: '25'
override: '0'
summary:
sort_order: asc
number_of_records: '0'
format: default_summary
specify_validation: '0'
validate:
type: none
fail: 'not found'
validate_options: { }
glossary: '0'
limit: '0'
case: none
path_case: none
transform_dash: '0'
break_phrase: '0'
plugin_id: node_type
label: test_node_view
module: views
id: test_node_view
tag: ''
uuid: 377e9d79-57ea-4836-a100-a255c58e40ca
langcode: en
......@@ -23,7 +23,7 @@
* module = "rest",
* title = @Translation("REST export"),
* help = @Translation("Create a REST export resource."),
* uses_hook_menu = TRUE,
* uses_route = TRUE,
* admin = @Translation("REST export")
* )
*/
......
......@@ -14,6 +14,10 @@
*/
class TestControllers {
public function test() {
return new Response('test');
}
public function test1() {
return new Response('test1');
}
......
......@@ -10,6 +10,7 @@
use Drupal\Component\Annotation\Plugin;
use Drupal\views\Plugin\views\access\AccessPluginBase;
use Drupal\Core\Annotation\Translation;
use Symfony\Component\Routing\Route;
/**
* Access plugin that provides permission-based access control.
......@@ -30,11 +31,14 @@ class Permission extends AccessPluginBase {
protected $usesOptions = TRUE;
public function access($account) {
return views_check_perm($this->options['perm'], $account);
return user_access($this->options['perm'], $account) || user_access('access all views', $account);
}
function get_access_callback() {
return array('views_check_perm', array($this->options['perm']));
/**
* {@inheritdoc}
*/
public function alterRouteDefinition(Route $route) {
$route->setRequirement('_permission', $this->options['perm']);
}
public function summaryTitle() {
......
......@@ -10,6 +10,7 @@
use Drupal\Component\Annotation\Plugin;
use Drupal\views\Plugin\views\access\AccessPluginBase;
use Drupal\Core\Annotation\Translation;
use Symfony\Component\Routing\Route;
/**
* Access plugin that provides role-based access control.
......@@ -29,12 +30,18 @@ class Role extends AccessPluginBase {
*/
protected $usesOptions = TRUE;
/**
* {@inheritdoc}
*/
public function access($account) {
return views_check_roles(array_filter($this->options['role']), $account);
return user_access('access all views', $account) || array_intersect(array_filter($this->options['role']), $account->roles);
}
function get_access_callback() {
return array('views_check_roles', array(array_filter($this->options['role'])));
/**
* {@inheritdoc}
*/
public function alterRouteDefinition(Route $route) {
$route->setRequirement('_role_id', $this->options['role']);
}
public function summaryTitle() {
......
<?php
/**
* @file
* Contains \Drupal\views\EventSubscriber\RouteSubscriber.
*/
namespace Drupal\views\EventSubscriber;
use Drupal\Core\Routing\RouteBuildEvent;
use Drupal\Core\Routing\RoutingEvents;
use Drupal\views\Plugin\views\display\DisplayRouterInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Builds up the routes of all views.
*/
class RouteSubscriber implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[RoutingEvents::DYNAMIC] = 'dynamicRoutes';
return $events;
}
/**
* Adds routes defined by all views.
*
* @param \Drupal\Core\Routing\RouteBuildEvent $event
* The route building event.
*/
public function dynamicRoutes(RouteBuildEvent $event) {
$collection = $event->getRouteCollection();
$views = views_get_applicable_views('uses_route');
foreach ($views as $data) {
list($view, $display_id) = $data;
if ($view->setDisplay($display_id) && $display = $view->displayHandlers->get($display_id)) {
if ($display instanceof DisplayRouterInterface) {
$display->collectRoutes($collection);
}
}
$view->destroy();
}
}
}
......@@ -9,13 +9,14 @@
use Drupal\views\Plugin\views\PluginBase;
use Drupal\views\ViewExecutable;
use Symfony\Component\Routing\Route;
/**
* @defgroup views_access_plugins Views access plugins
* @{
* The base plugin to handle access to a view.
*
* Therefore it primarily has to implement the access and the get_access_callback
* Therefore it primarily has to implement the access and the alterRouteDefinition
* method.
*/
......@@ -65,18 +66,15 @@ public function summaryTitle() {
abstract public function access($account);
/**
* Determine the access callback and arguments.
* Allows access plugins to alter the route definition of a view.
*
* This information will be embedded in the menu in order to reduce
* performance hits during menu item access testing, which happens
* a lot.
* Likely the access plugin will add new requirements, so its custom access
* checker can be applied.
*
* @return array
* The first item of the array should be the function to call,and the
* second item should be an array of arguments. The first item may also be
* TRUE (bool only) which will indicate no access control.
* @param \Symfony\Component\Routing\Route $route
* The route to change.
*/
abstract function get_access_callback();
abstract public function alterRouteDefinition(Route $route);
}
......
......@@ -10,6 +10,8 @@
use Drupal\Core\Annotation\Translation;
use Drupal\Component\Annotation\Plugin;
use Symfony\Component\Routing\Route;
/**
* Access plugin that provides no access control at all.
*
......@@ -36,11 +38,10 @@ public function access($account) {
}
/**
* Implements Drupal\views\Plugin\views\access\AccessPluginBase::get_access_callback().
* {@inheritdoc}
*/
public function get_access_callback() {
// No access control.
return TRUE;
public function alterRouteDefinition(Route $route) {
$route->setRequirement('_access', 'TRUE');
}
}
<?php
/**
* @file
* Contains \Drupal\views\Plugin\views\display\DisplayRouterInterface.
*/
namespace Drupal\views\Plugin\views\display;
/**
* Defines an interface for displays that can collect routes.
*
* In addition to implementing the interface, specify 'uses_routes' in the
* plugin definition.
*/
use Symfony\Component\Routing\RouteCollection;
interface DisplayRouterInterface {
/**
* Adds the route entry of a view to the collection.
*
* @param \Symfony\Component\Routing\RouteCollection $collection
* A collection of routes that should be registered for this resource.
*/
public function collectRoutes(RouteCollection $collection);
}
......@@ -22,7 +22,7 @@
* id = "feed",
* title = @Translation("Feed"),
* help = @Translation("Display the view as a feed, such as an RSS feed."),
* uses_hook_menu = TRUE,
* uses_route = TRUE,
* admin = @Translation("Feed")
* )
*/
......
......@@ -22,6 +22,7 @@
* title = @Translation("Page"),
* help = @Translation("Display the view as a page, with a URL and menu links."),
* uses_hook_menu = TRUE,
* uses_route = TRUE,
* contextual_links_locations = {"page"},
* theme = "views_view",
* admin = @Translation("Page")
......@@ -92,7 +93,10 @@ public function execute() {
// And the title, which is much easier.
drupal_set_title(filter_xss_admin($this->view->getTitle()), PASS_THROUGH);
return $render;
$response = $this->view->getResponse();
$response->setContent(drupal_render_page($render));
return $response;
}
/**
......
......@@ -8,13 +8,16 @@
namespace Drupal\views\Plugin\views\display;
use Drupal\views\Views;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* The base display plugin for path/callbacks. This is used for pages and feeds.
*/
abstract class PathPluginBase extends DisplayPluginBase {
abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouterInterface {
/**
* Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::hasPath().
......@@ -33,6 +36,69 @@ protected function defineOptions() {
return $options;
}
/**
* {@inheritdoc}
*/
public function collectRoutes(RouteCollection $collection) {
$view_id = $this->view->storage->id();
$display_id = $this->display['id'];
$defaults = array(
'_controller' => 'Drupal\views\Routing\ViewPageController::handle',
'view_id' => $view_id,
'display_id' => $display_id,
);
// @todo How do we apply argument validation?
$bits = explode('/', $this->getOption('path'));
// @todo Figure out validation/argument loading.
// Replace % with %views_arg for menu autoloading and add to the
// page arguments so the argument actually comes through.
$arg_counter = 0;
$this->view->initHandlers();
$view_arguments = $this->view->argument;
$argument_ids = array_keys($view_arguments);
$total_arguments = count($argument_ids);
// Replace arguments in the views UI (defined via %) with parameters in
// routes (defined via {}). As a name for the parameter use arg_$key, so
// it can be pulled in the views controller from the request.
foreach ($bits as $pos => $bit) {
if ($bit == '%') {
// Generate the name of the parameter using the key of the argument
// handler.
$arg_id = 'arg_' . $argument_ids[$arg_counter++];
$bits[$pos] = '{' . $arg_id . '}';
}
}
// Add missing arguments not defined in the path, but added as handler.
while (($total_arguments - $arg_counter) > 0) {
$arg_id = 'arg_' . $argument_ids[$arg_counter++];
$bit = '{' . $arg_id . '}';
// In contrast to the previous loop add the defaults here, as % was not
// specified, which means the argument is optional.
$defaults[$arg_id] = NULL;
$bits[] = $bit;
}
$route_path = '/' . implode('/', $bits);
$route = new Route($route_path, $defaults);
// Add access check parameters to the route.
$access_plugin = $this->getPlugin('access');
if (!isset($access_plugin)) {
// @todo Do we want to support a default plugin in getPlugin itself?
$access_plugin = Views::pluginManager('access')->createInstance('none');
}
$access_plugin->alterRouteDefinition($route);
$collection->add("view.$view_id.$display_id", $route);
}
/**
* Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::executeHookMenu().
*/
......@@ -60,49 +126,9 @@ public function executeHookMenu($callbacks) {
$path = implode('/', $bits);
$access_plugin = $this->getPlugin('access');
if (!isset($access_plugin)) {
$access_plugin = Views::pluginManager('access')->createInstance('none');
}
// Get access callback might return an array of the callback + the dynamic
// arguments.
$access_plugin_callback = $access_plugin->get_access_callback();
if (is_array($access_plugin_callback)) {
$access_arguments = array();
// Find the plugin arguments.
$access_plugin_method = array_shift($access_plugin_callback);
$access_plugin_arguments = array_shift($access_plugin_callback);
if (!is_array($access_plugin_arguments)) {
$access_plugin_arguments = array();
}
$access_arguments[0] = array($access_plugin_method, &$access_plugin_arguments);
// Move the plugin arguments to the access arguments array.
$i = 1;
foreach ($access_plugin_arguments as $key => $value) {
if (is_int($value)) {
$access_arguments[$i] = $value;
$access_plugin_arguments[$key] = $i;
$i++;
}
}
}
else {
$access_arguments = array($access_plugin_callback);
}
if ($path) {
$items[$path] = array(
// Default views page entry.
'page callback' => 'views_page',
'page arguments' => $page_arguments,
// Default access check (per display).
'access callback' => 'views_access',
'access arguments' => $access_arguments,
'route_name' => "view.{$this->view->storage->id()}.{$this->display['id']}",
// Identify URL embedded arguments and correlate them to a handler.
'load arguments' => array($this->view->storage->id(), $this->display['id'], '%index'),
);
......@@ -159,11 +185,6 @@ public function executeHookMenu($callbacks) {
$default_path = implode('/', $bits);
$items[$default_path] = array(
// Default views page entry.
'page callback' => 'views_page',
'page arguments' => $page_arguments,
// Default access check (per display).
'access callback' => 'views_access',
'access arguments' => $access_arguments,
// Identify URL embedded arguments and correlate them to a
// handler.
'load arguments' => array($this->view->storage->id(), $this->display['id'], '%index'),
......
<?php
/**
* @file
* Contains \Drupal\views\Routing\ViewPageController.
*/
namespace Drupal\views\Routing;
use Drupal\Core\ControllerInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\views\ViewExecutableFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Defines a page controller to execute and render a view.
*/
class ViewPageController implements ControllerInterface {
/**
* The entity storage controller.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
*/
protected $storageController;
/**
* The view executable factory.
*
* @var \Drupal\views\ViewExecutableFactory
*/
protected $executableFactory;
/**
* Constructs a ViewPageController object.
*
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
* The entity storage controller.
* @param \Drupal\views\ViewExecutableFactory $executable_factory
* The view executable factory
*/
public function __construct(EntityStorageControllerInterface $storage_controller, ViewExecutableFactory $executable_factory) {
$this->storageController = $storage_controller;
$this->executableFactory = $executable_factory;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.entity')->getStorageController('view'),
$container->get('views.executable')
);
}
/**
* Handles a response for a view.
*/
public function handle(Request $request) {
$view_id = $request->attributes->get('view_id');
$display_id = $request->attributes->get('display_id');
$entities = $this->storageController->load(array($view_id));
$entity = reset($entities);
if (empty($entity)) {
throw new NotFoundHttpException(format_string('Page controller for view %id requested, but view was not found.', array('%id' => $view_id)));
}
$view = $this->executableFactory->get($entity);
$view->setDisplay($display_id);
$view->initHandlers();
$args = array();
foreach (array_keys((array) $view->argument) as $argument_id) {
$arg = $request->attributes->get('arg_' . $argument_id);
if (isset($arg)) {
$args[] = $arg;
}
}
return $view->executeDisplay($display_id, $args);
}
}
......@@ -7,6 +7,8 @@
namespace Drupal\views\Tests\Plugin;
use Drupal\views\Tests\ViewTestData;
/**
* Basic test for pluggable access.
*
......@@ -35,6 +37,8 @@ protected function setUp() {
$this->enableViewsTestModule();
ViewTestData::importTestViews(get_class($this), array('views_test_data'));
$this->admin_user = $this->drupalCreateUser(array('access all views'));
$this->web_user = $this->drupalCreateUser();
$this->web_role = current($this->web_user->roles);
......@@ -42,6 +46,7 @@ protected function setUp() {
$this->normal_role = $this->drupalCreateRole(array());
$this->normal_user = $this->drupalCreateUser(array('views_test_data test permission'));
$this->normal_user->roles[$this->normal_role] = $this->normal_role;
// @todo when all the plugin information is cached make a reset function and
// call it here.
}
......@@ -74,57 +79,19 @@ function testStaticAccessPlugin() {
$access_plugin = $view->display_handler->getPlugin('access');
$this->assertFalse($access_plugin->access($this->normal_user));
$this->drupalGet('test_access_static');
$this->assertResponse(403);
$display = &$view->storage->getDisplay('default');
$display['display_options']['access']['options']['access'] = TRUE;
$access_plugin->options['access'] = TRUE;
$this->assertTrue($access_plugin->access($this->normal_user));
// FALSE comes from hook_menu caching.
$expected_hook_menu = array(
'views_test_data_test_static_access_callback', array(FALSE)
);
$hook_menu = $view->executeHookMenu('page_1');
$this->assertEqual($expected_hook_menu, $hook_menu['test_access_static']['access arguments'][0]);
$expected_hook_menu = array(
'views_test_data_test_static_access_callback', array(TRUE)
);
$this->assertTrue(views_access($expected_hook_menu));
}
/**
* Tests dynamic access plugin.
*
* @see Drupal\views_test\Plugin\views\access\DyamicTest
*/
function testDynamicAccessPlugin() {
$view = views_get_view('test_access_dynamic');
$view->setDisplay();
$argument1 = $this->randomName();
$argument2 = $this->randomName();
state()->set('test_dynamic_access_argument1', $argument1);
state()->set('test_dynamic_access_argument2', $argument2);
$access_plugin = $view->display_handler->getPlugin('access');
$this->assertFalse($access_plugin->access($this->normal_user));
$access_plugin->options['access'] = TRUE;
$this->assertFalse($access_plugin->access($this->normal_user));
$view->save();
$this->container->get('router.builder')->rebuild();
$view->setArguments(array($argument1, $argument2));
$this->assertTrue($access_plugin->access($this->normal_user));
// FALSE comes from hook_menu caching.
$expected_hook_menu = array(
'views_test_data_test_dynamic_access_callback', array(FALSE, 1, 2)
);
$hook_menu = $view->executeHookMenu('page_1');
$this->assertEqual($expected_hook_menu, $hook_menu['test_access_dynamic']['access arguments'][0]);
$expected_hook_menu = array(
'views_test_data_test_dynamic_access_callback', array(TRUE, 1, 2)
);
$this->assertTrue(views_access($expected_hook_menu, $argument1, $argument2));
$this->drupalGet('test_access_static');
$this->assertResponse(200);
}
}
......@@ -2,26 +2,45 @@
/**
* @file
* Definition of Drupal\views\Tests\Plugin\DisplayPageTest.
* Contains \Drupal\views\Tests\Plugin\DisplayPageTest.
*/
namespace Drupal\views\Tests\Plugin;
use Drupal\views\Tests\Plugin\PluginTestBase;
use Drupal\Core\Routing\RouteBuildEvent;
use Drupal\views\EventSubscriber\RouteSubscriber;
use Drupal\views\Tests\ViewUnitTestBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\RouteCollection;
/**
* Tests the page display plugin.
*
* @see Drupal\views\Plugin\display\Page
*/
class DisplayPageTest extends PluginTestBase {
class DisplayPageTest extends ViewUnitTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = array('test_page_display');
public static $testViews = array('test_page_display', 'test_page_display_route');
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'user');
/**
* The router dumper to get all routes.
*
* @var \Drupal\Core\Routing\MatcherDumper
*/
protected $routerDumper;
public static function getInfo() {
return array(
......@@ -31,21 +50,82 @@ public static function getInfo() {
);
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->enableViewsTestModule();
// Setup the needed tables in order to make the drupal router working.
$this->installSchema('system', 'router');
$this->installSchema('system', 'url_alias');
$this->installSchema('system', 'menu_router');
$this->installSchema('user', 'role_permission');
}
/**
* Checks the behavior of the page for access denied/not found behaviours.
*/
public function testPageResponses() {
$view = views_get_view('test_page_display');
$this->drupalGet('test_page_display_403');
$this->assertResponse(403);
$this->drupalGet('test_page_display_404');
$this->assertResponse(404);
// @todo Importing a route should fire a container rebuild.
$this->container->get('router.builder')->rebuild();
$subrequest = Request::create('/test_page_display_403', 'GET');
$response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
$this->assertEqual($response->getStatusCode(), 403);
$subrequest = Request::create('/test_page_display_404', 'GET');
$response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
$this->assertEqual($response->getStatusCode(), 404);
$subrequest = Request::create('/test_page_display_200', 'GET');
$response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
$this->assertEqual($response->getStatusCode(), 200);
}
/**
* Checks that the router items are properly registered
*/
public function testPageRouterItems() {
$subscriber = new RouteSubscriber();
$collection = new RouteCollection();
$subscriber->dynamicRoutes(new RouteBuildEvent($collection, 'dynamic_routes'));
// Check the controller defaults.
foreach ($collection as $id => $route) {
if (strpos($id, 'test_page_display_route') === 0) {
$this->assertEqual($route->getDefault('_controller'), 'Drupal\views\Routing\ViewPageController::handle');
$this->assertEqual($route->getDefault('view_id'), 'test_page_display_route');
$this->assertEqual($route->getDefault('display_id'), str_replace('test_page_display_route.', '', $id));
}
}
// Check the generated patterns and default values.
$route = $collection->get('view.test_page_display_route.page_1');
$this->assertEqual($route->getPath(), '/test_route_without_arguments');
$route = $collection->get('view.test_page_display_route.page_2');
$this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}');
$this->assertTrue($route->hasDefault('arg_id'), 'A default value is set for the optional argument id.');
$route = $collection->get('view.test_page_display_route.page_3');
$this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}/suffix');
$this->assertFalse($route->hasDefault('arg_id'), 'No default value is set for the required argument id.');
$route = $collection->get('view.test_page_display_route.page_4');
$this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}/suffix/{arg_id_2}');
$this->assertFalse($route->hasDefault('arg_id'), 'No default value is set for the required argument id.');
$this->assertTrue($route->hasDefault('arg_id_2'), 'A default value is set for the optional argument id_2.');
$route = $collection->get('view.test_page_display_route.page_5');
$this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}/{arg_id_2}');
$this->assertTrue($route->hasDefault('arg_id'), 'A default value is set for the optional argument id.');
$this->assertTrue($route->hasDefault('arg_id_2'), 'A default value is set for the optional argument id_2.');
$route = $collection->get('view.test_page_display_route.page_6');
$this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_id}/{arg_id_2}');
$this->assertFalse($route->hasDefault('arg_id'), 'No default value is set for the required argument id.');
$this->assertFalse($route->hasDefault('arg_id_2'), 'No default value is set for the required argument id_2.');
}
}
<?php
/**
* @file
* Contains \Drupal\views\Tests\Plugin\DisplayPageWebTest.
*/
namespace Drupal\views\Tests\Plugin;
/**
* Tests the views page display plugin as webtest.
*/
class DisplayPageWebTest extends PluginTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = array('test_page_display_arguments');
public static function getInfo() {
return array(
'name' => 'Display: Page plugin (web)',
'description' => 'Tests the page display plugin (web).',
'group' => 'Views Plugins',
);
}
protected function setUp() {
parent::setUp();
$this->enableViewsTestModule();
}
/**
* Tests arguments.
*/
public function testArguments() {
$this->drupalGet('test_route_without_arguments');
$this->assertResponse(200);
$result = $this->xpath('//span[@class="field-content"]');
$this->assertEqual(count($result), 5, 'All entries was returned');
$this->drupalGet('test_route_without_arguments/1');
$this->assertResponse(404);
$this->drupalGet('test_route_with_argument/1');
$this->assertResponse(200);
$result = $this->xpath('//span[@class="field-content"]');
$this->assertEqual(count($result), 1, 'Ensure that just the filtered entry was returned.');
$this->assertEqual((string) $result[0], 1, 'The passed ID was returned.');
$this->drupalGet('test_route_with_suffix/1/suffix');
$this->assertResponse(200);
$result = $this->xpath('//span[@class="field-content"]');
$this->assertEqual(count($result), 1, 'Ensure that just the filtered entry was returned.');
$this->assertEqual((string) $result[0], 1, 'The passed ID was returned.');
$this->drupalGet('test_route_with_suffix_and_argument/1/suffix/2');
$this->assertResponse(200);
$result = $this->xpath('//span[@class="field-content"]');
$this->assertEqual(count($result), 0, 'No result was returned.');
$this->drupalGet('test_route_with_suffix_and_argument/1/suffix/1');
$this->assertResponse(200);
$result = $this->xpath('//span[@class="field-content"]');
$this->assertEqual(count($result), 1, 'Ensure that just the filtered entry was returned.');
$this->assertEqual((string) $result[0], 1, 'The passed ID was returned.');
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment