@amazeelabs/silverback-gutenberg 2.6.2

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 (94) hide show
  1. package/CHANGELOG.md +983 -0
  2. package/drupal/silverback_gutenberg/README.md +439 -0
  3. package/drupal/silverback_gutenberg/composer.json +20 -0
  4. package/drupal/silverback_gutenberg/config/install/silverback_gutenberg.settings.yml +1 -0
  5. package/drupal/silverback_gutenberg/config/schema/silverback_gutenberg.schema.yml +13 -0
  6. package/drupal/silverback_gutenberg/css/gutenberg-tweaks.css +46 -0
  7. package/drupal/silverback_gutenberg/directives.gql +40 -0
  8. package/drupal/silverback_gutenberg/directives.graphql +46 -0
  9. package/drupal/silverback_gutenberg/js/base.js +24 -0
  10. package/drupal/silverback_gutenberg/js/gutenberg-tweaks.js +154 -0
  11. package/drupal/silverback_gutenberg/silverback_gutenberg.api.php +76 -0
  12. package/drupal/silverback_gutenberg/silverback_gutenberg.info.yml +8 -0
  13. package/drupal/silverback_gutenberg/silverback_gutenberg.libraries.yml +14 -0
  14. package/drupal/silverback_gutenberg/silverback_gutenberg.module +97 -0
  15. package/drupal/silverback_gutenberg/silverback_gutenberg.services.yml +29 -0
  16. package/drupal/silverback_gutenberg/src/Annotation/GutenbergBlockMutator.php +39 -0
  17. package/drupal/silverback_gutenberg/src/Annotation/GutenbergValidator.php +37 -0
  18. package/drupal/silverback_gutenberg/src/Annotation/GutenbergValidatorRule.php +37 -0
  19. package/drupal/silverback_gutenberg/src/Attribute/GutenbergBlockMutator.php +29 -0
  20. package/drupal/silverback_gutenberg/src/BlockMutator/BlockMutatorBase.php +24 -0
  21. package/drupal/silverback_gutenberg/src/BlockMutator/BlockMutatorInterface.php +41 -0
  22. package/drupal/silverback_gutenberg/src/BlockMutator/BlockMutatorManager.php +114 -0
  23. package/drupal/silverback_gutenberg/src/BlockMutator/BlockMutatorManagerInterface.php +30 -0
  24. package/drupal/silverback_gutenberg/src/BlockMutator/EntityBlockMutatorBase.php +189 -0
  25. package/drupal/silverback_gutenberg/src/BlockSerializer.php +84 -0
  26. package/drupal/silverback_gutenberg/src/Controller/LinkitAutocomplete.php +84 -0
  27. package/drupal/silverback_gutenberg/src/Directives.php +74 -0
  28. package/drupal/silverback_gutenberg/src/EditorBlocksProcessor.php +53 -0
  29. package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergCardinalityValidatorInterface.php +19 -0
  30. package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergCardinalityValidatorTrait.php +221 -0
  31. package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorBase.php +24 -0
  32. package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorInterface.php +65 -0
  33. package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorManager.php +37 -0
  34. package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorRuleInterface.php +20 -0
  35. package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorRuleManager.php +37 -0
  36. package/drupal/silverback_gutenberg/src/LinkProcessor.php +405 -0
  37. package/drupal/silverback_gutenberg/src/LinkedContentExtractor.php +35 -0
  38. package/drupal/silverback_gutenberg/src/Normalizer/GutenbergContentEntityNormalizer.php +123 -0
  39. package/drupal/silverback_gutenberg/src/Plugin/EntityUsage/Track/GutenbergContentTrackTrait.php +51 -0
  40. package/drupal/silverback_gutenberg/src/Plugin/EntityUsage/Track/GutenbergLinkedContent.php +96 -0
  41. package/drupal/silverback_gutenberg/src/Plugin/EntityUsage/Track/GutenbergMediaEmbed.php +63 -0
  42. package/drupal/silverback_gutenberg/src/Plugin/EntityUsage/Track/GutenbergReferencedContent.php +101 -0
  43. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockAttribute.php +42 -0
  44. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockChildren.php +32 -0
  45. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockHtml.php +30 -0
  46. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockMedia.php +159 -0
  47. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockType.php +29 -0
  48. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlocks.php +127 -0
  49. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockAttribute.php +29 -0
  50. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockChildren.php +21 -0
  51. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockMarkup.php +21 -0
  52. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockMedia.php +21 -0
  53. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockType.php +21 -0
  54. package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlocks.php +36 -0
  55. package/drupal/silverback_gutenberg/src/Plugin/GutenbergBlockMutator/MediaBlockMutator.php +30 -0
  56. package/drupal/silverback_gutenberg/src/Plugin/GutenbergBlockMutator/NodeBlockMutator.php +25 -0
  57. package/drupal/silverback_gutenberg/src/Plugin/GutenbergBlockMutator/TermReferenceBlockMutator.php +104 -0
  58. package/drupal/silverback_gutenberg/src/Plugin/Linkit/Matcher/SilverbackMatcherTrait.php +69 -0
  59. package/drupal/silverback_gutenberg/src/Plugin/Linkit/Matcher/SilverbackMediaMatcher.php +53 -0
  60. package/drupal/silverback_gutenberg/src/Plugin/Linkit/Matcher/SilverbackNodeMatcher.php +19 -0
  61. package/drupal/silverback_gutenberg/src/Plugin/Validation/Constraint/Gutenberg.php +15 -0
  62. package/drupal/silverback_gutenberg/src/Plugin/Validation/Constraint/GutenbergValidator.php +210 -0
  63. package/drupal/silverback_gutenberg/src/Plugin/Validation/GutenbergValidatorRule/Email.php +28 -0
  64. package/drupal/silverback_gutenberg/src/Plugin/Validation/GutenbergValidatorRule/Required.php +29 -0
  65. package/drupal/silverback_gutenberg/src/ReferencedContentExtractor.php +67 -0
  66. package/drupal/silverback_gutenberg/src/Routing/RouteSubscriber.php +17 -0
  67. package/drupal/silverback_gutenberg/src/Service/MediaService.php +27 -0
  68. package/drupal/silverback_gutenberg/src/SilverbackGutenbergServiceProvider.php +28 -0
  69. package/drupal/silverback_gutenberg/src/Utils.php +30 -0
  70. package/drupal/silverback_gutenberg/src/WebformMessageManager.php +29 -0
  71. package/drupal/silverback_gutenberg/tests/graphql/.graphqlrc.json +5 -0
  72. package/drupal/silverback_gutenberg/tests/graphql/queries/editor.gql +30 -0
  73. package/drupal/silverback_gutenberg/tests/graphql/schema.graphqls +37 -0
  74. package/drupal/silverback_gutenberg/tests/modules/silverback_gutenberg_test_validator/silverback_gutenberg_test_validator.info.yml +9 -0
  75. package/drupal/silverback_gutenberg/tests/modules/silverback_gutenberg_test_validator/src/Plugin/Validation/GutenbergValidator/ColumnValidator.php +42 -0
  76. package/drupal/silverback_gutenberg/tests/modules/silverback_gutenberg_test_validator/src/Plugin/Validation/GutenbergValidator/GroupValidator.php +50 -0
  77. package/drupal/silverback_gutenberg/tests/modules/silverback_gutenberg_test_validator/src/Plugin/Validation/GutenbergValidator/LinkValidator.php +43 -0
  78. package/drupal/silverback_gutenberg/tests/src/Kernel/BlockValidationRuleTest.php +194 -0
  79. package/drupal/silverback_gutenberg/tests/src/Kernel/EditorDirectivesTest.php +255 -0
  80. package/drupal/silverback_gutenberg/tests/src/Kernel/GutenbergLinkedContentEUTrackTest.php +133 -0
  81. package/drupal/silverback_gutenberg/tests/src/Kernel/GutenbergReferencedContentEUTrackTest.php +225 -0
  82. package/drupal/silverback_gutenberg/tests/src/Kernel/LinkProcessorTest.php +284 -0
  83. package/drupal/silverback_gutenberg/tests/src/Kernel/MediaNormalizerTest.php +174 -0
  84. package/drupal/silverback_gutenberg/tests/src/Traits/SampleAssetTrait.php +15 -0
  85. package/drupal/silverback_gutenberg/tests/src/Unit/BlockSerializerTest.php +27 -0
  86. package/drupal/silverback_gutenberg/tests/src/Unit/BlockValidatorCardinalityTest.php +1537 -0
  87. package/drupal/silverback_gutenberg/tests/src/Unit/EditorBlocksProcessorTest.php +159 -0
  88. package/drupal/silverback_gutenberg/tests/src/Unit/LinkedContentExtractorTest.php +65 -0
  89. package/drupal/silverback_gutenberg/tests/src/Unit/ReferencedContentExtractorTest.php +248 -0
  90. package/drupal/silverback_gutenberg/tests/src/assets/media/data.json +4 -0
  91. package/drupal/silverback_gutenberg/tests/src/assets/media/source.html +71 -0
  92. package/drupal/silverback_gutenberg/tests/src/assets/media/target.html +71 -0
  93. package/package.json +16 -0
  94. package/turbo.json +15 -0
@@ -0,0 +1,439 @@
1
+ # Silverback Gutenberg
2
+
3
+ Helps integrating Drupal's
4
+ [Gutenberg module](https://www.drupal.org/project/gutenberg) into Silverback
5
+ projects.
6
+
7
+ ## GraphQL directives
8
+
9
+ This module provides a set of GraphQL directives that are picked up by the
10
+ `amazeelabs/graphql_directives` module. This allows to easily expose Gutenberg
11
+ blocks through a GraphQL schema.
12
+
13
+ ### `@resolveEdtiorBlocks`
14
+
15
+ Parse the raw output of a field at a given path and expose its content as
16
+ structured block data. Allows to define `aggregated` and `ignored` blocks:
17
+
18
+ - `aggregated`: All subsequent blocks of these types will be merged into one
19
+ `core/paragraph` block. In Gutenberg, standard HTML elements like lists,
20
+ headings or tables are represented as separate blocks. This directive allows
21
+ to merge them into one and simplify handling in the frontend.
22
+ - `ignored`: Blocks of these types will be ignored. This is useful for blocks
23
+ that are not relevant for the frontend, like the `core/group` block. The block
24
+ will simply not part of the result and any children are spread where the block
25
+ was.
26
+
27
+ ```graphql
28
+ type Page {
29
+ title: String! @resolveProperty(path: "title.value")
30
+ content: [Blocks!]!
31
+ @resolveEditorBlocks(
32
+ path: "body.value"
33
+ aggregated: ["core/paragraph", "core/list"]
34
+ ignored: ["core/group"]
35
+ )
36
+ }
37
+ ```
38
+
39
+ ### `@resolveEditorBlockType`
40
+
41
+ Retrieve the type of gutenberg block. Useful for resolving types of a block
42
+ union.
43
+
44
+ ```graphql
45
+ union Blocks @resolveEditorBlockType = Paragraph | Heading | List
46
+ ```
47
+
48
+ ### `@resolveEditorBlockMarkup`
49
+
50
+ Extract inner markup of a block that was provided by the user via rich HTML.
51
+
52
+ ```graphql
53
+ type Text @type(id: "core/paragraph") {
54
+ content: String @resolveEditorBlockMarkup
55
+ }
56
+ ```
57
+
58
+ ### `@resolveEditorBlockAttribute`
59
+
60
+ Retrieve a specific attribute, stored in a block.
61
+
62
+ ```graphql
63
+ type Figure @type(id: "custom/figure") {
64
+ caption: String @resolveEditorBlockAttribute(key: "caption")
65
+ }
66
+ ```
67
+
68
+ ### `@resolveEditorBlockMedia`
69
+
70
+ Resolve a media entity referenced in a block.
71
+
72
+ ```graphql
73
+ type Figure @type(id: "custom/figure") {
74
+ image: Image @resolveEditorBlockMedia
75
+ }
76
+ ```
77
+
78
+ ### `@resolveEditorBlockChildren`
79
+
80
+ Extract all child blocks of a given block.
81
+
82
+ ```graphql
83
+ type Columns @type(id: "custom/columns") {
84
+ columns: [ColumnBlocks!]! @resolveEditorBlockChildren
85
+ }
86
+ ```
87
+
88
+ ## LinkProcessor
89
+
90
+ The main idea is that all links added to a Gutenberg page are
91
+
92
+ - kept in internal format (e.g. `/node/123`) when saved to Drupal database
93
+ - processed to language-prefixed aliased form (e.g. `/en/my-page`) when
94
+ - they are displayed in Gutenberg editor
95
+ - they are sent out via GraphQL
96
+
97
+ This helps to
98
+
99
+ - always display fresh path aliases
100
+ - be sure that the language prefix is correct
101
+ - update link URLs when translating content (e.g. `/en/my-page` will become
102
+ `/fr/ma-page` automatically because it's `/node/123` under the hood)
103
+ - keep track of entity usage (TBD)
104
+
105
+ ### Implementation
106
+
107
+ The module does most of the things automatically. Yet there are few things
108
+ developers should take care of.
109
+
110
+ First, custom Gutenberg blocks which store links in block attributes should
111
+ implement `hook_silverback_gutenberg_link_processor_block_attrs_alter`. See
112
+ [`silverback_gutenberg.api.php`](./silverback_gutenberg.api.php) for an example.
113
+
114
+ Next, GraphQL resolvers which parse Gutenberg code should call
115
+ `LinkProcessor::processLinks` before parsing the blocks. See
116
+ [`DataProducer/Gutenberg.php`](../../../../apps/silverback-drupal/web/modules/custom/silverback_gatsby_test/src/Plugin/GraphQL/DataProducer/Gutenberg.php)
117
+ for an example.
118
+
119
+ ## Validation
120
+
121
+ Custom validator plugins can be created in
122
+ `src/Plugin/Validation/GutenbergValidator`
123
+
124
+ ### Field level validation
125
+
126
+ Example: to validate that an email is valid and required.
127
+
128
+ - the block name is `custom/my-block`
129
+ - the field attribute is `email` and the label `Email`
130
+
131
+ ```php
132
+ <?php
133
+
134
+ namespace Drupal\custom_gutenberg\Plugin\Validation\GutenbergValidator;
135
+
136
+ use Drupal\silverback_gutenberg\GutenbergValidation\GutenbergValidatorBase;
137
+ use Drupal\Core\StringTranslation\StringTranslationTrait;
138
+
139
+ /**
140
+ * @GutenbergValidator(
141
+ * id="my_block_validator",
142
+ * label = @Translation("My block validator")
143
+ * )
144
+ */
145
+ class MyBlockValidator extends GutenbergValidatorBase {
146
+
147
+ use StringTranslationTrait;
148
+
149
+ /**
150
+ * {@inheritDoc}
151
+ */
152
+ public function applies(array $block) {
153
+ return $block['blockName'] === 'custom/my-block';
154
+ }
155
+
156
+ /**
157
+ * {@inheritDoc}
158
+ */
159
+ public function validatedFields(array $block = []) {
160
+ return [
161
+ 'email' => [
162
+ 'field_label' => $this->t('Email'),
163
+ 'rules' => ['required', 'email'],
164
+ ],
165
+ ];
166
+ }
167
+
168
+ }
169
+ ```
170
+
171
+ ### Block level validation
172
+
173
+ Perform custom block validation logic then return the result.
174
+
175
+ ```php
176
+ public function validateContent(array $block) {
177
+ $isValid = TRUE;
178
+
179
+ // Custom validation logic.
180
+ // (...)
181
+
182
+ if (!$isValid) {
183
+ return [
184
+ 'is_valid' => FALSE,
185
+ 'message' => 'Message',
186
+ ];
187
+ }
188
+
189
+ // Passes validation.
190
+ return [
191
+ 'is_valid' => TRUE,
192
+ 'message' => '',
193
+ ];
194
+ }
195
+ ```
196
+
197
+ ### Cardinality validation
198
+
199
+ #### Backend
200
+
201
+ Uses the `validateContent()` method as a wrapper, with the cardinality validator
202
+ trait.
203
+
204
+ ```php
205
+ use GutenbergCardinalityValidatorTrait;
206
+ ```
207
+
208
+ Validate a given block type for inner blocks.
209
+
210
+ ```php
211
+ public function validateContent(array $block) {
212
+ $expectedChildren = [
213
+ [
214
+ 'blockName' => 'custom/teaser',
215
+ 'blockLabel' => $this->t('Teaser'),
216
+ 'min' => 1,
217
+ 'max' => 2,
218
+ ],
219
+ ];
220
+ return $this->validateCardinality($block, $expectedChildren);
221
+ }
222
+ ```
223
+
224
+ Validate any kind of block type for inner blocks.
225
+
226
+ ```php
227
+ public function validateContent(array $block) {
228
+ $expectedChildren = [
229
+ 'validationType' => GutenbergCardinalityValidatorInterface::CARDINALITY_ANY,
230
+ 'min' => 0,
231
+ 'max' => 1,
232
+ ];
233
+ return $this->validateCardinality($block, $expectedChildren);
234
+ }
235
+ ```
236
+
237
+ Validate a minimum with no maximum.
238
+
239
+ ```php
240
+ public function validateContent(array $block) {
241
+ $expectedChildren = [
242
+ [
243
+ 'blockName' => 'custom/teaser',
244
+ 'blockLabel' => $this->t('Teaser'),
245
+ 'min' => 1,
246
+ 'max' => GutenbergCardinalityValidatorInterface::CARDINALITY_UNLIMITED,
247
+ ],
248
+ ];
249
+ return $this->validateCardinality($block, $expectedChildren);
250
+ }
251
+ ```
252
+
253
+ #### Client side alternative
254
+
255
+ Client side cardinality validation can also be done in custom blocks with this
256
+ pattern.
257
+
258
+ - use `getBlockCount`
259
+ - remove the `InnerBlocks` appender when the limit is reached
260
+
261
+ ```tsx
262
+ /* global Drupal */
263
+ import { registerBlockType } from 'wordpress__blocks';
264
+ import { InnerBlocks } from 'wordpress__block-editor';
265
+ import { useSelect } from 'wordpress__data';
266
+
267
+ // @ts-ignore
268
+ const __ = Drupal.t;
269
+
270
+ const MAX_BLOCKS: number = 1;
271
+
272
+ registerBlockType('custom/my-block', {
273
+ title: __('My Block'),
274
+ icon: 'location',
275
+ category: 'layout',
276
+ attributes: {},
277
+ edit: (props) => {
278
+ const { blockCount } = useSelect((select) => ({
279
+ blockCount: select('core/block-editor').getBlockCount(props.clientId),
280
+ }));
281
+ return (
282
+ <div>
283
+ <InnerBlocks
284
+ templateLock={false}
285
+ renderAppender={() => {
286
+ if (blockCount >= MAX_BLOCKS) {
287
+ return null;
288
+ } else {
289
+ return <InnerBlocks.ButtonBlockAppender />;
290
+ }
291
+ }}
292
+ allowedBlocks={['core/block']}
293
+ template={[]}
294
+ />
295
+ </div>
296
+ );
297
+ },
298
+ save: () => {
299
+ return <InnerBlocks.Content />;
300
+ },
301
+ });
302
+ ```
303
+
304
+ ## Linkit integration
305
+
306
+ To enable the integration:
307
+
308
+ - Enable the linkit module and create a linkit profile with `gutenberg` machine
309
+ name
310
+ <details>
311
+ <summary>This brings</summary>
312
+
313
+ - Basic linkit integration
314
+ - Improved suggestion labels (e.g. `Content: Page`, `Media: PDF` instead of
315
+ `page`, `pdf`)
316
+
317
+ </details>
318
+
319
+ - Add `Silverback:` prefixed matchers to the profile
320
+ <details> <summary>How they differ from the default linkit matchers</summary>
321
+
322
+ - Suggestions order is done by the position of the search string in the label.
323
+ For example, if you search for "best", the order will be:
324
+ - _Best_ in class
325
+ - The _best_ choice
326
+ - Always choose _best_
327
+ - Improved display of translated content. By default, linkit searches through
328
+ all content translations but displays suggestions in the current language.
329
+ Which can be confusing. The Silverback matchers changes this a bit. If the
330
+ displayed item does not contain the prompt, a translation containing the
331
+ prompt will be added in the brackets. For example, if you search for "gift"
332
+ with the English UI, the suggestions will look like this:
333
+
334
+ - _Gift_ for a friend
335
+ - Poison for an enemy (_Gift_ für einen Feind)
336
+ </details>
337
+
338
+ - To use a different profile when using the LinkControl component, add the
339
+ machine name of the profile to the `subtype` query parameter in the
340
+ component prop `suggestionsQuery` like below, where the custom linkit
341
+ profile is called `customer`.
342
+
343
+ ```
344
+ <DrupalLinkControl
345
+ searchInputPlaceholder={__('Target page')}
346
+ value={{
347
+ url: props.attributes.linkUrl,
348
+ }}
349
+ settings={[]}
350
+ suggestionsQuery={{
351
+ // Use the custom linkit profile called customer.
352
+ subtype: 'customer',
353
+ }}
354
+ onChange={(link) => {
355
+ props.setAttributes({
356
+ linkUrl: link.url,
357
+ });
358
+ }}
359
+ />
360
+ ```
361
+
362
+ ## Gutenberg block mutator
363
+
364
+ Entity id references can be used as Gutenberg attributes.
365
+
366
+ When using default content, we need to map the entity id with the uuid:
367
+
368
+ - id to uuid on export
369
+ - uuid to id on import
370
+
371
+ This is especially useful for schema tests, when using entity reference.
372
+
373
+ To facilitate this process, block mutator plugins can be used, the easiest way
374
+ is to extend the `EntityBlockMutatorBase` base class.
375
+
376
+ Example, with multi-valued Gutenberg attribute
377
+
378
+ ```php
379
+ <?php
380
+
381
+ namespace Drupal\silverback_gutenberg\Plugin\GutenbergBlockMutator;
382
+
383
+ use Drupal\silverback_gutenberg\Attribute\GutenbergBlockMutator;
384
+ use Drupal\silverback_gutenberg\BlockMutator\EntityBlockMutatorBase;
385
+ use Drupal\Core\StringTranslation\TranslatableMarkup;
386
+
387
+ #[GutenbergBlockMutator(
388
+ id: "media_block_mutator",
389
+ label: new TranslatableMarkup("Media IDs to UUIDs and viceversa."),
390
+ )]
391
+ class MediaBlockMutator extends EntityBlockMutatorBase {
392
+
393
+ /**
394
+ * {@inheritDoc}
395
+ */
396
+ public bool $isMultiple = TRUE;
397
+
398
+ /**
399
+ * {@inheritDoc}
400
+ */
401
+ public string $gutenbergAttribute = 'mediaEntityIds';
402
+
403
+ /**
404
+ * {@inheritDoc}
405
+ */
406
+ public string $entityTypeId = 'media';
407
+
408
+ }
409
+ ```
410
+
411
+ Example, with single-valued Gutenberg attribute
412
+
413
+ ```php
414
+ <?php
415
+
416
+ namespace Drupal\silverback_gutenberg\Plugin\GutenbergBlockMutator;
417
+
418
+ use Drupal\silverback_gutenberg\Attribute\GutenbergBlockMutator;
419
+ use Drupal\silverback_gutenberg\BlockMutator\EntityBlockMutatorBase;
420
+ use Drupal\Core\StringTranslation\TranslatableMarkup;
421
+
422
+ #[GutenbergBlockMutator(
423
+ id: "node_block_mutator",
424
+ label: new TranslatableMarkup("Node ID to UUID and viceversa."),
425
+ )]
426
+ class NodeBlockMutator extends EntityBlockMutatorBase {
427
+
428
+ /**
429
+ * {@inheritDoc}
430
+ */
431
+ public string $gutenbergAttribute = 'nodeId';
432
+
433
+ /**
434
+ * {@inheritDoc}
435
+ */
436
+ public string $entityTypeId = 'node';
437
+
438
+ }
439
+ ```
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "amazeelabs/silverback_gutenberg",
3
+ "type": "drupal-module",
4
+ "version": "2.6.1",
5
+ "description": "Adjusts Gutenberg for Amazee Labs needs.",
6
+ "homepage": "https://github.com/AmazeeLabs/silverback-mono/tree/development/packages/composer/amazeelabs/silverback_gutenberg#readme",
7
+ "license": "GPL-2.0+",
8
+ "repositories": [
9
+ {
10
+ "type": "composer",
11
+ "url": "https://packages.drupal.org/8"
12
+ }
13
+ ],
14
+ "require": {
15
+ "drupal/gutenberg": "^2.14.0"
16
+ },
17
+ "conflict": {
18
+ "drupal/linkit": "<7.0.0"
19
+ }
20
+ }
@@ -0,0 +1,13 @@
1
+ silverback_gutenberg.settings:
2
+ type: config_object
3
+ label: 'Silverback Gutenberg settings'
4
+ mapping:
5
+ local_hosts:
6
+ type: sequence
7
+ nullable: true
8
+ label:
9
+ 'URLs with these hosts will be turned to relative. If null, any absolute
10
+ URLs will NOT be processed (BC behavior).'
11
+ sequence:
12
+ type: string
13
+ label: 'Host'
@@ -0,0 +1,46 @@
1
+ /* There is no other way to disable the columns amount slider. */
2
+ .components-range-control {
3
+ display: none;
4
+ }
5
+
6
+ /* Hide the Upload button as it skips the media edit form (which may cause
7
+ required fields to be left blank). Users still can upload new files via the
8
+ media dialog. */
9
+ .block-editor-media-placeholder .components-form-file-upload {
10
+ display: none;
11
+ }
12
+
13
+ /* Hide the "Welcome to Gutenberg" overlay, since it is annoying and messes
14
+ with cypress tests.*/
15
+ .components-modal__screen-overlay {
16
+ display: none !important;
17
+ }
18
+
19
+ /* Make the link suggestions box wider (for real world page titles) */
20
+ .components-popover__content div.block-editor-link-control {
21
+ width: 600px;
22
+ max-width: 600px;
23
+ }
24
+
25
+ /* Do not trim the link suggestions. */
26
+ .block-editor-link-control__search-item
27
+ span.block-editor-link-control__search-item-title {
28
+ white-space: normal;
29
+ }
30
+
31
+ /* Avoid word wrap in the type label. */
32
+ .block-editor-link-control__search-item
33
+ .block-editor-link-control__search-item-type {
34
+ white-space: pre;
35
+ }
36
+
37
+ /* Validator */
38
+ .block-editor-block-list__layout .not-valid {
39
+ filter: drop-shadow(2px 2px 5px #cc1818);
40
+ background: white;
41
+ }
42
+
43
+ /* Hide the preview button. */
44
+ .block-editor-post-preview__dropdown {
45
+ display: none;
46
+ }
@@ -0,0 +1,40 @@
1
+ """
2
+ implementation(drupal): \Drupal\silverback_gutenberg\Directives::editorBlockChildren
3
+ """
4
+ directive @resolveEditorBlockChildren repeatable on FIELD_DEFINITION
5
+
6
+ """
7
+ implementation(drupal): \Drupal\silverback_gutenberg\Directives::editorBlockMarkup
8
+ """
9
+ directive @resolveEditorBlockMarkup repeatable on FIELD_DEFINITION
10
+
11
+ """
12
+ implementation(drupal): \Drupal\silverback_gutenberg\Directives::editorBlockMedia
13
+ """
14
+ directive @resolveEditorBlockMedia repeatable on FIELD_DEFINITION
15
+
16
+ """
17
+ implementation(drupal): \Drupal\silverback_gutenberg\Directives::editorBlockType
18
+ """
19
+ directive @resolveEditorBlockType repeatable on FIELD_DEFINITION | UNION | ENUM | INTERFACE
20
+
21
+ """
22
+ Parse a gutenberg document into block data.
23
+
24
+ implementation(drupal): \Drupal\silverback_gutenberg\Directives::editorBlocks
25
+ """
26
+ directive @resolveEditorBlocks(
27
+ path: String!
28
+ ignored: [String!]
29
+ aggregated: [String!]
30
+ ) repeatable on FIELD_DEFINITION
31
+
32
+ """
33
+ Retrieve an editor block attribute.
34
+
35
+ implementation(drupal): \Drupal\silverback_gutenberg\Directives::editorBlockAttribute
36
+ """
37
+ directive @resolveEditorBlockAttribute(
38
+ key: String!
39
+ plainText: Boolean
40
+ ) repeatable on FIELD_DEFINITION | UNION | INTERFACE
@@ -0,0 +1,46 @@
1
+ """
2
+ Provided by the "silverback_gutenberg" module.
3
+ Implemented in "Drupal\silverback_gutenberg\Plugin\GraphQL\Directive\EditorBlockChildren".
4
+ """
5
+ directive @resolveEditorBlockChildren repeatable on FIELD_DEFINITION | SCALAR | UNION | ENUM | INTERFACE | OBJECT
6
+
7
+ """
8
+ Provided by the "silverback_gutenberg" module.
9
+ Implemented in "Drupal\silverback_gutenberg\Plugin\GraphQL\Directive\EditorBlockMarkup".
10
+ """
11
+ directive @resolveEditorBlockMarkup repeatable on FIELD_DEFINITION | SCALAR | UNION | ENUM | INTERFACE | OBJECT
12
+
13
+ """
14
+ Provided by the "silverback_gutenberg" module.
15
+ Implemented in "Drupal\silverback_gutenberg\Plugin\GraphQL\Directive\EditorBlockMedia".
16
+ """
17
+ directive @resolveEditorBlockMedia repeatable on FIELD_DEFINITION | SCALAR | UNION | ENUM | INTERFACE | OBJECT
18
+
19
+ """
20
+ Provided by the "silverback_gutenberg" module.
21
+ Implemented in "Drupal\silverback_gutenberg\Plugin\GraphQL\Directive\EditorBlockType".
22
+ """
23
+ directive @resolveEditorBlockType repeatable on FIELD_DEFINITION | SCALAR | UNION | ENUM | INTERFACE | OBJECT
24
+
25
+ """
26
+ Parse a gutenberg document into block data.
27
+
28
+ Provided by the "silverback_gutenberg" module.
29
+ Implemented in "Drupal\silverback_gutenberg\Plugin\GraphQL\Directive\EditorBlocks".
30
+ """
31
+ directive @resolveEditorBlocks(
32
+ path: String!
33
+ ignored: [String!]
34
+ aggregated: [String!]
35
+ ) repeatable on FIELD_DEFINITION | SCALAR | UNION | ENUM | INTERFACE | OBJECT
36
+
37
+ """
38
+ Retrieve an editor block attribute.
39
+
40
+ Provided by the "silverback_gutenberg" module.
41
+ Implemented in "Drupal\silverback_gutenberg\Plugin\GraphQL\Directive\EditorBlockAttribute".
42
+ """
43
+ directive @resolveEditorBlockAttribute(
44
+ key: String!
45
+ plainText: Boolean
46
+ ) repeatable on FIELD_DEFINITION | SCALAR | UNION | ENUM | INTERFACE | OBJECT
@@ -0,0 +1,24 @@
1
+ var silverbackGutenbergUtils = {
2
+ sanitizeText: function (text) {
3
+ return (
4
+ text
5
+ // When copying text from Word, HTML comments are escaped. So we get this:
6
+ // ...<br>&lt;!-- /* Font Definitions */ @font-face {...} --&gt;<br>...
7
+ // Unescape them back.
8
+ .replace('&lt;!--', '<!--')
9
+ .replace('--&gt;', '-->')
10
+ // Now clean all HTML tags.
11
+ .replace(/(<([^>]+)>)/gi, '')
12
+ );
13
+ },
14
+
15
+ setPlainTextAttribute: function (props, name, text) {
16
+ const sanitizedText = silverbackGutenbergUtils.sanitizeText(text);
17
+ props.setAttributes({
18
+ [name]: sanitizedText,
19
+ });
20
+ if (text !== sanitizedText) {
21
+ props.setState({ rerender: Date.now() });
22
+ }
23
+ },
24
+ };