1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Groundskeeper\Tokens\Elements; |
4
|
|
|
|
5
|
|
|
use Groundskeeper\Tokens\Attribute; |
6
|
|
|
use Groundskeeper\Tokens\ElementTypes\ClosedElement; |
7
|
|
|
use Groundskeeper\Tokens\ElementTypes\MetadataContent; |
8
|
|
|
use Psr\Log\LoggerInterface; |
9
|
|
|
|
10
|
|
|
class Link extends ClosedElement implements MetadataContent |
11
|
|
|
{ |
12
|
6 |
|
protected function getAllowedAttributes() |
13
|
|
|
{ |
14
|
|
|
$linkAllowedAttributes = array( |
15
|
6 |
|
'/^href$/i' => Attribute::URI, |
16
|
|
|
'/^crossorigin$/i' => Attribute::CS_STRING, |
17
|
|
|
'/^rel$/i' => Attribute::CI_SSENUM . '("alternate","author","help","icon","license","next","pingback","prefetch","prev","search","stylesheet")', |
18
|
|
|
'/^media$/i' => Attribute::CI_STRING, |
19
|
|
|
'/^hreflang$/i' => Attribute::CS_STRING, |
20
|
|
|
'/^type$/i' => Attribute::CI_STRING, |
21
|
|
|
'/^sizes$/i' => Attribute::CI_STRING |
22
|
|
|
); |
23
|
|
|
|
24
|
6 |
|
return array_merge( |
25
|
6 |
|
$linkAllowedAttributes, |
26
|
6 |
|
parent::getAllowedAttributes() |
27
|
|
|
); |
28
|
|
|
} |
29
|
|
|
|
30
|
6 |
|
protected function removeInvalidSelf(LoggerInterface $logger) : bool |
31
|
|
|
{ |
32
|
|
|
// Must have "href" attribute. |
33
|
6 |
|
if (!$this->hasAttribute('href')) { |
34
|
1 |
|
$logger->debug('Removing ' . $this . '. Requires "href" attribute.'); |
35
|
|
|
|
36
|
1 |
|
return true; |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
// Must have either "rel" or "itemprop" attribute, but not both. |
40
|
5 |
|
$attrCount = 0; |
41
|
5 |
|
foreach ($this->attributes as $attribute) { |
42
|
5 |
|
if ($attribute->getName() === 'rel' || |
43
|
5 |
|
$attribute->getName() === 'itemprop') { |
44
|
4 |
|
++$attrCount; |
45
|
|
|
} |
46
|
|
|
|
47
|
5 |
|
if ($attrCount > 1) { |
48
|
|
|
// If both, then we don't know which one should be kept, |
49
|
|
|
// so we recommend to delete the entire element. |
50
|
1 |
|
$logger->debug('Removing ' . $this . '. Requires either "rel" or "itemprop" attribute, but not both.'); |
51
|
|
|
|
52
|
5 |
|
return true; |
53
|
|
|
} |
54
|
|
|
} |
55
|
|
|
|
56
|
4 |
|
if ($attrCount == 0) { |
57
|
1 |
|
$logger->debug('Removing ' . $this . '. Requires either "rel" or "itemprop" attribute.'); |
58
|
|
|
|
59
|
1 |
|
return true; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
// If inside "body" element, then we check if allowed. |
63
|
3 |
|
$body = new Body($this->configuration, 0, 0, 'body'); |
64
|
3 |
|
if ($this->hasAncestor($body) && !$this->isAllowedInBody()) { |
65
|
1 |
|
$logger->debug('Removing ' . $this . '. Does not have the correct attributes to be allowed inside the "body" element.'); |
66
|
|
|
|
67
|
1 |
|
return true; |
68
|
|
|
} |
69
|
|
|
|
70
|
2 |
|
return false; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Is this element allowed inside a body element? |
75
|
|
|
* |
76
|
|
|
* https://html.spec.whatwg.org/multipage/semantics.html#allowed-in-the-body |
77
|
|
|
* |
78
|
|
|
* @return bool True if allowed. |
79
|
|
|
*/ |
80
|
2 |
|
public function isAllowedInBody() : bool |
81
|
|
|
{ |
82
|
2 |
|
if ($this->hasAttribute('itemprop')) { |
83
|
1 |
|
return true; |
84
|
|
|
} |
85
|
|
|
|
86
|
2 |
|
if ($this->hasAttribute('rel') && |
87
|
2 |
|
($this->attributes['rel']->getValue() === 'pingback' || |
88
|
2 |
|
$this->attributes['rel']->getValue() === 'prefetch' || |
89
|
2 |
|
$this->attributes['rel']->getValue() === 'stylesheet')) { |
90
|
1 |
|
return true; |
91
|
|
|
} |
92
|
|
|
|
93
|
1 |
|
return false; |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
|