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,68 @@
<?php
namespace Barryvdh\DomPDF\Facade;
use Barryvdh\DomPDF\PDF as BasePDF;
use Illuminate\Support\Facades\Facade as IlluminateFacade;
use RuntimeException;
/**
* @method static BasePDF setBaseHost(string $baseHost)
* @method static BasePDF setBasePath(string $basePath)
* @method static BasePDF setCanvas(\Dompdf\Canvas $canvas)
* @method static BasePDF setCallbacks(array<string, mixed> $callbacks)
* @method static BasePDF setCss(\Dompdf\Css\Stylesheet $css)
* @method static BasePDF setDefaultView(string $defaultView, array<string, mixed> $options)
* @method static BasePDF setDom(\DOMDocument $dom)
* @method static BasePDF setFontMetrics(\Dompdf\FontMetrics $fontMetrics)
* @method static BasePDF setHttpContext(resource|array<string, mixed> $httpContext)
* @method static BasePDF setPaper(string|float[] $paper, string $orientation = 'portrait')
* @method static BasePDF setProtocol(string $protocol)
* @method static BasePDF setTree(\Dompdf\Frame\FrameTree $tree)
* @method static BasePDF setWarnings(bool $warnings)
* @method static BasePDF setOption(array<string, mixed>|string $attribute, $value = null)
* @method static BasePDF setOptions(array<string, mixed> $options)
* @method static BasePDF loadView(string $view, array<string, mixed> $data = [], array<string, mixed> $mergeData = [], ?string $encoding = null)
* @method static BasePDF loadHTML(string $string, ?string $encoding = null)
* @method static BasePDF loadFile(string $file)
* @method static BasePDF addInfo(array<string, string> $info)
* @method static string output(array<string, int> $options = [])
* @method static BasePDF save()
* @method static \Illuminate\Http\Response download(string $filename = 'document.pdf')
* @method static \Illuminate\Http\Response stream(string $filename = 'document.pdf')
*/
class Pdf extends IlluminateFacade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'dompdf.wrapper';
}
/**
* Handle dynamic, static calls to the object.
*
* @param string $method
* @param array<mixed> $args
* @return mixed
*
* @throws \RuntimeException
*/
public static function __callStatic($method, $args)
{
/** @var \Illuminate\Contracts\Foundation\Application|null */
$app = static::getFacadeApplication();
if (! $app) {
throw new RuntimeException('Facade application has not been set.');
}
// Resolve a new instance, avoid using a cached instance
$instance = $app->make(static::getFacadeAccessor());
return $instance->$method(...$args);
}
}

View File

@@ -0,0 +1,319 @@
<?php
namespace Barryvdh\DomPDF;
use Dompdf\Adapter\CPDF;
use Dompdf\Dompdf;
use Dompdf\Options;
use Exception;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\HeaderUtils;
/**
* A Laravel wrapper for Dompdf
*
* @package laravel-dompdf
* @author Barry vd. Heuvel
*
* @method PDF setBaseHost(string $baseHost)
* @method PDF setBasePath(string $basePath)
* @method PDF setCanvas(\Dompdf\Canvas $canvas)
* @method PDF setCallbacks(array<string, mixed> $callbacks)
* @method PDF setCss(\Dompdf\Css\Stylesheet $css)
* @method PDF setDefaultView(string $defaultView, array<string, mixed> $options)
* @method PDF setDom(\DOMDocument $dom)
* @method PDF setFontMetrics(\Dompdf\FontMetrics $fontMetrics)
* @method PDF setHttpContext(resource|array<string, mixed> $httpContext)
* @method PDF setPaper(string|float[] $paper, string $orientation = 'portrait')
* @method PDF setProtocol(string $protocol)
* @method PDF setTree(\Dompdf\Frame\FrameTree $tree)
* @method string getBaseHost()
* @method string getBasePath()
* @method \Dompdf\Canvas getCanvas()
* @method array<string, mixed> getCallbacks()
* @method \Dompdf\Css\Stylesheet getCss()
* @method \DOMDocument getDom()
* @method \Dompdf\FontMetrics getFontMetrics()
* @method resource getHttpContext()
* @method Options getOptions()
* @method \Dompdf\Frame\FrameTree getTree()
* @method string getPaperOrientation()
* @method float[] getPaperSize()
* @method string getProtocol()
*/
class PDF
{
/** @var Dompdf */
protected $dompdf;
/** @var \Illuminate\Contracts\Config\Repository */
protected $config;
/** @var \Illuminate\Filesystem\Filesystem */
protected $files;
/** @var \Illuminate\Contracts\View\Factory */
protected $view;
/** @var bool */
protected $rendered = false;
/** @var bool */
protected $showWarnings;
/** @var string */
protected $public_path;
public function __construct(Dompdf $dompdf, ConfigRepository $config, Filesystem $files, ViewFactory $view)
{
$this->dompdf = $dompdf;
$this->config = $config;
$this->files = $files;
$this->view = $view;
$this->showWarnings = $this->config->get('dompdf.show_warnings', false);
}
/**
* Get the DomPDF instance
*/
public function getDomPDF(): Dompdf
{
return $this->dompdf;
}
/**
* Show or hide warnings
*/
public function setWarnings(bool $warnings): self
{
$this->showWarnings = $warnings;
return $this;
}
/**
* Load a HTML string
*
* @param string|null $encoding Not used yet
*/
public function loadHTML(string $string, ?string $encoding = null): self
{
$string = $this->convertEntities($string);
$this->dompdf->loadHtml($string, $encoding);
$this->rendered = false;
return $this;
}
/**
* Load a HTML file
*/
public function loadFile(string $file): self
{
$this->dompdf->loadHtmlFile($file);
$this->rendered = false;
return $this;
}
/**
* Add metadata info
* @param array<string, string> $info
*/
public function addInfo(array $info): self
{
foreach ($info as $name => $value) {
$this->dompdf->add_info($name, $value);
}
return $this;
}
/**
* Load a View and convert to HTML
* @param array<string, mixed> $data
* @param array<string, mixed> $mergeData
* @param string|null $encoding Not used yet
*/
public function loadView(string $view, array $data = [], array $mergeData = [], ?string $encoding = null): self
{
$html = $this->view->make($view, $data, $mergeData)->render();
return $this->loadHTML($html, $encoding);
}
/**
* Set/Change an option (or array of options) in Dompdf
*
* @param array<string, mixed>|string $attribute
* @param null|mixed $value
*/
public function setOption($attribute, $value = null): self
{
$this->dompdf->getOptions()->set($attribute, $value);
return $this;
}
/**
* Replace all the Options from DomPDF
*
* @param array<string, mixed> $options
*/
public function setOptions(array $options, bool $mergeWithDefaults = false): self
{
if ($mergeWithDefaults) {
$options = array_merge(app()->make('dompdf.options'), $options);
}
$this->dompdf->setOptions(new Options($options));
return $this;
}
/**
* Output the PDF as a string.
*
* The options parameter controls the output. Accepted options are:
*
* 'compress' = > 1 or 0 - apply content stream compression, this is
* on (1) by default
*
* @param array<string, int> $options
*
* @return string The rendered PDF as string
*/
public function output(array $options = []): string
{
if (!$this->rendered) {
$this->render();
}
return (string) $this->dompdf->output($options);
}
/**
* Save the PDF to a file
*/
public function save(string $filename, ?string $disk = null): self
{
$disk = $disk ?: $this->config->get('dompdf.disk');
if (! is_null($disk)) {
Storage::disk($disk)->put($filename, $this->output());
return $this;
}
$this->files->put($filename, $this->output());
return $this;
}
/**
* Make the PDF downloadable by the user
*/
public function download(string $filename = 'document.pdf'): Response
{
$output = $this->output();
$fallback = $this->fallbackName($filename);
return new Response($output, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => HeaderUtils::makeDisposition('attachment', $filename, $fallback),
'Content-Length' => strlen($output),
]);
}
/**
* Return a response with the PDF to show in the browser
*/
public function stream(string $filename = 'document.pdf'): Response
{
$output = $this->output();
$fallback = $this->fallbackName($filename);
return new Response($output, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => HeaderUtils::makeDisposition('inline', $filename, $fallback),
]);
}
/**
* Render the PDF
*/
public function render(): void
{
$this->dompdf->render();
if ($this->showWarnings) {
global $_dompdf_warnings;
if (!empty($_dompdf_warnings) && count($_dompdf_warnings)) {
$warnings = '';
foreach ($_dompdf_warnings as $msg) {
$warnings .= $msg . "\n";
}
// $warnings .= $this->dompdf->get_canvas()->get_cpdf()->messages;
if (!empty($warnings)) {
throw new Exception($warnings);
}
}
}
$this->rendered = true;
}
/** @param array<string> $pc */
public function setEncryption(string $password, string $ownerpassword = '', array $pc = []): void
{
$this->render();
$canvas = $this->dompdf->getCanvas();
if (! $canvas instanceof CPDF) {
throw new \RuntimeException('Encryption is only supported when using CPDF');
}
$canvas->get_cpdf()->setEncryption($password, $ownerpassword, $pc);
}
protected function convertEntities(string $subject): string
{
if (false === $this->config->get('dompdf.convert_entities', true)) {
return $subject;
}
$entities = [
'€' => '&euro;',
'£' => '&pound;',
];
foreach ($entities as $search => $replace) {
$subject = str_replace($search, $replace, $subject);
}
return $subject;
}
/**
* Dynamically handle calls into the dompdf instance.
*
* @param string $method
* @param array<mixed> $parameters
* @return $this|mixed
*/
public function __call($method, $parameters)
{
if (method_exists($this, $method)) {
return $this->$method(...$parameters);
}
if (method_exists($this->dompdf, $method)) {
$return = $this->dompdf->$method(...$parameters);
return $return == $this->dompdf ? $this : $return;
}
throw new \UnexpectedValueException("Method [{$method}] does not exist on PDF instance.");
}
/**
* Make a safe fallback filename
*/
protected function fallbackName(string $filename): string
{
return str_replace('%', '', Str::ascii($filename));
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Barryvdh\DomPDF;
use Dompdf\Dompdf;
use Exception;
use Illuminate\Support\Str;
use Illuminate\Support\ServiceProvider as IlluminateServiceProvider;
class ServiceProvider extends IlluminateServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Register the service provider.
*
* @throws \Exception
* @return void
*/
public function register(): void
{
$configPath = __DIR__ . '/../config/dompdf.php';
$this->mergeConfigFrom($configPath, 'dompdf');
$this->app->bind('dompdf.options', function ($app) {
$defines = $app['config']->get('dompdf.defines');
if ($defines) {
$options = [];
/**
* @var string $key
* @var mixed $value
*/
foreach ($defines as $key => $value) {
$key = strtolower(str_replace('DOMPDF_', '', $key));
$options[$key] = $value;
}
} else {
$options = $app['config']->get('dompdf.options');
}
return $options;
});
$this->app->bind('dompdf', function ($app) {
$options = $app->make('dompdf.options');
$dompdf = new Dompdf($options);
$path = realpath($app['config']->get('dompdf.public_path') ?: base_path('public'));
if ($path === false) {
throw new \RuntimeException('Cannot resolve public path');
}
$dompdf->setBasePath($path);
return $dompdf;
});
$this->app->alias('dompdf', Dompdf::class);
$this->app->bind('dompdf.wrapper', function ($app) {
return new PDF($app['dompdf'], $app['config'], $app['files'], $app['view']);
});
}
/**
* Check if package is running under Lumen app
*/
protected function isLumen(): bool
{
return Str::contains($this->app->version(), 'Lumen') === true;
}
public function boot(): void
{
if (! $this->isLumen()) {
$configPath = __DIR__ . '/../config/dompdf.php';
$this->publishes([$configPath => config_path('dompdf.php')], 'config');
}
}
/**
* Get the services provided by the provider.
*
* @return array<string>
*/
public function provides(): array
{
return ['dompdf', 'dompdf.options', 'dompdf.wrapper'];
}
}