web: Hilfeseite "Was eine gute Pressemitteilung ausmacht", Hinweis in der Ablehnungsmail (Roadmap 1d.1, 1d.2, 1d.6)

Ohne kommerziellen Anteil (E6): Anlass, Aufbau, Sprache, Pruefablauf mit
konfigurierbarer Versuchszahl, die sieben Qualitaetsdimensionen offen,
Gewichtung nicht (Offenlegungsgrenze), Pressebilder, Partner als BALD.
DE und EN, Sitemap, Hilfe-Center. Die Ablehnungsmail verlinkt die Seite
auf dem Portal der Meldung mit Herkunft mail_reject.

Config: yellow_to_manual_queue Vorgabe false (Entscheidung 16.09.2026),
max_automatic_attempts = 3 (E3).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-16 13:59:23 +00:00
parent 7fb5a2f9e9
commit 5f7fbd0c65
11 changed files with 403 additions and 5 deletions

View file

@ -24,13 +24,13 @@ class SitemapController extends Controller
{
/** @var list<string> */
private const HUB_PAGES = [
'faq', 'hilfe', 'api', 'kuendigung',
'faq', 'hilfe', 'gute-pressemitteilung', 'api', 'kuendigung',
'impressum', 'datenschutz', 'agb', 'richtlinien', 'melden',
];
/** @var list<string> */
private const PORTAL_PAGES = [
'preise', 'veroeffentlichen', 'newsrooms', 'faq', 'hilfe', 'api',
'preise', 'veroeffentlichen', 'newsrooms', 'faq', 'hilfe', 'gute-pressemitteilung', 'api',
'impressum', 'datenschutz', 'agb', 'richtlinien', 'melden',
];

View file

@ -4,6 +4,7 @@ namespace App\Mail;
use App\Enums\ConversionSource;
use App\Enums\EmailNotificationType;
use App\Enums\Portal;
use App\Mail\Concerns\Unsubscribable;
use App\Models\PressRelease;
use App\Models\User;
@ -19,6 +20,9 @@ class PressReleaseRejected extends Mailable
public string $editUrl;
/** Hilfeseite ohne kommerziellen Anteil (Roadmap 1d.2, E6). */
public string $helpUrl;
/**
* Name für die Anrede — bei API-Einlieferungen der Pressekontakt bzw.
* die Firma, nie der Kontoname des API-Einlieferers.
@ -43,9 +47,31 @@ class PressReleaseRejected extends Mailable
'pr' => $pressRelease->id,
]);
$this->helpUrl = $this->helpUrl($pressRelease);
$this->configureUnsubscribe($user, EmailNotificationType::PressReleaseRejected);
}
/**
* „Was eine gute Pressemitteilung ausmacht" auf dem Portal der Meldung,
* mit Herkunft und Meldung im Link — damit messbar ist, ob der Hinweis
* gelesen wird und ob danach eine Wiedereinreichung folgt.
*/
private function helpUrl(PressRelease $pressRelease): string
{
$host = rtrim((string) match ($pressRelease->portal) {
Portal::Presseecho => config('domains.domain_presseecho_url'),
Portal::Businessportal24 => config('domains.domain_businessportal_url'),
default => config('domains.domain_portal_url'),
}, '/');
$edition = in_array($pressRelease->language, ['de', 'en'], true) ? $pressRelease->language : 'de';
return sprintf('%s/%s/gute-pressemitteilung?%s', $host, $edition, http_build_query([
'src' => ConversionSource::MailReject->value,
'pr' => $pressRelease->id,
]));
}
/**
* Bounce-Referenz: Ein DSN bringt die Original-Header zurück, so ordnet
* der Parser den Rückläufer der Meldung zu (Roadmap 1b.6). Gesetzt über

View file

@ -42,8 +42,17 @@ return [
// (Konzept-Option, 0 = sofort).
'green_delay_minutes' => (int) env('CLASSIFICATION_GREEN_DELAY', 0),
// Ob „gelb" eingestufte PMs in die manuelle Admin-Queue gehen.
'yellow_to_manual_queue' => (bool) env('CLASSIFICATION_YELLOW_MANUAL', true),
// Ob „gelb" eingestufte PMs in die manuelle Admin-Queue gehen statt
// automatisch online (Entscheidung 16.09.2026: aus — Gelb geht durch
// und wird im Admin sichtbar markiert; der Schalter bleibt für den
// Fall, dass das einmal gebraucht wird).
'yellow_to_manual_queue' => (bool) env('CLASSIFICATION_YELLOW_MANUAL', false),
// E3 (entschieden 16.09.2026): So oft läuft die automatische Prüfung je
// Meldung. Ist der Text danach weiterhin Rot, geht die Meldung geblockt
// in die Admin-Queue; nach der menschlichen Entscheidung ist die
// Automatik für diese Meldung aus.
'max_automatic_attempts' => (int) env('CLASSIFICATION_MAX_ATTEMPTS', 3),
],
/*

View file

@ -70,6 +70,106 @@ return [
'visit_help' => 'Hilfe-Center besuchen',
],
'good_release' => [
'meta_title' => 'Was eine gute Pressemitteilung ausmacht',
'meta_description' => 'Anlass, Aufbau, Sprache, Prüfung und Pressebilder: was eine Pressemitteilung braucht, um veröffentlicht und gelesen zu werden.',
'eyebrow' => 'Hilfe',
'title' => 'Was eine gute Pressemitteilung ausmacht',
'lead' => 'Eine Pressemitteilung wird veröffentlicht, wenn sie eine Nachricht enthält und den Richtlinien entspricht. Gelesen wird sie, wenn sie gut gemacht ist. Diese Seite erklärt beides — ohne Geheimnisse, ohne Verkaufsabsicht.',
'breadcrumb' => 'Gute Pressemitteilung',
'occasion_heading' => 'Der Anlass: erst die Nachricht, dann alles andere',
'occasion_p1' => 'Eine Pressemitteilung berichtet über etwas, das passiert ist oder passieren wird. Sie beantwortet in den ersten Zeilen, was neu ist und für wen das von Bedeutung ist. Fehlt dieser Anlass, ist der Text eine Anzeige — und wird als solche gelesen, auch wenn er formal wie eine Pressemitteilung aussieht.',
'occasion_p2' => 'Tragfähige Anlässe sind zum Beispiel:',
'occasion_li1' => 'ein neues Produkt, eine neue Dienstleistung oder eine wesentliche Änderung an einem bestehenden Angebot,',
'occasion_li2' => 'ein Ereignis: Eröffnung, Messeauftritt, Jubiläum, Kooperation, Auszeichnung,',
'occasion_li3' => 'eine Zahl, die etwas belegt: Umsatz, Studie, Umfrage, Marktanteil — mit Quelle,',
'occasion_li4' => 'eine Personalie oder eine Entscheidung, die das Unternehmen verändert.',
'occasion_p3' => 'Ein Test dafür: Streichen Sie den Firmennamen. Bleibt eine Information übrig, die jemand außerhalb des Unternehmens wissen wollte? Dann ist es eine Nachricht.',
'structure_heading' => 'Der Aufbau: das Wichtigste zuerst',
'structure_p1' => 'Journalisten und Leser entscheiden in Sekunden, ob sie weiterlesen. Der bewährte Aufbau folgt deshalb einer umgekehrten Pyramide — die Nachricht oben, Hintergrund und Details darunter, das Unternehmen am Ende.',
'structure_headline' => 'Überschrift',
'structure_headline_text' => 'sagt in einem Satz, was passiert ist. Aktiv, konkret, ohne Superlative. 30 bis 90 Zeichen sind ein guter Rahmen.',
'structure_lead' => 'Vorspann (Lead)',
'structure_lead_text' => 'zwei bis vier Sätze mit Ort, Datum und den wichtigsten Antworten: Wer, was, wann, wo, warum. Wer nur den Lead liest, kennt die Nachricht.',
'structure_body' => 'Hauptteil',
'structure_body_text' => 'Hintergrund, Zahlen, Einordnung — in kurzen Absätzen, jeder mit einem eigenen Gedanken. Zwischenüberschriften helfen ab etwa 2.000 Zeichen.',
'structure_quote' => 'Zitat',
'structure_quote_text' => 'eine Person mit Namen und Funktion sagt etwas, das der Text nicht schon gesagt hat: eine Einschätzung, eine Begründung, einen Ausblick. Kein Werbespruch in Anführungszeichen.',
'structure_boilerplate' => 'Unternehmensinformation',
'structure_boilerplate_text' => 'ein kurzer, gleichbleibender Absatz über das Unternehmen — Gründung, Tätigkeit, Größe, Sitz.',
'structure_contact' => 'Pressekontakt',
'structure_contact_text' => 'Name, Funktion, E-Mail, Telefon. Eine erreichbare Adresse — Rückfragen sind der Sinn der Sache.',
'language_heading' => 'Die Sprache: informieren statt anpreisen',
'language_p1' => 'Pressesprache ist sachlich. Sie beschreibt, was ist, und überlässt die Bewertung dem Leser. Vier Regeln, die den meisten Texten sofort helfen:',
'language_li1' => 'Aktiv formulieren: „Das Unternehmen eröffnet“ statt „Es wurde eröffnet“.',
'language_li2' => 'Konkret werden: „340 Mitarbeitende an drei Standorten“ statt „ein großes Team“.',
'language_li3' => 'Wertungen weglassen: „innovativ“, „einzigartig“, „führend“ belegen nichts. Ein Faktum belegt etwas.',
'language_li4' => 'Zahlen mit Quelle: Wer eine Studie zitiert, nennt sie. Wer einen Marktanteil behauptet, sagt, woher die Zahl kommt.',
'check_heading' => 'So prüfen wir',
'check_p1' => 'Wenn Sie eine Mitteilung zur Prüfung freigeben, geht sie nicht sofort online. Zuerst läuft eine automatische Inhaltsprüfung gegen unsere Veröffentlichungsrichtlinien. Das Ergebnis ist eine von drei Stufen:',
'check_green' => 'Grün',
'check_green_text' => 'die Mitteilung erfüllt die Richtlinien und wird veröffentlicht.',
'check_yellow' => 'Gelb',
'check_yellow_text' => 'veröffentlichungsfähig mit Hinweisen, etwa werblicher Sprache oder Zahlen ohne Quelle. Sie wird veröffentlicht; die Hinweise sehen Sie in Ihrem Konto.',
'check_red' => 'Rot',
'check_red_text' => 'die Mitteilung verstößt gegen mindestens eine Richtlinienkategorie und geht nicht online. Sie erhalten die Kategorie und können den Text überarbeiten und erneut freigeben.',
'check_p2' => 'Die automatische Prüfung läuft je Mitteilung bis zu :attempts Mal. Ist der Text danach weiterhin Rot, prüft ein Mensch. Er schaltet die Mitteilung frei oder schreibt Ihnen, woran es liegt — ab dann entscheidet für diese Mitteilung nur noch ein Mensch, nicht mehr die Automatik. Die Kategorien, gegen die geprüft wird, stehen in den <a href=":guidelines">Veröffentlichungsrichtlinien</a>.',
'check_p3' => 'Eine abgelehnte Mitteilung verbraucht kein Kontingent. Und Sie können jederzeit eine Überprüfung durch einen Menschen verlangen — formlos über Ihr Konto.',
'dimensions_heading' => 'Was wir bei der Qualität ansehen',
'dimensions_intro' => 'Neben der Zulässigkeit bewerten wir die handwerkliche Qualität. Diese Bewertung entscheidet nicht über die Veröffentlichung, aber sie zeigt Ihnen, wo der Text besser werden kann. Wir sehen auf sieben Dinge:',
'dim_style' => 'Pressestil',
'dim_style_text' => 'informativ statt werblich, aktive Sprache, echte Zitate.',
'dim_structure' => 'Struktur',
'dim_structure_text' => 'ein Lead-Absatz, sinnvolle Absätze, das Wichtigste oben.',
'dim_readability' => 'Lesbarkeit',
'dim_readability_text' => 'Satzlängen, die man atmen kann; Fachsprache nur, wo sie nötig ist.',
'dim_completeness' => 'Vollständigkeit',
'dim_completeness_text' => 'Pressekontakt, Unternehmensinformation, Datum, Ort.',
'dim_sources' => 'Quellen und Belege',
'dim_sources_text' => 'Verlinkungen, Studien, Datenquellen für Behauptungen.',
'dim_headline' => 'Überschrift',
'dim_headline_text' => 'Länge, Klarheit, ob sie die Nachricht trägt.',
'dim_originality' => 'Eigenständigkeit',
'dim_originality_text' => 'ein eigener Ton statt austauschbarer Textbausteine.',
'dimensions_note' => 'Diese sieben Dimensionen sind offen — Verbesserung an ihnen ist tatsächliche Verbesserung. Wie sie gewichtet werden und wo die Schwellen liegen, veröffentlichen wir nicht: Wer wüsste, wie man knapp durchrutscht, würde knapp durchrutschen.',
'improve_heading' => 'Was Sie vor der Freigabe prüfen können',
'improve_p1' => 'Sechs Fragen, die die häufigsten Schwächen aufdecken:',
'improve_li1' => 'Steht die Nachricht im ersten Satz — oder erst nach drei Sätzen über das Unternehmen?',
'improve_li2' => 'Kommen Ort und Datum vor? Weiß der Leser, wann und wo das passiert ist?',
'improve_li3' => 'Hat jede Zahl eine Quelle und jedes Zitat einen Namen mit Funktion?',
'improve_li4' => 'Lässt sich jedes „führend“, „innovativ“, „einzigartig“ durch ein Faktum ersetzen — oder streichen?',
'improve_li5' => 'Ist der Pressekontakt erreichbar und die E-Mail-Adresse richtig geschrieben?',
'improve_li6' => 'Würde der Text auch dann Sinn ergeben, wenn er nicht auf Ihrer eigenen Website stünde?',
'images_heading' => 'Pressebilder',
'images_p1' => 'Ein Bild erhöht die Aufmerksamkeit deutlich — wenn es ein Pressebild ist und keine Werbegrafik. Fünf Dinge, die stimmen müssen:',
'images_rights' => 'Rechte',
'images_rights_text' => 'Sie dürfen das Bild veröffentlichen und zur Weiterverwendung freigeben. Bei abgebildeten Personen liegt deren Einverständnis vor. Der Urheber wird genannt.',
'images_ai' => 'KI-Kennzeichnung',
'images_ai_text' => 'Ob ein Bild mit KI erzeugt oder wesentlich verändert wurde, deklarieren Sie beim Hochladen. Fotorealistische KI-Motive werden entsprechend gekennzeichnet — das verlangt die KI-Verordnung, und es schützt Ihre Glaubwürdigkeit.',
'images_motif' => 'Motiv',
'images_motif_text' => 'Das Bild zeigt, worum es geht: das Produkt, den Ort, die Person, das Ereignis. Logos, Banner und Textgrafiken sind keine Pressebilder.',
'images_caption' => 'Bildunterschrift',
'images_caption_text' => 'Wer oder was ist zu sehen, wo und wann? Eine Zeile reicht — ohne sie ist das Bild für Redaktionen unbrauchbar.',
'images_format' => 'Format',
'images_format_text' => 'JPG, PNG oder WebP, ausreichend groß für die Verwendung durch Dritte; Querformat funktioniert in den meisten Layouts am besten.',
'more_heading' => 'Weiterführend',
'more_guidelines' => 'Veröffentlichungsrichtlinien — die Kategorien, gegen die geprüft wird',
'more_publish' => 'Veröffentlichen — der Weg vom Entwurf zur Meldung',
'more_faq' => 'Häufige Fragen',
'partners_heading' => 'Partner',
'partners_badge' => 'Bald',
'partners_text' => 'An dieser Stelle stellen wir künftig Partner vor, die bei Text, Bild und Verbreitung unterstützen. Bis dahin steht hier bewusst nichts.',
],
'help' => [
'meta_title' => 'Hilfe-Center',
'meta_description' => 'Hilfe-Center: Antworten, Anleitungen und direkter Kontakt zum Support-Team.',
@ -92,6 +192,7 @@ return [
'link_discover_newsrooms' => 'Newsrooms entdecken',
'link_faq_start' => 'Häufige Fragen zum Einstieg',
'topic_releases' => 'Pressemitteilungen',
'link_good_release' => 'Was eine gute Pressemitteilung ausmacht',
'link_file_formats' => 'Dateiformate und Größen',
'link_seo' => 'SEO-Optimierung',
'link_corrections' => 'Korrekturen nach Veröffentlichung',

View file

@ -69,6 +69,106 @@ return [
'visit_help' => 'Visit the help center',
],
'good_release' => [
'meta_title' => 'What makes a good press release',
'meta_description' => 'Occasion, structure, language, review and press images: what a press release needs to be published and read.',
'eyebrow' => 'Help',
'title' => 'What makes a good press release',
'lead' => 'A press release is published when it carries news and meets the guidelines. It is read when it is well made. This page explains both — no secrets, nothing for sale.',
'breadcrumb' => 'Good press release',
'occasion_heading' => 'The occasion: news first, everything else after',
'occasion_p1' => 'A press release reports on something that has happened or will happen. Its first lines answer what is new and who it matters to. Without that occasion the text is an advertisement — and will be read as one, however much it looks like a press release.',
'occasion_p2' => 'Occasions that carry a release include:',
'occasion_li1' => 'a new product, a new service or a substantial change to an existing offer,',
'occasion_li2' => 'an event: opening, trade fair, anniversary, partnership, award,',
'occasion_li3' => 'a figure that proves something: revenue, study, survey, market share — with its source,',
'occasion_li4' => 'an appointment or a decision that changes the company.',
'occasion_p3' => 'A quick test: remove the company name. Is there still information someone outside the company would want to know? Then it is news.',
'structure_heading' => 'The structure: the most important thing first',
'structure_p1' => 'Journalists and readers decide within seconds whether to keep reading. The proven structure is therefore an inverted pyramid — the news on top, background and detail below, the company at the end.',
'structure_headline' => 'Headline',
'structure_headline_text' => 'says in one sentence what happened. Active, concrete, no superlatives. 30 to 90 characters is a good range.',
'structure_lead' => 'Lead paragraph',
'structure_lead_text' => 'two to four sentences with place, date and the key answers: who, what, when, where, why. Whoever reads only the lead knows the news.',
'structure_body' => 'Body',
'structure_body_text' => 'background, figures, context — in short paragraphs, each with one idea. Subheadings help from roughly 2,000 characters on.',
'structure_quote' => 'Quote',
'structure_quote_text' => 'a named person with a role says something the text has not already said: an assessment, a reason, an outlook. Not a slogan in quotation marks.',
'structure_boilerplate' => 'About the company',
'structure_boilerplate_text' => 'a short, consistent paragraph — founded when, does what, how large, based where.',
'structure_contact' => 'Press contact',
'structure_contact_text' => 'name, role, e-mail, phone. A reachable address — follow-up questions are the point.',
'language_heading' => 'The language: inform, do not promote',
'language_p1' => 'Press language is factual. It describes what is and leaves the judgement to the reader. Four rules that help most texts immediately:',
'language_li1' => 'Use the active voice: "The company opens" rather than "was opened".',
'language_li2' => 'Be concrete: "340 employees at three sites" rather than "a large team".',
'language_li3' => 'Drop the adjectives: "innovative", "unique", "leading" prove nothing. A fact does.',
'language_li4' => 'Give figures a source: whoever cites a study names it; whoever claims a market share says where the number comes from.',
'check_heading' => 'How we review',
'check_p1' => 'When you release a text for review it does not go online immediately. An automatic content check against our publishing guidelines runs first. The result is one of three levels:',
'check_green' => 'Green',
'check_green_text' => 'the release meets the guidelines and is published.',
'check_yellow' => 'Yellow',
'check_yellow_text' => 'publishable with notes, such as promotional language or figures without a source. It is published; you see the notes in your account.',
'check_red' => 'Red',
'check_red_text' => 'the release violates at least one guideline category and does not go online. You receive the category and can revise the text and release it again.',
'check_p2' => 'The automatic check runs up to :attempts times per release. If the text is still red after that, a person reviews it. They either approve the release or write to you about what is wrong — from then on, a person decides on this release, not the automation. The categories we check against are listed in the <a href=":guidelines">publishing guidelines</a>.',
'check_p3' => 'A rejected release does not use up your quota. And you can request a human review at any time — informally, from your account.',
'dimensions_heading' => 'What we look at for quality',
'dimensions_intro' => 'Besides admissibility we assess craftsmanship. This assessment does not decide publication, but it shows you where the text can improve. We look at seven things:',
'dim_style' => 'Press style',
'dim_style_text' => 'informative rather than promotional, active voice, real quotes.',
'dim_structure' => 'Structure',
'dim_structure_text' => 'a lead paragraph, sensible paragraphs, the essentials on top.',
'dim_readability' => 'Readability',
'dim_readability_text' => 'sentence lengths you can breathe through; jargon only where needed.',
'dim_completeness' => 'Completeness',
'dim_completeness_text' => 'press contact, company information, date, place.',
'dim_sources' => 'Sources and evidence',
'dim_sources_text' => 'links, studies, data sources for claims.',
'dim_headline' => 'Headline',
'dim_headline_text' => 'length, clarity, whether it carries the news.',
'dim_originality' => 'Originality',
'dim_originality_text' => 'a voice of its own rather than interchangeable boilerplate.',
'dimensions_note' => 'These seven dimensions are public — improving on them is real improvement. How they are weighted and where the thresholds lie we do not publish: whoever knew how to scrape through would scrape through.',
'improve_heading' => 'What to check before releasing',
'improve_p1' => 'Six questions that expose the most common weaknesses:',
'improve_li1' => 'Is the news in the first sentence — or only after three sentences about the company?',
'improve_li2' => 'Are place and date given? Does the reader know when and where it happened?',
'improve_li3' => 'Does every figure have a source and every quote a name with a role?',
'improve_li4' => 'Can every "leading", "innovative", "unique" be replaced by a fact — or cut?',
'improve_li5' => 'Is the press contact reachable and the e-mail address spelled correctly?',
'improve_li6' => 'Would the text still make sense if it were not on your own website?',
'images_heading' => 'Press images',
'images_p1' => 'An image raises attention considerably — if it is a press image, not a promotional graphic. Five things must be right:',
'images_rights' => 'Rights',
'images_rights_text' => 'you may publish the image and release it for reuse. People shown have consented. The author is credited.',
'images_ai' => 'AI labelling',
'images_ai_text' => 'whether an image was generated or substantially altered with AI is declared on upload. Photorealistic AI motifs are labelled accordingly — the AI Act requires it, and it protects your credibility.',
'images_motif' => 'Motif',
'images_motif_text' => 'the image shows the subject: the product, the place, the person, the event. Logos, banners and text graphics are not press images.',
'images_caption' => 'Caption',
'images_caption_text' => 'who or what is shown, where and when? One line is enough — without it the image is useless to editors.',
'images_format' => 'Format',
'images_format_text' => 'JPG, PNG or WebP, large enough for third-party use; landscape works best in most layouts.',
'more_heading' => 'Further reading',
'more_guidelines' => 'Publishing guidelines — the categories we check against',
'more_publish' => 'Publishing — from draft to release',
'more_faq' => 'Frequently asked questions',
'partners_heading' => 'Partners',
'partners_badge' => 'Soon',
'partners_text' => 'This is where we will introduce partners who help with text, images and distribution. Until then this space is deliberately empty.',
],
'help' => [
'meta_title' => 'Help center',
'meta_description' => 'Help center: answers, guides and direct contact with the support team.',
@ -91,6 +191,7 @@ return [
'link_discover_newsrooms' => 'Discover newsrooms',
'link_faq_start' => 'Common questions for getting started',
'topic_releases' => 'Press releases',
'link_good_release' => 'What makes a good press release',
'link_file_formats' => 'File formats and sizes',
'link_seo' => 'SEO optimisation',
'link_corrections' => 'Corrections after publication',

View file

@ -16,6 +16,14 @@
<x-emails.button :url="$editUrl">Pressemitteilung bearbeiten</x-emails.button>
{{-- Roadmap 1d.2 (E6: die Ablehnungsstrecke bleibt frei von Angeboten):
ein Hinweis, was eine Mitteilung braucht, und der Weg zur Freigabe. --}}
<p style="font-size: 12.5px; color: #6b7280;">
Was eine Pressemitteilung braucht, um veröffentlicht zu werden — Anlass, Aufbau, Sprache, Bilder —
und wie die Prüfung abläuft, haben wir auf einer Seite zusammengefasst:<br>
<a href="{{ $helpUrl }}">Was eine gute Pressemitteilung ausmacht</a>
</p>
<div class="note">
<p>Bei Fragen erreichen Sie uns über den <a href="{{ config('domains.domain_portal_url') }}/de/hilfe">Hilfe-Bereich auf pressekonto</a>.</p>
</div>

View file

@ -0,0 +1,114 @@
@extends('web.layouts.web-master')
@section('title', __('static.good_release.meta_title'))
@section('meta_description', __('static.good_release.meta_description'))
@php
// Roadmap 1d.1: Hilfeseite ohne kommerziellen Anteil. Offenlegungsgrenze
// (Roadmap 1d): Rot-Kriterien nur als Kategorie (die stehen in den
// Richtlinien), Content-Dimensionen offen erklärt, Gewichtung und
// Schwellen nie. Kein Preis-, Tarif- oder Angebotslink auf dieser Seite.
$attempts = (int) config('scoring.classification.max_automatic_attempts', 3);
@endphp
@section('content')
<x-web.static-page
:eyebrow="__('static.good_release.eyebrow')"
:title="__('static.good_release.title')"
:lead="__('static.good_release.lead')"
:breadcrumb="__('static.good_release.breadcrumb')"
>
<div class="pm-body max-w-[760px]">
<h2 id="anlass">{{ __('static.good_release.occasion_heading') }}</h2>
<p>{{ __('static.good_release.occasion_p1') }}</p>
<p>{{ __('static.good_release.occasion_p2') }}</p>
<ul>
<li>{{ __('static.good_release.occasion_li1') }}</li>
<li>{{ __('static.good_release.occasion_li2') }}</li>
<li>{{ __('static.good_release.occasion_li3') }}</li>
<li>{{ __('static.good_release.occasion_li4') }}</li>
</ul>
<p>{{ __('static.good_release.occasion_p3') }}</p>
<h2 id="aufbau">{{ __('static.good_release.structure_heading') }}</h2>
<p>{{ __('static.good_release.structure_p1') }}</p>
<ol>
<li><strong>{{ __('static.good_release.structure_headline') }}</strong> — {{ __('static.good_release.structure_headline_text') }}</li>
<li><strong>{{ __('static.good_release.structure_lead') }}</strong> — {{ __('static.good_release.structure_lead_text') }}</li>
<li><strong>{{ __('static.good_release.structure_body') }}</strong> — {{ __('static.good_release.structure_body_text') }}</li>
<li><strong>{{ __('static.good_release.structure_quote') }}</strong> — {{ __('static.good_release.structure_quote_text') }}</li>
<li><strong>{{ __('static.good_release.structure_boilerplate') }}</strong> — {{ __('static.good_release.structure_boilerplate_text') }}</li>
<li><strong>{{ __('static.good_release.structure_contact') }}</strong> — {{ __('static.good_release.structure_contact_text') }}</li>
</ol>
<h2 id="sprache">{{ __('static.good_release.language_heading') }}</h2>
<p>{{ __('static.good_release.language_p1') }}</p>
<ul>
<li>{{ __('static.good_release.language_li1') }}</li>
<li>{{ __('static.good_release.language_li2') }}</li>
<li>{{ __('static.good_release.language_li3') }}</li>
<li>{{ __('static.good_release.language_li4') }}</li>
</ul>
<h2 id="pruefung">{{ __('static.good_release.check_heading') }}</h2>
<p>{{ __('static.good_release.check_p1') }}</p>
<ul>
<li><strong>{{ __('static.good_release.check_green') }}</strong> — {{ __('static.good_release.check_green_text') }}</li>
<li><strong>{{ __('static.good_release.check_yellow') }}</strong> — {{ __('static.good_release.check_yellow_text') }}</li>
<li><strong>{{ __('static.good_release.check_red') }}</strong> — {{ __('static.good_release.check_red_text') }}</li>
</ul>
<p>{!! __('static.good_release.check_p2', ['attempts' => $attempts, 'guidelines' => route('richtlinien')]) !!}</p>
<p>{{ __('static.good_release.check_p3') }}</p>
<h3>{{ __('static.good_release.dimensions_heading') }}</h3>
<p>{{ __('static.good_release.dimensions_intro') }}</p>
<ul>
<li><strong>{{ __('static.good_release.dim_style') }}</strong> — {{ __('static.good_release.dim_style_text') }}</li>
<li><strong>{{ __('static.good_release.dim_structure') }}</strong> — {{ __('static.good_release.dim_structure_text') }}</li>
<li><strong>{{ __('static.good_release.dim_readability') }}</strong> — {{ __('static.good_release.dim_readability_text') }}</li>
<li><strong>{{ __('static.good_release.dim_completeness') }}</strong> — {{ __('static.good_release.dim_completeness_text') }}</li>
<li><strong>{{ __('static.good_release.dim_sources') }}</strong> — {{ __('static.good_release.dim_sources_text') }}</li>
<li><strong>{{ __('static.good_release.dim_headline') }}</strong> — {{ __('static.good_release.dim_headline_text') }}</li>
<li><strong>{{ __('static.good_release.dim_originality') }}</strong> — {{ __('static.good_release.dim_originality_text') }}</li>
</ul>
<p class="text-ink-3">{{ __('static.good_release.dimensions_note') }}</p>
<h2 id="verbessern">{{ __('static.good_release.improve_heading') }}</h2>
<p>{{ __('static.good_release.improve_p1') }}</p>
<ul>
<li>{{ __('static.good_release.improve_li1') }}</li>
<li>{{ __('static.good_release.improve_li2') }}</li>
<li>{{ __('static.good_release.improve_li3') }}</li>
<li>{{ __('static.good_release.improve_li4') }}</li>
<li>{{ __('static.good_release.improve_li5') }}</li>
<li>{{ __('static.good_release.improve_li6') }}</li>
</ul>
<h2 id="bilder">{{ __('static.good_release.images_heading') }}</h2>
<p>{{ __('static.good_release.images_p1') }}</p>
<ul>
<li><strong>{{ __('static.good_release.images_rights') }}</strong> — {{ __('static.good_release.images_rights_text') }}</li>
<li><strong>{{ __('static.good_release.images_ai') }}</strong> — {{ __('static.good_release.images_ai_text') }}</li>
<li><strong>{{ __('static.good_release.images_motif') }}</strong> — {{ __('static.good_release.images_motif_text') }}</li>
<li><strong>{{ __('static.good_release.images_caption') }}</strong> — {{ __('static.good_release.images_caption_text') }}</li>
<li><strong>{{ __('static.good_release.images_format') }}</strong> — {{ __('static.good_release.images_format_text') }}</li>
</ul>
<h2 id="weiter">{{ __('static.good_release.more_heading') }}</h2>
<ul>
<li><a href="{{ route('richtlinien') }}">{{ __('static.good_release.more_guidelines') }}</a></li>
<li><a href="{{ route('veroeffentlichen') }}">{{ __('static.good_release.more_publish') }}</a></li>
<li><a href="{{ route('faq') }}">{{ __('static.good_release.more_faq') }}</a></li>
</ul>
</div>
{{-- Roadmap 1d.6: Partnerbereich, vorerst als BALD ohne Namen und Logos. --}}
<section class="mt-14 border-t border-bg-rule pt-8 max-w-[760px]" aria-labelledby="partner-heading">
<div class="flex items-center gap-3">
<h2 id="partner-heading" class="font-serif text-[20px] font-semibold tracking-[-0.3px] text-ink m-0">{{ __('static.good_release.partners_heading') }}</h2>
<span class="inline-flex items-center rounded-[3px] border border-bg-rule bg-bg-elev px-2 py-0.5 text-[10.5px] font-semibold uppercase tracking-[0.6px] text-ink-3">{{ __('static.good_release.partners_badge') }}</span>
</div>
<p class="text-[13.5px] text-ink-3 mt-2 mb-0">{{ __('static.good_release.partners_text') }}</p>
</section>
</x-web.static-page>
@endsection

View file

@ -27,6 +27,7 @@
[
'title' => __('static.help.topic_releases'),
'links' => [
['label' => __('static.help.link_good_release'), 'href' => route('gute-pressemitteilung')],
['label' => __('static.help.link_file_formats'), 'href' => route('faq')],
['label' => __('static.help.link_seo'), 'href' => route('faq')],
['label' => __('static.help.link_corrections'), 'href' => route('faq')],

View file

@ -157,7 +157,7 @@ $htmlRedirect = static function (string $edition, string $path) {
$staticPages = [
'preise', 'faq', 'veroeffentlichen',
'suche', 'newsrooms', 'api',
'hilfe', 'impressum', 'datenschutz', 'agb',
'hilfe', 'gute-pressemitteilung', 'impressum', 'datenschutz', 'agb',
];
if (isset($staticAliases[$clean])) {
@ -251,6 +251,9 @@ Route::prefix('{edition}')->whereIn('edition', SetEdition::EDITIONS)->group(func
->name('newsroom.claim.store');
Route::view('api', 'web.api')->name('api');
Route::view('hilfe', 'web.hilfe')->name('hilfe');
// Roadmap 1d.1: Hilfeseite ohne kommerziellen Anteil — Ziel des
// Textbausteins in der Ablehnungsmail (1d.2).
Route::view('gute-pressemitteilung', 'web.gute-pressemitteilung')->name('gute-pressemitteilung');
Route::view('impressum', 'web.impressum')->name('impressum');
// Gesetzlicher Kündigungsbutton (§ 312k BGB): öffentliche Bestätigungsseite

View file

@ -53,6 +53,15 @@ test('the rejection mail marks its own origin', function () {
// Ohne eigenen Herkunftswert wäre später nicht unterscheidbar, ob eine
// Wiedereinreichung aus der Ablehnungsmail kam.
expect($mail->editUrl)->toContain('src='.ConversionSource::MailReject->value);
// Roadmap 1d.2: Hilfeseite auf dem Portal der Meldung, gleiche Herkunft,
// im Text der Mail verlinkt — und nichts Kommerzielles daneben (E6).
$html = $mail->render();
expect($mail->helpUrl)->toContain('/gute-pressemitteilung?')
->and($mail->helpUrl)->toContain('src='.ConversionSource::MailReject->value)
->and($mail->helpUrl)->toContain('pr='.$release->id)
->and($html)->toContain('Was eine gute Pressemitteilung ausmacht')
->and($html)->not->toContain('/preise');
});
test('the origin is stored on the magic link token, not in a session', function () {

View file

@ -2,6 +2,7 @@
use App\Models\RejectionCategory;
use Database\Seeders\RejectionCategorySeeder;
use Illuminate\Support\Str;
use Tests\TestCase;
dataset('static pages', [
@ -10,6 +11,7 @@ dataset('static pages', [
'agb' => ['de/agb', 'Allgemeine Geschäftsbedingungen'],
'faq' => ['de/faq', 'Häufig gestellte Fragen'],
'hilfe' => ['de/hilfe', 'Hilfe-Center'],
'gute-pressemitteilung' => ['de/gute-pressemitteilung', 'Was eine gute Pressemitteilung ausmacht'],
'api' => ['de/api', 'API-Dokumentation'],
'richtlinien' => ['de/richtlinien', 'Veröffentlichungsrichtlinien'],
'melden' => ['de/melden', 'Inhalte melden oder Änderungen beantragen'],
@ -172,6 +174,7 @@ test('the help center shows quick links and topics', function () {
dataset('static pages in english', [
'faq' => ['en/faq', 'Frequently asked questions'],
'hilfe' => ['en/hilfe', 'Help center'],
'gute-pressemitteilung' => ['en/gute-pressemitteilung', 'What makes a good press release'],
'api' => ['en/api', 'API documentation'],
'impressum' => ['en/impressum', 'Imprint'],
'melden' => ['en/melden', 'Report content or request changes'],
@ -241,3 +244,26 @@ test('german validation messages are used on the de edition forms', function ()
expect($messages)->toContain('muss ausgefüllt werden')
->not->toContain('field is required');
});
test('the good-release help page explains the check without selling anything', function () {
/** @var TestCase $this */
// Roadmap 1d.1: ohne kommerziellen Anteil, Rot nur als Kategorie,
// Dimensionen offen, Versuchszahl aus der Konfiguration.
config()->set('scoring.classification.max_automatic_attempts', 2);
$response = $this->get('https://businessportal24.test/de/gute-pressemitteilung')
->assertSuccessful()
->assertSeeText('bis zu 2 Mal')
->assertSeeText('Pressestil')
->assertSeeText('Pressebilder')
->assertSeeText('Partner')
->assertSee(route('richtlinien'), false);
// Kein kommerzieller Anteil im Seiteninhalt (die Shell-Navigation zählt nicht).
$body = Str::between($response->getContent(), 'class="pm-body', 'aria-labelledby="partner-heading"');
expect($body)->not->toContain('/preise')->not->toContain('Credits');
// Aus dem Hilfe-Center und der Sitemap erreichbar.
$this->get('https://businessportal24.test/de/hilfe')->assertSee('/de/gute-pressemitteilung', false);
$this->get('https://businessportal24.test/sitemap.xml')->assertSee('/de/gute-pressemitteilung', false);
});