@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.
- package/CHANGELOG.md +11 -0
- package/drupal/silverback_gatsby/.prettierignore +1 -0
- package/drupal/silverback_gatsby/CHANGELOG.md +1216 -0
- package/drupal/silverback_gatsby/README.md +297 -0
- package/drupal/silverback_gatsby/composer.json +15 -0
- package/drupal/silverback_gatsby/directives.gql +63 -0
- package/drupal/silverback_gatsby/directives.graphql +62 -0
- package/drupal/silverback_gatsby/drush.services.yml +9 -0
- package/drupal/silverback_gatsby/graphql/entity.directive.graphqls +2 -0
- package/drupal/silverback_gatsby/graphql/menu.directive.graphqls +17 -0
- package/drupal/silverback_gatsby/graphql/silverback_gatsby.base.graphqls +25 -0
- package/drupal/silverback_gatsby/graphql/silverback_gatsby.extension.graphqls +10 -0
- package/drupal/silverback_gatsby/graphql/stringTranslation.directive.graphqls +4 -0
- package/drupal/silverback_gatsby/graphql/translatableString.directive.graphqls +2 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/directives.gql +6 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/graphql/.graphqlrc.json +4 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/graphql/silverback_gatsby_example.graphqls +39 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/silverback_gatsby_example.info.yml +7 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/silverback_gatsby_example.module +16 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/src/Directives.php +24 -0
- package/drupal/silverback_gatsby/silverback_gatsby.info.yml +8 -0
- package/drupal/silverback_gatsby/silverback_gatsby.install +180 -0
- package/drupal/silverback_gatsby/silverback_gatsby.links.task.yml +6 -0
- package/drupal/silverback_gatsby/silverback_gatsby.module +119 -0
- package/drupal/silverback_gatsby/silverback_gatsby.permissions.yml +8 -0
- package/drupal/silverback_gatsby/silverback_gatsby.post_update.php +15 -0
- package/drupal/silverback_gatsby/silverback_gatsby.routing.yml +28 -0
- package/drupal/silverback_gatsby/silverback_gatsby.services.yml +50 -0
- package/drupal/silverback_gatsby/src/Annotation/GatsbyFeed.php +41 -0
- package/drupal/silverback_gatsby/src/Commands/SilverbackGatsbyCommands.php +102 -0
- package/drupal/silverback_gatsby/src/Controller/BuildController.php +45 -0
- package/drupal/silverback_gatsby/src/Controller/PublisherController.php +34 -0
- package/drupal/silverback_gatsby/src/Directives.php +51 -0
- package/drupal/silverback_gatsby/src/GatsbyBuildTrigger.php +230 -0
- package/drupal/silverback_gatsby/src/GatsbyBuildTriggerInterface.php +52 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdate.php +38 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateHandler.php +181 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateTracker.php +98 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateTrackerInterface.php +59 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateTrigger.php +69 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateTriggerInterface.php +22 -0
- package/drupal/silverback_gatsby/src/GraphQL/Build.php +235 -0
- package/drupal/silverback_gatsby/src/GraphQL/ComposableSchema.php +73 -0
- package/drupal/silverback_gatsby/src/LocaleStorageDecorator.php +181 -0
- package/drupal/silverback_gatsby/src/MenuTreeStorageDecorator.php +139 -0
- package/drupal/silverback_gatsby/src/Plugin/FeedBase.php +121 -0
- package/drupal/silverback_gatsby/src/Plugin/FeedInterface.php +132 -0
- package/drupal/silverback_gatsby/src/Plugin/FeedPluginManager.php +39 -0
- package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/EntityFeed.php +252 -0
- package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/MenuFeed.php +292 -0
- package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/StringTranslationFeed.php +171 -0
- package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/TranslatableStringFeed.php +128 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/CurrentUserEntity.php +43 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/EntityQueryBase.php +40 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchEntity.php +371 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchString.php +64 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchTranslatableString.php +74 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FocalPoint.php +47 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyBuildId.php +36 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyExtractId.php +28 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyExtractLangcode.php +28 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ImageProps.php +149 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ListEntities.php +88 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ListStrings.php +87 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/StringId.php +37 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/StringTranslations.php +94 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/EntityFetch.php +51 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/EntityTranslationsWithDefault.php +30 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/FocalPoint.php +27 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/ImageProps.php +27 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/MenuLangcode.php +27 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/MenuTranslations.php +36 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/SilverbackGatsbyEntityId.php +39 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/SchemaExtension/SilverbackGatsbySchemaExtension.php +384 -0
- package/drupal/silverback_gatsby/src/SilverbackGatsbyServiceProvider.php +43 -0
- package/drupal/silverback_gatsby/src/SilverbackGatsbySessionConfiguration.php +30 -0
- package/drupal/silverback_gatsby/src/SilverbackReverseProxyMiddleware.php +49 -0
- package/drupal/silverback_gatsby/tests/queries/create-page-fields.gql +12 -0
- package/drupal/silverback_gatsby/tests/queries/current-user.gql +6 -0
- package/drupal/silverback_gatsby/tests/queries/feed_info.gql +12 -0
- package/drupal/silverback_gatsby/tests/queries/load-entity.gql +5 -0
- package/drupal/silverback_gatsby/tests/queries/menus.gql +31 -0
- package/drupal/silverback_gatsby/tests/queries/multilingual-menus.gql +11 -0
- package/drupal/silverback_gatsby/tests/queries/revisionable-translatable.gql +15 -0
- package/drupal/silverback_gatsby/tests/queries/revisionable.gql +8 -0
- package/drupal/silverback_gatsby/tests/queries/translatable.gql +14 -0
- package/drupal/silverback_gatsby/tests/queries/untranslatable.gql +10 -0
- package/drupal/silverback_gatsby/tests/schema/.graphqlrc.json +4 -0
- package/drupal/silverback_gatsby/tests/schema/translatable-strings.graphql +7 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/CurrentUserTest.php +37 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/EntityFeedTest.php +490 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/EntityFeedTestBase.php +178 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyBuildTriggerTest.php +155 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyFeedInfoTest.php +319 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateHandlerTest.php +131 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateTrackerTest.php +196 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateTriggerTest.php +177 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/MenuFeedTest.php +192 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/TranslatableStringFeedTest.php +210 -0
- package/drupal/silverback_gatsby/tests/src/Traits/NotificationCheckTrait.php +43 -0
- package/package.json +15 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gatsby\Plugin\Gatsby\Feed;
|
|
4
|
+
|
|
5
|
+
use Drupal\content_translation\ContentTranslationManagerInterface;
|
|
6
|
+
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
|
7
|
+
use Drupal\Core\Entity\TranslatableInterface;
|
|
8
|
+
use Drupal\Core\Language\LanguageInterface;
|
|
9
|
+
use Drupal\Core\Language\LanguageManagerInterface;
|
|
10
|
+
use Drupal\Core\Menu\MenuLinkTreeElement;
|
|
11
|
+
use Drupal\Core\Menu\MenuLinkTreeInterface;
|
|
12
|
+
use Drupal\Core\Menu\MenuTreeParameters;
|
|
13
|
+
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
|
14
|
+
use Drupal\Core\Session\AccountInterface;
|
|
15
|
+
use Drupal\graphql\GraphQL\Execution\FieldContext;
|
|
16
|
+
use Drupal\graphql\GraphQL\Execution\ResolveContext;
|
|
17
|
+
use Drupal\graphql\GraphQL\Resolver\ResolverInterface;
|
|
18
|
+
use Drupal\graphql\GraphQL\ResolverBuilder;
|
|
19
|
+
use Drupal\graphql\GraphQL\ResolverRegistryInterface;
|
|
20
|
+
use Drupal\graphql_directives\Plugin\GraphQL\DataProducer\FilterMenuItems;
|
|
21
|
+
use Drupal\silverback_gatsby\Annotation\GatsbyFeed;
|
|
22
|
+
use Drupal\silverback_gatsby\Plugin\FeedBase;
|
|
23
|
+
use Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer\GatsbyBuildId;
|
|
24
|
+
use Drupal\system\Entity\Menu;
|
|
25
|
+
use GraphQL\Deferred;
|
|
26
|
+
use GraphQL\Language\AST\DocumentNode;
|
|
27
|
+
use GraphQL\Type\Definition\ResolveInfo;
|
|
28
|
+
use Symfony\Component\DependencyInjection\ContainerInterface;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Feed plugin that creates Gatsby feeds based on Drupal menus.
|
|
32
|
+
*
|
|
33
|
+
* @GatsbyFeed(
|
|
34
|
+
* id = "menu"
|
|
35
|
+
* )
|
|
36
|
+
*/
|
|
37
|
+
class MenuFeed extends FeedBase implements ContainerFactoryPluginInterface {
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The target menu id.
|
|
41
|
+
*
|
|
42
|
+
* @var string | null
|
|
43
|
+
*/
|
|
44
|
+
protected $menu_id;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Internal menu id's. The first one the current user has access to will be
|
|
48
|
+
* picked.
|
|
49
|
+
*
|
|
50
|
+
* @var string[] | null
|
|
51
|
+
*/
|
|
52
|
+
protected $menu_ids;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The maximum menu level.
|
|
56
|
+
*
|
|
57
|
+
* @var int
|
|
58
|
+
*/
|
|
59
|
+
protected int $max_level;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The GraphQL item type used for menu items.
|
|
63
|
+
*
|
|
64
|
+
* @var string | null
|
|
65
|
+
*/
|
|
66
|
+
protected $item_type;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @var \Drupal\content_translation\ContentTranslationManagerInterface|null
|
|
70
|
+
*/
|
|
71
|
+
protected ?ContentTranslationManagerInterface $contentTranslationManager;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @var \Drupal\Core\Language\LanguageManagerInterface
|
|
75
|
+
*/
|
|
76
|
+
protected LanguageManagerInterface $languageManager;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @var \Drupal\Core\Menu\MenuLinkTreeInterface
|
|
80
|
+
*/
|
|
81
|
+
protected MenuLinkTreeInterface $menuLinkTree;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
|
85
|
+
*/
|
|
86
|
+
protected EntityTypeManagerInterface $entityTypeManager;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* {@inheritDoc}
|
|
90
|
+
*/
|
|
91
|
+
public static function create(
|
|
92
|
+
ContainerInterface $container,
|
|
93
|
+
array $configuration,
|
|
94
|
+
$plugin_id,
|
|
95
|
+
$plugin_definition
|
|
96
|
+
) {
|
|
97
|
+
return new static(
|
|
98
|
+
$configuration,
|
|
99
|
+
$plugin_id,
|
|
100
|
+
$plugin_definition,
|
|
101
|
+
$container->has('content_translation.manager')
|
|
102
|
+
? $container->get('content_translation.manager')
|
|
103
|
+
: NULL,
|
|
104
|
+
$container->get('menu.link_tree'),
|
|
105
|
+
$container->get('language_manager'),
|
|
106
|
+
$container->get('entity_type.manager')
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public function __construct(
|
|
111
|
+
$config,
|
|
112
|
+
$plugin_id,
|
|
113
|
+
$plugin_definition,
|
|
114
|
+
?ContentTranslationManagerInterface $contentTranslationManager,
|
|
115
|
+
MenuLinkTreeInterface $menuLinkTree,
|
|
116
|
+
LanguageManagerInterface $languageManager,
|
|
117
|
+
EntityTypeManagerInterface $entityTypeManager
|
|
118
|
+
) {
|
|
119
|
+
$this->menu_id = $config['menu_id'] ?? NULL;
|
|
120
|
+
$this->menu_ids = $config['menu_ids'] ?? NULL;
|
|
121
|
+
$this->max_level = $config['max_level'] ?? -1;
|
|
122
|
+
$this->item_type = $config['item_type'] ?? NULL;
|
|
123
|
+
$this->contentTranslationManager = $contentTranslationManager;
|
|
124
|
+
$this->menuLinkTree = $menuLinkTree;
|
|
125
|
+
$this->languageManager = $languageManager;
|
|
126
|
+
$this->entityTypeManager = $entityTypeManager;
|
|
127
|
+
|
|
128
|
+
parent::__construct(
|
|
129
|
+
$config,
|
|
130
|
+
$plugin_id,
|
|
131
|
+
$plugin_definition
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Expose the configured max level so it can be used by the schema extension.
|
|
137
|
+
*
|
|
138
|
+
* @return int|mixed
|
|
139
|
+
*/
|
|
140
|
+
public function getMaxLevel() {
|
|
141
|
+
return $this->max_level;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The menu ids to be loaded and negotiated.
|
|
146
|
+
*
|
|
147
|
+
* @return string[]
|
|
148
|
+
*/
|
|
149
|
+
public function menuIds() {
|
|
150
|
+
$ids = $this->menu_ids ?: [];
|
|
151
|
+
if ($this->menu_id) {
|
|
152
|
+
$ids[] = $this->menu_id;
|
|
153
|
+
}
|
|
154
|
+
return $ids;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* {@inheritDoc}
|
|
159
|
+
*/
|
|
160
|
+
public function isTranslatable(): bool {
|
|
161
|
+
return $this->contentTranslationManager &&
|
|
162
|
+
$this->contentTranslationManager->isEnabled('menu_link_content', 'menu_link_content');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* {@inheritDoc}
|
|
167
|
+
*/
|
|
168
|
+
public function getUpdateIds($context, ?AccountInterface $account) : array {
|
|
169
|
+
$params = new MenuTreeParameters();
|
|
170
|
+
if ($this->max_level > 0) {
|
|
171
|
+
$params->maxDepth = $this->max_level;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Get all menus that are associated with this feed, and pick the first one
|
|
175
|
+
// the account has access to.
|
|
176
|
+
/** @var \Drupal\system\Entity\Menu[] $menus */
|
|
177
|
+
$menus = $this->entityTypeManager->getStorage('menu')->loadMultiple($this->menuIds());
|
|
178
|
+
$relevantMenu = NULL;
|
|
179
|
+
foreach($menus as $menu) {
|
|
180
|
+
if (!$account || $menu->access('view label', $account)) {
|
|
181
|
+
$relevantMenu = $menu;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!$relevantMenu) {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
$tree = $this->menuLinkTree->load($relevantMenu->id(), $params);
|
|
191
|
+
$items = FilterMenuItems::flatten($tree, -1);
|
|
192
|
+
|
|
193
|
+
$ids = [$context];
|
|
194
|
+
$match = count(array_filter($items, function (MenuLinkTreeElement $item) use ($ids) {
|
|
195
|
+
return in_array($item->link->getPluginId(), $ids);
|
|
196
|
+
})) > 0;
|
|
197
|
+
|
|
198
|
+
// If the menu item is not in any exposed menus, don't return any updates.
|
|
199
|
+
if (!$match) {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if ($this->isTranslatable()) {
|
|
204
|
+
// If menu items are translatable, trigger an update in each language,
|
|
205
|
+
// since we can't determine easily which language was affected.
|
|
206
|
+
return array_map(
|
|
207
|
+
fn (LanguageInterface $lang) =>
|
|
208
|
+
GatsbyBuildId::build($relevantMenu->id(), $lang->getId())
|
|
209
|
+
, $this->languageManager->getLanguages());
|
|
210
|
+
}
|
|
211
|
+
// Else, simply return the menu id.
|
|
212
|
+
return [$relevantMenu->id()];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* {@inheritDoc}
|
|
217
|
+
*/
|
|
218
|
+
public function resolveItem(ResolverInterface $id, ?ResolverInterface $langcode = null): ResolverInterface {
|
|
219
|
+
$resolver = $this->builder->produce('entity_load')
|
|
220
|
+
->map('type', $this->builder->fromValue('menu'))
|
|
221
|
+
->map('id', $id)
|
|
222
|
+
->map('access', $this->builder->fromValue(true))
|
|
223
|
+
->map('access_operation', $this->builder->fromValue('view label'));
|
|
224
|
+
if ($this->isTranslatable() && $langcode) {
|
|
225
|
+
$resolver->map('language', $langcode);
|
|
226
|
+
$resolver = $this->builder->compose(
|
|
227
|
+
$this->builder->context('current_menu_language', $langcode),
|
|
228
|
+
$resolver,
|
|
229
|
+
$this->builder->callback(function ($value, $args, ResolveContext $context, ResolveInfo $info, FieldContext $fieldContext) {
|
|
230
|
+
$langcode = $fieldContext->getContextValue('current_menu_language');
|
|
231
|
+
if ($langcode && $langcode !== $value->language()->getId()) {
|
|
232
|
+
$clone = clone $value;
|
|
233
|
+
$clone->set('langcode', $langcode);
|
|
234
|
+
return $clone;
|
|
235
|
+
}
|
|
236
|
+
return $value;
|
|
237
|
+
})
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return $resolver;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* {@inheritDoc}
|
|
245
|
+
*/
|
|
246
|
+
public function resolveItems(ResolverInterface $limit, ResolverInterface $offset): ResolverInterface {
|
|
247
|
+
return $this->builder->produce('entity_load_multiple')
|
|
248
|
+
->map('type', $this->builder->fromValue('menu'))
|
|
249
|
+
->map('ids', $this->builder->fromValue($this->menuIds()))
|
|
250
|
+
->map('access', $this->builder->fromValue(true))
|
|
251
|
+
->map('access_operation', $this->builder->fromValue('view label'));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* {@inheritDoc}
|
|
256
|
+
*/
|
|
257
|
+
public function resolveId(): ResolverInterface {
|
|
258
|
+
return $this->builder->produce('entity_id')
|
|
259
|
+
->map('entity', $this->builder->fromParent());
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* {@inheritDoc}
|
|
264
|
+
*/
|
|
265
|
+
public function resolveLangcode(): ResolverInterface {
|
|
266
|
+
return $this->builder->callback(
|
|
267
|
+
fn(Menu $value) => $value->language()->getId()
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* {@inheritDoc}
|
|
273
|
+
*/
|
|
274
|
+
public function resolveDefaultTranslation(): ResolverInterface {
|
|
275
|
+
return $this->builder->callback(
|
|
276
|
+
fn(Menu $value) => $value->language()->isDefault()
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* {@inheritDoc}
|
|
282
|
+
*/
|
|
283
|
+
public function resolveTranslations(): ResolverInterface {
|
|
284
|
+
return $this->builder->compose($this->builder->callback(function (Menu $menu) {
|
|
285
|
+
return array_map(function (LanguageInterface $lang) use ($menu) {
|
|
286
|
+
$clone = clone $menu;
|
|
287
|
+
$clone->set('langcode', $lang->getId());
|
|
288
|
+
return $clone;
|
|
289
|
+
}, $this->languageManager->getLanguages());
|
|
290
|
+
}));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gatsby\Plugin\Gatsby\Feed;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Session\AccountInterface;
|
|
6
|
+
use Drupal\graphql\GraphQL\Resolver\ResolverInterface;
|
|
7
|
+
use Drupal\graphql\GraphQL\ResolverBuilder;
|
|
8
|
+
use Drupal\graphql\GraphQL\ResolverRegistryInterface;
|
|
9
|
+
use Drupal\locale\SourceString;
|
|
10
|
+
use Drupal\locale\StringInterface;
|
|
11
|
+
use Drupal\locale\TranslationString;
|
|
12
|
+
use Drupal\silverback_gatsby\Plugin\FeedBase;
|
|
13
|
+
use GraphQL\Language\AST\DocumentNode;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Feed plugin that creates Gatsby feeds based on Drupal string translations.
|
|
17
|
+
*
|
|
18
|
+
* DEPRECATED: Use @translatableString instead, when working with a directive
|
|
19
|
+
* based schema.
|
|
20
|
+
*
|
|
21
|
+
* @GatsbyFeed(
|
|
22
|
+
* id = "stringTranslation"
|
|
23
|
+
* )
|
|
24
|
+
*/
|
|
25
|
+
class StringTranslationFeed extends FeedBase {
|
|
26
|
+
|
|
27
|
+
protected $contextPrefix;
|
|
28
|
+
|
|
29
|
+
public function __construct(
|
|
30
|
+
$config,
|
|
31
|
+
$plugin_id,
|
|
32
|
+
$plugin_definition
|
|
33
|
+
) {
|
|
34
|
+
$this->contextPrefix = $config['contextPrefix'];
|
|
35
|
+
parent::__construct($config, $plugin_id, $plugin_definition);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public function getTranslationTypeName() {
|
|
39
|
+
return $this->getTypeName() . 'Translation';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* {@inheritDoc}
|
|
44
|
+
*/
|
|
45
|
+
function getUpdateIds($context, ?AccountInterface $account): array {
|
|
46
|
+
if (!$context instanceof StringInterface) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// If the string has the 'context' property, we only return its id if it has
|
|
51
|
+
// the context prefix match. If there's no context property, then we return
|
|
52
|
+
// its id in any case.
|
|
53
|
+
if (!isset($context->context) || empty($this->contextPrefix)) {
|
|
54
|
+
return [$context->getId()];
|
|
55
|
+
}
|
|
56
|
+
if (strpos($context->context, $this->contextPrefix) === 0) {
|
|
57
|
+
return [$context->getId()];
|
|
58
|
+
}
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* {@inheritDoc}
|
|
64
|
+
*/
|
|
65
|
+
public function isTranslatable(): bool {
|
|
66
|
+
return FALSE;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* {@inheritDoc}
|
|
71
|
+
*/
|
|
72
|
+
public function resolveId(): ResolverInterface {
|
|
73
|
+
return $this->builder->produce('string_id')
|
|
74
|
+
->map('string', $this->builder->fromParent());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* {@inheritDoc}
|
|
79
|
+
*/
|
|
80
|
+
public function resolveLangcode(): ResolverInterface {
|
|
81
|
+
return $this->builder->callback(
|
|
82
|
+
fn(StringInterface $value) => $value instanceof TranslationString ? $value->language : ''
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* {@inheritDoc}
|
|
88
|
+
*/
|
|
89
|
+
public function resolveDefaultTranslation(): ResolverInterface {
|
|
90
|
+
return $this->builder->callback(
|
|
91
|
+
fn(StringInterface $value) => FALSE
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* {@inheritDoc}
|
|
97
|
+
*/
|
|
98
|
+
public function resolveTranslations(): ResolverInterface {
|
|
99
|
+
return $this->builder->produce('string_translations')
|
|
100
|
+
->map('sourceString', $this->builder->fromParent());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public function resolveItems(ResolverInterface $limit, ResolverInterface $offset): ResolverInterface {
|
|
104
|
+
return $this->builder->produce('list_strings')
|
|
105
|
+
->map('offset', $this->builder->defaultValue(
|
|
106
|
+
$this->builder->fromArgument('offset'),
|
|
107
|
+
$this->builder->fromValue(0)
|
|
108
|
+
))
|
|
109
|
+
->map('limit', $this->builder->defaultValue(
|
|
110
|
+
$this->builder->fromArgument('limit'),
|
|
111
|
+
$this->builder->fromValue(10),
|
|
112
|
+
))
|
|
113
|
+
// For now, the schema extension does not allow array arguments. However,
|
|
114
|
+
// the list strings resolver can do that, so we just convert the context
|
|
115
|
+
// prefix to an array here. By doing that, when we add support for array
|
|
116
|
+
// arguments in directives, we don't have to change this code.
|
|
117
|
+
->map('translationContext', $this->builder->fromValue(is_array($this->contextPrefix) ? $this->contextPrefix : [$this->contextPrefix]));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* {@inheritDoc}
|
|
122
|
+
*/
|
|
123
|
+
public function resolveItem(ResolverInterface $id, ?ResolverInterface $langcode = null): ResolverInterface {
|
|
124
|
+
$resolver = $this->builder->produce('fetch_string')
|
|
125
|
+
->map('id', $id);
|
|
126
|
+
return $resolver;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public function getExtensionDefinition(DocumentNode $parentAst): string {
|
|
130
|
+
$typeName = $this->getTypeName();
|
|
131
|
+
$translationTypeName = $this->getTranslationTypeName();
|
|
132
|
+
$def = [];
|
|
133
|
+
$def[] = "extend type $typeName {";
|
|
134
|
+
$def[] = " source: String!";
|
|
135
|
+
$def[] = " context: String";
|
|
136
|
+
$def[] = " translations: [$translationTypeName]";
|
|
137
|
+
$def[] = "}";
|
|
138
|
+
$def[] = "type $translationTypeName {";
|
|
139
|
+
$def[] = " id: String!";
|
|
140
|
+
$def[] = " source: String!";
|
|
141
|
+
$def[] = " langcode: String!";
|
|
142
|
+
$def[] = " translation: String!";
|
|
143
|
+
$def[] = "}";
|
|
144
|
+
return implode("\n", $def);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public function addExtensionResolvers(
|
|
148
|
+
ResolverRegistryInterface $registry,
|
|
149
|
+
ResolverBuilder $builder
|
|
150
|
+
): void {
|
|
151
|
+
$registry->addFieldResolver($this->getTypeName(), 'source', $builder->callback(
|
|
152
|
+
fn (StringInterface $value) => $value->getString()
|
|
153
|
+
));
|
|
154
|
+
$registry->addFieldResolver($this->getTypeName(), 'context', $builder->callback(
|
|
155
|
+
fn (StringInterface $value) => $value->context
|
|
156
|
+
));
|
|
157
|
+
$registry->addFieldResolver($this->getTypeName(), 'translations', $this->resolveTranslations());
|
|
158
|
+
|
|
159
|
+
$translationTypeName = $this->getTranslationTypeName();
|
|
160
|
+
$registry->addFieldResolver($translationTypeName, 'id', $builder->produce('gatsby_build_id')
|
|
161
|
+
->map('id', $this->resolveId())
|
|
162
|
+
->map('langcode', $this->resolveLangcode()));
|
|
163
|
+
$registry->addFieldResolver($translationTypeName, 'source', $builder->callback(
|
|
164
|
+
fn (StringInterface $value) => $value->source
|
|
165
|
+
));
|
|
166
|
+
$registry->addFieldResolver($translationTypeName, 'langcode', $this->resolveLangcode());
|
|
167
|
+
$registry->addFieldResolver($translationTypeName, 'translation', $builder->callback(
|
|
168
|
+
fn (StringInterface $value) => $value->getString()
|
|
169
|
+
));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gatsby\Plugin\Gatsby\Feed;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Session\AccountInterface;
|
|
6
|
+
use Drupal\graphql\GraphQL\Resolver\ResolverInterface;
|
|
7
|
+
use Drupal\locale\StringInterface;
|
|
8
|
+
use Drupal\locale\TranslationString;
|
|
9
|
+
use Drupal\silverback_gatsby\Plugin\FeedBase;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Feed plugin that sources string translations into Gatsby.
|
|
13
|
+
*
|
|
14
|
+
* @GatsbyFeed(
|
|
15
|
+
* id = "translatableString"
|
|
16
|
+
* )
|
|
17
|
+
*/
|
|
18
|
+
class TranslatableStringFeed extends FeedBase {
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The context prefix, matching the drupal translation context.
|
|
22
|
+
*/
|
|
23
|
+
protected string $contextPrefix;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* {@inheritDoc}
|
|
27
|
+
*/
|
|
28
|
+
public function __construct(
|
|
29
|
+
$config,
|
|
30
|
+
$plugin_id,
|
|
31
|
+
$plugin_definition
|
|
32
|
+
) {
|
|
33
|
+
$this->contextPrefix = $config['contextPrefix'];
|
|
34
|
+
parent::__construct($config, $plugin_id, $plugin_definition);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* {@inheritDoc}
|
|
39
|
+
*/
|
|
40
|
+
public function getUpdateIds($context, ?AccountInterface $account): array {
|
|
41
|
+
if (!$context instanceof StringInterface) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// If the string has the 'context' property, we only return its id if it has
|
|
46
|
+
// the context prefix match. If there's no context property, then we return
|
|
47
|
+
// its id in any case.
|
|
48
|
+
if (!isset($context->context) || empty($this->contextPrefix)) {
|
|
49
|
+
return [$context->getId()];
|
|
50
|
+
}
|
|
51
|
+
if (strpos($context->context, $this->contextPrefix) === 0) {
|
|
52
|
+
return [$context->getId()];
|
|
53
|
+
}
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* {@inheritDoc}
|
|
59
|
+
*/
|
|
60
|
+
public function isTranslatable(): bool {
|
|
61
|
+
return TRUE;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* {@inheritDoc}
|
|
66
|
+
*/
|
|
67
|
+
public function resolveId(): ResolverInterface {
|
|
68
|
+
return $this->builder->produce('string_id')
|
|
69
|
+
->map('string', $this->builder->fromParent());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* {@inheritDoc}
|
|
74
|
+
*/
|
|
75
|
+
public function resolveLangcode(): ResolverInterface {
|
|
76
|
+
return $this->builder->callback(
|
|
77
|
+
fn(StringInterface $value) => $value instanceof TranslationString ? $value->language : 'en'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* {@inheritDoc}
|
|
83
|
+
*/
|
|
84
|
+
public function resolveDefaultTranslation(): ResolverInterface {
|
|
85
|
+
return $this->builder->callback(
|
|
86
|
+
fn(StringInterface $value) => FALSE
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* {@inheritDoc}
|
|
92
|
+
*/
|
|
93
|
+
public function resolveTranslations(): ResolverInterface {
|
|
94
|
+
return $this->builder->produce('string_translations')
|
|
95
|
+
->map('sourceString', $this->builder->fromParent());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* {@inheritDoc}
|
|
100
|
+
*/
|
|
101
|
+
public function resolveItems(ResolverInterface $limit, ResolverInterface $offset): ResolverInterface {
|
|
102
|
+
return $this->builder->produce('list_strings')
|
|
103
|
+
->map('offset', $this->builder->defaultValue(
|
|
104
|
+
$this->builder->fromArgument('offset'),
|
|
105
|
+
$this->builder->fromValue(0)
|
|
106
|
+
))
|
|
107
|
+
->map('limit', $this->builder->defaultValue(
|
|
108
|
+
$this->builder->fromArgument('limit'),
|
|
109
|
+
$this->builder->fromValue(10),
|
|
110
|
+
))
|
|
111
|
+
// For now, the schema extension does not allow array arguments. However,
|
|
112
|
+
// the list strings resolver can do that, so we just convert the context
|
|
113
|
+
// prefix to an array here. By doing that, when we add support for array
|
|
114
|
+
// arguments in directives, we don't have to change this code.
|
|
115
|
+
->map('translationContext', $this->builder->fromValue(is_array($this->contextPrefix) ? $this->contextPrefix : [$this->contextPrefix]));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* {@inheritDoc}
|
|
120
|
+
*/
|
|
121
|
+
public function resolveItem(ResolverInterface $id, ?ResolverInterface $langcode = NULL): ResolverInterface {
|
|
122
|
+
$resolver = $this->builder->produce('fetch_translatable_string')
|
|
123
|
+
->map('id', $id)
|
|
124
|
+
->map('language', $langcode);
|
|
125
|
+
return $resolver;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Session\AccountProxyInterface;
|
|
6
|
+
use Drupal\graphql\GraphQL\Execution\FieldContext;
|
|
7
|
+
use Drupal\graphql\Plugin\GraphQL\DataProducer\User\CurrentUser;
|
|
8
|
+
use Drupal\user\Entity\User;
|
|
9
|
+
use Drupal\user\UserInterface;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @DataProducer(
|
|
13
|
+
* id = "current_user_entity",
|
|
14
|
+
* name = @Translation("Current User Entity"),
|
|
15
|
+
* description = @Translation("Returns the current authenticated user. Extends the core current_user, to return the User entity instead of the AccountProxy."),
|
|
16
|
+
* produces = @ContextDefinition("User",
|
|
17
|
+
* label = @Translation("User")
|
|
18
|
+
* )
|
|
19
|
+
* )
|
|
20
|
+
*/
|
|
21
|
+
class CurrentUserEntity extends CurrentUser {
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns the current user.
|
|
25
|
+
*
|
|
26
|
+
* @param \Drupal\graphql\GraphQL\Execution\FieldContext $field_context
|
|
27
|
+
* Field context.
|
|
28
|
+
*
|
|
29
|
+
* @return \Drupal\user\UserInterface
|
|
30
|
+
* The current user.
|
|
31
|
+
*/
|
|
32
|
+
public function resolve(FieldContext $field_context): UserInterface {
|
|
33
|
+
// Response must be cached based on current user as a cache context,
|
|
34
|
+
// otherwise a new user would become a previous user.
|
|
35
|
+
$field_context->addCacheableDependency($this->currentUser);
|
|
36
|
+
$user = NULL;
|
|
37
|
+
if ($this->currentUser instanceof AccountProxyInterface) {
|
|
38
|
+
$user = User::load($this->currentUser->id());
|
|
39
|
+
}
|
|
40
|
+
return $user;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
|
6
|
+
use Drupal\Core\Entity\Query\QueryInterface;
|
|
7
|
+
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
|
|
8
|
+
|
|
9
|
+
class EntityQueryBase extends DataProducerPluginBase {
|
|
10
|
+
|
|
11
|
+
protected function getQuery(string $type, RefinableCacheableDependencyInterface $metadata): QueryInterface {
|
|
12
|
+
$currentUser = \Drupal::currentUser();
|
|
13
|
+
$entityType = \Drupal::entityTypeManager()->getDefinition($type);
|
|
14
|
+
|
|
15
|
+
$query = \Drupal::entityQuery($type)
|
|
16
|
+
->currentRevision()
|
|
17
|
+
->accessCheck();
|
|
18
|
+
|
|
19
|
+
// Add access conditions. Because accessCheck() does nothing by default. For
|
|
20
|
+
// example it does not filter out unpublished nodes for anonymous users.
|
|
21
|
+
// Special permission for nodes.
|
|
22
|
+
if ($type === 'node' && !$currentUser->hasPermission('bypass node access')) {
|
|
23
|
+
$query->condition('status', 1);
|
|
24
|
+
}
|
|
25
|
+
// Admin permission for other entity types.
|
|
26
|
+
if (
|
|
27
|
+
($publishedKey = $entityType->getKey('published')) &&
|
|
28
|
+
($adminPermission = $entityType->getAdminPermission()) &&
|
|
29
|
+
!$currentUser->hasPermission($adminPermission)
|
|
30
|
+
) {
|
|
31
|
+
$query->condition($publishedKey, 1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
$metadata->addCacheTags($entityType->getListCacheTags());
|
|
35
|
+
$metadata->addCacheContexts($entityType->getListCacheContexts());
|
|
36
|
+
|
|
37
|
+
return $query;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|