eda841509cd3d5507fc7c66e64fa2750c683cadf
[yaffs-website] / web / modules / contrib / paragraphs / src / Plugin / Field / FieldWidget / ParagraphsWidget.php
1 <?php
2
3 namespace Drupal\paragraphs\Plugin\Field\FieldWidget;
4
5 use Drupal\Component\Utility\NestedArray;
6 use Drupal\Component\Utility\Html;
7 use Drupal\Core\Entity\Entity\EntityFormDisplay;
8 use Drupal\Core\Entity\EntityInterface;
9 use Drupal\Core\Entity\RevisionableInterface;
10 use Drupal\Core\Field\FieldDefinitionInterface;
11 use Drupal\Core\Field\FieldFilteredMarkup;
12 use Drupal\Core\Field\FieldStorageDefinitionInterface;
13 use Drupal\Core\Field\WidgetBase;
14 use Drupal\Core\Form\FormStateInterface;
15 use Drupal\Core\Field\FieldItemListInterface;
16 use Drupal\Core\Form\SubformState;
17 use Drupal\Core\Render\Element;
18 use Drupal\paragraphs;
19 use Drupal\paragraphs\ParagraphInterface;
20
21 /**
22  * Plugin implementation of the 'entity_reference_revisions paragraphs' widget.
23  *
24  * @FieldWidget(
25  *   id = "paragraphs",
26  *   label = @Translation("Paragraphs EXPERIMENTAL"),
27  *   description = @Translation("An experimental paragraphs inline form widget."),
28  *   field_types = {
29  *     "entity_reference_revisions"
30  *   }
31  * )
32  */
33 class ParagraphsWidget extends WidgetBase {
34
35   /**
36    * Indicates whether the current widget instance is in translation.
37    *
38    * @var bool
39    */
40   protected $isTranslating;
41
42   /**
43    * Id to name ajax buttons that includes field parents and field name.
44    *
45    * @var string
46    */
47   protected $fieldIdPrefix;
48
49   /**
50    * Wrapper id to identify the paragraphs.
51    *
52    * @var string
53    */
54   protected $fieldWrapperId;
55
56   /**
57    * Number of paragraphs item on form.
58    *
59    * @var int
60    */
61   protected $realItemCount;
62
63   /**
64    * Parents for the current paragraph.
65    *
66    * @var array
67    */
68   protected $fieldParents;
69
70   /**
71    * Accessible paragraphs types.
72    *
73    * @var array
74    */
75   protected $accessOptions = NULL;
76
77   /**
78    * {@inheritdoc}
79    */
80   public static function defaultSettings() {
81     return array(
82       'title' => t('Paragraph'),
83       'title_plural' => t('Paragraphs'),
84       'edit_mode' => 'open',
85       'add_mode' => 'dropdown',
86       'form_display_mode' => 'default',
87       'default_paragraph_type' => '',
88     );
89   }
90
91   /**
92    * {@inheritdoc}
93    */
94   public function settingsForm(array $form, FormStateInterface $form_state) {
95     $elements = array();
96
97     $elements['title'] = array(
98       '#type' => 'textfield',
99       '#title' => $this->t('Paragraph Title'),
100       '#description' => $this->t('Label to appear as title on the button as "Add new [title]", this label is translatable'),
101       '#default_value' => $this->getSetting('title'),
102       '#required' => TRUE,
103     );
104
105     $elements['title_plural'] = array(
106       '#type' => 'textfield',
107       '#title' => $this->t('Plural Paragraph Title'),
108       '#description' => $this->t('Title in its plural form.'),
109       '#default_value' => $this->getSetting('title_plural'),
110       '#required' => TRUE,
111     );
112
113     $elements['edit_mode'] = array(
114       '#type' => 'select',
115       '#title' => $this->t('Edit mode'),
116       '#description' => $this->t('The mode the paragraph is in by default. Preview will render the paragraph in the preview view mode.'),
117       '#options' => array(
118         'open' => $this->t('Open'),
119         'closed' => $this->t('Closed'),
120         'preview' => $this->t('Preview'),
121       ),
122       '#default_value' => $this->getSetting('edit_mode'),
123       '#required' => TRUE,
124     );
125
126     $elements['add_mode'] = array(
127       '#type' => 'select',
128       '#title' => $this->t('Add mode'),
129       '#description' => $this->t('The way to add new paragraphs.'),
130       '#options' => array(
131         'select' => $this->t('Select list'),
132         'button' => $this->t('Buttons'),
133         'dropdown' => $this->t('Dropdown button')
134       ),
135       '#default_value' => $this->getSetting('add_mode'),
136       '#required' => TRUE,
137     );
138
139     $elements['form_display_mode'] = array(
140       '#type' => 'select',
141       '#options' => \Drupal::service('entity_display.repository')->getFormModeOptions($this->getFieldSetting('target_type')),
142       '#description' => $this->t('The form display mode to use when rendering the paragraph form.'),
143       '#title' => $this->t('Form display mode'),
144       '#default_value' => $this->getSetting('form_display_mode'),
145       '#required' => TRUE,
146     );
147
148     $options  = [];
149     foreach ($this->getAllowedTypes() as $key => $bundle) {
150       $options[$key] = $bundle['label'];
151     }
152
153     $elements['default_paragraph_type'] = [
154       '#type' => 'select',
155       '#title' => $this->t('Default paragraph type'),
156       '#empty_value' => '_none',
157       '#default_value' => $this->getDefaultParagraphTypeMachineName(),
158       '#options' => $options,
159       '#description' => $this->t('When creating a new host entity, a paragraph of this type is added.'),
160     ];
161
162     return $elements;
163   }
164
165   /**
166    * {@inheritdoc}
167    */
168   public function settingsSummary() {
169     $summary = array();
170     $summary[] = $this->t('Title: @title', ['@title' => $this->getSetting('title')]);
171     $summary[] = $this->t('Plural title: @title_plural', [
172       '@title_plural' => $this->getSetting('title_plural')
173     ]);
174
175     switch($this->getSetting('edit_mode')) {
176       case 'open':
177       default:
178         $edit_mode = $this->t('Open');
179         break;
180       case 'closed':
181         $edit_mode = $this->t('Closed');
182         break;
183       case 'preview':
184         $edit_mode = $this->t('Preview');
185         break;
186     }
187
188     switch($this->getSetting('add_mode')) {
189       case 'select':
190       default:
191         $add_mode = $this->t('Select list');
192         break;
193       case 'button':
194         $add_mode = $this->t('Buttons');
195         break;
196       case 'dropdown':
197         $add_mode = $this->t('Dropdown button');
198         break;
199     }
200
201     $summary[] = $this->t('Edit mode: @edit_mode', ['@edit_mode' => $edit_mode]);
202     $summary[] = $this->t('Add mode: @add_mode', ['@add_mode' => $add_mode]);
203     $summary[] = $this->t('Form display mode: @form_display_mode', [
204       '@form_display_mode' => $this->getSetting('form_display_mode')
205     ]);
206     if ($this->getDefaultParagraphTypeLabelName() !== NULL) {
207       $summary[] = $this->t('Default paragraph type: @default_paragraph_type', [
208         '@default_paragraph_type' => $this->getDefaultParagraphTypeLabelName()
209       ]);
210     }
211
212     return $summary;
213   }
214
215   /**
216    * {@inheritdoc}
217    *
218    * @see \Drupal\content_translation\Controller\ContentTranslationController::prepareTranslation()
219    *   Uses a similar approach to populate a new translation.
220    */
221   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
222     $field_name = $this->fieldDefinition->getName();
223     $parents = $element['#field_parents'];
224     $info = [];
225
226     $paragraphs_entity = NULL;
227     $host = $items->getEntity();
228     $widget_state = static::getWidgetState($parents, $field_name, $form_state);
229
230     $entity_manager = \Drupal::entityTypeManager();
231     $target_type = $this->getFieldSetting('target_type');
232
233     $item_mode = isset($widget_state['paragraphs'][$delta]['mode']) ? $widget_state['paragraphs'][$delta]['mode'] : 'edit';
234     $default_edit_mode = $this->getSetting('edit_mode');
235
236     $show_must_be_saved_warning = !empty($widget_state['paragraphs'][$delta]['show_warning']);
237
238     if (isset($widget_state['paragraphs'][$delta]['entity'])) {
239       $paragraphs_entity = $widget_state['paragraphs'][$delta]['entity'];
240     }
241     elseif (isset($items[$delta]->entity)) {
242       $paragraphs_entity = $items[$delta]->entity;
243
244       // We don't have a widget state yet, get from selector settings.
245       if (!isset($widget_state['paragraphs'][$delta]['mode'])) {
246
247         if ($default_edit_mode == 'open') {
248           $item_mode = 'edit';
249         }
250         elseif ($default_edit_mode == 'closed') {
251           $item_mode = 'closed';
252         }
253         elseif ($default_edit_mode == 'preview') {
254           $item_mode = 'preview';
255         }
256       }
257     }
258     elseif (isset($widget_state['selected_bundle'])) {
259
260       $entity_type = $entity_manager->getDefinition($target_type);
261       $bundle_key = $entity_type->getKey('bundle');
262
263       $paragraphs_entity = $entity_manager->getStorage($target_type)->create(array(
264         $bundle_key => $widget_state['selected_bundle'],
265       ));
266
267       $item_mode = 'edit';
268     }
269
270     if ($item_mode == 'closed') {
271       // Validate closed paragraphs and expand if needed.
272       // @todo Consider recursion.
273       $violations = $paragraphs_entity->validate();
274       $violations->filterByFieldAccess();
275       if (count($violations) > 0) {
276         $item_mode = 'edit';
277         $messages = [];
278         foreach ($violations as $violation) {
279           $messages[] = $violation->getMessage();
280         }
281         $info['validation_error'] = array(
282           '#type' => 'container',
283           '#markup' => $this->t('@messages', ['@messages' => strip_tags(implode('\n', $messages))]),
284           '#attributes' => ['class' => ['messages', 'messages--warning']],
285         );
286       }
287     }
288
289     if ($paragraphs_entity) {
290       // Detect if we are translating.
291       $this->initIsTranslating($form_state, $host);
292       $langcode = $form_state->get('langcode');
293
294       if (!$this->isTranslating) {
295         // Set the langcode if we are not translating.
296         $langcode_key = $paragraphs_entity->getEntityType()->getKey('langcode');
297         if ($paragraphs_entity->get($langcode_key)->value != $langcode) {
298           // If a translation in the given language already exists, switch to
299           // that. If there is none yet, update the language.
300           if ($paragraphs_entity->hasTranslation($langcode)) {
301             $paragraphs_entity = $paragraphs_entity->getTranslation($langcode);
302           }
303           else {
304             $paragraphs_entity->set($langcode_key, $langcode);
305           }
306         }
307       }
308       else {
309         // Add translation if missing for the target language.
310         if (!$paragraphs_entity->hasTranslation($langcode)) {
311           // Get the selected translation of the paragraph entity.
312           $entity_langcode = $paragraphs_entity->language()->getId();
313           $source = $form_state->get(['content_translation', 'source']);
314           $source_langcode = $source ? $source->getId() : $entity_langcode;
315           $paragraphs_entity = $paragraphs_entity->getTranslation($source_langcode);
316           // The paragraphs entity has no content translation source field if
317           // no paragraph entity field is translatable, even if the host is.
318           if ($paragraphs_entity->hasField('content_translation_source')) {
319             // Initialise the translation with source language values.
320             $paragraphs_entity->addTranslation($langcode, $paragraphs_entity->toArray());
321             $translation = $paragraphs_entity->getTranslation($langcode);
322             $manager = \Drupal::service('content_translation.manager');
323             $manager->getTranslationMetadata($translation)->setSource($paragraphs_entity->language()->getId());
324           }
325         }
326         // If any paragraphs type is translatable do not switch.
327         if ($paragraphs_entity->hasField('content_translation_source')) {
328           // Switch the paragraph to the translation.
329           $paragraphs_entity = $paragraphs_entity->getTranslation($langcode);
330         }
331       }
332
333       $element_parents = $parents;
334       $element_parents[] = $field_name;
335       $element_parents[] = $delta;
336       $element_parents[] = 'subform';
337
338       $id_prefix = implode('-', array_merge($parents, array($field_name, $delta)));
339       $wrapper_id = Html::getUniqueId($id_prefix . '-item-wrapper');
340
341       $element += array(
342         '#type' => 'container',
343         '#element_validate' => array(array($this, 'elementValidate')),
344         'subform' => array(
345           '#type' => 'container',
346           '#parents' => $element_parents,
347         ),
348       );
349
350       $element['#prefix'] = '<div id="' . $wrapper_id . '">';
351       $element['#suffix'] = '</div>';
352
353       $item_bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo($target_type);
354       if (isset($item_bundles[$paragraphs_entity->bundle()])) {
355         $bundle_info = $item_bundles[$paragraphs_entity->bundle()];
356
357         $element['top'] = array(
358           '#type' => 'container',
359           '#weight' => -1000,
360           '#attributes' => array(
361             'class' => array(
362               'paragraph-type-top',
363             ),
364           ),
365         );
366
367         $element['top']['paragraph_type_title'] = array(
368           '#type' => 'container',
369           '#weight' => 0,
370           '#attributes' => array(
371             'class' => array(
372               'paragraph-type-title',
373             ),
374           ),
375         );
376
377         $element['top']['paragraph_type_title']['info'] = array(
378           '#markup' => $bundle_info['label'],
379         );
380
381         $actions = [];
382         $links = [];
383
384         $links['duplicate_button'] = [
385           '#type' => 'submit',
386           '#value' => $this->t('Duplicate'),
387           '#name' => strtr($id_prefix, '-', '_') . '_duplicate',
388           '#weight' => 502,
389           '#submit' => [[get_class($this), 'duplicateSubmit']],
390           '#limit_validation_errors' => [array_merge($parents, [$field_name, 'add_more'])],
391           '#delta' => $delta,
392           '#ajax' => [
393             'callback' => [get_class($this), 'itemAjax'],
394             'wrapper' => $widget_state['ajax_wrapper_id'],
395             'effect' => 'fade',
396           ],
397           '#access' => $paragraphs_entity->access('update'),
398           '#prefix' => '<li class="duplicate">',
399           '#suffix' => '</li>',
400         ];
401
402         // Hide the button when translating.
403         $button_access = $paragraphs_entity->access('delete') && !$this->isTranslating;
404         if($item_mode != 'remove') {
405           $links['remove_button'] = [
406             '#type' => 'submit',
407             '#value' => $this->t('Remove'),
408             '#name' => strtr($id_prefix, '-', '_') . '_remove',
409             '#weight' => 501 ,
410             '#submit' => [[get_class($this), 'paragraphsItemSubmit']],
411             '#limit_validation_errors' => [array_merge($parents, [$field_name, 'add_more'])],
412             '#delta' => $delta,
413             '#ajax' => [
414               'callback' => array(get_class($this), 'itemAjax'),
415               'wrapper' => $widget_state['ajax_wrapper_id'],
416               'effect' => 'fade',
417             ],
418             '#access' => $button_access,
419             '#prefix' => '<li class="remove">',
420             '#suffix' => '</li>',
421             '#paragraphs_mode' => 'remove',
422           ];
423         }
424
425         if ($item_mode == 'edit') {
426
427           if (isset($paragraphs_entity)) {
428             $links['collapse_button'] = array(
429               '#type' => 'submit',
430               '#value' => $this->t('Collapse'),
431               '#name' => strtr($id_prefix, '-', '_') . '_collapse',
432               '#weight' => 499,
433               '#submit' => array(array(get_class($this), 'paragraphsItemSubmit')),
434               '#delta' => $delta,
435               '#ajax' => array(
436                 'callback' => array(get_class($this), 'itemAjax'),
437                 'wrapper' => $widget_state['ajax_wrapper_id'],
438                 'effect' => 'fade',
439               ),
440               '#access' => $paragraphs_entity->access('update'),
441               '#prefix' => '<li class="collapse">',
442               '#suffix' => '</li>',
443               '#paragraphs_mode' => 'closed',
444               '#paragraphs_show_warning' => TRUE,
445             );
446           }
447
448           $info['edit_button_info'] = array(
449             '#type' => 'container',
450             '#markup' => $this->t('You are not allowed to edit this @title.', array('@title' => $this->getSetting('title'))),
451             '#attributes' => ['class' => ['messages', 'messages--warning']],
452             '#access' => !$paragraphs_entity->access('update') && $paragraphs_entity->access('delete'),
453           );
454
455           $info['remove_button_info'] = array(
456             '#type' => 'container',
457             '#markup' => $this->t('You are not allowed to remove this @title.', array('@title' => $this->getSetting('title'))),
458             '#attributes' => ['class' => ['messages', 'messages--warning']],
459             '#access' => !$paragraphs_entity->access('delete') && $paragraphs_entity->access('update'),
460           );
461
462           $info['edit_remove_button_info'] = array(
463             '#type' => 'container',
464             '#markup' => $this->t('You are not allowed to edit or remove this @title.', array('@title' => $this->getSetting('title'))),
465             '#attributes' => ['class' => ['messages', 'messages--warning']],
466             '#access' => !$paragraphs_entity->access('update') && !$paragraphs_entity->access('delete'),
467           );
468         }
469         else {
470           $element['top']['paragraphs_edit_button_container'] = [
471             '#type' => 'container',
472             '#weight' => 1,
473             '#attributes' => [
474               'class' => [
475                 'paragraphs-edit-button-container',
476               ],
477             ],
478             'paragraphs_edit_button' => [
479               '#type' => 'submit',
480               '#value' => $this->t('Edit'),
481               '#name' => strtr($id_prefix, '-', '_') . '_edit',
482               '#weight' => 500,
483               '#attributes' => [
484                 'class' => [
485                   'paragraphs-edit-button',
486                 ],
487               ],
488               '#submit' => [[get_class($this), 'paragraphsItemSubmit']],
489               '#limit_validation_errors' => [array_merge($parents, [$field_name, 'add_more'])],
490               '#delta' => $delta,
491               '#ajax' => [
492                 'callback' => [get_class($this), 'itemAjax'],
493                 'wrapper' => $widget_state['ajax_wrapper_id'],
494                 'effect' => 'fade',
495               ],
496               '#access' => $paragraphs_entity->access('update'),
497               '#paragraphs_mode' => 'edit',
498             ]
499           ];
500
501           if ($show_must_be_saved_warning) {
502             $info['must_be_saved_info'] = array(
503               '#type' => 'container',
504               '#markup' => $this->t('You have unsaved changes on this @title item.', array('@title' => $this->getSetting('title'))),
505               '#attributes' => ['class' => ['messages', 'messages--warning']],
506             );
507           }
508
509           $info['preview_info'] = array(
510             '#type' => 'container',
511             '#markup' => $this->t('You are not allowed to view this @title.', array('@title' => $this->getSetting('title'))),
512             '#attributes' => ['class' => ['messages', 'messages--warning']],
513             '#access' => !$paragraphs_entity->access('view'),
514           );
515
516           $info['edit_button_info'] = array(
517             '#type' => 'container',
518             '#markup' => $this->t('You are not allowed to edit this @title.', array('@title' => $this->getSetting('title'))),
519             '#attributes' => ['class' => ['messages', 'messages--warning']],
520             '#access' => !$paragraphs_entity->access('update') && $paragraphs_entity->access('delete'),
521           );
522
523           $info['remove_button_info'] = array(
524             '#type' => 'container',
525             '#markup' => $this->t('You are not allowed to remove this @title.', array('@title' => $this->getSetting('title'))),
526             '#attributes' => ['class' => ['messages', 'messages--warning']],
527             '#access' => !$paragraphs_entity->access('delete') && $paragraphs_entity->access('update'),
528           );
529
530           $info['edit_remove_button_info'] = array(
531             '#type' => 'container',
532             '#markup' => $this->t('You are not allowed to edit or remove this @title.', array('@title' => $this->getSetting('title'))),
533             '#attributes' => ['class' => ['messages', 'messages--warning']],
534             '#access' => !$paragraphs_entity->access('update') && !$paragraphs_entity->access('delete'),
535           );
536         }
537
538         if (count($links)) {
539           $show_links = 0;
540           foreach($links as $link_item) {
541             if (!isset($link_item['#access']) || $link_item['#access']) {
542               $show_links++;
543             }
544           }
545
546           if ($show_links > 0) {
547
548             $element['top']['links'] = $links;
549             if ($show_links > 1) {
550               $element['top']['links']['#theme_wrappers'] = array('dropbutton_wrapper', 'paragraphs_dropbutton_wrapper');
551               $element['top']['links']['prefix'] = array(
552                 '#markup' => '<ul class="dropbutton">',
553                 '#weight' => -999,
554               );
555               $element['top']['links']['suffix'] = array(
556                 '#markup' => '</li>',
557                 '#weight' => 999,
558               );
559             }
560             else {
561               $element['top']['links']['#theme_wrappers'] = array('paragraphs_dropbutton_wrapper');
562               foreach($links as $key => $link_item) {
563                 unset($element['top']['links'][$key]['#prefix']);
564                 unset($element['top']['links'][$key]['#suffix']);
565               }
566             }
567             $element['top']['links']['#weight'] = 1;
568           }
569         }
570
571         if (count($info)) {
572           $show_info = FALSE;
573           foreach($info as $info_item) {
574             if (!isset($info_item['#access']) || $info_item['#access']) {
575               $show_info = TRUE;
576               break;
577             }
578           }
579
580           if ($show_info) {
581             $element['info'] = $info;
582             $element['info']['#weight'] = 998;
583           }
584         }
585
586         if (count($actions)) {
587           $show_actions = FALSE;
588           foreach($actions as $action_item) {
589             if (!isset($action_item['#access']) || $action_item['#access']) {
590               $show_actions = TRUE;
591               break;
592             }
593           }
594
595           if ($show_actions) {
596             $element['actions'] = $actions;
597             $element['actions']['#type'] = 'actions';
598             $element['actions']['#weight'] = 999;
599           }
600         }
601       }
602
603       $display = EntityFormDisplay::collectRenderDisplay($paragraphs_entity, $this->getSetting('form_display_mode'));
604
605       // @todo Remove as part of https://www.drupal.org/node/2640056
606       if (\Drupal::moduleHandler()->moduleExists('field_group')) {
607         $context = [
608           'entity_type' => $paragraphs_entity->getEntityTypeId(),
609           'bundle' => $paragraphs_entity->bundle(),
610           'entity' => $paragraphs_entity,
611           'context' => 'form',
612           'display_context' => 'form',
613           'mode' => $display->getMode(),
614         ];
615
616         field_group_attach_groups($element['subform'], $context);
617         $element['subform']['#pre_render'][] = 'field_group_form_pre_render';
618       }
619
620       if ($item_mode == 'edit') {
621         $display->buildForm($paragraphs_entity, $element['subform'], $form_state);
622         foreach (Element::children($element['subform']) as $field) {
623           if ($paragraphs_entity->hasField($field)) {
624             $translatable = $paragraphs_entity->{$field}->getFieldDefinition()->isTranslatable();
625             if ($translatable) {
626               $element['subform'][$field]['widget']['#after_build'][] = [
627                 static::class,
628                 'removeTranslatabilityClue'
629               ];
630             }
631           }
632         }
633
634         // Build the behavior plugins fields.
635         $paragraphs_type = $paragraphs_entity->getParagraphType();
636         if ($paragraphs_type) {
637           foreach ($paragraphs_type->getEnabledBehaviorPlugins() as $plugin_id => $plugin) {
638             $element['behavior_plugins'][$plugin_id] = [];
639             $subform_state = SubformState::createForSubform($element['behavior_plugins'][$plugin_id], $form, $form_state);
640             if ($plugin_form = $plugin->buildBehaviorForm($paragraphs_entity, $element['behavior_plugins'][$plugin_id], $subform_state)) {
641               $element['behavior_plugins'][$plugin_id] = $plugin_form;
642             }
643           }
644         }
645       }
646       elseif ($item_mode == 'preview') {
647         $element['subform'] = array();
648         $element['behavior_plugins'] = [];
649         $element['preview'] = entity_view($paragraphs_entity, 'preview', $paragraphs_entity->language()->getId());
650         $element['preview']['#access'] = $paragraphs_entity->access('view');
651       }
652       elseif ($item_mode == 'closed') {
653         $element['subform'] = array();
654         $element['behavior_plugins'] = [];
655         if ($paragraphs_entity) {
656           $summary = $this->addCollapsedSummary($paragraphs_entity);
657           $element['top']['paragraph_summary']['fields_info'] = [
658             '#markup' => $summary,
659             '#prefix' => '<div class="paragraphs-collapsed-description">',
660             '#suffix' => '</div>',
661           ];
662         }
663       }
664       else {
665         $element['subform'] = array();
666       }
667
668       $element['subform']['#attributes']['class'][] = 'paragraphs-subform';
669       $element['subform']['#access'] = $paragraphs_entity->access('update');
670
671       if ($item_mode == 'remove') {
672         $element['#access'] = FALSE;
673       }
674
675       $widget_state['paragraphs'][$delta]['entity'] = $paragraphs_entity;
676       $widget_state['paragraphs'][$delta]['display'] = $display;
677       $widget_state['paragraphs'][$delta]['mode'] = $item_mode;
678
679       static::setWidgetState($parents, $field_name, $form_state, $widget_state);
680     }
681     else {
682       $element['#access'] = FALSE;
683     }
684
685     return $element;
686   }
687
688   public function getAllowedTypes() {
689
690     $return_bundles = array();
691
692     $target_type = $this->getFieldSetting('target_type');
693     $bundles = \Drupal::service('entity_type.bundle.info')->getBundleInfo($target_type);
694
695     if ($this->getSelectionHandlerSetting('target_bundles') !== NULL) {
696       $bundles = array_intersect_key($bundles, $this->getSelectionHandlerSetting('target_bundles'));
697     }
698
699     // Support for the paragraphs reference type.
700     $drag_drop_settings = $this->getSelectionHandlerSetting('target_bundles_drag_drop');
701     if ($drag_drop_settings) {
702       $max_weight = count($bundles);
703
704       foreach ($drag_drop_settings as $bundle_info) {
705         if (isset($bundle_info['weight']) && $bundle_info['weight'] && $bundle_info['weight'] > $max_weight) {
706           $max_weight = $bundle_info['weight'];
707         }
708       }
709
710       // Default weight for new items.
711       $weight = $max_weight + 1;
712       foreach ($bundles as $machine_name => $bundle) {
713         $return_bundles[$machine_name] = array(
714           'label' => $bundle['label'],
715           'weight' => isset($drag_drop_settings[$machine_name]['weight']) ? $drag_drop_settings[$machine_name]['weight'] : $weight,
716         );
717         $weight++;
718       }
719     }
720     // Support for other reference types.
721     else {
722       $weight = 0;
723       foreach ($bundles as $machine_name => $bundle) {
724         if (!count($this->getSelectionHandlerSetting('target_bundles'))
725           || in_array($machine_name, $this->getSelectionHandlerSetting('target_bundles'))) {
726
727           $return_bundles[$machine_name] = array(
728             'label' => $bundle['label'],
729             'weight' => $weight,
730           );
731
732           $weight++;
733         }
734       }
735     }
736
737     uasort($return_bundles, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
738
739     return $return_bundles;
740   }
741
742   /**
743    * {@inheritdoc}
744    */
745   public function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
746     $field_name = $this->fieldDefinition->getName();
747     $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
748     $this->fieldParents = $form['#parents'];
749     $field_state = static::getWidgetState($this->fieldParents, $field_name, $form_state);
750
751     $max = $field_state['items_count'];
752     $entity_type_manager = \Drupal::entityTypeManager();
753
754     // Consider adding a default paragraph for new host entities.
755     if ($max == 0 && $items->getEntity()->isNew()) {
756       $default_type = $this->getDefaultParagraphTypeMachineName();
757
758       // Checking if default_type is not none and if is allowed.
759       if ($default_type) {
760         // Place the default paragraph.
761         $target_type = $this->getFieldSetting('target_type');
762         $paragraphs_entity = $entity_type_manager->getStorage($target_type)->create([
763           'type' => $default_type,
764         ]);
765         $field_state['selected_bundle'] = $default_type;
766         $display = EntityFormDisplay::collectRenderDisplay($paragraphs_entity, $this->getSetting('form_display_mode'));
767         $field_state['paragraphs'][0] = [
768           'entity' => $paragraphs_entity,
769           'display' => $display,
770           'mode' => 'edit',
771           'original_delta' => 1
772         ];
773         $max = 1;
774         $field_state['items_count'] = $max;
775       }
776     }
777
778     $this->realItemCount = $max;
779     $is_multiple = $this->fieldDefinition->getFieldStorageDefinition()->isMultiple();
780
781     $title = $this->fieldDefinition->getLabel();
782     $description = FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription()));
783
784     $elements = array();
785     $this->fieldIdPrefix = implode('-', array_merge($this->fieldParents, array($field_name)));
786     $this->fieldWrapperId = Html::getUniqueId($this->fieldIdPrefix . '-add-more-wrapper');
787     $elements['#prefix'] = '<div id="' . $this->fieldWrapperId . '">';
788     $elements['#suffix'] = '</div>';
789
790     $field_state['ajax_wrapper_id'] = $this->fieldWrapperId;
791     // Persist the widget state so formElement() can access it.
792     static::setWidgetState($this->fieldParents, $field_name, $form_state, $field_state);
793
794     if ($max > 0) {
795       for ($delta = 0; $delta < $max; $delta++) {
796
797         // Add a new empty item if it doesn't exist yet at this delta.
798         if (!isset($items[$delta])) {
799           $items->appendItem();
800         }
801
802         // For multiple fields, title and description are handled by the wrapping
803         // table.
804         $element = array(
805           '#title' => $is_multiple ? '' : $title,
806           '#description' => $is_multiple ? '' : $description,
807         );
808         $element = $this->formSingleElement($items, $delta, $element, $form, $form_state);
809
810         if ($element) {
811           // Input field for the delta (drag-n-drop reordering).
812           if ($is_multiple) {
813             // We name the element '_weight' to avoid clashing with elements
814             // defined by widget.
815             $element['_weight'] = array(
816               '#type' => 'weight',
817               '#title' => $this->t('Weight for row @number', array('@number' => $delta + 1)),
818               '#title_display' => 'invisible',
819               // Note: this 'delta' is the FAPI #type 'weight' element's property.
820               '#delta' => $max,
821               '#default_value' => $items[$delta]->_weight ?: $delta,
822               '#weight' => 100,
823             );
824           }
825
826           // Access for the top element is set to FALSE only when the paragraph
827           // was removed. A paragraphs that a user can not edit has access on
828           // lower level.
829           if (isset($element['#access']) && !$element['#access']) {
830             $this->realItemCount--;
831           }
832           else {
833             $elements[$delta] = $element;
834           }
835         }
836       }
837     }
838
839     $field_state = static::getWidgetState($this->fieldParents, $field_name, $form_state);
840     $field_state['real_item_count'] = $this->realItemCount;
841     static::setWidgetState($this->fieldParents, $field_name, $form_state, $field_state);
842
843     if ($this->realItemCount > 0) {
844       $elements += array(
845         '#theme' => 'field_multiple_value_form',
846         '#field_name' => $field_name,
847         '#cardinality' => $cardinality,
848         '#cardinality_multiple' => $is_multiple,
849         '#required' => $this->fieldDefinition->isRequired(),
850         '#title' => $title,
851         '#description' => $description,
852         '#max_delta' => $max-1,
853       );
854     }
855     else {
856       $elements += [
857         '#type' => 'container',
858         '#theme_wrappers' => ['container'],
859         '#field_name' => $field_name,
860         '#cardinality' => $cardinality,
861         '#cardinality_multiple' => TRUE,
862         '#max_delta' => $max-1,
863         'title' => [
864           '#type' => 'html_tag',
865           '#tag' => 'strong',
866           '#value' => $title,
867         ],
868         'text' => [
869           '#type' => 'container',
870           'value' => [
871             '#markup' => $this->t('No @title added yet.', ['@title' => $this->getSetting('title')]),
872             '#prefix' => '<em>',
873             '#suffix' => '</em>',
874           ]
875         ],
876       ];
877
878       if ($description) {
879         $elements['description'] = [
880           '#type' => 'container',
881           'value' => ['#markup' => $description],
882           '#attributes' => ['class' => ['description']],
883         ];
884       }
885     }
886
887     $host = $items->getEntity();
888     $this->initIsTranslating($form_state, $host);
889
890     if (($this->realItemCount < $cardinality || $cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) && !$form_state->isProgrammed() && !$this->isTranslating) {
891       $elements['add_more'] = $this->buildAddActions();
892     }
893
894     $elements['#attached']['library'][] = 'paragraphs/drupal.paragraphs.widget';
895
896     return $elements;
897   }
898
899   /**
900    * {@inheritdoc}
901    */
902   public function form(FieldItemListInterface $items, array &$form, FormStateInterface $form_state, $get_delta = NULL) {
903     $parents = $form['#parents'];
904
905     // Identify the manage field settings default value form.
906     if (in_array('default_value_input', $parents, TRUE)) {
907       // Since the entity is not reusable neither cloneable, having a default
908       // value is not supported.
909       return ['#markup' => $this->t('No widget available for: %label.', ['%label' => $items->getFieldDefinition()->getLabel()])];
910     }
911
912     return parent::form($items, $form, $form_state, $get_delta);
913   }
914
915   /**
916    * Add 'add more' button, if not working with a programmed form.
917    *
918    * @return array
919    *    The form element array.
920    */
921   protected function buildAddActions() {
922     if (count($this->getAccessibleOptions()) === 0) {
923       if (count($this->getAllowedTypes()) === 0) {
924         $add_more_elements['info'] = [
925           '#type' => 'container',
926           '#markup' => $this->t('You are not allowed to add any of the @title types.', ['@title' => $this->getSetting('title')]),
927           '#attributes' => ['class' => ['messages', 'messages--warning']],
928         ];
929       }
930       else {
931         $add_more_elements['info'] = [
932           '#type' => 'container',
933           '#markup' => $this->t('You did not add any @title types yet.', ['@title' => $this->getSetting('title')]),
934           '#attributes' => ['class' => ['messages', 'messages--warning']],
935         ];
936       }
937
938       return $add_more_elements ;
939     }
940
941     if ($this->getSetting('add_mode') == 'button' || $this->getSetting('add_mode') == 'dropdown') {
942       return $this->buildButtonsAddMode();
943     }
944
945     return $this->buildSelectAddMode();
946   }
947
948   /**
949    * Returns the available paragraphs type.
950    *
951    * @return array
952    *   Available paragraphs types.
953    */
954   protected function getAccessibleOptions() {
955     if ($this->accessOptions !== NULL) {
956       return $this->accessOptions;
957     }
958
959     $entity_type_manager = \Drupal::entityTypeManager();
960     $target_type = $this->getFieldSetting('target_type');
961     $bundles = $this->getAllowedTypes();
962     $access_control_handler = $entity_type_manager->getAccessControlHandler($target_type);
963     $dragdrop_settings = $this->getSelectionHandlerSetting('target_bundles_drag_drop');
964
965     foreach ($bundles as $machine_name => $bundle) {
966       if ($dragdrop_settings || (!count($this->getSelectionHandlerSetting('target_bundles'))
967           || in_array($machine_name, $this->getSelectionHandlerSetting('target_bundles')))) {
968         if ($access_control_handler->createAccess($machine_name)) {
969           $this->accessOptions[$machine_name] = $bundle['label'];
970         }
971       }
972     }
973
974     return $this->accessOptions;
975   }
976
977   /**
978    * Builds dropdown button for adding new paragraph.
979    *
980    * @return array
981    *   The form element array.
982    */
983   protected function buildButtonsAddMode() {
984     // Hide the button when translating.
985     $add_more_elements = [
986       '#type' => 'container',
987       '#theme_wrappers' => ['paragraphs_dropbutton_wrapper'],
988     ];
989     $field_name = $this->fieldDefinition->getName();
990     $title = $this->fieldDefinition->getLabel();
991
992     $drop_button = FALSE;
993     if (count($this->getAccessibleOptions()) > 1 && $this->getSetting('add_mode') == 'dropdown') {
994       $drop_button = TRUE;
995       $add_more_elements['#theme_wrappers'] = ['dropbutton_wrapper'];
996       $add_more_elements['prefix'] = [
997         '#markup' => '<ul class="dropbutton">',
998         '#weight' => -999,
999       ];
1000       $add_more_elements['suffix'] = [
1001         '#markup' => '</ul>',
1002         '#weight' => 999,
1003       ];
1004       $add_more_elements['#suffix'] = $this->t(' to %type', ['%type' => $title]);
1005     }
1006
1007     foreach ($this->getAccessibleOptions() as $machine_name => $label) {
1008       $add_more_elements['add_more_button_' . $machine_name] = [
1009         '#type' => 'submit',
1010         '#name' => strtr($this->fieldIdPrefix, '-', '_') . '_' . $machine_name . '_add_more',
1011         '#value' => $this->t('Add @type', ['@type' => $label]),
1012         '#attributes' => ['class' => ['field-add-more-submit']],
1013         '#limit_validation_errors' => [array_merge($this->fieldParents, [$field_name, 'add_more'])],
1014         '#submit' => [[get_class($this), 'addMoreSubmit']],
1015         '#ajax' => [
1016           'callback' => [get_class($this), 'addMoreAjax'],
1017           'wrapper' => $this->fieldWrapperId,
1018           'effect' => 'fade',
1019         ],
1020         '#bundle_machine_name' => $machine_name,
1021       ];
1022
1023       if ($drop_button) {
1024         $add_more_elements['add_more_button_' . $machine_name]['#prefix'] = '<li>';
1025         $add_more_elements['add_more_button_' . $machine_name]['#suffix'] = '</li>';
1026       }
1027     }
1028
1029     return $add_more_elements;
1030   }
1031
1032   /**
1033    * Builds list of actions based on paragraphs type.
1034    *
1035    * @return array
1036    *   The form element array.
1037    */
1038   protected function buildSelectAddMode() {
1039     $field_name = $this->fieldDefinition->getName();
1040     $title = $this->fieldDefinition->getLabel();
1041     $add_more_elements['add_more_select'] = [
1042       '#type' => 'select',
1043       '#options' => $this->getAccessibleOptions(),
1044       '#title' => $this->t('@title type', ['@title' => $this->getSetting('title')]),
1045       '#label_display' => 'hidden',
1046     ];
1047
1048     $text = $this->t('Add @title', ['@title' => $this->getSetting('title')]);
1049
1050     if ($this->realItemCount > 0) {
1051       $text = $this->t('Add another @title', ['@title' => $this->getSetting('title')]);
1052     }
1053
1054     $add_more_elements['add_more_button'] = [
1055       '#type' => 'submit',
1056       '#name' => strtr($this->fieldIdPrefix, '-', '_') . '_add_more',
1057       '#value' => $text,
1058       '#attributes' => ['class' => ['field-add-more-submit']],
1059       '#limit_validation_errors' => [array_merge($this->fieldParents, [$field_name, 'add_more'])],
1060       '#submit' => [[get_class($this), 'addMoreSubmit']],
1061       '#ajax' => [
1062         'callback' => [get_class($this), 'addMoreAjax'],
1063         'wrapper' => $this->fieldWrapperId,
1064         'effect' => 'fade',
1065       ],
1066     ];
1067
1068     $add_more_elements['add_more_button']['#suffix'] = $this->t(' to %type', ['%type' => $title]);
1069     return $add_more_elements;
1070   }
1071
1072   /**
1073    * {@inheritdoc}
1074    */
1075   public static function addMoreAjax(array $form, FormStateInterface $form_state) {
1076     $button = $form_state->getTriggeringElement();
1077     // Go one level up in the form, to the widgets container.
1078     $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2));
1079
1080     // Add a DIV around the delta receiving the Ajax effect.
1081     $delta = $element['#max_delta'];
1082     $element[$delta]['#prefix'] = '<div class="ajax-new-content">' . (isset($element[$delta]['#prefix']) ? $element[$delta]['#prefix'] : '');
1083     $element[$delta]['#suffix'] = (isset($element[$delta]['#suffix']) ? $element[$delta]['#suffix'] : '') . '</div>';
1084
1085     return $element;
1086   }
1087
1088   /**
1089    * {@inheritdoc}
1090    */
1091   public static function addMoreSubmit(array $form, FormStateInterface $form_state) {
1092     $button = $form_state->getTriggeringElement();
1093
1094     // Go one level up in the form, to the widgets container.
1095     $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2));
1096     $field_name = $element['#field_name'];
1097     $parents = $element['#field_parents'];
1098
1099     // Increment the items count.
1100     $widget_state = static::getWidgetState($parents, $field_name, $form_state);
1101
1102     if ($widget_state['real_item_count'] < $element['#cardinality'] || $element['#cardinality'] == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
1103       $widget_state['items_count']++;
1104     }
1105
1106     if (isset($button['#bundle_machine_name'])) {
1107       $widget_state['selected_bundle'] = $button['#bundle_machine_name'];
1108     }
1109     else {
1110       $widget_state['selected_bundle'] = $element['add_more']['add_more_select']['#value'];
1111     }
1112
1113     static::setWidgetState($parents, $field_name, $form_state, $widget_state);
1114
1115     $form_state->setRebuild();
1116   }
1117
1118   /**
1119    * Creates a duplicate of the paragraph entity.
1120    */
1121   public static function duplicateSubmit(array $form, FormStateInterface $form_state) {
1122     $button = $form_state->getTriggeringElement();
1123     // Go one level up in the form, to the widgets container.
1124     $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -4));
1125     $field_name = $element['#field_name'];
1126     $parents = $element['#field_parents'];
1127
1128     // Inserting new element in the array.
1129     $widget_state = static::getWidgetState($parents, $field_name, $form_state);
1130     $delta = $button['#delta'];
1131     $widget_state['items_count']++;
1132     $widget_state['real_item_count']++;
1133     $widget_state['original_deltas'] = array_merge($widget_state['original_deltas'], ['1' => 1]) ;
1134
1135     // Check if the replicate module is enabled
1136     if (\Drupal::hasService('replicate.replicator')) {
1137       $duplicate_entity = \Drupal::getContainer()->get('replicate.replicator')->replicateEntity($widget_state['paragraphs'][$delta]['entity']);
1138       }
1139     else {
1140       $duplicate_entity = $widget_state['paragraphs'][$delta]['entity']->createDuplicate();
1141      }
1142     // Create the duplicated paragraph and insert it below the original.
1143     $paragraph[] = [
1144       'entity' => $duplicate_entity,
1145       'display' => $widget_state['paragraphs'][$delta]['display'],
1146       'mode' => 'edit'
1147     ];
1148
1149     array_splice($widget_state['paragraphs'], $delta + 1, 0, $paragraph);
1150
1151     static::setWidgetState($parents, $field_name, $form_state, $widget_state);
1152     $form_state->setRebuild();
1153   }
1154
1155   public static function paragraphsItemSubmit(array $form, FormStateInterface $form_state) {
1156     $button = $form_state->getTriggeringElement();
1157
1158     // Go one level up in the form, to the widgets container.
1159     $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -4));
1160
1161     $delta = array_slice($button['#array_parents'], -4, -3);
1162     $delta = $delta[0];
1163
1164     $field_name = $element['#field_name'];
1165     $parents = $element['#field_parents'];
1166
1167     $widget_state = static::getWidgetState($parents, $field_name, $form_state);
1168
1169     $widget_state['paragraphs'][$delta]['mode'] = $button['#paragraphs_mode'];
1170
1171     if (!empty($button['#paragraphs_show_warning'])) {
1172       $widget_state['paragraphs'][$delta]['show_warning'] = $button['#paragraphs_show_warning'];
1173     }
1174
1175     static::setWidgetState($parents, $field_name, $form_state, $widget_state);
1176
1177     $form_state->setRebuild();
1178   }
1179
1180   public static function itemAjax(array $form, FormStateInterface $form_state) {
1181     $button = $form_state->getTriggeringElement();
1182     // Go one level up in the form, to the widgets container.
1183     $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -4));
1184
1185     $element['#prefix'] = '<div class="ajax-new-content">' . (isset($element['#prefix']) ? $element['#prefix'] : '');
1186     $element['#suffix'] = (isset($element['#suffix']) ? $element['#suffix'] : '') . '</div>';
1187
1188     return $element;
1189   }
1190
1191   /**
1192    * Returns the value of a setting for the entity reference selection handler.
1193    *
1194    * @param string $setting_name
1195    *   The setting name.
1196    *
1197    * @return mixed
1198    *   The setting value.
1199    */
1200   protected function getSelectionHandlerSetting($setting_name) {
1201     $settings = $this->getFieldSetting('handler_settings');
1202     return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
1203   }
1204
1205   /**
1206    * {@inheritdoc}
1207    */
1208   public function elementValidate($element, FormStateInterface $form_state, $form) {
1209     $field_name = $this->fieldDefinition->getName();
1210     $widget_state = static::getWidgetState($element['#field_parents'], $field_name, $form_state);
1211     $delta = $element['#delta'];
1212
1213     if (isset($widget_state['paragraphs'][$delta]['entity'])) {
1214       $entity = $widget_state['paragraphs'][$delta]['entity'];
1215
1216       /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $display */
1217       $display = $widget_state['paragraphs'][$delta]['display'];
1218
1219       if ($widget_state['paragraphs'][$delta]['mode'] == 'edit') {
1220         // Extract the form values on submit for getting the current paragraph.
1221         $display->extractFormValues($entity, $element['subform'], $form_state);
1222         $display->validateFormValues($entity, $element['subform'], $form_state);
1223
1224         // Validate all enabled behavior plugins.
1225         $paragraphs_type = $entity->getParagraphType();
1226         foreach ($paragraphs_type->getEnabledBehaviorPlugins() as $plugin_id => $plugin_values) {
1227           $subform_state = SubformState::createForSubform($element['behavior_plugins'][$plugin_id], $form_state->getCompleteForm(), $form_state);
1228           $plugin_values->validateBehaviorForm($entity, $element['behavior_plugins'][$plugin_id], $subform_state);
1229         }
1230       }
1231     }
1232
1233     static::setWidgetState($element['#field_parents'], $field_name, $form_state, $widget_state);
1234   }
1235
1236   /**
1237    * {@inheritdoc}
1238    */
1239   public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
1240     $entity = $form_state->getFormObject()->getEntity();
1241     $field_name = $this->fieldDefinition->getName();
1242     $widget_state = static::getWidgetState($form['#parents'], $field_name, $form_state);
1243     $element = NestedArray::getValue($form_state->getCompleteForm(), $widget_state['array_parents']);
1244
1245     $new_revision = FALSE;
1246     if ($entity instanceof RevisionableInterface) {
1247       if ($entity->isNewRevision()) {
1248         $new_revision = TRUE;
1249       }
1250       // Most of the time we don't know yet if the host entity is going to be
1251       // saved as a new revision using RevisionableInterface::isNewRevision().
1252       // Most entity types (at least nodes) however use a boolean property named
1253       // "revision" to indicate whether a new revision should be saved. Use that
1254       // property.
1255       elseif ($entity->getEntityType()->hasKey('revision') && $form_state->getValue('revision')) {
1256         $new_revision = TRUE;
1257       }
1258     }
1259
1260     foreach ($values as $delta => &$item) {
1261       if (isset($widget_state['paragraphs'][$item['_original_delta']]['entity'])
1262         && $widget_state['paragraphs'][$item['_original_delta']]['mode'] != 'remove') {
1263         $paragraphs_entity = $widget_state['paragraphs'][$item['_original_delta']]['entity'];
1264
1265         /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $display */
1266         $display =  $widget_state['paragraphs'][$item['_original_delta']]['display'];
1267         if ($widget_state['paragraphs'][$item['_original_delta']]['mode'] == 'edit') {
1268           $display->extractFormValues($paragraphs_entity, $element[$item['_original_delta']]['subform'], $form_state);
1269         }
1270         $paragraphs_entity->setNewRevision($new_revision);
1271         // A content entity form saves without any rebuild. It needs to set the
1272         // language to update it in case of language change.
1273         $langcode_key = $paragraphs_entity->getEntityType()->getKey('langcode');
1274         if ($paragraphs_entity->get($langcode_key)->value != $form_state->get('langcode')) {
1275           // If a translation in the given language already exists, switch to
1276           // that. If there is none yet, update the language.
1277           if ($paragraphs_entity->hasTranslation($form_state->get('langcode'))) {
1278             $paragraphs_entity = $paragraphs_entity->getTranslation($form_state->get('langcode'));
1279           }
1280           else {
1281             $paragraphs_entity->set($langcode_key, $form_state->get('langcode'));
1282           }
1283         }
1284         if (isset($item['behavior_plugins'])) {
1285           // Submit all enabled behavior plugins.
1286           $paragraphs_type = $paragraphs_entity->getParagraphType();
1287           foreach ($paragraphs_type->getEnabledBehaviorPlugins() as $plugin_id => $plugin_values) {
1288             if (!isset($item['behavior_plugins'][$plugin_id])) {
1289               $item['behavior_plugins'][$plugin_id] = [];
1290             }
1291             if (isset($element[$delta]) && isset($element[$delta]['behavior_plugins'][$plugin_id]) && $form_state->getCompleteForm()) {
1292               $subform_state = SubformState::createForSubform($element[$delta]['behavior_plugins'][$plugin_id], $form_state->getCompleteForm(), $form_state);
1293               $plugin_values->submitBehaviorForm($paragraphs_entity, $item['behavior_plugins'][$plugin_id], $subform_state);
1294             }
1295           }
1296         }
1297
1298         $paragraphs_entity->setNeedsSave(TRUE);
1299         $item['entity'] = $paragraphs_entity;
1300         $item['target_id'] = $paragraphs_entity->id();
1301         $item['target_revision_id'] = $paragraphs_entity->getRevisionId();
1302       }
1303       // If our mode is remove don't save or reference this entity.
1304       // @todo: Maybe we should actually delete it here?
1305       elseif($widget_state['paragraphs'][$item['_original_delta']]['mode'] == 'remove') {
1306         $item['target_id'] = NULL;
1307         $item['target_revision_id'] = NULL;
1308       }
1309     }
1310     return $values;
1311   }
1312
1313   /**
1314    * {@inheritdoc}
1315    */
1316   public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
1317     // Filter possible empty items.
1318     $items->filterEmptyItems();
1319     return parent::extractFormValues($items, $form, $form_state);
1320   }
1321
1322   /**
1323    * Initializes the translation form state.
1324    *
1325    * @param \Drupal\Core\Form\FormStateInterface $form_state
1326    * @param \Drupal\Core\Entity\EntityInterface $host
1327    */
1328   protected function initIsTranslating(FormStateInterface $form_state, EntityInterface $host) {
1329     if ($this->isTranslating != NULL) {
1330       return;
1331     }
1332     $this->isTranslating = FALSE;
1333     if (!$host->isTranslatable()) {
1334       return;
1335     }
1336     if (!$host->getEntityType()->hasKey('default_langcode')) {
1337       return;
1338     }
1339     $default_langcode_key = $host->getEntityType()->getKey('default_langcode');
1340     if (!$host->hasField($default_langcode_key)) {
1341       return;
1342     }
1343
1344     if (!empty($form_state->get('content_translation'))) {
1345       // Adding a language through the ContentTranslationController.
1346       $this->isTranslating = TRUE;
1347     }
1348     if ($host->hasTranslation($form_state->get('langcode')) && $host->getTranslation($form_state->get('langcode'))->get($default_langcode_key)->value == 0) {
1349       // Editing a translation.
1350       $this->isTranslating = TRUE;
1351     }
1352   }
1353
1354   /**
1355    * After-build callback for removing the translatability clue from the widget.
1356    *
1357    * If the fields on the paragraph type are translatable,
1358    * ContentTranslationHandler::addTranslatabilityClue()adds an
1359    * "(all languages)" suffix to the widget title. That suffix is incorrect and
1360    * is being removed by this method using a #after_build on the field widget.
1361    *
1362    * @param array $element
1363    * @param \Drupal\Core\Form\FormStateInterface $form_state
1364    *
1365    * @return array
1366    */
1367   public static function removeTranslatabilityClue(array $element, FormStateInterface $form_state) {
1368     // Widgets could have multiple elements with their own titles, so remove the
1369     // suffix if it exists, do not recurse lower than this to avoid going into
1370     // nested paragraphs or similar nested field types.
1371     $suffix = ' <span class="translation-entity-all-languages">(' . t('all languages') . ')</span>';
1372     if (isset($element['#title']) && strpos($element['#title'], $suffix)) {
1373       $element['#title'] = str_replace($suffix, '', $element['#title']);
1374     }
1375     // Loop over all widget deltas.
1376     foreach (Element::children($element) as $delta) {
1377       if (isset($element[$delta]['#title']) && strpos($element[$delta]['#title'], $suffix)) {
1378         $element[$delta]['#title'] = str_replace($suffix, '', $element[$delta]['#title']);
1379       }
1380       // Loop over all form elements within the current delta.
1381       foreach (Element::children($element[$delta]) as $field) {
1382         if (isset($element[$delta][$field]['#title']) && strpos($element[$delta][$field]['#title'], $suffix)) {
1383           $element[$delta][$field]['#title'] = str_replace($suffix, '', $element[$delta][$field]['#title']);
1384         }
1385       }
1386     }
1387     return $element;
1388   }
1389
1390   /**
1391    * Returns the default paragraph type.
1392    *
1393    * @return string $default_paragraph_type
1394    *   Label name for default paragraph type.
1395    */
1396   protected function getDefaultParagraphTypeLabelName(){
1397     if ($this->getDefaultParagraphTypeMachineName() !== NULL) {
1398       $allowed_types = $this->getAllowedTypes();
1399       return $allowed_types[$this->getDefaultParagraphTypeMachineName()]['label'];
1400     }
1401
1402     return NULL;
1403   }
1404
1405   /**
1406    * Returns the machine name for default paragraph type.
1407    *
1408    * @return string
1409    *   Machine name for default paragraph type.
1410    */
1411   protected function getDefaultParagraphTypeMachineName() {
1412     $default_type = $this->getSetting('default_paragraph_type');
1413     $allowed_types = $this->getAllowedTypes();
1414     if ($default_type && isset($allowed_types[$default_type])) {
1415       return $default_type;
1416     }
1417     // Check if the user explicitly selected not to have any default Paragraph
1418     // type. Othewise, if there is only one type available, that one is the
1419     // default.
1420     if ($default_type === '_none') {
1421       return NULL;
1422     }
1423     if (count($allowed_types) === 1) {
1424       return key($allowed_types);
1425     }
1426
1427     return NULL;
1428   }
1429
1430   /**
1431    * @param \Drupal\paragraphs\Entity\Paragraph $paragraphs_entity
1432    *   Entity where to extract the values.
1433    *
1434    * @return string $collapsed_summary_text
1435    *   The text without tags to return.
1436    */
1437   public function addCollapsedSummary(paragraphs\Entity\Paragraph $paragraphs_entity) {
1438     $text_types = ['text_with_summary', 'text', 'text_long', 'list_string'];
1439     $summary = [];
1440     foreach ($paragraphs_entity->getFieldDefinitions() as $key => $value) {
1441       if ($value->getType() == 'image') {
1442         if ($paragraphs_entity->get($key)->entity) {
1443           foreach ($paragraphs_entity->get($key) as $image_key => $image_value) {
1444             if ($image_value->title != '') {
1445               $text = $image_value->title;
1446             }
1447             elseif ($image_value->alt != '') {
1448               $text = $image_value->alt;
1449             }
1450             elseif ($text = $image_value->entity->filename->value) {
1451               $text = $image_value->entity->filename->value;
1452             }
1453             if (strlen($text) > 50) {
1454               $text = strip_tags(substr($text, 0, 150));
1455             }
1456             $summary[] = $text;
1457           }
1458         }
1459       }
1460       if (in_array($value->getType(), $text_types)) {
1461         $text = $paragraphs_entity->get($key)->value;
1462         if (strlen($text) > 50) {
1463           $text = strip_tags(substr($text, 0, 150));
1464         }
1465         $summary[] = $text;
1466       }
1467       if ($field_type = $value->getType() == 'entity_reference_revisions') {
1468         if ($paragraphs_entity->get($key) && $paragraphs_entity->get($key)->entity) {
1469           $summary[] = $this->addCollapsedSummary($paragraphs_entity->get($key)->entity);
1470         }
1471       }
1472       if ($field_type = $value->getType() == 'entity_reference') {
1473         if (!in_array($key, ['type', 'uid', 'revision_uid'])) {
1474           if ($paragraphs_entity->get($key)->entity) {
1475             $summary[] = $paragraphs_entity->get($key)->entity->label();
1476           }
1477         }
1478       }
1479     }
1480     $paragraphs_type = $paragraphs_entity->getParagraphType();
1481     foreach ($paragraphs_type->getEnabledBehaviorPlugins() as $plugin_id => $plugin) {
1482       if ($plugin_summary = $plugin->settingsSummary($paragraphs_entity)) {
1483         $summary = array_merge($summary, $plugin_summary);
1484       }
1485     }
1486     $collapsed_summary_text = implode(', ', $summary);
1487     return strip_tags($collapsed_summary_text);
1488   }
1489
1490   /**
1491    * {@inheritdoc}
1492    */
1493   public static function isApplicable(FieldDefinitionInterface $field_definition) {
1494     $target_type = $field_definition->getSetting('target_type');
1495     $paragraph_type = \Drupal::entityTypeManager()->getDefinition($target_type);
1496     if ($paragraph_type) {
1497       return $paragraph_type->isSubclassOf(ParagraphInterface::class);
1498     }
1499
1500     return FALSE;
1501   }
1502
1503 }