3 namespace Drupal\views_ui;
5 use Drupal\Component\Utility\Html;
6 use Drupal\Component\Utility\Timer;
7 use Drupal\Component\Utility\Xss;
8 use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
9 use Drupal\Core\Form\FormStateInterface;
10 use Drupal\views\Views;
11 use Drupal\Core\Entity\EntityStorageInterface;
12 use Drupal\views\ViewExecutable;
13 use Drupal\Core\Database\Database;
14 use Drupal\Core\Session\AccountInterface;
15 use Drupal\views\Plugin\views\query\Sql;
16 use Drupal\views\Entity\View;
17 use Drupal\views\ViewEntityInterface;
18 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
19 use Symfony\Component\HttpFoundation\ParameterBag;
20 use Symfony\Component\HttpFoundation\Request;
23 * Stores UI related temporary settings.
25 class ViewUI implements ViewEntityInterface {
28 * Indicates if a view is currently being edited.
32 public $editing = FALSE;
35 * Stores an array of displays that have been changed.
39 public $changed_display;
42 * How long the view takes to render in microseconds.
49 * If this view is locked for editing.
51 * If this view is locked it will contain the result of
52 * \Drupal\user\SharedTempStore::getMetadata(). Which can be a stdClass or
60 * If this view has been changed.
67 * Stores options temporarily while editing.
71 public $temporary_options;
74 * Stores a stack of UI forms to display.
81 * Is the view run in a context of the preview in the admin interface.
87 public $renderPreview = FALSE;
90 * The View storage object.
92 * @var \Drupal\views\ViewEntityInterface
97 * Stores a list of database queries run beside the main one from views.
101 * @see \Drupal\Core\Database\Log
103 protected $additionalQueries;
106 * Contains an array of form keys and their respective classes.
110 public static $forms = [
111 'add-handler' => '\Drupal\views_ui\Form\Ajax\AddItem',
112 'analyze' => '\Drupal\views_ui\Form\Ajax\Analyze',
113 'handler' => '\Drupal\views_ui\Form\Ajax\ConfigHandler',
114 'handler-extra' => '\Drupal\views_ui\Form\Ajax\ConfigHandlerExtra',
115 'handler-group' => '\Drupal\views_ui\Form\Ajax\ConfigHandlerGroup',
116 'display' => '\Drupal\views_ui\Form\Ajax\Display',
117 'edit-details' => '\Drupal\views_ui\Form\Ajax\EditDetails',
118 'rearrange' => '\Drupal\views_ui\Form\Ajax\Rearrange',
119 'rearrange-filter' => '\Drupal\views_ui\Form\Ajax\RearrangeFilter',
120 'reorder-displays' => '\Drupal\views_ui\Form\Ajax\ReorderDisplays',
124 * Whether the config is being created, updated or deleted through the
129 private $isSyncing = FALSE;
132 * Whether the config is being deleted through the uninstall process.
136 private $isUninstalling = FALSE;
139 * Constructs a View UI object.
141 * @param \Drupal\views\ViewEntityInterface $storage
142 * The View storage object to wrap.
144 public function __construct(ViewEntityInterface $storage) {
145 $this->entityType = 'view';
146 $this->storage = $storage;
152 public function get($property_name, $langcode = NULL) {
153 if (property_exists($this->storage, $property_name)) {
154 return $this->storage->get($property_name, $langcode);
157 return isset($this->{$property_name}) ? $this->{$property_name} : NULL;
163 public function setStatus($status) {
164 return $this->storage->setStatus($status);
170 public function set($property_name, $value, $notify = TRUE) {
171 if (property_exists($this->storage, $property_name)) {
172 $this->storage->set($property_name, $value);
175 $this->{$property_name} = $value;
182 public function setSyncing($syncing) {
183 $this->isSyncing = $syncing;
189 public function setUninstalling($isUninstalling) {
190 $this->isUninstalling = $isUninstalling;
196 public function isSyncing() {
197 return $this->isSyncing;
203 public function isUninstalling() {
204 return $this->isUninstalling;
208 * Basic submit handler applicable to all 'standard' forms.
210 * This submit handler determines whether the user wants the submitted changes
211 * to apply to the default display or to the current display, and dispatches
212 * control appropriately.
214 public function standardSubmit($form, FormStateInterface $form_state) {
215 // Determine whether the values the user entered are intended to apply to
216 // the current display or the default display.
218 list($was_defaulted, $is_defaulted, $revert) = $this->getOverrideValues($form, $form_state);
220 // Based on the user's choice in the display dropdown, determine which display
221 // these changes apply to.
222 $display_id = $form_state->get('display_id');
224 // If it's revert just change the override and return.
225 $display = &$this->getExecutable()->displayHandlers->get($display_id);
226 $display->optionsOverride($form, $form_state);
228 // Don't execute the normal submit handling but still store the changed view into cache.
232 elseif ($was_defaulted === $is_defaulted) {
233 // We're not changing which display these form values apply to.
234 // Run the regular submit handler for this form.
236 elseif ($was_defaulted && !$is_defaulted) {
237 // We were using the default display's values, but we're now overriding
238 // the default display and saving values specific to this display.
239 $display = &$this->getExecutable()->displayHandlers->get($display_id);
240 // optionsOverride toggles the override of this section.
241 $display->optionsOverride($form, $form_state);
242 $display->submitOptionsForm($form, $form_state);
244 elseif (!$was_defaulted && $is_defaulted) {
245 // We used to have an override for this display, but the user now wants
246 // to go back to the default display.
247 // Overwrite the default display with the current form values, and make
248 // the current display use the new default values.
249 $display = &$this->getExecutable()->displayHandlers->get($display_id);
250 // optionsOverride toggles the override of this section.
251 $display->optionsOverride($form, $form_state);
252 $display->submitOptionsForm($form, $form_state);
255 $submit_handler = [$form_state->getFormObject(), 'submitForm'];
256 call_user_func_array($submit_handler, [&$form, $form_state]);
260 * Submit handler for cancel button
262 public function standardCancel($form, FormStateInterface $form_state) {
263 if (!empty($this->changed) && isset($this->form_cache)) {
264 unset($this->form_cache);
268 $form_state->setRedirectUrl($this->urlInfo('edit-form'));
272 * Provide a standard set of Apply/Cancel/OK buttons for the forms. Also provide
273 * a hidden op operator because the forms plugin doesn't seem to properly
274 * provide which button was clicked.
276 * TODO: Is the hidden op operator still here somewhere, or is that part of the
279 public function getStandardButtons(&$form, FormStateInterface $form_state, $form_id, $name = NULL) {
281 '#type' => 'actions',
286 if (!empty($this->stack) && count($this->stack) > 1) {
287 $name = t('Apply and continue');
289 $names = [t('Apply'), t('Apply and continue')];
292 // Forms that are purely informational set an ok_button flag, so we know not
293 // to create an "Apply" button for them.
294 if (!$form_state->get('ok_button')) {
295 $form['actions']['submit'] = [
298 '#id' => 'edit-submit-' . Html::getUniqueId($form_id),
299 // The regular submit handler ($form_id . '_submit') does not apply if
300 // we're updating the default display. It does apply if we're updating
301 // the current display. Since we have no way of knowing at this point
302 // which display the user wants to update, views_ui_standard_submit will
303 // take care of running the regular submit handler as appropriate.
304 '#submit' => [[$this, 'standardSubmit']],
305 '#button_type' => 'primary',
307 // Form API button click detection requires the button's #value to be the
308 // same between the form build of the initial page request, and the
309 // initial form build of the request processing the form submission.
310 // Ideally, the button's #value shouldn't change until the form rebuild
311 // step. However, \Drupal\views_ui\Form\Ajax\ViewsFormBase::getForm()
312 // implements a different multistep form workflow than the Form API does,
313 // and adjusts $view->stack prior to form processing, so we compensate by
314 // extending button click detection code to support any of the possible
317 $form['actions']['submit']['#values'] = $names;
318 $form['actions']['submit']['#process'] = array_merge(['views_ui_form_button_was_clicked'], \Drupal::service('element_info')->getInfoProperty($form['actions']['submit']['#type'], '#process', []));
320 // If a validation handler exists for the form, assign it to this button.
321 $form['actions']['submit']['#validate'][] = [$form_state->getFormObject(), 'validateForm'];
324 // Create a "Cancel" button. For purely informational forms, label it "OK".
325 $cancel_submit = function_exists($form_id . '_cancel') ? $form_id . '_cancel' : [$this, 'standardCancel'];
326 $form['actions']['cancel'] = [
328 '#value' => !$form_state->get('ok_button') ? t('Cancel') : t('Ok'),
329 '#submit' => [$cancel_submit],
331 '#limit_validation_errors' => [],
334 // Compatibility, to be removed later: // TODO: When is "later"?
335 // We used to set these items on the form, but now we want them on the $form_state:
336 if (isset($form['#title'])) {
337 $form_state->set('title', $form['#title']);
339 if (isset($form['#section'])) {
340 $form_state->set('#section', $form['#section']);
342 // Finally, we never want these cached -- our object cache does that for us.
343 $form['#no_cache'] = TRUE;
347 * Return the was_defaulted, is_defaulted and revert state of a form.
349 public function getOverrideValues($form, FormStateInterface $form_state) {
350 // Make sure the dropdown exists in the first place.
351 if ($form_state->hasValue(['override', 'dropdown'])) {
352 // #default_value is used to determine whether it was the default value or not.
353 // So the available options are: $display, 'default' and 'default_revert', not 'defaults'.
354 $was_defaulted = ($form['override']['dropdown']['#default_value'] === 'defaults');
355 $dropdown = $form_state->getValue(['override', 'dropdown']);
356 $is_defaulted = ($dropdown === 'default');
357 $revert = ($dropdown === 'default_revert');
359 if ($was_defaulted !== $is_defaulted && isset($form['#section'])) {
360 // We're changing which display these values apply to.
361 // Update the #section so it knows what to mark changed.
362 $form['#section'] = str_replace('default-', $form_state->get('display_id') . '-', $form['#section']);
366 // The user didn't get the dropdown for overriding the default display.
367 $was_defaulted = FALSE;
368 $is_defaulted = FALSE;
372 return [$was_defaulted, $is_defaulted, $revert];
376 * Add another form to the stack; clicking 'apply' will go to this form
377 * rather than closing the ajax popup.
379 public function addFormToStack($key, $display_id, $type, $id = NULL, $top = FALSE, $rebuild_keys = FALSE) {
380 // Reset the cache of IDs. Drupal rather aggressively prevents ID
381 // duplication but this causes it to remember IDs that are no longer even
383 Html::resetSeenIds();
385 if (empty($this->stack)) {
389 $stack = [implode('-', array_filter([$key, $this->id(), $display_id, $type, $id])), $key, $display_id, $type, $id];
390 // If we're being asked to add this form to the bottom of the stack, no
391 // special logic is required. Our work is equally easy if we were asked to add
392 // to the top of the stack, but there's nothing in it yet.
393 if (!$top || empty($this->stack)) {
394 $this->stack[] = $stack;
396 // If we're adding to the top of an existing stack, we have to maintain the
397 // existing integer keys, so they can be used for the "2 of 3" progress
398 // indicator (which will now read "2 of 4").
400 $keys = array_keys($this->stack);
401 $first = current($keys);
403 for ($i = $last; $i >= $first; $i--) {
404 if (!isset($this->stack[$i])) {
407 // Move form number $i to the next position in the stack.
408 $this->stack[$i + 1] = $this->stack[$i];
409 unset($this->stack[$i]);
411 // Now that the previously $first slot is free, move the new form into it.
412 $this->stack[$first] = $stack;
415 // Start the keys from 0 again, if requested.
417 $this->stack = array_values($this->stack);
423 * Submit handler for adding new item(s) to a view.
425 public function submitItemAdd($form, FormStateInterface $form_state) {
426 $type = $form_state->get('type');
427 $types = ViewExecutable::getHandlerTypes();
428 $section = $types[$type]['plural'];
429 $display_id = $form_state->get('display_id');
431 // Handle the override select.
432 list($was_defaulted, $is_defaulted) = $this->getOverrideValues($form, $form_state);
433 if ($was_defaulted && !$is_defaulted) {
434 // We were using the default display's values, but we're now overriding
435 // the default display and saving values specific to this display.
436 $display = &$this->getExecutable()->displayHandlers->get($display_id);
437 // setOverride toggles the override of this section.
438 $display->setOverride($section);
440 elseif (!$was_defaulted && $is_defaulted) {
441 // We used to have an override for this display, but the user now wants
442 // to go back to the default display.
443 // Overwrite the default display with the current form values, and make
444 // the current display use the new default values.
445 $display = &$this->getExecutable()->displayHandlers->get($display_id);
446 // optionsOverride toggles the override of this section.
447 $display->setOverride($section);
450 if (!$form_state->isValueEmpty('name') && is_array($form_state->getValue('name'))) {
451 // Loop through each of the items that were checked and add them to the view.
452 foreach (array_keys(array_filter($form_state->getValue('name'))) as $field) {
453 list($table, $field) = explode('.', $field, 2);
455 if ($cut = strpos($field, '$')) {
456 $field = substr($field, 0, $cut);
458 $id = $this->getExecutable()->addHandler($display_id, $type, $table, $field);
460 // check to see if we have group by settings
462 // Footer,header and empty text have a different internal handler type(area).
463 if (isset($types[$type]['type'])) {
464 $key = $types[$type]['type'];
470 $handler = Views::handlerManager($key)->getHandler($item);
471 if ($this->getExecutable()->displayHandlers->get('default')->useGroupBy() && $handler->usesGroupBy()) {
472 $this->addFormToStack('handler-group', $display_id, $type, $id);
475 // check to see if this type has settings, if so add the settings form first
476 if ($handler && $handler->hasExtraOptions()) {
477 $this->addFormToStack('handler-extra', $display_id, $type, $id);
479 // Then add the form to the stack
480 $this->addFormToStack('handler', $display_id, $type, $id);
484 if (isset($this->form_cache)) {
485 unset($this->form_cache);
493 * Set up query capturing.
495 * \Drupal\Core\Database\Database stores the queries that it runs, if logging
498 * @see ViewUI::endQueryCapture()
500 public function startQueryCapture() {
501 Database::startLog('views');
505 * Add the list of queries run during render to buildinfo.
507 * @see ViewUI::startQueryCapture()
509 public function endQueryCapture() {
510 $queries = Database::getLog('views');
512 $this->additionalQueries = $queries;
515 public function renderPreview($display_id, $args = []) {
516 // Save the current path so it can be restored before returning from this function.
517 $request_stack = \Drupal::requestStack();
518 $current_request = $request_stack->getCurrentRequest();
519 $executable = $this->getExecutable();
521 // Determine where the query and performance statistics should be output.
522 $config = \Drupal::config('views.settings');
523 $show_query = $config->get('ui.show.sql_query.enabled');
524 $show_info = $config->get('ui.show.preview_information');
525 $show_location = $config->get('ui.show.sql_query.where');
527 $show_stats = $config->get('ui.show.performance_statistics');
529 $show_stats = $config->get('ui.show.sql_query.where');
532 $combined = $show_query && $show_stats;
534 $rows = ['query' => [], 'statistics' => []];
536 $errors = $executable->validate();
537 $executable->destroy();
538 if (empty($errors)) {
540 $executable->live_preview = TRUE;
542 // AJAX happens via HTTP POST but everything expects exposed data to
543 // be in GET. Copy stuff but remove ajax-framework specific keys.
544 // If we're clicking on links in a preview, though, we could actually
545 // have some input in the query parameters, so we merge request() and
546 // query() to ensure we get it all.
547 $exposed_input = array_merge(\Drupal::request()->request->all(), \Drupal::request()->query->all());
548 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) {
549 if (isset($exposed_input[$key])) {
550 unset($exposed_input[$key]);
553 $executable->setExposedInput($exposed_input);
555 if (!$executable->setDisplay($display_id)) {
557 '#markup' => t('Invalid display id @display', ['@display' => $display_id]),
561 $executable->setArguments($args);
563 // Store the current view URL for later use:
564 if ($executable->hasUrl() && $executable->display_handler->getOption('path')) {
565 $path = $executable->getUrl();
568 // Make view links come back to preview.
570 // Also override the current path so we get the pager, and make sure the
571 // Request object gets all of the proper values from $_SERVER.
572 $request = Request::createFromGlobals();
573 $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'entity.view.preview_form');
574 $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, \Drupal::service('router.route_provider')->getRouteByName('entity.view.preview_form'));
575 $request->attributes->set('view', $this->storage);
576 $request->attributes->set('display_id', $display_id);
577 $raw_parameters = new ParameterBag();
578 $raw_parameters->set('view', $this->id());
579 $raw_parameters->set('display_id', $display_id);
580 $request->attributes->set('_raw_variables', $raw_parameters);
582 foreach ($args as $key => $arg) {
583 $request->attributes->set('arg_' . $key, $arg);
585 $request_stack->push($request);
587 // Suppress contextual links of entities within the result set during a
589 // @todo We'll want to add contextual links specific to editing the View, so
590 // the suppression may need to be moved deeper into the Preview pipeline.
591 views_ui_contextual_links_suppress_push();
593 $show_additional_queries = $config->get('ui.show.additional_queries');
595 Timer::start('entity.view.preview_form');
597 if ($show_additional_queries) {
598 $this->startQueryCapture();
601 // Execute/get the view preview.
602 $preview = $executable->preview($display_id, $args);
604 if ($show_additional_queries) {
605 $this->endQueryCapture();
608 $this->render_time = Timer::stop('entity.view.preview_form')['time'];
610 views_ui_contextual_links_suppress_pop();
612 // Prepare the query information and statistics to show either above or
613 // below the view preview.
614 // Initialise the empty rows arrays so we can safely merge them later.
616 $rows['statistics'] = [];
617 if ($show_info || $show_query || $show_stats) {
618 // Get information from the preview for display.
619 if (!empty($executable->build_info['query'])) {
621 $query_string = $executable->build_info['query'];
622 // Only the sql default class has a method getArguments.
625 if ($executable->query instanceof Sql) {
626 $quoted = $query_string->getArguments();
627 $connection = Database::getConnection();
628 foreach ($quoted as $key => $val) {
629 if (is_array($val)) {
630 $quoted[$key] = implode(', ', array_map([$connection, 'quote'], $val));
633 $quoted[$key] = $connection->quote($val);
640 '#type' => 'inline_template',
641 '#template' => "<strong>{% trans 'Query' %}</strong>",
646 '#type' => 'inline_template',
647 '#template' => '<pre>{{ query }}</pre>',
648 '#context' => ['query' => strtr($query_string, $quoted)],
652 if (!empty($this->additionalQueries)) {
654 '#prefix' => '<strong>',
655 '#markup' => t('These queries were run during view rendering:'),
656 '#suffix' => '</strong>',
658 foreach ($this->additionalQueries as $query) {
659 $query_string = strtr($query['query'], $query['args']);
662 '#markup' => t('[@time ms] @query', ['@time' => round($query['time'] * 100000, 1) / 100000.0, '@query' => $query_string]),
669 '#type' => 'inline_template',
670 '#template' => "<strong>{% trans 'Other queries' %}</strong>",
675 '#prefix' => '<pre>',
676 'queries' => $queries,
677 '#suffix' => '</pre>',
687 '#type' => 'inline_template',
688 '#template' => "<strong>{% trans 'Title' %}</strong>",
691 Xss::filterAdmin($executable->getTitle()),
694 // @todo Views should expect and store a leading /. See:
695 // https://www.drupal.org/node/2423913
696 $path = \Drupal::l($path->toString(), $path);
699 $path = t('This display has no path.');
704 '#prefix' => '<strong>',
705 '#markup' => t('Path'),
706 '#suffix' => '</strong>',
717 $rows['statistics'][] = [
720 '#type' => 'inline_template',
721 '#template' => "<strong>{% trans 'Query build time' %}</strong>",
724 t('@time ms', ['@time' => intval($executable->build_time * 100000) / 100]),
727 $rows['statistics'][] = [
730 '#type' => 'inline_template',
731 '#template' => "<strong>{% trans 'Query execute time' %}</strong>",
734 t('@time ms', ['@time' => intval($executable->execute_time * 100000) / 100]),
737 $rows['statistics'][] = [
740 '#type' => 'inline_template',
741 '#template' => "<strong>{% trans 'View render time' %}</strong>",
744 t('@time ms', ['@time' => intval($this->render_time * 100) / 100]),
747 \Drupal::moduleHandler()->alter('views_preview_info', $rows, $executable);
750 // No query was run. Display that information in place of either the
751 // query or the performance statistics, whichever comes first.
752 if ($combined || ($show_location === 'above')) {
756 '#prefix' => '<strong>',
757 '#markup' => t('Query'),
758 '#suffix' => '</strong>',
763 '#markup' => t('No query was run'),
769 $rows['statistics'][] = [
772 '#prefix' => '<strong>',
773 '#markup' => t('Query'),
774 '#suffix' => '</strong>',
779 '#markup' => t('No query was run'),
788 foreach ($errors as $display_errors) {
789 foreach ($display_errors as $error) {
790 drupal_set_message($error, 'error');
793 $preview = ['#markup' => t('Unable to preview due to validation errors.')];
796 // Assemble the preview, the query info, and the query statistics in the
800 '#prefix' => '<div class="views-query-info">',
801 '#suffix' => '</div>',
802 '#rows' => array_merge($rows['query'], $rows['statistics']),
805 if ($show_location == 'above') {
808 'preview' => $preview,
813 'preview' => $preview,
818 // Ensure that we just remove an additional request we pushed earlier.
819 // This could happen if $errors was not empty.
820 if ($request_stack->getCurrentRequest() != $current_request) {
821 $request_stack->pop();
827 * Get the user's current progress through the form stack.
830 * FALSE if the user is not currently in a multiple-form stack. Otherwise,
831 * an associative array with the following keys:
832 * - current: The number of the current form on the stack.
833 * - total: The total number of forms originally on the stack.
835 public function getFormProgress() {
837 if (!empty($this->stack)) {
838 // The forms on the stack have integer keys that don't change as the forms
839 // are completed, so we can see which ones are still left.
840 $keys = array_keys($this->stack);
841 // Add 1 to the array keys for the benefit of humans, who start counting
843 $current = reset($keys) + 1;
844 $total = end($keys) + 1;
847 $progress['current'] = $current;
848 $progress['total'] = $total;
855 * Sets a cached view object in the user tempstore.
857 public function cacheSet() {
858 if ($this->isLocked()) {
859 drupal_set_message(t('Changes cannot be made to a locked view.'), 'error');
863 // Let any future object know that this view has changed.
864 $this->changed = TRUE;
866 $executable = $this->getExecutable();
867 if (isset($executable->current_display)) {
868 // Add the knowledge of the changed display, too.
869 $this->changed_display[$executable->current_display] = TRUE;
870 $executable->current_display = NULL;
873 // Unset handlers. We don't want to write these into the cache.
874 $executable->display_handler = NULL;
875 $executable->default_display = NULL;
876 $executable->query = NULL;
877 $executable->displayHandlers = NULL;
878 \Drupal::service('user.shared_tempstore')->get('views')->set($this->id(), $this);
882 * Returns whether the current view is locked.
885 * TRUE if the view is locked, FALSE otherwise.
887 public function isLocked() {
888 return is_object($this->lock) && ($this->lock->owner != \Drupal::currentUser()->id());
892 * Passes through all unknown calls onto the storage object.
894 public function __call($method, $args) {
895 return call_user_func_array([$this->storage, $method], $args);
901 public function &getDisplay($display_id) {
902 return $this->storage->getDisplay($display_id);
908 public function id() {
909 return $this->storage->id();
915 public function uuid() {
916 return $this->storage->uuid();
922 public function isNew() {
923 return $this->storage->isNew();
929 public function getEntityTypeId() {
930 return $this->storage->getEntityTypeId();
936 public function bundle() {
937 return $this->storage->bundle();
943 public function getEntityType() {
944 return $this->storage->getEntityType();
950 public function createDuplicate() {
951 return $this->storage->createDuplicate();
957 public static function load($id) {
958 return View::load($id);
964 public static function loadMultiple(array $ids = NULL) {
965 return View::loadMultiple($ids);
971 public static function create(array $values = []) {
972 return View::create($values);
978 public function delete() {
979 return $this->storage->delete();
985 public function save() {
986 return $this->storage->save();
992 public function urlInfo($rel = 'edit-form', array $options = []) {
993 return $this->storage->urlInfo($rel, $options);
999 public function toUrl($rel = 'edit-form', array $options = []) {
1000 return $this->storage->toUrl($rel, $options);
1006 public function link($text = NULL, $rel = 'edit-form', array $options = []) {
1007 return $this->storage->link($text, $rel, $options);
1013 public function toLink($text = NULL, $rel = 'edit-form', array $options = []) {
1014 return $this->storage->toLink($text, $rel, $options);
1020 public function label() {
1021 return $this->storage->label();
1027 public function enforceIsNew($value = TRUE) {
1028 return $this->storage->enforceIsNew($value);
1034 public function toArray() {
1035 return $this->storage->toArray();
1041 public function language() {
1042 return $this->storage->language();
1048 public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) {
1049 return $this->storage->access($operation, $account, $return_as_object);
1055 public function enable() {
1056 return $this->storage->enable();
1062 public function disable() {
1063 return $this->storage->disable();
1069 public function status() {
1070 return $this->storage->status();
1076 public function getOriginalId() {
1077 return $this->storage->getOriginalId();
1083 public function setOriginalId($id) {
1084 return $this->storage->setOriginalId($id);
1090 public function preSave(EntityStorageInterface $storage) {
1091 $this->storage->presave($storage);
1097 public function postSave(EntityStorageInterface $storage, $update = TRUE) {
1098 $this->storage->postSave($storage, $update);
1104 public static function preCreate(EntityStorageInterface $storage, array &$values) {
1110 public function postCreate(EntityStorageInterface $storage) {
1111 $this->storage->postCreate($storage);
1117 public static function preDelete(EntityStorageInterface $storage, array $entities) {
1123 public static function postDelete(EntityStorageInterface $storage, array $entities) {
1129 public static function postLoad(EntityStorageInterface $storage, array &$entities) {
1135 public function getExecutable() {
1136 return $this->storage->getExecutable();
1142 public function duplicateDisplayAsType($old_display_id, $new_display_type) {
1143 return $this->storage->duplicateDisplayAsType($old_display_id, $new_display_type);
1149 public function mergeDefaultDisplaysOptions() {
1150 $this->storage->mergeDefaultDisplaysOptions();
1156 public function uriRelationships() {
1157 return $this->storage->uriRelationships();
1163 public function referencedEntities() {
1164 return $this->storage->referencedEntities();
1170 public function url($rel = 'edit-form', $options = []) {
1171 return $this->storage->url($rel, $options);
1177 public function hasLinkTemplate($key) {
1178 return $this->storage->hasLinkTemplate($key);
1184 public function calculateDependencies() {
1185 $this->storage->calculateDependencies();
1192 public function getConfigDependencyKey() {
1193 return $this->storage->getConfigDependencyKey();
1199 public function getConfigDependencyName() {
1200 return $this->storage->getConfigDependencyName();
1206 public function getConfigTarget() {
1207 return $this->storage->getConfigTarget();
1213 public function onDependencyRemoval(array $dependencies) {
1214 return $this->storage->onDependencyRemoval($dependencies);
1220 public function getDependencies() {
1221 return $this->storage->getDependencies();
1227 public function getCacheContexts() {
1228 return $this->storage->getCacheContexts();
1234 public function getCacheTags() {
1235 return $this->storage->getCacheTags();
1241 public function getCacheMaxAge() {
1242 return $this->storage->getCacheMaxAge();
1248 public function getTypedData() {
1249 $this->storage->getTypedData();
1255 public function addDisplay($plugin_id = 'page', $title = NULL, $id = NULL) {
1256 return $this->storage->addDisplay($plugin_id, $title, $id);
1262 public function isInstallable() {
1263 return $this->storage->isInstallable();
1269 public function setThirdPartySetting($module, $key, $value) {
1270 return $this->storage->setThirdPartySetting($module, $key, $value);
1276 public function getThirdPartySetting($module, $key, $default = NULL) {
1277 return $this->storage->getThirdPartySetting($module, $key, $default);
1283 public function getThirdPartySettings($module) {
1284 return $this->storage->getThirdPartySettings($module);
1290 public function unsetThirdPartySetting($module, $key) {
1291 return $this->storage->unsetThirdPartySetting($module, $key);
1297 public function getThirdPartyProviders() {
1298 return $this->storage->getThirdPartyProviders();
1304 public function trustData() {
1305 return $this->storage->trustData();
1311 public function hasTrustedData() {
1312 return $this->storage->hasTrustedData();
1318 public function addCacheableDependency($other_object) {
1319 $this->storage->addCacheableDependency($other_object);
1326 public function addCacheContexts(array $cache_contexts) {
1327 return $this->storage->addCacheContexts($cache_contexts);
1333 public function mergeCacheMaxAge($max_age) {
1334 return $this->storage->mergeCacheMaxAge($max_age);
1340 public function getCacheTagsToInvalidate() {
1341 return $this->storage->getCacheTagsToInvalidate();
1347 public function addCacheTags(array $cache_tags) {
1348 return $this->storage->addCacheTags($cache_tags);