Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
use OpenNIT\Tickets\TicketManager;
/**
* Deckt ab, dass eine @-Erwähnung bis zur Benachrichtigung durchkommt.
*
* Die Kennung der erwähnten Person reist als `data-uid` am Erwähnungs-Token
* durch die Bereinigung des eingehenden HTML. Überlebt sie diesen Schritt
* nicht, findet die Auswertung nichts mehr vor und die Erwähnung bleibt
* folgenlos – ohne jede Fehlermeldung. Festgehalten wird deshalb beides: dass
* das Attribut erhalten bleibt und dass es nichts anderes transportieren kann.
*/
return static function (): void {
$quelle = static fn(): string => (string)file_get_contents(__DIR__ . '/../src/Tickets/TicketManager.php');
test('Die Kennung einer Erwaehnung ueberlebt die Bereinigung', function () {
$html = '<p>Hallo <span class="ts-mention" data-uid="42" contenteditable="false">@Max Mustermann</span>, schau mal.</p>';
$rein = TicketManager::sanitizeIncomingHtml($html);
assertTrue(preg_match('/data-uid=["\']42["\']/', $rein) === 1,
'Die Kennung der erwähnten Person fehlt nach der Bereinigung: ' . $rein);
// Ohne die Klasse verliert das Token seine Hervorhebung in der Anzeige.
assertTrue(str_contains($rein, 'ts-mention'), 'Die Klasse des Tokens fehlt');
assertTrue(str_contains($rein, '@Max Mustermann'), 'Der angezeigte Name fehlt');
});
test('Mehrere Erwaehnungen bleiben einzeln erhalten', function () {
$html = '<p><span class="ts-mention" data-uid="7">@A</span> und '
. '<span class="ts-mention" data-uid="9">@B</span></p>';
$rein = TicketManager::sanitizeIncomingHtml($html);
preg_match_all('/data-uid=["\'](\d+)["\']/', $rein, $m);
assertSame(['7', '9'], $m[1], 'Nicht alle Kennungen sind erhalten: ' . $rein);
});
test('Die Kennung nimmt nur Ziffern an', function () {
foreach (['42 onmouseover=x', '', 'abc', '4.2', ' 42'] as $wert) {
$rein = TicketManager::sanitizeIncomingHtml(
'<p><span data-uid="' . $wert . '">@X</span></p>'
);
assertFalse(str_contains($rein, 'data-uid'),
'Der Wert "' . $wert . '" hätte entfernt werden müssen: ' . $rein);
}
});
test('Nur das Erwaehnungs-Token traegt die Kennung', function () {
// An anderen Elementen hat das Attribut keinen Zweck.
$rein = TicketManager::sanitizeIncomingHtml('<div data-uid="7">x</div><p data-uid="8">y</p>');
assertFalse(str_contains($rein, 'data-uid'), 'Fremde Elemente tragen die Kennung: ' . $rein);
// Andere data-Attribute bleiben weiterhin draußen.
$rein = TicketManager::sanitizeIncomingHtml('<p><span data-foo="1" data-uid="9">@Y</span></p>');
assertFalse(str_contains($rein, 'data-foo'), 'Ein fremdes data-Attribut kommt durch: ' . $rein);
assertTrue(str_contains($rein, 'data-uid="9"'), 'Die Kennung fehlt: ' . $rein);
});
test('Die Bereinigung bleibt im Uebrigen streng', function () {
$rein = TicketManager::sanitizeIncomingHtml(
'<p><span data-uid="5" onclick="alert(1)">@Z</span><script>alert(1)</script></p>'
);
assertFalse(str_contains($rein, 'onclick'), 'Ein Event-Handler kommt durch: ' . $rein);
assertFalse(str_contains($rein, 'script'), 'Ein Script kommt durch: ' . $rein);
assertTrue(str_contains($rein, 'data-uid="5"'), 'Die Kennung fehlt: ' . $rein);
});
test('Eine Erwaehnung wirkt in der internen Notiz wie in der Antwort', function () use ($quelle) {
$text = $quelle();
$von = strpos($text, 'public function addStaffReply(');
assertTrue($von !== false, 'addStaffReply() nicht gefunden');
$bis = strpos($text, "\n }", $von);
$rumpf = substr($text, $von, $bis - $von);
assertTrue(str_contains($rumpf, '$this->notifyMentions('), 'Die Erwähnungen werden nicht ausgewertet');
// Der Aufruf gehört hinter den Block, der nur für sichtbare Antworten
// gilt – sonst bliebe die interne Notiz stumm.
$internBlock = strpos($rumpf, 'if (!$isInternal) {');
$mention = strpos($rumpf, '$this->notifyMentions(');
assertTrue($internBlock !== false, 'Die Fallunterscheidung fehlt');
assertTrue($mention > $internBlock, 'Die Auswertung steht vor der Fallunterscheidung');
$zwischen = substr($rumpf, $internBlock, $mention - $internBlock);
// Der Block schließt vor dem Aufruf: gleich viele geschweifte Klammern
// auf wie zu bedeutet, dass die Auswertung außerhalb steht.
assertSame(substr_count($zwischen, '{'), substr_count($zwischen, '}'),
'Die Auswertung liegt innerhalb des Blocks für sichtbare Antworten');
});
test('Die Auswertung sucht genau nach dieser Kennung', function () use ($quelle) {
// Das Muster der Auswertung und das vom Editor erzeugte Attribut
// müssen zueinander passen.
assertTrue(str_contains($quelle(), 'data-uid=["\\\'](\\d+)["\\\']'),
'Das Suchmuster der Auswertung hat sich geändert');
$js = (string)file_get_contents(__DIR__ . '/../public/assets/js/ticket-mention.js');
assertTrue(str_contains($js, "setAttribute('data-uid'"), 'Der Editor setzt die Kennung nicht mehr');
});
};