Skip to content

Commit 6a0f46d

Browse files
mpdudeMalteWunsch
andauthored
Provide a semantic bundle configuration (#21)
Instead of having to set up service configuration like this: ```xml <service id="webfactory.shortcode.your-shortcode-name" parent="Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler.inline" class="Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler"> <argument index="1">reference-to-your-replacement-controller</argument> <tag name="webfactory.shortcode" shortcode="your-shortcode-name"/> </service> ``` ... and using the `Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler.inline` or `Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler.esi` parent services, with this PR you can instead write: ```yml # config.yml webfactory_shortcode: shortcodes: my-shortcode: 'My\Handling\Controller::method' another-shortcode: controller: 'My\Handling\Controller::method' method: esi ``` The first is a shorthand notation to register the `my-shortcode` shortcode which will be rendered with the `inline` strategy. The second is a bit more elaborate, but allows to specify `esi` as the rendering strategy. Co-authored-by: Malte Wunsch <mw@webfactory.de>
1 parent 2c1dbb8 commit 6a0f46d

File tree

20 files changed

+305
-342
lines changed

20 files changed

+305
-342
lines changed

.github/workflows/tests.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ jobs:
1818
matrix:
1919
include:
2020
- { php-version: 7.2, symfony-locked-version: none, dependency-version: prefer-lowest }
21-
- { php-version: 7.2, symfony-locked-version: 3.4.*, dependency-version: prefer-stable }
2221
- { php-version: 7.4, symfony-locked-version: 4.4.*, dependency-version: prefer-stable }
2322
- { php-version: 8.1, symfony-locked-version: none, dependency-version: prefer-stable }
2423
name: PHPUnit (PHP ${{matrix.php-version}}, Symfony Version Lock ${{ matrix.symfony-locked-version }}, ${{ matrix.dependency-version }})

.php_cs.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ return PhpCsFixer\Config::create()
2222
->notPath('node_modules/')
2323
->notPath('var/cache')
2424
->notPath('vendor/')
25+
->notPath('tests/Fixtures/cache')
2526
)
2627
;

README.md

Lines changed: 61 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,22 @@
11
# WebfactoryShortcodeBundle
22

3-
WebfactoryShortcodeBundle is a Symfony bundle that integrates [thunderer/Shortcode](https://github.com/thunderer/Shortcode).
3+
A Symfony bundle to resolve `[shortcode]` markup in Twig templates, using the [thunderer/Shortcode](https://github.com/thunderer/Shortcode) library.
44

5-
It allows you to define shortcodes and their replacements in a jiffy. Shortcodes are special text fragments that can be
6-
used by users in user generated content to embed some other content or markup. E.g. a user could use the following in a
7-
comment:
5+
It allows you to define shortcodes and their replacements in a jiffy. Shortcodes are special text fragments that can be replaced with other content or markup. E.g. a user could use the following in a comment:
86

97
```
108
[image url="https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png"]
119
[text color="red"]This is red text.[/text]
1210
```
1311

14-
In analogy to living style guides, this bundle also provides an optional shortcode guide. This guide can be used for
15-
automated testing of your shortcodes as well.
12+
In analogy to living style guides, this bundle provides a shortcode guide that lists all registered shortcodes with an optional description and example.
1613

1714
## Installation
1815

19-
As usual, install via [composer](https://getcomposer.org/) and register the bundle in your application:
16+
As usual, install via [Composer](https://getcomposer.org/) and register the bundle in your application:
2017

2118
composer require webfactory/shortcode-bundle
2219

23-
For Symfony < 4:
24-
25-
```php
26-
<?php
27-
// app/AppKernel.php
28-
29-
public function registerBundles()
30-
{
31-
$bundles = array(
32-
// ...
33-
new Webfactory\ShortcodeBundle\WebfactoryShortcodeBundle(),
34-
// ...
35-
);
36-
// ...
37-
}
38-
```
39-
40-
For Symfony >= 4:
41-
4220
```php
4321
<?php
4422
// config/bundles.php
@@ -56,131 +34,78 @@ public function registerBundles()
5634

5735
## Usage
5836

59-
### Defining your own shortcodes
37+
### Twig Filter
6038

61-
The easiest way is to add one service for each shortcode in your services definition:
39+
The bundle will set up a `shortcodes` Twig filter. What you pass through this filter will be processed by the `Processor` class (see [docs](https://github.com/thunderer/Shortcode#processing)).
6240

63-
```xml
64-
<service id="webfactory.shortcode.your-shortcode-name" parent="Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler.inline" class="Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler">
65-
<argument index="1">reference-to-your-replacement-controller</argument>
66-
<tag name="webfactory.shortcode" shortcode="your-shortcode-name"/>
67-
</service>
41+
```twig
42+
{% apply shortcodes %}
43+
[image url="https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png"]
44+
[text color="red"]This is red text.[/text]
45+
{% endapply %}
46+
{{ some_content |shortcodes }}
6847
```
6948

70-
The parent ```Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler.inline``` will use
71-
inline rendering while the parent ```Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler.esi``` will use [ESI rendering](https://symfony.com/doc/current/http_cache/esi.html).
72-
73-
ESI may be nice for caching but comes with a problem: ESI embeds controller actions by calling a special internal `_fragment`-URL and needs to somehow serialize all parameters for an action in this URL. This works well for scalar values but neither for objects nor arrays of scalar values. But for context sensitive shortcodes, we pass the request attributes to the embedded controller action. And these request attributes might contain objects, e.g. the result object of a ParamConverter. This can lead to hard to debug errors, especially when recursion comes into play.
74-
75-
Also, logging needs more configuration (explained in the Logging section) with ESI.
49+
### Using Controllers as Shortcode Handlers
7650

77-
The ```reference-to-your-replacement-controller``` could be a string like ```AppBundle\Controller\EmbeddedImageController::showAction```
78-
or if use controllers as services, something like ```AppBundle\Controller\EmbeddedImageController:showAction```. We recommend
79-
using several controllers grouped by feature with only a few actions to keep things simple and unit testable, instead of
80-
one huge ShortcodeController for all shortcodes. But of course, that's up to you.
51+
This bundle comes with a helper class that allows to use Symfony's [Fragment Sub-Framework](https://symfony.com/blog/new-in-symfony-2-2-the-new-fragment-sub-framework) and the technique of [embedding controllers](https://symfony.com/doc/current/templates.html#embedding-controllers) to have controllers generate the replacement output for shortcodes.
8152

82-
Finally ```your-shortcode-name``` is the name the users can use in their text inside the squared bracktes. Anything
83-
after the name in the suqared brackets wll be considered as parameters that will be passed onto the controller.
53+
To give an example, assume the following configuration:
8454

85-
### Full example
86-
87-
To allow a user input of ```[image url="https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png"]``` to be replaced
88-
with HTML markup for this image, use the twig filter "shortcodes" on the user input:
89-
90-
```twig
91-
{# user-generated-comment.html.twig #}
92-
<div class="comment">
93-
{{ comment |shortcodes }}
94-
</div>
55+
```yaml
56+
# config.yml
57+
webfactory_shortcodes:
58+
shortcodes:
59+
image: AppBundle\Controller\EmbeddedImageController::show
9560
```
9661
97-
Then, write a service definition like this:
62+
Then, when doing something like this in Twig:
9863
99-
```xml
100-
<?xml version="1.0" ?>
101-
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
102-
<services>
103-
104-
<!-- ... -->
105-
106-
<service id="webfactory.shortcode.image" parent="Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler.inline" class="Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler">
107-
<argument index="1">AppBundle\Controller\EmbeddedImageController:showAction</argument>
108-
<tag name="webfactory.shortcode" shortcode="image"/>
109-
</service>
110-
111-
<service id="AppBundle\Controller\EmbeddedImageController">
112-
<argument type="service" id="templating" />
113-
</service>
114-
115-
<!-- ... -->
116-
117-
</services>
118-
</container>
64+
```twig
65+
{{ '[image url="https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png"]' |shortcodes }}
11966
```
12067

121-
A controller like this:
68+
... the `AppBundle\Controller\EmbeddedImageController::show()` controller method will be called. Additional shortcode attributes, like `url` in the above example, will be passed as parameters to the controller. The response returned by the controller will be used to replace the shortcode in the given content. The controller can generate the response directly, or use Twig to render a template to create it.
12269

123-
```php
124-
<?php
125-
// src/AppBundle/Controller/EmbeddedImageController.php
70+
#### Rendering with Edge Side Includes
12671

127-
namespace AppBundle\Controller;
72+
You can also use [ESI rendering](https://symfony.com/doc/current/http_cache/esi.html) for particular shortcodes. The advantage of ESI is that single shortcode replacements can be stored in edge caches and/or reverse proxies like Varnish and possibly be reused on multiple pages.
12873

129-
use Symfony\Bundle\TwigBundle\TwigEngine;
130-
use Symfony\Component\HttpFoundation\Response;
74+
⚠️ Take care: Due to the way ESI works, the (master) `Request` visible to controllers is no longer the one where the shortcode was used. Keep that in mind wehn you, for example, want to log the URLs where shortcodes are beiung used.
13175

132-
final class EmbeddedImageController
133-
{
134-
/** @var TwigEngine */
135-
private $twigEngine;
76+
To use ESI-based embedding for a particular shortcode, use the following configuration:
13677

137-
public function __construct(TwigEngine $twigEngine)
138-
{
139-
$this->twigEngine = $twigEngine;
140-
}
141-
142-
public function showAction(string $url): Response
143-
{
144-
if (!$url) {
145-
throw new \RuntimeException('No url provided');
146-
}
147-
148-
return $this->twigEngine->renderResponse('@App/EmbeddedImage/show.html.twig', ['url' => $url]);
149-
}
150-
}
78+
```yaml
79+
# config.yml
80+
webfactory_shortcodes:
81+
shortcodes:
82+
image:
83+
controller: AppBundle\Controller\EmbeddedImageController::showAction
84+
method: esi
15185
```
15286
153-
And finally a twig template like this:
87+
### Registering Handlers as Services
15488
155-
```twig
156-
{# src/Ressources/views/EmbeddedImage/show.html.twig #}
157-
<div class="shortcode-container">
158-
<img src="{{ url }}" />
159-
</div>
89+
In the [thunderer/Shortcode](https://github.com/thunderer/Shortcode) package, _handlers_ transform shortcodes into desired replacements. You can register services from the Symfony Dependency Injection Container to be used as shortcode handlers by tagging them with `webfactory.shortcode` and adding a `shortcode` attribute to the tag indicating the shortcode name.
90+
91+
```yaml
92+
services:
93+
My\Shortcode\Handler\Service:
94+
tags:
95+
- { name: 'webfactory.shortcode', shortcode: 'my-shortcode-name' }
16096
```
16197

162-
### Activating the Shortcode Guide
98+
### Removing `<p>` Tags around Shortcodes
16399

164-
The optional shortcode guide is a controller providing an overview page of the configured shortcodes and a detail page
165-
for each shortcode including a rendered example. Activate it in three simple steps:
100+
By default, the `RemoveWrappingParagraphElementsEventHandler` contained in this bundle will be used to remove `<p>...</p>` tags around shortcodes, if the shortcode is the only text content in that paragraph.
166101

167-
At first, include the controller service definition. It is located at ```webfactory/shortcode-bundle/Resources/config/guide.xml```.
168-
You can easily import it from your own configurations, just have a think about the correct environment. E.g.:
102+
## Activating the Shortcode Guide
169103

170-
```xml
171-
<!-- src/AppBundle/Resources/config/shortcodes.xml -->
172-
<?xml version="1.0" ?>
173-
<container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
174-
<imports>
175-
<import resource="../../../../vendor/webfactory/shortcode-bundle/Resources/config/guide.xml"/>
176-
</imports>
104+
The optional Shortcode Guide is a controller providing an overview page of all configured shortcodes. For every shortcode, there is also a detail page including a rendered example.
177105

178-
<!-- your shortcode services -->
179-
</container>
180-
```
106+
To use the Shortcode Guide, include the routing configuration from `@WebfactoryShortcodeBundle/Resources/config/guide-routing.xml`.
181107

182-
Secondly, include the routes located at ```@WebfactoryShortcodeBundle/Resources/config/guide-routing.xml```, again
183-
considering the environment. Maybe you want to restrict access in your security configuration.
108+
⚠️ You probably want to do this only for your Symfony `dev` and/or `test` environment, and possibly restrict access in your security configuration in addition to that.
184109

185110
```yaml
186111
# src/routing.yml
@@ -189,41 +114,27 @@ _shortcode-guide:
189114
resource: "@WebfactoryShortcodeBundle/Resources/config/guide-routing.xml"
190115
```
191116

192-
Finally, enrich your shortcode tags with description and example attributes for the guide:
193-
194-
```xml
195-
<!-- src/AppBundle/Resources/config/shortcodes.xml -->
196-
<?xml version="1.0" ?>
197-
<container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
198-
199-
<!-- import guide.xml -->
117+
With the route prefix defined as above, visit `/shortcodes/` to see a list of all defined shortcodes. If you want to add descriptions to shortcodes and/or provide the example shortcode that shall be rendered on the detail page, you can add this information when configuring shortcodes:
200118

201-
<services>
202-
<service id="webfactory.shortcode.image" parent="Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler.inline" class="Webfactory\ShortcodeBundle\Handler\EmbeddedShortcodeHandler">
203-
<argument index="1">AppBundle\Controller\EmbeddedImageController:showAction</argument>
204-
<tag
205-
name="webfactory.shortcode"
206-
shortcode="image"
207-
description="Renders an image tag with the {url} as it's source."
208-
example="image url=https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png"
209-
/>
210-
</service>
211-
</services>
212-
</container>
119+
```yaml
120+
# config.yml
121+
webfactory_shortcodes:
122+
shortcodes:
123+
image:
124+
controller: AppBundle\Controller\EmbeddedImageController::showAction
125+
description: "Renders an image tag with the {url} as it's source."
126+
example: "image url=https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png"
213127
```
214128

215-
With the route prefix defined as above, call ```/shortcodes/``` to get the list of shortcodes and follow the links to the
216-
detail pages.
217-
218-
### Configuration
129+
### Other Configuration Parameters
219130

220131
In most cases, the default values should work fine. But you might want to configure something else, e.g. if the default
221132
parser needs too much memory for a large snippet. See thunderer's documentation on [parsing](https://github.com/thunderer/Shortcode#parsing)
222133
and [configuration](https://github.com/thunderer/Shortcode#configuration) so you understand the advantages,
223134
disadvantages and limitations:
224135

225136
```yaml
226-
// config.yml
137+
# config.yml
227138
228139
webfactory_shortcode:
229140
parser: 'regex' # default: regular
@@ -323,4 +234,4 @@ This bundle was started at webfactory GmbH, Bonn.
323234
- <https://www.webfactory.de>
324235
- <https://twitter.com/webfactory>
325236

326-
Copyright 2018-2021 webfactory GmbH, Bonn. Code released under [the MIT license](LICENSE).
237+
Copyright 2018-2022 webfactory GmbH, Bonn. Code released under [the MIT license](LICENSE).

composer.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,23 @@
88
"ext-json": "*",
99
"ext-mbstring": "*",
1010
"psr/log": "^1.0.2",
11-
"symfony/config": "^3.4.11|^4.0|^5.0",
12-
"symfony/dependency-injection": "^3.4.11|^4.0|^5.0",
13-
"symfony/http-foundation": "^3.4.11|^4.0|^5.0",
14-
"symfony/http-kernel": "^3.4.11|^4.0|^5.0",
11+
"symfony/config": "^4.4|^5.0",
12+
"symfony/dependency-injection": "^4.4|^5.0",
13+
"symfony/http-foundation": "^4.4|^5.0",
14+
"symfony/http-kernel": "^4.4|^5.0",
1515
"thunderer/shortcode": "^0.6.5|^0.7",
1616
"twig/twig": "^1.34|^2.0|^3.0"
1717
},
1818

1919
"require-dev": {
2020
"phpunit/phpunit": "^8.5|^9.5",
21-
"symfony/expression-language": "^3.4.11|^4.0|^5.0",
22-
"symfony/framework-bundle": "^3.4.23|^4.2.4|^5.0",
21+
"symfony/browser-kit": "^4.4|^5.4",
22+
"symfony/expression-language": "^4.4|^5.0",
23+
"symfony/framework-bundle": "^4.4|^5.0",
2324
"symfony/phpunit-bridge": ">= 6.0",
24-
"symfony/routing": "^3.4.11|^4.0|^5.0",
25-
"symfony/templating": "^3.4.11|^4.0|^5.0",
26-
"symfony/twig-bundle": "^3.4.11|^4.0|^5.0",
27-
"symfony/yaml": "^3.4.11|^4.0|^5.0"
25+
"symfony/routing": "^4.4|^5.0",
26+
"symfony/twig-bundle": "^4.4|^5.0",
27+
"symfony/yaml": "^4.4|^5.0"
2828
},
2929

3030
"autoload": {

0 commit comments

Comments
 (0)