PageRenderTime 52ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 1ms

/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php

http://github.com/drupal/drupal
PHP | 2741 lines | 1942 code | 295 blank | 504 comment | 260 complexity | 00a7d8bbdaba9c09e19bf202500b090b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. namespace Drupal\views\Plugin\views\display;
  3. use Drupal\Component\Plugin\DependentPluginInterface;
  4. use Drupal\Component\Utility\Html;
  5. use Drupal\Component\Render\FormattableMarkup;
  6. use Drupal\Component\Utility\UrlHelper;
  7. use Drupal\Core\Cache\Cache;
  8. use Drupal\Core\Cache\CacheableMetadata;
  9. use Drupal\Core\Cache\CacheableDependencyInterface;
  10. use Drupal\Core\Form\FormStateInterface;
  11. use Drupal\Core\Language\LanguageInterface;
  12. use Drupal\Core\Plugin\PluginDependencyTrait;
  13. use Drupal\Core\Session\AccountInterface;
  14. use Drupal\Core\Link;
  15. use Drupal\Core\Url;
  16. use Drupal\views\Form\ViewsForm;
  17. use Drupal\views\Plugin\views\area\AreaPluginBase;
  18. use Drupal\views\ViewExecutable;
  19. use Drupal\views\Plugin\views\PluginBase;
  20. use Drupal\views\Views;
  21. /**
  22. * Base class for views display plugins.
  23. */
  24. abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInterface, DependentPluginInterface {
  25. use PluginDependencyTrait;
  26. /**
  27. * The top object of a view.
  28. *
  29. * @var \Drupal\views\ViewExecutable
  30. */
  31. public $view = NULL;
  32. /**
  33. * An array of instantiated handlers used in this display.
  34. *
  35. * @var \Drupal\views\Plugin\views\ViewsHandlerInterface[]
  36. */
  37. public $handlers = [];
  38. /**
  39. * An array of instantiated plugins used in this display.
  40. *
  41. * @var \Drupal\views\Plugin\views\ViewsPluginInterface[]
  42. */
  43. protected $plugins = [];
  44. /**
  45. * Stores all available display extenders.
  46. *
  47. * @var \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase[]
  48. */
  49. protected $extenders = [];
  50. /**
  51. * {@inheritdoc}
  52. */
  53. protected $usesOptions = TRUE;
  54. /**
  55. * Stores the rendered output of the display.
  56. *
  57. * @see View::render
  58. * @var string
  59. */
  60. public $output = NULL;
  61. /**
  62. * Whether the display allows the use of AJAX or not.
  63. *
  64. * @var bool
  65. */
  66. protected $usesAJAX = TRUE;
  67. /**
  68. * Whether the display allows the use of a pager or not.
  69. *
  70. * @var bool
  71. */
  72. protected $usesPager = TRUE;
  73. /**
  74. * Whether the display allows the use of a 'more' link or not.
  75. *
  76. * @var bool
  77. */
  78. protected $usesMore = TRUE;
  79. /**
  80. * Whether the display allows attachments.
  81. *
  82. * @var bool
  83. * TRUE if the display can use attachments, or FALSE otherwise.
  84. */
  85. protected $usesAttachments = FALSE;
  86. /**
  87. * Whether the display allows area plugins.
  88. *
  89. * @var bool
  90. */
  91. protected $usesAreas = TRUE;
  92. /**
  93. * Static cache for unpackOptions, but not if we are in the UI.
  94. *
  95. * @var array
  96. */
  97. protected static $unpackOptions = [];
  98. /**
  99. * The display information coming directly from the view entity.
  100. *
  101. * @see \Drupal\views\Entity\View::getDisplay()
  102. *
  103. * @todo \Drupal\views\Entity\View::duplicateDisplayAsType directly access it.
  104. *
  105. * @var array
  106. */
  107. public $display;
  108. /**
  109. * Constructs a new DisplayPluginBase object.
  110. *
  111. * Because DisplayPluginBase::initDisplay() takes the display configuration by
  112. * reference and handles it differently than usual plugin configuration, pass
  113. * an empty array of configuration to the parent. This prevents our
  114. * configuration from being duplicated.
  115. *
  116. * @todo Replace DisplayPluginBase::$display with
  117. * DisplayPluginBase::$configuration to standardize with other plugins.
  118. *
  119. * @param array $configuration
  120. * A configuration array containing information about the plugin instance.
  121. * @param string $plugin_id
  122. * The plugin_id for the plugin instance.
  123. * @param mixed $plugin_definition
  124. * The plugin implementation definition.
  125. */
  126. public function __construct(array $configuration, $plugin_id, $plugin_definition) {
  127. parent::__construct([], $plugin_id, $plugin_definition);
  128. }
  129. /**
  130. * {@inheritdoc}
  131. */
  132. public function initDisplay(ViewExecutable $view, array &$display, array &$options = NULL) {
  133. $this->view = $view;
  134. // Load extenders as soon as possible.
  135. $display['display_options'] += ['display_extenders' => []];
  136. $this->extenders = [];
  137. if ($extenders = Views::getEnabledDisplayExtenders()) {
  138. $manager = Views::pluginManager('display_extender');
  139. $display_extender_options = $display['display_options']['display_extenders'];
  140. foreach ($extenders as $extender) {
  141. /** @var \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase $plugin */
  142. if ($plugin = $manager->createInstance($extender)) {
  143. $extender_options = isset($display_extender_options[$plugin->getPluginId()]) ? $display_extender_options[$plugin->getPluginId()] : [];
  144. $plugin->init($this->view, $this, $extender_options);
  145. $this->extenders[$extender] = $plugin;
  146. }
  147. }
  148. }
  149. $this->setOptionDefaults($this->options, $this->defineOptions());
  150. $this->display = &$display;
  151. // Track changes that the user should know about.
  152. $changed = FALSE;
  153. if (!isset($options) && isset($display['display_options'])) {
  154. $options = $display['display_options'];
  155. }
  156. if ($this->isDefaultDisplay() && isset($options['defaults'])) {
  157. unset($options['defaults']);
  158. }
  159. $skip_cache = \Drupal::config('views.settings')->get('skip_cache');
  160. if (empty($view->editing) || !$skip_cache) {
  161. $cid = 'views:unpack_options:' . hash('sha256', serialize([$this->options, $options])) . ':' . \Drupal::languageManager()->getCurrentLanguage()->getId();
  162. if (empty(static::$unpackOptions[$cid])) {
  163. $cache = \Drupal::cache('data')->get($cid);
  164. if (!empty($cache->data)) {
  165. $this->options = $cache->data;
  166. }
  167. else {
  168. $this->unpackOptions($this->options, $options);
  169. \Drupal::cache('data')->set($cid, $this->options, Cache::PERMANENT, $this->view->storage->getCacheTags());
  170. }
  171. static::$unpackOptions[$cid] = $this->options;
  172. }
  173. else {
  174. $this->options = static::$unpackOptions[$cid];
  175. }
  176. }
  177. else {
  178. $this->unpackOptions($this->options, $options);
  179. }
  180. // Mark the view as changed so the user has a chance to save it.
  181. if ($changed) {
  182. $this->view->changed = TRUE;
  183. }
  184. }
  185. /**
  186. * {@inheritdoc}
  187. */
  188. public function destroy() {
  189. parent::destroy();
  190. foreach ($this->handlers as $type => $handlers) {
  191. foreach ($handlers as $id => $handler) {
  192. if (is_object($handler)) {
  193. $this->handlers[$type][$id]->destroy();
  194. }
  195. }
  196. }
  197. if (isset($this->default_display)) {
  198. unset($this->default_display);
  199. }
  200. foreach ($this->extenders as $extender) {
  201. $extender->destroy();
  202. }
  203. }
  204. /**
  205. * {@inheritdoc}
  206. */
  207. public function isDefaultDisplay() {
  208. return FALSE;
  209. }
  210. /**
  211. * {@inheritdoc}
  212. */
  213. public function usesExposed() {
  214. if (!isset($this->has_exposed)) {
  215. foreach ($this->handlers as $type => $value) {
  216. foreach ($this->view->$type as $handler) {
  217. if ($handler->canExpose() && $handler->isExposed()) {
  218. // One is all we need; if we find it, return TRUE.
  219. $this->has_exposed = TRUE;
  220. return TRUE;
  221. }
  222. }
  223. }
  224. $pager = $this->getPlugin('pager');
  225. if (isset($pager) && $pager->usesExposed()) {
  226. $this->has_exposed = TRUE;
  227. return TRUE;
  228. }
  229. $this->has_exposed = FALSE;
  230. }
  231. return $this->has_exposed;
  232. }
  233. /**
  234. * {@inheritdoc}
  235. */
  236. public function displaysExposed() {
  237. return TRUE;
  238. }
  239. /**
  240. * {@inheritdoc}
  241. */
  242. public function usesAJAX() {
  243. return $this->usesAJAX;
  244. }
  245. /**
  246. * {@inheritdoc}
  247. */
  248. public function ajaxEnabled() {
  249. if ($this->usesAJAX()) {
  250. return $this->getOption('use_ajax');
  251. }
  252. return FALSE;
  253. }
  254. /**
  255. * {@inheritdoc}
  256. */
  257. public function isEnabled() {
  258. return (bool) $this->getOption('enabled');
  259. }
  260. /**
  261. * {@inheritdoc}
  262. */
  263. public function usesPager() {
  264. return $this->usesPager;
  265. }
  266. /**
  267. * {@inheritdoc}
  268. */
  269. public function isPagerEnabled() {
  270. if ($this->usesPager()) {
  271. $pager = $this->getPlugin('pager');
  272. if ($pager) {
  273. return $pager->usePager();
  274. }
  275. }
  276. return FALSE;
  277. }
  278. /**
  279. * {@inheritdoc}
  280. */
  281. public function usesMore() {
  282. return $this->usesMore;
  283. }
  284. /**
  285. * {@inheritdoc}
  286. */
  287. public function isMoreEnabled() {
  288. if ($this->usesMore()) {
  289. return $this->getOption('use_more');
  290. }
  291. return FALSE;
  292. }
  293. /**
  294. * {@inheritdoc}
  295. */
  296. public function useGroupBy() {
  297. return $this->getOption('group_by');
  298. }
  299. /**
  300. * {@inheritdoc}
  301. */
  302. public function useMoreAlways() {
  303. if ($this->usesMore()) {
  304. return $this->getOption('use_more_always');
  305. }
  306. return FALSE;
  307. }
  308. /**
  309. * {@inheritdoc}
  310. */
  311. public function useMoreText() {
  312. if ($this->usesMore()) {
  313. return $this->getOption('use_more_text');
  314. }
  315. return FALSE;
  316. }
  317. /**
  318. * {@inheritdoc}
  319. */
  320. public function acceptAttachments() {
  321. if (!$this->usesAttachments()) {
  322. return FALSE;
  323. }
  324. if (!empty($this->view->argument) && $this->getOption('hide_attachment_summary')) {
  325. foreach ($this->view->argument as $argument) {
  326. if ($argument->needsStylePlugin() && empty($argument->argument_validated)) {
  327. return FALSE;
  328. }
  329. }
  330. }
  331. return TRUE;
  332. }
  333. /**
  334. * {@inheritdoc}
  335. */
  336. public function usesAttachments() {
  337. return $this->usesAttachments;
  338. }
  339. /**
  340. * {@inheritdoc}
  341. */
  342. public function usesAreas() {
  343. return $this->usesAreas;
  344. }
  345. /**
  346. * {@inheritdoc}
  347. */
  348. public function attachTo(ViewExecutable $view, $display_id, array &$build) {}
  349. /**
  350. * {@inheritdoc}
  351. */
  352. public function defaultableSections($section = NULL) {
  353. $sections = [
  354. 'access' => ['access'],
  355. 'cache' => ['cache'],
  356. 'title' => ['title'],
  357. 'css_class' => ['css_class'],
  358. 'use_ajax' => ['use_ajax'],
  359. 'hide_attachment_summary' => ['hide_attachment_summary'],
  360. 'show_admin_links' => ['show_admin_links'],
  361. 'group_by' => ['group_by'],
  362. 'query' => ['query'],
  363. 'use_more' => ['use_more', 'use_more_always', 'use_more_text'],
  364. 'use_more_always' => ['use_more', 'use_more_always', 'use_more_text'],
  365. 'use_more_text' => ['use_more', 'use_more_always', 'use_more_text'],
  366. 'link_display' => ['link_display', 'link_url'],
  367. // Force these to cascade properly.
  368. 'style' => ['style', 'row'],
  369. 'row' => ['style', 'row'],
  370. 'pager' => ['pager'],
  371. 'exposed_form' => ['exposed_form'],
  372. // These sections are special.
  373. 'header' => ['header'],
  374. 'footer' => ['footer'],
  375. 'empty' => ['empty'],
  376. 'relationships' => ['relationships'],
  377. 'fields' => ['fields'],
  378. 'sorts' => ['sorts'],
  379. 'arguments' => ['arguments'],
  380. 'filters' => ['filters', 'filter_groups'],
  381. 'filter_groups' => ['filters', 'filter_groups'],
  382. ];
  383. // If the display cannot use a pager, then we cannot default it.
  384. if (!$this->usesPager()) {
  385. unset($sections['pager']);
  386. unset($sections['items_per_page']);
  387. }
  388. foreach ($this->extenders as $extender) {
  389. $extender->defaultableSections($sections, $section);
  390. }
  391. if ($section) {
  392. if (!empty($sections[$section])) {
  393. return $sections[$section];
  394. }
  395. }
  396. else {
  397. return $sections;
  398. }
  399. }
  400. protected function defineOptions() {
  401. $options = [
  402. 'defaults' => [
  403. 'default' => [
  404. 'access' => TRUE,
  405. 'cache' => TRUE,
  406. 'query' => TRUE,
  407. 'title' => TRUE,
  408. 'css_class' => TRUE,
  409. 'display_description' => FALSE,
  410. 'use_ajax' => TRUE,
  411. 'hide_attachment_summary' => TRUE,
  412. 'show_admin_links' => TRUE,
  413. 'pager' => TRUE,
  414. 'use_more' => TRUE,
  415. 'use_more_always' => TRUE,
  416. 'use_more_text' => TRUE,
  417. 'exposed_form' => TRUE,
  418. 'link_display' => TRUE,
  419. 'link_url' => TRUE,
  420. 'group_by' => TRUE,
  421. 'style' => TRUE,
  422. 'row' => TRUE,
  423. 'header' => TRUE,
  424. 'footer' => TRUE,
  425. 'empty' => TRUE,
  426. 'relationships' => TRUE,
  427. 'fields' => TRUE,
  428. 'sorts' => TRUE,
  429. 'arguments' => TRUE,
  430. 'filters' => TRUE,
  431. 'filter_groups' => TRUE,
  432. ],
  433. ],
  434. 'title' => [
  435. 'default' => '',
  436. ],
  437. 'enabled' => [
  438. 'default' => TRUE,
  439. ],
  440. 'display_comment' => [
  441. 'default' => '',
  442. ],
  443. 'css_class' => [
  444. 'default' => '',
  445. ],
  446. 'display_description' => [
  447. 'default' => '',
  448. ],
  449. 'use_ajax' => [
  450. 'default' => FALSE,
  451. ],
  452. 'hide_attachment_summary' => [
  453. 'default' => FALSE,
  454. ],
  455. 'show_admin_links' => [
  456. 'default' => TRUE,
  457. ],
  458. 'use_more' => [
  459. 'default' => FALSE,
  460. ],
  461. 'use_more_always' => [
  462. 'default' => TRUE,
  463. ],
  464. 'use_more_text' => [
  465. 'default' => 'more',
  466. ],
  467. 'link_display' => [
  468. 'default' => '',
  469. ],
  470. 'link_url' => [
  471. 'default' => '',
  472. ],
  473. 'group_by' => [
  474. 'default' => FALSE,
  475. ],
  476. 'rendering_language' => [
  477. 'default' => '***LANGUAGE_entity_translation***',
  478. ],
  479. // These types are all plugins that can have individual settings
  480. // and therefore need special handling.
  481. 'access' => [
  482. 'contains' => [
  483. 'type' => ['default' => 'none'],
  484. 'options' => ['default' => []],
  485. ],
  486. 'merge_defaults' => [$this, 'mergePlugin'],
  487. ],
  488. 'cache' => [
  489. 'contains' => [
  490. 'type' => ['default' => 'tag'],
  491. 'options' => ['default' => []],
  492. ],
  493. 'merge_defaults' => [$this, 'mergePlugin'],
  494. ],
  495. 'query' => [
  496. 'contains' => [
  497. 'type' => ['default' => 'views_query'],
  498. 'options' => ['default' => []],
  499. ],
  500. 'merge_defaults' => [$this, 'mergePlugin'],
  501. ],
  502. 'exposed_form' => [
  503. 'contains' => [
  504. 'type' => ['default' => 'basic'],
  505. 'options' => ['default' => []],
  506. ],
  507. 'merge_defaults' => [$this, 'mergePlugin'],
  508. ],
  509. 'pager' => [
  510. 'contains' => [
  511. 'type' => ['default' => 'mini'],
  512. 'options' => ['default' => []],
  513. ],
  514. 'merge_defaults' => [$this, 'mergePlugin'],
  515. ],
  516. 'style' => [
  517. 'contains' => [
  518. 'type' => ['default' => 'default'],
  519. 'options' => ['default' => []],
  520. ],
  521. 'merge_defaults' => [$this, 'mergePlugin'],
  522. ],
  523. 'row' => [
  524. 'contains' => [
  525. 'type' => ['default' => 'fields'],
  526. 'options' => ['default' => []],
  527. ],
  528. 'merge_defaults' => [$this, 'mergePlugin'],
  529. ],
  530. 'exposed_block' => [
  531. 'default' => FALSE,
  532. ],
  533. 'header' => [
  534. 'default' => [],
  535. 'merge_defaults' => [$this, 'mergeHandler'],
  536. ],
  537. 'footer' => [
  538. 'default' => [],
  539. 'merge_defaults' => [$this, 'mergeHandler'],
  540. ],
  541. 'empty' => [
  542. 'default' => [],
  543. 'merge_defaults' => [$this, 'mergeHandler'],
  544. ],
  545. // We want these to export last.
  546. // These are the 5 handler types.
  547. 'relationships' => [
  548. 'default' => [],
  549. 'merge_defaults' => [$this, 'mergeHandler'],
  550. ],
  551. 'fields' => [
  552. 'default' => [],
  553. 'merge_defaults' => [$this, 'mergeHandler'],
  554. ],
  555. 'sorts' => [
  556. 'default' => [],
  557. 'merge_defaults' => [$this, 'mergeHandler'],
  558. ],
  559. 'arguments' => [
  560. 'default' => [],
  561. 'merge_defaults' => [$this, 'mergeHandler'],
  562. ],
  563. 'filter_groups' => [
  564. 'contains' => [
  565. 'operator' => ['default' => 'AND'],
  566. 'groups' => ['default' => [1 => 'AND']],
  567. ],
  568. ],
  569. 'filters' => [
  570. 'default' => [],
  571. ],
  572. ];
  573. if (!$this->usesPager()) {
  574. $options['defaults']['default']['pager'] = FALSE;
  575. $options['pager']['contains']['type']['default'] = 'some';
  576. }
  577. if ($this->isDefaultDisplay()) {
  578. unset($options['defaults']);
  579. }
  580. $options['display_extenders'] = ['default' => []];
  581. // First allow display extenders to provide new options.
  582. foreach ($this->extenders as $extender_id => $extender) {
  583. $options['display_extenders']['contains'][$extender_id]['contains'] = $extender->defineOptions();
  584. }
  585. // Then allow display extenders to alter existing default values.
  586. foreach ($this->extenders as $extender) {
  587. $extender->defineOptionsAlter($options);
  588. }
  589. return $options;
  590. }
  591. /**
  592. * {@inheritdoc}
  593. */
  594. public function hasPath() {
  595. return FALSE;
  596. }
  597. /**
  598. * {@inheritdoc}
  599. */
  600. public function usesLinkDisplay() {
  601. return !$this->hasPath();
  602. }
  603. /**
  604. * {@inheritdoc}
  605. */
  606. public function usesExposedFormInBlock() {
  607. return $this->hasPath();
  608. }
  609. /**
  610. * {@inheritdoc}
  611. */
  612. public function getAttachedDisplays() {
  613. $current_display_id = $this->display['id'];
  614. $attached_displays = [];
  615. // Go through all displays and search displays which link to this one.
  616. foreach ($this->view->storage->get('display') as $display_id => $display) {
  617. if (isset($display['display_options']['displays'])) {
  618. $displays = $display['display_options']['displays'];
  619. if (isset($displays[$current_display_id])) {
  620. $attached_displays[] = $display_id;
  621. }
  622. }
  623. }
  624. return $attached_displays;
  625. }
  626. /**
  627. * {@inheritdoc}
  628. */
  629. public function getLinkDisplay() {
  630. $display_id = $this->getOption('link_display');
  631. // If unknown, pick the first one.
  632. if (empty($display_id) || !$this->view->displayHandlers->has($display_id)) {
  633. foreach ($this->view->displayHandlers as $display_id => $display) {
  634. if (!empty($display) && $display->hasPath()) {
  635. return $display_id;
  636. }
  637. }
  638. }
  639. else {
  640. return $display_id;
  641. }
  642. // Fall-through returns NULL.
  643. }
  644. /**
  645. * {@inheritdoc}
  646. */
  647. public function getPath() {
  648. if ($this->hasPath()) {
  649. return $this->getOption('path');
  650. }
  651. $display_id = $this->getLinkDisplay();
  652. if ($display_id && $this->view->displayHandlers->has($display_id) && is_object($this->view->displayHandlers->get($display_id))) {
  653. return $this->view->displayHandlers->get($display_id)->getPath();
  654. }
  655. }
  656. /**
  657. * {@inheritdoc}
  658. */
  659. public function getRoutedDisplay() {
  660. // If this display has a route, return this display.
  661. if ($this instanceof DisplayRouterInterface) {
  662. return $this;
  663. }
  664. // If the display does not have a route (e.g. a block display), get the
  665. // route for the linked display.
  666. $display_id = $this->getLinkDisplay();
  667. if ($display_id && $this->view->displayHandlers->has($display_id) && is_object($this->view->displayHandlers->get($display_id))) {
  668. return $this->view->displayHandlers->get($display_id)->getRoutedDisplay();
  669. }
  670. // No routed display exists, so return NULL
  671. return NULL;
  672. }
  673. /**
  674. * {@inheritdoc}
  675. */
  676. public function getUrl() {
  677. return $this->view->getUrl(NULL, $this->display['id']);
  678. }
  679. /**
  680. * {@inheritdoc}
  681. */
  682. public function isDefaulted($option) {
  683. return !$this->isDefaultDisplay() && !empty($this->default_display) && !empty($this->options['defaults'][$option]);
  684. }
  685. /**
  686. * {@inheritdoc}
  687. */
  688. public function getOption($option) {
  689. if ($this->isDefaulted($option)) {
  690. return $this->default_display->getOption($option);
  691. }
  692. if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
  693. return $this->options[$option];
  694. }
  695. }
  696. /**
  697. * {@inheritdoc}
  698. */
  699. public function usesFields() {
  700. return $this->getPlugin('style')->usesFields();
  701. }
  702. /**
  703. * {@inheritdoc}
  704. */
  705. public function getPlugin($type) {
  706. // Look up the plugin name to use for this instance.
  707. $options = $this->getOption($type);
  708. // Return now if no options have been loaded.
  709. if (empty($options) || !isset($options['type'])) {
  710. return;
  711. }
  712. // Query plugins allow specifying a specific query class per base table.
  713. if ($type == 'query') {
  714. $views_data = Views::viewsData()->get($this->view->storage->get('base_table'));
  715. $name = isset($views_data['table']['base']['query_id']) ? $views_data['table']['base']['query_id'] : 'views_query';
  716. }
  717. else {
  718. $name = $options['type'];
  719. }
  720. // Plugin instances are stored on the display for re-use.
  721. if (!isset($this->plugins[$type][$name])) {
  722. $plugin = Views::pluginManager($type)->createInstance($name);
  723. // Initialize the plugin.
  724. $plugin->init($this->view, $this, $options['options']);
  725. $this->plugins[$type][$name] = $plugin;
  726. }
  727. return $this->plugins[$type][$name];
  728. }
  729. /**
  730. * {@inheritdoc}
  731. */
  732. public function &getHandler($type, $id) {
  733. if (!isset($this->handlers[$type])) {
  734. $this->getHandlers($type);
  735. }
  736. if (isset($this->handlers[$type][$id])) {
  737. return $this->handlers[$type][$id];
  738. }
  739. // So we can return a reference.
  740. $null = NULL;
  741. return $null;
  742. }
  743. /**
  744. * {@inheritdoc}
  745. */
  746. public function &getHandlers($type) {
  747. if (!isset($this->handlers[$type])) {
  748. $this->handlers[$type] = [];
  749. $types = ViewExecutable::getHandlerTypes();
  750. $plural = $types[$type]['plural'];
  751. // Cast to an array so that if the display does not have any handlers of
  752. // this type there is no PHP error.
  753. foreach ((array) $this->getOption($plural) as $id => $info) {
  754. // If this is during form submission and there are temporary options
  755. // which can only appear if the view is in the edit cache, use those
  756. // options instead. This is used for AJAX multi-step stuff.
  757. if ($this->view->getRequest()->request->get('form_id') && isset($this->view->temporary_options[$type][$id])) {
  758. $info = $this->view->temporary_options[$type][$id];
  759. }
  760. if ($info['id'] != $id) {
  761. $info['id'] = $id;
  762. }
  763. // If aggregation is on, the group type might override the actual
  764. // handler that is in use. This piece of code checks that and,
  765. // if necessary, sets the override handler.
  766. $override = NULL;
  767. if ($this->useGroupBy() && !empty($info['group_type'])) {
  768. if (empty($this->view->query)) {
  769. $this->view->initQuery();
  770. }
  771. $aggregate = $this->view->query->getAggregationInfo();
  772. if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
  773. $override = $aggregate[$info['group_type']]['handler'][$type];
  774. }
  775. }
  776. if (!empty($types[$type]['type'])) {
  777. $handler_type = $types[$type]['type'];
  778. }
  779. else {
  780. $handler_type = $type;
  781. }
  782. if ($handler = Views::handlerManager($handler_type)->getHandler($info, $override)) {
  783. // Special override for area types so they know where they come from.
  784. if ($handler instanceof AreaPluginBase) {
  785. $handler->areaType = $type;
  786. }
  787. $handler->init($this->view, $this, $info);
  788. $this->handlers[$type][$id] = &$handler;
  789. }
  790. // Prevent reference problems.
  791. unset($handler);
  792. }
  793. }
  794. return $this->handlers[$type];
  795. }
  796. /**
  797. * Gets all the handlers used by the display.
  798. *
  799. * @param bool $only_overrides
  800. * Whether to include only overridden handlers.
  801. *
  802. * @return \Drupal\views\Plugin\views\ViewsHandlerInterface[]
  803. */
  804. protected function getAllHandlers($only_overrides = FALSE) {
  805. $handler_types = Views::getHandlerTypes();
  806. $handlers = [];
  807. // Collect all dependencies of all handlers.
  808. foreach ($handler_types as $handler_type => $handler_type_info) {
  809. if ($only_overrides && $this->isDefaulted($handler_type_info['plural'])) {
  810. continue;
  811. }
  812. $handlers = array_merge($handlers, array_values($this->getHandlers($handler_type)));
  813. }
  814. return $handlers;
  815. }
  816. /**
  817. * Gets all the plugins used by the display.
  818. *
  819. * @param bool $only_overrides
  820. * Whether to include only overridden plugins.
  821. *
  822. * @return \Drupal\views\Plugin\views\ViewsPluginInterface[]
  823. */
  824. protected function getAllPlugins($only_overrides = FALSE) {
  825. $plugins = [];
  826. // Collect all dependencies of plugins.
  827. foreach (Views::getPluginTypes('plugin') as $plugin_type) {
  828. $plugin = $this->getPlugin($plugin_type);
  829. if (!$plugin) {
  830. continue;
  831. }
  832. if ($only_overrides && $this->isDefaulted($plugin_type)) {
  833. continue;
  834. }
  835. $plugins[] = $plugin;
  836. }
  837. return $plugins;
  838. }
  839. /**
  840. * {@inheritdoc}
  841. */
  842. public function calculateDependencies() {
  843. $this->dependencies = parent::calculateDependencies();
  844. // Collect all the dependencies of handlers and plugins. Only calculate
  845. // their dependencies if they are configured by this display.
  846. $plugins = array_merge($this->getAllHandlers(TRUE), $this->getAllPlugins(TRUE));
  847. array_walk($plugins, [$this, 'calculatePluginDependencies']);
  848. return $this->dependencies;
  849. }
  850. /**
  851. * {@inheritdoc}
  852. */
  853. public function getFieldLabels($groupable_only = FALSE) {
  854. $options = [];
  855. foreach ($this->getHandlers('relationship') as $relationship => $handler) {
  856. $relationships[$relationship] = $handler->adminLabel();
  857. }
  858. foreach ($this->getHandlers('field') as $id => $handler) {
  859. if ($groupable_only && !$handler->useStringGroupBy()) {
  860. // Continue to next handler if it's not groupable.
  861. continue;
  862. }
  863. if ($label = $handler->label()) {
  864. $options[$id] = $label;
  865. }
  866. else {
  867. $options[$id] = $handler->adminLabel();
  868. }
  869. if (!empty($handler->options['relationship']) && !empty($relationships[$handler->options['relationship']])) {
  870. $options[$id] = '(' . $relationships[$handler->options['relationship']] . ') ' . $options[$id];
  871. }
  872. }
  873. return $options;
  874. }
  875. /**
  876. * {@inheritdoc}
  877. */
  878. public function setOption($option, $value) {
  879. if ($this->isDefaulted($option)) {
  880. return $this->default_display->setOption($option, $value);
  881. }
  882. // Set this in two places: On the handler where we'll notice it
  883. // but also on the display object so it gets saved. This should
  884. // only be a temporary fix.
  885. $this->display['display_options'][$option] = $value;
  886. return $this->options[$option] = $value;
  887. }
  888. /**
  889. * {@inheritdoc}
  890. */
  891. public function overrideOption($option, $value) {
  892. $this->setOverride($option, FALSE);
  893. $this->setOption($option, $value);
  894. }
  895. /**
  896. * {@inheritdoc}
  897. */
  898. public function optionLink($text, $section, $class = '', $title = '') {
  899. if (!trim($text)) {
  900. $text = $this->t('Broken field');
  901. }
  902. if (!empty($class)) {
  903. $text = new FormattableMarkup('<span>@text</span>', ['@text' => $text]);
  904. }
  905. if (empty($title)) {
  906. $title = $text;
  907. }
  908. return Link::fromTextAndUrl($text, Url::fromRoute('views_ui.form_display', [
  909. 'js' => 'nojs',
  910. 'view' => $this->view->storage->id(),
  911. 'display_id' => $this->display['id'],
  912. 'type' => $section,
  913. ], [
  914. 'attributes' => [
  915. 'class' => ['views-ajax-link', $class],
  916. 'title' => $title,
  917. 'id' => Html::getUniqueId('views-' . $this->display['id'] . '-' . $section),
  918. ],
  919. ]))->toString();
  920. }
  921. /**
  922. * {@inheritdoc}
  923. */
  924. public function getArgumentsTokens() {
  925. $tokens = [];
  926. if (!empty($this->view->build_info['substitutions'])) {
  927. $tokens = $this->view->build_info['substitutions'];
  928. }
  929. return $tokens;
  930. }
  931. /**
  932. * {@inheritdoc}
  933. */
  934. public function optionsSummary(&$categories, &$options) {
  935. $categories = [
  936. 'title' => [
  937. 'title' => $this->t('Title'),
  938. 'column' => 'first',
  939. ],
  940. 'format' => [
  941. 'title' => $this->t('Format'),
  942. 'column' => 'first',
  943. ],
  944. 'filters' => [
  945. 'title' => $this->t('Filters'),
  946. 'column' => 'first',
  947. ],
  948. 'fields' => [
  949. 'title' => $this->t('Fields'),
  950. 'column' => 'first',
  951. ],
  952. 'pager' => [
  953. 'title' => $this->t('Pager'),
  954. 'column' => 'second',
  955. ],
  956. 'language' => [
  957. 'title' => $this->t('Language'),
  958. 'column' => 'second',
  959. ],
  960. 'exposed' => [
  961. 'title' => $this->t('Exposed form'),
  962. 'column' => 'third',
  963. 'build' => [
  964. '#weight' => 1,
  965. ],
  966. ],
  967. 'access' => [
  968. 'title' => '',
  969. 'column' => 'second',
  970. 'build' => [
  971. '#weight' => -5,
  972. ],
  973. ],
  974. 'other' => [
  975. 'title' => $this->t('Other'),
  976. 'column' => 'third',
  977. 'build' => [
  978. '#weight' => 2,
  979. ],
  980. ],
  981. ];
  982. if ($this->display['id'] != 'default') {
  983. $options['display_id'] = [
  984. 'category' => 'other',
  985. 'title' => $this->t('Machine Name'),
  986. 'value' => !empty($this->display['new_id']) ? $this->display['new_id'] : $this->display['id'],
  987. 'desc' => $this->t('Change the machine name of this display.'),
  988. ];
  989. }
  990. $display_comment = views_ui_truncate($this->getOption('display_comment'), 80);
  991. $options['display_comment'] = [
  992. 'category' => 'other',
  993. 'title' => $this->t('Administrative comment'),
  994. 'value' => !empty($display_comment) ? $display_comment : $this->t('None'),
  995. 'desc' => $this->t('Comment or document this display.'),
  996. ];
  997. $title = strip_tags($this->getOption('title'));
  998. if (!$title) {
  999. $title = $this->t('None');
  1000. }
  1001. $options['title'] = [
  1002. 'category' => 'title',
  1003. 'title' => $this->t('Title'),
  1004. 'value' => views_ui_truncate($title, 32),
  1005. 'desc' => $this->t('Change the title that this display will use.'),
  1006. ];
  1007. $style_plugin_instance = $this->getPlugin('style');
  1008. $style_summary = empty($style_plugin_instance->definition['title']) ? $this->t('Missing style plugin') : $style_plugin_instance->summaryTitle();
  1009. $style_title = empty($style_plugin_instance->definition['title']) ? $this->t('Missing style plugin') : $style_plugin_instance->pluginTitle();
  1010. $options['style'] = [
  1011. 'category' => 'format',
  1012. 'title' => $this->t('Format'),
  1013. 'value' => $style_title,
  1014. 'setting' => $style_summary,
  1015. 'desc' => $this->t('Change the way content is formatted.'),
  1016. ];
  1017. // This adds a 'Settings' link to the style_options setting if the style has
  1018. // options.
  1019. if ($style_plugin_instance->usesOptions()) {
  1020. $options['style']['links']['style_options'] = $this->t('Change settings for this format');
  1021. }
  1022. if ($style_plugin_instance->usesRowPlugin()) {
  1023. $row_plugin_instance = $this->getPlugin('row');
  1024. $row_summary = empty($row_plugin_instance->definition['title']) ? $this->t('Missing row plugin') : $row_plugin_instance->summaryTitle();
  1025. $row_title = empty($row_plugin_instance->definition['title']) ? $this->t('Missing row plugin') : $row_plugin_instance->pluginTitle();
  1026. $options['row'] = [
  1027. 'category' => 'format',
  1028. 'title' => $this->t('Show'),
  1029. 'value' => $row_title,
  1030. 'setting' => $row_summary,
  1031. 'desc' => $this->t('Change the way each row in the view is styled.'),
  1032. ];
  1033. // This adds a 'Settings' link to the row_options setting if the row style
  1034. // has options.
  1035. if ($row_plugin_instance->usesOptions()) {
  1036. $options['row']['links']['row_options'] = $this->t('Change settings for this style');
  1037. }
  1038. }
  1039. if ($this->usesAJAX()) {
  1040. $options['use_ajax'] = [
  1041. 'category' => 'other',
  1042. 'title' => $this->t('Use AJAX'),
  1043. 'value' => $this->getOption('use_ajax') ? $this->t('Yes') : $this->t('No'),
  1044. 'desc' => $this->t('Change whether or not this display will use AJAX.'),
  1045. ];
  1046. }
  1047. if ($this->usesAttachments()) {
  1048. $options['hide_attachment_summary'] = [
  1049. 'category' => 'other',
  1050. 'title' => $this->t('Hide attachments in summary'),
  1051. 'value' => $this->getOption('hide_attachment_summary') ? $this->t('Yes') : $this->t('No'),
  1052. 'desc' => $this->t('Change whether or not to display attachments when displaying a contextual filter summary.'),
  1053. ];
  1054. }
  1055. if (!isset($this->definition['contextual links locations']) || !empty($this->definition['contextual links locations'])) {
  1056. $options['show_admin_links'] = [
  1057. 'category' => 'other',
  1058. 'title' => $this->t('Contextual links'),
  1059. 'value' => $this->getOption('show_admin_links') ? $this->t('Shown') : $this->t('Hidden'),
  1060. 'desc' => $this->t('Change whether or not to display contextual links for this view.'),
  1061. ];
  1062. }
  1063. $pager_plugin = $this->getPlugin('pager');
  1064. if (!$pager_plugin) {
  1065. // Default to the no pager plugin.
  1066. $pager_plugin = Views::pluginManager('pager')->createInstance('none');
  1067. }
  1068. $pager_str = $pager_plugin->summaryTitle();
  1069. $options['pager'] = [
  1070. 'category' => 'pager',
  1071. 'title' => $this->t('Use pager'),
  1072. 'value' => $pager_plugin->pluginTitle(),
  1073. 'setting' => $pager_str,
  1074. 'desc' => $this->t("Change this display's pager setting."),
  1075. ];
  1076. // If pagers aren't allowed, change the text of the item.
  1077. if (!$this->usesPager()) {
  1078. $options['pager']['title'] = $this->t('Items to display');
  1079. }
  1080. if ($pager_plugin->usesOptions()) {
  1081. $options['pager']['links']['pager_options'] = $this->t('Change settings for this pager type.');
  1082. }
  1083. if ($this->usesMore()) {
  1084. $options['use_more'] = [
  1085. 'category' => 'pager',
  1086. 'title' => $this->t('More link'),
  1087. 'value' => $this->getOption('use_more') ? $this->t('Yes') : $this->t('No'),
  1088. 'desc' => $this->t('Specify whether this display will provide a "more" link.'),
  1089. ];
  1090. }
  1091. $this->view->initQuery();
  1092. if ($this->view->query->getAggregationInfo()) {
  1093. $options['group_by'] = [
  1094. 'category' => 'other',
  1095. 'title' => $this->t('Use aggregation'),
  1096. 'value' => $this->getOption('group_by') ? $this->t('Yes') : $this->t('No'),
  1097. 'desc' => $this->t('Allow grouping and aggregation (calculation) of fields.'),
  1098. ];
  1099. }
  1100. $options['query'] = [
  1101. 'category' => 'other',
  1102. 'title' => $this->t('Query settings'),
  1103. 'value' => $this->t('Settings'),
  1104. 'desc' => $this->t('Allow to set some advanced settings for the query plugin'),
  1105. ];
  1106. if (\Drupal::languageManager()->isMultilingual() && $this->isBaseTableTranslatable()) {
  1107. $rendering_language_options = $this->buildRenderingLanguageOptions();
  1108. $options['rendering_language'] = [
  1109. 'category' => 'language',
  1110. 'title' => $this->t('Rendering Language'),
  1111. 'value' => $rendering_language_options[$this->getOption('rendering_language')],
  1112. 'desc' => $this->t('All content that supports translations will be displayed in the selected language.'),
  1113. ];
  1114. }
  1115. $access_plugin = $this->getPlugin('access');
  1116. if (!$access_plugin) {
  1117. // Default to the no access control plugin.
  1118. $access_plugin = Views::pluginManager('access')->createInstance('none');
  1119. }
  1120. $access_str = $access_plugin->summaryTitle();
  1121. $options['access'] = [
  1122. 'category' => 'access',
  1123. 'title' => $this->t('Access'),
  1124. 'value' => $access_plugin->pluginTitle(),
  1125. 'setting' => $access_str,
  1126. 'desc' => $this->t('Specify access control type for this display.'),
  1127. ];
  1128. if ($access_plugin->usesOptions()) {
  1129. $options['access']['links']['access_options'] = $this->t('Change settings for this access type.');
  1130. }
  1131. $cache_plugin = $this->getPlugin('cache');
  1132. if (!$cache_plugin) {
  1133. // Default to the no cache control plugin.
  1134. $cache_plugin = Views::pluginManager('cache')->createInstance('none');
  1135. }
  1136. $cache_str = $cache_plugin->summaryTitle();
  1137. $options['cache'] = [
  1138. 'category' => 'other',
  1139. 'title' => $this->t('Caching'),
  1140. 'value' => $cache_plugin->pluginTitle(),
  1141. 'setting' => $cache_str,
  1142. 'desc' => $this->t('Specify caching type for this display.'),
  1143. ];
  1144. if ($cache_plugin->usesOptions()) {
  1145. $options['cache']['links']['cache_options'] = $this->t('Change settings for this caching type.');
  1146. }
  1147. if ($access_plugin->usesOptions()) {
  1148. $options['access']['links']['access_options'] = $this->t('Change settings for this access type.');
  1149. }
  1150. if ($this->usesLinkDisplay()) {
  1151. $link_display_option = $this->getOption('link_display');
  1152. $link_display = $this->t('None');
  1153. if ($link_display_option == 'custom_url') {
  1154. $link_display = $this->t('Custom URL');
  1155. }
  1156. elseif (!empty($link_display_option)) {
  1157. $display_id = $this->getLinkDisplay();
  1158. $displays = $this->view->storage->get('display');
  1159. if (!empty($displays[$display_id])) {
  1160. $link_display = $displays[$display_id]['display_title'];
  1161. }
  1162. }
  1163. $options['link_display'] = [
  1164. 'category' => 'pager',
  1165. 'title' => $this->t('Link display'),
  1166. 'value' => $link_display,
  1167. 'desc' => $this->t('Specify which display or custom URL this display will link to.'),
  1168. ];
  1169. }
  1170. if ($this->usesExposedFormInBlock()) {
  1171. $options['exposed_block'] = [
  1172. 'category' => 'exposed',
  1173. 'title' => $this->t('Exposed form in block'),
  1174. 'value' => $this->getOption('exposed_block') ? $this->t('Yes') : $this->t('No'),
  1175. 'desc' => $this->t('Allow the exposed form to appear in a block instead of the view.'),
  1176. ];
  1177. }
  1178. /** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginInterface $exposed_form_plugin */
  1179. $exposed_form_plugin = $this->getPlugin('exposed_form');
  1180. if (!$exposed_form_plugin) {
  1181. // Default to the no cache control plugin.
  1182. $exposed_form_plugin = Views::pluginManager('exposed_form')->createInstance('basic');
  1183. }
  1184. $exposed_form_str = $exposed_form_plugin->summaryTitle();
  1185. $options['exposed_form'] = [
  1186. 'category' => 'exposed',
  1187. 'title' => $this->t('Exposed form style'),
  1188. 'value' => $exposed_form_plugin->pluginTitle(),
  1189. 'setting' => $exposed_form_str,
  1190. 'desc' => $this->t('Select the kind of exposed filter to use.'),
  1191. ];
  1192. if ($exposed_form_plugin->usesOptions()) {
  1193. $options['exposed_form']['links']['exposed_form_options'] = $this->t('Exposed form settings for this exposed form style.');
  1194. }
  1195. $css_class = trim($this->getOption('css_class'));
  1196. if (!$css_class) {
  1197. $css_class = $this->t('None');
  1198. }
  1199. $options['css_class'] = [
  1200. 'category' => 'other',
  1201. 'title' => $this->t('CSS class'),
  1202. 'value' => $css_class,
  1203. 'desc' => $this->t('Change the CSS class name(s) that will be added to this display.'),
  1204. ];
  1205. foreach ($this->extenders as $extender) {
  1206. $extender->optionsSummary($categories, $options);
  1207. }
  1208. }
  1209. /**
  1210. * {@inheritdoc}
  1211. */
  1212. public function buildOptionsForm(&$form, FormStateInterface $form_state) {
  1213. parent::buildOptionsForm($form, $form_state);
  1214. $section = $form_state->get('section');
  1215. if ($this->defaultableSections($section)) {
  1216. views_ui_standard_display_dropdown($form, $form_state, $section);
  1217. }
  1218. $form['#title'] = $this->display['display_title'] . ': ';
  1219. // Set the 'section' to highlight on the form.
  1220. // If it's the item we're looking at is pulling from the default display,
  1221. // reflect that. Don't use is_defaulted since we want it to show up even
  1222. // on the default display.
  1223. if (!empty($this->options['defaults'][$section])) {
  1224. $form['#section'] = 'default-' . $section;
  1225. }
  1226. else {
  1227. $form['#section'] = $this->display['id'] . '-' . $section;
  1228. }
  1229. switch ($section) {
  1230. case 'display_id':
  1231. $form['#title'] .= $this->t('The machine name of this display');
  1232. $form['display_id'] = [
  1233. '#type' => 'textfield',
  1234. '#title' => $this->t('Machine name of the display'),
  1235. '#default_value' => !empty($this->display['new_id']) ? $this->display['new_id'] : $this->display['id'],
  1236. '#required' => TRUE,
  1237. '#size' => 64,
  1238. ];
  1239. break;
  1240. case 'display_title':
  1241. $form['#title'] .= $this->t('The name and the description of this display');
  1242. $form['display_title'] = [
  1243. '#title' => $this->t('Administrative name'),
  1244. '#type' => 'textfield',
  1245. '#default_value' => $this->display['display_title'],
  1246. ];
  1247. $form['display_description'] = [
  1248. '#title' => $this->t('Administrative description'),
  1249. '#type' => 'textfield',
  1250. '#default_value' => $this->getOption('display_description'),
  1251. ];
  1252. break;
  1253. case 'display_comment':
  1254. $form['#title'] .= $this->t('Administrative comment');
  1255. $form['display_comment'] = [
  1256. '#type' => 'textarea',
  1257. '#title' => $this->t('Administrative comment'),
  1258. '#description' => $this->t('This description will only be seen within the administrative interface and can be used to document this display.'),
  1259. '#default_value' => $this->getOption('display_comment'),
  1260. ];
  1261. break;
  1262. case 'title':
  1263. $form['#title'] .= $this->t('The title of this view');
  1264. $form['title'] = [
  1265. '#title' => $this->t('Title'),
  1266. '#type' => 'textfield',
  1267. '#description' => $this->t('This title will be displayed with the view, wherever titles are normally displayed; i.e, as the page title, block title, etc.'),
  1268. '#default_value' => $this->getOption('title'),
  1269. '#maxlength' => 255,
  1270. ];
  1271. break;
  1272. case 'css_class':
  1273. $form['#title'] .= $this->t('CSS class');
  1274. $form['css_class'] = [
  1275. '#type' => 'textfield',
  1276. '#title' => $this->t('CSS class name(s)'),
  1277. '#description' => $this->t('Separate multiple classes by spaces.'),
  1278. '#default_value' => $this->getOption('css_class'),
  1279. ];
  1280. break;
  1281. case 'use_ajax':
  1282. $form['#title'] .= $this->t('AJAX');
  1283. $form['use_ajax'] = [
  1284. '#description' => $this->t('Options such as paging, table sorting, and exposed filters will not initiate a page refresh.'),
  1285. '#type' => 'checkbox',
  1286. '#title' => $this->t('Use AJAX'),
  1287. '#default_value' => $this->getOption('use_ajax') ? 1 : 0,
  1288. ];
  1289. break;
  1290. case 'hide_attachment_summary':
  1291. $form['#title'] .= $this->t('Hide attachments when displaying a contextual filter summary');
  1292. $form['hide_attachment_summary'] = [
  1293. '#type' => 'checkbox',
  1294. '#title' => $this->t('Hide attachments in summary'),
  1295. '#default_value' => $this->getOption('hide_attachment_summary') ? 1 : 0,
  1296. ];
  1297. break;
  1298. case 'show_admin_links':
  1299. $form['#title'] .= $this->t('Show contextual links on this view.');
  1300. $form['show_admin_links'] = [
  1301. '#type' => 'checkbox',
  1302. '#title' => $this->t('Show contextual links'),
  1303. '#default_value' => $this->getOption('show_admin_links'),
  1304. ];
  1305. break;
  1306. case 'use_more':
  1307. $form['#title'] .= $this->t('Add a more link to the bottom of the display.');
  1308. $form['use_more'] = [
  1309. '#type' => 'checkbox',
  1310. '#title' => $this->t('Create more link'),
  1311. '#description' => $this->t("This will add a more link to the bottom of this view, which will link to the page view. If you have more than one page view, the link will point to the display specified in 'Link display' section under pager. You can override the URL at the link display setting."),
  1312. '#default_value' => $this->getOption('use_more'),
  1313. ];
  1314. $form['use_more_always'] = [
  1315. '#type' => 'checkbox',
  1316. '#title' => $this->t('Always display the more link'),
  1317. '#description' => $this->t('Check this to display the more link even if there are no more items to display.'),
  1318. '#default_value' => $this->getOption('use_more_always'),
  1319. '#states' => [
  1320. 'visible' => [
  1321. ':input[name="use_more"]' => ['checked' => TRUE],
  1322. ],
  1323. ],
  1324. ];
  1325. $form['use_more_text'] = [
  1326. '#type' => 'textfield',
  1327. '#title' => $this->t('More link text'),
  1328. '#description' => $this->t('The text to display for the more link.'),
  1329. '#default_value' => $this->getOption('use_more_text'),
  1330. '#states' => [
  1331. 'visible' => [
  1332. ':input[name="use_more"]' => ['checked' => TRUE],
  1333. ],
  1334. ],
  1335. ];
  1336. break;
  1337. case 'group_by':
  1338. $form['#title'] .= $this->t('Allow grouping and aggregation (calculation) of fields.');
  1339. $form['group_by'] = [
  1340. '#type' => 'checkbox',
  1341. '#title' => $this->t('Aggregate'),
  1342. '#description' => $this->t('If enabled, some fields may become unavailable. All fields that are selected for grouping will be collapsed to one record per distinct value. Other fields which are selected for aggregation will have the function run on them. For example, you can group nodes on title and count the number of nids in order to get a list of duplicate titles.'),
  1343. '#default_value' => $this->getOption('group_by'),
  1344. ];
  1345. break;
  1346. case 'access':
  1347. $form['#title'] .= $this->t('Access restrictions');
  1348. $form['access'] = [
  1349. '#prefix' => '<div class="clearfix">',
  1350. '#suffix' => '</div>',
  1351. '#tree' => TRUE,
  1352. ];
  1353. $access = $this->getOption('access');
  1354. $form['access']['type'] = [
  1355. '#title' => $this->t('Access'),
  1356. '#title_display' => 'invisible',
  1357. '#type' => 'radios',
  1358. '#options' => Views::fetchPluginNames('access', $this->getType(), [$this->view->storage->get('base_table')]),
  1359. '#default_value' => $access['type'],
  1360. ];
  1361. $access_plugin = $this->getPlugin('access');
  1362. if ($access_plugin->usesOptions()) {
  1363. $form['markup'] = [
  1364. '#prefix' => '<div class="js-form-item form-item description">',
  1365. '#markup' => $this->t('You may also adjust the @settings for the currently selected access restriction.', ['@settings' => $this->optionLink($this->t('settings'), 'access_options')]),
  1366. '#suffix' => '</div>',
  1367. ];
  1368. }
  1369. break;
  1370. case 'access_options':
  1371. $plugin = $this->getPlugin('access');
  1372. $form['#title'] .= $this->t('Access options');
  1373. if ($plugin) {
  1374. $form['access_options'] = [
  1375. '#tree' => TRUE,
  1376. ];
  1377. $plugin->buildOptionsForm($form['access_options'], $form_state);
  1378. }
  1379. break;
  1380. case 'cache':
  1381. $form['#title'] .= $this->t('Caching');
  1382. $form['cache'] = [
  1383. '#prefix' => '<div class="clearfix">',
  1384. '#suffix' => '</div>',
  1385. '#tree' => TRUE,
  1386. ];
  1387. $cache = $this->getOption('cache');
  1388. $form['cache']['type'] = [
  1389. '#title' => $this->t('Caching'),
  1390. '#title_display' => 'invisible',
  1391. '#type' => 'radios',
  1392. '#options' => Views::fetchPluginNames('cache', $this->getType(), [$this->view->storage->get('base_table')]),
  1393. '#default_value' => $cache['type'],
  1394. ];
  1395. $cache_plugin = $this->getPlugin('cache');
  1396. if ($cache_plugin->usesOptions()) {
  1397. $form['markup'] = [
  1398. '#prefix' => '<div class="js-form-item form-item description">',
  1399. '#suffix' => '</div>',
  1400. '#markup' => $this->t('You may also adjust the @settings for the currently selected cache mechanism.', ['@settings' => $this->optionLink($this->t('settings'), 'cache_options')]),
  1401. ];
  1402. }
  1403. break;
  1404. case 'cache_options':
  1405. $plugin = $this->getPlugin('cache');
  1406. $form['#title'] .= $this->t('Caching options');
  1407. if ($plugin) {
  1408. $form['cache_options'] = [
  1409. '#tree' => TRUE,
  1410. ];
  1411. $plugin->buildOptionsForm($form['cache_options'], $form_state);
  1412. }
  1413. break;
  1414. case 'query':
  1415. $query_options = $this->getOption('query');
  1416. $plugin_name = $query_options['type'];
  1417. $form['#title'] .= $this->t('Query options');
  1418. $this->view->initQuery();
  1419. if ($this->view->query) {
  1420. $form['query'] = [
  1421. '#tree' => TRUE,
  1422. 'type' => [
  1423. '#type' => 'value',
  1424. '#value' => $plugin_name,
  1425. ],
  1426. 'options' => [
  1427. '#tree' => TRUE,
  1428. ],
  1429. ];
  1430. $this->view->query->buildOptionsForm($form['query']['options'], $form_state);
  1431. }
  1432. break;
  1433. case 'rendering_language':
  1434. $form['#title'] .= $this->t('Rendering language');
  1435. if (\Drupal::languageManager()->isMultilingual() && $this->isBaseTableTranslatable()) {
  1436. $options = $this->buildRenderingLanguageOptions();
  1437. $form['rendering_language'] = [
  1438. '#type' => 'select',
  1439. '#options' => $options,
  1440. '#title' => $this->t('Rendering language'),
  1441. '#description' => $this->t('All content that supports translations will be displayed in the selected language.'),
  1442. '#default_value' => $this->getOption('rendering_language'),
  1443. ];
  1444. }
  1445. else {
  1446. $form['rendering_language']['#markup'] = $this->t('The view is not based on a translatable entity type or the site is not multilingual.');
  1447. }
  1448. break;
  1449. case 'style':
  1450. $form['#title'] .= $this->t('How should this view be styled');
  1451. $style_plugin = $this->getPlugin('style');
  1452. $form['style'] = [
  1453. '#preā€¦

Large files files are truncated, but you can click here to view the full file