Skip to content

Commit 2d17c01

Browse files
authored
Merge pull request tgalopin#16 from tgalopin/details-ext
Add a new extension for "details" and "summary" tags
2 parents 0912523 + 74fd1fa commit 2d17c01

File tree

10 files changed

+222
-4
lines changed

10 files changed

+222
-4
lines changed

docs/1-getting-started.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ enable to allow specific tags in the content (read the next part to learn more a
3636
## Extensions
3737

3838
Extensions are a way to quickly add sets of tags to the whitelist of allowed tags.
39-
There are 7 core extensions that you can enable by adding them in your configuration:
39+
There are 8 core extensions that you can enable by adding them in your configuration:
4040

4141
```php
4242
$sanitizer = HtmlSanitizer\Sanitizer::create([
43-
'extensions' => ['basic', 'code', 'image', 'list', 'table', 'iframe', 'extra'],
43+
'extensions' => ['basic', 'code', 'image', 'list', 'table', 'iframe', 'details', 'extra'],
4444
]);
4545
$safeHtml = $sanitizer->sanitize($untrustedHtml);
4646
```
@@ -57,6 +57,7 @@ Here is the list of tags each extension allow:
5757
- **image** allows the insertion of images: `img`
5858
- **code** allows the insertion of code blocks: `pre`, `code`
5959
- **iframe** allows the insertion of iframes: `iframe`
60+
- **details** allows the insertion of view/hide blocks: `details`, `summary`
6061
- **extra** allows the insertion of the following tags: `abbr`, `caption`, `hr`, `rp`, `rt`, `ruby`
6162

6263
> Note: sensible attributes are allowed by default for each tag (for instance, the `src` attribute is

docs/3-configuration-reference.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ $sanitizer = HtmlSanitizer\Sanitizer::create([
1717
/*
1818
* List of extensions to enable on this sanitizer.
1919
*/
20-
'extensions' => ['basic', 'list', 'table', 'image', 'code', 'iframe', 'extra'],
20+
'extensions' => ['basic', 'list', 'table', 'image', 'code', 'iframe', 'details', 'extra'],
2121

2222
/*
2323
* Configuration for specific tags.
@@ -69,6 +69,9 @@ $sanitizer = HtmlSanitizer\Sanitizer::create([
6969
'del' => [
7070
'allowed_attributes' => [],
7171
],
72+
'details' => [
73+
'allowed_attributes' => ['open'],
74+
],
7275
'div' => [
7376
'allowed_attributes' => [],
7477
],
@@ -190,6 +193,9 @@ $sanitizer = HtmlSanitizer\Sanitizer::create([
190193
'sub' => [
191194
'allowed_attributes' => [],
192195
],
196+
'summary' => [
197+
'allowed_attributes' => [],
198+
],
193199
'sup' => [
194200
'allowed_attributes' => [],
195201
],
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the HTML sanitizer project.
5+
*
6+
* (c) Titouan Galopin <galopintitouan@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace HtmlSanitizer\Extension\Details;
13+
14+
use HtmlSanitizer\Extension\ExtensionInterface;
15+
16+
/**
17+
* @author Titouan Galopin <galopintitouan@gmail.com>
18+
*
19+
* @final
20+
*/
21+
class DetailsExtension implements ExtensionInterface
22+
{
23+
public function getName(): string
24+
{
25+
return 'details';
26+
}
27+
28+
public function createNodeVisitors(array $config = []): array
29+
{
30+
return [
31+
'details' => new NodeVisitor\DetailsNodeVisitor($config['tags']['details'] ?? []),
32+
'summary' => new NodeVisitor\SummaryNodeVisitor($config['tags']['summary'] ?? []),
33+
];
34+
}
35+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the HTML sanitizer project.
5+
*
6+
* (c) Titouan Galopin <galopintitouan@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace HtmlSanitizer\Extension\Details\Node;
13+
14+
use HtmlSanitizer\Node\AbstractTagNode;
15+
use HtmlSanitizer\Node\HasChildrenTrait;
16+
17+
/**
18+
* @author Titouan Galopin <galopintitouan@gmail.com>
19+
*
20+
* @final
21+
*/
22+
class DetailsNode extends AbstractTagNode
23+
{
24+
use HasChildrenTrait;
25+
26+
public function getTagName(): string
27+
{
28+
return 'details';
29+
}
30+
31+
public function render(): string
32+
{
33+
$isOpen = null !== $this->getAttribute('open');
34+
35+
return '<details'.($isOpen ? ' open="open"' : '').'>'.$this->renderChildren().'</details>';
36+
}
37+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the HTML sanitizer project.
5+
*
6+
* (c) Titouan Galopin <galopintitouan@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace HtmlSanitizer\Extension\Details\Node;
13+
14+
use HtmlSanitizer\Node\AbstractTagNode;
15+
use HtmlSanitizer\Node\HasChildrenTrait;
16+
17+
/**
18+
* @author Titouan Galopin <galopintitouan@gmail.com>
19+
*
20+
* @final
21+
*/
22+
class SummaryNode extends AbstractTagNode
23+
{
24+
use HasChildrenTrait;
25+
26+
public function getTagName(): string
27+
{
28+
return 'summary';
29+
}
30+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the HTML sanitizer project.
5+
*
6+
* (c) Titouan Galopin <galopintitouan@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace HtmlSanitizer\Extension\Details\NodeVisitor;
13+
14+
use HtmlSanitizer\Extension\Details\Node\DetailsNode;
15+
use HtmlSanitizer\Model\Cursor;
16+
use HtmlSanitizer\Node\NodeInterface;
17+
use HtmlSanitizer\Visitor\AbstractNodeVisitor;
18+
use HtmlSanitizer\Visitor\HasChildrenNodeVisitorTrait;
19+
use HtmlSanitizer\Visitor\NamedNodeVisitorInterface;
20+
21+
/**
22+
* @author Titouan Galopin <galopintitouan@gmail.com>
23+
*
24+
* @final
25+
*/
26+
class DetailsNodeVisitor extends AbstractNodeVisitor implements NamedNodeVisitorInterface
27+
{
28+
use HasChildrenNodeVisitorTrait;
29+
30+
protected function getDomNodeName(): string
31+
{
32+
return 'details';
33+
}
34+
35+
public function getDefaultAllowedAttributes(): array
36+
{
37+
return ['open'];
38+
}
39+
40+
protected function createNode(\DOMNode $domNode, Cursor $cursor): NodeInterface
41+
{
42+
return new DetailsNode($cursor->node);
43+
}
44+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the HTML sanitizer project.
5+
*
6+
* (c) Titouan Galopin <galopintitouan@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace HtmlSanitizer\Extension\Details\NodeVisitor;
13+
14+
use HtmlSanitizer\Extension\Details\Node\SummaryNode;
15+
use HtmlSanitizer\Model\Cursor;
16+
use HtmlSanitizer\Node\NodeInterface;
17+
use HtmlSanitizer\Visitor\AbstractNodeVisitor;
18+
use HtmlSanitizer\Visitor\HasChildrenNodeVisitorTrait;
19+
use HtmlSanitizer\Visitor\NamedNodeVisitorInterface;
20+
21+
/**
22+
* @author Titouan Galopin <galopintitouan@gmail.com>
23+
*
24+
* @final
25+
*/
26+
class SummaryNodeVisitor extends AbstractNodeVisitor implements NamedNodeVisitorInterface
27+
{
28+
use HasChildrenNodeVisitorTrait;
29+
30+
protected function getDomNodeName(): string
31+
{
32+
return 'summary';
33+
}
34+
35+
protected function createNode(\DOMNode $domNode, Cursor $cursor): NodeInterface
36+
{
37+
return new SummaryNode($cursor->node);
38+
}
39+
}

src/Sanitizer.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use HtmlSanitizer\Extension\Basic\BasicExtension;
1515
use HtmlSanitizer\Extension\Code\CodeExtension;
16+
use HtmlSanitizer\Extension\Details\DetailsExtension;
1617
use HtmlSanitizer\Extension\Extra\ExtraExtension;
1718
use HtmlSanitizer\Extension\Iframe\IframeExtension;
1819
use HtmlSanitizer\Extension\Image\ImageExtension;
@@ -73,6 +74,7 @@ public static function create(array $config): SanitizerInterface
7374
$builder->registerExtension(new CodeExtension());
7475
$builder->registerExtension(new TableExtension());
7576
$builder->registerExtension(new IframeExtension());
77+
$builder->registerExtension(new DetailsExtension());
7678
$builder->registerExtension(new ExtraExtension());
7779

7880
return $builder->build($config);

tests/EmptySanitizerTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ public function provideFixtures(): array
6060
'<del class="foo">Lorem ipsum</del>',
6161
'Lorem ipsum',
6262
],
63+
[
64+
'<details class="foo">Lorem ipsum</details>',
65+
'Lorem ipsum',
66+
],
67+
[
68+
'<details class="foo" open>Lorem ipsum</details>',
69+
'Lorem ipsum',
70+
],
6371
[
6472
'<div class="foo">Lorem ipsum dolor sit amet, consectetur adipisicing elit.</div>',
6573
'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
@@ -180,6 +188,10 @@ public function provideFixtures(): array
180188
'<strong class="foo">Lorem ipsum</strong>',
181189
'Lorem ipsum',
182190
],
191+
[
192+
'<summary class="foo">Lorem ipsum</summary>',
193+
'Lorem ipsum',
194+
],
183195
[
184196
'<b class="foo">Lorem ipsum</b>',
185197
'Lorem ipsum',

tests/FullSanitizerTest.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class FullSanitizerTest extends AbstractSanitizerTest
1919
public function createSanitizer(): SanitizerInterface
2020
{
2121
return Sanitizer::create([
22-
'extensions' => ['basic', 'code', 'image', 'list', 'table', 'iframe', 'extra'],
22+
'extensions' => ['basic', 'code', 'details', 'image', 'list', 'table', 'iframe', 'extra'],
2323
'tags' => [
2424
'a' => [
2525
'allowed_hosts' => ['trusted.com', 'external.com'],
@@ -87,6 +87,14 @@ public function provideFixtures(): array
8787
'<del class="foo">Lorem ipsum</del>',
8888
'<del>Lorem ipsum</del>',
8989
],
90+
[
91+
'<details class="foo">Lorem ipsum</details>',
92+
'<details>Lorem ipsum</details>',
93+
],
94+
[
95+
'<details class="foo" open>Lorem ipsum</details>',
96+
'<details open="open">Lorem ipsum</details>',
97+
],
9098
[
9199
'<div class="foo">Lorem ipsum dolor sit amet, consectetur adipisicing elit.</div>',
92100
'<div>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</div>',
@@ -223,6 +231,10 @@ public function provideFixtures(): array
223231
'<strong class="foo">Lorem ipsum</strong>',
224232
'<strong>Lorem ipsum</strong>',
225233
],
234+
[
235+
'<summary class="foo">Lorem ipsum</summary>',
236+
'<summary>Lorem ipsum</summary>',
237+
],
226238
[
227239
'<b class="foo">Lorem ipsum</b>',
228240
'<strong>Lorem ipsum</strong>',

0 commit comments

Comments
 (0)