Browse Source

moving image uploads to be in data, not assets

Medowar 1 month ago
parent
commit
de4f5463b5

+ 1 - 0
README.md

@@ -28,6 +28,7 @@ Dieses Projekt ist ein internes Bestellsystem für persönliche Schutzausrüstun
 - Organisationen: `data/organizations.json`
 - Systemeinstellungen: `data/settings.json`
 - Produkte: `data/products.json`
+- Produktbilder: `data/uploads/`
 
 ## Einrichtung
 

+ 5 - 5
admin/products.php

@@ -36,9 +36,9 @@ function handleImageUpload($fileInputName = 'image_file') {
         return ['success' => false, 'message' => 'Die Datei ist kein gültiges Bild.'];
     }
 
-    $imagesDir = __DIR__ . '/../assets/images';
-    if (!is_dir($imagesDir)) {
-        mkdir($imagesDir, 0755, true);
+    $uploadsDir = rtrim(UPLOADS_DIR, '/\\');
+    if (!is_dir($uploadsDir)) {
+        mkdir($uploadsDir, 0755, true);
     }
 
     $safeBaseName = preg_replace('/[^a-zA-Z0-9_-]/', '-', pathinfo($originalName, PATHINFO_FILENAME));
@@ -48,11 +48,11 @@ function handleImageUpload($fileInputName = 'image_file') {
     }
 
     $targetFilename = $safeBaseName . '.' . $extension;
-    $targetPath = $imagesDir . '/' . $targetFilename;
+    $targetPath = $uploadsDir . '/' . $targetFilename;
     $counter = 1;
     while (file_exists($targetPath)) {
         $targetFilename = $safeBaseName . '-' . $counter . '.' . $extension;
-        $targetPath = $imagesDir . '/' . $targetFilename;
+        $targetPath = $uploadsDir . '/' . $targetFilename;
         $counter++;
     }
 

+ 2 - 0
config.sample.php

@@ -34,6 +34,7 @@ define('FROM_NAME', SITE_FULL_NAME);
 
 // Data file paths
 define('DATA_DIR', __DIR__ . '/data/');
+define('UPLOADS_DIR', DATA_DIR . 'uploads/');
 define('PRODUCTS_FILE', DATA_DIR . 'products.json');
 define('ORDERS_FILE', DATA_DIR . 'orders.json');
 define('ORGANIZATIONS_FILE', DATA_DIR . 'organizations.json');
@@ -41,6 +42,7 @@ define('SETTINGS_FILE', DATA_DIR . 'settings.json');
 define('ADMINS_FILE', DATA_DIR . 'admins.json');
 define('CATEGORIES_FILE', DATA_DIR . 'categories.json');
 define('FAQ_FILE', DATA_DIR . 'faq.json');
+define('UPLOADS_URL', SITE_URL . '/data/uploads');
 
 // Session settings
 if (session_status() === PHP_SESSION_NONE) {

BIN
data/uploads/Jacke-Atemschutz.webp


BIN
data/uploads/Jacke-thl.webp


BIN
data/uploads/helm.jpg


BIN
data/uploads/hose-thl.webp


+ 0 - 0
assets/images/poloshirt.jpg → data/uploads/poloshirt.jpg


+ 0 - 0
assets/images/tshirt.jpg → data/uploads/tshirt.jpg


+ 1 - 1
docs/STYLE_SYSTEM.md

@@ -317,7 +317,7 @@ Email styles are inline HTML/CSS and must keep the same dark-theme palette.
   - `assets/feuerwehr-Logo-invers.webp`
   - If rebranding for another intranet, replace asset file while preserving placement/sizing behavior.
 - Media/image behavior:
-  - source path pattern in current system: `assets/images/<filename>`
+  - source path pattern in current system: `data/uploads/<filename>`
   - detail/media view uses full-width image with `8px` radius and soft shadow.
 - Placeholder fallback behavior:
   - inline SVG fallback uses:

+ 27 - 0
includes/functions.php

@@ -32,6 +32,33 @@ function escape($value) {
     return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
 }
 
+function getUploadFilename($filename) {
+    $filename = trim((string) $filename);
+    if ($filename === '') {
+        return '';
+    }
+
+    return basename($filename);
+}
+
+function getUploadPath($filename) {
+    $filename = getUploadFilename($filename);
+    if ($filename === '') {
+        return null;
+    }
+
+    return rtrim(UPLOADS_DIR, '/\\') . '/' . $filename;
+}
+
+function getUploadUrl($filename) {
+    $filename = getUploadFilename($filename);
+    if ($filename === '') {
+        return null;
+    }
+
+    return rtrim(UPLOADS_URL, '/\\') . '/' . rawurlencode($filename);
+}
+
 function setFlashMessage($key, $type, $message) {
     $key = trim((string) $key);
     $type = trim((string) $type);

+ 4 - 2
index.php

@@ -44,8 +44,10 @@ include __DIR__ . '/includes/header.php';
         <?php foreach ($products as $product): ?>
             <div class="product-card">
                 <a href="product.php?id=<?php echo (int) $product['id']; ?>">
-                    <?php if (!empty($product['image']) && file_exists(__DIR__ . '/assets/images/' . $product['image'])): ?>
-                        <img src="<?php echo escape(SITE_URL); ?>/assets/images/<?php echo escape($product['image']); ?>" alt="<?php echo escape($product['name']); ?>">
+                    <?php $imagePath = getUploadPath($product['image'] ?? ''); ?>
+                    <?php $imageUrl = getUploadUrl($product['image'] ?? ''); ?>
+                    <?php if ($imagePath !== null && $imageUrl !== null && file_exists($imagePath)): ?>
+                        <img src="<?php echo escape($imageUrl); ?>" alt="<?php echo escape($product['name']); ?>">
                     <?php else: ?>
                         <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='250' height='200'%3E%3Crect fill='%233a4150' width='250' height='200'/%3E%3Ctext x='50%25' y='50%25' text-anchor='middle' dy='.3em' fill='%23c7ccd6'%3EKein Bild%3C/text%3E%3C/svg%3E" alt="Kein Bild">
                     <?php endif; ?>

+ 4 - 2
product.php

@@ -49,8 +49,10 @@ include __DIR__ . '/includes/header.php';
 
 <div class="product-detail-grid">
     <div>
-        <?php if (!empty($product['image']) && file_exists(__DIR__ . '/assets/images/' . $product['image'])): ?>
-            <img class="product-image" src="<?php echo escape(SITE_URL); ?>/assets/images/<?php echo escape($product['image']); ?>" alt="<?php echo escape($product['name']); ?>">
+        <?php $imagePath = getUploadPath($product['image'] ?? ''); ?>
+        <?php $imageUrl = getUploadUrl($product['image'] ?? ''); ?>
+        <?php if ($imagePath !== null && $imageUrl !== null && file_exists($imagePath)): ?>
+            <img class="product-image" src="<?php echo escape($imageUrl); ?>" alt="<?php echo escape($product['name']); ?>">
         <?php else: ?>
             <div class="product-placeholder">Kein Bild verfügbar</div>
         <?php endif; ?>