Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
321
core/modules/views_ui/admin.inc
Normal file
|
@ -0,0 +1,321 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the Views' administrative interface.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Converts a form element in the add view wizard to be AJAX-enabled.
|
||||
*
|
||||
* This function takes a form element and adds AJAX behaviors to it such that
|
||||
* changing it triggers another part of the form to update automatically. It
|
||||
* also adds a submit button to the form that appears next to the triggering
|
||||
* element and that duplicates its functionality for users who do not have
|
||||
* JavaScript enabled (the button is automatically hidden for users who do have
|
||||
* JavaScript).
|
||||
*
|
||||
* To use this function, call it directly from your form builder function
|
||||
* immediately after you have defined the form element that will serve as the
|
||||
* JavaScript trigger. Calling it elsewhere (such as in hook_form_alter()) may
|
||||
* mean that the non-JavaScript fallback button does not appear in the correct
|
||||
* place in the form.
|
||||
*
|
||||
* @param $wrapping_element
|
||||
* The element whose child will server as the AJAX trigger. For example, if
|
||||
* $form['some_wrapper']['triggering_element'] represents the element which
|
||||
* will trigger the AJAX behavior, you would pass $form['some_wrapper'] for
|
||||
* this parameter.
|
||||
* @param $trigger_key
|
||||
* The key within the wrapping element that identifies which of its children
|
||||
* serves as the AJAX trigger. In the above example, you would pass
|
||||
* 'triggering_element' for this parameter.
|
||||
* @param $refresh_parents
|
||||
* An array of parent keys that point to the part of the form that will be
|
||||
* refreshed by AJAX. For example, if triggering the AJAX behavior should
|
||||
* cause $form['dynamic_content']['section'] to be refreshed, you would pass
|
||||
* array('dynamic_content', 'section') for this parameter.
|
||||
*/
|
||||
function views_ui_add_ajax_trigger(&$wrapping_element, $trigger_key, $refresh_parents) {
|
||||
$seen_ids = &drupal_static(__FUNCTION__ . ':seen_ids', array());
|
||||
$seen_buttons = &drupal_static(__FUNCTION__ . ':seen_buttons', array());
|
||||
|
||||
// Add the AJAX behavior to the triggering element.
|
||||
$triggering_element = &$wrapping_element[$trigger_key];
|
||||
$triggering_element['#ajax']['callback'] = 'views_ui_ajax_update_form';
|
||||
|
||||
// We do not use \Drupal\Component\Utility\Html::getUniqueId() to get an ID
|
||||
// for the AJAX wrapper, because it remembers IDs across AJAX requests (and
|
||||
// won't reuse them), but in our case we need to use the same ID from request
|
||||
// to request so that the wrapper can be recognized by the AJAX system and
|
||||
// its content can be dynamically updated. So instead, we will keep track of
|
||||
// duplicate IDs (within a single request) on our own, later in this function.
|
||||
$triggering_element['#ajax']['wrapper'] = 'edit-view-' . implode('-', $refresh_parents) . '-wrapper';
|
||||
|
||||
// Add a submit button for users who do not have JavaScript enabled. It
|
||||
// should be displayed next to the triggering element on the form.
|
||||
$button_key = $trigger_key . '_trigger_update';
|
||||
$element_info = \Drupal::service('element_info');
|
||||
$wrapping_element[$button_key] = array(
|
||||
'#type' => 'submit',
|
||||
// Hide this button when JavaScript is enabled.
|
||||
'#attributes' => array('class' => array('js-hide')),
|
||||
'#submit' => array('views_ui_nojs_submit'),
|
||||
// Add a process function to limit this button's validation errors to the
|
||||
// triggering element only. We have to do this in #process since until the
|
||||
// form API has added the #parents property to the triggering element for
|
||||
// us, we don't have any (easy) way to find out where its submitted values
|
||||
// will eventually appear in $form_state->getValues().
|
||||
'#process' => array_merge(array('views_ui_add_limited_validation'), $element_info->getInfoProperty('submit', '#process', array())),
|
||||
// Add an after-build function that inserts a wrapper around the region of
|
||||
// the form that needs to be refreshed by AJAX (so that the AJAX system can
|
||||
// detect and dynamically update it). This is done in #after_build because
|
||||
// it's a convenient place where we have automatic access to the complete
|
||||
// form array, but also to minimize the chance that the HTML we add will
|
||||
// get clobbered by code that runs after we have added it.
|
||||
'#after_build' => array_merge($element_info->getInfoProperty('submit', '#after_build', array()), array('views_ui_add_ajax_wrapper')),
|
||||
);
|
||||
// Copy #weight and #access from the triggering element to the button, so
|
||||
// that the two elements will be displayed together.
|
||||
foreach (array('#weight', '#access') as $property) {
|
||||
if (isset($triggering_element[$property])) {
|
||||
$wrapping_element[$button_key][$property] = $triggering_element[$property];
|
||||
}
|
||||
}
|
||||
// For easiest integration with the form API and the testing framework, we
|
||||
// always give the button a unique #value, rather than playing around with
|
||||
// #name.
|
||||
$button_title = !empty($triggering_element['#title']) ? $triggering_element['#title'] : $trigger_key;
|
||||
if (empty($seen_buttons[$button_title])) {
|
||||
$wrapping_element[$button_key]['#value'] = t('Update "@title" choice', array(
|
||||
'@title' => $button_title,
|
||||
));
|
||||
$seen_buttons[$button_title] = 1;
|
||||
}
|
||||
else {
|
||||
$wrapping_element[$button_key]['#value'] = t('Update "@title" choice (@number)', array(
|
||||
'@title' => $button_title,
|
||||
'@number' => ++$seen_buttons[$button_title],
|
||||
));
|
||||
}
|
||||
|
||||
// Attach custom data to the triggering element and submit button, so we can
|
||||
// use it in both the process function and AJAX callback.
|
||||
$ajax_data = array(
|
||||
'wrapper' => $triggering_element['#ajax']['wrapper'],
|
||||
'trigger_key' => $trigger_key,
|
||||
'refresh_parents' => $refresh_parents,
|
||||
);
|
||||
$seen_ids[$triggering_element['#ajax']['wrapper']] = TRUE;
|
||||
$triggering_element['#views_ui_ajax_data'] = $ajax_data;
|
||||
$wrapping_element[$button_key]['#views_ui_ajax_data'] = $ajax_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a non-JavaScript fallback submit button to limit its validation errors.
|
||||
*/
|
||||
function views_ui_add_limited_validation($element, FormStateInterface $form_state) {
|
||||
// Retrieve the AJAX triggering element so we can determine its parents. (We
|
||||
// know it's at the same level of the complete form array as the submit
|
||||
// button, so all we have to do to find it is swap out the submit button's
|
||||
// last array parent.)
|
||||
$array_parents = $element['#array_parents'];
|
||||
array_pop($array_parents);
|
||||
$array_parents[] = $element['#views_ui_ajax_data']['trigger_key'];
|
||||
$ajax_triggering_element = NestedArray::getValue($form_state->getCompleteForm(), $array_parents);
|
||||
|
||||
// Limit this button's validation to the AJAX triggering element, so it can
|
||||
// update the form for that change without requiring that the rest of the
|
||||
// form be filled out properly yet.
|
||||
$element['#limit_validation_errors'] = array($ajax_triggering_element['#parents']);
|
||||
|
||||
// If we are in the process of a form submission and this is the button that
|
||||
// was clicked, the form API workflow in \Drupal::formBuilder()->doBuildForm()
|
||||
// will have already copied it to $form_state->getTriggeringElement() before
|
||||
// our #process function is run. So we need to make the same modifications in
|
||||
// $form_state as we did to the element itself, to ensure that
|
||||
// #limit_validation_errors will actually be set in the correct place.
|
||||
$clicked_button = &$form_state->getTriggeringElement();
|
||||
if ($clicked_button && $clicked_button['#name'] == $element['#name'] && $clicked_button['#value'] == $element['#value']) {
|
||||
$clicked_button['#limit_validation_errors'] = $element['#limit_validation_errors'];
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* After-build function that adds a wrapper to a form region (for AJAX refreshes).
|
||||
*
|
||||
* This function inserts a wrapper around the region of the form that needs to
|
||||
* be refreshed by AJAX, based on information stored in the corresponding
|
||||
* submit button form element.
|
||||
*/
|
||||
function views_ui_add_ajax_wrapper($element, FormStateInterface $form_state) {
|
||||
// Find the region of the complete form that needs to be refreshed by AJAX.
|
||||
// This was earlier stored in a property on the element.
|
||||
$complete_form = &$form_state->getCompleteForm();
|
||||
$refresh_parents = $element['#views_ui_ajax_data']['refresh_parents'];
|
||||
$refresh_element = NestedArray::getValue($complete_form, $refresh_parents);
|
||||
|
||||
// The HTML ID that AJAX expects was also stored in a property on the
|
||||
// element, so use that information to insert the wrapper <div> here.
|
||||
$id = $element['#views_ui_ajax_data']['wrapper'];
|
||||
$refresh_element += array(
|
||||
'#prefix' => '',
|
||||
'#suffix' => '',
|
||||
);
|
||||
$refresh_element['#prefix'] = '<div id="' . $id . '" class="views-ui-ajax-wrapper">' . $refresh_element['#prefix'];
|
||||
$refresh_element['#suffix'] .= '</div>';
|
||||
|
||||
// Copy the element that needs to be refreshed back into the form, with our
|
||||
// modifications to it.
|
||||
NestedArray::setValue($complete_form, $refresh_parents, $refresh_element);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a part of the add view form via AJAX.
|
||||
*
|
||||
* @return
|
||||
* The part of the form that has changed.
|
||||
*/
|
||||
function views_ui_ajax_update_form($form, FormStateInterface $form_state) {
|
||||
// The region that needs to be updated was stored in a property of the
|
||||
// triggering element by views_ui_add_ajax_trigger(), so all we have to do is
|
||||
// retrieve that here.
|
||||
return NestedArray::getValue($form, $form_state->getTriggeringElement()['#views_ui_ajax_data']['refresh_parents']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-Javascript fallback for updating the add view form.
|
||||
*/
|
||||
function views_ui_nojs_submit($form, FormStateInterface $form_state) {
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a <select> dropdown for a given section, allowing the user to
|
||||
* change whether this info is stored on the default display or on
|
||||
* the current display.
|
||||
*/
|
||||
function views_ui_standard_display_dropdown(&$form, FormStateInterface $form_state, $section) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$executable = $view->getExecutable();
|
||||
$displays = $executable->displayHandlers;
|
||||
$current_display = $executable->display_handler;
|
||||
|
||||
// @todo Move this to a separate function if it's needed on any forms that
|
||||
// don't have the display dropdown.
|
||||
$form['override'] = array(
|
||||
'#prefix' => '<div class="views-override clearfix form--inline views-offset-top" data-drupal-views-offset="top">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -1000,
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
// Add the "2 of 3" progress indicator.
|
||||
if ($form_progress = $view->getFormProgress()) {
|
||||
$form['progress']['#markup'] = '<div id="views-progress-indicator" class="views-progress-indicator">' . t('@current of @total', array('@current' => $form_progress['current'], '@total' => $form_progress['total'])) . '</div>';
|
||||
$form['progress']['#weight'] = -1001;
|
||||
}
|
||||
|
||||
if ($current_display->isDefaultDisplay()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine whether any other displays have overrides for this section.
|
||||
$section_overrides = FALSE;
|
||||
$section_defaulted = $current_display->isDefaulted($section);
|
||||
foreach ($displays as $id => $display) {
|
||||
if ($id === 'default' || $id === $display_id) {
|
||||
continue;
|
||||
}
|
||||
if ($display && !$display->isDefaulted($section)) {
|
||||
$section_overrides = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
$display_dropdown['default'] = ($section_overrides ? t('All displays (except overridden)') : t('All displays'));
|
||||
$display_dropdown[$display_id] = t('This @display_type (override)', array('@display_type' => $current_display->getPluginId()));
|
||||
// Only display the revert option if we are in a overridden section.
|
||||
if (!$section_defaulted) {
|
||||
$display_dropdown['default_revert'] = t('Revert to default');
|
||||
}
|
||||
|
||||
$form['override']['dropdown'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('For'), // @TODO: Translators may need more context than this.
|
||||
'#options' => $display_dropdown,
|
||||
);
|
||||
if ($current_display->isDefaulted($section)) {
|
||||
$form['override']['dropdown']['#default_value'] = 'defaults';
|
||||
}
|
||||
else {
|
||||
$form['override']['dropdown']['#default_value'] = $display_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the menu path for one of our standard AJAX forms based upon known
|
||||
* information about the form.
|
||||
*
|
||||
* @return \Drupal\Core\Url
|
||||
* The URL object pointing to the form URL.
|
||||
*/
|
||||
function views_ui_build_form_url(FormStateInterface $form_state) {
|
||||
$ajax = !$form_state->get('ajax') ? 'nojs' : 'ajax';
|
||||
$name = $form_state->get('view')->id();
|
||||
$form_key = $form_state->get('form_key');
|
||||
$display_id = $form_state->get('display_id');
|
||||
|
||||
$form_key = str_replace('-', '_', $form_key);
|
||||
$route_name = "views_ui.form_{$form_key}";
|
||||
$route_parameters = [
|
||||
'js' => $ajax,
|
||||
'view' => $name,
|
||||
'display_id' => $display_id
|
||||
];
|
||||
$url = Url::fromRoute($route_name, $route_parameters);
|
||||
if ($type = $form_state->get('type')) {
|
||||
$url->setRouteParameter('type', $type);
|
||||
}
|
||||
if ($id = $form_state->get('id')) {
|
||||
$url->setRouteParameter('id', $id);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* #process callback for a button; determines if a button is the form's triggering element.
|
||||
*
|
||||
* The Form API has logic to determine the form's triggering element based on
|
||||
* the data in POST. However, it only checks buttons based on a single #value
|
||||
* per button. This function may be added to a button's #process callbacks to
|
||||
* extend button click detection to support multiple #values per button. If the
|
||||
* data in POST matches any value in the button's #values array, then the
|
||||
* button is detected as having been clicked. This can be used when the value
|
||||
* (label) of the same logical button may be different based on context (e.g.,
|
||||
* "Apply" vs. "Apply and continue").
|
||||
*
|
||||
* @see _form_builder_handle_input_element()
|
||||
* @see _form_button_was_clicked()
|
||||
*/
|
||||
function views_ui_form_button_was_clicked($element, FormStateInterface $form_state) {
|
||||
$user_input = $form_state->getUserInput();
|
||||
$process_input = empty($element['#disabled']) && ($form_state->isProgrammed() || ($form_state->isProcessingInput() && (!isset($element['#access']) || $element['#access'])));
|
||||
if ($process_input && !$form_state->getTriggeringElement() && !empty($element['#is_button']) && isset($user_input[$element['#name']]) && isset($element['#values']) && in_array($user_input[$element['#name']], $element['#values'], TRUE)) {
|
||||
$form_state->setTriggeringElement($element);
|
||||
}
|
||||
return $element;
|
||||
}
|
91
core/modules/views_ui/config/optional/tour.tour.views-ui.yml
Normal file
|
@ -0,0 +1,91 @@
|
|||
id: views-ui
|
||||
module: views_ui
|
||||
label: 'View edit page'
|
||||
langcode: en
|
||||
routes:
|
||||
- route_name: entity.view.edit_form
|
||||
- route_name: entity.view.edit_display_form
|
||||
tips:
|
||||
views-main:
|
||||
id: views-main
|
||||
plugin: text
|
||||
label: 'Manage view settings'
|
||||
body: 'View or edit the configuration.'
|
||||
weight: 1
|
||||
views-ui-displays:
|
||||
id: views-ui-displays
|
||||
plugin: text
|
||||
label: 'Displays in this view'
|
||||
body: 'A display is a way of outputting the results, e.g., as a page or a block. A view can contain multiple displays, which are listed here. The active display is highlighted.'
|
||||
weight: 2
|
||||
attributes:
|
||||
data-id: views-display-top
|
||||
views-ui-view-admin:
|
||||
id: views-ui-view-admin
|
||||
plugin: text
|
||||
label: 'View administration'
|
||||
body: 'Perform administrative tasks, including adding a description and creating a clone. Click the drop-down button to view the available options.'
|
||||
weight: 3
|
||||
location: left
|
||||
attributes:
|
||||
data-id: views-display-extra-actions
|
||||
views-ui-format:
|
||||
id: views-ui-format
|
||||
plugin: text
|
||||
label: 'Output format'
|
||||
body: 'Choose how to output results. E.g., choose <em>Content</em> to output each item completely, using your configured display settings. Or choose <em>Fields</em>, which allows you to output only specific fields for each result. Additional formats can be added by installing modules to <em>extend</em> Drupal''s base functionality.'
|
||||
weight: 4
|
||||
attributes:
|
||||
data-class: views-ui-display-tab-bucket.format
|
||||
views-ui-fields:
|
||||
id: views-ui-fields
|
||||
plugin: text
|
||||
label: Fields
|
||||
body: 'If this view uses fields, they are listed here. You can click on a field to configure it.'
|
||||
weight: 5
|
||||
attributes:
|
||||
data-class: views-ui-display-tab-bucket.field
|
||||
views-ui-filter:
|
||||
id: views-ui-filter
|
||||
plugin: text
|
||||
label: 'Filter your view'
|
||||
body: 'Add filters to limit the results in the output. E.g., to only show content that is <em>published</em>, you would add a filter for <em>Published</em> and select <em>Yes</em>.'
|
||||
weight: 6
|
||||
attributes:
|
||||
data-class: views-ui-display-tab-bucket.filter
|
||||
views-ui-filter-operations:
|
||||
id: views-ui-filter-operations
|
||||
plugin: text
|
||||
label: 'Filter actions'
|
||||
body: 'Add, rearrange or remove filters.'
|
||||
weight: 7
|
||||
attributes:
|
||||
data-class: 'views-ui-display-tab-bucket.filter .dropbutton-widget'
|
||||
views-ui-sorts:
|
||||
id: views-ui-sorts
|
||||
plugin: text
|
||||
label: 'Sort Criteria'
|
||||
body: 'Control the order in which the results are output. Click on an active sort rule to configure it.'
|
||||
weight: 8
|
||||
attributes:
|
||||
data-class: views-ui-display-tab-bucket.sort
|
||||
views-ui-sorts-operations:
|
||||
id: views-ui-sorts-operations
|
||||
plugin: text
|
||||
label: 'Sort actions'
|
||||
body: 'Add, rearrange or remove sorting rules.'
|
||||
weight: 9
|
||||
attributes:
|
||||
data-class: 'views-ui-display-tab-bucket.sort .dropbutton-widget'
|
||||
views-ui-preview:
|
||||
id: views-ui-preview
|
||||
plugin: text
|
||||
label: 'Preview'
|
||||
body: 'Show a preview of the view output.'
|
||||
weight: 10
|
||||
location: left
|
||||
attributes:
|
||||
data-id: preview-submit
|
||||
dependencies:
|
||||
module:
|
||||
- views_ui
|
208
core/modules/views_ui/css/views_ui.admin.css
Normal file
|
@ -0,0 +1,208 @@
|
|||
/**
|
||||
* @file
|
||||
* The .admin.css file is intended to only contain positioning and size
|
||||
* declarations. For example: display, position, float, clear, and overflow.
|
||||
*/
|
||||
|
||||
.views-admin ul,
|
||||
.views-admin menu,
|
||||
.views-admin dir {
|
||||
padding: 0;
|
||||
}
|
||||
.views-admin pre {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.views-left-25 {
|
||||
float: left; /* LTR */
|
||||
width: 25%;
|
||||
}
|
||||
[dir="rtl"] .views-left-25 {
|
||||
float: right;
|
||||
}
|
||||
.views-left-30 {
|
||||
float: left; /* LTR */
|
||||
width: 30%;
|
||||
}
|
||||
[dir="rtl"] .views-left-30 {
|
||||
float: right;
|
||||
}
|
||||
.views-left-40 {
|
||||
float: left; /* LTR */
|
||||
width: 40%;
|
||||
}
|
||||
[dir="rtl"] .views-left-40 {
|
||||
float: right;
|
||||
}
|
||||
.views-left-50 {
|
||||
float: left; /* LTR */
|
||||
width: 50%;
|
||||
}
|
||||
[dir="rtl"] .views-left-50 {
|
||||
float: right;
|
||||
}
|
||||
.views-left-75 {
|
||||
float: left; /* LTR */
|
||||
width: 75%;
|
||||
}
|
||||
[dir="rtl"] .views-left-75 {
|
||||
float: right;
|
||||
}
|
||||
.views-right-50 {
|
||||
float: right; /* LTR */
|
||||
width: 50%;
|
||||
}
|
||||
[dir="rtl"] .views-right-50 {
|
||||
float: left;
|
||||
}
|
||||
.views-right-60 {
|
||||
float: right; /* LTR */
|
||||
width: 60%;
|
||||
}
|
||||
[dir="rtl"] .views-right-60 {
|
||||
float: left;
|
||||
}
|
||||
.views-right-70 {
|
||||
float: right; /* LTR */
|
||||
width: 70%;
|
||||
}
|
||||
[dir="rtl"] .views-right-70 {
|
||||
float: left;
|
||||
}
|
||||
.views-group-box .form-item {
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
/*
|
||||
* The attachment details section, its tabs for each section and the buttons
|
||||
* to add a new section
|
||||
*/
|
||||
.views-displays {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* The tabs that switch between sections */
|
||||
.views-displays .tabs {
|
||||
border-bottom: 0 none;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
padding: 0;
|
||||
}
|
||||
.views-displays .tabs > li {
|
||||
border-right: 0 none; /* LTR */
|
||||
float: left; /* LTR */
|
||||
padding: 0;
|
||||
}
|
||||
[dir="rtl"] .views-displays .tabs > li {
|
||||
float: right;
|
||||
border-left: 0 none;
|
||||
border-right: 1px solid #bfbfbf;
|
||||
}
|
||||
.views-displays .tabs .open > a {
|
||||
position: relative;
|
||||
z-index: 51;
|
||||
}
|
||||
.views-displays .tabs .views-display-deleted-link {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.views-display-deleted > details > summary,
|
||||
.views-display-deleted .details-wrapper > .views-ui-display-tab-bucket > *,
|
||||
.views-display-deleted .views-display-columns {
|
||||
opacity: 0.25;
|
||||
}
|
||||
.views-display-disabled > details > summary,
|
||||
.views-display-disabled .details-wrapper > .views-ui-display-tab-bucket > *,
|
||||
.views-display-disabled .views-display-columns {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.views-display-tab .details-wrapper > .views-ui-display-tab-bucket .actions {
|
||||
opacity: 1.0;
|
||||
}
|
||||
.views-displays .tabs .add {
|
||||
position: relative;
|
||||
}
|
||||
.views-displays .tabs .action-list {
|
||||
left: 0; /* LTR */
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 23px;
|
||||
z-index: 50;
|
||||
}
|
||||
[dir="rtl"] .views-displays .tabs .action-list {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
.views-displays .tabs .action-list li {
|
||||
display: block;
|
||||
}
|
||||
.views-display-columns .details-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
.views-display-column {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.views-display-columns > * {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
@media screen and (min-width:45em) { /* 720px */
|
||||
.views-display-columns > * {
|
||||
float: left; /* LTR */
|
||||
margin-left: 2%; /* LTR */
|
||||
margin-bottom: 0;
|
||||
width: 32%;
|
||||
}
|
||||
[dir="rtl"] .views-display-columns > * {
|
||||
float: right;
|
||||
margin-left: 0;
|
||||
margin-right: 2%;
|
||||
}
|
||||
.views-display-columns > *:first-child {
|
||||
margin-left: 0; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-display-columns > *:first-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.views-ui-dialog .scroll {
|
||||
overflow: auto;
|
||||
padding: 1em;
|
||||
}
|
||||
.views-filterable-options-controls {
|
||||
display: none;
|
||||
}
|
||||
.views-ui-dialog .views-filterable-options-controls {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Don't let the messages overwhelm the modal */
|
||||
.views-ui-dialog .views-messages {
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
.views-display-setting .label,
|
||||
.views-display-setting .views-ajax-link {
|
||||
float: left; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-display-setting .label,
|
||||
[dir="rtl"] .views-display-setting .views-ajax-link {
|
||||
float: right;
|
||||
}
|
||||
.form-item-options-value-all {
|
||||
display: none;
|
||||
}
|
||||
.js-only {
|
||||
display: none;
|
||||
}
|
||||
html.js .js-only {
|
||||
display: inherit;
|
||||
}
|
||||
html.js span.js-only {
|
||||
display: inline;
|
||||
}
|
||||
.js .views-edit-view .dropbutton-wrapper {
|
||||
width: auto;
|
||||
}
|
831
core/modules/views_ui/css/views_ui.admin.theme.css
Normal file
|
@ -0,0 +1,831 @@
|
|||
/**
|
||||
* @file
|
||||
* The .admin.theme.css file is intended to contain presentation declarations
|
||||
* including images, borders, colors, and fonts.
|
||||
*/
|
||||
|
||||
.views-admin .links {
|
||||
list-style: none outside none;
|
||||
margin: 0;
|
||||
}
|
||||
.views-admin a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.box-padding {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.box-margin {
|
||||
margin: 12px 12px 0 12px;
|
||||
}
|
||||
.views-admin .icon {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
.views-admin .icon,
|
||||
.views-admin .icon-text {
|
||||
background-attachment: scroll;
|
||||
background-image: url(../images/sprites.png);
|
||||
background-position: left top; /* LTR */
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
[dir="rtl"] .views-admin .icon,
|
||||
[dir="rtl"] .views-admin .icon-text {
|
||||
background-position: right top;
|
||||
}
|
||||
.views-admin a.icon {
|
||||
background: linear-gradient(-90deg, #fff 0, #e8e8e8 100%) no-repeat, repeat-y;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 0 rgba(0,0,0,0.3333) inset;
|
||||
}
|
||||
.views-admin a.icon:hover {
|
||||
border-color: #d0d0d0;
|
||||
box-shadow: 0 0 1px rgba(0,0,0,0.3333) inset;
|
||||
}
|
||||
.views-admin a.icon:active {
|
||||
border-color: #c0c0c0;
|
||||
}
|
||||
.views-admin span.icon {
|
||||
float: left; /* LTR */
|
||||
position: relative;
|
||||
}
|
||||
[dir="rtl"] .views-admin span.icon {
|
||||
float: right;
|
||||
}
|
||||
.views-admin .icon.compact {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
direction: ltr;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
/* Targets any element with an icon -> text combo */
|
||||
.views-admin .icon-text {
|
||||
padding-left: 19px; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-admin .icon-text {
|
||||
padding-left: 0;
|
||||
padding-right: 19px;
|
||||
}
|
||||
.views-admin .icon.linked {
|
||||
background-position: center -153px;
|
||||
}
|
||||
.views-admin .icon.unlinked {
|
||||
background-position: center -195px;
|
||||
}
|
||||
.views-admin .icon.add {
|
||||
background-position: center 3px;
|
||||
}
|
||||
.views-admin a.icon.add {
|
||||
background-position: center 3px, left top; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-admin a.icon.add {
|
||||
background-position: center 3px, right top;
|
||||
}
|
||||
.views-admin .icon.delete {
|
||||
background-position: center -52px;
|
||||
}
|
||||
.views-admin a.icon.delete {
|
||||
background-position: center -52px, left top; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-admin a.icon.delete {
|
||||
background-position: center -52px, right top;
|
||||
}
|
||||
.views-admin .icon.rearrange {
|
||||
background-position: center -111px;
|
||||
}
|
||||
.views-admin a.icon.rearrange {
|
||||
background-position: center -111px, left top; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-admin a.icon.rearrange {
|
||||
background-position: center -111px, right top;
|
||||
}
|
||||
.views-displays .tabs a:hover > .icon.add {
|
||||
background-position: center -25px;
|
||||
}
|
||||
.views-displays .tabs .open a:hover > .icon.add {
|
||||
background-position: center 3px;
|
||||
}
|
||||
details.box-padding {
|
||||
border: none;
|
||||
}
|
||||
.views-admin details details {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.form-item {
|
||||
margin-top: 9px;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
.form-type-checkbox {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.form-checkbox,
|
||||
.form-radio {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
.container-inline {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.container-inline > * + *,
|
||||
.container-inline .details-wrapper > * + * {
|
||||
padding-left: 4px; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .container-inline > * + *,
|
||||
[dir="rtl"] .container-inline .details-wrapper > * + * {
|
||||
padding-left: 0;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.views-admin details details.container-inline {
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1em;
|
||||
padding-top: 0;
|
||||
}
|
||||
.views-admin details details.container-inline > .details-wrapper {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
/* Indent form elements so they're directly underneath the label of the checkbox that reveals them */
|
||||
.views-admin .form-type-checkbox + .form-wrapper {
|
||||
margin-left: 16px; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-admin .form-type-checkbox + .form-wrapper {
|
||||
margin-left: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
/* Hide 'remove' checkboxes. */
|
||||
.views-remove-checkbox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* sizes the labels of checkboxes and radio button to the height of the text */
|
||||
.views-admin .form-type-checkbox label,
|
||||
.views-admin .form-type-radio label {
|
||||
line-height: 2;
|
||||
}
|
||||
.views-admin-dependent .form-item {
|
||||
margin-bottom: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.views-ui-view-title {
|
||||
font-weight: bold;
|
||||
margin-top: 0;
|
||||
}
|
||||
.view-changed {
|
||||
margin-bottom: 21px;
|
||||
}
|
||||
.views-admin .unit-title {
|
||||
font-size: 15px;
|
||||
line-height: 1.6154;
|
||||
margin-bottom: 0;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
/* These header classes are ambiguous and should be scoped to th elements */
|
||||
.views-ui-name {
|
||||
width: 18%;
|
||||
}
|
||||
.views-ui-description {
|
||||
width: 26%;
|
||||
}
|
||||
.views-ui-tag {
|
||||
width: 8%;
|
||||
}
|
||||
.views-ui-path {
|
||||
width: auto;
|
||||
}
|
||||
.views-ui-operations {
|
||||
width: 24%;
|
||||
}
|
||||
|
||||
/**
|
||||
* I wish this didn't have to be so specific
|
||||
*/
|
||||
.form-item-description-enable + .form-item-description {
|
||||
margin-top: 0;
|
||||
}
|
||||
.form-item-description-enable label {
|
||||
font-weight: bold;
|
||||
}
|
||||
.form-item-page-create,
|
||||
.form-item-block-create {
|
||||
margin-top: 13px;
|
||||
}
|
||||
.form-item-page-create label,
|
||||
.form-item-block-create label,
|
||||
.form-item-rest-export-create label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* This makes the form elements after the "Display Format" label flow underneath the label */
|
||||
.form-item-page-style-style-plugin > label,
|
||||
.form-item-block-style-style-plugin > label {
|
||||
display: block;
|
||||
}
|
||||
.views-attachment .options-set label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Styling for the form that allows views filters to be rearranged. */
|
||||
.group-populated {
|
||||
display: none;
|
||||
}
|
||||
td.group-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
.views-ui-dialog td.group-title {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.views-ui-dialog td.group-title span {
|
||||
display: block;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.group-message .form-submit,
|
||||
.views-remove-group-link,
|
||||
.views-add-group {
|
||||
float: right; /* LTR */
|
||||
clear: both;
|
||||
}
|
||||
[dir="rtl"] .group-message .form-submit,
|
||||
[dir="rtl"] .views-remove-group-link,
|
||||
[dir="rtl"] .views-add-group {
|
||||
float: left;
|
||||
}
|
||||
.views-operator-label {
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
padding-left: 0.5em; /* LTR */
|
||||
text-transform: uppercase;
|
||||
}
|
||||
[dir="rtl"] .views-operator-label {
|
||||
padding-left: 0;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
.grouped-description,
|
||||
.exposed-description {
|
||||
float: left; /* LTR */
|
||||
padding-top: 3px;
|
||||
padding-right: 10px; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .grouped-description,
|
||||
[dir="rtl"] .exposed-description {
|
||||
float: right;
|
||||
padding-left: 10px;
|
||||
padding-right: 0;
|
||||
}
|
||||
.views-displays {
|
||||
border: 1px solid #ccc;
|
||||
padding-bottom: 36px;
|
||||
}
|
||||
.views-display-top {
|
||||
background-color: #e1e2dc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 8px 8px 3px;
|
||||
position: relative;
|
||||
}
|
||||
.views-display-top .tabs {
|
||||
margin-right: 18em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-display-top .tabs {
|
||||
margin-left: 18em;
|
||||
margin-right: 0;
|
||||
}
|
||||
.views-display-top .tabs > li {
|
||||
margin-right: 6px; /* LTR */
|
||||
padding-left: 0; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-display-top .tabs > li {
|
||||
margin-left: 6px;
|
||||
margin-right: 0.3em;
|
||||
padding-right: 0;
|
||||
}
|
||||
.views-display-top .tabs > li:last-child {
|
||||
margin-right: 0; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-display-top .tabs > li:last-child {
|
||||
margin-left: 0;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
.form-edit .form-actions {
|
||||
background-color: #e1e2dc;
|
||||
border-right: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-left: 1px solid #ccc;
|
||||
margin-top: 0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
.views-displays .tabs.secondary {
|
||||
margin-right: 200px; /* LTR */
|
||||
border: 0;
|
||||
}
|
||||
[dir="rtl"] .views-displays .tabs.secondary {
|
||||
margin-left: 200px;
|
||||
margin-right: 0;
|
||||
}
|
||||
.views-displays .tabs.secondary li,
|
||||
.views-displays .tabs.secondary li.is-active {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
width: auto;
|
||||
}
|
||||
.views-displays .tabs li.add ul.action-list li{
|
||||
margin: 0;
|
||||
}
|
||||
.views-displays .tabs.secondary li {
|
||||
margin: 0 5px 5px 6px; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-displays .tabs.secondary li {
|
||||
margin-left: 5px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.views-displays .tabs.secondary .tabs__tab + .tabs__tab {
|
||||
border-top: 0;
|
||||
}
|
||||
.views-displays .tabs li.tabs__tab:hover {
|
||||
border: 0;
|
||||
padding-left: 0; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-displays .tabs li.tabs__tab:hover {
|
||||
padding-left: 15px;
|
||||
padding-right: 0;
|
||||
}
|
||||
.views-displays .tabs.secondary a {
|
||||
border: 1px solid #cbcbcb;
|
||||
border-radius: 7px;
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
line-height: 1.3333;
|
||||
padding: 3px 7px;
|
||||
}
|
||||
|
||||
/* Display a red border if the display doesn't validate. */
|
||||
.views-displays .tabs li.is-active a.is-active.error,
|
||||
.views-displays .tabs .error {
|
||||
border: 2px solid #ed541d;
|
||||
padding: 1px 6px;
|
||||
}
|
||||
.views-displays .tabs a:focus {
|
||||
outline: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.views-displays .tabs.secondary li a {
|
||||
background-color: #fff;
|
||||
}
|
||||
.views-displays .tabs li a:hover,
|
||||
.views-displays .tabs li.is-active a,
|
||||
.views-displays .tabs li.is-active a.is-active {
|
||||
background-color: #555;
|
||||
color: #fff;
|
||||
}
|
||||
.views-displays .tabs .open > a {
|
||||
background-color: #f1f1f1;
|
||||
border-bottom: 1px solid transparent;
|
||||
position: relative;
|
||||
}
|
||||
.views-displays .tabs .open > a:hover {
|
||||
color: #0074bd;
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
.views-displays .tabs .action-list li {
|
||||
background-color: #f1f1f1;
|
||||
border-color: #cbcbcb;
|
||||
border-style: solid;
|
||||
border-width: 0 1px;
|
||||
padding: 2px 9px;
|
||||
}
|
||||
.views-displays .tabs .action-list li:first-child {
|
||||
border-width: 1px 1px 0;
|
||||
}
|
||||
.views-displays .action-list li:last-child {
|
||||
border-width: 0 1px 1px;
|
||||
}
|
||||
.views-displays .tabs .action-list li:last-child {
|
||||
border-width: 0 1px 1px;
|
||||
}
|
||||
.views-displays .tabs .action-list input.form-submit {
|
||||
background: none repeat scroll 0 0 transparent;
|
||||
border: medium none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.views-displays .tabs .action-list input.form-submit:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
.views-displays .tabs .action-list li:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
.edit-display-settings {
|
||||
margin: 12px 12px 0 12px
|
||||
}
|
||||
.edit-display-settings-top.views-ui-display-tab-bucket {
|
||||
border: 1px solid #f3f3f3;
|
||||
line-height: 20px;
|
||||
margin: 0 0 15px 0;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
position: relative;
|
||||
}
|
||||
.views-display-column {
|
||||
border: 1px solid #f3f3f3;
|
||||
}
|
||||
.views-display-column + .views-display-column {
|
||||
margin-top: 0;
|
||||
}
|
||||
.view-preview-form .form-item-view-args,
|
||||
.view-preview-form .form-actions {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.view-preview-form .arguments-preview {
|
||||
font-size: 1em;
|
||||
}
|
||||
.view-preview-form .arguments-preview,
|
||||
.view-preview-form .form-item-view-args {
|
||||
margin-left: 10px; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .view-preview-form .arguments-preview,
|
||||
[dir="rtl"] .view-preview-form .form-item-view-args {
|
||||
margin-left: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.view-preview-form .form-item-view-args label {
|
||||
float: left; /* LTR */
|
||||
font-weight: normal;
|
||||
height: 6ex;
|
||||
margin-right: 0.75em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .view-preview-form .form-item-view-args label {
|
||||
float: right;
|
||||
margin-left: 0.75em;
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
.form-item-live-preview,
|
||||
.form-item-view-args,
|
||||
.preview-submit-wrapper {
|
||||
display: inline-block;
|
||||
}
|
||||
.form-item-live-preview,
|
||||
.view-preview-form .form-actions {
|
||||
vertical-align: top;
|
||||
}
|
||||
@media screen and (min-width:45em) { /* 720px */
|
||||
.view-preview-form .form-type-textfield .description {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* These are the individual "buckets," or boxes, inside the display settings area */
|
||||
.views-ui-display-tab-bucket {
|
||||
border-bottom: 1px solid #f3f3f3;
|
||||
line-height: 20px;
|
||||
margin: 0;
|
||||
padding-top: 4px;
|
||||
position: relative;
|
||||
}
|
||||
.views-ui-display-tab-bucket:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
.views-ui-display-tab-bucket + .views-ui-display-tab-bucket {
|
||||
border-top: medium none;
|
||||
}
|
||||
.views-ui-display-tab-bucket__title,
|
||||
.views-ui-display-tab-bucket > .views-display-setting {
|
||||
padding: 2px 6px 4px;
|
||||
}
|
||||
.views-ui-display-tab-bucket__title {
|
||||
font-size: small;
|
||||
margin: 0;
|
||||
}
|
||||
.views-ui-display-tab-bucket.access {
|
||||
padding-top: 0;
|
||||
}
|
||||
.views-ui-display-tab-bucket.page-settings {
|
||||
border-bottom: medium none;
|
||||
}
|
||||
.views-display-setting .views-ajax-link {
|
||||
margin-left: 0.2083em;
|
||||
margin-right: 0.2083em;
|
||||
}
|
||||
|
||||
/** Applies an overridden(italics) font style to overridden buckets.
|
||||
* The better way to implement this would be to add the overridden class
|
||||
* to the bucket header when the bucket is overridden and style it as a
|
||||
* generic icon classed element. For the moment, we'll style the bucket
|
||||
* header specifically with the overridden font style.
|
||||
*/
|
||||
.views-ui-display-tab-setting.overridden,
|
||||
.views-ui-display-tab-bucket.overridden .views-ui-display-tab-bucket__title {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* This is each row within one of the "boxes." */
|
||||
.views-ui-display-tab-bucket .views-display-setting {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
.views-ui-display-tab-bucket .views-display-setting:nth-of-type(even) {
|
||||
background-color: #f3f5ee;
|
||||
}
|
||||
.views-ui-display-tab-actions.views-ui-display-tab-bucket .views-display-setting {
|
||||
background-color: transparent;
|
||||
}
|
||||
.views-ui-display-tab-bucket .views-group-text {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.views-display-setting .label {
|
||||
margin-right: 3px; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-display-setting .label {
|
||||
margin-left: 3px;
|
||||
margin-right: 0;
|
||||
}
|
||||
.views-edit-view {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
/* The contents of the popup dialog on the views edit form. */
|
||||
.views-filterable-options .form-type-checkbox {
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 8px;
|
||||
border-top: none;
|
||||
}
|
||||
.views-filterable-options {
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
.views-filterable-options .filterable-option.odd .form-type-checkbox {
|
||||
background-color: #f3f4ee;
|
||||
}
|
||||
.filterable-option .form-item {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
.views-filterable-options .form-type-checkbox .description {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.views-filterable-options-controls .form-item {
|
||||
width: 30%;
|
||||
margin: 0 0 0 2%; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-filterable-options-controls .form-item {
|
||||
margin: 0 2% 0 0;
|
||||
}
|
||||
.views-filterable-options-controls input,
|
||||
.views-filterable-options-controls select {
|
||||
width: 100%;
|
||||
}
|
||||
.views-ui-dialog .ui-dialog-content {
|
||||
padding: 0;
|
||||
}
|
||||
.views-ui-dialog .views-filterable-options {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.views-ui-dialog .views-add-form-selected.container-inline {
|
||||
padding: 0;
|
||||
}
|
||||
.views-ui-dialog .views-add-form-selected.container-inline > div {
|
||||
display: block;
|
||||
}
|
||||
.views-ui-dialog .form-item-selected {
|
||||
margin: 0;
|
||||
padding: 6px 16px;
|
||||
}
|
||||
.views-ui-dialog .views-override {
|
||||
background-color: #f3f4ee;
|
||||
padding: 8px 13px;
|
||||
}
|
||||
.views-ui-dialog.views-ui-dialog-scroll .ui-dialog-titlebar {
|
||||
border: none;
|
||||
}
|
||||
.views-ui-dialog .views-offset-top {
|
||||
border-bottom: 1px solid #CCC;
|
||||
}
|
||||
.views-ui-dialog .views-offset-bottom {
|
||||
border-top: 1px solid #CCC;
|
||||
}
|
||||
.views-ui-dialog .views-override > * {
|
||||
margin: 0;
|
||||
}
|
||||
.views-ui-dialog .views-progress-indicator {
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
position: absolute;
|
||||
right: 10px; /* LTR */
|
||||
top: 32px;
|
||||
}
|
||||
[dir="rtl"] .views-ui-dialog .views-progress-indicator {
|
||||
left: 10px;
|
||||
right: auto;
|
||||
}
|
||||
.views-ui-dialog .views-progress-indicator:before {
|
||||
content: "\003C\00A0";
|
||||
}
|
||||
.views-ui-dialog .views-progress-indicator:after {
|
||||
content: "\00A0\003E";
|
||||
}
|
||||
.views-ui-dialog details .item-list {
|
||||
padding-left: 2em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-ui-dialog details .item-list {
|
||||
padding-left: 0;
|
||||
padding-right: 2em;
|
||||
}
|
||||
.views-ui-rearrange-filter-form table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.views-ui-rearrange-filter-form tr td[rowspan] {
|
||||
border-color: #cdcdcd;
|
||||
border-style: solid;
|
||||
border-width: 0 1px 1px 1px;
|
||||
}
|
||||
.views-ui-rearrange-filter-form tr[id^="views-row"] {
|
||||
border-right: 1px solid #cdcdcd; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .views-ui-rearrange-filter-form tr[id^="views-row"] {
|
||||
border-left: 1px solid #cdcdcd;
|
||||
border-right: 0;
|
||||
}
|
||||
.views-ui-rearrange-filter-form .even td {
|
||||
background-color: #f3f4ed;
|
||||
}
|
||||
.views-ui-rearrange-filter-form .views-group-title {
|
||||
border-top: 1px solid #cdcdcd;
|
||||
}
|
||||
.views-ui-rearrange-filter-form .group-empty {
|
||||
border-bottom: 1px solid #cdcdcd;
|
||||
}
|
||||
.form-item-options-expose-required,
|
||||
.form-item-options-expose-label,
|
||||
.form-item-options-expose-description {
|
||||
margin-bottom: 6px;
|
||||
margin-left: 18px; /* LTR */
|
||||
margin-top: 6px;
|
||||
}
|
||||
[dir="rtl"] .form-item-options-expose-required,
|
||||
[dir="rtl"] .form-item-options-expose-label,
|
||||
[dir="rtl"] .form-item-options-expose-description {
|
||||
margin-left: 0;
|
||||
margin-right: 18px;
|
||||
}
|
||||
.views-preview-wrapper {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.view-preview-form {
|
||||
position: relative;
|
||||
}
|
||||
.view-preview-form__title {
|
||||
background-color: #e1e2dc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin-top: 0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
.view-preview-form .form-item-live-preview {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 3px;
|
||||
margin-top: 2px;
|
||||
margin-left: 2px; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .view-preview-form .form-item-live-preview {
|
||||
right: auto;
|
||||
left: 12px;
|
||||
margin-left: 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.views-live-preview {
|
||||
padding: 12px;
|
||||
}
|
||||
.views-live-preview .views-query-info {
|
||||
overflow: auto;
|
||||
}
|
||||
.views-live-preview .section-title {
|
||||
color: #818181;
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
line-height: 1.6154;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
.views-live-preview .view > * {
|
||||
margin-top: 18px;
|
||||
}
|
||||
.views-live-preview .preview-section {
|
||||
border: 1px dashed #dedede;
|
||||
margin: 0 -5px;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
.views-live-preview li.views-row + li.views-row {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
/* The div.views-row is intentional and excludes li.views-row, for example */
|
||||
.views-live-preview div.views-row + div.views-row {
|
||||
margin-top: 36px;
|
||||
}
|
||||
.views-query-info table {
|
||||
border-collapse: separate;
|
||||
border-color: #ddd;
|
||||
border-spacing: 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.views-query-info table tr {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.views-query-info table th,
|
||||
.views-query-info table td {
|
||||
color: #666;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
.messages {
|
||||
margin-bottom: 18px;
|
||||
line-height: 1.4555;
|
||||
}
|
||||
.dropbutton-multiple {
|
||||
position: absolute;
|
||||
}
|
||||
.dropbutton-widget {
|
||||
position: relative;
|
||||
}
|
||||
.js .views-edit-view .dropbutton-wrapper .dropbutton .dropbutton-action > * {
|
||||
font-size: 10px;
|
||||
}
|
||||
.js .dropbutton-wrapper .dropbutton .dropbutton-action > .ajax-progress-throbber {
|
||||
position: absolute;
|
||||
right: -5px; /* LTR */
|
||||
top: -1px;
|
||||
z-index: 2;
|
||||
}
|
||||
[dir="rtl"].js .dropbutton-wrapper .dropbutton .dropbutton-action > .ajax-progress-throbber {
|
||||
left: -5px;
|
||||
right: auto;
|
||||
}
|
||||
.js .dropbutton-wrapper.dropbutton-multiple.open .dropbutton-action:first-child a {
|
||||
border-radius: 1.1em 0 0 0; /* LTR */
|
||||
}
|
||||
[dir="rtl"].js .dropbutton-wrapper.dropbutton-multiple.open .dropbutton-action:first-child a {
|
||||
border-radius: 0 1.1em 0 0;
|
||||
}
|
||||
.js .dropbutton-wrapper.dropbutton-multiple.open .dropbutton-action:last-child a {
|
||||
border-radius: 0 0 0 1.1em; /* LTR */
|
||||
}
|
||||
[dir="rtl"].js .dropbutton-wrapper.dropbutton-multiple.open .dropbutton-action:last-child a {
|
||||
border-radius: 0 0 1.1em 0;
|
||||
}
|
||||
.views-display-top .dropbutton-wrapper {
|
||||
position: absolute;
|
||||
right: 12px; /* LTR */
|
||||
top: 7px;
|
||||
}
|
||||
[dir="rtl"] .views-display-top .dropbutton-wrapper {
|
||||
left: 12px;
|
||||
right: auto;
|
||||
}
|
||||
.views-display-top .dropbutton-wrapper .dropbutton-widget .dropbutton-action a {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.views-ui-display-tab-bucket .dropbutton-wrapper {
|
||||
position: absolute;
|
||||
right: 5px; /* LTR */
|
||||
top: 4px;
|
||||
}
|
||||
[dir="rtl"] .views-ui-display-tab-bucket .dropbutton-wrapper {
|
||||
left: 5px;
|
||||
right: auto;
|
||||
}
|
||||
.views-ui-display-tab-bucket .dropbutton-wrapper .dropbutton-widget .dropbutton-action a {
|
||||
width: auto;
|
||||
}
|
||||
.views-ui-display-tab-actions .dropbutton-wrapper li a,
|
||||
.views-ui-display-tab-actions .dropbutton-wrapper input {
|
||||
background: none;
|
||||
border: medium;
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
padding-left: 12px; /* LTR */
|
||||
margin-bottom: 0;
|
||||
}
|
||||
[dir="rtl"] .views-ui-display-tab-actions .dropbutton-wrapper li a,
|
||||
[dir="rtl"] .views-ui-display-tab-actions .dropbutton-wrapper input {
|
||||
padding-left: 0.5em;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.views-ui-display-tab-actions .dropbutton-wrapper input:hover {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
.views-list-section {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.form-textarea-wrapper,
|
||||
.form-item-options-content {
|
||||
width: 100%;
|
||||
}
|
57
core/modules/views_ui/css/views_ui.contextual.css
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* @file
|
||||
* The .contextual.css file is intended to contain styles that override declarations
|
||||
* in the Contextual module.
|
||||
*/
|
||||
|
||||
.views-live-preview .contextual-region-active {
|
||||
outline: medium none;
|
||||
}
|
||||
.views-live-preview .contextual {
|
||||
right: auto; /* LTR */
|
||||
top: auto;
|
||||
}
|
||||
[dir="rtl"] .views-live-preview .contextual {
|
||||
left: auto;
|
||||
}
|
||||
.js .views-live-preview .contextual {
|
||||
display: inline;
|
||||
}
|
||||
.views-live-preview .contextual-links-trigger {
|
||||
display: block;
|
||||
}
|
||||
.contextual .contextual-links {
|
||||
border-radius: 0 4px 4px 4px; /* LTR */
|
||||
min-width: 10em;
|
||||
padding: 6px 6px 9px 6px;
|
||||
right: auto; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .contextual .contextual-links {
|
||||
border-radius: 4px 0 4px 4px;
|
||||
left: auto;
|
||||
}
|
||||
.contextual-links li a,
|
||||
.contextual-links li span {
|
||||
padding-bottom: 0.25em;
|
||||
padding-right: 0.1667em; /* LTR */
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
[dir="rtl"] .contextual-links li a,
|
||||
[dir="rtl"] .contextual-links li span {
|
||||
padding-left: 0.1667em;
|
||||
padding-right: 0;
|
||||
}
|
||||
.contextual-links li span {
|
||||
font-weight: bold;
|
||||
}
|
||||
.contextual-links li a {
|
||||
margin: 0.25em 0;
|
||||
padding-left: 1em; /* LTR */
|
||||
}
|
||||
[dir="rtl"] .contextual-links li a {
|
||||
padding-left: 0.1667em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
.contextual-links li a:hover {
|
||||
background-color: #badbec;
|
||||
}
|
BIN
core/modules/views_ui/images/arrow-active.png
Normal file
After Width: | Height: | Size: 313 B |
BIN
core/modules/views_ui/images/close.png
Normal file
After Width: | Height: | Size: 227 B |
BIN
core/modules/views_ui/images/expanded-options.png
Normal file
After Width: | Height: | Size: 228 B |
BIN
core/modules/views_ui/images/loading.gif
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
core/modules/views_ui/images/overridden.gif
Normal file
After Width: | Height: | Size: 175 B |
BIN
core/modules/views_ui/images/sprites.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
core/modules/views_ui/images/status-active.gif
Normal file
After Width: | Height: | Size: 2.1 KiB |
188
core/modules/views_ui/js/ajax.js
Normal file
|
@ -0,0 +1,188 @@
|
|||
/**
|
||||
* @file
|
||||
* Handles AJAX submission and response in Views UI.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* @param {object} response
|
||||
* @param {string} response.selector
|
||||
* @param {number} [status]
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.viewsHighlight = function (ajax, response, status) {
|
||||
$('.hilited').removeClass('hilited');
|
||||
$(response.selector).addClass('hilited');
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* @param {object} response
|
||||
* @param {bool} response.changed
|
||||
* @param {number} [status]
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.viewsShowButtons = function (ajax, response, status) {
|
||||
$('div.views-edit-view div.form-actions').removeClass('js-hide');
|
||||
if (response.changed) {
|
||||
$('div.views-edit-view div.view-changed.messages').removeClass('js-hide');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* @param {object} [response]
|
||||
* @param {number} [status]
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.viewsTriggerPreview = function (ajax, response, status) {
|
||||
if ($('input#edit-displays-live-preview').is(':checked')) {
|
||||
$('#preview-submit').trigger('click');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* @param {object} response
|
||||
* @param {string} response.siteName
|
||||
* @param {string} response.title
|
||||
* @param {number} [status]
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.viewsReplaceTitle = function (ajax, response, status) {
|
||||
var doc = document;
|
||||
// For the <title> element, make a best-effort attempt to replace the page
|
||||
// title and leave the site name alone. If the theme doesn't use the site
|
||||
// name in the <title> element, this will fail.
|
||||
var oldTitle = doc.title;
|
||||
// Escape the site name, in case it has special characters in it, so we can
|
||||
// use it in our regex.
|
||||
var escapedSiteName = response.siteName.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
var re = new RegExp('.+ (.) ' + escapedSiteName);
|
||||
doc.title = oldTitle.replace(re, response.title + ' $1 ' + response.siteName);
|
||||
|
||||
$('h1.page-title').text(response.title);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get rid of irritating tabledrag messages.
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
Drupal.theme.tableDragChangedWarning = function () {
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger preview when the "live preview" checkbox is checked.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.livePreview = {
|
||||
attach: function (context) {
|
||||
$('input#edit-displays-live-preview', context).once('views-ajax').on('click', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
$('#preview-submit').trigger('click');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync preview display.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.syncPreviewDisplay = {
|
||||
attach: function (context) {
|
||||
$("#views-tabset a").once('views-ajax').on('click', function () {
|
||||
var href = $(this).attr('href');
|
||||
// Cut of #views-tabset.
|
||||
var display_id = href.substr(11);
|
||||
// Set the form element.
|
||||
$("#views-live-preview #preview-display-id").val(display_id);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.viewsAjax = {
|
||||
collapseReplaced: false,
|
||||
attach: function (context, settings) {
|
||||
var base_element_settings = {
|
||||
'event': 'click',
|
||||
'progress': {'type': 'fullscreen'}
|
||||
};
|
||||
// Bind AJAX behaviors to all items showing the class.
|
||||
$('a.views-ajax-link', context).once('views-ajax').each(function () {
|
||||
var element_settings = base_element_settings;
|
||||
element_settings.base = base;
|
||||
element_settings.element = this;
|
||||
// Set the URL to go to the anchor.
|
||||
if ($(this).attr('href')) {
|
||||
element_settings.url = $(this).attr('href');
|
||||
}
|
||||
var base = $(this).attr('id');
|
||||
Drupal.ajax(element_settings);
|
||||
});
|
||||
|
||||
$('div#views-live-preview a')
|
||||
.once('views-ajax').each(function () {
|
||||
// We don't bind to links without a URL.
|
||||
if (!$(this).attr('href')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var element_settings = base_element_settings;
|
||||
// Set the URL to go to the anchor.
|
||||
element_settings.url = $(this).attr('href');
|
||||
if (Drupal.Views.getPath(element_settings.url).substring(0, 21) !== 'admin/structure/views') {
|
||||
return true;
|
||||
}
|
||||
|
||||
element_settings.wrapper = 'views-preview-wrapper';
|
||||
element_settings.method = 'replaceWith';
|
||||
element_settings.base = base;
|
||||
element_settings.element = this;
|
||||
var base = $(this).attr('id');
|
||||
Drupal.ajax(element_settings);
|
||||
});
|
||||
|
||||
// Within a live preview, make exposed widget form buttons re-trigger the
|
||||
// Preview button.
|
||||
// @todo Revisit this after fixing Views UI to display a Preview outside
|
||||
// of the main Edit form.
|
||||
$('div#views-live-preview input[type=submit]')
|
||||
.once('views-ajax').each(function (event) {
|
||||
$(this).on('click', function () {
|
||||
this.form.clk = this;
|
||||
return true;
|
||||
});
|
||||
var element_settings = base_element_settings;
|
||||
// Set the URL to go to the anchor.
|
||||
element_settings.url = $(this.form).attr('action');
|
||||
if (Drupal.Views.getPath(element_settings.url).substring(0, 21) !== 'admin/structure/views') {
|
||||
return true;
|
||||
}
|
||||
|
||||
element_settings.wrapper = 'views-preview-wrapper';
|
||||
element_settings.method = 'replaceWith';
|
||||
element_settings.event = 'click';
|
||||
element_settings.base = base;
|
||||
element_settings.element = this;
|
||||
|
||||
var base = $(this).attr('id');
|
||||
Drupal.ajax(element_settings);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
52
core/modules/views_ui/js/dialog.views.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* @file
|
||||
* Views dialog behaviors.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
"use strict";
|
||||
|
||||
function handleDialogResize(e) {
|
||||
var $modal = $(e.currentTarget);
|
||||
var $viewsOverride = $modal.find('[data-drupal-views-offset]');
|
||||
var $scroll = $modal.find('[data-drupal-views-scroll]');
|
||||
var offset = 0;
|
||||
var modalHeight;
|
||||
if ($scroll.length) {
|
||||
// Add a class to do some styles adjustments.
|
||||
$modal.closest('.views-ui-dialog').addClass('views-ui-dialog-scroll');
|
||||
// Let scroll element take all the height available.
|
||||
$scroll.css({overflow: 'visible', height: 'auto'});
|
||||
modalHeight = $modal.height();
|
||||
$viewsOverride.each(function () { offset += $(this).outerHeight(); });
|
||||
|
||||
// Take internal padding into account.
|
||||
var scrollOffset = $scroll.outerHeight() - $scroll.height();
|
||||
$scroll.height(modalHeight - offset - scrollOffset);
|
||||
// Reset scrolling properties.
|
||||
$modal.css('overflow', 'hidden');
|
||||
$scroll.css('overflow', 'auto');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.viewsModalContent = {
|
||||
attach: function (context) {
|
||||
$('body').once('viewsDialog').on('dialogContentResize.viewsDialog', '.ui-dialog-content', handleDialogResize);
|
||||
// When expanding details, make sure the modal is resized.
|
||||
$(context).find('.scroll').once('detailsUpdate').on('click', 'summary', function (e) {
|
||||
$(e.currentTarget).trigger('dialogContentResize');
|
||||
});
|
||||
},
|
||||
detach: function (context, settings, trigger) {
|
||||
if (trigger === 'unload') {
|
||||
$('body').removeOnce('viewsDialog').off('.viewsDialog');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
1132
core/modules/views_ui/js/views-admin.js
Normal file
51
core/modules/views_ui/js/views_ui.listing.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* @file
|
||||
* Views listing behaviors.
|
||||
*/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Filters the view listing tables by a text input search string.
|
||||
*
|
||||
* Text search input: input.views-filter-text
|
||||
* Target table: input.views-filter-text[data-table]
|
||||
* Source text: .views-table-filter-text-source
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.viewTableFilterByText = {
|
||||
attach: function (context, settings) {
|
||||
var $input = $('input.views-filter-text').once('views-filter-text');
|
||||
var $table = $($input.attr('data-table'));
|
||||
var $rows;
|
||||
|
||||
function filterViewList(e) {
|
||||
var query = $(e.target).val().toLowerCase();
|
||||
|
||||
function showViewRow(index, row) {
|
||||
var $row = $(row);
|
||||
var $sources = $row.find('.views-table-filter-text-source');
|
||||
var textMatch = $sources.text().toLowerCase().indexOf(query) !== -1;
|
||||
$row.closest('tr').toggle(textMatch);
|
||||
}
|
||||
|
||||
// Filter if the length of the query is at least 2 characters.
|
||||
if (query.length >= 2) {
|
||||
$rows.each(showViewRow);
|
||||
}
|
||||
else {
|
||||
$rows.show();
|
||||
}
|
||||
}
|
||||
|
||||
if ($table.length) {
|
||||
$rows = $table.find('tbody tr');
|
||||
$input.on('keyup', filterViewList);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}(jQuery, Drupal));
|
221
core/modules/views_ui/src/Controller/ViewsUIController.php
Normal file
|
@ -0,0 +1,221 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Controller\ViewsUIController.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Controller;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views_ui\ViewUI;
|
||||
use Drupal\views\ViewsData;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\ReplaceCommand;
|
||||
|
||||
/**
|
||||
* Returns responses for Views UI routes.
|
||||
*/
|
||||
class ViewsUIController extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Stores the Views data cache object.
|
||||
*
|
||||
* @var \Drupal\views\ViewsData
|
||||
*/
|
||||
protected $viewsData;
|
||||
|
||||
/**
|
||||
* Constructs a new \Drupal\views_ui\Controller\ViewsUIController object.
|
||||
*
|
||||
* @param \Drupal\views\ViewsData views_data
|
||||
* The Views data cache object.
|
||||
*/
|
||||
public function __construct(ViewsData $views_data) {
|
||||
$this->viewsData = $views_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('views.views_data')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all instances of fields on any views.
|
||||
*
|
||||
* @return array
|
||||
* The Views fields report page.
|
||||
*/
|
||||
public function reportFields() {
|
||||
$views = $this->entityManager()->getStorage('view')->loadMultiple();
|
||||
|
||||
// Fetch all fieldapi fields which are used in views
|
||||
// Therefore search in all views, displays and handler-types.
|
||||
$fields = array();
|
||||
$handler_types = ViewExecutable::getHandlerTypes();
|
||||
foreach ($views as $view) {
|
||||
$executable = $view->getExecutable();
|
||||
$executable->initDisplay();
|
||||
foreach ($executable->displayHandlers as $display_id => $display) {
|
||||
if ($executable->setDisplay($display_id)) {
|
||||
foreach ($handler_types as $type => $info) {
|
||||
foreach ($executable->getHandlers($type, $display_id) as $item) {
|
||||
$table_data = $this->viewsData->get($item['table']);
|
||||
if (isset($table_data[$item['field']]) && isset($table_data[$item['field']][$type])
|
||||
&& $field_data = $table_data[$item['field']][$type]) {
|
||||
// The final check that we have a fieldapi field now.
|
||||
if (isset($field_data['field_name'])) {
|
||||
$fields[$field_data['field_name']][$view->id()] = $view->id();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$header = array(t('Field name'), t('Used in'));
|
||||
$rows = array();
|
||||
foreach ($fields as $field_name => $views) {
|
||||
$rows[$field_name]['data'][0] = SafeMarkup::checkPlain($field_name);
|
||||
foreach ($views as $view) {
|
||||
$rows[$field_name]['data'][1][] = $this->l($view, new Url('entity.view.edit_form', array('view' => $view)));
|
||||
}
|
||||
$rows[$field_name]['data'][1] = SafeMarkup::set(implode(', ', $rows[$field_name]['data'][1]));
|
||||
}
|
||||
|
||||
// Sort rows by field name.
|
||||
ksort($rows);
|
||||
$output = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#empty' => t('No fields have been used in views yet.'),
|
||||
);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all plugins and what enabled Views use them.
|
||||
*
|
||||
* @return array
|
||||
* The Views plugins report page.
|
||||
*/
|
||||
public function reportPlugins() {
|
||||
$rows = Views::pluginList();
|
||||
foreach ($rows as &$row) {
|
||||
// Link each view name to the view itself.
|
||||
foreach ($row['views'] as $row_name => $view) {
|
||||
$row['views'][$row_name] = $this->l($view, new Url('entity.view.edit_form', array('view' => $view)));
|
||||
}
|
||||
$row['views'] = SafeMarkup::set(implode(', ', $row['views']));
|
||||
}
|
||||
|
||||
// Sort rows by field name.
|
||||
ksort($rows);
|
||||
return array(
|
||||
'#type' => 'table',
|
||||
'#header' => array(t('Type'), t('Name'), t('Provided by'), t('Used in')),
|
||||
'#rows' => $rows,
|
||||
'#empty' => t('There are no enabled views.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a method on a view and reloads the listing page.
|
||||
*
|
||||
* @param \Drupal\views\ViewEntityInterface $view
|
||||
* The view being acted upon.
|
||||
* @param string $op
|
||||
* The operation to perform, e.g., 'enable' or 'disable'.
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The current request.
|
||||
*
|
||||
* @return \Drupal\Core\Ajax\AjaxResponse|\Symfony\Component\HttpFoundation\RedirectResponse
|
||||
* Either returns a rebuilt listing page as an AJAX response, or redirects
|
||||
* back to the listing page.
|
||||
*
|
||||
*/
|
||||
public function ajaxOperation(ViewEntityInterface $view, $op, Request $request) {
|
||||
// Perform the operation.
|
||||
$view->$op()->save();
|
||||
|
||||
// If the request is via AJAX, return the rendered list as JSON.
|
||||
if ($request->request->get('js')) {
|
||||
$list = $this->entityManager()->getListBuilder('view')->render();
|
||||
$response = new AjaxResponse();
|
||||
$response->addCommand(new ReplaceCommand('#views-entity-list', $list));
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Otherwise, redirect back to the page.
|
||||
return $this->redirect('entity.view.collection');
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu callback for Views tag autocompletion.
|
||||
*
|
||||
* Like other autocomplete functions, this function inspects the 'q' query
|
||||
* parameter for the string to use to search for suggestions.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\JsonResponse
|
||||
* A JSON response containing the autocomplete suggestions for Views tags.
|
||||
*/
|
||||
public function autocompleteTag(Request $request) {
|
||||
$matches = array();
|
||||
$string = $request->query->get('q');
|
||||
// Get matches from default views.
|
||||
$views = $this->entityManager()->getStorage('view')->loadMultiple();
|
||||
foreach ($views as $view) {
|
||||
$tag = $view->get('tag');
|
||||
if ($tag && strpos($tag, $string) === 0) {
|
||||
$matches[$tag] = $tag;
|
||||
if (count($matches) >= 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new JsonResponse($matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the form to edit a view.
|
||||
*
|
||||
* @param \Drupal\views_ui\ViewUI $view
|
||||
* The view being deleted.
|
||||
* @param string|null $display_id
|
||||
* (optional) The display ID being edited. Defaults to NULL, which will load
|
||||
* the first available display.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the Views edit and preview forms.
|
||||
*/
|
||||
public function edit(ViewUI $view, $display_id = NULL) {
|
||||
$name = $view->label();
|
||||
$data = $this->viewsData->get($view->get('base_table'));
|
||||
|
||||
if (isset($data['table']['base']['title'])) {
|
||||
$name .= ' (' . $data['table']['base']['title'] . ')';
|
||||
}
|
||||
$build['#title'] = $name;
|
||||
|
||||
$build['edit'] = $this->entityFormBuilder()->getForm($view, 'edit', array('display_id' => $display_id));
|
||||
$build['preview'] = $this->entityFormBuilder()->getForm($view, 'preview', array('display_id' => $display_id));
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
112
core/modules/views_ui/src/Form/AdvancedSettingsForm.php
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\AdvancedSettingsForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Form builder for the advanced admin settings page.
|
||||
*/
|
||||
class AdvancedSettingsForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_admin_settings_advanced';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['views.settings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
$config = $this->config('views.settings');
|
||||
$form['cache'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Caching'),
|
||||
'#open' => TRUE,
|
||||
);
|
||||
|
||||
$form['cache']['skip_cache'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Disable views data caching'),
|
||||
'#description' => $this->t("Views caches data about tables, modules and views available, to increase performance. By checking this box, Views will skip this cache and always rebuild this data when needed. This can have a serious performance impact on your site."),
|
||||
'#default_value' => $config->get('skip_cache'),
|
||||
);
|
||||
|
||||
$form['cache']['clear_cache'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t("Clear Views' cache"),
|
||||
'#submit' => array('::cacheSubmit'),
|
||||
);
|
||||
|
||||
$form['debug'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Debugging'),
|
||||
'#open' => TRUE,
|
||||
);
|
||||
|
||||
$form['debug']['sql_signature'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Add Views signature to all SQL queries'),
|
||||
'#description' => $this->t("All Views-generated queries will include the name of the views and display 'view-name:display-name' as a string at the end of the SELECT clause. This makes identifying Views queries in database server logs simpler, but should only be used when troubleshooting."),
|
||||
|
||||
'#default_value' => $config->get('sql_signature'),
|
||||
);
|
||||
|
||||
$options = Views::fetchPluginNames('display_extender');
|
||||
if (!empty($options)) {
|
||||
$form['extenders'] = array(
|
||||
'#type' => 'details',
|
||||
'#open' => TRUE,
|
||||
);
|
||||
$form['extenders']['display_extenders'] = array(
|
||||
'#title' => $this->t('Display extenders'),
|
||||
'#default_value' => array_filter($config->get('display_extenders')),
|
||||
'#options' => $options,
|
||||
'#type' => 'checkboxes',
|
||||
'#description' => $this->t('Select extensions of the views interface.')
|
||||
);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('views.settings')
|
||||
->set('skip_cache', $form_state->getValue('skip_cache'))
|
||||
->set('sql_signature', $form_state->getValue('sql_signature'))
|
||||
->set('display_extenders', $form_state->getValue('display_extenders', array()))
|
||||
->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submission handler to clear the Views cache.
|
||||
*/
|
||||
public function cacheSubmit() {
|
||||
views_invalidate_cache();
|
||||
drupal_set_message($this->t('The cache has been cleared.'));
|
||||
}
|
||||
|
||||
}
|
189
core/modules/views_ui/src/Form/Ajax/AddHandler.php
Normal file
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\AddHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Provides a form for adding an item in the Views UI.
|
||||
*/
|
||||
class AddHandler extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* Constructs a new AddHandler object.
|
||||
*/
|
||||
public function __construct($type = NULL) {
|
||||
$this->setType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'add-handler';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getForm(ViewEntityInterface $view, $display_id, $js, $type = NULL) {
|
||||
$this->setType($type);
|
||||
return parent::getForm($view, $display_id, $js);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_add_handler_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$type = $form_state->get('type');
|
||||
|
||||
$form = array(
|
||||
'options' => array(
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#attributes' => array('class' => array('scroll'), 'data-drupal-views-scroll' => TRUE),
|
||||
),
|
||||
);
|
||||
|
||||
$executable = $view->getExecutable();
|
||||
if (!$executable->setDisplay($display_id)) {
|
||||
$form['markup'] = array('#markup' => $this->t('Invalid display id @display', array('@display' => $display_id)));
|
||||
return $form;
|
||||
}
|
||||
$display = &$executable->displayHandlers->get($display_id);
|
||||
|
||||
$types = ViewExecutable::getHandlerTypes();
|
||||
$ltitle = $types[$type]['ltitle'];
|
||||
$section = $types[$type]['plural'];
|
||||
|
||||
if (!empty($types[$type]['type'])) {
|
||||
$type = $types[$type]['type'];
|
||||
}
|
||||
|
||||
$form['#title'] = $this->t('Add @type', array('@type' => $ltitle));
|
||||
$form['#section'] = $display_id . 'add-handler';
|
||||
|
||||
// Add the display override dropdown.
|
||||
views_ui_standard_display_dropdown($form, $form_state, $section);
|
||||
|
||||
// Figure out all the base tables allowed based upon what the relationships provide.
|
||||
$base_tables = $executable->getBaseTables();
|
||||
$options = Views::viewsDataHelper()->fetchFields(array_keys($base_tables), $type, $display->useGroupBy(), $form_state->get('type'));
|
||||
|
||||
if (!empty($options)) {
|
||||
$form['override']['controls'] = array(
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#id' => 'views-filterable-options-controls',
|
||||
'#attributes' => ['class' => ['form--inline', 'views-filterable-options-controls']],
|
||||
);
|
||||
$form['override']['controls']['options_search'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Search'),
|
||||
);
|
||||
|
||||
$groups = array('all' => $this->t('- All -'));
|
||||
$form['override']['controls']['group'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Type'),
|
||||
'#options' => array(),
|
||||
);
|
||||
|
||||
$form['options']['name'] = array(
|
||||
'#prefix' => '<div class="views-radio-box form-checkboxes views-filterable-options">',
|
||||
'#suffix' => '</div>',
|
||||
'#tree' => TRUE,
|
||||
'#default_value' => 'all',
|
||||
);
|
||||
|
||||
// Group options first to simplify the usage of #states.
|
||||
$grouped_options = array();
|
||||
foreach ($options as $key => $option) {
|
||||
$group = preg_replace('/[^a-z0-9]/', '-', strtolower($option['group']));
|
||||
$groups[$group] = $option['group'];
|
||||
$grouped_options[$group][$key] = $option;
|
||||
if (!empty($option['aliases']) && is_array($option['aliases'])) {
|
||||
foreach ($option['aliases'] as $id => $alias) {
|
||||
if (empty($alias['base']) || !empty($base_tables[$alias['base']])) {
|
||||
$copy = $option;
|
||||
$copy['group'] = $alias['group'];
|
||||
$copy['title'] = $alias['title'];
|
||||
if (isset($alias['help'])) {
|
||||
$copy['help'] = $alias['help'];
|
||||
}
|
||||
|
||||
$group = preg_replace('/[^a-z0-9]/', '-', strtolower($copy['group']));
|
||||
$groups[$group] = $copy['group'];
|
||||
$grouped_options[$group][$key . '$' . $id] = $copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($grouped_options as $group => $group_options) {
|
||||
foreach ($group_options as $key => $option) {
|
||||
$form['options']['name'][$key] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('!group: !field', array('!group' => $option['group'], '!field' => $option['title'])),
|
||||
'#description' => $option['help'],
|
||||
'#return_value' => $key,
|
||||
'#prefix' => "<div class='filterable-option'>",
|
||||
'#suffix' => '</div>',
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
array(
|
||||
':input[name="override[controls][group]"]' => array('value' => 'all'),
|
||||
),
|
||||
array(
|
||||
':input[name="override[controls][group]"]' => array('value' => $group),
|
||||
),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$form['override']['controls']['group']['#options'] = $groups;
|
||||
}
|
||||
else {
|
||||
$form['options']['markup'] = array(
|
||||
'#markup' => '<div class="form-item">' . $this->t('There are no @types available to add.', array('@types' => $ltitle)) . '</div>',
|
||||
);
|
||||
}
|
||||
// Add a div to show the selected items
|
||||
$form['selected'] = array(
|
||||
'#type' => 'item',
|
||||
'#markup' => '<span class="views-ui-view-title">' . $this->t('Selected:') . '</span> ' . '<div class="views-selected-options"></div>',
|
||||
'#theme_wrappers' => array('form_element', 'views_ui_container'),
|
||||
'#attributes' => array(
|
||||
'class' => array('container-inline', 'views-add-form-selected', 'views-offset-bottom'),
|
||||
'data-drupal-views-offset' => 'bottom',
|
||||
),
|
||||
);
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_add_handler_form', $this->t('Add and configure @types', array('@types' => $ltitle)));
|
||||
|
||||
// Remove the default submit function.
|
||||
$form['actions']['submit']['#submit'] = array_filter($form['actions']['submit']['#submit'], function($var) {
|
||||
return !(is_array($var) && isset($var[1]) && $var[1] == 'standardSubmit');
|
||||
});
|
||||
$form['actions']['submit']['#submit'][] = array($view, 'submitItemAdd');
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
}
|
67
core/modules/views_ui/src/Form/Ajax/Analyze.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\Analyze.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views_ui\ViewUI;
|
||||
use Drupal\views\Analyzer;
|
||||
|
||||
/**
|
||||
* Displays analysis information for a view.
|
||||
*/
|
||||
class Analyze extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'analyze';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_analyze_view_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
|
||||
$form['#title'] = $this->t('View analysis');
|
||||
$form['#section'] = 'analyze';
|
||||
|
||||
$analyzer = Views::analyzer();
|
||||
$messages = $analyzer->getMessages($view->getExecutable());
|
||||
|
||||
$form['analysis'] = array(
|
||||
'#prefix' => '<div class="form-item">',
|
||||
'#suffix' => '</div>',
|
||||
'#markup' => $analyzer->formatMessages($messages),
|
||||
);
|
||||
|
||||
// Inform the standard button function that we want an OK button.
|
||||
$form_state->set('ok_button', TRUE);
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_analyze_view_form');
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
/** @var $view \Drupal\views_ui\ViewUI */
|
||||
$view = $form_state->get('view');
|
||||
$form_state->setRedirectUrl($view->urlInfo('edit-form'));
|
||||
}
|
||||
|
||||
}
|
289
core/modules/views_ui/src/Form/Ajax/ConfigHandler.php
Normal file
|
@ -0,0 +1,289 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\ConfigHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Views;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Provides a form for configuring an item in the Views UI.
|
||||
*/
|
||||
class ConfigHandler extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigHandler object.
|
||||
*/
|
||||
public function __construct($type = NULL, $id = NULL) {
|
||||
$this->setType($type);
|
||||
$this->setID($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'handler';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getForm(ViewEntityInterface $view, $display_id, $js, $type = NULL, $id = NULL) {
|
||||
$this->setType($type);
|
||||
$this->setID($id);
|
||||
return parent::getForm($view, $display_id, $js);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_config_item_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, Request $request = NULL) {
|
||||
/** @var \Drupal\views\Entity\View $view */
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$type = $form_state->get('type');
|
||||
$id = $form_state->get('id');
|
||||
|
||||
$form = array(
|
||||
'options' => array(
|
||||
'#tree' => TRUE,
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#attributes' => array('class' => array('scroll'), 'data-drupal-views-scroll' => TRUE),
|
||||
),
|
||||
);
|
||||
$executable = $view->getExecutable();
|
||||
$save_ui_cache = FALSE;
|
||||
if (!$executable->setDisplay($display_id)) {
|
||||
$form['markup'] = array('#markup' => $this->t('Invalid display id @display', array('@display' => $display_id)));
|
||||
return $form;
|
||||
}
|
||||
$item = $executable->getHandler($display_id, $type, $id);
|
||||
|
||||
if ($item) {
|
||||
$handler = $executable->display_handler->getHandler($type, $id);
|
||||
if (empty($handler)) {
|
||||
$form['markup'] = array('#markup' => $this->t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
|
||||
}
|
||||
else {
|
||||
$types = ViewExecutable::getHandlerTypes();
|
||||
|
||||
// If this item can come from the default display, show a dropdown
|
||||
// that lets the user choose which display the changes should apply to.
|
||||
if ($executable->display_handler->defaultableSections($types[$type]['plural'])) {
|
||||
$section = $types[$type]['plural'];
|
||||
$form_state->set('section', $section);
|
||||
views_ui_standard_display_dropdown($form, $form_state, $section);
|
||||
}
|
||||
|
||||
// A whole bunch of code to figure out what relationships are valid for
|
||||
// this item.
|
||||
$relationships = $executable->display_handler->getOption('relationships');
|
||||
$relationship_options = array();
|
||||
|
||||
foreach ($relationships as $relationship) {
|
||||
// relationships can't link back to self. But also, due to ordering,
|
||||
// relationships can only link to prior relationships.
|
||||
if ($type == 'relationship' && $id == $relationship['id']) {
|
||||
break;
|
||||
}
|
||||
$relationship_handler = Views::handlerManager('relationship')->getHandler($relationship);
|
||||
// ignore invalid/broken relationships.
|
||||
if (empty($relationship_handler)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this relationship is valid for this type, add it to the list.
|
||||
$data = Views::viewsData()->get($relationship['table']);
|
||||
if (isset($data[$relationship['field']]['relationship']['base']) && $base = $data[$relationship['field']]['relationship']['base']) {
|
||||
$base_fields = Views::viewsDataHelper()->fetchFields($base, $type, $executable->display_handler->useGroupBy());
|
||||
if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
|
||||
$relationship_handler->init($executable, $executable->display_handler, $relationship);
|
||||
$relationship_options[$relationship['id']] = $relationship_handler->adminLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($relationship_options)) {
|
||||
// Make sure the existing relationship is even valid. If not, force
|
||||
// it to none.
|
||||
$base_fields = Views::viewsDataHelper()->fetchFields($view->get('base_table'), $type, $executable->display_handler->useGroupBy());
|
||||
if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
|
||||
$relationship_options = array_merge(array('none' => $this->t('Do not use a relationship')), $relationship_options);
|
||||
}
|
||||
$rel = empty($item['relationship']) ? 'none' : $item['relationship'];
|
||||
if (empty($relationship_options[$rel])) {
|
||||
// Pick the first relationship.
|
||||
$rel = key($relationship_options);
|
||||
// We want this relationship option to get saved even if the user
|
||||
// skips submitting the form.
|
||||
$executable->setHandlerOption($display_id, $type, $id, 'relationship', $rel);
|
||||
$save_ui_cache = TRUE;
|
||||
// Re-initialize with new relationship.
|
||||
$item['relationship'] = $rel;
|
||||
$handler->init($executable, $executable->display_handler, $item);
|
||||
}
|
||||
|
||||
$form['options']['relationship'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Relationship'),
|
||||
'#options' => $relationship_options,
|
||||
'#default_value' => $rel,
|
||||
'#weight' => -500,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['options']['relationship'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => 'none',
|
||||
);
|
||||
}
|
||||
|
||||
$form['#title'] = $this->t('Configure @type: @item', array('@type' => $types[$type]['lstitle'], '@item' => $handler->adminLabel()));
|
||||
|
||||
if (!empty($handler->definition['help'])) {
|
||||
$form['options']['form_description'] = array(
|
||||
'#markup' => $handler->definition['help'],
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#attributes' => array('class' => array('form-item description')),
|
||||
'#weight' => -1000,
|
||||
);
|
||||
}
|
||||
|
||||
$form['#section'] = $display_id . '-' . $type . '-' . $id;
|
||||
|
||||
// Get form from the handler.
|
||||
$handler->buildOptionsForm($form['options'], $form_state);
|
||||
$form_state->set('handler', $handler);
|
||||
}
|
||||
|
||||
$name = $form_state->get('update_name');
|
||||
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_config_item_form', $name);
|
||||
// Add a 'remove' button.
|
||||
$form['actions']['remove'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Remove'),
|
||||
'#submit' => array(array($this, 'remove')),
|
||||
'#limit_validation_errors' => array(array('override')),
|
||||
'#ajax' => array(
|
||||
'url' => Url::fromRoute('<current>'),
|
||||
),
|
||||
'#button_type' => 'danger',
|
||||
);
|
||||
}
|
||||
|
||||
if ($save_ui_cache) {
|
||||
$view->cacheSet();
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->get('handler')->validateOptionsForm($form['options'], $form_state);
|
||||
|
||||
if ($form_state->getErrors()) {
|
||||
$form_state->set('rerender', TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$id = $form_state->get('id');
|
||||
$handler = $form_state->get('handler');
|
||||
|
||||
// Run it through the handler's submit function.
|
||||
$handler->submitOptionsForm($form['options'], $form_state);
|
||||
$item = $handler->options;
|
||||
$types = ViewExecutable::getHandlerTypes();
|
||||
|
||||
// For footer/header $handler_type is area but $type is footer/header.
|
||||
// For all other handle types it's the same.
|
||||
$handler_type = $type = $form_state->get('type');
|
||||
if (!empty($types[$type]['type'])) {
|
||||
$handler_type = $types[$type]['type'];
|
||||
}
|
||||
|
||||
$override = NULL;
|
||||
$executable = $view->getExecutable();
|
||||
if ($executable->display_handler->useGroupBy() && !empty($item['group_type'])) {
|
||||
if (empty($executable->query)) {
|
||||
$executable->initQuery();
|
||||
}
|
||||
$aggregate = $executable->query->getAggregationInfo();
|
||||
if (!empty($aggregate[$item['group_type']]['handler'][$type])) {
|
||||
$override = $aggregate[$item['group_type']]['handler'][$type];
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new handler and unpack the options from the form onto it. We
|
||||
// can use that for storage.
|
||||
$handler = Views::handlerManager($handler_type)->getHandler($item, $override);
|
||||
$handler->init($executable, $executable->display_handler, $item);
|
||||
|
||||
// Add the incoming options to existing options because items using
|
||||
// the extra form may not have everything in the form here.
|
||||
$options = $form_state->getValue('options') + $handler->options;
|
||||
|
||||
// This unpacks only options that are in the definition, ensuring random
|
||||
// extra stuff on the form is not sent through.
|
||||
$handler->unpackOptions($handler->options, $options, NULL, FALSE);
|
||||
|
||||
// Store the item back on the view
|
||||
$executable->setHandler($display_id, $type, $id, $handler->options);
|
||||
|
||||
// Ensure any temporary options are removed.
|
||||
if (isset($view->temporary_options[$type][$id])) {
|
||||
unset($view->temporary_options[$type][$id]);
|
||||
}
|
||||
|
||||
// Write to cache
|
||||
$view->cacheSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for removing an item from a view
|
||||
*/
|
||||
public function remove(&$form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$type = $form_state->get('type');
|
||||
$id = $form_state->get('id');
|
||||
// Store the item back on the view
|
||||
list($was_defaulted, $is_defaulted) = $view->getOverrideValues($form, $form_state);
|
||||
$executable = $view->getExecutable();
|
||||
// If the display selection was changed toggle the override value.
|
||||
if ($was_defaulted != $is_defaulted) {
|
||||
$display = &$executable->displayHandlers->get($display_id);
|
||||
$display->optionsOverride($form, $form_state);
|
||||
}
|
||||
$executable->removeHandler($display_id, $type, $id);
|
||||
|
||||
// Write to cache
|
||||
$view->cacheSet();
|
||||
}
|
||||
|
||||
}
|
125
core/modules/views_ui/src/Form/Ajax/ConfigHandlerExtra.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\ConfigHandlerExtra.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
/**
|
||||
* Provides a form for configuring extra information for a Views UI item.
|
||||
*/
|
||||
class ConfigHandlerExtra extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigHandlerExtra object.
|
||||
*/
|
||||
public function __construct($type = NULL, $id = NULL) {
|
||||
$this->setType($type);
|
||||
$this->setID($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'handler-extra';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getForm(ViewEntityInterface $view, $display_id, $js, $type = NULL, $id = NULL) {
|
||||
$this->setType($type);
|
||||
$this->setID($id);
|
||||
return parent::getForm($view, $display_id, $js);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_config_item_extra_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$type = $form_state->get('type');
|
||||
$id = $form_state->get('id');
|
||||
|
||||
$form = array(
|
||||
'options' => array(
|
||||
'#tree' => true,
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#attributes' => array('class' => array('scroll'), 'data-drupal-views-scroll' => TRUE),
|
||||
),
|
||||
);
|
||||
$executable = $view->getExecutable();
|
||||
if (!$executable->setDisplay($display_id)) {
|
||||
$form['markup'] = array('#markup' => $this->t('Invalid display id @display', array('@display' => $display_id)));
|
||||
return $form;
|
||||
}
|
||||
$item = $executable->getHandler($display_id, $type, $id);
|
||||
|
||||
if ($item) {
|
||||
$handler = $executable->display_handler->getHandler($type, $id);
|
||||
if (empty($handler)) {
|
||||
$form['markup'] = array('#markup' => $this->t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
|
||||
}
|
||||
else {
|
||||
$handler->init($executable, $executable->display_handler, $item);
|
||||
$types = ViewExecutable::getHandlerTypes();
|
||||
|
||||
$form['#title'] = $this->t('Configure extra settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->adminLabel()));
|
||||
|
||||
$form['#section'] = $display_id . '-' . $type . '-' . $id;
|
||||
|
||||
// Get form from the handler.
|
||||
$handler->buildExtraOptionsForm($form['options'], $form_state);
|
||||
$form_state->set('handler', $handler);
|
||||
}
|
||||
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_config_item_extra_form');
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$form_state->get('handler')->validateExtraOptionsForm($form['options'], $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$handler = $form_state->get('handler');
|
||||
// Run it through the handler's submit function.
|
||||
$handler->submitExtraOptionsForm($form['options'], $form_state);
|
||||
$item = $handler->options;
|
||||
|
||||
// Store the data we're given.
|
||||
foreach ($form_state->getValue('options') as $key => $value) {
|
||||
$item[$key] = $value;
|
||||
}
|
||||
|
||||
// Store the item back on the view
|
||||
$view->getExecutable()->setHandler($form_state->get('display_id'), $form_state->get('type'), $form_state->get('id'), $item);
|
||||
|
||||
// Write to cache
|
||||
$view->cacheSet();
|
||||
}
|
||||
|
||||
}
|
118
core/modules/views_ui/src/Form/Ajax/ConfigHandlerGroup.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\ConfigHandlerGroup.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
/**
|
||||
* Provides a form for configuring grouping information for a Views UI handler.
|
||||
*/
|
||||
class ConfigHandlerGroup extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* Constructs a new ConfigHandlerGroup object.
|
||||
*/
|
||||
public function __construct($type = NULL, $id = NULL) {
|
||||
$this->setType($type);
|
||||
$this->setID($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'handler-group';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getForm(ViewEntityInterface $view, $display_id, $js, $type = NULL, $id = NULL) {
|
||||
$this->setType($type);
|
||||
$this->setID($id);
|
||||
return parent::getForm($view, $display_id, $js);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_config_item_group_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$type = $form_state->get('type');
|
||||
$id = $form_state->get('id');
|
||||
|
||||
$form = array(
|
||||
'options' => array(
|
||||
'#tree' => TRUE,
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#attributes' => array('class' => array('scroll'), 'data-drupal-views-scroll' => TRUE),
|
||||
),
|
||||
);
|
||||
$executable = $view->getExecutable();
|
||||
if (!$executable->setDisplay($display_id)) {
|
||||
$form['markup'] = array('#markup' => $this->t('Invalid display id @display', array('@display' => $display_id)));
|
||||
return $form;
|
||||
}
|
||||
|
||||
$executable->initQuery();
|
||||
|
||||
$item = $executable->getHandler($display_id, $type, $id);
|
||||
|
||||
if ($item) {
|
||||
$handler = $executable->display_handler->getHandler($type, $id);
|
||||
if (empty($handler)) {
|
||||
$form['markup'] = array('#markup' => $this->t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
|
||||
}
|
||||
else {
|
||||
$handler->init($executable, $executable->display_handler, $item);
|
||||
$types = ViewExecutable::getHandlerTypes();
|
||||
|
||||
$form['#title'] = $this->t('Configure aggregation settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->adminLabel()));
|
||||
|
||||
$handler->buildGroupByForm($form['options'], $form_state);
|
||||
$form_state->set('handler', $handler);
|
||||
}
|
||||
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_config_item_group_form');
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$item = &$form_state->get('handler')->options;
|
||||
$type = $form_state->get('type');
|
||||
|
||||
$handler = Views::handlerManager($type)->getHandler($item);
|
||||
$executable = $view->getExecutable();
|
||||
$handler->init($executable, $executable->display_handler, $item);
|
||||
|
||||
$handler->submitGroupByForm($form, $form_state);
|
||||
|
||||
// Store the item back on the view
|
||||
$executable->setHandler($form_state->get('display_id'), $form_state->get('type'), $form_state->get('id'), $item);
|
||||
|
||||
// Write to cache
|
||||
$view->cacheSet();
|
||||
}
|
||||
|
||||
}
|
119
core/modules/views_ui/src/Form/Ajax/Display.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\Display.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides a form for editing the Views display.
|
||||
*/
|
||||
class Display extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* Constructs a new Display object.
|
||||
*/
|
||||
public function __construct($type = NULL) {
|
||||
$this->setType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'display';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo Remove this and switch all usage of $form_state->get('section') to
|
||||
* $form_state->get('type').
|
||||
*/
|
||||
public function getFormState(ViewEntityInterface $view, $display_id, $js) {
|
||||
$form_state = parent::getFormState($view, $display_id, $js);
|
||||
$form_state->set('section', $this->type);
|
||||
return $form_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getForm(ViewEntityInterface $view, $display_id, $js, $type = NULL) {
|
||||
$this->setType($type);
|
||||
return parent::getForm($view, $display_id, $js);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_edit_display_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
|
||||
$executable = $view->getExecutable();
|
||||
if (!$executable->setDisplay($display_id)) {
|
||||
$form['markup'] = array('#markup' => $this->t('Invalid display id @display', array('@display' => $display_id)));
|
||||
return $form;
|
||||
}
|
||||
|
||||
// Get form from the handler.
|
||||
$form['options'] = array(
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#attributes' => array('class' => array('scroll'), 'data-drupal-views-scroll' => TRUE),
|
||||
);
|
||||
$executable->display_handler->buildOptionsForm($form['options'], $form_state);
|
||||
|
||||
// The handler options form sets $form['#title'], which we need on the entire
|
||||
// $form instead of just the ['options'] section.
|
||||
$form['#title'] = $form['options']['#title'];
|
||||
unset($form['options']['#title']);
|
||||
|
||||
// Move the override dropdown out of the scrollable section of the form.
|
||||
if (isset($form['options']['override'])) {
|
||||
$form['override'] = $form['options']['override'];
|
||||
unset($form['options']['override']);
|
||||
}
|
||||
|
||||
$name = $form_state->get('update_name');
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_edit_display_form', $name);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$view->getExecutable()->displayHandlers->get($display_id)->validateOptionsForm($form['options'], $form_state);
|
||||
|
||||
if ($form_state->getErrors()) {
|
||||
$form_state->set('rerender', TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$view->getExecutable()->displayHandlers->get($display_id)->submitOptionsForm($form['options'], $form_state);
|
||||
|
||||
$view->cacheSet();
|
||||
}
|
||||
|
||||
}
|
96
core/modules/views_ui/src/Form/Ajax/EditDetails.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\EditDetails.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views_ui\ViewUI;
|
||||
|
||||
/**
|
||||
* Provides a form for editing the details of a View.
|
||||
*/
|
||||
class EditDetails extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'edit-details';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_edit_details_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
|
||||
$form['#title'] = $this->t('Name and description');
|
||||
$form['#section'] = 'details';
|
||||
|
||||
$form['details'] = array(
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#attributes' => array('class' => array('scroll'), 'data-drupal-views-scroll' => TRUE),
|
||||
);
|
||||
$form['details']['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Administrative name'),
|
||||
'#default_value' => $view->label(),
|
||||
);
|
||||
$form['details']['langcode'] = array(
|
||||
'#type' => 'language_select',
|
||||
'#title' => $this->t('View language'),
|
||||
'#description' => $this->t('Language of labels and other textual elements in this view.'),
|
||||
'#default_value' => $view->get('langcode'),
|
||||
);
|
||||
$form['details']['description'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Administrative description'),
|
||||
'#default_value' => $view->get('description'),
|
||||
);
|
||||
$form['details']['tag'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Administrative tags'),
|
||||
'#description' => t('Enter a comma-separated list of words to describe your view.'),
|
||||
'#default_value' => $view->get('tag'),
|
||||
'#autocomplete_route_name' => 'views_ui.autocomplete',
|
||||
);
|
||||
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_edit_details_form');
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
foreach ($form_state->getValues() as $key => $value) {
|
||||
// Only save values onto the view if they're actual view properties
|
||||
// (as opposed to 'op' or 'form_build_id').
|
||||
if (isset($form['details'][$key])) {
|
||||
$view->set($key, $value);
|
||||
}
|
||||
}
|
||||
$bases = Views::viewsData()->fetchBaseTables();
|
||||
$page_title = $view->label();
|
||||
if (isset($bases[$view->get('base_table')])) {
|
||||
$page_title .= ' (' . $bases[$view->get('base_table')]['title'] . ')';
|
||||
}
|
||||
$form_state->set('page_title', $page_title);
|
||||
|
||||
$view->cacheSet();
|
||||
}
|
||||
|
||||
}
|
184
core/modules/views_ui/src/Form/Ajax/Rearrange.php
Normal file
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\Rearrange.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
/**
|
||||
* Provides a rearrange form for Views handlers.
|
||||
*/
|
||||
class Rearrange extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* Constructs a new Rearrange object.
|
||||
*/
|
||||
public function __construct($type = NULL) {
|
||||
$this->setType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'rearrange';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getForm(ViewEntityInterface $view, $display_id, $js, $type = NULL) {
|
||||
$this->setType($type);
|
||||
return parent::getForm($view, $display_id, $js);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_rearrange_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$type = $form_state->get('type');
|
||||
|
||||
$types = ViewExecutable::getHandlerTypes();
|
||||
$executable = $view->getExecutable();
|
||||
if (!$executable->setDisplay($display_id)) {
|
||||
$form['markup'] = array('#markup' => $this->t('Invalid display id @display', array('@display' => $display_id)));
|
||||
return $form;
|
||||
}
|
||||
$display = &$executable->displayHandlers->get($display_id);
|
||||
$form['#title'] = $this->t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
|
||||
$form['#section'] = $display_id . 'rearrange-item';
|
||||
|
||||
if ($display->defaultableSections($types[$type]['plural'])) {
|
||||
$section = $types[$type]['plural'];
|
||||
$form_state->set('section', $section);
|
||||
views_ui_standard_display_dropdown($form, $form_state, $section);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
|
||||
// Get relationship labels
|
||||
$relationships = array();
|
||||
foreach ($display->getHandlers('relationship') as $id => $handler) {
|
||||
$relationships[$id] = $handler->adminLabel();
|
||||
}
|
||||
|
||||
$form['fields'] = array(
|
||||
'#type' => 'table',
|
||||
'#header' => array('', $this->t('Weight'), $this->t('Remove')),
|
||||
'#empty' => $this->t('No fields available.'),
|
||||
'#tabledrag' => array(
|
||||
array(
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'weight',
|
||||
)
|
||||
),
|
||||
'#tree' => TRUE,
|
||||
'#prefix' => '<div class="scroll" data-drupal-views-scroll>',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
|
||||
foreach ($display->getOption($types[$type]['plural']) as $id => $field) {
|
||||
$form['fields'][$id] = array();
|
||||
|
||||
$form['fields'][$id]['#attributes'] = array('class' => array('draggable'), 'id' => 'views-row-' . $id);
|
||||
|
||||
$handler = $display->getHandler($type, $id);
|
||||
if ($handler) {
|
||||
$name = $handler->adminLabel() . ' ' . $handler->adminSummary();
|
||||
if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
|
||||
$name = '(' . $relationships[$field['relationship']] . ') ' . $name;
|
||||
}
|
||||
$markup = $name;
|
||||
}
|
||||
else {
|
||||
$name = $id;
|
||||
$markup = $this->t('Broken field @id', array('@id' => $id));
|
||||
}
|
||||
$form['fields'][$id]['name'] = array('#markup' => $markup);
|
||||
|
||||
$form['fields'][$id]['weight'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => ++$count,
|
||||
'#attributes' => array('class' => array('weight')),
|
||||
'#title' => t('Weight for @title', array('@title' => $name)),
|
||||
'#title_display' => 'invisible',
|
||||
);
|
||||
|
||||
$form['fields'][$id]['removed'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Remove @title', array('@title' => $name)),
|
||||
'#title_display' => 'invisible',
|
||||
'#id' => 'views-removed-' . $id,
|
||||
'#attributes' => array('class' => array('views-remove-checkbox')),
|
||||
'#default_value' => 0,
|
||||
'#suffix' => \Drupal::l(SafeMarkup::format('<span>@text</span>', array('@text' => $this->t('Remove'))),
|
||||
Url::fromRoute('<none>', array(), array('attributes' => array(
|
||||
'id' => 'views-remove-link-' . $id,
|
||||
'class' => array('views-hidden', 'views-button-remove', 'views-remove-link'),
|
||||
'alt' => $this->t('Remove this item'),
|
||||
'title' => $this->t('Remove this item')),
|
||||
))
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_rearrange_form');
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$type = $form_state->get('type');
|
||||
|
||||
$types = ViewExecutable::getHandlerTypes();
|
||||
$display = &$view->getExecutable()->displayHandlers->get($display_id);
|
||||
|
||||
$old_fields = $display->getOption($types[$type]['plural']);
|
||||
$new_fields = $order = array();
|
||||
|
||||
// Make an array with the weights
|
||||
foreach ($form_state->getValue('fields') as $field => $info) {
|
||||
// add each value that is a field with a weight to our list, but only if
|
||||
// it has had its 'removed' checkbox checked.
|
||||
if (is_array($info) && isset($info['weight']) && empty($info['removed'])) {
|
||||
$order[$field] = $info['weight'];
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the array
|
||||
asort($order);
|
||||
|
||||
// Create a new list of fields in the new order.
|
||||
foreach (array_keys($order) as $field) {
|
||||
$new_fields[$field] = $old_fields[$field];
|
||||
}
|
||||
$display->setOption($types[$type]['plural'], $new_fields);
|
||||
|
||||
// Store in cache
|
||||
$view->cacheSet();
|
||||
}
|
||||
|
||||
}
|
361
core/modules/views_ui/src/Form/Ajax/RearrangeFilter.php
Normal file
|
@ -0,0 +1,361 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\RearrangeFilter.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
/**
|
||||
* Provides a rearrange form for Views filters.
|
||||
*/
|
||||
class RearrangeFilter extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* Constructs a new RearrangeFilter object.
|
||||
*/
|
||||
public function __construct($type = NULL) {
|
||||
$this->setType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'rearrange-filter';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_rearrange_filter_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
$type = 'filter';
|
||||
|
||||
$types = ViewExecutable::getHandlerTypes();
|
||||
$executable = $view->getExecutable();
|
||||
if (!$executable->setDisplay($display_id)) {
|
||||
$form['markup'] = array('#markup' => $this->t('Invalid display id @display', array('@display' => $display_id)));
|
||||
return $form;
|
||||
}
|
||||
$display = $executable->displayHandlers->get($display_id);
|
||||
$form['#title'] = SafeMarkup::checkPlain($display->display['display_title']) . ': ';
|
||||
$form['#title'] .= $this->t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
|
||||
$form['#section'] = $display_id . 'rearrange-item';
|
||||
|
||||
if ($display->defaultableSections($types[$type]['plural'])) {
|
||||
$section = $types[$type]['plural'];
|
||||
$form_state->set('section', $section);
|
||||
views_ui_standard_display_dropdown($form, $form_state, $section);
|
||||
}
|
||||
|
||||
if (!empty($view->form_cache)) {
|
||||
$groups = $view->form_cache['groups'];
|
||||
$handlers = $view->form_cache['handlers'];
|
||||
}
|
||||
else {
|
||||
$groups = $display->getOption('filter_groups');
|
||||
$handlers = $display->getOption($types[$type]['plural']);
|
||||
}
|
||||
$count = 0;
|
||||
|
||||
// Get relationship labels
|
||||
$relationships = array();
|
||||
foreach ($display->getHandlers('relationship') as $id => $handler) {
|
||||
$relationships[$id] = $handler->adminLabel();
|
||||
}
|
||||
|
||||
$group_options = array();
|
||||
|
||||
/**
|
||||
* Filter groups is an array that contains:
|
||||
* array(
|
||||
* 'operator' => 'and' || 'or',
|
||||
* 'groups' => array(
|
||||
* $group_id => 'and' || 'or',
|
||||
* ),
|
||||
* );
|
||||
*/
|
||||
|
||||
$grouping = count(array_keys($groups['groups'])) > 1;
|
||||
|
||||
$form['filter_groups']['#tree'] = TRUE;
|
||||
$form['filter_groups']['operator'] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => array(
|
||||
'AND' => $this->t('And'),
|
||||
'OR' => $this->t('Or'),
|
||||
),
|
||||
'#default_value' => $groups['operator'],
|
||||
'#attributes' => array(
|
||||
'class' => array('warning-on-change'),
|
||||
),
|
||||
'#title' => $this->t('Operator to use on all groups'),
|
||||
'#description' => $this->t('Either "group 0 AND group 1 AND group 2" or "group 0 OR group 1 OR group 2", etc'),
|
||||
'#access' => $grouping,
|
||||
);
|
||||
|
||||
$form['remove_groups']['#tree'] = TRUE;
|
||||
|
||||
foreach ($groups['groups'] as $id => $group) {
|
||||
$form['filter_groups']['groups'][$id] = array(
|
||||
'#title' => $this->t('Operator'),
|
||||
'#type' => 'select',
|
||||
'#options' => array(
|
||||
'AND' => $this->t('And'),
|
||||
'OR' => $this->t('Or'),
|
||||
),
|
||||
'#default_value' => $group,
|
||||
'#attributes' => array(
|
||||
'class' => array('warning-on-change'),
|
||||
),
|
||||
);
|
||||
|
||||
$form['remove_groups'][$id] = array(); // to prevent a notice
|
||||
if ($id != 1) {
|
||||
$form['remove_groups'][$id] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Remove group @group', array('@group' => $id)),
|
||||
'#id' => "views-remove-group-$id",
|
||||
'#attributes' => array(
|
||||
'class' => array('views-remove-group'),
|
||||
),
|
||||
'#group' => $id,
|
||||
'#ajax' => ['url' => NULL],
|
||||
);
|
||||
}
|
||||
$group_options[$id] = $id == 1 ? $this->t('Default group') : $this->t('Group @group', array('@group' => $id));
|
||||
$form['#group_renders'][$id] = array();
|
||||
}
|
||||
|
||||
$form['#group_options'] = $group_options;
|
||||
$form['#groups'] = $groups;
|
||||
// We don't use getHandlers() because we want items without handlers to
|
||||
// appear and show up as 'broken' so that the user can see them.
|
||||
$form['filters'] = array('#tree' => TRUE);
|
||||
foreach ($handlers as $id => $field) {
|
||||
// If the group does not exist, move the filters to the default group.
|
||||
if (empty($field['group']) || empty($groups['groups'][$field['group']])) {
|
||||
$field['group'] = 1;
|
||||
}
|
||||
|
||||
$handler = $display->getHandler($type, $id);
|
||||
if ($grouping && $handler && !$handler->canGroup()) {
|
||||
$field['group'] = 'ungroupable';
|
||||
}
|
||||
|
||||
// If not grouping and the handler is set ungroupable, move it back to
|
||||
// the default group to prevent weird errors from having it be in its
|
||||
// own group:
|
||||
if (!$grouping && $field['group'] == 'ungroupable') {
|
||||
$field['group'] = 1;
|
||||
}
|
||||
|
||||
// Place this item into the proper group for rendering.
|
||||
$form['#group_renders'][$field['group']][] = $id;
|
||||
|
||||
$form['filters'][$id]['weight'] = array(
|
||||
'#title' => t('Weight for @id', array('@id' => $id)),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => ++$count,
|
||||
'#size' => 8,
|
||||
);
|
||||
$form['filters'][$id]['group'] = array(
|
||||
'#title' => t('Group for @id', array('@id' => $id)),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'select',
|
||||
'#options' => $group_options,
|
||||
'#default_value' => $field['group'],
|
||||
'#attributes' => array(
|
||||
'class' => array('views-region-select', 'views-region-' . $id),
|
||||
),
|
||||
'#access' => $field['group'] !== 'ungroupable',
|
||||
);
|
||||
|
||||
if ($handler) {
|
||||
$name = $handler->adminLabel() . ' ' . $handler->adminSummary();
|
||||
if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
|
||||
$name = '(' . $relationships[$field['relationship']] . ') ' . $name;
|
||||
}
|
||||
|
||||
$form['filters'][$id]['name'] = array(
|
||||
'#markup' => $name,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['filters'][$id]['name'] = array('#markup' => $this->t('Broken field @id', array('@id' => $id)));
|
||||
}
|
||||
$form['filters'][$id]['removed'] = array(
|
||||
'#title' => t('Remove @id', array('@id' => $id)),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'checkbox',
|
||||
'#id' => 'views-removed-' . $id,
|
||||
'#attributes' => array('class' => array('views-remove-checkbox')),
|
||||
'#default_value' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_rearrange_filter_form');
|
||||
$form['actions']['add_group'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Create new filter group'),
|
||||
'#id' => 'views-add-group',
|
||||
'#group' => 'add',
|
||||
'#attributes' => array(
|
||||
'class' => array('views-add-group'),
|
||||
),
|
||||
'#ajax' => ['url' => NULL],
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$types = ViewExecutable::getHandlerTypes();
|
||||
$view = $form_state->get('view');
|
||||
$display = &$view->getExecutable()->displayHandlers->get($form_state->get('display_id'));
|
||||
$remember_groups = array();
|
||||
|
||||
if (!empty($view->form_cache)) {
|
||||
$old_fields = $view->form_cache['handlers'];
|
||||
}
|
||||
else {
|
||||
$old_fields = $display->getOption($types['filter']['plural']);
|
||||
}
|
||||
|
||||
$groups = $form_state->getValue('filter_groups');
|
||||
// Whatever button was clicked, re-calculate field information.
|
||||
$new_fields = $order = array();
|
||||
|
||||
// Make an array with the weights
|
||||
foreach ($form_state->getValue('filters') as $field => $info) {
|
||||
// add each value that is a field with a weight to our list, but only if
|
||||
// it has had its 'removed' checkbox checked.
|
||||
if (is_array($info) && empty($info['removed'])) {
|
||||
if (isset($info['weight'])) {
|
||||
$order[$field] = $info['weight'];
|
||||
}
|
||||
|
||||
if (isset($info['group'])) {
|
||||
$old_fields[$field]['group'] = $info['group'];
|
||||
$remember_groups[$info['group']][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the array
|
||||
asort($order);
|
||||
|
||||
// Create a new list of fields in the new order.
|
||||
foreach (array_keys($order) as $field) {
|
||||
$new_fields[$field] = $old_fields[$field];
|
||||
}
|
||||
|
||||
// If the #group property is set on the clicked button, that means we are
|
||||
// either adding or removing a group, not actually updating the filters.
|
||||
$triggering_element = $form_state->getTriggeringElement();
|
||||
if (!empty($triggering_element['#group'])) {
|
||||
if ($triggering_element['#group'] == 'add') {
|
||||
// Add a new group
|
||||
$groups['groups'][] = 'AND';
|
||||
}
|
||||
else {
|
||||
// Renumber groups above the removed one down.
|
||||
foreach (array_keys($groups['groups']) as $group_id) {
|
||||
if ($group_id >= $triggering_element['#group']) {
|
||||
$old_group = $group_id + 1;
|
||||
if (isset($groups['groups'][$old_group])) {
|
||||
$groups['groups'][$group_id] = $groups['groups'][$old_group];
|
||||
if (isset($remember_groups[$old_group])) {
|
||||
foreach ($remember_groups[$old_group] as $id) {
|
||||
$new_fields[$id]['group'] = $group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If this is the last one, just unset it.
|
||||
unset($groups['groups'][$group_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update our cache with values so that cancel still works the way
|
||||
// people expect.
|
||||
$view->form_cache = [
|
||||
'key' => 'rearrange-filter',
|
||||
'groups' => $groups,
|
||||
'handlers' => $new_fields,
|
||||
];
|
||||
|
||||
// Return to this form except on actual Update.
|
||||
$view->addFormToStack('rearrange-filter', $form_state->get('display_id'), 'filter');
|
||||
}
|
||||
else {
|
||||
// The actual update button was clicked. Remove the empty groups, and
|
||||
// renumber them sequentially.
|
||||
ksort($remember_groups);
|
||||
$groups['groups'] = static::arrayKeyPlus(array_values(array_intersect_key($groups['groups'], $remember_groups)));
|
||||
// Change the 'group' key on each field to match. Here, $mapping is an
|
||||
// array whose keys are the old group numbers and whose values are the new
|
||||
// (sequentially numbered) ones.
|
||||
$mapping = array_flip(static::arrayKeyPlus(array_keys($remember_groups)));
|
||||
foreach ($new_fields as &$new_field) {
|
||||
$new_field['group'] = $mapping[$new_field['group']];
|
||||
}
|
||||
|
||||
// Write the changed handler values.
|
||||
$display->setOption($types['filter']['plural'], $new_fields);
|
||||
$display->setOption('filter_groups', $groups);
|
||||
if (isset($view->form_cache)) {
|
||||
unset($view->form_cache);
|
||||
}
|
||||
}
|
||||
|
||||
// Store in cache.
|
||||
$view->cacheSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one to each key of an array.
|
||||
*
|
||||
* For example array(0 => 'foo') would be array(1 => 'foo').
|
||||
*
|
||||
* @param array
|
||||
* The array to increment keys on.
|
||||
*
|
||||
* @return array
|
||||
* The array with incremented keys.
|
||||
*/
|
||||
public static function arrayKeyPlus($array) {
|
||||
$keys = array_keys($array);
|
||||
// Sort the keys in reverse order so incrementing them doesn't overwrite any
|
||||
// existing keys.
|
||||
rsort($keys);
|
||||
foreach ($keys as $key) {
|
||||
$array[$key + 1] = $array[$key];
|
||||
unset($array[$key]);
|
||||
}
|
||||
// Sort the keys back to ascending order.
|
||||
ksort($array);
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
||||
|
205
core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
Normal file
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\ReorderDisplays.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\views_ui\ViewUI;
|
||||
|
||||
/**
|
||||
* Displays the display reorder form.
|
||||
*/
|
||||
class ReorderDisplays extends ViewsFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormKey() {
|
||||
return 'reorder-displays';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_reorder_displays_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
/** @var $view \Drupal\views\ViewEntityInterface */
|
||||
$view = $form_state->get('view');
|
||||
$display_id = $form_state->get('display_id');
|
||||
|
||||
$form['#title'] = $this->t('Reorder displays');
|
||||
$form['#section'] = 'reorder';
|
||||
$form['#action'] = $this->url('views_ui.form_reorder_displays', [
|
||||
'js' => 'nojs',
|
||||
'view' => $view->id(),
|
||||
'display_id' => $display_id,
|
||||
]);
|
||||
$form['view'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $view
|
||||
);
|
||||
|
||||
$displays = $view->get('display');
|
||||
$count = count($displays);
|
||||
|
||||
// Sort the displays.
|
||||
uasort($displays, function ($display1, $display2) {
|
||||
if ($display1['position'] != $display2['position']) {
|
||||
return $display1['position'] < $display2['position'] ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
$form['displays'] = array(
|
||||
'#type' => 'table',
|
||||
'#id' => 'reorder-displays',
|
||||
'#header' => array($this->t('Display'), $this->t('Weight'), $this->t('Remove')),
|
||||
'#empty' => $this->t('No displays available.'),
|
||||
'#tabledrag' => array(
|
||||
array(
|
||||
'action' => 'order',
|
||||
'relationship' => 'sibling',
|
||||
'group' => 'weight',
|
||||
)
|
||||
),
|
||||
'#tree' => TRUE,
|
||||
'#prefix' => '<div class="scroll" data-drupal-views-scroll>',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
|
||||
foreach ($displays as $id => $display) {
|
||||
$form['displays'][$id] = array(
|
||||
'#display' => $display,
|
||||
'#attributes' => array(
|
||||
'id' => 'display-row-' . $id,
|
||||
),
|
||||
'#weight' => $display['position'],
|
||||
);
|
||||
|
||||
// Only make row draggable if it's not the default display.
|
||||
if ($id !== 'default') {
|
||||
$form['displays'][$id]['#attributes']['class'][] = 'draggable';
|
||||
}
|
||||
|
||||
$form['displays'][$id]['title'] = array(
|
||||
'#markup' => $display['display_title'],
|
||||
);
|
||||
|
||||
$form['displays'][$id]['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#value' => $display['position'],
|
||||
'#delta' => $count,
|
||||
'#title' => $this->t('Weight for @display', array('@display' => $display['display_title'])),
|
||||
'#title_display' => 'invisible',
|
||||
'#attributes' => array(
|
||||
'class' => array('weight'),
|
||||
),
|
||||
);
|
||||
|
||||
$form['displays'][$id]['removed'] = array(
|
||||
'checkbox' => array(
|
||||
'#title' => t('Remove @id', array('@id' => $id)),
|
||||
'#title_display' => 'invisible',
|
||||
'#type' => 'checkbox',
|
||||
'#id' => 'display-removed-' . $id,
|
||||
'#attributes' => array(
|
||||
'class' => array('views-remove-checkbox'),
|
||||
),
|
||||
'#default_value' => !empty($display['deleted']),
|
||||
),
|
||||
'link' => array(
|
||||
'#type' => 'link',
|
||||
'#title' => SafeMarkup::format('<span>@text</span>', array('@text' => $this->t('Remove'))),
|
||||
'#url' => Url::fromRoute('<none>'),
|
||||
'#attributes' => array(
|
||||
'id' => 'display-remove-link-' . $id,
|
||||
'class' => array('views-button-remove', 'display-remove-link'),
|
||||
'alt' => $this->t('Remove this display'),
|
||||
'title' => $this->t('Remove this display'),
|
||||
),
|
||||
),
|
||||
'#access' => ($id !== 'default'),
|
||||
);
|
||||
|
||||
if (!empty($display['deleted'])) {
|
||||
$form['displays'][$id]['deleted'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => TRUE,
|
||||
);
|
||||
|
||||
$form['displays'][$id]['#attributes']['class'][] = 'hidden';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$view->getStandardButtons($form, $form_state, 'views_ui_reorder_displays_form');
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
/** @var $view \Drupal\views_ui\ViewUI */
|
||||
$view = $form_state->get('view');
|
||||
$order = array();
|
||||
|
||||
$user_input = $form_state->getUserInput();
|
||||
foreach ($user_input['displays'] as $display => $info) {
|
||||
// Add each value that is a field with a weight to our list, but only if
|
||||
// it has had its 'removed' checkbox checked.
|
||||
if (is_array($info) && isset($info['weight']) && empty($info['removed']['checkbox'])) {
|
||||
$order[$display] = $info['weight'];
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the order array.
|
||||
asort($order);
|
||||
|
||||
// Remove the default display from ordering.
|
||||
unset($order['default']);
|
||||
// Increment up positions.
|
||||
$position = 1;
|
||||
|
||||
foreach (array_keys($order) as $display) {
|
||||
$order[$display] = $position++;
|
||||
}
|
||||
|
||||
// Setting up position and removing deleted displays.
|
||||
$displays = $view->get('display');
|
||||
foreach ($displays as $display_id => &$display) {
|
||||
// Don't touch the default.
|
||||
if ($display_id === 'default') {
|
||||
$display['position'] = 0;
|
||||
continue;
|
||||
}
|
||||
if (isset($order[$display_id])) {
|
||||
$display['position'] = $order[$display_id];
|
||||
}
|
||||
else {
|
||||
$display['deleted'] = TRUE;
|
||||
}
|
||||
}
|
||||
$view->set('display', $displays);
|
||||
|
||||
// Store in cache.
|
||||
$view->cacheSet();
|
||||
$url = $view->urlInfo('edit-form')
|
||||
->setOption('fragment', 'views-tab-default');
|
||||
$form_state->setRedirectUrl($url);
|
||||
}
|
||||
|
||||
}
|
261
core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
Normal file
|
@ -0,0 +1,261 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\ViewsFormBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Ajax\OpenModalDialogCommand;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views_ui\ViewUI;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
use Drupal\views\Ajax;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\CloseModalDialogCommand;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Provides a base class for Views UI AJAX forms.
|
||||
*/
|
||||
abstract class ViewsFormBase extends FormBase implements ViewsFormInterface {
|
||||
|
||||
/**
|
||||
* The ID of the item this form is manipulating.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The type of item this form is manipulating.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Sets the ID for this form.
|
||||
*
|
||||
* @param string $id
|
||||
* The ID of the item this form is manipulating.
|
||||
*/
|
||||
protected function setID($id) {
|
||||
if ($id) {
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type for this form.
|
||||
*
|
||||
* @param string $type
|
||||
* The type of the item this form is manipulating.
|
||||
*/
|
||||
protected function setType($type) {
|
||||
if ($type) {
|
||||
$this->type = $type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormState(ViewEntityInterface $view, $display_id, $js) {
|
||||
// $js may already have been converted to a Boolean.
|
||||
$ajax = is_string($js) ? $js === 'ajax' : $js;
|
||||
return (new FormState())
|
||||
->set('form_id', $this->getFormId())
|
||||
->set('form_key', $this->getFormKey())
|
||||
->set('ajax', $ajax)
|
||||
->set('display_id', $display_id)
|
||||
->set('view', $view)
|
||||
->set('type', $this->type)
|
||||
->set('id', $this->id)
|
||||
->disableRedirect()
|
||||
->addBuildInfo('callback_object', $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getForm(ViewEntityInterface $view, $display_id, $js) {
|
||||
$form_state = $this->getFormState($view, $display_id, $js);
|
||||
$view = $form_state->get('view');
|
||||
$key = $form_state->get('form_key');
|
||||
|
||||
// @todo Remove the need for this.
|
||||
\Drupal::moduleHandler()->loadInclude('views_ui', 'inc', 'admin');
|
||||
|
||||
// Reset the cache of IDs. Drupal rather aggressively prevents ID
|
||||
// duplication but this causes it to remember IDs that are no longer even
|
||||
// being used.
|
||||
Html::resetSeenIds();
|
||||
|
||||
// check to see if this is the top form of the stack. If it is, pop
|
||||
// it off; if it isn't, the user clicked somewhere else and the stack is
|
||||
// now irrelevant.
|
||||
if (!empty($view->stack)) {
|
||||
$identifier = implode('-', array_filter([$key, $view->id(), $display_id, $form_state->get('type'), $form_state->get('id')]));
|
||||
// Retrieve the first form from the stack without changing the integer keys,
|
||||
// as they're being used for the "2 of 3" progress indicator.
|
||||
reset($view->stack);
|
||||
list($key, $top) = each($view->stack);
|
||||
unset($view->stack[$key]);
|
||||
|
||||
if (array_shift($top) != $identifier) {
|
||||
$view->stack = array();
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically remove the form cache if it is set and the key does
|
||||
// not match. This way navigating away from the form without hitting
|
||||
// update will work.
|
||||
if (isset($view->form_cache) && $view->form_cache['key'] != $key) {
|
||||
unset($view->form_cache);
|
||||
}
|
||||
|
||||
$form_class = get_class($form_state->getFormObject());
|
||||
$response = $this->ajaxFormWrapper($form_class, $form_state);
|
||||
|
||||
// If the form has not been submitted, or was not set for rerendering, stop.
|
||||
if (!$form_state->isSubmitted() || $form_state->get('rerender')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Sometimes we need to re-generate the form for multi-step type operations.
|
||||
if (!empty($view->stack)) {
|
||||
$stack = $view->stack;
|
||||
$top = array_shift($stack);
|
||||
|
||||
// Build the new form state for the next form in the stack.
|
||||
$reflection = new \ReflectionClass($view::$forms[$top[1]]);
|
||||
/** @var $form_state \Drupal\Core\Form\FormStateInterface */
|
||||
$form_state = $reflection->newInstanceArgs(array_slice($top, 3, 2))->getFormState($view, $top[2], $form_state->get('ajax'));
|
||||
$form_class = get_class($form_state->getFormObject());
|
||||
|
||||
$form_state->setUserInput(array());
|
||||
$form_url = views_ui_build_form_url($form_state);
|
||||
if (!$form_state->get('ajax')) {
|
||||
return new RedirectResponse($form_url->setAbsolute()->toString());
|
||||
}
|
||||
$form_state->set('url', $form_url);
|
||||
$response = $this->ajaxFormWrapper($form_class, $form_state);
|
||||
}
|
||||
elseif (!$form_state->get('ajax')) {
|
||||
// if nothing on the stack, non-js forms just go back to the main view editor.
|
||||
$display_id = $form_state->get('display_id');
|
||||
return new RedirectResponse($this->url('entity.view.edit_display_form', ['view' => $view->id(), 'display_id' => $display_id], ['absolute' => TRUE]));
|
||||
}
|
||||
else {
|
||||
$response = new AjaxResponse();
|
||||
$response->addCommand(new CloseModalDialogCommand());
|
||||
$response->addCommand(new Ajax\ShowButtonsCommand(!empty($view->changed)));
|
||||
$response->addCommand(new Ajax\TriggerPreviewCommand());
|
||||
if ($page_title = $form_state->get('page_title')) {
|
||||
$response->addCommand(new Ajax\ReplaceTitleCommand($page_title));
|
||||
}
|
||||
}
|
||||
// If this form was for view-wide changes, there's no need to regenerate
|
||||
// the display section of the form.
|
||||
if ($display_id !== '') {
|
||||
\Drupal::entityManager()->getFormObject('view', 'edit')->rebuildCurrentTab($view, $response, $display_id);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for handling AJAX forms.
|
||||
*
|
||||
* Wrapper around \Drupal\Core\Form\FormBuilderInterface::buildForm() to
|
||||
* handle some AJAX stuff automatically.
|
||||
* This makes some assumptions about the client.
|
||||
*
|
||||
* @param \Drupal\Core\Form\FormInterface|string $form_class
|
||||
* The value must be one of the following:
|
||||
* - The name of a class that implements \Drupal\Core\Form\FormInterface.
|
||||
* - An instance of a class that implements \Drupal\Core\Form\FormInterface.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*
|
||||
* @return \Drupal\Core\Ajax\AjaxResponse|string|array
|
||||
* Returns one of three possible values:
|
||||
* - A \Drupal\Core\Ajax\AjaxResponse object.
|
||||
* - The rendered form, as a string.
|
||||
* - A render array with the title in #title and the rendered form in the
|
||||
* #markup array.
|
||||
*/
|
||||
protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state) {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
|
||||
// This won't override settings already in.
|
||||
if (!$form_state->has('rerender')) {
|
||||
$form_state->set('rerender', FALSE);
|
||||
}
|
||||
$ajax = $form_state->get('ajax');
|
||||
// Do not overwrite if the redirect has been disabled.
|
||||
if (!$form_state->isRedirectDisabled()) {
|
||||
$form_state->disableRedirect($ajax);
|
||||
}
|
||||
$form_state->disableCache();
|
||||
|
||||
$form = \Drupal::formBuilder()->buildForm($form_class, $form_state);
|
||||
$output = $renderer->renderRoot($form);
|
||||
drupal_process_attached($form);
|
||||
|
||||
// These forms have the title built in, so set the title here:
|
||||
$title = $form_state->get('title') ?: '';
|
||||
|
||||
if ($ajax && (!$form_state->isExecuted() || $form_state->get('rerender'))) {
|
||||
// If the form didn't execute and we're using ajax, build up an
|
||||
// Ajax command list to execute.
|
||||
$response = new AjaxResponse();
|
||||
|
||||
// Attach the library necessary for using the OpenModalDialogCommand and
|
||||
// set the attachments for this Ajax response.
|
||||
$form['#attached']['library'][] = 'core/drupal.dialog.ajax';
|
||||
$response->setAttachments($form['#attached']);
|
||||
|
||||
$display = '';
|
||||
$status_messages = array('#type' => 'status_messages');
|
||||
if ($messages = $renderer->renderRoot($status_messages)) {
|
||||
$display = '<div class="views-messages">' . $messages . '</div>';
|
||||
}
|
||||
$display .= $output;
|
||||
|
||||
$options = array(
|
||||
'dialogClass' => 'views-ui-dialog',
|
||||
'width' => '75%',
|
||||
);
|
||||
|
||||
$response->addCommand(new OpenModalDialogCommand($title, $display, $options));
|
||||
|
||||
if ($section = $form_state->get('#section')) {
|
||||
$response->addCommand(new Ajax\HighlightCommand('.' . Html::cleanCssIdentifier($section)));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $title ? ['#title' => $title, '#markup' => $output] : $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
}
|
||||
|
||||
}
|
60
core/modules/views_ui/src/Form/Ajax/ViewsFormInterface.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\Ajax\ViewsFormInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form\Ajax;
|
||||
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\views\ViewEntityInterface;
|
||||
|
||||
interface ViewsFormInterface extends FormInterface {
|
||||
|
||||
/**
|
||||
* Returns the key that represents this form.
|
||||
*
|
||||
* @return string
|
||||
* The form key used in the URL, e.g., the string 'add-handler' in
|
||||
* 'admin/structure/views/%/add-handler/%/%/%'.
|
||||
*/
|
||||
public function getFormKey();
|
||||
|
||||
/**
|
||||
* Gets the form state for this form.
|
||||
*
|
||||
* @param \Drupal\views\ViewEntityInterface $view
|
||||
* The view being edited.
|
||||
* @param string|null $display_id
|
||||
* The display ID being edited, or NULL to load the first available display.
|
||||
* @param string $js
|
||||
* If this is an AJAX form, it will be the string 'ajax'. Otherwise, it will
|
||||
* be 'nojs'. This determines the response.
|
||||
*
|
||||
* @return \Drupal\Core\Form\FormStateInterface
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function getFormState(ViewEntityInterface $view, $display_id, $js);
|
||||
|
||||
/**
|
||||
* Creates a new instance of this form.
|
||||
*
|
||||
* @param \Drupal\views\ViewEntityInterface $view
|
||||
* The view being edited.
|
||||
* @param string|null $display_id
|
||||
* The display ID being edited, or NULL to load the first available display.
|
||||
* @param string $js
|
||||
* If this is an AJAX form, it will be the string 'ajax'. Otherwise, it will
|
||||
* be 'nojs'. This determines the response.
|
||||
*
|
||||
* @return array
|
||||
* An form for a specific operation in the Views UI, or an array of AJAX
|
||||
* commands to render a form.
|
||||
*
|
||||
* @todo When https://www.drupal.org/node/1843224 is in, this will return
|
||||
* \Drupal\Core\Ajax\AjaxResponse instead of the array of AJAX commands.
|
||||
*/
|
||||
public function getForm(ViewEntityInterface $view, $display_id, $js);
|
||||
|
||||
}
|
188
core/modules/views_ui/src/Form/BasicSettingsForm.php
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\BasicSettingsForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Form builder for the admin display defaults page.
|
||||
*/
|
||||
class BasicSettingsForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\views_ui\Form\BasicSettingsForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The factory for configuration objects.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ThemeHandlerInterface $theme_handler) {
|
||||
parent::__construct($config_factory);
|
||||
|
||||
$this->themeHandler = $theme_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('theme_handler')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_admin_settings_basic';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['views.settings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
$config = $this->config('views.settings');
|
||||
$options = array();
|
||||
foreach ($this->themeHandler->listInfo() as $name => $theme) {
|
||||
if ($theme->status) {
|
||||
$options[$name] = $theme->info['name'];
|
||||
}
|
||||
}
|
||||
|
||||
// This is not currently a fieldset but we may want it to be later,
|
||||
// so this will make it easier to change if we do.
|
||||
$form['basic'] = array();
|
||||
|
||||
$form['basic']['ui_show_master_display'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Always show the master (default) display'),
|
||||
'#default_value' => $config->get('ui.show.master_display'),
|
||||
);
|
||||
|
||||
$form['basic']['ui_show_advanced_column'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Always show advanced display settings'),
|
||||
'#default_value' => $config->get('ui.show.advanced_column'),
|
||||
);
|
||||
|
||||
$form['basic']['ui_show_display_embed'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Allow embedded displays'),
|
||||
'#description' => t('Embedded displays can be used in code via views_embed_view().'),
|
||||
'#default_value' => $config->get('ui.show.display_embed'),
|
||||
);
|
||||
|
||||
$form['basic']['ui_exposed_filter_any_label'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Label for "Any" value on non-required single-select exposed filters'),
|
||||
'#options' => array('old_any' => '<Any>', 'new_any' => $this->t('- Any -')),
|
||||
'#default_value' => $config->get('ui.exposed_filter_any_label'),
|
||||
);
|
||||
|
||||
$form['live_preview'] = array(
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Live preview settings'),
|
||||
'#open' => TRUE,
|
||||
);
|
||||
|
||||
$form['live_preview']['ui_always_live_preview'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Automatically update preview on changes'),
|
||||
'#default_value' => $config->get('ui.always_live_preview'),
|
||||
);
|
||||
|
||||
$form['live_preview']['ui_show_preview_information'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Show information and statistics about the view during live preview'),
|
||||
'#default_value' => $config->get('ui.show.preview_information'),
|
||||
);
|
||||
|
||||
$form['live_preview']['options'] = array(
|
||||
'#type' => 'container',
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="ui_show_preview_information"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$form['live_preview']['options']['ui_show_sql_query_where'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Show SQL query'),
|
||||
'#options' => array(
|
||||
'above' => $this->t('Above the preview'),
|
||||
'below' => $this->t('Below the preview'),
|
||||
),
|
||||
'#default_value' => $config->get('ui.show.sql_query.where'),
|
||||
);
|
||||
|
||||
$form['live_preview']['options']['ui_show_sql_query_enabled'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Show the SQL query'),
|
||||
'#default_value' => $config->get('ui.show.sql_query.enabled'),
|
||||
);
|
||||
$form['live_preview']['options']['ui_show_performance_statistics'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Show performance statistics'),
|
||||
'#default_value' => $config->get('ui.show.performance_statistics'),
|
||||
);
|
||||
|
||||
$form['live_preview']['options']['ui_show_additional_queries'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Show other queries run during render during live preview'),
|
||||
'#description' => $this->t("Drupal has the potential to run many queries while a view is being rendered. Checking this box will display every query run during view render as part of the live preview."),
|
||||
'#default_value' => $config->get('ui.show.additional_queries'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->config('views.settings')
|
||||
->set('ui.show.master_display', $form_state->getValue('ui_show_master_display'))
|
||||
->set('ui.show.advanced_column', $form_state->getValue('ui_show_advanced_column'))
|
||||
->set('ui.show.display_embed', $form_state->getValue('ui_show_display_embed'))
|
||||
->set('ui.exposed_filter_any_label', $form_state->getValue('ui_exposed_filter_any_label'))
|
||||
->set('ui.always_live_preview', $form_state->getValue('ui_always_live_preview'))
|
||||
->set('ui.show.preview_information', $form_state->getValue('ui_show_preview_information'))
|
||||
->set('ui.show.sql_query.where', $form_state->getValue('ui_show_sql_query_where'))
|
||||
->set('ui.show.sql_query.enabled', $form_state->getValue('ui_show_sql_query_enabled'))
|
||||
->set('ui.show.performance_statistics', $form_state->getValue('ui_show_performance_statistics'))
|
||||
->set('ui.show.additional_queries', $form_state->getValue('ui_show_additional_queries'))
|
||||
->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
119
core/modules/views_ui/src/Form/BreakLockForm.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Form\BreakLockForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityConfirmFormBase;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\user\SharedTempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Builds the form to break the lock of an edited view.
|
||||
*/
|
||||
class BreakLockForm extends EntityConfirmFormBase {
|
||||
|
||||
/**
|
||||
* Stores the Entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Stores the user tempstore.
|
||||
*
|
||||
* @var \Drupal\user\SharedTempStore
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\views_ui\Form\BreakLockForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The Entity manager.
|
||||
* @param \Drupal\user\SharedTempStoreFactory $temp_store_factory
|
||||
* The factory for the temp store object.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, SharedTempStoreFactory $temp_store_factory) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->tempStore = $temp_store_factory->get('views');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('user.shared_tempstore')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'views_ui_break_lock_confirm';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Do you want to break the lock on view %name?', array('%name' => $this->entity->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
$locked = $this->tempStore->getMetadata($this->entity->id());
|
||||
$account = $this->entityManager->getStorage('user')->load($locked->owner);
|
||||
$username = array(
|
||||
'#theme' => 'username',
|
||||
'#account' => $account,
|
||||
);
|
||||
return $this->t('By breaking this lock, any unsaved changes made by !user will be lost.', array('!user' => drupal_render($username)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return $this->entity->urlInfo('edit-form');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Break lock');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
if (!$this->tempStore->getMetadata($this->entity->id())) {
|
||||
$form['message']['#markup'] = $this->t('There is no lock on view %name to break.', array('%name' => $this->entity->id()));
|
||||
return $form;
|
||||
}
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->tempStore->delete($this->entity->id());
|
||||
$form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
|
||||
drupal_set_message($this->t('The lock has been broken and you may now edit this view.'));
|
||||
}
|
||||
|
||||
}
|
93
core/modules/views_ui/src/ParamConverter/ViewUIConverter.php
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\ParamConverter\ViewUIConverter.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\ParamConverter;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\ParamConverter\EntityConverter;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Drupal\Core\ParamConverter\ParamConverterInterface;
|
||||
use Drupal\user\SharedTempStoreFactory;
|
||||
use Drupal\views_ui\ViewUI;
|
||||
|
||||
/**
|
||||
* Provides upcasting for a view entity to be used in the Views UI.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* pattern: '/some/{view}/and/{bar}'
|
||||
* options:
|
||||
* parameters:
|
||||
* view:
|
||||
* type: 'entity:view'
|
||||
* tempstore: TRUE
|
||||
*
|
||||
* The value for {view} will be converted to a view entity prepared for the
|
||||
* Views UI and loaded from the views temp store, but it will not touch the
|
||||
* value for {bar}.
|
||||
*/
|
||||
class ViewUIConverter extends EntityConverter implements ParamConverterInterface {
|
||||
|
||||
/**
|
||||
* Stores the tempstore factory.
|
||||
*
|
||||
* @var \Drupal\user\SharedTempStoreFactory
|
||||
*/
|
||||
protected $tempStoreFactory;
|
||||
|
||||
/**
|
||||
* Constructs a new ViewUIConverter.
|
||||
*
|
||||
* @param \Drupal\user\SharedTempStoreFactory $temp_store_factory
|
||||
* The factory for the temp store object.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, SharedTempStoreFactory $temp_store_factory) {
|
||||
parent::__construct($entity_manager);
|
||||
|
||||
$this->tempStoreFactory = $temp_store_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert($value, $definition, $name, array $defaults) {
|
||||
if (!$entity = parent::convert($value, $definition, $name, $defaults)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the temp store for this variable if it needs one. Attempt to load the
|
||||
// view from the temp store, synchronize its status with the existing view,
|
||||
// and store the lock metadata.
|
||||
$store = $this->tempStoreFactory->get('views');
|
||||
if ($view = $store->get($value)) {
|
||||
if ($entity->status()) {
|
||||
$view->enable();
|
||||
}
|
||||
else {
|
||||
$view->disable();
|
||||
}
|
||||
$view->lock = $store->getMetadata($value);
|
||||
}
|
||||
// Otherwise, decorate the existing view for use in the UI.
|
||||
else {
|
||||
$view = new ViewUI($entity);
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($definition, $name, Route $route) {
|
||||
if (parent::applies($definition, $name, $route)) {
|
||||
return !empty($definition['tempstore']) && $definition['type'] === 'entity:view';
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
64
core/modules/views_ui/src/Tests/AnalyzeTest.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\AnalyzeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
|
||||
/**
|
||||
* Tests the views analyze system.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class AnalyzeTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views_ui');
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
|
||||
// Add an admin user will full rights;
|
||||
$this->admin = $this->drupalCreateUser(array('administer views'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that analyze works in general.
|
||||
*/
|
||||
function testAnalyzeBasic() {
|
||||
$this->drupalLogin($this->admin);
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_view/edit');
|
||||
$this->assertLink(t('Analyze view'));
|
||||
|
||||
// This redirects the user to the analyze form.
|
||||
$this->clickLink(t('Analyze view'));
|
||||
$this->assertText(t('View analysis'));
|
||||
|
||||
foreach (array('ok', 'warning', 'error') as $type) {
|
||||
$xpath = $this->xpath('//div[contains(@class, :class)]', array(':class' => $type));
|
||||
$this->assertTrue(count($xpath), format_string('Analyse messages with @type found', array('@type' => $type)));
|
||||
}
|
||||
|
||||
// This redirects the user back to the main views edit page.
|
||||
$this->drupalPostForm(NULL, array(), t('Ok'));
|
||||
}
|
||||
|
||||
}
|
106
core/modules/views_ui/src/Tests/AreaEntityUITest.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\AreaEntityUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
/**
|
||||
* Tests the entity area UI test.
|
||||
*
|
||||
* @see \Drupal\views\Plugin\views\area\Entity
|
||||
* @group views_ui
|
||||
*/
|
||||
class AreaEntityUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['entity_test'];
|
||||
|
||||
public function testUI() {
|
||||
// Set up a block and a entity_test entity.
|
||||
$block = Block::create(['id' => 'test_id', 'plugin' => 'system_main_block']);
|
||||
$block->save();
|
||||
|
||||
$entity_test = EntityTest::create(['bundle' => 'entity_test']);
|
||||
$entity_test->save();
|
||||
|
||||
$default = $this->randomView([]);
|
||||
$id = $default['id'];
|
||||
$view = View::load($id);
|
||||
|
||||
$this->drupalGet($view->urlInfo('edit-form'));
|
||||
|
||||
// Add a global NULL argument to the view for testing argument placeholders.
|
||||
$this->drupalPostForm("admin/structure/views/nojs/add-handler/$id/page_1/argument", ['name[views.null]' => 1], 'Add and configure contextual filters');
|
||||
$this->drupalPostForm(NULL, [], 'Apply');
|
||||
|
||||
// Configure both the entity_test area header and the block header to
|
||||
// reference the given entities.
|
||||
$this->drupalPostForm("admin/structure/views/nojs/add-handler/$id/page_1/header", ['name[views.entity_block]' => 1], 'Add and configure header');
|
||||
$this->drupalPostForm(NULL, ['options[target]' => $block->id()], 'Apply');
|
||||
|
||||
$this->drupalPostForm("admin/structure/views/nojs/add-handler/$id/page_1/header", ['name[views.entity_entity_test]' => 1], 'Add and configure header');
|
||||
$this->drupalPostForm(NULL, ['options[target]' => $entity_test->id()], 'Apply');
|
||||
|
||||
$this->drupalPostForm(NULL, [], 'Save');
|
||||
|
||||
// Confirm the correct target identifiers were saved for both entities.
|
||||
$view = View::load($id);
|
||||
$header = $view->getDisplay('default')['display_options']['header'];
|
||||
$this->assertEqual(['entity_block', 'entity_entity_test'], array_keys($header));
|
||||
|
||||
$this->assertEqual($block->id(), $header['entity_block']['target']);
|
||||
$this->assertEqual($entity_test->uuid(), $header['entity_entity_test']['target']);
|
||||
|
||||
// Confirm that the correct serial ID (for the entity_test) and config ID
|
||||
// (for the block) are displayed in the form.
|
||||
$this->drupalGet("admin/structure/views/nojs/handler/$id/page_1/header/entity_block");
|
||||
$this->assertFieldByName('options[target]', $block->id());
|
||||
|
||||
$this->drupalGet("admin/structure/views/nojs/handler/$id/page_1/header/entity_entity_test");
|
||||
$this->assertFieldByName('options[target]', $entity_test->id());
|
||||
|
||||
// Replace the header target entities with argument placeholders.
|
||||
$this->drupalPostForm("admin/structure/views/nojs/handler/$id/page_1/header/entity_block", ['options[target]' => '!1'], 'Apply');
|
||||
$this->drupalPostForm("admin/structure/views/nojs/handler/$id/page_1/header/entity_entity_test", ['options[target]' => '!1'], 'Apply');
|
||||
$this->drupalPostForm(NULL, [], 'Save');
|
||||
|
||||
// Confirm that the argument placeholders are saved.
|
||||
$view = View::load($id);
|
||||
$header = $view->getDisplay('default')['display_options']['header'];
|
||||
$this->assertEqual(['entity_block', 'entity_entity_test'], array_keys($header));
|
||||
|
||||
$this->assertEqual('!1', $header['entity_block']['target']);
|
||||
$this->assertEqual('!1', $header['entity_entity_test']['target']);
|
||||
|
||||
// Confirm that the argument placeholders are still displayed in the form.
|
||||
$this->drupalGet("admin/structure/views/nojs/handler/$id/page_1/header/entity_block");
|
||||
$this->assertFieldByName('options[target]', '!1');
|
||||
|
||||
$this->drupalGet("admin/structure/views/nojs/handler/$id/page_1/header/entity_entity_test");
|
||||
$this->assertFieldByName('options[target]', '!1');
|
||||
|
||||
// Change the targets for both headers back to the entities.
|
||||
$this->drupalPostForm("admin/structure/views/nojs/handler/$id/page_1/header/entity_block", ['options[target]' => $block->id()], 'Apply');
|
||||
$this->drupalPostForm("admin/structure/views/nojs/handler/$id/page_1/header/entity_entity_test", ['options[target]' => $entity_test->id()], 'Apply');
|
||||
$this->drupalPostForm(NULL, [], 'Save');
|
||||
|
||||
// Confirm the targets were again saved correctly and not skipped based on
|
||||
// the previous form value.
|
||||
$view = View::load($id);
|
||||
$header = $view->getDisplay('default')['display_options']['header'];
|
||||
$this->assertEqual(['entity_block', 'entity_entity_test'], array_keys($header));
|
||||
|
||||
$this->assertEqual($block->id(), $header['entity_block']['target']);
|
||||
$this->assertEqual($entity_test->uuid(), $header['entity_entity_test']['target']);
|
||||
}
|
||||
|
||||
}
|
79
core/modules/views_ui/src/Tests/CachedDataUITest.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\CachedDataUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests the user tempstore cache in the UI.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class CachedDataUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Tests the user tempstore views data in the UI.
|
||||
*/
|
||||
public function testCacheData() {
|
||||
$views_admin_user_uid = $this->fullAdminUser->id();
|
||||
|
||||
$temp_store = $this->container->get('user.shared_tempstore')->get('views');
|
||||
// The view should not be locked.
|
||||
$this->assertEqual($temp_store->getMetadata('test_view'), NULL, 'The view is not locked.');
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_view/edit');
|
||||
// Make sure we have 'changes' to the view.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/default/title', array(), t('Apply'));
|
||||
$this->assertText('You have unsaved changes.');
|
||||
$this->assertEqual($temp_store->getMetadata('test_view')->owner, $views_admin_user_uid, 'View cache has been saved.');
|
||||
|
||||
$view_cache = $temp_store->get('test_view');
|
||||
// The view should be enabled.
|
||||
$this->assertTrue($view_cache->status(), 'The view is enabled.');
|
||||
// The view should now be locked.
|
||||
$this->assertEqual($temp_store->getMetadata('test_view')->owner, $views_admin_user_uid, 'The view is locked.');
|
||||
|
||||
// Cancel the view edit and make sure the cache is deleted.
|
||||
$this->drupalPostForm(NULL, array(), t('Cancel'));
|
||||
$this->assertEqual($temp_store->getMetadata('test_view'), NULL, 'User tempstore data has been removed.');
|
||||
// Test we are redirected to the view listing page.
|
||||
$this->assertUrl('admin/structure/views', array(), 'Redirected back to the view listing page.');
|
||||
|
||||
// Login with another user and make sure the view is locked and break.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/default/title', array(), t('Apply'));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_view/edit');
|
||||
// Test that save and cancel buttons are not shown.
|
||||
$this->assertNoFieldById('edit-actions-submit', t('Save'));
|
||||
$this->assertNoFieldById('edit-actions-cancel', t('Cancel'));
|
||||
// Test we have the break lock link.
|
||||
$this->assertLinkByHref('admin/structure/views/view/test_view/break-lock');
|
||||
// Break the lock.
|
||||
$this->clickLink(t('break this lock'));
|
||||
$this->drupalPostForm(NULL, array(), t('Break lock'));
|
||||
// Test that save and cancel buttons are shown.
|
||||
$this->assertFieldById('edit-actions-submit', t('Save'));
|
||||
$this->assertFieldById('edit-actions-cancel', t('Cancel'));
|
||||
// Test we can save the view.
|
||||
$this->drupalPostForm('admin/structure/views/view/test_view/edit', array(), t('Save'));
|
||||
$this->assertRaw(t('The view %view has been saved.', array('%view' => 'Test view')));
|
||||
|
||||
// Test that a deleted view has no tempstore data.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/default/title', array(), t('Apply'));
|
||||
$this->drupalPostForm('admin/structure/views/view/test_view/delete', array(), t('Delete'));
|
||||
// No view tempstore data should be returned for this view after deletion.
|
||||
$this->assertEqual($temp_store->getMetadata('test_view'), NULL, 'View tempstore data has been removed after deletion.');
|
||||
}
|
||||
|
||||
}
|
112
core/modules/views_ui/src/Tests/CustomBooleanTest.php
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\CustomBooleanTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the UI and functionality for the Custom boolean field handler options.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\field\Boolean
|
||||
*/
|
||||
class CustomBooleanTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* \Drupal\views\Tests\ViewTestBase::viewsData().
|
||||
*/
|
||||
public function viewsData() {
|
||||
$data = parent::viewsData();
|
||||
$data['views_test_data']['age']['field']['id'] = 'boolean';
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Tests\ViewTestBase::dataSet().
|
||||
*/
|
||||
public function dataSet() {
|
||||
$data = parent::dataSet();
|
||||
$data[0]['age'] = 0;
|
||||
$data[3]['age'] = 0;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the setting and output of custom labels for boolean values.
|
||||
*/
|
||||
public function testCustomOption() {
|
||||
// Add the boolean field handler to the test view.
|
||||
$view = Views::getView('test_view');
|
||||
$view->setDisplay();
|
||||
|
||||
$view->displayHandlers->get('default')->overrideOption('fields', array(
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test_data',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
'plugin_id' => 'boolean',
|
||||
),
|
||||
));
|
||||
$view->save();
|
||||
|
||||
$this->executeView($view);
|
||||
|
||||
$custom_true = 'Yay';
|
||||
$custom_false = 'Nay';
|
||||
|
||||
// Set up some custom value mappings for different types.
|
||||
$custom_values = array(
|
||||
'plain' => array(
|
||||
'true' => $custom_true,
|
||||
'false' => $custom_false,
|
||||
'test' => 'assertTrue',
|
||||
),
|
||||
'allowed tag' => array(
|
||||
'true' => '<p>' . $custom_true . '</p>',
|
||||
'false' => '<p>' . $custom_false . '</p>',
|
||||
'test' => 'assertTrue',
|
||||
),
|
||||
'disallowed tag' => array(
|
||||
'true' => '<script>' . $custom_true . '</script>',
|
||||
'false' => '<script>' . $custom_false . '</script>',
|
||||
'test' => 'assertFalse',
|
||||
),
|
||||
);
|
||||
|
||||
// Run the same tests on each type.
|
||||
foreach ($custom_values as $type => $values) {
|
||||
$options = array(
|
||||
'options[type]' => 'custom',
|
||||
'options[type_custom_true]' => $values['true'],
|
||||
'options[type_custom_false]' => $values['false'],
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_view/default/field/age', $options, 'Apply');
|
||||
|
||||
// Save the view.
|
||||
$this->drupalPostForm('admin/structure/views/view/test_view', array(), 'Save');
|
||||
|
||||
$view = Views::getView('test_view');
|
||||
$output = $view->preview();
|
||||
$output = \Drupal::service('renderer')->renderRoot($output);
|
||||
|
||||
$replacements = array('%type' => $type);
|
||||
$this->{$values['test']}(strpos($output, $values['true']), format_string('Expected custom boolean TRUE value in output for %type.', $replacements));
|
||||
$this->{$values['test']}(strpos($output, $values['false']), format_string('Expected custom boolean FALSE value in output for %type', $replacements));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
228
core/modules/views_ui/src/Tests/DefaultViewsTest.php
Normal file
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\DefaultViewsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests enabling, disabling, and reverting default views via the listing page.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class DefaultViewsTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view_status', 'test_page_display_menu', 'test_page_display_arguments');
|
||||
|
||||
/**
|
||||
* Tests default views.
|
||||
*/
|
||||
function testDefaultViews() {
|
||||
// Make sure the view starts off as disabled (does not appear on the listing
|
||||
// page).
|
||||
$edit_href = 'admin/structure/views/view/glossary';
|
||||
$this->drupalGet('admin/structure/views');
|
||||
// @todo Disabled default views do now appear on the front page. Test this
|
||||
// behavior with templates instead.
|
||||
// $this->assertNoLinkByHref($edit_href);
|
||||
|
||||
// Enable the view, and make sure it is now visible on the main listing
|
||||
// page.
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$this->clickViewsOperationLink(t('Enable'), '/glossary/');
|
||||
$this->assertUrl('admin/structure/views');
|
||||
$this->assertLinkByHref($edit_href);
|
||||
|
||||
// It should not be possible to revert the view yet.
|
||||
// @todo Figure out how to handle this with the new configuration system.
|
||||
// $this->assertNoLink(t('Revert'));
|
||||
// $revert_href = 'admin/structure/views/view/glossary/revert';
|
||||
// $this->assertNoLinkByHref($revert_href);
|
||||
|
||||
// Edit the view and change the title. Make sure that the new title is
|
||||
// displayed.
|
||||
$new_title = $this->randomMachineName(16);
|
||||
$edit = array('title' => $new_title);
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/glossary/page_1/title', $edit, t('Apply'));
|
||||
$this->drupalPostForm('admin/structure/views/view/glossary/edit/page_1', array(), t('Save'));
|
||||
$this->drupalGet('glossary');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($new_title);
|
||||
|
||||
// Save another view in the UI.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/archive/page_1/title', array(), t('Apply'));
|
||||
$this->drupalPostForm('admin/structure/views/view/archive/edit/page_1', array(), t('Save'));
|
||||
|
||||
// Check there is an enable link. i.e. The view has not been enabled after
|
||||
// editing.
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$this->assertLinkByHref('admin/structure/views/view/archive/enable');
|
||||
// Enable it again so it can be tested for access permissions.
|
||||
$this->clickViewsOperationLink(t('Enable'), '/archive/');
|
||||
|
||||
// It should now be possible to revert the view. Do that, and make sure the
|
||||
// view title we added above no longer is displayed.
|
||||
// $this->drupalGet('admin/structure/views');
|
||||
// $this->assertLink(t('Revert'));
|
||||
// $this->assertLinkByHref($revert_href);
|
||||
// $this->drupalPostForm($revert_href, array(), t('Revert'));
|
||||
// $this->drupalGet('glossary');
|
||||
// $this->assertNoText($new_title);
|
||||
|
||||
// Duplicate the view and check that the normal schema of duplicated views is used.
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$this->clickViewsOperationLink(t('Duplicate'), '/glossary');
|
||||
$edit = array(
|
||||
'id' => 'duplicate_of_glossary',
|
||||
);
|
||||
$this->assertTitle(t('Duplicate of @label | @site-name', array('@label' => 'Glossary', '@site-name' => $this->config('system.site')->get('name'))));
|
||||
$this->drupalPostForm(NULL, $edit, t('Duplicate'));
|
||||
$this->assertUrl('admin/structure/views/view/duplicate_of_glossary', array(), 'The normal duplicating name schema is applied.');
|
||||
|
||||
// Duplicate a view and set a custom name.
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$this->clickViewsOperationLink(t('Duplicate'), '/glossary');
|
||||
$random_name = strtolower($this->randomMachineName());
|
||||
$this->drupalPostForm(NULL, array('id' => $random_name), t('Duplicate'));
|
||||
$this->assertUrl("admin/structure/views/view/$random_name", array(), 'The custom view name got saved.');
|
||||
|
||||
// Now disable the view, and make sure it stops appearing on the main view
|
||||
// listing page but instead goes back to displaying on the disabled views
|
||||
// listing page.
|
||||
// @todo Test this behavior with templates instead.
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$this->clickViewsOperationLink(t('Disable'), '/glossary/');
|
||||
// $this->assertUrl('admin/structure/views');
|
||||
// $this->assertNoLinkByHref($edit_href);
|
||||
// The easiest way to verify it appears on the disabled views listing page
|
||||
// is to try to click the "enable" link from there again.
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$this->clickViewsOperationLink(t('Enable'), '/glossary/');
|
||||
$this->assertUrl('admin/structure/views');
|
||||
$this->assertLinkByHref($edit_href);
|
||||
|
||||
// Clear permissions for anonymous users to check access for default views.
|
||||
Role::load(RoleInterface::ANONYMOUS_ID)->revokePermission('access content')->save();
|
||||
|
||||
// Test the default views disclose no data by default.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('glossary');
|
||||
$this->assertResponse(403);
|
||||
$this->drupalGet('archive');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Test deleting a view.
|
||||
$this->drupalLogin($this->fullAdminUser);
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$this->clickViewsOperationLink(t('Delete'), '/glossary/');
|
||||
// Submit the confirmation form.
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
// Ensure the view is no longer listed.
|
||||
$this->assertUrl('admin/structure/views');
|
||||
$this->assertNoLinkByHref($edit_href);
|
||||
// Ensure the view is no longer available.
|
||||
$this->drupalGet($edit_href);
|
||||
$this->assertResponse(404);
|
||||
$this->assertText('Page not found');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that enabling views moves them to the correct table.
|
||||
*/
|
||||
function testSplitListing() {
|
||||
// Build a re-usable xpath query.
|
||||
$xpath = '//div[@id="views-entity-list"]/div[@class = :status]/table//tr[@title = :title]';
|
||||
$arguments = array(
|
||||
':status' => 'views-list-section enabled',
|
||||
':title' => t('Machine name: test_view_status'),
|
||||
);
|
||||
|
||||
$this->drupalGet('admin/structure/views');
|
||||
|
||||
$elements = $this->xpath($xpath, $arguments);
|
||||
$this->assertIdentical(count($elements), 0, 'A disabled view is not found in the enabled views table.');
|
||||
|
||||
$arguments[':status'] = 'views-list-section disabled';
|
||||
$elements = $this->xpath($xpath, $arguments);
|
||||
$this->assertIdentical(count($elements), 1, 'A disabled view is found in the disabled views table.');
|
||||
|
||||
// Enable the view.
|
||||
$this->clickViewsOperationLink(t('Enable'), '/test_view_status/');
|
||||
|
||||
$elements = $this->xpath($xpath, $arguments);
|
||||
$this->assertIdentical(count($elements), 0, 'After enabling a view, it is not found in the disabled views table.');
|
||||
|
||||
$arguments[':status'] = 'views-list-section enabled';
|
||||
$elements = $this->xpath($xpath, $arguments);
|
||||
$this->assertIdentical(count($elements), 1, 'After enabling a view, it is found in the enabled views table.');
|
||||
|
||||
// Attempt to disable the view by path directly, with no token.
|
||||
$this->drupalGet('admin/structure/views/view/test_view_status/disable');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that page displays show the correct path.
|
||||
*/
|
||||
public function testPathDestination() {
|
||||
$this->drupalGet('admin/structure/views');
|
||||
|
||||
// Check that links to views on default tabs are rendered correctly.
|
||||
$this->assertLinkByHref('test_page_display_menu');
|
||||
$this->assertNoLinkByHref('test_page_display_menu/default');
|
||||
$this->assertLinkByHref('test_page_display_menu/local');
|
||||
|
||||
// Check that a dynamic path is shown as text.
|
||||
$this->assertRaw('test_route_with_suffix/%/suffix');
|
||||
$this->assertNoLinkByHref(Url::fromUri('base:test_route_with_suffix/%/suffix')->toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Click a link to perform an operation on a view.
|
||||
*
|
||||
* In general, we expect lots of links titled "enable" or "disable" on the
|
||||
* various views listing pages, and they might have tokens in them. So we
|
||||
* need special code to find the correct one to click.
|
||||
*
|
||||
* @param $label
|
||||
* Text between the anchor tags of the desired link.
|
||||
* @param $unique_href_part
|
||||
* A unique string that is expected to occur within the href of the desired
|
||||
* link. For example, if the link URL is expected to look like
|
||||
* "admin/structure/views/view/glossary/*", then "/glossary/" could be
|
||||
* passed as the expected unique string.
|
||||
*
|
||||
* @return
|
||||
* The page content that results from clicking on the link, or FALSE on
|
||||
* failure. Failure also results in a failed assertion.
|
||||
*/
|
||||
function clickViewsOperationLink($label, $unique_href_part) {
|
||||
$links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
|
||||
foreach ($links as $link_index => $link) {
|
||||
$position = strpos($link['href'], $unique_href_part);
|
||||
if ($position !== FALSE) {
|
||||
$index = $link_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertTrue(isset($index), format_string('Link to "@label" containing @part found.', array('@label' => $label, '@part' => $unique_href_part)));
|
||||
if (isset($index)) {
|
||||
return $this->clickLink($label, $index);
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
60
core/modules/views_ui/src/Tests/DisplayAttachmentTest.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\DisplayAttachmentTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the UI for the attachment display plugin.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\display\Attachment
|
||||
*/
|
||||
class DisplayAttachmentTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_attachment_ui');
|
||||
|
||||
/**
|
||||
* Tests the attachment UI.
|
||||
*/
|
||||
public function testAttachmentUI() {
|
||||
$this->drupalGet('admin/structure/views/view/test_attachment_ui/edit/attachment_1');
|
||||
$this->assertText(t('Not defined'), 'The right text appears if there is no attachment selection yet.');
|
||||
|
||||
$attachment_display_url = 'admin/structure/views/nojs/display/test_attachment_ui/attachment_1/displays';
|
||||
$this->drupalGet($attachment_display_url);
|
||||
|
||||
foreach (array('default', 'page-1') as $display_id) {
|
||||
$this->assertNoFieldChecked("edit-displays-$display_id", format_string('Make sure the @display_id can be marked as attached', array('@display_id' => $display_id)));
|
||||
}
|
||||
|
||||
// Save the attachments and test the value on the view.
|
||||
$this->drupalPostForm($attachment_display_url, array('displays[page_1]' => 1), t('Apply'));
|
||||
$result = $this->xpath('//a[@id = :id]', array(':id' => 'views-attachment-1-displays'));
|
||||
$this->assertEqual($result[0]->attributes()->title, t('Page'));
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
$view = Views::getView('test_attachment_ui');
|
||||
$view->initDisplay();
|
||||
$this->assertEqual(array_keys(array_filter($view->displayHandlers->get('attachment_1')->getOption('displays'))), array('page_1'), 'The attached displays got saved as expected');
|
||||
|
||||
$this->drupalPostForm($attachment_display_url, array('displays[default]' => 1, 'displays[page_1]' => 1), t('Apply'));
|
||||
$result = $this->xpath('//a[@id = :id]', array(':id' => 'views-attachment-1-displays'));
|
||||
$this->assertEqual($result[0]->attributes()->title, t('Multiple displays'));
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
$view = Views::getView('test_attachment_ui');
|
||||
$view->initDisplay();
|
||||
$this->assertEqual(array_keys($view->displayHandlers->get('attachment_1')->getOption('displays')), array('default', 'page_1'), 'The attached displays got saved as expected');
|
||||
}
|
||||
}
|
143
core/modules/views_ui/src/Tests/DisplayCRUDTest.php
Normal file
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\DisplayCRUDTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests creation, retrieval, updating, and deletion of displays in the Web UI.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class DisplayCRUDTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_display');
|
||||
|
||||
/**
|
||||
* Modules to enable
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('contextual');
|
||||
|
||||
/**
|
||||
* Tests adding a display.
|
||||
*/
|
||||
public function testAddDisplay() {
|
||||
// Show the master display.
|
||||
$this->config('views.settings')->set('ui.show.master_display', TRUE)->save();
|
||||
|
||||
$settings['page[create]'] = FALSE;
|
||||
$view = $this->randomView($settings);
|
||||
|
||||
$path_prefix = 'admin/structure/views/view/' . $view['id'] .'/edit';
|
||||
$this->drupalGet($path_prefix);
|
||||
|
||||
// Add a new display.
|
||||
$this->drupalPostForm(NULL, array(), 'Add Page');
|
||||
$this->assertLinkByHref($path_prefix . '/page_1', 0, 'Make sure after adding a display the new display appears in the UI');
|
||||
|
||||
$this->assertNoLink('Master*', 'Make sure the master display is not marked as changed.');
|
||||
$this->assertLink('Page*', 0, 'Make sure the added display is marked as changed.');
|
||||
|
||||
$this->drupalPostForm("admin/structure/views/nojs/display/{$view['id']}/page_1/path", array('path' => 'test/path'), t('Apply'));
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests removing a display.
|
||||
*/
|
||||
public function testRemoveDisplay() {
|
||||
$view = $this->randomView();
|
||||
$path_prefix = 'admin/structure/views/view/' . $view['id'] .'/edit';
|
||||
|
||||
$this->drupalGet($path_prefix . '/default');
|
||||
$this->assertNoFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-delete', 'Delete Page', 'Make sure there is no delete button on the default display.');
|
||||
|
||||
$this->drupalGet($path_prefix . '/page_1');
|
||||
$this->assertFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-delete', 'Delete Page', 'Make sure there is a delete button on the page display.');
|
||||
|
||||
// Delete the page, so we can test the undo process.
|
||||
$this->drupalPostForm($path_prefix . '/page_1', array(), 'Delete Page');
|
||||
$this->assertFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-undo-delete', 'Undo delete of Page', 'Make sure there a undo button on the page display after deleting.');
|
||||
$element = $this->xpath('//a[contains(@href, :href) and contains(@class, :class)]', array(':href' => $path_prefix . '/page_1', ':class' => 'views-display-deleted-link'));
|
||||
$this->assertTrue(!empty($element), 'Make sure the display link is marked as to be deleted.');
|
||||
|
||||
$element = $this->xpath('//a[contains(@href, :href) and contains(@class, :class)]', array(':href' => $path_prefix . '/page_1', ':class' => 'views-display-deleted-link'));
|
||||
$this->assertTrue(!empty($element), 'Make sure the display link is marked as to be deleted.');
|
||||
|
||||
// Undo the deleting of the display.
|
||||
$this->drupalPostForm($path_prefix . '/page_1', array(), 'Undo delete of Page');
|
||||
$this->assertNoFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-undo-delete', 'Undo delete of Page', 'Make sure there is no undo button on the page display after reverting.');
|
||||
$this->assertFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-delete', 'Delete Page', 'Make sure there is a delete button on the page display after the reverting.');
|
||||
|
||||
// Now delete again and save the view.
|
||||
$this->drupalPostForm($path_prefix . '/page_1', array(), 'Delete Page');
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
$this->assertNoLinkByHref($path_prefix . '/page_1', 'Make sure there is no display tab for the deleted display.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the correct display is loaded by default.
|
||||
*/
|
||||
public function testDefaultDisplay() {
|
||||
$this->drupalGet('admin/structure/views/view/test_display');
|
||||
$elements = $this->xpath('//*[@id="views-page-1-display-title"]');
|
||||
$this->assertEqual(count($elements), 1, 'The page display is loaded as the default display.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the duplicating of a display.
|
||||
*/
|
||||
public function testDuplicateDisplay() {
|
||||
$view = $this->randomView();
|
||||
$path_prefix = 'admin/structure/views/view/' . $view['id'] .'/edit';
|
||||
$path = $view['page[path]'];
|
||||
|
||||
$this->drupalGet($path_prefix);
|
||||
$this->drupalPostForm(NULL, array(), 'Duplicate Page');
|
||||
$this->assertLinkByHref($path_prefix . '/page_2', 0, 'Make sure after duplicating the new display appears in the UI');
|
||||
$this->assertUrl($path_prefix . '/page_2', array(), 'The user got redirected to the new display.');
|
||||
|
||||
// Set the title and override the css classes.
|
||||
$random_title = $this->randomMachineName();
|
||||
$random_css = $this->randomMachineName();
|
||||
$this->drupalPostForm("admin/structure/views/nojs/display/{$view['id']}/page_2/title", array('title' => $random_title), t('Apply'));
|
||||
$this->drupalPostForm("admin/structure/views/nojs/display/{$view['id']}/page_2/css_class", array('override[dropdown]' => 'page_2', 'css_class' => $random_css), t('Apply'));
|
||||
|
||||
// Duplicate as a different display type.
|
||||
$this->drupalPostForm(NULL, array(), 'Duplicate as Block');
|
||||
$this->assertLinkByHref($path_prefix . '/block_1', 0, 'Make sure after duplicating the new display appears in the UI');
|
||||
$this->assertUrl($path_prefix . '/block_1', array(), 'The user got redirected to the new display.');
|
||||
$this->assertText(t('Block settings'));
|
||||
$this->assertNoText(t('Page settings'));
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$view = Views::getView($view['id']);
|
||||
$view->initDisplay();
|
||||
|
||||
$page_2 = $view->displayHandlers->get('page_2');
|
||||
$this->assertTrue($page_2, 'The new page display got saved.');
|
||||
$this->assertEqual($page_2->display['display_title'], 'Page');
|
||||
$this->assertEqual($page_2->display['display_options']['path'], $path);
|
||||
$block_1 = $view->displayHandlers->get('block_1');
|
||||
$this->assertTrue($block_1, 'The new block display got saved.');
|
||||
$this->assertEqual($block_1->display['display_plugin'], 'block');
|
||||
$this->assertEqual($block_1->display['display_title'], 'Block', 'The new display title got generated as expected.');
|
||||
$this->assertFalse(isset($block_1->display['display_options']['path']));
|
||||
$this->assertEqual($block_1->getOption('title'), $random_title, 'The overridden title option from the display got copied into the duplicate');
|
||||
$this->assertEqual($block_1->getOption('css_class'), $random_css, 'The overridden css_class option from the display got copied into the duplicate');
|
||||
}
|
||||
|
||||
}
|
49
core/modules/views_ui/src/Tests/DisplayExtenderUITest.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\DisplayExtenderUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the display extender UI.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class DisplayExtenderUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Tests the display extender UI.
|
||||
*/
|
||||
public function testDisplayExtenderUI() {
|
||||
$this->config('views.settings')->set('display_extenders', array('display_extender_test'))->save();
|
||||
|
||||
$view = Views::getView('test_view');
|
||||
$view_edit_url = "admin/structure/views/view/{$view->storage->id()}/edit";
|
||||
$display_option_url = 'admin/structure/views/nojs/display/test_view/default/test_extender_test_option';
|
||||
|
||||
$this->drupalGet($view_edit_url);
|
||||
$this->assertLinkByHref($display_option_url, 0, 'Make sure the option defined by the test display extender appears in the UI.');
|
||||
|
||||
$random_text = $this->randomMachineName();
|
||||
$this->drupalPostForm($display_option_url, array('test_extender_test_option' => $random_text), t('Apply'));
|
||||
$this->assertLink($random_text);
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$view = Views::getView($view->storage->id());
|
||||
$view->initDisplay();
|
||||
$display_extender_options = $view->display_handler->getOption('display_extenders');
|
||||
$this->assertEqual($display_extender_options['display_extender_test']['test_extender_test_option'], $random_text, 'Make sure that the display extender option got saved.');
|
||||
}
|
||||
|
||||
}
|
83
core/modules/views_ui/src/Tests/DisplayFeedTest.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\DisplayFeedTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests the UI for feed display plugin.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\display\Feed
|
||||
*/
|
||||
class DisplayFeedTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_display_feed', 'test_style_opml');
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views_ui', 'aggregator');
|
||||
|
||||
/**
|
||||
* Tests feed display admin UI.
|
||||
*/
|
||||
public function testFeedUI() {
|
||||
// Test both RSS and OPML feeds.
|
||||
foreach (self::$testViews as $view_name) {
|
||||
$this->checkFeedViewUi($view_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks views UI for a specific feed view.
|
||||
*
|
||||
* @param string $view_name
|
||||
* The view name to check against.
|
||||
*/
|
||||
protected function checkFeedViewUi($view_name) {
|
||||
$this->drupalGet('admin/structure/views');
|
||||
// Verify that the page lists the $view_name view.
|
||||
// Regression test: ViewListBuilder::getDisplayPaths() did not properly
|
||||
// check whether a DisplayPluginCollection was returned in iterating over
|
||||
// all displays.
|
||||
$this->assertText($view_name);
|
||||
|
||||
// Check the attach TO interface.
|
||||
$this->drupalGet('admin/structure/views/nojs/display/' . $view_name . '/feed_1/displays');
|
||||
|
||||
// Load all the options of the checkbox.
|
||||
$result = $this->xpath('//div[@id="edit-displays"]/div');
|
||||
$options = array();
|
||||
foreach ($result as $item) {
|
||||
foreach ($item->input->attributes() as $attribute => $value) {
|
||||
if ($attribute == 'value') {
|
||||
$options[] = (string) $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertEqual($options, array('default', 'page'), 'Make sure all displays appears as expected.');
|
||||
|
||||
// Post and save this and check the output.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/' . $view_name . '/feed_1/displays', array('displays[page]' => 'page'), t('Apply'));
|
||||
$this->drupalGet('admin/structure/views/view/' . $view_name . '/edit/feed_1');
|
||||
$this->assertFieldByXpath('//*[@id="views-feed-1-displays"]', 'Page');
|
||||
|
||||
// Add the default display, so there should now be multiple displays.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/' . $view_name . '/feed_1/displays', array('displays[default]' => 'default'), t('Apply'));
|
||||
$this->drupalGet('admin/structure/views/view/' . $view_name . '/edit/feed_1');
|
||||
$this->assertFieldByXpath('//*[@id="views-feed-1-displays"]', 'Multiple displays');
|
||||
}
|
||||
|
||||
}
|
152
core/modules/views_ui/src/Tests/DisplayPathTest.php
Normal file
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\DisplayPathTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests the UI of generic display path plugin.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\display\PathPluginBase
|
||||
*/
|
||||
class DisplayPathTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('menu_ui');
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view', 'test_page_display_menu');
|
||||
|
||||
/**
|
||||
* Runs the tests.
|
||||
*/
|
||||
public function testPathUI() {
|
||||
$this->doBasicPathUITest();
|
||||
$this->doAdvancedPathsValidationTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic functionality in configuring a view.
|
||||
*/
|
||||
protected function doBasicPathUITest() {
|
||||
$this->drupalGet('admin/structure/views/view/test_view');
|
||||
|
||||
// Add a new page display and check the appearing text.
|
||||
$this->drupalPostForm(NULL, array(), 'Add Page');
|
||||
$this->assertText(t('No path is set'), 'The right text appears if no path was set.');
|
||||
$this->assertNoLink(t('View @display', array('@display' => 'page')), 'No view page link found on the page.');
|
||||
|
||||
// Save a path and make sure the summary appears as expected.
|
||||
$random_path = $this->randomMachineName();
|
||||
// @todo Once https://www.drupal.org/node/2351379 is resolved, Views will no
|
||||
// longer use Url::fromUri(), and this path will be able to contain ':'.
|
||||
$random_path = str_replace(':', '', $random_path);
|
||||
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/page_1/path', array('path' => $random_path), t('Apply'));
|
||||
$this->assertText('/' . $random_path, 'The custom path appears in the summary.');
|
||||
$this->assertLink(t('View @display', array('@display' => 'Page')), 0, 'view page link found on the page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a couple of invalid path patterns.
|
||||
*/
|
||||
protected function doAdvancedPathsValidationTest() {
|
||||
$url = 'admin/structure/views/nojs/display/test_view/page_1/path';
|
||||
|
||||
$this->drupalPostForm($url, array('path' => '%/magrathea'), t('Apply'));
|
||||
$this->assertUrl($url);
|
||||
$this->assertText('"%" may not be used for the first segment of a path.');
|
||||
|
||||
$this->drupalPostForm($url, array('path' => 'user/%1/example'), t('Apply'));
|
||||
$this->assertUrl($url);
|
||||
$this->assertText("Numeric placeholders may not be used. Please use plain placeholders (%).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting a page display that has no path.
|
||||
*/
|
||||
public function testDeleteWithNoPath() {
|
||||
$this->drupalGet('admin/structure/views/view/test_view');
|
||||
$this->drupalPostForm(NULL, array(), t('Add Page'));
|
||||
$this->drupalPostForm(NULL, array(), t('Delete Page'));
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$this->assertRaw(t('The view %view has been saved.', array('%view' => 'Test view')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the menu and tab option form.
|
||||
*/
|
||||
public function testMenuOptions() {
|
||||
$this->container->get('module_installer')->install(array('menu_ui'));
|
||||
$this->drupalGet('admin/structure/views/view/test_view');
|
||||
|
||||
// Add a new page display.
|
||||
$this->drupalPostForm(NULL, array(), 'Add Page');
|
||||
|
||||
// Add an invalid path (only fragment).
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/page_1/path', array('path' => '#foo'), t('Apply'));
|
||||
$this->assertText('Path is empty');
|
||||
|
||||
// Add an invalid path with a query.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/page_1/path', array('path' => 'foo?bar'), t('Apply'));
|
||||
$this->assertText('No query allowed.');
|
||||
|
||||
// Add an invalid path with just a query.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/page_1/path', array('path' => '?bar'), t('Apply'));
|
||||
$this->assertText('Path is empty');
|
||||
|
||||
// Provide a random, valid path string.
|
||||
$random_string = $this->randomMachineName();
|
||||
|
||||
// Save a path.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/page_1/path', array('path' => $random_string), t('Apply'));
|
||||
$this->drupalGet('admin/structure/views/view/test_view');
|
||||
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/page_1/menu', array('menu[type]' => 'default tab', 'menu[title]' => 'Test tab title'), t('Apply'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertUrl('admin/structure/views/nojs/display/test_view/page_1/tab_options');
|
||||
|
||||
$this->drupalPostForm(NULL, array('tab_options[type]' => 'tab', 'tab_options[title]' => $this->randomString()), t('Apply'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertUrl('admin/structure/views/view/test_view/edit/page_1');
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_view');
|
||||
$this->assertLink(t('Tab: @title', array('@title' => 'Test tab title')));
|
||||
// If it's a default tab, it should also have an additional settings link.
|
||||
$this->assertLinkByHref('admin/structure/views/nojs/display/test_view/page_1/tab_options');
|
||||
|
||||
// Ensure that you can select a parent in case the parent does not exist.
|
||||
$this->drupalGet('admin/structure/views/nojs/display/test_page_display_menu/page_5/menu');
|
||||
$this->assertResponse(200);
|
||||
$menu_parent = $this->xpath('//select[@id="edit-menu-parent"]');
|
||||
$menu_options = (array) $menu_parent[0]->option;
|
||||
unset($menu_options['@attributes']);
|
||||
|
||||
$this->assertEqual([
|
||||
'<User account menu>',
|
||||
'-- My account',
|
||||
'-- Log out',
|
||||
'<Administration>',
|
||||
'<Footer>',
|
||||
'<Main navigation>',
|
||||
'<Tools>',
|
||||
'-- Compose tips (disabled)',
|
||||
'-- Test menu link',
|
||||
], $menu_options);
|
||||
|
||||
// The cache contexts associated with the (in)accessible menu links are
|
||||
// bubbled.
|
||||
$this->assertCacheContext('user.permissions');
|
||||
}
|
||||
|
||||
}
|
240
core/modules/views_ui/src/Tests/DisplayTest.php
Normal file
|
@ -0,0 +1,240 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\DisplayTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
use Drupal\views\Views;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
|
||||
/**
|
||||
* Tests the display UI.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class DisplayTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_display');
|
||||
|
||||
/**
|
||||
* Modules to enable
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('contextual');
|
||||
|
||||
/**
|
||||
* Tests reordering of displays.
|
||||
*/
|
||||
public function testReorderDisplay() {
|
||||
$view = array(
|
||||
'block[create]' => TRUE
|
||||
);
|
||||
$view = $this->randomView($view);
|
||||
|
||||
$this->clickLink(t('Reorder displays'));
|
||||
$this->assertTrue($this->xpath('//tr[@id="display-row-default"]'), 'Make sure the default display appears on the reorder listing');
|
||||
$this->assertTrue($this->xpath('//tr[@id="display-row-page_1"]'), 'Make sure the page display appears on the reorder listing');
|
||||
$this->assertTrue($this->xpath('//tr[@id="display-row-block_1"]'), 'Make sure the block display appears on the reorder listing');
|
||||
|
||||
// Ensure the view displays are in the expected order in configuration.
|
||||
$expected_display_order = array('default', 'block_1', 'page_1');
|
||||
$this->assertEqual(array_keys(Views::getView($view['id'])->storage->get('display')), $expected_display_order, 'The correct display names are present.');
|
||||
// Put the block display in front of the page display.
|
||||
$edit = array(
|
||||
'displays[page_1][weight]' => 2,
|
||||
'displays[block_1][weight]' => 1
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
$view = Views::getView($view['id']);
|
||||
$displays = $view->storage->get('display');
|
||||
$this->assertEqual($displays['default']['position'], 0, 'Make sure the master display comes first.');
|
||||
$this->assertEqual($displays['block_1']['position'], 1, 'Make sure the block display comes before the page display.');
|
||||
$this->assertEqual($displays['page_1']['position'], 2, 'Make sure the page display comes after the block display.');
|
||||
|
||||
// Ensure the view displays are in the expected order in configuration.
|
||||
$this->assertEqual(array_keys($view->storage->get('display')), $expected_display_order, 'The correct display names are present.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests disabling of a display.
|
||||
*/
|
||||
public function testDisableDisplay() {
|
||||
$view = $this->randomView();
|
||||
$path_prefix = 'admin/structure/views/view/' . $view['id'] .'/edit';
|
||||
|
||||
$this->drupalGet($path_prefix);
|
||||
$this->assertFalse($this->xpath('//div[contains(@class, :class)]', array(':class' => 'views-display-disabled')), 'Make sure the disabled display css class does not appear after initial adding of a view.');
|
||||
|
||||
$this->assertFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-disable', '', 'Make sure the disable button is visible.');
|
||||
$this->assertNoFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-enable', '', 'Make sure the enable button is not visible.');
|
||||
$this->drupalPostForm(NULL, array(), 'Disable Page');
|
||||
$this->assertTrue($this->xpath('//div[contains(@class, :class)]', array(':class' => 'views-display-disabled')), 'Make sure the disabled display css class appears once the display is marked as such.');
|
||||
|
||||
$this->assertNoFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-disable', '', 'Make sure the disable button is not visible.');
|
||||
$this->assertFieldById('edit-displays-settings-settings-content-tab-content-details-top-actions-enable', '', 'Make sure the enable button is visible.');
|
||||
$this->drupalPostForm(NULL, array(), 'Enable Page');
|
||||
$this->assertFalse($this->xpath('//div[contains(@class, :class)]', array(':class' => 'views-display-disabled')), 'Make sure the disabled display css class does not appears once the display is enabled again.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests views_ui_views_plugins_display_alter is altering plugin definitions.
|
||||
*/
|
||||
public function testDisplayPluginsAlter() {
|
||||
$definitions = Views::pluginManager('display')->getDefinitions();
|
||||
|
||||
$expected = array(
|
||||
'route_name' => 'entity.view.edit_form',
|
||||
'route_parameters_names' => array('view' => 'id'),
|
||||
);
|
||||
|
||||
// Test the expected views_ui array exists on each definition.
|
||||
foreach ($definitions as $definition) {
|
||||
$this->assertIdentical($definition['contextual links']['entity.view.edit_form'], $expected, 'Expected views_ui array found in plugin definition.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests display areas.
|
||||
*/
|
||||
public function testDisplayAreas() {
|
||||
// Show the advanced column.
|
||||
$this->config('views.settings')->set('ui.show.advanced_column', TRUE)->save();
|
||||
|
||||
// Add a new data display to the view.
|
||||
$view = Views::getView('test_display');
|
||||
$view->storage->addDisplay('display_no_area_test');
|
||||
$view->save();
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_display/edit/display_no_area_test_1');
|
||||
|
||||
$areas = array(
|
||||
'header',
|
||||
'footer',
|
||||
'empty',
|
||||
);
|
||||
|
||||
// Assert that the expected text is found in each area category.
|
||||
foreach ($areas as $type) {
|
||||
$element = $this->xpath('//div[contains(@class, :class)]/div', array(':class' => $type));
|
||||
$this->assertEqual((string) $element[0], SafeMarkup::format('The selected display type does not use @type plugins', array('@type' => $type)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the link-display setting.
|
||||
*/
|
||||
public function testLinkDisplay() {
|
||||
// Test setting the link display in the UI form.
|
||||
$path = 'admin/structure/views/view/test_display/edit/block_1';
|
||||
$link_display_path = 'admin/structure/views/nojs/display/test_display/block_1/link_display';
|
||||
|
||||
// Test the link text displays 'None' and not 'Block 1'
|
||||
$this->drupalGet($path);
|
||||
$result = $this->xpath("//a[contains(@href, :path)]", array(':path' => $link_display_path));
|
||||
$this->assertEqual($result[0], t('None'), 'Make sure that the link option summary shows "None" by default.');
|
||||
|
||||
$this->drupalGet($link_display_path);
|
||||
$this->assertFieldChecked('edit-link-display-0');
|
||||
|
||||
// Test the default radio option on the link display form.
|
||||
$this->drupalPostForm($link_display_path, array('link_display' => 'page_1'), t('Apply'));
|
||||
// The form redirects to the master display.
|
||||
$this->drupalGet($path);
|
||||
|
||||
$result = $this->xpath("//a[contains(@href, :path)]", array(':path' => $link_display_path));
|
||||
$this->assertEqual($result[0], 'Page', 'Make sure that the link option summary shows the right linked display.');
|
||||
|
||||
$this->drupalPostForm($link_display_path, array('link_display' => 'custom_url', 'link_url' => 'a-custom-url'), t('Apply'));
|
||||
// The form redirects to the master display.
|
||||
$this->drupalGet($path);
|
||||
|
||||
$this->assertLink(t('Custom URL'), 0, 'The link option has custom url as summary.');
|
||||
|
||||
// Test the default link_url value for new display
|
||||
$this->drupalPostForm(NULL, array(), t('Add Block'));
|
||||
$this->assertUrl('admin/structure/views/view/test_display/edit/block_2');
|
||||
$this->clickLink(t('Custom URL'));
|
||||
$this->assertFieldByName('link_url', 'a-custom-url');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests contextual links on Views page displays.
|
||||
*/
|
||||
public function testPageContextualLinks() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('administer views', 'access contextual links')));
|
||||
$view = entity_load('view', 'test_display');
|
||||
$view->enable()->save();
|
||||
$this->container->get('router.builder')->rebuildIfNeeded();
|
||||
|
||||
$this->drupalGet('test-display');
|
||||
$id = 'entity.view.edit_form:view=test_display:location=page&name=test_display&display_id=page_1&langcode=en';
|
||||
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:assertContextualLinkPlaceHolder()
|
||||
$this->assertRaw('<div' . new Attribute(array('data-contextual-id' => $id)) . '></div>', format_string('Contextual link placeholder with id @id exists.', array('@id' => $id)));
|
||||
|
||||
// Get server-rendered contextual links.
|
||||
// @see \Drupal\contextual\Tests\ContextualDynamicContextTest:renderContextualLinks()
|
||||
$post = array('ids[0]' => $id);
|
||||
$response = $this->drupalPostWithFormat('contextual/render', 'json', $post, array('query' => array('destination' => 'test-display')));
|
||||
$this->assertResponse(200);
|
||||
$json = Json::decode($response);
|
||||
$this->assertIdentical($json[$id], '<ul class="contextual-links"><li class="entityviewedit-form"><a href="' . base_path() . 'admin/structure/views/view/test_display/edit/page_1">Edit view</a></li></ul>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the view status is correctly reflected on the edit form.
|
||||
*/
|
||||
public function testViewStatus() {
|
||||
$view = $this->randomView();
|
||||
$id = $view['id'];
|
||||
|
||||
// The view should initially have the enabled class on it's form wrapper.
|
||||
$this->drupalGet('admin/structure/views/view/' . $id);
|
||||
$elements = $this->xpath('//div[contains(@class, :edit) and contains(@class, :status)]', array(':edit' => 'views-edit-view', ':status' => 'enabled'));
|
||||
$this->assertTrue($elements, 'The enabled class was found on the form wrapper');
|
||||
|
||||
$view = Views::getView($id);
|
||||
$view->storage->disable()->save();
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/' . $id);
|
||||
$elements = $this->xpath('//div[contains(@class, :edit) and contains(@class, :status)]', array(':edit' => 'views-edit-view', ':status' => 'disabled'));
|
||||
$this->assertTrue($elements, 'The disabled class was found on the form wrapper.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the action links on the edit display UI.
|
||||
*/
|
||||
public function testActionLinks() {
|
||||
// Change the display title of a display so it contains characters that will
|
||||
// be escaped when rendered.
|
||||
$display_title = "'<test>'";
|
||||
$this->drupalGet('admin/structure/views/view/test_display');
|
||||
$display_title_path = 'admin/structure/views/nojs/display/test_display/block_1/display_title';
|
||||
$this->drupalPostForm($display_title_path, array('display_title' => $display_title), t('Apply'));
|
||||
|
||||
$placeholder = array('!display_title' => $display_title);
|
||||
// Ensure that the dropdown buttons are displayed correctly.
|
||||
$this->assertFieldByXpath('//input[@type="submit"]', t('Duplicate !display_title', $placeholder));
|
||||
$this->assertFieldByXpath('//input[@type="submit"]', t('Delete !display_title', $placeholder));
|
||||
$this->assertFieldByXpath('//input[@type="submit"]', t('Disable !display_title', $placeholder));
|
||||
$this->assertNoFieldByXpath('//input[@type="submit"]', t('Enable !display_title', $placeholder));
|
||||
|
||||
// Disable the display so we can test the rendering of the "Enable" button.
|
||||
$this->drupalPostForm(NULL, NULL, t('Disable !display_title', $placeholder));
|
||||
$this->assertFieldByXpath('//input[@type="submit"]', t('Enable !display_title', $placeholder));
|
||||
$this->assertNoFieldByXpath('//input[@type="submit"]', t('Disable !display_title', $placeholder));
|
||||
}
|
||||
}
|
41
core/modules/views_ui/src/Tests/DuplicateTest.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\DuplicateTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests the UI for view duplicate tool.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class DuplicateTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Checks if duplicated view exists and has correct label.
|
||||
*/
|
||||
public function testDuplicateView() {
|
||||
|
||||
// Create random view.
|
||||
$random_view = $this->randomView();
|
||||
|
||||
// Initialize array for duplicated view.
|
||||
$view = array();
|
||||
|
||||
// Generate random label and id for new view.
|
||||
$view['label'] = $this->randomMachineName(255);
|
||||
$view['id'] = strtolower($this->randomMachineName(128));
|
||||
|
||||
// Duplicate view.
|
||||
$this->drupalPostForm('admin/structure/views/view/' . $random_view['id'] . '/duplicate', $view, t('Duplicate'));
|
||||
|
||||
// Assert that the page url is correct.
|
||||
$this->assertUrl('admin/structure/views/view/' . $view['id'], array(), 'Make sure the view saving was successful and the browser got redirected to the edit page.');
|
||||
|
||||
// Assert that the page title is correctly displayed.
|
||||
$this->assertText($view['label']);
|
||||
}
|
||||
}
|
155
core/modules/views_ui/src/Tests/ExposedFormUITest.php
Normal file
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\ExposedFormUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests exposed forms UI functionality.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class ExposedFormUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_exposed_admin_ui');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalCreateContentType(array('type' => 'article'));
|
||||
$this->drupalCreateContentType(array('type' => 'page'));
|
||||
|
||||
// Create some random nodes.
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$this->drupalCreateNode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the admin interface of exposed filter and sort items.
|
||||
*/
|
||||
function testExposedAdminUi() {
|
||||
$edit = array();
|
||||
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/filter/type');
|
||||
// Be sure that the button is called exposed.
|
||||
$this->helperButtonHasLabel('edit-options-expose-button-button', t('Expose filter'));
|
||||
|
||||
// The first time the filter UI is displayed, the operator and the
|
||||
// value forms should be shown.
|
||||
$this->assertFieldById('edit-options-operator-in', '', 'Operator In exists');
|
||||
$this->assertFieldById('edit-options-operator-not-in', '', 'Operator Not In exists');
|
||||
$this->assertFieldById('edit-options-value-page', '', 'Checkbox for Page exists');
|
||||
$this->assertFieldById('edit-options-value-article', '', 'Checkbox for Article exists');
|
||||
|
||||
// Click the Expose filter button.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/filter/type', $edit, t('Expose filter'));
|
||||
// Check the label of the expose button.
|
||||
$this->helperButtonHasLabel('edit-options-expose-button-button', t('Hide filter'));
|
||||
// Check the label of the grouped exposed button
|
||||
$this->helperButtonHasLabel('edit-options-group-button-button', t('Grouped filters'));
|
||||
|
||||
// After exposing the filter, Operator and Value should be still here.
|
||||
$this->assertFieldById('edit-options-operator-in', '', 'Operator In exists');
|
||||
$this->assertFieldById('edit-options-operator-not-in', '', 'Operator Not In exists');
|
||||
$this->assertFieldById('edit-options-value-page', '', 'Checkbox for Page exists');
|
||||
$this->assertFieldById('edit-options-value-article', '', 'Checkbox for Article exists');
|
||||
|
||||
// Check the validations of the filter handler.
|
||||
$edit = array();
|
||||
$edit['options[expose][identifier]'] = '';
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->assertText(t('The identifier is required if the filter is exposed.'));
|
||||
|
||||
$edit = array();
|
||||
$edit['options[expose][identifier]'] = 'value';
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->assertText(t('This identifier is not allowed.'));
|
||||
|
||||
// Now check the sort criteria.
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/sort/created');
|
||||
$this->helperButtonHasLabel('edit-options-expose-button-button', t('Expose sort'));
|
||||
$this->assertNoFieldById('edit-options-expose-label', '', 'Make sure no label field is shown');
|
||||
|
||||
// Click the Grouped Filters button.
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/filter/type');
|
||||
$this->drupalPostForm(NULL, array(), t('Grouped filters'));
|
||||
|
||||
// After click on 'Grouped Filters', the standard operator and value should
|
||||
// not be displayed.
|
||||
$this->assertNoFieldById('edit-options-operator-in', '', 'Operator In not exists');
|
||||
$this->assertNoFieldById('edit-options-operator-not-in', '', 'Operator Not In not exists');
|
||||
$this->assertNoFieldById('edit-options-value-page', '', 'Checkbox for Page not exists');
|
||||
$this->assertNoFieldById('edit-options-value-article', '', 'Checkbox for Article not exists');
|
||||
|
||||
// Check that after click on 'Grouped Filters', a new button is shown to
|
||||
// add more items to the list.
|
||||
$this->helperButtonHasLabel('edit-options-group-info-add-group', t('Add another item'));
|
||||
|
||||
// Create a grouped filter
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/filter/type');
|
||||
$edit = array();
|
||||
$edit["options[group_info][group_items][1][title]"] = 'Is Article';
|
||||
$edit["options[group_info][group_items][1][value][article]"] = 'article';
|
||||
|
||||
$edit["options[group_info][group_items][2][title]"] = 'Is Page';
|
||||
$edit["options[group_info][group_items][2][value][page]"] = TRUE;
|
||||
|
||||
$edit["options[group_info][group_items][3][title]"] = 'Is Page and Article';
|
||||
$edit["options[group_info][group_items][3][value][article]"] = TRUE;
|
||||
$edit["options[group_info][group_items][3][value][page]"] = TRUE;
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
|
||||
// Select the empty operator, so the empty value should not trigger a form
|
||||
// error.
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/filter/body_value');
|
||||
$edit = array();
|
||||
$edit["options[group_info][group_items][1][title]"] = $this->randomMachineName();
|
||||
$edit["options[group_info][group_items][1][operator]"] = 'empty';
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->assertUrl('admin/structure/views/view/test_exposed_admin_ui/edit/default', array(), 'Validation did not run for the empty operator.');
|
||||
// Test the validation error message text is not shown.
|
||||
$this->assertNoText(t('The value is required if title for this item is defined.'));
|
||||
|
||||
// Validate that all the titles are defined for each group
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/filter/type');
|
||||
$edit = array();
|
||||
$edit["options[group_info][group_items][1][title]"] = 'Is Article';
|
||||
$edit["options[group_info][group_items][1][value][article]"] = TRUE;
|
||||
|
||||
// This should trigger an error
|
||||
$edit["options[group_info][group_items][2][title]"] = '';
|
||||
$edit["options[group_info][group_items][2][value][page]"] = TRUE;
|
||||
|
||||
$edit["options[group_info][group_items][3][title]"] = 'Is Page and Article';
|
||||
$edit["options[group_info][group_items][3][value][article]"] = TRUE;
|
||||
$edit["options[group_info][group_items][3][value][page]"] = TRUE;
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->assertRaw(t('The title is required if value for this item is defined.'), 'Group items should have a title');
|
||||
|
||||
// Un-expose the filter.
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/filter/type');
|
||||
$this->drupalPostForm(NULL, array(), t('Hide filter'));
|
||||
|
||||
// After Un-exposing the filter, Operator and Value should be shown again.
|
||||
$this->assertFieldById('edit-options-operator-in', '', 'Operator In exists after hide filter');
|
||||
$this->assertFieldById('edit-options-operator-not-in', '', 'Operator Not In exists after hide filter');
|
||||
$this->assertFieldById('edit-options-value-page', '', 'Checkbox for Page exists after hide filter');
|
||||
$this->assertFieldById('edit-options-value-article', '', 'Checkbox for Article exists after hide filter');
|
||||
|
||||
// Click the Expose sort button.
|
||||
$edit = array();
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/sort/created', $edit, t('Expose sort'));
|
||||
// Check the label of the expose button.
|
||||
$this->helperButtonHasLabel('edit-options-expose-button-button', t('Hide sort'));
|
||||
$this->assertFieldById('edit-options-expose-label', '', 'Make sure a label field is shown');
|
||||
}
|
||||
}
|
84
core/modules/views_ui/src/Tests/FieldUITest.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\FieldUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the UI of field handlers.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\field\FieldPluginBase
|
||||
*/
|
||||
class FieldUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Tests the UI of field handlers.
|
||||
*/
|
||||
public function testFieldUI() {
|
||||
// Ensure the field is not marked as hidden on the first run.
|
||||
$this->drupalGet('admin/structure/views/view/test_view/edit');
|
||||
$this->assertText('Views test: Name');
|
||||
$this->assertNoText('Views test: Name [' . t('hidden') . ']');
|
||||
|
||||
// Hides the field and check whether the hidden label is appended.
|
||||
$edit_handler_url = 'admin/structure/views/nojs/handler/test_view/default/field/name';
|
||||
$this->drupalPostForm($edit_handler_url, array('options[exclude]' => TRUE), t('Apply'));
|
||||
|
||||
$this->assertText('Views test: Name [' . t('hidden') . ']');
|
||||
|
||||
// Ensure that the expected tokens appear in the UI.
|
||||
$edit_handler_url = 'admin/structure/views/nojs/handler/test_view/default/field/age';
|
||||
$this->drupalGet($edit_handler_url);
|
||||
$result = $this->xpath('//details[@id="edit-options-alter-help"]/div[@class="details-wrapper"]/div[@class="item-list"]/fields/li');
|
||||
$this->assertEqual((string) $result[0], '{{ age }} == Age');
|
||||
|
||||
$edit_handler_url = 'admin/structure/views/nojs/handler/test_view/default/field/id';
|
||||
$this->drupalGet($edit_handler_url);
|
||||
$result = $this->xpath('//details[@id="edit-options-alter-help"]/div[@class="details-wrapper"]/div[@class="item-list"]/fields/li');
|
||||
$this->assertEqual((string) $result[0], '{{ age }} == Age');
|
||||
$this->assertEqual((string) $result[1], '{{ id }} == ID');
|
||||
|
||||
$edit_handler_url = 'admin/structure/views/nojs/handler/test_view/default/field/name';
|
||||
$this->drupalGet($edit_handler_url);
|
||||
$result = $this->xpath('//details[@id="edit-options-alter-help"]/div[@class="details-wrapper"]/div[@class="item-list"]/fields/li');
|
||||
$this->assertEqual((string) $result[0], '{{ age }} == Age');
|
||||
$this->assertEqual((string) $result[1], '{{ id }} == ID');
|
||||
$this->assertEqual((string) $result[2], '{{ name }} == Name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the field labels.
|
||||
*/
|
||||
public function testFieldLabel() {
|
||||
// Create a view with unformatted style and make sure the fields have no
|
||||
// labels by default.
|
||||
$view = array();
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['description'] = $this->randomMachineName(16);
|
||||
$view['show[wizard_key]'] = 'node';
|
||||
$view['page[create]'] = TRUE;
|
||||
$view['page[style][style_plugin]'] = 'default';
|
||||
$view['page[title]'] = $this->randomMachineName(16);
|
||||
$view['page[path]'] = $view['id'];
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
$view = Views::getView($view['id']);
|
||||
$view->initHandlers();
|
||||
$this->assertEqual($view->field['title']->options['label'], '', 'The field label for normal styles are empty.');
|
||||
}
|
||||
|
||||
}
|
69
core/modules/views_ui/src/Tests/FilterBooleanWebTest.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\FilterBooleanWebTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests the boolean filter UI.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\filter\BooleanOperator
|
||||
*/
|
||||
class FilterBooleanWebTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Tests the filter boolean UI.
|
||||
*/
|
||||
public function testFilterBooleanUI() {
|
||||
$this->drupalPostForm('admin/structure/views/nojs/add-handler/test_view/default/filter', array('name[views_test_data.status]' => TRUE), t('Add and configure @handler', array('@handler' => t('filter criteria'))));
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Expose filter'));
|
||||
$this->drupalPostForm(NULL, array(), t('Grouped filters'));
|
||||
|
||||
$edit = array();
|
||||
$edit['options[group_info][group_items][1][title]'] = 'Published';
|
||||
$edit['options[group_info][group_items][1][operator]'] = '=';
|
||||
$edit['options[group_info][group_items][1][value]'] = 1;
|
||||
$edit['options[group_info][group_items][2][title]'] = 'Not published';
|
||||
$edit['options[group_info][group_items][2][operator]'] = '=';
|
||||
$edit['options[group_info][group_items][2][value]'] = 0;
|
||||
$edit['options[group_info][group_items][3][title]'] = 'Not published2';
|
||||
$edit['options[group_info][group_items][3][operator]'] = '!=';
|
||||
$edit['options[group_info][group_items][3][value]'] = 1;
|
||||
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_view/default/filter/status');
|
||||
|
||||
$result = $this->xpath('//input[@name="options[group_info][group_items][1][value]"]');
|
||||
$this->assertEqual((int) $result[1]->attributes()->checked, 'checked');
|
||||
$result = $this->xpath('//input[@name="options[group_info][group_items][2][value]"]');
|
||||
$this->assertEqual((int) $result[2]->attributes()->checked, 'checked');
|
||||
$result = $this->xpath('//input[@name="options[group_info][group_items][3][value]"]');
|
||||
$this->assertEqual((int) $result[1]->attributes()->checked, 'checked');
|
||||
|
||||
// Test that there is a remove link for each group.
|
||||
$this->assertEqual(count($this->cssSelect('a.views-remove-link')), 3);
|
||||
|
||||
// Test selecting a default and removing an item.
|
||||
$edit = array();
|
||||
$edit['options[group_info][default_group]'] = 2;
|
||||
$edit['options[group_info][group_items][3][remove]'] = 1;
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_view/default/filter/status');
|
||||
$this->assertFieldByName('options[group_info][default_group]', 2, 'Second item was set as the default.');
|
||||
$this->assertNoField('options[group_info][group_items][3][remove]', 'Third item was removed.');
|
||||
}
|
||||
|
||||
}
|
97
core/modules/views_ui/src/Tests/FilterNumericWebTest.php
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\FilterNumericWebTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* Tests the numeric filter UI.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\filter\NumericFilter
|
||||
*/
|
||||
class FilterNumericWebTest extends UITestBase {
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Tests the filter numeric UI.
|
||||
*/
|
||||
public function testFilterNumericUI() {
|
||||
$this->drupalPostForm('admin/structure/views/nojs/add-handler/test_view/default/filter', array('name[views_test_data.age]' => TRUE), t('Add and configure @handler', array('@handler' => t('filter criteria'))));
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Expose filter'));
|
||||
$this->drupalPostForm(NULL, array(), t('Grouped filters'));
|
||||
|
||||
$edit = array();
|
||||
$edit['options[group_info][group_items][1][title]'] = 'Old';
|
||||
$edit['options[group_info][group_items][1][operator]'] = '>';
|
||||
$edit['options[group_info][group_items][1][value][value]'] = 27;
|
||||
$edit['options[group_info][group_items][2][title]'] = 'Young';
|
||||
$edit['options[group_info][group_items][2][operator]'] = '<=';
|
||||
$edit['options[group_info][group_items][2][value][value]'] = 27;
|
||||
$edit['options[group_info][group_items][3][title]'] = 'From 26 to 28';
|
||||
$edit['options[group_info][group_items][3][operator]'] = 'between';
|
||||
$edit['options[group_info][group_items][3][value][min]'] = 26;
|
||||
$edit['options[group_info][group_items][3][value][max]'] = 28;
|
||||
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/test_view/default/filter/age');
|
||||
foreach ($edit as $name => $value) {
|
||||
$this->assertFieldByName($name, $value);
|
||||
}
|
||||
|
||||
$this->drupalPostForm('admin/structure/views/view/test_view', array(), t('Save'));
|
||||
$this->assertConfigSchemaByName('views.view.test_view');
|
||||
|
||||
// Test that the exposed filter works as expected.
|
||||
$this->drupalPostForm(NULL, array(), t('Update preview'));
|
||||
$this->assertText('John');
|
||||
$this->assertText('Paul');
|
||||
$this->assertText('Ringo');
|
||||
$this->assertText('George');
|
||||
$this->assertText('Meredith');
|
||||
$this->drupalPostForm(NULL, array('age' => '2'), t('Update preview'));
|
||||
$this->assertText('John');
|
||||
$this->assertText('Paul');
|
||||
$this->assertNoText('Ringo');
|
||||
$this->assertText('George');
|
||||
$this->assertNoText('Meredith');
|
||||
|
||||
// Change the filter to a single filter to test the schema when the operator
|
||||
// is not exposed.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_view/default/filter/age', array(), t('Single filter'));
|
||||
$edit = array();
|
||||
$edit['options[value][value]'] = 25;
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm('admin/structure/views/view/test_view', array(), t('Save'));
|
||||
$this->assertConfigSchemaByName('views.view.test_view');
|
||||
|
||||
// Test that the filter works as expected.
|
||||
$this->drupalPostForm(NULL, array(), t('Update preview'));
|
||||
$this->assertText('John');
|
||||
$this->assertNoText('Paul');
|
||||
$this->assertNoText('Ringo');
|
||||
$this->assertNoText('George');
|
||||
$this->assertNoText('Meredith');
|
||||
$this->drupalPostForm(NULL, array('age' => '26'), t('Update preview'));
|
||||
$this->assertNoText('John');
|
||||
$this->assertText('Paul');
|
||||
$this->assertNoText('Ringo');
|
||||
$this->assertNoText('George');
|
||||
$this->assertNoText('Meredith');
|
||||
}
|
||||
|
||||
}
|
98
core/modules/views_ui/src/Tests/FilterUITest.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\FilterUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
|
||||
/**
|
||||
* Tests for the filters from the UI.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class FilterUITest extends ViewTestBase {
|
||||
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_filter_in_operator_ui', 'test_filter_groups');
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views_ui', 'node');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalCreateContentType(array('type' => 'page'));
|
||||
$this->enableViewsTestModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an option for a filter is saved as expected from the UI.
|
||||
*/
|
||||
public function testFilterInOperatorUi() {
|
||||
$admin_user = $this->drupalCreateUser(array('administer views', 'administer site configuration'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$path = 'admin/structure/views/nojs/handler/test_filter_in_operator_ui/default/filter/type';
|
||||
$this->drupalGet($path);
|
||||
// Verifies that "Limit list to selected items" option is not selected.
|
||||
$this->assertFieldByName('options[expose][reduce]', FALSE);
|
||||
|
||||
// Select "Limit list to selected items" option and apply.
|
||||
$edit = array(
|
||||
'options[expose][reduce]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm($path, $edit, t('Apply'));
|
||||
|
||||
// Verifies that the option was saved as expected.
|
||||
$this->drupalGet($path);
|
||||
$this->assertFieldByName('options[expose][reduce]', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the filters from the UI.
|
||||
*/
|
||||
public function testFiltersUI() {
|
||||
$admin_user = $this->drupalCreateUser(array('administer views', 'administer site configuration'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_filter_groups');
|
||||
|
||||
$this->assertLink('Content: Node ID (= 1)', 0, 'Content: Node ID (= 1) link appears correctly.');
|
||||
|
||||
// Tests that we can create a new filter group from UI.
|
||||
$this->drupalGet('admin/structure/views/nojs/rearrange-filter/test_filter_groups/page');
|
||||
$this->assertNoRaw('<span>Group 3</span>', 'Group 3 has not been added yet.');
|
||||
|
||||
// Create 2 new groups.
|
||||
$this->drupalPostForm(NULL, [], t('Create new filter group'));
|
||||
$this->drupalPostForm(NULL, [], t('Create new filter group'));
|
||||
|
||||
// Remove the new group 3.
|
||||
$this->drupalPostForm(NULL, [], t('Remove group 3'));
|
||||
|
||||
// Verify that the group 4 is now named as 3.
|
||||
$this->assertRaw('<span>Group 3</span>', 'Group 3 still exists.');
|
||||
|
||||
// Remove the group 3 again.
|
||||
$this->drupalPostForm(NULL, [], t('Remove group 3'));
|
||||
|
||||
// Group 3 now does not exist.
|
||||
$this->assertNoRaw('<span>Group 3</span>', 'Group 3 has not been added yet.');
|
||||
}
|
||||
|
||||
}
|
55
core/modules/views_ui/src/Tests/GroupByTest.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\GroupByTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests UI of aggregate functionality..
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class GroupByTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_views_groupby_save');
|
||||
|
||||
/**
|
||||
* Tests whether basic saving works.
|
||||
*
|
||||
* @todo This should check the change of the settings as well.
|
||||
*/
|
||||
function testGroupBySave() {
|
||||
$this->drupalGet('admin/structure/views/view/test_views_groupby_save/edit');
|
||||
|
||||
$edit_groupby_url = 'admin/structure/views/nojs/handler-group/test_views_groupby_save/default/field/id';
|
||||
$this->assertNoLinkByHref($edit_groupby_url, 0, 'No aggregation link found.');
|
||||
|
||||
// Enable aggregation on the view.
|
||||
$edit = array(
|
||||
'group_by' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_views_groupby_save/default/group_by', $edit, t('Apply'));
|
||||
|
||||
$this->assertLinkByHref($edit_groupby_url, 0, 'Aggregation link found.');
|
||||
|
||||
// Change the groupby type in the UI.
|
||||
$this->drupalPostForm($edit_groupby_url, array('options[group_type]' => 'count'), t('Apply'));
|
||||
$this->assertLink('COUNT(Views test: ID)', 0, 'The count setting is displayed in the UI');
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
$view = $this->container->get('entity.manager')->getStorage('view')->load('test_views_groupby_save');
|
||||
$display = $view->getDisplay('default');
|
||||
$this->assertTrue($display['display_options']['group_by'], 'The groupby setting was saved on the view.');
|
||||
$this->assertEqual($display['display_options']['fields']['id']['group_type'], 'count', 'Count groupby_type was saved on the view.');
|
||||
}
|
||||
|
||||
}
|
175
core/modules/views_ui/src/Tests/HandlerTest.php
Normal file
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\HandlerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
/**
|
||||
* Tests handler UI for views.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\HandlerBase
|
||||
*/
|
||||
class HandlerTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view_empty', 'test_view_broken');
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Tests\ViewTestBase::schemaDefinition().
|
||||
*
|
||||
* Adds a uid column to test the relationships.
|
||||
*/
|
||||
protected function schemaDefinition() {
|
||||
$schema = parent::schemaDefinition();
|
||||
|
||||
$schema['views_test_data']['fields']['uid'] = array(
|
||||
'description' => "The {users}.uid of the author of the beatle entry.",
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Tests\ViewTestBase::viewsData().
|
||||
*
|
||||
* Adds a relationship for the uid column.
|
||||
*/
|
||||
protected function viewsData() {
|
||||
$data = parent::viewsData();
|
||||
$data['views_test_data']['uid'] = array(
|
||||
'title' => t('UID'),
|
||||
'help' => t('The test data UID'),
|
||||
'relationship' => array(
|
||||
'id' => 'standard',
|
||||
'base' => 'users_field_data',
|
||||
'base field' => 'uid'
|
||||
)
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests UI CRUD.
|
||||
*/
|
||||
public function testUICRUD() {
|
||||
$handler_types = ViewExecutable::getHandlerTypes();
|
||||
foreach ($handler_types as $type => $type_info) {
|
||||
// Test adding handlers.
|
||||
$add_handler_url = "admin/structure/views/nojs/add-handler/test_view_empty/default/$type";
|
||||
|
||||
// Area handler types need to use a different handler.
|
||||
if (in_array($type, array('header', 'footer', 'empty'))) {
|
||||
$this->drupalPostForm($add_handler_url, array('name[views.area]' => TRUE), t('Add and configure @handler', array('@handler' => $type_info['ltitle'])));
|
||||
$id = 'area';
|
||||
$edit_handler_url = "admin/structure/views/nojs/handler/test_view_empty/default/$type/$id";
|
||||
}
|
||||
elseif ($type == 'relationship') {
|
||||
$this->drupalPostForm($add_handler_url, array('name[views_test_data.uid]' => TRUE), t('Add and configure @handler', array('@handler' => $type_info['ltitle'])));
|
||||
$id = 'uid';
|
||||
$edit_handler_url = "admin/structure/views/nojs/handler/test_view_empty/default/$type/$id";
|
||||
}
|
||||
else {
|
||||
$this->drupalPostForm($add_handler_url, array('name[views_test_data.job]' => TRUE), t('Add and configure @handler', array('@handler' => $type_info['ltitle'])));
|
||||
$id = 'job';
|
||||
$edit_handler_url = "admin/structure/views/nojs/handler/test_view_empty/default/$type/$id";
|
||||
}
|
||||
|
||||
$this->assertUrl($edit_handler_url, array(), 'The user got redirected to the handler edit form.');
|
||||
$random_label = $this->randomMachineName();
|
||||
$this->drupalPostForm(NULL, array('options[admin_label]' => $random_label), t('Apply'));
|
||||
|
||||
$this->assertUrl('admin/structure/views/view/test_view_empty/edit/default', array(), 'The user got redirected to the views edit form.');
|
||||
|
||||
$this->assertLinkByHref($edit_handler_url, 0, 'The handler edit link appears in the UI.');
|
||||
$links = $this->xpath('//a[starts-with(normalize-space(text()), :label)]', array(':label' => $random_label));
|
||||
$this->assertTrue(isset($links[0]), 'The handler edit link has the right label');
|
||||
|
||||
// Save the view and have a look whether the handler was added as expected.
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$view = $this->container->get('entity.manager')->getStorage('view')->load('test_view_empty');
|
||||
$display = $view->getDisplay('default');
|
||||
$this->assertTrue(isset($display['display_options'][$type_info['plural']][$id]), 'Ensure the field was added to the view itself.');
|
||||
|
||||
// Remove the item and check that it's removed
|
||||
$this->drupalPostForm($edit_handler_url, array(), t('Remove'));
|
||||
$this->assertNoLinkByHref($edit_handler_url, 0, 'The handler edit link does not appears in the UI after removing.');
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$view = $this->container->get('entity.manager')->getStorage('view')->load('test_view_empty');
|
||||
$display = $view->getDisplay('default');
|
||||
$this->assertFalse(isset($display['display_options'][$type_info['plural']][$id]), 'Ensure the field was removed from the view itself.');
|
||||
}
|
||||
|
||||
// Test adding a field of the user table using the uid relationship.
|
||||
$type_info = $handler_types['relationship'];
|
||||
$add_handler_url = "admin/structure/views/nojs/add-handler/test_view_empty/default/relationship";
|
||||
$this->drupalPostForm($add_handler_url, array('name[views_test_data.uid]' => TRUE), t('Add and configure @handler', array('@handler' => $type_info['ltitle'])));
|
||||
|
||||
$add_handler_url = "admin/structure/views/nojs/add-handler/test_view_empty/default/field";
|
||||
$type_info = $handler_types['field'];
|
||||
$this->drupalPostForm($add_handler_url, array('name[users_field_data.name]' => TRUE), t('Add and configure @handler', array('@handler' => $type_info['ltitle'])));
|
||||
$id = 'name';
|
||||
$edit_handler_url = "admin/structure/views/nojs/handler/test_view_empty/default/field/$id";
|
||||
|
||||
$this->assertUrl($edit_handler_url, array(), 'The user got redirected to the handler edit form.');
|
||||
$this->assertFieldByName('options[relationship]', 'uid', 'Ensure the relationship select is filled with the UID relationship.');
|
||||
$this->drupalPostForm(NULL, array(), t('Apply'));
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$view = $this->container->get('entity.manager')->getStorage('view')->load('test_view_empty');
|
||||
$display = $view->getDisplay('default');
|
||||
$this->assertTrue(isset($display['display_options'][$type_info['plural']][$id]), 'Ensure the field was added to the view itself.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests broken handlers.
|
||||
*/
|
||||
public function testBrokenHandlers() {
|
||||
$handler_types = ViewExecutable::getHandlerTypes();
|
||||
foreach ($handler_types as $type => $type_info) {
|
||||
$this->drupalGet('admin/structure/views/view/test_view_broken/edit');
|
||||
|
||||
$href = "admin/structure/views/nojs/handler/test_view_broken/default/$type/id_broken";
|
||||
|
||||
$result = $this->xpath('//a[contains(@href, :href)]', array(':href' => $href));
|
||||
$this->assertEqual(count($result), 1, SafeMarkup::format('Handler (%type) edit link found.', array('%type' => $type)));
|
||||
|
||||
$text = t('Broken/missing handler');
|
||||
|
||||
$this->assertIdentical((string) $result[0], $text, 'Ensure the broken handler text was found.');
|
||||
|
||||
$this->drupalGet($href);
|
||||
$result = $this->xpath('//h1');
|
||||
$this->assertTrue(strpos((string) $result[0], $text) !== FALSE, 'Ensure the broken handler text was found.');
|
||||
|
||||
$original_configuration = [
|
||||
'field' => 'id_broken',
|
||||
'id' => 'id_broken',
|
||||
'relationship' => 'none',
|
||||
'table' => 'views_test_data',
|
||||
'plugin_id' => 'numeric',
|
||||
];
|
||||
|
||||
foreach ($original_configuration as $key => $value) {
|
||||
$this->assertText(SafeMarkup::format('@key: @value', array('@key' => $key, '@value' => $value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
56
core/modules/views_ui/src/Tests/NewViewConfigSchemaTest.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\NewViewConfigSchemaTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests configuration schema against new views.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class NewViewConfigSchemaTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views_ui', 'node', 'comment', 'file', 'taxonomy', 'dblog', 'aggregator');
|
||||
|
||||
/**
|
||||
* Tests creating brand new views.
|
||||
*/
|
||||
public function testNewViews() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('administer views')));
|
||||
|
||||
// Create views with all core Views wizards.
|
||||
$wizards = array(
|
||||
// Wizard with their own classes.
|
||||
'node',
|
||||
'node_revision',
|
||||
'users',
|
||||
'comment',
|
||||
'file_managed',
|
||||
'taxonomy_term',
|
||||
'watchdog',
|
||||
// Standard derivative classes.
|
||||
'standard:aggregator_feed',
|
||||
'standard:aggregator_item',
|
||||
);
|
||||
foreach($wizards as $wizard_key) {
|
||||
$edit = array();
|
||||
$edit['label'] = $this->randomString();
|
||||
$edit['id'] = strtolower($this->randomMachineName());
|
||||
$edit['show[wizard_key]'] = $wizard_key;
|
||||
$edit['description'] = $this->randomString();
|
||||
$this->drupalPostForm('admin/structure/views/add', $edit, t('Save and edit'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
196
core/modules/views_ui/src/Tests/OverrideDisplaysTest.php
Normal file
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\OverrideDisplaysTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests that displays can be correctly overridden via the user interface.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class OverrideDisplaysTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Tests that displays can be overridden via the UI.
|
||||
*/
|
||||
function testOverrideDisplays() {
|
||||
// Create a basic view that shows all content, with a page and a block
|
||||
// display.
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['page[create]'] = 1;
|
||||
$view['page[path]'] = $this->randomMachineName(16);
|
||||
$view['block[create]'] = 1;
|
||||
$view_path = $view['page[path]'];
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
// Configure its title. Since the page and block both started off with the
|
||||
// same (empty) title in the views wizard, we expect the wizard to have set
|
||||
// things up so that they both inherit from the default display, and we
|
||||
// therefore only need to change that to have it take effect for both.
|
||||
$edit = array();
|
||||
$edit['title'] = $original_title = $this->randomMachineName(16);
|
||||
$edit['override[dropdown]'] = 'default';
|
||||
$this->drupalPostForm("admin/structure/views/nojs/display/{$view['id']}/page_1/title", $edit, t('Apply'));
|
||||
$this->drupalPostForm("admin/structure/views/view/{$view['id']}/edit/page_1", array(), t('Save'));
|
||||
|
||||
// Add a node that will appear in the view, so that the block will actually
|
||||
// be displayed.
|
||||
$this->drupalCreateContentType(array('type' => 'page'));
|
||||
$this->drupalCreateNode();
|
||||
|
||||
// Make sure the title appears in the page.
|
||||
$this->drupalGet($view_path);
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($original_title);
|
||||
|
||||
// Confirm that the view block is available in the block administration UI.
|
||||
$this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default'));
|
||||
$this->assertText($view['label']);
|
||||
|
||||
// Place the block.
|
||||
$this->drupalPlaceBlock("views_block:{$view['id']}-block_1");
|
||||
|
||||
// Make sure the title appears in the block.
|
||||
$this->drupalGet('');
|
||||
$this->assertText($original_title);
|
||||
|
||||
// Change the title for the page display only, and make sure that the
|
||||
// original title still appears on the page.
|
||||
$edit = array();
|
||||
$edit['title'] = $new_title = $this->randomMachineName(16);
|
||||
$edit['override[dropdown]'] = 'page_1';
|
||||
$this->drupalPostForm("admin/structure/views/nojs/display/{$view['id']}/page_1/title", $edit, t('Apply'));
|
||||
$this->drupalPostForm("admin/structure/views/view/{$view['id']}/edit/page_1", array(), t('Save'));
|
||||
$this->drupalGet($view_path);
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($new_title);
|
||||
$this->assertText($original_title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the wizard correctly sets up default and overridden displays.
|
||||
*/
|
||||
function testWizardMixedDefaultOverriddenDisplays() {
|
||||
// Create a basic view with a page, block, and feed. Give the page and feed
|
||||
// identical titles, but give the block a different one, so we expect the
|
||||
// page and feed to inherit their titles from the default display, but the
|
||||
// block to override it.
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['page[create]'] = 1;
|
||||
$view['page[title]'] = $this->randomMachineName(16);
|
||||
$view['page[path]'] = $this->randomMachineName(16);
|
||||
$view['page[feed]'] = 1;
|
||||
$view['page[feed_properties][path]'] = $this->randomMachineName(16);
|
||||
$view['block[create]'] = 1;
|
||||
$view['block[title]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
// Add a node that will appear in the view, so that the block will actually
|
||||
// be displayed.
|
||||
$this->drupalCreateContentType(array('type' => 'page'));
|
||||
$this->drupalCreateNode();
|
||||
|
||||
// Make sure that the feed, page and block all start off with the correct
|
||||
// titles.
|
||||
$this->drupalGet($view['page[path]']);
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($view['page[title]']);
|
||||
$this->assertNoText($view['block[title]']);
|
||||
$this->drupalGet($view['page[feed_properties][path]']);
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($view['page[title]']);
|
||||
$this->assertNoText($view['block[title]']);
|
||||
|
||||
// Confirm that the block is available in the block administration UI.
|
||||
$this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default'));
|
||||
$this->assertText($view['label']);
|
||||
|
||||
// Put the block into the first sidebar region, and make sure it will not
|
||||
// display on the view's page display (since we will be searching for the
|
||||
// presence/absence of the view's title in both the page and the block).
|
||||
$this->drupalPlaceBlock("views_block:{$view['id']}-block_1", array(
|
||||
'visibility' => array(
|
||||
'request_path' => array(
|
||||
'pages' => '/' . $view['page[path]'],
|
||||
'negate' => TRUE,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
$this->drupalGet('');
|
||||
$this->assertText($view['block[title]']);
|
||||
$this->assertNoText($view['page[title]']);
|
||||
|
||||
// Edit the page and change the title. This should automatically change
|
||||
// the feed's title also, but not the block.
|
||||
$edit = array();
|
||||
$edit['title'] = $new_default_title = $this->randomMachineName(16);
|
||||
$this->drupalPostForm("admin/structure/views/nojs/display/{$view['id']}/page_1/title", $edit, t('Apply'));
|
||||
$this->drupalPostForm("admin/structure/views/view/{$view['id']}/edit/page_1", array(), t('Save'));
|
||||
$this->drupalGet($view['page[path]']);
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($new_default_title);
|
||||
$this->assertNoText($view['page[title]']);
|
||||
$this->assertNoText($view['block[title]']);
|
||||
$this->drupalGet($view['page[feed_properties][path]']);
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($new_default_title);
|
||||
$this->assertNoText($view['page[title]']);
|
||||
$this->assertNoText($view['block[title]']);
|
||||
$this->drupalGet('');
|
||||
$this->assertNoText($new_default_title);
|
||||
$this->assertNoText($view['page[title]']);
|
||||
$this->assertText($view['block[title]']);
|
||||
|
||||
// Edit the block and change the title. This should automatically change
|
||||
// the block title only, and leave the defaults alone.
|
||||
$edit = array();
|
||||
$edit['title'] = $new_block_title = $this->randomMachineName(16);
|
||||
$this->drupalPostForm("admin/structure/views/nojs/display/{$view['id']}/block_1/title", $edit, t('Apply'));
|
||||
$this->drupalPostForm("admin/structure/views/view/{$view['id']}/edit/block_1", array(), t('Save'));
|
||||
$this->drupalGet($view['page[path]']);
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($new_default_title);
|
||||
$this->drupalGet($view['page[feed_properties][path]']);
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($new_default_title);
|
||||
$this->assertNoText($new_block_title);
|
||||
$this->drupalGet('');
|
||||
$this->assertText($new_block_title);
|
||||
$this->assertNoText($view['block[title]']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the revert to all displays select-option works as expected.
|
||||
*/
|
||||
function testRevertAllDisplays() {
|
||||
// Create a basic view with a page, block.
|
||||
// Because there is both a title on page and block we expect the title on
|
||||
// the block be overridden.
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['page[create]'] = 1;
|
||||
$view['page[title]'] = $this->randomMachineName(16);
|
||||
$view['page[path]'] = $this->randomMachineName(16);
|
||||
$view['block[create]'] = 1;
|
||||
$view['block[title]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
// Revert the title of the block to the default ones, but submit some new
|
||||
// values to be sure that the new value is not stored.
|
||||
$edit = array();
|
||||
$edit['title'] = $new_block_title = $this->randomMachineName();
|
||||
$edit['override[dropdown]'] = 'default_revert';
|
||||
|
||||
$this->drupalPostForm("admin/structure/views/nojs/display/{$view['id']}/block_1/title", $edit, t('Apply'));
|
||||
$this->drupalPostForm("admin/structure/views/view/{$view['id']}/edit/block_1", array(), t('Save'));
|
||||
$this->assertText($view['page[title]']);
|
||||
}
|
||||
|
||||
}
|
312
core/modules/views_ui/src/Tests/PreviewTest.php
Normal file
|
@ -0,0 +1,312 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\PreviewTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
|
||||
/**
|
||||
* Tests the UI preview functionality.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class PreviewTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_preview', 'test_pager_full', 'test_mini_pager');
|
||||
|
||||
/**
|
||||
* Tests contextual links in the preview form.
|
||||
*/
|
||||
public function testPreviewContextual() {
|
||||
\Drupal::service('module_installer')->install(array('contextual'));
|
||||
$this->resetAll();
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_preview/edit');
|
||||
$this->assertResponse(200);
|
||||
$this->drupalPostForm(NULL, $edit = array(), t('Update preview'));
|
||||
|
||||
$elements = $this->xpath('//div[@id="views-live-preview"]//ul[contains(@class, :ul-class)]/li[contains(@class, :li-class)]', array(':ul-class' => 'contextual-links', ':li-class' => 'filter-add'));
|
||||
$this->assertEqual(count($elements), 1, 'The contextual link to add a new field is shown.');
|
||||
|
||||
$this->drupalPostForm(NULL, $edit = array('view_args' => '100'), t('Update preview'));
|
||||
|
||||
// Test that area text and exposed filters are present and rendered.
|
||||
$this->assertFieldByName('id', NULL, 'ID exposed filter field found.');
|
||||
$this->assertText('Test header text', 'Rendered header text found');
|
||||
$this->assertText('Test footer text', 'Rendered footer text found.');
|
||||
$this->assertText('Test empty text', 'Rendered empty text found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests arguments in the preview form.
|
||||
*/
|
||||
function testPreviewUI() {
|
||||
$this->drupalGet('admin/structure/views/view/test_preview/edit');
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalPostForm(NULL, $edit = array(), t('Update preview'));
|
||||
|
||||
$elements = $this->xpath('//div[@class = "view-content"]/div[contains(@class, views-row)]');
|
||||
$this->assertEqual(count($elements), 5);
|
||||
|
||||
// Filter just the first result.
|
||||
$this->drupalPostForm(NULL, $edit = array('view_args' => '1'), t('Update preview'));
|
||||
|
||||
$elements = $this->xpath('//div[@class = "view-content"]/div[contains(@class, views-row)]');
|
||||
$this->assertEqual(count($elements), 1);
|
||||
|
||||
// Filter for no results.
|
||||
$this->drupalPostForm(NULL, $edit = array('view_args' => '100'), t('Update preview'));
|
||||
|
||||
$elements = $this->xpath('//div[@class = "view-content"]/div[contains(@class, views-row)]');
|
||||
$this->assertEqual(count($elements), 0);
|
||||
|
||||
// Test that area text and exposed filters are present and rendered.
|
||||
$this->assertFieldByName('id', NULL, 'ID exposed filter field found.');
|
||||
$this->assertText('Test header text', 'Rendered header text found');
|
||||
$this->assertText('Test footer text', 'Rendered footer text found.');
|
||||
$this->assertText('Test empty text', 'Rendered empty text found.');
|
||||
|
||||
// Test feed preview.
|
||||
$view = array();
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['page[create]'] = 1;
|
||||
$view['page[title]'] = $this->randomMachineName(16);
|
||||
$view['page[path]'] = $this->randomMachineName(16);
|
||||
$view['page[feed]'] = 1;
|
||||
$view['page[feed_properties][path]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
$this->clickLink(t('Feed'));
|
||||
$this->drupalPostForm(NULL, array(), t('Update preview'));
|
||||
$result = $this->xpath('//div[@id="views-live-preview"]/pre');
|
||||
$this->assertTrue(strpos($result[0], '<title>' . $view['page[title]'] . '</title>'), 'The Feed RSS preview was rendered.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the taxonomy term preview AJAX.
|
||||
*
|
||||
* This tests a specific regression in the taxonomy term view preview.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2452659
|
||||
*/
|
||||
public function testTaxonomyAJAX() {
|
||||
\Drupal::service('module_installer')->install(array('taxonomy'));
|
||||
$this->getPreviewAJAX('taxonomy_term', 'page_1', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests pagers in the preview form.
|
||||
*/
|
||||
public function testPreviewWithPagersUI() {
|
||||
|
||||
// Create 11 nodes and make sure that everyone is returned.
|
||||
$this->drupalCreateContentType(array('type' => 'page'));
|
||||
for ($i = 0; $i < 11; $i++) {
|
||||
$this->drupalCreateNode();
|
||||
}
|
||||
|
||||
// Test Full Pager.
|
||||
$this->getPreviewAJAX('test_pager_full', 'default', 5);
|
||||
|
||||
// Test that the pager is present and rendered.
|
||||
$elements = $this->xpath('//ul[contains(@class, :class)]/li', array(':class' => 'pager__items'));
|
||||
$this->assertTrue(!empty($elements), 'Full pager found.');
|
||||
|
||||
// Verify elements and links to pages.
|
||||
// We expect to find 5 elements: current page == 1, links to pages 2 and
|
||||
// and 3, links to 'next >' and 'last >>' pages.
|
||||
$this->assertClass($elements[0], 'is-active', 'Element for current page has .is-active class.');
|
||||
$this->assertTrue($elements[0]->a, 'Element for current page has link.');
|
||||
|
||||
$this->assertClass($elements[1], 'pager__item', 'Element for page 2 has .pager__item class.');
|
||||
$this->assertTrue($elements[1]->a, 'Link to page 2 found.');
|
||||
|
||||
$this->assertClass($elements[2], 'pager__item', 'Element for page 3 has .pager__item class.');
|
||||
$this->assertTrue($elements[2]->a, 'Link to page 3 found.');
|
||||
|
||||
$this->assertClass($elements[3], 'pager__item--next', 'Element for next page has .pager__item--next class.');
|
||||
$this->assertTrue($elements[3]->a, 'Link to next page found.');
|
||||
|
||||
$this->assertClass($elements[4], 'pager__item--last', 'Element for last page has .pager__item--last class.');
|
||||
$this->assertTrue($elements[4]->a, 'Link to last page found.');
|
||||
|
||||
// Navigate to next page.
|
||||
$elements = $this->xpath('//li[contains(@class, :class)]/a', array(':class' => 'pager__item--next'));
|
||||
$this->clickPreviewLinkAJAX($elements[0]['href'], 5);
|
||||
|
||||
// Test that the pager is present and rendered.
|
||||
$elements = $this->xpath('//ul[contains(@class, :class)]/li', array(':class' => 'pager__items'));
|
||||
$this->assertTrue(!empty($elements), 'Full pager found.');
|
||||
|
||||
// Verify elements and links to pages.
|
||||
// We expect to find 7 elements: links to '<< first' and '< previous'
|
||||
// pages, link to page 1, current page == 2, link to page 3 and links
|
||||
// to 'next >' and 'last >>' pages.
|
||||
$this->assertClass($elements[0], 'pager__item--first', 'Element for first page has .pager__item--first class.');
|
||||
$this->assertTrue($elements[0]->a, 'Link to first page found.');
|
||||
|
||||
$this->assertClass($elements[1], 'pager__item--previous', 'Element for previous page has .pager__item--previous class.');
|
||||
$this->assertTrue($elements[1]->a, 'Link to previous page found.');
|
||||
|
||||
$this->assertClass($elements[2], 'pager__item', 'Element for page 1 has .pager__item class.');
|
||||
$this->assertTrue($elements[2]->a, 'Link to page 1 found.');
|
||||
|
||||
$this->assertClass($elements[3], 'is-active', 'Element for current page has .is-active class.');
|
||||
$this->assertTrue($elements[3]->a, 'Element for current page has link.');
|
||||
|
||||
$this->assertClass($elements[4], 'pager__item', 'Element for page 3 has .pager__item class.');
|
||||
$this->assertTrue($elements[4]->a, 'Link to page 3 found.');
|
||||
|
||||
$this->assertClass($elements[5], 'pager__item--next', 'Element for next page has .pager__item--next class.');
|
||||
$this->assertTrue($elements[5]->a, 'Link to next page found.');
|
||||
|
||||
$this->assertClass($elements[6], 'pager__item--last', 'Element for last page has .pager__item--last class.');
|
||||
$this->assertTrue($elements[6]->a, 'Link to last page found.');
|
||||
|
||||
// Test Mini Pager.
|
||||
$this->getPreviewAJAX('test_mini_pager', 'default', 3);
|
||||
|
||||
// Test that the pager is present and rendered.
|
||||
$elements = $this->xpath('//ul[contains(@class, :class)]/li', array(':class' => 'pager__items'));
|
||||
$this->assertTrue(!empty($elements), 'Mini pager found.');
|
||||
|
||||
// Verify elements and links to pages.
|
||||
// We expect to find current pages element with no link, next page element
|
||||
// with a link, and not to find previous page element.
|
||||
$this->assertClass($elements[0], 'is-active', 'Element for current page has .is-active class.');
|
||||
|
||||
$this->assertClass($elements[1], 'pager__item--next', 'Element for next page has .pager__item--next class.');
|
||||
$this->assertTrue($elements[1]->a, 'Link to next page found.');
|
||||
|
||||
// Navigate to next page.
|
||||
$elements = $this->xpath('//li[contains(@class, :class)]/a', array(':class' => 'pager__item--next'));
|
||||
$this->clickPreviewLinkAJAX($elements[0]['href'], 3);
|
||||
|
||||
// Test that the pager is present and rendered.
|
||||
$elements = $this->xpath('//ul[contains(@class, :class)]/li', array(':class' => 'pager__items'));
|
||||
$this->assertTrue(!empty($elements), 'Mini pager found.');
|
||||
|
||||
// Verify elements and links to pages.
|
||||
// We expect to find 3 elements: previous page with a link, current
|
||||
// page with no link, and next page with a link.
|
||||
$this->assertClass($elements[0], 'pager__item--previous', 'Element for previous page has .pager__item--previous class.');
|
||||
$this->assertTrue($elements[0]->a, 'Link to previous page found.');
|
||||
|
||||
$this->assertClass($elements[1], 'is-active', 'Element for current page has .is-active class.');
|
||||
$this->assertFalse(isset($elements[1]->a), 'Element for current page has no link.');
|
||||
|
||||
$this->assertClass($elements[2], 'pager__item--next', 'Element for next page has .pager__item--next class.');
|
||||
$this->assertTrue($elements[2]->a, 'Link to next page found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the additional information query info area.
|
||||
*/
|
||||
public function testPreviewAdditionalInfo() {
|
||||
\Drupal::service('module_installer')->install(array('views_ui_test'));
|
||||
$this->resetAll();
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_preview/edit');
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalPostForm(NULL, $edit = array(), t('Update preview'));
|
||||
|
||||
// Check for implementation of hook_views_preview_info_alter().
|
||||
// @see views_ui_test.module
|
||||
$elements = $this->xpath('//div[@id="views-live-preview"]/div[contains(@class, views-query-info)]//td[text()=:text]', array(':text' => t('Test row count')));
|
||||
$this->assertEqual(count($elements), 1, 'Views Query Preview Info area altered.');
|
||||
// Check that additional assets are attached.
|
||||
$this->assertTrue(strpos($this->getDrupalSettings()['ajaxPageState']['libraries'], 'views_ui_test/views_ui_test.test') !== FALSE, 'Attached library found.');
|
||||
$this->assertRaw('css/views_ui_test.test.css', 'Attached CSS asset found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preview form and force an AJAX preview update.
|
||||
*
|
||||
* @param string $view_name
|
||||
* The view to test.
|
||||
* @param string $panel_id
|
||||
* The view panel to test.
|
||||
* @param int $row_count
|
||||
* The expected number of rows in the preview.
|
||||
*/
|
||||
protected function getPreviewAJAX($view_name, $panel_id, $row_count) {
|
||||
$this->drupalGet('admin/structure/views/view/' . $view_name . '/preview/' . $panel_id);
|
||||
$result = $this->drupalPostAjaxForm(NULL, array(), array('op' => t('Update preview')));
|
||||
$this->assertPreviewAJAX($result, $row_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimic clicking on a preview link.
|
||||
*
|
||||
* @param string $url
|
||||
* The url to navigate to.
|
||||
* @param int $row_count
|
||||
* The expected number of rows in the preview.
|
||||
*/
|
||||
protected function clickPreviewLinkAJAX($url, $row_count) {
|
||||
$content = $this->content;
|
||||
$drupal_settings = $this->drupalSettings;
|
||||
$ajax_settings = array(
|
||||
'wrapper' => 'views-preview-wrapper',
|
||||
'method' => 'replaceWith',
|
||||
);
|
||||
$url = $this->getAbsoluteUrl($url);
|
||||
$post = array('js' => 'true') + $this->getAjaxPageStatePostData();
|
||||
$result = Json::decode($this->drupalPost($url, 'application/vnd.drupal-ajax', $post));
|
||||
if (!empty($result)) {
|
||||
$this->drupalProcessAjaxResponse($content, $result, $ajax_settings, $drupal_settings);
|
||||
}
|
||||
$this->assertPreviewAJAX($result, $row_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the AJAX response contains expected data.
|
||||
*
|
||||
* @param array $result
|
||||
* An array of AJAX commands.
|
||||
* @param int $row_count
|
||||
* The expected number of rows in the preview.
|
||||
*/
|
||||
protected function assertPreviewAJAX($result, $row_count) {
|
||||
// Has AJAX callback replied with an insert command? If so, we can
|
||||
// assume that the page content was updated with AJAX returned data.
|
||||
$result_commands = array();
|
||||
foreach ($result as $command) {
|
||||
$result_commands[$command['command']] = $command;
|
||||
}
|
||||
$this->assertTrue(isset($result_commands['insert']), 'AJAX insert command received.');
|
||||
|
||||
// Test if preview contains the expected number of rows.
|
||||
$elements = $this->xpath('//div[@class = "view-content"]/div[contains(@class, views-row)]');
|
||||
$this->assertEqual(count($elements), $row_count, 'Expected items found on page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that an element has a given class.
|
||||
*
|
||||
* @param \SimpleXMLElement $element
|
||||
* The element to test.
|
||||
* @param string $class
|
||||
* The class to assert.
|
||||
* @param string $message
|
||||
* (optional) A verbose message to output.
|
||||
*/
|
||||
protected function assertClass(\SimpleXMLElement $element, $class, $message = NULL) {
|
||||
if (!isset($message)) {
|
||||
$message = "Class .$class found.";
|
||||
}
|
||||
$this->assertTrue(strpos($element['class'], $class) !== FALSE, $message);
|
||||
}
|
||||
|
||||
}
|
61
core/modules/views_ui/src/Tests/QueryTest.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\QueryTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views\Entity\View;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views_test_data\Plugin\views\query\QueryTest as QueryTestPlugin;
|
||||
|
||||
/**
|
||||
* Tests query plugins.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class QueryTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\views\Tests\ViewTestBase::viewsData().
|
||||
*/
|
||||
protected function viewsData() {
|
||||
$data = parent::viewsData();
|
||||
$data['views_test_data']['table']['base']['query_id'] = 'query_test';
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests query plugins settings.
|
||||
*/
|
||||
public function testQueryUI() {
|
||||
$view = View::load('test_view');
|
||||
$display = &$view->getDisplay('default');
|
||||
$display['display_options']['query'] = ['type' => 'query_test'];
|
||||
$view->save();
|
||||
|
||||
// Save some query settings.
|
||||
$query_settings_path = "admin/structure/views/nojs/display/test_view/default/query";
|
||||
$random_value = $this->randomMachineName();
|
||||
$this->drupalPostForm($query_settings_path, array('query[options][test_setting]' => $random_value), t('Apply'));
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
// Check that the settings are saved into the view itself.
|
||||
$view = Views::getView('test_view');
|
||||
$view->initDisplay();
|
||||
$view->initQuery();
|
||||
$this->assertEqual($random_value, $view->query->options['test_setting'], 'Query settings got saved');
|
||||
}
|
||||
|
||||
}
|
83
core/modules/views_ui/src/Tests/RearrangeFieldsTest.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\RearrangeFieldsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the reordering of fields via AJAX.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views_ui\Form\Ajax\Rearrange
|
||||
*/
|
||||
class RearrangeFieldsTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Gets the fields from the View.
|
||||
*/
|
||||
protected function getViewFields($view_name = 'test_view', $display_id = 'default') {
|
||||
$view = Views::getView($view_name);
|
||||
$view->setDisplay($display_id);
|
||||
$fields = array();
|
||||
foreach ($view->displayHandlers->get('default')->getHandlers('field') as $field => $handler) {
|
||||
$fields[] = $field;
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the fields are in the correct order.
|
||||
*
|
||||
* @param $view_name
|
||||
* The name of the view.
|
||||
* @param $fields
|
||||
* Array of field names.
|
||||
*/
|
||||
protected function assertFieldOrder($view_name, $fields) {
|
||||
$this->drupalGet('admin/structure/views/nojs/rearrange/' . $view_name . '/default/field');
|
||||
|
||||
foreach ($fields as $idx => $field) {
|
||||
$this->assertFieldById('edit-fields-' . $field . '-weight', $idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field sorting.
|
||||
*/
|
||||
public function testRearrangeFields() {
|
||||
$view_name = 'test_view';
|
||||
|
||||
// Checks that the order on the rearrange form matches the creation order.
|
||||
$this->assertFieldOrder($view_name, $this->getViewFields($view_name));
|
||||
|
||||
// Checks that a field is not deleted if a value is not passed back.
|
||||
$fields = array();
|
||||
$this->drupalPostForm('admin/structure/views/nojs/rearrange/' . $view_name . '/default/field', $fields, t('Apply'));
|
||||
$this->assertFieldOrder($view_name, $this->getViewFields($view_name));
|
||||
|
||||
// Checks that revers the new field order is respected.
|
||||
$reversedFields = array_reverse($this->getViewFields($view_name));
|
||||
$fields = array();
|
||||
foreach ($reversedFields as $delta => $field) {
|
||||
$fields['fields[' . $field . '][weight]'] = $delta;
|
||||
}
|
||||
$this->drupalPostForm('admin/structure/views/nojs/rearrange/' . $view_name . '/default/field', $fields, t('Apply'));
|
||||
$this->assertFieldOrder($view_name, $reversedFields);
|
||||
|
||||
// Checks that there is a remove link for each field.
|
||||
$this->assertEqual(count($this->cssSelect('a.views-remove-link')), count($fields));
|
||||
}
|
||||
|
||||
}
|
50
core/modules/views_ui/src/Tests/RedirectTest.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\RedirectTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests the redirecting after saving a views.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class RedirectTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view', 'test_redirect_view');
|
||||
|
||||
/**
|
||||
* Tests the redirecting.
|
||||
*/
|
||||
public function testRedirect() {
|
||||
$view_name = 'test_view';
|
||||
|
||||
$random_destination = $this->randomMachineName();
|
||||
$edit_path = "admin/structure/views/view/$view_name/edit";
|
||||
|
||||
$this->drupalPostForm($edit_path, array(), t('Save') , array('query' => array('destination' => $random_destination)));
|
||||
$this->assertUrl($random_destination, array(), 'Make sure the user got redirected to the expected page defined in the destination.');
|
||||
|
||||
// Setup a view with a certain page display path. If you change the path
|
||||
// but have the old url in the destination the user should be redirected to
|
||||
// the new path.
|
||||
$view_name = 'test_redirect_view';
|
||||
$new_path = $this->randomMachineName();
|
||||
|
||||
$edit_path = "admin/structure/views/view/$view_name/edit";
|
||||
$path_edit_path = "admin/structure/views/nojs/display/$view_name/page_1/path";
|
||||
|
||||
$this->drupalPostForm($path_edit_path, array('path' => $new_path), t('Apply'));
|
||||
$this->drupalPostForm($edit_path, array(), t('Save'), array('query' => array('destination' => 'test-redirect-view')));
|
||||
$this->assertUrl($new_path, array(), 'Make sure the user got redirected to the expected page after changing the url of a page display.');
|
||||
}
|
||||
|
||||
}
|
48
core/modules/views_ui/src/Tests/ReportTest.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\ReportTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests existence of the views plugin report.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class ReportTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views', 'views_ui');
|
||||
|
||||
/**
|
||||
* Stores an admin user used by the different tests.
|
||||
*
|
||||
* @var \Drupal\user\User
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->adminUser = $this->drupalCreateUser(array('administer views'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the existence of the views plugin report.
|
||||
*/
|
||||
public function testReport() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Test the report page.
|
||||
$this->drupalGet('admin/reports/views-plugins');
|
||||
$this->assertResponse(200, "Views report page exists");
|
||||
}
|
||||
|
||||
}
|
92
core/modules/views_ui/src/Tests/RowUITest.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\RowUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the UI of row plugins.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views_test_data\Plugin\views\row\RowTest.
|
||||
*/
|
||||
class RowUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Tests changing the row plugin and changing some options of a row.
|
||||
*/
|
||||
public function testRowUI() {
|
||||
$view_name = 'test_view';
|
||||
$view_edit_url = "admin/structure/views/view/$view_name/edit";
|
||||
|
||||
$row_plugin_url = "admin/structure/views/nojs/display/$view_name/default/row";
|
||||
$row_options_url = "admin/structure/views/nojs/display/$view_name/default/row_options";
|
||||
|
||||
$this->drupalGet($row_plugin_url);
|
||||
$this->assertFieldByName('row[type]', 'fields', 'The default row plugin selected in the UI should be fields.');
|
||||
|
||||
$edit = array(
|
||||
'row[type]' => 'test_row'
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->assertFieldByName('row_options[test_option]', NULL, 'Make sure the custom settings form from the test plugin appears.');
|
||||
$random_name = $this->randomMachineName();
|
||||
$edit = array(
|
||||
'row_options[test_option]' => $random_name
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalGet($row_options_url);
|
||||
$this->assertFieldByName('row_options[test_option]', $random_name, 'Make sure the custom settings form field has the expected value stored.');
|
||||
|
||||
$this->drupalPostForm($view_edit_url, array(), t('Save'));
|
||||
$this->assertLink(t('Test row plugin'), 0, 'Make sure the test row plugin is shown in the UI');
|
||||
|
||||
$view = Views::getView($view_name);
|
||||
$view->initDisplay();
|
||||
$row = $view->display_handler->getOption('row');
|
||||
$this->assertEqual($row['type'], 'test_row', 'Make sure that the test_row got saved as used row plugin.');
|
||||
$this->assertEqual($row['options']['test_option'], $random_name, 'Make sure that the custom settings field got saved as expected.');
|
||||
|
||||
// Change the row plugin to fields using ajax.
|
||||
// Note: this is the best approximation we can achieve, because we cannot
|
||||
// simulate the 'openDialog' command in
|
||||
// WebTestBase::drupalProcessAjaxResponse(), hence we have to make do.
|
||||
$row_plugin_url_ajax = str_replace('/nojs/', '/ajax/', $row_plugin_url);
|
||||
$ajax_settings = [
|
||||
'accepts' => 'application/vnd.drupal-ajax',
|
||||
'submit' => [
|
||||
'_triggering_element_name' => 'op',
|
||||
'_triggering_element_value' => 'Apply',
|
||||
],
|
||||
'url' => $row_plugin_url_ajax,
|
||||
];
|
||||
$this->drupalPostAjaxForm($row_plugin_url, ['row[type]' => 'fields'], NULL, $row_plugin_url_ajax, [], [], NULL, $ajax_settings);
|
||||
$this->drupalGet($row_plugin_url);
|
||||
$this->assertResponse(200);
|
||||
$this->assertFieldByName('row[type]', 'fields', 'Make sure that the fields got saved as used row plugin.');
|
||||
|
||||
// Ensure that entity row plugins appear.
|
||||
$view_name = 'content';
|
||||
$row_plugin_url = "admin/structure/views/nojs/display/$view_name/default/row";
|
||||
$row_options_url = "admin/structure/views/nojs/display/$view_name/default/row_options";
|
||||
|
||||
$this->drupalGet($row_plugin_url);
|
||||
$this->assertFieldByName('row[type]', 'entity:node');
|
||||
$this->drupalPostForm(NULL, ['row[type]' => 'entity:node'], t('Apply'));
|
||||
$this->assertUrl($row_options_url);
|
||||
$this->assertFieldByName('row_options[view_mode]', 'teaser');
|
||||
}
|
||||
|
||||
}
|
139
core/modules/views_ui/src/Tests/SettingsTest.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\SettingsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests all ui related settings under admin/structure/views/settings.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class SettingsTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Stores an admin user used by the different tests.
|
||||
*
|
||||
* @var \Drupal\user\User
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Tests the settings for the edit ui.
|
||||
*/
|
||||
function testEditUI() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Test the settings tab exists.
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$this->assertLinkByHref('admin/structure/views/settings');
|
||||
|
||||
// Test the confirmation message.
|
||||
$this->drupalPostForm('admin/structure/views/settings', array(), t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
|
||||
// Configure to always show the master display.
|
||||
$edit = array(
|
||||
'ui_show_master_display' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/settings', $edit, t('Save configuration'));
|
||||
|
||||
$view = array();
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['description'] = $this->randomMachineName(16);
|
||||
$view['page[create]'] = TRUE;
|
||||
$view['page[title]'] = $this->randomMachineName(16);
|
||||
$view['page[path]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
// Configure to not always show the master display.
|
||||
// If you have a view without a page or block the master display should be
|
||||
// still shown.
|
||||
$edit = array(
|
||||
'ui_show_master_display' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/settings', $edit, t('Save configuration'));
|
||||
|
||||
$view['page[create]'] = FALSE;
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
// Create a view with an additional display, so master should be hidden.
|
||||
$view['page[create]'] = TRUE;
|
||||
$view['id'] = strtolower($this->randomMachineName());
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
$this->assertNoLink(t('Master'));
|
||||
|
||||
// Configure to always show the advanced settings.
|
||||
// @todo It doesn't seem to be a way to test this as this works just on js.
|
||||
|
||||
// Configure to show the embeddable display.
|
||||
$edit = array(
|
||||
'ui_show_display_embed' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/settings', $edit, t('Save configuration'));
|
||||
|
||||
$view['id'] = strtolower($this->randomMachineName());
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
$this->assertFieldById('edit-displays-top-add-display-embed');
|
||||
|
||||
$edit = array(
|
||||
'ui_show_display_embed' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/settings', $edit, t('Save configuration'));
|
||||
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
$this->assertNoFieldById('edit-displays-top-add-display-embed');
|
||||
|
||||
// Configure to hide/show the sql at the preview.
|
||||
$edit = array(
|
||||
'ui_show_sql_query_enabled' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/settings', $edit, t('Save configuration'));
|
||||
|
||||
$view['id'] = strtolower($this->randomMachineName());
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Update preview'));
|
||||
$xpath = $this->xpath('//div[@class="views-query-info"]/pre');
|
||||
$this->assertEqual(count($xpath), 0, 'The views sql is hidden.');
|
||||
|
||||
$edit = array(
|
||||
'ui_show_sql_query_enabled' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/settings', $edit, t('Save configuration'));
|
||||
|
||||
$view['id'] = strtolower($this->randomMachineName());
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Update preview'));
|
||||
$xpath = $this->xpath('//div[@class="views-query-info"]//pre');
|
||||
$this->assertEqual(count($xpath), 1, 'The views sql is shown.');
|
||||
$this->assertFalse(strpos($xpath[0], 'db_condition_placeholder') !== FALSE, 'No placeholders are shown in the views sql.');
|
||||
$this->assertTrue(strpos($xpath[0], "node_field_data.status = '1'") !== FALSE, 'The placeholders in the views sql is replace by the actual value.');
|
||||
|
||||
// Test the advanced settings form.
|
||||
|
||||
// Test the confirmation message.
|
||||
$this->drupalPostForm('admin/structure/views/settings/advanced', array(), t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
|
||||
$edit = array(
|
||||
'skip_cache' => TRUE,
|
||||
'sql_signature' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/settings/advanced', $edit, t('Save configuration'));
|
||||
|
||||
$this->assertFieldChecked('edit-skip-cache', 'The skip_cache option is checked.');
|
||||
$this->assertFieldChecked('edit-sql-signature', 'The sql_signature option is checked.');
|
||||
|
||||
// Test the "Clear Views' cache" button.
|
||||
$this->drupalPostForm('admin/structure/views/settings/advanced', array(), t("Clear Views' cache"));
|
||||
$this->assertText(t('The cache has been cleared.'));
|
||||
}
|
||||
|
||||
}
|
61
core/modules/views_ui/src/Tests/StorageTest.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\StorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the UI of storage properties of views.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class StorageTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views_ui', 'language');
|
||||
|
||||
/**
|
||||
* Tests changing label, description and tag.
|
||||
*
|
||||
* @see views_ui_edit_details_form
|
||||
*/
|
||||
public function testDetails() {
|
||||
$view_name = 'test_view';
|
||||
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
|
||||
$edit = array(
|
||||
'label' => $this->randomMachineName(),
|
||||
'tag' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(30),
|
||||
'langcode' => 'fr',
|
||||
);
|
||||
|
||||
$this->drupalPostForm("admin/structure/views/nojs/edit-details/$view_name/default", $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
$view = Views::getView($view_name);
|
||||
|
||||
foreach (array('label', 'tag', 'description', 'langcode') as $property) {
|
||||
$this->assertEqual($view->storage->get($property), $edit[$property], format_string('Make sure the property @property got probably saved.', array('@property' => $property)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
40
core/modules/views_ui/src/Tests/StyleTableTest.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\StyleTableTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the UI of views when using the table style.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\style\Table.
|
||||
*/
|
||||
class StyleTableTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Tests created a table style view.
|
||||
*/
|
||||
public function testWizard() {
|
||||
// Create a new view and check that the first field has a label.
|
||||
$view = array();
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['show[wizard_key]'] = 'node';
|
||||
$view['page[create]'] = TRUE;
|
||||
$view['page[style][style_plugin]'] = 'table';
|
||||
$view['page[title]'] = $this->randomMachineName(16);
|
||||
$view['page[path]'] = $view['id'];
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
$view = Views::getView($view['id']);
|
||||
$view->initHandlers();
|
||||
$this->assertEqual($view->field['title']->options['label'], 'Title', 'The field label for table styles is not empty.');
|
||||
}
|
||||
|
||||
}
|
70
core/modules/views_ui/src/Tests/StyleUITest.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\StyleUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the UI of style plugins.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views_test_data\Plugin\views\style\StyleTest.
|
||||
*/
|
||||
class StyleUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view');
|
||||
|
||||
/**
|
||||
* Tests changing the style plugin and changing some options of a style.
|
||||
*/
|
||||
public function testStyleUI() {
|
||||
$view_name = 'test_view';
|
||||
$view_edit_url = "admin/structure/views/view/$view_name/edit";
|
||||
|
||||
$style_plugin_url = "admin/structure/views/nojs/display/$view_name/default/style";
|
||||
$style_options_url = "admin/structure/views/nojs/display/$view_name/default/style_options";
|
||||
|
||||
$this->drupalGet($style_plugin_url);
|
||||
$this->assertFieldByName('style[type]', 'default', 'The default style plugin selected in the UI should be unformatted list.');
|
||||
|
||||
$edit = array(
|
||||
'style[type]' => 'test_style'
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->assertFieldByName('style_options[test_option]', NULL, 'Make sure the custom settings form from the test plugin appears.');
|
||||
$random_name = $this->randomMachineName();
|
||||
$edit = array(
|
||||
'style_options[test_option]' => $random_name
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalGet($style_options_url);
|
||||
$this->assertFieldByName('style_options[test_option]', $random_name, 'Make sure the custom settings form field has the expected value stored.');
|
||||
|
||||
$this->drupalPostForm($view_edit_url, array(), t('Save'));
|
||||
$this->assertLink(t('Test style plugin'), 0, 'Make sure the test style plugin is shown in the UI');
|
||||
|
||||
$view = Views::getView($view_name);
|
||||
$view->initDisplay();
|
||||
$style = $view->display_handler->getOption('style');
|
||||
$this->assertEqual($style['type'], 'test_style', 'Make sure that the test_style got saved as used style plugin.');
|
||||
$this->assertEqual($style['options']['test_option'], $random_name, 'Make sure that the custom settings field got saved as expected.');
|
||||
|
||||
// Test that fields are working correctly in the UI for style plugins when
|
||||
// a field row plugin is selected.
|
||||
$this->drupalPostForm("admin/structure/views/view/$view_name/edit", array(), 'Add Page');
|
||||
$this->drupalPostForm("admin/structure/views/nojs/display/$view_name/page_1/row", array('row[type]' => 'fields'), t('Apply'));
|
||||
// If fields are being used this text will not be shown.
|
||||
$this->assertNoText(t('The selected style or row format does not use fields.'));
|
||||
}
|
||||
|
||||
}
|
66
core/modules/views_ui/src/Tests/TagTest.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\TagTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Tests\ViewUnitTestBase;
|
||||
use Drupal\views_ui\Controller\ViewsUIController;
|
||||
|
||||
/**
|
||||
* Tests the views ui tagging functionality.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class TagTest extends ViewUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views', 'views_ui', 'user');
|
||||
|
||||
/**
|
||||
* Tests the views_ui_autocomplete_tag function.
|
||||
*/
|
||||
public function testViewsUiAutocompleteTag() {
|
||||
\Drupal::moduleHandler()->loadInclude('views_ui', 'inc', 'admin');
|
||||
|
||||
// Save 15 views with a tag.
|
||||
$tags = array();
|
||||
for ($i = 0; $i < 16; $i++) {
|
||||
$suffix = $i % 2 ? 'odd' : 'even';
|
||||
$tag = 'autocomplete_tag_test_' . $suffix . $this->randomMachineName();
|
||||
$tags[] = $tag;
|
||||
entity_create('view', array('tag' => $tag, 'id' => $this->randomMachineName()))->save();
|
||||
}
|
||||
|
||||
// Make sure just ten results are returns.
|
||||
$controller = ViewsUIController::create($this->container);
|
||||
$request = $this->container->get('request_stack')->getCurrentRequest();
|
||||
$request->query->set('q', 'autocomplete_tag_test');
|
||||
$result = $controller->autocompleteTag($request);
|
||||
$matches = (array) json_decode($result->getContent());
|
||||
$this->assertEqual(count($matches), 10, 'Make sure the maximum amount of tag results is 10.');
|
||||
|
||||
// Make sure that matching by a certain prefix works.
|
||||
$request->query->set('q', 'autocomplete_tag_test_even');
|
||||
$result = $controller->autocompleteTag($request);
|
||||
$matches = (array) json_decode($result->getContent());
|
||||
$this->assertEqual(count($matches), 8, 'Make sure that only a subset is returned.');
|
||||
foreach ($matches as $tag) {
|
||||
$this->assertTrue(array_search($tag, $tags) !== FALSE, format_string('Make sure the returned tag @tag actually exists.', array('@tag' => $tag)));
|
||||
}
|
||||
|
||||
// Make sure an invalid result doesn't return anything.
|
||||
$request->query->set('q', $this->randomMachineName());
|
||||
$result = $controller->autocompleteTag($request);
|
||||
$matches = (array) json_decode($result->getContent());
|
||||
$this->assertEqual(count($matches), 0, "Make sure an invalid tag doesn't return anything.");
|
||||
}
|
||||
|
||||
}
|
77
core/modules/views_ui/src/Tests/UITestBase.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\UITestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
|
||||
/**
|
||||
* Provides a base class for testing the Views UI.
|
||||
*/
|
||||
abstract class UITestBase extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* An admin user with the 'administer views' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* An admin user with administrative permissions for views, blocks, and nodes.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $fullAdminUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'views_ui', 'block', 'taxonomy');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(array('administer views'));
|
||||
|
||||
$this->fullAdminUser = $this->drupalCreateUser(array('administer views',
|
||||
'administer blocks',
|
||||
'bypass node access',
|
||||
'access user profiles',
|
||||
'view all revisions',
|
||||
'administer permissions',
|
||||
));
|
||||
$this->drupalLogin($this->fullAdminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method which creates a random view.
|
||||
*/
|
||||
public function randomView(array $view = array()) {
|
||||
// Create a new view in the UI.
|
||||
$default = array();
|
||||
$default['label'] = $this->randomMachineName(16);
|
||||
$default['id'] = strtolower($this->randomMachineName(16));
|
||||
$default['description'] = $this->randomMachineName(16);
|
||||
$default['page[create]'] = TRUE;
|
||||
$default['page[path]'] = $default['id'];
|
||||
|
||||
$view += $default;
|
||||
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
}
|
84
core/modules/views_ui/src/Tests/UnsavedPreviewTest.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\UnsavedPreviewTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
|
||||
/**
|
||||
* Tests covering Preview of unsaved Views.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class UnsavedPreviewTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['content'];
|
||||
|
||||
/**
|
||||
* An admin user with the 'administer views' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('node', 'views_ui');
|
||||
|
||||
/**
|
||||
* Sets up a Drupal site for running functional and integration tests.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp(FALSE);
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer views']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests previews of unsaved new page displays.
|
||||
*/
|
||||
public function testUnsavedPageDisplayPreview() {
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$this->drupalCreateNode();
|
||||
}
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/content');
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Add Page'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalGet('admin/structure/views/nojs/display/content/page_2/path');
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalPostForm(NULL, ['path' => 'foobarbaz'], t('Apply'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Update preview'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertText(t('This display has no path'));
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/content/edit/page_2');
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Update preview'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertLinkByHref('foobarbaz');
|
||||
}
|
||||
|
||||
}
|
224
core/modules/views_ui/src/Tests/ViewEditTest.php
Normal file
|
@ -0,0 +1,224 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\ViewEditTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\views\Entity\View;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests some general functionality of editing views, like deleting a view.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class ViewEditTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view', 'test_display', 'test_groupwise_term_ui');
|
||||
|
||||
/**
|
||||
* Tests the delete link on a views UI.
|
||||
*/
|
||||
public function testDeleteLink() {
|
||||
$this->drupalGet('admin/structure/views/view/test_view');
|
||||
$this->assertLink(t('Delete view'), 0, 'Ensure that the view delete link appears');
|
||||
|
||||
$view = $this->container->get('entity.manager')->getStorage('view')->load('test_view');
|
||||
$this->assertTrue($view instanceof View);
|
||||
$this->clickLink(t('Delete view'));
|
||||
$this->assertUrl('admin/structure/views/view/test_view/delete');
|
||||
$this->drupalPostForm(NULL, array(), t('Delete'));
|
||||
$this->assertRaw(t('The view %name has been deleted.', array('%name' => $view->label())));
|
||||
|
||||
$this->assertUrl('admin/structure/views');
|
||||
$view = $this->container->get('entity.manager')->getStorage('view')->load('test_view');
|
||||
$this->assertFalse($view instanceof View);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the machine name form.
|
||||
*/
|
||||
public function testMachineNameOption() {
|
||||
$this->drupalGet('admin/structure/views/view/test_view');
|
||||
// Add a new attachment display.
|
||||
$this->drupalPostForm(NULL, array(), 'Add Attachment');
|
||||
|
||||
// Change the machine name for the display from page_1 to test_1.
|
||||
$edit = array('display_id' => 'test_1');
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view/attachment_1/display_id', $edit, 'Apply');
|
||||
$this->assertLink(t('test_1'));
|
||||
|
||||
// Save the view, and test the new ID has been saved.
|
||||
$this->drupalPostForm(NULL, array(), 'Save');
|
||||
$view = \Drupal::entityManager()->getStorage('view')->load('test_view');
|
||||
$displays = $view->get('display');
|
||||
$this->assertTrue(!empty($displays['test_1']), 'Display data found for new display ID key.');
|
||||
$this->assertIdentical($displays['test_1']['id'], 'test_1', 'New display ID matches the display ID key.');
|
||||
$this->assertFalse(array_key_exists('attachment_1', $displays), 'Old display ID not found.');
|
||||
|
||||
// Test the form validation with invalid IDs.
|
||||
$machine_name_edit_url = 'admin/structure/views/nojs/display/test_view/test_1/display_id';
|
||||
$error_text = t('Display name must be letters, numbers, or underscores only.');
|
||||
|
||||
// Test that potential invalid display ID requests are detected
|
||||
$this->drupalGet('admin/structure/views/ajax/handler/test_view/fake_display_name/filter/title');
|
||||
$this->assertText('Invalid display id fake_display_name');
|
||||
|
||||
$edit = array('display_id' => 'test 1');
|
||||
$this->drupalPostForm($machine_name_edit_url, $edit, 'Apply');
|
||||
$this->assertText($error_text);
|
||||
|
||||
$edit = array('display_id' => 'test_1#');
|
||||
$this->drupalPostForm($machine_name_edit_url, $edit, 'Apply');
|
||||
$this->assertText($error_text);
|
||||
|
||||
// Test using an existing display ID.
|
||||
$edit = array('display_id' => 'default');
|
||||
$this->drupalPostForm($machine_name_edit_url, $edit, 'Apply');
|
||||
$this->assertText(t('Display id should be unique.'));
|
||||
|
||||
// Test that the display ID has not been changed.
|
||||
$this->drupalGet('admin/structure/views/view/test_view/edit/test_1');
|
||||
$this->assertLink(t('test_1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the language options on the views edit form.
|
||||
*/
|
||||
public function testEditFormLanguageOptions() {
|
||||
// Language options should not exist without language module.
|
||||
$test_views = array(
|
||||
'test_view' => 'default',
|
||||
'test_display' => 'page_1',
|
||||
);
|
||||
foreach ($test_views as $view_name => $display) {
|
||||
$this->drupalGet('admin/structure/views/view/' . $view_name);
|
||||
$this->assertResponse(200);
|
||||
$langcode_url = 'admin/structure/views/nojs/display/' . $view_name . '/' . $display . '/rendering_language';
|
||||
$this->assertNoLinkByHref($langcode_url);
|
||||
$this->assertNoLink(t('!type language selected for page', array('!type' => t('Content'))));
|
||||
$this->assertNoLink(t('Content language of view row'));
|
||||
}
|
||||
|
||||
// Make the site multilingual and test the options again.
|
||||
$this->container->get('module_installer')->install(array('language', 'content_translation'));
|
||||
ConfigurableLanguage::createFromLangcode('hu')->save();
|
||||
$this->resetAll();
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Language options should now exist with entity language the default.
|
||||
foreach ($test_views as $view_name => $display) {
|
||||
$this->drupalGet('admin/structure/views/view/' . $view_name);
|
||||
$this->assertResponse(200);
|
||||
$langcode_url = 'admin/structure/views/nojs/display/' . $view_name . '/' . $display . '/rendering_language';
|
||||
if ($view_name == 'test_view') {
|
||||
$this->assertNoLinkByHref($langcode_url);
|
||||
$this->assertNoLink(t('!type language selected for page', array('!type' => t('Content'))));
|
||||
$this->assertNoLink(t('Content language of view row'));
|
||||
}
|
||||
else {
|
||||
$this->assertLinkByHref($langcode_url);
|
||||
$this->assertNoLink(t('!type language selected for page', array('!type' => t('Content'))));
|
||||
$this->assertLink(t('Content language of view row'));
|
||||
}
|
||||
|
||||
$this->drupalGet($langcode_url);
|
||||
$this->assertResponse(200);
|
||||
if ($view_name == 'test_view') {
|
||||
$this->assertText(t('The view is not based on a translatable entity type or the site is not multilingual.'));
|
||||
}
|
||||
else {
|
||||
$this->assertFieldByName('rendering_language', '***LANGUAGE_entity_translation***');
|
||||
// Test that the order of the language list is similar to other language
|
||||
// lists, such as in the content translation settings.
|
||||
$expected_elements = array(
|
||||
'***LANGUAGE_entity_translation***',
|
||||
'***LANGUAGE_entity_default***',
|
||||
'***LANGUAGE_site_default***',
|
||||
'***LANGUAGE_language_interface***',
|
||||
'en',
|
||||
'hu',
|
||||
);
|
||||
$elements = $this->xpath('//select[@id="edit-rendering-language"]/option');
|
||||
// Compare values inside the option elements with expected values.
|
||||
for ($i = 0; $i < count($elements); $i++) {
|
||||
$this->assertEqual($elements[$i]->attributes()->{'value'}, $expected_elements[$i]);
|
||||
}
|
||||
|
||||
// Check that the selected values are respected even we they are not
|
||||
// supposed to be listed.
|
||||
// Give permission to edit languages to authenticated users.
|
||||
$edit = [
|
||||
'authenticated[administer languages]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('/admin/people/permissions', $edit, t('Save permissions'));
|
||||
// Enable Content language negotiation so we have one more item
|
||||
// to select.
|
||||
$edit = [
|
||||
'language_content[configurable]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
|
||||
|
||||
// Choose the new negotiation as the rendering language.
|
||||
$edit = [
|
||||
'rendering_language' => '***LANGUAGE_language_content***',
|
||||
];
|
||||
$this->drupalPostForm('/admin/structure/views/nojs/display/' . $view_name . '/' . $display . '/rendering_language', $edit, t('Apply'));
|
||||
|
||||
// Disable language content negotiation.
|
||||
$edit = [
|
||||
'language_content[configurable]' => FALSE,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
|
||||
|
||||
// Check that the previous selection is listed and selected.
|
||||
$this->drupalGet($langcode_url);
|
||||
$element = $this->xpath('//select[@id="edit-rendering-language"]/option[@value="***LANGUAGE_language_content***" and @selected="selected"]');
|
||||
$this->assertFalse(empty($element), 'Current selection is not lost');
|
||||
|
||||
// Check the order for the langcode filter.
|
||||
$langcode_url = 'admin/structure/views/nojs/handler/' . $view_name . '/' . $display . '/filter/langcode';
|
||||
$this->drupalGet($langcode_url);
|
||||
$this->assertResponse(200);
|
||||
|
||||
$expected_elements = array(
|
||||
'all',
|
||||
'***LANGUAGE_site_default***',
|
||||
'***LANGUAGE_language_interface***',
|
||||
'***LANGUAGE_language_content***',
|
||||
'en',
|
||||
'hu',
|
||||
'und',
|
||||
'zxx',
|
||||
);
|
||||
$elements = $this->xpath('//div[@id="edit-options-value"]//input');
|
||||
// Compare values inside the option elements with expected values.
|
||||
for ($i = 0; $i < count($elements); $i++) {
|
||||
$this->assertEqual($elements[$i]->attributes()->{'value'}, $expected_elements[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Representative Node for a Taxonomy Term.
|
||||
*/
|
||||
public function testRelationRepresentativeNode() {
|
||||
// Populate and submit the form.
|
||||
$edit["name[taxonomy_term_field_data.tid_representative]"] = TRUE;
|
||||
$this->drupalPostForm('admin/structure/views/nojs/add-handler/test_groupwise_term_ui/default/relationship', $edit, 'Add and configure relationships');
|
||||
// Apply changes.
|
||||
$edit = array();
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_groupwise_term_ui/default/relationship/tid_representative', $edit, 'Apply');
|
||||
}
|
||||
|
||||
}
|
116
core/modules/views_ui/src/Tests/ViewsUITourTest.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\ViewsUITourTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\tour\Tests\TourTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests the Views UI tour.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class ViewsUITourTest extends TourTestBase {
|
||||
|
||||
/**
|
||||
* An admin user with administrative permissions for views.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* String translation storage object.
|
||||
*
|
||||
* @var \Drupal\locale\StringStorageInterface
|
||||
*/
|
||||
protected $localeStorage;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views_ui', 'tour', 'language', 'locale');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->adminUser = $this->drupalCreateUser(array('administer views', 'access tour'));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests views_ui tour tip availability.
|
||||
*/
|
||||
public function testViewsUiTourTips() {
|
||||
// Create a basic view that shows all content, with a page and a block
|
||||
// display.
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['page[create]'] = 1;
|
||||
$view['page[path]'] = $this->randomMachineName(16);
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
$this->assertTourTips();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests views_ui tour tip availability in a different language.
|
||||
*/
|
||||
public function testViewsUiTourTipsTranslated() {
|
||||
$langcode = 'nl';
|
||||
|
||||
// Add a default locale storage for this test.
|
||||
$this->localeStorage = $this->container->get('locale.storage');
|
||||
|
||||
// Add Dutch language programmatically.
|
||||
ConfigurableLanguage::createFromLangcode($langcode)->save();
|
||||
|
||||
// Handler titles that need translations.
|
||||
$handler_titles = array(
|
||||
'Format',
|
||||
'Fields',
|
||||
'Sort criteria',
|
||||
'Filter criteria',
|
||||
);
|
||||
|
||||
foreach ($handler_titles as $handler_title) {
|
||||
// Create source string.
|
||||
$source = $this->localeStorage->createString(array(
|
||||
'source' => $handler_title
|
||||
));
|
||||
$source->save();
|
||||
$this->createTranslation($source, $langcode);
|
||||
}
|
||||
|
||||
// Create a basic view that shows all content, with a page and a block
|
||||
// display.
|
||||
$view['label'] = $this->randomMachineName(16);
|
||||
$view['id'] = strtolower($this->randomMachineName(16));
|
||||
$view['page[create]'] = 1;
|
||||
$view['page[path]'] = $this->randomMachineName(16);
|
||||
// Load the page in dutch.
|
||||
$this->drupalPostForm(
|
||||
$langcode . '/admin/structure/views/add',
|
||||
$view,
|
||||
t('Save and edit')
|
||||
);
|
||||
$this->assertTourTips();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates single translation for source string.
|
||||
*/
|
||||
public function createTranslation($source, $langcode) {
|
||||
return $this->localeStorage->createTranslation(array(
|
||||
'lid' => $source->lid,
|
||||
'language' => $langcode,
|
||||
'translation' => $this->randomMachineName(100),
|
||||
))->save();
|
||||
}
|
||||
|
||||
}
|
67
core/modules/views_ui/src/Tests/WizardTest.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\WizardTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
use Drupal\views\Tests\Wizard\WizardTestBase;
|
||||
|
||||
/**
|
||||
* Tests the wizard.
|
||||
*
|
||||
* @group views_ui
|
||||
* @see \Drupal\views\Plugin\views\display\DisplayPluginBase
|
||||
* @see \Drupal\views\Plugin\views\display\PathPluginBase
|
||||
* @see \Drupal\views\Plugin\views\wizard\WizardPluginBase
|
||||
*/
|
||||
class WizardTest extends WizardTestBase {
|
||||
|
||||
/**
|
||||
* Tests filling in the wizard with really long strings.
|
||||
*/
|
||||
public function testWizardFieldLength() {
|
||||
$view = array();
|
||||
$view['label'] = $this->randomMachineName(256);
|
||||
$view['id'] = strtolower($this->randomMachineName(129));
|
||||
$view['page[create]'] = TRUE;
|
||||
$view['page[path]'] = $this->randomMachineName(255);
|
||||
$view['page[title]'] = $this->randomMachineName(256);
|
||||
$view['page[feed]'] = TRUE;
|
||||
$view['page[feed_properties][path]'] = $this->randomMachineName(255);
|
||||
$view['block[create]'] = TRUE;
|
||||
$view['block[title]'] = $this->randomMachineName(256);
|
||||
$view['rest_export[create]'] = TRUE;
|
||||
$view['rest_export[path]'] = $this->randomMachineName(255);
|
||||
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
|
||||
$this->assertText('Machine-readable name cannot be longer than 128 characters but is currently 129 characters long.');
|
||||
$this->assertText('Path cannot be longer than 254 characters but is currently 255 characters long.');
|
||||
$this->assertText('Page title cannot be longer than 255 characters but is currently 256 characters long.');
|
||||
$this->assertText('View name cannot be longer than 255 characters but is currently 256 characters long.');
|
||||
$this->assertText('Feed path cannot be longer than 254 characters but is currently 255 characters long.');
|
||||
$this->assertText('Block title cannot be longer than 255 characters but is currently 256 characters long.');
|
||||
$this->assertText('REST export path cannot be longer than 254 characters but is currently 255 characters long.');
|
||||
|
||||
$view['label'] = $this->randomMachineName(255);
|
||||
$view['id'] = strtolower($this->randomMachineName(128));
|
||||
$view['page[create]'] = TRUE;
|
||||
$view['page[path]'] = $this->randomMachineName(254);
|
||||
$view['page[title]'] = $this->randomMachineName(255);
|
||||
$view['page[feed]'] = TRUE;
|
||||
$view['page[feed_properties][path]'] = $this->randomMachineName(254);
|
||||
$view['block[create]'] = TRUE;
|
||||
$view['block[title]'] = $this->randomMachineName(255);
|
||||
$view['rest_export[create]'] = TRUE;
|
||||
$view['rest_export[path]'] = $this->randomMachineName(254);
|
||||
|
||||
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
|
||||
$this->assertUrl('admin/structure/views/view/' . $view['id'], array(), 'Make sure the view saving was successful and the browser got redirected to the edit page.');
|
||||
// Assert that the page title is correctly truncated.
|
||||
$this->assertText(views_ui_truncate($view['page[title]'], 32));
|
||||
}
|
||||
|
||||
}
|
36
core/modules/views_ui/src/Tests/XssTest.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\Tests\XssTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui\Tests;
|
||||
|
||||
/**
|
||||
* Tests the Xss vulnerability.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class XssTest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'user', 'views_ui', 'views_ui_test');
|
||||
|
||||
public function testViewsUi() {
|
||||
$this->drupalGet('admin/structure/views');
|
||||
$this->assertRaw('<script>alert("foo");</script>, <marquee>test</marquee>', 'The view tag is properly escaped.');
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/sa_contrib_2013_035');
|
||||
$this->assertRaw('&lt;marquee&gt;test&lt;/marquee&gt;', 'Field admin label is properly escaped.');
|
||||
|
||||
$this->drupalGet('admin/structure/views/nojs/handler/sa_contrib_2013_035/page_1/header/area');
|
||||
$this->assertRaw('[title] == &lt;marquee&gt;test&lt;/marquee&gt;', 'Token label is properly escaped.');
|
||||
$this->assertRaw('[title_1] == &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;', 'Token label is properly escaped.');
|
||||
}
|
||||
|
||||
}
|
211
core/modules/views_ui/src/ViewAddForm.php
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\ViewAddForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Plugin\views\wizard\WizardPluginBase;
|
||||
use Drupal\views\Plugin\views\wizard\WizardException;
|
||||
use Drupal\views\Plugin\ViewsPluginManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Form controller for the Views edit form.
|
||||
*/
|
||||
class ViewAddForm extends ViewFormBase {
|
||||
|
||||
/**
|
||||
* The wizard plugin manager.
|
||||
*
|
||||
* @var \Drupal\views\Plugin\ViewsPluginManager
|
||||
*/
|
||||
protected $wizardManager;
|
||||
|
||||
/**
|
||||
* Constructs a new ViewEditForm object.
|
||||
*
|
||||
* @param \Drupal\views\Plugin\ViewsPluginManager $wizard_manager
|
||||
* The wizard plugin manager.
|
||||
*/
|
||||
public function __construct(ViewsPluginManager $wizard_manager) {
|
||||
$this->wizardManager = $wizard_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.views.wizard')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareEntity() {
|
||||
// Do not prepare the entity while it is being added.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form['#attached']['library'][] = 'views_ui/views_ui.admin';
|
||||
$form['#attributes']['class'] = array('views-admin');
|
||||
|
||||
$form['name'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('View basic information'),
|
||||
'#attributes' => array('class' => array('fieldset-no-legend')),
|
||||
);
|
||||
|
||||
$form['name']['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('View name'),
|
||||
'#required' => TRUE,
|
||||
'#size' => 32,
|
||||
'#default_value' => '',
|
||||
'#maxlength' => 255,
|
||||
);
|
||||
$form['name']['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#maxlength' => 128,
|
||||
'#machine_name' => array(
|
||||
'exists' => '\Drupal\views\Views::getView',
|
||||
'source' => array('name', 'label'),
|
||||
),
|
||||
'#description' => $this->t('A unique machine-readable name for this View. It must only contain lowercase letters, numbers, and underscores.'),
|
||||
);
|
||||
|
||||
$form['name']['description_enable'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Description'),
|
||||
);
|
||||
$form['name']['description'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Provide description'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 64,
|
||||
'#default_value' => '',
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
':input[name="description_enable"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Create a wrapper for the entire dynamic portion of the form. Everything
|
||||
// that can be updated by AJAX goes somewhere inside here. For example, this
|
||||
// is needed by "Show" dropdown (below); it changes the base table of the
|
||||
// view and therefore potentially requires all options on the form to be
|
||||
// dynamically updated.
|
||||
$form['displays'] = array();
|
||||
|
||||
// Create the part of the form that allows the user to select the basic
|
||||
// properties of what the view will display.
|
||||
$form['displays']['show'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('View settings'),
|
||||
'#tree' => TRUE,
|
||||
'#attributes' => array('class' => array('container-inline')),
|
||||
);
|
||||
|
||||
// Create the "Show" dropdown, which allows the base table of the view to be
|
||||
// selected.
|
||||
$wizard_plugins = $this->wizardManager->getDefinitions();
|
||||
$options = array();
|
||||
foreach ($wizard_plugins as $key => $wizard) {
|
||||
$options[$key] = $wizard['title'];
|
||||
}
|
||||
$form['displays']['show']['wizard_key'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Show'),
|
||||
'#options' => $options,
|
||||
);
|
||||
$show_form = &$form['displays']['show'];
|
||||
$default_value = \Drupal::moduleHandler()->moduleExists('node') ? 'node' : 'users';
|
||||
$show_form['wizard_key']['#default_value'] = WizardPluginBase::getSelected($form_state, array('show', 'wizard_key'), $default_value, $show_form['wizard_key']);
|
||||
// Changing this dropdown updates the entire content of $form['displays'] via
|
||||
// AJAX.
|
||||
views_ui_add_ajax_trigger($show_form, 'wizard_key', array('displays'));
|
||||
|
||||
// Build the rest of the form based on the currently selected wizard plugin.
|
||||
$wizard_key = $show_form['wizard_key']['#default_value'];
|
||||
$wizard_instance = $this->wizardManager->createInstance($wizard_key);
|
||||
$form = $wizard_instance->buildForm($form, $form_state);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Save and edit');
|
||||
// Remove EntityFormController::save() form the submission handlers.
|
||||
$actions['submit']['#submit'] = array(array($this, 'submitForm'));
|
||||
$actions['cancel'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Cancel'),
|
||||
'#submit' => array('::cancel'),
|
||||
'#limit_validation_errors' => array(),
|
||||
);
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate(array $form, FormStateInterface $form_state) {
|
||||
$wizard_type = $form_state->getValue(array('show', 'wizard_key'));
|
||||
$wizard_instance = $this->wizardManager->createInstance($wizard_type);
|
||||
$form_state->set('wizard', $wizard_instance->getPluginDefinition());
|
||||
$form_state->set('wizard_instance', $wizard_instance);
|
||||
$errors = $wizard_instance->validateView($form, $form_state);
|
||||
|
||||
foreach ($errors as $display_errors) {
|
||||
foreach ($display_errors as $name => $message) {
|
||||
$form_state->setErrorByName($name, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
try {
|
||||
/** @var $wizard \Drupal\views\Plugin\views\wizard\WizardInterface */
|
||||
$wizard = $form_state->get('wizard_instance');
|
||||
$this->entity = $wizard->createView($form, $form_state);
|
||||
}
|
||||
// @todo Figure out whether it really makes sense to throw and catch exceptions on the wizard.
|
||||
catch (WizardException $e) {
|
||||
drupal_set_message($e->getMessage(), 'error');
|
||||
$form_state->setRedirect('entity.view.collection');
|
||||
return;
|
||||
}
|
||||
$this->entity->save();
|
||||
|
||||
$form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for the 'cancel' action.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
public function cancel(array $form, FormStateInterface $form_state) {
|
||||
$form_state->setRedirect('entity.view.collection');
|
||||
}
|
||||
|
||||
}
|
84
core/modules/views_ui/src/ViewDuplicateForm.php
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\ViewDuplicateForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Form controller for the Views duplicate form.
|
||||
*/
|
||||
class ViewDuplicateForm extends ViewFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareEntity() {
|
||||
// Do not prepare the entity while it is being added.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
parent::form($form, $form_state);
|
||||
|
||||
$form['#title'] = $this->t('Duplicate of @label', array('@label' => $this->entity->label()));
|
||||
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('View name'),
|
||||
'#required' => TRUE,
|
||||
'#size' => 32,
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $this->t('Duplicate of @label', array('@label' => $this->entity->label())),
|
||||
);
|
||||
$form['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#maxlength' => 128,
|
||||
'#machine_name' => array(
|
||||
'exists' => '\Drupal\views\Views::getView',
|
||||
'source' => array('label'),
|
||||
),
|
||||
'#default_value' => '',
|
||||
'#description' => $this->t('A unique machine-readable name for this View. It must only contain lowercase letters, numbers, and underscores.'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Duplicate'),
|
||||
'#submit' => array('::submitForm'),
|
||||
);
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for the 'clone' action.
|
||||
*
|
||||
* @param array $form
|
||||
* An associative array containing the structure of the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* A reference to a keyed array containing the current state of the form.
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->entity = $this->entity->createDuplicate();
|
||||
$this->entity->set('label', $form_state->getValue('label'));
|
||||
$this->entity->set('id', $form_state->getValue('id'));
|
||||
$this->entity->save();
|
||||
|
||||
// Redirect the user to the view admin form.
|
||||
$form_state->setRedirectUrl($this->entity->urlInfo('edit-form'));
|
||||
}
|
||||
|
||||
}
|
1185
core/modules/views_ui/src/ViewEditForm.php
Normal file
175
core/modules/views_ui/src/ViewFormBase.php
Normal file
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\ViewFormBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
/**
|
||||
* Base form for Views forms.
|
||||
*/
|
||||
abstract class ViewFormBase extends EntityForm {
|
||||
|
||||
/**
|
||||
* The name of the display used by the form.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $displayID;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init(FormStateInterface $form_state) {
|
||||
parent::init($form_state);
|
||||
|
||||
// @todo Remove the need for this.
|
||||
$form_state->loadInclude('views_ui', 'inc', 'admin');
|
||||
$form_state->set('view', $this->entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, $display_id = NULL) {
|
||||
if (isset($display_id) && $form_state->has('display_id') && ($display_id !== $form_state->get('display_id'))) {
|
||||
throw new \InvalidArgumentException('Mismatch between $form_state->get(\'display_id\') and $display_id.');
|
||||
}
|
||||
$this->displayID = $form_state->has('display_id') ? $form_state->get('display_id') : $display_id;
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareEntity() {
|
||||
// Determine the displays available for editing.
|
||||
if ($tabs = $this->getDisplayTabs($this->entity)) {
|
||||
if (empty($this->displayID)) {
|
||||
// If a display isn't specified, use the first one after sorting by
|
||||
// #weight.
|
||||
uasort($tabs, 'Drupal\Component\Utility\SortArray::sortByWeightProperty');
|
||||
foreach ($tabs as $id => $tab) {
|
||||
if (!isset($tab['#access']) || $tab['#access']) {
|
||||
$this->displayID = $id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If a display is specified, but we don't have access to it, return
|
||||
// an access denied page.
|
||||
if ($this->displayID && !isset($tabs[$this->displayID])) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
elseif ($this->displayID && (isset($tabs[$this->displayID]['#access']) && !$tabs[$this->displayID]['#access'])) {
|
||||
throw new AccessDeniedHttpException();
|
||||
}
|
||||
|
||||
}
|
||||
elseif ($this->displayID) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tabs for navigating across Displays when editing a View.
|
||||
*
|
||||
* This function can be called from hook_menu_local_tasks_alter() to implement
|
||||
* these tabs as secondary local tasks, or it can be called from elsewhere if
|
||||
* having them as secondary local tasks isn't desired. The caller is responsible
|
||||
* for setting the active tab's #active property to TRUE.
|
||||
*
|
||||
* @param $display_id
|
||||
* The display_id which is edited on the current request.
|
||||
*/
|
||||
public function getDisplayTabs(ViewUI $view) {
|
||||
$executable = $view->getExecutable();
|
||||
$executable->initDisplay();
|
||||
$display_id = $this->displayID;
|
||||
$tabs = array();
|
||||
|
||||
// Create a tab for each display.
|
||||
foreach ($view->get('display') as $id => $display) {
|
||||
// Get an instance of the display plugin, to make sure it will work in the
|
||||
// UI.
|
||||
$display_plugin = $executable->displayHandlers->get($id);
|
||||
if (empty($display_plugin)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tabs[$id] = array(
|
||||
'#theme' => 'menu_local_task',
|
||||
'#weight' => $display['position'],
|
||||
'#link' => array(
|
||||
'title' => $this->getDisplayLabel($view, $id),
|
||||
'localized_options' => array(),
|
||||
'url' => $view->urlInfo('edit-display-form')->setRouteParameter('display_id', $id),
|
||||
),
|
||||
);
|
||||
if (!empty($display['deleted'])) {
|
||||
$tabs[$id]['#link']['localized_options']['attributes']['class'][] = 'views-display-deleted-link';
|
||||
}
|
||||
if (isset($display['display_options']['enabled']) && !$display['display_options']['enabled']) {
|
||||
$tabs[$id]['#link']['localized_options']['attributes']['class'][] = 'views-display-disabled-link';
|
||||
}
|
||||
}
|
||||
|
||||
// If the default display isn't supposed to be shown, don't display its tab, unless it's the only display.
|
||||
if ((!$this->isDefaultDisplayShown($view) && $display_id != 'default') && count($tabs) > 1) {
|
||||
$tabs['default']['#access'] = FALSE;
|
||||
}
|
||||
|
||||
// Mark the display tab as red to show validation errors.
|
||||
$errors = $executable->validate();
|
||||
foreach ($view->get('display') as $id => $display) {
|
||||
if (!empty($errors[$id])) {
|
||||
// Always show the tab.
|
||||
$tabs[$id]['#access'] = TRUE;
|
||||
// Add a class to mark the error and a title to make a hover tip.
|
||||
$tabs[$id]['#link']['localized_options']['attributes']['class'][] = 'error';
|
||||
$tabs[$id]['#link']['localized_options']['attributes']['title'] = $this->t('This display has one or more validation errors.');
|
||||
}
|
||||
}
|
||||
|
||||
return $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether or not the default display should have its own tab on edit.
|
||||
*/
|
||||
public function isDefaultDisplayShown(ViewUI $view) {
|
||||
// Always show the default display for advanced users who prefer that mode.
|
||||
$advanced_mode = \Drupal::config('views.settings')->get('ui.show.master_display');
|
||||
// For other users, show the default display only if there are no others, and
|
||||
// hide it if there's at least one "real" display.
|
||||
$additional_displays = (count($view->getExecutable()->displayHandlers) == 1);
|
||||
|
||||
return $advanced_mode || $additional_displays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder function for overriding $display['display_title'].
|
||||
*
|
||||
* @todo Remove this function once editing the display title is possible.
|
||||
*/
|
||||
public function getDisplayLabel(ViewUI $view, $display_id, $check_changed = TRUE) {
|
||||
$display = $view->get('display');
|
||||
$title = $display_id == 'default' ? $this->t('Master') : $display[$display_id]['display_title'];
|
||||
$title = views_ui_truncate($title, 25);
|
||||
|
||||
if ($check_changed && !empty($view->changed_display[$display_id])) {
|
||||
$changed = '*';
|
||||
$title = $title . $changed;
|
||||
}
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
}
|
278
core/modules/views_ui/src/ViewListBuilder.php
Normal file
|
@ -0,0 +1,278 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\ViewListBuilder.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of view entities.
|
||||
*
|
||||
* @see \Drupal\views\Entity\View
|
||||
*/
|
||||
class ViewListBuilder extends ConfigEntityListBuilder {
|
||||
|
||||
/**
|
||||
* The views display plugin manager to use.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $displayManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity.manager')->getStorage($entity_type->id()),
|
||||
$container->get('plugin.manager.views.display')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ViewListBuilder object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage.
|
||||
* The entity storage class.
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $display_manager
|
||||
* The views display plugin manager to use.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, PluginManagerInterface $display_manager) {
|
||||
parent::__construct($entity_type, $storage);
|
||||
|
||||
$this->displayManager = $display_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load() {
|
||||
$entities = array(
|
||||
'enabled' => array(),
|
||||
'disabled' => array(),
|
||||
);
|
||||
foreach (parent::load() as $entity) {
|
||||
if ($entity->status()) {
|
||||
$entities['enabled'][] = $entity;
|
||||
}
|
||||
else {
|
||||
$entities['disabled'][] = $entity;
|
||||
}
|
||||
}
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $view) {
|
||||
$row = parent::buildRow($view);
|
||||
$display_paths = '';
|
||||
$separator = '';
|
||||
foreach ($this->getDisplayPaths($view) as $display_path) {
|
||||
$display_paths .= $separator . SafeMarkup::escape($display_path);
|
||||
$separator = ', ';
|
||||
}
|
||||
return array(
|
||||
'data' => array(
|
||||
'view_name' => array(
|
||||
'data' => array(
|
||||
'#theme' => 'views_ui_view_info',
|
||||
'#view' => $view,
|
||||
'#displays' => $this->getDisplaysList($view)
|
||||
),
|
||||
),
|
||||
'description' => array(
|
||||
'data' => array(
|
||||
'#markup' => SafeMarkup::checkPlain($view->get('description')),
|
||||
),
|
||||
'class' => array('views-table-filter-text-source'),
|
||||
),
|
||||
'tag' => $view->get('tag'),
|
||||
'path' => SafeMarkup::set($display_paths),
|
||||
'operations' => $row['operations'],
|
||||
),
|
||||
'title' => $this->t('Machine name: @name', array('@name' => $view->id())),
|
||||
'class' => array($view->status() ? 'views-ui-list-enabled' : 'views-ui-list-disabled'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
return array(
|
||||
'view_name' => array(
|
||||
'data' => $this->t('View name'),
|
||||
'class' => array('views-ui-name'),
|
||||
),
|
||||
'description' => array(
|
||||
'data' => $this->t('Description'),
|
||||
'class' => array('views-ui-description'),
|
||||
),
|
||||
'tag' => array(
|
||||
'data' => $this->t('Tag'),
|
||||
'class' => array('views-ui-tag'),
|
||||
),
|
||||
'path' => array(
|
||||
'data' => $this->t('Path'),
|
||||
'class' => array('views-ui-path'),
|
||||
),
|
||||
'operations' => array(
|
||||
'data' => $this->t('Operations'),
|
||||
'class' => array('views-ui-operations'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefaultOperations(EntityInterface $entity) {
|
||||
$operations = parent::getDefaultOperations($entity);
|
||||
|
||||
if ($entity->hasLinkTemplate('duplicate-form')) {
|
||||
$operations['duplicate'] = array(
|
||||
'title' => $this->t('Duplicate'),
|
||||
'weight' => 15,
|
||||
'url' => $entity->urlInfo('duplicate-form'),
|
||||
);
|
||||
}
|
||||
|
||||
// Add AJAX functionality to enable/disable operations.
|
||||
foreach (array('enable', 'disable') as $op) {
|
||||
if (isset($operations[$op])) {
|
||||
$operations[$op]['url'] = $entity->urlInfo($op);
|
||||
// Enable and disable operations should use AJAX.
|
||||
$operations[$op]['attributes']['class'][] = 'use-ajax';
|
||||
}
|
||||
}
|
||||
|
||||
return $operations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render() {
|
||||
$entities = $this->load();
|
||||
$list['#type'] = 'container';
|
||||
$list['#attributes']['id'] = 'views-entity-list';
|
||||
|
||||
$list['#attached']['library'][] = 'core/drupal.ajax';
|
||||
$list['#attached']['library'][] = 'views_ui/views_ui.listing';
|
||||
|
||||
$form['filters'] = array(
|
||||
'#type' => 'container',
|
||||
'#attributes' => array(
|
||||
'class' => array('table-filter', 'js-show'),
|
||||
),
|
||||
);
|
||||
|
||||
$list['filters']['text'] = array(
|
||||
'#type' => 'search',
|
||||
'#title' => $this->t('Filter'),
|
||||
'#title_display' => 'invisible',
|
||||
'#size' => 40,
|
||||
'#placeholder' => $this->t('Filter by view name or description'),
|
||||
'#attributes' => array(
|
||||
'class' => array('views-filter-text'),
|
||||
'data-table' => '.views-listing-table',
|
||||
'autocomplete' => 'off',
|
||||
'title' => $this->t('Enter a part of the view name or description to filter by.'),
|
||||
),
|
||||
);
|
||||
|
||||
$list['enabled']['heading']['#markup'] = '<h2>' . $this->t('Enabled', array(), array('context' => 'Plural')) . '</h2>';
|
||||
$list['disabled']['heading']['#markup'] = '<h2>' . $this->t('Disabled', array(), array('context' => 'Plural')) . '</h2>';
|
||||
foreach (array('enabled', 'disabled') as $status) {
|
||||
$list[$status]['#type'] = 'container';
|
||||
$list[$status]['#attributes'] = array('class' => array('views-list-section', $status));
|
||||
$list[$status]['table'] = array(
|
||||
'#type' => 'table',
|
||||
'#attributes' => array(
|
||||
'class' => array('views-listing-table'),
|
||||
),
|
||||
'#header' => $this->buildHeader(),
|
||||
'#rows' => array(),
|
||||
);
|
||||
foreach ($entities[$status] as $entity) {
|
||||
$list[$status]['table']['#rows'][$entity->id()] = $this->buildRow($entity);
|
||||
}
|
||||
}
|
||||
// @todo Use a placeholder for the entity label if this is abstracted to
|
||||
// other entity types.
|
||||
$list['enabled']['table']['#empty'] = $this->t('There are no enabled views.');
|
||||
$list['disabled']['table']['#empty'] = $this->t('There are no disabled views.');
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of displays included in the view.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $view
|
||||
* The view entity instance to get a list of displays for.
|
||||
*
|
||||
* @return array
|
||||
* An array of display types that this view includes.
|
||||
*/
|
||||
protected function getDisplaysList(EntityInterface $view) {
|
||||
$displays = array();
|
||||
foreach ($view->get('display') as $display) {
|
||||
$definition = $this->displayManager->getDefinition($display['display_plugin']);
|
||||
if (!empty($definition['admin'])) {
|
||||
// Cast the admin label to a string since it is an object.
|
||||
// @see \Drupal\Core\StringTranslation\TranslationWrapper
|
||||
$displays[] = (string) $definition['admin'];
|
||||
}
|
||||
}
|
||||
|
||||
sort($displays);
|
||||
return $displays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of paths assigned to the view.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $view
|
||||
* The view entity.
|
||||
*
|
||||
* @return array
|
||||
* An array of paths for this view.
|
||||
*/
|
||||
protected function getDisplayPaths(EntityInterface $view) {
|
||||
$all_paths = array();
|
||||
$executable = $view->getExecutable();
|
||||
$executable->initDisplay();
|
||||
foreach ($executable->displayHandlers as $display) {
|
||||
if ($display->hasPath()) {
|
||||
$path = $display->getPath();
|
||||
if ($view->status() && strpos($path, '%') === FALSE) {
|
||||
// @todo Views should expect and store a leading /. See:
|
||||
// https://www.drupal.org/node/2423913
|
||||
$all_paths[] = \Drupal::l('/' . $path, Url::fromUserInput('/' . $path));
|
||||
}
|
||||
else {
|
||||
$all_paths[] = SafeMarkup::checkPlain('/' . $path);
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_unique($all_paths);
|
||||
}
|
||||
|
||||
}
|
110
core/modules/views_ui/src/ViewPreviewForm.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views_ui\ViewPreviewForm.
|
||||
*/
|
||||
|
||||
namespace Drupal\views_ui;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Form controller for the Views preview form.
|
||||
*/
|
||||
class ViewPreviewForm extends ViewFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$view = $this->entity;
|
||||
|
||||
$form['#prefix'] = '<div id="views-preview-wrapper" class="views-preview-wrapper views-admin clearfix">';
|
||||
$form['#suffix'] = '</div>';
|
||||
$form['#id'] = 'views-ui-preview-form';
|
||||
|
||||
$form_state->disableCache();
|
||||
|
||||
$form['controls']['#attributes'] = array('class' => array('clearfix'));
|
||||
|
||||
$form['controls']['title'] = array(
|
||||
'#prefix' => '<h2 class="view-preview-form__title">',
|
||||
'#markup' => $this->t('Preview'),
|
||||
'#suffix' => '</h2>',
|
||||
);
|
||||
|
||||
// Add a checkbox controlling whether or not this display auto-previews.
|
||||
$form['controls']['live_preview'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#id' => 'edit-displays-live-preview',
|
||||
'#title' => $this->t('Auto preview'),
|
||||
'#default_value' => \Drupal::config('views.settings')->get('ui.always_live_preview'),
|
||||
);
|
||||
|
||||
// Add the arguments textfield
|
||||
$form['controls']['view_args'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Preview with contextual filters:'),
|
||||
'#description' => $this->t('Separate contextual filter values with a "/". For example, %example.', array('%example' => '40/12/10')),
|
||||
'#id' => 'preview-args',
|
||||
);
|
||||
|
||||
$args = array();
|
||||
if (!$form_state->isValueEmpty('view_args')) {
|
||||
$args = explode('/', $form_state->getValue('view_args'));
|
||||
}
|
||||
|
||||
$user_input = $form_state->getUserInput();
|
||||
if ($form_state->get('show_preview') || !empty($user_input['js'])) {
|
||||
$form['preview'] = array(
|
||||
'#weight' => 110,
|
||||
'#theme_wrappers' => array('container'),
|
||||
'#attributes' => array('id' => 'views-live-preview', 'class' => 'views-live-preview'),
|
||||
'preview' => $view->renderPreview($this->displayID, $args),
|
||||
);
|
||||
}
|
||||
$uri = $view->urlInfo('preview-form');
|
||||
$uri->setRouteParameter('display_id', $this->displayID);
|
||||
$form['#action'] = $uri->toString();
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$view = $this->entity;
|
||||
return array(
|
||||
'#attributes' => array(
|
||||
'id' => 'preview-submit-wrapper',
|
||||
'class' => array('preview-submit-wrapper')
|
||||
),
|
||||
'button' => array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Update preview'),
|
||||
'#attributes' => array('class' => array('arguments-preview')),
|
||||
'#submit' => array('::submitPreview'),
|
||||
'#id' => 'preview-submit',
|
||||
'#ajax' => array(
|
||||
'url' => Url::fromRoute('entity.view.preview_form', ['view' => $view->id(), 'display_id' => $this->displayID]),
|
||||
'wrapper' => 'views-preview-wrapper',
|
||||
'event' => 'click',
|
||||
'progress' => array('type' => 'fullscreen'),
|
||||
'method' => 'replaceWith',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for the Preview button.
|
||||
*/
|
||||
public function submitPreview($form, FormStateInterface $form_state) {
|
||||
$form_state->set('show_preview', TRUE);
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
}
|
1339
core/modules/views_ui/src/ViewUI.php
Normal file
15
core/modules/views_ui/templates/views-ui-container.html.twig
Normal file
|
@ -0,0 +1,15 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for a generic views UI container/wrapper.
|
||||
*
|
||||
* Available variables:
|
||||
* - attributes: HTML attributes to apply to the container element.
|
||||
* - children: The remaining elements such as dropbuttons and tabs.
|
||||
*
|
||||
* @see template_preprocess_views_ui_container()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
<div{{ attributes }}>{{ children }}</div>
|
|
@ -0,0 +1,35 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for each "box" on the display query edit screen.
|
||||
*
|
||||
* Available variables:
|
||||
* - attributes: HTML attributes to apply to the container element.
|
||||
* - actions: Action links such as "Add", "And/Or, Rearrange" for the content.
|
||||
* - title: The title of the bucket, e.g. "Fields", "Filter Criteria", etc.
|
||||
* - content: Content items such as fields or settings in this container.
|
||||
* - name: The name of the bucket, e.g. "Fields", "Filter Criteria", etc.
|
||||
* - overridden: A boolean indicating the setting has been overridden from the
|
||||
* default.
|
||||
*
|
||||
* @see template_preprocess_views_ui_display_tab_bucket()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{%
|
||||
set classes = [
|
||||
'views-ui-display-tab-bucket',
|
||||
name ? name|clean_class,
|
||||
overridden ? 'overridden',
|
||||
]
|
||||
%}
|
||||
<div{{ attributes.addClass(classes) }}>
|
||||
{% if title -%}
|
||||
<h3 class="views-ui-display-tab-bucket__title">{{ title }}</h3>
|
||||
{%- endif %}
|
||||
{% if actions -%}
|
||||
{{ actions }}
|
||||
{%- endif %}
|
||||
{{ content }}
|
||||
</div>
|
|
@ -0,0 +1,37 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for Views UI display tab settings.
|
||||
*
|
||||
* Template for each row inside the "boxes" on the display query edit screen.
|
||||
*
|
||||
* Available variables:
|
||||
* - attributes: HTML attributes such as class for the container.
|
||||
* - description: The description or label for this setting.
|
||||
* - settings_links: A list of links for this setting.
|
||||
* - defaulted: A boolean indicating the setting is in its default state.
|
||||
* - overridden: A boolean indicating the setting has been overridden from the
|
||||
* default.
|
||||
*
|
||||
* @see template_preprocess_views_ui_display_tab_setting()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{%
|
||||
set classes = [
|
||||
'views-display-setting',
|
||||
'clearfix',
|
||||
'views-ui-display-tab-setting',
|
||||
defaulted ? 'defaulted',
|
||||
overridden ? 'overridden',
|
||||
]
|
||||
%}
|
||||
<div{{ attributes.addClass(classes) }}>
|
||||
{% if description -%}
|
||||
<span class="label">{{ description }}</span>
|
||||
{%- endif %}
|
||||
{% if settings_links %}
|
||||
{{ settings_links|safe_join('<span class="label"> | </span>') }}
|
||||
{% endif %}
|
||||
</div>
|
|
@ -0,0 +1,67 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for exposed filter form.
|
||||
*
|
||||
* Available variables:
|
||||
* - form_description: The exposed filter's description.
|
||||
* - expose_button: The button to toggle the expose filter form.
|
||||
* - group_button: Toggle options between single and grouped filters.
|
||||
* - required: A checkbox to require this filter or not.
|
||||
* - label: A filter label input field.
|
||||
* - description: A filter description field.
|
||||
* - operator: The operators for how the filters value should be treated.
|
||||
* - #type: The operator type.
|
||||
* - value: The filters available values.
|
||||
* - use_operator: Checkbox to allow the user to expose the operator.
|
||||
* - more: A details element for additional field exposed filter fields.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{{ form.form_description }}
|
||||
{{ form.expose_button }}
|
||||
{{ form.group_button }}
|
||||
{{ form.required }}
|
||||
{{ form.label }}
|
||||
{{ form.description }}
|
||||
|
||||
{{ form.operator }}
|
||||
{{ form.value }}
|
||||
|
||||
{% if form.use_operator %}
|
||||
<div class="views-left-40">
|
||||
{{ form.use_operator }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{#
|
||||
Collect a list of elements printed to exclude when printing the
|
||||
remaining elements.
|
||||
#}
|
||||
{% set remaining_form = form|without(
|
||||
'form_description',
|
||||
'expose_button',
|
||||
'group_button',
|
||||
'required',
|
||||
'label',
|
||||
'description',
|
||||
'operator',
|
||||
'value',
|
||||
'use_operator',
|
||||
'more'
|
||||
)
|
||||
%}
|
||||
|
||||
{#
|
||||
Only output the right column markup if there's a left column to begin with.
|
||||
#}
|
||||
{% if form.operator['#type'] %}
|
||||
<div class="views-right-60">
|
||||
{{ remaining_form }}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ remaining_form }}
|
||||
{% endif %}
|
||||
|
||||
{{ form.more }}
|
|
@ -0,0 +1,27 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for Views UI rearrange filter form.
|
||||
*
|
||||
* Available variables:
|
||||
* - form: A render element representing the form.
|
||||
* - grouping: A flag whether or not there is more than one group.
|
||||
* - ungroupable_table: The ungroupable filter table.
|
||||
* - table: The groupable filter table.
|
||||
*
|
||||
* @see template_preprocess_views_ui_rearrange_filter_form()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{{ form.override }}
|
||||
<div class="scroll" data-drupal-views-scroll>
|
||||
{% if grouping %}
|
||||
{{ form.filter_groups.operator }}
|
||||
{% else %}
|
||||
{{ form.filter_groups.groups.0 }}
|
||||
{% endif %}
|
||||
{{ ungroupable_table }}
|
||||
{{ table }}
|
||||
</div>
|
||||
{{ form|without('override', 'filter_groups', 'remove_groups', 'filters') }}
|
|
@ -0,0 +1,18 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default template for the settings of a table style views display.
|
||||
*
|
||||
* Available variables:
|
||||
* - table: A table of options for each field in this display.
|
||||
* - form: Any remaining form fields not included in the table.
|
||||
* - description_markup: An overview for the settings of this display.
|
||||
*
|
||||
* @see template_preprocess_views_ui_style_plugin_table()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{{ form.description_markup }}
|
||||
{{ table }}
|
||||
{{ form }}
|
28
core/modules/views_ui/templates/views-ui-view-info.html.twig
Normal file
|
@ -0,0 +1,28 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for basic administrative info about a View.
|
||||
*
|
||||
* Available variables:
|
||||
* - displays: List of displays.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
<h3 class="views-ui-view-title views-table-filter-text-source">{{ view.label }}</h3>
|
||||
<div class="views-ui-view-displays">
|
||||
{% if displays %}
|
||||
{% trans %}
|
||||
Display
|
||||
{% plural displays %}
|
||||
Displays
|
||||
{% endtrans %}:
|
||||
<em>{{ displays|safe_join(', ') }}</em>
|
||||
{% else %}
|
||||
{{ 'None'|t }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="views-ui-view-machine-name">
|
||||
{{ 'Machine name:'|t }}
|
||||
<span class="views-table-filter-text-source">{{ view.id }}</span>
|
||||
</div>
|
|
@ -0,0 +1,20 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for a views UI preview section.
|
||||
*
|
||||
* Available variables:
|
||||
* - title: The human readable section title.
|
||||
* - links: A list of contextual links.
|
||||
* - content: The content for this section preview.
|
||||
*
|
||||
* @see template_preprocess_views_ui_view_preview_section()
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
<h1 class="section-title">{{ title }}</h1>
|
||||
{% if links %}
|
||||
<div class="contextual">{{ links }}</div>
|
||||
{% endif %}
|
||||
<div class="preview-section">{{ content }}</div>
|
|
@ -0,0 +1,187 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- user
|
||||
id: sa_contrib_2013_035
|
||||
label: SA_CONTRIB_2013_035
|
||||
module: views
|
||||
description: ''
|
||||
tag: '<script>alert("foo");</script>, <marquee>test</marquee>'
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: none
|
||||
options:
|
||||
offset: 0
|
||||
style:
|
||||
type: default
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
uses_fields: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
fields:
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: '<marquee>test</marquee>'
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: false
|
||||
ellipsis: false
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
title_1:
|
||||
id: title_1
|
||||
table: node_field_data
|
||||
field: title
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: '<script>alert("XSS")</script>'
|
||||
label: ''
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: false
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
plugin_id: field
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
filters: { }
|
||||
sorts: { }
|
||||
header:
|
||||
area:
|
||||
id: area
|
||||
table: views
|
||||
field: area
|
||||
plugin_id: text
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments: { }
|
||||
title: '<marquee>VIEWS TITLE</marquee>'
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 2
|
||||
display_options:
|
||||
path: foobar
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* @file
|
||||
* Just a placeholder file for the test.
|
||||
*/
|
|
@ -0,0 +1,8 @@
|
|||
name: 'Views UI Test'
|
||||
type: module
|
||||
description: 'Test module for Views UI.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- views_ui
|
|
@ -0,0 +1,4 @@
|
|||
views_ui_test.test:
|
||||
css:
|
||||
component:
|
||||
css/views_ui_test.test.css: {}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Helper module for Views UI tests.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_preview_info_alter().
|
||||
*
|
||||
* Add a row count row to the live preview area.
|
||||
*/
|
||||
function views_ui_test_views_preview_info_alter(&$rows, $view) {
|
||||
$data = ['#markup' => t('Test row count')];
|
||||
$data['#attached']['library'][] = 'views_ui_test/views_ui_test.test';
|
||||
$rows['query'][] = [['data' => $data], count($view->result)];
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\views_ui\Unit\Form\Ajax\RearrangeFilterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\views_ui\Unit\Form\Ajax;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\views_ui\Form\Ajax\RearrangeFilter;
|
||||
|
||||
/**
|
||||
* Unit tests for Views UI module functions.
|
||||
*
|
||||
* @group views_ui
|
||||
*/
|
||||
class RearrangeFilterTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests static methods.
|
||||
*/
|
||||
public function testStaticMethods() {
|
||||
// Test the RearrangeFilter::arrayKeyPlus method.
|
||||
$original = array(0 => 'one', 1 => 'two', 2 => 'three');
|
||||
$expected = array(1 => 'one', 2 => 'two', 3 => 'three');
|
||||
$this->assertSame(RearrangeFilter::arrayKeyPlus($original), $expected);
|
||||
}
|
||||
|
||||
}
|
156
core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\views_ui\Unit\ViewListBuilderTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\views_ui\Unit;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\views\Entity\View;
|
||||
use Drupal\views\ViewExecutableFactory;
|
||||
use Drupal\views_ui\ViewListBuilder;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\views_ui\ViewListBuilder
|
||||
* @group views_ui
|
||||
*/
|
||||
class ViewListBuilderTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests the listing of displays on a views list builder.
|
||||
*
|
||||
* @see \Drupal\views_ui\ViewListBuilder::getDisplaysList()
|
||||
* @covers ::buildRow
|
||||
*/
|
||||
public function testBuildRowEntityList() {
|
||||
$storage = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigEntityStorage')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$display_manager = $this->getMockBuilder('\Drupal\views\Plugin\ViewsPluginManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$display_manager->expects($this->any())
|
||||
->method('getDefinition')
|
||||
->will($this->returnValueMap(array(
|
||||
array(
|
||||
'default',
|
||||
TRUE,
|
||||
array(
|
||||
'id' => 'default',
|
||||
'title' => 'Master',
|
||||
'theme' => 'views_view',
|
||||
'no_ui' => TRUE,
|
||||
'admin' => '',
|
||||
)
|
||||
),
|
||||
array(
|
||||
'page',
|
||||
TRUE,
|
||||
array(
|
||||
'id' => 'page',
|
||||
'title' => 'Page',
|
||||
'uses_menu_links' => TRUE,
|
||||
'uses_route' => TRUE,
|
||||
'contextual_links_locations' => array('page'),
|
||||
'theme' => 'views_view',
|
||||
'admin' => 'Page admin label',
|
||||
)
|
||||
),
|
||||
array(
|
||||
'embed',
|
||||
TRUE,
|
||||
array(
|
||||
'id' => 'embed',
|
||||
'title' => 'embed',
|
||||
'theme' => 'views_view',
|
||||
'admin' => 'Embed admin label',
|
||||
)
|
||||
),
|
||||
)));
|
||||
|
||||
|
||||
$default_display = $this->getMock('Drupal\views\Plugin\views\display\DefaultDisplay',
|
||||
array('initDisplay'),
|
||||
array(array(), 'default', $display_manager->getDefinition('default'))
|
||||
);
|
||||
$route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
|
||||
$state = $this->getMock('\Drupal\Core\State\StateInterface');
|
||||
$menu_storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
|
||||
$page_display = $this->getMock('Drupal\views\Plugin\views\display\Page',
|
||||
array('initDisplay', 'getPath'),
|
||||
array(array(), 'default', $display_manager->getDefinition('page'), $route_provider, $state, $menu_storage)
|
||||
);
|
||||
$page_display->expects($this->any())
|
||||
->method('getPath')
|
||||
->will($this->returnValue('test_page'));
|
||||
|
||||
$embed_display = $this->getMock('Drupal\views\Plugin\views\display\Embed', array('initDisplay'),
|
||||
array(array(), 'default', $display_manager->getDefinition('embed'))
|
||||
);
|
||||
|
||||
$values = array();
|
||||
$values['status'] = FALSE;
|
||||
$values['display']['default']['id'] = 'default';
|
||||
$values['display']['default']['display_title'] = 'Display';
|
||||
$values['display']['default']['display_plugin'] = 'default';
|
||||
|
||||
$values['display']['page_1']['id'] = 'page_1';
|
||||
$values['display']['page_1']['display_title'] = 'Page 1';
|
||||
$values['display']['page_1']['display_plugin'] = 'page';
|
||||
$values['display']['page_1']['display_options']['path'] = 'test_page';
|
||||
|
||||
$values['display']['embed']['id'] = 'embed';
|
||||
$values['display']['embed']['display_title'] = 'Embedded';
|
||||
$values['display']['embed']['display_plugin'] = 'embed';
|
||||
|
||||
$display_manager->expects($this->any())
|
||||
->method('createInstance')
|
||||
->will($this->returnValueMap(array(
|
||||
array('default', $values['display']['default'], $default_display),
|
||||
array('page', $values['display']['page_1'], $page_display),
|
||||
array('embed', $values['display']['embed'], $embed_display),
|
||||
)));
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$user = $this->getMock('Drupal\Core\Session\AccountInterface');
|
||||
$request_stack = new RequestStack();
|
||||
$request_stack->push(new Request());
|
||||
$views_data = $this->getMockBuilder('Drupal\views\ViewsData')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
|
||||
$executable_factory = new ViewExecutableFactory($user, $request_stack, $views_data, $route_provider);
|
||||
$container->set('views.executable', $executable_factory);
|
||||
$container->set('plugin.manager.views.display', $display_manager);
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
// Setup a view list builder with a mocked buildOperations method,
|
||||
// because t() is called on there.
|
||||
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||
$view_list_builder = new TestViewListBuilder($entity_type, $storage, $display_manager);
|
||||
$view_list_builder->setStringTranslation($this->getStringTranslationStub());
|
||||
|
||||
$view = new View($values, 'view');
|
||||
|
||||
$row = $view_list_builder->buildRow($view);
|
||||
|
||||
$this->assertEquals(array('Embed admin label', 'Page admin label'), $row['data']['view_name']['data']['#displays'], 'Wrong displays got added to view list');
|
||||
$this->assertEquals($row['data']['path'], '/test_page', 'The path of the page display is not added.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestViewListBuilder extends ViewListBuilder {
|
||||
|
||||
public function buildOperations(EntityInterface $entity) {
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
148
core/modules/views_ui/tests/src/Unit/ViewUIObjectTest.php
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\views_ui\Unit\ViewUIObjectTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\views_ui\Unit;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\views\Entity\View;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views_ui\ViewUI;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\views_ui\ViewUI
|
||||
* @group views_ui
|
||||
*/
|
||||
class ViewUIObjectTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests entity method decoration.
|
||||
*/
|
||||
public function testEntityDecoration() {
|
||||
$method_args = array();
|
||||
$method_args['setOriginalId'] = array(12);
|
||||
$method_args['setStatus'] = array(TRUE);
|
||||
$method_args['enforceIsNew'] = array(FALSE);
|
||||
$method_args['label'] = array(LanguageInterface::LANGCODE_NOT_SPECIFIED);
|
||||
|
||||
$reflection = new \ReflectionClass('Drupal\Core\Config\Entity\ConfigEntityInterface');
|
||||
$interface_methods = array();
|
||||
foreach ($reflection->getMethods() as $reflection_method) {
|
||||
$interface_methods[] = $reflection_method->getName();
|
||||
|
||||
// EntityInterface::isNew() is missing from the list of methods, because it
|
||||
// calls id(), which breaks the ->expect($this->once()) call. Call it later.
|
||||
// EntityInterface::isSyncing() is only called during syncing process.
|
||||
// EntityInterface::isUninstalling() is only called during uninstallation
|
||||
// process. EntityInterface::getConfigDependencyName() and
|
||||
// ConfigEntityInterface::calculateDependencies() are only used for
|
||||
// dependency management.
|
||||
if (!in_array($reflection_method->getName(), ['isNew', 'isSyncing', 'isUninstalling', 'getConfigDependencyKey', 'getConfigDependencyName', 'calculateDependencies'])) {
|
||||
if (count($reflection_method->getParameters()) == 0) {
|
||||
$method_args[$reflection_method->getName()] = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$storage = $this->getMock('Drupal\views\Entity\View', $interface_methods, array(array(), 'view'));
|
||||
$executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
|
||||
->disableOriginalConstructor()
|
||||
->setConstructorArgs(array($storage))
|
||||
->getMock();
|
||||
$storage->set('executable', $executable);
|
||||
|
||||
$view_ui = new ViewUI($storage);
|
||||
|
||||
foreach ($method_args as $method => $args) {
|
||||
$method_mock = $storage->expects($this->once())
|
||||
->method($method);
|
||||
foreach ($args as $arg) {
|
||||
$method_mock->with($this->equalTo($arg));
|
||||
}
|
||||
call_user_func_array(array($view_ui, $method), $args);
|
||||
}
|
||||
|
||||
$storage->expects($this->once())
|
||||
->method('isNew');
|
||||
$view_ui->isNew();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the isLocked method.
|
||||
*/
|
||||
public function testIsLocked() {
|
||||
$storage = $this->getMock('Drupal\views\Entity\View', array(), array(array(), 'view'));
|
||||
$executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
|
||||
->disableOriginalConstructor()
|
||||
->setConstructorArgs(array($storage))
|
||||
->getMock();
|
||||
$storage->set('executable', $executable);
|
||||
$account = $this->getMock('Drupal\Core\Session\AccountInterface');
|
||||
$account->expects($this->exactly(2))
|
||||
->method('id')
|
||||
->will($this->returnValue(1));
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('current_user', $account);
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$view_ui = new ViewUI($storage);
|
||||
|
||||
// A view_ui without a lock object is not locked.
|
||||
$this->assertFalse($view_ui->isLocked());
|
||||
|
||||
// Set the lock object with a different owner than the mocked account above.
|
||||
$lock = (object) array(
|
||||
'owner' => 2,
|
||||
'data' => array(),
|
||||
'updated' => (int) $_SERVER['REQUEST_TIME'],
|
||||
);
|
||||
$view_ui->lock = $lock;
|
||||
$this->assertTrue($view_ui->isLocked());
|
||||
|
||||
// Set a different lock object with the same object as the mocked account.
|
||||
$lock = (object) array(
|
||||
'owner' => 1,
|
||||
'data' => array(),
|
||||
'updated' => (int) $_SERVER['REQUEST_TIME'],
|
||||
);
|
||||
$view_ui->lock = $lock;
|
||||
$this->assertFalse($view_ui->isLocked());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests serialization of the ViewUI object.
|
||||
*/
|
||||
public function testSerialization() {
|
||||
// Set a container so the DependencySerializationTrait has it.
|
||||
$container = new ContainerBuilder();
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$storage = new View([], 'view');
|
||||
$executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
|
||||
->disableOriginalConstructor()
|
||||
->setConstructorArgs([$storage])
|
||||
->getMock();
|
||||
$storage->set('executable', $executable);
|
||||
|
||||
$view_ui = new ViewUI($storage);
|
||||
|
||||
// Make sure the executable is returned before serializing.
|
||||
$this->assertInstanceOf('Drupal\views\ViewExecutable', $view_ui->getExecutable());
|
||||
|
||||
$serialized = serialize($view_ui);
|
||||
|
||||
// Make sure the ViewExecutable class is not found in the serialized string.
|
||||
$this->assertSame(strpos($serialized, '"Drupal\views\ViewExecutable"'), FALSE);
|
||||
|
||||
$unserialized = unserialize($serialized);
|
||||
$this->assertInstanceOf('Drupal\views_ui\ViewUI', $unserialized);
|
||||
}
|
||||
|
||||
}
|
9
core/modules/views_ui/views_ui.info.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
name: 'Views UI'
|
||||
type: module
|
||||
description: 'Administrative interface for Views.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
configure: entity.view.collection
|
||||
dependencies:
|
||||
- views
|
35
core/modules/views_ui/views_ui.libraries.yml
Normal file
|
@ -0,0 +1,35 @@
|
|||
views_ui.admin:
|
||||
version: VERSION
|
||||
js:
|
||||
js/ajax.js: {}
|
||||
js/dialog.views.js: {}
|
||||
js/views-admin.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
- core/jquery.once
|
||||
- core/jquery.form
|
||||
- core/drupal.ajax
|
||||
- core/drupal.dropbutton
|
||||
- views/views.ajax
|
||||
- views_ui/admin.styling
|
||||
|
||||
views_ui.listing:
|
||||
version: VERSION
|
||||
js:
|
||||
js/views_ui.listing.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/jquery.once
|
||||
- views_ui/admin.styling
|
||||
|
||||
admin.styling:
|
||||
version: VERSION
|
||||
css:
|
||||
component:
|
||||
css/views_ui.admin.css: {}
|
||||
theme:
|
||||
css/views_ui.admin.theme.css: {}
|
||||
css/views_ui.contextual.css: {}
|
5
core/modules/views_ui/views_ui.links.action.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
views_add_local_action:
|
||||
route_name: views_ui.add
|
||||
title: 'Add new view'
|
||||
appears_on:
|
||||
- entity.view.collection
|
9
core/modules/views_ui/views_ui.links.contextual.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
entity.view.edit_form:
|
||||
title: 'Edit view'
|
||||
route_name: entity.view.edit_form
|
||||
group: entity.view.edit_form
|
||||
|
||||
entity.view.preview_form:
|
||||
title: 'Preview view'
|
||||
route_name: entity.view.preview_form
|
||||
group: entity.view.preview_form
|
10
core/modules/views_ui/views_ui.links.menu.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
entity.view.collection:
|
||||
title: Views
|
||||
parent: system.admin_structure
|
||||
description: 'Manage customized lists of content.'
|
||||
route_name: entity.view.collection
|
||||
views_ui.reports_plugins:
|
||||
title: 'Views plugins'
|
||||
parent: system.admin_reports
|
||||
description: 'Overview of plugins used in all views.'
|
||||
route_name: views_ui.reports_plugins
|
30
core/modules/views_ui/views_ui.links.task.yml
Normal file
|
@ -0,0 +1,30 @@
|
|||
views_ui.settings_tab:
|
||||
route_name: views_ui.settings_basic
|
||||
title: Settings
|
||||
base_route: entity.view.collection
|
||||
|
||||
views_ui.settings_basic_tab:
|
||||
route_name: views_ui.settings_basic
|
||||
title: Basic
|
||||
parent_id: views_ui.settings_tab
|
||||
|
||||
views_ui.settings_advanced_tab:
|
||||
route_name: views_ui.settings_advanced
|
||||
title: Advanced
|
||||
parent_id: views_ui.settings_tab
|
||||
weight: 10
|
||||
|
||||
views_ui.list_tab:
|
||||
route_name: entity.view.collection
|
||||
title: List
|
||||
base_route: entity.view.collection
|
||||
|
||||
views_ui.reports_fields:
|
||||
route_name: views_ui.reports_fields
|
||||
title: 'Used in views'
|
||||
base_route: entity.field_storage_config.collection
|
||||
|
||||
entity.view.edit_form:
|
||||
title: 'Edit'
|
||||
route_name: entity.view.edit_form
|
||||
base_route: entity.view.edit_form
|