changelog: Entwürfe aus den Commits, Freigabe im Admin, öffentliche Seite mit RSS

Decision-Update Changelog, Stufen 1 bis 3:
- changelog:collect liest die Commits zwischen zwei Tags und legt aus den
  Zeilen Changelog:/Public: Entwürfe an (ganze Nachricht, nicht nur der
  Trailer-Block); ohne Changelog-Zeile intern, Sicherheit als eine
  Sammelzeile je Release, Sicherheitsbegriffe im Titel stufen hoch, ohne
  Public-Satz ein markierter Vorschlag aus dem Titel; idempotent.
- changelog:backfill trägt die Neuerungen seit dem Relaunch als Entwurf nach.
- Admin ▸ Changelog (settings:manage): gegenlesen, bearbeiten, Rubrik,
  artikelwürdig, freigeben/verwerfen, Tag gesammelt freigeben, Eintrag von Hand.
- pressekonto.com/changelog und /changelog/feed (RSS), auf den Portalen
  301 zum Hub; Kundenmenü „Was ist neu“ mit Neu-Kennzeichen (14 Tage).

Changelog: Neu
Public: Unter pressekonto.com/changelog steht, was sich auf pressekonto, presseecho und businessportal24 ändert – auch als RSS-Feed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-10-14 08:30:46 +00:00
parent ebd61c8fd9
commit 2c590fa9df
17 changed files with 1302 additions and 0 deletions

View file

@ -0,0 +1,81 @@
<?php
namespace App\Http\Controllers\Web;
use App\Http\Controllers\Controller;
use App\Models\ChangelogEntry;
use App\Services\Changelog\ChangelogTimeline;
use App\Support\DomainAssetContext;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
/**
* Öffentlicher Changelog unter pressekonto.com/changelog mit RSS-Feed
* (Decision-Update Changelog, E2). Er gehört zum Hub; auf den Portalen leitet
* die Adresse dorthin weiter.
*/
class ChangelogController extends Controller
{
public function __construct(private readonly ChangelogTimeline $timeline) {}
public function index(Request $request): View|RedirectResponse
{
if (! $this->onHub($request)) {
return redirect($this->hubBase().'/changelog', 301);
}
DomainAssetContext::applyWebDomainConfig('pressekonto');
return view('web.changelog', [
'entries' => $this->timeline->entries(),
'feedUrl' => $this->hubBase().'/changelog/feed',
]);
}
public function feed(Request $request): Response|RedirectResponse
{
if (! $this->onHub($request)) {
return redirect($this->hubBase().'/changelog/feed', 301);
}
$base = $this->hubBase();
$entries = $this->timeline->entries(50);
$items = $entries->map(fn (ChangelogEntry $entry): array => [
'title' => $entry->category->label().': '.Str::limit($entry->publicText(), 90),
'link' => $base.'/changelog#eintrag-'.$entry->id,
'published_at' => $entry->released_on->copy()->setTime(12, 0)->toRfc2822String(),
'description' => $entry->publicText(),
'category' => $entry->category->label(),
'creator' => null,
])->all();
$xml = view('web.feed', [
'channel' => [
'title' => 'pressekonto – Changelog',
'link' => $base.'/changelog',
'self' => $base.'/changelog/feed',
'description' => 'Neuerungen auf pressekonto, presseecho und businessportal24.',
'language' => 'de',
'last_build' => ($entries->first()?->released_on?->copy()->setTime(12, 0) ?? Carbon::now())->toRfc2822String(),
],
'items' => $items,
])->render();
return response($xml, 200, ['Content-Type' => 'application/rss+xml; charset=UTF-8']);
}
private function onHub(Request $request): bool
{
return str_contains($request->getHost(), 'pressekonto');
}
private function hubBase(): string
{
return rtrim((string) config('domains.domain_portal_url'), '/');
}
}