Newer
Older
<?php
/**
* @file
* Definition of Drupal\views_ui\ViewUI.
namespace Drupal\views_ui;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Timer;
Angie Byron
committed
use Drupal\Component\Utility\Xss;
use Drupal\views\Views;
catch
committed
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\views\ViewExecutable;
Angie Byron
committed
use Drupal\Core\Database\Database;
catch
committed
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\Session\AccountInterface;
Angie Byron
committed
use Drupal\views\Plugin\views\query\Sql;
use Drupal\views\Entity\View;
use Drupal\views\ViewStorageInterface;
/**
* Stores UI related temporary settings.
*/
class ViewUI implements ViewStorageInterface {
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* Indicates if a view is currently being edited.
*
* @var bool
*/
public $editing = FALSE;
/**
* Stores an array of displays that have been changed.
*
* @var array
*/
public $changed_display;
/**
* How long the view takes to build.
*
* @var int
*/
public $build_time;
/**
* How long the view takes to render.
*
* @var int
*/
public $render_time;
/**
* How long the view takes to execute.
*
* @var int
*/
public $execute_time;
/**
* If this view is locked for editing.
*
Alex Pott
committed
* If this view is locked it will contain the result of
* \Drupal\user\TempStore::getMetadata(). Which can be a stdClass or NULL.
*
* @var stdClass
Alex Pott
committed
public $lock;
/**
* If this view has been changed.
*
* @var bool
*/
public $changed;
/**
* Stores options temporarily while editing.
*
* @var array
*/
public $temporary_options;
/**
* Stores a stack of UI forms to display.
*
* @var array
*/
public $stack;
/**
* Is the view runned in a context of the preview in the admin interface.
*
* @var bool
*/
public $live_preview;
public $renderPreview = FALSE;
/**
* The View storage object.
Alex Pott
committed
* @var \Drupal\views\ViewStorageInterface
*/
protected $storage;
/**
* The View executable object.
* @var \Drupal\views\ViewExecutable
*/
protected $executable;
/**
* Stores a list of database queries run beside the main one from views.
*
* @var array
*
* @see \Drupal\Core\Database\Log
*/
protected $additionalQueries;
Angie Byron
committed
/**
* Contains an array of form keys and their respective classes.
*
* @var array
*/
public static $forms = array(
Angie Byron
committed
'add-handler' => '\Drupal\views_ui\Form\Ajax\AddItem',
Angie Byron
committed
'analyze' => '\Drupal\views_ui\Form\Ajax\Analyze',
Angie Byron
committed
'handler' => '\Drupal\views_ui\Form\Ajax\ConfigHandler',
'handler-extra' => '\Drupal\views_ui\Form\Ajax\ConfigHandlerExtra',
'handler-group' => '\Drupal\views_ui\Form\Ajax\ConfigHandlerGroup',
Angie Byron
committed
'display' => '\Drupal\views_ui\Form\Ajax\Display',
'edit-details' => '\Drupal\views_ui\Form\Ajax\EditDetails',
'rearrange' => '\Drupal\views_ui\Form\Ajax\Rearrange',
'rearrange-filter' => '\Drupal\views_ui\Form\Ajax\RearrangeFilter',
'reorder-displays' => '\Drupal\views_ui\Form\Ajax\ReorderDisplays',
);
/**
* Whether the config is being created, updated or deleted through the
* import process.
*
* @var bool
*/
private $isSyncing = FALSE;
/**
* Whether the config is being deleted through the uninstall process.
*
* @var bool
*/
private $isUninstalling = FALSE;
/**
* Constructs a View UI object.
* @param \Drupal\views\ViewStorageInterface $storage
* The View storage object to wrap.
*/
public function __construct(ViewStorageInterface $storage, ViewExecutable $executable = NULL) {
$this->entityType = 'view';
$this->storage = $storage;
if (!isset($executable)) {
$executable = Views::executableFactory()->get($this);
}
$this->executable = $executable;
}
/**
* Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::get().
*/
public function get($property_name, $langcode = NULL) {
if (property_exists($this->storage, $property_name)) {
return $this->storage->get($property_name, $langcode);
}
return isset($this->{$property_name}) ? $this->{$property_name} : NULL;
}
Angie Byron
committed
/**
* Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::setStatus().
*/
public function setStatus($status) {
return $this->storage->setStatus($status);
}
/**
* Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::set().
*/
catch
committed
public function set($property_name, $value, $notify = TRUE) {
if (property_exists($this->storage, $property_name)) {
$this->storage->set($property_name, $value);
}
else {
$this->{$property_name} = $value;
}
}
public static function getDefaultAJAXMessage() {
return '<div class="message">' . t("Click on an item to edit that item's details.") . '</div>';
}
/**
* {@inheritdoc}
*/
public function setSyncing($syncing) {
$this->isSyncing = $syncing;
}
/**
* {@inheritdoc}
*/
public function setUninstalling($isUninstalling) {
$this->isUninstalling = $isUninstalling;
}
/**
* {@inheritdoc}
*/
public function isSyncing() {
return $this->isSyncing;
}
/**
* {@inheritdoc}
*/
public function isUninstalling() {
return $this->isUninstalling;
}
/**
* Basic submit handler applicable to all 'standard' forms.
*
* This submit handler determines whether the user wants the submitted changes
* to apply to the default display or to the current display, and dispatches
* control appropriately.
*/
public function standardSubmit($form, &$form_state) {
// Determine whether the values the user entered are intended to apply to
// the current display or the default display.
list($was_defaulted, $is_defaulted, $revert) = $this->getOverrideValues($form, $form_state);
// Based on the user's choice in the display dropdown, determine which display
// these changes apply to.
if ($revert) {
// If it's revert just change the override and return.
$display = &$this->executable->displayHandlers->get($form_state['display_id']);
$display->optionsOverride($form, $form_state);
// Don't execute the normal submit handling but still store the changed view into cache.
Alex Pott
committed
$this->cacheSet();
return;
}
elseif ($was_defaulted === $is_defaulted) {
// We're not changing which display these form values apply to.
// Run the regular submit handler for this form.
}
elseif ($was_defaulted && !$is_defaulted) {
// We were using the default display's values, but we're now overriding
// the default display and saving values specific to this display.
$display = &$this->executable->displayHandlers->get($form_state['display_id']);
// optionsOverride toggles the override of this section.
$display->optionsOverride($form, $form_state);
$display->submitOptionsForm($form, $form_state);
}
elseif (!$was_defaulted && $is_defaulted) {
// We used to have an override for this display, but the user now wants
// to go back to the default display.
// Overwrite the default display with the current form values, and make
// the current display use the new default values.
$display = &$this->executable->displayHandlers->get($form_state['display_id']);
// optionsOverride toggles the override of this section.
$display->optionsOverride($form, $form_state);
$display->submitOptionsForm($form, $form_state);
}
$submit_handler = $form['#form_id'] . '_submit';
Angie Byron
committed
if (isset($form_state['build_info']['callback_object'])) {
$submit_handler = array($form_state['build_info']['callback_object'], 'submitForm');
}
if (is_callable($submit_handler)) {
Dries Buytaert
committed
// The submit handler might be a function or a method on the
// callback_object. Additional note that we have to pass the parameters
// by reference, as php 5.4 requires us to do that.
call_user_func_array($submit_handler, array(&$form, &$form_state));
}
}
/**
* Submit handler for cancel button
*/
public function standardCancel($form, &$form_state) {
if (!empty($this->changed) && isset($this->form_cache)) {
unset($this->form_cache);
Alex Pott
committed
$this->cacheSet();
}
Alex Pott
committed
$form_state['redirect_route'] = $this->urlInfo('edit-form');
}
/**
* Provide a standard set of Apply/Cancel/OK buttons for the forms. Also provide
* a hidden op operator because the forms plugin doesn't seem to properly
* provide which button was clicked.
*
* TODO: Is the hidden op operator still here somewhere, or is that part of the
* docblock outdated?
*/
Angie Byron
committed
public function getStandardButtons(&$form, &$form_state, $form_id, $name = NULL) {
$form['actions'] = array(
'#type' => 'actions',
);
if (empty($name)) {
$name = t('Apply');
if (!empty($this->stack) && count($this->stack) > 1) {
$name = t('Apply and continue');
}
$names = array(t('Apply'), t('Apply and continue'));
}
// Views provides its own custom handling of AJAX form submissions. Usually
// this happens at the same path, but custom paths may be specified in
// $form_state.
$form_path = empty($form_state['path']) ? current_path() : $form_state['path'];
// Forms that are purely informational set an ok_button flag, so we know not
// to create an "Apply" button for them.
if (empty($form_state['ok_button'])) {
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $name,
'#id' => 'edit-submit-' . drupal_html_id($form_id),
// The regular submit handler ($form_id . '_submit') does not apply if
// we're updating the default display. It does apply if we're updating
// the current display. Since we have no way of knowing at this point
// which display the user wants to update, views_ui_standard_submit will
// take care of running the regular submit handler as appropriate.
'#submit' => array(array($this, 'standardSubmit')),
'#button_type' => 'primary',
'#ajax' => array(
'path' => $form_path,
),
);
// Form API button click detection requires the button's #value to be the
// same between the form build of the initial page request, and the
// initial form build of the request processing the form submission.
// Ideally, the button's #value shouldn't change until the form rebuild
Angie Byron
committed
// step. However, \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm()
// implements a different multistep form workflow than the Form API does,
// and adjusts $view->stack prior to form processing, so we compensate by
// extending button click detection code to support any of the possible
// button labels.
if (isset($names)) {
$form['actions']['submit']['#values'] = $names;
$form['actions']['submit']['#process'] = array_merge(array('views_ui_form_button_was_clicked'), element_info_property($form['actions']['submit']['#type'], '#process', array()));
}
// If a validation handler exists for the form, assign it to this button.
Angie Byron
committed
if (isset($form_state['build_info']['callback_object'])) {
$form['actions']['submit']['#validate'][] = array($form_state['build_info']['callback_object'], 'validateForm');
Angie Byron
committed
}
if (function_exists($form_id . '_validate')) {
$form['actions']['submit']['#validate'][] = $form_id . '_validate';
}
}
// Create a "Cancel" button. For purely informational forms, label it "OK".
$cancel_submit = function_exists($form_id . '_cancel') ? $form_id . '_cancel' : array($this, 'standardCancel');
$form['actions']['cancel'] = array(
'#type' => 'submit',
'#value' => empty($form_state['ok_button']) ? t('Cancel') : t('Ok'),
'#submit' => array($cancel_submit),
'#validate' => array(),
'#ajax' => array(
'path' => $form_path,
),
Alex Pott
committed
'#limit_validation_errors' => array(),
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
);
// Compatibility, to be removed later: // TODO: When is "later"?
// We used to set these items on the form, but now we want them on the $form_state:
if (isset($form['#title'])) {
$form_state['title'] = $form['#title'];
}
if (isset($form['#section'])) {
$form_state['#section'] = $form['#section'];
}
// Finally, we never want these cached -- our object cache does that for us.
$form['#no_cache'] = TRUE;
}
/**
* Return the was_defaulted, is_defaulted and revert state of a form.
*/
public function getOverrideValues($form, $form_state) {
// Make sure the dropdown exists in the first place.
if (isset($form_state['values']['override']['dropdown'])) {
// #default_value is used to determine whether it was the default value or not.
// So the available options are: $display, 'default' and 'default_revert', not 'defaults'.
$was_defaulted = ($form['override']['dropdown']['#default_value'] === 'defaults');
$is_defaulted = ($form_state['values']['override']['dropdown'] === 'default');
$revert = ($form_state['values']['override']['dropdown'] === 'default_revert');
if ($was_defaulted !== $is_defaulted && isset($form['#section'])) {
// We're changing which display these values apply to.
// Update the #section so it knows what to mark changed.
$form['#section'] = str_replace('default-', $form_state['display_id'] . '-', $form['#section']);
}
}
else {
// The user didn't get the dropdown for overriding the default display.
$was_defaulted = FALSE;
$is_defaulted = FALSE;
$revert = FALSE;
}
return array($was_defaulted, $is_defaulted, $revert);
}
/**
* Add another form to the stack; clicking 'apply' will go to this form
* rather than closing the ajax popup.
*/
Angie Byron
committed
public function addFormToStack($key, $display_id, $type, $id = NULL, $top = FALSE, $rebuild_keys = FALSE) {
// 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.
$seen_ids_init = &drupal_static('drupal_html_id:init');
$seen_ids_init = array();
if (empty($this->stack)) {
$this->stack = array();
}
Angie Byron
committed
$stack = array(implode('-', array_filter(array($key, $this->id(), $display_id, $type, $id))), $key, $display_id, $type, $id);
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
// If we're being asked to add this form to the bottom of the stack, no
// special logic is required. Our work is equally easy if we were asked to add
// to the top of the stack, but there's nothing in it yet.
if (!$top || empty($this->stack)) {
$this->stack[] = $stack;
}
// If we're adding to the top of an existing stack, we have to maintain the
// existing integer keys, so they can be used for the "2 of 3" progress
// indicator (which will now read "2 of 4").
else {
$keys = array_keys($this->stack);
$first = current($keys);
$last = end($keys);
for ($i = $last; $i >= $first; $i--) {
if (!isset($this->stack[$i])) {
continue;
}
// Move form number $i to the next position in the stack.
$this->stack[$i + 1] = $this->stack[$i];
unset($this->stack[$i]);
}
// Now that the previously $first slot is free, move the new form into it.
$this->stack[$first] = $stack;
ksort($this->stack);
// Start the keys from 0 again, if requested.
if ($rebuild_keys) {
$this->stack = array_values($this->stack);
}
}
}
/**
* Submit handler for adding new item(s) to a view.
*/
public function submitItemAdd($form, &$form_state) {
$type = $form_state['type'];
catch
committed
$types = ViewExecutable::getHandlerTypes();
$section = $types[$type]['plural'];
// Handle the override select.
list($was_defaulted, $is_defaulted) = $this->getOverrideValues($form, $form_state);
if ($was_defaulted && !$is_defaulted) {
// We were using the default display's values, but we're now overriding
// the default display and saving values specific to this display.
$display = &$this->executable->displayHandlers->get($form_state['display_id']);
// setOverride toggles the override of this section.
$display->setOverride($section);
}
elseif (!$was_defaulted && $is_defaulted) {
// We used to have an override for this display, but the user now wants
// to go back to the default display.
// Overwrite the default display with the current form values, and make
// the current display use the new default values.
$display = &$this->executable->displayHandlers->get($form_state['display_id']);
// optionsOverride toggles the override of this section.
$display->setOverride($section);
}
if (!empty($form_state['values']['name']) && is_array($form_state['values']['name'])) {
// Loop through each of the items that were checked and add them to the view.
foreach (array_keys(array_filter($form_state['values']['name'])) as $field) {
list($table, $field) = explode('.', $field, 2);
if ($cut = strpos($field, '$')) {
$field = substr($field, 0, $cut);
}
Angie Byron
committed
$id = $this->executable->addHandler($form_state['display_id'], $type, $table, $field);
// check to see if we have group by settings
$key = $type;
// Footer,header and empty text have a different internal handler type(area).
if (isset($types[$type]['type'])) {
$key = $types[$type]['type'];
}
Angie Byron
committed
$item = array(
'table' => $table,
'field' => $field,
);
Dries Buytaert
committed
$handler = Views::handlerManager($key)->getHandler($item);
if ($this->executable->displayHandlers->get('default')->useGroupBy() && $handler->usesGroupBy()) {
Angie Byron
committed
$this->addFormToStack('handler-group', $form_state['display_id'], $type, $id);
}
// check to see if this type has settings, if so add the settings form first
if ($handler && $handler->hasExtraOptions()) {
Angie Byron
committed
$this->addFormToStack('handler-extra', $form_state['display_id'], $type, $id);
}
// Then add the form to the stack
Angie Byron
committed
$this->addFormToStack('handler', $form_state['display_id'], $type, $id);
}
}
if (isset($this->form_cache)) {
unset($this->form_cache);
}
// Store in cache
Alex Pott
committed
$this->cacheSet();
}
/**
* Set up query capturing.
*
* \Drupal\Core\Database\Database stores the queries that it runs, if logging
* is enabled.
*
* @see ViewUI::endQueryCapture()
*/
public function startQueryCapture() {
Database::startLog('views');
}
/**
* Add the list of queries run during render to buildinfo.
*
* @see ViewUI::startQueryCapture()
*/
public function endQueryCapture() {
$queries = Database::getLog('views');
$this->additionalQueries = $queries;
}
public function renderPreview($display_id, $args = array()) {
// Save the current path so it can be restored before returning from this function.
$old_q = current_path();
// Determine where the query and performance statistics should be output.
Angie Byron
committed
$config = \Drupal::config('views.settings');
$show_query = $config->get('ui.show.sql_query.enabled');
$show_info = $config->get('ui.show.preview_information');
$show_location = $config->get('ui.show.sql_query.where');
$show_stats = $config->get('ui.show.performance_statistics');
if ($show_stats) {
$show_stats = $config->get('ui.show.sql_query.where');
}
$combined = $show_query && $show_stats;
$rows = array('query' => array(), 'statistics' => array());
$output = '';
$errors = $this->executable->validate();
Alex Pott
committed
$this->executable->destroy();
if (empty($errors)) {
$this->ajax = TRUE;
$this->executable->live_preview = TRUE;
$this->views_ui_context = TRUE;
// AJAX happens via HTTP POST but everything expects exposed data to
// be in GET. Copy stuff but remove ajax-framework specific keys.
// If we're clicking on links in a preview, though, we could actually
// have some input in the query parameters, so we merge request() and
// query() to ensure we get it all.
$exposed_input = array_merge(\Drupal::request()->request->all(), \Drupal::request()->query->all());
foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', 'ajax_html_ids', 'ajax_page_state', 'form_id', 'form_build_id', 'form_token') as $key) {
if (isset($exposed_input[$key])) {
unset($exposed_input[$key]);
}
}
$this->executable->setExposedInput($exposed_input);
if (!$this->executable->setDisplay($display_id)) {
return t('Invalid display id @display', array('@display' => $display_id));
}
$this->executable->setArguments($args);
// Store the current view URL for later use:
if ($this->executable->display_handler->getOption('path')) {
$path = $this->executable->getUrl();
}
// Make view links come back to preview.
$this->override_path = 'admin/structure/views/view/' . $this->id() . '/preview/' . $display_id;
// Also override the current path so we get the pager.
$original_path = current_path();
$q = _current_path($this->override_path);
if ($args) {
$q .= '/' . implode('/', $args);
_current_path($q);
}
// Suppress contextual links of entities within the result set during a
// Preview.
// @todo We'll want to add contextual links specific to editing the View, so
// the suppression may need to be moved deeper into the Preview pipeline.
views_ui_contextual_links_suppress_push();
$show_additional_queries = $config->get('ui.show.additional_queries');
Timer::start('views_ui.preview');
if ($show_additional_queries) {
$this->startQueryCapture();
}
// Execute/get the view preview.
$preview = $this->executable->preview($display_id, $args);
Angie Byron
committed
$preview = drupal_render($preview);
if ($show_additional_queries) {
$this->endQueryCapture();
}
$this->render_time = Timer::stop('views_ui.preview');
views_ui_contextual_links_suppress_pop();
// Reset variables.
unset($this->override_path);
_current_path($original_path);
// Prepare the query information and statistics to show either above or
// below the view preview.
if ($show_info || $show_query || $show_stats) {
// Get information from the preview for display.
if (!empty($this->executable->build_info['query'])) {
if ($show_query) {
$query_string = $this->executable->build_info['query'];
// Only the sql default class has a method getArguments.
$quoted = array();
if ($this->executable->query instanceof Sql) {
$quoted = $query_string->getArguments();
$connection = Database::getConnection();
foreach ($quoted as $key => $val) {
if (is_array($val)) {
$quoted[$key] = implode(', ', array_map(array($connection, 'quote'), $val));
}
else {
$quoted[$key] = $connection->quote($val);
}
}
}
$rows['query'][] = array('<strong>' . t('Query') . '</strong>', '<pre>' . String::checkPlain(strtr($query_string, $quoted)) . '</pre>');
if (!empty($this->additionalQueries)) {
$queries = '<strong>' . t('These queries were run during view rendering:') . '</strong>';
foreach ($this->additionalQueries as $query) {
if ($queries) {
$queries .= "\n";
}
$query_string = strtr($query['query'], $query['args']);
$queries .= t('[@time ms] @query', array('@time' => round($query['time'] * 100000, 1) / 100000.0, '@query' => $query_string));
}
$rows['query'][] = array('<strong>' . t('Other queries') . '</strong>', '<pre>' . $queries . '</pre>');
}
}
if ($show_info) {
Angie Byron
committed
$rows['query'][] = array('<strong>' . t('Title') . '</strong>', Xss::filterAdmin($this->executable->getTitle()));
if (isset($path)) {
$path = l($path, $path);
}
else {
$path = t('This display has no path.');
}
$rows['query'][] = array('<strong>' . t('Path') . '</strong>', $path);
}
if ($show_stats) {
$rows['statistics'][] = array('<strong>' . t('Query build time') . '</strong>', t('@time ms', array('@time' => intval($this->executable->build_time * 100000) / 100)));
$rows['statistics'][] = array('<strong>' . t('Query execute time') . '</strong>', t('@time ms', array('@time' => intval($this->executable->execute_time * 100000) / 100)));
$rows['statistics'][] = array('<strong>' . t('View render time') . '</strong>', t('@time ms', array('@time' => intval($this->executable->render_time * 100000) / 100)));
}
Alex Pott
committed
\Drupal::moduleHandler()->alter('views_preview_info', $rows, $this);
}
else {
// No query was run. Display that information in place of either the
// query or the performance statistics, whichever comes first.
if ($combined || ($show_location === 'above')) {
$rows['query'] = array(array('<strong>' . t('Query') . '</strong>', t('No query was run')));
}
else {
$rows['statistics'] = array(array('<strong>' . t('Query') . '</strong>', t('No query was run')));
}
}
}
}
else {
foreach ($errors as $display_errors) {
foreach ($display_errors as $error) {
drupal_set_message($error, 'error');
}
}
$preview = t('Unable to preview due to validation errors.');
}
// Assemble the preview, the query info, and the query statistics in the
// requested order.
$table = array(
Angie Byron
committed
'#type' => 'table',
'#prefix' => '<div class="views-query-info">',
'#suffix' => '</div>',
);
if ($show_location === 'above' || $show_location === 'below') {
if ($combined) {
$table['#rows'] = array_merge($rows['query'], $rows['statistics']);
}
else {
$table['#rows'] = $rows['query'];
}
}
elseif ($show_stats === 'above' || $show_stats === 'below') {
$table['#rows'] = $rows['statistics'];
}
if ($show_location === 'above' || $show_stats === 'above') {
$output .= drupal_render($table) . $preview;
elseif ($show_location === 'below' || $show_stats === 'below') {
$output .= $preview . drupal_render($table);
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
}
_current_path($old_q);
return $output;
}
/**
* Get the user's current progress through the form stack.
*
* @return
* FALSE if the user is not currently in a multiple-form stack. Otherwise,
* an associative array with the following keys:
* - current: The number of the current form on the stack.
* - total: The total number of forms originally on the stack.
*/
public function getFormProgress() {
$progress = FALSE;
if (!empty($this->stack)) {
// The forms on the stack have integer keys that don't change as the forms
// are completed, so we can see which ones are still left.
$keys = array_keys($this->stack);
// Add 1 to the array keys for the benefit of humans, who start counting
// from 1 and not 0.
$current = reset($keys) + 1;
$total = end($keys) + 1;
if ($total > 1) {
$progress = array();
$progress['current'] = $current;
$progress['total'] = $total;
}
}
return $progress;
}
Alex Pott
committed
/**
* Sets a cached view object in the user tempstore.
*/
public function cacheSet() {
if ($this->isLocked()) {
drupal_set_message(t('Changes cannot be made to a locked view.'), 'error');
return;
}
// Let any future object know that this view has changed.
$this->changed = TRUE;
Alex Pott
committed
$executable = $this->getExecutable();
Alex Pott
committed
if (isset($executable->current_display)) {
// Add the knowledge of the changed display, too.
$this->changed_display[$executable->current_display] = TRUE;
unset($executable->current_display);
}
// Unset handlers; we don't want to write these into the cache.
unset($executable->display_handler);
unset($executable->default_display);
$executable->query = NULL;
unset($executable->displayHandlers);
\Drupal::service('user.tempstore')->get('views')->set($this->id(), $this);
}
/**
* Returns whether the current view is locked.
*
* @return bool
* TRUE if the view is locked, FALSE otherwise.
*/
public function isLocked() {
return is_object($this->lock) && ($this->lock->owner != \Drupal::currentUser()->id());
Alex Pott
committed
}
/**
* Passes through all unknown calls onto the storage object.
*/
public function __call($method, $args) {
return call_user_func_array(array($this->storage, $method), $args);
}
/**
* {@inheritdoc}
*/
public function &getDisplay($display_id) {
return $this->storage->getDisplay($display_id);
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::id().
*/
public function id() {
Angie Byron
committed
return $this->storage->id();
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::uuid().
*/
public function uuid() {
Angie Byron
committed
return $this->storage->uuid();
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::isNew().
*/
public function isNew() {
Angie Byron
committed
return $this->storage->isNew();
}
/**
* {@inheritdoc}
*/
public function getEntityTypeId() {
return $this->storage->getEntityTypeId();
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::bundle().
*/
public function bundle() {
Angie Byron
committed
return $this->storage->bundle();
}
/**
* {@inheritdoc}
*/
public function getEntityType() {
return $this->storage->getEntityType();
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::createDuplicate().
*/
public function createDuplicate() {
Angie Byron
committed
return $this->storage->createDuplicate();
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::delete().
*/
public function delete() {
Angie Byron
committed
return $this->storage->delete();
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::save().
*/
public function save() {
Angie Byron
committed
return $this->storage->save();
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::uri().
*/
Alex Pott
committed
public function urlInfo($rel = 'edit-form') {
return $this->storage->urlInfo($rel);
}
/**
* {@inheritdoc}
*/
public function getSystemPath($rel = 'edit-form') {
return $this->storage->getSystemPath($rel);
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::label().
*/
public function label() {
return $this->storage->label();
}
/**
* Implements \Drupal\Core\Entity\EntityInterface::enforceIsNew().
*/
public function enforceIsNew($value = TRUE) {
Angie Byron
committed
return $this->storage->enforceIsNew($value);
}
Dries Buytaert
committed
/**
Angie Byron
committed
* {@inheritdoc}
Dries Buytaert
committed
*/
Angie Byron
committed
public function toArray() {
return $this->storage->toArray();
Dries Buytaert
committed
}
/**
* {@inheritdoc}
*/
public function language() {
Angie Byron
committed
return $this->storage->language();
}
/**
* {@inheritdoc}
*/
public function access($operation = 'view', AccountInterface $account = NULL) {
Angie Byron
committed
return $this->storage->access($operation, $account);
}
/**
* Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::enable().
*/
public function enable() {
Angie Byron
committed
return $this->storage->enable();
}
/**
* Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::disable().
*/
public function disable() {
Angie Byron
committed
return $this->storage->disable();
}
/**
* Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::status().
*/
public function status() {
Angie Byron
committed
return $this->storage->status();
}
/**
Angie Byron
committed
* {@inheritdoc}
*/
Angie Byron
committed
public function getOriginalId() {
return $this->storage->getOriginalId();
}
/**
Angie Byron
committed
* {@inheritdoc}
*/
Angie Byron
committed
public function setOriginalId($id) {
return $this->storage->setOriginalId($id);
}
Angie Byron
committed
/**
* Implements \Drupal\Core\Entity\EntityInterface::isTranslatable().
*/
public function isTranslatable() {
return $this->storage->isTranslatable();
}
/**
* {@inheritdoc}
*/