*/ use HasFactory, HasUniqueSlug, SoftDeletes; /** * Anzeige-Zeitzone für vom Nutzer erfasste Termine (scheduled_at, * embargo_at). In der Datenbank wird weiterhin UTC gespeichert * (config app.timezone). */ public const DISPLAY_TIMEZONE = 'Europe/Berlin'; /** * @return list */ protected function slugScopeAttributes(): array { return ['portal', 'language']; } protected function slugFallback(): string { return 'pressemitteilung'; } protected static function booted(): void { static::addGlobalScope(new PortalScope); static::creating(function (self $pressRelease): void { if (blank($pressRelease->placeholder_variant)) { $seed = $pressRelease->uuid ?? $pressRelease->title ?? (string) now()->timestamp; $pressRelease->placeholder_variant = PressReleasePlaceholder::fromSeed($seed)->value; } }); } protected $fillable = [ 'uuid', 'portal', 'user_id', 'company_id', 'category_id', 'language', 'title', 'subtitle', 'slug', 'text', 'boilerplate_override', 'placeholder_variant', 'backlink_url', 'keywords', 'status', 'classification', 'classified_at', 'content_score', 'content_tier', 'scored_at', 'hits', 'teaser_begin', 'teaser_end', 'no_export', 'published_at', 'scheduled_at', 'embargo_at', 'legacy_portal', 'legacy_id', ]; protected function casts(): array { return [ 'portal' => Portal::class, 'placeholder_variant' => PressReleasePlaceholder::class, 'status' => PressReleaseStatus::class, 'classification' => PressReleaseClassification::class, 'classified_at' => 'datetime', 'content_score' => 'integer', 'content_tier' => PressReleaseContentTier::class, 'scored_at' => 'datetime', 'hits' => 'integer', 'teaser_begin' => 'integer', 'teaser_end' => 'integer', 'no_export' => 'boolean', 'published_at' => 'datetime', 'scheduled_at' => 'datetime', 'embargo_at' => 'datetime', 'deleted_at' => 'datetime', ]; } /** * Geplanter Veröffentlichungstermin in der Anzeige-Zeitzone (Europe/Berlin). */ public function scheduledAtLocal(): ?Carbon { return $this->scheduled_at?->copy()->setTimezone(self::DISPLAY_TIMEZONE); } /** * Sperrfrist (Embargo) in der Anzeige-Zeitzone (Europe/Berlin). */ public function embargoAtLocal(): ?Carbon { return $this->embargo_at?->copy()->setTimezone(self::DISPLAY_TIMEZONE); } public function user(): BelongsTo { return $this->belongsTo(User::class); } public function company(): BelongsTo { return $this->belongsTo(Company::class); } public function category(): BelongsTo { return $this->belongsTo(Category::class); } public function images(): HasMany { return $this->hasMany(PressReleaseImage::class); } public function attachments(): HasMany { return $this->hasMany(PressReleaseAttachment::class); } public function contacts(): BelongsToMany { return $this->belongsToMany(Contact::class, 'press_release_contact'); } public function statusLogs(): HasMany { return $this->hasMany(PressReleaseStatusLog::class)->orderByDesc('created_at'); } public function kiAudits(): HasMany { return $this->hasMany(KiAudit::class)->orderByDesc('created_at'); } /** * Display-ready text. Returns sanitized HTML for Phase-7+ PMs and *

/
-wrapped legacy plain text for older imports. */ public function renderedText(): HtmlString { return app(PressReleaseHtmlSanitizer::class)->render($this->text); } public function plainTextLength(): int { return app(PressReleaseHtmlSanitizer::class)->plainTextLength($this->text); } }