web: RSS-Feeds je Branche und je Portal (Roadmap 3.1); dev: Sichtung des Ordners, Protokoll-Teile umnummeriert

- /{edition}/feed und /{edition}/category/{slug}/feed (FeedController,
  RSS 2.0 mit atom:link self, dc:creator, category; jüngste 50 der Ausgabe,
  Branche inkl. aktiver Unterkategorien, nur visibleInArchive).
- Cache über WebPageCache (Key-Gruppe feed), Hub leitet per 301 aufs Portal
  (canonical.portal_only_content_routes).
- Autodiscovery über @yield('feed_links') auf Start-, Branchen- und
  Archivseiten, sichtbarer Link in der Archiv-Leiste, llms.txt nennt die
  Feed-Muster, Block „RSS-Feeds" im Panel „Auslieferung"
  (VisibilityStats::feedLinks).
- Übersetzungen unter web.rss (web.feed ist die Startseiten-Liste).
- Tests: FeedTest (7), VisibilityStatsTest erweitert.
- dev/2026-09-01: Sichtung 18.09. (Teil 11), zweite Teil-Serie des
  Protokolls jetzt 7–10 mit nachgezogenen Verweisen, Anhang 1 abgeglichen,
  E6 abgehakt, 2.6 extern, 1c.5/E8 wartet auf Daten, 3.1 abgehakt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-18 13:57:09 +00:00
parent e2ef4dee31
commit 9d5e5a0b54
20 changed files with 547 additions and 28 deletions

View file

@ -1598,6 +1598,21 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
</div>
</div>
{{-- RSS-Feeds (Roadmap 3.1): Branchen- und Portal-Feed je Portal. --}}
<div class="rounded-[5px] border border-[color:var(--color-bg-rule)] bg-[color:var(--color-bg-elev)] p-3">
<div class="text-[11px] uppercase tracking-[0.6px] text-[color:var(--color-ink-3)] font-semibold">{{ __('RSS-Feeds') }}</div>
@if (($visibility['feeds'] ?? []) !== [])
<ul class="mt-1 m-0 p-0 list-none space-y-0.5 text-[12.5px]">
@foreach ($visibility['feeds'] as $feed)
<li><a href="{{ $feed['url'] }}" target="_blank" rel="noopener" class="text-[color:var(--color-ink-2)] underline underline-offset-2 hover:text-[color:var(--color-ink)]">{{ $feed['label'] }}</a></li>
@endforeach
</ul>
<p class="mt-2 mb-0 text-[11px] text-[color:var(--color-ink-4)]">{{ __('Die Feeds führen die jüngsten :n Meldungen — wer sie abonniert hat, ist uns nicht bekannt.', ['n' => \App\Http\Controllers\Web\FeedController::MAX_ITEMS]) }}</p>
@else
<p class="mt-1 mb-0 text-[12.5px] text-[color:var(--color-ink-3)]">{{ __('Derzeit in keinem Feed — die Meldung ist nicht öffentlich.') }}</p>
@endif
</div>
{{-- Abrufe durch Such- und KI-Dienste (Roadmap 1c.6). Ein
Abruf ist kein Zitat — die Anzeige belegt nur, dass der
Crawler des Dienstes die Seite geholt hat. --}}

View file

@ -49,6 +49,10 @@
<link rel="alternate" hreflang="x-default" href="{{ url('/de') }}">
@endsection
@section('feed_links')
<link rel="alternate" type="application/rss+xml" title="{{ __('web.rss.portal_link_title', ['brand' => ($brand['name'] ?? 'businessportal24').($brand['accent'] ?? '')]) }}" href="{{ rtrim($domainUrl ?? config('app.url'), '/') }}/{{ app()->getLocale() }}/feed">
@endsection
@section('content')
<x-web.site-header />

View file

@ -0,0 +1,27 @@
{!! '<?xml version="1.0" encoding="UTF-8"?>' !!}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>{{ $channel['title'] }}</title>
<link>{{ $channel['link'] }}</link>
<atom:link href="{{ $channel['self'] }}" rel="self" type="application/rss+xml" />
<description>{{ $channel['description'] }}</description>
<language>{{ $channel['language'] }}</language>
<lastBuildDate>{{ $channel['last_build'] }}</lastBuildDate>
<ttl>15</ttl>
@foreach ($items as $item)
<item>
<title>{{ $item['title'] }}</title>
<link>{{ $item['link'] }}</link>
<guid isPermaLink="true">{{ $item['link'] }}</guid>
<pubDate>{{ $item['published_at'] }}</pubDate>
<description>{{ $item['description'] }}</description>
@if ($item['category'])
<category>{{ $item['category'] }}</category>
@endif
@if ($item['creator'])
<dc:creator>{{ $item['creator'] }}</dc:creator>
@endif
</item>
@endforeach
</channel>
</rss>

View file

@ -75,6 +75,12 @@
@endsection
@endif
@section('feed_links')
@if ($catSlug)
<link rel="alternate" type="application/rss+xml" title="{{ __('web.rss.category_link_title', ['category' => $catName, 'brand' => $brandLabel]) }}" href="{{ $portalBase }}/{{ app()->getLocale() }}/category/{{ $catSlug }}/feed">
@endif
@endsection
@section('structured_data')
<x-web.structured-data :data="$jsonLd" />
@endsection

View file

@ -72,6 +72,13 @@
@endsection
@endif
@section('feed_links')
@if ($catSlug)
<link rel="alternate" type="application/rss+xml" title="{{ __('web.rss.category_link_title', ['category' => $catName, 'brand' => $brandLabel]) }}" href="{{ $portalBase }}/{{ app()->getLocale() }}/category/{{ $catSlug }}/feed">
@endif
<link rel="alternate" type="application/rss+xml" title="{{ __('web.rss.portal_link_title', ['brand' => $brandLabel]) }}" href="{{ $portalBase }}/{{ app()->getLocale() }}/feed">
@endsection
@section('structured_data')
<x-web.structured-data :data="$jsonLd" />
@endsection
@ -270,7 +277,10 @@
<nav class="mt-9 pt-6 border-t border-bg-rule" aria-label="{{ __('web.category_archive.nav_label') }}">
<div class="flex items-baseline justify-between flex-wrap gap-2 mb-3">
<h3 class="font-serif text-[18px] font-semibold m-0 text-ink">{{ __('web.category_archive.section_title', ['category' => $catName]) }}</h3>
<span class="text-[11.5px] text-ink-3">{{ __('web.category_archive.section_total', ['count' => $fmt(array_sum($archiveYears))]) }}</span>
<span class="text-[11.5px] text-ink-3">
{{ __('web.category_archive.section_total', ['count' => $fmt(array_sum($archiveYears))]) }}
· <a href="{{ $portalBase }}/{{ app()->getLocale() }}/category/{{ $catSlug }}/feed" type="application/rss+xml" class="text-ink-2 underline underline-offset-2 hover:text-ink" aria-label="{{ __('web.rss.link_aria', ['category' => $catName]) }}">{{ __('web.rss.link') }}</a>
</span>
</div>
<ul class="m-0 p-0 list-none flex flex-wrap gap-1.5">
@foreach ($archiveYears as $archiveYear => $archiveCount)

View file

@ -48,6 +48,9 @@
@yield('hreflang')
@endif
{{-- RSS-Autodiscovery (Roadmap 3.1) – Portal-Feed und, auf Branchenseiten, der Feed der Branche --}}
@yield('feed_links')
{{-- OpenGraph / Social --}}
<meta property="og:type" content="{{ $ogType }}">
<meta property="og:site_name" content="{{ $siteName }}">

View file

@ -49,6 +49,10 @@
<link rel="alternate" hreflang="x-default" href="{{ url('/de') }}">
@endsection
@section('feed_links')
<link rel="alternate" type="application/rss+xml" title="{{ __('web.rss.portal_link_title', ['brand' => ($brand['name'] ?? 'presseecho').($brand['accent'] ?? '')]) }}" href="{{ rtrim($domainUrl ?? config('app.url'), '/') }}/{{ app()->getLocale() }}/feed">
@endsection
@section('content')
<x-web.site-header />