3 namespace Drupal\views_ui;
5 use Drupal\Component\Utility\Html;
6 use Drupal\Component\Utility\Timer;
7 use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\views\Views;
10 use Drupal\Core\Entity\EntityStorageInterface;
11 use Drupal\views\ViewExecutable;
12 use Drupal\Core\Database\Database;
13 use Drupal\Core\Session\AccountInterface;
14 use Drupal\views\Plugin\views\query\Sql;
15 use Drupal\views\Entity\View;
16 use Drupal\views\ViewEntityInterface;
17 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
18 use Symfony\Component\HttpFoundation\ParameterBag;
19 use Symfony\Component\HttpFoundation\Request;
22 * Stores UI related temporary settings.
24 class ViewUI implements ViewEntityInterface {
27 * Indicates if a view is currently being edited.
31 public $editing = FALSE;
34 * Stores an array of displays that have been changed.
38 public $changed_display;
41 * How long the view takes to render in microseconds.
48 * If this view is locked for editing.
50 * If this view is locked it will contain the result of
51 * \Drupal\Core\TempStore\SharedTempStore::getMetadata(). Which can be a stdClass or
59 * If this view has been changed.
66 * Stores options temporarily while editing.
70 public $temporary_options;
73 * Stores a stack of UI forms to display.
80 * Is the view run in a context of the preview in the admin interface.
86 public $renderPreview = FALSE;
89 * The View storage object.
91 * @var \Drupal\views\ViewEntityInterface
96 * Stores a list of database queries run beside the main one from views.
100 * @see \Drupal\Core\Database\Log
102 protected $additionalQueries;
105 * Contains an array of form keys and their respective classes.
109 public static $forms = [
110 'add-handler' => '\Drupal\views_ui\Form\Ajax\AddItem',
111 'analyze' => '\Drupal\views_ui\Form\Ajax\Analyze',
112 'handler' => '\Drupal\views_ui\Form\Ajax\ConfigHandler',
113 'handler-extra' => '\Drupal\views_ui\Form\Ajax\ConfigHandlerExtra',
114 'handler-group' => '\Drupal\views_ui\Form\Ajax\ConfigHandlerGroup',
115 'display' => '\Drupal\views_ui\Form\Ajax\Display',
116 'edit-details' => '\Drupal\views_ui\Form\Ajax\EditDetails',
117 'rearrange' => '\Drupal\views_ui\Form\Ajax\Rearrange',
118 'rearrange-filter' => '\Drupal\views_ui\Form\Ajax\RearrangeFilter',
119 'reorder-displays' => '\Drupal\views_ui\Form\Ajax\ReorderDisplays',
123 * Whether the config is being created, updated or deleted through the
128 private $isSyncing = FALSE;
131 * Whether the config is being deleted through the uninstall process.
135 private $isUninstalling = FALSE;
138 * Constructs a View UI object.
140 * @param \Drupal\views\ViewEntityInterface $storage
141 * The View storage object to wrap.
143 public function __construct(ViewEntityInterface $storage) {
144 $this->entityType = 'view';
145 $this->storage = $storage;
151 public function get($property_name, $langcode = NULL) {
152 if (property_exists($this->storage, $property_name)) {
153 return $this->storage->get($property_name, $langcode);
156 return isset($this->{$property_name}) ? $this->{$property_name} : NULL;
162 public function setStatus($status) {
163 return $this->storage->setStatus($status);
169 public function set($property_name, $value, $notify = TRUE) {
170 if (property_exists($this->storage, $property_name)) {
171 $this->storage->set($property_name, $value);
174 $this->{$property_name} = $value;
181 public function setSyncing($syncing) {
182 $this->isSyncing = $syncing;
188 public function setUninstalling($isUninstalling) {
189 $this->isUninstalling = $isUninstalling;
195 public function isSyncing() {
196 return $this->isSyncing;
202 public function isUninstalling() {
203 return $this->isUninstalling;
207 * Basic submit handler applicable to all 'standard' forms.
209 * This submit handler determines whether the user wants the submitted changes
210 * to apply to the default display or to the current display, and dispatches
211 * control appropriately.
213 public function standardSubmit($form, FormStateInterface $form_state) {
214 // Determine whether the values the user entered are intended to apply to
215 // the current display or the default display.
217 list($was_defaulted, $is_defaulted, $revert) = $this->getOverrideValues($form, $form_state);
219 // Based on the user's choice in the display dropdown, determine which display
220 // these changes apply to.
221 $display_id = $form_state->get('display_id');
223 // If it's revert just change the override and return.
224 $display = &$this->getExecutable()->displayHandlers->get($display_id);
225 $display->optionsOverride($form, $form_state);
227 // Don't execute the normal submit handling but still store the changed view into cache.
231 elseif ($was_defaulted === $is_defaulted) {
232 // We're not changing which display these form values apply to.
233 // Run the regular submit handler for this form.
235 elseif ($was_defaulted && !$is_defaulted) {
236 // We were using the default display's values, but we're now overriding
237 // the default display and saving values specific to this display.
238 $display = &$this->getExecutable()->displayHandlers->get($display_id);
239 // optionsOverride toggles the override of this section.
240 $display->optionsOverride($form, $form_state);
241 $display->submitOptionsForm($form, $form_state);
243 elseif (!$was_defaulted && $is_defaulted) {
244 // We used to have an override for this display, but the user now wants
245 // to go back to the default display.
246 // Overwrite the default display with the current form values, and make
247 // the current display use the new default values.
248 $display = &$this->getExecutable()->displayHandlers->get($display_id);
249 // optionsOverride toggles the override of this section.
250 $display->optionsOverride($form, $form_state);
251 $display->submitOptionsForm($form, $form_state);
254 $submit_handler = [$form_state->getFormObject(), 'submitForm'];
255 call_user_func_array($submit_handler, [&$form, $form_state]);
259 * Submit handler for cancel button
261 public function standardCancel($form, FormStateInterface $form_state) {
262 if (!empty($this->changed) && isset($this->form_cache)) {
263 unset($this->form_cache);
267 $form_state->setRedirectUrl($this->urlInfo('edit-form'));
271 * Provide a standard set of Apply/Cancel/OK buttons for the forms. Also provide
272 * a hidden op operator because the forms plugin doesn't seem to properly
273 * provide which button was clicked.
275 * TODO: Is the hidden op operator still here somewhere, or is that part of the
278 public function getStandardButtons(&$form, FormStateInterface $form_state, $form_id, $name = NULL) {
280 '#type' => 'actions',
285 if (!empty($this->stack) && count($this->stack) > 1) {
286 $name = t('Apply and continue');
288 $names = [t('Apply'), t('Apply and continue')];
291 // Forms that are purely informational set an ok_button flag, so we know not
292 // to create an "Apply" button for them.
293 if (!$form_state->get('ok_button')) {
294 $form['actions']['submit'] = [
297 '#id' => 'edit-submit-' . Html::getUniqueId($form_id),
298 // The regular submit handler ($form_id . '_submit') does not apply if
299 // we're updating the default display. It does apply if we're updating
300 // the current display. Since we have no way of knowing at this point
301 // which display the user wants to update, views_ui_standard_submit will
302 // take care of running the regular submit handler as appropriate.
303 '#submit' => [[$this, 'standardSubmit']],
304 '#button_type' => 'primary',
306 // Form API button click detection requires the button's #value to be the
307 // same between the form build of the initial page request, and the
308 // initial form build of the request processing the form submission.
309 // Ideally, the button's #value shouldn't change until the form rebuild
310 // step. However, \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm()
311 // implements a different multistep form workflow than the Form API does,
312 // and adjusts $view->stack prior to form processing, so we compensate by
313 // extending button click detection code to support any of the possible
316 $form['actions']['submit']['#values'] = $names;
317 $form['actions']['submit']['#process'] = array_merge(['views_ui_form_button_was_clicked'], \Drupal::service('element_info')->getInfoProperty($form['actions']['submit']['#type'], '#process', []));
319 // If a validation handler exists for the form, assign it to this button.
320 $form['actions']['submit']['#validate'][] = [$form_state->getFormObject(), 'validateForm'];
323 // Create a "Cancel" button. For purely informational forms, label it "OK".
324 $cancel_submit = function_exists($form_id . '_cancel') ? $form_id . '_cancel' : [$this, 'standardCancel'];
325 $form['actions']['cancel'] = [
327 '#value' => !$form_state->get('ok_button') ? t('Cancel') : t('Ok'),
328 '#submit' => [$cancel_submit],
330 '#limit_validation_errors' => [],
333 // Compatibility, to be removed later: // TODO: When is "later"?
334 // We used to set these items on the form, but now we want them on the $form_state:
335 if (isset($form['#title'])) {
336 $form_state->set('title', $form['#title']);
338 if (isset($form['#section'])) {
339 $form_state->set('#section', $form['#section']);
341 // Finally, we never want these cached -- our object cache does that for us.
342 $form['#no_cache'] = TRUE;
346 * Return the was_defaulted, is_defaulted and revert state of a form.
348 public function getOverrideValues($form, FormStateInterface $form_state) {
349 // Make sure the dropdown exists in the first place.
350 if ($form_state->hasValue(['override', 'dropdown'])) {
351 // #default_value is used to determine whether it was the default value or not.
352 // So the available options are: $display, 'default' and 'default_revert', not 'defaults'.
353 $was_defaulted = ($form['override']['dropdown']['#default_value'] === 'defaults');
354 $dropdown = $form_state->getValue(['override', 'dropdown']);
355 $is_defaulted = ($dropdown === 'default');
356 $revert = ($dropdown === 'default_revert');
358 if ($was_defaulted !== $is_defaulted && isset($form['#section'])) {
359 // We're changing which display these values apply to.
360 // Update the #section so it knows what to mark changed.
361 $form['#section'] = str_replace('default-', $form_state->get('display_id') . '-', $form['#section']);
365 // The user didn't get the dropdown for overriding the default display.
366 $was_defaulted = FALSE;
367 $is_defaulted = FALSE;
371 return [$was_defaulted, $is_defaulted, $revert];
375 * Add another form to the stack; clicking 'apply' will go to this form
376 * rather than closing the ajax popup.
378 public function addFormToStack($key, $display_id, $type, $id = NULL, $top = FALSE, $rebuild_keys = FALSE) {
379 // Reset the cache of IDs. Drupal rather aggressively prevents ID
380 // duplication but this causes it to remember IDs that are no longer even
382 Html::resetSeenIds();
384 if (empty($this->stack)) {
388 $stack = [implode('-', array_filter([$key, $this->id(), $display_id, $type, $id])), $key, $display_id, $type, $id];
389 // If we're being asked to add this form to the bottom of the stack, no
390 // special logic is required. Our work is equally easy if we were asked to add
391 // to the top of the stack, but there's nothing in it yet.
392 if (!$top || empty($this->stack)) {
393 $this->stack[] = $stack;
395 // If we're adding to the top of an existing stack, we have to maintain the
396 // existing integer keys, so they can be used for the "2 of 3" progress
397 // indicator (which will now read "2 of 4").
399 $keys = array_keys($this->stack);
400 $first = current($keys);
402 for ($i = $last; $i >= $first; $i--) {
403 if (!isset($this->stack[$i])) {
406 // Move form number $i to the next position in the stack.
407 $this->stack[$i + 1] = $this->stack[$i];
408 unset($this->stack[$i]);
410 // Now that the previously $first slot is free, move the new form into it.
411 $this->stack[$first] = $stack;
414 // Start the keys from 0 again, if requested.
416 $this->stack = array_values($this->stack);
422 * Submit handler for adding new item(s) to a view.
424 public function submitItemAdd($form, FormStateInterface $form_state) {
425 $type = $form_state->get('type');
426 $types = ViewExecutable::getHandlerTypes();
427 $section = $types[$type]['plural'];
428 $display_id = $form_state->get('display_id');
430 // Handle the override select.
431 list($was_defaulted, $is_defaulted) = $this->getOverrideValues($form, $form_state);
432 if ($was_defaulted && !$is_defaulted) {
433 // We were using the default display's values, but we're now overriding
434 // the default display and saving values specific to this display.
435 $display = &$this->getExecutable()->displayHandlers->get($display_id);
436 // setOverride toggles the override of this section.
437 $display->setOverride($section);
439 elseif (!$was_defaulted && $is_defaulted) {
440 // We used to have an override for this display, but the user now wants
441 // to go back to the default display.
442 // Overwrite the default display with the current form values, and make
443 // the current display use the new default values.
444 $display = &$this->getExecutable()->displayHandlers->get($display_id);
445 // optionsOverride toggles the override of this section.
446 $display->setOverride($section);
449 if (!$form_state->isValueEmpty('name') && is_array($form_state->getValue('name'))) {
450 // Loop through each of the items that were checked and add them to the view.
451 foreach (array_keys(array_filter($form_state->getValue('name'))) as $field) {
452 list($table, $field) = explode('.', $field, 2);
454 if ($cut = strpos($field, '$')) {
455 $field = substr($field, 0, $cut);
457 $id = $this->getExecutable()->addHandler($display_id, $type, $table, $field);
459 // check to see if we have group by settings
461 // Footer,header and empty text have a different internal handler type(area).
462 if (isset($types[$type]['type'])) {
463 $key = $types[$type]['type'];
469 $handler = Views::handlerManager($key)->getHandler($item);
470 if ($this->getExecutable()->displayHandlers->get('default')->useGroupBy() && $handler->usesGroupBy()) {
471 $this->addFormToStack('handler-group', $display_id, $type, $id);
474 // check to see if this type has settings, if so add the settings form first
475 if ($handler && $handler->hasExtraOptions()) {
476 $this->addFormToStack('handler-extra', $display_id, $type, $id);
478 // Then add the form to the stack
479 $this->addFormToStack('handler', $display_id, $type, $id);
483 if (isset($this->form_cache)) {
484 unset($this->form_cache);
492 * Set up query capturing.
494 * \Drupal\Core\Database\Database stores the queries that it runs, if logging
497 * @see ViewUI::endQueryCapture()
499 public function startQueryCapture() {
500 Database::startLog('views');
504 * Add the list of queries run during render to buildinfo.
506 * @see ViewUI::startQueryCapture()
508 public function endQueryCapture() {
509 $queries = Database::getLog('views');
511 $this->additionalQueries = $queries;
514 public function renderPreview($display_id, $args = []) {
515 // Save the current path so it can be restored before returning from this function.
516 $request_stack = \Drupal::requestStack();
517 $current_request = $request_stack->getCurrentRequest();
518 $executable = $this->getExecutable();
520 // Determine where the query and performance statistics should be output.
521 $config = \Drupal::config('views.settings');
522 $show_query = $config->get('ui.show.sql_query.enabled');
523 $show_info = $config->get('ui.show.preview_information');
524 $show_location = $config->get('ui.show.sql_query.where');
526 $show_stats = $config->get('ui.show.performance_statistics');
528 $show_stats = $config->get('ui.show.sql_query.where');
531 $combined = $show_query && $show_stats;
533 $rows = ['query' => [], 'statistics' => []];
535 $errors = $executable->validate();
536 $executable->destroy();
537 if (empty($errors)) {
539 $executable->live_preview = TRUE;
541 // AJAX happens via HTTP POST but everything expects exposed data to
542 // be in GET. Copy stuff but remove ajax-framework specific keys.
543 // If we're clicking on links in a preview, though, we could actually
544 // have some input in the query parameters, so we merge request() and
545 // query() to ensure we get it all.
546 $exposed_input = array_merge(\Drupal::request()->request->all(), \Drupal::request()->query->all());
547 foreach (['view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER, 'ajax_page_state', 'form_id', 'form_build_id', 'form_token'] as $key) {
548 if (isset($exposed_input[$key])) {
549 unset($exposed_input[$key]);
552 $executable->setExposedInput($exposed_input);
554 if (!$executable->setDisplay($display_id)) {
556 '#markup' => t('Invalid display id @display', ['@display' => $display_id]),
560 $executable->setArguments($args);
562 // Store the current view URL for later use:
563 if ($executable->hasUrl() && $executable->display_handler->getOption('path')) {
564 $path = $executable->getUrl();
567 // Make view links come back to preview.
569 // Also override the current path so we get the pager, and make sure the
570 // Request object gets all of the proper values from $_SERVER.
571 $request = Request::createFromGlobals();
572 $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'entity.view.preview_form');
573 $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, \Drupal::service('router.route_provider')->getRouteByName('entity.view.preview_form'));
574 $request->attributes->set('view', $this->storage);
575 $request->attributes->set('display_id', $display_id);
576 $raw_parameters = new ParameterBag();
577 $raw_parameters->set('view', $this->id());
578 $raw_parameters->set('display_id', $display_id);
579 $request->attributes->set('_raw_variables', $raw_parameters);
581 foreach ($args as $key => $arg) {
582 $request->attributes->set('arg_' . $key, $arg);
584 $request_stack->push($request);
586 // Suppress contextual links of entities within the result set during a
588 // @todo We'll want to add contextual links specific to editing the View, so
589 // the suppression may need to be moved deeper into the Preview pipeline.
590 views_ui_contextual_links_suppress_push();
592 $show_additional_queries = $config->get('ui.show.additional_queries');
594 Timer::start('entity.view.preview_form');
596 if ($show_additional_queries) {
597 $this->startQueryCapture();
600 // Execute/get the view preview.
601 $preview = $executable->preview($display_id, $args);
603 if ($show_additional_queries) {
604 $this->endQueryCapture();
607 $this->render_time = Timer::stop('entity.view.preview_form')['time'];
609 views_ui_contextual_links_suppress_pop();
611 // Prepare the query information and statistics to show either above or
612 // below the view preview.
613 // Initialise the empty rows arrays so we can safely merge them later.
615 $rows['statistics'] = [];
616 if ($show_info || $show_query || $show_stats) {
617 // Get information from the preview for display.
618 if (!empty($executable->build_info['query'])) {
620 $query_string = $executable->build_info['query'];
621 // Only the sql default class has a method getArguments.
624 if ($executable->query instanceof Sql) {
625 $quoted = $query_string->getArguments();
626 $connection = Database::getConnection();
627 foreach ($quoted as $key => $val) {
628 if (is_array($val)) {
629 $quoted[$key] = implode(', ', array_map([$connection, 'quote'], $val));
632 $quoted[$key] = $connection->quote($val);
639 '#type' => 'inline_template',
640 '#template' => "<strong>{% trans 'Query' %}</strong>",
645 '#type' => 'inline_template',
646 '#template' => '<pre>{{ query }}</pre>',
647 '#context' => ['query' => strtr($query_string, $quoted)],
651 if (!empty($this->additionalQueries)) {
653 '#prefix' => '<strong>',
654 '#markup' => t('These queries were run during view rendering:'),
655 '#suffix' => '</strong>',
657 foreach ($this->additionalQueries as $query) {
658 $query_string = strtr($query['query'], $query['args']);
661 '#markup' => t('[@time ms] @query', ['@time' => round($query['time'] * 100000, 1) / 100000.0, '@query' => $query_string]),
668 '#type' => 'inline_template',
669 '#template' => "<strong>{% trans 'Other queries' %}</strong>",
674 '#prefix' => '<pre>',
675 'queries' => $queries,
676 '#suffix' => '</pre>',
686 '#type' => 'inline_template',
687 '#template' => "<strong>{% trans 'Title' %}</strong>",
692 '#markup' => $executable->getTitle(),
697 // @todo Views should expect and store a leading /. See:
698 // https://www.drupal.org/node/2423913
699 $path = \Drupal::l($path->toString(), $path);
702 $path = t('This display has no path.');
707 '#prefix' => '<strong>',
708 '#markup' => t('Path'),
709 '#suffix' => '</strong>',
720 $rows['statistics'][] = [
723 '#type' => 'inline_template',
724 '#template' => "<strong>{% trans 'Query build time' %}</strong>",
727 t('@time ms', ['@time' => intval($executable->build_time * 100000) / 100]),
730 $rows['statistics'][] = [
733 '#type' => 'inline_template',
734 '#template' => "<strong>{% trans 'Query execute time' %}</strong>",
737 t('@time ms', ['@time' => intval($executable->execute_time * 100000) / 100]),
740 $rows['statistics'][] = [
743 '#type' => 'inline_template',
744 '#template' => "<strong>{% trans 'View render time' %}</strong>",
747 t('@time ms', ['@time' => intval($this->render_time * 100) / 100]),
750 \Drupal::moduleHandler()->alter('views_preview_info', $rows, $executable);
753 // No query was run. Display that information in place of either the
754 // query or the performance statistics, whichever comes first.
755 if ($combined || ($show_location === 'above')) {
759 '#prefix' => '<strong>',
760 '#markup' => t('Query'),
761 '#suffix' => '</strong>',
766 '#markup' => t('No query was run'),
772 $rows['statistics'][] = [
775 '#prefix' => '<strong>',
776 '#markup' => t('Query'),
777 '#suffix' => '</strong>',
782 '#markup' => t('No query was run'),
791 foreach ($errors as $display_errors) {
792 foreach ($display_errors as $error) {
793 \Drupal::messenger()->addError($error);
796 $preview = ['#markup' => t('Unable to preview due to validation errors.')];
799 // Assemble the preview, the query info, and the query statistics in the
803 '#prefix' => '<div class="views-query-info">',
804 '#suffix' => '</div>',
805 '#rows' => array_merge($rows['query'], $rows['statistics']),
808 if ($show_location == 'above') {
811 'preview' => $preview,
816 'preview' => $preview,
821 // Ensure that we just remove an additional request we pushed earlier.
822 // This could happen if $errors was not empty.
823 if ($request_stack->getCurrentRequest() != $current_request) {
824 $request_stack->pop();
830 * Get the user's current progress through the form stack.
833 * FALSE if the user is not currently in a multiple-form stack. Otherwise,
834 * an associative array with the following keys:
835 * - current: The number of the current form on the stack.
836 * - total: The total number of forms originally on the stack.
838 public function getFormProgress() {
840 if (!empty($this->stack)) {
841 // The forms on the stack have integer keys that don't change as the forms
842 // are completed, so we can see which ones are still left.
843 $keys = array_keys($this->stack);
844 // Add 1 to the array keys for the benefit of humans, who start counting
846 $current = reset($keys) + 1;
847 $total = end($keys) + 1;
850 $progress['current'] = $current;
851 $progress['total'] = $total;
858 * Sets a cached view object in the shared tempstore.
860 public function cacheSet() {
861 if ($this->isLocked()) {
862 \Drupal::messenger()->addError(t('Changes cannot be made to a locked view.'));
866 // Let any future object know that this view has changed.
867 $this->changed = TRUE;
869 $executable = $this->getExecutable();
870 if (isset($executable->current_display)) {
871 // Add the knowledge of the changed display, too.
872 $this->changed_display[$executable->current_display] = TRUE;
873 $executable->current_display = NULL;
876 // Unset handlers. We don't want to write these into the cache.
877 $executable->display_handler = NULL;
878 $executable->default_display = NULL;
879 $executable->query = NULL;
880 $executable->displayHandlers = NULL;
881 \Drupal::service('tempstore.shared')->get('views')->set($this->id(), $this);
885 * Returns whether the current view is locked.
888 * TRUE if the view is locked, FALSE otherwise.
890 public function isLocked() {
891 return is_object($this->lock) && ($this->lock->owner != \Drupal::currentUser()->id());
895 * Passes through all unknown calls onto the storage object.
897 public function __call($method, $args) {
898 return call_user_func_array([$this->storage, $method], $args);
904 public function &getDisplay($display_id) {
905 return $this->storage->getDisplay($display_id);
911 public function id() {
912 return $this->storage->id();
918 public function uuid() {
919 return $this->storage->uuid();
925 public function isNew() {
926 return $this->storage->isNew();
932 public function getEntityTypeId() {
933 return $this->storage->getEntityTypeId();
939 public function bundle() {
940 return $this->storage->bundle();
946 public function getEntityType() {
947 return $this->storage->getEntityType();
953 public function createDuplicate() {
954 return $this->storage->createDuplicate();
960 public static function load($id) {
961 return View::load($id);
967 public static function loadMultiple(array $ids = NULL) {
968 return View::loadMultiple($ids);
974 public static function create(array $values = []) {
975 return View::create($values);
981 public function delete() {
982 return $this->storage->delete();
988 public function save() {
989 return $this->storage->save();
995 public function urlInfo($rel = 'edit-form', array $options = []) {
996 return $this->storage->urlInfo($rel, $options);
1002 public function toUrl($rel = 'edit-form', array $options = []) {
1003 return $this->storage->toUrl($rel, $options);
1009 public function link($text = NULL, $rel = 'edit-form', array $options = []) {
1010 return $this->storage->link($text, $rel, $options);
1016 public function toLink($text = NULL, $rel = 'edit-form', array $options = []) {
1017 return $this->storage->toLink($text, $rel, $options);
1023 public function label() {
1024 return $this->storage->label();
1030 public function enforceIsNew($value = TRUE) {
1031 return $this->storage->enforceIsNew($value);
1037 public function toArray() {
1038 return $this->storage->toArray();
1044 public function language() {
1045 return $this->storage->language();
1051 public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) {
1052 return $this->storage->access($operation, $account, $return_as_object);
1058 public function enable() {
1059 return $this->storage->enable();
1065 public function disable() {
1066 return $this->storage->disable();
1072 public function status() {
1073 return $this->storage->status();
1079 public function getOriginalId() {
1080 return $this->storage->getOriginalId();
1086 public function setOriginalId($id) {
1087 return $this->storage->setOriginalId($id);
1093 public function preSave(EntityStorageInterface $storage) {
1094 $this->storage->presave($storage);
1100 public function postSave(EntityStorageInterface $storage, $update = TRUE) {
1101 $this->storage->postSave($storage, $update);
1107 public static function preCreate(EntityStorageInterface $storage, array &$values) {
1113 public function postCreate(EntityStorageInterface $storage) {
1114 $this->storage->postCreate($storage);
1120 public static function preDelete(EntityStorageInterface $storage, array $entities) {
1126 public static function postDelete(EntityStorageInterface $storage, array $entities) {
1132 public static function postLoad(EntityStorageInterface $storage, array &$entities) {
1138 public function getExecutable() {
1139 return $this->storage->getExecutable();
1145 public function duplicateDisplayAsType($old_display_id, $new_display_type) {
1146 return $this->storage->duplicateDisplayAsType($old_display_id, $new_display_type);
1152 public function mergeDefaultDisplaysOptions() {
1153 $this->storage->mergeDefaultDisplaysOptions();
1159 public function uriRelationships() {
1160 return $this->storage->uriRelationships();
1166 public function referencedEntities() {
1167 return $this->storage->referencedEntities();
1173 public function url($rel = 'edit-form', $options = []) {
1174 return $this->storage->url($rel, $options);
1180 public function hasLinkTemplate($key) {
1181 return $this->storage->hasLinkTemplate($key);
1187 public function calculateDependencies() {
1188 $this->storage->calculateDependencies();
1195 public function getConfigDependencyKey() {
1196 return $this->storage->getConfigDependencyKey();
1202 public function getConfigDependencyName() {
1203 return $this->storage->getConfigDependencyName();
1209 public function getConfigTarget() {
1210 return $this->storage->getConfigTarget();
1216 public function onDependencyRemoval(array $dependencies) {
1217 return $this->storage->onDependencyRemoval($dependencies);
1223 public function getDependencies() {
1224 return $this->storage->getDependencies();
1230 public function getCacheContexts() {
1231 return $this->storage->getCacheContexts();
1237 public function getCacheTags() {
1238 return $this->storage->getCacheTags();
1244 public function getCacheMaxAge() {
1245 return $this->storage->getCacheMaxAge();
1251 public function getTypedData() {
1252 $this->storage->getTypedData();
1258 public function addDisplay($plugin_id = 'page', $title = NULL, $id = NULL) {
1259 return $this->storage->addDisplay($plugin_id, $title, $id);
1265 public function isInstallable() {
1266 return $this->storage->isInstallable();
1272 public function setThirdPartySetting($module, $key, $value) {
1273 return $this->storage->setThirdPartySetting($module, $key, $value);
1279 public function getThirdPartySetting($module, $key, $default = NULL) {
1280 return $this->storage->getThirdPartySetting($module, $key, $default);
1286 public function getThirdPartySettings($module) {
1287 return $this->storage->getThirdPartySettings($module);
1293 public function unsetThirdPartySetting($module, $key) {
1294 return $this->storage->unsetThirdPartySetting($module, $key);
1300 public function getThirdPartyProviders() {
1301 return $this->storage->getThirdPartyProviders();
1307 public function trustData() {
1308 return $this->storage->trustData();
1314 public function hasTrustedData() {
1315 return $this->storage->hasTrustedData();
1321 public function addCacheableDependency($other_object) {
1322 $this->storage->addCacheableDependency($other_object);
1329 public function addCacheContexts(array $cache_contexts) {
1330 return $this->storage->addCacheContexts($cache_contexts);
1336 public function mergeCacheMaxAge($max_age) {
1337 return $this->storage->mergeCacheMaxAge($max_age);
1343 public function getCacheTagsToInvalidate() {
1344 return $this->storage->getCacheTagsToInvalidate();
1350 public function addCacheTags(array $cache_tags) {
1351 return $this->storage->addCacheTags($cache_tags);