web: RSS-Feed je Newsroom, RSS-Übersichtsseite, „RSS" in der Topleiste (Roadmap 3.1, Nachtrag)
- /{edition}/newsroom/{slug}/feed: Sichtbarkeit wie die Newsroom-Seite
(aktiv, nicht entfernt, mindestens eine Meldung), Prüfung vor dem Cache,
301 bei zusammengeführter Firma; Autodiscovery und Link „RSS-Feed" auf
der Newsroom-Seite; Hub leitet auf das Portal der Firma.
- /{edition}/rss (RssPageController, static.rss DE/EN): Was RSS ist, wie
ein Reader funktioniert, Hauptfeed, alle Branchen-Feeds mit Adresse,
Muster des Newsroom-Feeds, Hinweise. Portalseite mit Portal-Kontext,
Hub → presseecho, in Sitemap und llms.txt.
- Topleiste, Burger-Menü und Footer verlinken RSS neben FAQ/Hilfe/API.
- FeedController in portal/category/newsroom aufgeteilt; Panel
„Auslieferung" nennt zusätzlich den Newsroom-Feed.
- Tests: FeedTest (+3), RssPageTest (4), StaticPagesTest, VisibilityStatsTest.
- dev/2026-09-01: Nachtrag in Protokoll Teil 11 und Roadmap 3.1.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
9d5e5a0b54
commit
ff75159b8f
22 changed files with 577 additions and 55 deletions
|
|
@ -59,6 +59,7 @@
|
|||
<li><a href="{{ route('veroeffentlichen') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.publish') }}</a></li>
|
||||
<li><a href="{{ route('preise') }}" class="cursor-pointer hover:text-white transition-colors">{{ __('web.footer.pricing') }}</a></li>
|
||||
<li><a href="{{ route('api') }}" class="cursor-pointer hover:text-white transition-colors">API</a></li>
|
||||
<li><a href="{{ route('rss') }}" class="cursor-pointer hover:text-white transition-colors">RSS</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@
|
|||
<a href="{{ route('faq') }}" class="hidden lg:inline cursor-pointer hover:text-ink-on-dark transition-colors">FAQ</a>
|
||||
<a href="{{ route('hilfe') }}" class="hidden lg:inline cursor-pointer hover:text-ink-on-dark transition-colors">{{ __('web.header.help') }}</a>
|
||||
<a href="{{ route('api') }}" class="hidden lg:inline cursor-pointer hover:text-ink-on-dark transition-colors">API</a>
|
||||
<a href="{{ route('rss') }}" class="hidden lg:inline cursor-pointer hover:text-ink-on-dark transition-colors">RSS</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -441,7 +442,7 @@
|
|||
|
||||
{{-- Service-Links: auf Mobile sonst nur im Footer erreichbar (FAQ/Hilfe/API liegen in der Topbar hinter lg:). --}}
|
||||
<div class="mt-1 pt-1">
|
||||
@foreach ([['label' => __('web.header.pricing'), 'href' => route('preise')], ['label' => 'FAQ', 'href' => route('faq')], ['label' => __('web.header.help'), 'href' => route('hilfe')], ['label' => 'API', 'href' => route('api')]] as $service)
|
||||
@foreach ([['label' => __('web.header.pricing'), 'href' => route('preise')], ['label' => 'FAQ', 'href' => route('faq')], ['label' => __('web.header.help'), 'href' => route('hilfe')], ['label' => 'API', 'href' => route('api')], ['label' => 'RSS', 'href' => route('rss')]] as $service)
|
||||
<a href="{{ $service['href'] }}"
|
||||
class="flex items-center justify-between px-3 py-3 text-[13.5px] border-b border-bg-rule last:border-b-0 font-medium text-ink-3 hover:text-ink hover:bg-bg cursor-pointer transition-colors">
|
||||
<span>{{ $service['label'] }}</span>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@
|
|||
// Organization-Markup verknüpft PMs (author) mit diesem Profil.
|
||||
// Portal-Basis wie beim Canonical im Layout (unabhängig von forceRootUrl).
|
||||
$newsroomUrl = StructuredData::newsroomUrl(StructuredData::portalBase($domainUrl ?? null), $company->slug);
|
||||
// RSS-Feed des Newsrooms (Roadmap 3.1) — Adresse aus der Portal-Basis.
|
||||
$newsroomFeedUrl = StructuredData::portalBase($domainUrl ?? null).'/'.app()->getLocale().'/newsroom/'.$company->slug.'/feed';
|
||||
$portalFeedUrl = StructuredData::portalBase($domainUrl ?? null).'/'.app()->getLocale().'/feed';
|
||||
$jsonLd = [
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'Organization',
|
||||
|
|
@ -58,6 +61,11 @@
|
|||
<link rel="alternate" hreflang="x-default" href="{{ url('/de/newsroom/'.$company->slug) }}">
|
||||
@endsection
|
||||
|
||||
@section('feed_links')
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ __('web.rss.newsroom_link_title', ['company' => $company->name, 'brand' => $brandName.$brandAccent]) }}" href="{{ $newsroomFeedUrl }}">
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ __('web.rss.portal_link_title', ['brand' => $brandName.$brandAccent]) }}" href="{{ $portalFeedUrl }}">
|
||||
@endsection
|
||||
|
||||
@section('structured_data')
|
||||
<x-web.structured-data :data="$jsonLd" />
|
||||
@endsection
|
||||
|
|
@ -115,6 +123,11 @@
|
|||
<span class="text-ink-4" aria-hidden="true">·</span>
|
||||
<span>{{ __('web.newsroom_page.since', ['year' => $newsroomStats['sinceYear']]) }}</span>
|
||||
@endif
|
||||
<span class="text-ink-4" aria-hidden="true">·</span>
|
||||
<a href="{{ $newsroomFeedUrl }}" type="application/rss+xml" class="inline-flex items-center gap-1.5 font-medium hover:text-ink transition-colors" aria-label="{{ __('web.rss.newsroom_link_aria', ['company' => $company->name]) }}">
|
||||
<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M2 12.5A1.5 1.5 0 1 1 3.5 14 1.5 1.5 0 0 1 2 12.5ZM2 8a6 6 0 0 1 6 6h2a8 8 0 0 0-8-8Zm0-4a10 10 0 0 1 10 10h2A12 12 0 0 0 2 2Z"/></svg>
|
||||
{{ __('web.newsroom_page.feed_link') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
104
resources/views/web/rss.blade.php
Normal file
104
resources/views/web/rss.blade.php
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
@extends('web.layouts.web-master')
|
||||
|
||||
@php
|
||||
// Roadmap 3.1: Übersicht der Feeds dieses Portals. Adressen aus der
|
||||
// Portal-Basis (wie Canonical und Autodiscovery), nicht aus url()/route(),
|
||||
// die auf dem Hub den falschen Host trügen. Kontext und Kategorien liefert
|
||||
// RssPageController.
|
||||
$edition = app()->getLocale();
|
||||
$themeKey = $portalContext->domainKey;
|
||||
$portalBase = rtrim((string) ($portalContext->domainConfig['url'] ?? config('app.url')), '/');
|
||||
$brand = (array) ($portalContext->domainConfig['brand'] ?? []);
|
||||
$brandLabel = ($brand['name'] ?? $themeKey).($brand['accent'] ?? '');
|
||||
|
||||
$otherKey = $themeKey === 'presseecho' ? 'businessportal24' : 'presseecho';
|
||||
$otherBrand = (array) config('domains.domains.'.$otherKey.'.brand', []);
|
||||
$otherLabel = ($otherBrand['name'] ?? $otherKey).($otherBrand['accent'] ?? '');
|
||||
$otherBase = rtrim((string) config('domains.domains.'.$otherKey.'.url'), '/');
|
||||
|
||||
$portalFeed = $portalBase.'/'.$edition.'/feed';
|
||||
$categories = $feedCategories;
|
||||
$maxItems = \App\Http\Controllers\Web\FeedController::MAX_ITEMS;
|
||||
@endphp
|
||||
|
||||
@section('title', __('static.rss.meta_title', ['brand' => $brandLabel]))
|
||||
@section('meta_description', __('static.rss.meta_description'))
|
||||
|
||||
@section('feed_links')
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ __('web.rss.portal_link_title', ['brand' => $brandLabel]) }}" href="{{ $portalFeed }}">
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<x-web.static-page
|
||||
:eyebrow="__('static.rss.eyebrow')"
|
||||
:title="__('static.rss.title')"
|
||||
:lead="__('static.rss.lead', ['brand' => $brandLabel])"
|
||||
:breadcrumb="__('static.rss.breadcrumb')"
|
||||
>
|
||||
<div class="pm-body max-w-[760px]">
|
||||
<h2 id="was-ist-rss">{{ __('static.rss.what_heading') }}</h2>
|
||||
<p>{{ __('static.rss.what_p1') }}</p>
|
||||
<p>{{ __('static.rss.what_p2', ['brand' => $brandLabel]) }}</p>
|
||||
|
||||
<h2 id="so-funktioniert-es">{{ __('static.rss.how_heading') }}</h2>
|
||||
<ol>
|
||||
<li><strong>{{ __('static.rss.how_step1') }}</strong> — {{ __('static.rss.how_step1_text') }}</li>
|
||||
<li><strong>{{ __('static.rss.how_step2') }}</strong> — {{ __('static.rss.how_step2_text') }}</li>
|
||||
<li><strong>{{ __('static.rss.how_step3') }}</strong> — {{ __('static.rss.how_step3_text') }}</li>
|
||||
</ol>
|
||||
<p>{{ __('static.rss.how_p1', ['count' => $maxItems]) }}</p>
|
||||
<p>{{ __('static.rss.how_p2') }}</p>
|
||||
</div>
|
||||
|
||||
{{-- Portal-Feed --}}
|
||||
<h2 id="feeds" class="font-serif text-[22px] font-semibold tracking-[-0.3px] text-ink m-0 mt-12 mb-4">{{ __('static.rss.feeds_heading', ['brand' => $brandLabel]) }}</h2>
|
||||
<div class="border border-bg-rule bg-bg-elev p-6 sm:p-8">
|
||||
<div class="eyebrow mb-2">{{ __('static.rss.main_eyebrow') }}</div>
|
||||
<h3 class="font-serif text-[20px] font-semibold tracking-[-0.3px] text-ink m-0">{{ __('static.rss.main_title', ['brand' => $brandLabel]) }}</h3>
|
||||
<p class="text-[13px] text-ink-2 mt-1.5 mb-4">{{ __('static.rss.main_text') }}</p>
|
||||
<a href="{{ $portalFeed }}" type="application/rss+xml" class="inline-flex items-center gap-2 font-mono text-[13px] text-ink underline underline-offset-2 hover:text-brand transition-colors break-all">
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M2 12.5A1.5 1.5 0 1 1 3.5 14 1.5 1.5 0 0 1 2 12.5ZM2 8a6 6 0 0 1 6 6h2a8 8 0 0 0-8-8Zm0-4a10 10 0 0 1 10 10h2A12 12 0 0 0 2 2Z"/></svg>
|
||||
{{ $portalFeed }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- Branchen-Feeds --}}
|
||||
<h2 id="branchen" class="font-serif text-[22px] font-semibold tracking-[-0.3px] text-ink m-0 mt-12 mb-1.5">{{ __('static.rss.categories_heading') }}</h2>
|
||||
<p class="text-[13px] text-ink-2 m-0 mb-4">{{ __('static.rss.categories_text') }}</p>
|
||||
@if ($categories->isNotEmpty())
|
||||
<ul class="list-none p-0 m-0 grid gap-px bg-bg-rule border border-bg-rule grid-cols-1 md:grid-cols-2">
|
||||
@foreach ($categories as $category)
|
||||
@php $categoryFeed = $portalBase.'/'.$edition.'/category/'.$category['slug'].'/feed'; @endphp
|
||||
<li class="bg-bg p-4 sm:p-5">
|
||||
<div class="flex items-baseline justify-between gap-3 flex-wrap">
|
||||
<a href="{{ $category['href'] }}" class="text-[14px] font-semibold text-ink hover:text-brand transition-colors">{{ $category['label'] }}</a>
|
||||
<a href="{{ $categoryFeed }}" type="application/rss+xml" class="inline-flex items-center gap-1.5 text-[12px] font-semibold text-ink-3 hover:text-brand transition-colors" aria-label="{{ __('web.rss.link_aria', ['category' => $category['label']]) }}">
|
||||
<svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M2 12.5A1.5 1.5 0 1 1 3.5 14 1.5 1.5 0 0 1 2 12.5ZM2 8a6 6 0 0 1 6 6h2a8 8 0 0 0-8-8Zm0-4a10 10 0 0 1 10 10h2A12 12 0 0 0 2 2Z"/></svg>
|
||||
{{ __('web.rss.link') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="font-mono text-[11.5px] text-ink-3 mt-1 break-all">{{ $categoryFeed }}</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@else
|
||||
<p class="text-[13px] text-ink-3 m-0">{{ __('static.rss.categories_empty') }}</p>
|
||||
@endif
|
||||
|
||||
{{-- Newsroom-Feeds --}}
|
||||
<h2 id="newsrooms" class="font-serif text-[22px] font-semibold tracking-[-0.3px] text-ink m-0 mt-12 mb-1.5">{{ __('static.rss.newsrooms_heading') }}</h2>
|
||||
<div class="pm-body max-w-[760px]">
|
||||
<p>{{ __('static.rss.newsrooms_p1') }}</p>
|
||||
<p>{{ __('static.rss.newsrooms_p2') }} <code>{{ $portalBase }}/{{ $edition }}/newsroom/{{ __('static.rss.newsrooms_slug_placeholder') }}/feed</code></p>
|
||||
<p><a href="{{ route('newsrooms') }}">{{ __('static.rss.newsrooms_link') }}</a></p>
|
||||
|
||||
<h2 id="hinweise">{{ __('static.rss.notes_heading') }}</h2>
|
||||
<ul>
|
||||
<li>{{ __('static.rss.note_editions', ['de' => $portalBase.'/de/feed', 'en' => $portalBase.'/en/feed']) }}</li>
|
||||
<li>{{ __('static.rss.note_other_portal', ['portal' => $otherLabel]) }} <a href="{{ $otherBase }}/{{ $edition }}/rss">{{ $otherBase }}/{{ $edition }}/rss</a></li>
|
||||
<li>{{ __('static.rss.note_content') }}</li>
|
||||
<li>{{ __('static.rss.note_free') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</x-web.static-page>
|
||||
@endsection
|
||||
Loading…
Add table
Add a link
Reference in a new issue