Adjust watermark permissions and transparency
This commit is contained in:
@@ -143,7 +143,7 @@ class PackageResource extends Resource
|
||||
->nullable()
|
||||
->visible(fn ($get) => $get('type') === 'reseller'),
|
||||
Toggle::make('watermark_allowed')
|
||||
->label('Wasserzeichen erlaubt')
|
||||
->label('Eigenes Wasserzeichen erlaubt')
|
||||
->default(true),
|
||||
Toggle::make('branding_allowed')
|
||||
->label('Eigenes Branding erlaubt')
|
||||
|
||||
@@ -27,7 +27,7 @@ class WatermarkSettingsPage extends Page
|
||||
return __('admin.nav.branding');
|
||||
}
|
||||
|
||||
public ?string $asset = null;
|
||||
public $asset = [];
|
||||
|
||||
public string $position = 'bottom-right';
|
||||
|
||||
@@ -46,7 +46,7 @@ class WatermarkSettingsPage extends Page
|
||||
$settings = WatermarkSetting::query()->first();
|
||||
|
||||
if ($settings) {
|
||||
$this->asset = $settings->asset;
|
||||
$this->asset = $settings->asset ? [$settings->asset] : [];
|
||||
$this->position = $settings->position;
|
||||
$this->opacity = (float) $settings->opacity;
|
||||
$this->scale = (float) $settings->scale;
|
||||
@@ -119,8 +119,14 @@ class WatermarkSettingsPage extends Page
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$state = $this->form->getState();
|
||||
$asset = $state['asset'] ?? $this->asset;
|
||||
if (is_array($asset)) {
|
||||
$asset = $asset[0] ?? null;
|
||||
}
|
||||
|
||||
$settings = WatermarkSetting::query()->firstOrNew([]);
|
||||
$settings->asset = $this->asset;
|
||||
$settings->asset = $asset;
|
||||
$settings->position = $this->position;
|
||||
$settings->opacity = $this->opacity;
|
||||
$settings->scale = $this->scale;
|
||||
|
||||
@@ -20,6 +20,7 @@ use App\Models\User;
|
||||
use App\Services\EventJoinTokenService;
|
||||
use App\Support\ApiError;
|
||||
use App\Support\TenantMemberPermissions;
|
||||
use App\Support\WatermarkConfigResolver;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
@@ -414,6 +415,7 @@ class EventController extends Controller
|
||||
$package = $event->eventPackage?->package;
|
||||
$brandingAllowed = optional($package)->branding_allowed !== false;
|
||||
$watermarkAllowed = optional($package)->watermark_allowed !== false;
|
||||
$watermarkRemovalAllowed = WatermarkConfigResolver::determineRemovalAllowed($event);
|
||||
|
||||
if (isset($validated['settings']) && is_array($validated['settings'])) {
|
||||
$validated['settings'] = array_merge($event->settings ?? [], $validated['settings']);
|
||||
@@ -423,6 +425,7 @@ class EventController extends Controller
|
||||
|
||||
$validated['settings']['branding_allowed'] = $brandingAllowed;
|
||||
$validated['settings']['watermark_allowed'] = $watermarkAllowed;
|
||||
$validated['settings']['watermark_removal_allowed'] = $watermarkRemovalAllowed;
|
||||
|
||||
$settings = $validated['settings'];
|
||||
$branding = Arr::get($settings, 'branding', []);
|
||||
@@ -435,20 +438,19 @@ class EventController extends Controller
|
||||
|
||||
if (is_array($watermark)) {
|
||||
$mode = $watermark['mode'] ?? 'base';
|
||||
$policy = $watermarkAllowed ? 'basic' : 'none';
|
||||
|
||||
if (! $watermarkAllowed) {
|
||||
$mode = 'off';
|
||||
$mode = 'base';
|
||||
} elseif (! $brandingAllowed) {
|
||||
$mode = 'base';
|
||||
} elseif ($mode === 'off' && $policy === 'basic') {
|
||||
} elseif ($mode === 'off' && ! $watermarkRemovalAllowed) {
|
||||
$mode = 'base';
|
||||
}
|
||||
|
||||
$assetPath = $watermark['asset'] ?? null;
|
||||
$assetDataUrl = $watermark['asset_data_url'] ?? null;
|
||||
|
||||
if (! $watermarkAllowed) {
|
||||
if (! $watermarkAllowed || $mode === 'off') {
|
||||
$assetPath = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
|
||||
namespace App\Http\Resources\Tenant;
|
||||
|
||||
use App\Models\WatermarkSetting;
|
||||
use App\Services\Packages\PackageLimitEvaluator;
|
||||
use App\Support\TenantMemberPermissions;
|
||||
use App\Support\WatermarkConfigResolver;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Http\Resources\MissingValue;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
use function app;
|
||||
@@ -47,6 +50,8 @@ class EventResource extends JsonResource
|
||||
$limitEvaluator = app()->make(PackageLimitEvaluator::class);
|
||||
}
|
||||
|
||||
$settings['watermark_removal_allowed'] = WatermarkConfigResolver::determineRemovalAllowed($this->resource);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
@@ -108,6 +113,27 @@ class EventResource extends JsonResource
|
||||
$watermark = Arr::get($settings, 'watermark');
|
||||
$base = config('watermark.base', []);
|
||||
$base = is_array($base) ? $base : [];
|
||||
$baseSetting = null;
|
||||
|
||||
if (class_exists(WatermarkSetting::class) && Schema::hasTable('watermark_settings')) {
|
||||
try {
|
||||
$baseSetting = WatermarkSetting::query()->first();
|
||||
} catch (\Throwable) {
|
||||
$baseSetting = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($baseSetting) {
|
||||
$base = array_merge($base, array_filter([
|
||||
'asset' => $baseSetting->asset,
|
||||
'position' => $baseSetting->position,
|
||||
'opacity' => $baseSetting->opacity,
|
||||
'scale' => $baseSetting->scale,
|
||||
'padding' => $baseSetting->padding,
|
||||
'offset_x' => $baseSetting->offset_x,
|
||||
'offset_y' => $baseSetting->offset_y,
|
||||
], static fn ($value) => $value !== null));
|
||||
}
|
||||
|
||||
if (! is_array($watermark)) {
|
||||
$watermark = [];
|
||||
|
||||
@@ -13,7 +13,8 @@ trait PresentsPackages
|
||||
$packageArray = $package->toArray();
|
||||
$features = $packageArray['features'] ?? [];
|
||||
$features = $this->normaliseFeatures($features);
|
||||
$watermarkPolicy = $package->watermark_allowed === false ? 'none' : 'basic';
|
||||
$watermarkRemovalAllowed = in_array('no_watermark', $features, true) || in_array('watermark_removal', $features, true);
|
||||
$watermarkPolicy = $watermarkRemovalAllowed ? 'none' : 'basic';
|
||||
|
||||
$locale = app()->getLocale();
|
||||
$name = $this->resolveTranslation($package->name_translations ?? null, $package->name ?? '', $locale);
|
||||
|
||||
@@ -178,10 +178,13 @@ class ImageHelper
|
||||
$y = max(0, min($srcH - $targetH, $y + $offsetY));
|
||||
|
||||
$opacity = max(0.0, min(1.0, (float) ($config['opacity'] ?? 0.25)));
|
||||
$mergeOpacity = (int) round($opacity * 100); // imagecopymerge uses 0-100
|
||||
|
||||
if ($opacity < 1.0) {
|
||||
self::applyOpacity($resized, $opacity);
|
||||
}
|
||||
|
||||
imagealphablending($src, true);
|
||||
imagecopymerge($src, $resized, $x, $y, 0, 0, $targetW, $targetH, $mergeOpacity);
|
||||
imagecopy($src, $resized, $x, $y, 0, 0, $targetW, $targetH);
|
||||
imagedestroy($resized);
|
||||
|
||||
// Overwrite original (respect mime: always JPEG for compatibility)
|
||||
@@ -210,4 +213,34 @@ class ImageHelper
|
||||
|
||||
return $applied ? $destPath : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \GdImage|resource $image
|
||||
*/
|
||||
private static function applyOpacity($image, float $opacity): void
|
||||
{
|
||||
$width = imagesx($image);
|
||||
$height = imagesy($image);
|
||||
|
||||
if ($width <= 0 || $height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
imagealphablending($image, false);
|
||||
imagesavealpha($image, true);
|
||||
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$rgba = imagecolorat($image, $x, $y);
|
||||
$alpha = ($rgba >> 24) & 0x7F;
|
||||
$red = ($rgba >> 16) & 0xFF;
|
||||
$green = ($rgba >> 8) & 0xFF;
|
||||
$blue = $rgba & 0xFF;
|
||||
|
||||
$adjustedAlpha = (int) round(127 - (127 - $alpha) * $opacity);
|
||||
$color = imagecolorallocatealpha($image, $red, $green, $blue, max(0, min(127, $adjustedAlpha)));
|
||||
imagesetpixel($image, $x, $y, $color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Support;
|
||||
|
||||
use App\Models\Event;
|
||||
use App\Models\Package;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class WatermarkConfigResolver
|
||||
@@ -29,7 +30,21 @@ class WatermarkConfigResolver
|
||||
{
|
||||
$event->loadMissing('eventPackage.package');
|
||||
|
||||
return $event->eventPackage?->package?->watermark_allowed === false ? 'none' : 'basic';
|
||||
return self::determineRemovalAllowed($event) ? 'none' : 'basic';
|
||||
}
|
||||
|
||||
public static function determineRemovalAllowed(Event $event): bool
|
||||
{
|
||||
$event->loadMissing('eventPackage.package', 'eventPackages.package');
|
||||
|
||||
$package = $event->eventPackage?->package;
|
||||
|
||||
if (! $package && $event->relationLoaded('eventPackages')) {
|
||||
$package = $event->eventPackages->first()?->package;
|
||||
}
|
||||
|
||||
return self::packageHasFeature($package, 'no_watermark')
|
||||
|| self::packageHasFeature($package, 'watermark_removal');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,13 +54,6 @@ class WatermarkConfigResolver
|
||||
{
|
||||
$policy = self::determinePolicy($event);
|
||||
|
||||
if ($policy === 'none') {
|
||||
return [
|
||||
'type' => 'none',
|
||||
'policy' => $policy,
|
||||
];
|
||||
}
|
||||
|
||||
$baseSetting = null;
|
||||
|
||||
if (class_exists(\App\Models\WatermarkSetting::class) && \Illuminate\Support\Facades\Schema::hasTable('watermark_settings')) {
|
||||
@@ -65,8 +73,15 @@ class WatermarkConfigResolver
|
||||
'offset_y' => $baseSetting?->offset_y ?? config('watermark.base.offset_y', 0),
|
||||
];
|
||||
|
||||
$event->loadMissing('eventPackage.package', 'tenant');
|
||||
$event->loadMissing('eventPackage.package', 'eventPackages.package', 'tenant');
|
||||
$package = $event->eventPackage?->package;
|
||||
|
||||
if (! $package && $event->relationLoaded('eventPackages')) {
|
||||
$package = $event->eventPackages->first()?->package;
|
||||
}
|
||||
$brandingAllowed = self::determineBrandingAllowed($event);
|
||||
$watermarkAllowed = $package?->watermark_allowed !== false;
|
||||
$removalAllowed = self::determineRemovalAllowed($event);
|
||||
$eventWatermark = Arr::get($event->settings, 'watermark', []);
|
||||
$tenantWatermark = Arr::get($event->tenant?->settings, 'watermark', []);
|
||||
$serveOriginals = (bool) Arr::get($event->settings, 'watermark_serve_originals', false);
|
||||
@@ -75,7 +90,11 @@ class WatermarkConfigResolver
|
||||
? ($eventWatermark['mode'] ?? $tenantWatermark['mode'] ?? 'base')
|
||||
: 'base';
|
||||
|
||||
if ($mode === 'off' && $policy === 'basic') {
|
||||
if ($mode === 'custom' && (! $brandingAllowed || ! $watermarkAllowed)) {
|
||||
$mode = 'base';
|
||||
}
|
||||
|
||||
if ($mode === 'off' && ! $removalAllowed) {
|
||||
$mode = 'base';
|
||||
}
|
||||
|
||||
@@ -111,4 +130,30 @@ class WatermarkConfigResolver
|
||||
'serve_originals' => $serveOriginals,
|
||||
];
|
||||
}
|
||||
|
||||
private static function packageHasFeature(?Package $package, string $feature): bool
|
||||
{
|
||||
if (! $package) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$features = $package->features ?? [];
|
||||
|
||||
if (is_string($features)) {
|
||||
$decoded = json_decode($features, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
$features = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
if (! is_array($features)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_is_list($features)) {
|
||||
return in_array($feature, $features, true);
|
||||
}
|
||||
|
||||
return isset($features[$feature]) && (bool) $features[$feature];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user