event photo wasserzeichen umgesetzt. Event admins können eigene einsetzen (als branding) falls das Paket es erlaubt. der Super Admin kann für die günstigen Pakete eigene Wasserzeichen erzwingen
This commit is contained in:
@@ -13,6 +13,7 @@ trait PresentsPackages
|
||||
$packageArray = $package->toArray();
|
||||
$features = $packageArray['features'] ?? [];
|
||||
$features = $this->normaliseFeatures($features);
|
||||
$watermarkPolicy = $package->watermark_allowed === false ? 'none' : 'basic';
|
||||
|
||||
$locale = app()->getLocale();
|
||||
$name = $this->resolveTranslation($package->name_translations ?? null, $package->name ?? '', $locale);
|
||||
@@ -50,6 +51,7 @@ trait PresentsPackages
|
||||
'gallery_duration_label' => $galleryDuration,
|
||||
'events' => $package->type === 'endcustomer' ? 1 : ($package->max_events_per_year ?? null),
|
||||
'features' => $features,
|
||||
'watermark_policy' => $watermarkPolicy,
|
||||
'limits' => $package->limits,
|
||||
'max_photos' => $package->max_photos,
|
||||
'max_guests' => $package->max_guests,
|
||||
|
||||
@@ -65,5 +65,122 @@ class ImageHelper
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a watermark in-place on the given disk/path.
|
||||
* Expects $config with keys: asset (path), position, opacity (0..1), scale (0..1), padding (px).
|
||||
*/
|
||||
public static function applyWatermarkOnDisk(string $disk, string $path, array $config): bool
|
||||
{
|
||||
$fullSrc = Storage::disk($disk)->path($path);
|
||||
if (! file_exists($fullSrc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$assetPath = $config['asset'] ?? null;
|
||||
if (! $assetPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$assetFull = null;
|
||||
if (Storage::disk('public')->exists($assetPath)) {
|
||||
$assetFull = Storage::disk('public')->path($assetPath);
|
||||
} elseif (file_exists($assetPath)) {
|
||||
$assetFull = $assetPath;
|
||||
}
|
||||
|
||||
if (! $assetFull || ! file_exists($assetFull)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = @file_get_contents($fullSrc);
|
||||
$src = $data !== false ? @imagecreatefromstring($data) : null;
|
||||
if (! $src) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$wmData = @file_get_contents($assetFull);
|
||||
$watermark = $wmData !== false ? @imagecreatefromstring($wmData) : null;
|
||||
if (! $watermark) {
|
||||
imagedestroy($src);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
imagesavealpha($src, true);
|
||||
imagesavealpha($watermark, true);
|
||||
|
||||
$srcW = imagesx($src);
|
||||
$srcH = imagesy($src);
|
||||
$wmW = imagesx($watermark);
|
||||
$wmH = imagesy($watermark);
|
||||
|
||||
if ($srcW <= 0 || $srcH <= 0 || $wmW <= 0 || $wmH <= 0) {
|
||||
imagedestroy($src);
|
||||
imagedestroy($watermark);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$scale = max(0.05, min(1.0, (float) ($config['scale'] ?? 0.2)));
|
||||
$targetW = max(1, (int) round($srcW * $scale));
|
||||
$targetH = max(1, (int) round($wmH * ($targetW / $wmW)));
|
||||
|
||||
$resized = imagecreatetruecolor($targetW, $targetH);
|
||||
imagealphablending($resized, false);
|
||||
imagesavealpha($resized, true);
|
||||
imagecopyresampled($resized, $watermark, 0, 0, 0, 0, $targetW, $targetH, $wmW, $wmH);
|
||||
imagedestroy($watermark);
|
||||
|
||||
$padding = max(0, (int) ($config['padding'] ?? 0));
|
||||
$position = $config['position'] ?? 'bottom-right';
|
||||
$x = $padding;
|
||||
$y = $padding;
|
||||
|
||||
if ($position === 'top-right') {
|
||||
$x = max(0, $srcW - $targetW - $padding);
|
||||
} elseif ($position === 'bottom-left') {
|
||||
$y = max(0, $srcH - $targetH - $padding);
|
||||
} elseif ($position === 'bottom-right') {
|
||||
$x = max(0, $srcW - $targetW - $padding);
|
||||
$y = max(0, $srcH - $targetH - $padding);
|
||||
} elseif ($position === 'center') {
|
||||
$x = (int) max(0, ($srcW - $targetW) / 2);
|
||||
$y = (int) max(0, ($srcH - $targetH) / 2);
|
||||
}
|
||||
|
||||
$opacity = max(0.0, min(1.0, (float) ($config['opacity'] ?? 0.25)));
|
||||
$mergeOpacity = (int) round($opacity * 100); // imagecopymerge uses 0-100
|
||||
|
||||
imagealphablending($src, true);
|
||||
imagecopymerge($src, $resized, $x, $y, 0, 0, $targetW, $targetH, $mergeOpacity);
|
||||
imagedestroy($resized);
|
||||
|
||||
// Overwrite original (respect mime: always JPEG for compatibility)
|
||||
@imagejpeg($src, $fullSrc, 90);
|
||||
imagedestroy($src);
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a source to destination and apply watermark there.
|
||||
*/
|
||||
public static function copyWithWatermark(string $disk, string $sourcePath, string $destPath, array $config): ?string
|
||||
{
|
||||
if (! Storage::disk($disk)->exists($sourcePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Storage::disk($disk)->makeDirectory(dirname($destPath));
|
||||
Storage::disk($disk)->copy($sourcePath, $destPath);
|
||||
|
||||
$applied = self::applyWatermarkOnDisk($disk, $destPath, $config);
|
||||
|
||||
return $applied ? $destPath : null;
|
||||
}
|
||||
}
|
||||
|
||||
90
app/Support/WatermarkConfigResolver.php
Normal file
90
app/Support/WatermarkConfigResolver.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use App\Models\Event;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class WatermarkConfigResolver
|
||||
{
|
||||
public static function determineBrandingAllowed(Event $event): bool
|
||||
{
|
||||
$event->loadMissing('eventPackage.package');
|
||||
|
||||
return $event->eventPackage?->package?->branding_allowed === true;
|
||||
}
|
||||
|
||||
public static function determinePolicy(Event $event): string
|
||||
{
|
||||
$event->loadMissing('eventPackage.package');
|
||||
|
||||
return $event->eventPackage?->package?->watermark_allowed === false ? 'none' : 'basic';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{type:string, policy:string, asset?:string, position?:string, opacity?:float, scale?:float, padding?:int}
|
||||
*/
|
||||
public static function resolve(Event $event): array
|
||||
{
|
||||
$policy = self::determinePolicy($event);
|
||||
|
||||
if ($policy === 'none') {
|
||||
return [
|
||||
'type' => 'none',
|
||||
'policy' => $policy,
|
||||
];
|
||||
}
|
||||
|
||||
$baseSetting = WatermarkSetting::query()->first();
|
||||
$base = [
|
||||
'asset' => $baseSetting?->asset ?? config('watermark.base.asset', 'branding/fotospiel-watermark.png'),
|
||||
'position' => $baseSetting?->position ?? config('watermark.base.position', 'bottom-right'),
|
||||
'opacity' => $baseSetting?->opacity ?? config('watermark.base.opacity', 0.25),
|
||||
'scale' => $baseSetting?->scale ?? config('watermark.base.scale', 0.2),
|
||||
'padding' => $baseSetting?->padding ?? config('watermark.base.padding', 16),
|
||||
];
|
||||
|
||||
$event->loadMissing('eventPackage.package', 'tenant');
|
||||
$brandingAllowed = self::determineBrandingAllowed($event);
|
||||
$eventWatermark = Arr::get($event->settings, 'watermark', []);
|
||||
$tenantWatermark = Arr::get($event->tenant?->settings, 'watermark', []);
|
||||
$serveOriginals = (bool) Arr::get($event->settings, 'watermark_serve_originals', false);
|
||||
|
||||
$mode = $brandingAllowed
|
||||
? ($eventWatermark['mode'] ?? $tenantWatermark['mode'] ?? 'base')
|
||||
: 'base';
|
||||
|
||||
if ($mode === 'off' && $policy === 'basic') {
|
||||
$mode = 'base';
|
||||
}
|
||||
|
||||
if ($mode === 'off') {
|
||||
return [
|
||||
'type' => 'none',
|
||||
'policy' => $policy,
|
||||
];
|
||||
}
|
||||
|
||||
$source = $mode === 'custom' && $brandingAllowed ? ($eventWatermark ?: $tenantWatermark) : [];
|
||||
|
||||
$asset = $source['asset'] ?? $base['asset'] ?? null;
|
||||
$position = $source['position'] ?? $base['position'] ?? 'bottom-right';
|
||||
$opacity = (float) ($source['opacity'] ?? $base['opacity'] ?? 0.25);
|
||||
$scale = (float) ($source['scale'] ?? $base['scale'] ?? 0.2);
|
||||
$padding = (int) ($source['padding'] ?? $base['padding'] ?? 16);
|
||||
|
||||
$clamp = static fn (float $value, float $min, float $max) => max($min, min($max, $value));
|
||||
|
||||
return [
|
||||
'type' => $mode === 'custom' && $brandingAllowed ? 'custom' : 'base',
|
||||
'policy' => $policy,
|
||||
'asset' => $asset,
|
||||
'position' => $position,
|
||||
'opacity' => $clamp($opacity, 0.0, 1.0),
|
||||
'scale' => $clamp($scale, 0.05, 1.0),
|
||||
'padding' => max(0, $padding),
|
||||
'serve_originals' => $serveOriginals,
|
||||
];
|
||||
}
|
||||
}
|
||||
use App\Models\WatermarkSetting;
|
||||
Reference in New Issue
Block a user