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