|
|
@@ -52,20 +52,55 @@ function random_token(int $chars = 8): string
|
|
|
return $out;
|
|
|
}
|
|
|
|
|
|
-/** Turn a title into a URL slug fragment ("Wedding Müller" → "wedding-mueller"). */
|
|
|
-function slugify(string $title): string
|
|
|
+/**
|
|
|
+ * Transliterate German (and, best effort, other accented) characters to ASCII
|
|
|
+ * so they survive in slugs, filenames and S3 keys instead of being dropped:
|
|
|
+ * ä→ae, ö→oe, ü→ue, ß→ss, é→e, … Case is preserved.
|
|
|
+ */
|
|
|
+function ascii_transliterate(string $s): string
|
|
|
{
|
|
|
- $map = ['ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'Ä' => 'ae', 'Ö' => 'oe', 'Ü' => 'ue', 'ß' => 'ss'];
|
|
|
- $s = strtr($title, $map);
|
|
|
+ $map = [
|
|
|
+ 'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue',
|
|
|
+ 'Ä' => 'Ae', 'Ö' => 'Oe', 'Ü' => 'Ue', 'ß' => 'ss',
|
|
|
+ ];
|
|
|
+ $s = strtr($s, $map);
|
|
|
if (function_exists('iconv')) {
|
|
|
- $s = (string)@iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
|
|
+ $converted = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
|
|
+ if ($converted !== false) {
|
|
|
+ $s = $converted;
|
|
|
+ }
|
|
|
}
|
|
|
- $s = strtolower($s);
|
|
|
+ return $s;
|
|
|
+}
|
|
|
+
|
|
|
+/** Turn a title into a URL slug fragment ("Wedding Müller" → "wedding-mueller"). */
|
|
|
+function slugify(string $title): string
|
|
|
+{
|
|
|
+ $s = strtolower(ascii_transliterate($title));
|
|
|
$s = preg_replace('/[^a-z0-9]+/', '-', $s) ?? '';
|
|
|
$s = trim($s, '-');
|
|
|
return $s !== '' ? $s : 'gallery';
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Sanitize an upload filename to a safe ASCII basename, keeping the extension.
|
|
|
+ * German characters are transliterated rather than replaced by dashes, so
|
|
|
+ * "Straße.jpg" becomes "Strasse.jpg" instead of "Stra-e.jpg".
|
|
|
+ */
|
|
|
+function safe_filename(string $name, string $fallback = 'file'): string
|
|
|
+{
|
|
|
+ $name = basename($name);
|
|
|
+ $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
|
|
|
+ $base = ascii_transliterate(pathinfo($name, PATHINFO_FILENAME));
|
|
|
+ $base = preg_replace('/[^A-Za-z0-9._-]+/', '-', $base) ?? '';
|
|
|
+ $base = trim($base, '-.');
|
|
|
+ if ($base === '') {
|
|
|
+ $base = $fallback;
|
|
|
+ }
|
|
|
+ $ext = preg_replace('/[^A-Za-z0-9]+/', '', $ext) ?? '';
|
|
|
+ return $ext !== '' ? $base . '.' . $ext : $base;
|
|
|
+}
|
|
|
+
|
|
|
// ---------------------------------------------------------------------------
|
|
|
// Local media (hero + showreel images in media/)
|
|
|
// ---------------------------------------------------------------------------
|
|
|
@@ -89,8 +124,8 @@ function media_store_upload(array $file): ?string
|
|
|
if (function_exists('getimagesize') && @getimagesize($file['tmp_name']) === false) {
|
|
|
return null;
|
|
|
}
|
|
|
- $base = pathinfo($file['name'], PATHINFO_FILENAME);
|
|
|
- $base = preg_replace('/[^A-Za-z0-9._-]+/', '-', $base) ?: 'image';
|
|
|
+ $base = ascii_transliterate(pathinfo($file['name'], PATHINFO_FILENAME));
|
|
|
+ $base = trim(preg_replace('/[^A-Za-z0-9._-]+/', '-', $base) ?? '', '-.') ?: 'image';
|
|
|
$name = substr($base, 0, 60) . '-' . random_token(6) . '.' . $ext;
|
|
|
if (!move_uploaded_file($file['tmp_name'], MEDIA_DIR . '/' . $name)) {
|
|
|
return null;
|