Stand: SMTP-Test, Admin-Mail-Tab, Notifiable-Fix, Lazy-Quill

- Fix: Notifiable-Trait zum User-Model hinzugefuegt (behebt notify()-500er)
- Installer: SMTP-Verbindungstest mit EsmtpTransport + Ueberspringen-Link
- Admin: Neuer E-Mail-Tab mit SMTP-Konfiguration + Verbindungstest
- Admin: Lazy Quill-Initialisierung (nur sichtbare Locale wird geladen)
- Uebersetzungen: 17 neue Mail-Keys in allen 6 Sprachen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Rhino
2026-03-02 07:30:37 +01:00
commit 2e24a40d68
9633 changed files with 1300799 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
use League\CommonMark\Delimiter\DelimiterInterface;
/**
* Special marker interface for delimiter processors that return dynamic values from getDelimiterUse()
*
* In order to guarantee linear performance of delimiter processing, the delimiter stack must be able to
* cache the lower bound when searching for a matching opener. This gets complicated for delimiter processors
* that use a dynamic number of characters (like with emphasis and its "multiple of 3" rule).
*/
interface CacheableDelimiterProcessorInterface extends DelimiterProcessorInterface
{
/**
* Returns a cache key of the factors that determine the number of characters to use.
*
* In order to guarantee linear performance of delimiter processing, the delimiter stack must be able to
* cache the lower bound when searching for a matching opener. This lower bound is usually quite simple;
* for example, with quotes, it's just the last opener with that characted. However, this gets complicated
* for delimiter processors that use a dynamic number of characters (like with emphasis and its "multiple
* of 3" rule), because the delimiter length being considered may change during processing because of that
* dynamic logic in getDelimiterUse(). Therefore, we cannot safely cache the lower bound for these dynamic
* processors without knowing the factors that determine the number of characters to use.
*
* At a minimum, this should include the delimiter character, plus any other factors used to determine
* the result of getDelimiterUse(). The format of the string is not important so long as it is unique
* (compared to other processors) and consistent for a given set of factors.
*
* If getDelimiterUse() always returns the same hard-coded value, this method should return just
* the delimiter character.
*/
public function getCacheKey(DelimiterInterface $closer): string;
}

View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
use League\CommonMark\Exception\InvalidArgumentException;
final class DelimiterProcessorCollection implements DelimiterProcessorCollectionInterface
{
/**
* @var array<string,DelimiterProcessorInterface>|DelimiterProcessorInterface[]
*
* @psalm-readonly-allow-private-mutation
*/
private array $processorsByChar = [];
public function add(DelimiterProcessorInterface $processor): void
{
$opening = $processor->getOpeningCharacter();
$closing = $processor->getClosingCharacter();
if ($opening === $closing) {
$old = $this->processorsByChar[$opening] ?? null;
if ($old !== null && $old->getOpeningCharacter() === $old->getClosingCharacter()) {
$this->addStaggeredDelimiterProcessorForChar($opening, $old, $processor);
} else {
$this->addDelimiterProcessorForChar($opening, $processor);
}
} else {
$this->addDelimiterProcessorForChar($opening, $processor);
$this->addDelimiterProcessorForChar($closing, $processor);
}
}
public function getDelimiterProcessor(string $char): ?DelimiterProcessorInterface
{
return $this->processorsByChar[$char] ?? null;
}
/**
* @return string[]
*/
public function getDelimiterCharacters(): array
{
return \array_keys($this->processorsByChar);
}
private function addDelimiterProcessorForChar(string $delimiterChar, DelimiterProcessorInterface $processor): void
{
if (isset($this->processorsByChar[$delimiterChar])) {
throw new InvalidArgumentException(\sprintf('Delim processor for character "%s" already exists', $processor->getOpeningCharacter()));
}
$this->processorsByChar[$delimiterChar] = $processor;
}
private function addStaggeredDelimiterProcessorForChar(string $opening, DelimiterProcessorInterface $old, DelimiterProcessorInterface $new): void
{
if ($old instanceof StaggeredDelimiterProcessor) {
$s = $old;
} else {
$s = new StaggeredDelimiterProcessor($opening, $old);
}
$s->add($new);
$this->processorsByChar[$opening] = $s;
}
public function count(): int
{
return \count($this->processorsByChar);
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
use League\CommonMark\Exception\InvalidArgumentException;
interface DelimiterProcessorCollectionInterface extends \Countable
{
/**
* Add the given delim processor to the collection
*
* @param DelimiterProcessorInterface $processor The delim processor to add
*
* @throws InvalidArgumentException Exception will be thrown if attempting to add multiple processors for the same character
*/
public function add(DelimiterProcessorInterface $processor): void;
/**
* Returns the delim processor which handles the given character if one exists
*/
public function getDelimiterProcessor(string $char): ?DelimiterProcessorInterface;
/**
* Returns an array of delimiter characters who have associated processors
*
* @return string[]
*/
public function getDelimiterCharacters(): array;
}

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
* - (c) John MacFarlane
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
use League\CommonMark\Delimiter\DelimiterInterface;
use League\CommonMark\Node\Inline\AbstractStringContainer;
/**
* Interface for a delimiter processor
*/
interface DelimiterProcessorInterface
{
/**
* Returns the character that marks the beginning of a delimited node.
*
* This must not clash with any other processors being added to the environment.
*/
public function getOpeningCharacter(): string;
/**
* Returns the character that marks the ending of a delimited node.
*
* This must not clash with any other processors being added to the environment.
*
* Note that for a symmetric delimiter such as "*", this is the same as the opening.
*/
public function getClosingCharacter(): string;
/**
* Minimum number of delimiter characters that are needed to active this.
*
* Must be at least 1.
*/
public function getMinLength(): int;
/**
* Determine how many (if any) of the delimiter characters should be used.
*
* This allows implementations to decide how many characters to be used
* based on the properties of the delimiter runs. An implementation can also
* return 0 when it doesn't want to allow this particular combination of
* delimiter runs.
*
* IMPORTANT: Unless this method returns the same hard-coded value in all cases,
* you MUST implement the CacheableDelimiterProcessorInterface interface instead.
*
* @param DelimiterInterface $opener The opening delimiter run
* @param DelimiterInterface $closer The closing delimiter run
*/
public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int;
/**
* Process the matched delimiters, e.g. by wrapping the nodes between opener
* and closer in a new node, or appending a new node after the opener.
*
* Note that removal of the delimiter from the delimiter nodes and detaching
* them is done by the caller.
*
* @param AbstractStringContainer $opener The node that contained the opening delimiter
* @param AbstractStringContainer $closer The node that contained the closing delimiter
* @param int $delimiterUse The number of delimiters that were used
*/
public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse): void;
}

View File

@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
/*
* This file is part of the league/commonmark package.
*
* (c) Colin O'Dell <colinodell@gmail.com>
*
* Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
* - (c) Atlassian Pty Ltd
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\CommonMark\Delimiter\Processor;
use League\CommonMark\Delimiter\DelimiterInterface;
use League\CommonMark\Exception\InvalidArgumentException;
use League\CommonMark\Node\Inline\AbstractStringContainer;
/**
* An implementation of DelimiterProcessorInterface that dispatches all calls to two or more other DelimiterProcessors
* depending on the length of the delimiter run. All child DelimiterProcessors must have different minimum
* lengths. A given delimiter run is dispatched to the child with the largest acceptable minimum length. If no
* child is applicable, the one with the largest minimum length is chosen.
*
* @internal
*/
final class StaggeredDelimiterProcessor implements DelimiterProcessorInterface
{
/** @psalm-readonly */
private string $delimiterChar;
/** @psalm-readonly-allow-private-mutation */
private int $minLength = 0;
/**
* @var array<int, DelimiterProcessorInterface>|DelimiterProcessorInterface[]
*
* @psalm-readonly-allow-private-mutation
*/
private array $processors = []; // keyed by minLength in reverse order
public function __construct(string $char, DelimiterProcessorInterface $processor)
{
$this->delimiterChar = $char;
$this->add($processor);
}
public function getOpeningCharacter(): string
{
return $this->delimiterChar;
}
public function getClosingCharacter(): string
{
return $this->delimiterChar;
}
public function getMinLength(): int
{
return $this->minLength;
}
/**
* Adds the given processor to this staggered delimiter processor
*
* @throws InvalidArgumentException if attempting to add another processors for the same character and minimum length
*/
public function add(DelimiterProcessorInterface $processor): void
{
$len = $processor->getMinLength();
if (isset($this->processors[$len])) {
throw new InvalidArgumentException(\sprintf('Cannot add two delimiter processors for char "%s" and minimum length %d', $this->delimiterChar, $len));
}
$this->processors[$len] = $processor;
\krsort($this->processors);
$this->minLength = \min($this->minLength, $len);
}
public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int
{
return $this->findProcessor($opener->getLength())->getDelimiterUse($opener, $closer);
}
public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse): void
{
$this->findProcessor($delimiterUse)->process($opener, $closer, $delimiterUse);
}
private function findProcessor(int $len): DelimiterProcessorInterface
{
// Find the "longest" processor which can handle this length
foreach ($this->processors as $processor) {
if ($processor->getMinLength() <= $len) {
return $processor;
}
}
// Just use the first one in our list
$first = \reset($this->processors);
\assert($first instanceof DelimiterProcessorInterface);
return $first;
}
}