getConnection(); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); /** Permesso richiesto: cambialo se crei un permesso dedicato (es. 'kiosk.manage'). */ const KIOSK_PERM = 'production.programming.view'; const KIOSK_IMG_DIR = __DIR__ . '/photos/kiosk/'; function kOut(array $a) { while (ob_get_level()) ob_end_clean(); header('Content-Type: application/json; charset=utf-8'); echo json_encode($a); exit; } if (!userCan(KIOSK_PERM)) { if (isset($_GET['action']) || isset($_POST['action'])) kOut(['success' => false, 'message' => 'Non hai i permessi per questa pagina']); while (ob_get_level()) ob_end_clean(); http_response_code(403); echo '

Accesso non consentito

Torna alla Dashboard
'; exit; } $userName = ''; if (!empty($iduserlogin)) { try { $st = $pdo->prepare("SELECT COALESCE(NULLIF(TRIM(CONCAT_WS(' ', first_name, last_name)), ''), username, email) FROM auth_users WHERE id = ?"); $st->execute([(int)$iduserlogin]); $userName = (string)$st->fetchColumn(); } catch (Throwable $e) { } } function kDate($v) { $v = trim((string)$v); return preg_match('/^\d{4}-\d{2}-\d{2}$/', $v) ? $v : null; } /** Salva l'immagine caricata (ridotta a 1920 px se serve) e restituisce il nome del file. */ function kSaveImage(array $f) { if (($f['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) return null; if ($f['error'] !== UPLOAD_ERR_OK) throw new RuntimeException('Immagine non caricata (errore ' . $f['error'] . ')'); if ($f['size'] > 15 * 1024 * 1024) throw new RuntimeException('Immagine troppo grande (massimo 15 MB)'); $info = @getimagesize($f['tmp_name']); $ext = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp', 'image/gif' => 'gif'][$info['mime'] ?? ''] ?? null; if (!$ext) throw new RuntimeException('Formato non supportato: usa JPG, PNG o WEBP'); if (!is_dir(KIOSK_IMG_DIR) && !mkdir(KIOSK_IMG_DIR, 0775, true)) throw new RuntimeException('Impossibile creare la cartella photos/kiosk'); $name = date('Ymd_His') . '_' . bin2hex(random_bytes(4)); // riduco le foto grandi (dal telefono) a 1920 px: lo schermo non ne ha bisogno di più if (function_exists('imagecreatefromstring') && max($info[0], $info[1]) > 1920 && $ext !== 'gif') { $src = @imagecreatefromstring(file_get_contents($f['tmp_name'])); if ($src) { $r = 1920 / max($info[0], $info[1]); $w = (int)round($info[0] * $r); $h = (int)round($info[1] * $r); $dst = imagecreatetruecolor($w, $h); imagecopyresampled($dst, $src, 0, 0, 0, 0, $w, $h, $info[0], $info[1]); imagejpeg($dst, KIOSK_IMG_DIR . $name . '.jpg', 82); imagedestroy($src); imagedestroy($dst); return $name . '.jpg'; } } if (!move_uploaded_file($f['tmp_name'], KIOSK_IMG_DIR . $name . '.' . $ext)) throw new RuntimeException('Impossibile salvare l\'immagine'); return $name . '.' . $ext; } function kDeleteImage($name) { $name = basename((string)$name); if ($name !== '' && is_file(KIOSK_IMG_DIR . $name)) @unlink(KIOSK_IMG_DIR . $name); } function kioskUrl(PDO $pdo) { $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'; $dir = rtrim(str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])), '/'); return ($https ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $dir . '/kiosk.php?k=' . Kiosk::token($pdo); } /* ===================== AJAX ===================== */ $action = $_GET['action'] ?? $_POST['action'] ?? ''; if ($action !== '') { try { if ($action === 'load') { $s = Kiosk::settings($pdo); unset($s['token']); $goals = Kiosk::goals($pdo, false); kOut([ 'success' => true, 'url' => kioskUrl($pdo), 'settings' => $s, 'safety' => Kiosk::safety(Kiosk::settings($pdo)), 'items' => Kiosk::items($pdo, false), 'goals' => $goals, 'slides' => Kiosk::SLIDES, 'types' => Kiosk::ITEM_TYPES, 'sources' => Kiosk::GOAL_SOURCES, 'today' => Kiosk::day()->format('Y-m-d'), ]); } if ($action === 'save_settings') { $slides = array_values(array_intersect(array_keys(Kiosk::SLIDES), (array)($_POST['slides'] ?? []))); $vals = [ 'company' => mb_substr(trim((string)($_POST['company'] ?? '')), 0, 60), 'slide_seconds' => (string)max(6, min(120, (int)($_POST['slide_seconds'] ?? 15))), 'slides' => implode(',', $slides), 'slide_durations' => json_encode((object)array_filter(array_map('intval', array_intersect_key((array)($_POST['dur'] ?? []), Kiosk::SLIDES)), fn($v) => $v > 0)), 'scarto_target' => (string)max(0, (float)str_replace(',', '.', (string)($_POST['scarto_target'] ?? 3))), 'city' => mb_substr(trim((string)($_POST['city'] ?? '')), 0, 60), 'lat' => is_numeric($_POST['lat'] ?? '') ? (string)(float)$_POST['lat'] : '', 'lon' => is_numeric($_POST['lon'] ?? '') ? (string)(float)$_POST['lon'] : '', 'last_incident' => (string)kDate($_POST['last_incident'] ?? ''), 'record_days' => ($_POST['record_days'] ?? '') === '' ? '' : (string)max(0, (int)$_POST['record_days']), 'safety_note' => mb_substr(trim((string)($_POST['safety_note'] ?? '')), 0, 250), ]; foreach ($vals as $k => $v) Kiosk::set($pdo, $k, $v); kOut(['success' => true]); } if ($action === 'new_token') { Kiosk::token($pdo, true); kOut(['success' => true, 'url' => kioskUrl($pdo)]); } if ($action === 'item_save') { $id = (int)($_POST['id'] ?? 0); $type = (string)($_POST['item_type'] ?? ''); if (!isset(Kiosk::ITEM_TYPES[$type])) throw new RuntimeException('Scegli il tipo di contenuto'); $title = mb_substr(trim((string)($_POST['title'] ?? '')), 0, 150); if ($title === '') throw new RuntimeException('Scrivi il titolo'); $from = kDate($_POST['valid_from'] ?? ''); $to = kDate($_POST['valid_to'] ?? ''); if ($from && $to && $to < $from) throw new RuntimeException('La data di fine è prima di quella di inizio'); $old = ''; if ($id) { $st = $pdo->prepare("SELECT image FROM kiosk_items WHERE id = ?"); $st->execute([$id]); $old = $st->fetchColumn(); if ($old === false) throw new RuntimeException('Contenuto non trovato'); } $img = $old; $new = kSaveImage($_FILES['image'] ?? ['error' => UPLOAD_ERR_NO_FILE]); if ($new !== null || !empty($_POST['remove_image'])) { kDeleteImage($old); $img = $new ?? ''; } $f = [ 'item_type' => $type, 'title' => $title, 'body' => trim((string)($_POST['body'] ?? '')), 'image' => $img, 'valid_from' => $from, 'valid_to' => $to, 'sort' => (int)($_POST['sort'] ?? 0), 'active' => !empty($_POST['active']) ? 1 : 0, ]; if ($id) { $f['id'] = $id; $pdo->prepare("UPDATE kiosk_items SET item_type = :item_type, title = :title, body = :body, image = :image, valid_from = :valid_from, valid_to = :valid_to, sort = :sort, active = :active, updated_at = NOW() WHERE id = :id")->execute($f); } else { $f['by'] = mb_substr($userName, 0, 100); $pdo->prepare("INSERT INTO kiosk_items (item_type, title, body, image, valid_from, valid_to, sort, active, created_by, created_at, updated_at) VALUES (:item_type, :title, :body, :image, :valid_from, :valid_to, :sort, :active, :by, NOW(), NOW())")->execute($f); } kOut(['success' => true]); } if ($action === 'item_toggle') { $pdo->prepare("UPDATE kiosk_items SET active = 1 - active, updated_at = NOW() WHERE id = ?")->execute([(int)($_POST['id'] ?? 0)]); kOut(['success' => true]); } if ($action === 'item_delete') { $id = (int)($_POST['id'] ?? 0); $st = $pdo->prepare("SELECT image FROM kiosk_items WHERE id = ?"); $st->execute([$id]); kDeleteImage($st->fetchColumn()); $pdo->prepare("DELETE FROM kiosk_items WHERE id = ?")->execute([$id]); kOut(['success' => true]); } if ($action === 'goal_save') { $id = (int)($_POST['id'] ?? 0); $src = (string)($_POST['source'] ?? 'manuale'); if (!isset(Kiosk::GOAL_SOURCES[$src])) $src = 'manuale'; $title = mb_substr(trim((string)($_POST['title'] ?? '')), 0, 150); if ($title === '') throw new RuntimeException('Scrivi il nome dell\'obiettivo'); $target = (float)str_replace(',', '.', (string)($_POST['target'] ?? 0)); if ($target <= 0) throw new RuntimeException('Indica il valore da raggiungere'); $from = kDate($_POST['period_from'] ?? ''); $to = kDate($_POST['period_to'] ?? ''); if ($from && $to && $to < $from) throw new RuntimeException('La data di fine è prima di quella di inizio'); $unit = mb_substr(trim((string)($_POST['unit'] ?? '')), 0, 20); if ($src !== 'manuale') $unit = Kiosk::GOAL_SOURCES[$src][1]; $cur = ($_POST['current_value'] ?? '') === '' ? null : (float)str_replace(',', '.', (string)$_POST['current_value']); $f = [ 'title' => $title, 'source' => $src, 'unit' => $unit, 'target' => $target, 'cur' => $src === 'manuale' ? $cur : null, 'lower' => $src === 'manuale' ? (!empty($_POST['lower_is_better']) ? 1 : 0) : (Kiosk::GOAL_SOURCES[$src][2] ? 1 : 0), 'pf' => $from, 'pt' => $to, 'sort' => (int)($_POST['sort'] ?? 0), 'active' => !empty($_POST['active']) ? 1 : 0, ]; if ($id) { $f['id'] = $id; $pdo->prepare("UPDATE kiosk_goals SET title = :title, source = :source, unit = :unit, target = :target, current_value = :cur, lower_is_better = :lower, period_from = :pf, period_to = :pt, sort = :sort, active = :active, updated_at = NOW() WHERE id = :id")->execute($f); } else { $pdo->prepare("INSERT INTO kiosk_goals (title, source, unit, target, current_value, lower_is_better, period_from, period_to, sort, active, updated_at) VALUES (:title, :source, :unit, :target, :cur, :lower, :pf, :pt, :sort, :active, NOW())")->execute($f); } kOut(['success' => true]); } if ($action === 'goal_delete') { $pdo->prepare("DELETE FROM kiosk_goals WHERE id = ?")->execute([(int)($_POST['id'] ?? 0)]); kOut(['success' => true]); } kOut(['success' => false, 'message' => 'Azione sconosciuta']); } catch (Throwable $e) { kOut(['success' => false, 'message' => $e->getMessage()]); } } ?> Schermo di reparto - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?>
Schermo di reparto
Cosa vedono i dipendenti in sala pausa: contenuti, obiettivi e sicurezza
Apri lo schermo
Contenuti
Obiettivi
Anteprima dal vivo
Apri il link sul PC o sul TV della sala pausa e mettilo a schermo intero (F11). Nessun login: chi ha il link vede lo schermo.
Sicurezza
—
giorni senza infortuni
Impostazioni
Secondi vuoti = durata predefinita. Le slide con più contenuti (es. 3 comunicazioni) usano la durata per ciascuno.