input->getInt('id', 0); $this->setState('circolare.id', $id); $categoria_id = $app->input->getInt('categoria_id', 0); $this->setState('filter.categoria_id', $categoria_id); parent::populateState(); } public function getItem($pk = null) { $pk = (int) ($pk ?: $this->getState('circolare.id')); if ($pk <= 0) { return null; } $db = $this->getDatabase(); $q = $db->getQuery(true) ->select('c.*') ->from($db->quoteName('#__circolari') . ' AS c') ->where('c.id = ' . (int) $pk); // Se esistono le colonne, applica filtri $cols = array_change_key_case( $db->getTableColumns($db->replacePrefix('#__circolari'), false), CASE_LOWER ); // categoria_id (se impostato e colonna esiste) $categoria_id = (int) $this->getState('filter.categoria_id', 0); if ($categoria_id > 0 && isset($cols['categoria_id'])) { $q->where('c.categoria_id = ' . $categoria_id); } // stato (se colonna esiste) if (isset($cols['state'])) { $q->where('COALESCE(c.state, 1) = 1'); } $db->setQuery($q); $row = $db->loadObject(); return $row ?: null; } public function getBottoniFirma(int $firmatipoId): array { $buttons = []; // <- sempre inizializzato if ($firmatipoId <= 0) { return $buttons; } $db = Factory::getContainer()->get('DatabaseDriver'); // 1) Tabella relazionale $q = $db->getQuery(true) ->select($db->quoteName(['id', 'label'])) ->from($db->quoteName('#__circolari_firmetipi_bottoni')) ->where($db->quoteName('firmatipo_id') . ' = ' . (int) $firmatipoId) ->order($db->escape('ordering ASC, id ASC')); $db->setQuery($q); $buttons = (array) $db->loadObjectList(); // 2) Fallback JSON if (!$buttons) { $q2 = $db->getQuery(true) ->select($db->quoteName('bottoni_firma')) ->from($db->quoteName('#__circolari_firmetipi')) ->where($db->quoteName('id') . ' = ' . (int) $firmatipoId); $db->setQuery($q2); $json = (string) $db->loadResult(); if ($json) { $rows = json_decode($json, true) ?: []; foreach ($rows as $r) { $label = trim((string) ($r['etichetta'] ?? '')); if ($label !== '') { $buttons[] = (object) ['id' => 0, 'label' => $label]; } } } } return $buttons; } public function userCanFirmare(int $circolareId, ?int $userId = null): bool { $user = Factory::getUser($userId); if ($user->guest) { return false; } // Gruppi (inclusa ereditarietà). In J4/J5 puoi usare UserHelper::getUserGroups $userGroups = array_map('intval', UserHelper::getUserGroups($user->id)); if (!$userGroups) { return false; } $db = Factory::getContainer()->get('DatabaseDriver'); $q = $db->getQuery(true) ->select('COUNT(*)') ->from($db->quoteName('#__circolari_usergroups')) ->where($db->quoteName('circolare_id') . ' = ' . (int) $circolareId) ->where($db->quoteName('usergroup_id') . ' IN (' . implode(',', $userGroups) . ')'); $db->setQuery($q); return ((int) $db->loadResult()) > 0; } public function bottoneValidoPerCircolare(int $circolareId, int $bottoneId): bool { $db = Factory::getContainer()->get('DatabaseDriver'); $q = $db->getQuery(true) ->select('1') ->from($db->quoteName('#__circolari', 'c')) ->join('INNER', $db->quoteName('#__circolari_firmetipi_bottoni', 'b') . ' ON ' . $db->quoteName('b.firmatipo_id') . ' = ' . $db->quoteName('c.tipologia_firma_id')) ->where($db->quoteName('c.id') . ' = ' . (int) $circolareId) ->where($db->quoteName('b.id') . ' = ' . (int) $bottoneId); $db->setQuery($q, 0, 1); return (bool) $db->loadResult(); } public function insertFirma(int $circolareId, int $userId, int $bottoneId): bool { $db = Factory::getContainer()->get('DatabaseDriver'); $now = Factory::getDate()->toSql(); $ip = $_SERVER['REMOTE_ADDR'] ?? ''; $columns = ['circolare_id', 'user_id', 'firmatipo_bottone_id', 'signed_at', 'ip']; $values = [ (int) $circolareId, (int) $userId, (int) $bottoneId, $db->quote($now), $db->quote($ip) ]; $q = $db->getQuery(true) ->insert($db->quoteName('#__circolari_firme')) ->columns($db->quoteName($columns)) ->values(implode(',', $values)); try { $db->setQuery($q)->execute(); } catch (\RuntimeException $e) { // 1062 = duplicate key (violazione UNIQUE circolare_id+user_id) if ((int) $e->getCode() === 1062 || stripos($e->getMessage(), 'Duplicate') !== false) { throw new \RuntimeException(Text::_('COM_CIRCOLARI_ERR_ALREADY_SIGNED'), 409); } throw $e; } return true; } public function getFirmaUtente(int $circolareId, int $userId): ?object { if ($circolareId <= 0 || $userId <= 0) { return null; } $db = Factory::getContainer()->get('DatabaseDriver'); // Schema compatibile con entrambe le versioni (ENUM "firma" oppure nuovo schema con firmatipo_bottone_id/firma_label) $q = $db->getQuery(true) ->select($db->quoteName(['id', 'circolare_id', 'user_id'])) ->select('' . $db->quoteName('firmatipo_bottone_id') . ' AS firmatipo_bottone_id') ->select('' . $db->quoteName('firma_label') . ' AS firma_label') //->select('' . $db->quoteName('firma') . ' AS firma_enum') ->from($db->quoteName('#__circolari_firme')) ->where($db->quoteName('circolare_id') . ' = ' . (int)$circolareId) ->where($db->quoteName('user_id') . ' = ' . (int)$userId); $db->setQuery($q, 0, 1); $row = $db->loadObject(); return $row ?: null; } public function getFirmeCircolare(int $circolareId): array { if ($circolareId <= 0) { return []; } $db = Factory::getContainer()->get('DatabaseDriver'); // Rileva le colonne della tabella firme (schema nuovo vs enum) $cols = array_change_key_case($db->getTableColumns('#__circolari_firme', false)); $hasBtn = isset($cols['firmatipo_bottone_id']); $hasLabel = isset($cols['firma_label']); $hasEnum = isset($cols['firma']); $q = $db->getQuery(true) ->select([ 'f.id', 'f.circolare_id', 'f.user_id', 'f.data_firma', ($hasLabel ? 'f.firma_label' : 'NULL') . ' AS firma_label', ($hasEnum ? 'f.firma' : 'NULL') . ' AS firma_enum' ]) ->from($db->quoteName('#__circolari_firme', 'f')) ->join('INNER', $db->quoteName('#__users', 'u') . ' ON u.id = f.user_id') ->select('u.name AS user_name, u.username, u.email') ->where('f.circolare_id = ' . (int) $circolareId) ->order('f.data_firma DESC'); if ($hasBtn) { $q->select('b.label AS bottone_label') ->join('LEFT', $db->quoteName('#__circolari_firmetipi_bottoni', 'b') . ' ON b.id = f.firmatipo_bottone_id'); } else { $q->select('NULL AS bottone_label'); } $db->setQuery($q); $rows = (array) $db->loadAssocList(); // Post-processing: normalizza la label scelta foreach ($rows as &$r) { $label = $r['firma_label'] ?: $r['bottone_label'] ?: $r['firma_enum']; if ($label && $label === $r['firma_enum']) { $label = ucwords(str_replace('_', ' ', $label)); // enum -> "Presa Visione" } $r['scelta_label'] = $label ?: '-'; } unset($r); return $rows; } /** * Incrementa gli hits della circolare. */ public function hit($pk = null) { $pk = $pk ?: (int) $this->getState($this->getName() . '.id'); if (!$pk) { return false; } $db = $this->getDatabase(); $query = $db->getQuery(true) ->update($db->quoteName('#__circolari')) ->set($db->quoteName('hits') . ' = ' . $db->quoteName('hits') . ' + 1') ->where($db->quoteName('id') . ' = ' . (int)$pk); $db->setQuery($query)->execute(); return true; } }