Support Image-Upload and Paste-from-Clipboard
This commit is contained in:
213
item.php
213
item.php
@@ -2,7 +2,6 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/include/image_fetch.php';
|
||||
|
||||
use WList\Net\ImageFetch;
|
||||
|
||||
/* ========= Session & Bootstrap ========= */
|
||||
@@ -93,6 +92,116 @@ function is_valid_http_url(string $url): bool
|
||||
return $s === 'http' || $s === 'https';
|
||||
}
|
||||
|
||||
/* ===== Bild-Speicher-Helper ===== */
|
||||
function ensure_imagedir(): string
|
||||
{
|
||||
global $imagedir;
|
||||
if (!is_dir($imagedir))
|
||||
@mkdir($imagedir, 0755, true);
|
||||
return rtrim($imagedir, '/');
|
||||
}
|
||||
function save_image_from_tmp(string $tmp, ?string $sourceNameOrUrl = null): string
|
||||
{
|
||||
$info = @getimagesize($tmp);
|
||||
if ($info === false || empty($info['mime']) || stripos($info['mime'], 'image/') !== 0) {
|
||||
@unlink($tmp);
|
||||
fail('Ungültige Bilddatei', 400);
|
||||
}
|
||||
$dir = ensure_imagedir();
|
||||
// Dateiname: wenn URL vorhanden → davon die Ext, sonst aus MIME
|
||||
if ($sourceNameOrUrl && is_valid_http_url($sourceNameOrUrl)) {
|
||||
$name = ImageFetch::safeFileNameFromUrl($sourceNameOrUrl);
|
||||
} else {
|
||||
$ext = 'jpg';
|
||||
$mime = strtolower($info['mime']);
|
||||
if (str_contains($mime, 'png'))
|
||||
$ext = 'png';
|
||||
elseif (str_contains($mime, 'webp'))
|
||||
$ext = 'webp';
|
||||
elseif (str_contains($mime, 'gif'))
|
||||
$ext = 'gif';
|
||||
elseif (str_contains($mime, 'avif'))
|
||||
$ext = 'avif';
|
||||
elseif (str_contains($mime, 'jpeg'))
|
||||
$ext = 'jpg';
|
||||
$name = bin2hex(random_bytes(10)) . '.' . $ext;
|
||||
}
|
||||
$target = $dir . '/' . $name;
|
||||
|
||||
if (!@rename($tmp, $target)) {
|
||||
if (!@copy($tmp, $target)) {
|
||||
@unlink($tmp);
|
||||
fail('Bildspeicherung fehlgeschlagen', 500);
|
||||
}
|
||||
@unlink($tmp);
|
||||
}
|
||||
@chmod($target, 0644);
|
||||
return $name;
|
||||
}
|
||||
function save_image_from_upload(array $file): string
|
||||
{
|
||||
// Erwartet $_FILES['ItemImageFile']
|
||||
if (!isset($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) {
|
||||
fail('Datei-Upload fehlgeschlagen', 400);
|
||||
}
|
||||
if (!empty($file['error'])) {
|
||||
fail('Datei-Upload Fehler (Code ' . (int) $file['error'] . ')', 400);
|
||||
}
|
||||
$size = (int) ($file['size'] ?? 0);
|
||||
if ($size <= 0 || $size > 8 * 1024 * 1024) {
|
||||
fail('Datei ist zu groß (max. 8 MB)', 400);
|
||||
}
|
||||
$tmp = $file['tmp_name'];
|
||||
// doppelt absichern: in separärer Temp-Datei lesen/schreiben (optional)
|
||||
$probe = @getimagesize($tmp);
|
||||
if ($probe === false || empty($probe['mime']) || stripos($probe['mime'], 'image/') !== 0) {
|
||||
fail('Hochgeladene Datei ist kein gültiges Bild', 400);
|
||||
}
|
||||
// in einen eigenen tmp kopieren, um unify mit save_image_from_tmp zu haben
|
||||
$tmp2 = tempnam(sys_get_temp_dir(), 'wlimg_');
|
||||
if ($tmp2 === false)
|
||||
fail('Temp-Datei Fehler', 500);
|
||||
if (!@copy($tmp, $tmp2)) {
|
||||
@unlink($tmp2);
|
||||
fail('Temp-Datei Fehler', 500);
|
||||
}
|
||||
|
||||
$origName = (string) ($file['name'] ?? '');
|
||||
// Falls Originalname eine Extension hat, nutzen wir sie indirekt über getimagesize() (oben)
|
||||
return save_image_from_tmp($tmp2, $origName !== '' ? $origName : null);
|
||||
}
|
||||
function save_image_from_dataurl(string $dataUrl, string $suggestedName = 'clipboard.png'): string
|
||||
{
|
||||
// data:image/png;base64,....
|
||||
if (!preg_match('#^data:(image/[\w\-\+\.]+);base64,(.+)$#i', $dataUrl, $m)) {
|
||||
fail('Zwischenablage-Daten ungültig', 400);
|
||||
}
|
||||
$mime = strtolower($m[1]);
|
||||
$b64 = $m[2];
|
||||
|
||||
// Größenlimit grob prüfen (Base64 ist ~33% größer)
|
||||
$rawLen = (int) (strlen($b64) * 3 / 4);
|
||||
if ($rawLen > 8 * 1024 * 1024) {
|
||||
fail('Zwischenablage-Bild ist zu groß (max. 8 MB)', 400);
|
||||
}
|
||||
|
||||
$bin = base64_decode($b64, true);
|
||||
if ($bin === false || strlen($bin) === 0) {
|
||||
fail('Zwischenablage-Daten ungültig (Decode)', 400);
|
||||
}
|
||||
|
||||
$tmp = tempnam(sys_get_temp_dir(), 'wlimg_');
|
||||
if ($tmp === false)
|
||||
fail('Temp-Datei Fehler', 500);
|
||||
if (file_put_contents($tmp, $bin) === false) {
|
||||
@unlink($tmp);
|
||||
fail('Temp-Datei Fehler', 500);
|
||||
}
|
||||
|
||||
// suggestedName nur als Hint – finale Ext über getimagesize/MIME
|
||||
return save_image_from_tmp($tmp, $suggestedName);
|
||||
}
|
||||
|
||||
/* ============= Controller ============= */
|
||||
|
||||
require_csrf();
|
||||
@@ -104,7 +213,12 @@ $ItemDescription = trim((string) ($_POST['ItemDescription'] ?? ''));
|
||||
$ItemPrice = parse_price_to_cents((string) ($_POST['ItemPrice'] ?? ''));
|
||||
$ItemQty = isset($_POST['ItemQty']) ? max(1, (int) $_POST['ItemQty']) : 1;
|
||||
$ItemLink = trim((string) ($_POST['ItemLink'] ?? ''));
|
||||
$ItemImageUrl = trim((string) ($_POST['ItemImage'] ?? '')); // optional URL zum Pull
|
||||
|
||||
// NEU: drei mögliche Bild-Inputs
|
||||
$ItemImageUrl = trim((string) ($_POST['ItemImageUrl'] ?? ''));
|
||||
$ItemImagePaste = (string) ($_POST['ItemImagePaste'] ?? '');
|
||||
$ItemImagePasteName = trim((string) ($_POST['ItemImagePasteName'] ?? 'clipboard.png'));
|
||||
|
||||
$ListUUID = trim((string) ($_POST['ItemListUUID'] ?? ''));
|
||||
$sortbyTransfer = (string) ($_POST['sortby_transfer'] ?? 'priority');
|
||||
|
||||
@@ -141,54 +255,52 @@ if ($ItemTitle === '')
|
||||
if ($ItemLink !== '' && !is_valid_http_url($ItemLink))
|
||||
fail('Ungültiger Angebotslink', 400);
|
||||
|
||||
/* Optional: Bild von externer URL holen */
|
||||
/* ===== Bild verarbeiten: Upload → Paste → URL ===== */
|
||||
$imageLocalLink = null;
|
||||
if (!$removeImage && $ItemImageUrl !== '') {
|
||||
$hasUpload = isset($_FILES['ItemImageFile']) && is_array($_FILES['ItemImageFile']) && !empty($_FILES['ItemImageFile']['name']);
|
||||
$hasPaste = $ItemImagePaste !== '';
|
||||
$hasUrl = $ItemImageUrl !== '';
|
||||
|
||||
$whitelist = $image_host_whitelist ?? null;
|
||||
if (!$removeImage) {
|
||||
if ($hasUpload) {
|
||||
// 1) Datei-Upload
|
||||
$imageLocalLink = save_image_from_upload($_FILES['ItemImageFile']);
|
||||
|
||||
$fetch = ImageFetch::download($ItemImageUrl, [
|
||||
'max_bytes' => 8_000_000,
|
||||
'timeout' => 12,
|
||||
'connect_timeout' => 5,
|
||||
'retries' => 4,
|
||||
'retry_backoff_ms' => 300,
|
||||
'whitelist_hosts' => $whitelist,
|
||||
'ip_resolve_v4' => true,
|
||||
'referer' => 'auto',
|
||||
'log_prefix' => 'wishlist-img',
|
||||
]);
|
||||
} elseif ($hasPaste) {
|
||||
// 2) Zwischenablage (Data-URL)
|
||||
$imageLocalLink = save_image_from_dataurl($ItemImagePaste, $ItemImagePasteName);
|
||||
|
||||
if (!$fetch['ok']) {
|
||||
error_log("wishlist image error: http=" . ($fetch['http_code'] ?? 0) . " curl=" . ($fetch['curl_err'] ?? '-') . " url=$ItemImageUrl");
|
||||
$conn->close();
|
||||
fail('Bild-Download fehlgeschlagen', 400);
|
||||
}
|
||||
|
||||
$info = @getimagesize($fetch['tmp_path']);
|
||||
$mime = $info['mime'] ?? $fetch['mime'] ?? 'image/*';
|
||||
if (stripos($mime, 'image/') !== 0) {
|
||||
@unlink($fetch['tmp_path']);
|
||||
$conn->close();
|
||||
fail('Link ist kein gültiges Bild', 400);
|
||||
}
|
||||
|
||||
global $imagedir;
|
||||
if (!is_dir($imagedir))
|
||||
@mkdir($imagedir, 0755, true);
|
||||
$filename = ImageFetch::safeFileNameFromUrl($ItemImageUrl);
|
||||
$target = rtrim($imagedir, '/') . '/' . $filename;
|
||||
|
||||
if (!@rename($fetch['tmp_path'], $target)) {
|
||||
if (!@copy($fetch['tmp_path'], $target)) {
|
||||
@unlink($fetch['tmp_path']);
|
||||
} elseif ($hasUrl) {
|
||||
// 3) Bild-URL (Download-Pflicht, sonst Fehler)
|
||||
if (!is_valid_http_url($ItemImageUrl)) {
|
||||
$conn->close();
|
||||
fail('Bildspeicherung fehlgeschlagen', 500);
|
||||
fail('Ungültiger Bildlink', 400);
|
||||
}
|
||||
@unlink($fetch['tmp_path']);
|
||||
|
||||
$fetch = ImageFetch::download($ItemImageUrl, [
|
||||
'page_url' => $ItemLink !== '' && is_valid_http_url($ItemLink) ? $ItemLink : null,
|
||||
'max_bytes' => 8_000_000,
|
||||
'timeout' => 12,
|
||||
'connect_timeout' => 5,
|
||||
'retries' => 5,
|
||||
'retry_backoff_ms' => 300,
|
||||
'whitelist_hosts' => $image_host_whitelist ?? null,
|
||||
'referer' => 'auto', // Falls keine page_url, nimmt er Bild-Origin
|
||||
'log_prefix' => 'wishlist-img',
|
||||
'debug' => true,
|
||||
'try_http_versions' => ['2', '1.1'],
|
||||
'try_ip_resolve_combo' => ['v4', 'auto'],
|
||||
'force_client_hints' => true,
|
||||
]);
|
||||
|
||||
if (!$fetch['ok']) {
|
||||
error_log("wishlist image error: http=" . ($fetch['http_code'] ?? 0) . " curl=" . ($fetch['curl_err'] ?? '-') . " url=$ItemImageUrl");
|
||||
$conn->close();
|
||||
fail('Bild-Download fehlgeschlagen. Bitte Bild manuell speichern/hochladen oder per Zwischenablage einfügen und erneut versuchen.', 400);
|
||||
}
|
||||
|
||||
$imageLocalLink = save_image_from_tmp($fetch['tmp_path'], $ItemImageUrl);
|
||||
}
|
||||
@chmod($target, 0644);
|
||||
$imageLocalLink = $filename;
|
||||
}
|
||||
|
||||
/* ====== ADD ====== */
|
||||
@@ -227,8 +339,9 @@ if ($action === 'add') {
|
||||
fail('Speichern fehlgeschlagen', 500);
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
/* ====== EDIT ====== */ elseif ($action === 'edit') {
|
||||
|
||||
/* ====== EDIT ====== */
|
||||
} elseif ($action === 'edit') {
|
||||
if ($WhishID <= 0) {
|
||||
$conn->close();
|
||||
fail('Ungültige Item-ID', 400);
|
||||
@@ -251,16 +364,14 @@ if ($action === 'add') {
|
||||
$newImage = $oldImage;
|
||||
if ($removeImage) {
|
||||
if (!empty($oldImage)) {
|
||||
global $imagedir;
|
||||
$full = rtrim($imagedir, '/') . '/' . $oldImage;
|
||||
$full = ensure_imagedir() . '/' . $oldImage;
|
||||
if (is_file($full))
|
||||
@unlink($full);
|
||||
}
|
||||
$newImage = '';
|
||||
} elseif ($imageLocalLink !== null) {
|
||||
if (!empty($oldImage)) {
|
||||
global $imagedir;
|
||||
$full = rtrim($imagedir, '/') . '/' . $oldImage;
|
||||
$full = ensure_imagedir() . '/' . $oldImage;
|
||||
if (is_file($full))
|
||||
@unlink($full);
|
||||
}
|
||||
@@ -291,8 +402,8 @@ if ($action === 'add') {
|
||||
fail('Update fehlgeschlagen', 500);
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
/* ====== Unbekannt ====== */ else {
|
||||
|
||||
} else {
|
||||
$conn->close();
|
||||
fail('Unbekannte Aktion', 400);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user