@amazeelabs/silverback-gatsby 3.7.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/drupal/silverback_gatsby/.prettierignore +1 -0
  3. package/drupal/silverback_gatsby/CHANGELOG.md +1216 -0
  4. package/drupal/silverback_gatsby/README.md +297 -0
  5. package/drupal/silverback_gatsby/composer.json +15 -0
  6. package/drupal/silverback_gatsby/directives.gql +63 -0
  7. package/drupal/silverback_gatsby/directives.graphql +62 -0
  8. package/drupal/silverback_gatsby/drush.services.yml +9 -0
  9. package/drupal/silverback_gatsby/graphql/entity.directive.graphqls +2 -0
  10. package/drupal/silverback_gatsby/graphql/menu.directive.graphqls +17 -0
  11. package/drupal/silverback_gatsby/graphql/silverback_gatsby.base.graphqls +25 -0
  12. package/drupal/silverback_gatsby/graphql/silverback_gatsby.extension.graphqls +10 -0
  13. package/drupal/silverback_gatsby/graphql/stringTranslation.directive.graphqls +4 -0
  14. package/drupal/silverback_gatsby/graphql/translatableString.directive.graphqls +2 -0
  15. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/directives.gql +6 -0
  16. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/graphql/.graphqlrc.json +4 -0
  17. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/graphql/silverback_gatsby_example.graphqls +39 -0
  18. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/silverback_gatsby_example.info.yml +7 -0
  19. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/silverback_gatsby_example.module +16 -0
  20. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/src/Directives.php +24 -0
  21. package/drupal/silverback_gatsby/silverback_gatsby.info.yml +8 -0
  22. package/drupal/silverback_gatsby/silverback_gatsby.install +180 -0
  23. package/drupal/silverback_gatsby/silverback_gatsby.links.task.yml +6 -0
  24. package/drupal/silverback_gatsby/silverback_gatsby.module +119 -0
  25. package/drupal/silverback_gatsby/silverback_gatsby.permissions.yml +8 -0
  26. package/drupal/silverback_gatsby/silverback_gatsby.post_update.php +15 -0
  27. package/drupal/silverback_gatsby/silverback_gatsby.routing.yml +28 -0
  28. package/drupal/silverback_gatsby/silverback_gatsby.services.yml +50 -0
  29. package/drupal/silverback_gatsby/src/Annotation/GatsbyFeed.php +41 -0
  30. package/drupal/silverback_gatsby/src/Commands/SilverbackGatsbyCommands.php +102 -0
  31. package/drupal/silverback_gatsby/src/Controller/BuildController.php +45 -0
  32. package/drupal/silverback_gatsby/src/Controller/PublisherController.php +34 -0
  33. package/drupal/silverback_gatsby/src/Directives.php +51 -0
  34. package/drupal/silverback_gatsby/src/GatsbyBuildTrigger.php +230 -0
  35. package/drupal/silverback_gatsby/src/GatsbyBuildTriggerInterface.php +52 -0
  36. package/drupal/silverback_gatsby/src/GatsbyUpdate.php +38 -0
  37. package/drupal/silverback_gatsby/src/GatsbyUpdateHandler.php +181 -0
  38. package/drupal/silverback_gatsby/src/GatsbyUpdateTracker.php +98 -0
  39. package/drupal/silverback_gatsby/src/GatsbyUpdateTrackerInterface.php +59 -0
  40. package/drupal/silverback_gatsby/src/GatsbyUpdateTrigger.php +69 -0
  41. package/drupal/silverback_gatsby/src/GatsbyUpdateTriggerInterface.php +22 -0
  42. package/drupal/silverback_gatsby/src/GraphQL/Build.php +235 -0
  43. package/drupal/silverback_gatsby/src/GraphQL/ComposableSchema.php +73 -0
  44. package/drupal/silverback_gatsby/src/LocaleStorageDecorator.php +181 -0
  45. package/drupal/silverback_gatsby/src/MenuTreeStorageDecorator.php +139 -0
  46. package/drupal/silverback_gatsby/src/Plugin/FeedBase.php +121 -0
  47. package/drupal/silverback_gatsby/src/Plugin/FeedInterface.php +132 -0
  48. package/drupal/silverback_gatsby/src/Plugin/FeedPluginManager.php +39 -0
  49. package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/EntityFeed.php +252 -0
  50. package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/MenuFeed.php +292 -0
  51. package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/StringTranslationFeed.php +171 -0
  52. package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/TranslatableStringFeed.php +128 -0
  53. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/CurrentUserEntity.php +43 -0
  54. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/EntityQueryBase.php +40 -0
  55. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchEntity.php +371 -0
  56. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchString.php +64 -0
  57. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchTranslatableString.php +74 -0
  58. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FocalPoint.php +47 -0
  59. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyBuildId.php +36 -0
  60. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyExtractId.php +28 -0
  61. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyExtractLangcode.php +28 -0
  62. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ImageProps.php +149 -0
  63. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ListEntities.php +88 -0
  64. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ListStrings.php +87 -0
  65. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/StringId.php +37 -0
  66. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/StringTranslations.php +94 -0
  67. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/EntityFetch.php +51 -0
  68. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/EntityTranslationsWithDefault.php +30 -0
  69. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/FocalPoint.php +27 -0
  70. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/ImageProps.php +27 -0
  71. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/MenuLangcode.php +27 -0
  72. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/MenuTranslations.php +36 -0
  73. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/SilverbackGatsbyEntityId.php +39 -0
  74. package/drupal/silverback_gatsby/src/Plugin/GraphQL/SchemaExtension/SilverbackGatsbySchemaExtension.php +384 -0
  75. package/drupal/silverback_gatsby/src/SilverbackGatsbyServiceProvider.php +43 -0
  76. package/drupal/silverback_gatsby/src/SilverbackGatsbySessionConfiguration.php +30 -0
  77. package/drupal/silverback_gatsby/src/SilverbackReverseProxyMiddleware.php +49 -0
  78. package/drupal/silverback_gatsby/tests/queries/create-page-fields.gql +12 -0
  79. package/drupal/silverback_gatsby/tests/queries/current-user.gql +6 -0
  80. package/drupal/silverback_gatsby/tests/queries/feed_info.gql +12 -0
  81. package/drupal/silverback_gatsby/tests/queries/load-entity.gql +5 -0
  82. package/drupal/silverback_gatsby/tests/queries/menus.gql +31 -0
  83. package/drupal/silverback_gatsby/tests/queries/multilingual-menus.gql +11 -0
  84. package/drupal/silverback_gatsby/tests/queries/revisionable-translatable.gql +15 -0
  85. package/drupal/silverback_gatsby/tests/queries/revisionable.gql +8 -0
  86. package/drupal/silverback_gatsby/tests/queries/translatable.gql +14 -0
  87. package/drupal/silverback_gatsby/tests/queries/untranslatable.gql +10 -0
  88. package/drupal/silverback_gatsby/tests/schema/.graphqlrc.json +4 -0
  89. package/drupal/silverback_gatsby/tests/schema/translatable-strings.graphql +7 -0
  90. package/drupal/silverback_gatsby/tests/src/Kernel/CurrentUserTest.php +37 -0
  91. package/drupal/silverback_gatsby/tests/src/Kernel/EntityFeedTest.php +490 -0
  92. package/drupal/silverback_gatsby/tests/src/Kernel/EntityFeedTestBase.php +178 -0
  93. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyBuildTriggerTest.php +155 -0
  94. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyFeedInfoTest.php +319 -0
  95. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateHandlerTest.php +131 -0
  96. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateTrackerTest.php +196 -0
  97. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateTriggerTest.php +177 -0
  98. package/drupal/silverback_gatsby/tests/src/Kernel/MenuFeedTest.php +192 -0
  99. package/drupal/silverback_gatsby/tests/src/Kernel/TranslatableStringFeedTest.php +210 -0
  100. package/drupal/silverback_gatsby/tests/src/Traits/NotificationCheckTrait.php +43 -0
  101. package/package.json +15 -0
@@ -0,0 +1,371 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer;
4
+
5
+ use Drupal\Core\Entity\EntityRepositoryInterface;
6
+ use Drupal\Core\Entity\EntityTypeManagerInterface;
7
+ use Drupal\Core\Entity\TranslatableInterface;
8
+ use Drupal\Core\Language\LanguageManagerInterface;
9
+ use Drupal\Core\Path\PathValidatorInterface;
10
+ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
11
+ use Drupal\Core\Session\AccountInterface;
12
+ use Drupal\graphql\GraphQL\Buffers\EntityBuffer;
13
+ use Drupal\graphql\GraphQL\Buffers\EntityRevisionBuffer;
14
+ use Drupal\graphql\GraphQL\Execution\FieldContext;
15
+ use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
16
+ use GraphQL\Deferred;
17
+ use Symfony\Component\DependencyInjection\ContainerInterface;
18
+ use Symfony\Component\HttpFoundation\Request;
19
+ use Symfony\Component\HttpFoundation\RequestStack;
20
+
21
+ /**
22
+ * @todo Add an option to upstream "entity_load" to return null on missing
23
+ * translation and remove this plugin.
24
+ *
25
+ * Loads a single entity.
26
+ *
27
+ * @DataProducer(
28
+ * id = "fetch_entity",
29
+ * name = @Translation("Fetch entity"),
30
+ * description = @Translation("Loads a single entity."),
31
+ * produces = @ContextDefinition("entity",
32
+ * label = @Translation("Entity")
33
+ * ),
34
+ * consumes = {
35
+ * "type" = @ContextDefinition("string",
36
+ * label = @Translation("Entity type")
37
+ * ),
38
+ * "id" = @ContextDefinition("string",
39
+ * label = @Translation("Identifier"),
40
+ * required = FALSE
41
+ * ),
42
+ * "revision_id" = @ContextDefinition("string",
43
+ * label = @Translation("A specific revision id to fetch"),
44
+ * required = FALSE
45
+ * ),
46
+ * "language" = @ContextDefinition("string",
47
+ * label = @Translation("Entity language"),
48
+ * required = FALSE
49
+ * ),
50
+ * "bundles" = @ContextDefinition("string",
51
+ * label = @Translation("Entity bundle(s)"),
52
+ * multiple = TRUE,
53
+ * required = FALSE
54
+ * ),
55
+ * "access" = @ContextDefinition("boolean",
56
+ * label = @Translation("Check access"),
57
+ * required = FALSE,
58
+ * default_value = TRUE
59
+ * ),
60
+ * "access_user" = @ContextDefinition("entity:user",
61
+ * label = @Translation("User"),
62
+ * required = FALSE,
63
+ * default_value = NULL
64
+ * ),
65
+ * "access_operation" = @ContextDefinition("string",
66
+ * label = @Translation("Operation"),
67
+ * required = FALSE,
68
+ * default_value = "view"
69
+ * ),
70
+ * "load_latest_revision" = @ContextDefinition("boolean",
71
+ * label = @Translation("Load latest revision"),
72
+ * required = FALSE,
73
+ * default_value = FALSE
74
+ * ),
75
+ * }
76
+ * )
77
+ */
78
+ class FetchEntity extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
79
+
80
+ /**
81
+ * The entity type manager service.
82
+ *
83
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
84
+ */
85
+ protected $entityTypeManager;
86
+
87
+ /**
88
+ * The entity repository service.
89
+ *
90
+ * @var \Drupal\Core\Entity\EntityRepositoryInterface
91
+ */
92
+ protected $entityRepository;
93
+
94
+ /**
95
+ * The entity buffer service.
96
+ *
97
+ * @var \Drupal\graphql\GraphQL\Buffers\EntityBuffer
98
+ */
99
+ protected $entityBuffer;
100
+
101
+ /**
102
+ * @var \Drupal\graphql\GraphQL\Buffers\EntityRevisionBuffer
103
+ */
104
+ protected $entityRevisionBuffer;
105
+
106
+ /**
107
+ * @var \Drupal\Core\Path\PathValidatorInterface
108
+ */
109
+ protected $pathValidator;
110
+
111
+ /**
112
+ * @var \Symfony\Component\HttpFoundation\RequestStack
113
+ */
114
+ protected $requestStack;
115
+
116
+ /**
117
+ * @var \Drupal\Core\Language\LanguageManagerInterface
118
+ */
119
+ protected $languageManager;
120
+
121
+ /**
122
+ * {@inheritdoc}
123
+ *
124
+ * @codeCoverageIgnore
125
+ */
126
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
127
+ return new static(
128
+ $configuration,
129
+ $plugin_id,
130
+ $plugin_definition,
131
+ $container->get('entity_type.manager'),
132
+ $container->get('entity.repository'),
133
+ $container->get('graphql.buffer.entity'),
134
+ $container->get('graphql.buffer.entity_revision'),
135
+ $container->get('path.validator'),
136
+ $container->get('request_stack'),
137
+ $container->get('language_manager')
138
+ );
139
+ }
140
+
141
+ /**
142
+ * EntityLoad constructor.
143
+ *
144
+ * @param array $configuration
145
+ * The plugin configuration array.
146
+ * @param string $pluginId
147
+ * The plugin id.
148
+ * @param array $pluginDefinition
149
+ * The plugin definition array.
150
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
151
+ * The entity type manager service.
152
+ * @param \Drupal\Core\Entity\EntityRepositoryInterface $entityRepository
153
+ * The entity repository service.
154
+ * @param \Drupal\graphql\GraphQL\Buffers\EntityBuffer $entityBuffer
155
+ * The entity buffer service.
156
+ * @param \Drupal\graphql\GraphQL\Buffers\EntityRevisionBuffer $entityRevisionBuffer
157
+ * The entity revision buffer service.
158
+ *
159
+ * @codeCoverageIgnore
160
+ */
161
+ public function __construct(
162
+ array $configuration,
163
+ string $pluginId,
164
+ array $pluginDefinition,
165
+ EntityTypeManagerInterface $entityTypeManager,
166
+ EntityRepositoryInterface $entityRepository,
167
+ EntityBuffer $entityBuffer,
168
+ EntityRevisionBuffer $entityRevisionBuffer,
169
+ PathValidatorInterface $pathValidator,
170
+ RequestStack $request_stack,
171
+ LanguageManagerInterface $language_manager
172
+ ) {
173
+ parent::__construct($configuration, $pluginId, $pluginDefinition);
174
+ $this->entityTypeManager = $entityTypeManager;
175
+ $this->entityRepository = $entityRepository;
176
+ $this->entityBuffer = $entityBuffer;
177
+ $this->entityRevisionBuffer = $entityRevisionBuffer;
178
+ $this->pathValidator = $pathValidator;
179
+ $this->requestStack = $request_stack;
180
+ $this->languageManager = $language_manager;
181
+ }
182
+
183
+ /**
184
+ * Resolver.
185
+ *
186
+ * @param string $type
187
+ * @param string $id
188
+ * @param string|null $revisionId
189
+ * @param string|null $language
190
+ * @param array|null $bundles
191
+ * @param bool|null $access
192
+ * @param \Drupal\Core\Session\AccountInterface|null $accessUser
193
+ * @param string|null $accessOperation
194
+ * @param bool|null $loadLatestRevision
195
+ * @param \Drupal\graphql\GraphQL\Execution\FieldContext $context
196
+ *
197
+ * @return \GraphQL\Deferred
198
+ */
199
+ public function resolve(
200
+ string $type,
201
+ string $id,
202
+ ?string $revisionId,
203
+ ?string $language,
204
+ ?array $bundles,
205
+ ?bool $access,
206
+ ?AccountInterface $accessUser,
207
+ ?string $accessOperation,
208
+ ?bool $loadLatestRevision,
209
+ FieldContext $context
210
+ ) {
211
+ if ($id[0] === '/') {
212
+ // We are dealing with a path. Attempt to resolve it to an entity. And if
213
+ // the language is not defined, then we try to extract it from the path.
214
+ if (empty($language)) {
215
+ // In order to extract the language we basically just push a temporary
216
+ // request to the stack, reset the language negotiation and get the
217
+ // current language again, which will use the request at the top of the
218
+ // stack. At the end, we pop the temporary request from the stack and
219
+ // reste the language negotiation again.ss
220
+ $tmpRequest = Request::create($id);
221
+ $this->requestStack->push($tmpRequest);
222
+ $this->languageManager->reset();
223
+ $language = $this->languageManager->getCurrentLanguage()->getId();
224
+ $this->requestStack->pop();
225
+ $this->languageManager->reset();
226
+ }
227
+ $url = $this->pathValidator->getUrlIfValidWithoutAccessCheck($id);
228
+ if (!($url && $url->isRouted() && $url->access())) {
229
+ $context->addCacheTags(['4xx-response']);
230
+ return NULL;
231
+ }
232
+
233
+ $parameters = $url->getRouteParameters();
234
+ $id = $parameters[$type];
235
+ }
236
+
237
+ if (!preg_match('/^[0-9]+$/', $id)) {
238
+ // Looks like we got a UUID. Transform it to a regular ID.
239
+ $result = $this->entityTypeManager
240
+ ->getStorage($type)
241
+ ->getQuery()
242
+ ->condition('uuid', $id)
243
+ ->accessCheck(FALSE)
244
+ ->execute();
245
+ if ($result) {
246
+ $id = reset($result);
247
+ }
248
+ }
249
+
250
+ $resolver = $revisionId
251
+ ? $this->entityRevisionBuffer->add($type, $revisionId)
252
+ : $this->entityBuffer->add($type, $id);
253
+
254
+ return new Deferred(function () use ($type, $id, $revisionId, $language, $bundles, $resolver, $context, $access, $accessUser, $accessOperation, $loadLatestRevision) {
255
+ /** @var $entity \Drupal\Core\Entity\EntityInterface */
256
+ if (!$entity = $resolver()) {
257
+ // If there is no entity with this id, add the list cache tags so that
258
+ // the cache entry is purged whenever a new entity of this type is
259
+ // saved.
260
+ $type = $this->entityTypeManager->getDefinition($type);
261
+ /** @var \Drupal\Core\Entity\EntityTypeInterface $type */
262
+ $tags = $type->getListCacheTags();
263
+ $context->addCacheTags($tags);
264
+ return NULL;
265
+ }
266
+
267
+ $context->addCacheableDependency($entity);
268
+
269
+ // Make sure that the loaded entity has the exact same id as the one which
270
+ // was computed from the route. It maybe be that a wrong revision was sent
271
+ // as parameter. In that case, return NULL.
272
+ if ($entity->id() !== $id) {
273
+ return NULL;
274
+ }
275
+
276
+ if (isset($bundles) && !in_array($entity->bundle(), $bundles)) {
277
+ // If the entity is not among the allowed bundles, don't return it.
278
+ return NULL;
279
+ }
280
+
281
+
282
+ // If we did not request a specific revision, then we just load the most
283
+ // up to date one (the latest/active revision), but only if the flag is
284
+ // set.
285
+ if (empty($revisionId) && $loadLatestRevision) {
286
+ $activeEntityContext = [];
287
+ $additionalCacheContexts = [];
288
+ if (isset($language)) {
289
+ $activeEntityContext['langcode'] = $language;
290
+ $additionalCacheContexts[] = "static:language:{$language}";
291
+ }
292
+ $entity = $this->entityRepository->getActive($type, $entity->id(), $activeEntityContext);
293
+ if (!empty($additionalCacheContexts)) {
294
+ $entity->addCacheContexts($additionalCacheContexts);
295
+ }
296
+ // If the language of the found revision does not match the requested
297
+ // language, then we just return NULL.
298
+ if (isset($language) && $language !== $entity->language()->getId()) {
299
+ return NULL;
300
+ }
301
+ }
302
+ // If we are not interested in the latest revision, then we try to get the
303
+ // correct translation of the entity. When the latest revision is fetched,
304
+ // the proper language is already set into the context, so we do not need
305
+ // to request the translation separately.
306
+ elseif (isset($language) && $language !== $entity->language()->getId() && $entity instanceof TranslatableInterface) {
307
+ if (!$entity->hasTranslation($language)) {
308
+ return NULL;
309
+ }
310
+ $entity = $entity->getTranslation($language);
311
+ $entity->addCacheContexts(["static:language:{$language}"]);
312
+ }
313
+
314
+ // Check if the passed user (or current user if none is passed) has access
315
+ // to the entity, if not return NULL.
316
+ if ($access) {
317
+ /** @var \Drupal\Core\Access\AccessResultInterface $accessResult */
318
+ $accessResult = $entity->access($accessOperation, $accessUser, TRUE);
319
+ $context->addCacheableDependency($accessResult);
320
+ if (!$accessResult->isAllowed()) {
321
+ return NULL;
322
+ }
323
+ }
324
+
325
+ // If a specific revision was requested, we show that one and do not check
326
+ // further for autosaved values.
327
+ if (!empty($revisionId)) {
328
+ return $entity;
329
+ }
330
+
331
+ // Autosave: get autosaved values.
332
+ if (\Drupal::service('module_handler')->moduleExists('silverback_autosave')) {
333
+ $context->mergeCacheMaxAge(0);
334
+ // @todo Add DI to both.
335
+ /** @var \Drupal\silverback_autosave\Storage\AutosaveEntityFormStorageInterface $autoSaveFormStorage */
336
+ $autoSaveFormStorage = \Drupal::service('silverback_autosave.entity_form_storage');
337
+ $currentUser = \Drupal::currentUser();
338
+ $autosaveUserId = $currentUser->hasPermission('fetch any autosaved entity') ? NULL : $currentUser->id();
339
+
340
+ /**
341
+ * This causes leaked metadata error.
342
+ * $entityForm = \Drupal::service('entity.form_builder')->getForm($entity, 'edit');
343
+ * $form_id = $entityForm['form_id']['#value'];
344
+ */
345
+ $form_id = "{$entity->getEntityTypeId()}_{$entity->bundle()}_edit_form";
346
+ $autosaved_state = $autoSaveFormStorage->getEntityAndFormState($form_id, $entity->getEntityTypeId(), $entity->id(), $entity->language()->getId(), $autosaveUserId);
347
+ /** @var \Drupal\Core\Entity\EntityInterface $autosaved_entity */
348
+ $autosaved_entity = $autosaved_state['entity'] ?? NULL;
349
+ /** @var \Drupal\Core\Form\FormStateInterface $autosaved_form_state */
350
+ $autosaved_form_state = $autosaved_state['form_state'] ?? [];
351
+ if ($autosaved_entity && !empty($autosaved_form_state)) {
352
+ $current_user_input = $autosaved_form_state->getUserInput();
353
+ foreach ($autosaved_entity->getFields() as $name => $field) {
354
+ if (in_array($name, [
355
+ 'title',
356
+ 'body',
357
+ ]) || str_starts_with($name, 'field_')) {
358
+ if (isset($current_user_input[$name])) {
359
+ $field->setValue($current_user_input[$name]);
360
+ }
361
+ }
362
+ }
363
+ return $autosaved_entity;
364
+ }
365
+ }
366
+
367
+ return $entity;
368
+ });
369
+ }
370
+
371
+ }
@@ -0,0 +1,64 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer;
4
+
5
+ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
6
+ use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
7
+ use Drupal\locale\StringStorageInterface;
8
+ use Symfony\Component\DependencyInjection\ContainerInterface;
9
+
10
+ /**
11
+ *
12
+ * Loads a single string or translated string.
13
+ *
14
+ * @DataProducer(
15
+ * id = "fetch_string",
16
+ * name = @Translation("Fetch string"),
17
+ * description = @Translation("Loads a single string."),
18
+ * produces = @ContextDefinition("any",
19
+ * label = @Translation("String")
20
+ * ),
21
+ * consumes = {
22
+ * "id" = @ContextDefinition("string",
23
+ * label = @Translation("Identifier"),
24
+ * required = FALSE
25
+ * )
26
+ * }
27
+ * )
28
+ */
29
+ class FetchString extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
30
+
31
+ /**
32
+ * The locale storage service.
33
+ *
34
+ * @var \Drupal\locale\StringStorageInterface
35
+ */
36
+ protected $localeStorage;
37
+
38
+ public function __construct(
39
+ array $configuration,
40
+ $plugin_id,
41
+ $plugin_definition,
42
+ StringStorageInterface $locale_storage
43
+ ) {
44
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
45
+ $this->localeStorage = $locale_storage;
46
+ }
47
+
48
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
49
+ return new static(
50
+ $configuration,
51
+ $plugin_id,
52
+ $plugin_definition,
53
+ $container->get('locale.storage')
54
+ );
55
+ }
56
+
57
+ public function resolve($id) {
58
+ $strings = $this->localeStorage->getStrings(['lid' => $id]);
59
+ if (!empty($strings)) {
60
+ return reset($strings);
61
+ }
62
+ return NULL;
63
+ }
64
+ }
@@ -0,0 +1,74 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer;
4
+
5
+ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
6
+ use Drupal\graphql\GraphQL\Execution\FieldContext;
7
+ use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
8
+ use Drupal\locale\StringStorageInterface;
9
+ use Symfony\Component\DependencyInjection\ContainerInterface;
10
+
11
+ /**
12
+ *
13
+ * Loads a single string or translated string.
14
+ *
15
+ * @DataProducer(
16
+ * id = "fetch_translatable_string",
17
+ * name = @Translation("Fetch translatable string"),
18
+ * description = @Translation("Loads a single translatable string."),
19
+ * produces = @ContextDefinition("any",
20
+ * label = @Translation("String")
21
+ * ),
22
+ * consumes = {
23
+ * "id" = @ContextDefinition("string",
24
+ * label = @Translation("Identifier"),
25
+ * required = FALSE
26
+ * ),
27
+ * "language" = @ContextDefinition("string",
28
+ * label = @Translation("Language"),
29
+ * required = FALSE
30
+ * )
31
+ * }
32
+ * )
33
+ */
34
+ class FetchTranslatableString extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
35
+
36
+ /**
37
+ * The locale storage service.
38
+ *
39
+ * @var \Drupal\locale\StringStorageInterface
40
+ */
41
+ protected $localeStorage;
42
+
43
+ public function __construct(
44
+ array $configuration,
45
+ $plugin_id,
46
+ $plugin_definition,
47
+ StringStorageInterface $locale_storage
48
+ ) {
49
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
50
+ $this->localeStorage = $locale_storage;
51
+ }
52
+
53
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
54
+ return new static(
55
+ $configuration,
56
+ $plugin_id,
57
+ $plugin_definition,
58
+ $container->get('locale.storage')
59
+ );
60
+ }
61
+
62
+ public function resolve($id, $language, FieldContext $context) {
63
+ $strings = $this->localeStorage->getTranslations([
64
+ 'lid' => $id,
65
+ 'language' => $language,
66
+ ]);
67
+ // Make sure the result clears when new translations are added.
68
+ $context->addCacheTags(['locale']);
69
+ if (!empty($strings)) {
70
+ return reset($strings);
71
+ }
72
+ return NULL;
73
+ }
74
+ }
@@ -0,0 +1,47 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer;
4
+
5
+ use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
6
+ use Drupal\crop\Entity\Crop;
7
+ use Drupal\Core\StreamWrapper\PublicStream;
8
+
9
+ /**
10
+ * @DataProducer(
11
+ * id = "focal_point",
12
+ * name = @Translation("Focal Point"),
13
+ * description = @Translation("Retrieve focal point coordinates for an image source."),
14
+ * produces = @ContextDefinition("any",
15
+ * label = @Translation("Image properties")
16
+ * ),
17
+ * consumes = {
18
+ * "image_props" = @ContextDefinition("any",
19
+ * label = @Translation("Image Props"),
20
+ * required = FALSE
21
+ * )
22
+ * }
23
+ * )
24
+ */
25
+ class FocalPoint extends DataProducerPluginBase {
26
+ /**
27
+ * Resolver to return the focal point coordinates.
28
+ *
29
+ * @param array $image_props
30
+ *
31
+ * @return array|null
32
+ */
33
+ public function resolve(?array $image_props = NULL) {
34
+ $path = parse_url($image_props['src']);
35
+ $publicFilesDirectory = '/' . PublicStream::basePath();
36
+ // Reverse-engineer the public path to find the crop.
37
+ $public = 'public:/' . substr($path['path'], strlen($publicFilesDirectory));
38
+ $crop = Crop::findCrop($public, 'focal_point');
39
+ $x = $crop?->x->value;
40
+ $y = $crop?->y->value;
41
+ $image_props['focalPoint'] = $x && $y ? [
42
+ 'x' => $x,
43
+ 'y' => $y,
44
+ ] : NULL;
45
+ return $image_props;
46
+ }
47
+ }
@@ -0,0 +1,36 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer;
4
+
5
+ use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
6
+
7
+ /**
8
+ * @DataProducer(
9
+ * id = "gatsby_build_id",
10
+ * name = @Translation("Build the Gatsby ID "),
11
+ * produces = @ContextDefinition("string",
12
+ * label = @Translation("Gatsby ID"),
13
+ * multiple = TRUE,
14
+ * ),
15
+ * consumes = {
16
+ * "id" = @ContextDefinition("string",
17
+ * label = @Translation("ID"),
18
+ * required = TRUE,
19
+ * ),
20
+ * "langcode" = @ContextDefinition("string",
21
+ * label = @Translation("Langcode"),
22
+ * required = TRUE,
23
+ * )
24
+ * },
25
+ * )
26
+ */
27
+ class GatsbyBuildId extends DataProducerPluginBase {
28
+
29
+ public static function build(string $id, string $langcode) {
30
+ return $id . ':' . $langcode;
31
+ }
32
+
33
+ public function resolve(string $id, string $langcode) {
34
+ return static::build($id, $langcode);
35
+ }
36
+ }
@@ -0,0 +1,28 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer;
4
+
5
+ use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
6
+
7
+ /**
8
+ * @DataProducer(
9
+ * id = "gatsby_extract_id",
10
+ * name = @Translation("Extract ID from a Gatsby ID "),
11
+ * produces = @ContextDefinition("string",
12
+ * label = @Translation("ID"),
13
+ * multiple = TRUE,
14
+ * ),
15
+ * consumes = {
16
+ * "id" = @ContextDefinition("string",
17
+ * label = @Translation("Gatsby ID"),
18
+ * required = TRUE,
19
+ * )
20
+ * },
21
+ * )
22
+ */
23
+ class GatsbyExtractId extends DataProducerPluginBase {
24
+
25
+ public function resolve(string $id) {
26
+ return explode(':', $id)[0];
27
+ }
28
+ }
@@ -0,0 +1,28 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer;
4
+
5
+ use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
6
+
7
+ /**
8
+ * @DataProducer(
9
+ * id = "gatsby_extract_langcode",
10
+ * name = @Translation("Extract langcode from a Gatsby ID "),
11
+ * produces = @ContextDefinition("string",
12
+ * label = @Translation("Langcode"),
13
+ * multiple = TRUE,
14
+ * ),
15
+ * consumes = {
16
+ * "id" = @ContextDefinition("string",
17
+ * label = @Translation("Gatsby ID"),
18
+ * required = TRUE,
19
+ * )
20
+ * },
21
+ * )
22
+ */
23
+ class GatsbyExtractLangcode extends DataProducerPluginBase {
24
+
25
+ public function resolve(string $id) {
26
+ return explode(':', $id)[1] ?? NULL;
27
+ }
28
+ }