@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.
- package/CHANGELOG.md +983 -0
- package/drupal/silverback_gutenberg/README.md +439 -0
- package/drupal/silverback_gutenberg/composer.json +20 -0
- package/drupal/silverback_gutenberg/config/install/silverback_gutenberg.settings.yml +1 -0
- package/drupal/silverback_gutenberg/config/schema/silverback_gutenberg.schema.yml +13 -0
- package/drupal/silverback_gutenberg/css/gutenberg-tweaks.css +46 -0
- package/drupal/silverback_gutenberg/directives.gql +40 -0
- package/drupal/silverback_gutenberg/directives.graphql +46 -0
- package/drupal/silverback_gutenberg/js/base.js +24 -0
- package/drupal/silverback_gutenberg/js/gutenberg-tweaks.js +154 -0
- package/drupal/silverback_gutenberg/silverback_gutenberg.api.php +76 -0
- package/drupal/silverback_gutenberg/silverback_gutenberg.info.yml +8 -0
- package/drupal/silverback_gutenberg/silverback_gutenberg.libraries.yml +14 -0
- package/drupal/silverback_gutenberg/silverback_gutenberg.module +97 -0
- package/drupal/silverback_gutenberg/silverback_gutenberg.services.yml +29 -0
- package/drupal/silverback_gutenberg/src/Annotation/GutenbergBlockMutator.php +39 -0
- package/drupal/silverback_gutenberg/src/Annotation/GutenbergValidator.php +37 -0
- package/drupal/silverback_gutenberg/src/Annotation/GutenbergValidatorRule.php +37 -0
- package/drupal/silverback_gutenberg/src/Attribute/GutenbergBlockMutator.php +29 -0
- package/drupal/silverback_gutenberg/src/BlockMutator/BlockMutatorBase.php +24 -0
- package/drupal/silverback_gutenberg/src/BlockMutator/BlockMutatorInterface.php +41 -0
- package/drupal/silverback_gutenberg/src/BlockMutator/BlockMutatorManager.php +114 -0
- package/drupal/silverback_gutenberg/src/BlockMutator/BlockMutatorManagerInterface.php +30 -0
- package/drupal/silverback_gutenberg/src/BlockMutator/EntityBlockMutatorBase.php +189 -0
- package/drupal/silverback_gutenberg/src/BlockSerializer.php +84 -0
- package/drupal/silverback_gutenberg/src/Controller/LinkitAutocomplete.php +84 -0
- package/drupal/silverback_gutenberg/src/Directives.php +74 -0
- package/drupal/silverback_gutenberg/src/EditorBlocksProcessor.php +53 -0
- package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergCardinalityValidatorInterface.php +19 -0
- package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergCardinalityValidatorTrait.php +221 -0
- package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorBase.php +24 -0
- package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorInterface.php +65 -0
- package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorManager.php +37 -0
- package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorRuleInterface.php +20 -0
- package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergValidatorRuleManager.php +37 -0
- package/drupal/silverback_gutenberg/src/LinkProcessor.php +405 -0
- package/drupal/silverback_gutenberg/src/LinkedContentExtractor.php +35 -0
- package/drupal/silverback_gutenberg/src/Normalizer/GutenbergContentEntityNormalizer.php +123 -0
- package/drupal/silverback_gutenberg/src/Plugin/EntityUsage/Track/GutenbergContentTrackTrait.php +51 -0
- package/drupal/silverback_gutenberg/src/Plugin/EntityUsage/Track/GutenbergLinkedContent.php +96 -0
- package/drupal/silverback_gutenberg/src/Plugin/EntityUsage/Track/GutenbergMediaEmbed.php +63 -0
- package/drupal/silverback_gutenberg/src/Plugin/EntityUsage/Track/GutenbergReferencedContent.php +101 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockAttribute.php +42 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockChildren.php +32 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockHtml.php +30 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockMedia.php +159 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlockType.php +29 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/DataProducer/EditorBlocks.php +127 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockAttribute.php +29 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockChildren.php +21 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockMarkup.php +21 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockMedia.php +21 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlockType.php +21 -0
- package/drupal/silverback_gutenberg/src/Plugin/GraphQL/Directive/EditorBlocks.php +36 -0
- package/drupal/silverback_gutenberg/src/Plugin/GutenbergBlockMutator/MediaBlockMutator.php +30 -0
- package/drupal/silverback_gutenberg/src/Plugin/GutenbergBlockMutator/NodeBlockMutator.php +25 -0
- package/drupal/silverback_gutenberg/src/Plugin/GutenbergBlockMutator/TermReferenceBlockMutator.php +104 -0
- package/drupal/silverback_gutenberg/src/Plugin/Linkit/Matcher/SilverbackMatcherTrait.php +69 -0
- package/drupal/silverback_gutenberg/src/Plugin/Linkit/Matcher/SilverbackMediaMatcher.php +53 -0
- package/drupal/silverback_gutenberg/src/Plugin/Linkit/Matcher/SilverbackNodeMatcher.php +19 -0
- package/drupal/silverback_gutenberg/src/Plugin/Validation/Constraint/Gutenberg.php +15 -0
- package/drupal/silverback_gutenberg/src/Plugin/Validation/Constraint/GutenbergValidator.php +210 -0
- package/drupal/silverback_gutenberg/src/Plugin/Validation/GutenbergValidatorRule/Email.php +28 -0
- package/drupal/silverback_gutenberg/src/Plugin/Validation/GutenbergValidatorRule/Required.php +29 -0
- package/drupal/silverback_gutenberg/src/ReferencedContentExtractor.php +67 -0
- package/drupal/silverback_gutenberg/src/Routing/RouteSubscriber.php +17 -0
- package/drupal/silverback_gutenberg/src/Service/MediaService.php +27 -0
- package/drupal/silverback_gutenberg/src/SilverbackGutenbergServiceProvider.php +28 -0
- package/drupal/silverback_gutenberg/src/Utils.php +30 -0
- package/drupal/silverback_gutenberg/src/WebformMessageManager.php +29 -0
- package/drupal/silverback_gutenberg/tests/graphql/.graphqlrc.json +5 -0
- package/drupal/silverback_gutenberg/tests/graphql/queries/editor.gql +30 -0
- package/drupal/silverback_gutenberg/tests/graphql/schema.graphqls +37 -0
- package/drupal/silverback_gutenberg/tests/modules/silverback_gutenberg_test_validator/silverback_gutenberg_test_validator.info.yml +9 -0
- package/drupal/silverback_gutenberg/tests/modules/silverback_gutenberg_test_validator/src/Plugin/Validation/GutenbergValidator/ColumnValidator.php +42 -0
- package/drupal/silverback_gutenberg/tests/modules/silverback_gutenberg_test_validator/src/Plugin/Validation/GutenbergValidator/GroupValidator.php +50 -0
- package/drupal/silverback_gutenberg/tests/modules/silverback_gutenberg_test_validator/src/Plugin/Validation/GutenbergValidator/LinkValidator.php +43 -0
- package/drupal/silverback_gutenberg/tests/src/Kernel/BlockValidationRuleTest.php +194 -0
- package/drupal/silverback_gutenberg/tests/src/Kernel/EditorDirectivesTest.php +255 -0
- package/drupal/silverback_gutenberg/tests/src/Kernel/GutenbergLinkedContentEUTrackTest.php +133 -0
- package/drupal/silverback_gutenberg/tests/src/Kernel/GutenbergReferencedContentEUTrackTest.php +225 -0
- package/drupal/silverback_gutenberg/tests/src/Kernel/LinkProcessorTest.php +284 -0
- package/drupal/silverback_gutenberg/tests/src/Kernel/MediaNormalizerTest.php +174 -0
- package/drupal/silverback_gutenberg/tests/src/Traits/SampleAssetTrait.php +15 -0
- package/drupal/silverback_gutenberg/tests/src/Unit/BlockSerializerTest.php +27 -0
- package/drupal/silverback_gutenberg/tests/src/Unit/BlockValidatorCardinalityTest.php +1537 -0
- package/drupal/silverback_gutenberg/tests/src/Unit/EditorBlocksProcessorTest.php +159 -0
- package/drupal/silverback_gutenberg/tests/src/Unit/LinkedContentExtractorTest.php +65 -0
- package/drupal/silverback_gutenberg/tests/src/Unit/ReferencedContentExtractorTest.php +248 -0
- package/drupal/silverback_gutenberg/tests/src/assets/media/data.json +4 -0
- package/drupal/silverback_gutenberg/tests/src/assets/media/source.html +71 -0
- package/drupal/silverback_gutenberg/tests/src/assets/media/target.html +71 -0
- package/package.json +16 -0
- package/turbo.json +15 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gutenberg\Plugin\EntityUsage\Track;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Config\ConfigFactoryInterface;
|
|
6
|
+
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
|
7
|
+
use Drupal\Core\Entity\EntityRepositoryInterface;
|
|
8
|
+
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
|
9
|
+
use Drupal\Core\Field\FieldItemInterface;
|
|
10
|
+
use Drupal\entity_usage\EntityUsageInterface;
|
|
11
|
+
use Drupal\entity_usage\EntityUsageTrackBase;
|
|
12
|
+
use Drupal\entity_usage\UrlToEntityInterface;
|
|
13
|
+
use Drupal\silverback_gutenberg\LinkedContentExtractor;
|
|
14
|
+
use Symfony\Component\DependencyInjection\ContainerInterface;
|
|
15
|
+
use Psr\Log\LoggerInterface;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Tracks usage of linked content in Gutenberg editor.
|
|
19
|
+
*
|
|
20
|
+
* @EntityUsageTrack(
|
|
21
|
+
* id = "gutenberg_linked_content",
|
|
22
|
+
* label = @Translation("Linked content in Gutenberg"),
|
|
23
|
+
* description = @Translation("Tracks linked content entities in Gutenberg."),
|
|
24
|
+
* field_types = {"text", "text_long", "text_with_summary"},
|
|
25
|
+
* )
|
|
26
|
+
*/
|
|
27
|
+
class GutenbergLinkedContent extends EntityUsageTrackBase {
|
|
28
|
+
use GutenbergContentTrackTrait;
|
|
29
|
+
|
|
30
|
+
/* @var \Drupal\silverback_gutenberg\LinkedContentExtractor */
|
|
31
|
+
protected $linkedContentExtractor;
|
|
32
|
+
|
|
33
|
+
public function __construct(
|
|
34
|
+
array $configuration,
|
|
35
|
+
$plugin_id,
|
|
36
|
+
$plugin_definition,
|
|
37
|
+
EntityUsageInterface $usage_service,
|
|
38
|
+
EntityTypeManagerInterface $entity_type_manager,
|
|
39
|
+
EntityFieldManagerInterface $entity_field_manager,
|
|
40
|
+
ConfigFactoryInterface $config_factory,
|
|
41
|
+
EntityRepositoryInterface $entity_repository,
|
|
42
|
+
?LoggerInterface $entityUsageLogger = NULL,
|
|
43
|
+
?UrlToEntityInterface $urlToEntity = NULL,
|
|
44
|
+
?array $always_track_base_fields = NULL,
|
|
45
|
+
?LinkedContentExtractor $linked_content_extractor = NULL,
|
|
46
|
+
) {
|
|
47
|
+
parent::__construct(
|
|
48
|
+
$configuration, $plugin_id,
|
|
49
|
+
$plugin_definition,
|
|
50
|
+
$usage_service,
|
|
51
|
+
$entity_type_manager,
|
|
52
|
+
$entity_field_manager,
|
|
53
|
+
$config_factory,
|
|
54
|
+
$entity_repository,
|
|
55
|
+
$entityUsageLogger,
|
|
56
|
+
$urlToEntity,
|
|
57
|
+
$always_track_base_fields
|
|
58
|
+
);
|
|
59
|
+
$this->linkedContentExtractor = $linked_content_extractor;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* {@inheritDoc}
|
|
64
|
+
*/
|
|
65
|
+
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
|
66
|
+
return new static(
|
|
67
|
+
$configuration,
|
|
68
|
+
$plugin_id,
|
|
69
|
+
$plugin_definition,
|
|
70
|
+
$container->get('entity_usage.usage'),
|
|
71
|
+
$container->get('entity_type.manager'),
|
|
72
|
+
$container->get('entity_field.manager'),
|
|
73
|
+
$container->get('config.factory'),
|
|
74
|
+
$container->get('entity.repository'),
|
|
75
|
+
$container->get('logger.channel.entity_usage'),
|
|
76
|
+
$container->get(UrlToEntityInterface::class),
|
|
77
|
+
$container->getParameter('entity_usage')['always_track_base_fields'] ?? [],
|
|
78
|
+
$container->get('silverback_gutenberg.linked_content_extractor')
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* {@inheritDoc}
|
|
84
|
+
*/
|
|
85
|
+
public function getTargetEntities(FieldItemInterface $item): array {
|
|
86
|
+
$itemValue = $item->getValue();
|
|
87
|
+
if (empty($itemValue['value'])) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
$references = $this->linkedContentExtractor->getTargetEntities($itemValue['value']);
|
|
91
|
+
if (empty($references)) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
return $this->convertReferencesToEntityUsageList($references);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gutenberg\Plugin\EntityUsage\Track;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Field\FieldItemInterface;
|
|
6
|
+
use Drupal\entity_usage\EntityUsageTrackBase;
|
|
7
|
+
use Drupal\gutenberg\Parser\BlockParser;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Tracks usage of media entities in the Gutenberg editor.
|
|
11
|
+
*
|
|
12
|
+
* @EntityUsageTrack(
|
|
13
|
+
* id = "gutenberg_media_embed",
|
|
14
|
+
* label = @Translation("Media embed in Gutenberg"),
|
|
15
|
+
* description = @Translation("Tracks embedded media entities in Gutenberg."),
|
|
16
|
+
* field_types = {"text", "text_long", "text_with_summary"},
|
|
17
|
+
* )
|
|
18
|
+
*/
|
|
19
|
+
class GutenbergMediaEmbed extends EntityUsageTrackBase {
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* {@inheritDoc}
|
|
23
|
+
*/
|
|
24
|
+
public function getTargetEntities(FieldItemInterface $item): array {
|
|
25
|
+
$itemValue = $item->getValue();
|
|
26
|
+
if (empty($itemValue['value'])) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
$text = $itemValue['value'];
|
|
30
|
+
$blockParser = new BlockParser();
|
|
31
|
+
$blocks = $blockParser->parse($text);
|
|
32
|
+
$references = [];
|
|
33
|
+
$this->extractReferencesFromGutenbergBlocks($blocks, $references);
|
|
34
|
+
return array_unique($references);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Recursively extract all the media references from a set of gutenberg
|
|
39
|
+
* blocks.
|
|
40
|
+
*
|
|
41
|
+
* This method will just check if there is an attribute called mediaEntityIds
|
|
42
|
+
* for every block. If yes, then it will just add those ids to the list of
|
|
43
|
+
* media references. Then, if the current block has any inner blocks it will
|
|
44
|
+
* just call itself with the inner blocks as parameter.
|
|
45
|
+
*
|
|
46
|
+
* @param array $blocks
|
|
47
|
+
* An array of gutenberg blocks.
|
|
48
|
+
* @param $references
|
|
49
|
+
* An array with all the extracted references until the current call.
|
|
50
|
+
*/
|
|
51
|
+
protected function extractReferencesFromGutenbergBlocks(array $blocks, array &$references) {
|
|
52
|
+
foreach ($blocks as $block) {
|
|
53
|
+
if (!empty($block['attrs']['mediaEntityIds'])) {
|
|
54
|
+
foreach ($block['attrs']['mediaEntityIds'] as $mediaEntityId) {
|
|
55
|
+
$references[] = 'media|' . $mediaEntityId;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!empty($block['innerBlocks'])) {
|
|
59
|
+
$this->extractReferencesFromGutenbergBlocks($block['innerBlocks'], $references);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
package/drupal/silverback_gutenberg/src/Plugin/EntityUsage/Track/GutenbergReferencedContent.php
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gutenberg\Plugin\EntityUsage\Track;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Config\ConfigFactoryInterface;
|
|
6
|
+
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
|
7
|
+
use Drupal\Core\Entity\EntityRepositoryInterface;
|
|
8
|
+
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
|
9
|
+
use Drupal\Core\Field\FieldItemInterface;
|
|
10
|
+
use Drupal\entity_usage\EntityUsageInterface;
|
|
11
|
+
use Drupal\entity_usage\EntityUsageTrackBase;
|
|
12
|
+
use Drupal\entity_usage\UrlToEntityInterface;
|
|
13
|
+
use Drupal\gutenberg\Parser\BlockParser;
|
|
14
|
+
use Drupal\silverback_gutenberg\ReferencedContentExtractor;
|
|
15
|
+
use Psr\Log\LoggerInterface;
|
|
16
|
+
use Symfony\Component\DependencyInjection\ContainerInterface;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Tracks usage of referenced content in Gutenberg editor.
|
|
20
|
+
*
|
|
21
|
+
* @EntityUsageTrack(
|
|
22
|
+
* id = "gutenberg_referenced_content",
|
|
23
|
+
* label = @Translation("Referenced content in Gutenberg"),
|
|
24
|
+
* description = @Translation("Tracks referenced content entities in Gutenberg."),
|
|
25
|
+
* field_types = {"text", "text_long", "text_with_summary"},
|
|
26
|
+
* )
|
|
27
|
+
*/
|
|
28
|
+
class GutenbergReferencedContent extends EntityUsageTrackBase {
|
|
29
|
+
use GutenbergContentTrackTrait;
|
|
30
|
+
|
|
31
|
+
/* @var \Drupal\silverback_gutenberg\ReferencedContentExtractor */
|
|
32
|
+
protected $referencedContentExtractor;
|
|
33
|
+
|
|
34
|
+
public function __construct(
|
|
35
|
+
array $configuration,
|
|
36
|
+
$plugin_id,
|
|
37
|
+
$plugin_definition,
|
|
38
|
+
EntityUsageInterface $usage_service,
|
|
39
|
+
EntityTypeManagerInterface $entity_type_manager,
|
|
40
|
+
EntityFieldManagerInterface $entity_field_manager,
|
|
41
|
+
ConfigFactoryInterface $config_factory,
|
|
42
|
+
EntityRepositoryInterface $entity_repository,
|
|
43
|
+
?LoggerInterface $entityUsageLogger = NULL,
|
|
44
|
+
?UrlToEntityInterface $urlToEntity = NULL,
|
|
45
|
+
?array $always_track_base_fields = NULL,
|
|
46
|
+
?ReferencedContentExtractor $referenced_content_extractor = NULL,
|
|
47
|
+
) {
|
|
48
|
+
parent::__construct(
|
|
49
|
+
$configuration,
|
|
50
|
+
$plugin_id,
|
|
51
|
+
$plugin_definition,
|
|
52
|
+
$usage_service,
|
|
53
|
+
$entity_type_manager,
|
|
54
|
+
$entity_field_manager,
|
|
55
|
+
$config_factory,
|
|
56
|
+
$entity_repository,
|
|
57
|
+
$entityUsageLogger,
|
|
58
|
+
$urlToEntity, $always_track_base_fields
|
|
59
|
+
);
|
|
60
|
+
$this->referencedContentExtractor = $referenced_content_extractor;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* {@inheritDoc}
|
|
65
|
+
*/
|
|
66
|
+
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
|
67
|
+
return new static(
|
|
68
|
+
$configuration,
|
|
69
|
+
$plugin_id,
|
|
70
|
+
$plugin_definition,
|
|
71
|
+
$container->get('entity_usage.usage'),
|
|
72
|
+
$container->get('entity_type.manager'),
|
|
73
|
+
$container->get('entity_field.manager'),
|
|
74
|
+
$container->get('config.factory'),
|
|
75
|
+
$container->get('entity.repository'),
|
|
76
|
+
$container->get('logger.channel.entity_usage'),
|
|
77
|
+
$container->get(UrlToEntityInterface::class),
|
|
78
|
+
$container->getParameter('entity_usage')['always_track_base_fields'] ?? [],
|
|
79
|
+
$container->get('silverback_gutenberg.referenced_content_extractor')
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* {@inheritDoc}
|
|
85
|
+
*/
|
|
86
|
+
public function getTargetEntities(FieldItemInterface $item): array {
|
|
87
|
+
$itemValue = $item->getValue();
|
|
88
|
+
if (empty($itemValue['value'])) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
$text = $itemValue['value'];
|
|
92
|
+
$blockParser = new BlockParser();
|
|
93
|
+
$blocks = $blockParser->parse($text);
|
|
94
|
+
$references = $this->referencedContentExtractor->getTargetEntities($blocks);
|
|
95
|
+
|
|
96
|
+
if (empty($references)) {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
return $this->convertReferencesToEntityUsageList($references);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gutenberg\Plugin\GraphQL\DataProducer;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
|
6
|
+
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the attribute value from an editor block.
|
|
11
|
+
*
|
|
12
|
+
* @DataProducer(
|
|
13
|
+
* id = "editor_block_attribute",
|
|
14
|
+
* name = @Translation("Editor block attribute value"),
|
|
15
|
+
* description = @Translation("Resolve an attribute value from an editor block."),
|
|
16
|
+
* produces = @ContextDefinition("string",
|
|
17
|
+
* label = @Translation("The attribute value")
|
|
18
|
+
* ),
|
|
19
|
+
* consumes = {
|
|
20
|
+
* "block" = @ContextDefinition("any",
|
|
21
|
+
* label = @Translation("The parsed editor block")
|
|
22
|
+
* ),
|
|
23
|
+
* "name" = @ContextDefinition("string",
|
|
24
|
+
* label = @Translation("The attribute name.")
|
|
25
|
+
* ),
|
|
26
|
+
* "plainText" = @ContextDefinition("boolean",
|
|
27
|
+
* label = @Translation("Whether to process as plain text.")
|
|
28
|
+
* ),
|
|
29
|
+
* }
|
|
30
|
+
* )
|
|
31
|
+
*/
|
|
32
|
+
class EditorBlockAttribute extends DataProducerPluginBase {
|
|
33
|
+
protected function processPlainText(string $text): string {
|
|
34
|
+
// Even if we do not use any HTML markup in a block text, Gutenberg still
|
|
35
|
+
// treats it as HTML, e.g. it turns "<" into "<".
|
|
36
|
+
return html_entity_decode(trim($text));
|
|
37
|
+
}
|
|
38
|
+
public function resolve($block, $name, $plain_text = true) {
|
|
39
|
+
$field_value = $block['attrs'][$name] ?? NULL;
|
|
40
|
+
return $field_value && $plain_text ? $this->processPlainText($field_value) : $field_value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gutenberg\Plugin\GraphQL\DataProducer;
|
|
4
|
+
|
|
5
|
+
use Drupal\graphql\GraphQL\Execution\FieldContext;
|
|
6
|
+
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
|
|
7
|
+
use Drupal\silverback_gutenberg\EditorBlocksProcessor;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve any child blocks from an editor block.
|
|
12
|
+
*
|
|
13
|
+
* @DataProducer(
|
|
14
|
+
* id = "editor_block_children",
|
|
15
|
+
* name = @Translation("Editor block children"),
|
|
16
|
+
* description = @Translation("Resolve children from an editor block."),
|
|
17
|
+
* produces = @ContextDefinition("any",
|
|
18
|
+
* label = @Translation("An array of child blocks")
|
|
19
|
+
* ),
|
|
20
|
+
* consumes = {
|
|
21
|
+
* "block" = @ContextDefinition("any",
|
|
22
|
+
* label = @Translation("The parsed editor block")
|
|
23
|
+
* ),
|
|
24
|
+
* }
|
|
25
|
+
* )
|
|
26
|
+
*/
|
|
27
|
+
class EditorBlockChildren extends DataProducerPluginBase {
|
|
28
|
+
public function resolve($block, FieldContext $fieldContext) {
|
|
29
|
+
$transientEditorBlocks = $fieldContext->getContextValue('ignored_editor_blocks');
|
|
30
|
+
return EditorBlocksProcessor::processsIgnoredBlocks($block['innerBlocks'] ?? [], $transientEditorBlocks);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gutenberg\Plugin\GraphQL\DataProducer;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
|
6
|
+
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the HTML content inside an editor block.
|
|
11
|
+
*
|
|
12
|
+
* @DataProducer(
|
|
13
|
+
* id = "editor_block_html",
|
|
14
|
+
* name = @Translation("Editor block inner html"),
|
|
15
|
+
* description = @Translation("Resolve html content from an editor block."),
|
|
16
|
+
* produces = @ContextDefinition("string",
|
|
17
|
+
* label = @Translation("The HTML content of the block")
|
|
18
|
+
* ),
|
|
19
|
+
* consumes = {
|
|
20
|
+
* "block" = @ContextDefinition("any",
|
|
21
|
+
* label = @Translation("The parsed editor block")
|
|
22
|
+
* ),
|
|
23
|
+
* }
|
|
24
|
+
* )
|
|
25
|
+
*/
|
|
26
|
+
class EditorBlockHtml extends DataProducerPluginBase {
|
|
27
|
+
public function resolve($block) {
|
|
28
|
+
return $block['innerHTML'];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gutenberg\Plugin\GraphQL\DataProducer;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
|
6
|
+
use Drupal\Core\Entity\EntityRepositoryInterface;
|
|
7
|
+
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
|
8
|
+
use Drupal\Core\Entity\TranslatableInterface;
|
|
9
|
+
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
|
10
|
+
use Drupal\graphql\GraphQL\Buffers\EntityBuffer;
|
|
11
|
+
use Drupal\graphql\GraphQL\Execution\FieldContext;
|
|
12
|
+
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
|
|
13
|
+
use Drupal\layout_builder_fieldblock_test\ContextProvider\FakeViewModeContext;
|
|
14
|
+
use Drupal\media\Entity\Media;
|
|
15
|
+
use GraphQL\Deferred;
|
|
16
|
+
use Symfony\Component\DependencyInjection\ContainerInterface;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolves a media entity attached to an editor block.
|
|
21
|
+
*
|
|
22
|
+
* @DataProducer(
|
|
23
|
+
* id = "editor_block_media",
|
|
24
|
+
* name = @Translation("Editor blockmedia"),
|
|
25
|
+
* description = @Translation("Resolve the media item attached to an editor block."),
|
|
26
|
+
* produces = @ContextDefinition("entity:media",
|
|
27
|
+
* label = @Translation("The media item")
|
|
28
|
+
* ),
|
|
29
|
+
* consumes = {
|
|
30
|
+
* "block" = @ContextDefinition("any",
|
|
31
|
+
* label = @Translation("A parsed editor block")
|
|
32
|
+
* )
|
|
33
|
+
* }
|
|
34
|
+
* )
|
|
35
|
+
*/
|
|
36
|
+
class EditorBlockMedia extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The entity type manager service.
|
|
40
|
+
*
|
|
41
|
+
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
|
42
|
+
*/
|
|
43
|
+
protected $entityTypeManager;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The entity repository service.
|
|
47
|
+
*
|
|
48
|
+
* @var \Drupal\Core\Entity\EntityRepositoryInterface
|
|
49
|
+
*/
|
|
50
|
+
protected $entityRepository;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The entity buffer service.
|
|
54
|
+
*
|
|
55
|
+
* @var \Drupal\graphql\GraphQL\Buffers\EntityBuffer
|
|
56
|
+
*/
|
|
57
|
+
protected $entityBuffer;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* {@inheritdoc}
|
|
61
|
+
*
|
|
62
|
+
* @codeCoverageIgnore
|
|
63
|
+
*/
|
|
64
|
+
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
|
65
|
+
return new static(
|
|
66
|
+
$configuration,
|
|
67
|
+
$plugin_id,
|
|
68
|
+
$plugin_definition,
|
|
69
|
+
$container->get('entity_type.manager'),
|
|
70
|
+
$container->get('entity.repository'),
|
|
71
|
+
$container->get('graphql.buffer.entity')
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* EntityLoad constructor.
|
|
77
|
+
*
|
|
78
|
+
* @param array $configuration
|
|
79
|
+
* The plugin configuration array.
|
|
80
|
+
* @param string $pluginId
|
|
81
|
+
* The plugin id.
|
|
82
|
+
* @param array $pluginDefinition
|
|
83
|
+
* The plugin definition array.
|
|
84
|
+
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
|
|
85
|
+
* The entity type manager service.
|
|
86
|
+
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entityRepository
|
|
87
|
+
* The entity repository service.
|
|
88
|
+
* @param \Drupal\graphql\GraphQL\Buffers\EntityBuffer $entityBuffer
|
|
89
|
+
* The entity buffer service.
|
|
90
|
+
*
|
|
91
|
+
* @codeCoverageIgnore
|
|
92
|
+
*/
|
|
93
|
+
public function __construct(
|
|
94
|
+
array $configuration,
|
|
95
|
+
$pluginId,
|
|
96
|
+
array $pluginDefinition,
|
|
97
|
+
EntityTypeManagerInterface $entityTypeManager,
|
|
98
|
+
EntityRepositoryInterface $entityRepository,
|
|
99
|
+
EntityBuffer $entityBuffer
|
|
100
|
+
) {
|
|
101
|
+
parent::__construct($configuration, $pluginId, $pluginDefinition);
|
|
102
|
+
$this->entityTypeManager = $entityTypeManager;
|
|
103
|
+
$this->entityRepository = $entityRepository;
|
|
104
|
+
$this->entityBuffer = $entityBuffer;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public function resolve($block, FieldContext $context): ?Deferred {
|
|
108
|
+
$id = $block['attrs']['mediaEntityIds'][0] ?? NULL;
|
|
109
|
+
|
|
110
|
+
if (!$id) {
|
|
111
|
+
return new Deferred(function () { return NULL; });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
$resolver = $this->entityBuffer->add('media', $id);
|
|
115
|
+
$language = $context->getContextValue('document_language');
|
|
116
|
+
|
|
117
|
+
return new Deferred(function () use ($language, $resolver, $context) {
|
|
118
|
+
if (!$entity = $resolver()) {
|
|
119
|
+
// If there is no entity with this id, add the list cache tags so that
|
|
120
|
+
// the cache entry is purged whenever a new entity of this type is
|
|
121
|
+
// saved.
|
|
122
|
+
$type = $this->entityTypeManager->getDefinition('media');
|
|
123
|
+
/** @var \Drupal\Core\Entity\EntityTypeInterface $type */
|
|
124
|
+
$tags = $type->getListCacheTags();
|
|
125
|
+
$context->addCacheTags($tags);
|
|
126
|
+
return NULL;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
$context->addCacheableDependency($entity);
|
|
130
|
+
if (isset($bundles) && !in_array($entity->bundle(), $bundles)) {
|
|
131
|
+
// If the entity is not among the allowed bundles, don't return it.
|
|
132
|
+
return NULL;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Get the correct translation.
|
|
136
|
+
if (
|
|
137
|
+
isset($language) &&
|
|
138
|
+
$language !== $entity->language()->getId() &&
|
|
139
|
+
$entity instanceof TranslatableInterface &&
|
|
140
|
+
$entity->hasTranslation($language)
|
|
141
|
+
) {
|
|
142
|
+
$entity = $entity->getTranslation($language);
|
|
143
|
+
$entity->addCacheContexts(["static:language:{$language}"]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check if the passed user (or current user if none is passed) has access
|
|
147
|
+
// to the entity, if not return NULL.
|
|
148
|
+
/** @var \Drupal\Core\Access\AccessResultInterface $accessResult */
|
|
149
|
+
$accessResult = $entity->access('view', NULL, TRUE);
|
|
150
|
+
$context->addCacheableDependency($accessResult);
|
|
151
|
+
if ($accessResult->isForbidden()) {
|
|
152
|
+
return NULL;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return $entity;
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gutenberg\Plugin\GraphQL\DataProducer;
|
|
4
|
+
|
|
5
|
+
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the HTML content inside an editor block.
|
|
10
|
+
*
|
|
11
|
+
* @DataProducer(
|
|
12
|
+
* id = "editor_block_type",
|
|
13
|
+
* name = @Translation("Editor block type"),
|
|
14
|
+
* description = @Translation("Resolve an editor blocks type."),
|
|
15
|
+
* produces = @ContextDefinition("string",
|
|
16
|
+
* label = @Translation("The blocks typename")
|
|
17
|
+
* ),
|
|
18
|
+
* consumes = {
|
|
19
|
+
* "block" = @ContextDefinition("any",
|
|
20
|
+
* label = @Translation("The parsed editor block")
|
|
21
|
+
* ),
|
|
22
|
+
* }
|
|
23
|
+
* )
|
|
24
|
+
*/
|
|
25
|
+
class EditorBlockType extends DataProducerPluginBase {
|
|
26
|
+
public function resolve($block) {
|
|
27
|
+
return $block['blockName'];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Drupal\silverback_gutenberg\Plugin\GraphQL\DataProducer;
|
|
4
|
+
|
|
5
|
+
use Drupal\Core\Annotation\ContextDefinition;
|
|
6
|
+
use Drupal\Core\Annotation\Translation;
|
|
7
|
+
use Drupal\Core\Entity\EntityInterface;
|
|
8
|
+
use Drupal\Core\Render\BubbleableMetadata;
|
|
9
|
+
use Drupal\Core\Render\RenderContext;
|
|
10
|
+
use Drupal\Core\TypedData\Exception\MissingDataException;
|
|
11
|
+
use Drupal\Core\TypedData\TypedDataInterface;
|
|
12
|
+
use Drupal\Core\TypedData\TypedDataTrait;
|
|
13
|
+
use Drupal\graphql\GraphQL\Execution\FieldContext;
|
|
14
|
+
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
|
|
15
|
+
use Drupal\gutenberg\Parser\BlockParser;
|
|
16
|
+
use Drupal\silverback_gutenberg\EditorBlocksProcessor;
|
|
17
|
+
use Drupal\silverback_gutenberg\LinkProcessor;
|
|
18
|
+
use Drupal\typed_data\DataFetcherTrait;
|
|
19
|
+
use Drupal\typed_data\Exception\InvalidArgumentException;
|
|
20
|
+
use Drupal\typed_data\Exception\LogicException;
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolves a typed data value at a given property path and parses it to blocks.
|
|
25
|
+
*
|
|
26
|
+
* @DataProducer(
|
|
27
|
+
* id = "editor_blocks",
|
|
28
|
+
* name = @Translation("Editor blocks"),
|
|
29
|
+
* description = @Translation("Resolve html content from a property path and parse it into blocks."),
|
|
30
|
+
* produces = @ContextDefinition("any",
|
|
31
|
+
* label = @Translation("List of Blocks")
|
|
32
|
+
* ),
|
|
33
|
+
* consumes = {
|
|
34
|
+
* "path" = @ContextDefinition("string",
|
|
35
|
+
* label = @Translation("Property path")
|
|
36
|
+
* ),
|
|
37
|
+
* "entity" = @ContextDefinition("any",
|
|
38
|
+
* label = @Translation("Root value")
|
|
39
|
+
* ),
|
|
40
|
+
* "type" = @ContextDefinition("string",
|
|
41
|
+
* label = @Translation("Root type"),
|
|
42
|
+
* required = FALSE
|
|
43
|
+
* ),
|
|
44
|
+
* "ignored" = @ContextDefinition("any",
|
|
45
|
+
* label = @Translation("Ignored block types"),
|
|
46
|
+
* required = FALSE
|
|
47
|
+
* ),
|
|
48
|
+
* "aggregated" = @ContextDefinition("any",
|
|
49
|
+
* label = @Translation("Aggregated into core/paragraph"),
|
|
50
|
+
* required = FALSE
|
|
51
|
+
* )
|
|
52
|
+
* }
|
|
53
|
+
* )
|
|
54
|
+
*/
|
|
55
|
+
class EditorBlocks extends DataProducerPluginBase {
|
|
56
|
+
use TypedDataTrait;
|
|
57
|
+
use DataFetcherTrait;
|
|
58
|
+
|
|
59
|
+
public function resolve(
|
|
60
|
+
$path,
|
|
61
|
+
$entity,
|
|
62
|
+
$type,
|
|
63
|
+
$ignored,
|
|
64
|
+
$aggregated,
|
|
65
|
+
FieldContext $field
|
|
66
|
+
) {
|
|
67
|
+
if (!$entity instanceof EntityInterface) {
|
|
68
|
+
throw new LogicException('Editor blocks can only be retrieved from Entities');
|
|
69
|
+
}
|
|
70
|
+
$field->setContextValue('document_language', $entity->language()->getId());
|
|
71
|
+
if (!($entity instanceof TypedDataInterface) && !empty($type)) {
|
|
72
|
+
$manager = $this->getTypedDataManager();
|
|
73
|
+
$definition = $manager->createDataDefinition($type);
|
|
74
|
+
$value = $manager->create($definition, $entity);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!($value instanceof TypedDataInterface)) {
|
|
78
|
+
throw new \BadMethodCallException('Could not derive typed data type.');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
$bubbleable = new BubbleableMetadata();
|
|
82
|
+
$fetcher = $this->getDataFetcher();
|
|
83
|
+
|
|
84
|
+
$html = '';
|
|
85
|
+
try {
|
|
86
|
+
$html = $fetcher->fetchDataByPropertyPath($value, $path, $bubbleable)->getValue();
|
|
87
|
+
}
|
|
88
|
+
catch (MissingDataException $exception) {
|
|
89
|
+
// There is no data at the given path.
|
|
90
|
+
}
|
|
91
|
+
catch (InvalidArgumentException $exception) {
|
|
92
|
+
// The path is invalid for the source object.
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
$field->addCacheableDependency($bubbleable);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
$linkProcessor = \Drupal::service(LinkProcessor::class);
|
|
99
|
+
$linkProcessor->resetCacheableMetadata();
|
|
100
|
+
|
|
101
|
+
$context = new RenderContext();
|
|
102
|
+
$result = \Drupal::service('renderer')->executeInRenderContext(
|
|
103
|
+
$context,
|
|
104
|
+
function () use ($entity, $field, $html, $linkProcessor) {
|
|
105
|
+
$parser = new BlockParser();
|
|
106
|
+
$html = $linkProcessor->processLinks($html, 'outbound', $entity->language());
|
|
107
|
+
return $parser->parse($html);
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
if (!$context->isEmpty()) {
|
|
111
|
+
$field->addCacheableDependency($context->pop());
|
|
112
|
+
}
|
|
113
|
+
$field->addCacheableDependency($linkProcessor->getCacheableMetadata());
|
|
114
|
+
$linkProcessor->resetCacheableMetadata();
|
|
115
|
+
|
|
116
|
+
$ignored = array_merge(['core/group'], $ignored ?? []);
|
|
117
|
+
|
|
118
|
+
$field->setContextValue('ignored_editor_blocks', $ignored);
|
|
119
|
+
$blocks = EditorBlocksProcessor::processsIgnoredBlocks($result, $ignored);
|
|
120
|
+
$blocks = EditorBlocksProcessor::aggregateParagraphs($blocks, $aggregated ?: ['core/paragraph']);
|
|
121
|
+
|
|
122
|
+
\Drupal::moduleHandler()->alter('editor_blocks', $blocks, $entity);
|
|
123
|
+
|
|
124
|
+
return $blocks;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|