Plugin Directory

Changeset 3381077


Ignore:
Timestamp:
10/20/2025 07:05:59 AM (5 months ago)
Author:
d_alinus2004
Message:

Update trunk to version 1.2.2 – info box toggle, loader, persistent UI memory

Location:
kombatoptimizer/trunk
Files:
4 added
3 deleted
4 edited

Legend:

Unmodified
Added
Removed
  • kombatoptimizer/trunk/assets/admin.css

    r3379089 r3381077  
    115115    100% { opacity: 0; display: none; }
    116116}
     117
     118
     119/* 🔄 Loader vizual Kombat Optimizer */
     120#kombat-loader {
     121  display: none;
     122  margin-top: 20px;
     123  text-align: center;
     124}
     125
     126#kombat-loader .spinner {
     127  width: 40px;
     128  height: 40px;
     129  border: 4px solid #ccc;
     130  border-top: 4px solid #2271b1;
     131  border-radius: 50%;
     132  animation: kombat-spin 0.8s linear infinite;
     133  margin: 0 auto;
     134}
     135
     136@keyframes kombat-spin {
     137  0% { transform: rotate(0deg); }
     138  100% { transform: rotate(360deg); }
     139}
     140
     141#close-info-box {
     142  position: absolute;
     143  top: 8px;
     144  right: 10px;
     145  background: none;
     146  border: none;
     147  font-size: 16px;
     148  color: #666;
     149  cursor: pointer;
     150}
     151
     152#close-info-box:hover {
     153  color: #000;
     154}
     155
     156#kombat-info-restore {
     157  text-align: center;
     158}
  • kombatoptimizer/trunk/assets/admin.js

    r3379089 r3381077  
    11document.addEventListener("DOMContentLoaded", function () {
    2     const buttons = document.querySelectorAll(".kombat-tab-buttons button");
    3     const contents = document.querySelectorAll(".kombat-tab-content");
     2  // 🔁 Tabs
     3  const buttons = document.querySelectorAll(".kombat-tab-buttons button");
     4  const contents = document.querySelectorAll(".kombat-tab-content");
    45
    5     function activateTab(tabId) {
    6         buttons.forEach((b) => b.classList.remove("active"));
    7         contents.forEach((c) => c.classList.remove("active"));
    8         const btn = document.querySelector(`.kombat-tab-buttons button[data-target="${tabId}"]`);
    9         const content = document.getElementById(tabId);
    10         if (btn && content) {
    11             btn.classList.add("active");
    12             content.classList.add("active");
    13         }
     6  function activateTab(tabId) {
     7    buttons.forEach((b) => b.classList.remove("active"));
     8    contents.forEach((c) => c.classList.remove("active"));
     9    const btn = document.querySelector(`.kombat-tab-buttons button[data-target="${tabId}"]`);
     10    const content = document.getElementById(tabId);
     11    if (btn && content) {
     12      btn.classList.add("active");
     13      content.classList.add("active");
    1414    }
     15  }
    1516
    16     buttons.forEach((btn) => {
    17         btn.addEventListener("click", function () {
    18             const target = btn.dataset.target;
    19             localStorage.setItem("kombatoptimizer_active_tab", target);
    20             activateTab(target);
    21         });
     17  buttons.forEach((btn) => {
     18    btn.addEventListener("click", function () {
     19      const target = btn.dataset.target;
     20      localStorage.setItem("kombatoptimizer_active_tab", target);
     21      activateTab(target);
    2222    });
     23  });
    2324
    24     const savedTab = localStorage.getItem("kombatoptimizer_active_tab");
    25     activateTab(savedTab || "tab-generator");
     25  const savedTab = localStorage.getItem("kombatoptimizer_active_tab");
     26  activateTab(savedTab || "tab-generator");
     27
     28  // ✅ Mesaje temporare
     29  const messages = document.querySelectorAll('.kombat-success:not(.kombat-persistent)');
     30  messages.forEach(msg => {
     31    setTimeout(() => {
     32      msg.style.display = 'none';
     33    }, 3000);
     34  });
     35
     36  // ✖ Închide mesajul informativ și salvează preferința
     37  const closeBtn = document.getElementById("close-info-box");
     38  const infoBox = document.getElementById("kombat-info-box");
     39  const restoreBox = document.getElementById("kombat-info-restore");
     40  const restoreLink = restoreBox ? restoreBox.querySelector("a") : null;
     41
     42  if (localStorage.getItem("kombatoptimizer_info_closed") === "true") {
     43    if (infoBox) infoBox.style.display = "none";
     44    if (restoreBox) restoreBox.style.display = "block";
     45  } else {
     46    if (restoreBox) restoreBox.style.display = "none";
     47  }
     48
     49  if (closeBtn && infoBox) {
     50    closeBtn.addEventListener("click", function () {
     51      infoBox.style.display = "none";
     52      localStorage.setItem("kombatoptimizer_info_closed", "true");
     53      if (restoreBox) restoreBox.style.display = "block";
     54    });
     55  }
     56
     57  if (restoreLink && infoBox) {
     58    restoreLink.addEventListener("click", function () {
     59      localStorage.removeItem("kombatoptimizer_info_closed");
     60      infoBox.style.display = "block";
     61      if (restoreBox) restoreBox.style.display = "none";
     62    });
     63  }
     64
     65  // 🔄 Loader vizual la submit
     66  const form = document.querySelector("#tab-generator form");
     67  const loader = document.getElementById("kombat-loader");
     68
     69  if (form && loader) {
     70    form.addEventListener("submit", function () {
     71      loader.style.display = "block";
     72    });
     73  }
    2674});
    27 
    28 document.addEventListener("DOMContentLoaded", function () {
    29     const messages = document.querySelectorAll('.kombat-success:not(.kombat-persistent)');
    30     messages.forEach(msg => {
    31         setTimeout(() => {
    32             msg.style.display = 'none';
    33         }, 3000); // doar cele fără .kombat-persistent dispar
    34     });
    35 });
    36 
    37 
  • kombatoptimizer/trunk/inc/generate-webp.php

    r3379089 r3381077  
    22if (!defined('ABSPATH')) exit;
    33
    4 // 🔧 Generează WebP din folderul selectat
     4// 🔧 Generează WebP din folderul selectat, cu limită de procesare
    55if (!function_exists('generate_webp_from_folder')) {
    66    function generate_webp_from_folder($subfolder) {
     
    1313        $target_dir = trailingslashit($upload_dir['basedir']) . 'kombatoptimizer/' . $subfolder;
    1414
     15        // 🛑 Verificare existență folder sursă
    1516        if (!is_dir($source_dir)) {
    1617            kombatoptimizer_log("Folder sursă invalid: $source_dir");
     
    1819        }
    1920
     21        // 📦 Creează folderul destinație dacă nu există
    2022        if (!file_exists($target_dir)) {
    2123            wp_mkdir_p($target_dir);
     
    2426        $converted = 0;
    2527        $skipped   = 0;
     28        $processed = 0;
     29        $max_images = 150; // 🔒 Limită maximă de imagini procesate
    2630
     31        // 🔁 Iterare recursivă prin fișierele din folderul sursă
    2732        $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source_dir));
    2833        foreach ($rii as $file) {
    2934            if ($file->isDir()) continue;
    3035
     36            // 🛑 Oprire dacă s-a atins limita
     37            if ($processed >= $max_images) {
     38                kombatoptimizer_log("Limită atinsă: s-au procesat doar $max_images imagini.");
     39                break;
     40            }
     41
    3142            $image_path = $file->getPathname();
     43
     44            // 🎯 Filtrare doar JPG/JPEG/PNG
    3245            if (!preg_match('/\.(jpg|jpeg|png)$/i', $image_path)) continue;
    3346
     
    4558            }
    4659
     60            // ⏭️ Dacă fișierul WebP există deja, îl ignorăm
    4761            if (file_exists($webp_path)) {
    4862                $skipped++;
     63                $processed++;
    4964                continue;
    5065            }
     
    5368            if (preg_match('/\.png$/i', $image_path)) {
    5469                $image = @imagecreatefrompng($image_path);
    55                 if (!$image) { $skipped++; continue; }
     70                if (!$image) { $skipped++; $processed++; continue; }
    5671                if (!imageistruecolor($image)) imagepalettetotruecolor($image);
    5772                imagealphablending($image, false);
     
    5974            } else {
    6075                $image = @imagecreatefromstring(file_get_contents($image_path));
    61                 if (!$image) { $skipped++; continue; }
     76                if (!$image) { $skipped++; $processed++; continue; }
    6277            }
    6378
     79            // 💾 Salvare WebP
    6480            imagewebp($image, $webp_path, 85);
    6581
     82            // 🧼 Verificare fișier corupt
    6683            if (filesize($webp_path) < 100) {
    6784                wp_delete_file($webp_path);
     
    7390
    7491            imagedestroy($image);
     92            $processed++;
    7593        }
    7694
     95        // 📊 Log final
    7796        kombatoptimizer_log("Generare WebP din '$subfolder': convertite=$converted, ignorate=$skipped");
    7897
     
    8099    }
    81100}
     101
     102/**
     103 * 🔍 Returnează doar folderele de tip an (ex: 2025, 2024) din wp-content/uploads/
     104 * Ignoră folderele non-media (kombatoptimizer, rank_math, etc.)
     105 */
     106function get_year_folders() {
     107    $upload_dir = wp_upload_dir()['basedir'];
     108    $excluded = ['kombatoptimizer', 'rank_math', 'elementor', 'wpforms', 'woocommerce', 'siteorigin'];
     109
     110    // 📁 Obține toate subfolderele din uploads/
     111    $folders = glob($upload_dir . '/*', GLOB_ONLYDIR);
     112
     113    // 🧹 Filtrare: doar cele care sunt ani și nu conțin nume excluse
     114    $year_folders = array_filter($folders, function($folder) use ($excluded) {
     115        $basename = basename($folder);
     116
     117        // ✅ Trebuie să fie format de an (ex: 2025)
     118        if (!preg_match('/^\d{4}$/', $basename)) return false;
     119
     120        // ❌ Excludem folderele non-media
     121        foreach ($excluded as $ex) {
     122            if (stripos($folder, $ex) !== false) return false;
     123        }
     124
     125        return true;
     126    });
     127
     128    return $year_folders; // 🔙 Returnează array cu căi complete
     129}
  • kombatoptimizer/trunk/kombat-optimizer.php

    r3379089 r3381077  
    44Plugin URI: https://webdesign-profesional.com/kombat-optimizer
    55Description: Optimizare automată WebP, preload inteligent și monitorizare TTFB. Include cron săptămânal și scanare pe foldere.
    6 Version: 1.2.1
     6Version: 1.2.2
    77Author: Alin
    88Author URI: https://webdesign-profesional.com
     
    7272
    7373// 🔧 Tab 1: Generator WebP
     74
    7475echo '<div id="tab-generator" class="kombat-tab-content">';
    7576echo '<h3>🔧 Generator & Optimizare WebP</h3>';
     
    7980echo '<p><label for="webp_folder"><strong>Selectează folderul:</strong></label><br>';
    8081echo '<select name="webp_folder" id="webp_folder">';
    81 foreach ($folders as $folder) {
     82foreach (get_year_folders() as $folder) {
    8283    $name = str_replace($base_dir, '', $folder);
    8384    echo '<option value="' . esc_attr($name) . '">' . esc_html($name) . '</option>';
    8485}
    85 echo '</select></p>';
     86echo '</select> </p>';
    8687
    8788echo '<p>';
     
    8990echo '<button type="submit" name="optimize_webp" class="kombat-btn">Optimizează imaginile WebP</button>';
    9091echo '</p>';
    91 echo '</form>';
     92
     93echo '<div id="kombat-loader">';
     94echo '<div class="spinner"></div>';
     95echo '<p>Se procesează imaginile... te rugăm să aștepți.</p>';
     96echo '</div>';
     97
     98
     99echo '</form>';
     100
     101
     102// ℹ️ Mesaj explicativ permanent sub butoane
     103echo '<div id="kombat-info-box" class="notice notice-info" style="margin-top:20px; position:relative;">';
     104echo '<button type="button" id="close-info-box" style="position:absolute; top:8px; right:10px; background:none; border:none; font-size:16px; cursor:pointer;">✖</button>';
     105echo '<p><strong>ℹ️ Cum funcționează:</strong></p>';
     106echo '<ul style="margin-left:20px;">';
     107echo '<li>Se afișează doar folderele de tip <code>uploads/2025</code>, <code>uploads/2024</code> etc. — cele care conțin imagini media.</li>';
     108echo '<li>Folderele interne ale pluginurilor (ex: <code>rank_math</code>, <code>kombatoptimizer</code>) sunt automat excluse.</li>';
     109echo '<li>Se procesează maxim <strong>150 imagini</strong> per folder, pentru a evita blocarea serverului.</li>';
     110echo '<li>Imaginile deja convertite în WebP sunt ignorate automat.</li>';
     111echo '</ul>';
     112echo '<p>Pentru rezultate optime, selectează folderul corespunzător anului în care au fost încărcate imaginile.</p>';
     113echo '</div>';
     114echo '<p id="kombat-info-restore" style="margin-top:10px;">';
     115echo '<a href="#" style="text-decoration:none;" onclick="return false;">🔄 Afișează din nou explicația</a>';
     116echo '</p>';
    92117
    93118// 🔁 Procesare acțiuni POST
     
    187212
    188213echo '<select name="scan_folder">';
    189 foreach ($folders as $folder) {
     214foreach (get_year_folders() as $folder) {
    190215    $name = str_replace($base_dir, '', $folder);
    191216    echo '<option value="' . esc_attr($name) . '">' . esc_html($name) . '</option>';
Note: See TracChangeset for help on using the changeset viewer.