Plugin Directory

Changeset 3451104


Ignore:
Timestamp:
01/31/2026 05:25:05 PM (3 weeks ago)
Author:
blaminhor
Message:
  1. 1.4.6
Location:
blaminhor-essentials
Files:
100 added
27 edited

Legend:

Unmodified
Added
Removed
  • blaminhor-essentials/trunk/assets/js/modules.js

    r3450705 r3451104  
    876876            this.ajaxurl = config.ajaxurl || ajaxurl;
    877877            this.strings = config.strings || {};
     878            this.maxUploadSize = config.maxUploadSize || 0;
     879            this.maxUploadLabel = config.maxUploadLabel || '';
    878880            this.bindEvents();
    879881            this.checkAutoBackup();
     
    890892
    891893            $(document).off('click' + ns, '.ap-backup-download').on('click' + ns, '.ap-backup-download', function() {
    892                 var $row = $(this).closest('tr');
    893                 var archives = $row.data('archives');
    894                 if (archives && archives.length) {
    895                     self.downloadBackupArchives(archives);
    896                 }
     894                self.downloadBackupBundle($(this));
    897895            });
    898896
     
    12081206            var self = this;
    12091207            var validExtensions = ['.zip'];
     1208            var skippedTooLarge = [];
    12101209
    12111210            for (var i = 0; i < files.length; i++) {
     
    12151214                if (validExtensions.indexOf(ext) === -1) {
    12161215                    continue; // Skip non-ZIP files
     1216                }
     1217
     1218                // Check file size against server limit.
     1219                if (this.maxUploadSize && file.size > this.maxUploadSize) {
     1220                    skippedTooLarge.push(file.name + ' (' + this.formatFileSize(file.size) + ')');
     1221                    continue;
    12171222                }
    12181223
     
    12291234                    this.uploadQueue.push(file);
    12301235                }
     1236            }
     1237
     1238            // Show warning for files that exceed the server upload limit.
     1239            if (skippedTooLarge.length > 0) {
     1240                var $tabContent = $('[data-tab-content="upload"]');
     1241                $tabContent.find('.ap-upload-size-notice').remove();
     1242                var notice = '<div class="blaminhor-essentials-notice error ap-upload-size-notice" style="margin-bottom: 15px;">' +
     1243                    '<span class="dashicons dashicons-warning"></span> ' +
     1244                    (this.strings.fileTooLarge || 'The following file(s) exceed the server upload limit') +
     1245                    ' (' + (this.maxUploadLabel || '') + '):<br>' +
     1246                    skippedTooLarge.join('<br>') +
     1247                    '</div>';
     1248                $tabContent.prepend(notice);
    12311249            }
    12321250
     
    14611479        },
    14621480
    1463         downloadBackupArchives: function(archives) {
    1464             var self = this;
    1465             var urls = [];
    1466             var loaded = 0;
    1467 
    1468             // Step 1: Fetch all download URLs in parallel.
    1469             $.each(archives, function(i, filename) {
    1470                 $.post(self.ajaxurl, {
    1471                     action: 'ap_backup_download',
    1472                     nonce: self.nonce,
    1473                     filename: filename
    1474                 }, function(response) {
    1475                     if (response.success) {
    1476                         urls[i] = response.data.url;
    1477                     }
    1478                     loaded++;
    1479                     if (loaded === archives.length) {
    1480                         triggerDownloads();
    1481                     }
    1482                 });
    1483             });
    1484 
    1485             // Step 2: Trigger downloads sequentially via window.location.href.
    1486             function triggerDownloads() {
    1487                 var index = 0;
    1488                 function next() {
    1489                     if (index >= urls.length) {
    1490                         return;
    1491                     }
    1492                     if (urls[index]) {
    1493                         window.location.href = urls[index];
    1494                     }
    1495                     index++;
    1496                     if (index < urls.length) {
    1497                         setTimeout(next, 1500);
    1498                     }
    1499                 }
    1500                 next();
    1501             }
     1481        downloadBackupBundle: function($btn) {
     1482            var label = $btn.text();
     1483            $btn.prop('disabled', true).html('<span class="spinner is-active" style="float: none; margin: 0 5px 0 0;"></span>' + (this.strings.preparing || 'Preparing...'));
     1484            $.post(this.ajaxurl, {
     1485                action: 'ap_backup_download_bundle',
     1486                nonce: this.nonce,
     1487                prefix: $btn.data('prefix')
     1488            }, function(response) {
     1489                $btn.prop('disabled', false).text(label);
     1490                if (response.success) {
     1491                    window.location.href = response.data.url;
     1492                } else {
     1493                    alert(response.data);
     1494                }
     1495            }).fail(function() {
     1496                $btn.prop('disabled', false).text(label);
     1497            });
    15021498        },
    15031499
     
    18341830            $('#ap-restore-new-domain').val('');
    18351831
    1836             // For uploaded backups, check domain info
    1837             if (type === 'uploaded' || components.indexOf('uploaded') !== -1) {
    1838                 // Get the first file from the backup
    1839                 var $row = $btn.closest('tr');
    1840                 var files = $row.data('files') || [];
    1841                 var filename = files.length > 0 ? files[0].filename : (prefix + '-uploaded.zip');
    1842 
    1843                 $.post(this.ajaxurl, {
    1844                     action: 'ap_backup_get_domain_info',
    1845                     nonce: this.nonce,
    1846                     filename: filename
    1847                 }, function(response) {
    1848                     if (response.success && response.data.domain_differs) {
    1849                         $('#ap-restore-domain-option').show();
    1850                         $('#ap-restore-domain-info').html(
    1851                             (self.strings.backupFrom || 'This backup is from') + ' <strong>' + response.data.backup_domain + '</strong>. ' +
    1852                             (self.strings.currentDomain || 'Current domain is') + ' <strong>' + response.data.current_domain + '</strong>.'
    1853                         );
    1854                         $('#ap-restore-old-domain').val(response.data.backup_url);
    1855                         $('#ap-restore-new-domain').val(response.data.current_url);
    1856                     }
    1857                 });
    1858             }
     1832            // Check domain info for all backup types
     1833            $.post(this.ajaxurl, {
     1834                action: 'ap_backup_get_domain_info',
     1835                nonce: this.nonce,
     1836                prefix: prefix
     1837            }, function(response) {
     1838                if (response.success && response.data.domain_differs) {
     1839                    $('#ap-restore-domain-option').show();
     1840                    $('#ap-restore-domain-info').html(
     1841                        (self.strings.backupFrom || 'This backup is from') + ' <strong>' + response.data.backup_domain + '</strong>. ' +
     1842                        (self.strings.currentDomain || 'Current domain is') + ' <strong>' + response.data.current_domain + '</strong>.'
     1843                    );
     1844                    $('#ap-restore-old-domain').val(response.data.backup_url);
     1845                    $('#ap-restore-new-domain').val(response.data.current_url);
     1846                }
     1847            });
    18591848
    18601849            $('#ap-restore-modal').fadeIn(200);
  • blaminhor-essentials/trunk/blaminhor-essentials.php

    r3450705 r3451104  
    44 * Plugin URI:        https://wp.blaminhor.com/
    55 * Description:       A modular toolkit for WordPress with activatable features. Lightweight, secure, and reliable.
    6  * Version:           1.4.5
     6 * Version:           1.4.6
    77 * Requires at least: 6.2
    88 * Requires PHP:      7.4
     
    2323
    2424// Plugin constants
    25 define('BLAMINHOR_ESSENTIALS_VERSION', '1.4.5');
     25define('BLAMINHOR_ESSENTIALS_VERSION', '1.4.6');
    2626define('BLAMINHOR_ESSENTIALS_PLUGIN_FILE', __FILE__);
    2727define('BLAMINHOR_ESSENTIALS_PLUGIN_DIR', plugin_dir_path(__FILE__));
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-de_DE.po

    r3450351 r3451104  
    2727msgstr "%1$s: %2$d Vorschaubilder gelöscht"
    2828
     29#. translators: %d: number of backup archives extracted from the bundle
     30msgid "%d archives extracted"
     31msgstr "%d Archive extrahiert"
     32
    2933msgid "%d Broken Link"
    3034msgstr "%d defekter Link"
     
    111115msgid "A backup was created before your last restore operation on %1$s (%2$s). You can undo the restore to revert to this state."
    112116msgstr "Vor Ihrer letzten Wiederherstellung am %1$s (%2$s) wurde eine Sicherung erstellt. Sie können die Wiederherstellung rückgängig machen, um zu diesem Zustand zurückzukehren."
     117
     118msgid "A full backup of your current site will be created automatically before restoring."
     119msgstr "Vor der Wiederherstellung wird automatisch eine vollständige Sicherung Ihrer aktuellen Website erstellt."
    113120
    114121msgid "A PHP extension stopped the file upload."
     
    411418msgstr "Domain nach Wiederherstellung ändern"
    412419
     420msgid "Change domain automatically after restore"
     421msgstr "Domain nach der Wiederherstellung automatisch ändern"
     422
    413423msgid "Change Domain Now"
    414424msgstr "Domain jetzt ändern"
     
    703713msgstr "Erkanntes Plugin:"
    704714
     715msgid "Different domain detected"
     716msgstr "Anderer Domain erkannt"
     717
    705718msgid "Disable"
    706719msgstr "Deaktivieren"
     
    961974msgstr "Fehler beim Erstellen des Sicherungsarchivs."
    962975
     976msgid "Failed to create bundle archive."
     977msgstr "Fehler beim Erstellen des gebündelten Archivs."
     978
    963979msgid "Failed to create pre-restore backup."
    964980msgstr "Fehler beim Erstellen der Vor-Wiederherstellungs-Sicherung."
     
    972988msgid "Failed to open backup archive."
    973989msgstr "Fehler beim Öffnen des Sicherungsarchivs."
     990
     991msgid "Failed to read the bundle archive."
     992msgstr "Das Paketarchiv konnte nicht gelesen werden."
    974993
    975994msgid "Failed to regenerate thumbnails."
     
    12571276msgid "Invalid backup identifier"
    12581277msgstr "Ungültiger Backup-Bezeichner"
     1278
     1279msgid "Invalid backup prefix."
     1280msgstr "Ungültiges Backup-Präfix."
    12591281
    12601282msgid "Invalid backup parameters."
     
    14921514msgstr "Keine Datenbanktabellen gefunden."
    14931515
     1516msgid "No database.sql found in the database archive."
     1517msgstr "Keine database.sql in der Datenbank-Archivdatei gefunden."
     1518
    14941519msgid "No file uploaded."
    14951520msgstr "Keine Datei hochgeladen."
     
    15321557msgid "No type specified."
    15331558msgstr "Kein Typ angegeben."
     1559
     1560msgid "No valid backup archives found in the bundle."
     1561msgstr "Keine gültigen Sicherungsarchive im Paket gefunden."
    15341562
    15351563msgid "Not set"
     
    17761804msgid "Pre-update Backup"
    17771805msgstr "Sicherung vor Updates"
     1806
     1807msgid "Preparing..."
     1808msgstr "Vorbereitung..."
    17781809
    17791810msgid "Press Enter or comma to add a keyword. Click × to remove."
     
    22622293msgstr "Wiederherstellungsmodus testen"
    22632294
     2295msgid "The archive does not contain any recognizable backup data."
     2296msgstr "Das Archiv enthält keine erkennbaren Sicherungsdaten."
     2297
    22642298msgid "The Backup module is not active."
    22652299msgstr "Das Backup-Modul ist nicht aktiv."
     
    22672301msgid "The Block Editor is currently disabled."
    22682302msgstr "Der Block-Editor ist derzeit deaktiviert."
     2303
     2304msgid "The database backup file is empty or contains no valid SQL statements."
     2305msgstr "Die Datenbank-Sicherungsdatei ist leer oder enthält keine gültigen SQL-Anweisungen."
     2306
     2307msgid "The following file(s) exceed the server upload limit"
     2308msgstr "Die folgenden Datei(en) überschreiten das Server-Upload-Limit"
    22692309
    22702310msgid "The new domain is the same as the current domain."
     
    25282568msgstr "Warnung:"
    25292569
     2570msgid "Warning: Restoring a backup will overwrite your current data. This action cannot be undone."
     2571msgstr "Warnung: Die Wiederherstellung einer Sicherung überschreibt Ihre aktuellen Daten. Diese Aktion kann nicht rückgängig gemacht werden."
     2572
    25302573msgid "We are working hard to bring you something amazing. Stay tuned!"
    25312574msgstr ""
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-es_ES.po

    r3450351 r3451104  
    1818msgstr " - copia"
    1919
     20#. translators: %d: number of backup archives extracted from the bundle
     21msgid "%d archives extracted"
     22msgstr "%d archivos extraídos"
     23
    2024#. translators: %d: number of selected items
    2125msgid "%d selected"
     
    4448msgstr "Un nombre amigable para identificar este relay."
    4549
     50msgid "A full backup of your current site will be created automatically before restoring."
     51msgstr "Se creará automáticamente una copia de seguridad completa de su sitio actual antes de la restauración."
     52
    4653msgid "A PHP extension stopped the file upload."
    4754msgstr "Una extensión PHP detuvo la subida del archivo."
     
    203210msgstr "Archivos de categorías y etiquetas"
    204211
     212msgid "Change domain automatically after restore"
     213msgstr "Cambiar el dominio automáticamente después de la restauración"
     214
    205215msgid "Change this setting"
    206216msgstr "Cambiar este ajuste"
     
    323333msgstr "Descripción"
    324334
     335msgid "Different domain detected"
     336msgstr "Dominio diferente detectado"
     337
    325338msgid "Disable HTTPS Redirect"
    326339msgstr "Desactivar redirección HTTPS"
     
    449462msgstr "Error al crear la copia de seguridad."
    450463
     464msgid "Failed to create bundle archive."
     465msgstr "Error al crear el archivo del paquete."
     466
     467msgid "Failed to read the bundle archive."
     468msgstr "Error al leer el archivo del paquete."
     469
    451470msgid "Failed to save the uploaded file."
    452471msgstr "Error al guardar el archivo subido."
     
    557576msgstr "Inicializando..."
    558577
     578msgid "Invalid backup prefix."
     579msgstr "Prefijo de copia de seguridad no válido."
     580
    559581msgid "Invalid email address."
    560582msgstr "Dirección de correo no válida."
     
    661683msgid "No backups selected."
    662684msgstr "No se han seleccionado copias de seguridad."
     685
     686msgid "No database.sql found in the database archive."
     687msgstr "No se encontró database.sql en el archivo de base de datos."
    663688
    664689msgid "No file uploaded."
     
    690715msgid "No type specified."
    691716msgstr "Ningún tipo especificado."
     717
     718msgid "No valid backup archives found in the bundle."
     719msgstr "No se encontraron archivos de respaldo válidos en el paquete."
    692720
    693721msgid "None"
     
    803831msgstr "Copia de seguridad antes de actualizar"
    804832
     833msgid "Preparing..."
     834msgstr "Preparando..."
     835
    805836msgid "Preview"
    806837msgstr "Vista previa"
     
    10431074msgstr "¡Gracias por tus comentarios!"
    10441075
     1076msgid "The archive does not contain any recognizable backup data."
     1077msgstr "El archivo no contiene datos de copia de seguridad reconocibles."
     1078
    10451079msgid "The Block Editor is currently disabled."
    10461080msgstr "El editor de bloques está actualmente desactivado."
    10471081
     1082msgid "The database backup file is empty or contains no valid SQL statements."
     1083msgstr "El archivo de copia de seguridad de la base de datos está vacío o no contiene instrucciones SQL válidas."
     1084
     1085msgid "The following file(s) exceed the server upload limit"
     1086msgstr "Los siguientes archivos exceden el límite de subida del servidor"
     1087
    10481088msgid "The uploaded file exceeds the MAX_FILE_SIZE directive."
    10491089msgstr "El archivo subido excede la directiva MAX_FILE_SIZE."
     
    11471187msgid "Warning"
    11481188msgstr "Advertencia"
     1189
     1190msgid "Warning: Restoring a backup will overwrite your current data. This action cannot be undone."
     1191msgstr "Advertencia: la restauración de una copia de seguridad sobrescribirá sus datos actuales. Esta acción no se puede deshacer."
    11491192
    11501193msgid "Weekly"
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-fr_FR.po

    r3450351 r3451104  
    2727msgstr "%1$s : %2$d miniatures supprimées"
    2828
     29#. translators: %d: number of backup archives extracted from the bundle
     30msgid "%d archives extracted"
     31msgstr "%d archives extraites"
     32
    2933msgid "%d Broken Link"
    3034msgstr "%d lien cassé"
     
    119123msgid "A friendly name to identify this relay."
    120124msgstr "Un nom convivial pour identifier ce relais."
     125
     126msgid "A full backup of your current site will be created automatically before restoring."
     127msgstr "Une sauvegarde complète de votre site actuel sera créée automatiquement avant la restauration."
    121128
    122129msgid "A PHP extension stopped the file upload."
     
    514521msgstr "Changer le domaine après la restauration"
    515522
     523msgid "Change domain automatically after restore"
     524msgstr "Changer le domaine automatiquement après la restauration"
     525
    516526msgid "Change Domain Now"
    517527msgstr "Changer le domaine maintenant"
     
    931941msgstr "Développeurs, intégration API"
    932942
     943msgid "Different domain detected"
     944msgstr "Domaine différent détecté"
     945
    933946msgid "Dimensions"
    934947msgstr "Dimensions"
     
    12551268msgstr "Échec de la création de l'archive de sauvegarde."
    12561269
     1270msgid "Failed to create bundle archive."
     1271msgstr "Échec de la création de l'archive groupée."
     1272
    12571273msgid "Failed to create pre-restore backup."
    12581274msgstr "Échec de la création de la sauvegarde pré-restauration."
     
    12661282msgid "Failed to open backup archive."
    12671283msgstr "Échec de l'ouverture de l'archive de sauvegarde."
     1284
     1285msgid "Failed to read the bundle archive."
     1286msgstr "Échec de la lecture de l'archive groupée."
    12681287
    12691288msgid "Failed to regenerate thumbnails."
     
    16381657msgid "Invalid backup identifier"
    16391658msgstr "Identifiant de sauvegarde invalide"
     1659
     1660msgid "Invalid backup prefix."
     1661msgstr "Préfixe de sauvegarde invalide."
    16401662
    16411663msgid "Invalid backup parameters."
     
    19421964msgstr "Aucune table de base de données trouvée."
    19431965
     1966msgid "No database.sql found in the database archive."
     1967msgstr "Aucun fichier database.sql trouvé dans l'archive de base de données."
     1968
    19441969msgid "No emails logged yet."
    19451970msgstr "Aucun email enregistré pour l'instant."
     
    19882013msgid "No type specified."
    19892014msgstr "Aucun type spécifié."
     2015
     2016msgid "No valid backup archives found in the bundle."
     2017msgstr "Aucune archive de sauvegarde valide trouvée dans le lot."
    19902018
    19912019msgid "None"
     
    29993027msgstr "L'email d'expédition authentifié pour ce relais."
    30003028
     3029msgid "The archive does not contain any recognizable backup data."
     3030msgstr "L'archive ne contient aucune donnée de sauvegarde reconnaissable."
     3031
    30013032msgid "The Backup module is not active."
    30023033msgstr "Le module de sauvegarde n'est pas actif."
     
    30063037msgstr "L'éditeur de blocs est actuellement désactivé."
    30073038
     3039msgid "The database backup file is empty or contains no valid SQL statements."
     3040msgstr "Le fichier de sauvegarde de la base de données est vide ou ne contient aucune instruction SQL valide."
     3041
     3042msgid "The following file(s) exceed the server upload limit"
     3043msgstr "Le(s) fichier(s) suivant(s) dépassent la limite d'envoi du serveur"
     3044
    30083045msgid "The new domain is the same as the current domain."
    30093046msgstr "Le nouveau domaine est identique au domaine actuel."
     
    33233360msgstr "Attention :"
    33243361
     3362msgid "Warning: Restoring a backup will overwrite your current data. This action cannot be undone."
     3363msgstr "Attention : la restauration d'une sauvegarde écrasera vos données actuelles. Cette action est irréversible."
     3364
    33253365msgid "We are working hard to bring you something amazing. Stay tuned!"
    33263366msgstr ""
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-id_ID.po

    r3450351 r3451104  
    1818msgstr " - salinan"
    1919
     20#. translators: %d: number of backup archives extracted from the bundle
     21msgid "%d archives extracted"
     22msgstr "%d arsip diekstrak"
     23
    2024#. translators: %d: number of selected items
    2125msgid "%d selected"
     
    4448msgstr "Nama yang mudah dikenali untuk mengidentifikasi relay ini."
    4549
     50msgid "A full backup of your current site will be created automatically before restoring."
     51msgstr "Cadangan lengkap situs Anda saat ini akan dibuat secara otomatis sebelum pemulihan."
     52
    4653msgid "A PHP extension stopped the file upload."
    4754msgstr "Ekstensi PHP menghentikan unggahan berkas."
     
    203210msgstr "Arsip kategori dan tag"
    204211
     212msgid "Change domain automatically after restore"
     213msgstr "Ubah domain secara otomatis setelah pemulihan"
     214
    205215msgid "Change this setting"
    206216msgstr "Ubah pengaturan ini"
     
    323333msgstr "Deskripsi"
    324334
     335msgid "Different domain detected"
     336msgstr "Domain berbeda terdeteksi"
     337
    325338msgid "Disable HTTPS Redirect"
    326339msgstr "Nonaktifkan Pengalihan HTTPS"
     
    449462msgstr "Gagal membuat cadangan."
    450463
     464msgid "Failed to create bundle archive."
     465msgstr "Gagal membuat arsip bundel."
     466
     467msgid "Failed to read the bundle archive."
     468msgstr "Gagal membaca arsip bundel."
     469
    451470msgid "Failed to save the uploaded file."
    452471msgstr "Gagal menyimpan berkas yang diunggah."
     
    557576msgstr "Menginisialisasi..."
    558577
     578msgid "Invalid backup prefix."
     579msgstr "Awalan cadangan tidak valid."
     580
    559581msgid "Invalid email address."
    560582msgstr "Alamat email tidak valid."
     
    662684msgstr "Tidak ada cadangan yang dipilih."
    663685
     686msgid "No database.sql found in the database archive."
     687msgstr "database.sql tidak ditemukan dalam arsip database."
     688
    664689msgid "No file uploaded."
    665690msgstr "Tidak ada berkas yang diunggah."
     
    688713msgid "No type specified."
    689714msgstr "Tidak ada tipe yang ditentukan."
     715
     716msgid "No valid backup archives found in the bundle."
     717msgstr "Tidak ditemukan arsip cadangan yang valid dalam bundel."
    690718
    691719msgid "None"
     
    799827msgstr "Cadangan Sebelum Pembaruan"
    800828
     829msgid "Preparing..."
     830msgstr "Mempersiapkan..."
     831
    801832msgid "Preview"
    802833msgstr "Pratinjau"
     
    10391070msgstr "Terima kasih atas umpan balik Anda!"
    10401071
     1072msgid "The archive does not contain any recognizable backup data."
     1073msgstr "Arsip tidak berisi data cadangan yang dapat dikenali."
     1074
    10411075msgid "The Block Editor is currently disabled."
    10421076msgstr "Editor Blok saat ini dinonaktifkan."
    10431077
     1078msgid "The database backup file is empty or contains no valid SQL statements."
     1079msgstr "File cadangan database kosong atau tidak berisi pernyataan SQL yang valid."
     1080
     1081msgid "The following file(s) exceed the server upload limit"
     1082msgstr "File berikut melebihi batas unggah server"
     1083
    10441084msgid "The uploaded file exceeds the MAX_FILE_SIZE directive."
    10451085msgstr "Berkas yang diunggah melebihi direktif MAX_FILE_SIZE."
     
    11441184msgstr "Peringatan"
    11451185
     1186msgid "Warning: Restoring a backup will overwrite your current data. This action cannot be undone."
     1187msgstr "Peringatan: memulihkan cadangan akan menimpa data Anda saat ini. Tindakan ini tidak dapat dibatalkan."
     1188
    11461189msgid "Weekly"
    11471190msgstr "Mingguan"
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-it_IT.po

    r3450351 r3451104  
    1818msgstr " - copia"
    1919
     20#. translators: %d: number of backup archives extracted from the bundle
     21msgid "%d archives extracted"
     22msgstr "%d archivi estratti"
     23
    2024#. translators: %d: number of selected items
    2125msgid "%d selected"
     
    4448msgstr "Un nome amichevole per identificare questo relay."
    4549
     50msgid "A full backup of your current site will be created automatically before restoring."
     51msgstr "Un backup completo del sito attuale verrà creato automaticamente prima del ripristino."
     52
    4653msgid "A PHP extension stopped the file upload."
    4754msgstr "Un'estensione PHP ha interrotto il caricamento del file."
     
    203210msgstr "Archivi categorie e tag"
    204211
     212msgid "Change domain automatically after restore"
     213msgstr "Cambia il dominio automaticamente dopo il ripristino"
     214
    205215msgid "Change this setting"
    206216msgstr "Modifica questa impostazione"
     
    323333msgstr "Descrizione"
    324334
     335msgid "Different domain detected"
     336msgstr "Dominio diverso rilevato"
     337
    325338msgid "Disable HTTPS Redirect"
    326339msgstr "Disattiva reindirizzamento HTTPS"
     
    449462msgstr "Impossibile creare il backup."
    450463
     464msgid "Failed to create bundle archive."
     465msgstr "Impossibile creare l'archivio del pacchetto."
     466
     467msgid "Failed to read the bundle archive."
     468msgstr "Impossibile leggere l'archivio del pacchetto."
     469
    451470msgid "Failed to save the uploaded file."
    452471msgstr "Impossibile salvare il file caricato."
     
    557576msgstr "Inizializzazione..."
    558577
     578msgid "Invalid backup prefix."
     579msgstr "Prefisso di backup non valido."
     580
    559581msgid "Invalid email address."
    560582msgstr "Indirizzo email non valido."
     
    662684msgstr "Nessun backup selezionato."
    663685
     686msgid "No database.sql found in the database archive."
     687msgstr "Nessun file database.sql trovato nell'archivio del database."
     688
    664689msgid "No file uploaded."
    665690msgstr "Nessun file caricato."
     
    688713msgid "No type specified."
    689714msgstr "Nessun tipo specificato."
     715
     716msgid "No valid backup archives found in the bundle."
     717msgstr "Nessun archivio di backup valido trovato nel pacchetto."
    690718
    691719msgid "None"
     
    799827msgstr "Backup prima dell'aggiornamento"
    800828
     829msgid "Preparing..."
     830msgstr "Preparazione..."
     831
    801832msgid "Preview"
    802833msgstr "Anteprima"
     
    10391070msgstr "Grazie per il tuo feedback!"
    10401071
     1072msgid "The archive does not contain any recognizable backup data."
     1073msgstr "L'archivio non contiene dati di backup riconoscibili."
     1074
    10411075msgid "The Block Editor is currently disabled."
    10421076msgstr "L'editor a blocchi è attualmente disattivato."
    10431077
     1078msgid "The database backup file is empty or contains no valid SQL statements."
     1079msgstr "Il file di backup del database è vuoto o non contiene istruzioni SQL valide."
     1080
     1081msgid "The following file(s) exceed the server upload limit"
     1082msgstr "I seguenti file superano il limite di caricamento del server"
     1083
    10441084msgid "The uploaded file exceeds the MAX_FILE_SIZE directive."
    10451085msgstr "Il file caricato supera la direttiva MAX_FILE_SIZE."
     
    11441184msgstr "Avviso"
    11451185
     1186msgid "Warning: Restoring a backup will overwrite your current data. This action cannot be undone."
     1187msgstr "Attenzione: il ripristino di un backup sovrascriverà i dati attuali. Questa azione non può essere annullata."
     1188
    11461189msgid "Weekly"
    11471190msgstr "Settimanale"
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-ja.po

    r3450351 r3451104  
    2121msgstr "%2$d 件中 %1$d 件のメールタイプがミュートされました。"
    2222
     23#. translators: %d: number of backup archives extracted from the bundle
     24msgid "%d archives extracted"
     25msgstr "%d 個のアーカイブを展開しました"
     26
    2327msgid "%d redirections imported."
    2428msgstr "%d 件のリダイレクトをインポートしました。"
     
    7175msgstr "このリレーを識別するための名前。"
    7276
     77msgid "A full backup of your current site will be created automatically before restoring."
     78msgstr "復元前に現在のサイトの完全バックアップが自動的に作成されます。"
     79
    7380msgid "Accepted format: .zip"
    7481msgstr "受け入れ形式: .zip"
     
    302309msgstr "ドメインを変更"
    303310
     311msgid "Change domain automatically after restore"
     312msgstr "復元後にドメインを自動的に変更する"
     313
    304314msgid "Change Domain Now"
    305315msgstr "今すぐドメインを変更"
     
    524534msgstr "デスクトップ"
    525535
     536msgid "Different domain detected"
     537msgstr "異なるドメインが検出されました"
     538
    526539msgid "Disable"
    527540msgstr "無効"
     
    758771msgstr "失敗"
    759772
     773msgid "Failed to create bundle archive."
     774msgstr "バンドルアーカイブの作成に失敗しました。"
     775
     776msgid "Failed to read the bundle archive."
     777msgstr "バンドルアーカイブの読み取りに失敗しました。"
     778
    760779msgid "Failed to send feedback. Please try again or contact us directly."
    761780msgstr "フィードバックの送信に失敗しました。もう一度お試しいただくか、直接ご連絡ください。"
     
    944963msgstr "初期化中..."
    945964
     965msgid "Invalid backup prefix."
     966msgstr "無効なバックアッププレフィックス。"
     967
    946968msgid "Invalid module."
    947969msgstr "無効なモジュール。"
     
    11301152msgstr "データが見つかりません。"
    11311153
     1154msgid "No database.sql found in the database archive."
     1155msgstr "データベースアーカイブにdatabase.sqlが見つかりません。"
     1156
    11321157msgid "No emails logged yet."
    11331158msgstr "まだメールは記録されていません。"
     
    11561181msgid "No type specified."
    11571182msgstr "タイプが指定されていません。"
     1183
     1184msgid "No valid backup archives found in the bundle."
     1185msgstr "バンドル内に有効なバックアップアーカイブが見つかりませんでした。"
    11581186
    11591187msgid "None"
     
    13601388msgstr "更新前バックアップ"
    13611389
     1390msgid "Preparing..."
     1391msgstr "準備中..."
     1392
    13621393msgid "Preview"
    13631394msgstr "プレビュー"
     
    17981829msgstr "フィードバックありがとうございます!"
    17991830
     1831msgid "The archive does not contain any recognizable backup data."
     1832msgstr "アーカイブに認識可能なバックアップデータが含まれていません。"
     1833
    18001834msgid "The authenticated sender email for this relay."
    18011835msgstr "このリレーの認証済み送信者メール。"
     
    18071841msgstr "ブロックエディターは現在無効になっています。"
    18081842
     1843msgid "The database backup file is empty or contains no valid SQL statements."
     1844msgstr "データベースバックアップファイルが空か、有効なSQL文が含まれていません。"
     1845
     1846msgid "The following file(s) exceed the server upload limit"
     1847msgstr "次のファイルはサーバーのアップロード制限を超えています"
     1848
    18091849msgid "The URL path to redirect from. Example: /old-page/"
    18101850msgstr "リダイレクト元のURLパス。例: /old-page/"
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-nl_NL.po

    r3450351 r3451104  
    2121msgstr "%1$d van %2$d e-mailtypes gedempt."
    2222
     23#. translators: %d: number of backup archives extracted from the bundle
     24msgid "%d archives extracted"
     25msgstr "%d archieven uitgepakt"
     26
    2327msgid "%d redirections imported."
    2428msgstr "%d omleidingen geimporteerd."
     
    8690msgstr "Een herkenbare naam om dit relais te identificeren."
    8791
     92msgid "A full backup of your current site will be created automatically before restoring."
     93msgstr "Er wordt automatisch een volledige back-up van uw huidige site gemaakt voordat het herstel begint."
     94
    8895msgid "About"
    8996msgstr "Over"
     
    309316msgstr "Domein wijzigen"
    310317
     318msgid "Change domain automatically after restore"
     319msgstr "Domein automatisch wijzigen na herstel"
     320
    311321msgid "Change Domain Now"
    312322msgstr "Domein nu wijzigen"
     
    513523msgstr "Alles deselecteren"
    514524
     525msgid "Different domain detected"
     526msgstr "Ander domein gedetecteerd"
     527
    515528msgid "Disable"
    516529msgstr "Uitschakelen"
     
    762775msgstr "Omleiding toevoegen mislukt."
    763776
     777msgid "Failed to create bundle archive."
     778msgstr "Kan bundelarchief niet aanmaken."
     779
     780msgid "Failed to read the bundle archive."
     781msgstr "Kan het bundelarchief niet lezen."
     782
    764783msgid "Failed to delete redirection."
    765784msgstr "Omleiding verwijderen mislukt."
     
    972991msgstr "Instagram-URL"
    973992
     993msgid "Invalid backup prefix."
     994msgstr "Ongeldig back-upvoorvoegsel."
     995
    974996msgid "Invalid data."
    975997msgstr "Ongeldige gegevens."
     
    11461168msgstr "Geen CSV-inhoud opgegeven."
    11471169
     1170msgid "No database.sql found in the database archive."
     1171msgstr "Geen database.sql gevonden in het database-archief."
     1172
    11481173msgid "No emails logged yet."
    11491174msgstr "Nog geen e-mails geregistreerd."
     
    11901215msgid "No type specified."
    11911216msgstr "Geen type opgegeven."
     1217
     1218msgid "No valid backup archives found in the bundle."
     1219msgstr "Geen geldige back-uparchieven gevonden in de bundel."
    11921220
    11931221msgid "None"
     
    13761404msgstr "Back-up voor update"
    13771405
     1406msgid "Preparing..."
     1407msgstr "Voorbereiden..."
     1408
    13781409msgid "Preview"
    13791410msgstr "Voorbeeld"
     
    18351866msgstr "Bedankt voor uw feedback!"
    18361867
     1868msgid "The archive does not contain any recognizable backup data."
     1869msgstr "Het archief bevat geen herkenbare back-upgegevens."
     1870
    18371871msgid "The authenticated sender email for this relay."
    18381872msgstr "Het geverifieerde afzender-e-mailadres voor dit relais."
     
    18441878msgstr "De blokeditor is momenteel uitgeschakeld."
    18451879
     1880msgid "The database backup file is empty or contains no valid SQL statements."
     1881msgstr "Het database-back-upbestand is leeg of bevat geen geldige SQL-instructies."
     1882
     1883msgid "The following file(s) exceed the server upload limit"
     1884msgstr "De volgende bestanden overschrijden de uploadlimiet van de server"
     1885
    18461886msgid "The new domain is the same as the current domain."
    18471887msgstr "Het nieuwe domein is hetzelfde als het huidige domein."
     
    20182058msgstr "Waarschuwing: Zodra HSTS door browsers is gecached, moet uw site op HTTPS blijven. Het uitschakelen van deze instelling maakt de browsercache niet onmiddellijk ongedaan."
    20192059
     2060msgid "Warning: Restoring a backup will overwrite your current data. This action cannot be undone."
     2061msgstr "Waarschuwing: het herstellen van een back-up overschrijft uw huidige gegevens. Deze actie kan niet ongedaan worden gemaakt."
     2062
    20202063msgid "We strongly recommend creating a database backup before changing your domain."
    20212064msgstr "We raden sterk aan om een databaseback-up te maken voordat u uw domein wijzigt."
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-pt_BR.po

    r3450351 r3451104  
    2121msgstr "%1$d de %2$d tipos de e-mail silenciados."
    2222
     23#. translators: %d: number of backup archives extracted from the bundle
     24msgid "%d archives extracted"
     25msgstr "%d arquivos extraídos"
     26
    2327msgid "%d redirections imported."
    2428msgstr "%d redirecionamentos importados."
     
    9296msgstr "Um nome amigavel para identificar este relay."
    9397
     98msgid "A full backup of your current site will be created automatically before restoring."
     99msgstr "Um backup completo do seu site atual será criado automaticamente antes da restauração."
     100
    94101msgid "A PHP extension stopped the file upload."
    95102msgstr "Uma extensao PHP interrompeu o upload do arquivo."
     
    314321msgstr "Alterar Dominio"
    315322
     323msgid "Change domain automatically after restore"
     324msgstr "Alterar o domínio automaticamente após a restauração"
     325
    316326msgid "Change Domain Now"
    317327msgstr "Alterar Dominio Agora"
     
    515525msgstr "Descricao"
    516526
     527msgid "Different domain detected"
     528msgstr "Domínio diferente detectado"
     529
    517530msgid "Disable"
    518531msgstr "Desativar"
     
    773786msgstr "Falha ao criar backup."
    774787
     788msgid "Failed to create bundle archive."
     789msgstr "Falha ao criar arquivo do pacote."
     790
     791msgid "Failed to read the bundle archive."
     792msgstr "Falha ao ler o arquivo do pacote."
     793
    775794msgid "Failed to delete redirection."
    776795msgstr "Falha ao excluir redirecionamento."
     
    962981msgstr "Inicializando..."
    963982
     983msgid "Invalid backup prefix."
     984msgstr "Prefixo de backup invalido."
     985
    964986msgid "Invalid data."
    965987msgstr "Dados invalidos."
     
    11361158msgstr "Nenhum conteudo CSV fornecido."
    11371159
     1160msgid "No database.sql found in the database archive."
     1161msgstr "Arquivo database.sql não encontrado no arquivo de banco de dados."
     1162
    11381163msgid "No emails logged yet."
    11391164msgstr "Nenhum e-mail registrado ainda."
     
    11711196msgid "No type specified."
    11721197msgstr "Nenhum tipo especificado."
     1198
     1199msgid "No valid backup archives found in the bundle."
     1200msgstr "Nenhum arquivo de backup válido encontrado no pacote."
    11731201
    11741202msgid "None"
     
    13571385msgstr "Backup antes da atualização"
    13581386
     1387msgid "Preparing..."
     1388msgstr "Preparando..."
     1389
    13591390msgid "Preview"
    13601391msgstr "Visualizar"
     
    17651796msgstr "Obrigado pelo seu feedback!"
    17661797
     1798msgid "The archive does not contain any recognizable backup data."
     1799msgstr "O arquivo não contém dados de backup reconhecíveis."
     1800
    17671801msgid "The authenticated sender email for this relay."
    17681802msgstr "O e-mail do remetente autenticado para este relay."
     
    17741808msgstr "O editor de blocos está atualmente desativado."
    17751809
     1810msgid "The database backup file is empty or contains no valid SQL statements."
     1811msgstr "O arquivo de backup do banco de dados está vazio ou não contém instruções SQL válidas."
     1812
     1813msgid "The following file(s) exceed the server upload limit"
     1814msgstr "Os seguintes arquivos excedem o limite de envio do servidor"
     1815
    17761816msgid "The new domain is the same as the current domain."
    17771817msgstr "O novo dominio e o mesmo que o dominio atual."
     
    19571997msgstr "Aviso: Uma vez que o HSTS e armazenado em cache pelos navegadores, seu site deve permanecer em HTTPS. Desativar esta configuracao nao desfara imediatamente o cache do navegador."
    19581998
     1999msgid "Warning: Restoring a backup will overwrite your current data. This action cannot be undone."
     2000msgstr "Aviso: a restauração de um backup substituirá seus dados atuais. Esta ação não pode ser desfeita."
     2001
    19592002msgid "We strongly recommend creating a database backup before changing your domain."
    19602003msgstr "Recomendamos fortemente criar um backup do banco de dados antes de alterar seu dominio."
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-pt_PT.po

    r3450351 r3451104  
    1818msgstr " - cópia"
    1919
     20#. translators: %d: number of backup archives extracted from the bundle
     21msgid "%d archives extracted"
     22msgstr "%d arquivos extraídos"
     23
    2024#. translators: %d: number of selected items
    2125msgid "%d selected"
     
    4448msgstr "Um nome amigável para identificar este relay."
    4549
     50msgid "A full backup of your current site will be created automatically before restoring."
     51msgstr "Uma cópia de segurança completa do seu site atual será criada automaticamente antes da restauração."
     52
    4653msgid "A PHP extension stopped the file upload."
    4754msgstr "Uma extensão PHP parou o carregamento do ficheiro."
     
    203210msgstr "Arquivos de categorias e etiquetas"
    204211
     212msgid "Change domain automatically after restore"
     213msgstr "Alterar o domínio automaticamente após a restauração"
     214
    205215msgid "Change this setting"
    206216msgstr "Alterar esta definição"
     
    323333msgstr "Descrição"
    324334
     335msgid "Different domain detected"
     336msgstr "Domínio diferente detetado"
     337
    325338msgid "Disable HTTPS Redirect"
    326339msgstr "Desativar redirecionamento HTTPS"
     
    449462msgstr "Falha ao criar cópia de segurança."
    450463
     464msgid "Failed to create bundle archive."
     465msgstr "Falha ao criar o arquivo do pacote."
     466
     467msgid "Failed to read the bundle archive."
     468msgstr "Falha ao ler o arquivo do pacote."
     469
    451470msgid "Failed to save the uploaded file."
    452471msgstr "Falha ao guardar o ficheiro carregado."
     
    557576msgstr "A inicializar..."
    558577
     578msgid "Invalid backup prefix."
     579msgstr "Prefixo de cópia de segurança inválido."
     580
    559581msgid "Invalid email address."
    560582msgstr "Endereço de email inválido."
     
    662684msgstr "Nenhuma cópia de segurança selecionada."
    663685
     686msgid "No database.sql found in the database archive."
     687msgstr "Ficheiro database.sql não encontrado no arquivo da base de dados."
     688
    664689msgid "No file uploaded."
    665690msgstr "Nenhum ficheiro carregado."
     
    688713msgid "No type specified."
    689714msgstr "Nenhum tipo especificado."
     715
     716msgid "No valid backup archives found in the bundle."
     717msgstr "Nenhum arquivo de backup válido encontrado no pacote."
    690718
    691719msgid "None"
     
    799827msgstr "Cópia de segurança antes da atualização"
    800828
     829msgid "Preparing..."
     830msgstr "A preparar..."
     831
    801832msgid "Preview"
    802833msgstr "Pré-visualizar"
     
    10391070msgstr "Obrigado pelo seu feedback!"
    10401071
     1072msgid "The archive does not contain any recognizable backup data."
     1073msgstr "O arquivo não contém dados de cópia de segurança reconhecíveis."
     1074
    10411075msgid "The Block Editor is currently disabled."
    10421076msgstr "O editor de blocos está atualmente desativado."
    10431077
     1078msgid "The database backup file is empty or contains no valid SQL statements."
     1079msgstr "O ficheiro de cópia de segurança da base de dados está vazio ou não contém instruções SQL válidas."
     1080
     1081msgid "The following file(s) exceed the server upload limit"
     1082msgstr "Os seguintes ficheiros excedem o limite de envio do servidor"
     1083
    10441084msgid "The uploaded file exceeds the MAX_FILE_SIZE directive."
    10451085msgstr "O ficheiro carregado excede a diretiva MAX_FILE_SIZE."
     
    11441184msgstr "Aviso"
    11451185
     1186msgid "Warning: Restoring a backup will overwrite your current data. This action cannot be undone."
     1187msgstr "Aviso: a restauração de uma cópia de segurança irá substituir os seus dados atuais. Esta ação não pode ser revertida."
     1188
    11461189msgid "Weekly"
    11471190msgstr "Semanal"
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-ru_RU.po

    r3450351 r3451104  
    2727msgstr "%1$s: удалено миниатюр: %2$d"
    2828
     29#. translators: %d: number of backup archives extracted from the bundle
     30msgid "%d archives extracted"
     31msgstr "%d архивов извлечено"
     32
    2933msgid "%d Broken Link"
    3034msgstr "%d битая ссылка"
     
    506510msgstr "Сменить домен после восстановления"
    507511
     512msgid "Change domain automatically after restore"
     513msgstr "Автоматически изменить домен после восстановления"
     514
    508515msgid "Change Domain Now"
    509516msgstr "Сменить домен сейчас"
     
    884891msgstr "Разработчиков, API-интеграция"
    885892
     893msgid "Different domain detected"
     894msgstr "Обнаружен другой домен"
     895
    886896msgid "Dimensions"
    887897msgstr "Размеры"
     
    11931203msgstr "Не удалось создать архив резервной копии."
    11941204
     1205msgid "Failed to create bundle archive."
     1206msgstr "Не удалось создать архив-пакет."
     1207
    11951208msgid "Failed to create pre-restore backup."
    11961209msgstr "Не удалось создать резервную копию перед восстановлением."
     
    12051218msgstr "Не удалось открыть архив резервной копии."
    12061219
     1220msgid "Failed to read the bundle archive."
     1221msgstr "Не удалось прочитать архив пакета."
     1222
    12071223msgid "Failed to regenerate thumbnails."
    12081224msgstr "Не удалось перегенерировать миниатюры."
     
    15111527msgstr "Недействительные параметры резервного копирования."
    15121528
     1529msgid "Invalid backup prefix."
     1530msgstr "Недействительный префикс резервной копии."
     1531
    15131532msgid "Invalid data."
    15141533msgstr "Недействительные данные."
     
    17811800msgstr "Таблицы базы данных не найдены."
    17821801
     1802msgid "No database.sql found in the database archive."
     1803msgstr "Файл database.sql не найден в архиве базы данных."
     1804
    17831805msgid "No emails logged yet."
    17841806msgstr "Письма ещё не записаны."
     
    18251847msgid "No type specified."
    18261848msgstr "Тип не указан."
     1849
     1850msgid "No valid backup archives found in the bundle."
     1851msgstr "В пакете не найдено допустимых архивов резервного копирования."
    18271852
    18281853msgid "None"
     
    27882813msgstr "Модуль %s активен. Сохраните URL восстановления перед включением HTTPS-перенаправления."
    27892814
     2815msgid "The archive does not contain any recognizable backup data."
     2816msgstr "Архив не содержит распознаваемых данных резервной копии."
     2817
    27902818msgid "The authenticated sender email for this relay."
    27912819msgstr "Авторизованный email отправителя для этого ретранслятора."
     
    27972825msgstr "Блочный редактор в настоящее время отключен."
    27982826
     2827msgid "The database backup file is empty or contains no valid SQL statements."
     2828msgstr "Файл резервной копии базы данных пуст или не содержит допустимых SQL-запросов."
     2829
     2830msgid "The following file(s) exceed the server upload limit"
     2831msgstr "Следующие файлы превышают лимит загрузки сервера"
     2832
    27992833msgid "The new domain is the same as the current domain."
    28002834msgstr "Новый домен совпадает с текущим доменом."
  • blaminhor-essentials/trunk/languages/blaminhor-essentials-tr_TR.po

    r3450351 r3451104  
    2121msgstr "%2$d e-posta turunden %1$d tanesi sessize alindi."
    2222
     23#. translators: %d: number of backup archives extracted from the bundle
     24msgid "%d archives extracted"
     25msgstr "%d arşiv çıkarıldı"
     26
    2327msgid "%d redirections imported."
    2428msgstr "%d yonlendirme ice aktarildi."
     
    8387msgstr "Bu aktariciyi tanimlamak icin kolay bir ad."
    8488
     89msgid "A full backup of your current site will be created automatically before restoring."
     90msgstr "Geri yüklemeden önce mevcut sitenizin tam bir yedeği otomatik olarak oluşturulacaktır."
     91
    8592msgid "About"
    8693msgstr "Hakkinda"
     
    297304msgstr "Alan Adi Degistir"
    298305
     306msgid "Change domain automatically after restore"
     307msgstr "Geri yüklemeden sonra alan adını otomatik olarak değiştir"
     308
    299309msgid "Change Domain Now"
    300310msgstr "Alan Adini Simdi Degistir"
     
    495505msgstr "Tumunun Secimini Kaldir"
    496506
     507msgid "Different domain detected"
     508msgstr "Farklı alan adı algılandı"
     509
    497510msgid "Disable"
    498511msgstr "Devre Disi Birak"
     
    732745msgstr "Yonlendirme eklenemedi."
    733746
     747msgid "Failed to create bundle archive."
     748msgstr "Paket arsivi olusturulamadi."
     749
     750msgid "Failed to read the bundle archive."
     751msgstr "Paket arşivi okunamadı."
     752
    734753msgid "Failed to delete redirection."
    735754msgstr "Yonlendirme silinemedi."
     
    900919msgstr "Bilgi"
    901920
     921msgid "Invalid backup prefix."
     922msgstr "Gecersiz yedek oneki."
     923
    902924msgid "Invalid data."
    903925msgstr "Gecersiz veri."
     
    10921114msgstr "CSV icerigi saglanmadi."
    10931115
     1116msgid "No database.sql found in the database archive."
     1117msgstr "Veritabanı arşivinde database.sql bulunamadı."
     1118
    10941119msgid "No emails logged yet."
    10951120msgstr "Henuz kayitli e-posta yok."
     
    11181143msgid "No type specified."
    11191144msgstr "Tür belirtilmedi."
     1145
     1146msgid "No valid backup archives found in the bundle."
     1147msgstr "Pakette geçerli yedekleme arşivi bulunamadı."
    11201148
    11211149msgid "Nobody"
     
    12861314msgstr "Güncelleme Öncesi Yedekleme"
    12871315
     1316msgid "Preparing..."
     1317msgstr "Hazirlaniyor..."
     1318
    12881319msgid "Preview"
    12891320msgstr "Onizleme"
     
    16671698msgstr "Geri bildiriminiz icin tesekkurler!"
    16681699
     1700msgid "The archive does not contain any recognizable backup data."
     1701msgstr "Arşiv tanınabilir yedekleme verisi içermiyor."
     1702
    16691703msgid "The authenticated sender email for this relay."
    16701704msgstr "Bu aktarici icin kimlik dogrulanmis gonderen e-postasi."
     
    16761710msgstr "Blok Düzenleyici şu anda devre dışı."
    16771711
     1712msgid "The database backup file is empty or contains no valid SQL statements."
     1713msgstr "Veritabanı yedekleme dosyası boş veya geçerli SQL ifadeleri içermiyor."
     1714
     1715msgid "The following file(s) exceed the server upload limit"
     1716msgstr "Aşağıdaki dosyalar sunucu yükleme sınırını aşıyor"
     1717
    16781718msgid "The new domain is the same as the current domain."
    16791719msgstr "Yeni alan adi mevcut alan adiyla ayni."
     
    18171857msgstr "Uyari: HSTS tarayicilar tarafindan onbellege alindiktan sonra siteniz HTTPS'de kalmalidir. Bu ayari devre disi birakmak tarayici onbellegini hemen geri almaz."
    18181858
     1859msgid "Warning: Restoring a backup will overwrite your current data. This action cannot be undone."
     1860msgstr "Uyarı: bir yedeği geri yüklemek mevcut verilerinizin üzerine yazacaktır. Bu işlem geri alınamaz."
     1861
    18191862msgid "We strongly recommend creating a database backup before changing your domain."
    18201863msgstr "Alan adinizi degistirmeden once bir veritabani yedegi olusturmanizi siddetle oneririz."
  • blaminhor-essentials/trunk/modules/backup/class-module-backup.php

    r3450705 r3451104  
    8686        add_action( 'wp_ajax_ap_backup_delete', array( $this, 'ajax_delete_backup' ) );
    8787        add_action( 'wp_ajax_ap_backup_download', array( $this, 'ajax_download_backup' ) );
     88        add_action( 'wp_ajax_ap_backup_download_bundle', array( $this, 'ajax_download_bundle' ) );
    8889        add_action( 'wp_ajax_ap_backup_restore', array( $this, 'ajax_restore_backup' ) );
    8990        add_action( 'wp_ajax_ap_backup_undo_restore', array( $this, 'ajax_undo_restore' ) );
     
    121122
    122123        $config = array(
    123             'nonce'   => wp_create_nonce( 'blaminhor_essentials_admin' ),
    124             'ajaxurl' => admin_url( 'admin-ajax.php' ),
    125             'strings' => array(
     124            'nonce'          => wp_create_nonce( 'blaminhor_essentials_admin' ),
     125            'ajaxurl'        => admin_url( 'admin-ajax.php' ),
     126            'maxUploadSize'  => wp_max_upload_size(),
     127            'maxUploadLabel' => size_format( wp_max_upload_size() ),
     128            'strings'        => array(
    126129                'error'              => __( 'An error occurred. Please try again.', 'blaminhor-essentials' ),
    127130                'confirmDelete'      => __( 'Are you sure you want to delete this backup?', 'blaminhor-essentials' ),
     
    142145                'uploadSuccess'    => __( 'Backup uploaded successfully!', 'blaminhor-essentials' ),
    143146                'uploading'        => __( 'Uploading', 'blaminhor-essentials' ),
     147            'preparing'        => __( 'Preparing...', 'blaminhor-essentials' ),
    144148                'backupsUploaded'  => __( 'backups uploaded successfully!', 'blaminhor-essentials' ),
    145149                'uploadsFailed'    => __( 'file(s) failed to upload.', 'blaminhor-essentials' ),
     150                'fileTooLarge'     => __( 'The following file(s) exceed the server upload limit', 'blaminhor-essentials' ),
    146151            ),
    147152        );
     
    616621
    617622        // Known components.
    618         $components = array( 'database', 'plugins', 'themes', 'uploads', 'wp-content', 'wp-core', 'full', 'files', 'custom', 'pre-restore', 'uploaded', 'scheduled' );
     623        $components = array( 'database', 'plugins', 'themes', 'uploads', 'wp-content', 'wp-core', 'full', 'files', 'custom', 'pre-restore', 'uploaded', 'scheduled', 'bundle' );
    619624
    620625        $component = 'unknown';
     
    972977                                return 'NULL';
    973978                            }
    974                             return "'" . $wpdb->_real_escape( $value ) . "'";
     979                            return "'" . $wpdb->remove_placeholder_escape( esc_sql( $value ) ) . "'";
    975980                        }, $row );
    976981                        $values[] = '(' . implode( ', ', $row_values ) . ')';
     
    12591264        }
    12601265
     1266        // Detect bundle: a ZIP containing multiple .zip files.
     1267        $is_bundle   = false;
     1268        $zip_entries  = array();
     1269        $num_entries  = $zip->numFiles;
     1270
     1271        for ( $i = 0; $i < $num_entries; $i++ ) {
     1272            $entry_name = $zip->getNameIndex( $i );
     1273            if ( false === $entry_name ) {
     1274                continue;
     1275            }
     1276            // Skip directory entries.
     1277            if ( substr( $entry_name, -1 ) === '/' ) {
     1278                continue;
     1279            }
     1280            if ( strtolower( pathinfo( $entry_name, PATHINFO_EXTENSION ) ) === 'zip' ) {
     1281                $zip_entries[] = $entry_name;
     1282            }
     1283        }
     1284
     1285        if ( count( $zip_entries ) > 1 ) {
     1286            $is_bundle = true;
     1287        }
     1288
    12611289        // Check for manifest or database file to validate it's a backup.
    12621290        $has_manifest = ( $zip->locateName( 'manifest.json' ) !== false );
     
    12701298        }
    12711299
    1272         if ( ! $has_manifest && ! $has_database && ! $is_component_backup ) {
     1300        if ( ! $has_manifest && ! $has_database && ! $is_component_backup && ! $is_bundle ) {
    12731301            $zip->close();
    12741302            wp_send_json_error( __( 'This does not appear to be a valid backup file. It must contain a manifest.json or database.sql file.', 'blaminhor-essentials' ) );
     
    12921320        $zip->close();
    12931321
    1294         // Generate a new filename with current site name and timestamp.
     1322        // Prepare site name and timestamp for destination filenames.
    12951323        $site_name = sanitize_title( get_bloginfo( 'name' ) );
    12961324        if ( empty( $site_name ) ) {
    12971325            $site_name = 'backup';
    12981326        }
    1299         $timestamp    = current_time( 'Y-m-d-H-i-s' );
    1300         $new_filename = $site_name . '-' . $timestamp . '-uploaded.zip';
    1301 
    1302         // Move file to backup directory using WordPress Filesystem API.
    1303         $destination = $this->backup_dir . '/' . $new_filename;
     1327        $timestamp = current_time( 'Y-m-d-H-i-s' );
    13041328
    13051329        global $wp_filesystem;
     
    13071331            require_once ABSPATH . 'wp-admin/includes/file.php';
    13081332            WP_Filesystem();
     1333        }
     1334
     1335        if ( $is_bundle ) {
     1336            // Bundle: extract sub-archives to disk (avoids loading large files into memory).
     1337            $extracted_files = array();
     1338            $extract_zip     = new ZipArchive();
     1339
     1340            if ( $extract_zip->open( $file['tmp_name'] ) !== true ) {
     1341                wp_send_json_error( __( 'Failed to read the bundle archive.', 'blaminhor-essentials' ) );
     1342            }
     1343
     1344            // Create a temporary directory for extraction.
     1345            $temp_dir = $this->backup_dir . '/tmp-upload-' . wp_generate_password( 8, false );
     1346            wp_mkdir_p( $temp_dir );
     1347
     1348            // Collect entries to extract.
     1349            $entries_to_process = array();
     1350            for ( $i = 0; $i < $extract_zip->numFiles; $i++ ) {
     1351                $entry_name = $extract_zip->getNameIndex( $i );
     1352                if ( false === $entry_name || substr( $entry_name, -1 ) === '/' ) {
     1353                    continue;
     1354                }
     1355                if ( strtolower( pathinfo( $entry_name, PATHINFO_EXTENSION ) ) !== 'zip' ) {
     1356                    continue;
     1357                }
     1358                $entries_to_process[] = $entry_name;
     1359            }
     1360
     1361            // Extract all sub-archives to temp directory (streams to disk, no memory issue).
     1362            if ( ! $extract_zip->extractTo( $temp_dir, $entries_to_process ) ) {
     1363                $extract_zip->close();
     1364                $wp_filesystem->rmdir( $temp_dir, true );
     1365                wp_send_json_error( __( 'Failed to save the uploaded file.', 'blaminhor-essentials' ) );
     1366            }
     1367
     1368            $extract_zip->close();
     1369
     1370            // Rename each extracted file to the proper destination.
     1371            foreach ( $entries_to_process as $entry_name ) {
     1372                $extracted_path = $temp_dir . '/' . basename( $entry_name );
     1373                if ( ! file_exists( $extracted_path ) ) {
     1374                    continue;
     1375                }
     1376
     1377                // Determine the component from the sub-archive filename.
     1378                $entry_base = sanitize_file_name( pathinfo( $entry_name, PATHINFO_FILENAME ) );
     1379                $entry_info = $this->parse_backup_filename( $entry_base . '.zip' );
     1380                $component  = $entry_info['component'];
     1381
     1382                // If parse_backup_filename didn't recognize the component,
     1383                // check if the filename itself IS a known component name (e.g. "database.zip").
     1384                if ( 'unknown' === $component || 'bundle' === $component ) {
     1385                    $known_components = array( 'database', 'plugins', 'themes', 'uploads', 'wp-content', 'wp-core' );
     1386                    if ( in_array( $entry_base, $known_components, true ) ) {
     1387                        $component = $entry_base;
     1388                    } else {
     1389                        $component = 'uploaded';
     1390                    }
     1391                }
     1392
     1393                $new_filename = $site_name . '-' . $timestamp . '-' . $component . '.zip';
     1394                $destination  = $this->backup_dir . '/' . $new_filename;
     1395
     1396                // Avoid collisions.
     1397                $counter = 2;
     1398                while ( file_exists( $destination ) ) {
     1399                    $new_filename = $site_name . '-' . $timestamp . '-' . $component . '-' . $counter . '.zip';
     1400                    $destination  = $this->backup_dir . '/' . $new_filename;
     1401                    $counter++;
     1402                }
     1403
     1404                if ( ! $wp_filesystem->move( $extracted_path, $destination ) ) {
     1405                    // Fallback: try copy + delete if rename fails (cross-device move).
     1406                    // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- File operations may fail silently.
     1407                    $content = @file_get_contents( $extracted_path );
     1408                    if ( false === $content || ! $wp_filesystem->put_contents( $destination, $content, FS_CHMOD_FILE ) ) {
     1409                        // Clean up on failure.
     1410                        foreach ( $extracted_files as $extracted ) {
     1411                            $wp_filesystem->delete( $this->backup_dir . '/' . $extracted );
     1412                        }
     1413                        $wp_filesystem->rmdir( $temp_dir, true );
     1414                        wp_send_json_error( __( 'Failed to save the uploaded file.', 'blaminhor-essentials' ) );
     1415                    }
     1416                }
     1417
     1418                $extracted_files[] = $new_filename;
     1419            }
     1420
     1421            // Clean up temp directory.
     1422            $wp_filesystem->rmdir( $temp_dir, true );
     1423
     1424            // Clean up temp file.
     1425            // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.unlink_unlink -- Cleaning up PHP temp file.
     1426            @unlink( $file['tmp_name'] );
     1427
     1428            if ( empty( $extracted_files ) ) {
     1429                wp_send_json_error( __( 'No valid backup archives found in the bundle.', 'blaminhor-essentials' ) );
     1430            }
     1431
     1432            // Get current domain for comparison.
     1433            $current_url    = home_url();
     1434            $parsed_current = wp_parse_url( $current_url );
     1435            $current_domain = isset( $parsed_current['host'] ) ? $parsed_current['host'] : '';
     1436
     1437            $domain_differs = ! empty( $backup_domain ) && $backup_domain !== $current_domain;
     1438
     1439            wp_send_json_success(
     1440                array(
     1441                    'is_bundle'      => true,
     1442                    'files'          => $extracted_files,
     1443                    'file_count'     => count( $extracted_files ),
     1444                    /* translators: %d: number of backup archives extracted from the bundle */
     1445                    'filename'       => sprintf( __( '%d archives extracted', 'blaminhor-essentials' ), count( $extracted_files ) ),
     1446                    'domain_differs' => $domain_differs,
     1447                    'backup_domain'  => $backup_domain,
     1448                    'backup_url'     => $backup_url,
     1449                    'current_domain' => $current_domain,
     1450                    'current_url'    => $current_url,
     1451                )
     1452            );
     1453        }
     1454
     1455        // Single file upload: preserve component suffix from original filename.
     1456        $original_info = $this->parse_backup_filename( $original_name . '.zip' );
     1457        $component     = $original_info['component'];
     1458
     1459        // Use original component if recognized, otherwise default to 'uploaded'.
     1460        $valid_components = array( 'database', 'plugins', 'themes', 'uploads', 'wp-content', 'wp-core', 'full', 'files' );
     1461        if ( ! in_array( $component, $valid_components, true ) ) {
     1462            $component = 'uploaded';
     1463        }
     1464
     1465        $new_filename = $site_name . '-' . $timestamp . '-' . $component . '.zip';
     1466        $destination  = $this->backup_dir . '/' . $new_filename;
     1467
     1468        // Avoid collisions.
     1469        $counter = 2;
     1470        while ( file_exists( $destination ) ) {
     1471            $new_filename = $site_name . '-' . $timestamp . '-' . $component . '-' . $counter . '.zip';
     1472            $destination  = $this->backup_dir . '/' . $new_filename;
     1473            $counter++;
    13091474        }
    13101475
     
    13511516
    13521517        $filename = isset( $_POST['filename'] ) ? sanitize_file_name( wp_unslash( $_POST['filename'] ) ) : '';
     1518        $prefix   = isset( $_POST['prefix'] ) ? sanitize_file_name( wp_unslash( $_POST['prefix'] ) ) : '';
     1519
     1520        // If prefix is provided, find the first matching archive.
     1521        if ( ! empty( $prefix ) && empty( $filename ) ) {
     1522            $archives = glob( $this->backup_dir . '/' . $prefix . '-*.zip' );
     1523            if ( ! empty( $archives ) ) {
     1524                $filename = basename( $archives[0] );
     1525            }
     1526        }
     1527
    13531528        if ( empty( $filename ) ) {
    13541529            wp_send_json_error( __( 'No filename provided.', 'blaminhor-essentials' ) );
     
    14951670        }
    14961671
     1672        $restored_components = array();
     1673
    14971674        // Restore each archive based on its component type.
    14981675        foreach ( $archives as $archive_path ) {
     
    15181695                return $result;
    15191696            }
    1520         }
    1521 
    1522         return true;
     1697
     1698            $restored_components[] = $component;
     1699
     1700            // Collect database stats if available.
     1701            if ( is_array( $result ) && isset( $result['executed'] ) ) {
     1702                $db_stats = $result;
     1703            }
     1704        }
     1705
     1706        // Verify that the database was actually restored when requested.
     1707        if ( $options['database'] && ! in_array( 'database', $restored_components, true ) ) {
     1708            return new WP_Error(
     1709                'database_not_restored',
     1710                sprintf(
     1711                    /* translators: %s: comma-separated list of component names found in the backup */
     1712                    __( 'Database restoration was requested but no database archive was found. Components found: %s', 'blaminhor-essentials' ),
     1713                    implode( ', ', $restored_components )
     1714                )
     1715            );
     1716        }
     1717
     1718        return array(
     1719            'components' => $restored_components,
     1720            'db_stats'   => isset( $db_stats ) ? $db_stats : null,
     1721        );
    15231722    }
    15241723
     
    15391738            // Restore database.
    15401739            $sql_content = $zip->getFromName( 'database.sql' );
    1541             if ( $sql_content ) {
    1542                 $result = $this->import_database( $sql_content );
    1543                 if ( is_wp_error( $result ) ) {
    1544                     $zip->close();
    1545                     return $result;
    1546                 }
    1547             }
     1740            if ( false === $sql_content ) {
     1741                $zip->close();
     1742                return new WP_Error( 'no_sql', __( 'No database.sql found in the database archive.', 'blaminhor-essentials' ) );
     1743            }
     1744            $db_result = $this->import_database( $sql_content );
     1745            if ( is_wp_error( $db_result ) ) {
     1746                $zip->close();
     1747                return $db_result;
     1748            }
     1749            $zip->close();
     1750            return $db_result; // Return array with executed/failed stats.
    15481751        } else {
    15491752            // Restore files.
     
    15661769                list( $source, $dest ) = $destinations[ $component ];
    15671770                if ( is_dir( $source ) ) {
     1771                    // Clear destination before restoring to replace contents, not merge.
     1772                    if ( in_array( $component, array( 'plugins', 'themes', 'uploads' ), true ) ) {
     1773                        $this->clear_directory_contents( $dest );
     1774                    }
    15681775                    $this->copy_directory( $source, $dest );
    15691776                }
     
    15731780                $dirs = glob( $temp_dir . '/*', GLOB_ONLYDIR );
    15741781                foreach ( $dirs as $dir ) {
    1575                     $this->copy_directory( $dir, $dest . '/' . basename( $dir ) );
     1782                    $target = $dest . '/' . basename( $dir );
     1783                    // Clear the specific plugin/theme folder before restoring.
     1784                    if ( is_dir( $target ) ) {
     1785                        $this->delete_directory( $target );
     1786                    }
     1787                    $this->copy_directory( $dir, $target );
    15761788                }
    15771789            }
     
    15931805    private function import_database( $sql_content ) {
    15941806        global $wpdb;
     1807
     1808        // Clean up wpdb placeholder escape hashes left by _real_escape() in older backups.
     1809        // Pattern: {64-char hex SHA-256 hash} that replaced % characters during export.
     1810        $sql_content = preg_replace( '/\{[a-f0-9]{64}\}/', '%', $sql_content );
     1811
     1812        // Detect source table prefix and replace with current prefix if different.
     1813        $source_prefix = '';
     1814        if ( preg_match( '/DROP TABLE IF EXISTS `([a-zA-Z0-9_]+?)options`/', $sql_content, $matches ) ) {
     1815            $source_prefix = $matches[1];
     1816        }
     1817
     1818        if ( ! empty( $source_prefix ) && $source_prefix !== $wpdb->prefix ) {
     1819            $escaped_prefix = preg_quote( $source_prefix, '/' );
     1820            // Only replace prefix in SQL identifiers (backtick-quoted table/column names).
     1821            $sql_content = preg_replace(
     1822                '/`' . $escaped_prefix . '([a-zA-Z0-9_]+)`/',
     1823                '`' . $wpdb->prefix . '$1`',
     1824                $sql_content
     1825            );
     1826        }
    15951827
    15961828        // Split into statements.
     
    16141846        }
    16151847
     1848        if ( empty( $statements ) ) {
     1849            return new WP_Error( 'empty_sql', __( 'The database backup file is empty or contains no valid SQL statements.', 'blaminhor-essentials' ) );
     1850        }
     1851
    16161852        // Execute statements.
     1853        $executed   = 0;
     1854        $failed     = 0;
     1855        $last_error = '';
     1856
    16171857        foreach ( $statements as $statement ) {
    16181858            if ( empty( trim( $statement ) ) ) {
     
    16261866
    16271867            if ( false === $result ) {
    1628                 // Log error but continue (only in debug mode).
    1629                 if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
    1630                     // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Only logs in debug mode
    1631                     error_log( 'Blaminhor Essentials Backup: SQL Error - ' . $wpdb->last_error );
    1632                 }
    1633             }
    1634         }
    1635 
    1636         return true;
     1868                $failed++;
     1869                $last_error = $wpdb->last_error;
     1870            } else {
     1871                $executed++;
     1872            }
     1873        }
     1874
     1875        // Fix prefix-dependent meta keys and options after prefix replacement.
     1876        if ( ! empty( $source_prefix ) && $source_prefix !== $wpdb->prefix ) {
     1877            $old_prefix_like = $wpdb->esc_like( $source_prefix ) . '%';
     1878            $old_prefix_len  = strlen( $source_prefix );
     1879
     1880            // Update all usermeta keys that start with the old prefix.
     1881            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Fixing prefix-dependent meta keys after restore
     1882            $wpdb->query(
     1883                $wpdb->prepare(
     1884                    "UPDATE `{$wpdb->usermeta}` SET `meta_key` = CONCAT(%s, SUBSTRING(`meta_key`, %d)) WHERE `meta_key` LIKE %s",
     1885                    $wpdb->prefix,
     1886                    $old_prefix_len + 1,
     1887                    $old_prefix_like
     1888                )
     1889            );
     1890
     1891            // Fix user_roles option: delete any existing one first to avoid duplicate key error.
     1892            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Fixing prefix-dependent option after restore
     1893            $wpdb->query(
     1894                $wpdb->prepare(
     1895                    "DELETE FROM `{$wpdb->options}` WHERE `option_name` = %s",
     1896                    $wpdb->prefix . 'user_roles'
     1897                )
     1898            );
     1899            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Fixing prefix-dependent option after restore
     1900            $wpdb->query(
     1901                $wpdb->prepare(
     1902                    "UPDATE `{$wpdb->options}` SET `option_name` = %s WHERE `option_name` = %s",
     1903                    $wpdb->prefix . 'user_roles',
     1904                    $source_prefix . 'user_roles'
     1905                )
     1906            );
     1907        }
     1908
     1909        if ( 0 === $executed ) {
     1910            return new WP_Error(
     1911                'sql_all_failed',
     1912                sprintf(
     1913                    /* translators: 1: number of failed statements, 2: last SQL error message */
     1914                    __( 'Database import failed: %1$d statements failed. Last error: %2$s', 'blaminhor-essentials' ),
     1915                    $failed,
     1916                    $last_error
     1917                )
     1918            );
     1919        }
     1920
     1921        return array(
     1922            'executed' => $executed,
     1923            'failed'   => $failed,
     1924        );
     1925    }
     1926
     1927    /**
     1928     * Clear directory contents without removing the directory itself
     1929     *
     1930     * @param string $dir Directory path.
     1931     */
     1932    private function clear_directory_contents( $dir ) {
     1933        if ( ! is_dir( $dir ) ) {
     1934            return;
     1935        }
     1936
     1937        $files = array_diff( scandir( $dir ), array( '.', '..' ) );
     1938        foreach ( $files as $file ) {
     1939            $path = $dir . '/' . $file;
     1940            if ( is_dir( $path ) ) {
     1941                $this->delete_directory( $path );
     1942            } else {
     1943                unlink( $path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
     1944            }
     1945        }
    16371946    }
    16381947
     
    19682277                <tbody>
    19692278                    <?php foreach ( $backups as $backup ) : ?>
    1970                         <tr data-prefix="<?php echo esc_attr( $backup['prefix'] ); ?>" data-type="<?php echo esc_attr( $backup['type'] ); ?>" data-size="<?php echo esc_attr( $backup['total_size'] ); ?>" data-date="<?php echo esc_attr( $backup['date'] ); ?>" data-archives="<?php echo esc_attr( wp_json_encode( wp_list_pluck( $backup['archives'], 'filename' ) ) ); ?>">
     2279                        <tr data-prefix="<?php echo esc_attr( $backup['prefix'] ); ?>" data-type="<?php echo esc_attr( $backup['type'] ); ?>" data-size="<?php echo esc_attr( $backup['total_size'] ); ?>" data-date="<?php echo esc_attr( $backup['date'] ); ?>">
    19712280                            <td>
    19722281                                <code style="font-size: 12px;"><?php echo esc_html( $backup['prefix'] ); ?></code>
     
    21092418
    21102419    /**
     2420     * AJAX: Download backup as a single bundle ZIP
     2421     *
     2422     * Creates a ZIP containing all archives for a given prefix,
     2423     * or returns a direct download URL if only one archive exists.
     2424     */
     2425    public function ajax_download_bundle() {
     2426        check_ajax_referer( 'blaminhor_essentials_admin', 'nonce' );
     2427
     2428        if ( ! current_user_can( 'manage_options' ) ) {
     2429            wp_send_json_error( __( 'Unauthorized', 'blaminhor-essentials' ) );
     2430        }
     2431
     2432        $prefix = isset( $_POST['prefix'] ) ? sanitize_file_name( wp_unslash( $_POST['prefix'] ) ) : '';
     2433        if ( empty( $prefix ) ) {
     2434            wp_send_json_error( __( 'Invalid backup prefix.', 'blaminhor-essentials' ) );
     2435        }
     2436
     2437        $files = glob( $this->backup_dir . '/' . $prefix . '-*.zip' );
     2438        if ( empty( $files ) ) {
     2439            wp_send_json_error( __( 'Backup file not found.', 'blaminhor-essentials' ) );
     2440        }
     2441
     2442        // Filter out any existing bundle files.
     2443        $archives = array();
     2444        foreach ( $files as $file ) {
     2445            if ( strpos( basename( $file ), '-bundle.zip' ) === false ) {
     2446                $archives[] = $file;
     2447            }
     2448        }
     2449
     2450        if ( empty( $archives ) ) {
     2451            wp_send_json_error( __( 'Backup file not found.', 'blaminhor-essentials' ) );
     2452        }
     2453
     2454        // Single archive: download directly without creating a bundle.
     2455        if ( count( $archives ) === 1 ) {
     2456            $filename = basename( $archives[0] );
     2457            wp_send_json_success( array(
     2458                'url' => admin_url( 'admin.php?page=blaminhor-essentials-backup&ap_backup_action=download&file=' . rawurlencode( $filename ) . '&_wpnonce=' . wp_create_nonce( 'ap_backup_action' ) ),
     2459            ) );
     2460        }
     2461
     2462        // Multiple archives: create a bundle ZIP.
     2463        $bundle_name = $prefix . '-bundle.zip';
     2464        $bundle_path = $this->backup_dir . '/' . $bundle_name;
     2465
     2466        if ( file_exists( $bundle_path ) ) {
     2467            wp_delete_file( $bundle_path );
     2468        }
     2469
     2470        $zip = new ZipArchive();
     2471        if ( true !== $zip->open( $bundle_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
     2472            wp_send_json_error( __( 'Failed to create bundle archive.', 'blaminhor-essentials' ) );
     2473        }
     2474
     2475        foreach ( $archives as $archive ) {
     2476            $zip->addFile( $archive, basename( $archive ) );
     2477        }
     2478        $zip->close();
     2479
     2480        wp_send_json_success( array(
     2481            'url' => admin_url( 'admin.php?page=blaminhor-essentials-backup&ap_backup_action=download&file=' . rawurlencode( $bundle_name ) . '&_wpnonce=' . wp_create_nonce( 'ap_backup_action' ) ),
     2482        ) );
     2483    }
     2484
     2485    /**
    21112486     * AJAX: Restore backup
    21122487     */
     
    21642539        }
    21652540
     2541        // Build success message with restore details.
     2542        $message = __( 'Backup restored successfully!', 'blaminhor-essentials' );
     2543        if ( is_array( $result ) ) {
     2544            $components = isset( $result['components'] ) ? $result['components'] : array();
     2545            $db_stats   = isset( $result['db_stats'] ) ? $result['db_stats'] : null;
     2546
     2547            if ( $db_stats ) {
     2548                $message .= sprintf( ' [DB: %d executed, %d failed]', $db_stats['executed'], $db_stats['failed'] );
     2549            }
     2550            $message .= sprintf( ' [Components: %s]', implode( ', ', $components ) );
     2551        }
     2552
    21662553        wp_send_json_success(
    21672554            array(
    2168                 'message'      => __( 'Backup restored successfully!', 'blaminhor-essentials' ),
     2555                'message'      => $message,
    21692556                'redirect_url' => $redirect_url,
    21702557            )
     
    21902577        }
    21912578
    2192         // Enable Domain Changer module if not active.
    2193         $active_modules = get_option( 'blaminhor_essentials_active_modules', array() );
    2194         if ( ! in_array( 'domain-changer', $active_modules, true ) ) {
    2195             $active_modules[] = 'domain-changer';
    2196             update_option( 'blaminhor_essentials_active_modules', $active_modules );
    2197         }
    2198 
    2199         // Update WordPress site URLs first.
    2200         update_option( 'siteurl', $new_url );
    2201         update_option( 'home', $new_url );
    2202 
    2203         // Replace in posts.
     2579        // Extract domains for protocol-agnostic replacement.
     2580        $old_domain = preg_replace( '#^https?://#', '', $old_url );
     2581        $new_domain = preg_replace( '#^https?://#', '', $new_url );
     2582
     2583        // Flush object cache: after a raw SQL database import, the cache is stale
     2584        // and update_option() would compare against cached (pre-import) values,
     2585        // causing it to skip the update if cached value matches new value.
     2586        wp_cache_flush();
     2587
     2588        // Update siteurl and home using direct SQL to bypass cache.
     2589        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2590        $wpdb->update(
     2591            $wpdb->options,
     2592            array( 'option_value' => $new_url ),
     2593            array( 'option_name' => 'siteurl' )
     2594        );
     2595        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2596        $wpdb->update(
     2597            $wpdb->options,
     2598            array( 'option_value' => $new_url ),
     2599            array( 'option_name' => 'home' )
     2600        );
     2601
     2602        // Update other options with serialized data handling.
     2603        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2604        $options = $wpdb->get_results(
     2605            $wpdb->prepare(
     2606                "SELECT option_id, option_value FROM {$wpdb->options} WHERE option_value LIKE %s AND option_name NOT IN ('siteurl', 'home')",
     2607                '%' . $wpdb->esc_like( $old_domain ) . '%'
     2608            )
     2609        );
     2610
     2611        foreach ( $options as $option ) {
     2612            $new_value = $this->replace_domain_in_data( $option->option_value, $old_domain, $new_domain );
     2613            if ( $new_value !== $option->option_value ) {
     2614                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2615                $wpdb->update(
     2616                    $wpdb->options,
     2617                    array( 'option_value' => $new_value ),
     2618                    array( 'option_id' => $option->option_id )
     2619                );
     2620            }
     2621        }
     2622
     2623        // Replace in posts (plain text, no serialized data).
    22042624        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    22052625        $wpdb->query(
    22062626            $wpdb->prepare(
    22072627                "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)",
    2208                 $old_url,
    2209                 $new_url
     2628                'https://' . $old_domain,
     2629                'https://' . $new_domain
    22102630            )
    22112631        );
    2212 
     2632        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2633        $wpdb->query(
     2634            $wpdb->prepare(
     2635                "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)",
     2636                'http://' . $old_domain,
     2637                'http://' . $new_domain
     2638            )
     2639        );
    22132640        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    22142641        $wpdb->query(
    22152642            $wpdb->prepare(
    22162643                "UPDATE {$wpdb->posts} SET guid = REPLACE(guid, %s, %s)",
    2217                 $old_url,
    2218                 $new_url
     2644                $old_domain,
     2645                $new_domain
    22192646            )
    22202647        );
    22212648
    2222         // Replace in postmeta.
     2649        // Update postmeta with serialized data handling.
     2650        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2651        $postmeta = $wpdb->get_results(
     2652            $wpdb->prepare(
     2653                "SELECT meta_id, meta_value FROM {$wpdb->postmeta} WHERE meta_value LIKE %s",
     2654                '%' . $wpdb->esc_like( $old_domain ) . '%'
     2655            )
     2656        );
     2657
     2658        foreach ( $postmeta as $meta ) {
     2659            $new_value = $this->replace_domain_in_data( $meta->meta_value, $old_domain, $new_domain );
     2660            if ( $new_value !== $meta->meta_value ) {
     2661                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_value
     2662                $wpdb->update(
     2663                    $wpdb->postmeta,
     2664                    array( 'meta_value' => $new_value ),
     2665                    array( 'meta_id' => $meta->meta_id )
     2666                );
     2667            }
     2668        }
     2669
     2670        // Replace in comments (plain text).
    22232671        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    22242672        $wpdb->query(
    22252673            $wpdb->prepare(
    2226                 "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_value LIKE %s",
    2227                 $old_url,
    2228                 $new_url,
    2229                 '%' . $wpdb->esc_like( $old_url ) . '%'
     2674                "UPDATE {$wpdb->comments} SET comment_content = REPLACE(comment_content, %s, %s)",
     2675                $old_domain,
     2676                $new_domain
    22302677            )
    22312678        );
    2232 
    2233         // Replace in options (excluding siteurl and home which we already updated).
    22342679        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    22352680        $wpdb->query(
    22362681            $wpdb->prepare(
    2237                 "UPDATE {$wpdb->options} SET option_value = REPLACE(option_value, %s, %s) WHERE option_name NOT IN ('siteurl', 'home') AND option_value LIKE %s",
    2238                 $old_url,
    2239                 $new_url,
    2240                 '%' . $wpdb->esc_like( $old_url ) . '%'
     2682                "UPDATE {$wpdb->comments} SET comment_author_url = REPLACE(comment_author_url, %s, %s)",
     2683                $old_domain,
     2684                $new_domain
    22412685            )
    22422686        );
    22432687
    2244         // Clear caches.
     2688        // Update commentmeta with serialized data handling.
     2689        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2690        $commentmeta = $wpdb->get_results(
     2691            $wpdb->prepare(
     2692                "SELECT meta_id, meta_value FROM {$wpdb->commentmeta} WHERE meta_value LIKE %s",
     2693                '%' . $wpdb->esc_like( $old_domain ) . '%'
     2694            )
     2695        );
     2696
     2697        foreach ( $commentmeta as $meta ) {
     2698            $new_value = $this->replace_domain_in_data( $meta->meta_value, $old_domain, $new_domain );
     2699            if ( $new_value !== $meta->meta_value ) {
     2700                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_value
     2701                $wpdb->update(
     2702                    $wpdb->commentmeta,
     2703                    array( 'meta_value' => $new_value ),
     2704                    array( 'meta_id' => $meta->meta_id )
     2705                );
     2706            }
     2707        }
     2708
     2709        // Update termmeta with serialized data handling.
     2710        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2711        $termmeta = $wpdb->get_results(
     2712            $wpdb->prepare(
     2713                "SELECT meta_id, meta_value FROM {$wpdb->termmeta} WHERE meta_value LIKE %s",
     2714                '%' . $wpdb->esc_like( $old_domain ) . '%'
     2715            )
     2716        );
     2717
     2718        foreach ( $termmeta as $meta ) {
     2719            $new_value = $this->replace_domain_in_data( $meta->meta_value, $old_domain, $new_domain );
     2720            if ( $new_value !== $meta->meta_value ) {
     2721                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_value
     2722                $wpdb->update(
     2723                    $wpdb->termmeta,
     2724                    array( 'meta_value' => $new_value ),
     2725                    array( 'meta_id' => $meta->meta_id )
     2726                );
     2727            }
     2728        }
     2729
     2730        // Update usermeta with serialized data handling.
     2731        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2732        $usermeta = $wpdb->get_results(
     2733            $wpdb->prepare(
     2734                "SELECT umeta_id, meta_value FROM {$wpdb->usermeta} WHERE meta_value LIKE %s",
     2735                '%' . $wpdb->esc_like( $old_domain ) . '%'
     2736            )
     2737        );
     2738
     2739        foreach ( $usermeta as $meta ) {
     2740            $new_value = $this->replace_domain_in_data( $meta->meta_value, $old_domain, $new_domain );
     2741            if ( $new_value !== $meta->meta_value ) {
     2742                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_value
     2743                $wpdb->update(
     2744                    $wpdb->usermeta,
     2745                    array( 'meta_value' => $new_value ),
     2746                    array( 'umeta_id' => $meta->umeta_id )
     2747                );
     2748            }
     2749        }
     2750
     2751        // Replace in links (plain text).
     2752        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2753        $wpdb->query(
     2754            $wpdb->prepare(
     2755                "UPDATE {$wpdb->links} SET link_url = REPLACE(link_url, %s, %s)",
     2756                $old_domain,
     2757                $new_domain
     2758            )
     2759        );
     2760        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     2761        $wpdb->query(
     2762            $wpdb->prepare(
     2763                "UPDATE {$wpdb->links} SET link_image = REPLACE(link_image, %s, %s)",
     2764                $old_domain,
     2765                $new_domain
     2766            )
     2767        );
     2768
     2769        // Flush cache after all replacements.
    22452770        wp_cache_flush();
    22462771
     2772        // If restoring from HTTPS to HTTP, deactivate the HTTPS Redirect module.
     2773        if ( strpos( $old_url, 'https://' ) === 0 && strpos( $new_url, 'https://' ) !== 0 ) {
     2774            $active_modules = get_option( 'blaminhor_essentials_active_modules', array() );
     2775            $key = array_search( 'https-redirect', $active_modules, true );
     2776            if ( false !== $key ) {
     2777                unset( $active_modules[ $key ] );
     2778                update_option( 'blaminhor_essentials_active_modules', array_values( $active_modules ) );
     2779            }
     2780        }
     2781
     2782        // Final cache flush.
     2783        wp_cache_flush();
     2784
    22472785        return true;
     2786    }
     2787
     2788    /**
     2789     * Replace domain in data, handling serialized PHP and JSON safely.
     2790     *
     2791     * Unserializes data before replacing, then re-serializes so that
     2792     * string length prefixes remain correct (prevents data corruption).
     2793     *
     2794     * @param mixed  $data       Data to process.
     2795     * @param string $old_domain Old domain (without protocol).
     2796     * @param string $new_domain New domain (without protocol).
     2797     * @return mixed Processed data.
     2798     */
     2799    private function replace_domain_in_data( $data, $old_domain, $new_domain ) {
     2800        if ( is_string( $data ) ) {
     2801            // Check if it's serialized data.
     2802            $unserialized = @unserialize( $data ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, WordPress.PHP.NoSilencedErrors.Discouraged
     2803            if ( false !== $unserialized && is_array( $unserialized ) ) {
     2804                $processed = $this->replace_domain_in_data( $unserialized, $old_domain, $new_domain );
     2805                return serialize( $processed ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
     2806            }
     2807
     2808            // Check if it's JSON.
     2809            $json = json_decode( $data, true );
     2810            if ( json_last_error() === JSON_ERROR_NONE && is_array( $json ) ) {
     2811                $processed = $this->replace_domain_in_data( $json, $old_domain, $new_domain );
     2812                return wp_json_encode( $processed );
     2813            }
     2814
     2815            // Plain string: replace across all protocols.
     2816            $data = str_replace( 'https://' . $old_domain, 'https://' . $new_domain, $data );
     2817            $data = str_replace( 'http://' . $old_domain, 'http://' . $new_domain, $data );
     2818            $data = str_replace( '//' . $old_domain, '//' . $new_domain, $data );
     2819
     2820            return $data;
     2821        }
     2822
     2823        if ( is_array( $data ) ) {
     2824            foreach ( $data as $key => $value ) {
     2825                $data[ $key ] = $this->replace_domain_in_data( $value, $old_domain, $new_domain );
     2826            }
     2827            return $data;
     2828        }
     2829
     2830        return $data;
    22482831    }
    22492832
     
    25153098                            <tbody>
    25163099                                <?php foreach ( $backups as $backup ) : ?>
    2517                                     <tr data-prefix="<?php echo esc_attr( $backup['prefix'] ); ?>" data-type="<?php echo esc_attr( $backup['type'] ); ?>" data-size="<?php echo esc_attr( $backup['total_size'] ); ?>" data-date="<?php echo esc_attr( $backup['date'] ); ?>" data-archives="<?php echo esc_attr( wp_json_encode( wp_list_pluck( $backup['archives'], 'filename' ) ) ); ?>">
     3100                                    <tr data-prefix="<?php echo esc_attr( $backup['prefix'] ); ?>" data-type="<?php echo esc_attr( $backup['type'] ); ?>" data-size="<?php echo esc_attr( $backup['total_size'] ); ?>" data-date="<?php echo esc_attr( $backup['date'] ); ?>">
    25183101                                        <td style="text-align: center;"><input type="checkbox" class="ap-backup-checkbox" value="<?php echo esc_attr( $backup['prefix'] ); ?>"></td>
    25193102                                        <td>
  • blaminhor-essentials/trunk/modules/post-types-order/class-module-post-types-order.php

    r3450526 r3451104  
    7676        // Auto-sort on frontend and admin list screens.
    7777        if ( $this->get_setting( 'auto_sort', true ) ) {
    78             add_action( 'pre_get_posts', array( $this, 'auto_sort_posts' ) );
     78            add_filter( 'posts_clauses', array( $this, 'auto_sort_posts_clauses' ), 99, 2 );
    7979            add_filter( 'terms_clauses', array( $this, 'auto_sort_terms_clauses' ), 10, 3 );
    8080        }
     
    187187
    188188    /**
    189      * Modify main queries to sort by menu_order (frontend and admin edit.php)
    190      *
    191      * @param WP_Query $query The query object.
    192      */
    193     public function auto_sort_posts( $query ) {
     189     * Modify SQL clauses to sort by menu_order (frontend and admin edit.php)
     190     *
     191     * Uses posts_clauses filter at priority 99 to override the ORDER BY clause
     192     * directly at the SQL level. This ensures the custom order is respected even
     193     * when page builders (Elementor, JetEngine) modify the query via their own
     194     * posts_clauses or posts_orderby filters at lower priorities.
     195     *
     196     * @param array    $clauses SQL clauses (fields, join, where, groupby, orderby, distinct, limits).
     197     * @param WP_Query $query   The query object.
     198     * @return array
     199     */
     200    public function auto_sort_posts_clauses( $clauses, $query ) {
    194201        // In admin, only apply on edit.php list screens and only for the main query.
    195202        if ( is_admin() ) {
    196203            if ( ! $query->is_main_query() ) {
    197                 return;
     204                return $clauses;
    198205            }
    199206            $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
    200207            if ( ! $screen || 'edit' !== $screen->base ) {
    201                 return;
     208                return $clauses;
     209            }
     210            // In admin, don't override explicit column sorting (user clicked a column header).
     211            if ( '' !== $query->get( 'orderby' ) ) {
     212                return $clauses;
    202213            }
    203214        }
     
    210221
    211222        if ( is_array( $post_type ) ) {
    212             return;
     223            if ( 1 === count( $post_type ) ) {
     224                $post_type = reset( $post_type );
     225            } else {
     226                return $clauses;
     227            }
    213228        }
    214229
     
    217232
    218233        if ( ! in_array( $post_type, $enabled, true ) ) {
    219             return;
    220         }
    221 
    222         // On frontend: always apply custom order for enabled post types.
    223         // On admin: only override if no explicit orderby was set (column click).
    224         if ( ! is_admin() || '' === $query->get( 'orderby' ) ) {
    225             $query->set( 'orderby', 'menu_order date' );
    226             $query->set( 'order', 'ASC' );
    227         }
     234            return $clauses;
     235        }
     236
     237        global $wpdb;
     238
     239        $clauses['orderby'] = "{$wpdb->posts}.menu_order ASC, {$wpdb->posts}.post_date DESC";
     240        $clauses['order']   = '';
     241
     242        return $clauses;
    228243    }
    229244
  • blaminhor-essentials/trunk/readme.txt

    r3450705 r3451104  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 1.4.5
     7Stable tag: 1.4.6
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    203203
    204204== Changelog ==
     205
     206= 1.4.6 =
     207* Fixed: Post Types Order - Custom post order now applies correctly on the frontend with Elementor and other page builders.
     208* Fixed: Backup - Corrupted permalinks during SQL export caused by placeholder escaping.
     209* Fixed: Backup - Data corruption during restore when source and target table prefixes differ.
     210* Fixed: Backup - Lost admin permissions after restore with different table prefix.
     211* Fixed: Backup - HTTP/HTTPS protocol difference between source and target sites is now handled correctly during restore.
     212* Fixed: Backup - Serialized data from plugins like Elementor is now properly updated during restore (correct string length recalculation).
     213* Fixed: Backup - Restore now replaces directory contents instead of merging, preventing leftover files from previous installations.
    205214
    206215= 1.4.5 =
     
    326335== Upgrade Notice ==
    327336
     337= 1.4.6 =
     338Backup restore fixes (prefix, permissions, permalinks, serialized data, HTTP/HTTPS). Post Types Order now works with Elementor.
     339
    328340= 1.4.5 =
    329341Fixed Download button crash on PHP 8+ and multi-archive download support.
Note: See TracChangeset for help on using the changeset viewer.