Skip to content

Commit 13b2339

Browse files
committed
Initial commit
0 parents  commit 13b2339

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+5795
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
vendor

LICENSE.txt

+339
Large diffs are not rendered by default.

README.md

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Data Transform Plugin for Pattern Lab PHP
2+
3+
## Installation
4+
5+
To install the plugin run:
6+
7+
```sh
8+
composer require aleksip/plugin-data-transform
9+
```
10+
11+
12+
## Features
13+
14+
Currently the plugin provides 3 transform functions for the JSON data read by Pattern Lab.
15+
16+
17+
### Include pattern files
18+
19+
If a value contains the name of a pattern in shorthand partials syntax, the plugin will replace the value with the rendered pattern:
20+
21+
```json
22+
{
23+
"key": "atoms-form-element-label.html"
24+
}
25+
```
26+
27+
Advanced syntax with support for passing variables and disabling access to the default data:
28+
29+
```json
30+
{
31+
"key": {
32+
"include()": {
33+
"pattern": "atoms-form-element-label.html",
34+
"with": {
35+
"title": "Textfield label"
36+
},
37+
"only": true
38+
}
39+
}
40+
}
41+
```
42+
43+
In both examples the value of `key` will replaced with the rendered pattern.
44+
45+
46+
### Join text values
47+
48+
```json
49+
{
50+
"key": {
51+
"join()": [
52+
"molecules-comment.html",
53+
"<div class=\"indented\">",
54+
"molecules-comment.html",
55+
"</div>",
56+
"molecules-comment.html"
57+
]
58+
}
59+
}
60+
```
61+
62+
The value of `key` will be replaced with the joined strings. Note that in the example `molecules-comment.html` is the name of a pattern in shorthand partials syntax. These will be replaced with the rendered pattern before the join.
63+
64+
65+
### Create Drupal `Attribute` objects
66+
67+
```json
68+
{
69+
"key": {
70+
"Attribute()": {
71+
"id": [ "edit-submit" ],
72+
"type": [ "submit" ],
73+
"value": [ "Submit" ],
74+
"class": [ "button", "button-primary" ]
75+
}
76+
}
77+
}
78+
```
79+
80+
The value of `key` will be replaced with an [Attribute object](https://www.drupal.org/node/2513632).
81+
82+
83+
## More examples
84+
85+
All functions provided by this plugin are used extensively in [Shila Drupal Theme StarterKit](https://github.com/aleksip/starterkit-shila-drupal-theme).

composer.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "aleksip/plugin-data-transform",
3+
"description": "Data Transform Plugin for Pattern Lab PHP",
4+
"keywords": ["pattern lab", "plugin", "drupal"],
5+
"homepage": "https://github.com/aleksip/plugin-data-transform",
6+
"license": "GPL-2.0+",
7+
"type": "patternlab-plugin",
8+
"authors": [
9+
{
10+
"name": "Aleksi Peebles"
11+
}
12+
],
13+
"support": {
14+
"issues": "https://github.com/aleksip/plugin-data-transform/issues",
15+
"source": "https://github.com/aleksip/plugin-data-transform/releases"
16+
},
17+
"autoload": {
18+
"psr-0": {
19+
"aleksip\\DataTransformPlugin\\": "src/",
20+
"Drupal\\Component\\Render\\": "src/",
21+
"Drupal\\Component\\Utility\\": "src/",
22+
"Drupal\\Core\\Template\\": "src/"
23+
}
24+
},
25+
"require": {
26+
"php": ">=5.5.9"
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
<?php
2+
3+
/**
4+
* @file
5+
* Contains \Drupal\Component\Render\FormattableMarkup.
6+
*/
7+
8+
namespace Drupal\Component\Render;
9+
10+
use Drupal\Component\Utility\Html;
11+
use Drupal\Component\Utility\SafeMarkup;
12+
use Drupal\Component\Utility\Unicode;
13+
use Drupal\Component\Utility\UrlHelper;
14+
15+
/**
16+
* Formats a string for HTML display by replacing variable placeholders.
17+
*
18+
* When cast to a string, this object replaces variable placeholders in the
19+
* string with the arguments passed in during construction and escapes the
20+
* values so they can be safely displayed as HTML. See the documentation of
21+
* \Drupal\Component\Render\FormattableMarkup::placeholderFormat() for details
22+
* on the supported placeholders and how to use them securely. Incorrect use of
23+
* this class can result in security vulnerabilities.
24+
*
25+
* In most cases, you should use TranslatableMarkup or PluralTranslatableMarkup
26+
* rather than this object, since they will translate the text (on
27+
* non-English-only sites) in addition to formatting it. Variables concatenated
28+
* without the insertion of language-specific words or punctuation are some
29+
* examples where translation is not applicable and using this class directly
30+
* directly is appropriate.
31+
*
32+
* This class is designed for formatting messages that are mostly text, not as
33+
* an HTML template language. As such:
34+
* - The passed in string should contain no (or minimal) HTML.
35+
* - Variable placeholders should not be used within the "<" and ">" of an
36+
* HTML tag, such as in HTML attribute values. This would be a security
37+
* risk. Examples:
38+
* @code
39+
* // Insecure (placeholder within "<" and ">"):
40+
* $this->placeholderFormat('<@variable>text</@variable>', ['@variable' => $variable]);
41+
* // Insecure (placeholder within "<" and ">"):
42+
* $this->placeholderFormat('<a @variable>link text</a>', ['@variable' => $variable]);
43+
* // Insecure (placeholder within "<" and ">"):
44+
* $this->placeholderFormat('<a title="@variable">link text</a>', ['@variable' => $variable]);
45+
* @endcode
46+
* Only the "href" attribute is supported via the special ":variable"
47+
* placeholder, to allow simple links to be inserted:
48+
* @code
49+
* // Secure (usage of ":variable" placeholder for href attribute):
50+
* $this->placeholderFormat('<a href=":variable">link text</a>', [':variable' , $variable]);
51+
* // Secure (usage of ":variable" placeholder for href attribute):
52+
* $this->placeholderFormat('<a href=":variable" title="static text">link text</a>', [':variable' => $variable]);
53+
* // Insecure (the "@variable" placeholder does not filter dangerous
54+
* // protocols):
55+
* $this->placeholderFormat('<a href="@variable">link text</a>', ['@variable' => $variable]);
56+
* // Insecure ("@variable" placeholder within "<" and ">"):
57+
* $this->placeholderFormat('<a href=":url" title="@variable">link text</a>', [':url' => $url, '@variable' => $variable]);
58+
* @endcode
59+
* To build non-minimal HTML, use an HTML template language such as Twig,
60+
* rather than this class.
61+
*
62+
* @ingroup sanitization
63+
*
64+
* @see \Drupal\Core\StringTranslation\TranslatableMarkup
65+
* @see \Drupal\Core\StringTranslation\PluralTranslatableMarkup
66+
* @see \Drupal\Component\Render\FormattableMarkup::placeholderFormat()
67+
*/
68+
class FormattableMarkup implements MarkupInterface, \Countable {
69+
70+
/**
71+
* The arguments to replace placeholders with.
72+
*
73+
* @var array
74+
*/
75+
protected $arguments = [];
76+
77+
/**
78+
* Constructs a new class instance.
79+
*
80+
* @param string $string
81+
* A string containing placeholders. The string itself will not be escaped,
82+
* any unsafe content must be in $args and inserted via placeholders.
83+
* @param array $arguments
84+
* An array with placeholder replacements, keyed by placeholder. See
85+
* \Drupal\Component\Render\FormattableMarkup::placeholderFormat() for
86+
* additional information about placeholders.
87+
*
88+
* @see \Drupal\Component\Render\FormattableMarkup::placeholderFormat()
89+
*/
90+
public function __construct($string, array $arguments) {
91+
$this->string = (string) $string;
92+
$this->arguments = $arguments;
93+
}
94+
95+
/**
96+
* {@inheritdoc}
97+
*/
98+
public function __toString() {
99+
return static::placeholderFormat($this->string, $this->arguments);
100+
}
101+
102+
/**
103+
* Returns the string length.
104+
*
105+
* @return int
106+
* The length of the string.
107+
*/
108+
public function count() {
109+
return Unicode::strlen($this->string);
110+
}
111+
112+
/**
113+
* Returns a representation of the object for use in JSON serialization.
114+
*
115+
* @return string
116+
* The safe string content.
117+
*/
118+
public function jsonSerialize() {
119+
return $this->__toString();
120+
}
121+
122+
/**
123+
* Replaces placeholders in a string with values.
124+
*
125+
* @param string $string
126+
* A string containing placeholders. The string itself is expected to be
127+
* safe and correct HTML. Any unsafe content must be in $args and
128+
* inserted via placeholders.
129+
* @param array $args
130+
* An associative array of replacements. Each array key should be the same
131+
* as a placeholder in $string. The corresponding value should be a string
132+
* or an object that implements
133+
* \Drupal\Component\Render\MarkupInterface. The value replaces the
134+
* placeholder in $string. Sanitization and formatting will be done before
135+
* replacement. The type of sanitization and formatting depends on the first
136+
* character of the key:
137+
* - @variable: When the placeholder replacement value is:
138+
* - A string, the replaced value in the returned string will be sanitized
139+
* using \Drupal\Component\Utility\Html::escape().
140+
* - A MarkupInterface object, the replaced value in the returned string
141+
* will not be sanitized.
142+
* - A MarkupInterface object cast to a string, the replaced value in the
143+
* returned string be forcibly sanitized using
144+
* \Drupal\Component\Utility\Html::escape().
145+
* @code
146+
* $this->placeholderFormat('This will force HTML-escaping of the replacement value: @text', ['@text' => (string) $safe_string_interface_object));
147+
* @endcode
148+
* Use this placeholder as the default choice for anything displayed on
149+
* the site, but not within HTML attributes, JavaScript, or CSS. Doing so
150+
* is a security risk.
151+
* - %variable: Use when the replacement value is to be wrapped in <em>
152+
* tags.
153+
* A call like:
154+
* @code
155+
* $string = "%output_text";
156+
* $arguments = ['output_text' => 'text output here.'];
157+
* $this->placeholderFormat($string, $arguments);
158+
* @endcode
159+
* makes the following HTML code:
160+
* @code
161+
* <em class="placeholder">text output here.</em>
162+
* @endcode
163+
* As with @variable, do not use this within HTML attributes, JavaScript,
164+
* or CSS. Doing so is a security risk.
165+
* - :variable: Return value is escaped with
166+
* \Drupal\Component\Utility\Html::escape() and filtered for dangerous
167+
* protocols using UrlHelper::stripDangerousProtocols(). Use this when
168+
* using the "href" attribute, ensuring the attribute value is always
169+
* wrapped in quotes:
170+
* @code
171+
* // Secure (with quotes):
172+
* $this->placeholderFormat('<a href=":url">@variable</a>', [':url' => $url, @variable => $variable]);
173+
* // Insecure (without quotes):
174+
* $this->placeholderFormat('<a href=:url>@variable</a>', [':url' => $url, @variable => $variable]);
175+
* @endcode
176+
* When ":variable" comes from arbitrary user input, the result is secure,
177+
* but not guaranteed to be a valid URL (which means the resulting output
178+
* could fail HTML validation). To guarantee a valid URL, use
179+
* Url::fromUri($user_input)->toString() (which either throws an exception
180+
* or returns a well-formed URL) before passing the result into a
181+
* ":variable" placeholder.
182+
*
183+
* @return string
184+
* A formatted HTML string with the placeholders replaced.
185+
*
186+
* @ingroup sanitization
187+
*
188+
* @see \Drupal\Core\StringTranslation\TranslatableMarkup
189+
* @see \Drupal\Core\StringTranslation\PluralTranslatableMarkup
190+
* @see \Drupal\Component\Utility\Html::escape()
191+
* @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
192+
* @see \Drupal\Core\Url::fromUri()
193+
*/
194+
protected static function placeholderFormat($string, array $args) {
195+
// Transform arguments before inserting them.
196+
foreach ($args as $key => $value) {
197+
switch ($key[0]) {
198+
case '@':
199+
// Escape if the value is not an object from a class that implements
200+
// \Drupal\Component\Render\MarkupInterface, for example strings will
201+
// be escaped.
202+
// \Drupal\Component\Utility\SafeMarkup\SafeMarkup::isSafe() may
203+
// return TRUE for content that is safe within HTML fragments, but not
204+
// within other contexts, so this placeholder type must not be used
205+
// within HTML attributes, JavaScript, or CSS.
206+
$args[$key] = static::placeholderEscape($value);
207+
break;
208+
209+
case ':':
210+
// Strip URL protocols that can be XSS vectors.
211+
$value = UrlHelper::stripDangerousProtocols($value);
212+
// Escape unconditionally, without checking
213+
// \Drupal\Component\Utility\SafeMarkup\SafeMarkup::isSafe(). This
214+
// forces characters that are unsafe for use in an "href" HTML
215+
// attribute to be encoded. If a caller wants to pass a value that is
216+
// extracted from HTML and therefore is already HTML encoded, it must
217+
// invoke
218+
// \Drupal\Component\Render\OutputStrategyInterface::renderFromHtml()
219+
// on it prior to passing it in as a placeholder value of this type.
220+
// @todo Add some advice and stronger warnings.
221+
// https://www.drupal.org/node/2569041.
222+
$args[$key] = Html::escape($value);
223+
break;
224+
225+
case '%':
226+
// Similarly to @, escape non-safe values. Also, add wrapping markup
227+
// in order to render as a placeholder. Not for use within attributes,
228+
// per the warning above about
229+
// \Drupal\Component\Utility\SafeMarkup\SafeMarkup::isSafe() and also
230+
// due to the wrapping markup.
231+
$args[$key] = '<em class="placeholder">' . static::placeholderEscape($value) . '</em>';
232+
break;
233+
234+
default:
235+
// We do not trigger an error for placeholder that start with an
236+
// alphabetic character.
237+
if (!ctype_alpha($key[0])) {
238+
// We trigger an error as we may want to introduce new placeholders
239+
// in the future without breaking backward compatibility.
240+
trigger_error('Invalid placeholder (' . $key . ') in string: ' . $string, E_USER_ERROR);
241+
}
242+
break;
243+
}
244+
}
245+
246+
return strtr($string, $args);
247+
}
248+
249+
/**
250+
* Escapes a placeholder replacement value if needed.
251+
*
252+
* @param string|\Drupal\Component\Render\MarkupInterface $value
253+
* A placeholder replacement value.
254+
*
255+
* @return string
256+
* The properly escaped replacement value.
257+
*/
258+
protected static function placeholderEscape($value) {
259+
return SafeMarkup::isSafe($value) ? (string) $value : Html::escape($value);
260+
}
261+
262+
}

0 commit comments

Comments
 (0)