fixed newsletyter1

This commit is contained in:
2026-10-19 08:41:08 +02:00
parent ffe4ccbb06
commit f980c66257
2 changed files with 99 additions and 55 deletions
+71 -28
View File
@@ -167,9 +167,15 @@ try {
SET title = ?, subject = ?, preheader = ?, blocks = ?, settings = ?,
revision = revision + 1, updated_by = ?, updated_at = NOW()
WHERE iddraft = ? AND deleted_at IS NULL" . ($force ? '' : ' AND revision = ?');
$params = [$c['title'], $c['subject'], $c['preheader'],
json_encode($c['blocks'], JSON_UNESCAPED_UNICODE), json_encode($c['settings'], JSON_UNESCAPED_UNICODE),
$userId, $id];
$params = [
$c['title'],
$c['subject'],
$c['preheader'],
json_encode($c['blocks'], JSON_UNESCAPED_UNICODE),
json_encode($c['settings'], JSON_UNESCAPED_UNICODE),
$userId,
$id
];
if (!$force) $params[] = $rev;
$st = $pdo->prepare($sql);
@@ -178,7 +184,9 @@ try {
$d = draftRow($pdo, $id);
if ($st->rowCount() === 0) {
fail('Questa newsletter è stata salvata da un\'altra parte (un\'altra scheda o un altro computer).', 409, [
'conflict' => true, 'revision' => (int) $d['revision'], 'updated_at' => $d['updated_at'],
'conflict' => true,
'revision' => (int) $d['revision'],
'updated_at' => $d['updated_at'],
]);
}
@@ -251,49 +259,71 @@ try {
case 'upload_image':
if (!$isPost) fail('Metodo non consentito.', 405);
$f = $_FILES['image'] ?? null;
if (!$f || $f['error'] !== UPLOAD_ERR_OK) fail('Caricamento non riuscito.');
if (!$f || ($f['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
$why = [
UPLOAD_ERR_INI_SIZE => 'il file supera il limite del server (upload_max_filesize)',
UPLOAD_ERR_FORM_SIZE => 'il file è troppo grande',
UPLOAD_ERR_PARTIAL => 'caricamento interrotto',
UPLOAD_ERR_NO_FILE => 'nessun file ricevuto',
UPLOAD_ERR_NO_TMP_DIR => 'manca la cartella temporanea sul server',
UPLOAD_ERR_CANT_WRITE => 'il server non riesce a scrivere il file',
][$f['error'] ?? UPLOAD_ERR_NO_FILE] ?? 'errore sconosciuto';
fail('Caricamento non riuscito: ' . $why . '.');
}
if ($f['size'] > 15 * 1024 * 1024) fail('Immagine troppo grande (massimo 15 MB).');
$info = @getimagesize($f['tmp_name']);
$mime = $info['mime'] ?? '';
if (!in_array($mime, ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], true)) {
fail('Formato non supportato: usa JPG, PNG, GIF o WebP.');
}
$ext = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif', 'image/webp' => 'webp'][$mime] ?? null;
if (!$ext) fail('Formato non supportato: usa JPG, PNG, GIF o WebP.');
$dirRel = 'assets/uploads/newsletter/' . date('Y/m');
$dirAbs = __DIR__ . '/' . $dirRel;
if (!is_dir($dirAbs) && !mkdir($dirAbs, 0755, true)) fail('Impossibile creare la cartella delle immagini.', 500);
if (!is_dir($dirAbs) && !@mkdir($dirAbs, 0755, true)) {
fail('Impossibile creare la cartella ' . $dirRel . ': controlla i permessi di assets/uploads.', 500);
}
if (!is_writable($dirAbs)) fail('La cartella ' . $dirRel . ' non è scrivibile: controlla i permessi.', 500);
$name = date('Ymd_His') . '_' . bin2hex(random_bytes(4));
[$w, $h] = $info;
$note = '';
if ($mime === 'image/gif') {
// Le GIF possono essere animate: le teniamo come sono
$file = $name . '.gif';
move_uploaded_file($f['tmp_name'], $dirAbs . '/' . $file);
// Si può ottimizzare? Serve GD con il formato giusto
$reader = ['image/jpeg' => 'imagecreatefromjpeg', 'image/png' => 'imagecreatefrompng', 'image/webp' => 'imagecreatefromwebp'][$mime] ?? null;
$canResize = $mime !== 'image/gif' && $reader && function_exists($reader) && function_exists('imagecreatetruecolor');
if (!$canResize) {
// GIF (forse animata) oppure server senza GD: salviamo l'originale
if ($mime !== 'image/gif' && $f['size'] > 3 * 1024 * 1024) {
fail('Il server non può ridimensionare le immagini (manca la libreria GD) e questa supera 3 MB: riducila prima di caricarla.');
}
$file = $name . '.' . $ext;
if (!move_uploaded_file($f['tmp_name'], $dirAbs . '/' . $file)) fail('Impossibile salvare l\'immagine.', 500);
if ($mime !== 'image/gif') $note = 'Caricata senza ottimizzazione (sul server manca la libreria GD).';
} else {
$src = match ($mime) {
'image/jpeg' => imagecreatefromjpeg($f['tmp_name']),
'image/png' => imagecreatefrompng($f['tmp_name']),
'image/webp' => imagecreatefromwebp($f['tmp_name']),
};
if (!$src) fail('Immagine non leggibile.');
try {
@ini_set('memory_limit', '512M'); // le foto dal telefono sono grandi
$src = @$reader($f['tmp_name']);
if (!$src) fail('Immagine non leggibile o danneggiata.');
// Foto dal telefono: raddrizza secondo l'orientamento EXIF
if ($mime === 'image/jpeg' && function_exists('exif_read_data')) {
$o = (int) (@exif_read_data($f['tmp_name'])['Orientation'] ?? 1);
$rot = [3 => 180, 6 => -90, 8 => 90][$o] ?? 0;
if ($rot) { $src = imagerotate($src, $rot, 0); $w = imagesx($src); $h = imagesy($src); }
if ($rot) {
$src = imagerotate($src, $rot, 0);
$w = imagesx($src);
$h = imagesy($src);
}
}
$maxW = 1200; // il doppio della larghezza email: nitida sugli schermi retina
$nw = min($w, $maxW);
$nh = (int) round($h * $nw / $w);
$nw = min($w, 1200); // il doppio della larghezza email: nitida sugli schermi retina
$nh = max(1, (int) round($h * $nw / $w));
// PNG con trasparenza resta PNG, tutto il resto diventa JPG (molto più leggero)
// PNG con trasparenza resta PNG, il resto diventa JPG (molto più leggero)
$keepPng = false;
if ($mime === 'image/png') {
$ct = ord(file_get_contents($f['tmp_name'], false, null, 25, 1));
$ct = ord((string) file_get_contents($f['tmp_name'], false, null, 25, 1));
$keepPng = in_array($ct, [4, 6], true);
}
@@ -310,8 +340,12 @@ try {
$ok = $keepPng ? imagepng($dst, $dirAbs . '/' . $file, 9) : imagejpeg($dst, $dirAbs . '/' . $file, 82);
imagedestroy($src);
imagedestroy($dst);
if (!$ok) fail('Impossibile salvare l\'immagine.', 500);
if (!$ok) fail('Impossibile salvare l\'immagine ottimizzata.', 500);
[$w, $h] = [$nw, $nh];
} catch (Throwable $e) {
error_log('newsletter_api [upload_image]: ' . $e->getMessage());
fail('Ottimizzazione non riuscita: ' . $e->getMessage(), 500);
}
}
out([
@@ -320,6 +354,7 @@ try {
'width' => $w,
'height' => $h,
'kb' => (int) round(filesize($dirAbs . '/' . $file) / 1024),
'note' => $note,
]);
case 'send_test':
@@ -413,8 +448,16 @@ try {
$imp = NewsletterWp::import($wpId);
// Se in WordPress non è ancora partita, la colleghiamo: "Pubblica" aggiornerà quella
$status = NewsletterWp::statuses([$wpId])[$wpId] ?? '';
$id = createDraft($pdo, $userId, $imp['subject'] ?: 'Importata da WordPress', $imp['subject'], $imp['preheader'],
$imp['blocks'], $imp['settings'], $status === 'new' ? $wpId : null);
$id = createDraft(
$pdo,
$userId,
$imp['subject'] ?: 'Importata da WordPress',
$imp['subject'],
$imp['preheader'],
$imp['blocks'],
$imp['settings'],
$status === 'new' ? $wpId : null
);
out(['success' => true, 'id' => $id, 'linked' => $status === 'new']);
default:
+3 -2
View File
@@ -1219,7 +1219,8 @@ $jsonFlags = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_
$('#nlChecks').innerHTML = lastChecks.map(c =>
'<div class="' + c[0] + '"><i class="fas ' + (c[0] === 'error' ? 'fa-circle-xmark' : 'fa-triangle-exclamation') + ' me-1"></i>' + esc(c[1]) + '</div>').join('');
} catch (e) {
/* l'anteprima riprova alla prossima modifica */ }
/* l'anteprima riprova alla prossima modifica */
}
}
$$('.nl-device button').forEach(b => b.addEventListener('click', () => {
@@ -1333,7 +1334,7 @@ $jsonFlags = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_
const t = $('[data-k="' + fileInput.dataset.upload + '"]', root);
t.value = j.url;
t.dispatchEvent(new Event('input'));
info.textContent = 'Caricata: ' + j.width + '×' + j.height + ' px, ' + j.kb + ' KB';
info.textContent = 'Caricata: ' + j.width + '×' + j.height + ' px, ' + j.kb + ' KB' + (j.note ? ' – ' + j.note : '');
} catch (e) {
info.textContent = '';
Swal.fire('Immagine non caricata', e.message || 'Errore di rete.', 'error');