Skip to content

Commit 5eb2a05

Browse files
authored
Merge pull request #74 from codebar-ag/feature-updates
Feature updates
2 parents 7b04578 + 112b6cb commit 5eb2a05

43 files changed

Lines changed: 1108 additions & 164 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/Actions/ViewDataAction.php

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,31 +81,16 @@ public function contacts(string $locale): object
8181
->orderBy('name')
8282
->get();
8383

84-
$employeeIds = $publishedContacts
85-
->filter(fn (Contact $contact): bool => array_key_exists(
86-
ContactSectionEnum::EMPLOYEES,
87-
$contact->sections ?? []
88-
))
89-
->pluck('id');
90-
9184
return (object) collect([
9285
ContactSectionEnum::EMPLOYEES,
9386
ContactSectionEnum::COLLABORATIONS,
9487
ContactSectionEnum::BOARD_MEMBERS,
95-
])->mapWithKeys(function (string $section) use ($publishedContacts, $locale, $employeeIds): array {
88+
])->mapWithKeys(function (string $section) use ($publishedContacts, $locale): array {
9689
$contacts = $publishedContacts
97-
->filter(function (Contact $contact) use ($section, $employeeIds): bool {
90+
->filter(function (Contact $contact) use ($section): bool {
9891
$sections = $contact->sections ?? [];
9992

100-
if (! array_key_exists($section, $sections)) {
101-
return false;
102-
}
103-
104-
if ($section === ContactSectionEnum::BOARD_MEMBERS && $employeeIds->contains($contact->id)) {
105-
return false;
106-
}
107-
108-
return true;
93+
return array_key_exists($section, $sections);
10994
})
11095
->map(fn (Contact $contact) => ContactDTO::fromModel($contact, $section, $locale));
11196

app/Http/Controllers/Legal/PrivacyIndexController.php

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,14 @@
44

55
use App\Actions\PageAction;
66
use App\Http\Controllers\Controller;
7-
use Illuminate\Http\RedirectResponse;
8-
use Illuminate\Support\Str;
97
use Illuminate\View\View;
108

119
class PrivacyIndexController extends Controller
1210
{
13-
/**
14-
* Display the user's profile form.
15-
*/
16-
public function __invoke(): View|RedirectResponse
11+
public function __invoke(): View
1712
{
18-
return redirect()->route(Str::slug(app()->getLocale()).'.start.index');
19-
20-
// @todo Notification
21-
/* return view('app.legal.privacy.index')->with([
22-
'page' => (new PageAction(locale: null, routeName: 'legal.privacy.index'))->default(),
23-
]);*/
13+
return view('app.legal.privacy.index')->with([
14+
'page' => (new PageAction(locale: null, routeName: 'legal.privacy.index'))->default(),
15+
]);
2416
}
2517
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace App\Http\Controllers\Robots;
4+
5+
use App\Http\Controllers\Controller;
6+
use Illuminate\Http\Response;
7+
8+
class RobotsController extends Controller
9+
{
10+
public function __invoke(): Response
11+
{
12+
$sitemapUrl = rtrim(config('app.url'), '/').'/sitemap.xml';
13+
14+
$content = implode("\n", [
15+
'User-agent: *',
16+
'Disallow:',
17+
'',
18+
'Sitemap: '.$sitemapUrl,
19+
]);
20+
21+
return response(content: $content, headers: [
22+
'Content-Type' => 'text/plain',
23+
]);
24+
}
25+
}

app/Http/Controllers/Sitemap/SitemapController.php

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ class SitemapController extends Controller
2121
protected const array DEFAULT_ROUTES = [
2222
'start.index',
2323
'about-us.index',
24+
'news.index',
2425
'products.index',
2526
'services.index',
2627
'contact.index',
27-
'legal.terms.index',
28+
'media.index',
2829
'legal.imprint.index',
2930
'legal.privacy.index',
30-
'jobs.index',
3131
];
3232

3333
protected const array DEFAULT_LOCALES = [
@@ -37,12 +37,16 @@ class SitemapController extends Controller
3737

3838
public function __invoke(): Response
3939
{
40-
$content = Cache::rememberForever(key: 'sitemap_xml', callback: function (): string {
41-
$this->sitemap = new SitemapBuilder;
42-
$this->builder();
40+
$content = Cache::remember(
41+
key: 'sitemap_xml',
42+
ttl: now()->addHours(24),
43+
callback: function (): string {
44+
$this->sitemap = new SitemapBuilder;
45+
$this->builder();
4346

44-
return $this->sitemap->toXml();
45-
});
47+
return $this->sitemap->toXml();
48+
}
49+
);
4650

4751
return response(content: $content)
4852
->header('Content-Type', 'application/xml')
@@ -55,6 +59,7 @@ private function builder(): void
5559

5660
// Use chunked queries to prevent memory issues
5761
News::whereNotNull('published_at')
62+
->where('published_at', '<=', now())
5863
->with('references')
5964
->chunk(100, function (Collection $news): void {
6065
/** @var News $item */

app/Models/Contact.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
namespace App\Models;
44

5+
use App\Enums\LocaleEnum;
56
use Illuminate\Database\Eloquent\Factories\HasFactory;
67
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Support\Facades\Cache;
9+
use Illuminate\Support\Str;
710

811
class Contact extends Model
912
{
@@ -14,4 +17,17 @@ class Contact extends Model
1417
'sections' => 'json',
1518
'icons' => 'json',
1619
];
20+
21+
protected static function booted(): void
22+
{
23+
static::saved(fn () => self::clearPublishedCache());
24+
static::deleted(fn () => self::clearPublishedCache());
25+
}
26+
27+
public static function clearPublishedCache(): void
28+
{
29+
foreach (LocaleEnum::cases() as $locale) {
30+
Cache::forget(Str::slug("contacts_published_{$locale->value}"));
31+
}
32+
}
1733
}

app/Sitemap/SitemapBuilder.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ public function addItem(PageDTO $page): void
2626
$url->setPriority(priority: 1.0);
2727
$url->setChangeFrequency(changeFrequency: Url::CHANGE_FREQUENCY_WEEKLY);
2828
$url->setLastModificationDate(lastModificationDate: $this->lastModificationDate);
29-
$url->addImage(url: $page->image, caption: $page->title);
29+
30+
if (filled($page->image)) {
31+
$url->addImage(url: $page->image, caption: $page->title);
32+
}
3033

3134
if (! empty($page->referencePages) && $page->referencePages->count()) {
3235
$page->referencePages->each(function (PageDTO $page) use ($url): void {

config/csp.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
/*
4444
* Headers will only be added if this setting is set to true.
4545
*/
46-
'enabled' => env('CSP_ENABLED', false),
46+
'enabled' => env('CSP_ENABLED', true),
4747

4848
/*
4949
* The class responsible for generating the nonces used in inline tags and headers.

config/seo.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
return [
4-
'default_image' => 'images/seo/og-codebar.webp',
4+
'default_image' => 'images/seo/og-codebar.png',
55
'image_width' => 1200,
66
'image_height' => 630,
77
];

database/seeders/Codebar/PagesTableSeeder.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,18 @@ private function enCH()
9393
]
9494
);
9595

96+
Page::updateOrCreate(
97+
[
98+
'key' => 'legal.privacy.index',
99+
'locale' => $locale,
100+
],
101+
[
102+
'robots' => 'index,follow',
103+
'title' => 'Privacy Policy – codebar',
104+
'description' => 'How codebar Solutions AG processes personal data on this website under Swiss data protection law.',
105+
]
106+
);
107+
96108
Page::updateOrCreate(
97109
[
98110
'key' => 'contact.index',
@@ -194,6 +206,18 @@ private function deCH()
194206
]
195207
);
196208

209+
Page::updateOrCreate(
210+
[
211+
'key' => 'legal.privacy.index',
212+
'locale' => $locale,
213+
],
214+
[
215+
'robots' => 'index,follow',
216+
'title' => 'Datenschutzerklärung – codebar',
217+
'description' => 'Wie die codebar Solutions AG Personendaten auf dieser Website gemäss Schweizer Datenschutzrecht bearbeitet.',
218+
]
219+
);
220+
197221
Page::updateOrCreate(
198222
[
199223
'key' => 'contact.index',

lang/de_CH.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,21 @@
2121
"Headquarter": "Hauptsitz",
2222
"Home": "Start",
2323
"Imprint": "Impressum",
24+
"Imprint disclaimer": "Die Inhalte dieser Website wurden mit grösster Sorgfalt zusammengestellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen. Als Diensteanbieter sind wir für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Wir sind nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.",
25+
"Imprint representatives placeholder": "Angaben zu zeichnungsberechtigten Personen werden in Kürze veröffentlicht.",
26+
"Imprint individual signature": "Einzelunterschrift",
27+
"Imprint Sebastian Burgin residence": "von Arlesheim, in Oberwil (BL)",
28+
"Imprint Sebastian Burgin role": "Präsident des Verwaltungsrates, Geschäftsführer",
29+
"Imprint Melanie Burgin residence": "von Rothenfluh, in Oberwil (BL)",
30+
"Imprint Melanie Burgin role": "Mitglied des Verwaltungsrates",
2431
"Info(at)paperflakes.ch": "Info(at)paperflakes.ch",
32+
"info(at)codebar.ch": "info(at)codebar.ch",
2533
"Jobs": "Stellen",
2634
"Language": "Sprache",
2735
"Last updated at": "Zuletzt aktualisiert am",
2836
"Last updated at: :date": "Zuletzt aktualisiert am: :date",
2937
"Legal": "Rechtliches",
38+
"Legal form AG": "Aktiengesellschaft (AG)",
3039
"Locations": "Standorte",
3140
"Media": "Medien",
3241
"Media intro": "Offizielle codebar-Logos für Presse und Partner.",
@@ -44,6 +53,41 @@
4453
"paperflakes AG": "paperflakes AG",
4554
"Phone": "Telefon",
4655
"Privacy": "Datenschutz",
56+
"Privacy changes body": "Wir können diese Datenschutzerklärung von Zeit zu Zeit anpassen. Die aktuelle Version ist stets auf dieser Seite mit dem Datum der letzten Aktualisierung verfügbar.",
57+
"Privacy changes heading": "Änderungen",
58+
"Privacy contact body": "Bei Fragen zum Datenschutz oder zur Ausübung Ihrer Rechte:",
59+
"Privacy contact body paperflakes": "Bei Fragen zum Datenschutz oder zur Ausübung Ihrer Rechte:",
60+
"Privacy contact heading": "Kontakt",
61+
"Privacy controller body": "Verantwortlich für die Datenbearbeitung auf dieser Website ist die codebar Solutions AG.",
62+
"Privacy controller body paperflakes": "Verantwortlich für die Datenbearbeitung auf dieser Website ist die paperflakes AG.",
63+
"Privacy controller heading": "Verantwortliche Stelle",
64+
"Privacy data collected analytics": "Nutzungsstatistiken über Fathom Analytics (cookieless, anonymisierte Seitenaufrufe; keine Personenprofile)",
65+
"Privacy data collected errors": "Fehler- und Leistungsdaten an Flare zur Fehlerbehebung (serverseitig; sensible Header werden zensiert)",
66+
"Privacy data collected heading": "Welche Daten wir erheben",
67+
"Privacy data collected intro": "Beim Besuch dieser Website verarbeiten wir folgende Datenkategorien:",
68+
"Privacy data collected logs": "Server-Logdaten (IP-Adresse, Datum und Uhrzeit des Zugriffs, Browsertyp, besuchte Seiten)",
69+
"Privacy data collected session": "Technisch notwendige Session- und CSRF-Cookies für den sicheren Betrieb der Website",
70+
"Privacy processors body": "Wir setzen folgende Dienstleister ein, die Personendaten in unserem Auftrag verarbeiten können:",
71+
"Privacy processors cloudinary": "Cloudinary – Auslieferung von Bildern und Medien (Datenschutz: cloudinary.com/privacy)",
72+
"Privacy processors fathom": "Fathom Analytics – datenschutzfreundliche Website-Statistiken (usefathom.com/privacy)",
73+
"Privacy processors flare": "Flare – Fehlerüberwachung unserer Anwendung (flareapp.io/privacy)",
74+
"Privacy processors heading": "Auftragsbearbeiter",
75+
"Privacy processors hosting": "Laravel Cloud – Website-Hosting und Infrastruktur (laravel.com)",
76+
"Privacy purpose body": "Wir verarbeiten diese Daten, um die Website zu betreiben und abzusichern, aggregierte Nutzungsmuster zu verstehen und technische Probleme zu beheben. Rechtsgrundlage ist unser berechtigtes Interesse an einem sicheren und funktionsfähigen Webangebot sowie gegebenenfalls die Vertragserfüllung.",
77+
"Privacy purpose heading": "Zweck und Rechtsgrundlage",
78+
"Privacy retention analytics": "Analysedaten werden gemäss Richtlinie von Fathom aufbewahrt.",
79+
"Privacy retention errors": "Fehlermeldungen werden so lange aufbewahrt, wie zur Behebung erforderlich.",
80+
"Privacy retention heading": "Aufbewahrungsdauer",
81+
"Privacy retention logs": "Server-Logs werden bis zu 90 Tage aufbewahrt.",
82+
"Privacy retention session": "Session-Daten werden beim Schliessen des Browsers gelöscht.",
83+
"Privacy rights body": "Nach dem Schweizer Datenschutzgesetz (nDSG) haben Sie das Recht auf Auskunft, Berichtigung unrichtiger Daten, Löschung soweit anwendbar sowie Widerspruch gegen die Bearbeitung. Wenden Sie sich dazu über unsere Kontaktseite an uns.",
84+
"Privacy rights heading": "Ihre Rechte",
85+
"Privacy scope body": "Diese Datenschutzerklärung gilt für die codebar-Website unter codebar.ch und deren Unterseiten. Sie gilt nicht für verlinkte Websites Dritter.",
86+
"Privacy scope heading": "Geltungsbereich",
87+
"Privacy security body": "Wir schützen Ihre Daten durch HTTPS-Verschlüsselung und branchenübliche Sicherheitsmassnahmen auf unserer Hosting-Infrastruktur.",
88+
"Privacy security heading": "Datensicherheit",
89+
"Authorized representatives": "Zeichnungsberechtigte Personen",
90+
"Disclaimer": "Haftungsausschluss",
4791
"Products": "Produkte",
4892
"Published at": "Veröffentlicht am",
4993
"Published at: :date": "Veröffentlicht am: :date",

0 commit comments

Comments
 (0)