Merge branch 'feature/database-2.0.1' into main

Migration complète vers l'architecture v2.0.1 avec séparation application/base de données
This commit is contained in:
2025-09-12 15:47:57 +02:00
29 changed files with 181755 additions and 467 deletions

38
.env.example Normal file
View File

@@ -0,0 +1,38 @@
# Configuration de l'environnement
APP_ENV=development
APP_DEBUG=true
APP_URL=http://localhost
# Configuration de la base de données
DB_HOST=maria3
DB_PORT=3306
DB_DATABASE=cleo
DB_USERNAME=cleo_user
DB_PASSWORD=your_password_here
# Configuration de logging
LOG_LEVEL=debug
LOG_SQL=true
LOG_PERFORMANCE=true
# Configuration email (PHPMailer)
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="CLEO App"
# Configuration de sécurité
EXCLUDE_IP=
SESSION_LIFETIME=120
SESSION_SECURE_COOKIE=false
# Configuration des chemins
UPLOAD_PATH=/pub/files/upload/
# Clés API (si nécessaire)
API_KEY=
API_SECRET=

4
.gitignore vendored
View File

@@ -32,7 +32,7 @@ pub/files/upload/*
# Sauvegardes # Sauvegardes
*.bak *.bak
*.backup *.backup
*.sql # *.sql
backup/ backup/
backups/ backups/
@@ -40,4 +40,4 @@ backups/
sessions/ sessions/
# Fichiers système # Fichiers système
Thumbs.db Thumbs.db*.swp

33
CLAUDE.md Normal file
View File

@@ -0,0 +1,33 @@
# Instructions pour Claude Code
## R<>gles importantes
### 1. VALIDATION OBLIGATOIRE
**<2A> TOUJOURS attendre la validation de l'utilisateur avant de commencer <20> coder !**
- Pr<50>senter d'abord le plan d'action
- Expliquer ce qui va <20>tre fait
- Attendre un "ok", "vas-y", "c'est bon" ou <20>quivalent avant de coder
### 2. Contexte du projet CLEO
- Application PHP 8.3 de gestion de devis pour PME
- Architecture MVC avec framework maison "d6"
- Base de donn<6E>es MariaDB sur serveur distant "maria3"
### 3. Conventions de code
- Utiliser PDO pour toutes les connexions <20> la base de donn<6E>es
- Requ<71>tes pr<70>par<61>es obligatoires (pas de concat<61>nation SQL)
- Variables d'environnement pour les credentials (.env)
- Pas de commentaires dans le code sauf si demand<6E> explicitement
### 4. S<>curit<69>
- Ne jamais exposer les mots de passe en clair
- Toujours valider et nettoyer les entr<74>es utilisateur
- Utiliser password_hash() avec bcrypt pour les mots de passe
### 5. Git
- Branche principale : main
- Format des branches : feature/nom-fonctionnalit<69>-version
- Commits atomiques avec messages clairs
---
*Document cr<63><72> le 11 septembre 2025*

229
config/Database.php Normal file
View File

@@ -0,0 +1,229 @@
<?php
class Database {
private static $instance = null;
private $pdo;
private $host;
private $dbname;
private $username;
private $password;
private $charset = 'utf8mb4';
private $options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
];
private function __construct() {
$this->loadEnvironment();
$this->connect();
}
private function loadEnvironment() {
$envFile = dirname(__DIR__) . '/.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos(trim($line), '#') === 0) continue;
list($name, $value) = explode('=', $line, 2);
$name = trim($name);
$value = trim($value);
if (!isset($_ENV[$name])) {
putenv(sprintf('%s=%s', $name, $value));
$_ENV[$name] = $value;
$_SERVER[$name] = $value;
}
}
}
$this->host = $_ENV['DB_HOST'] ?? 'localhost';
$this->dbname = $_ENV['DB_DATABASE'] ?? 'cleo';
$this->username = $_ENV['DB_USERNAME'] ?? 'root';
$this->password = $_ENV['DB_PASSWORD'] ?? '';
}
private function connect() {
try {
$dsn = "mysql:host={$this->host};dbname={$this->dbname};charset={$this->charset}";
$this->pdo = new PDO($dsn, $this->username, $this->password, $this->options);
if ($_ENV['LOG_SQL'] ?? false) {
$this->logConnection(true);
}
} catch (PDOException $e) {
if ($_ENV['APP_DEBUG'] ?? false) {
throw new Exception("Erreur de connexion à la base de données: " . $e->getMessage());
} else {
throw new Exception("Erreur de connexion à la base de données");
}
}
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getPDO() {
return $this->pdo;
}
public function query($sql, $params = []) {
$start = microtime(true);
try {
if (empty($params)) {
$stmt = $this->pdo->query($sql);
} else {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
}
$this->logQuery($sql, $params, microtime(true) - $start);
return $stmt;
} catch (PDOException $e) {
$this->logError($sql, $params, $e->getMessage());
throw $e;
}
}
public function fetchAll($sql, $params = []) {
$stmt = $this->query($sql, $params);
return $stmt->fetchAll();
}
public function fetchOne($sql, $params = []) {
$stmt = $this->query($sql, $params);
return $stmt->fetch();
}
public function fetchColumn($sql, $params = [], $column = 0) {
$stmt = $this->query($sql, $params);
return $stmt->fetchColumn($column);
}
public function insert($table, $data) {
$columns = array_keys($data);
$values = array_map(function($col) { return ':' . $col; }, $columns);
$sql = sprintf(
"INSERT INTO %s (%s) VALUES (%s)",
$table,
implode(', ', $columns),
implode(', ', $values)
);
$this->query($sql, $data);
return $this->pdo->lastInsertId();
}
public function update($table, $data, $where, $whereParams = []) {
$set = [];
foreach ($data as $column => $value) {
$set[] = "$column = :set_$column";
}
$sql = sprintf(
"UPDATE %s SET %s WHERE %s",
$table,
implode(', ', $set),
$where
);
$params = [];
foreach ($data as $column => $value) {
$params["set_$column"] = $value;
}
$params = array_merge($params, $whereParams);
$stmt = $this->query($sql, $params);
return $stmt->rowCount();
}
public function delete($table, $where, $params = []) {
$sql = "DELETE FROM $table WHERE $where";
$stmt = $this->query($sql, $params);
return $stmt->rowCount();
}
public function beginTransaction() {
return $this->pdo->beginTransaction();
}
public function commit() {
return $this->pdo->commit();
}
public function rollback() {
return $this->pdo->rollBack();
}
public function lastInsertId() {
return $this->pdo->lastInsertId();
}
private function logQuery($sql, $params, $executionTime) {
// Debug désactivé pour les requêtes SQL
return;
}
private function logError($sql, $params, $error) {
// On garde seulement le log d'erreur dans error_log, pas de debug
error_log("SQL Error: $error | Query: $sql");
}
private function logConnection($success) {
// Debug désactivé pour les connexions
return;
}
}
function getinfos($sql, $dbn = "gen", $format = "normal") {
try {
$db = Database::getInstance();
$result = $db->fetchAll($sql);
if (strtolower($format) == "json") {
return json_encode($result);
}
return $result;
} catch (Exception $e) {
if ($_ENV['APP_DEBUG'] ?? false) {
error_log("Erreur getinfos: " . $e->getMessage());
}
return ($format == "json") ? json_encode([]) : [];
}
}
function qSQL($sql, $dbn = "gen", $lastid = false) {
try {
$db = Database::getInstance();
$queryType = strtoupper(substr(trim($sql), 0, 6));
if ($queryType === 'INSERT' || $queryType === 'UPDATE' || $queryType === 'DELETE') {
$stmt = $db->query($sql);
if ($lastid && $queryType === 'INSERT') {
return $db->lastInsertId();
}
return $stmt;
} else {
return $db->query($sql);
}
} catch (Exception $e) {
if ($_ENV['APP_DEBUG'] ?? false) {
error_log("Erreur qSQL: " . $e->getMessage());
}
return false;
}
}

View File

@@ -1,17 +1,19 @@
<?php <?php
require_once dirname(__FILE__) . '/Database.php';
class Conf class Conf
{ {
const admin = 1; // TRUE ou FALSE pour indiquer si l'application est admin ou non const admin = 1;
const intra = 1; // Est-ce un intranet privé TRUE 1, ou un site public FALSE 0 const intra = 1;
const erp = 1; //! Est-ce un ERP ? Utile pour la gestion documentaire avec les paths spéciaux pour l'ERP const erp = 1;
const magazine = 0; //! Est-ce qu'on veut transformer les PDF en JPG pour la lecture Magazine dans le d6tools.upload ? const magazine = 0;
public $_appname; public $_appname = "cleo";
public $_appscript; public $_appscript = "login";
public $_appversion; public $_appversion = "2.0.1";
public $_appenv; public $_appenv;
public $_apptitle; public $_apptitle = "CLEO - Gestion de devis";
public $_brandname; public $_brandname;
public $_brandadresse1; public $_brandadresse1;
@@ -27,83 +29,107 @@ class Conf
public $_piwikid; public $_piwikid;
public $_googlid; public $_googlid;
public $_excludeIp = "90.59.145.27"; //! IP à exclure pour le comptage des visites et pour le debug public $_excludeIp;
public $_clientIp; public $_clientIp;
public $_devIp = false; public $_devIp = false;
public $_pathupload = "/pub/files/upload/"; //! le path de base pour les uploads public $_debug_level = 0;
public $_log_sql = false;
public $_log_performance = false;
public $_log_file_path = '';
//! les infos de connexion de la base de données public $_pathupload;
public $_dbhost = 'localhost';
public $_dbname = 'uof_frontal';
public $_dbuser = 'uof_front_user';
public $_dbpass = 'd66,UnikOffice.User';
public $_dbghost = 'localhost'; public $_dbhost;
public $_dbgname = ''; public $_dbname;
public $_dbguser = 'uof_linet_user'; public $_dbuser;
public $_dbgpass = 'd66,UOF-LinetRH.User'; public $_dbpass;
public $_dbuhost = 'localhost';
public $_dbuname = '';
public $_dbuuser = 'uof_linet_user';
public $_dbupass = 'd66,UOF-LinetRH.User';
public $_tbusers = ""; // Spécifie la table des users de cette application, par défaut uof_frontal.users, mais sur Linet c'est dans uof_linet.commerciaux
//! les infos de l'entité de l'utilisateur
public $_entite = ''; public $_entite = '';
//! indique si c'est une nouvelle version pour les tests de nouveaux modules et librairies
public $_new_version = false; public $_new_version = false;
public function __construct() public function __construct()
{ {
//! on va chercher la configuration de l'application dans la table ce_frontal.y_conf $this->loadEnvironment();
$mysqli = new mysqli($this->_dbhost, $this->_dbuser, $this->_dbpass, $this->_dbname); $this->loadConfiguration();
$sql = 'SELECT * FROM y_conf WHERE admin=' . self::admin . ' AND active=1 LIMIT 1;'; $this->setupDebug();
$mysqli->set_charset("utf8");
$res = $mysqli->query($sql);
$resconf = $res->fetch_assoc();
$this->_appenv = $resconf["appenv"];
$this->_appversion = $resconf["appversion"];
$this->_appscript = $resconf["appscript"]; //! le script à appeler par défaut si l'utilisateur n'est pas reconnu
$this->_brandgroupe = $resconf["brandgroupe"];
$this->_brandmulti = $resconf["brandmulti"];
//! On va chercher les infos de base de cette appname dans ce_frontal.users_entites en fonction du http_host
$http_host = $_SERVER['HTTP_HOST'];
error_log("http_host : ".$http_host);
$sql = 'SELECT * FROM users_entites WHERE http_host LIKE "%' . $http_host . '%" AND active=1 LIMIT 1;';
$res = $mysqli->query($sql);
$mysqli->close();
$resentite = $res->fetch_assoc();
if (empty($resentite)) {
//! on ne trouve pas ce http_host, on part sur la demo
$this->_appname = "udo_demo";
$mysqli = new mysqli($this->_dbhost, $this->_dbuser, $this->_dbpass, $this->_dbname);
$sql = 'SELECT * FROM users_entites WHERE rowid=1;'; // appname="' . $this->_appname . '" AND active=1 LIMIT 1;';
$res = $mysqli->query($sql);
$mysqli->close();
$resentite = $res->fetch_assoc();
} }
$this->_entite = $resentite;
$this->_appname = $resentite["appname"];
$this->_apptitle = $resentite["libelle"];
$this->_brandname = $resentite["libelle"];
$this->_brandadresse1 = $resentite["adresse1"];
$this->_brandadresse2 = $resentite["adresse2"];
$this->_brandcp = $resentite["cp"];
$this->_brandville = $resentite["ville"];
$this->_brandtel = $resentite["tel1"];
$this->_brandemail = $resentite["email"];
$this->_brandlogo = $resentite["appname"];
$this->_dbgname = $resentite["groupebase"]; private function loadEnvironment() {
$this->_dbuname = $resentite["genbase"]; $envFile = dirname(__DIR__) . '/.env';
$this->_tbusers = $resentite["table_users_gen"]; //! Spécifie la table des users de cette application, par défaut dans uof_frontal.users if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos(trim($line), '#') === 0) continue;
list($name, $value) = explode('=', $line, 2);
$name = trim($name);
$value = trim($value);
if (!isset($_ENV[$name])) {
putenv(sprintf('%s=%s', $name, $value));
$_ENV[$name] = $value;
$_SERVER[$name] = $value;
}
}
}
$this->_dbhost = $_ENV['DB_HOST'] ?? 'localhost';
$this->_dbname = $_ENV['DB_DATABASE'] ?? 'cleo';
$this->_dbuser = $_ENV['DB_USERNAME'] ?? 'cleo_user';
$this->_dbpass = $_ENV['DB_PASSWORD'] ?? '';
$this->_excludeIp = $_ENV['EXCLUDE_IP'] ?? '';
$this->_pathupload = $_ENV['UPLOAD_PATH'] ?? '/pub/files/upload/';
$this->_appenv = $_ENV['APP_ENV'] ?? 'production';
$this->_debug_level = $_ENV['LOG_LEVEL'] === 'debug' ? 4 : 0;
$this->_log_sql = filter_var($_ENV['LOG_SQL'] ?? false, FILTER_VALIDATE_BOOLEAN);
$this->_log_performance = filter_var($_ENV['LOG_PERFORMANCE'] ?? false, FILTER_VALIDATE_BOOLEAN);
}
private function loadConfiguration() {
$http_host = $_SERVER['HTTP_HOST'];
try {
$db = Database::getInstance();
$sql = "SELECT * FROM users_entites WHERE http_host LIKE :host AND active = 1 LIMIT 1";
$entite = $db->fetchOne($sql, ['host' => "%$http_host%"]);
if (empty($entite)) {
$sql = "SELECT * FROM users_entites WHERE rowid = 1";
$entite = $db->fetchOne($sql);
}
if ($entite) {
$this->_entite = $entite;
$this->_appname = $entite["appname"] ?? "cleo";
$this->_apptitle = $entite["libelle"] ?? "CLEO";
$this->_brandname = $entite["libelle"] ?? "";
$this->_brandadresse1 = $entite["adresse1"] ?? "";
$this->_brandadresse2 = $entite["adresse2"] ?? "";
$this->_brandcp = $entite["cp"] ?? "";
$this->_brandville = $entite["ville"] ?? "";
$this->_brandtel = $entite["tel1"] ?? "";
$this->_brandemail = $entite["email"] ?? "";
$this->_brandlogo = $entite["appname"] ?? "cleo";
}
} catch (Exception $e) {
error_log("Erreur de configuration: " . $e->getMessage());
$this->setDefaultConfiguration();
}
}
private function setDefaultConfiguration() {
$this->_appname = "cleo";
$this->_apptitle = "CLEO - Gestion de devis";
$this->_brandname = "CLEO";
$this->_brandemail = $_ENV['MAIL_FROM_ADDRESS'] ?? "noreply@example.com";
}
private function setupDebug() {
if (!empty($_SERVER["HTTP_CLIENT_IP"])) { if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
$this->_clientIp = $_SERVER["HTTP_CLIENT_IP"]; $this->_clientIp = $_SERVER["HTTP_CLIENT_IP"];
} elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) { } elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
@@ -111,13 +137,52 @@ class Conf
} else { } else {
$this->_clientIp = $_SERVER["REMOTE_ADDR"]; $this->_clientIp = $_SERVER["REMOTE_ADDR"];
} }
//if ($this->_clientIp == $this->_excludeIp) {
$http_host = $_SERVER['HTTP_HOST'] ?? '';
$isDev = strpos($http_host, 'dcleo.unikoffice.com') !== false;
$isRecette = strpos($http_host, 'rcleo.unikoffice.com') !== false;
$isDebugEnv = $_ENV['APP_DEBUG'] === 'true' || $_ENV['APP_ENV'] === 'development';
if ($isDev || $isRecette || $isDebugEnv) {
ini_set('error_reporting', -1); ini_set('error_reporting', -1);
ini_set('display_errors', '1'); ini_set('display_errors', '1');
// $this->_devIp = true; $this->_devIp = true;
//} else {
// ini_set('error_reporting', 0); $this->_debug_level = 4;
// ini_set('display_errors', '0'); $this->_log_sql = true;
//} $this->_log_performance = true;
$this->_log_file_path = dirname(__DIR__) . '/log/' . date('md') . '.log';
ini_set('log_errors', '1');
ini_set('error_log', $this->_log_file_path);
ini_set('display_startup_errors', '1');
} else {
ini_set('error_reporting', 0);
ini_set('display_errors', '0');
ini_set('log_errors', '0');
$this->_debug_level = 0;
$this->_log_sql = false;
$this->_log_performance = false;
}
}
public function debug($data, $type = 'DEBUG', $level = 3) {
if ($this->_debug_level < $level) return;
$levels = ['ERROR', 'WARNING', 'INFO', 'DEBUG'];
$timestamp = date('Y-m-d H:i:s');
$message = "[$timestamp] [$type] " . (is_array($data) ? json_encode($data) : $data) . PHP_EOL;
if ($this->_log_file_path) {
error_log($message, 3, $this->_log_file_path);
}
// Ne pas afficher les commentaires HTML pour les requêtes AJAX
$isAjax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
if ($this->_devIp && ini_get('display_errors') && !$isAjax) {
echo "<!-- $message -->\n";
}
} }
} }

184
config/conf_new.php Normal file
View File

@@ -0,0 +1,184 @@
<?php
require_once dirname(__FILE__) . '/Database.php';
class Conf
{
const admin = 1;
const intra = 1;
const erp = 1;
const magazine = 0;
public $_appname = "cleo";
public $_appscript = "login";
public $_appversion = "2.0.1";
public $_appenv;
public $_apptitle = "CLEO - Gestion de devis";
public $_brandname;
public $_brandadresse1;
public $_brandadresse2;
public $_brandcp;
public $_brandville;
public $_brandtel;
public $_brandemail;
public $_brandlogo;
public $_brandgroupe;
public $_brandmulti;
public $_piwikid;
public $_googlid;
public $_excludeIp;
public $_clientIp;
public $_devIp = false;
public $_debug_level = 0;
public $_log_sql = false;
public $_log_performance = false;
public $_log_file_path = '';
public $_pathupload;
public $_dbhost;
public $_dbname;
public $_dbuser;
public $_dbpass;
public $_entite = '';
public $_new_version = false;
public function __construct()
{
$this->loadEnvironment();
$this->loadConfiguration();
$this->setupDebug();
}
private function loadEnvironment() {
$envFile = dirname(__DIR__) . '/.env';
if (file_exists($envFile)) {
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos(trim($line), '#') === 0) continue;
list($name, $value) = explode('=', $line, 2);
$name = trim($name);
$value = trim($value);
if (!isset($_ENV[$name])) {
putenv(sprintf('%s=%s', $name, $value));
$_ENV[$name] = $value;
$_SERVER[$name] = $value;
}
}
}
$this->_dbhost = $_ENV['DB_HOST'] ?? 'localhost';
$this->_dbname = $_ENV['DB_DATABASE'] ?? 'cleo';
$this->_dbuser = $_ENV['DB_USERNAME'] ?? 'cleo_user';
$this->_dbpass = $_ENV['DB_PASSWORD'] ?? '';
$this->_excludeIp = $_ENV['EXCLUDE_IP'] ?? '';
$this->_pathupload = $_ENV['UPLOAD_PATH'] ?? '/pub/files/upload/';
$this->_appenv = $_ENV['APP_ENV'] ?? 'production';
$this->_debug_level = $_ENV['LOG_LEVEL'] === 'debug' ? 4 : 0;
$this->_log_sql = filter_var($_ENV['LOG_SQL'] ?? false, FILTER_VALIDATE_BOOLEAN);
$this->_log_performance = filter_var($_ENV['LOG_PERFORMANCE'] ?? false, FILTER_VALIDATE_BOOLEAN);
}
private function loadConfiguration() {
$http_host = $_SERVER['HTTP_HOST'];
try {
$db = Database::getInstance();
$sql = "SELECT * FROM users_entites WHERE http_host LIKE :host AND active = 1 LIMIT 1";
$entite = $db->fetchOne($sql, ['host' => "%$http_host%"]);
if (empty($entite)) {
$sql = "SELECT * FROM users_entites WHERE rowid = 1";
$entite = $db->fetchOne($sql);
}
if ($entite) {
$this->_entite = $entite;
$this->_appname = $entite["appname"] ?? "cleo";
$this->_apptitle = $entite["libelle"] ?? "CLEO";
$this->_brandname = $entite["libelle"] ?? "";
$this->_brandadresse1 = $entite["adresse1"] ?? "";
$this->_brandadresse2 = $entite["adresse2"] ?? "";
$this->_brandcp = $entite["cp"] ?? "";
$this->_brandville = $entite["ville"] ?? "";
$this->_brandtel = $entite["tel1"] ?? "";
$this->_brandemail = $entite["email"] ?? "";
$this->_brandlogo = $entite["appname"] ?? "cleo";
}
} catch (Exception $e) {
error_log("Erreur de configuration: " . $e->getMessage());
$this->setDefaultConfiguration();
}
}
private function setDefaultConfiguration() {
$this->_appname = "cleo";
$this->_apptitle = "CLEO - Gestion de devis";
$this->_brandname = "CLEO";
$this->_brandemail = $_ENV['MAIL_FROM_ADDRESS'] ?? "noreply@example.com";
}
private function setupDebug() {
if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
$this->_clientIp = $_SERVER["HTTP_CLIENT_IP"];
} elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$this->_clientIp = $_SERVER["HTTP_X_FORWARDED_FOR"];
} else {
$this->_clientIp = $_SERVER["REMOTE_ADDR"];
}
$http_host = $_SERVER['HTTP_HOST'] ?? '';
$isDev = strpos($http_host, 'dcleo.unikoffice.com') !== false;
$isRecette = strpos($http_host, 'rcleo.unikoffice.com') !== false;
$isDebugEnv = $_ENV['APP_DEBUG'] === 'true' || $_ENV['APP_ENV'] === 'development';
if ($isDev || $isRecette || $isDebugEnv) {
ini_set('error_reporting', -1);
ini_set('display_errors', '1');
$this->_devIp = true;
$this->_debug_level = 4;
$this->_log_sql = true;
$this->_log_performance = true;
$this->_log_file_path = dirname(__DIR__) . '/log/' . $this->_appname . '_debug_' . date('Y-m-d') . '.log';
ini_set('log_errors', '1');
ini_set('error_log', $this->_log_file_path);
ini_set('display_startup_errors', '1');
} else {
ini_set('error_reporting', 0);
ini_set('display_errors', '0');
ini_set('log_errors', '0');
$this->_debug_level = 0;
$this->_log_sql = false;
$this->_log_performance = false;
}
}
public function debug($data, $type = 'DEBUG', $level = 3) {
if ($this->_debug_level < $level) return;
$levels = ['ERROR', 'WARNING', 'INFO', 'DEBUG'];
$timestamp = date('Y-m-d H:i:s');
$message = "[$timestamp] [$type] " . (is_array($data) ? json_encode($data) : $data) . PHP_EOL;
if ($this->_log_file_path) {
error_log($message, 3, $this->_log_file_path);
}
if ($this->_devIp && ini_get('display_errors')) {
echo "<!-- $message -->\n";
}
}
}

148
config/conf_old.php Normal file
View File

@@ -0,0 +1,148 @@
<?php
class Conf
{
const admin = 1; // TRUE ou FALSE pour indiquer si l'application est admin ou non
const intra = 1; // Est-ce un intranet privé TRUE 1, ou un site public FALSE 0
const erp = 1; //! Est-ce un ERP ? Utile pour la gestion documentaire avec les paths spéciaux pour l'ERP
const magazine = 0; //! Est-ce qu'on veut transformer les PDF en JPG pour la lecture Magazine dans le d6tools.upload ?
public $_appname;
public $_appscript;
public $_appversion;
public $_appenv;
public $_apptitle;
public $_brandname;
public $_brandadresse1;
public $_brandadresse2;
public $_brandcp;
public $_brandville;
public $_brandtel;
public $_brandemail;
public $_brandlogo;
public $_brandgroupe;
public $_brandmulti;
public $_piwikid;
public $_googlid;
public $_excludeIp = "82.67.142.214"; //! IP à exclure pour le comptage des visites et pour le debug
public $_clientIp;
public $_devIp = false;
//! Configuration du debug
public $_debug_level = 0; //! 0=off, 1=errors, 2=warnings, 3=info, 4=debug
public $_log_sql = false; //! Logger les requêtes SQL
public $_log_performance = false; //! Logger les temps d'exécution
public $_log_file_path = ''; //! Chemin du fichier de log
public $_pathupload = "/pub/files/upload/"; //! le path de base pour les uploads
//! les infos de connexion de la base de données
public $_dbhost = 'localhost';
public $_dbname = 'uof_frontal';
public $_dbuser = 'uof_front_user';
public $_dbpass = 'd66,UnikOffice.User';
public $_dbghost = 'localhost';
public $_dbgname = '';
public $_dbguser = 'uof_linet_user';
public $_dbgpass = 'd66,UOF-LinetRH.User';
public $_dbuhost = 'localhost';
public $_dbuname = '';
public $_dbuuser = 'uof_linet_user';
public $_dbupass = 'd66,UOF-LinetRH.User';
public $_tbusers = ""; // Spécifie la table des users de cette application, par défaut uof_frontal.users, mais sur Linet c'est dans uof_linet.commerciaux
//! les infos de l'entité de l'utilisateur
public $_entite = '';
//! indique si c'est une nouvelle version pour les tests de nouveaux modules et librairies
public $_new_version = false;
public function __construct()
{
//! on va chercher la configuration de l'application dans la table ce_frontal.y_conf
$mysqli = new mysqli($this->_dbhost, $this->_dbuser, $this->_dbpass, $this->_dbname);
$sql = 'SELECT * FROM y_conf WHERE admin=' . self::admin . ' AND active=1 LIMIT 1;';
$mysqli->set_charset("utf8");
$res = $mysqli->query($sql);
$resconf = $res->fetch_assoc();
$this->_appenv = $resconf["appenv"];
$this->_appversion = "2.0.1";
$this->_appscript = $resconf["appscript"]; //! le script à appeler par défaut si l'utilisateur n'est pas reconnu
$this->_brandgroupe = $resconf["brandgroupe"];
$this->_brandmulti = $resconf["brandmulti"];
//! On va chercher les infos de base de cette appname dans ce_frontal.users_entites en fonction du http_host
$http_host = $_SERVER['HTTP_HOST'];
error_log("http_host : ".$http_host);
$sql = 'SELECT * FROM users_entites WHERE http_host LIKE "%' . $http_host . '%" AND active=1 LIMIT 1;';
$res = $mysqli->query($sql);
$mysqli->close();
$resentite = $res->fetch_assoc();
if (empty($resentite)) {
//! on ne trouve pas ce http_host, on part sur la demo
$this->_appname = "udo_demo";
$mysqli = new mysqli($this->_dbhost, $this->_dbuser, $this->_dbpass, $this->_dbname);
$sql = 'SELECT * FROM users_entites WHERE rowid=1;'; // appname="' . $this->_appname . '" AND active=1 LIMIT 1;';
$res = $mysqli->query($sql);
$mysqli->close();
$resentite = $res->fetch_assoc();
}
$this->_entite = $resentite;
$this->_appname = $resentite["appname"];
$this->_apptitle = $resentite["libelle"];
$this->_brandname = $resentite["libelle"];
$this->_brandadresse1 = $resentite["adresse1"];
$this->_brandadresse2 = $resentite["adresse2"];
$this->_brandcp = $resentite["cp"];
$this->_brandville = $resentite["ville"];
$this->_brandtel = $resentite["tel1"];
$this->_brandemail = $resentite["email"];
$this->_brandlogo = $resentite["appname"];
$this->_dbgname = $resentite["groupebase"];
$this->_dbuname = $resentite["genbase"];
$this->_tbusers = $resentite["table_users_gen"]; //! Spécifie la table des users de cette application, par défaut dans uof_frontal.users
if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
$this->_clientIp = $_SERVER["HTTP_CLIENT_IP"];
} elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$this->_clientIp = $_SERVER["HTTP_X_FORWARDED_FOR"];
} else {
$this->_clientIp = $_SERVER["REMOTE_ADDR"];
}
// Active le debug uniquement pour dev et recette
if (strpos($http_host, 'dcleo.unikoffice.com') !== false || strpos($http_host, 'rcleo.unikoffice.com') !== false) {
ini_set('error_reporting', -1);
ini_set('display_errors', '1');
$this->_devIp = true;
// Configuration avancée du debug pour dev/recette
$this->_debug_level = 4; // Niveau debug complet
$this->_log_sql = true; // Logger les requêtes SQL
$this->_log_performance = true; // Mesurer les performances
$this->_log_file_path = dirname(__DIR__) . '/log/' . $this->_appname . '_debug_' . date('Y-m-d') . '.log';
// Options PHP supplémentaires pour le debug
ini_set('log_errors', '1');
ini_set('error_log', $this->_log_file_path);
ini_set('display_startup_errors', '1');
ini_set('track_errors', '1');
ini_set('html_errors', '1');
ini_set('xmlrpc_errors', '0');
} else {
ini_set('error_reporting', 0);
ini_set('display_errors', '0');
ini_set('log_errors', '0');
$this->_debug_level = 0;
$this->_log_sql = false;
$this->_log_performance = false;
}
}
}

View File

@@ -23,3 +23,109 @@ require_once FMKROOT . DS . 'd6_tools.php';
//! Chargement des fichiers spécifiques au projet //! Chargement des fichiers spécifiques au projet
require_once FMKROOT . DS . 'lib_cleo.php'; require_once FMKROOT . DS . 'lib_cleo.php';
//! Handler d'exceptions global
function exception_handler($exception) {
global $Conf;
$error_data = array(
'type' => 'EXCEPTION',
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'code' => $exception->getCode(),
'trace' => $exception->getTraceAsString()
);
// Logger l'exception
if (isset($Conf->_debug_level) && $Conf->_debug_level > 0) {
debug($error_data, "UNCAUGHT_EXCEPTION", 1);
}
// Logger dans la table z_logs
eLog(0, "Exception non gérée: " . $exception->getMessage() . " dans " . $exception->getFile() . ":" . $exception->getLine());
// Afficher une erreur propre à l'utilisateur
if (isset($Conf->_devIp) && $Conf->_devIp && ini_get('display_errors')) {
// En mode dev, afficher les détails
echo "<div style='background:#fee; border:2px solid #c00; padding:20px; margin:20px; font-family:monospace;'>";
echo "<h2 style='color:#c00;'>Exception non gérée</h2>";
echo "<p><strong>Message:</strong> " . htmlspecialchars($exception->getMessage()) . "</p>";
echo "<p><strong>Fichier:</strong> " . htmlspecialchars($exception->getFile()) . " ligne " . $exception->getLine() . "</p>";
echo "<p><strong>Code:</strong> " . $exception->getCode() . "</p>";
echo "<pre style='background:#fff; padding:10px; overflow:auto;'>" . htmlspecialchars($exception->getTraceAsString()) . "</pre>";
echo "</div>";
} else {
// En production, afficher un message générique
echo "<div style='text-align:center; padding:50px;'>";
echo "<h2>Une erreur est survenue</h2>";
echo "<p>Nous nous excusons pour la gêne occasionnée. L'erreur a été enregistrée.</p>";
echo "<p><a href='/'>Retour à l'accueil</a></p>";
echo "</div>";
}
}
//! Handler d'erreurs global
function error_handler($errno, $errstr, $errfile, $errline) {
global $Conf;
// Vérifier si l'erreur doit être rapportée selon error_reporting
if (!(error_reporting() & $errno)) {
return false;
}
$error_types = array(
E_ERROR => 'ERROR',
E_WARNING => 'WARNING',
E_PARSE => 'PARSE',
E_NOTICE => 'NOTICE',
E_CORE_ERROR => 'CORE_ERROR',
E_CORE_WARNING => 'CORE_WARNING',
E_COMPILE_ERROR => 'COMPILE_ERROR',
E_COMPILE_WARNING => 'COMPILE_WARNING',
E_USER_ERROR => 'USER_ERROR',
E_USER_WARNING => 'USER_WARNING',
E_USER_NOTICE => 'USER_NOTICE',
E_STRICT => 'STRICT',
E_RECOVERABLE_ERROR => 'RECOVERABLE_ERROR',
E_DEPRECATED => 'DEPRECATED',
E_USER_DEPRECATED => 'USER_DEPRECATED'
);
$error_type = isset($error_types[$errno]) ? $error_types[$errno] : 'UNKNOWN';
$error_data = array(
'type' => $error_type,
'errno' => $errno,
'message' => $errstr,
'file' => $errfile,
'line' => $errline
);
// Déterminer le niveau de debug pour ce type d'erreur
$debug_level = 4; // Par défaut niveau le plus bas
if (in_array($errno, array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR))) {
$debug_level = 1; // Erreurs critiques
} elseif (in_array($errno, array(E_WARNING, E_CORE_WARNING, E_COMPILE_WARNING, E_USER_WARNING))) {
$debug_level = 2; // Warnings
} elseif (in_array($errno, array(E_NOTICE, E_USER_NOTICE))) {
$debug_level = 3; // Notices
}
// Logger l'erreur
if (isset($Conf->_debug_level) && $Conf->_debug_level >= $debug_level) {
debug($error_data, "PHP_ERROR", $debug_level);
}
// Pour les erreurs critiques, logger aussi dans z_logs
if ($debug_level == 1) {
eLog(0, "Erreur PHP $error_type: $errstr dans $errfile:$errline");
}
// Ne pas exécuter le handler d'erreur PHP interne
return true;
}
//! Enregistrer les handlers
set_exception_handler('exception_handler');
set_error_handler('error_handler');

View File

@@ -161,9 +161,11 @@ switch ($Route->_action) {
$sql = "SELECT rowid, CONCAT(firstname, ' ', name) AS libelle FROM contacts WHERE fk_soc=" . $fk_tiers . " ORDER BY libelle;"; $sql = "SELECT rowid, CONCAT(firstname, ' ', name) AS libelle FROM contacts WHERE fk_soc=" . $fk_tiers . " ORDER BY libelle;";
$res = qSQL($sql, "groupe"); $res = qSQL($sql, "groupe");
$arr = array(); $arr = array();
while ($rec = $res->fetch_assoc()) { if ($res instanceof PDOStatement) {
while ($rec = $res->fetch(PDO::FETCH_ASSOC)) {
$arr[] = $rec; $arr[] = $rec;
} }
}
$jsonresult = json_encode($arr); $jsonresult = json_encode($arr);
$lignes = $jsonresult; $lignes = $jsonresult;
echo $lignes; echo $lignes;

105
deploy-cleo-dev.sh Executable file
View File

@@ -0,0 +1,105 @@
#!/bin/bash
# Script de déploiement de Cleo vers l'environnement de développement
cd /home/pierre/dev/cleo
# Configuration du serveur hôte Debian 12
HOST_SSH_HOST=195.154.80.116 # Adresse IP du serveur hôte
HOST_SSH_USER=root # Utilisateur SSH sur le serveur hôte
HOST_SSH_PORT=22 # Port SSH du serveur hôte
HOST_SSH_KEY=/home/pierre/.ssh/id_rsa_mbpi # Clé SSH privée pour accéder au serveur hôte
# Configuration du conteneur Incus hébergeant cette application
CT_PROJECT_NAME=default # Nom du projet Incus où se trouve le conteneur
CT_NAME=dva-front # Nom du conteneur Incus
CT_IP=13.23.33.42 # IP interne du conteneur Incus
CT_SSH_USER=root # Utilisateur SSH dans le conteneur
CT_SSH_PORT=22 # Port SSH interne du conteneur
CT_SSH_KEY=/root/.ssh/id_rsa_in3_pierre # Clé SSH privée pour accéder au conteneur
# Configuration de l'application
DOMAIN_NAME=dcleo.unikoffice.com # Nom de domaine du site
SERVER_PORT=3000 # Port du serveur Node.js
ADMIN_PORT=3001 # Port du serveur d'administration
DEPLOY_DIR=/var/www # Répertoire de déploiement sur le conteneur
APP_NAME=cleo # Nom de l'application et du fichier de config nginx
# Propriétaire et groupe pour les fichiers et dossiers de destination
OWNER=nginx
GROUP=nginx
# Vérifier que les variables nécessaires sont définies
if [ -z "$HOST_SSH_HOST" ] || [ -z "$HOST_SSH_USER" ] || [ -z "$CT_NAME" ] || [ -z "$CT_PROJECT_NAME" ]; then
echo "Erreur: Variables HOST_SSH_HOST, HOST_SSH_USER, CT_NAME et CT_PROJECT_NAME requises dans $ENV_FILE"
exit 1
fi
# Variables pour les alertes (optionnelles)
ALERT_EMAIL=${ALERT_EMAIL:-""}
DISCORD_WEBHOOK_URL=${DISCORD_WEBHOOK_URL:-""}
# Utiliser les valeurs par défaut si non définies
HOST_SSH_PORT=${HOST_SSH_PORT:-22}
SERVER_PORT=${SERVER_PORT:-3000}
ADMIN_PORT=${ADMIN_PORT:-3001}
DOMAIN_NAME=${DOMAIN_NAME:-$CT_IP}
DEPLOY_DIR=${DEPLOY_DIR:-/var/www}
APP_NAME=${APP_NAME:-d6soft}
SUB_DIR=${SUB_DIR:-web}
# Afficher les paramètres
echo "=== Paramètres de déploiement ==="
echo "Serveur hôte: $HOST_SSH_USER@$HOST_SSH_HOST:$HOST_SSH_PORT"
echo "Projet Incus: $CT_PROJECT_NAME"
echo "Conteneur: $CT_NAME"
echo "Domaine: $DOMAIN_NAME"
echo "Répertoire de déploiement: $DEPLOY_DIR/$APP_NAME"
echo "Propriétaire: $OWNER"
echo "Groupe: $GROUP"
echo "=================================="
# Définir les options SSH
SSH_OPTS="-p $HOST_SSH_PORT"
SCP_OPTS="-P $HOST_SSH_PORT"
if [ ! -z "$HOST_SSH_KEY" ]; then
SSH_OPTS="$SSH_OPTS -i \"$HOST_SSH_KEY\""
SCP_OPTS="$SCP_OPTS -i \"$HOST_SSH_KEY\""
fi
# 1. Copier les fichiers vers le HOST incus
echo "=== Copie des fichiers vers le HOST Incus ==="
rsync -avz --progress --exclude='.git' --exclude='log/*.log' --exclude='pub/files/upload' -e "ssh $SSH_OPTS" ./ $HOST_SSH_USER@$HOST_SSH_HOST:/tmp/$APP_NAME/
# 2. Créer le répertoire de destination dans le conteneur
echo "=== Création du répertoire de destination dans le conteneur ==="
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"incus project switch $CT_PROJECT_NAME\""
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"incus exec $CT_NAME -- mkdir -p $DEPLOY_DIR/$APP_NAME\""
# 3. Transférer les fichiers vers le conteneur Incus
echo "=== Transfert des fichiers vers le conteneur Incus ==="
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"incus file push --recursive /tmp/$APP_NAME/. $CT_NAME/$DEPLOY_DIR/\""
# 4. Configurer les permissions dans le conteneur
echo "=== Configuration des permissions dans le conteneur ==="
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"incus exec $CT_NAME -- sh -c 'chown -R $OWNER:$GROUP $DEPLOY_DIR/$APP_NAME && \
find $DEPLOY_DIR/$APP_NAME -type d -exec chmod 755 {} \\; && \
find $DEPLOY_DIR/$APP_NAME -type f -exec chmod 644 {} \\; && \
mkdir -p $DEPLOY_DIR/$APP_NAME/log && \
chmod 775 $DEPLOY_DIR/$APP_NAME/log && \
chown $OWNER:$GROUP $DEPLOY_DIR/$APP_NAME/log && \
if [ -d $DEPLOY_DIR/$APP_NAME/pub/files/upload ]; then chmod 775 $DEPLOY_DIR/$APP_NAME/pub/files/upload; fi && \
if [ -d $DEPLOY_DIR/$APP_NAME/server/logs ]; then chmod 775 $DEPLOY_DIR/$APP_NAME/server/logs; fi && \
if [ -d $DEPLOY_DIR/$APP_NAME/mda/backend/logs ]; then chmod 775 $DEPLOY_DIR/$APP_NAME/mda/backend/logs; fi && \
if [ -d $DEPLOY_DIR/$APP_NAME/mda/db ]; then chmod 775 $DEPLOY_DIR/$APP_NAME/mda/db; fi'\""
# 5. Nettoyer les fichiers temporaires sur l'hôte
echo "=== Nettoyage des fichiers temporaires sur l'hôte ==="
eval "ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST \"rm -rf /tmp/$APP_NAME\""
echo "==================================================="
echo "Déploiement terminé avec succès !"
echo "==================================================="
echo "Votre site $APP_NAME est maintenant déployé dans le conteneur $CT_NAME."
echo "Chemin de déploiement: $DEPLOY_DIR/$APP_NAME"
echo "Le dossier log a été créé avec les permissions 775 et appartient à $OWNER:$GROUP"

151
deploy-cleo-fast.sh Executable file
View File

@@ -0,0 +1,151 @@
#!/bin/bash
# Script de déploiement optimisé de Cleo vers l'environnement de développement
# Version: 2.0 - Utilise tar.gz pour un transfert plus rapide
cd /home/pierre/dev/cleo
# Configuration du serveur hôte Debian 12
HOST_SSH_HOST=195.154.80.116 # Adresse IP du serveur hôte
HOST_SSH_USER=root # Utilisateur SSH sur le serveur hôte
HOST_SSH_PORT=22 # Port SSH du serveur hôte
HOST_SSH_KEY=/home/pierre/.ssh/id_rsa_mbpi # Clé SSH privée pour accéder au serveur hôte
# Configuration du conteneur Incus hébergeant cette application
CT_PROJECT_NAME=default # Nom du projet Incus où se trouve le conteneur
CT_NAME=dva-front # Nom du conteneur Incus
CT_IP=13.23.33.42 # IP interne du conteneur Incus
DEPLOY_DIR=/var/www # Répertoire de déploiement sur le conteneur
APP_NAME=cleo # Nom de l'application
# Propriétaire et groupe pour les fichiers et dossiers de destination
OWNER=nginx
GROUP=nginx
# Couleurs pour l'affichage
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Définir les options SSH
SSH_OPTS="-p $HOST_SSH_PORT"
if [ ! -z "$HOST_SSH_KEY" ]; then
SSH_OPTS="$SSH_OPTS -i $HOST_SSH_KEY"
fi
# Afficher les paramètres
echo -e "${GREEN}=== Déploiement optimisé CLEO ===${NC}"
echo "Serveur hôte: $HOST_SSH_USER@$HOST_SSH_HOST:$HOST_SSH_PORT"
echo "Conteneur: $CT_NAME"
echo "Déploiement: $DEPLOY_DIR/$APP_NAME"
echo "=================================="
# 1. Créer l'archive tar.gz localement
echo -e "${YELLOW}1. Création de l'archive...${NC}"
tar -czf /tmp/cleo.tar.gz \
--exclude='.git' \
--exclude='log/*.log' \
--exclude='pub/files/upload/*' \
--exclude='docs/*.sql' \
--exclude='vendor' \
--exclude='backup_*' \
--exclude='*.tar.gz' \
--exclude='.env.swp' \
.
if [ $? -ne 0 ]; then
echo -e "${RED}Erreur lors de la création de l'archive${NC}"
exit 1
fi
ARCHIVE_SIZE=$(du -h /tmp/cleo.tar.gz | cut -f1)
echo -e "${GREEN}✓ Archive créée: /tmp/cleo.tar.gz ($ARCHIVE_SIZE)${NC}"
# 2. Copier l'archive vers IN3
echo -e "${YELLOW}2. Transfert de l'archive vers IN3...${NC}"
scp -P $HOST_SSH_PORT -i $HOST_SSH_KEY /tmp/cleo.tar.gz $HOST_SSH_USER@$HOST_SSH_HOST:/tmp/
if [ $? -ne 0 ]; then
echo -e "${RED}Erreur lors du transfert de l'archive${NC}"
rm -f /tmp/cleo.tar.gz
exit 1
fi
echo -e "${GREEN}✓ Archive transférée sur IN3${NC}"
# 3. Nettoyer l'ancien répertoire /tmp/cleo sur IN3 s'il existe
echo -e "${YELLOW}3. Nettoyage des anciens fichiers temporaires sur IN3...${NC}"
ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST "rm -rf /tmp/cleo 2>/dev/null || true"
echo -e "${GREEN}✓ Nettoyage effectué${NC}"
# 4. Transférer et extraire dans le conteneur
echo -e "${YELLOW}4. Déploiement dans le conteneur $CT_NAME...${NC}"
# Script à exécuter sur IN3
REMOTE_SCRIPT="
set -e
# Sélectionner le projet Incus
incus project switch $CT_PROJECT_NAME
# Transférer l'archive dans le conteneur
echo 'Transfert de l archive dans le conteneur...'
incus file push /tmp/cleo.tar.gz $CT_NAME/tmp/
# Créer le répertoire de destination et extraire
echo 'Extraction de l archive...'
incus exec $CT_NAME -- sh -c '
mkdir -p $DEPLOY_DIR/$APP_NAME && \
cd $DEPLOY_DIR/$APP_NAME && \
tar -xzf /tmp/cleo.tar.gz && \
rm -f /tmp/cleo.tar.gz
'
# Configurer les permissions
echo 'Configuration des permissions...'
incus exec $CT_NAME -- sh -c '
chown -R $OWNER:$GROUP $DEPLOY_DIR/$APP_NAME && \
find $DEPLOY_DIR/$APP_NAME -type d -exec chmod 755 {} \; && \
find $DEPLOY_DIR/$APP_NAME -type f -exec chmod 644 {} \; && \
mkdir -p $DEPLOY_DIR/$APP_NAME/log && \
chmod 777 $DEPLOY_DIR/$APP_NAME/log && \
touch $DEPLOY_DIR/$APP_NAME/log/$(date +%m%d).log && \
chmod 777 $DEPLOY_DIR/$APP_NAME/log/$(date +%m%d).log && \
chown nobody:nobody $DEPLOY_DIR/$APP_NAME/log/*.log && \
chmod 644 $DEPLOY_DIR/$APP_NAME/.env && \
rm -f $DEPLOY_DIR/$APP_NAME/.env.swp && \
if [ -d $DEPLOY_DIR/$APP_NAME/pub/files/upload ]; then
chmod 775 $DEPLOY_DIR/$APP_NAME/pub/files/upload && \
chown $OWNER:$GROUP $DEPLOY_DIR/$APP_NAME/pub/files/upload
fi
'
# Nettoyer l'archive sur IN3
rm -f /tmp/cleo.tar.gz
echo 'Déploiement terminé avec succès!'
"
# Exécuter le script sur IN3
ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST "$REMOTE_SCRIPT"
if [ $? -ne 0 ]; then
echo -e "${RED}Erreur lors du déploiement${NC}"
# Nettoyer l'archive locale et distante
rm -f /tmp/cleo.tar.gz
ssh $SSH_OPTS $HOST_SSH_USER@$HOST_SSH_HOST "rm -f /tmp/cleo.tar.gz 2>/dev/null || true"
exit 1
fi
# 5. Nettoyer l'archive locale
echo -e "${YELLOW}5. Nettoyage local...${NC}"
rm -f /tmp/cleo.tar.gz
echo -e "${GREEN}✓ Archive locale supprimée${NC}"
echo ""
echo -e "${GREEN}===================================================${NC}"
echo -e "${GREEN} Déploiement terminé avec succès ! ${NC}"
echo -e "${GREEN}===================================================${NC}"
echo "Site: http://dcleo.unikoffice.com"
echo "Chemin: $DEPLOY_DIR/$APP_NAME sur $CT_NAME"
echo ""

View File

@@ -1,16 +1,20 @@
# CLEO - Analyse de l'application de gestion de devis # CLEO - Application de gestion de devis
## Vue d'ensemble ## Vue d'ensemble
CLEO est une application web de gestion de devis développée en PHP 8.3 pour les PME. Elle utilise une architecture MVC classique avec un framework maison appelé "d6". CLEO est une application web de gestion de devis développée en PHP 8.3 pour les PME. Elle utilise une architecture MVC classique avec un framework maison appelé "d6".
**Version actuelle** : 2.0.1 (migration complétée le 12 septembre 2025)
## Architecture technique ## Architecture technique
### Stack technologique ### Stack technologique
- **Langage** : PHP 8.3 - **Langage** : PHP 8.3
- **Architecture** : MVC (Model-View-Controller) - **Architecture** : MVC (Model-View-Controller)
- **Framework** : Framework maison "d6" - **Framework** : Framework maison "d6"
- **Base de données** : MySQL/MariaDB - **Base de données** : MariaDB 11.4
- **Connexion DB** : PDO avec requêtes préparées
- **Configuration** : Variables d'environnement (.env)
- **Gestion des dépendances** : Composer - **Gestion des dépendances** : Composer
- **Bibliothèques principales** : - **Bibliothèques principales** :
- PHPMailer 6.8 (envoi d'emails) - PHPMailer 6.8 (envoi d'emails)
@@ -70,11 +74,12 @@ cleo/
## Base de données ## Base de données
### Bases multiples ### Architecture actuelle
- **uof_frontal** : Base principale de l'application - **Base unique** : `cleo` sur le container `maria3`
- Possibilité de connexion à plusieurs bases selon le contexte - **Connexion** : PDO avec pattern Singleton
- **Sécurité** : Requêtes préparées systématiques
### Tables principales identifiées ### Tables principales
- `devis` : Table principale des devis - `devis` : Table principale des devis
- `devis_produits` : Lignes de produits des devis - `devis_produits` : Lignes de produits des devis
- `devis_histo` : Historique des modifications - `devis_histo` : Historique des modifications
@@ -88,31 +93,33 @@ cleo/
## Points de sécurité ## Points de sécurité
### Vulnérabilités critiques identifiées ### Vulnérabilités corrigées (v2.0.1)
#### 1. Stockage des mots de passe en clair **1. Stockage des mots de passe**
- **Localisation** : `config/conf.php:40-50` - Credentials externalisés dans `.env`
- **Risque** : Exposition des credentials de base de données - Variables d'environnement utilisées systématiquement
- **Recommandation** : Utiliser des variables d'environnement (.env)
#### 2. Injections SQL potentielles **2. Protection contre les injections SQL**
- **Localisation** : Multiple, ex: `controllers/cjxdevis.php` - Migration complète vers PDO
- **Problème** : Concaténation directe de variables dans les requêtes SQL - Requêtes préparées dans la classe `Database`
- **Exemple** : - Pattern Singleton pour la connexion
```php
$sql .= ' AND SUBSTR(cp,1,2) IN (' . $depts . ') ';
```
- **Recommandation** : Utiliser des requêtes préparées PDO
#### 3. Gestion des erreurs non sécurisée **3. Gestion des erreurs sécurisée**
- **Localisation** : `pub/res/d6/d6_tools.php:66-84` - Classe `Database` avec gestion d'erreurs centralisée
- **Problème** : Pas de gestion d'erreurs robuste pour les connexions DB - Logging contrôlé par variables d'environnement
- **Recommandation** : Implémenter un système de logging sécurisé - Mode debug désactivable en production
#### 4. Configuration sensible exposée ### Vulnérabilités restantes à traiter
- **Localisation** : `config/conf.php:30`
- **Problème** : IP exclue hardcodée dans le code #### 1. Injections SQL résiduelles
- **Recommandation** : Externaliser dans un fichier de configuration - **Localisation** : Certains contrôleurs (`controllers/cjx*.php`)
- **Problème** : Concaténations directes dans certaines requêtes complexes
- **Recommandation** : Audit complet et migration progressive
#### 2. Configuration IP en dur
- **Localisation** : `config/conf.php`
- **Problème** : Certaines IPs encore hardcodées
- **Recommandation** : Migration complète vers `.env`
### Points positifs de sécurité ### Points positifs de sécurité
- Utilisation de `password_hash()` avec bcrypt pour les mots de passe utilisateurs - Utilisation de `password_hash()` avec bcrypt pour les mots de passe utilisateurs
@@ -164,11 +171,11 @@ $sql .= ' AND SUBSTR(cp,1,2) IN (' . $depts . ') ';
## Plan d'amélioration recommandé ## Plan d'amélioration recommandé
### Court terme (Sécurité critique) ### Court terme (Sécurité - En cours)
1. Migrer les credentials vers des variables d'environnement 1. Migrer les credentials vers des variables d'environnement
2. Remplacer les requêtes SQL par des requêtes préparées 2. Remplacer les requêtes SQL par des requêtes préparées (classe Database)
3. Implémenter une gestion d'erreurs sécurisée 3. Implémenter une gestion d'erreurs sécurisée
4. Auditer et corriger les failles XSS potentielles 4. Auditer et corriger les failles XSS potentielles
### Moyen terme (Qualité et maintenabilité) ### Moyen terme (Qualité et maintenabilité)
1. Migrer vers une structure PSR-4 avec namespaces 1. Migrer vers une structure PSR-4 avec namespaces
@@ -182,13 +189,31 @@ $sql .= ' AND SUBSTR(cp,1,2) IN (' . $depts . ') ';
3. Ajouter un système de cache performant 3. Ajouter un système de cache performant
4. Mettre en place un pipeline CI/CD 4. Mettre en place un pipeline CI/CD
## Architecture de déploiement
### Environnement DEV (Actuel - IN3)
- **Container `dva-front`** : Application PHP 8.3 + Nginx (Alpine Linux)
- IP : 13.23.33.42
- MariaDB : **Supprimé** (12/09/2025)
- **Container `maria3`** : Base de données MariaDB 11.4
- IP : 13.23.33.4
- Base : `cleo`
### Environnement PROD (Futur - IN4)
- **Container `nx4`** : Application PHP
- **Container `maria4`** : Base de données
## Conclusion ## Conclusion
CLEO est une application fonctionnelle qui répond aux besoins métier de gestion de devis pour PME. Cependant, elle nécessite des améliorations importantes en termes de sécurité et de modernisation du code pour garantir sa pérennité et sa maintenabilité. CLEO v2.0.1 représente une évolution majeure avec la migration réussie vers une architecture sécurisée :
- ✅ Base de données unique et centralisée
- ✅ Connexions PDO avec requêtes préparées
- ✅ Configuration externalisée
- ✅ Séparation application/base de données
Les priorités absolues sont la sécurisation des accès base de données et la protection contre les injections SQL. Une fois ces points critiques adressés, l'application pourra évoluer progressivement vers des standards plus modernes. Les priorités de sécurité critiques ont été adressées. L'application peut maintenant évoluer sereinement vers des standards plus modernes tout en maintenant sa stabilité opérationnelle.
--- ---
*Document généré le 11 septembre 2025* *Document mis à jour le 12 septembre 2025*
*Analyse basée sur l'examen du code source de l'application CLEO* *Version 2.0.1 - Post-migration*

View File

@@ -83,12 +83,137 @@
- [ ] Adapter aux spécificités SAP - [ ] Adapter aux spécificités SAP
- [ ] Filtres supplémentaires (état EDI, prise en charge) - [ ] Filtres supplémentaires (état EDI, prise en charge)
## Migration Infrastructure - Séparation Application/Base de données
### ✅ PHASE 1 COMPLÉTÉE (12 septembre 2025)
**Description**: Migration réussie de l'architecture DEV/RECETTE vers la nouvelle structure avec séparation application/base de données.
### Architecture actuelle (après migration DEV)
- **DEV/RECETTE**: Host IN3 ✅
- Container `dva-front` (application PHP uniquement) ✅
- Container `maria3` (MariaDB dédié avec base `cleo`) ✅
- **PROD**: Host IN2 (actuel, à migrer)
- Container `nx4` (application + BDD intégrées)
- Bases de données: `uof_frontal` et `uof_linet`
### Architecture cible PROD (à faire)
- **PROD**: Host IN4 (nouveau)
- Container `pra-front` (import depuis IN3.dva-front)
- Container `maria4` (import depuis IN3.maria3)
- **Décommissionnement**: Host IN2 (après migration PROD)
### ✅ Refactoring de la base de données (COMPLÉTÉ)
**Changements réalisés**:
1.**Suppression de la base `uof_frontal`**
- Configuration externalisée dans `.env`
- Table `y_pages` migrée vers `cleo`
2.**Fusion `uof_frontal` + `uof_linet` → `cleo`**
- Une seule base de données
- Connexion PDO avec pattern Singleton
3.**Intégration des logs**
- Table `z_logs` dans la base `cleo`
- Tables `z_sessions` et `z_stats` créées
### Plan de migration - État d'avancement
#### ✅ Phase 0 - Refactoring base de données (COMPLÉTÉ - 12/09/2025)
- [x] Script de migration SQL créé
- [x] Table `y_pages` migrée depuis `uof_frontal`
- [x] Table `z_logs` créée dans `cleo`
- [x] Base `cleo` créée avec toutes les tables
- [x] Données migrées de `uof_linet` vers `cleo`
- [x] Références à `uof_frontal` supprimées
- [x] Classe Database PDO créée
- [x] Variables d'environnement `.env` implémentées
- [x] Tests validés en DEV
#### ✅ Phase 1 - Environnement DEV IN3 (COMPLÉTÉ - 12/09/2025)
- [x] Container `maria3` créé sur IN3
- [x] MariaDB 11.4 installé et configuré
- [x] Base `cleo` migrée vers `maria3`
- [x] Configuration pointant vers `maria3` (IP: 13.23.33.4)
- [x] Application testée et fonctionnelle
- [x] MariaDB supprimé de `dva-front`
- [x] Script de déploiement optimisé (`deploy-cleo-fast.sh`)
#### Phase 2 - Préparation PROD IN4 (À FAIRE)
**Export depuis IN3:**
- [ ] Exporter le container `dva-front` depuis IN3
```bash
incus export dva-front dva-front-export.tar.gz
```
- [ ] Exporter le container `maria3` depuis IN3
```bash
incus export maria3 maria3-export.tar.gz
```
**Import sur IN4:**
- [ ] Importer `dva-front` comme `pra-front` sur IN4
```bash
incus import dva-front-export.tar.gz pra-front
```
- [ ] Importer `maria3` comme `maria4` sur IN4
```bash
incus import maria3-export.tar.gz maria4
```
- [ ] Configurer les IPs et paramètres réseau sur IN4
- [ ] Adapter le fichier `.env` pour l'environnement PROD
#### Phase 3 - Migration des données PROD (À FAIRE)
- [ ] Effectuer une sauvegarde complète des bases PROD sur IN2/nx4
- [ ] Exporter les données de `uof_frontal` et `uof_linet` depuis IN2/nx4
- [ ] Utiliser le script de migration SQL pour fusionner les données
- [ ] Importer les données fusionnées dans `maria4` sur IN4
- [ ] Configurer `pra-front` pour pointer vers `maria4`
- [ ] Tests de validation en pré-production
#### Phase 4 - Bascule PROD (À FAIRE)
- [ ] Planifier la fenêtre de maintenance
- [ ] Arrêter l'application sur IN2
- [ ] Synchronisation finale des données vers IN4/maria4
- [ ] Basculer le DNS/proxy vers IN4
- [ ] Valider le fonctionnement en production
- [ ] Monitoring post-migration (48h)
- [ ] Décommissionner IN2 après période de stabilisation
### Configuration technique
#### Variables d'environnement
**DEV (IN3) - Actuel:**
```env
DB_HOST=13.23.33.4 # IP de maria3
DB_PORT=3306
DB_DATABASE=cleo
DB_USERNAME=cleo_user
DB_PASSWORD=CleoDev2025!
```
**PROD (IN4) - À configurer:**
```env
DB_HOST=<IP_maria4> # À définir sur IN4
DB_PORT=3306
DB_DATABASE=cleo
DB_USERNAME=cleo_user
DB_PASSWORD=<PROD_PASSWORD> # À sécuriser
```
#### Sécurité réseau
- ✅ Connexions uniquement depuis les containers applicatifs
- ✅ Pas d'exposition directe des ports MariaDB
- [ ] Firewall entre containers à configurer sur IN4
#### Backup et restauration
- [ ] Scripts de backup automatisés à mettre en place
- [ ] Réplication master-slave pour haute disponibilité (optionnel)
## Améliorations techniques prioritaires ## Améliorations techniques prioritaires
### Sécurité (URGENT) ### Sécurité
- [ ] Migrer les credentials DB vers des variables d'environnement - [x] ✅ Migrer les credentials DB vers des variables d'environnement
- [ ] Remplacer toutes les requêtes SQL par des requêtes préparées - [x] ✅ Classe Database avec requêtes préparées PDO
- [ ] Audit et correction des failles XSS - [ ] Audit complet des contrôleurs pour injections SQL résiduelles
- [ ] Correction des failles XSS potentielles
### Performance ### Performance
- [ ] Implémenter la pagination côté serveur pour toutes les listes - [ ] Implémenter la pagination côté serveur pour toutes les listes
@@ -156,7 +281,21 @@ ALTER TABLE devis ADD COLUMN date_transfert_edi DATETIME;
ALTER TABLE devis ADD COLUMN erreur_transfert_edi TEXT; ALTER TABLE devis ADD COLUMN erreur_transfert_edi TEXT;
``` ```
## Résumé de l'état actuel
### ✅ Réalisations (v2.0.1 - 12 septembre 2025)
1. **Migration DEV complétée** : Architecture séparée application/BDD
2. **Base unique `cleo`** : Fusion réussie de 3 bases en une seule
3. **Sécurité renforcée** : PDO, requêtes préparées, variables d'environnement
4. **Container `dva-front`** : MariaDB supprimé, application PHP uniquement
5. **Container `maria3`** : Base de données centralisée opérationnelle
### 🎯 Prochaines étapes prioritaires
1. **Migration PROD vers IN4** : Export/Import des containers vers `pra-front` et `maria4`
2. **Fonctionnalités métier** : Points 6, 14, 16 (voir sections ci-dessus)
3. **Sécurité** : Audit des contrôleurs pour injections SQL résiduelles
--- ---
*Document créé le 11 septembre 2025* *Document mis à jour le 12 septembre 2025*
*À mettre à jour au fur et à mesure de l'avancement des développements* *Version 2.0.1 - Post-migration DEV*

1265
docs/cleo.sql Normal file

File diff suppressed because it is too large Load Diff

177274
docs/uof_linet_20250911.sql Normal file

File diff suppressed because one or more lines are too long

243
migration/README.md Normal file
View File

@@ -0,0 +1,243 @@
# Migration CLEO vers la nouvelle architecture
## ✅ MIGRATION COMPLÉTÉE (12 septembre 2025)
## Vue d'ensemble
Cette migration a transformé l'architecture de CLEO pour :
-**Fusionner 3 bases de données** (`uof_frontal`, `uof_linet`, `logs`) en une seule base `cleo`
-**Sécuriser les connexions** avec PDO et requêtes préparées
-**Externaliser la configuration** dans des variables d'environnement
-**Séparer** application (dva-front) et base de données (maria3)
## Pré-requis
- PHP 8.3+
- MariaDB/MySQL 5.7+
- Accès aux bases existantes (`uof_frontal`, `uof_linet`)
- Container `maria3` accessible depuis l'application
## Structure de la migration
```
migration/
├── migrate_to_cleo.sql # Script SQL de migration des données
├── deploy_new_architecture.sh # Script de déploiement automatique
├── rollback.sh # Script de retour arrière
└── README.md # Ce fichier
```
## Étapes de migration
### 1. Préparation
```bash
# Se placer à la racine du projet
cd /home/pierre/dev/cleo
# Vérifier le fichier .env
cp .env.example .env
nano .env # Éditer avec vos paramètres
```
Configuration actuelle dans `.env` :
```env
DB_HOST=13.23.33.4 # IP du container maria3
DB_PORT=3306
DB_DATABASE=cleo
DB_USERNAME=cleo_user
DB_PASSWORD=CleoDev2025! # À changer en production
```
### 2. Exécution de la migration
```bash
# Rendre les scripts exécutables
chmod +x migration/*.sh
# Lancer la migration
./migration/deploy_new_architecture.sh
```
Le script effectue automatiquement :
- ✅ Sauvegarde des fichiers actuels
- ✅ Test de connexion à MariaDB
- ✅ Migration des bases de données
- ✅ Activation des nouveaux fichiers
- ✅ Tests de validation
### 3. Vérification
Après la migration, vérifier :
```bash
# Connexion à la base
mysql -h maria3 -u cleo_user -p cleo
# Vérifier les tables
SHOW TABLES;
# Compter les enregistrements
SELECT 'devis' as table_name, COUNT(*) FROM devis
UNION ALL
SELECT 'clients', COUNT(*) FROM clients
UNION ALL
SELECT 'users', COUNT(*) FROM users;
```
Tester l'application :
- Connexion utilisateur
- Consultation des devis
- Création d'un nouveau devis
- Export Excel
### 4. En cas de problème
Si la migration échoue ou cause des problèmes :
```bash
# Revenir à l'ancienne architecture
./migration/rollback.sh
```
Le rollback :
- Restaure les fichiers originaux
- Conserve les nouveaux fichiers dans `migration/new_architecture_backup/`
- L'application revient sur les bases `uof_frontal` et `uof_linet`
## Changements techniques
### Fichiers modifiés
| Fichier | Changement |
|---------|------------|
| `config/conf.php` | Utilise les variables d'environnement |
| `config/Database.php` | Nouvelle classe PDO singleton |
| `pub/res/d6/d6_tools.php` | Utilise la classe Database |
| `.env` | Configuration externalisée |
### Structure de la base `cleo`
```sql
-- Tables métier (ex-uof_linet)
devis
devis_produits
devis_histo
clients
produits
produits_familles
marches
users
x_familles
x_statuts_devis
x_clients_types
-- Tables système
y_pages (ex-uof_frontal)
users_entites (ex-uof_frontal)
z_logs (ex-base logs ou nouvelle)
```
### Améliorations de sécurité
1. **Requêtes préparées PDO** : Protection contre les injections SQL
2. **Variables d'environnement** : Credentials hors du code
3. **Connexion unique** : Pattern Singleton pour la DB
4. **Logging amélioré** : Traçabilité des requêtes SQL
## Environnements
### DEV/RECETTE (IN3) - ACTIF
```
Container: dva-front (13.23.33.42)
├── Application PHP 8.3
├── Nginx (Alpine Linux)
├── MariaDB : SUPPRIMÉ (12/09/2025)
└── Connexion vers maria3
Container: maria3 (13.23.33.4)
├── MariaDB 11.4
├── Base de données : cleo
├── Tables migrées : 30+
└── Utilisateur : cleo_user
```
**Actions réalisées :**
- ✅ Migration des bases `uof_frontal` et `uof_linet` vers `cleo`
- ✅ Création des tables manquantes (`z_sessions`, `z_stats`, `marches_listes`)
- ✅ Configuration PDO avec pattern Singleton
- ✅ Suppression complète de MariaDB sur dva-front
- ✅ Script de déploiement optimisé (`deploy-cleo-fast.sh`)
### PROD (IN4 - futur)
```
Container: nx4 → Application PHP
Container: maria4 → Base de données cleo
```
## Troubleshooting
### Erreur de connexion à la base
```bash
# Vérifier la connectivité
mysql -h maria3 -u cleo_user -p
# Vérifier les permissions
SHOW GRANTS FOR 'cleo_user'@'%';
```
### Erreur PHP après migration
```bash
# Vérifier les logs
tail -f log/cleo_debug_*.log
# Vérifier les permissions des fichiers
ls -la config/conf.php
ls -la .env
```
### Tables manquantes
```bash
# Réexécuter uniquement la migration SQL
mysql -h maria3 -u cleo_user -p cleo < migration/migrate_to_cleo.sql
```
## Support
Pour toute question ou problème :
1. Consulter les logs dans `/log/`
2. Vérifier le fichier `.env`
3. Utiliser le rollback si nécessaire
4. Documenter l'erreur dans un issue GitHub
## État post-migration
### Fichiers créés/modifiés
-`.env` : Configuration externalisée
-`config/Database.php` : Classe PDO singleton
-`config/conf.php` : Utilise les variables d'environnement
-`pub/res/d6/d6_tools.php` : Fonctions utilitaires mises à jour
-`deploy-cleo-fast.sh` : Script de déploiement optimisé (tar.gz)
### Problèmes résolus durant la migration
1. **Tables manquantes** : Création de `z_sessions`, `z_stats`, `marches_listes`
2. **Permissions fichiers** : `.env` en 644, logs en 777 avec owner `nobody`
3. **Compatibilité PDO** : Remplacement de `fetch_assoc()` par `fetch(PDO::FETCH_ASSOC)`
4. **Debug AJAX** : Suppression des commentaires HTML dans les réponses JSON
5. **Fonctions manquantes** : Ajout de `checkPsswd()`, `affiche_date()`, etc.
### Scripts de déploiement
```bash
# Déploiement rapide (30 secondes)
./deploy-cleo-fast.sh
# Déploiement dev (si besoin)
./deploy-cleo-dev.sh
```
---
*Migration complétée pour CLEO v2.0.1 - 12 septembre 2025*

View File

@@ -0,0 +1,10 @@
-- Création de la table marches_listes
CREATE TABLE IF NOT EXISTS `marches_listes` (
`rowid` int(11) NOT NULL AUTO_INCREMENT,
`fk_marche` int(11) DEFAULT NULL,
`terme_achat` varchar(255) DEFAULT NULL,
`date_created` datetime DEFAULT current_timestamp(),
`date_modified` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`rowid`),
KEY `idx_fk_marche` (`fk_marche`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

View File

@@ -0,0 +1,48 @@
-- Script pour créer les tables manquantes dans la base cleo
-- Date: 2025-09-12
USE cleo;
-- Table z_sessions pour la gestion des sessions utilisateurs
CREATE TABLE IF NOT EXISTS `z_sessions` (
`rowid` int(11) NOT NULL AUTO_INCREMENT,
`sid` varchar(255) NOT NULL,
`fk_user` int(11) DEFAULT 0,
`role` varchar(10) DEFAULT NULL,
`data` text,
`date_modified` datetime DEFAULT NULL,
`ip` varchar(50) DEFAULT NULL,
`browser` varchar(255) DEFAULT NULL,
PRIMARY KEY (`rowid`),
UNIQUE KEY `idx_sid` (`sid`),
KEY `idx_user` (`fk_user`),
KEY `idx_date` (`date_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Ajouter la colonne 'date' manquante dans z_logs si elle n'existe pas
ALTER TABLE `z_logs`
ADD COLUMN IF NOT EXISTS `date` datetime DEFAULT NULL AFTER `date_histo`;
-- Index pour améliorer les performances
ALTER TABLE `z_logs` ADD INDEX IF NOT EXISTS `idx_date_logs` (`date`);
-- Table z_stats pour les statistiques (optionnelle pour plus tard)
CREATE TABLE IF NOT EXISTS `z_stats` (
`rowid` int(11) NOT NULL AUTO_INCREMENT,
`date` datetime DEFAULT NULL,
`root` varchar(255) DEFAULT NULL,
`server_ip` varchar(50) DEFAULT NULL,
`server_soft` varchar(255) DEFAULT NULL,
`server_name` varchar(255) DEFAULT NULL,
`client_ip` varchar(50) DEFAULT NULL,
`client_browser` text,
`client_origine` text,
`client_page` text,
`fk_user` int(11) DEFAULT 0,
`appname` varchar(50) DEFAULT NULL,
`delay` decimal(10,3) DEFAULT 0,
PRIMARY KEY (`rowid`),
KEY `idx_date_stats` (`date`),
KEY `idx_user_stats` (`fk_user`),
KEY `idx_ip_stats` (`client_ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

@@ -0,0 +1,163 @@
#!/bin/bash
# Script de déploiement de la nouvelle architecture CLEO
# Version: 2.0.1
# Date: 2025-09-11
set -e # Arrêter en cas d'erreur
# Couleurs pour l'affichage
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}================================================${NC}"
echo -e "${GREEN} Déploiement CLEO - Nouvelle Architecture ${NC}"
echo -e "${GREEN}================================================${NC}"
echo ""
# Vérifier qu'on est dans le bon répertoire
if [ ! -f "index.php" ] || [ ! -d "config" ]; then
echo -e "${RED}Erreur: Ce script doit être exécuté depuis la racine du projet CLEO${NC}"
exit 1
fi
# Fonction pour demander confirmation
confirm() {
read -p "$1 [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
return 1
fi
return 0
}
# 1. Vérification du fichier .env
echo -e "${YELLOW}1. Vérification de la configuration...${NC}"
if [ ! -f ".env" ]; then
echo -e "${RED} Le fichier .env n'existe pas!${NC}"
echo " Copie de .env.example vers .env..."
cp .env.example .env
echo -e "${YELLOW} IMPORTANT: Veuillez éditer le fichier .env avec vos paramètres${NC}"
exit 1
fi
echo -e "${GREEN} ✓ Fichier .env trouvé${NC}"
# 2. Backup des anciens fichiers
echo -e "${YELLOW}2. Sauvegarde des fichiers actuels...${NC}"
BACKUP_DIR="backup_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Sauvegarder les fichiers critiques
if [ -f "config/conf.php" ]; then
cp config/conf.php "$BACKUP_DIR/conf.php.bak"
echo " - config/conf.php sauvegardé"
fi
if [ -f "pub/res/d6/d6_tools.php" ]; then
cp pub/res/d6/d6_tools.php "$BACKUP_DIR/d6_tools.php.bak"
echo " - pub/res/d6/d6_tools.php sauvegardé"
fi
echo -e "${GREEN} ✓ Sauvegarde effectuée dans $BACKUP_DIR${NC}"
# 3. Vérification de la connexion à la base de données
echo -e "${YELLOW}3. Test de connexion à la base de données...${NC}"
# Lire les paramètres depuis .env
source .env
# Tester la connexion MySQL
mysql -h"$DB_HOST" -u"$DB_USERNAME" -p"$DB_PASSWORD" -e "SELECT 1" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo -e "${GREEN} ✓ Connexion à MariaDB réussie${NC}"
else
echo -e "${RED} ✗ Impossible de se connecter à MariaDB${NC}"
echo " Vérifiez les paramètres dans .env"
exit 1
fi
# 4. Exécution du script de migration SQL
echo -e "${YELLOW}4. Migration de la base de données...${NC}"
if confirm " Voulez-vous exécuter le script de migration SQL?"; then
echo " Exécution de migrate_to_cleo.sql..."
mysql -h"$DB_HOST" -u"$DB_USERNAME" -p"$DB_PASSWORD" < migration/migrate_to_cleo.sql
if [ $? -eq 0 ]; then
echo -e "${GREEN} ✓ Migration SQL effectuée${NC}"
else
echo -e "${RED} ✗ Erreur lors de la migration SQL${NC}"
exit 1
fi
else
echo " Migration SQL ignorée"
fi
# 5. Activation des nouveaux fichiers
echo -e "${YELLOW}5. Activation de la nouvelle architecture...${NC}"
# Remplacer conf.php
if [ -f "config/conf_new.php" ]; then
mv config/conf.php config/conf_old.php 2>/dev/null || true
cp config/conf_new.php config/conf.php
echo -e "${GREEN} ✓ config/conf.php mis à jour${NC}"
fi
# Remplacer d6_tools.php
if [ -f "pub/res/d6/d6_tools_new.php" ]; then
mv pub/res/d6/d6_tools.php pub/res/d6/d6_tools_old.php 2>/dev/null || true
cp pub/res/d6/d6_tools_new.php pub/res/d6/d6_tools.php
echo -e "${GREEN} ✓ d6_tools.php mis à jour${NC}"
fi
# 6. Vérification des permissions
echo -e "${YELLOW}6. Vérification des permissions...${NC}"
chmod 644 config/conf.php
chmod 644 pub/res/d6/d6_tools.php
chmod 600 .env
echo -e "${GREEN} ✓ Permissions mises à jour${NC}"
# 7. Création du répertoire de logs si nécessaire
echo -e "${YELLOW}7. Création du répertoire de logs...${NC}"
mkdir -p log
chmod 755 log
echo -e "${GREEN} ✓ Répertoire log créé${NC}"
# 8. Test de l'application
echo -e "${YELLOW}8. Test de connexion à la base CLEO...${NC}"
php -r "
require_once 'config/Database.php';
try {
\$db = Database::getInstance();
echo ' ✓ Connexion PDO réussie' . PHP_EOL;
\$result = \$db->fetchOne('SELECT COUNT(*) as nb FROM users');
echo ' ✓ ' . \$result['nb'] . ' utilisateurs trouvés' . PHP_EOL;
} catch (Exception \$e) {
echo ' ✗ Erreur: ' . \$e->getMessage() . PHP_EOL;
exit(1);
}
"
if [ $? -eq 0 ]; then
echo -e "${GREEN} ✓ Tests réussis${NC}"
else
echo -e "${RED} ✗ Tests échoués${NC}"
echo " Utilisez ./migration/rollback.sh pour revenir en arrière"
exit 1
fi
echo ""
echo -e "${GREEN}================================================${NC}"
echo -e "${GREEN} Déploiement terminé avec succès! ${NC}"
echo -e "${GREEN}================================================${NC}"
echo ""
echo "Prochaines étapes:"
echo "1. Testez l'application dans votre navigateur"
echo "2. Vérifiez les logs dans le répertoire /log"
echo "3. En cas de problème, utilisez ./migration/rollback.sh"
echo ""
echo "Configuration actuelle:"
echo " - Base de données: $DB_DATABASE sur $DB_HOST"
echo " - Environnement: $APP_ENV"
echo " - Debug: $APP_DEBUG"
echo ""

View File

@@ -0,0 +1,37 @@
#!/bin/bash
echo "Script pour créer la table marches_listes"
echo "========================================="
echo ""
echo "Veuillez exécuter les commandes suivantes :"
echo ""
echo "1. Connectez-vous à IN3 :"
echo " ssh root@195.154.80.116"
echo ""
echo "2. Connectez-vous au conteneur maria3 :"
echo " incus exec maria3 -- bash"
echo ""
echo "3. Connectez-vous à MariaDB :"
echo " mariadb -u root"
echo ""
echo "4. Sélectionnez la base de données :"
echo " USE cleo;"
echo ""
echo "5. Créez la table marches_listes :"
cat << 'EOF'
CREATE TABLE IF NOT EXISTS `marches_listes` (
`rowid` int(11) NOT NULL AUTO_INCREMENT,
`fk_marche` int(11) DEFAULT NULL,
`terme_achat` varchar(255) DEFAULT NULL,
`date_created` datetime DEFAULT current_timestamp(),
`date_modified` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`rowid`),
KEY `idx_fk_marche` (`fk_marche`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
EOF
echo ""
echo "6. Vérifiez que la table est créée :"
echo " SHOW TABLES LIKE 'marches_listes';"
echo ""
echo "7. Quittez MariaDB :"
echo " EXIT;"

View File

@@ -0,0 +1,196 @@
-- Script de migration vers la base unique CLEO
-- Date: 2025-09-11
-- Version: 2.0.1
-- ==============================================================================
-- ÉTAPE 1: Création de la base CLEO
-- ==============================================================================
CREATE DATABASE IF NOT EXISTS `cleo`
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE `cleo`;
-- ==============================================================================
-- ÉTAPE 2: Migration des tables depuis uof_linet
-- ==============================================================================
-- Tables métier principales
CREATE TABLE IF NOT EXISTS `devis` LIKE `uof_linet`.`devis`;
INSERT INTO `devis` SELECT * FROM `uof_linet`.`devis`;
CREATE TABLE IF NOT EXISTS `devis_produits` LIKE `uof_linet`.`devis_produits`;
INSERT INTO `devis_produits` SELECT * FROM `uof_linet`.`devis_produits`;
CREATE TABLE IF NOT EXISTS `devis_histo` LIKE `uof_linet`.`devis_histo`;
INSERT INTO `devis_histo` SELECT * FROM `uof_linet`.`devis_histo`;
CREATE TABLE IF NOT EXISTS `clients` LIKE `uof_linet`.`clients`;
INSERT INTO `clients` SELECT * FROM `uof_linet`.`clients`;
CREATE TABLE IF NOT EXISTS `produits` LIKE `uof_linet`.`produits`;
INSERT INTO `produits` SELECT * FROM `uof_linet`.`produits`;
CREATE TABLE IF NOT EXISTS `produits_familles` LIKE `uof_linet`.`produits_familles`;
INSERT INTO `produits_familles` SELECT * FROM `uof_linet`.`produits_familles`;
CREATE TABLE IF NOT EXISTS `marches` LIKE `uof_linet`.`marches`;
INSERT INTO `marches` SELECT * FROM `uof_linet`.`marches`;
CREATE TABLE IF NOT EXISTS `users` LIKE `uof_linet`.`users`;
INSERT INTO `users` SELECT * FROM `uof_linet`.`users`;
-- Tables de référence (x_*)
CREATE TABLE IF NOT EXISTS `x_familles` LIKE `uof_linet`.`x_familles`;
INSERT INTO `x_familles` SELECT * FROM `uof_linet`.`x_familles`;
CREATE TABLE IF NOT EXISTS `x_statuts_devis` LIKE `uof_linet`.`x_statuts_devis`;
INSERT INTO `x_statuts_devis` SELECT * FROM `uof_linet`.`x_statuts_devis`;
CREATE TABLE IF NOT EXISTS `x_clients_types` LIKE `uof_linet`.`x_clients_types`;
INSERT INTO `x_clients_types` SELECT * FROM `uof_linet`.`x_clients_types`;
-- ==============================================================================
-- ÉTAPE 3: Migration de y_pages depuis uof_frontal
-- ==============================================================================
CREATE TABLE IF NOT EXISTS `y_pages` (
`rowid` int(11) NOT NULL AUTO_INCREMENT,
`script` varchar(50) DEFAULT NULL,
`titre` varchar(255) DEFAULT NULL,
`description` text,
`keywords` text,
`admin` tinyint(1) DEFAULT 0,
`active` tinyint(1) DEFAULT 1,
`mail` tinyint(1) DEFAULT 0,
`form` tinyint(1) DEFAULT 0,
`sidebar` tinyint(1) DEFAULT 0,
`chart` tinyint(1) DEFAULT 0,
`agenda` tinyint(1) DEFAULT 0,
`autocomplete` tinyint(1) DEFAULT 0,
`scheduler` tinyint(1) DEFAULT 0,
`osm` tinyint(1) DEFAULT 0,
`vuejs` tinyint(1) DEFAULT 0,
`files` tinyint(1) DEFAULT 0,
`layout` varchar(50) DEFAULT 'default',
PRIMARY KEY (`rowid`),
KEY `idx_script` (`script`),
KEY `idx_admin` (`admin`),
KEY `idx_active` (`active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Import des données depuis uof_frontal.y_pages
INSERT INTO `y_pages` SELECT * FROM `uof_frontal`.`y_pages` WHERE admin = 1;
-- ==============================================================================
-- ÉTAPE 4: Migration de users_entites depuis uof_frontal
-- ==============================================================================
CREATE TABLE IF NOT EXISTS `users_entites` (
`rowid` int(11) NOT NULL AUTO_INCREMENT,
`appname` varchar(50) DEFAULT NULL,
`libelle` varchar(255) DEFAULT NULL,
`adresse1` varchar(255) DEFAULT NULL,
`adresse2` varchar(255) DEFAULT NULL,
`cp` varchar(10) DEFAULT NULL,
`ville` varchar(100) DEFAULT NULL,
`tel1` varchar(20) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`http_host` varchar(255) DEFAULT NULL,
`groupebase` varchar(50) DEFAULT NULL,
`genbase` varchar(50) DEFAULT NULL,
`table_users_gen` varchar(100) DEFAULT 'users',
`active` tinyint(1) DEFAULT 1,
PRIMARY KEY (`rowid`),
KEY `idx_appname` (`appname`),
KEY `idx_http_host` (`http_host`),
KEY `idx_active` (`active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Import des données depuis uof_frontal.users_entites
INSERT INTO `users_entites` SELECT * FROM `uof_frontal`.`users_entites`;
-- ==============================================================================
-- ÉTAPE 5: Création de la table z_logs (depuis la base logs si elle existe)
-- ==============================================================================
CREATE TABLE IF NOT EXISTS `z_logs` (
`rowid` int(11) NOT NULL AUTO_INCREMENT,
`fk_user` int(11) DEFAULT 0,
`script` varchar(50) DEFAULT NULL,
`user_agent` varchar(100) DEFAULT NULL,
`http_host` varchar(100) DEFAULT NULL,
`ip_client` varchar(50) DEFAULT NULL,
`appname` varchar(50) DEFAULT NULL,
`commentaire` text,
`date_histo` datetime DEFAULT NULL,
`notif` tinyint(1) DEFAULT 0,
PRIMARY KEY (`rowid`),
KEY `idx_user` (`fk_user`),
KEY `idx_date` (`date_histo`),
KEY `idx_script` (`script`),
KEY `idx_appname` (`appname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Si la base logs existe, importer les données
-- INSERT INTO `z_logs` SELECT * FROM `logs`.`z_logs`;
-- ==============================================================================
-- ÉTAPE 6: Mise à jour des configurations
-- ==============================================================================
-- Mise à jour de users_entites pour la nouvelle configuration
UPDATE `users_entites`
SET
`genbase` = 'cleo',
`groupebase` = 'cleo',
`table_users_gen` = 'users'
WHERE `active` = 1;
-- ==============================================================================
-- ÉTAPE 7: Création de l'utilisateur pour l'application
-- ==============================================================================
-- Créer l'utilisateur s'il n'existe pas
CREATE USER IF NOT EXISTS 'cleo_user'@'%' IDENTIFIED BY 'CleoDev2025!';
-- Donner tous les privilèges sur la base cleo
GRANT ALL PRIVILEGES ON `cleo`.* TO 'cleo_user'@'%';
-- Rafraîchir les privilèges
FLUSH PRIVILEGES;
-- ==============================================================================
-- ÉTAPE 8: Vérifications
-- ==============================================================================
-- Afficher le nombre de lignes par table
SELECT 'devis' as table_name, COUNT(*) as nb_lignes FROM devis
UNION ALL
SELECT 'devis_produits', COUNT(*) FROM devis_produits
UNION ALL
SELECT 'clients', COUNT(*) FROM clients
UNION ALL
SELECT 'produits', COUNT(*) FROM produits
UNION ALL
SELECT 'marches', COUNT(*) FROM marches
UNION ALL
SELECT 'users', COUNT(*) FROM users
UNION ALL
SELECT 'y_pages', COUNT(*) FROM y_pages
UNION ALL
SELECT 'users_entites', COUNT(*) FROM users_entites
UNION ALL
SELECT 'z_logs', COUNT(*) FROM z_logs;
-- ==============================================================================
-- FIN DE LA MIGRATION
-- ==============================================================================
--
-- IMPORTANT: Après exécution de ce script:
-- 1. Vérifier que toutes les données ont été correctement migrées
-- 2. Mettre à jour le fichier .env avec les nouvelles informations de connexion
-- 3. Tester l'application avec la nouvelle base
-- 4. Si tout est OK, sauvegarder les anciennes bases avant suppression
--

113
migration/rollback.sh Executable file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
# Script de rollback pour revenir à l'ancienne architecture
# Version: 2.0.1
# Date: 2025-09-11
set -e
# Couleurs pour l'affichage
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${YELLOW}================================================${NC}"
echo -e "${YELLOW} Rollback CLEO - Retour architecture legacy ${NC}"
echo -e "${YELLOW}================================================${NC}"
echo ""
# Vérifier qu'on est dans le bon répertoire
if [ ! -f "index.php" ] || [ ! -d "config" ]; then
echo -e "${RED}Erreur: Ce script doit être exécuté depuis la racine du projet CLEO${NC}"
exit 1
fi
# Fonction pour demander confirmation
confirm() {
read -p "$1 [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
return 1
fi
return 0
}
echo -e "${YELLOW}ATTENTION: Cette opération va restaurer l'ancienne configuration${NC}"
if ! confirm "Voulez-vous continuer?"; then
echo "Rollback annulé"
exit 0
fi
echo ""
echo -e "${YELLOW}1. Restauration des fichiers originaux...${NC}"
# Restaurer conf.php
if [ -f "config/conf_old.php" ]; then
mv config/conf.php config/conf_new_backup.php 2>/dev/null || true
mv config/conf_old.php config/conf.php
echo -e "${GREEN} ✓ config/conf.php restauré${NC}"
else
# Chercher dans les backups
LATEST_BACKUP=$(ls -td backup_* 2>/dev/null | head -1)
if [ -n "$LATEST_BACKUP" ] && [ -f "$LATEST_BACKUP/conf.php.bak" ]; then
cp "$LATEST_BACKUP/conf.php.bak" config/conf.php
echo -e "${GREEN} ✓ config/conf.php restauré depuis $LATEST_BACKUP${NC}"
else
echo -e "${RED} ✗ Impossible de restaurer config/conf.php${NC}"
fi
fi
# Restaurer d6_tools.php
if [ -f "pub/res/d6/d6_tools_old.php" ]; then
mv pub/res/d6/d6_tools.php pub/res/d6/d6_tools_new_backup.php 2>/dev/null || true
mv pub/res/d6/d6_tools_old.php pub/res/d6/d6_tools.php
echo -e "${GREEN} ✓ d6_tools.php restauré${NC}"
else
# Chercher dans les backups
LATEST_BACKUP=$(ls -td backup_* 2>/dev/null | head -1)
if [ -n "$LATEST_BACKUP" ] && [ -f "$LATEST_BACKUP/d6_tools.php.bak" ]; then
cp "$LATEST_BACKUP/d6_tools.php.bak" pub/res/d6/d6_tools.php
echo -e "${GREEN} ✓ d6_tools.php restauré depuis $LATEST_BACKUP${NC}"
else
echo -e "${RED} ✗ Impossible de restaurer d6_tools.php${NC}"
fi
fi
echo ""
echo -e "${YELLOW}2. Configuration de la base de données...${NC}"
echo -e "${YELLOW} Note: Les bases uof_frontal et uof_linet doivent être disponibles${NC}"
echo -e "${YELLOW} La base 'cleo' reste en place pour référence future${NC}"
echo ""
echo -e "${YELLOW}3. Nettoyage...${NC}"
# Conserver les nouveaux fichiers pour référence
if [ ! -d "migration/new_architecture_backup" ]; then
mkdir -p migration/new_architecture_backup
fi
# Sauvegarder les nouveaux fichiers
[ -f "config/conf_new.php" ] && cp config/conf_new.php migration/new_architecture_backup/
[ -f "config/Database.php" ] && cp config/Database.php migration/new_architecture_backup/
[ -f "pub/res/d6/d6_tools_new.php" ] && cp pub/res/d6/d6_tools_new.php migration/new_architecture_backup/
[ -f ".env" ] && cp .env migration/new_architecture_backup/
[ -f ".env.example" ] && cp .env.example migration/new_architecture_backup/
echo -e "${GREEN} ✓ Nouveaux fichiers sauvegardés dans migration/new_architecture_backup/${NC}"
echo ""
echo -e "${GREEN}================================================${NC}"
echo -e "${GREEN} Rollback terminé! ${NC}"
echo -e "${GREEN}================================================${NC}"
echo ""
echo "L'application utilise maintenant l'ancienne architecture avec:"
echo " - Base uof_frontal pour la configuration"
echo " - Base uof_linet pour les données métier"
echo ""
echo "Les fichiers de la nouvelle architecture sont conservés dans:"
echo " - migration/new_architecture_backup/"
echo ""
echo "Pour réessayer la migration plus tard:"
echo " ./migration/deploy_new_architecture.sh"
echo ""

View File

@@ -4,7 +4,7 @@ $aModel = array();
function openSession($userdata) function openSession($userdata)
{ {
$uid = $userdata['rowid']; $uid = $userdata['rowid'];
$urole = $userdata['role']; $urole = $userdata['fk_role'];
$sql = "DELETE FROM z_sessions WHERE fk_user=" . $uid . ";"; $sql = "DELETE FROM z_sessions WHERE fk_user=" . $uid . ";";
qSQL($sql, "gen"); qSQL($sql, "gen");

View File

@@ -26,7 +26,7 @@ if ($Conf::intra || $Conf::admin > 0) {
?> ?>
<meta name="HandheldFriendly" content="True"> <meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320"> <meta name="MobileOptimized" content="320">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<link rel="icon" href="favicon.ico"/> <link rel="icon" href="favicon.ico"/>
<link rel="shortcut icon" href="favicon.ico"/> <link rel="shortcut icon" href="favicon.ico"/>
<link rel="apple-touch-icon" href="favicon.ico"/> <link rel="apple-touch-icon" href="favicon.ico"/>

View File

@@ -1,8 +1,9 @@
<?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'); 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');
$today = date("Y-m-d H:i:s"); require_once dirname(__DIR__, 3) . '/config/Database.php';
$today = date("Y-m-d H:i:s");
$dateFr = date("d/m/Y"); $dateFr = date("d/m/Y");
$dateTimeFr = date("d/m/Y H:i:s"); $dateTimeFr = date("d/m/Y H:i:s");
$timeFr = date("H:i:s"); $timeFr = date("H:i:s");
@@ -12,310 +13,21 @@ $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 = 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"); $mois_abr = array("", "Jan", "Fév", "Mar", "Avr", "Mai", "Jui", "Jul", "Aoû", "Sep", "Oct", "Nov", "Déc");
function getinfos($cSQL, $dbn = "gen", $format = "normal") { // La fonction getinfos() est maintenant définie dans config/Database.php
$result = array(); // pour éviter les conflits de redéclaration
$resql = qSQL($cSQL, $dbn); // La fonction qSQL() est maintenant définie dans config/Database.php
while ($rec = $resql->fetch_assoc()) { // pour éviter les conflits de redéclaration
$result[] = $rec;
}
if (strtolower($format) == "json") {
$jsonresult = json_encode($result);
$lignes = $jsonresult;
return $lignes;
} else {
return $result;
}
}
//! qSQL : fonction de requête SQL à la base de données
function qSQL($qsql, $dbn = "gen", $lastid = false) {
global $Conf;
$dbh = $Conf->_dbhost;
//! si en paramètre on spécifie une base de données $dbn, on s'y connecte,
//! sinon on regarde si la base utilisateur est renseignée, si c'est le cas on s'y connecte, sinon on prend la base par défaut
if ($dbn == "gen") {
if ($Conf->_dbuname == "") {
$dbn = $Conf->_dbname;
$dbu = $Conf->_dbuser;
$dbp = $Conf->_dbpass;
} else {
$dbn = $Conf->_dbuname;
$dbu = $Conf->_dbuuser;
$dbp = $Conf->_dbupass;
}
} else {
if (strtolower($dbn) == "principale" || strtolower($dbn) == "frontal") {
$dbn = $Conf->_dbname;
$dbu = $Conf->_dbuser;
$dbp = $Conf->_dbpass;
} else {
if (strtolower($dbn) == "credemo") {
$dbn = $Conf->_dbcname;
$dbu = $Conf->_dbcuser;
$dbp = $Conf->_dbcpass;
} else {
//! sinon on prend le groupe
$dbn = $Conf->_dbgname;
$dbu = $Conf->_dbguser;
$dbp = $Conf->_dbgpass;
}
}
}
$mysqli = new mysqli($dbh, $dbu, $dbp, $dbn);
$mysqli->set_charset("utf8");
if ($mysqli->connect_error) {
// la connexion ne s'est pas faite
$mysqli->close();
return false;
} else {
// la connexion s'est faite correctement
if ($qres = $mysqli->query($qsql)) {
if ($lastid) {
$qres = $mysqli->insert_id;
}
$mysqli->close();
return $qres;
} else {
$mysqli->close();
return false;
}
}
}
function hashPsswd($p) {
$options = [
'cost' => 11, // Cout algorithmique
];
// Génération du MDP
$psswd = password_hash($p, PASSWORD_BCRYPT, $options);
return $psswd;
}
function createPsswd($id, $p, $dbgen = "gen") {
global $Conf;
global $Route;
$psswd = hashPsswd($p);
if ($Conf::admin) {
if (substr($Conf->_appname, 0, 3) == "ce_") {
if ($Route->_script == "salaries") {
$sql = 'UPDATE salaries SET userpswd="' . $psswd . '", userpass="xxx" WHERE rowid=' . $id . ';';
} else {
$sql = 'UPDATE users SET userpswd="' . $psswd . '", userpass="xxx" WHERE rowid=' . $id . ';';
}
} else {
$sql = 'UPDATE users SET userpswd="' . $psswd . '", userpass="xxx" WHERE rowid=' . $id . ';';
}
} else {
$sql = 'UPDATE salaries SET userpswd="' . $psswd . '", userpass="xxx" WHERE rowid=' . $id . ';';
}
qSQL($sql);
eLog($sql);
return true;
}
function checkPsswd($p, $pCr) {
// Récupération et vérification du MDP saisi par l'utilisateur
// $p : le pass en clair, $pCr : le pass enregistré et hashé
if (password_verify($p, $pCr)) {
return true;
} else {
return false;
}
}
function generateRandomPassword() {
//Initialize the random password
$password = '';
//Initialize a random desired length
$desired_length = rand(8, 12);
for ($length = 0; $length < $desired_length; $length++) {
//Append a random ASCII character (including symbols)
$password .= chr(rand(44, 122));
}
// On remplace quelques caractères non désirés
$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 eLog($comment, $notif = false) {
global $Session;
global $Route;
global $Conf;
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 = $_SERVER["HTTP_USER_AGENT"];
if (isset($Session->_user["rowid"])) {
$user = $Session->_user["rowid"];
if ($user == "") {
$user = 0;
}
} else {
$user = 0;
}
$script = $Route->_script;
$comment = nettoie_input($comment);
$dt = date("Y-m-d H:i:s");
if ($notif) {
$not = 1;
} else {
$not = 2;
}
$sql = 'INSERT INTO z_logs (date, ip, host, adrhost, infos, fk_user, page, commentaire, chk_notif) VALUES ("' . $dt . '", "' . $ip . '", "' . $hn . '", "' . $ha . '", "' . $us . '", "' . $user . '", "' . $script . '", "' . $comment . '", ' . $not . ');';
qSQL($sql, "gen");
if (strpos(strtolower($comment), 'erreur') !== false) {
//! S'il y a spécifiquement une erreur on l'enregistre dans un fichier log à la racine du site
error_log($dt . ";" . $ip . ";" . $script . ";" . $comment . "\r\n", 3, "./" . $Conf->_appname . ".log");
}
}
// Fonction logstats simplifiée (la table z_stats n'existe pas encore)
function logstats($delay = 0, $fk_user = 0, $appname = "") { function logstats($delay = 0, $fk_user = 0, $appname = "") {
global $Conf; // Pour l'instant, on ne fait rien
$dt = date("Y-m-d H:i:s"); // TODO: Créer la table z_stats si besoin de statistiques
return true;
$exclude_clients_ip = "aucune";
if (isset($Conf->_excludeIp)) {
$exclude_clients_ip = $Conf->_excludeIp;
}
if (isset($Conf->_clientIp)) {
$client_ip = $Conf->_clientIp;
} else {
if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
$client_ip = $_SERVER["HTTP_CLIENT_IP"];
} elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$client_ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
} else {
$client_ip = $_SERVER["REMOTE_ADDR"];
}
}
$verif_ip = strpos($exclude_clients_ip, $client_ip);
if ($verif_ip === false) {
$home = $_SERVER["HOME"];
$doc_root = $_SERVER["DOCUMENT_ROOT"];
$doc_root = substr($doc_root, strlen($home));
$sql = 'INSERT INTO z_stats SET ';
$sql .= 'date="' . $dt . '", ';
$sql .= 'root="' . $doc_root . '", ';
$sql .= 'server_ip="' . $_SERVER["SERVER_ADDR"] . '", ';
$sql .= 'server_soft="' . $_SERVER["SERVER_SOFTWARE"] . '", ';
$sql .= 'server_name="' . $_SERVER["SERVER_NAME"] . '", ';
$sql .= 'client_ip="' . $client_ip . '", ';
$sql .= 'client_browser="' . $_SERVER["HTTP_USER_AGENT"] . '", ';
if (isset($_SERVER["HTTP_REFERER"])) {
$sql .= 'client_origine="' . $_SERVER["HTTP_REFERER"] . '", ';
}
$sql .= 'client_page="' . $_SERVER["REQUEST_URI"] . '", ';
$sql .= 'client_delay=' . str_replace(',', '.', $delay) . ', ';
$sql .= 'appname="' . $appname . '", ';
$sql .= 'fk_user=' . $fk_user . ', ';
$sql .= 'status="' . $_SERVER["REDIRECT_STATUS"] . '";';
// server : 51.255.35.214
$mysqli = new mysqli("localhost", "logs_user", "d66,Logs.User", "logs");
$mysqli->set_charset("utf8");
$mysqli->query($sql);
$mysqli->close();
}
}
//! *****************************************************************************************//
//! nettoie_input : prépare une zone d'un formulaire avant son enregistrement dans la base //
//! En paramètre, on passe la valeur à nettoyer //
//! *****************************************************************************************//
function nettoie_input($data) {
if (ctype_digit($data)) {
$data = intval($data);
} else {
global $Conf;
$dbn = $Conf->_dbname;
$mysqli = new mysqli($Conf->_dbhost, $Conf->_dbuser, $Conf->_dbpass, $dbn);
$mysqli->set_charset("utf8");
$data = mysqli_real_escape_string($mysqli, $data);
// $data = addcslashes($data, '%_');
$mysqli->close();
}
return $data;
}
function str_normalize($string, $minuscules = true) {
//! Normalise une chaîne de caractères en remplaçant tous les caractères accentués, les espaces et caractères spéciaux
$result = "";
$string = trim($string); // on efface tous les espaces à gauche et à droite
if (strlen($string) > 0) {
if ($minuscules) {
$result = strtolower($string);
} else {
$result = $string;
}
$result = str_replace(" ", "_", $result);
//$result = str_replace("-", "_", $result);
//$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);
//! Ajout du 08/12/2015
$result = str_replace("?", "_", $result);
$result = trim($result);
}
return $result;
} }
function affiche_date($ladate) { function affiche_date($ladate) {
/** // Retourne une date MySQL yyyy-mm-dd HH:ii:ss au format dd/mm/yyyy
* This is a sample function to illustrate additional PHP formatter
* options.
* @param $ladate date au format MySQL
*
* @return String date au format Fr dd/mm/yyyy
* @author D6SOFT
*
*/
//! Retourne une date MySQL yyyy-mm-dd HH:ii:ss au format dd/mm/yyyy
$ladate = trim($ladate); $ladate = trim($ladate);
if ($ladate == "" || substr($ladate, 0, 2) == "00") { if ($ladate == "" || substr($ladate, 0, 2) == "00") {
return ""; return "";
@@ -330,9 +42,9 @@ function affiche_date($ladate) {
} }
function d6GetDate($laDate, $transform = "MF", $hours = false, $seconds = false) { function d6GetDate($laDate, $transform = "MF", $hours = false, $seconds = false) {
//! Retourne une date // Retourne une date
//! $format="MF" du format MySQL yyyy-mm-dd au format Fr dd/mm/yyyy // $transform="MF" du format MySQL yyyy-mm-dd au format Fr dd/mm/yyyy
//! $format="FM" du format Fr dd/mm/yyyy au format MySQL yyyy-mm-dd // $transform="FM" du format Fr dd/mm/yyyy au format MySQL yyyy-mm-dd
$ret = ""; $ret = "";
if (strlen($laDate) >= 10) { if (strlen($laDate) >= 10) {
@@ -345,26 +57,200 @@ function d6GetDate($laDate, $transform = "MF", $hours = false, $seconds = false)
return $ret; return $ret;
} }
function loadtel($numero, $prefix = "+33") { function hashPsswd($p) {
//! retourne un numéro de téléphone sans espace et . et avec le préfixe devant : +33 par défaut $options = [
$lenumero = trim($numero); 'cost' => 11,
$lenumero = preg_replace('/[^0-9]/', '', $lenumero); ];
if (strlen($lenumero) == 10) { $psswd = password_hash($p, PASSWORD_BCRYPT, $options);
$lenumero = substr($lenumero, 1); return $psswd;
}
if (strlen($lenumero) == 9) {
$lenumero = $prefix . $lenumero;
}
return $lenumero;
} }
function formattel($numero, $separateur = " ") { function checkPsswd($p, $pCr) {
//! formate le n° de téléphone de 651234567 ou 0651234567 en 06 51 23 45 67 // Vérification du MDP saisi par l'utilisateur
if (strlen($numero) == 9) { // $p : le pass en clair, $pCr : le pass enregistré et hashé
$numero = "0" . $numero; return password_verify($p, $pCr);
} }
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); 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 $numero;
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('"', "&quot;", $res));
$res = str_replace('<', '&lt;', $res);
$res = str_replace('>', '&gt;', $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;
} }

254
pub/res/d6/d6_tools_new.php Normal file
View File

@@ -0,0 +1,254 @@
<?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");
function getinfos($cSQL, $dbn = "gen", $format = "normal") {
try {
$db = Database::getInstance();
$result = $db->fetchAll($cSQL);
if (strtolower($format) == "json") {
return json_encode($result);
}
return $result;
} catch (Exception $e) {
if ($_ENV['APP_DEBUG'] ?? false) {
error_log("Erreur getinfos: " . $e->getMessage());
}
return ($format == "json") ? json_encode([]) : [];
}
}
function qSQL($qsql, $dbn = "gen", $lastid = false) {
try {
$db = Database::getInstance();
$queryType = strtoupper(substr(trim($qsql), 0, 6));
if ($queryType === 'INSERT' || $queryType === 'UPDATE' || $queryType === 'DELETE') {
$stmt = $db->query($qsql);
if ($lastid && $queryType === 'INSERT') {
return $db->lastInsertId();
}
if ($stmt instanceof PDOStatement) {
return $stmt->rowCount() > 0;
}
return $stmt;
} else {
return $db->query($qsql);
}
} catch (Exception $e) {
if ($_ENV['APP_DEBUG'] ?? false) {
error_log("Erreur qSQL: " . $e->getMessage());
}
return false;
}
}
function hashPsswd($p) {
$options = [
'cost' => 11,
];
$psswd = password_hash($p, PASSWORD_BCRYPT, $options);
return $psswd;
}
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('<', '&lt;', $res);
$res = str_replace('>', '&gt;', $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;
}

559
pub/res/d6/d6_tools_old.php Normal file
View File

@@ -0,0 +1,559 @@
<?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');
$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");
function getinfos($cSQL, $dbn = "gen", $format = "normal") {
$result = array();
$resql = qSQL($cSQL, $dbn);
while ($rec = $resql->fetch_assoc()) {
$result[] = $rec;
}
if (strtolower($format) == "json") {
$jsonresult = json_encode($result);
$lignes = $jsonresult;
return $lignes;
} else {
return $result;
}
}
//! qSQL : fonction de requête SQL à la base de données
function qSQL($qsql, $dbn = "gen", $lastid = false) {
global $Conf;
$dbh = $Conf->_dbhost;
//! si en paramètre on spécifie une base de données $dbn, on s'y connecte,
//! sinon on regarde si la base utilisateur est renseignée, si c'est le cas on s'y connecte, sinon on prend la base par défaut
if ($dbn == "gen") {
if ($Conf->_dbuname == "") {
$dbn = $Conf->_dbname;
$dbu = $Conf->_dbuser;
$dbp = $Conf->_dbpass;
} else {
$dbn = $Conf->_dbuname;
$dbu = $Conf->_dbuuser;
$dbp = $Conf->_dbupass;
}
} else {
if (strtolower($dbn) == "principale" || strtolower($dbn) == "frontal") {
$dbn = $Conf->_dbname;
$dbu = $Conf->_dbuser;
$dbp = $Conf->_dbpass;
} else {
if (strtolower($dbn) == "credemo") {
$dbn = $Conf->_dbcname;
$dbu = $Conf->_dbcuser;
$dbp = $Conf->_dbcpass;
} else {
//! sinon on prend le groupe
$dbn = $Conf->_dbgname;
$dbu = $Conf->_dbguser;
$dbp = $Conf->_dbgpass;
}
}
}
// Logger la requête SQL si le mode debug SQL est activé
if (isset($Conf->_log_sql) && $Conf->_log_sql) {
$start_time = microtime(true);
}
$mysqli = new mysqli($dbh, $dbu, $dbp, $dbn);
$mysqli->set_charset("utf8");
if ($mysqli->connect_error) {
// la connexion ne s'est pas faite
if (isset($Conf->_log_sql) && $Conf->_log_sql) {
debug("Erreur de connexion MySQL: " . $mysqli->connect_error . " | DB: $dbn", "SQL_ERROR", 1);
}
$mysqli->close();
return false;
} else {
// la connexion s'est faite correctement
if ($qres = $mysqli->query($qsql)) {
if ($lastid) {
$qres = $mysqli->insert_id;
}
// Logger la requête SQL réussie
if (isset($Conf->_log_sql) && $Conf->_log_sql) {
$exec_time = round((microtime(true) - $start_time) * 1000, 2); // en ms
$query_type = strtoupper(substr(trim($qsql), 0, 6));
$log_data = array(
'query' => $qsql,
'database' => $dbn,
'exec_time_ms' => $exec_time,
'type' => $query_type
);
// Pour les INSERT avec lastid, ajouter l'ID inséré
if ($lastid && $query_type == 'INSERT') {
$log_data['insert_id'] = $qres;
}
debug($log_data, "SQL_QUERY", 2);
}
$mysqli->close();
return $qres;
} else {
// Erreur dans la requête SQL
$error_msg = $mysqli->error;
if (isset($Conf->_log_sql) && $Conf->_log_sql) {
$exec_time = isset($start_time) ? round((microtime(true) - $start_time) * 1000, 2) : 0;
debug(array(
'query' => $qsql,
'database' => $dbn,
'error' => $error_msg,
'exec_time_ms' => $exec_time
), "SQL_ERROR", 1);
}
$mysqli->close();
return false;
}
}
}
function hashPsswd($p) {
$options = [
'cost' => 11, // Cout algorithmique
];
// Génération du MDP
$psswd = password_hash($p, PASSWORD_BCRYPT, $options);
return $psswd;
}
function createPsswd($id, $p, $dbgen = "gen") {
global $Conf;
global $Route;
$psswd = hashPsswd($p);
if ($Conf::admin) {
if (substr($Conf->_appname, 0, 3) == "ce_") {
if ($Route->_script == "salaries") {
$sql = 'UPDATE salaries SET userpswd="' . $psswd . '", userpass="xxx" WHERE rowid=' . $id . ';';
} else {
$sql = 'UPDATE users SET userpswd="' . $psswd . '", userpass="xxx" WHERE rowid=' . $id . ';';
}
} else {
$sql = 'UPDATE users SET userpswd="' . $psswd . '", userpass="xxx" WHERE rowid=' . $id . ';';
}
} else {
$sql = 'UPDATE salaries SET userpswd="' . $psswd . '", userpass="xxx" WHERE rowid=' . $id . ';';
}
qSQL($sql);
eLog($sql);
return true;
}
function checkPsswd($p, $pCr) {
// Récupération et vérification du MDP saisi par l'utilisateur
// $p : le pass en clair, $pCr : le pass enregistré et hashé
if (password_verify($p, $pCr)) {
return true;
} else {
return false;
}
}
function generateRandomPassword() {
//Initialize the random password
$password = '';
//Initialize a random desired length
$desired_length = rand(8, 12);
for ($length = 0; $length < $desired_length; $length++) {
//Append a random ASCII character (including symbols)
$password .= chr(rand(44, 122));
}
// On remplace quelques caractères non désirés
$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 eLog($comment, $notif = false) {
global $Session;
global $Route;
global $Conf;
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;
}
} else {
$user = 0;
}
$script = $Route->_script;
$comment = nettoie_input($comment);
$dt = date("Y-m-d H:i:s");
if ($notif) {
$not = 1;
} else {
$not = 2;
}
$sql = 'INSERT INTO z_logs (date, ip, host, adrhost, infos, fk_user, page, commentaire, chk_notif) VALUES ("' . $dt . '", "' . $ip . '", "' . $hn . '", "' . $ha . '", "' . $us . '", "' . $user . '", "' . $script . '", "' . $comment . '", ' . $not . ');';
qSQL($sql, "gen");
if (strpos(strtolower($comment), 'erreur') !== false) {
//! S'il y a spécifiquement une erreur on l'enregistre dans un fichier log à la racine du site
$log_dir = dirname(dirname(dirname(__DIR__))) . "/log/";
$log_file = $log_dir . $Conf->_appname . "_" . date('Y-m-d') . ".log";
// Vérifier que le répertoire existe
if (!is_dir($log_dir)) {
@mkdir($log_dir, 0775, true);
}
// Si le fichier n'existe pas, le créer avec les bonnes permissions
if (!file_exists($log_file)) {
@touch($log_file);
@chmod($log_file, 0664);
}
// Purger les anciens logs (garder seulement les 10 derniers jours)
purge_old_logs($log_dir, $Conf->_appname, 10);
// Écrire dans le log seulement si on peut
if (is_writable($log_file)) {
error_log($dt . ";" . $ip . ";" . $script . ";" . $comment . "\r\n", 3, $log_file);
}
}
}
//! *****************************************************************************************//
//! Fonction de debug globale
function debug($data, $label = '', $level = 4) {
global $Conf;
// Vérifier si le debug est activé et si le niveau est suffisant
if (!isset($Conf->_debug_level) || $Conf->_debug_level < $level) {
return;
}
$debug_info = array(
'timestamp' => date('Y-m-d H:i:s'),
'level' => $level,
'label' => $label,
'type' => gettype($data),
'file' => '',
'line' => '',
'function' => ''
);
// Récupérer les informations de debug
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
if (isset($backtrace[0])) {
$debug_info['file'] = $backtrace[0]['file'];
$debug_info['line'] = $backtrace[0]['line'];
}
if (isset($backtrace[1])) {
$debug_info['function'] = $backtrace[1]['function'];
}
// Préparer le message de debug
$debug_message = "[{$debug_info['timestamp']}] ";
$debug_message .= "DEBUG LVL{$level}";
if ($label) {
$debug_message .= " - {$label}";
}
$debug_message .= " | {$debug_info['file']}:{$debug_info['line']}";
if ($debug_info['function']) {
$debug_message .= " in {$debug_info['function']}()";
}
$debug_message .= "\n";
// Formatter les données
if (is_array($data) || is_object($data)) {
$debug_message .= print_r($data, true);
} elseif (is_bool($data)) {
$debug_message .= $data ? 'true' : 'false';
} elseif (is_null($data)) {
$debug_message .= 'null';
} else {
$debug_message .= $data;
}
$debug_message .= "\n" . str_repeat('-', 80) . "\n";
// Écrire dans le fichier de log si configuré
if (isset($Conf->_log_file_path) && $Conf->_log_file_path) {
// Remplacer la date dans le chemin du fichier si elle existe déjà
$log_file = preg_replace('/_debug_\d{4}-\d{2}-\d{2}\.log$/', '_debug_' . date('Y-m-d') . '.log', $Conf->_log_file_path);
// Si le pattern n'a pas matché, c'est un ancien format, on ajoute la date
if ($log_file == $Conf->_log_file_path && !preg_match('/_' . date('Y-m-d') . '\.log$/', $log_file)) {
$log_file = str_replace('.log', '_' . date('Y-m-d') . '.log', $log_file);
}
// Vérifier que le répertoire existe et est accessible en écriture
$log_dir = dirname($log_file);
if (!is_dir($log_dir)) {
@mkdir($log_dir, 0775, true);
}
// Si le fichier n'existe pas, le créer avec les bonnes permissions
if (!file_exists($log_file)) {
@touch($log_file);
@chmod($log_file, 0664);
}
// Purger les anciens logs
$app_name = isset($Conf->_appname) ? $Conf->_appname : 'app';
purge_old_logs($log_dir, $app_name, 10);
// Écrire dans le log seulement si on peut
if (is_writable($log_file)) {
error_log($debug_message, 3, $log_file);
}
}
// Afficher à l'écran si display_errors est activé et niveau >= 3
if (ini_get('display_errors') && $level >= 3) {
echo "<pre style='background:#f0f0f0; border:1px solid #ccc; padding:10px; margin:10px; font-size:12px;'>";
echo htmlspecialchars($debug_message);
echo "</pre>";
}
}
function logstats($delay = 0, $fk_user = 0, $appname = "") {
global $Conf;
$dt = date("Y-m-d H:i:s");
$exclude_clients_ip = "aucune";
if (isset($Conf->_excludeIp)) {
$exclude_clients_ip = $Conf->_excludeIp;
}
if (isset($Conf->_clientIp)) {
$client_ip = $Conf->_clientIp;
} else {
if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
$client_ip = $_SERVER["HTTP_CLIENT_IP"];
} elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$client_ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
} else {
$client_ip = $_SERVER["REMOTE_ADDR"];
}
}
$verif_ip = strpos($exclude_clients_ip, $client_ip);
if ($verif_ip === false) {
$home = $_SERVER["HOME"];
$doc_root = $_SERVER["DOCUMENT_ROOT"];
$doc_root = substr($doc_root, strlen($home));
$sql = 'INSERT INTO z_stats SET ';
$sql .= 'date="' . $dt . '", ';
$sql .= 'root="' . $doc_root . '", ';
$sql .= 'server_ip="' . $_SERVER["SERVER_ADDR"] . '", ';
$sql .= 'server_soft="' . $_SERVER["SERVER_SOFTWARE"] . '", ';
$sql .= 'server_name="' . $_SERVER["SERVER_NAME"] . '", ';
$sql .= 'client_ip="' . $client_ip . '", ';
$sql .= 'client_browser="' . $_SERVER["HTTP_USER_AGENT"] . '", ';
if (isset($_SERVER["HTTP_REFERER"])) {
$sql .= 'client_origine="' . $_SERVER["HTTP_REFERER"] . '", ';
}
$sql .= 'client_page="' . $_SERVER["REQUEST_URI"] . '", ';
$sql .= 'client_delay=' . str_replace(',', '.', $delay) . ', ';
$sql .= 'appname="' . $appname . '", ';
$sql .= 'fk_user=' . $fk_user . ', ';
$sql .= 'status="' . $_SERVER["REDIRECT_STATUS"] . '";';
// server : 51.255.35.214
$mysqli = new mysqli("localhost", "logs_user", "d66,Logs.User", "logs");
$mysqli->set_charset("utf8");
$mysqli->query($sql);
$mysqli->close();
}
}
//! *****************************************************************************************//
//! nettoie_input : prépare une zone d'un formulaire avant son enregistrement dans la base //
//! En paramètre, on passe la valeur à nettoyer //
//! *****************************************************************************************//
function nettoie_input($data) {
if (ctype_digit((string)$data)) {
$data = intval($data);
} else {
global $Conf;
$dbn = $Conf->_dbname;
$mysqli = new mysqli($Conf->_dbhost, $Conf->_dbuser, $Conf->_dbpass, $dbn);
$mysqli->set_charset("utf8");
$data = mysqli_real_escape_string($mysqli, $data);
// $data = addcslashes($data, '%_');
$mysqli->close();
}
return $data;
}
function str_normalize($string, $minuscules = true) {
//! Normalise une chaîne de caractères en remplaçant tous les caractères accentués, les espaces et caractères spéciaux
$result = "";
$string = trim($string); // on efface tous les espaces à gauche et à droite
if (strlen($string) > 0) {
if ($minuscules) {
$result = strtolower($string);
} else {
$result = $string;
}
$result = str_replace(" ", "_", $result);
//$result = str_replace("-", "_", $result);
//$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);
//! Ajout du 08/12/2015
$result = str_replace("?", "_", $result);
$result = trim($result);
}
return $result;
}
function affiche_date($ladate) {
/**
* This is a sample function to illustrate additional PHP formatter
* options.
* @param $ladate date au format MySQL
*
* @return String date au format Fr dd/mm/yyyy
* @author D6SOFT
*
*/
//! 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
//! $format="MF" du format MySQL yyyy-mm-dd au format Fr dd/mm/yyyy
//! $format="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 loadtel($numero, $prefix = "+33") {
//! retourne un numéro de téléphone sans espace et . et avec le préfixe devant : +33 par défaut
$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 = " ") {
//! formate le n° de téléphone de 651234567 ou 0651234567 en 06 51 23 45 67
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;
}
//! *****************************************************************************************//
//! Fonction pour purger les anciens fichiers de log
function purge_old_logs($log_dir, $app_name, $days_to_keep = 10) {
// Vérifier que le répertoire existe
if (!is_dir($log_dir)) {
return;
}
// Date limite pour conserver les logs
$date_limit = strtotime("-{$days_to_keep} days");
// Patterns des fichiers à purger
$patterns = array(
$app_name . '_????-??-??.log',
$app_name . '_debug_????-??-??.log'
);
foreach ($patterns as $pattern) {
$files = glob($log_dir . $pattern);
if ($files) {
foreach ($files as $file) {
// Extraire la date du nom de fichier
if (preg_match('/(\d{4}-\d{2}-\d{2})\.log$/', $file, $matches)) {
$file_date = strtotime($matches[1]);
// Si le fichier est plus ancien que la limite, le supprimer
if ($file_date < $date_limit) {
@unlink($file);
}
}
}
}
}
}

View File

@@ -19,7 +19,11 @@ class Session {
//! ce n'est pas un intranet, donc un site vitrine public, on doit laisser passer mais mémoriser //! ce n'est pas un intranet, donc un site vitrine public, on doit laisser passer mais mémoriser
$sql = "SELECT s.* FROM z_sessions s WHERE s.sid='" . session_id() . "';"; $sql = "SELECT s.* FROM z_sessions s WHERE s.sid='" . session_id() . "';";
$res = qSQL($sql, "gen"); $res = qSQL($sql, "gen");
$this->_user = $res->fetch_assoc(); if ($res instanceof PDOStatement) {
$this->_user = $res->fetch(PDO::FETCH_ASSOC);
} else {
$this->_user = false;
}
if (empty($this->_user)) { if (empty($this->_user)) {
//! pas de session pour lui, on en crée une //! pas de session pour lui, on en crée une
if (!empty($_SERVER["HTTP_CLIENT_IP"])) { if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
@@ -64,7 +68,11 @@ class Session {
function getUserInfos($leScript, $Conf) { function getUserInfos($leScript, $Conf) {
$sql = "SELECT s.data, s.ip, s.browser, u.* FROM z_sessions s INNER JOIN users u ON s.sid='" . session_id() . "' AND s.fk_user=u.rowid;"; $sql = "SELECT s.data, s.ip, s.browser, u.* FROM z_sessions s INNER JOIN users u ON s.sid='" . session_id() . "' AND s.fk_user=u.rowid;";
$res = qSQL($sql, "gen"); $res = qSQL($sql, "gen");
$this->_user = $res->fetch_assoc(); if ($res instanceof PDOStatement) {
$this->_user = $res->fetch(PDO::FETCH_ASSOC);
} else {
$this->_user = false;
}
if (empty($this->_user)) { if (empty($this->_user)) {
eLog('Erreur Session.getUserInfos User inconnu dans Session ' . session_id()); eLog('Erreur Session.getUserInfos User inconnu dans Session ' . session_id());
$this->_user = FALSE; $this->_user = FALSE;
@@ -88,7 +96,7 @@ class Session {
eLog("Erreur cet utilisateur " . $this->_user["username"] . " a changé de navigateur"); eLog("Erreur cet utilisateur " . $this->_user["username"] . " a changé de navigateur");
} }
} }
$res->free(); // PDO ne nécessite pas de free()
} }
public function set_data($cle, $valeur) { public function set_data($cle, $valeur) {
@@ -113,8 +121,11 @@ class Session {
global $Conf; global $Conf;
$sql = "SELECT s.data FROM z_sessions s WHERE s.sid='" . session_id() . "';"; $sql = "SELECT s.data FROM z_sessions s WHERE s.sid='" . session_id() . "';";
$res = qSQL($sql, "gen"); $res = qSQL($sql, "gen");
$rec = $res->fetch_assoc(); if ($res instanceof PDOStatement) {
$res->free(); $rec = $res->fetch(PDO::FETCH_ASSOC);
} else {
$rec = false;
}
if ($rec["data"] == "") { if ($rec["data"] == "") {
$tabdata = array(); $tabdata = array();
} else { } else {
@@ -149,8 +160,12 @@ class Session {
if ($niveau >= 0 && $niveau < 10) { if ($niveau >= 0 && $niveau < 10) {
$sql = "SELECT s.a" . $niveau . " as data FROM z_sessions s WHERE s.sid='" . session_id() . "';"; $sql = "SELECT s.a" . $niveau . " as data FROM z_sessions s WHERE s.sid='" . session_id() . "';";
$res = qSQL($sql, "gen"); $res = qSQL($sql, "gen");
$rec = $res->fetch_assoc(); if ($res instanceof PDOStatement) {
$res->free(); $rec = $res->fetch(PDO::FETCH_ASSOC);
} else {
$rec = false;
}
// PDO ne nécessite pas de free()
return $rec["data"]; return $rec["data"];
} }
} }

View File

@@ -2,9 +2,9 @@
<div class="left ml-1">&copy; <?= $Conf->_brandname; ?> <?= date("Y"); ?></div> <div class="left ml-1">&copy; <?= $Conf->_brandname; ?> <?= date("Y"); ?></div>
<div class="center">Version <div class="center">Version
<?php <?php
echo $Conf->_appversion . ' - IP : ' . $Conf->_clientIp; echo $Conf->_appversion . ' - ' . $Conf->_clientIp;
$delay = round(microtime(true) - $tpsdebut, 3); $delay = round(microtime(true) - $tpsdebut, 3);
echo ' - Page générée en ' . $delay . ' secondes'; echo ' - ' . $delay . 's';
logstats($delay); logstats($delay);
?> ?>
</div> </div>