Issue #3025657 by seanB, dww, gbirch, benjifisher, Lendude, phenaproxima, starshaped, webchick, MrMason, dawehner: Add views area plugin to display a link to another view display within the same view
parent
e9792ce7e9
commit
8b4ced17a2
|
@ -77,3 +77,14 @@ views.area.http_status_code:
|
|||
status_code:
|
||||
type: integer
|
||||
label: 'HTTP status code'
|
||||
|
||||
views.area.display_link:
|
||||
type: views_area
|
||||
label: 'Display link'
|
||||
mapping:
|
||||
display_id:
|
||||
type: string
|
||||
label: 'The display ID of the view display to link to.'
|
||||
label:
|
||||
type: label
|
||||
label: 'The label of the link.'
|
||||
|
|
|
@ -17,3 +17,7 @@
|
|||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
/* Provide some space between display links. */
|
||||
.views-display-link + .views-display-link {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\views\Plugin\views\area;
|
||||
|
||||
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Form\FormBuilderInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Plugin\views\display\PathPluginBase;
|
||||
|
||||
/**
|
||||
* Views area display_link handler.
|
||||
*
|
||||
* @ingroup views_area_handlers
|
||||
*
|
||||
* @ViewsArea("display_link")
|
||||
*/
|
||||
class DisplayLink extends AreaPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['display_id'] = ['default' => NULL];
|
||||
$options['label'] = ['default' => NULL];
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
|
||||
$allowed_displays = [];
|
||||
$displays = $this->view->storage->get('display');
|
||||
foreach ($displays as $display_id => $display) {
|
||||
if (!$this->isPathBasedDisplay($display_id)) {
|
||||
unset($displays[$display_id]);
|
||||
continue;
|
||||
}
|
||||
$allowed_displays[$display_id] = $display['display_title'];
|
||||
}
|
||||
|
||||
$form['description'] = [
|
||||
[
|
||||
'#markup' => $this->t('To make sure the results are the same when switching to the other display, it is recommended to make sure the display:'),
|
||||
],
|
||||
[
|
||||
'#theme' => 'item_list',
|
||||
'#items' => [
|
||||
$this->t('Has a path.'),
|
||||
$this->t('Has the same filter criteria.'),
|
||||
$this->t('Has the same sort criteria.'),
|
||||
$this->t('Has the same pager settings.'),
|
||||
$this->t('Has the same contextual filters.'),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (!$allowed_displays) {
|
||||
$form['empty_message'] = [
|
||||
'#markup' => '<p><em>' . $this->t('There are no path-based displays available.') . '</em></p>',
|
||||
];
|
||||
}
|
||||
else {
|
||||
$form['display_id'] = [
|
||||
'#title' => $this->t('Display'),
|
||||
'#type' => 'select',
|
||||
'#options' => $allowed_displays,
|
||||
'#default_value' => $this->options['display_id'],
|
||||
'#required' => TRUE,
|
||||
];
|
||||
$form['label'] = [
|
||||
'#title' => $this->t('Label'),
|
||||
'#description' => $this->t('The text of the link.'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $this->options['label'],
|
||||
'#required' => TRUE,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate() {
|
||||
$errors = parent::validate();
|
||||
|
||||
// Do not add errors for the default display if it is not displayed in the
|
||||
// UI.
|
||||
if ($this->displayHandler->isDefaultDisplay() && !\Drupal::config('views.settings')->get('ui.show.master_display')) {
|
||||
return $errors;
|
||||
}
|
||||
|
||||
// Ajax errors can cause the plugin to be added without any settings.
|
||||
$linked_display_id = !empty($this->options['display_id']) ? $this->options['display_id'] : NULL;
|
||||
if (!$linked_display_id) {
|
||||
$errors[] = $this->t('%current_display: The link in the %area area has no configured display.', [
|
||||
'%current_display' => $this->displayHandler->display['display_title'],
|
||||
'%area' => $this->areaType,
|
||||
]);
|
||||
return $errors;
|
||||
}
|
||||
|
||||
// Check if the linked display hasn't been removed.
|
||||
if (!$this->view->displayHandlers->get($linked_display_id)) {
|
||||
$errors[] = $this->t('%current_display: The link in the %area area points to the %linked_display display which no longer exists.', [
|
||||
'%current_display' => $this->displayHandler->display['display_title'],
|
||||
'%area' => $this->areaType,
|
||||
'%linked_display' => $this->options['display_id'],
|
||||
]);
|
||||
return $errors;
|
||||
}
|
||||
|
||||
// Check if the linked display is a path-based display.
|
||||
if (!$this->isPathBasedDisplay($linked_display_id)) {
|
||||
$errors[] = $this->t('%current_display: The link in the %area area points to the %linked_display display which does not have a path.', [
|
||||
'%current_display' => $this->displayHandler->display['display_title'],
|
||||
'%area' => $this->areaType,
|
||||
'%linked_display' => $this->view->displayHandlers->get($linked_display_id)->display['display_title'],
|
||||
]);
|
||||
return $errors;
|
||||
}
|
||||
|
||||
// Check if options of the linked display are equal to the options of the
|
||||
// current display. We "only" show a warning here, because even though we
|
||||
// recommend keeping the display options equal, we do not want to enforce
|
||||
// this.
|
||||
$unequal_options = [
|
||||
'filters' => t('Filter criteria'),
|
||||
'sorts' => t('Sort criteria'),
|
||||
'pager' => t('Pager'),
|
||||
'arguments' => t('Contextual filters'),
|
||||
];
|
||||
$unequal_options = array_filter($unequal_options, function ($option) use ($linked_display_id) {
|
||||
return !$this->hasEqualOptions($linked_display_id, $option);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
if ($unequal_options) {
|
||||
$warning = $this->t('%current_display: The link in the %area area points to the %linked_display display which uses different settings than the %current_display display for: %unequal_options. To make sure users see the exact same result when clicking the link, please check that the settings are the same.', [
|
||||
'%current_display' => $this->displayHandler->display['display_title'],
|
||||
'%area' => $this->areaType,
|
||||
'%linked_display' => $this->view->displayHandlers->get($linked_display_id)->display['display_title'],
|
||||
'%unequal_options' => implode(', ', $unequal_options),
|
||||
]);
|
||||
$this->messenger()->addWarning($warning);
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render($empty = FALSE) {
|
||||
if (($empty && empty($this->options['empty'])) || empty($this->options['display_id'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!$this->isPathBasedDisplay($this->options['display_id'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get query parameters from the exposed input and pager.
|
||||
$query = $this->view->getExposedInput();
|
||||
if ($current_page = $this->view->getCurrentPage()) {
|
||||
$query['page'] = $current_page;
|
||||
}
|
||||
|
||||
// @todo Remove this parsing once these are removed from the request in
|
||||
// https://www.drupal.org/node/2504709.
|
||||
foreach ([
|
||||
'view_name',
|
||||
'view_display_id',
|
||||
'view_args',
|
||||
'view_path',
|
||||
'view_dom_id',
|
||||
'pager_element',
|
||||
'view_base_path',
|
||||
AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER,
|
||||
FormBuilderInterface::AJAX_FORM_REQUEST,
|
||||
MainContentViewSubscriber::WRAPPER_FORMAT,
|
||||
] as $key) {
|
||||
unset($query[$key]);
|
||||
}
|
||||
|
||||
// Set default classes.
|
||||
$classes = [
|
||||
'views-display-link',
|
||||
'views-display-link-' . $this->options['display_id'],
|
||||
];
|
||||
if ($this->options['display_id'] === $this->view->current_display) {
|
||||
$classes[] = 'is-active';
|
||||
}
|
||||
|
||||
// By default, this element sets #theme so that the 'link' theme hook is
|
||||
// used for rendering, with the 'views_display_link' suffix so that themes
|
||||
// can override this specifically without overriding all link theming.
|
||||
return [
|
||||
'#type' => 'link',
|
||||
'#theme' => 'link__views_display_link',
|
||||
'#title' => $this->options['label'],
|
||||
'#url' => $this->view->getUrl($this->view->args, $this->options['display_id'])->setOptions(['query' => $query]),
|
||||
'#options' => [
|
||||
'view' => $this->view,
|
||||
'target_display_id' => $this->options['display_id'],
|
||||
'attributes' => ['class' => $classes],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a views display is a path-based display.
|
||||
*
|
||||
* @param string $display_id
|
||||
* The display ID to check.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the display ID is an allowed display or not.
|
||||
*/
|
||||
protected function isPathBasedDisplay($display_id) {
|
||||
$loaded_display = $this->view->displayHandlers->get($display_id);
|
||||
return $loaded_display instanceof PathPluginBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the options of a views display are equal to the current display.
|
||||
*
|
||||
* @param string $display_id
|
||||
* The display ID to check.
|
||||
* @param string $option
|
||||
* The option to check.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the option of the view display are equal to the current display
|
||||
* or not.
|
||||
*/
|
||||
protected function hasEqualOptions($display_id, $option) {
|
||||
$loaded_display = $this->view->displayHandlers->get($display_id);
|
||||
return $loaded_display->getOption($option) === $this->displayHandler->getOption($option);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,418 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\views\Kernel\Handler;
|
||||
|
||||
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Form\FormBuilderInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Views;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Tests the core views_handler_area_display_link handler.
|
||||
*
|
||||
* @group views
|
||||
* @see \Drupal\views\Plugin\views\area\DisplayLink
|
||||
*/
|
||||
class AreaDisplayLinkTest extends ViewsKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'user', 'filter'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $testViews = ['test_view'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->installConfig(['system', 'filter']);
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
$view = Views::getView('test_view');
|
||||
|
||||
// Add two page displays and a block display.
|
||||
$page_1 = $view->newDisplay('page', 'Page 1', 'page_1');
|
||||
$page_1->setOption('path', 'page_1');
|
||||
$page_2 = $view->newDisplay('page', 'Page 2', 'page_2');
|
||||
$page_2->setOption('path', 'page_2');
|
||||
$view->newDisplay('block', 'Block 1', 'block_1');
|
||||
|
||||
// Add default filter criteria, sort criteria, pager settings and contextual
|
||||
// filters.
|
||||
$default = $view->displayHandlers->get('default');
|
||||
$default->setOption('filters', [
|
||||
'status' => [
|
||||
'id' => 'status',
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'status',
|
||||
'relationship' => 'none',
|
||||
'operator' => '=',
|
||||
'value' => 1,
|
||||
],
|
||||
]);
|
||||
$default->setOption('sorts', [
|
||||
'name' => [
|
||||
'id' => 'name',
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
'order' => 'ASC',
|
||||
],
|
||||
]);
|
||||
$default->setOption('pager', [
|
||||
'type' => 'mini',
|
||||
'options' => ['items_per_page' => 10],
|
||||
]);
|
||||
$default->setOption('arguments', [
|
||||
'uid' => [
|
||||
'id' => 'uid',
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'uid',
|
||||
'relationship' => 'none',
|
||||
],
|
||||
]);
|
||||
|
||||
// Add display links to both page displays.
|
||||
$display_links = [
|
||||
'display_link_1' => [
|
||||
'id' => 'display_link_1',
|
||||
'table' => 'views',
|
||||
'field' => 'display_link',
|
||||
'display_id' => 'page_1',
|
||||
'label' => 'Page 1',
|
||||
'plugin_id' => 'display_link',
|
||||
],
|
||||
'display_link_2' => [
|
||||
'id' => 'display_link_2',
|
||||
'table' => 'views',
|
||||
'field' => 'display_link',
|
||||
'display_id' => 'page_2',
|
||||
'label' => 'Page 2',
|
||||
'plugin_id' => 'display_link',
|
||||
],
|
||||
];
|
||||
$default->setOption('header', $display_links);
|
||||
$view->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the views area display_link handler.
|
||||
*/
|
||||
public function testAreaDisplayLink() {
|
||||
$view = Views::getView('test_view');
|
||||
|
||||
// Assert only path-based displays are available in the display link
|
||||
// settings form.
|
||||
$view->setDisplay('page_1');
|
||||
$this->assertFormOptions($view, 'display_link_1');
|
||||
$this->assertFormOptions($view, 'display_link_2');
|
||||
$view->setDisplay('page_2');
|
||||
$this->assertFormOptions($view, 'display_link_1');
|
||||
$this->assertFormOptions($view, 'display_link_2');
|
||||
$view->setDisplay('block_1');
|
||||
$this->assertFormOptions($view, 'display_link_1');
|
||||
$this->assertFormOptions($view, 'display_link_2');
|
||||
|
||||
// Assert the links are rendered correctly for all displays.
|
||||
$this->assertRenderedDisplayLinks($view, 'page_1');
|
||||
$this->assertRenderedDisplayLinks($view, 'page_2');
|
||||
$this->assertRenderedDisplayLinks($view, 'block_1');
|
||||
|
||||
// Assert some special request parameters are filtered from the display
|
||||
// links.
|
||||
$request_stack = new RequestStack();
|
||||
$request_stack->push(Request::create('page_1', 'GET', [
|
||||
'name' => 'John',
|
||||
'sort_by' => 'created',
|
||||
'sort_order' => 'ASC',
|
||||
'page' => 1,
|
||||
'keep' => 'keep',
|
||||
'keep_another' => 1,
|
||||
'view_name' => 1,
|
||||
'view_display_id' => 1,
|
||||
'view_args' => 1,
|
||||
'view_path' => 1,
|
||||
'view_dom_id' => 1,
|
||||
'pager_element' => 1,
|
||||
'view_base_path' => 1,
|
||||
AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER => 1,
|
||||
FormBuilderInterface::AJAX_FORM_REQUEST => 1,
|
||||
MainContentViewSubscriber::WRAPPER_FORMAT => 1,
|
||||
]));
|
||||
$this->container->set('request_stack', $request_stack);
|
||||
$view->destroy();
|
||||
$view->setDisplay('page_1');
|
||||
$view->setCurrentPage(2);
|
||||
$this->executeView($view, [1]);
|
||||
$this->assertSame('<a href="/page_1/1?name=John&sort_by=created&sort_order=ASC&keep=keep&keep_another=1&page=1" class="views-display-link views-display-link-page_1 is-active">Page 1</a>', $this->renderDisplayLink($view, 'display_link_1'));
|
||||
$this->assertSame('<a href="/page_2/1?name=John&sort_by=created&sort_order=ASC&keep=keep&keep_another=1&page=1" class="views-display-link views-display-link-page_2">Page 2</a>', $this->renderDisplayLink($view, 'display_link_2'));
|
||||
|
||||
// Assert the validation adds warning messages when a display link is added
|
||||
// to a display with different filter criteria, sort criteria, pager
|
||||
// settings or contextual filters. Since all options are added to the
|
||||
// default display there currently should be no warning messages.
|
||||
$this->assertNoWarningMessages($view);
|
||||
|
||||
// Assert the message are shown when changing the filter criteria of page_1.
|
||||
$filters = [
|
||||
'name' => [
|
||||
'id' => 'name',
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
'operator' => '=',
|
||||
'value' => '',
|
||||
'exposed' => TRUE,
|
||||
'expose' => [
|
||||
'identifier' => 'name',
|
||||
'label' => 'Name',
|
||||
],
|
||||
],
|
||||
];
|
||||
$view->displayHandlers->get('page_1')->overrideOption('filters', $filters);
|
||||
$this->assertWarningMessages($view, ['filters']);
|
||||
|
||||
// Assert no messages are added after the default display is changed with
|
||||
// the same options.
|
||||
$view->displayHandlers->get('default')->overrideOption('filters', $filters);
|
||||
$this->assertNoWarningMessages($view);
|
||||
|
||||
// Assert the message are shown when changing the sort criteria of page_1.
|
||||
$sorts = [
|
||||
'created' => [
|
||||
'id' => 'created',
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'created',
|
||||
'relationship' => 'none',
|
||||
'order' => 'DESC',
|
||||
'exposed' => TRUE,
|
||||
],
|
||||
];
|
||||
$view->displayHandlers->get('page_1')->overrideOption('sorts', $sorts);
|
||||
$this->assertWarningMessages($view, ['sorts']);
|
||||
|
||||
// Assert no messages are added after the default display is changed with
|
||||
// the same options.
|
||||
$view->displayHandlers->get('default')->overrideOption('sorts', $sorts);
|
||||
$this->assertNoWarningMessages($view);
|
||||
|
||||
// Assert the message are shown when changing the sort criteria of page_1.
|
||||
$pager = [
|
||||
'type' => 'full',
|
||||
'options' => ['items_per_page' => 10],
|
||||
];
|
||||
$view->displayHandlers->get('page_1')->overrideOption('pager', $pager);
|
||||
$this->assertWarningMessages($view, ['pager']);
|
||||
|
||||
// Assert no messages are added after the default display is changed with
|
||||
// the same options.
|
||||
$view->displayHandlers->get('default')->overrideOption('pager', $pager);
|
||||
$this->assertNoWarningMessages($view);
|
||||
|
||||
// Assert the message are shown when changing the contextual filters of
|
||||
// page_1.
|
||||
$arguments = [
|
||||
'id' => [
|
||||
'id' => 'id',
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
],
|
||||
];
|
||||
$view->displayHandlers->get('page_1')->overrideOption('arguments', $arguments);
|
||||
$this->assertWarningMessages($view, ['arguments']);
|
||||
|
||||
// Assert no messages are added after the default display is changed with
|
||||
// the same options.
|
||||
$view->displayHandlers->get('default')->overrideOption('arguments', $arguments);
|
||||
$this->assertNoWarningMessages($view);
|
||||
|
||||
// Assert an error is shown when the display ID is not set.
|
||||
$display_link = [
|
||||
'display_link_3' => [
|
||||
'id' => 'display_link_3',
|
||||
'table' => 'views',
|
||||
'field' => 'display_link',
|
||||
'display_id' => '',
|
||||
'label' => 'Empty',
|
||||
'plugin_id' => 'display_link',
|
||||
],
|
||||
];
|
||||
$view->displayHandlers->get('page_1')->overrideOption('header', $display_link);
|
||||
$view->destroy();
|
||||
$view->setDisplay('page_1');
|
||||
$errors = $view->validate();
|
||||
$this->assertCount(1, $errors);
|
||||
$this->assertCount(1, $errors['page_1']);
|
||||
$this->assertSame('<em class="placeholder">Page 1</em>: The link in the <em class="placeholder">header</em> area has no configured display.', $errors['page_1'][0]->__toString());
|
||||
|
||||
// Assert an error is shown when linking to a display ID that doesn't exist.
|
||||
$display_link['display_link_3']['display_id'] = 'non-existent';
|
||||
$view->displayHandlers->get('page_1')->overrideOption('header', $display_link);
|
||||
$view->destroy();
|
||||
$view->setDisplay('page_1');
|
||||
$errors = $view->validate();
|
||||
$this->assertCount(1, $errors);
|
||||
$this->assertCount(1, $errors['page_1']);
|
||||
$this->assertSame('<em class="placeholder">Page 1</em>: The link in the <em class="placeholder">header</em> area points to the <em class="placeholder">non-existent</em> display which no longer exists.', $errors['page_1'][0]->__toString());
|
||||
|
||||
// Assert an error is shown when linking to a display without a path.
|
||||
$display_link['display_link_3']['display_id'] = 'block_1';
|
||||
$view->displayHandlers->get('page_1')->overrideOption('header', $display_link);
|
||||
$view->destroy();
|
||||
$view->setDisplay('page_1');
|
||||
$errors = $view->validate();
|
||||
$this->assertCount(1, $errors);
|
||||
$this->assertCount(1, $errors['page_1']);
|
||||
$this->assertSame('<em class="placeholder">Page 1</em>: The link in the <em class="placeholder">header</em> area points to the <em class="placeholder">Block 1</em> display which does not have a path.', $errors['page_1'][0]->__toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the display options contains only path based displays.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view to check.
|
||||
* @param string $display_link_id
|
||||
* The display link ID to check the options for.
|
||||
*/
|
||||
protected function assertFormOptions(ViewExecutable $view, $display_link_id) {
|
||||
$form = [];
|
||||
$form_state = new FormState();
|
||||
$view->display_handler->getHandler('header', $display_link_id)->buildOptionsForm($form, $form_state);
|
||||
$this->assertTrue(isset($form['display_id']['#options']['page_1']));
|
||||
$this->assertTrue(isset($form['display_id']['#options']['page_2']));
|
||||
$this->assertFalse(isset($form['display_id']['#options']['block_1']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the display links are correctly rendered for a display.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view to check.
|
||||
* @param string $display_id
|
||||
* The display ID to check the links for.
|
||||
*/
|
||||
protected function assertRenderedDisplayLinks(ViewExecutable $view, $display_id) {
|
||||
$page_1_active = $display_id === 'page_1' ? ' is-active' : '';
|
||||
$page_2_active = $display_id === 'page_2' ? ' is-active' : '';
|
||||
|
||||
$view->destroy();
|
||||
$view->setDisplay($display_id);
|
||||
$this->executeView($view);
|
||||
$this->assertSame('<a href="/page_1" class="views-display-link views-display-link-page_1' . $page_1_active . '">Page 1</a>', $this->renderDisplayLink($view, 'display_link_1'));
|
||||
$this->assertSame('<a href="/page_2" class="views-display-link views-display-link-page_2' . $page_2_active . '">Page 2</a>', $this->renderDisplayLink($view, 'display_link_2'));
|
||||
|
||||
// Assert the exposed filters, pager and contextual links are passed
|
||||
// correctly in the links.
|
||||
$view->destroy();
|
||||
$view->setDisplay($display_id);
|
||||
$view->setExposedInput([
|
||||
'name' => 'John',
|
||||
'sort_by' => 'created',
|
||||
'sort_order' => 'ASC',
|
||||
]);
|
||||
$view->setCurrentPage(2);
|
||||
$this->executeView($view, [1]);
|
||||
$this->assertSame('<a href="/page_1/1?name=John&sort_by=created&sort_order=ASC&page=1" class="views-display-link views-display-link-page_1' . $page_1_active . '">Page 1</a>', $this->renderDisplayLink($view, 'display_link_1'));
|
||||
$this->assertSame('<a href="/page_2/1?name=John&sort_by=created&sort_order=ASC&page=1" class="views-display-link views-display-link-page_2' . $page_2_active . '">Page 2</a>', $this->renderDisplayLink($view, 'display_link_2'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a display link.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view to render the link for.
|
||||
* @param string $display_link_id
|
||||
* The display link ID to render.
|
||||
*
|
||||
* @return string
|
||||
* The rendered display link.
|
||||
*/
|
||||
protected function renderDisplayLink(ViewExecutable $view, $display_link_id) {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$display_link = $view->display_handler->getHandler('header', $display_link_id)->render();
|
||||
return $renderer->renderRoot($display_link)->__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert no warning messages are shown when all display are equal.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view to check.
|
||||
*/
|
||||
protected function assertNoWarningMessages(ViewExecutable $view) {
|
||||
$messenger = $this->container->get('messenger');
|
||||
|
||||
$view->validate();
|
||||
$this->assertCount(0, $messenger->messagesByType(MessengerInterface::TYPE_WARNING));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the warning messages are shown after changing the page_1 display.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view to check.
|
||||
* @param array $unequal_options
|
||||
* An array of options that should be unequal.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function assertWarningMessages(ViewExecutable $view, array $unequal_options) {
|
||||
$messenger = $this->container->get('messenger');
|
||||
|
||||
// Create a list of options to check.
|
||||
// @see \Drupal\views\Plugin\views\area\DisplayLink::validate()
|
||||
$options = [
|
||||
'filters' => 'Filter criteria',
|
||||
'sorts' => 'Sort criteria',
|
||||
'pager' => 'Pager',
|
||||
'arguments' => 'Contextual filters',
|
||||
];
|
||||
|
||||
// Create a list of options to check.
|
||||
// @see \Drupal\views\Plugin\views\area\DisplayLink::validate()
|
||||
$unequal_options_text = implode(', ', array_intersect_key($options, array_flip($unequal_options)));
|
||||
|
||||
$errors = $view->validate();
|
||||
$messages = $messenger->messagesByType(MessengerInterface::TYPE_WARNING);
|
||||
|
||||
$this->assertCount(0, $errors);
|
||||
$this->assertCount(3, $messages);
|
||||
$this->assertSame('<em class="placeholder">Block 1</em>: The link in the <em class="placeholder">header</em> area points to the <em class="placeholder">Page 1</em> display which uses different settings than the <em class="placeholder">Block 1</em> display for: <em class="placeholder">' . $unequal_options_text . '</em>. To make sure users see the exact same result when clicking the link, please check that the settings are the same.', $messages[0]->__toString());
|
||||
$this->assertSame('<em class="placeholder">Page 1</em>: The link in the <em class="placeholder">header</em> area points to the <em class="placeholder">Page 2</em> display which uses different settings than the <em class="placeholder">Page 1</em> display for: <em class="placeholder">' . $unequal_options_text . '</em>. To make sure users see the exact same result when clicking the link, please check that the settings are the same.', $messages[1]->__toString());
|
||||
$this->assertSame('<em class="placeholder">Page 2</em>: The link in the <em class="placeholder">header</em> area points to the <em class="placeholder">Page 1</em> display which uses different settings than the <em class="placeholder">Page 2</em> display for: <em class="placeholder">' . $unequal_options_text . '</em>. To make sure users see the exact same result when clicking the link, please check that the settings are the same.', $messages[2]->__toString());
|
||||
|
||||
$messenger->deleteAll();
|
||||
|
||||
// If the master display is shown in the UI, warnings should be shown for
|
||||
// this display as well.
|
||||
$this->config('views.settings')->set('ui.show.master_display', TRUE)->save();
|
||||
|
||||
$errors = $view->validate();
|
||||
$messages = $messenger->messagesByType(MessengerInterface::TYPE_WARNING);
|
||||
|
||||
$this->assertCount(0, $errors);
|
||||
$this->assertCount(4, $messages);
|
||||
$this->assertSame('<em class="placeholder">Master</em>: The link in the <em class="placeholder">header</em> area points to the <em class="placeholder">Page 1</em> display which uses different settings than the <em class="placeholder">Master</em> display for: <em class="placeholder">' . $unequal_options_text . '</em>. To make sure users see the exact same result when clicking the link, please check that the settings are the same.', $messages[0]->__toString());
|
||||
$this->assertSame('<em class="placeholder">Block 1</em>: The link in the <em class="placeholder">header</em> area points to the <em class="placeholder">Page 1</em> display which uses different settings than the <em class="placeholder">Block 1</em> display for: <em class="placeholder">' . $unequal_options_text . '</em>. To make sure users see the exact same result when clicking the link, please check that the settings are the same.', $messages[1]->__toString());
|
||||
$this->assertSame('<em class="placeholder">Page 1</em>: The link in the <em class="placeholder">header</em> area points to the <em class="placeholder">Page 2</em> display which uses different settings than the <em class="placeholder">Page 1</em> display for: <em class="placeholder">' . $unequal_options_text . '</em>. To make sure users see the exact same result when clicking the link, please check that the settings are the same.', $messages[2]->__toString());
|
||||
$this->assertSame('<em class="placeholder">Page 2</em>: The link in the <em class="placeholder">header</em> area points to the <em class="placeholder">Page 1</em> display which uses different settings than the <em class="placeholder">Page 2</em> display for: <em class="placeholder">' . $unequal_options_text . '</em>. To make sure users see the exact same result when clicking the link, please check that the settings are the same.', $messages[3]->__toString());
|
||||
|
||||
$messenger->deleteAll();
|
||||
$this->config('views.settings')->set('ui.show.master_display', FALSE)->save();
|
||||
}
|
||||
|
||||
}
|
|
@ -129,6 +129,14 @@ function views_views_data() {
|
|||
],
|
||||
];
|
||||
|
||||
$data['views']['display_link'] = [
|
||||
'title' => t('Link to display'),
|
||||
'help' => t('Displays a link to a path-based display of this view while keeping the filter criteria, sort criteria, pager settings and contextual filters.'),
|
||||
'area' => [
|
||||
'id' => 'display_link',
|
||||
],
|
||||
];
|
||||
|
||||
// Registers an entity area handler per entity type.
|
||||
foreach (\Drupal::entityManager()->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
// Excludes entity types, which cannot be rendered.
|
||||
|
|
|
@ -17,3 +17,7 @@
|
|||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
/* Provide some space between display links. */
|
||||
.views-display-link + .views-display-link {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue