Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / views / src / Plugin / views / field / EntityField.php
1 <?php
2
3 namespace Drupal\views\Plugin\views\field;
4
5 use Drupal\Component\Plugin\DependentPluginInterface;
6 use Drupal\Component\Utility\Xss;
7 use Drupal\Core\Cache\Cache;
8 use Drupal\Core\Cache\CacheableDependencyInterface;
9 use Drupal\Core\Entity\EntityInterface;
10 use Drupal\Core\Entity\EntityManagerInterface;
11 use Drupal\Core\Field\FieldStorageDefinitionInterface;
12 use Drupal\Core\Field\FieldTypePluginManagerInterface;
13 use Drupal\Core\Field\FormatterPluginManager;
14 use Drupal\Core\Form\FormHelper;
15 use Drupal\Core\Form\FormStateInterface;
16 use Drupal\Core\Language\LanguageManagerInterface;
17 use Drupal\Core\Plugin\PluginDependencyTrait;
18 use Drupal\Core\Render\BubbleableMetadata;
19 use Drupal\Core\Render\Element;
20 use Drupal\Core\Render\RendererInterface;
21 use Drupal\Core\Session\AccountInterface;
22 use Drupal\Core\TypedData\TypedDataInterface;
23 use Drupal\views\FieldAPIHandlerTrait;
24 use Drupal\views\Entity\Render\EntityFieldRenderer;
25 use Drupal\views\Plugin\views\display\DisplayPluginBase;
26 use Drupal\views\Plugin\DependentWithRemovalPluginInterface;
27 use Drupal\views\ResultRow;
28 use Drupal\views\ViewExecutable;
29 use Symfony\Component\DependencyInjection\ContainerInterface;
30
31 /**
32  * A field that displays entity field data.
33  *
34  * @ingroup views_field_handlers
35  *
36  * @ViewsField("field")
37  */
38 class EntityField extends FieldPluginBase implements CacheableDependencyInterface, MultiItemsFieldHandlerInterface, DependentWithRemovalPluginInterface {
39
40   use FieldAPIHandlerTrait;
41   use PluginDependencyTrait;
42
43   /**
44    * An array to store field renderable arrays for use by renderItems().
45    *
46    * @var array
47    */
48   public $items = [];
49
50   /**
51    * Does the field supports multiple field values.
52    *
53    * @var bool
54    */
55   public $multiple;
56
57   /**
58    * Does the rendered fields get limited.
59    *
60    * @var bool
61    */
62   public $limit_values;
63
64   /**
65    * A shortcut for $view->base_table.
66    *
67    * @var string
68    */
69   public $base_table;
70
71   /**
72    * An array of formatter options.
73    *
74    * @var array
75    */
76   protected $formatterOptions;
77
78   /**
79    * The entity manager.
80    *
81    * @var \Drupal\Core\Entity\EntityManagerInterface
82    */
83   protected $entityManager;
84
85   /**
86    * The field formatter plugin manager.
87    *
88    * @var \Drupal\Core\Field\FormatterPluginManager
89    */
90   protected $formatterPluginManager;
91
92   /**
93    * The language manager.
94    *
95    * @var \Drupal\Core\Language\LanguageManagerInterface
96    */
97   protected $languageManager;
98
99   /**
100    * The renderer.
101    *
102    * @var \Drupal\Core\Render\RendererInterface
103    */
104   protected $renderer;
105
106   /**
107    * The field type plugin manager.
108    *
109    * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
110    */
111   protected $fieldTypePluginManager;
112
113   /**
114    * Static cache for ::getEntityFieldRenderer().
115    *
116    * @var \Drupal\views\Entity\Render\EntityFieldRenderer
117    */
118   protected $entityFieldRenderer;
119
120   /**
121    * Constructs a \Drupal\field\Plugin\views\field\Field object.
122    *
123    * @param array $configuration
124    *   A configuration array containing information about the plugin instance.
125    * @param string $plugin_id
126    *   The plugin_id for the plugin instance.
127    * @param mixed $plugin_definition
128    *   The plugin implementation definition.
129    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
130    *   The field formatter plugin manager.
131    * @param \Drupal\Core\Field\FormatterPluginManager $formatter_plugin_manager
132    *   The field formatter plugin manager.
133    * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_plugin_manager
134    *   The field plugin type manager.
135    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
136    *   The language manager.
137    * @param \Drupal\Core\Render\RendererInterface $renderer
138    *   The renderer.
139    */
140   public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, FormatterPluginManager $formatter_plugin_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, LanguageManagerInterface $language_manager, RendererInterface $renderer) {
141     parent::__construct($configuration, $plugin_id, $plugin_definition);
142
143     $this->entityManager = $entity_manager;
144     $this->formatterPluginManager = $formatter_plugin_manager;
145     $this->fieldTypePluginManager = $field_type_plugin_manager;
146     $this->languageManager = $language_manager;
147     $this->renderer = $renderer;
148
149     // @todo Unify 'entity field'/'field_name' instead of converting back and
150     //   forth. https://www.drupal.org/node/2410779
151     if (isset($this->definition['entity field'])) {
152       $this->definition['field_name'] = $this->definition['entity field'];
153     }
154   }
155
156   /**
157    * {@inheritdoc}
158    */
159   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
160     return new static(
161       $configuration,
162       $plugin_id,
163       $plugin_definition,
164       $container->get('entity.manager'),
165       $container->get('plugin.manager.field.formatter'),
166       $container->get('plugin.manager.field.field_type'),
167       $container->get('language_manager'),
168       $container->get('renderer')
169     );
170   }
171
172   /**
173    * {@inheritdoc}
174    */
175   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
176     parent::init($view, $display, $options);
177
178     $this->multiple = FALSE;
179     $this->limit_values = FALSE;
180
181     $field_definition = $this->getFieldDefinition();
182     $cardinality = $field_definition->getFieldStorageDefinition()->getCardinality();
183     if ($field_definition->getFieldStorageDefinition()->isMultiple()) {
184       $this->multiple = TRUE;
185
186       // If "Display all values in the same row" is FALSE, then we always limit
187       // in order to show a single unique value per row.
188       if (!$this->options['group_rows']) {
189         $this->limit_values = TRUE;
190       }
191
192       // If "First and last only" is chosen, limit the values
193       if (!empty($this->options['delta_first_last'])) {
194         $this->limit_values = TRUE;
195       }
196
197       // Otherwise, we only limit values if the user hasn't selected "all", 0, or
198       // the value matching field cardinality.
199       if ((($this->options['delta_limit'] > 0) && ($this->options['delta_limit'] != $cardinality)) || intval($this->options['delta_offset'])) {
200         $this->limit_values = TRUE;
201       }
202     }
203   }
204
205   /**
206    * {@inheritdoc}
207    */
208   protected function getEntityManager() {
209     return $this->entityManager;
210   }
211
212   /**
213    * {@inheritdoc}
214    */
215   public function access(AccountInterface $account) {
216     $access_control_handler = $this->entityManager->getAccessControlHandler($this->getEntityType());
217     return $access_control_handler->fieldAccess('view', $this->getFieldDefinition(), $account);
218   }
219
220   /**
221    * Called to add the field to a query.
222    *
223    * By default, all needed data is taken from entities loaded by the query
224    * plugin. Columns are added only if they are used in groupings.
225    */
226   public function query($use_groupby = FALSE) {
227     $fields = $this->additional_fields;
228     // No need to add the entity type.
229     $entity_type_key = array_search('entity_type', $fields);
230     if ($entity_type_key !== FALSE) {
231       unset($fields[$entity_type_key]);
232     }
233
234     if ($use_groupby) {
235       // Add the fields that we're actually grouping on.
236       $options = [];
237       if ($this->options['group_column'] != 'entity_id') {
238         $options = [$this->options['group_column'] => $this->options['group_column']];
239       }
240       $options += is_array($this->options['group_columns']) ? $this->options['group_columns'] : [];
241
242       // Go through the list and determine the actual column name from field api.
243       $fields = [];
244       $table_mapping = $this->getTableMapping();
245       $field_definition = $this->getFieldStorageDefinition();
246
247       foreach ($options as $column) {
248         $fields[$column] = $table_mapping->getFieldColumnName($field_definition, $column);
249       }
250
251       $this->group_fields = $fields;
252     }
253
254     // Add additional fields (and the table join itself) if needed.
255     if ($this->add_field_table($use_groupby)) {
256       $this->ensureMyTable();
257       $this->addAdditionalFields($fields);
258     }
259
260     // Let the entity field renderer alter the query if needed.
261     $this->getEntityFieldRenderer()->query($this->query, $this->relationship);
262   }
263
264   /**
265    * Determine if the field table should be added to the query.
266    */
267   public function add_field_table($use_groupby) {
268     // Grouping is enabled.
269     if ($use_groupby) {
270       return TRUE;
271     }
272     // This a multiple value field, but "group multiple values" is not checked.
273     if ($this->multiple && !$this->options['group_rows']) {
274       return TRUE;
275     }
276     return FALSE;
277   }
278
279   /**
280    * {@inheritdoc}
281    */
282   public function clickSortable() {
283     // A field is not click sortable if it's a multiple field with
284     // "group multiple values" checked, since a click sort in that case would
285     // add a join to the field table, which would produce unwanted duplicates.
286     if ($this->multiple && $this->options['group_rows']) {
287       return FALSE;
288     }
289
290     // If field definition is set, use that.
291     if (isset($this->definition['click sortable'])) {
292       return (bool) $this->definition['click sortable'];
293     }
294
295     // Default to true.
296     return TRUE;
297   }
298
299   /**
300    * Called to determine what to tell the clicksorter.
301    */
302   public function clickSort($order) {
303     // No column selected, can't continue.
304     if (empty($this->options['click_sort_column'])) {
305       return;
306     }
307
308     $this->ensureMyTable();
309     $field_storage_definition = $this->getFieldStorageDefinition();
310     $column = $this->getTableMapping()->getFieldColumnName($field_storage_definition, $this->options['click_sort_column']);
311     if (!isset($this->aliases[$column])) {
312       // Column is not in query; add a sort on it (without adding the column).
313       $this->aliases[$column] = $this->tableAlias . '.' . $column;
314     }
315     $this->query->addOrderBy(NULL, NULL, $order, $this->aliases[$column]);
316   }
317
318   /**
319    * Gets the field storage definition.
320    *
321    * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
322    *   The field storage definition used by this handler.
323    */
324   protected function getFieldStorageDefinition() {
325     $entity_type_id = $this->definition['entity_type'];
326     $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
327
328     // @todo Unify 'entity field'/'field_name' instead of converting back and
329     //   forth. https://www.drupal.org/node/2410779
330     if (isset($this->definition['field_name']) && isset($field_storage_definitions[$this->definition['field_name']])) {
331       return $field_storage_definitions[$this->definition['field_name']];
332     }
333
334     if (isset($this->definition['entity field']) && isset($field_storage_definitions[$this->definition['entity field']])) {
335       return $field_storage_definitions[$this->definition['entity field']];
336     }
337
338     // The list of field storage definitions above does not include computed
339     // base fields, so we need to explicitly fetch a list of all base fields in
340     // order to support them.
341     // @see \Drupal\Core\Entity\EntityFieldManager::getFieldStorageDefinitions()
342     $base_fields = $this->entityManager->getBaseFieldDefinitions($entity_type_id);
343     if (isset($this->definition['field_name']) && isset($base_fields[$this->definition['field_name']])) {
344       return $base_fields[$this->definition['field_name']]->getFieldStorageDefinition();
345     }
346   }
347
348   /**
349    * {@inheritdoc}
350    */
351   protected function defineOptions() {
352     $options = parent::defineOptions();
353
354     $field_storage_definition = $this->getFieldStorageDefinition();
355     $field_type = $this->fieldTypePluginManager->getDefinition($field_storage_definition->getType());
356     $column_names = array_keys($field_storage_definition->getColumns());
357     $default_column = '';
358     // Try to determine a sensible default.
359     if (count($column_names) == 1) {
360       $default_column = $column_names[0];
361     }
362     elseif (in_array('value', $column_names)) {
363       $default_column = 'value';
364     }
365
366     // If the field has a "value" column, we probably need that one.
367     $options['click_sort_column'] = [
368       'default' => $default_column,
369     ];
370
371     if (isset($this->definition['default_formatter'])) {
372       $options['type'] = ['default' => $this->definition['default_formatter']];
373     }
374     elseif (isset($field_type['default_formatter'])) {
375       $options['type'] = ['default' => $field_type['default_formatter']];
376     }
377     else {
378       $options['type'] = ['default' => ''];
379     }
380
381     $options['settings'] = [
382       'default' => isset($this->definition['default_formatter_settings']) ? $this->definition['default_formatter_settings'] : [],
383     ];
384     $options['group_column'] = [
385       'default' => $default_column,
386     ];
387     $options['group_columns'] = [
388       'default' => [],
389     ];
390
391     // Options used for multiple value fields.
392     $options['group_rows'] = [
393       'default' => TRUE,
394     ];
395     // If we know the exact number of allowed values, then that can be
396     // the default. Otherwise, default to 'all'.
397     $options['delta_limit'] = [
398       'default' => ($field_storage_definition->getCardinality() > 1) ? $field_storage_definition->getCardinality() : 0,
399     ];
400     $options['delta_offset'] = [
401       'default' => 0,
402     ];
403     $options['delta_reversed'] = [
404       'default' => FALSE,
405     ];
406     $options['delta_first_last'] = [
407       'default' => FALSE,
408     ];
409
410     $options['multi_type'] = [
411       'default' => 'separator'
412     ];
413     $options['separator'] = [
414       'default' => ', '
415     ];
416
417     $options['field_api_classes'] = [
418       'default' => FALSE,
419     ];
420
421     return $options;
422   }
423
424   /**
425    * {@inheritdoc}
426    */
427   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
428     parent::buildOptionsForm($form, $form_state);
429
430     $field = $this->getFieldDefinition();
431     $formatters = $this->formatterPluginManager->getOptions($field->getType());
432     $column_names = array_keys($field->getColumns());
433
434     // If this is a multiple value field, add its options.
435     if ($this->multiple) {
436       $this->multiple_options_form($form, $form_state);
437     }
438
439     // No need to ask the user anything if the field has only one column.
440     if (count($field->getColumns()) == 1) {
441       $form['click_sort_column'] = [
442         '#type' => 'value',
443         '#value' => isset($column_names[0]) ? $column_names[0] : '',
444       ];
445     }
446     else {
447       $form['click_sort_column'] = [
448         '#type' => 'select',
449         '#title' => $this->t('Column used for click sorting'),
450         '#options' => array_combine($column_names, $column_names),
451         '#default_value' => $this->options['click_sort_column'],
452         '#description' => $this->t('Used by Style: Table to determine the actual column to click sort the field on. The default is usually fine.'),
453       ];
454     }
455
456     $form['type'] = [
457       '#type' => 'select',
458       '#title' => $this->t('Formatter'),
459       '#options' => $formatters,
460       '#default_value' => $this->options['type'],
461       '#ajax' => [
462         'url' => views_ui_build_form_url($form_state),
463       ],
464       '#submit' => [[$this, 'submitTemporaryForm']],
465       '#executes_submit_callback' => TRUE,
466     ];
467
468     $form['field_api_classes'] = [
469       '#title' => $this->t('Use field template'),
470       '#type' => 'checkbox',
471       '#default_value' => $this->options['field_api_classes'],
472       '#description' => $this->t('If checked, field api classes will be added by field templates. This is not recommended unless your CSS depends upon these classes. If not checked, template will not be used.'),
473       '#fieldset' => 'style_settings',
474       '#weight' => 20,
475     ];
476
477     if ($this->multiple) {
478       $form['field_api_classes']['#description'] .= ' ' . $this->t('Checking this option will cause the group Display Type and Separator values to be ignored.');
479     }
480
481     // Get the settings form.
482     $settings_form = ['#value' => []];
483     $format = isset($form_state->getUserInput()['options']['type']) ? $form_state->getUserInput()['options']['type'] : $this->options['type'];
484     if ($formatter = $this->getFormatterInstance($format)) {
485       $settings_form = $formatter->settingsForm($form, $form_state);
486       // Convert field UI selector states to work in the Views field form.
487       FormHelper::rewriteStatesSelector($settings_form, "fields[{$field->getName()}][settings_edit_form]", 'options');
488     }
489     $form['settings'] = $settings_form;
490   }
491
492   /**
493    * {@inheritdoc}
494    */
495   public function submitFormCalculateOptions(array $options, array $form_state_options) {
496     // When we change the formatter type we don't want to keep any of the
497     // previous configured formatter settings, as there might be schema
498     // conflict.
499     unset($options['settings']);
500     $options = $form_state_options + $options;
501     if (!isset($options['settings'])) {
502       $options['settings'] = [];
503     }
504     return $options;
505   }
506
507   /**
508    * Provide options for multiple value fields.
509    */
510   public function multiple_options_form(&$form, FormStateInterface $form_state) {
511     $field = $this->getFieldDefinition();
512
513     $form['multiple_field_settings'] = [
514       '#type' => 'details',
515       '#title' => $this->t('Multiple field settings'),
516       '#weight' => 5,
517     ];
518
519     $form['group_rows'] = [
520       '#title' => $this->t('Display all values in the same row'),
521       '#type' => 'checkbox',
522       '#default_value' => $this->options['group_rows'],
523       '#description' => $this->t('If checked, multiple values for this field will be shown in the same row. If not checked, each value in this field will create a new row. If using group by, please make sure to group by "Entity ID" for this setting to have any effect.'),
524       '#fieldset' => 'multiple_field_settings',
525     ];
526
527     // Make the string translatable by keeping it as a whole rather than
528     // translating prefix and suffix separately.
529     list($prefix, $suffix) = explode('@count', $this->t('Display @count value(s)'));
530
531     if ($field->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
532       $type = 'textfield';
533       $options = NULL;
534       $size = 5;
535     }
536     else {
537       $type = 'select';
538       $range = range(1, $field->getCardinality());
539       $options = array_combine($range, $range);
540       $size = 1;
541     }
542     $form['multi_type'] = [
543       '#type' => 'radios',
544       '#title' => $this->t('Display type'),
545       '#options' => [
546         'ul' => $this->t('Unordered list'),
547         'ol' => $this->t('Ordered list'),
548         'separator' => $this->t('Simple separator'),
549       ],
550       '#states' => [
551         'visible' => [
552           ':input[name="options[group_rows]"]' => ['checked' => TRUE],
553         ],
554       ],
555       '#default_value' => $this->options['multi_type'],
556       '#fieldset' => 'multiple_field_settings',
557     ];
558
559     $form['separator'] = [
560       '#type' => 'textfield',
561       '#title' => $this->t('Separator'),
562       '#default_value' => $this->options['separator'],
563       '#states' => [
564         'visible' => [
565           ':input[name="options[group_rows]"]' => ['checked' => TRUE],
566           ':input[name="options[multi_type]"]' => ['value' => 'separator'],
567         ],
568       ],
569       '#fieldset' => 'multiple_field_settings',
570     ];
571
572     $form['delta_limit'] = [
573       '#type' => $type,
574       '#size' => $size,
575       '#field_prefix' => $prefix,
576       '#field_suffix' => $suffix,
577       '#options' => $options,
578       '#default_value' => $this->options['delta_limit'],
579       '#prefix' => '<div class="container-inline">',
580       '#states' => [
581         'visible' => [
582           ':input[name="options[group_rows]"]' => ['checked' => TRUE],
583         ],
584       ],
585       '#fieldset' => 'multiple_field_settings',
586     ];
587
588     list($prefix, $suffix) = explode('@count', $this->t('starting from @count'));
589     $form['delta_offset'] = [
590       '#type' => 'textfield',
591       '#size' => 5,
592       '#field_prefix' => $prefix,
593       '#field_suffix' => $suffix,
594       '#default_value' => $this->options['delta_offset'],
595       '#states' => [
596         'visible' => [
597           ':input[name="options[group_rows]"]' => ['checked' => TRUE],
598         ],
599       ],
600       '#description' => $this->t('(first item is 0)'),
601       '#fieldset' => 'multiple_field_settings',
602     ];
603     $form['delta_reversed'] = [
604       '#title' => $this->t('Reversed'),
605       '#type' => 'checkbox',
606       '#default_value' => $this->options['delta_reversed'],
607       '#suffix' => $suffix,
608       '#states' => [
609         'visible' => [
610           ':input[name="options[group_rows]"]' => ['checked' => TRUE],
611         ],
612       ],
613       '#description' => $this->t('(start from last values)'),
614       '#fieldset' => 'multiple_field_settings',
615     ];
616     $form['delta_first_last'] = [
617       '#title' => $this->t('First and last only'),
618       '#type' => 'checkbox',
619       '#default_value' => $this->options['delta_first_last'],
620       '#suffix' => '</div>',
621       '#states' => [
622         'visible' => [
623           ':input[name="options[group_rows]"]' => ['checked' => TRUE],
624         ],
625       ],
626       '#fieldset' => 'multiple_field_settings',
627     ];
628   }
629
630   /**
631    * Extend the groupby form with group columns.
632    */
633   public function buildGroupByForm(&$form, FormStateInterface $form_state) {
634     parent::buildGroupByForm($form, $form_state);
635     // With "field API" fields, the column target of the grouping function
636     // and any additional grouping columns must be specified.
637
638     $field_columns = array_keys($this->getFieldDefinition()->getColumns());
639     $group_columns = [
640       'entity_id' => $this->t('Entity ID'),
641     ] + array_map('ucfirst', array_combine($field_columns, $field_columns));
642
643     $form['group_column'] = [
644       '#type' => 'select',
645       '#title' => $this->t('Group column'),
646       '#default_value' => $this->options['group_column'],
647       '#description' => $this->t('Select the column of this field to apply the grouping function selected above.'),
648       '#options' => $group_columns,
649     ];
650
651     $options = [
652       'bundle' => 'Bundle',
653       'language' => 'Language',
654       'entity_type' => 'Entity_type',
655     ];
656     // Add on defined fields, noting that they're prefixed with the field name.
657     $form['group_columns'] = [
658       '#type' => 'checkboxes',
659       '#title' => $this->t('Group columns (additional)'),
660       '#default_value' => $this->options['group_columns'],
661       '#description' => $this->t('Select any additional columns of this field to include in the query and to group on.'),
662       '#options' => $options + $group_columns,
663     ];
664   }
665
666   public function submitGroupByForm(&$form, FormStateInterface $form_state) {
667     parent::submitGroupByForm($form, $form_state);
668     $item = &$form_state->get('handler')->options;
669
670     // Add settings for "field API" fields.
671     $item['group_column'] = $form_state->getValue(['options', 'group_column']);
672     $item['group_columns'] = array_filter($form_state->getValue(['options', 'group_columns']));
673   }
674
675   /**
676    * Render all items in this field together.
677    *
678    * When using advanced render, each possible item in the list is rendered
679    * individually. Then the items are all pasted together.
680    */
681   public function renderItems($items) {
682     if (!empty($items)) {
683       $items = $this->prepareItemsByDelta($items);
684       if ($this->options['multi_type'] == 'separator' || !$this->options['group_rows']) {
685         $separator = $this->options['multi_type'] == 'separator' ? Xss::filterAdmin($this->options['separator']) : '';
686         $build = [
687           '#type' => 'inline_template',
688           '#template' => '{{ items | safe_join(separator) }}',
689           '#context' => ['separator' => $separator, 'items' => $items],
690         ];
691       }
692       else {
693         $build = [
694           '#theme' => 'item_list',
695           '#items' => $items,
696           '#title' => NULL,
697           '#list_type' => $this->options['multi_type'],
698         ];
699       }
700       return $this->renderer->render($build);
701     }
702   }
703
704   /**
705    * Adapts the $items according to the delta configuration.
706    *
707    * This selects displayed deltas, reorders items, and takes offsets into
708    * account.
709    *
710    * @param array $all_values
711    *   The items for individual rendering.
712    *
713    * @return array
714    *   The manipulated items.
715    */
716   protected function prepareItemsByDelta(array $all_values) {
717     if ($this->options['delta_reversed']) {
718       $all_values = array_reverse($all_values);
719     }
720
721     // We are supposed to show only certain deltas.
722     if ($this->limit_values) {
723       $row = $this->view->result[$this->view->row_index];
724
725       // Offset is calculated differently when row grouping for a field is not
726       // enabled. Since there are multiple rows, delta needs to be taken into
727       // account, so that different values are shown per row.
728       if (!$this->options['group_rows'] && isset($this->aliases['delta']) && isset($row->{$this->aliases['delta']})) {
729         $delta_limit = 1;
730         $offset = $row->{$this->aliases['delta']};
731       }
732       // Single fields don't have a delta available so choose 0.
733       elseif (!$this->options['group_rows'] && !$this->multiple) {
734         $delta_limit = 1;
735         $offset = 0;
736       }
737       else {
738         $delta_limit = $this->options['delta_limit'];
739         $offset = intval($this->options['delta_offset']);
740
741         // We should only get here in this case if there is an offset, and in
742         // that case we are limiting to all values after the offset.
743         if ($delta_limit === 0) {
744           $delta_limit = count($all_values) - $offset;
745         }
746       }
747
748       // Determine if only the first and last values should be shown.
749       $delta_first_last = $this->options['delta_first_last'];
750
751       $new_values = [];
752       for ($i = 0; $i < $delta_limit; $i++) {
753         $new_delta = $offset + $i;
754
755         if (isset($all_values[$new_delta])) {
756           // If first-last option was selected, only use the first and last
757           // values.
758           if (!$delta_first_last
759             // Use the first value.
760             || $new_delta == $offset
761             // Use the last value.
762             || $new_delta == ($delta_limit + $offset - 1)) {
763             $new_values[] = $all_values[$new_delta];
764           }
765         }
766       }
767       $all_values = $new_values;
768     }
769
770     return $all_values;
771   }
772
773   /**
774    * {@inheritdoc}
775    */
776   public function preRender(&$values) {
777     parent::preRender($values);
778     $this->getEntityFieldRenderer()->preRender($values);
779   }
780
781   /**
782    * Returns the entity field renderer.
783    *
784    * @return \Drupal\views\Entity\Render\EntityFieldRenderer
785    *   The entity field renderer.
786    */
787   protected function getEntityFieldRenderer() {
788     if (!isset($this->entityFieldRenderer)) {
789       // This can be invoked during field handler initialization in which case
790       // view fields are not set yet.
791       if (!empty($this->view->field)) {
792         foreach ($this->view->field as $field) {
793           // An entity field renderer can handle only a single relationship.
794           if ($field->relationship == $this->relationship && isset($field->entityFieldRenderer)) {
795             $this->entityFieldRenderer = $field->entityFieldRenderer;
796             break;
797           }
798         }
799       }
800       if (!isset($this->entityFieldRenderer)) {
801         $entity_type = $this->entityManager->getDefinition($this->getEntityType());
802         $this->entityFieldRenderer = new EntityFieldRenderer($this->view, $this->relationship, $this->languageManager, $entity_type, $this->entityManager);
803       }
804     }
805     return $this->entityFieldRenderer;
806   }
807
808   /**
809    * Gets an array of items for the field.
810    *
811    * @param \Drupal\views\ResultRow $values
812    *   The result row object containing the values.
813    *
814    * @return array
815    *   An array of items for the field.
816    */
817   public function getItems(ResultRow $values) {
818     if (!$this->displayHandler->useGroupBy()) {
819       $build_list = $this->getEntityFieldRenderer()->render($values, $this);
820     }
821     else {
822       // For grouped results we need to retrieve a massaged entity having
823       // grouped field values to ensure that "grouped by" values, especially
824       // those with multiple cardinality work properly. See
825       // \Drupal\Tests\views\Kernel\QueryGroupByTest::testGroupByFieldWithCardinality.
826       $display = [
827         'type' => $this->options['type'],
828         'settings' => $this->options['settings'],
829         'label' => 'hidden',
830       ];
831       // Optional relationships may not provide an entity at all. So we can't
832       // use createEntityForGroupBy() for those rows.
833       if ($entity = $this->getEntity($values)) {
834         $entity = $this->createEntityForGroupBy($entity, $values);
835         // Some bundles might not have a specific field, in which case the faked
836         // entity doesn't have it either.
837         $build_list = isset($entity->{$this->definition['field_name']}) ? $entity->{$this->definition['field_name']}->view($display) : NULL;
838       }
839       else {
840         $build_list = NULL;
841       }
842     }
843
844     if (!$build_list) {
845       return [];
846     }
847
848     if ($this->options['field_api_classes']) {
849       return [['rendered' => $this->renderer->render($build_list)]];
850     }
851
852     // Render using the formatted data itself.
853     $items = [];
854     // Each item is extracted and rendered separately, the top-level formatter
855     // render array itself is never rendered, so we extract its bubbleable
856     // metadata and add it to each child individually.
857     $bubbleable = BubbleableMetadata::createFromRenderArray($build_list);
858     foreach (Element::children($build_list) as $delta) {
859       BubbleableMetadata::createFromRenderArray($build_list[$delta])
860         ->merge($bubbleable)
861         ->applyTo($build_list[$delta]);
862       $items[$delta] = [
863         'rendered' => $build_list[$delta],
864         // Add the raw field items (for use in tokens).
865         'raw' => $build_list['#items'][$delta],
866       ];
867     }
868     return $items;
869   }
870
871   /**
872    * Creates a fake entity with grouped field values.
873    *
874    * @param \Drupal\Core\Entity\EntityInterface $entity
875    *   The entity to be processed.
876    * @param \Drupal\views\ResultRow $row
877    *   The result row object containing the values.
878    *
879    * @return bool|\Drupal\Core\Entity\FieldableEntityInterface
880    *   Returns a new entity object containing the grouped field values.
881    */
882   protected function createEntityForGroupBy(EntityInterface $entity, ResultRow $row) {
883     // Retrieve the correct translation object.
884     $processed_entity = clone $this->getEntityFieldRenderer()->getEntityTranslation($entity, $row);
885
886     // Copy our group fields into the cloned entity. It is possible this will
887     // cause some weirdness, but there is only so much we can hope to do.
888     if (!empty($this->group_fields) && isset($entity->{$this->definition['field_name']})) {
889       // first, test to see if we have a base value.
890       $base_value = [];
891       // Note: We would copy original values here, but it can cause problems.
892       // For example, text fields store cached filtered values as 'safe_value'
893       // which does not appear anywhere in the field definition so we cannot
894       // affect it. Other side effects could happen similarly.
895       $data = FALSE;
896       foreach ($this->group_fields as $field_name => $column) {
897         if (property_exists($row, $this->aliases[$column])) {
898           $base_value[$field_name] = $row->{$this->aliases[$column]};
899           if (isset($base_value[$field_name])) {
900             $data = TRUE;
901           }
902         }
903       }
904
905       // If any of our aggregated fields have data, fake it:
906       if ($data) {
907         // Now, overwrite the original value with our aggregated value.
908         // This overwrites it so there is always just one entry.
909         $processed_entity->{$this->definition['field_name']} = [$base_value];
910       }
911       else {
912         $processed_entity->{$this->definition['field_name']} = [];
913       }
914     }
915
916     return $processed_entity;
917   }
918
919   public function render_item($count, $item) {
920     return render($item['rendered']);
921   }
922
923   protected function documentSelfTokens(&$tokens) {
924     $field = $this->getFieldDefinition();
925     foreach ($field->getColumns() as $id => $column) {
926       $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = $this->t('Raw @column', ['@column' => $id]);
927     }
928   }
929
930   protected function addSelfTokens(&$tokens, $item) {
931     $field = $this->getFieldDefinition();
932     foreach ($field->getColumns() as $id => $column) {
933       // Use \Drupal\Component\Utility\Xss::filterAdmin() because it's user data
934       // and we can't be sure it is safe. We know nothing about the data,
935       // though, so we can't really do much else.
936       if (isset($item['raw'])) {
937         $raw = $item['raw'];
938
939         if (is_array($raw)) {
940           if (isset($raw[$id]) && is_scalar($raw[$id])) {
941             $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = Xss::filterAdmin($raw[$id]);
942           }
943           else {
944             // Make sure that empty values are replaced as well.
945             $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = '';
946           }
947         }
948
949         if (is_object($raw)) {
950           $property = $raw->get($id);
951           // Check if TypedDataInterface is implemented so we know how to render
952           // the item as a string.
953           if (!empty($property) && $property instanceof TypedDataInterface) {
954             $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = Xss::filterAdmin($property->getString());
955           }
956           else {
957             // Make sure that empty values are replaced as well.
958             $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = '';
959           }
960         }
961       }
962     }
963   }
964
965   /**
966    * Returns the field formatter instance.
967    *
968    * @return \Drupal\Core\Field\FormatterInterface|null
969    *   The field formatter instance.
970    */
971   protected function getFormatterInstance($format = NULL) {
972     if (!isset($format)) {
973       $format = $this->options['type'];
974     }
975     $settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($format);
976
977     $options = [
978       'field_definition' => $this->getFieldDefinition(),
979       'configuration' => [
980         'type' => $format,
981         'settings' => $settings,
982         'label' => '',
983         'weight' => 0,
984       ],
985       'view_mode' => '_custom',
986     ];
987
988     return $this->formatterPluginManager->getInstance($options);
989   }
990
991   /**
992    * {@inheritdoc}
993    */
994   public function calculateDependencies() {
995     $this->dependencies = parent::calculateDependencies();
996
997     // Add the module providing the configured field storage as a dependency.
998     if (($field_storage_definition = $this->getFieldStorageDefinition()) && $field_storage_definition instanceof EntityInterface) {
999       $this->dependencies['config'][] = $field_storage_definition->getConfigDependencyName();
1000     }
1001     if (!empty($this->options['type'])) {
1002       // Add the module providing the formatter.
1003       $this->dependencies['module'][] = $this->formatterPluginManager->getDefinition($this->options['type'])['provider'];
1004
1005       // Add the formatter's dependencies.
1006       if (($formatter = $this->getFormatterInstance()) && $formatter instanceof DependentPluginInterface) {
1007         $this->calculatePluginDependencies($formatter);
1008       }
1009     }
1010
1011     return $this->dependencies;
1012   }
1013
1014   /**
1015    * {@inheritdoc}
1016    */
1017   public function getCacheMaxAge() {
1018     return Cache::PERMANENT;
1019   }
1020
1021   /**
1022    * {@inheritdoc}
1023    */
1024   public function getCacheContexts() {
1025     return $this->getEntityFieldRenderer()->getCacheContexts();
1026   }
1027
1028   /**
1029    * {@inheritdoc}
1030    */
1031   public function getCacheTags() {
1032     $field_definition = $this->getFieldDefinition();
1033     $field_storage_definition = $this->getFieldStorageDefinition();
1034     return Cache::mergeTags(
1035       $field_definition instanceof CacheableDependencyInterface ? $field_definition->getCacheTags() : [],
1036       $field_storage_definition instanceof CacheableDependencyInterface ? $field_storage_definition->getCacheTags() : []
1037     );
1038   }
1039
1040   /**
1041    * Gets the table mapping for the entity type of the field.
1042    *
1043    * @return \Drupal\Core\Entity\Sql\DefaultTableMapping
1044    *   The table mapping.
1045    */
1046   protected function getTableMapping() {
1047     return $this->entityManager->getStorage($this->definition['entity_type'])->getTableMapping();
1048   }
1049
1050   /**
1051    * {@inheritdoc}
1052    */
1053   public function getValue(ResultRow $values, $field = NULL) {
1054     $entity = $this->getEntity($values);
1055     // Retrieve the translated object.
1056     $translated_entity = $this->getEntityFieldRenderer()->getEntityTranslation($entity, $values);
1057
1058     // Some bundles might not have a specific field, in which case the entity
1059     // (potentially a fake one) doesn't have it either.
1060     /** @var \Drupal\Core\Field\FieldItemListInterface $field_item_list */
1061     $field_item_list = isset($translated_entity->{$this->definition['field_name']}) ? $translated_entity->{$this->definition['field_name']} : NULL;
1062
1063     if (!isset($field_item_list)) {
1064       // There isn't anything we can do without a valid field.
1065       return NULL;
1066     }
1067
1068     $field_item_definition = $field_item_list->getFieldDefinition();
1069
1070     $values = [];
1071     foreach ($field_item_list as $field_item) {
1072       /** @var \Drupal\Core\Field\FieldItemInterface $field_item */
1073       if ($field) {
1074         $values[] = $field_item->$field;
1075       }
1076       // Find the value using the main property of the field. If no main
1077       // property is provided fall back to 'value'.
1078       elseif ($main_property_name = $field_item->mainPropertyName()) {
1079         $values[] = $field_item->{$main_property_name};
1080       }
1081       else {
1082         $values[] = $field_item->value;
1083       }
1084     }
1085     if ($field_item_definition->getFieldStorageDefinition()->getCardinality() == 1) {
1086       return reset($values);
1087     }
1088     else {
1089       return $values;
1090     }
1091   }
1092
1093   /**
1094    * {@inheritdoc}
1095    */
1096   public function onDependencyRemoval(array $dependencies) {
1097     // See if this handler is responsible for any of the dependencies being
1098     // removed. If this is the case, indicate that this handler needs to be
1099     // removed from the View.
1100     $remove = FALSE;
1101     // Get all the current dependencies for this handler.
1102     $current_dependencies = $this->calculateDependencies();
1103     foreach ($current_dependencies as $group => $dependency_list) {
1104       // Check if any of the handler dependencies match the dependencies being
1105       // removed.
1106       foreach ($dependency_list as $config_key) {
1107         if (isset($dependencies[$group]) && array_key_exists($config_key, $dependencies[$group])) {
1108           // This handlers dependency matches a dependency being removed,
1109           // indicate that this handler needs to be removed.
1110           $remove = TRUE;
1111           break 2;
1112         }
1113       }
1114     }
1115     return $remove;
1116   }
1117
1118 }