newsroom: Verwaltungszugang „Diesen Newsroom verwalten" (Roadmap 1b.2)

Neue Seite /newsroom/{slug}/verwalten auf den Web-Portalen: schickt den
Anmeldelink an die hinterlegte Firmen-E-Mail (sonst Pressekontakte), zeigt
sie nur maskiert, und funktioniert auch für Firmen ohne veröffentlichte
Meldung. Konto, Zuordnung und Login laufen durch ContactAccessService —
neu dort: Herkunft (profile_claim auf dem Token) und Ziel-Firma, auf deren
Kundenseite der Login landet. 410-entfernte bleiben Tombstone, aufgegangene
Slugs führen zum Überlebenden (MergedNewsroomLookup, geteilt mit der
Newsroom-Seite). 3 Links je Newsroom und Tag, 15 je IP und Stunde.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-15 10:42:20 +00:00
parent a3e0042055
commit 0c1c072487
15 changed files with 665 additions and 37 deletions

View file

@ -2,12 +2,11 @@
namespace App\Services\Web;
use App\Enums\CompanyMergeSubject;
use App\Models\Category;
use App\Models\Company;
use App\Models\CompanyMergeMove;
use App\Models\PressRelease;
use App\Scopes\PortalScope;
use App\Services\Company\MergedNewsroomLookup;
use App\Support\WebPageCache;
use App\Support\WebPortalContext;
use Illuminate\Database\Eloquent\Builder;
@ -22,6 +21,8 @@ use Illuminate\Support\Carbon;
*/
class NewsroomDetailPageData
{
public function __construct(private readonly MergedNewsroomLookup $mergedNewsrooms) {}
/**
* @return array<string, mixed>
*/
@ -80,37 +81,13 @@ class NewsroomDetailPageData
*/
private function mergedInto(array $portalValues, string $slug): ?string
{
$mergedIds = Company::onlyTrashed()
->withoutGlobalScope(PortalScope::class)
->whereIn('portal', $portalValues)
->where('slug', $slug)
->pluck('id');
$survivor = $this->mergedNewsrooms->survivorFor($portalValues, $slug);
if ($mergedIds->isEmpty()) {
if ($survivor === null || ! $survivor->is_active) {
return null;
}
$survivorId = CompanyMergeMove::query()
->where('subject', CompanyMergeSubject::Company->value)
->whereIn('subject_id', $mergedIds)
->whereHas('merge', fn (Builder $query) => $query->whereNull('reverted_at'))
->with('merge:id,survivor_company_id')
->latest('id')
->first()
?->merge
?->survivor_company_id;
if ($survivorId === null) {
return null;
}
$target = Company::query()
->withoutGlobalScope(PortalScope::class)
->whereKey($survivorId)
->where('is_active', true)
->value('slug');
return $target !== null && $target !== $slug ? $target : null;
return $survivor->slug !== $slug ? $survivor->slug : null;
}
/**