]> SAFE projects GIT repository - jmp/mailleur/commitdiff
Addition de gestbl to work on data display
authorJean-Marc Pigeon <jmp@safe.c>
Wed, 10 Dec 2025 20:17:47 +0000 (15:17 -0500)
committerJean-Marc Pigeon <jmp@safe.c>
Wed, 10 Dec 2025 20:17:47 +0000 (15:17 -0500)
www/chat3.php [new file with mode: 0644]
www/chat4.php [new file with mode: 0644]
www/exetbl.php [new file with mode: 0644]
www/gestbl.php [new file with mode: 0644]

diff --git a/www/chat3.php b/www/chat3.php
new file mode 100644 (file)
index 0000000..9debfeb
--- /dev/null
@@ -0,0 +1,169 @@
+<?php
+/* =======================================================
+   Super-DB One-File avec pagination SQL et multi-colonnes
+   Layout corrigé : [Lang] seule ligne, [Recherche][Pagination] sur ligne suivante
+   ======================================================= */
+class TableFilter
+{
+    private PDO $pdo;
+    private string $table;
+    private array $columns;
+
+    public function __construct(PDO $pdo, string $table, array $columns)
+    {
+        $this->pdo = $pdo;
+        $this->table = $table;
+        $this->columns = $columns;
+    }
+
+    public function fetchFiltered(array $selectedColumns, string $search, int $limit): array
+    {
+        if(empty($selectedColumns) || trim($search)===''){
+            $sql = "SELECT * FROM {$this->table} ORDER BY creation DESC LIMIT :limit";
+            $stmt = $this->pdo->prepare($sql);
+            $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchAll(PDO::FETCH_ASSOC);
+        }
+
+        $whereParts = [];
+        $params = [];
+        foreach($selectedColumns as $i => $col){
+            if(!in_array($col,$this->columns,true)) continue;
+            $whereParts[] = "$col ILIKE :s$i";
+            $params[":s$i"] = "%$search%";
+        }
+        $where = implode(" OR ", $whereParts);
+        $sql = "SELECT * FROM {$this->table} WHERE $where ORDER BY creation DESC LIMIT :limit";
+        $stmt = $this->pdo->prepare($sql);
+        foreach($params as $k=>$v) $stmt->bindValue($k,$v);
+        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+        $stmt->execute();
+        return $stmt->fetchAll(PDO::FETCH_ASSOC);
+    }
+}
+
+/* =======================================================
+   Gestion AJAX
+   ======================================================= */
+if(php_sapi_name()!=='cli' && isset($_POST['search'])){
+    header("Content-Type: application/json");
+    $pdo = new PDO("pgsql:host=localhost;dbname=mailleur","mailleur","mailleurpass",
+        [PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
+    $filter = new TableFilter($pdo,"actions",["remoteip","reverse","creation"]);
+    $selected = $_POST["columns"]??[];
+    $search   = $_POST["search"]??"";
+    $limit    = (int)($_POST["limit"]??20);
+    $data = $filter->fetchFiltered($selected,$search,$limit);
+    echo json_encode($data);
+    exit;
+}
+
+/* =======================================================
+   Page HTML
+   ======================================================= */
+$lang = $_GET["lang"]??"en";
+$labels = ["en"=>["Remote IP","Reverse","Creation"],"fr"=>["IP distante","Reverse","Création"]];
+$colKeys = ["remoteip","reverse","creation"];
+$L = $labels[$lang];
+?>
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Bigre Super-DB Paginated JMP</title>
+<style>
+.table-filter-row { display:flex; gap:8px; align-items:center; margin-bottom:6px; flex-wrap: wrap; }
+#langSelect-row { display:block; margin-bottom:12px; } /* isole Lang sur sa propre ligne */
+table{border-collapse:collapse;width:100%;}
+th,td{padding:6px;border:1px solid #888;}
+.filter-col{cursor:pointer;color:#003;background:#f7f7ff;}
+.filter-col.active{background:#cce;}
+</style>
+</head>
+<body>
+
+<!-- Ligne 1 : Langue -->
+<div id="langSelect-row">
+    <select id="langSelect" onchange="window.location='?lang='+this.value;">
+        <option value="en" <?= $lang==="en"?"selected":"" ?>>English</option>
+        <option value="fr" <?= $lang==="fr"?"selected":"" ?>>Français</option>
+    </select>
+</div>
+
+<!-- Ligne 2 : Recherche + Pagination -->
+<div class="table-filter-row">
+    <input type="text" id="searchInput" placeholder="Type to filter…">
+    <select id="limitSelect">
+        <?php foreach([5,10,20,40,80] as $n): ?>
+            <option value="<?= $n ?>" <?= $n===20?"selected":"" ?>><?= $n ?></option>
+        <?php endforeach; ?>
+    </select>
+</div>
+
+<!-- Tableau -->
+<table id="dataTable">
+    <thead>
+        <tr>
+            <?php foreach($L as $i=>$label): ?>
+                <th class="filter-col" data-col="<?= $colKeys[$i] ?>"><?= htmlspecialchars($label) ?></th>
+            <?php endforeach; ?>
+        </tr>
+    </thead>
+    <tbody></tbody>
+</table>
+
+<script>
+let columns=[];
+const searchInput = document.getElementById("searchInput");
+const limitSelect = document.getElementById("limitSelect");
+
+// Header click pour multi-colonnes
+document.querySelectorAll(".filter-col").forEach(th=>{
+    th.addEventListener("click",()=>{
+        const col = th.dataset.col;
+        if(columns.includes(col)){
+            columns = columns.filter(c=>c!==col);
+            th.classList.remove("active");
+        } else {
+            columns.push(col);
+            th.classList.add("active");
+        }
+        refresh();
+    });
+});
+
+// Recherche et changement de limit
+searchInput.addEventListener("input",refresh);
+limitSelect.addEventListener("change",refresh);
+
+// AJAX vers le même fichier
+function refresh(){
+    const form = new FormData();
+    form.append("search", searchInput.value);
+    form.append("limit", limitSelect.value);
+    columns.forEach(c=>form.append("columns[]",c));
+
+    fetch("", {method:"POST", body:form})
+        .then(r=>r.json())
+        .then(json=>renderTable(json));
+}
+
+// Remplissage tableau
+function renderTable(rows){
+    const tbody=document.querySelector("#dataTable tbody");
+    tbody.innerHTML="";
+    for(const row of rows){
+        const tr=document.createElement("tr");
+        tr.innerHTML=`<td>${row.remoteip}</td><td>${row.reverse}</td><td>${row.creation}</td>`;
+        tbody.appendChild(tr);
+    }
+}
+
+// Load initial
+refresh();
+</script>
+
+</body>
+</html>
+
diff --git a/www/chat4.php b/www/chat4.php
new file mode 100644 (file)
index 0000000..91da797
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+/* =======================================================
+   Super-DB One-File corrigé : affichage NULL explicite
+   ======================================================= */
+class TableFilter
+{
+    private PDO $pdo;
+    private string $table;
+    private array $columns;
+
+    public function __construct(PDO $pdo, string $table, array $columns)
+    {
+        $this->pdo = $pdo;
+        $this->table = $table;
+        $this->columns = $columns;
+    }
+
+    public function fetchFiltered(array $selectedColumns, string $search, int $limit): array
+    {
+        if(empty($selectedColumns)) $selectedColumns = $this->columns;
+
+        if(trim($search)===''){
+            $sql = "SELECT * FROM {$this->table} ORDER BY creation DESC LIMIT :limit";
+            $stmt = $this->pdo->prepare($sql);
+            $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchAll(PDO::FETCH_ASSOC);
+        }
+
+        $whereParts = [];
+        $params = [];
+        foreach($selectedColumns as $i => $col){
+            if(!in_array($col,$this->columns,true)) continue;
+            $key = ":s$i";
+            $whereParts[] = "$col ILIKE $key";
+            $params[$key] = "%$search%";
+        }
+
+        $where = implode(" OR ", $whereParts);
+        $sql = "SELECT * FROM {$this->table} WHERE $where ORDER BY creation DESC LIMIT :limit";
+        $stmt = $this->pdo->prepare($sql);
+
+        foreach($params as $k=>$v){
+            $stmt->bindValue($k,$v,PDO::PARAM_STR);
+        }
+        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+        $stmt->execute();
+        return $stmt->fetchAll(PDO::FETCH_ASSOC);
+    }
+}
+
+/* =======================================================
+   Gestion AJAX
+   ======================================================= */
+if(php_sapi_name()!=='cli' && isset($_POST['search'])){
+    header("Content-Type: application/json");
+    $pdo = new PDO("pgsql:host=localhost;dbname=mailleur","mailleur","mailleurpass",
+        [PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
+    $filter = new TableFilter($pdo,"actions",["remoteip","reverse","creation"]);
+
+    $selected = $_POST["columns"]??[];
+    if(empty($selected)) $selected = ["remoteip","reverse","creation"];
+
+    $search   = $_POST["search"]??"";
+    $limit    = (int)($_POST["limit"]??20);
+    $data = $filter->fetchFiltered($selected,$search,$limit);
+    echo json_encode(["rows"=>$data,"search"=>$search]);
+    exit;
+}
+
+/* =======================================================
+   Page HTML
+   ======================================================= */
+$lang = $_GET["lang"]??"en";
+$labels = ["en"=>["Remote IP","Reverse","Creation"],"fr"=>["IP distante","Reverse","Création"]];
+$colKeys = ["remoteip","reverse","creation"];
+$L = $labels[$lang];
+?>
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Super-DB Paginated Highlight</title>
+<style>
+.table-filter-row { display:flex; gap:8px; align-items:center; margin-bottom:6px; flex-wrap: wrap; }
+#langSelect-row { display:block; margin-bottom:12px; }
+table{border-collapse:collapse;width:100%;}
+th,td{padding:6px;border:1px solid #888;}
+.filter-col{cursor:pointer;color:#003;background:#f7f7ff;}
+.filter-col.active{background:#cce;}
+.highlight{background:#ff9;color:#000;}
+</style>
+</head>
+<body>
+
+<!-- Ligne 1 : Langue -->
+<div id="langSelect-row">
+    <select id="langSelect" onchange="window.location='?lang='+this.value;">
+        <option value="en" <?= $lang==="en"?"selected":"" ?>>English</option>
+        <option value="fr" <?= $lang==="fr"?"selected":"" ?>>Français</option>
+    </select>
+</div>
+
+<!-- Ligne 2 : Recherche + Pagination -->
+<div class="table-filter-row">
+    <input type="text" id="searchInput" placeholder="Type to filter…">
+    <select id="limitSelect">
+        <?php foreach([5,10,20,40,80] as $n): ?>
+            <option value="<?= $n ?>" <?= $n===20?"selected":"" ?>><?= $n ?></option>
+        <?php endforeach; ?>
+    </select>
+</div>
+
+<!-- Tableau -->
+<table id="dataTable">
+    <thead>
+        <tr>
+            <?php foreach($L as $i=>$label): ?>
+                <th class="filter-col" data-col="<?= $colKeys[$i] ?>"><?= htmlspecialchars($label) ?></th>
+            <?php endforeach; ?>
+        </tr>
+    </thead>
+    <tbody></tbody>
+</table>
+
+<script>
+let columns=[];
+let currentSearch="";
+
+const searchInput = document.getElementById("searchInput");
+const limitSelect = document.getElementById("limitSelect");
+
+// Header click pour multi-colonnes
+document.querySelectorAll(".filter-col").forEach(th=>{
+    th.addEventListener("click",()=>{
+        const col = th.dataset.col;
+        if(columns.includes(col)){
+            columns = columns.filter(c=>c!==col);
+            th.classList.remove("active");
+        } else {
+            columns.push(col);
+            th.classList.add("active");
+        }
+        refresh();
+    });
+});
+
+// Recherche et changement de limit
+searchInput.addEventListener("input",refresh);
+limitSelect.addEventListener("change",refresh);
+
+// AJAX vers le même fichier
+function refresh(){
+    currentSearch = searchInput.value.trim();
+    const form = new FormData();
+    form.append("search", currentSearch);
+    form.append("limit", limitSelect.value);
+
+    const activeColumns = columns.length ? columns : ["remoteip","reverse","creation"];
+    activeColumns.forEach(c=>form.append("columns[]",c));
+
+    fetch("", {method:"POST", body:form})
+        .then(r=>r.json())
+        .then(json=>renderTable(json.rows,json.search));
+}
+
+// Remplissage tableau avec surbrillance
+function renderTable(rows,search){
+    console.log("AJAX: nombre de lignes reçues =", rows.length);
+    const tbody=document.querySelector("#dataTable tbody");
+    tbody.innerHTML="";
+    const re = search ? new RegExp(search,"gi") : null;
+    for(const row of rows){
+        const tr=document.createElement("tr");
+        const cells = ["remoteip","reverse","creation"].map(c=>{
+            // NULL -> "NULL"
+            let val = row[c] === null ? "NULL" : row[c];
+            if(re) val = val.replace(re,m=>`<span class="highlight">${m}</span>`);
+            return `<td>${val}</td>`;
+        });
+        tr.innerHTML=cells.join("");
+        tbody.appendChild(tr);
+    }
+}
+
+// Load initial
+refresh();
+</script>
+
+</body>
+</html>
+
diff --git a/www/exetbl.php b/www/exetbl.php
new file mode 100644 (file)
index 0000000..83a7faa
--- /dev/null
@@ -0,0 +1,12 @@
+require_once 'TableFilter.php';
+$pdo = new PDO("pgsql:host=localhost;dbname=mailleur","mailleur","mailleurpass", [
+    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
+]);
+
+$filter = new TableFilter($pdo, "actions", ["remoteip","reverse","creation"]);
+
+// Exemple simple :
+$rows = $filter->fetchFiltered(["remoteip","reverse"], "localhost", 20);
+
+// Ensuite ton PHP construit son HTML/JS à partir de $rows
+
diff --git a/www/gestbl.php b/www/gestbl.php
new file mode 100644 (file)
index 0000000..06391b1
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+/* =======================================================
+   TableFilter.php -->gestbl.php
+   Generic and autonomous class for Mailleur
+   ======================================================= */
+class TableFilter
+{
+    private PDO $pdo;
+    private string $table;
+    private array $columns;
+
+    /**
+     * Constructor
+     * @param PDO $pdo      PDO connection
+     * @param string $table Table name
+     * @param array $columns Columns available for filtering
+     */
+    public function __construct(PDO $pdo, string $table, array $columns)
+    {
+        $this->pdo = $pdo;
+        $this->table = $table;
+        $this->columns = $columns;
+    }
+
+    /**
+     * Fetch filtered rows from the table
+     * 
+     * @param array $selectedColumns Columns to filter (subset of $this->columns)
+     * @param string $search Search text
+     * @param int $limit Number of rows to return
+     * @return array Associative array of rows
+     */
+    public function fetchFiltered(array $selectedColumns, string $search = '', int $limit = 20): array
+    {
+        // If no columns selected, use all
+        if (empty($selectedColumns)) $selectedColumns = $this->columns;
+
+        // If search is empty, just SELECT with LIMIT
+        if (trim($search) === '') {
+            $sql = "SELECT * FROM {$this->table} ORDER BY 1 LIMIT :limit";
+            $stmt = $this->pdo->prepare($sql);
+            $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+            $stmt->execute();
+            return $stmt->fetchAll(PDO::FETCH_ASSOC);
+        }
+
+        // Build WHERE clause for multiple columns
+        $whereParts = [];
+        $params = [];
+        foreach ($selectedColumns as $i => $col) {
+            if (!in_array($col, $this->columns, true)) continue;
+            $key = ":s$i";
+            $whereParts[] = "$col ILIKE $key";
+            $params[$key] = "%$search%";
+        }
+        $where = implode(" OR ", $whereParts);
+
+        // Prepare and execute statement
+        $sql = "SELECT * FROM {$this->table} WHERE $where ORDER BY 1 LIMIT :limit";
+        $stmt = $this->pdo->prepare($sql);
+
+        foreach ($params as $k => $v) {
+            $stmt->bindValue($k, $v, PDO::PARAM_STR);
+        }
+        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
+
+        $stmt->execute();
+
+        // Return associative array
+        return $stmt->fetchAll(PDO::FETCH_ASSOC);
+    }
+}
+