Paragraphs Editor
ParagraphsEditorPreparer.php
Go to the documentation of this file.
1 <?php
2 
4 
16 
17 /**
18  * A DOM processor plugin for preparing markup for use in an editor.
19  *
20  * When an is editor field is saved, all the contextual editor information is
21  * stripped from the DOM.
22  *
23  * Every time we go back to edit the field we generate a new editing context for
24  * the field, and all child fields.
25  *
26  * @DomProcessorDataProcessor(
27  * id = "paragraphs_editor_preparer",
28  * label = "Paragraphs Editor Preparer"
29  * )
30  */
31 class ParagraphsEditorPreparer implements ContainerFactoryPluginInterface {
33 
34  /**
35  * The widget data collection that will be delivered to the client initially.
36  *
37  * @var \Drupal\paragraphs_editor\WidgetBinder\WidgetBinderData
38  */
39  protected $widgetData = NULL;
40 
41  /**
42  * A count of the number of entities that were prerendered for delivery.
43  *
44  * @var int
45  */
46  protected $count = 0;
47 
48  /**
49  * The context factory for creating new edit contexts.
50  *
51  * @var \Drupal\paragraphs_editor\EditorCommand\CommandContextFactoryInterface
52  */
53  protected $contextFactory;
54 
55  /**
56  * The widget binder data compiler for generating prerendered entity models.
57  *
58  * @var \Drupal\paragraphs_editor\WidgetBinder\WidgetBinderDataCompilerInterface
59  */
60  protected $dataCompiler;
61 
62  /**
63  * Creates a paragraph editor preparation plugin.
64  *
65  * @param \Drupal\paragraphs_editor\EditorFieldValue\FieldValueManagerInterface $field_value_manager
66  * The field value manager service to initialize the element trait.
67  * @param \Drupal\paragraphs_editor\EditorCommand\CommandContextFactoryInterface $context_factory
68  * The context factory for generating new edit contexts.
69  * @param \Drupal\paragraphs_editor\WidgetBinder\WidgetBinderDataCompilerInterface $data_compiler
70  * The widget binder data compiler service for generating dthe initial data
71  * binder models that will be delivered to the client.
72  */
73  public function __construct(FieldValueManagerInterface $field_value_manager, CommandContextFactoryInterface $context_factory, WidgetBinderDataCompilerInterface $data_compiler) {
74  $this->initializeParagraphsEditorElementTrait($field_value_manager);
75  $this->contextFactory = $context_factory;
76  $this->dataCompiler = $data_compiler;
77  }
78 
79  /**
80  * {@inheritdoc}
81  */
82  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
83  return new static(
84  $container->get('paragraphs_editor.field_value.manager'),
85  $container->get('paragraphs_editor.command.context_factory'),
86  $container->get('paragraphs_editor.widget_binder.data_compiler')
87  );
88  }
89 
90  /**
91  * {@inheritdoc}
92  */
93  public function process(SemanticDataInterface $data, DomProcessorResultInterface $result) {
94  if ($data->has('preparer.ready')) {
95  if ($data->is($this->getSelector('widget'))) {
96  $paragraph = $data->get('paragraph.entity');
97  $field_context_id = $data->get('field.context_id');
98  $this->expandParagraph($data, $data->node(), $paragraph, $field_context_id);
99  return $result;
100  }
101  elseif ($data->isRoot()) {
102  return $this->finishResult($data, $result);
103  }
104  }
105  elseif ($data->isRoot()) {
106  return $this->generateOwnerInfo($data, $result);
107  }
108 
109  return $result;
110  }
111 
112  /**
113  * Initializes the widget binder data object that will be initially delivered.
114  *
115  * @param \Drupal\dom_processor\DomProcessor\SemanticDataInterface $data
116  * The current semantic data state.
117  * @param \Drupal\dom_processor\DomProcessor\DomProcessorResultInterface $result
118  * The current result object.
119  *
120  * @return \Drupal\dom_processor\DomProcessor\DomProcessorResultInterface
121  * An updated result object instructing the processor to reprocess the tree.
122  */
123  protected function generateOwnerInfo(SemanticDataInterface $data, DomProcessorResultInterface $result) {
124  $data = $data->tag('preparer', [
125  'ready' => TRUE,
126  ], TRUE);
127 
128  $field_value_wrapper = $data->get('field.wrapper');
129  $data = $data->tag('filter_format', $field_value_wrapper->getFormat());
130 
131  // Create a new editing context for the field.
132  $field_definition = $data->get('field.items')->getFieldDefinition();
133  $owner_entity = $data->get('owner.entity');
134  $settings = $data->get('settings');
135  $context = $this->contextFactory->create($field_definition->id(), $owner_entity->id(), $settings);
136 
137  $widget_data = new WidgetBinderData();
138  $widget_data->addModel('context', $context->getContextString(), [
139  'id' => $context->getContextString(),
140  'schemaId' => $field_definition->id(),
141  'settings' => $settings,
142  'bufferItems' => [],
143  ]);
144  $widget_data->addModel('schema', $field_definition->id(), [
145  'id' => $field_definition->id(),
146  'allowed' => [
147  'paragraphs_editor_text' => TRUE,
148  'tabs' => TRUE,
149  ],
150  ]);
151  $this->widgetData = $widget_data;
152  $data = $data->tag('field', [
153  'context_id' => $context->getContextString(),
154  ], TRUE);
155  $data = $data->tag('root_context', $context->getContextString());
156 
157  return $result->reprocess($data);
158  }
159 
160  /**
161  * Expands a widget embed code to include its children.
162  *
163  * @param \Drupal\dom_processor\DomProcessor\SemanticDataInterface $data
164  * The current semantic data state.
165  * @param \DOMElement $paragraph_node
166  * The dom element to be expanded.
167  * @param \Drupal\paragraphs\ParagraphInterface $entity
168  * The paragraph entity associated with the dom element.
169  * @param string|null $field_context_id
170  * The id of the editing context to which the entity belongs.
171  *
172  * @note This function should not get contextual data from the $data variable.
173  * All contextual information about the widget is passed as arguments to the
174  * function. This is because the paragraph being expanded may not be the
175  * paragraph the semantic data corresponds to.
176  */
177  protected function expandParagraph(SemanticDataInterface $data, \DOMElement $paragraph_node, ParagraphInterface $entity, $field_context_id = NULL) {
178 
179  if (!empty($field_context_id)) {
180  $this->setAttribute($paragraph_node, 'widget', '<context>', $field_context_id);
181 
182  $prerender_count = $data->get('settings.prerender_count');
183  if ($prerender_count > -1 && $this->count < $prerender_count) {
184  $this->compileParagraph($data, $entity, $field_context_id);
185  }
186  }
187 
188  foreach ($entity->getFields() as $items) {
189  $field_definition = $items->getFieldDefinition();
190 
191  if ($this->fieldValueManager->isParagraphsField($field_definition)) {
193  $field_definition = TypeUtility::ensureFieldConfig($field_definition);
194 
195  $field_node = $this->createElement($paragraph_node->ownerDocument, 'field', [
196  '<name>' => $field_definition->getName(),
197  ]);
198 
199  if ($this->fieldValueManager->isParagraphsEditorField($field_definition)) {
200 
201  $context_id = $this->widgetData->getContextId($entity->uuid(), $field_definition->id());
202  if (!$context_id) {
203  return;
204  }
205 
206  $this->setAttribute($field_node, 'field', '<editable>', 'true');
207  $this->setAttribute($field_node, 'field', '<context>', $context_id);
208 
209  $referenced_entities = $this->fieldValueManager->wrapItems($items)->getReferencedEntities();
210  }
211  else {
212  $referenced_entities = $this->fieldValueManager->getReferencedEntities($items);
213  }
214 
215  foreach ($referenced_entities as $child_entity) {
216  $child_paragraph_node = $this->createElement($field_node->ownerDocument, 'widget', [
217  '<uuid>' => $child_entity->uuid(),
218  ]);
219  $this->expandParagraph($data, $child_paragraph_node, $child_entity);
220  $field_node->appendChild($child_paragraph_node);
221  }
222  $paragraph_node->appendChild($field_node);
223  }
224  }
225  }
226 
227  /**
228  * Compiles a paragraph into a collection of widget binder data for delivery.
229  *
230  * @param \Drupal\dom_processor\DomProcessor\SemanticDataInterface $data
231  * The current semantic data state.
232  * @param \Drupal\paragraphs\ParagraphInterface $entity
233  * The paragraph entity to be compiled into widget binder data.
234  * @param string $field_context_id
235  * The id of the editing context to which the entity belongs.
236  */
237  protected function compileParagraph(SemanticDataInterface $data, ParagraphInterface $entity, $field_context_id) {
238  $context = $this->contextFactory->get($field_context_id);
239  $context->addAdditionalContext('editorContext', $data->get('root_context'));
240  $item = $context->getEditBuffer()->createItem($entity);
241  $view_mode = $data->get('settings.view_mode');
242  $langcode = $data->get('langcode');
243  $this->widgetData = $this->widgetData->merge($this->dataCompiler->compile($context, $item, $view_mode, $langcode));
244  $this->count++;
245  }
246 
247  /**
248  * Decorates the final result with data necessary to display the editor.
249  *
250  * @param \Drupal\dom_processor\DomProcessor\SemanticDataInterface $data
251  * The current semantic data state.
252  * @param \Drupal\dom_processor\DomProcessor\DomProcessorResultInterface $result
253  * The current result object.
254  *
255  * @return \Drupal\dom_processor\DomProcessor\DomProcessorResultInterface
256  * An updated result object decorated with the data needed to display the
257  * editor field widget.
258  */
259  protected function finishResult(SemanticDataInterface $data, DomProcessorResultInterface $result) {
260  $field_value_wrapper = $data->get('field.wrapper');
261 
262  // Add the core paragraphs editor library.
263  $result = $result->merge([
264  'libraries' => [
265  'paragraphs_editor/core',
266  ],
267  ]);
268 
269  // Add attributes for the editor.
270  $result = $result->merge([
271  'attributes' => [
272  'class' => [
273  'class' => 'paragraphs-editor',
274  ],
275  $this->fieldValueManager->getAttributeName('field', '<context>') => $data->get('field.context_id'),
276  ],
277  ]);
278 
279  $result = $result->merge([
280  'context_id' => $data->get('field.context_id'),
281  ]);
282 
283  $result = $result->merge([
284  'drupalSettings' => [
285  'paragraphs_editor' => $this->widgetData->toArray(),
286  ],
287  ]);
288 
289  $result = $result->merge([
290  'filter_format' => $field_value_wrapper->getFormat(),
291  ]);
292 
293  return $result;
294  }
295 
296 }
static ensureEntityReferenceRevisions(FieldItemListInterface $items)
Definition: TypeUtility.php:55
static create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition)
process(SemanticDataInterface $data, DomProcessorResultInterface $result)
setAttribute(\DOMNode $node, $element_name, $attribute_name, $value)
compileParagraph(SemanticDataInterface $data, ParagraphInterface $entity, $field_context_id)
expandParagraph(SemanticDataInterface $data,\DOMElement $paragraph_node, ParagraphInterface $entity, $field_context_id=NULL)
generateOwnerInfo(SemanticDataInterface $data, DomProcessorResultInterface $result)
static ensureFieldConfig(FieldDefinitionInterface $field_definition=NULL)
Definition: TypeUtility.php:39
__construct(FieldValueManagerInterface $field_value_manager, CommandContextFactoryInterface $context_factory, WidgetBinderDataCompilerInterface $data_compiler)
finishResult(SemanticDataInterface $data, DomProcessorResultInterface $result)
initializeParagraphsEditorElementTrait(FieldValueManagerInterface $field_value_manager)
createElement(\DOMDocument $document, $element_name, array $attributes=[])