Fonctionnalités principales : 1. Marchés hybrides - Onglet Mercurial - Ajout onglet Mercurial avec style distinct (vert, gras, blanc) - Affichage des produits mercuriaux pour marchés hybrides - Filtrage automatique des produits "Hors Marché 999" - Documentation Phase 2 avec CAS 1 et CAS 2 de marchés hybrides - Règles métier pour validation différenciée (devis 100% mercurial vs mixte) 2. Corrections bugs - Fix flag chkChange sur onglet "Sélection Produits" (callback asynchrone) - Plus d'alerte intempestive après sauvegarde des produits 3. Outils de déploiement - Nouveau script deploy-file.sh pour déploiement unitaire (DEV/PROD) - Amélioration deploy-cleo.sh 4. Gestion multi-contacts (v2.0.3) - Contrôleur AJAX cjxcontacts.php - Script migration clients_contacts - Documentation complète 5. Documentation - Mise à jour TODO.md avec Phase 2 marchés hybrides - Mise à jour README.md v2.0.3 - Ajout RULES.md - Ajout migration_clients_contacts.sql 6. Nettoyage - Suppression fichiers obsolètes (conf_new.php, conf_old.php, uof_linet_20250911.sql) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
358 lines
10 KiB
PHP
358 lines
10 KiB
PHP
<?php
|
|
setlocale(LC_ALL, 'fr', 'fr_FR', 'french', 'fra', 'fra_FRA', 'fr_FR.ISO_8859-1', 'fra_FRA.ISO_8859-1', 'fr_FR.utf8', 'fr_FR.utf-8', 'fra_FRA.utf8', 'fra_FRA.utf-8');
|
|
|
|
require_once dirname(__DIR__, 3) . '/config/Database.php';
|
|
|
|
$today = date("Y-m-d H:i:s");
|
|
$dateFr = date("d/m/Y");
|
|
$dateTimeFr = date("d/m/Y H:i:s");
|
|
$timeFr = date("H:i:s");
|
|
|
|
$jour = array("Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi");
|
|
$jour_abr = array("Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam");
|
|
$mois = array("", "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre");
|
|
$mois_abr = array("", "Jan", "Fév", "Mar", "Avr", "Mai", "Jui", "Jul", "Aoû", "Sep", "Oct", "Nov", "Déc");
|
|
|
|
// La fonction getinfos() est maintenant définie dans config/Database.php
|
|
// pour éviter les conflits de redéclaration
|
|
|
|
// La fonction qSQL() est maintenant définie dans config/Database.php
|
|
// pour éviter les conflits de redéclaration
|
|
|
|
// Fonction logstats simplifiée (la table z_stats n'existe pas encore)
|
|
function logstats($delay = 0, $fk_user = 0, $appname = "") {
|
|
// Pour l'instant, on ne fait rien
|
|
// TODO: Créer la table z_stats si besoin de statistiques
|
|
return true;
|
|
}
|
|
|
|
function affiche_date($ladate) {
|
|
// Retourne une date MySQL yyyy-mm-dd HH:ii:ss au format dd/mm/yyyy
|
|
$ladate = trim($ladate);
|
|
if ($ladate == "" || substr($ladate, 0, 2) == "00") {
|
|
return "";
|
|
} else {
|
|
if (strlen($ladate) < 10) {
|
|
return "";
|
|
} else {
|
|
$theday = substr($ladate, 8, 2) . "/" . substr($ladate, 5, 2) . "/" . substr($ladate, 0, 4);
|
|
return $theday;
|
|
}
|
|
}
|
|
}
|
|
|
|
function d6GetDate($laDate, $transform = "MF", $hours = false, $seconds = false) {
|
|
// Retourne une date
|
|
// $transform="MF" du format MySQL yyyy-mm-dd au format Fr dd/mm/yyyy
|
|
// $transform="FM" du format Fr dd/mm/yyyy au format MySQL yyyy-mm-dd
|
|
|
|
$ret = "";
|
|
if (strlen($laDate) >= 10) {
|
|
if ($transform == "FM") {
|
|
$ret = substr($laDate, -4) . "-" . substr($laDate, 3, 2) . "-" . substr($laDate, 0, 2);
|
|
} else {
|
|
$ret = substr($laDate, -2) . "/" . substr($laDate, 5, 2) . "/" . substr($laDate, 0, 4);
|
|
}
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
function hashPsswd($p) {
|
|
$options = [
|
|
'cost' => 11,
|
|
];
|
|
$psswd = password_hash($p, PASSWORD_BCRYPT, $options);
|
|
return $psswd;
|
|
}
|
|
|
|
function checkPsswd($p, $pCr) {
|
|
// Vérification du MDP saisi par l'utilisateur
|
|
// $p : le pass en clair, $pCr : le pass enregistré et hashé
|
|
return password_verify($p, $pCr);
|
|
}
|
|
|
|
function createPsswd($id, $p, $dbgen = "gen") {
|
|
global $Conf;
|
|
global $Route;
|
|
|
|
$psswd = hashPsswd($p);
|
|
|
|
try {
|
|
$db = Database::getInstance();
|
|
|
|
$table = $Conf->_tbusers ?? 'users';
|
|
$sql = "UPDATE $table SET password = :password WHERE rowid = :id";
|
|
|
|
$result = $db->query($sql, ['password' => $psswd, 'id' => $id]);
|
|
|
|
if ($result instanceof PDOStatement && $result->rowCount() > 0) {
|
|
eLog(0, "Changement de mot de passe réussi pour l'utilisateur ID: $id");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
} catch (Exception $e) {
|
|
error_log("Erreur createPsswd: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function vPassword($p, $hashed) {
|
|
if (password_verify($p, $hashed)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function nettoie_chaine($input) {
|
|
if (is_null($input)) {
|
|
$input = "";
|
|
}
|
|
$res = trim(str_replace("'", "'", $input));
|
|
$res = trim(str_replace('"', """, $res));
|
|
$res = str_replace('<', '<', $res);
|
|
$res = str_replace('>', '>', $res);
|
|
return $res;
|
|
}
|
|
|
|
function nettoie_input($input) {
|
|
if (is_null($input)) {
|
|
$input = "";
|
|
}
|
|
$input = trim($input);
|
|
$input = stripslashes($input);
|
|
$input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
|
|
return $input;
|
|
}
|
|
|
|
function nettoie($input) {
|
|
if (is_null($input)) {
|
|
$input = "";
|
|
}
|
|
$res = trim($input);
|
|
$res = str_replace("'", "'", $res);
|
|
$res = str_replace('"', '"', $res);
|
|
$res = str_replace('<', '', $res);
|
|
$res = str_replace('>', '', $res);
|
|
return $res;
|
|
}
|
|
|
|
function dateEN($date) {
|
|
if ($date == '') return '';
|
|
if (strpos($date, '/') !== false) {
|
|
list($jour, $mois, $annee) = explode('/', $date);
|
|
return $annee . '-' . $mois . '-' . $jour;
|
|
}
|
|
return $date;
|
|
}
|
|
|
|
function dateFR($date) {
|
|
if ($date == '' || $date == '0000-00-00') return '';
|
|
if (strpos($date, '-') !== false) {
|
|
list($annee, $mois, $jour) = explode('-', substr($date, 0, 10));
|
|
return $jour . '/' . $mois . '/' . $annee;
|
|
}
|
|
return $date;
|
|
}
|
|
|
|
function datetimeFR($datetime) {
|
|
if ($datetime == '' || $datetime == '0000-00-00 00:00:00') return '';
|
|
list($date, $time) = explode(' ', $datetime);
|
|
return dateFR($date) . ' ' . substr($time, 0, 5);
|
|
}
|
|
|
|
function eLog($user = 0, $comment = "", $notif = false) {
|
|
global $Conf;
|
|
global $Session;
|
|
global $Route;
|
|
|
|
if ($comment == "") return;
|
|
|
|
$script = isset($Route->_script) ? $Route->_script : "";
|
|
$dt = date("Y-m-d H:i:s");
|
|
|
|
if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
|
|
$ip = $_SERVER["HTTP_CLIENT_IP"];
|
|
} elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
|
|
$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
|
|
} else {
|
|
$ip = $_SERVER["REMOTE_ADDR"];
|
|
}
|
|
|
|
$hn = getHostByName($ip);
|
|
$ha = @getHostByAddr($hn);
|
|
$us = substr($_SERVER["HTTP_USER_AGENT"] ?? '', 0, 100);
|
|
|
|
if (isset($Session->_user["rowid"])) {
|
|
$user = $Session->_user["rowid"];
|
|
if ($user == "") {
|
|
$user = 0;
|
|
}
|
|
}
|
|
|
|
$appname = isset($Conf->_appname) ? $Conf->_appname : '';
|
|
|
|
try {
|
|
$db = Database::getInstance();
|
|
|
|
$sql = "INSERT INTO z_logs (fk_user, script, user_agent, http_host, ip_client, appname, commentaire, date_histo, notif)
|
|
VALUES (:user, :script, :user_agent, :host, :ip, :appname, :comment, :date, :notif)";
|
|
|
|
$params = [
|
|
'user' => $user,
|
|
'script' => $script,
|
|
'user_agent' => $us,
|
|
'host' => $ha,
|
|
'ip' => $ip,
|
|
'appname' => $appname,
|
|
'comment' => $comment,
|
|
'date' => $dt,
|
|
'notif' => $notif ? 1 : 0
|
|
];
|
|
|
|
$db->query($sql, $params);
|
|
|
|
} catch (Exception $e) {
|
|
error_log("Erreur eLog: " . $e->getMessage());
|
|
}
|
|
|
|
if (strpos(strtolower($comment), 'erreur') !== false) {
|
|
error_log($dt . ";" . $ip . ";" . $script . ";" . $comment . "\r\n", 3, "./" . $Conf->_appname . ".log");
|
|
}
|
|
}
|
|
|
|
function debug($data, $type = 'DEBUG', $level = 3) {
|
|
global $Conf;
|
|
|
|
if (!isset($Conf)) return;
|
|
|
|
if (method_exists($Conf, 'debug')) {
|
|
$Conf->debug($data, $type, $level);
|
|
} else {
|
|
if ($Conf->_debug_level >= $level) {
|
|
$timestamp = date('Y-m-d H:i:s');
|
|
$message = "[$timestamp] [$type] " . (is_array($data) ? json_encode($data) : $data);
|
|
error_log($message);
|
|
}
|
|
}
|
|
}
|
|
|
|
function timeStart() {
|
|
return microtime(true);
|
|
}
|
|
|
|
function timeEnd($start, $label = '') {
|
|
$end = microtime(true);
|
|
$time = round(($end - $start) * 1000, 2);
|
|
|
|
global $Conf;
|
|
if (isset($Conf) && $Conf->_log_performance) {
|
|
debug("Performance [$label]: {$time}ms", 'PERFORMANCE', 3);
|
|
}
|
|
|
|
return $time;
|
|
}
|
|
|
|
function loadtel($numero, $prefix = "+33") {
|
|
$lenumero = trim($numero);
|
|
$lenumero = preg_replace('/[^0-9]/', '', $lenumero);
|
|
if (strlen($lenumero) == 10) {
|
|
$lenumero = substr($lenumero, 1);
|
|
}
|
|
if (strlen($lenumero) == 9) {
|
|
$lenumero = $prefix . $lenumero;
|
|
}
|
|
return $lenumero;
|
|
}
|
|
|
|
function formattel($numero, $separateur = " ") {
|
|
if (strlen($numero) == 9) {
|
|
$numero = "0" . $numero;
|
|
}
|
|
if (strlen($numero) == 10) {
|
|
$numero = substr($numero, 0, 2) . $separateur . substr($numero, 2, 2) . $separateur . substr($numero, 4, 2) . $separateur . substr($numero, 6, 2) . $separateur . substr($numero, 8, 2);
|
|
}
|
|
return $numero;
|
|
}
|
|
|
|
function str_normalize($string, $minuscules = true) {
|
|
$result = "";
|
|
$string = trim($string);
|
|
if (strlen($string) > 0) {
|
|
if ($minuscules) {
|
|
$result = strtolower($string);
|
|
} else {
|
|
$result = $string;
|
|
}
|
|
$result = str_replace(" ", "_", $result);
|
|
$result = str_replace("é", "e", $result);
|
|
$result = str_replace("è", "e", $result);
|
|
$result = str_replace("ê", "e", $result);
|
|
$result = str_replace("ë", "e", $result);
|
|
$result = str_replace("à", "a", $result);
|
|
$result = str_replace("â", "a", $result);
|
|
$result = str_replace("ä", "a", $result);
|
|
$result = str_replace("ô", "o", $result);
|
|
$result = str_replace("ö", "o", $result);
|
|
$result = str_replace("ù", "u", $result);
|
|
$result = str_replace("û", "u", $result);
|
|
$result = str_replace("ü", "u", $result);
|
|
$result = str_replace("ç", "c", $result);
|
|
$result = str_replace("'", "", $result);
|
|
$result = str_replace("\"", "", $result);
|
|
$result = str_replace("/", "", $result);
|
|
$result = str_replace("(", "_", $result);
|
|
$result = str_replace(")", "_", $result);
|
|
$result = str_replace("!", "_", $result);
|
|
$result = str_replace("?", "_", $result);
|
|
$result = trim($result);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
function generateRandomPassword() {
|
|
$password = '';
|
|
$desired_length = rand(8, 12);
|
|
|
|
for ($length = 0; $length < $desired_length; $length++) {
|
|
$password .= chr(rand(44, 122));
|
|
}
|
|
$password = str_replace("/", "&", $password);
|
|
$password = str_replace("<", "!", $password);
|
|
$password = str_replace(">", "!", $password);
|
|
$password = str_replace("=", "#", $password);
|
|
$password = str_replace("\\", "&", $password);
|
|
$password = str_replace("^", "%", $password);
|
|
$password = str_replace(chr(96), "#", $password);
|
|
|
|
return $password;
|
|
}
|
|
|
|
function purge_old_logs($log_dir, $app_name, $days_to_keep = 10) {
|
|
if (!is_dir($log_dir)) {
|
|
return;
|
|
}
|
|
|
|
$date_limit = strtotime("-{$days_to_keep} days");
|
|
|
|
$patterns = array(
|
|
$app_name . '_????-??-??.log',
|
|
$app_name . '_debug_????-??-??.log'
|
|
);
|
|
|
|
foreach ($patterns as $pattern) {
|
|
$files = glob($log_dir . $pattern);
|
|
if ($files) {
|
|
foreach ($files as $file) {
|
|
if (preg_match('/(\d{4}-\d{2}-\d{2})\.log$/', $file, $matches)) {
|
|
$file_date = strtotime($matches[1]);
|
|
if ($file_date < $date_limit) {
|
|
@unlink($file);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |