Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 3 additions & 18 deletions app/Actions/ViewDataAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,31 +81,16 @@ public function contacts(string $locale): object
->orderBy('name')
->get();

$employeeIds = $publishedContacts
->filter(fn (Contact $contact): bool => array_key_exists(
ContactSectionEnum::EMPLOYEES,
$contact->sections ?? []
))
->pluck('id');

return (object) collect([
ContactSectionEnum::EMPLOYEES,
ContactSectionEnum::COLLABORATIONS,
ContactSectionEnum::BOARD_MEMBERS,
])->mapWithKeys(function (string $section) use ($publishedContacts, $locale, $employeeIds): array {
])->mapWithKeys(function (string $section) use ($publishedContacts, $locale): array {
$contacts = $publishedContacts
->filter(function (Contact $contact) use ($section, $employeeIds): bool {
->filter(function (Contact $contact) use ($section): bool {
$sections = $contact->sections ?? [];

if (! array_key_exists($section, $sections)) {
return false;
}

if ($section === ContactSectionEnum::BOARD_MEMBERS && $employeeIds->contains($contact->id)) {
return false;
}

return true;
return array_key_exists($section, $sections);
})
->map(fn (Contact $contact) => ContactDTO::fromModel($contact, $section, $locale));

Expand Down
16 changes: 4 additions & 12 deletions app/Http/Controllers/Legal/PrivacyIndexController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,14 @@

use App\Actions\PageAction;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Str;
use Illuminate\View\View;

class PrivacyIndexController extends Controller
{
/**
* Display the user's profile form.
*/
public function __invoke(): View|RedirectResponse
public function __invoke(): View
{
return redirect()->route(Str::slug(app()->getLocale()).'.start.index');

// @todo Notification
/* return view('app.legal.privacy.index')->with([
'page' => (new PageAction(locale: null, routeName: 'legal.privacy.index'))->default(),
]);*/
return view('app.legal.privacy.index')->with([
'page' => (new PageAction(locale: null, routeName: 'legal.privacy.index'))->default(),
]);
}
}
25 changes: 25 additions & 0 deletions app/Http/Controllers/Robots/RobotsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace App\Http\Controllers\Robots;

use App\Http\Controllers\Controller;
use Illuminate\Http\Response;

class RobotsController extends Controller
{
public function __invoke(): Response
{
$sitemapUrl = rtrim(config('app.url'), '/').'/sitemap.xml';

$content = implode("\n", [
'User-agent: *',
'Disallow:',
'',
'Sitemap: '.$sitemapUrl,
]);

return response(content: $content, headers: [
'Content-Type' => 'text/plain',
]);
}
}
19 changes: 12 additions & 7 deletions app/Http/Controllers/Sitemap/SitemapController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ class SitemapController extends Controller
protected const array DEFAULT_ROUTES = [
'start.index',
'about-us.index',
'news.index',
'products.index',
'services.index',
'contact.index',
'legal.terms.index',
'media.index',
'legal.imprint.index',
'legal.privacy.index',
'jobs.index',
];

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

public function __invoke(): Response
{
$content = Cache::rememberForever(key: 'sitemap_xml', callback: function (): string {
$this->sitemap = new SitemapBuilder;
$this->builder();
$content = Cache::remember(
key: 'sitemap_xml',
ttl: now()->addHours(24),
callback: function (): string {
$this->sitemap = new SitemapBuilder;
$this->builder();

return $this->sitemap->toXml();
});
return $this->sitemap->toXml();
}
);

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

// Use chunked queries to prevent memory issues
News::whereNotNull('published_at')
->where('published_at', '<=', now())
->with('references')
->chunk(100, function (Collection $news): void {
/** @var News $item */
Expand Down
16 changes: 16 additions & 0 deletions app/Models/Contact.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

namespace App\Models;

use App\Enums\LocaleEnum;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

class Contact extends Model
{
Expand All @@ -14,4 +17,17 @@ class Contact extends Model
'sections' => 'json',
'icons' => 'json',
];

protected static function booted(): void
{
static::saved(fn () => self::clearPublishedCache());
static::deleted(fn () => self::clearPublishedCache());
}

public static function clearPublishedCache(): void
{
foreach (LocaleEnum::cases() as $locale) {
Cache::forget(Str::slug("contacts_published_{$locale->value}"));
}
}
}
5 changes: 4 additions & 1 deletion app/Sitemap/SitemapBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ public function addItem(PageDTO $page): void
$url->setPriority(priority: 1.0);
$url->setChangeFrequency(changeFrequency: Url::CHANGE_FREQUENCY_WEEKLY);
$url->setLastModificationDate(lastModificationDate: $this->lastModificationDate);
$url->addImage(url: $page->image, caption: $page->title);

if (filled($page->image)) {
$url->addImage(url: $page->image, caption: $page->title);
}

if (! empty($page->referencePages) && $page->referencePages->count()) {
$page->referencePages->each(function (PageDTO $page) use ($url): void {
Expand Down
2 changes: 1 addition & 1 deletion config/csp.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
/*
* Headers will only be added if this setting is set to true.
*/
'enabled' => env('CSP_ENABLED', false),
'enabled' => env('CSP_ENABLED', true),

/*
* The class responsible for generating the nonces used in inline tags and headers.
Expand Down
2 changes: 1 addition & 1 deletion config/seo.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

return [
'default_image' => 'images/seo/og-codebar.webp',
'default_image' => 'images/seo/og-codebar.png',
'image_width' => 1200,
'image_height' => 630,
];
24 changes: 24 additions & 0 deletions database/seeders/Codebar/PagesTableSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ private function enCH()
]
);

Page::updateOrCreate(
[
'key' => 'legal.privacy.index',
'locale' => $locale,
],
[
'robots' => 'index,follow',
'title' => 'Privacy Policy – codebar',
'description' => 'How codebar Solutions AG processes personal data on this website under Swiss data protection law.',
]
);

Page::updateOrCreate(
[
'key' => 'contact.index',
Expand Down Expand Up @@ -194,6 +206,18 @@ private function deCH()
]
);

Page::updateOrCreate(
[
'key' => 'legal.privacy.index',
'locale' => $locale,
],
[
'robots' => 'index,follow',
'title' => 'Datenschutzerklärung – codebar',
'description' => 'Wie die codebar Solutions AG Personendaten auf dieser Website gemäss Schweizer Datenschutzrecht bearbeitet.',
]
);

Page::updateOrCreate(
[
'key' => 'contact.index',
Expand Down
44 changes: 44 additions & 0 deletions lang/de_CH.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,21 @@
"Headquarter": "Hauptsitz",
"Home": "Start",
"Imprint": "Impressum",
"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.",
"Imprint representatives placeholder": "Angaben zu zeichnungsberechtigten Personen werden in Kürze veröffentlicht.",
"Imprint individual signature": "Einzelunterschrift",
"Imprint Sebastian Burgin residence": "von Arlesheim, in Oberwil (BL)",
"Imprint Sebastian Burgin role": "Präsident des Verwaltungsrates, Geschäftsführer",
"Imprint Melanie Burgin residence": "von Rothenfluh, in Oberwil (BL)",
"Imprint Melanie Burgin role": "Mitglied des Verwaltungsrates",
"Info(at)paperflakes.ch": "Info(at)paperflakes.ch",
"info(at)codebar.ch": "info(at)codebar.ch",
"Jobs": "Stellen",
"Language": "Sprache",
"Last updated at": "Zuletzt aktualisiert am",
"Last updated at: :date": "Zuletzt aktualisiert am: :date",
"Legal": "Rechtliches",
"Legal form AG": "Aktiengesellschaft (AG)",
"Locations": "Standorte",
"Media": "Medien",
"Media intro": "Offizielle codebar-Logos für Presse und Partner.",
Expand All @@ -44,6 +53,41 @@
"paperflakes AG": "paperflakes AG",
"Phone": "Telefon",
"Privacy": "Datenschutz",
"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.",
"Privacy changes heading": "Änderungen",
"Privacy contact body": "Bei Fragen zum Datenschutz oder zur Ausübung Ihrer Rechte:",
"Privacy contact body paperflakes": "Bei Fragen zum Datenschutz oder zur Ausübung Ihrer Rechte:",
"Privacy contact heading": "Kontakt",
"Privacy controller body": "Verantwortlich für die Datenbearbeitung auf dieser Website ist die codebar Solutions AG.",
"Privacy controller body paperflakes": "Verantwortlich für die Datenbearbeitung auf dieser Website ist die paperflakes AG.",
"Privacy controller heading": "Verantwortliche Stelle",
"Privacy data collected analytics": "Nutzungsstatistiken über Fathom Analytics (cookieless, anonymisierte Seitenaufrufe; keine Personenprofile)",
"Privacy data collected errors": "Fehler- und Leistungsdaten an Flare zur Fehlerbehebung (serverseitig; sensible Header werden zensiert)",
"Privacy data collected heading": "Welche Daten wir erheben",
"Privacy data collected intro": "Beim Besuch dieser Website verarbeiten wir folgende Datenkategorien:",
"Privacy data collected logs": "Server-Logdaten (IP-Adresse, Datum und Uhrzeit des Zugriffs, Browsertyp, besuchte Seiten)",
"Privacy data collected session": "Technisch notwendige Session- und CSRF-Cookies für den sicheren Betrieb der Website",
"Privacy processors body": "Wir setzen folgende Dienstleister ein, die Personendaten in unserem Auftrag verarbeiten können:",
"Privacy processors cloudinary": "Cloudinary – Auslieferung von Bildern und Medien (Datenschutz: cloudinary.com/privacy)",
"Privacy processors fathom": "Fathom Analytics – datenschutzfreundliche Website-Statistiken (usefathom.com/privacy)",
"Privacy processors flare": "Flare – Fehlerüberwachung unserer Anwendung (flareapp.io/privacy)",
"Privacy processors heading": "Auftragsbearbeiter",
"Privacy processors hosting": "Laravel Cloud – Website-Hosting und Infrastruktur (laravel.com)",
"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.",
"Privacy purpose heading": "Zweck und Rechtsgrundlage",
"Privacy retention analytics": "Analysedaten werden gemäss Richtlinie von Fathom aufbewahrt.",
"Privacy retention errors": "Fehlermeldungen werden so lange aufbewahrt, wie zur Behebung erforderlich.",
"Privacy retention heading": "Aufbewahrungsdauer",
"Privacy retention logs": "Server-Logs werden bis zu 90 Tage aufbewahrt.",
"Privacy retention session": "Session-Daten werden beim Schliessen des Browsers gelöscht.",
"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.",
"Privacy rights heading": "Ihre Rechte",
"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.",
"Privacy scope heading": "Geltungsbereich",
"Privacy security body": "Wir schützen Ihre Daten durch HTTPS-Verschlüsselung und branchenübliche Sicherheitsmassnahmen auf unserer Hosting-Infrastruktur.",
"Privacy security heading": "Datensicherheit",
"Authorized representatives": "Zeichnungsberechtigte Personen",
"Disclaimer": "Haftungsausschluss",
"Products": "Produkte",
"Published at": "Veröffentlicht am",
"Published at: :date": "Veröffentlicht am: :date",
Expand Down
2 changes: 1 addition & 1 deletion lang/de_CH/components.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
],
'development' => [
'title' => 'Individuelle Softwareentwicklung',
'description' => 'Portallösungen, Schnittstellen und Open Source für Ihre Prozesse.',
'description' => 'Portallösungen, Schnittstellen und Integrationen – mit Open Source im Fokus.',
],
'dms' => [
'title' => 'DMS/ECM Consulting & Implementation',
Expand Down
44 changes: 44 additions & 0 deletions lang/en_CH.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,21 @@
"Google Maps": "Google Maps",
"Headquarter": "Headquarter",
"Imprint": "Imprint",
"Imprint disclaimer": "The content of this website has been compiled with the greatest possible care. Nevertheless, we cannot guarantee the accuracy, completeness or timeliness of the content. As a service provider, we are responsible for our own content on these pages in accordance with general laws. We are not obliged to monitor transmitted or stored third-party information or to investigate circumstances that indicate illegal activity.",
"Imprint representatives placeholder": "Details of authorized representatives will be published here shortly.",
"Imprint individual signature": "Individual signing authority",
"Imprint Sebastian Burgin residence": "from Arlesheim, resident in Oberwil (BL)",
"Imprint Sebastian Burgin role": "Chairman of the Board of Directors and Managing Director",
"Imprint Melanie Burgin residence": "from Rothenfluh, resident in Oberwil (BL)",
"Imprint Melanie Burgin role": "Member of the Board of Directors",
"Info(at)paperflakes.ch": "Info(at)paperflakes.ch",
"info(at)codebar.ch": "info(at)codebar.ch",
"Jobs": "Jobs",
"Language": "Language",
"Last updated at": "Last updated at",
"Last updated at: :date": "Last updated at: :date",
"Legal": "Legal",
"Legal form AG": "Aktiengesellschaft (AG)",
"Locations": "Locations",
"Media": "Media",
"Media intro": "Download official codebar logos for press and partner use.",
Expand All @@ -42,6 +51,41 @@
"paperflakes AG": "paperflakes AG",
"Phone": "Phone",
"Privacy": "Privacy",
"Privacy changes body": "We may update this privacy policy from time to time. The current version is always available on this page with the date of the last update.",
"Privacy changes heading": "Changes to this policy",
"Privacy contact body": "For questions about data protection or to exercise your rights:",
"Privacy contact body paperflakes": "For questions about data protection or to exercise your rights:",
"Privacy contact heading": "Contact",
"Privacy controller body": "The controller responsible for data processing on this website is codebar Solutions AG.",
"Privacy controller body paperflakes": "The controller responsible for data processing on this website is paperflakes AG.",
"Privacy controller heading": "Controller",
"Privacy data collected analytics": "Usage analytics via Fathom Analytics (cookieless, anonymized page views; no personal profiles)",
"Privacy data collected errors": "Error and performance data sent to Flare for troubleshooting (server-side; sensitive headers are censored)",
"Privacy data collected heading": "Data we collect",
"Privacy data collected intro": "When you visit this website, we process the following categories of data:",
"Privacy data collected logs": "Server log data (IP address, date and time of access, browser type, pages visited)",
"Privacy data collected session": "Technically necessary session and CSRF cookies for secure operation of the website",
"Privacy processors body": "We use the following third-party services that may process personal data on our behalf:",
"Privacy processors cloudinary": "Cloudinary – delivery of images and media assets (privacy policy: cloudinary.com/privacy)",
"Privacy processors fathom": "Fathom Analytics – privacy-friendly website statistics (usefathom.com/privacy)",
"Privacy processors flare": "Flare – error monitoring for our application (flareapp.io/privacy)",
"Privacy processors heading": "Third-party processors",
"Privacy processors hosting": "Laravel Cloud – website hosting and infrastructure (laravel.com)",
"Privacy purpose body": "We process this data to operate and secure the website, to understand aggregate usage patterns, and to diagnose technical issues. The legal basis is our legitimate interest in providing a secure and functional website, and where applicable the performance of a contract.",
"Privacy purpose heading": "Purpose and legal basis",
"Privacy retention analytics": "Analytics data is retained according to Fathom's policy.",
"Privacy retention errors": "Error reports are retained as long as necessary to resolve the issue.",
"Privacy retention heading": "Retention",
"Privacy retention logs": "Server logs are retained for up to 90 days.",
"Privacy retention session": "Session data is deleted when you close your browser.",
"Privacy rights body": "Under the Swiss Federal Act on Data Protection (nDSG), you have the right to request information about your personal data, to have inaccurate data corrected, to request deletion where applicable, and to object to processing. To exercise these rights, please use our contact page.",
"Privacy rights heading": "Your rights",
"Privacy scope body": "This privacy policy applies to the codebar website at codebar.ch and its subpages. It does not apply to third-party websites linked from this site.",
"Privacy scope heading": "Scope",
"Privacy security body": "We protect your data using HTTPS encryption and industry-standard security measures on our hosting infrastructure.",
"Privacy security heading": "Data security",
"Authorized representatives": "Authorized representatives",
"Disclaimer": "Disclaimer",
"Products": "Products",
"Published at": "Published at",
"Published at: :date": "Published at: :date",
Expand Down
2 changes: 1 addition & 1 deletion lang/en_CH/components.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
],
'development' => [
'title' => 'Individual software development',
'description' => 'Portal solutions, interfaces and open source for your processes.',
'description' => 'Portal solutions, interfaces and integrations – with a focus on open source.',
],
'dms' => [
'title' => 'DMS/ECM consulting & implementation',
Expand Down
Loading
Loading