|
| 1 | +<?php |
| 2 | +declare(strict_types=1); |
| 3 | + |
| 4 | +require_once __DIR__ . '/../src/http.php'; |
| 5 | +require_once __DIR__ . '/../src/vehicle-photos.php'; |
| 6 | + |
| 7 | +/** |
| 8 | + * Uploading and removing a vehicle's photograph. |
| 9 | + * |
| 10 | + * Separate from vehicles.php because this one takes multipart form data rather |
| 11 | + * than JSON, and folding a file upload into an endpoint that parses a JSON body |
| 12 | + * would mean the body being read two different ways depending on the request. |
| 13 | + * |
| 14 | + * Same guard as every other write: signed in, permitted, POST, CSRF token. |
| 15 | + * api_guard reads the token from X-CSRF-Token or a csrf_token field, and a |
| 16 | + * multipart form can carry either. |
| 17 | + */ |
| 18 | + |
| 19 | +$user = api_guard('vehicle.edit', true); |
| 20 | + |
| 21 | +$vehicleId = (int) ($_POST['vehicle_id'] ?? 0); |
| 22 | +if ($vehicleId <= 0) { |
| 23 | + json_error('Which vehicle this belongs to was not sent.', 422); |
| 24 | +} |
| 25 | + |
| 26 | +$vehicle = fetch_one('SELECT id, name, photo_file FROM vehicles WHERE id = ?', [$vehicleId]); |
| 27 | +if ($vehicle === null) { |
| 28 | + json_error('That vehicle no longer exists.', 404); |
| 29 | +} |
| 30 | + |
| 31 | +$previous = $vehicle['photo_file'] !== null ? (string) $vehicle['photo_file'] : null; |
| 32 | + |
| 33 | +// ------------------------------------------------------------- removal -- |
| 34 | +if (($_POST['remove'] ?? '') === '1') { |
| 35 | + query('UPDATE vehicles SET photo_file = NULL WHERE id = ?', [$vehicleId]); |
| 36 | + vehicle_photo_delete($previous); |
| 37 | + |
| 38 | + audit_log('vehicle_photo_removed', 'vehicles', 'vehicle', $vehicleId, |
| 39 | + ['photo_file' => $previous], ['photo_file' => null], null, |
| 40 | + (int) $user['id'], $user['name'], null, null, $vehicleId); |
| 41 | + |
| 42 | + json_out(['ok' => true, 'photo' => null]); |
| 43 | +} |
| 44 | + |
| 45 | +// -------------------------------------------------------------- upload -- |
| 46 | +$file = $_FILES['photo'] ?? null; |
| 47 | +if (!is_array($file)) { |
| 48 | + json_error('No image was attached.', 422); |
| 49 | +} |
| 50 | + |
| 51 | +[$extension, $problem] = vehicle_photo_check($file); |
| 52 | +if ($problem !== null) { |
| 53 | + json_error($problem, 422); |
| 54 | +} |
| 55 | + |
| 56 | +try { |
| 57 | + $stored = vehicle_photo_store($vehicleId, $file, (string) $extension); |
| 58 | +} catch (RuntimeException $e) { |
| 59 | + error_log('vehicle photo store failed: ' . $e->getMessage()); |
| 60 | + json_error('The image could not be saved on the server.', 500); |
| 61 | +} |
| 62 | + |
| 63 | +query('UPDATE vehicles SET photo_file = ? WHERE id = ?', [$stored, $vehicleId]); |
| 64 | + |
| 65 | +// Only after the row points at the new file. The other order would leave a |
| 66 | +// vehicle pointing at a file that had just been deleted if the update failed. |
| 67 | +vehicle_photo_delete($previous); |
| 68 | + |
| 69 | +audit_log('vehicle_photo_changed', 'vehicles', 'vehicle', $vehicleId, |
| 70 | + ['photo_file' => $previous], ['photo_file' => $stored], null, |
| 71 | + (int) $user['id'], $user['name'], null, null, $vehicleId); |
| 72 | + |
| 73 | +json_out(['ok' => true, 'photo' => vehicle_photo_url($stored)]); |
0 commit comments