Skip to content

Commit eaef9ff

Browse files
author
slucero
committed
Issue #3281936 by slucero, mariohernandez, johnle: Process text format filters for WYSIWYG fields
1 parent c4a2a3e commit eaef9ff

33 files changed

Lines changed: 4329 additions & 48 deletions

‎composer.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"license": "MIT",
1616
"require": {
1717
"php": ">=7.1.3",
18+
"swaggest/json-schema": "^0.12",
1819
"symfony/finder": "^3.4 || ^4.0",
1920
"symfony/yaml": "^3.4 || ^4.0"
2021
},

‎modules/patternkit_example/lib/patternkit/src/atoms/example_ref/src/example_ref.json‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"properties": {
88
"text": {
99
"$ref": "@patternkit/atoms/example_ref/../example/src/example.json#/properties/text"
10+
},
11+
"nested_reference": {
12+
"$ref": "@patternkit/atoms/example_ref/../example/src/example.json"
1013
}
1114
}
1215
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
Example twig template with include and reference.
22

3-
{% include '@patternkit/atoms/example/src/example.twig' with _context %}
3+
<div>{{- text|striptags('') -}}</div>
4+
5+
{% include '@patternkit/atoms/example/src/example.twig' with nested_reference %}

‎patternkit.api.php‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/**
4+
* @file
5+
* Documentation for custom hooks defined in the Patternkit module.
6+
*/
7+
8+
/**
9+
* Alter the list of registered field processor plugins.
10+
*
11+
* @param array $info
12+
* The field processor plugin definitions to be altered.
13+
*/
14+
function hook_pattern_field_processor_info_alter(array &$info): void {
15+
// Override the class for a provided processor.
16+
if (isset($info['token'])) {
17+
$info['token']['class'] = '\My\Custom\ProcessorPluginClass';
18+
}
19+
20+
// Unregister a provided processor.
21+
unset($info['wysiwyg']);
22+
}

‎patternkit.polyfil.php‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/**
4+
* @file
5+
* Polyfil functions for PHP version compatibility to be loaded if needed.
6+
*/
7+
8+
if (!function_exists("array_is_list")) {
9+
10+
/**
11+
* Test whether an array is associative or numerically indexed.
12+
*
13+
* @param array $array
14+
* The array for testing.
15+
*
16+
* @return bool
17+
* TRUE if all indexes are numeric and sequentially specified. FALSE if
18+
* there are any non-numeric indices, or they are out of order.
19+
*
20+
* @see https://www.php.net/manual/en/function.array-is-list
21+
*/
22+
function array_is_list(array $array): bool {
23+
$i = -1;
24+
foreach ($array as $k => $v) {
25+
++$i;
26+
if ($k !== $i) {
27+
return FALSE;
28+
}
29+
}
30+
return TRUE;
31+
}
32+
33+
}

‎patternkit.services.yml‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,23 @@ services:
4141
class: Drupal\patternkit\Routing\PatternkitTranslateBlockFormRouteSubscriber
4242
tags:
4343
- { name: event_subscriber }
44+
plugin.manager.pattern_field_processor:
45+
class: Drupal\patternkit\PatternFieldProcessorPluginManager
46+
arguments:
47+
- '@container.namespaces'
48+
- '@cache.discovery'
49+
- '@module_handler'
50+
- '@serialization.json'
51+
- '@patternkit.asset.library'
52+
- '@patternkit.schema.schema_walker_factory'
53+
- '@logger.channel.patternkit'
54+
patternkit.schema.ref_provider:
55+
class: Drupal\patternkit\Schema\PatternkitRefProvider
56+
arguments:
57+
- '@patternkit.asset.library'
58+
public: false
59+
patternkit.schema.schema_walker_factory:
60+
class: Drupal\patternkit\Schema\SchemaWalkerFactory
61+
arguments:
62+
- '@patternkit.schema.ref_provider'
63+
public: false
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Drupal\patternkit\Annotation;
4+
5+
use Drupal\Component\Annotation\Plugin;
6+
7+
/**
8+
* Defines pattern_field_processor annotation object.
9+
*
10+
* @Annotation
11+
*/
12+
class PatternFieldProcessor extends Plugin {
13+
14+
/**
15+
* The plugin ID.
16+
*
17+
* @var string
18+
*/
19+
public $id;
20+
21+
/**
22+
* The human-readable name of the plugin.
23+
*
24+
* @var \Drupal\Core\Annotation\Translation
25+
*
26+
* @ingroup plugin_translatable
27+
*/
28+
public $title;
29+
30+
/**
31+
* The description of the plugin.
32+
*
33+
* @var \Drupal\Core\Annotation\Translation
34+
*
35+
* @ingroup plugin_translatable
36+
*/
37+
public $description;
38+
39+
}

‎src/Asset/Library.php‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ public function getLibraryAsset($key) {
385385
$library_name = substr($key, 0, strpos($key, '/'));
386386
/** @var \Drupal\patternkit\PatternLibrary $library */
387387
$library = $this->get(trim($library_name, '@'));
388-
return $library->patterns[substr($key, strlen($library_name . '/'))] ?? NULL;
388+
$component_name = substr($key, strlen($library_name . '/'));
389+
return $library->patterns[$component_name] ?? NULL;
389390
}
390391

391392
/**

‎src/Element/Pattern.php‎

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
namespace Drupal\patternkit\Element;
4+
5+
use Drupal\Core\Render\BubbleableMetadata;
6+
use Drupal\Core\Render\Element\RenderElement;
7+
use Drupal\patternkit\Entity\PatternInterface;
8+
use Drupal\patternkit\PatternLibraryPluginInterface;
9+
10+
/**
11+
* Provides a render element to display a pattern.
12+
*
13+
* Properties:
14+
* - #pattern: The loaded Pattern entity to be rendered.
15+
* - #config: Configuration to be passed to the pattern for rendering.
16+
* - #context: Context values for rendering the pattern.
17+
*
18+
* Usage Example:
19+
* @code
20+
* $build['example_pattern'] = [
21+
* '#type' => 'pattern',
22+
* '#pattern' => 'example',
23+
* '#config' => [
24+
* 'text' => '[node:title]',
25+
* 'formatted_text' => '<p><strong>My formatted text</strong></p>',
26+
* ],
27+
* '#context' => [
28+
* 'node' => $node,
29+
* ],
30+
* ];
31+
* @endcode
32+
*
33+
* @RenderElement("pattern")
34+
*/
35+
class Pattern extends RenderElement {
36+
37+
/**
38+
* {@inheritdoc}
39+
*/
40+
public function getInfo(): array {
41+
return [
42+
'#pre_render' => [
43+
[get_class($this), 'preRenderPatternElement'],
44+
],
45+
'#pattern' => NULL,
46+
'#config' => [],
47+
'#context' => [],
48+
];
49+
}
50+
51+
/**
52+
* Pattern element pre render callback.
53+
*
54+
* @param array $element
55+
* An associative array containing the properties of the pattern element.
56+
*
57+
* @return array
58+
* The modified element.
59+
*
60+
* @throws \Drupal\Component\Plugin\Exception\PluginException
61+
*/
62+
public static function preRenderPatternElement(array $element): array {
63+
/** @var \Drupal\patternkit\Entity\Pattern $pattern */
64+
$pattern = $element['#pattern'];
65+
66+
// Fail early if a pattern was unable to be loaded.
67+
if (is_null($pattern)) {
68+
$element = [
69+
'#markup' => t('Pattern unavailable.'),
70+
];
71+
return $element;
72+
}
73+
74+
$pattern->config = $element['#config'];
75+
$pattern->context = $element['#context'];
76+
77+
$bubbleableMetadata = new BubbleableMetadata();
78+
static::preprocessConfigValues($pattern, $pattern->config, $pattern->context, $bubbleableMetadata);
79+
80+
$library_plugin = static::getPatternLibraryPlugin($pattern);
81+
$elements = $library_plugin->render([$pattern]);
82+
83+
// Apply all bubbleable metadata from preprocessing.
84+
$bubbleableMetadata->applyTo($elements);
85+
86+
return $elements;
87+
}
88+
89+
/**
90+
* Load the appropriate pattern library plugin for rendering the pattern.
91+
*
92+
* @param \Drupal\patternkit\Entity\PatternInterface $pattern
93+
* The pattern entity being prepared for rendering.
94+
*
95+
* @return \Drupal\patternkit\PatternLibraryPluginInterface
96+
* The pattern library plugin needed by the given pattern.
97+
*/
98+
protected static function getPatternLibraryPlugin(PatternInterface $pattern): PatternLibraryPluginInterface {
99+
/** @var \Drupal\patternkit\PatternLibraryPluginManager $pattern_plugin_manager */
100+
$patternLibraryPluginManager = \Drupal::service('plugin.manager.library.pattern');
101+
102+
$pattern_plugin = $pattern->getLibraryPluginId();
103+
$library_plugin_id = !empty($pattern_plugin) ? $pattern_plugin : 'twig';
104+
105+
return $patternLibraryPluginManager->createInstance($library_plugin_id);
106+
}
107+
108+
/**
109+
* Execute value processing on configuration values.
110+
*
111+
* @param \Drupal\patternkit\Entity\PatternInterface $pattern
112+
* The pattern being prepared and processed.
113+
* @param array $config
114+
* Configuration values for the pattern being prepared.
115+
* @param array $context
116+
* Context values configured for the pattern being prepared.
117+
* @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata
118+
* Bubbleable metadata to be tracked and updated during processing.
119+
*
120+
* @throws \Drupal\Component\Plugin\Exception\PluginException
121+
*/
122+
public static function preprocessConfigValues(PatternInterface $pattern, array &$config, array $context, BubbleableMetadata $bubbleable_metadata): void {
123+
/** @var \Drupal\patternkit\PatternFieldProcessorPluginManager $manager */
124+
$manager = \Drupal::service('plugin.manager.pattern_field_processor');
125+
$manager->processSchemaValues($pattern, $config, $context, $bubbleable_metadata);
126+
}
127+
128+
}

‎src/Exception/SchemaException.php‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace Drupal\patternkit\Exception;
4+
5+
use Swaggest\JsonSchema\SchemaContract;
6+
7+
/**
8+
* Base exception for unexpected events during Schema processing.
9+
*/
10+
class SchemaException extends \RuntimeException {
11+
12+
/**
13+
* The schema being processed at the time of the exception.
14+
*
15+
* @var \Swaggest\JsonSchema\SchemaContract|null
16+
*/
17+
protected ?SchemaContract $schema;
18+
19+
/**
20+
* Set the schema being processed at the time of the exception.
21+
*
22+
* @param \Swaggest\JsonSchema\SchemaContract $schema
23+
* The schema being processed at the time of the exception.
24+
*
25+
* @return static
26+
*/
27+
public function setSchema(SchemaContract $schema) {
28+
$this->schema = $schema;
29+
30+
return $this;
31+
}
32+
33+
/**
34+
* Get the schema being processed at the time of the exception.
35+
*
36+
* @return \Swaggest\JsonSchema\SchemaContract|null
37+
* The schema being processed at the time of the exception or null if it
38+
* was not set.
39+
*/
40+
public function getSchema(): ?SchemaContract {
41+
return $this->schema;
42+
}
43+
44+
}

0 commit comments

Comments
 (0)