Upgraded imagemagick and manually altered pdf to image module to handle changes....
[yaffs-website] / web / modules / contrib / paragraphs / src / Entity / Paragraph.php
1 <?php
2
3 namespace Drupal\paragraphs\Entity;
4
5 use Drupal\Component\Utility\NestedArray;
6 use Drupal\Component\Utility\Unicode;
7 use Drupal\Core\Entity\EntityStorageInterface;
8 use Drupal\Core\Field\BaseFieldDefinition;
9 use Drupal\Core\Entity\ContentEntityBase;
10 use Drupal\Core\Entity\EntityTypeInterface;
11 use Drupal\Core\Field\ChangedFieldItemList;
12 use Drupal\Core\Field\FieldDefinitionInterface;
13 use Drupal\Core\TypedData\TranslatableInterface;
14 use Drupal\field\Entity\FieldStorageConfig;
15 use Drupal\entity_reference_revisions\EntityNeedsSaveInterface;
16 use Drupal\entity_reference_revisions\EntityNeedsSaveTrait;
17 use Drupal\paragraphs\ParagraphInterface;
18 use Drupal\user\UserInterface;
19
20 /**
21  * Defines the Paragraph entity.
22  *
23  * @ingroup paragraphs
24  *
25  * @ContentEntityType(
26  *   id = "paragraph",
27  *   label = @Translation("Paragraph"),
28  *   bundle_label = @Translation("Paragraph type"),
29  *   handlers = {
30  *     "view_builder" = "Drupal\paragraphs\ParagraphViewBuilder",
31  *     "access" = "Drupal\paragraphs\ParagraphAccessControlHandler",
32  *     "storage_schema" = "Drupal\paragraphs\ParagraphStorageSchema",
33  *     "form" = {
34  *       "default" = "Drupal\Core\Entity\ContentEntityForm",
35  *       "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
36  *       "edit" = "Drupal\Core\Entity\ContentEntityForm"
37  *     },
38  *     "views_data" = "Drupal\views\EntityViewsData",
39  *   },
40  *   base_table = "paragraphs_item",
41  *   data_table = "paragraphs_item_field_data",
42  *   revision_table = "paragraphs_item_revision",
43  *   revision_data_table = "paragraphs_item_revision_field_data",
44  *   translatable = TRUE,
45  *   entity_revision_parent_type_field = "parent_type",
46  *   entity_revision_parent_id_field = "parent_id",
47  *   entity_revision_parent_field_name_field = "parent_field_name",
48  *   entity_keys = {
49  *     "id" = "id",
50  *     "uuid" = "uuid",
51  *     "bundle" = "type",
52  *     "langcode" = "langcode",
53  *     "revision" = "revision_id"
54  *   },
55  *   bundle_entity_type = "paragraphs_type",
56  *   field_ui_base_route = "entity.paragraphs_type.edit_form",
57  *   common_reference_revisions_target = TRUE,
58  *   content_translation_ui_skip = TRUE,
59  *   render_cache = FALSE,
60  *   default_reference_revision_settings = {
61  *     "field_storage_config" = {
62  *       "cardinality" = -1,
63  *       "settings" = {
64  *         "target_type" = "paragraph"
65  *       }
66  *     },
67  *     "field_config" = {
68  *       "settings" = {
69  *         "handler" = "default:paragraph"
70  *       }
71  *     },
72  *     "entity_form_display" = {
73  *       "type" = "entity_reference_paragraphs"
74  *     },
75  *     "entity_view_display" = {
76  *       "type" = "entity_reference_revisions_entity_view"
77  *     }
78  *   }
79  * )
80  */
81 class Paragraph extends ContentEntityBase implements ParagraphInterface {
82
83   use EntityNeedsSaveTrait;
84
85   /**
86    * The behavior plugin data for the paragraph entity.
87    *
88    * @var array
89    */
90   protected $unserializedBehaviorSettings;
91
92   /**
93    * Number of summaries.
94    *
95    * @var int
96    */
97   protected $summaryCount;
98
99   /**
100    * {@inheritdoc}
101    */
102   public function getParentEntity() {
103     if (!isset($this->get('parent_type')->value) || !isset($this->get('parent_id')->value)) {
104       return NULL;
105     }
106
107     $parent = \Drupal::entityTypeManager()->getStorage($this->get('parent_type')->value)->load($this->get('parent_id')->value);
108
109     // Return current translation of parent entity, if it exists.
110     if ($parent != NULL && ($parent instanceof TranslatableInterface) && $parent->hasTranslation($this->language()->getId())) {
111       return $parent->getTranslation($this->language()->getId());
112     }
113
114     return $parent;
115   }
116
117   /**
118    * {@inheritdoc}
119    */
120   public function label() {
121     $label = '';
122     if ($parent = $this->getParentEntity()) {
123       $parent_field = $this->get('parent_field_name')->value;
124       $values = $parent->{$parent_field};
125       foreach ($values as $key => $value) {
126         if ($value->entity->id() == $this->id()) {
127           $label = $parent->label() . ' > ' . $value->getFieldDefinition()->getLabel();
128         }
129       }
130     }
131     return $label;
132   }
133
134   /**
135    * {@inheritdoc}
136    */
137   public function preSave(EntityStorageInterface $storage) {
138     parent::preSave($storage);
139
140     // If no owner has been set explicitly, make the current user the owner.
141     if (!$this->getOwner()) {
142       $this->setOwnerId(\Drupal::currentUser()->id());
143     }
144     // If no revision author has been set explicitly, make the node owner the
145     // revision author.
146     if (!$this->getRevisionAuthor()) {
147       $this->setRevisionAuthorId($this->getOwnerId());
148     }
149
150     // If behavior settings are not set then get them from the entity.
151     if ($this->unserializedBehaviorSettings !== NULL) {
152       $this->set('behavior_settings', serialize($this->unserializedBehaviorSettings));
153     }
154   }
155
156   /**
157    * {@inheritdoc}
158    */
159   public function getAllBehaviorSettings() {
160     if ($this->unserializedBehaviorSettings === NULL) {
161       $this->unserializedBehaviorSettings = unserialize($this->get('behavior_settings')->value);
162     }
163     if (!is_array($this->unserializedBehaviorSettings)) {
164       $this->unserializedBehaviorSettings = [];
165     }
166     return $this->unserializedBehaviorSettings;
167   }
168
169   /**
170    * {@inheritdoc}
171    */
172   public function &getBehaviorSetting($plugin_id, $key, $default = NULL) {
173     $settings = $this->getAllBehaviorSettings();
174     $exists = NULL;
175     $value = &NestedArray::getValue($settings, array_merge((array) $plugin_id, (array) $key), $exists);
176     if (!$exists) {
177       $value = $default;
178     }
179     return $value;
180   }
181
182   /**
183    * {@inheritdoc}
184    */
185   public function setAllBehaviorSettings(array $settings) {
186     // Set behavior settings fields.
187     $this->unserializedBehaviorSettings = $settings;
188   }
189
190   /**
191    * {@inheritdoc}
192    */
193   public function setBehaviorSettings($plugin_id, array $settings) {
194     // Set behavior settings fields.
195     $this->unserializedBehaviorSettings[$plugin_id] = $settings;
196   }
197
198   /**
199    * {@inheritdoc}
200    */
201   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
202     $this->setNeedsSave(FALSE);
203     parent::postSave($storage, $update);
204   }
205
206   /**
207    * {@inheritdoc}
208    */
209   public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) {
210     parent::preSaveRevision($storage, $record);
211   }
212
213   /**
214    * {@inheritdoc}
215    */
216   public function getCreatedTime() {
217     return $this->get('created')->value;
218   }
219
220   /**
221    * {@inheritdoc}
222    */
223   public function getOwner() {
224     return $this->get('uid')->entity;
225   }
226
227   /**
228    * {@inheritdoc}
229    */
230   public function getOwnerId() {
231     return $this->get('uid')->target_id;
232   }
233
234   /**
235    * {@inheritdoc}
236    */
237   public function setOwnerId($uid) {
238     $this->set('uid', $uid);
239     return $this;
240   }
241
242   /**
243    * {@inheritdoc}
244    */
245   public function setOwner(UserInterface $account) {
246     $this->set('uid', $account->id());
247     return $this;
248   }
249
250   /**
251    * {@inheritdoc}
252    */
253   public function getType() {
254     return $this->bundle();
255   }
256
257   /**
258    * {@inheritdoc}
259    */
260   public function getParagraphType() {
261     return $this->type->entity;
262   }
263
264   /**
265    * {@inheritdoc}
266    */
267   public function getRevisionAuthor() {
268     return $this->get('revision_uid')->entity;
269   }
270
271   /**
272    * {@inheritdoc}
273    */
274   public function setRevisionAuthorId($uid) {
275     $this->set('revision_uid', $uid);
276     return $this;
277   }
278
279   /**
280    * {@inheritdoc}
281    */
282   public function getRevisionLog() {
283     return '';
284   }
285
286   /**
287    * {@inheritdoc}
288    */
289   public function setRevisionLog($revision_log) {
290     return $this;
291   }
292
293   /**
294    * {@inheritdoc}
295    */
296   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
297     $fields['id'] = BaseFieldDefinition::create('integer')
298       ->setLabel(t('ID'))
299       ->setDescription(t('The ID of the Paragraphs entity.'))
300       ->setReadOnly(TRUE)
301       ->setSetting('unsigned', TRUE);
302
303     $fields['uuid'] = BaseFieldDefinition::create('uuid')
304       ->setLabel(t('UUID'))
305       ->setDescription(t('The UUID of the paragraphs entity.'))
306       ->setReadOnly(TRUE);
307
308     $fields['revision_id'] = BaseFieldDefinition::create('integer')
309       ->setLabel(t('Revision ID'))
310       ->setDescription(t('The paragraphs entity revision ID.'))
311       ->setReadOnly(TRUE)
312       ->setSetting('unsigned', TRUE);
313
314     $fields['type'] = BaseFieldDefinition::create('entity_reference')
315       ->setLabel(t('Type'))
316       ->setDescription(t('The Paragraphs type.'))
317       ->setSetting('target_type', 'paragraphs_type')
318       ->setReadOnly(TRUE);
319
320     $fields['langcode'] = BaseFieldDefinition::create('language')
321       ->setLabel(t('Language code'))
322       ->setDescription(t('The paragraphs entity language code.'))
323       ->setRevisionable(TRUE);
324
325     $fields['uid'] = BaseFieldDefinition::create('entity_reference')
326       ->setLabel(t('Authored by'))
327       ->setDescription(t('The user ID of the paragraphs author.'))
328       ->setRevisionable(TRUE)
329       ->setSetting('target_type', 'user')
330       ->setSetting('handler', 'default')
331       ->setDefaultValueCallback('Drupal\paragraphs\Entity\Paragraph::getCurrentUserId')
332       ->setTranslatable(TRUE)
333       ->setDisplayOptions('form', array(
334         'type' => 'hidden',
335         'weight' => 0,
336       ))
337       ->setDisplayConfigurable('form', TRUE);
338
339     $fields['status'] = BaseFieldDefinition::create('boolean')
340       ->setLabel(t('Published'))
341       ->setRevisionable(TRUE)
342       ->setTranslatable(TRUE)
343       ->setDefaultValue(TRUE)
344       ->setDisplayConfigurable('form', TRUE);
345
346     $fields['created'] = BaseFieldDefinition::create('created')
347       ->setLabel(t('Authored on'))
348       ->setDescription(t('The time that the Paragraph was created.'))
349       ->setRevisionable(TRUE)
350       ->setTranslatable(TRUE)
351       ->setDisplayOptions('form', array(
352         'type' => 'hidden',
353         'weight' => 0,
354       ))
355       ->setDisplayConfigurable('form', TRUE);
356
357     $fields['revision_uid'] = BaseFieldDefinition::create('entity_reference')
358       ->setLabel(t('Revision user ID'))
359       ->setDescription(t('The user ID of the author of the current revision.'))
360       ->setSetting('target_type', 'user')
361       ->setQueryable(FALSE)
362       ->setRevisionable(TRUE);
363
364     $fields['parent_id'] = BaseFieldDefinition::create('string')
365       ->setLabel(t('Parent ID'))
366       ->setDescription(t('The ID of the parent entity of which this entity is referenced.'))
367       ->setSetting('is_ascii', TRUE);
368
369     $fields['parent_type'] = BaseFieldDefinition::create('string')
370       ->setLabel(t('Parent type'))
371       ->setDescription(t('The entity parent type to which this entity is referenced.'))
372       ->setSetting('is_ascii', TRUE)
373       ->setSetting('max_length', EntityTypeInterface::ID_MAX_LENGTH);
374
375     $fields['parent_field_name'] = BaseFieldDefinition::create('string')
376       ->setLabel(t('Parent field name'))
377       ->setDescription(t('The entity parent field name to which this entity is referenced.'))
378       ->setSetting('is_ascii', TRUE)
379       ->setSetting('max_length', FieldStorageConfig::NAME_MAX_LENGTH);
380
381     $fields['behavior_settings'] = BaseFieldDefinition::create('string_long')
382       ->setLabel(t('Behavior settings'))
383       ->setDescription(t('The behavior plugin settings'))
384       ->setRevisionable(TRUE)
385       ->setDefaultValue(serialize([]));
386
387     return $fields;
388   }
389
390   /**
391    * Default value callback for 'uid' base field definition.
392    *
393    * @see ::baseFieldDefinitions()
394    *
395    * @return array
396    *   An array of default values.
397    */
398   public static function getCurrentUserId() {
399     return array(\Drupal::currentUser()->id());
400   }
401
402   /**
403   * {@inheritdoc}
404   */
405  public function createDuplicate() {
406    $duplicate = parent::createDuplicate();
407    // Loop over entity fields and duplicate nested paragraphs.
408    foreach ($duplicate->getFields() as $field) {
409      if ($field->getFieldDefinition()->getType() == 'entity_reference_revisions') {
410        if ($field->getFieldDefinition()->getTargetEntityTypeId() == "paragraph") {
411          foreach ($field as $item) {
412            $item->entity = $item->entity->createDuplicate();
413          }
414        }
415      }
416    }
417    return $duplicate;
418  }
419
420   /**
421    * {@inheritdoc}
422    */
423   public function getSummary(array $options = []) {
424     $show_behavior_summary = isset($options['show_behavior_summary']) ? $options['show_behavior_summary'] : TRUE;
425     $depth_limit = isset($options['depth_limit']) ? $options['depth_limit'] : 1;
426     $this->summaryCount = 0;
427     $summary = [];
428
429     foreach ($this->getFieldDefinitions() as $field_name => $field_definition) {
430       if ($field_definition->getType() == 'image' || $field_definition->getType() == 'file') {
431         $file_summary = $this->getFileSummary($field_name);
432         if ($file_summary != '') {
433           $summary[] = $file_summary;
434         }
435       }
436
437       $text_summary = $this->getTextSummary($field_name, $field_definition);
438       if ($text_summary != '') {
439         $summary[] = $text_summary;
440       }
441
442       if ($field_definition->getType() == 'entity_reference_revisions') {
443         // Decrease the depth, since we are entering a nested paragraph.
444         $nested_summary = $this->getNestedSummary($field_name, [
445           'show_behavior_summary' => $show_behavior_summary,
446           'depth_limit' => $depth_limit - 1
447         ]);
448         if ($nested_summary != '') {
449           $summary[] = $nested_summary;
450         }
451       }
452
453       if ($field_type = $field_definition->getType() == 'entity_reference') {
454         if (!in_array($field_name, ['type', 'uid', 'revision_uid'])) {
455           if ($this->get($field_name)->entity) {
456             $summary[] = $this->get($field_name)->entity->label();
457           }
458         }
459       }
460
461       // Add the Block admin label referenced by block_field.
462       if ($field_definition->getType() == 'block_field') {
463         if (!empty($this->get($field_name)->first())) {
464           $block_admin_label = $this->get($field_name)->first()->getBlock()->getPluginDefinition()['admin_label'];
465           $summary[] = $block_admin_label;
466         }
467       }
468     }
469
470     if ($show_behavior_summary) {
471       $paragraphs_type = $this->getParagraphType();
472       foreach ($paragraphs_type->getEnabledBehaviorPlugins() as $plugin_id => $plugin) {
473         if ($plugin_summary = $plugin->settingsSummary($this)) {
474           $summary = array_merge($summary, $plugin_summary);
475         }
476       }
477     }
478
479     if ($this->summaryCount) {
480       array_unshift($summary, (string) \Drupal::translation()->formatPlural($this->summaryCount, '1 child', '@count children'));
481     }
482
483     $collapsed_summary_text = implode(', ', $summary);
484     return strip_tags($collapsed_summary_text);
485   }
486
487   /**
488    * Returns an array of field names to skip in ::isChanged.
489    *
490    * @return array
491    *   An array of field names.
492    */
493   protected function getFieldsToSkipFromChangedCheck() {
494     // A list of revision fields which should be skipped from the comparision.
495     $fields = [
496       $this->getEntityType()->getKey('revision'),
497       'revision_uid',
498       'revision_log',
499       'revision_log_message',
500     ];
501
502     return $fields;
503   }
504
505   /**
506    * {@inheritdoc}
507    */
508   public function isChanged() {
509     if ($this->isNew()) {
510       return TRUE;
511     }
512
513     // $this->original only exists during save. If it exists we re-use it here
514     // for performance reasons.
515     /** @var \Drupal\paragraphs\ParagraphInterface $original */
516     $original = $this->original ?: NULL;
517     if (!$original) {
518       $original = $this->entityTypeManager()->getStorage($this->getEntityTypeId())->loadRevision($this->getLoadedRevisionId());
519     }
520
521     // If the current revision has just been added, we have a change.
522     if ($original->isNewRevision()) {
523       return TRUE;
524     }
525
526     // The list of fields to skip from the comparision.
527     $skip_fields = $this->getFieldsToSkipFromChangedCheck();
528
529     // Compare field item current values with the original ones to determine
530     // whether we have changes. We skip also computed fields as comparing them
531     // with their original values might not be possible or be meaningless.
532     foreach ($this->getFieldDefinitions() as $field_name => $definition) {
533       if (in_array($field_name, $skip_fields, TRUE)) {
534         continue;
535       }
536       $field = $this->get($field_name);
537       // When saving entities in the user interface, the changed timestamp is
538       // automatically incremented by ContentEntityForm::submitForm() even if
539       // nothing was actually changed. Thus, the changed time needs to be
540       // ignored when determining whether there are any actual changes in the
541       // entity.
542       if (!($field instanceof ChangedFieldItemList) && !$definition->isComputed()) {
543         $items = $field->filterEmptyItems();
544         $original_items = $original->get($field_name)->filterEmptyItems();
545         if (!$items->equals($original_items)) {
546           return TRUE;
547         }
548       }
549     }
550
551     return FALSE;
552   }
553
554   /**
555    * Returns summary for file paragraph.
556    *
557    * @param string $field_name
558    *   Field name from field definition.
559    *
560    * @return string
561    *   Summary for image.
562    */
563   protected function getFileSummary($field_name) {
564     $summary = '';
565     if ($this->get($field_name)->entity) {
566       foreach ($this->get($field_name) as $file_key => $file_value) {
567
568         $text = '';
569         if ($file_value->description != '') {
570           $text = $file_value->description;
571         }
572         elseif ($file_value->title != '') {
573           $text = $file_value->title;
574         }
575         elseif ($file_value->alt != '') {
576           $text = $file_value->alt;
577         }
578         elseif ($file_value->entity->getFileName()) {
579           $text = $file_value->entity->getFileName();
580         }
581
582         if (strlen($text) > 150) {
583           $text = Unicode::truncate($text, 150);
584         }
585
586         $summary = $text;
587       }
588     }
589
590     return trim($summary);
591   }
592
593   /**
594    * Returns summary for nested paragraphs.
595    *
596    * @param string $field_name
597    *   Field definition id for paragraph.
598    * @param array $options
599    *   (optional) An associative array of additional options.
600    *   See \Drupal\paragraphs\ParagraphInterface::getSummary() for all of the
601    *   available options.
602    *
603    * @return string
604    *   Short summary for nested Paragraphs type
605    *   or NULL if the summary is empty.
606    */
607   protected function getNestedSummary($field_name, array $options) {
608     $summary = [];
609     if ($options['depth_limit'] >= 0) {
610       foreach ($this->get($field_name) as $item) {
611         $entity = $item->entity;
612         if ($entity instanceof ParagraphInterface) {
613           $summary[] = $entity->getSummary($options);
614           $this->summaryCount++;
615         }
616       }
617     }
618
619     $summary = array_filter($summary);
620
621     if (empty($summary)) {
622       return NULL;
623     }
624
625     $paragraph_summary = implode(', ', $summary);
626     return $paragraph_summary;
627   }
628
629   /**
630    * Returns summary for all text type paragraph.
631    *
632    * @param string $field_name
633    *   Field definition id for paragraph.
634    * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
635    *   Field definition for paragraph.
636    *
637    * @return string
638    *   Short summary for text paragraph.
639    */
640   public function getTextSummary($field_name, FieldDefinitionInterface $field_definition) {
641     $text_types = [
642       'text_with_summary',
643       'text',
644       'text_long',
645       'list_string',
646       'string',
647     ];
648
649     $excluded_text_types = [
650       'parent_id',
651       'parent_type',
652       'parent_field_name',
653     ];
654
655     $summary = '';
656     if (in_array($field_definition->getType(), $text_types)) {
657       if (in_array($field_name, $excluded_text_types)) {
658         return $summary;
659       }
660
661       $text = $this->get($field_name)->value;
662       if (strlen($text) > 150) {
663         $text = Unicode::truncate($text, 150);
664       }
665
666       $summary = $text;
667     }
668
669     return trim($summary);
670   }
671
672 }