3 namespace wcf\system\html\output\node
;
5 use GuzzleHttp\Psr7\Exception\MalformedUriException
;
6 use GuzzleHttp\Psr7\Uri
;
7 use GuzzleHttp\Psr7\UriComparator
;
8 use Psr\Http\Message\UriInterface
;
9 use wcf\system\application\ApplicationHandler
;
10 use wcf\system\html\node\AbstractHtmlNodeProcessor
;
11 use wcf\system\request\RouteHandler
;
13 use wcf\util\FileUtil
;
14 use wcf\util\StringUtil
;
19 * @author Alexander Ebert
20 * @copyright 2001-2019 WoltLab GmbH
21 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
24 class HtmlOutputNodeA
extends AbstractHtmlOutputNode
29 protected $tagName = 'a';
34 public function process(array $elements, AbstractHtmlNodeProcessor
$htmlNodeProcessor)
36 /** @var \DOMElement $element */
37 foreach ($elements as $element) {
39 $href = new Uri($element->getAttribute('href'));
40 } catch (MalformedUriException
) {
41 // If the link href is not a valid URI we drop the entire link.
42 DOMUtil
::removeNode($element, true);
47 if (ApplicationHandler
::getInstance()->isInternalURL($href->__toString())) {
48 $href = $href->withScheme(RouteHandler
::secureConnection() ?
'https' : 'http');
50 $element->setAttribute(
55 /** @var HtmlOutputNodeProcessor $htmlNodeProcessor */
56 self
::markLinkAsExternal($element, $htmlNodeProcessor->getHtmlProcessor()->enableUgc
);
59 $value = StringUtil
::trim($element->textContent
);
61 if ($element->childElementCount
=== 0) {
62 // Discard empty links, these were sometimes created by the
63 // previous editor when editing links.
64 DOMUtil
::removeNode($element);
68 } else if ($this->isSuspiciousValue($value, $href)) {
69 $value = $href->__toString();
72 if ($this->outputType
=== 'text/html' ||
$this->outputType
=== 'text/simplified-html') {
73 if ($value === $href->__toString()) {
74 while ($element->childNodes
->length
) {
75 DOMUtil
::removeNode($element->childNodes
->item(0));
79 if (\
mb_strlen($newValue) > 60) {
81 // The value returned by `Uri::__toString()` can be malformed.
82 // https://github.com/guzzle/psr7/issues/583
83 $uri = new Uri($newValue);
84 } catch (MalformedUriException
) {
88 $schemeHost = Uri
::composeComponents(
95 $pathQueryFragment = Uri
::composeComponents(
102 if (\
mb_strlen($pathQueryFragment) > 35) {
103 $pathQueryFragment = \
mb_substr($pathQueryFragment, 0, 15) . StringUtil
::HELLIP
. \
mb_substr($pathQueryFragment, -15);
105 $newValue = $schemeHost . $pathQueryFragment;
108 $element->appendChild(
109 $element->ownerDocument
->createTextNode($newValue)
112 } elseif ($this->outputType
=== 'text/plain') {
113 if ($value !== $href->__toString()) {
114 $text = $value . ' [URL:' . $href->__toString() . ']';
116 $text = $href->__toString();
119 $htmlNodeProcessor->replaceElementWithText($element, $text, false);
125 * Returns whether the given link value is suspicious with regard
126 * to the actual link target.
128 * A value is considered suspicious if it is a cross-origin URI (i.e.
129 * if one of host, port or scheme differs).
131 * @see \GuzzleHttp\Psr7\UriComparator::isCrossOrigin()
133 private function isSuspiciousValue(string $value, UriInterface
$href): bool
135 if (!\
preg_match(FileUtil
::LINK_REGEX
, $value)) {
140 $value = new Uri($value);
141 } catch (MalformedUriException
) {
145 return UriComparator
::isCrossOrigin($href, $value);
149 * Marks an element as external.
151 * @param \DOMElement $element
154 public static function markLinkAsExternal(\DOMElement
$element, $isUgc = false)
156 $element->setAttribute('class', 'externalURL');
159 if (EXTERNAL_LINK_TARGET_BLANK
) {
162 $element->setAttribute('target', '_blank');
168 $element->setAttribute('rel', $rel);
170 // If the link contains only a single image that is floated to the right,
171 // then the external link marker is misaligned. Inheriting the CSS class
172 // will cause the link marker to behave properly.
173 if ($element->childNodes
->length
=== 1) {
174 $child = $element->childNodes
->item(0);
175 if ($child instanceof \DOMElement
&& $child->nodeName
=== 'img') {
178 '~\b(?P<className>messageFloatObject(?:Left|Right))\b~',
179 $child->getAttribute('class'),
183 $element->setAttribute('class', $element->getAttribute('class') . ' ' . $match['className']);