From 106edb1384ad7c538ce1020ec5e4b20f83665f2c Mon Sep 17 00:00:00 2001 From: Kevin Adametz Date: Thu, 17 Sep 2026 12:00:28 +0000 Subject: [PATCH] =?UTF-8?q?web:=20Transparenzseiten=20=E2=80=94=20was=20ko?= =?UTF-8?q?stet,=20ohne=20Abo,=20loeschen/aendern=20(Roadmap=202.4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drei Seiten mit unterschiedlicher Suchintention, DE/EN, Hub + Spokes: - /was-kostet-eine-pressemitteilung: kurze Antwort im ersten Bildschirm, vier Abrechnungsmodelle ohne Anbieternamen und fremde Preise, sechs Prueffragen fuer Folgekosten, "Was bei uns gilt" (sieben Zusagen mit Einloesung), Rechenbeispiel Einzelabrechnung gegen Starter-Monate mit Zeilen fuer beide Seiten. - /pressemitteilung-veroeffentlichen-ohne-abo: Ablauf, enthalten / nicht enthalten, nichts verfaellt, wann sich ein Tarif lohnt. - /pressemitteilung-loeschen-oder-aendern-lassen: fuenf Wege als Tabelle (Korrektur, Aenderung, DSGVO, Depublikation, DSA), Antragsweg ueber /melden/pm, Korrektur statt Loeschung, Absatz fuer Meldungen auf fremden Portalen ohne Zusagen ueber Dritte. PricingTransparencyData liefert alle Zahlen aus config/billing, credits, legal und dem Starter-Tarif; TransparencyPageController leitet die Marke aus dem Host ab. Tabellen-Typografie fuer .pm-body. Verlinkt aus Preisuebersicht, Hilfe-Center, Sitemaps und llms.txt. Test TransparencyPagesTest (9) erzwingt u. a., dass im Rechenbeispiel beide Modelle Zeilen gewinnen. Protokoll Teil 5, Roadmap 2.4 abgehakt. Co-Authored-By: Claude Opus 5 (1M context) --- .../Controllers/Web/LlmsTxtController.php | 5 + .../Controllers/Web/SitemapController.php | 2 + .../Web/TransparencyPageController.php | 50 +++++ app/Services/Web/PricingTransparencyData.php | 87 +++++++++ dev/2026-09-01/roadmap-2026.md | 2 +- dev/2026-09-01/umsetzung-protokoll.md | 66 ++++++- lang/de/pricing.php | 1 + lang/de/static.php | 180 ++++++++++++++++++ lang/en/pricing.php | 1 + lang/en/static.php | 179 +++++++++++++++++ resources/css/web/shared-styles.css | 28 +++ resources/views/web/hilfe.blade.php | 4 +- resources/views/web/preise.blade.php | 1 + ...ung-loeschen-oder-aendern-lassen.blade.php | 79 ++++++++ ...eilung-veroeffentlichen-ohne-abo.blade.php | 64 +++++++ ...was-kostet-eine-pressemitteilung.blade.php | 114 +++++++++++ routes/web.php | 7 + tests/Feature/Web/TransparencyPagesTest.php | 87 +++++++++ 18 files changed, 953 insertions(+), 4 deletions(-) create mode 100644 app/Http/Controllers/Web/TransparencyPageController.php create mode 100644 app/Services/Web/PricingTransparencyData.php create mode 100644 resources/views/web/pressemitteilung-loeschen-oder-aendern-lassen.blade.php create mode 100644 resources/views/web/pressemitteilung-veroeffentlichen-ohne-abo.blade.php create mode 100644 resources/views/web/was-kostet-eine-pressemitteilung.blade.php create mode 100644 tests/Feature/Web/TransparencyPagesTest.php diff --git a/app/Http/Controllers/Web/LlmsTxtController.php b/app/Http/Controllers/Web/LlmsTxtController.php index 814e726b..453c8009 100644 --- a/app/Http/Controllers/Web/LlmsTxtController.php +++ b/app/Http/Controllers/Web/LlmsTxtController.php @@ -48,6 +48,8 @@ class LlmsTxtController extends Controller '- [Was eine gute Pressemitteilung ausmacht]('.$base.'/de/gute-pressemitteilung): Anlass, Aufbau, Sprache, Prüfablauf', '- [FAQ]('.$base.'/de/faq)', '- [Richtlinien]('.$base.'/de/richtlinien): Was veröffentlicht wird und was nicht', + '- [Was kostet eine Pressemitteilung?]('.$base.'/de/was-kostet-eine-pressemitteilung): Abrechnungsmodelle, Prüfliste für Folgekosten, Rechenbeispiel', + '- [Pressemitteilung löschen oder ändern lassen]('.$base.'/de/pressemitteilung-loeschen-oder-aendern-lassen)', '- [API]('.$base.'/de/api): Einlieferung per REST-API; OpenAPI-Spezifikation unter '.$base.'/docs/api/v1', '', '## Portale', @@ -106,6 +108,9 @@ class LlmsTxtController extends Controller '', '- [Veröffentlichen]('.$base.'/de/veroeffentlichen): Wie eine Meldung auf das Portal kommt', '- [Preise]('.$base.'/de/preise)', + '- [Was kostet eine Pressemitteilung?]('.$base.'/de/was-kostet-eine-pressemitteilung): Abrechnungsmodelle, Prüfliste für Folgekosten, Rechenbeispiel', + '- [Pressemitteilung veröffentlichen ohne Abo]('.$base.'/de/pressemitteilung-veroeffentlichen-ohne-abo)', + '- [Pressemitteilung löschen oder ändern lassen]('.$base.'/de/pressemitteilung-loeschen-oder-aendern-lassen): Korrektur, Änderung, DSGVO, Depublikation', '- [Was eine gute Pressemitteilung ausmacht]('.$base.'/de/gute-pressemitteilung)', '- [Hilfe]('.$base.'/de/hilfe)', '- [API]('.$base.'/de/api): Einlieferung per REST-API über den Publisher-Bereich '.$hub, diff --git a/app/Http/Controllers/Web/SitemapController.php b/app/Http/Controllers/Web/SitemapController.php index 95cceed3..996ffd05 100644 --- a/app/Http/Controllers/Web/SitemapController.php +++ b/app/Http/Controllers/Web/SitemapController.php @@ -26,12 +26,14 @@ class SitemapController extends Controller /** @var list */ private const HUB_PAGES = [ 'faq', 'hilfe', 'gute-pressemitteilung', 'api', 'kuendigung', + 'was-kostet-eine-pressemitteilung', 'pressemitteilung-veroeffentlichen-ohne-abo', 'pressemitteilung-loeschen-oder-aendern-lassen', 'impressum', 'datenschutz', 'agb', 'richtlinien', 'melden', ]; /** @var list */ private const PORTAL_PAGES = [ 'preise', 'veroeffentlichen', 'newsrooms', 'faq', 'hilfe', 'gute-pressemitteilung', 'api', + 'was-kostet-eine-pressemitteilung', 'pressemitteilung-veroeffentlichen-ohne-abo', 'pressemitteilung-loeschen-oder-aendern-lassen', 'impressum', 'datenschutz', 'agb', 'richtlinien', 'melden', ]; diff --git a/app/Http/Controllers/Web/TransparencyPageController.php b/app/Http/Controllers/Web/TransparencyPageController.php new file mode 100644 index 00000000..7c8c6b44 --- /dev/null +++ b/app/Http/Controllers/Web/TransparencyPageController.php @@ -0,0 +1,50 @@ +pageData($request, $data)); + } + + public function noSubscription(Request $request, PricingTransparencyData $data): View + { + return view('web.pressemitteilung-veroeffentlichen-ohne-abo', $this->pageData($request, $data)); + } + + public function changeOrDelete(Request $request, PricingTransparencyData $data): View + { + return view('web.pressemitteilung-loeschen-oder-aendern-lassen', $this->pageData($request, $data)); + } + + /** + * Markenname aus dem Host (wie /melden) — der Theme-Provider ist in + * Tests noch nicht auf den Request-Host gelaufen. + * + * @return array + */ + private function pageData(Request $request, PricingTransparencyData $data): array + { + $host = $request->getHost(); + $brandKey = str_contains($host, 'pressekonto') + ? 'pressekonto' + : (str_contains($host, 'presseecho') ? 'presseecho' : 'businessportal24'); + $brand = (array) config('domains.domains.'.$brandKey.'.brand', []); + + return [ + ...$data->build(), + 'brandLabel' => ($brand['name'] ?? '').($brand['accent'] ?? ''), + ]; + } +} diff --git a/app/Services/Web/PricingTransparencyData.php b/app/Services/Web/PricingTransparencyData.php new file mode 100644 index 00000000..c688be6e --- /dev/null +++ b/app/Services/Web/PricingTransparencyData.php @@ -0,0 +1,87 @@ + + */ + public function build(): array + { + $singleCents = (int) config('billing.single_pm_price_cents'); + $starter = Plan::query()->active()->where('slug', Tier::Starter->value)->first(); + $starterMonthlyCents = (int) ($starter->monthly_price_cents ?? 0); + $starterQuota = (int) ($starter->press_release_quota ?? 0); + $vatRate = (float) config('billing.vat_rate'); + + return [ + 'singleCents' => $singleCents, + 'singleNet' => $this->euro($singleCents), + 'singleGross' => $this->euro((int) round($singleCents * (1 + $vatRate))), + 'vatPercent' => (int) round($vatRate * 100), + 'starterName' => $starter->name ?? 'Starter', + 'starterMonthlyNet' => $this->euro($starterMonthlyCents), + 'starterYearlyNet' => $this->euro((int) ($starter->yearly_price_cents ?? 0)), + 'starterQuota' => $starterQuota, + 'extraPmStarterNet' => $this->euro((int) config('credits.extra_pm.'.Tier::Starter->value, 0) * 100), + 'changeNet' => $this->euro((int) config('billing.change_price_cents')), + 'depublicationNet' => $this->euro((int) config('billing.depublication_price_cents')), + 'correctionMaxPercent' => (int) config('legal.correction_max_distance_percent'), + 'examples' => $starterMonthlyCents > 0 && $starterQuota > 0 + ? $this->examples($singleCents, $starterMonthlyCents, $starterQuota) + : [], + ]; + } + + /** + * Rechenbeispiel: Meldungen im Jahr, Verteilung, Kosten je Modell. + * Der Starter-Monat wird nur in Monaten gebucht, in denen veröffentlicht + * wird (monatlich kündbar); je Monat höchstens das Kontingent. + * + * @return list + */ + private function examples(int $singleCents, int $starterMonthlyCents, int $starterQuota): array + { + $cases = [ + ['count' => 1, 'months' => 1, 'spread' => 'one'], + ['count' => $starterQuota, 'months' => 1, 'spread' => 'same_month'], + ['count' => $starterQuota, 'months' => $starterQuota, 'spread' => 'spread'], + ['count' => 12, 'months' => 12, 'spread' => 'monthly'], + ['count' => 12, 'months' => (int) ceil(12 / $starterQuota), 'spread' => 'clustered'], + ]; + + $rows = []; + + foreach ($cases as $case) { + $single = $case['count'] * $singleCents; + $starter = $case['months'] * $starterMonthlyCents; + + $rows[] = [ + 'count' => $case['count'], + 'spread' => $case['spread'], + 'months' => $case['months'], + 'single' => $this->euro($single), + 'starter' => $this->euro($starter), + 'cheaper' => $single === $starter ? 'equal' : ($single < $starter ? 'single' : 'starter'), + ]; + } + + return $rows; + } + + private function euro(int $cents): string + { + return number_format($cents / 100, $cents % 100 === 0 ? 0 : 2, ',', '.'); + } +} diff --git a/dev/2026-09-01/roadmap-2026.md b/dev/2026-09-01/roadmap-2026.md index 0ffca15c..9fbc5e6d 100644 --- a/dev/2026-09-01/roadmap-2026.md +++ b/dev/2026-09-01/roadmap-2026.md @@ -187,7 +187,7 @@ Verbreitert den Trichter oben. Sinnvoll erst, wenn Block 1 unten geschlossen ist | ~~2.1~~ | NewsArticle-Schema auf allen Meldungen, Organization-Schema auf Newsrooms — **Grundgerüst seit Juli (379688f), 17.09.2026 ergänzt:** gemeinsame `@id` zwischen `NewsArticle.author` und Newsroom-Organization, Publisher mit `@id` und Logo (`App\Support\StructuredData`) | M | 1b.1 | | 2.2 | Aufnahme in Google News und Publisher Center prüfen und beantragen — **technischer Teil 17.09.2026:** News-Sitemap `/sitemap-news.xml` je Spoke (48 h, ≤ 1.000), in `robots.txt`; Publisher-Center-Anmeldung liegt beim Betreiber (Protokoll Teil 5) | M | 2.1 | | ~~2.3~~ | Branchen- und Kategorie-Landingpages aus dem Archivbestand — **17.09.2026:** Jahresarchiv je Branche `/category/{slug}/archiv/{jahr}` (34.600 Meldungen intern verlinkt, in der Sitemap), Jahresleiste auf der Branchenseite, Beschreibungen für alle 14 Kategorien (`categories:seed-descriptions`), Portalname im Titel | M | keine | -| 2.4 | Transparenzseiten: Preisvergleich, ohne Abo veröffentlichen, ohne Mindestlaufzeit | S | keine | +| ~~2.4~~ | Transparenzseiten — **17.09.2026 nach Briefing, drei Seiten:** `was-kostet-eine-pressemitteilung` (Modellvergleich ohne Namen, Prüfliste Folgekosten, eigene Zusagen, Rechenbeispiel mit gemischtem Ausgang), `pressemitteilung-veroeffentlichen-ohne-abo`, `pressemitteilung-loeschen-oder-aendern-lassen` (Meldeverfahren + Absatz für fremde Portale); keine fremden Zahlen | S | keine | | 2.5 | Konsolidierung der bestehenden Ratgeberseiten unter `/de/presse/` gegen 1d.1 und 2.4 prüfen | S | 1d.1 | | 2.6 | Outreach an Betreiber von Presseportal-Listen und Vergleichsartikeln | M | 2.4 | | ~~2.7~~ | Auffindbarkeit für KI-Systeme: strukturierte Daten, klare Entitätszuordnung, `llms.txt` — **17.09.2026:** `/llms.txt` je Host (Hub verweist auf beide Portale), Entitätszuordnung über 2.1 | M | 2.1 | diff --git a/dev/2026-09-01/umsetzung-protokoll.md b/dev/2026-09-01/umsetzung-protokoll.md index c63ed364..998e95c5 100644 --- a/dev/2026-09-01/umsetzung-protokoll.md +++ b/dev/2026-09-01/umsetzung-protokoll.md @@ -1660,6 +1660,67 @@ Flag. Bewusst nicht gebaut: Landingpages unterhalb der Kategorien („Branchen" im engeren Sinn) — dafür gibt es keine Unterkategorien und keine Daten; die Meldungen tragen nur die 14 Hauptkategorien. +### 2.4 — Transparenzseiten (17.09.2026, nach Briefing) + +**Zuschnitt aus dem Briefing:** kein Preisvergleich Anbieter gegen +Anbieter, sondern (a) Modellvergleich ohne Namen und fremde Preise und +(b) ein internes Rechenbeispiel mit den eigenen Optionen. Aus dem einen +Roadmap-Punkt wurden drei Seiten mit unterschiedlicher Suchintention, +alle auf Hub und Spokes, DE und EN: + +| URL | Sucht danach | +|---|---| +| `/{edition}/was-kostet-eine-pressemitteilung` | Preis, Vergleich, Budget | +| `/{edition}/pressemitteilung-veroeffentlichen-ohne-abo` | Ablehnung eines Modells | +| `/{edition}/pressemitteilung-loeschen-oder-aendern-lassen` | akutes Problem, oft bei einem anderen Portal | + +**Umsetzung:** `TransparencyPageController` + `PricingTransparencyData` +— alle Zahlen aus `config/billing`, `config/credits`, `config/legal` und +dem Starter-Tarif (19 € Einzel, 29 €/3 Meldungen Starter, 15 € Extra-PM, +10 € Änderung, 35 € Depublikation, USt.), damit die Seiten nie einen +anderen Preis nennen als die Preisübersicht. Texte in +`lang/{de,en}/static.php` (`cost`, `no_subscription`, `change_delete`). +Tabellen-Typografie für `.pm-body` ergänzt (Asset-Build nötig). + +Seite 1, Aufbau wie im Briefing: kurze Antwort im ersten Bildschirm → +vier Abrechnungsmodelle und was sie bei seltener Nutzung kosten → sechs +Prüffragen für Folgekosten → „Was bei uns gilt" (dieselben sechs Fragen +beantwortet plus eine siebte: abgelehnte Meldungen kosten nichts) → +Rechenbeispiel Einzelabrechnung gegen bei Bedarf gebuchte Starter-Monate: +1 Meldung 19 vs. 29 (Einzel), 3 im selben Monat 57 vs. 29 (Starter), +3 verteilt 57 vs. 87 (Einzel), 12 monatlich 228 vs. 348 (Einzel), 12 in +4 Monaten 228 vs. 116 (Starter). Der Test `TransparencyPagesTest` +erzwingt, dass beide Modelle Zeilen gewinnen. + +Seite 2: Ablauf in vier Schritten, was enthalten ist, **was nicht** (kein +Versand an Redaktionen, keine Platzierungsgarantie, keine Zusage über +Rankings oder KI-Nennungen — Formulierungsgrenze 2.7), nichts verfällt, +wann sich ein Tarif trotzdem lohnt. + +Seite 3: fünf Wege als Tabelle (Anliegen / Wer / Kosten / Ergebnis), +Antragsweg über `/melden/pm` mit Bestätigungslink an die hinterlegte +Pressekontaktadresse, „Korrektur statt Löschung", und der Absatz für +fremde Portale: Impressum des Betreibers, DSA-Meldeweg und DSGVO-Anspruch +gelten überall und sind kostenlos, Dienstleister ist Ansprechpartner für +weitere Kopien — ohne Namen, ohne Zusagen über Dritte +(`docs/legal/Entscheidung.md`). + +**Verlinkung:** Preisübersicht (Zeile unter dem USt-Hinweis), Hilfe-Center +(„Korrekturen nach Veröffentlichung" zeigt jetzt auf Seite 3 statt FAQ), +Sitemaps Hub + Spokes, `llms.txt`, untereinander. + +**Bitte gegenlesen — drei Tatsachenbehauptungen, die ich aus Code und +Konzept abgeleitet habe:** (1) „Kein Versand an Redaktionen" (Seite 2), +(2) „Konto anlegen ohne Zahlungsdaten" (Seite 2), (3) „Kunden mit Konto +erledigen kleine Korrekturen direkt im Publisher-Bereich" (Seite 3, +Selbstbedienungskorrektur `legal.corrections_per_week`). Die „sieben +Zusagen" haben keine kanonische Liste in den Konzepten; die Fassung auf +Seite 1 ist aus Preisseite, AGB-Hinweisen und Decision-Update +Preisstruktur zusammengesetzt. + +Tests: `TransparencyPagesTest` (9). Nicht gebaut: fremde Zahlen, auch +nicht als datierte Erhebung (Empfehlung im Briefing: lassen). + ### Befund zu 2.5 / E5 Die alte Ratgeber-Strecke `/de/presse/*.html` leitet seit dem Rückbau @@ -1671,8 +1732,9 @@ Empfehlung: ja für Slugs, die erkennbar das Schreiben betreffen, sonst FAQ. Braucht die Liste der Alt-Slugs aus dem Webserver-Log oder der Search Console; ohne die bleibt es bei FAQ. -**Deploy (2.1/2.2/2.3/2.7):** kein `migrate`, `view:cache`/`route:cache` -wie üblich, dann `categories:seed-descriptions`. Danach +**Deploy (2.1–2.4/2.7):** kein `migrate`, Asset-Build (`npm run build`, +neue Tabellen-Styles), `view:cache`/`route:cache` wie üblich, dann +`categories:seed-descriptions`. Danach `/sitemap-news.xml`, `/llms.txt`, `/de/category/handel/archiv/2019` und eine Meldung im Rich-Results-Test (search.google.com/test/rich-results) prüfen. diff --git a/lang/de/pricing.php b/lang/de/pricing.php index 01e67785..79644b8b 100644 --- a/lang/de/pricing.php +++ b/lang/de/pricing.php @@ -104,6 +104,7 @@ return [ ], 'vat_note' => 'Alle Preise sind Nettopreise und verstehen sich zzgl. der gesetzlichen Umsatzsteuer. Die Abrechnung erfolgt sicher über unseren Zahlungsdienstleister.', + 'transparency_links' => 'Ausführlich: Was kostet eine Pressemitteilung? · Veröffentlichen ohne Abo · Meldung löschen oder ändern lassen', // Render-Pflichten aus der Leistungsbeschreibung v0.2 (Slots, Credits, // Partnerportale) — die AGB verweisen auf diese Seite als Preisquelle. diff --git a/lang/de/static.php b/lang/de/static.php index 80de0498..413f96a9 100644 --- a/lang/de/static.php +++ b/lang/de/static.php @@ -170,6 +170,184 @@ return [ '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.', ], + /* + | Transparenzseiten (Roadmap 2.4): Modellvergleich ohne Anbieternamen und + | fremde Preise; Rechenbeispiel nur mit eigenen Optionen. Zahlen kommen + | aus PricingTransparencyData, nie aus dem Text. + */ + 'cost' => [ + 'meta_title' => 'Was kostet eine Pressemitteilung? Preis, Modelle, Rechenbeispiel – :brand', + 'meta_description' => 'Eine Veröffentlichung kostet :price € netto — ohne Abo, ohne Mindestlaufzeit. Warum Preise am Markt so weit auseinandergehen, woran man Folgekosten erkennt, und ein Rechenbeispiel für 1 bis 12 Meldungen im Jahr.', + 'eyebrow' => 'Preise · Transparenz', + 'breadcrumb' => 'Was kostet eine Pressemitteilung', + 'title' => 'Was kostet eine Pressemitteilung?', + 'lead' => 'Die kurze Antwort, die Abrechnungsmodelle dahinter und ein Rechenbeispiel mit unseren eigenen Optionen — auch dort, wo das günstigere Modell nicht das Abo ist.', + + 'short_heading' => 'Die kurze Antwort', + 'short_p1' => 'Eine Veröffentlichung kostet bei uns :net € netto (:gross € inkl. :vat % USt.) — auf beiden Portalen, presseecho und businessportal24, mit Inhaltsprüfung, eigenem Newsroom und dauerhafter Archivierung. Kein Abo, keine Mindestlaufzeit, kein Einzelkauf, der verfällt.', + 'short_p2' => 'Wer selten veröffentlicht, zahlt pro Meldung. Wer regelmäßig veröffentlicht, bucht einen Monatstarif ab :starter € netto für :quota Meldungen im Monat — monatlich kündbar. Alle Tarife stehen in der Preisübersicht.', + + 'models_heading' => 'Warum die Angaben im Markt so weit auseinandergehen', + 'models_intro' => 'Presseportale rechnen nach vier Grundmodellen ab. Der Preis je Meldung sagt allein wenig; entscheidend ist, was das Modell kostet, wenn man es selten nutzt.', + 'models_col_model' => 'Modell', + 'models_col_cost' => 'Was es kostet, wenn man es selten nutzt', + 'model_1' => 'Monats- oder Jahresabo', + 'model_1_cost' => 'läuft weiter, auch in Monaten ohne Meldung', + 'model_2' => 'Guthabenpakete mit Verfallsdatum', + 'model_2_cost' => 'nicht genutztes Guthaben verfällt am Stichtag', + 'model_3' => 'Einzelabrechnung', + 'model_3_cost' => 'nur die tatsächliche Veröffentlichung', + 'model_4' => 'Kostenlos mit Auflagen', + 'model_4_cost' => 'keine Gebühr, dafür Einschränkungen bei Links, Bildern oder Sichtbarkeit', + 'models_note' => 'Jedes Modell hat seinen Platz: Ein Abo lohnt sich bei planbarem Volumen, die Einzelabrechnung bei unregelmäßigem. Problematisch ist nicht das Modell, sondern wenn es sich erst nach der Anmeldung zeigt.', + + 'check_heading' => 'Woran man Folgekosten erkennt', + 'check_intro' => 'Sechs Fragen, die sich auf jedes Angebot anwenden lassen — auch auf unseres. Die Antworten gehören auf die Preisseite; stehen sie nur in den AGB, ist das bereits eine Antwort.', + 'check_1' => 'Steht der Preis öffentlich, oder erst nach der Anmeldung?', + 'check_2' => 'Verlängert sich der Vertrag automatisch, und mit welcher Frist?', + 'check_3' => 'Verfällt ungenutztes Guthaben?', + 'check_4' => 'Was kostet eine Korrektur nach der Veröffentlichung?', + 'check_5' => 'Was kostet die Löschung?', + 'check_6' => 'Bleibt die Meldung dauerhaft online, oder nur für die Vertragslaufzeit?', + + 'ours_heading' => 'Was bei uns gilt', + 'ours_intro' => 'Dieselben sechs Fragen, beantwortet für presseecho und businessportal24 — und eine siebte, die wir uns selbst stellen.', + 'ours_1_t' => 'Preis öffentlich', + 'ours_1_d' => ':net € netto je Veröffentlichung, Tarife ab :starter € netto im Monat. Alle Preise stehen vor der Anmeldung in der Preisübersicht, netto zzgl. USt.', + 'ours_2_t' => 'Verlängerung', + 'ours_2_d' => 'Eine Einzelveröffentlichung verlängert nichts. Monatstarife verlängern sich monatlich und sind monatlich kündbar, Jahrestarife zum Ende der Laufzeit — auch ohne Anmeldung über die Kündigungsseite.', + 'ours_3_t' => 'Guthaben', + 'ours_3_d' => 'Eine gekaufte Einzelveröffentlichung verfällt nicht; sie wird erst mit der Veröffentlichung verbraucht. Das Monatskontingent eines Tarifs gilt für den jeweiligen Monat. Credits für Zusatzleistungen sind mindestens drei Jahre gültig, mit Erinnerung vor Ablauf.', + 'ours_4_t' => 'Korrektur', + 'ours_4_d' => 'Kleine Korrekturen — Tippfehler, Kontaktdaten, ein neuer Pressekontakt — sind kostenlos. Eine inhaltliche Änderung mit erneuter Prüfung kostet :change € netto.', + 'ours_5_t' => 'Löschung', + 'ours_5_d' => 'Rechtswidrige Inhalte melden und gesetzliche Rechte ausüben ist immer kostenlos. Die freiwillige Entfernung einer rechtmäßigen Meldung aus der öffentlichen Ansicht kostet :depub € netto; die URL bleibt mit einem Hinweis bestehen. Details: Pressemitteilung löschen oder ändern lassen.', + 'ours_6_t' => 'Dauer', + 'ours_6_d' => 'Veröffentlichte Meldungen bleiben online — unabhängig davon, ob ein Tarif noch läuft. Eine Kündigung nimmt nichts offline.', + 'ours_7_t' => 'Abgelehnte Meldungen', + 'ours_7_d' => 'Eine Meldung, die die Inhaltsprüfung nicht besteht, verbraucht weder Kontingent noch Einzelkauf. Bezahlt wird nur, was erscheint.', + + 'example_heading' => 'Rechenbeispiel: unsere eigenen Optionen im Vergleich', + 'example_intro' => 'Einzelabrechnung (:net € je Meldung) gegen den :starter-Tarif (:starter_price € im Monat für bis zu :quota Meldungen), der nur in den Monaten gebucht wird, in denen veröffentlicht wird. Alle Beträge netto.', + 'example_col_count' => 'Meldungen im Jahr', + 'example_col_spread' => 'Verteilung', + 'example_col_single' => 'Einzelabrechnung', + 'example_col_starter' => ':starter-Monate', + 'example_col_cheaper' => 'günstiger ist', + 'spread_one' => 'eine Meldung', + 'spread_same_month' => 'alle im selben Monat', + 'spread_spread' => 'je eine in :months Monaten', + 'spread_monthly' => 'eine je Monat', + 'spread_clustered' => 'je :per in :months Monaten', + 'cheaper_single' => 'Einzelabrechnung', + 'cheaper_starter' => ':starter', + 'cheaper_equal' => 'gleich', + 'example_note' => 'Der Tarif lohnt sich, wenn mehrere Meldungen in denselben Monat fallen. Wer über das Jahr verteilt eine Meldung nach der anderen veröffentlicht, fährt mit der Einzelabrechnung günstiger — das steht hier absichtlich.', + 'example_extra' => 'Über das Kontingent hinaus kostet eine zusätzliche Meldung im :starter-Tarif :extra € netto; größere Tarife senken diesen Preis. Alle Stufen in der Preisübersicht.', + + 'related_heading' => 'Weiterlesen', + 'related_no_sub' => 'Pressemitteilung veröffentlichen ohne Abo', + 'related_change' => 'Pressemitteilung löschen oder ändern lassen', + 'related_pricing' => 'Preisübersicht mit allen Tarifen', + ], + + 'no_subscription' => [ + 'meta_title' => 'Pressemitteilung veröffentlichen ohne Abo – :brand', + 'meta_description' => 'Eine einzelne Pressemitteilung für :price € netto veröffentlichen — ohne Abo, ohne Mindestlaufzeit, ohne Kündigung. Was enthalten ist, was nicht, und wann sich ein Tarif trotzdem lohnt.', + 'eyebrow' => 'Veröffentlichen · Ohne Abo', + 'breadcrumb' => 'Ohne Abo veröffentlichen', + 'title' => 'Pressemitteilung veröffentlichen ohne Abo', + 'lead' => 'Eine Meldung, ein Preis, kein Vertrag. So funktioniert die Einzelveröffentlichung auf presseecho und businessportal24.', + + 'how_heading' => 'So läuft es', + 'step_1' => 'Konto im Publisher-Bereich anlegen — kostenlos, ohne Zahlungsdaten.', + 'step_2' => 'Einzelveröffentlichung für :net € netto kaufen. Einmalige Zahlung, keine Laufzeit.', + 'step_3' => 'Meldung schreiben oder einfügen, Bild und Pressekontakt ergänzen, zur Prüfung freigeben.', + 'step_4' => 'Nach bestandener Inhaltsprüfung erscheint die Meldung auf beiden Portalen — dauerhaft, mit stabiler URL.', + + 'included_heading' => 'Was enthalten ist', + 'included_1' => 'Veröffentlichung auf presseecho und businessportal24, ohne Mehrkosten', + 'included_2' => 'Automatisierte Inhaltsprüfung, bei Bedarf persönliche Nachprüfung', + 'included_3' => 'Eigener Newsroom für Ihr Unternehmen mit allen Meldungen', + 'included_4' => 'Dauerhafte Archivierung mit stabiler URL', + 'included_5' => 'Kleine Korrekturen nach der Veröffentlichung kostenlos', + 'included_6' => 'Strukturierte Daten (NewsArticle), Aufnahme in Sitemap und News-Sitemap', + + 'excluded_heading' => 'Was nicht enthalten ist', + 'excluded_1' => 'Kein Versand an Redaktionen: Die Meldung wird veröffentlicht und auffindbar gemacht, nicht per E-Mail an Journalisten verteilt.', + 'excluded_2' => 'Keine Platzierungsgarantie auf der Startseite — die Hervorhebung ist ein optionaler, separat ausgewiesener Zukauf.', + 'excluded_3' => 'Keine Zusage über Suchmaschinen-Rankings oder Nennungen in KI-Antworten. Wir liefern die technischen Voraussetzungen, nicht das Ergebnis.', + + 'no_expiry_heading' => 'Nichts verfällt, nichts verlängert sich', + 'no_expiry_p' => 'Der Einzelkauf wird erst mit der Veröffentlichung verbraucht. Besteht die Meldung die Inhaltsprüfung nicht, bleibt der Kauf für die überarbeitete Fassung erhalten. Es gibt keine Laufzeit, keine automatische Verlängerung und nichts zu kündigen.', + + 'plan_heading' => 'Wann sich ein Tarif trotzdem lohnt', + 'plan_p' => 'Ab :quota Meldungen im selben Monat ist der :starter-Tarif (:starter_price € netto, monatlich kündbar) günstiger als :quota Einzelkäufe. Wer über das Jahr verteilt veröffentlicht, bleibt mit der Einzelabrechnung günstiger. Das Rechenbeispiel steht unter Was kostet eine Pressemitteilung.', + + 'cta_heading' => 'Einzelveröffentlichung starten', + 'cta_text' => 'Konto anlegen, Einzelveröffentlichung kaufen, Meldung einreichen. Ein Konto für beide Portale.', + 'cta_button' => 'Zum Publisher-Bereich', + 'cta_pricing' => 'Preisübersicht', + ], + + 'change_delete' => [ + 'meta_title' => 'Pressemitteilung löschen oder ändern lassen – :brand', + 'meta_description' => 'Korrektur, inhaltliche Änderung, Entfernung persönlicher Daten oder Depublikation einer veröffentlichten Pressemitteilung: Was kostenlos ist, was :change € oder :depub € kostet — und wie der Weg bei anderen Portalen aussieht.', + 'eyebrow' => 'Veröffentlichte Meldungen', + 'breadcrumb' => 'Löschen oder ändern', + 'title' => 'Pressemitteilung löschen oder ändern lassen', + 'lead' => 'Was möglich ist, was es kostet und wer es beantragen kann — für Meldungen auf presseecho und businessportal24. Und was gilt, wenn die Meldung woanders steht.', + + 'ways_heading' => 'Die fünf Wege', + 'ways_col_concern' => 'Anliegen', + 'ways_col_who' => 'Wer', + 'ways_col_cost' => 'Kosten', + 'ways_col_result' => 'Ergebnis', + 'way_1' => 'Kleine Korrektur — Tippfehler, Kontaktdaten, veralteter Pressekontakt', + 'way_1_who' => 'Auftraggeber oder hinterlegter Pressekontakt', + 'way_1_cost' => 'kostenlos', + 'way_1_result' => 'Text wird korrigiert, URL bleibt', + 'way_2' => 'Inhaltliche Änderung — neuer oder umgeschriebener Text, Bildtausch', + 'way_2_who' => 'Auftraggeber oder Pressekontakt', + 'way_2_cost' => ':change € netto', + 'way_2_result' => 'Neue Fassung nach erneuter Prüfung, URL bleibt', + 'way_3' => 'Eigene personenbezogene Daten entfernen (DSGVO)', + 'way_3_who' => 'die betroffene Person', + 'way_3_cost' => 'kostenlos', + 'way_3_result' => 'Name und Kontaktdaten werden geschwärzt, die Meldung bleibt online', + 'way_4' => 'Meldung offline nehmen (Depublikation)', + 'way_4_who' => 'Auftraggeber, Pressekontakt oder nachweislich Bevollmächtigte', + 'way_4_cost' => ':depub € netto', + 'way_4_result' => 'Meldung aus der öffentlichen Ansicht entfernt, URL bleibt mit Hinweis', + 'way_5' => 'Rechtswidrigen Inhalt melden (DSA)', + 'way_5_who' => 'jede Person', + 'way_5_cost' => 'kostenlos', + 'way_5_result' => 'Prüfung mit begründeter Entscheidung; Entfernung oder Sperrung bei Rechtsverstoß', + 'ways_note' => 'Kostenlose Anliegen setzen wir nach Prüfung direkt um. Bei kostenpflichtigen Anliegen erhalten Sie vor der Umsetzung eine Zahlungsaufforderung — ohne Ihre Bestätigung entstehen keine Kosten.', + + 'how_heading' => 'So beantragen Sie es', + 'how_p1' => 'Öffnen Sie Pressemitteilung melden oder ändern und fügen Sie die URL der Meldung ein. Wir senden einen Bestätigungslink an die in der Meldung hinterlegte Pressekontaktadresse — nicht an eine frei eingegebene Adresse. So kann nur der echte Kontakt Änderungen anstoßen.', + 'how_p2' => 'Ist diese Adresse nicht mehr erreichbar, prüfen wir Ihre Berechtigung anhand eines Nachweises über das Formular für die manuelle Prüfung. Rechtswidrige Inhalte und die Entfernung eigener Daten melden Sie ohne Konto über die Links direkt an der Meldung oder über die Meldeseite.', + 'how_p3' => 'Kunden mit Konto erledigen kleine Korrekturen direkt im Publisher-Bereich an der Meldung.', + + 'why_heading' => 'Warum wir korrigieren statt löschen', + 'why_p1' => 'Eine Pressemitteilung ist ein datiertes Dokument; Verweise, Verlinkungen und Suchergebnisse zeigen auf ihre URL. Deshalb bleibt die URL auch bei einer Depublikation bestehen und trägt dann nur noch einen kurzen Hinweis auf die Entfernung. Eine vollständige Löschung der URL nehmen wir nicht vor.', + 'why_p2' => 'Kopien in Suchmaschinen-Caches oder Archiven Dritter können zeitweise weiter abrufbar sein — darauf haben wir keinen Zugriff. Suchmaschinen bieten für veraltete Inhalte eigene Formulare zur Aktualisierung an.', + + 'other_heading' => 'Die Meldung steht auf einem anderen Portal?', + 'other_p1' => 'Wir können nur ändern, was auf presseecho oder businessportal24 veröffentlicht ist. Für eine Meldung auf einem anderen Portal wenden Sie sich an dessen Betreiber — die Kontaktdaten stehen im Impressum der Seite; viele Portale haben zusätzlich eine eigene Melde- oder Kontaktseite.', + 'other_p2' => 'Zwei Grundlagen gelten überall in der EU: Nach dem Digital Services Act muss jeder Hosting-Anbieter einen Weg anbieten, rechtswidrige Inhalte zu melden, und die Datenschutz-Grundverordnung gibt betroffenen Personen das Recht, die Entfernung eigener personenbezogener Daten zu verlangen. Beides ist kostenlos. Für die freiwillige Entfernung einer rechtmäßigen Meldung gelten die Bedingungen des jeweiligen Portals.', + 'other_p3' => 'Wurde Ihre Meldung über einen Dienstleister auf mehreren Portalen eingestellt, ist dieser Dienstleister für die anderen Kopien der richtige Ansprechpartner.', + + 'cta_heading' => 'Anliegen zu einer Meldung auf presseecho oder businessportal24', + 'cta_button' => 'Pressemitteilung melden oder ändern', + 'cta_all_ways' => 'Alle Wege im Detail', + + 'related_heading' => 'Weiterlesen', + 'related_cost' => 'Was kostet eine Pressemitteilung', + 'related_no_sub' => 'Pressemitteilung veröffentlichen ohne Abo', + ], + 'help' => [ 'meta_title' => 'Hilfe-Center', 'meta_description' => 'Hilfe-Center: Antworten, Anleitungen und direkter Kontakt zum Support-Team.', @@ -197,6 +375,8 @@ return [ 'link_seo' => 'SEO-Optimierung', 'link_corrections' => 'Korrekturen nach Veröffentlichung', 'topic_account' => 'Konto & Abrechnung', + 'link_cost' => 'Was kostet eine Pressemitteilung?', + 'link_no_subscription' => 'Veröffentlichen ohne Abo', 'link_change_plan' => 'Tarif wechseln', 'link_invoices' => 'Rechnungen im Konto abrufen', 'link_cancel' => 'Vertrag kündigen', diff --git a/lang/en/pricing.php b/lang/en/pricing.php index 5ad0f838..26cf749b 100644 --- a/lang/en/pricing.php +++ b/lang/en/pricing.php @@ -97,6 +97,7 @@ return [ ], 'vat_note' => 'All prices are net and exclude statutory VAT. Billing is handled securely via our payment provider.', + 'transparency_links' => 'In detail: What does a press release cost? · Publish without a subscription · Have a release deleted or changed', // Rendering obligations from the service description (slots/credits/ // boost/partner portals) — the T&Cs refer to this page. diff --git a/lang/en/static.php b/lang/en/static.php index 6a443e88..98813db5 100644 --- a/lang/en/static.php +++ b/lang/en/static.php @@ -169,6 +169,183 @@ return [ 'partners_text' => 'This is where we will introduce partners who help with text, images and distribution. Until then this space is deliberately empty.', ], + /* + | Transparency pages (Roadmap 2.4): model comparison without vendor names + | or third-party prices; worked example with our own options only. + */ + 'cost' => [ + 'meta_title' => 'What does a press release cost? Price, models, worked example – :brand', + 'meta_description' => 'One publication costs :price € net — no subscription, no minimum term. Why prices differ so widely, how to spot follow-up costs, and a worked example for 1 to 12 releases a year.', + 'eyebrow' => 'Pricing · Transparency', + 'breadcrumb' => 'What a press release costs', + 'title' => 'What does a press release cost?', + 'lead' => 'The short answer, the billing models behind it, and a worked example with our own options — including the rows where the subscription is not the cheaper choice.', + + 'short_heading' => 'The short answer', + 'short_p1' => 'One publication costs :net € net (:gross € incl. :vat % VAT) — on both portals, presseecho and businessportal24, with content review, your own newsroom and permanent archiving. No subscription, no minimum term, no single purchase that expires.', + 'short_p2' => 'If you publish rarely, you pay per release. If you publish regularly, a monthly plan starts at :starter € net for :quota releases a month — cancellable monthly. All plans are listed on the pricing page.', + + 'models_heading' => 'Why prices in the market differ so widely', + 'models_intro' => 'Press portals bill in four basic models. The price per release alone says little; what matters is what the model costs when you use it rarely.', + 'models_col_model' => 'Model', + 'models_col_cost' => 'What it costs when used rarely', + 'model_1' => 'Monthly or yearly subscription', + 'model_1_cost' => 'keeps running, even in months without a release', + 'model_2' => 'Credit packages with an expiry date', + 'model_2_cost' => 'unused credit expires on the cut-off date', + 'model_3' => 'Pay per release', + 'model_3_cost' => 'only the actual publication', + 'model_4' => 'Free with conditions', + 'model_4_cost' => 'no fee, but restrictions on links, images or visibility', + 'models_note' => 'Each model has its place: a subscription pays off with predictable volume, pay-per-release with irregular volume. The problem is not the model but a model that only shows itself after sign-up.', + + 'check_heading' => 'How to spot follow-up costs', + 'check_intro' => 'Six questions you can apply to any offer — including ours. The answers belong on the pricing page; if they only appear in the terms, that is already an answer.', + 'check_1' => 'Is the price public, or only visible after sign-up?', + 'check_2' => 'Does the contract renew automatically, and with what notice period?', + 'check_3' => 'Does unused credit expire?', + 'check_4' => 'What does a correction after publication cost?', + 'check_5' => 'What does deletion cost?', + 'check_6' => 'Does the release stay online permanently, or only for the contract term?', + + 'ours_heading' => 'What applies here', + 'ours_intro' => 'The same six questions, answered for presseecho and businessportal24 — plus a seventh we ask ourselves.', + 'ours_1_t' => 'Public price', + 'ours_1_d' => ':net € net per publication, plans from :starter € net a month. All prices are on the pricing page before sign-up, net plus VAT.', + 'ours_2_t' => 'Renewal', + 'ours_2_d' => 'A single publication renews nothing. Monthly plans renew monthly and can be cancelled monthly, yearly plans at the end of the term — also without logging in, via the cancellation page.', + 'ours_3_t' => 'Credit', + 'ours_3_d' => 'A purchased single publication does not expire; it is only used up when the release is published. A plan\'s monthly quota applies to that month. Credits for add-ons are valid for at least three years, with a reminder before expiry.', + 'ours_4_t' => 'Corrections', + 'ours_4_d' => 'Small corrections — typos, contact details, a new press contact — are free. A content change with a new review costs :change € net.', + 'ours_5_t' => 'Deletion', + 'ours_5_d' => 'Reporting unlawful content and exercising legal rights is always free. Voluntary removal of a lawful release from public view costs :depub € net; the URL remains with a notice. Details: have a press release deleted or changed.', + 'ours_6_t' => 'Duration', + 'ours_6_d' => 'Published releases stay online — whether or not a plan is still running. Cancelling takes nothing offline.', + 'ours_7_t' => 'Rejected releases', + 'ours_7_d' => 'A release that fails the content review uses neither quota nor a single purchase. You only pay for what appears.', + + 'example_heading' => 'Worked example: our own options compared', + 'example_intro' => 'Pay per release (:net € each) versus the :starter plan (:starter_price € a month for up to :quota releases), booked only in months with a publication. All amounts net.', + 'example_col_count' => 'Releases a year', + 'example_col_spread' => 'Distribution', + 'example_col_single' => 'Pay per release', + 'example_col_starter' => ':starter months', + 'example_col_cheaper' => 'cheaper', + 'spread_one' => 'one release', + 'spread_same_month' => 'all in the same month', + 'spread_spread' => 'one each in :months months', + 'spread_monthly' => 'one a month', + 'spread_clustered' => ':per each in :months months', + 'cheaper_single' => 'pay per release', + 'cheaper_starter' => ':starter', + 'cheaper_equal' => 'equal', + 'example_note' => 'The plan pays off when several releases fall into the same month. If you publish one release after another across the year, pay-per-release is cheaper — that is here on purpose.', + 'example_extra' => 'Beyond the quota, an additional release on the :starter plan costs :extra € net; larger plans lower that price. All tiers on the pricing page.', + + 'related_heading' => 'Read on', + 'related_no_sub' => 'Publish a press release without a subscription', + 'related_change' => 'Have a press release deleted or changed', + 'related_pricing' => 'Pricing page with all plans', + ], + + 'no_subscription' => [ + 'meta_title' => 'Publish a press release without a subscription – :brand', + 'meta_description' => 'Publish a single press release for :price € net — no subscription, no minimum term, nothing to cancel. What is included, what is not, and when a plan still pays off.', + 'eyebrow' => 'Publish · No subscription', + 'breadcrumb' => 'Publish without subscription', + 'title' => 'Publish a press release without a subscription', + 'lead' => 'One release, one price, no contract. How single publication works on presseecho and businessportal24.', + + 'how_heading' => 'How it works', + 'step_1' => 'Create an account in the publisher area — free, no payment details.', + 'step_2' => 'Buy a single publication for :net € net. One-off payment, no term.', + 'step_3' => 'Write or paste the release, add an image and a press contact, submit for review.', + 'step_4' => 'After passing the content review, the release appears on both portals — permanently, with a stable URL.', + + 'included_heading' => 'What is included', + 'included_1' => 'Publication on presseecho and businessportal24 at no extra cost', + 'included_2' => 'Automated content review, with a personal check where needed', + 'included_3' => 'Your own company newsroom with all releases', + 'included_4' => 'Permanent archiving with a stable URL', + 'included_5' => 'Small corrections after publication free of charge', + 'included_6' => 'Structured data (NewsArticle), inclusion in sitemap and news sitemap', + + 'excluded_heading' => 'What is not included', + 'excluded_1' => 'No distribution to editorial desks: the release is published and made findable, not e-mailed to journalists.', + 'excluded_2' => 'No guaranteed homepage placement — highlighting is an optional, separately priced add-on.', + 'excluded_3' => 'No promise about search rankings or mentions in AI answers. We provide the technical prerequisites, not the outcome.', + + 'no_expiry_heading' => 'Nothing expires, nothing renews', + 'no_expiry_p' => 'The single purchase is only used up when the release is published. If the release fails the content review, the purchase remains for the revised version. There is no term, no automatic renewal and nothing to cancel.', + + 'plan_heading' => 'When a plan still pays off', + 'plan_p' => 'From :quota releases in the same month, the :starter plan (:starter_price € net, cancellable monthly) is cheaper than :quota single purchases. If you publish spread across the year, pay-per-release stays cheaper. The worked example is on What does a press release cost.', + + 'cta_heading' => 'Start a single publication', + 'cta_text' => 'Create an account, buy a single publication, submit the release. One account for both portals.', + 'cta_button' => 'To the publisher area', + 'cta_pricing' => 'Pricing page', + ], + + 'change_delete' => [ + 'meta_title' => 'Have a press release deleted or changed – :brand', + 'meta_description' => 'Correction, content change, removal of personal data or depublication of a published press release: what is free, what costs :change € or :depub € — and what to do when the release is on another portal.', + 'eyebrow' => 'Published releases', + 'breadcrumb' => 'Delete or change', + 'title' => 'Have a press release deleted or changed', + 'lead' => 'What is possible, what it costs and who can request it — for releases on presseecho and businessportal24. And what applies when the release is elsewhere.', + + 'ways_heading' => 'The five ways', + 'ways_col_concern' => 'Request', + 'ways_col_who' => 'Who', + 'ways_col_cost' => 'Cost', + 'ways_col_result' => 'Result', + 'way_1' => 'Small correction — typos, contact details, outdated press contact', + 'way_1_who' => 'client or the press contact on file', + 'way_1_cost' => 'free', + 'way_1_result' => 'text corrected, URL unchanged', + 'way_2' => 'Content change — new or rewritten text, image swap', + 'way_2_who' => 'client or press contact', + 'way_2_cost' => ':change € net', + 'way_2_result' => 'new version after a fresh review, URL unchanged', + 'way_3' => 'Remove your own personal data (GDPR)', + 'way_3_who' => 'the person concerned', + 'way_3_cost' => 'free', + 'way_3_result' => 'name and contact details redacted, release stays online', + 'way_4' => 'Take the release offline (depublication)', + 'way_4_who' => 'client, press contact or a person with proof of authority', + 'way_4_cost' => ':depub € net', + 'way_4_result' => 'release removed from public view, URL remains with a notice', + 'way_5' => 'Report unlawful content (DSA)', + 'way_5_who' => 'anyone', + 'way_5_cost' => 'free', + 'way_5_result' => 'review with a reasoned decision; removal or blocking if unlawful', + 'ways_note' => 'Free requests are carried out after review. For paid requests you receive a payment request before anything is done — no costs arise without your confirmation.', + + 'how_heading' => 'How to request it', + 'how_p1' => 'Open report or change a press release and paste the URL of the release. We send a confirmation link to the press contact address stored in the release — not to an address entered freely. That way only the genuine contact can trigger changes.', + 'how_p2' => 'If that address is no longer reachable, we verify your authority with proof via the manual review form. Unlawful content and removal of your own data can be reported without an account via the links on the release itself or the reporting page.', + 'how_p3' => 'Customers with an account make small corrections directly in the publisher area on the release.', + + 'why_heading' => 'Why we correct instead of delete', + 'why_p1' => 'A press release is a dated document; references, links and search results point to its URL. That is why the URL remains even after depublication and then only carries a short notice of removal. We do not delete the URL entirely.', + 'why_p2' => 'Copies in search engine caches or third-party archives may remain available for a while — we have no access to those. Search engines offer their own forms for outdated content.', + + 'other_heading' => 'Is the release on another portal?', + 'other_p1' => 'We can only change what is published on presseecho or businessportal24. For a release on another portal, contact its operator — the contact details are in the site\'s legal notice; many portals also have a dedicated reporting or contact page.', + 'other_p2' => 'Two rules apply throughout the EU: under the Digital Services Act every hosting provider must offer a way to report unlawful content, and the GDPR gives data subjects the right to request removal of their own personal data. Both are free. Voluntary removal of a lawful release is subject to the respective portal\'s terms.', + 'other_p3' => 'If your release was placed on several portals by a service provider, that provider is the right contact for the other copies.', + + 'cta_heading' => 'A request about a release on presseecho or businessportal24', + 'cta_button' => 'Report or change a press release', + 'cta_all_ways' => 'All ways in detail', + + 'related_heading' => 'Read on', + 'related_cost' => 'What does a press release cost', + 'related_no_sub' => 'Publish a press release without a subscription', + ], + 'help' => [ 'meta_title' => 'Help center', 'meta_description' => 'Help center: answers, guides and direct contact with the support team.', @@ -196,6 +373,8 @@ return [ 'link_seo' => 'SEO optimisation', 'link_corrections' => 'Corrections after publication', 'topic_account' => 'Account & billing', + 'link_cost' => 'What does a press release cost?', + 'link_no_subscription' => 'Publish without a subscription', 'link_change_plan' => 'Change plan', 'link_invoices' => 'Access invoices in your account', 'link_cancel' => 'Cancel contract', diff --git a/resources/css/web/shared-styles.css b/resources/css/web/shared-styles.css index d1cc7380..67c10251 100644 --- a/resources/css/web/shared-styles.css +++ b/resources/css/web/shared-styles.css @@ -172,6 +172,34 @@ .pm-body strong { font-weight: 600; } + /* Tabellen der Info-/Transparenzseiten (Roadmap 2.4). */ + .pm-body > table { + width: 100%; + border-collapse: collapse; + margin: 0 0 18px; + font-size: 14.5px; + line-height: 1.5; + color: var(--color-ink); + } + .pm-body > table th, + .pm-body > table td { + padding: 10px 12px 10px 0; + border-bottom: 1px solid var(--color-bg-rule); + text-align: left; + vertical-align: top; + } + .pm-body > table thead th { + font-family: var(--font-mono); + font-size: 10.5px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--color-ink-3); + border-bottom: 1px solid var(--color-bg-rule-strong); + } + .pm-body > table tbody th { + font-weight: 600; + } /* Button Base Styles */ .btn { diff --git a/resources/views/web/hilfe.blade.php b/resources/views/web/hilfe.blade.php index ccbdc7b3..c5f74dfa 100644 --- a/resources/views/web/hilfe.blade.php +++ b/resources/views/web/hilfe.blade.php @@ -30,12 +30,14 @@ ['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')], + ['label' => __('static.help.link_corrections'), 'href' => route('pressemitteilung-loeschen-oder-aendern-lassen')], ], ], [ 'title' => __('static.help.topic_account'), 'links' => [ + ['label' => __('static.help.link_cost'), 'href' => route('was-kostet-eine-pressemitteilung')], + ['label' => __('static.help.link_no_subscription'), 'href' => route('pressemitteilung-veroeffentlichen-ohne-abo')], ['label' => __('static.help.link_change_plan'), 'href' => route('preise')], ['label' => __('static.help.link_invoices'), 'href' => $publisherUrl.'/login'], ['label' => __('static.help.link_cancel'), 'href' => route('kuendigung')], diff --git a/resources/views/web/preise.blade.php b/resources/views/web/preise.blade.php index 1568d12f..5a8a7e28 100644 --- a/resources/views/web/preise.blade.php +++ b/resources/views/web/preise.blade.php @@ -278,6 +278,7 @@ @endif

{{ __('pricing.vat_note') }}

+

{!! __('pricing.transparency_links', ['cost_url' => route('was-kostet-eine-pressemitteilung'), 'no_sub_url' => route('pressemitteilung-veroeffentlichen-ohne-abo'), 'change_url' => route('pressemitteilung-loeschen-oder-aendern-lassen')]) !!}

{{-- Render-Pflichten der Leistungsbeschreibung (Slots/Credits/Boost/ Partnerportale) — die AGB verweisen auf diese Seite. --}} diff --git a/resources/views/web/pressemitteilung-loeschen-oder-aendern-lassen.blade.php b/resources/views/web/pressemitteilung-loeschen-oder-aendern-lassen.blade.php new file mode 100644 index 00000000..d9477415 --- /dev/null +++ b/resources/views/web/pressemitteilung-loeschen-oder-aendern-lassen.blade.php @@ -0,0 +1,79 @@ +@extends('web.layouts.web-master') + +@php + // Roadmap 2.4, Transparenzseite 3 (nicht in der Roadmap, aus dem Briefing + // 17.09.2026): das Meldeverfahren als Antwort auf ein akutes Problem. + // Anfragen zu fremden Portalen fängt der letzte Abschnitt auf — ohne + // Zusagen über Dritte (docs/legal/Entscheidung.md). + $params = [ + 'change' => $changeNet, + 'depub' => $depublicationNet, + 'pm_url' => route('melden.pm'), + 'report_url' => route('melden'), + ]; +@endphp + +@section('title', __('static.change_delete.meta_title', ['brand' => $brandLabel])) +@section('meta_description', __('static.change_delete.meta_description', $params)) + +@section('content') + +
+

{{ __('static.change_delete.ways_heading') }}

+ + + + + + + + + + + @foreach ([1, 2, 3, 4, 5] as $way) + + + + + + + @endforeach + +
{{ __('static.change_delete.ways_col_concern') }}{{ __('static.change_delete.ways_col_who') }}{{ __('static.change_delete.ways_col_cost') }}{{ __('static.change_delete.ways_col_result') }}
{{ __('static.change_delete.way_'.$way) }}{{ __('static.change_delete.way_'.$way.'_who') }}{{ __('static.change_delete.way_'.$way.'_cost', $params) }}{{ __('static.change_delete.way_'.$way.'_result') }}
+

{{ __('static.change_delete.ways_note') }}

+ +

{{ __('static.change_delete.how_heading') }}

+

{!! __('static.change_delete.how_p1', $params) !!}

+

{!! __('static.change_delete.how_p2', $params) !!}

+

{{ __('static.change_delete.how_p3') }}

+ +

{{ __('static.change_delete.why_heading') }}

+

{{ __('static.change_delete.why_p1') }}

+

{{ __('static.change_delete.why_p2') }}

+ +

{{ __('static.change_delete.other_heading') }}

+

{{ __('static.change_delete.other_p1') }}

+

{{ __('static.change_delete.other_p2') }}

+

{{ __('static.change_delete.other_p3') }}

+ + + +

{{ __('static.change_delete.related_heading') }}

+ +
+
+@endsection diff --git a/resources/views/web/pressemitteilung-veroeffentlichen-ohne-abo.blade.php b/resources/views/web/pressemitteilung-veroeffentlichen-ohne-abo.blade.php new file mode 100644 index 00000000..a748ea65 --- /dev/null +++ b/resources/views/web/pressemitteilung-veroeffentlichen-ohne-abo.blade.php @@ -0,0 +1,64 @@ +@extends('web.layouts.web-master') + +@php + // Roadmap 2.4, Transparenzseite 2: Einzelveröffentlichung — was enthalten + // ist, was nicht (ehrlich), wann ein Tarif trotzdem lohnt. + $publisherUrl = rtrim((string) config('domains.domain_portal_url', config('app.url')), '/'); + $params = [ + 'net' => $singleNet, + 'starter' => $starterName, + 'starter_price' => $starterMonthlyNet, + 'quota' => $starterQuota, + 'cost_url' => route('was-kostet-eine-pressemitteilung'), + ]; +@endphp + +@section('title', __('static.no_subscription.meta_title', ['brand' => $brandLabel])) +@section('meta_description', __('static.no_subscription.meta_description', ['price' => $singleNet])) + +@section('content') + +
+

{{ __('static.no_subscription.how_heading') }}

+
    + @foreach ([1, 2, 3, 4] as $step) +
  1. {{ __('static.no_subscription.step_'.$step, $params) }}
  2. + @endforeach +
+ +

{{ __('static.no_subscription.included_heading') }}

+
    + @foreach ([1, 2, 3, 4, 5, 6] as $item) +
  • {{ __('static.no_subscription.included_'.$item) }}
  • + @endforeach +
+ +

{{ __('static.no_subscription.excluded_heading') }}

+
    + @foreach ([1, 2, 3] as $item) +
  • {{ __('static.no_subscription.excluded_'.$item) }}
  • + @endforeach +
+ +

{{ __('static.no_subscription.no_expiry_heading') }}

+

{{ __('static.no_subscription.no_expiry_p') }}

+ +

{{ __('static.no_subscription.plan_heading') }}

+

{!! __('static.no_subscription.plan_p', $params) !!}

+ +
+

{{ __('static.no_subscription.cta_heading') }}

+

{{ __('static.no_subscription.cta_text') }}

+ +
+
+
+@endsection diff --git a/resources/views/web/was-kostet-eine-pressemitteilung.blade.php b/resources/views/web/was-kostet-eine-pressemitteilung.blade.php new file mode 100644 index 00000000..c2786b69 --- /dev/null +++ b/resources/views/web/was-kostet-eine-pressemitteilung.blade.php @@ -0,0 +1,114 @@ +@extends('web.layouts.web-master') + +@php + // Roadmap 2.4, Transparenzseite 1: Modellvergleich ohne Anbieternamen + // und fremde Preise (kein Vergleich im Sinne von § 6 UWG, weil kein + // Wettbewerber erkennbar), Prüfliste, eigene Zusagen, Rechenbeispiel nur + // mit eigenen Optionen. Alle Zahlen aus PricingTransparencyData. + $params = [ + 'net' => $singleNet, + 'gross' => $singleGross, + 'vat' => $vatPercent, + 'starter' => $starterName, + 'starter_price' => $starterMonthlyNet, + 'quota' => $starterQuota, + 'change' => $changeNet, + 'depub' => $depublicationNet, + 'extra' => $extraPmStarterNet, + 'pricing_url' => route('preise'), + 'cancel_url' => route('kuendigung'), + 'change_url' => route('pressemitteilung-loeschen-oder-aendern-lassen'), + ]; + $spreadLabel = fn (array $row): string => __('static.cost.spread_'.$row['spread'], ['months' => $row['months'], 'per' => $starterQuota]); + $cheaperLabel = fn (array $row): string => __('static.cost.cheaper_'.$row['cheaper'], ['starter' => $starterName]); +@endphp + +@section('title', __('static.cost.meta_title', ['brand' => $brandLabel])) +@section('meta_description', __('static.cost.meta_description', ['price' => $singleNet])) + +@section('content') + +
+

{{ __('static.cost.short_heading') }}

+

{!! __('static.cost.short_p1', $params) !!}

+

{!! __('static.cost.short_p2', $params) !!}

+ +

{{ __('static.cost.models_heading') }}

+

{{ __('static.cost.models_intro') }}

+ + + + + + + + + @foreach ([1, 2, 3, 4] as $model) + + + + + @endforeach + +
{{ __('static.cost.models_col_model') }}{{ __('static.cost.models_col_cost') }}
{{ __('static.cost.model_'.$model) }}{{ __('static.cost.model_'.$model.'_cost') }}
+

{{ __('static.cost.models_note') }}

+ +

{{ __('static.cost.check_heading') }}

+

{{ __('static.cost.check_intro') }}

+
    + @foreach ([1, 2, 3, 4, 5, 6] as $question) +
  1. {{ __('static.cost.check_'.$question) }}
  2. + @endforeach +
+ +

{{ __('static.cost.ours_heading') }}

+

{{ __('static.cost.ours_intro') }}

+
    + @foreach ([1, 2, 3, 4, 5, 6, 7] as $promise) +
  1. {{ __('static.cost.ours_'.$promise.'_t') }} — {!! __('static.cost.ours_'.$promise.'_d', $params) !!}
  2. + @endforeach +
+ + @if ($examples !== []) +

{{ __('static.cost.example_heading') }}

+

{{ __('static.cost.example_intro', $params) }}

+ + + + + + + + + + + + @foreach ($examples as $row) + + + + + + + + @endforeach + +
{{ __('static.cost.example_col_count') }}{{ __('static.cost.example_col_spread') }}{{ __('static.cost.example_col_single') }}{{ __('static.cost.example_col_starter', ['starter' => $starterName]) }}{{ __('static.cost.example_col_cheaper') }}
{{ $row['count'] }}{{ $spreadLabel($row) }}{{ $row['single'] }} €{{ $row['starter'] }} €{{ $cheaperLabel($row) }}
+

{{ __('static.cost.example_note') }}

+

{!! __('static.cost.example_extra', $params) !!}

+ @endif + +

{{ __('static.cost.related_heading') }}

+ +
+
+@endsection diff --git a/routes/web.php b/routes/web.php index 2a79c97e..f65eaddf 100644 --- a/routes/web.php +++ b/routes/web.php @@ -22,6 +22,7 @@ use App\Http\Controllers\Web\ReportFormController; use App\Http\Controllers\Web\SitemapController; use App\Http\Controllers\Web\SucheController; use App\Http\Controllers\Web\TerminationController; +use App\Http\Controllers\Web\TransparencyPageController; use App\Http\Controllers\Web\VeroeffentlichenController; use App\Http\Middleware\SetEdition; use App\Models\Category; @@ -161,6 +162,7 @@ $htmlRedirect = static function (string $edition, string $path) { 'preise', 'faq', 'veroeffentlichen', 'suche', 'newsrooms', 'api', 'hilfe', 'gute-pressemitteilung', 'impressum', 'datenschutz', 'agb', + 'was-kostet-eine-pressemitteilung', 'pressemitteilung-veroeffentlichen-ohne-abo', 'pressemitteilung-loeschen-oder-aendern-lassen', ]; if (isset($staticAliases[$clean])) { @@ -262,6 +264,11 @@ Route::prefix('{edition}')->whereIn('edition', SetEdition::EDITIONS)->group(func // 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'); + + // Transparenzseiten (Roadmap 2.4): Preis, ohne Abo, löschen/ändern. + Route::get('was-kostet-eine-pressemitteilung', [TransparencyPageController::class, 'cost'])->name('was-kostet-eine-pressemitteilung'); + Route::get('pressemitteilung-veroeffentlichen-ohne-abo', [TransparencyPageController::class, 'noSubscription'])->name('pressemitteilung-veroeffentlichen-ohne-abo'); + Route::get('pressemitteilung-loeschen-oder-aendern-lassen', [TransparencyPageController::class, 'changeOrDelete'])->name('pressemitteilung-loeschen-oder-aendern-lassen'); Route::view('impressum', 'web.impressum')->name('impressum'); // Gesetzlicher Kündigungsbutton (§ 312k BGB): öffentliche Bestätigungsseite diff --git a/tests/Feature/Web/TransparencyPagesTest.php b/tests/Feature/Web/TransparencyPagesTest.php new file mode 100644 index 00000000..b4fa8ba6 --- /dev/null +++ b/tests/Feature/Web/TransparencyPagesTest.php @@ -0,0 +1,87 @@ +set('billing.single_pm_price_cents', 1900); + config()->set('billing.change_price_cents', 1000); + config()->set('billing.depublication_price_cents', 3500); + config()->set('billing.vat_rate', 0.19); + config()->set('credits.extra_pm.starter', 15); + + Plan::factory()->create(['slug' => 'starter', 'name' => 'Starter', 'monthly_price_cents' => 2900, 'yearly_price_cents' => 29000, 'press_release_quota' => 3, 'is_active' => true]); +}); + +test('the worked example lets pay-per-release win in some rows and the plan in others', function () { + $data = app(PricingTransparencyData::class)->build(); + + expect($data['singleNet'])->toBe('19') + ->and($data['singleGross'])->toBe('22,61') + ->and($data['starterMonthlyNet'])->toBe('29') + ->and(collect($data['examples'])->pluck('cheaper')->unique()->sort()->values()->all())->toBe(['single', 'starter']); +}); + +test('the cost page shows the short answer with the configured price and no competitor names', function () { + /** @var TestCase $this */ + $response = $this->get('https://presseecho.test/de/was-kostet-eine-pressemitteilung')->assertSuccessful(); + + $response + ->assertSee('Was kostet eine Pressemitteilung?') + ->assertSee('19 € netto (22,61 € inkl. 19 % USt.)', false) + ->assertSee('Woran man Folgekosten erkennt') + ->assertSee('Rechenbeispiel') + ->assertSee('228 €', false) + ->assertSee('10 € netto') + ->assertSee('35 € netto') + ->assertSee('/de/pressemitteilung-loeschen-oder-aendern-lassen', false) + ->assertSee('Was kostet eine Pressemitteilung? Preis, Modelle, Rechenbeispiel – presseecho', false); + + // Kein Anbietername, keine fremden Preise (Briefing 17.09.2026). + $this->assertStringNotContainsStringIgnoringCase('presseportal.de', (string) $response->getContent()); + $this->assertStringNotContainsStringIgnoringCase('openpr', (string) $response->getContent()); +}); + +test('the no-subscription page and the change page render on every host in both editions', function (string $url) { + /** @var TestCase $this */ + $this->get($url)->assertSuccessful(); +})->with([ + 'https://businessportal24.test/de/pressemitteilung-veroeffentlichen-ohne-abo', + 'https://businessportal24.test/en/pressemitteilung-veroeffentlichen-ohne-abo', + 'https://pressekonto.test/de/pressemitteilung-loeschen-oder-aendern-lassen', + 'https://presseecho.test/en/pressemitteilung-loeschen-oder-aendern-lassen', + 'https://pressekonto.test/en/was-kostet-eine-pressemitteilung', +]); + +test('the change page names the five ways with their prices and points to the self-service entry', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/de/pressemitteilung-loeschen-oder-aendern-lassen') + ->assertSuccessful() + ->assertSee('Die fünf Wege') + ->assertSee('kostenlos') + ->assertSee('10 € netto') + ->assertSee('35 € netto') + ->assertSee('/de/melden/pm', false) + ->assertSee('Die Meldung steht auf einem anderen Portal?'); +}); + +test('the pages are linked from the pricing page, the help centre and the sitemap', function () { + /** @var TestCase $this */ + $this->get('https://presseecho.test/de/preise')->assertSuccessful() + ->assertSee('/de/was-kostet-eine-pressemitteilung', false); + + $this->get('https://presseecho.test/de/hilfe')->assertSuccessful() + ->assertSee('/de/pressemitteilung-loeschen-oder-aendern-lassen', false) + ->assertSee('/de/pressemitteilung-veroeffentlichen-ohne-abo', false); + + $this->get('https://presseecho.test/sitemap.xml')->assertSuccessful() + ->assertSee('https://presseecho.test/de/was-kostet-eine-pressemitteilung', false); + + $this->get('https://pressekonto.test/sitemap.xml')->assertSuccessful() + ->assertSee('https://pressekonto.test/de/pressemitteilung-loeschen-oder-aendern-lassen', false); +});