Changeset 3484887
- Timestamp:
- 03/17/2026 03:14:25 PM (13 days ago)
- Location:
- luzid-backup-to-nextcloud/trunk
- Files:
-
- 5 edited
-
admin-page.php (modified) (13 diffs)
-
howto-de.php (modified) (10 diffs)
-
howto-en.php (modified) (9 diffs)
-
luzid-backup-to-nextcloud.php (modified) (17 diffs)
-
readme.txt (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
luzid-backup-to-nextcloud/trunk/admin-page.php
r3481769 r3484887 1 1 <?php 2 2 /** 3 * Admin Page Template for Luzid Backup to Nextcloud v1. 2.93 * Admin Page Template for Luzid Backup to Nextcloud v1.3.0 4 4 * 5 * This file is included from Luzid_Backup_To_Nextcloud::render_admin_page(),6 * which already verifies current_user_can('manage_options').7 5 * Variables $lang, $options, $de_url, $en_url, $t, $active_tab are set by the caller. 8 6 */ … … 13 11 14 12 $luzid_btn_source_names = array( 15 'updraftplus' => 'UpdraftPlus',16 'backwpup'=> 'BackWPup',17 'wpvivid'=> 'WPvivid',18 'ai1wm'=> 'All-in-One WP Migration',19 'duplicator'=> 'Duplicator',20 'custom' => 'Custom', 13 'updraftplus' => 'UpdraftPlus', 14 'backwpup' => 'BackWPup', 15 'wpvivid' => 'WPvivid', 16 'ai1wm' => 'All-in-One WP Migration', 17 'duplicator' => 'Duplicator', 18 'custom' => 'Custom' 21 19 ); 22 20 23 21 // Check for test result – set via wp_safe_redirect after nonce-verified POST handlers. 24 22 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag set by redirect. 25 $luzid_btn_show_test_modal = isset( $_GET['test_result']);23 $luzid_btn_show_test_modal = isset($_GET['test_result']); 26 24 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag set by redirect. 27 25 $luzid_btn_test_success = isset( $_GET['test_result'] ) && 'success' === sanitize_text_field( wp_unslash( $_GET['test_result'] ) ); 28 26 29 // Check for upload modal .30 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag.31 $luzid_btn_show_upload_modal = isset( $_GET['show_upload_modal']);32 33 // Check for settings saved modal .34 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag set by redirect.35 $luzid_btn_show_settings_saved_modal = isset( $_GET['settings_saved']);36 37 // Assets (logo + flags) .38 $luzid_btn_flag_de = LUZID_BACKUP_PLUGIN_URL . 'assets/img/ger.svg';39 $luzid_btn_flag_en = LUZID_BACKUP_PLUGIN_URL . 'assets/img/uk.svg';27 // Check for upload modal 28 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display flag only 29 $luzid_btn_show_upload_modal = isset($_GET['show_upload_modal']); 30 31 // Check for settings saved modal 32 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display flag only 33 $luzid_btn_show_settings_saved_modal = isset($_GET['settings_saved']); 34 35 // Assets (logo + flags) 36 $luzid_btn_flag_de = LUZID_BACKUP_PLUGIN_URL . 'assets/img/ger.svg'; 37 $luzid_btn_flag_en = LUZID_BACKUP_PLUGIN_URL . 'assets/img/uk.svg'; 40 38 $luzid_btn_logo_url = LUZID_BACKUP_PLUGIN_URL . 'assets/img/luzid-media-logo-plugins.svg'; 41 39 42 40 $luzid_btn_lang_switcher_html = '<div class="lcs-lang-switcher">'; 43 $luzid_btn_lang_switcher_html .= '<a href="' . esc_url( $de_url ) . '" class="' . ( 'de' === $lang ? 'active' : '' ) . '">' . '<img src="' . esc_url( $luzid_btn_flag_de) . '" alt="DE" title="Deutsch">' . '</a>';44 $luzid_btn_lang_switcher_html .= '<a href="' . esc_url( $en_url ) . '" class="' . ( 'en' === $lang ? 'active' : '' ) . '">' . '<img src="' . esc_url( $luzid_btn_flag_en) . '" alt="EN" title="English">' . '</a>';41 $luzid_btn_lang_switcher_html .= '<a href="' . esc_url($de_url) . '" class="' . ($lang === 'de' ? 'active' : '') . '">' . '<img src="' . esc_url($luzid_btn_flag_de) . '" alt="DE" title="Deutsch">' . '</a>'; 42 $luzid_btn_lang_switcher_html .= '<a href="' . esc_url($en_url) . '" class="' . ($lang === 'en' ? 'active' : '') . '">' . '<img src="' . esc_url($luzid_btn_flag_en) . '" alt="EN" title="English">' . '</a>'; 45 43 $luzid_btn_lang_switcher_html .= '</div>'; 46 44 47 // Check for delete confirmation .48 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag set by redirect.49 if ( isset( $_GET['all_deleted'] )) {50 echo '<div class="lcs-notice success"><p>' . esc_html( $t['all_deleted']) . '</p></div>';45 // Check for delete confirmation 46 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display flag only 47 if (isset($_GET['all_deleted'])) { 48 echo '<div class="lcs-notice success"><p>' . esc_html($t['all_deleted']) . '</p></div>'; 51 49 } 52 50 53 // Check for logs cleared. 54 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag set by redirect. 55 if ( isset( $_GET['logs_cleared'] ) ) { 56 echo '<div class="lcs-notice success"><p>' . esc_html( $t['logs_cleared'] ) . '</p></div>'; 51 // Check for logs cleared 52 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display flag only 53 if (isset($_GET['logs_cleared'])) { 54 echo '<div class="lcs-notice success"><p>' . esc_html($t['logs_cleared']) . '</p></div>'; 55 } 56 57 // Determine if any backup source is enabled (used for button visibility + hint). 58 $luzid_btn_has_enabled_source = false; 59 if ( ! empty( $options['sources'] ) && is_array( $options['sources'] ) ) { 60 foreach ( $options['sources'] as $luzid_btn_chk ) { 61 if ( ! empty( $luzid_btn_chk['enabled'] ) ) { 62 $luzid_btn_has_enabled_source = true; 63 break; 64 } 65 } 57 66 } 58 67 ?> … … 68 77 <div class="lcs-header-right"> 69 78 <div class="lcs-logo"> 70 <img class="lcs-logo-img" src="<?php echo esc_url( $luzid_btn_logo_url); ?>" alt="Luzid">79 <img class="lcs-logo-img" src="<?php echo esc_url($luzid_btn_logo_url); ?>" alt="Luzid"> 71 80 </div> 72 81 </div> … … 81 90 <button class="lcs-tab-link <?php echo $active_tab === 'tab-howto' ? 'active' : ''; ?>" data-tab="tab-howto"><?php echo esc_html($t['tab_howto']); ?></button> 82 91 </div> 83 <div class="lcs-tabs-nav__right"><?php echo wp_kses_post( $luzid_btn_lang_switcher_html); ?></div>92 <div class="lcs-tabs-nav__right"><?php echo wp_kses_post($luzid_btn_lang_switcher_html); ?></div> 84 93 </div> 85 94 … … 95 104 <th><?php echo esc_html($t['webdav_url']); ?></th> 96 105 <td> 97 <input type="url" name="webdav_url" value="<?php echo esc_attr($options['webdav_url']); ?>" placeholder="https://cloud.example.com/remote.php/dav/files/USERNAME/">106 <input type="url" name="webdav_url" value="<?php echo esc_attr($options['webdav_url']); ?>"> 98 107 <span class="lcs-description"><?php echo esc_html($t['webdav_url_desc']); ?></span> 99 108 </td> … … 108 117 <th><?php echo esc_html($t['password']); ?></th> 109 118 <td> 110 <input type="password" name="webdav_password" placeholder="<?php echo !empty($options['webdav_password']) ? '••••••••' : ''; ?>"> 119 <div style="display: flex; align-items: center; gap: 6px;"> 120 <input type="password" name="luzid_btn_app_password" id="luzid_btn_app_password" autocomplete="new-password" placeholder="<?php echo !empty($options['webdav_password']) ? '••••••••' : ''; ?>" value=""> 121 <button type="button" id="luzid-btn-toggle-pw" class="button button-secondary" style="padding: 2px 8px; min-height: 30px; line-height: 28px;" title="<?php echo 'de' === $lang ? 'Passwort anzeigen/verbergen' : 'Show/hide password'; ?>"> 122 <span class="dashicons dashicons-visibility" style="vertical-align: middle;"></span> 123 </button> 124 </div> 125 <script> 126 (function(){ 127 var btn = document.getElementById('luzid-btn-toggle-pw'); 128 if (btn) { 129 btn.addEventListener('click', function() { 130 var inp = document.getElementById('luzid_btn_app_password'); 131 var ico = btn.querySelector('.dashicons'); 132 if (inp.type === 'password') { 133 inp.type = 'text'; 134 ico.classList.remove('dashicons-visibility'); 135 ico.classList.add('dashicons-hidden'); 136 } else { 137 inp.type = 'password'; 138 ico.classList.remove('dashicons-hidden'); 139 ico.classList.add('dashicons-visibility'); 140 } 141 }); 142 } 143 })(); 144 </script> 111 145 <span class="lcs-description"><?php echo esc_html($t['password_desc']); ?></span> 112 <div class="lcs-checkbox-wrapper" style="margin-top: 10px;"> 113 <input type="checkbox" name="remove_password" value="1" id="remove_password"> 114 <label for="remove_password"><?php echo esc_html($t['remove_password']); ?></label> 115 </div> 146 116 147 </td> 117 148 </tr> … … 164 195 165 196 <?php 166 // Show hint if no source has been selected yet.167 $luzid_btn_has_enabled_source = false;168 if ( ! empty( $options['sources'] ) && is_array( $options['sources'] ) ) {169 foreach ( $options['sources'] as $luzid_btn_s ) {170 if ( ! empty( $luzid_btn_s['enabled'] ) ) {171 $luzid_btn_has_enabled_source = true;172 break;173 }174 }175 }176 197 if ( ! $luzid_btn_has_enabled_source ) { 177 198 echo '<p style="margin-top: 20px;"><strong>' . esc_html( $t['select_source_hint'] ?? 'Bitte wähle eine Backup-Quelle' ) . '</strong></p>'; … … 184 205 <h2><?php echo esc_html($t['sources_title']); ?></h2> 185 206 186 <?php foreach ( $luzid_btn_source_names as $luzid_btn_source_id => $luzid_btn_source_name ) :187 $luzid_btn_source = isset( $options['sources'][ $luzid_btn_source_id ] ) ? $options['sources'][ $luzid_btn_source_id ] : array();188 $luzid_btn_enabled = ! empty( $luzid_btn_source['enabled']);207 <?php foreach ($luzid_btn_source_names as $luzid_btn_source_id => $luzid_btn_source_name): 208 $luzid_btn_source = isset( $options['sources'][ $luzid_btn_source_id ] ) ? $options['sources'][ $luzid_btn_source_id ] : array(); 209 $luzid_btn_enabled = !empty($luzid_btn_source['enabled']); 189 210 ?> 190 211 <div class="lcs-source-item"> 191 212 <div class="lcs-source-header"> 192 <h3><?php echo esc_html( $luzid_btn_source_name); ?></h3>213 <h3><?php echo esc_html($luzid_btn_source_name); ?></h3> 193 214 <div class="lcs-checkbox-wrapper"> 194 <input type="checkbox" name="sources[<?php echo esc_attr( $luzid_btn_source_id ); ?>][enabled]" value="1" id="source_<?php echo esc_attr( $luzid_btn_source_id ); ?>" <?php checked( $luzid_btn_enabled); ?>>195 <label for="source_<?php echo esc_attr( $luzid_btn_source_id ); ?>"><?php echo esc_html( $t['source_enabled']); ?></label>215 <input type="checkbox" name="sources[<?php echo esc_attr($luzid_btn_source_id); ?>][enabled]" value="1" id="source_<?php echo esc_attr($luzid_btn_source_id); ?>" <?php checked($luzid_btn_enabled); ?>> 216 <label for="source_<?php echo esc_attr($luzid_btn_source_id); ?>"><?php echo esc_html($t['source_enabled']); ?></label> 196 217 </div> 197 218 </div> 198 219 <div class="lcs-source-fields"> 199 220 <div class="lcs-source-field"> 200 <label><?php echo esc_html( $t['source_path']); ?></label>201 <input type="text" name="sources[<?php echo esc_attr( $luzid_btn_source_id ); ?>][path]" value="<?php echo esc_attr( $luzid_btn_source['path'] ?? ''); ?>">221 <label><?php echo esc_html($t['source_path']); ?></label> 222 <input type="text" name="sources[<?php echo esc_attr($luzid_btn_source_id); ?>][path]" value="<?php echo esc_attr($luzid_btn_source['path'] ?? ''); ?>"> 202 223 </div> 203 224 <div class="lcs-source-field"> 204 <label><?php echo esc_html( $t['source_extensions']); ?></label>205 <input type="text" name="sources[<?php echo esc_attr( $luzid_btn_source_id ); ?>][extensions]" value="<?php echo esc_attr( $luzid_btn_source['extensions'] ?? ''); ?>">225 <label><?php echo esc_html($t['source_extensions']); ?></label> 226 <input type="text" name="sources[<?php echo esc_attr($luzid_btn_source_id); ?>][extensions]" value="<?php echo esc_attr($luzid_btn_source['extensions'] ?? ''); ?>"> 206 227 </div> 207 228 <div class="lcs-checkbox-wrapper"> 208 <input type="checkbox" name="sources[<?php echo esc_attr( $luzid_btn_source_id ); ?>][scan_real_files]" value="1" id="scan_<?php echo esc_attr( $luzid_btn_source_id ); ?>" <?php checked( ! empty( $luzid_btn_source['scan_real_files'] )); ?>>209 <label for="scan_<?php echo esc_attr( $luzid_btn_source_id ); ?>"><?php echo esc_html( $t['source_scan_real']); ?></label>210 <span class="lcs-description"><?php echo esc_html( $t['source_scan_real_desc'] ?? ''); ?></span>229 <input type="checkbox" name="sources[<?php echo esc_attr($luzid_btn_source_id); ?>][scan_real_files]" value="1" id="scan_<?php echo esc_attr($luzid_btn_source_id); ?>" <?php checked(!empty($luzid_btn_source['scan_real_files'])); ?>> 230 <label for="scan_<?php echo esc_attr($luzid_btn_source_id); ?>"><?php echo esc_html($t['source_scan_real']); ?></label> 231 <span class="lcs-description"><?php echo esc_html($t['source_scan_real_desc'] ?? ''); ?></span> 211 232 </div> 212 233 </div> … … 300 321 <div class="lcs-log-container"> 301 322 <?php 302 $luzid_btn_logs = get_option( 'luzid_backup_logs', array());303 if ( empty( $luzid_btn_logs )) {323 $luzid_btn_logs = get_option('luzid_backup_logs', array()); 324 if (empty($luzid_btn_logs)) { 304 325 echo '<div class="log-info">No logs yet</div>'; 305 326 } else { 306 foreach ( array_reverse( array_slice( $luzid_btn_logs, -50 ) ) as $luzid_btn_log) {307 $luzid_btn_class = 'log-' . esc_attr( $luzid_btn_log['level']);308 echo '<div class="' . esc_attr( $luzid_btn_class ) . '">[' . esc_html( $luzid_btn_log['time'] ) . '] ' . esc_html( $luzid_btn_log['message']) . '</div>';327 foreach (array_reverse(array_slice($luzid_btn_logs, -50)) as $luzid_btn_log) { 328 $luzid_btn_class = 'log-' . esc_attr($luzid_btn_log['level']); 329 echo '<div class="' . esc_attr($luzid_btn_class) . '">[' . esc_html($luzid_btn_log['time']) . '] ' . esc_html($luzid_btn_log['message']) . '</div>'; 309 330 } 310 331 } … … 318 339 <?php 319 340 $luzid_btn_howto_file = LUZID_BACKUP_PLUGIN_DIR . 'howto-' . $lang . '.php'; 320 if ( file_exists( $luzid_btn_howto_file )) {341 if (file_exists($luzid_btn_howto_file)) { 321 342 include $luzid_btn_howto_file; 322 343 } … … 326 347 </form> 327 348 328 <!-- Action Buttons - Always visible below tabs-->329 <div style="padding: 20px 30px; border-top: 1px solid #e5e5e5; background: #fafafa;">349 <!-- Action Buttons - Visibility depends on active tab --> 350 <div id="luzid-btn-actions" data-has-source="<?php echo $luzid_btn_has_enabled_source ? '1' : '0'; ?>" style="padding: 20px 30px; border-top: 1px solid #e5e5e5; background: #fafafa;"> 330 351 <div class="lcs-button-group"> 331 <button type="submit" form="luzid-backup-form" class="lcs-button" ><?php echo esc_html($t['save_settings']); ?></button>352 <button type="submit" form="luzid-backup-form" class="lcs-button" id="luzid-btn-save"><?php echo esc_html($t['save_settings']); ?></button> 332 353 333 <button type="button" onclick="luzidSaveAndTest()" class="lcs-button lcs-button-secondary" ><?php echo esc_html($t['test_connection']); ?></button>334 335 <button type="button" onclick="luzidStartUpload()" class="lcs-button lcs-button-secondary" ><?php echo esc_html($t['manual_run']); ?></button>354 <button type="button" onclick="luzidSaveAndTest()" class="lcs-button lcs-button-secondary" id="luzid-btn-test"><?php echo esc_html($t['test_connection']); ?></button> 355 356 <button type="button" onclick="luzidStartUpload()" class="lcs-button lcs-button-secondary" id="luzid-btn-upload"><?php echo esc_html($t['manual_run']); ?></button> 336 357 337 358 <a href="<?php echo esc_url(wp_nonce_url(add_query_arg(array('action' => 'luzid_backup_delete_all', 'luzid_lang' => $lang), admin_url('admin-post.php')), 'luzid_backup_delete_all')); ?>" 338 class="lcs-button lcs-button-secondary" 359 class="lcs-button lcs-button-secondary" id="luzid-btn-delete" 339 360 onclick="return confirm('<?php echo esc_js($t['confirm_delete_all']); ?>')"> 340 361 <?php echo esc_html($t['delete_all']); ?> … … 342 363 </div> 343 364 </div> 365 <script> 366 (function(){ 367 function luzidUpdateButtons() { 368 var tabInput = document.getElementById('active-tab-input'); 369 var tab = tabInput ? tabInput.value : 'tab-nextcloud'; 370 var btnSave = document.getElementById('luzid-btn-save'); 371 var btnTest = document.getElementById('luzid-btn-test'); 372 var btnUpload = document.getElementById('luzid-btn-upload'); 373 var btnDelete = document.getElementById('luzid-btn-delete'); 374 var bar = document.getElementById('luzid-btn-actions'); 375 var hasSource = bar && bar.getAttribute('data-has-source') === '1'; 376 377 if (tab === 'tab-nextcloud') { 378 btnSave.style.display = 'none'; 379 btnTest.style.display = ''; 380 btnUpload.style.display = 'none'; 381 btnDelete.style.display = ''; 382 bar.style.display = ''; 383 } else if (tab === 'tab-howto') { 384 bar.style.display = 'none'; 385 } else { 386 btnSave.style.display = ''; 387 btnTest.style.display = 'none'; 388 btnUpload.style.display = ''; 389 btnDelete.style.display = ''; 390 bar.style.display = ''; 391 } 392 // Upload button: disabled (greyed out) when no source is enabled 393 if (btnUpload) { 394 if (!hasSource) { 395 btnUpload.disabled = true; 396 btnUpload.style.opacity = '0.5'; 397 btnUpload.style.cursor = 'not-allowed'; 398 } else { 399 btnUpload.disabled = false; 400 btnUpload.style.opacity = ''; 401 btnUpload.style.cursor = ''; 402 } 403 } 404 } 405 document.addEventListener('click', function(e) { 406 if (e.target.classList.contains('lcs-tab-link')) { 407 setTimeout(luzidUpdateButtons, 10); 408 } 409 }); 410 document.addEventListener('DOMContentLoaded', luzidUpdateButtons); 411 })(); 412 </script> 344 413 </div> 345 414 </div> … … 363 432 <div id="test-result-modal" class="lcs-overlay <?php echo $luzid_btn_show_test_modal ? 'active' : ''; ?>"> 364 433 <div class="lcs-overlay-content"> 365 <h2><?php echo esc_html( $luzid_btn_test_success ? $t['test_success'] : $t['test_failed']); ?></h2>434 <h2><?php echo esc_html($luzid_btn_test_success ? $t['test_success'] : $t['test_failed']); ?></h2> 366 435 <p style="font-size: 48px; margin: 20px 0; text-align: center;"><?php echo $luzid_btn_test_success ? '✓' : '✗'; ?></p> 367 <p style="text-align: center;"><?php echo esc_html( $luzid_btn_test_success ? $t['test_success'] : $t['test_failed']); ?></p>436 <p style="text-align: center;"><?php echo esc_html($luzid_btn_test_success ? $t['test_success'] : $t['test_failed']); ?></p> 368 437 <div class="lcs-button-group"> 369 438 <button type="button" onclick="luzidCloseModal('test-result-modal')" class="lcs-button"><?php echo esc_html($t['close']); ?></button> -
luzid-backup-to-nextcloud/trunk/howto-de.php
r3481769 r3484887 4 4 */ 5 5 6 // Exit if accessed directly 7 if (!defined('ABSPATH')) { 6 if ( ! defined( 'ABSPATH' ) ) { 8 7 exit; 9 8 } … … 14 13 15 14 <h3>Voraussetzungen</h3> 16 17 <p>Bevor Sie beginnen, stellen Sie sicher, dass Sie folgendes haben:</p>18 15 19 16 <ul> … … 27 24 <p>Aus Sicherheitsgründen sollten Sie ein <strong>App-Passwort</strong> verwenden, nicht Ihr Hauptpasswort.</p> 28 25 29 <h4>So erstellen Sie ein App-Passwort:</h4>30 31 26 <ol> 32 27 <li>Melden Sie sich in Ihrer Nextcloud an</li> 33 <li>Klicken Sie oben rechts auf Ihr Profilbild</li> 34 <li>Wählen Sie <strong>Einstellungen</strong></li> 35 <li>Navigieren Sie zu <strong>Sicherheit</strong> im linken Menü</li> 36 <li>Scrollen Sie zu <strong>Geräte & Sitzungen</strong></li> 28 <li>Klicken Sie oben rechts auf Ihr Profilbild → <strong>Einstellungen</strong></li> 29 <li>Navigieren Sie zu <strong>Sicherheit</strong> → <strong>Geräte & Sitzungen</strong></li> 37 30 <li>Geben Sie einen App-Namen ein (z.B. "WordPress Backup")</li> 38 31 <li>Klicken Sie auf <strong>Neues App-Passwort erstellen</strong></li> … … 40 33 </ol> 41 34 42 <p><strong>Wichtig:</strong> Bewahren Sie dieses Passwort sicher auf. Sie benötigen es für die Plugin-Konfiguration.</p>43 44 35 <h3>Schritt 2: WebDAV-URL herausfinden</h3> 45 46 <p>Die WebDAV-URL ist die Adresse, über die das Plugin auf Ihre Nextcloud zugreift.</p>47 48 <h4>So finden Sie Ihre WebDAV-URL:</h4>49 36 50 37 <ol> 51 38 <li>Melden Sie sich in Ihrer Nextcloud an</li> 52 39 <li>Klicken Sie links unten auf das <strong>Einstellungen-Symbol</strong> (Zahnrad)</li> 53 <li>Gehen Sie zu <strong>Dateien</strong> → <strong>WebDAV</strong></li> 54 <li>Kopieren Sie die angezeigte WebDAV-URL</li> 40 <li>Die WebDAV-URL wird unter <strong>Dateien → WebDAV</strong> angezeigt</li> 55 41 </ol> 56 42 57 <p><strong>Die URL s ollte so aussehen:</strong></p>43 <p><strong>Die URL sieht so aus:</strong></p> 58 44 <pre>https://ihre-nextcloud.de/remote.php/dav/files/IHR-BENUTZERNAME/</pre> 59 45 60 <p><strong>Wichtig:</strong> Die URL muss mit einem <code>/</code> (Schrägstrich)enden!</p>46 <p><strong>Wichtig:</strong> Die URL muss mit einem <code>/</code> enden!</p> 61 47 62 <h3>Schritt 3: Plugin konfigurieren</h3> 63 64 <h4>Nextcloud-Verbindung einrichten:</h4> 48 <h3>Schritt 3: Verbindung einrichten</h3> 65 49 66 50 <ol> 67 51 <li>Gehen Sie in WordPress zu <strong>Luzid WP Tools → Backup to Nextcloud</strong></li> 68 <li>Im Tab <strong>Nextcloud</strong>:</li> 69 <ul> 70 <li><strong>WebDAV Base URL:</strong> Fügen Sie Ihre WebDAV-URL ein</li> 71 <li><strong>Benutzername:</strong> Ihr Nextcloud-Benutzername</li> 72 <li><strong>App-Passwort:</strong> Das in Schritt 1 erstellte App-Passwort</li> 73 <li><strong>Remote Ordner:</strong> Name des Zielordners in Nextcloud (z.B. "WP-Backups")</li> 74 </ul> 52 <li>Im Tab <strong>Setup</strong> füllen Sie alle Felder aus: 53 <ul> 54 <li><strong>WebDAV Base URL:</strong> Ihre WebDAV-URL</li> 55 <li><strong>Benutzername:</strong> Ihr Nextcloud-Benutzername</li> 56 <li><strong>App-Passwort:</strong> Das in Schritt 1 erstellte Passwort. Mit dem Auge-Symbol können Sie das Passwort sichtbar machen, um es zu überprüfen.</li> 57 <li><strong>Remote Ordner:</strong> Pfad zum Zielordner in Nextcloud (z.B. "/backups/wordpress")</li> 58 </ul> 59 </li> 75 60 <li>Klicken Sie auf <strong>Speichern & Testen</strong></li> 76 <li> Wenn die Verbindung erfolgreich ist, sehen Sie eine Bestätigung</li>61 <li>Bei erfolgreicher Verbindung erscheint eine Bestätigung mit ✓</li> 77 62 </ol> 78 63 79 <p><strong>Hinweis:</strong> Der Zielordner wird automatisch erstellt, falls er nicht existiert. </p>64 <p><strong>Hinweis:</strong> Der Zielordner wird automatisch erstellt, falls er nicht existiert. Im Setup-Tab gibt es nur den Button "Speichern & Testen" – damit werden Ihre Einstellungen gespeichert und die Verbindung gleichzeitig geprüft.</p> 80 65 81 66 <h4>Email Reporting konfigurieren (Optional):</h4> 82 67 83 <p>Im Bereich <strong>Reporting</strong> können Sie E-Mail-Benachrichtigungen einrichten:</p>68 <p>Im Bereich <strong>Reporting</strong> (ebenfalls im Setup-Tab) können Sie E-Mail-Benachrichtigungen einrichten:</p> 84 69 85 70 <ol> … … 88 73 <li>Wählen Sie aus: 89 74 <ul> 90 <li><strong>Bei erfolgreichem Transfer:</strong> Benachrichtigung bei jedem erfolgreichen Backup-Upload</li>75 <li><strong>Bei erfolgreichem Transfer:</strong> Benachrichtigung bei jedem erfolgreichen Upload</li> 91 76 <li><strong>Bei fehlerhaftem Transfer:</strong> Benachrichtigung nur bei Fehlern (empfohlen)</li> 92 77 </ul> 93 78 </li> 94 <li>Klicken Sie auf <strong>Einstellungen speichern</strong></li>95 79 </ol> 96 80 97 <h 4>Backup-Quellen aktivieren:</h4>81 <h3>Schritt 4: Backup-Quellen aktivieren</h3> 98 82 99 83 <ol> 100 84 <li>Wechseln Sie zum Tab <strong>Backup-Quellen</strong></li> 101 85 <li>Aktivieren Sie die Checkbox bei Ihrem Backup-Plugin (z.B. UpdraftPlus)</li> 102 <li>Überprüfen Sie den <strong>Lokalen Pfad</strong> (wird automatisch gesetzt)</li> 103 <li>Passen Sie die <strong>Dateiendungen</strong> an, falls nötig (z.B. <code>zip,gz,tar.gz</code>)</li> 104 <li>Optional: Aktivieren Sie <strong>Nur echte Dateien scannen</strong> (überspringt Dateien mit Berechtigungsproblemen)</li> 86 <li>Überprüfen Sie den <strong>Lokalen Pfad</strong> (wird automatisch vorausgefüllt)</li> 87 <li>Passen Sie die <strong>Dateiendungen</strong> an, falls nötig</li> 105 88 <li>Klicken Sie auf <strong>Einstellungen speichern</strong></li> 106 89 </ol> 107 90 108 <p><strong>Unterstützte Backup-Plugins:</strong></p> 109 <ul> 110 <li>UpdraftPlus</li> 111 <li>BackWPup</li> 112 <li>WPvivid</li> 113 <li>All-in-One WP Migration</li> 114 <li>Duplicator</li> 115 <li>Custom (für eigene Backup-Ordner)</li> 116 </ul> 91 <p><strong>Unterstützte Backup-Plugins:</strong> UpdraftPlus, BackWPup, WPvivid, All-in-One WP Migration, Duplicator sowie eigene Ordner (Custom).</p> 117 92 118 <h3>Schritt 4: Zeitplan einrichten (Optional)</h3>93 <h3>Schritt 5: Ersten Upload testen</h3> 119 94 120 <p>Sie können automatische Uploads zu bestimmten Zeiten einrichten.</p> 95 <ol> 96 <li>Stellen Sie sicher, dass Ihr Backup-Plugin mindestens ein Backup erstellt hat</li> 97 <li>Wechseln Sie zum Tab <strong>Backup-Quellen</strong> oder <strong>Zeitplan & Logs</strong></li> 98 <li>Klicken Sie auf <strong>Upload manuell starten</strong></li> 99 <li>Ein Fenster zeigt den Fortschritt an</li> 100 <li>Warten Sie, bis "Upload erfolgreich abgeschlossen!" erscheint</li> 101 <li>Prüfen Sie in Ihrer Nextcloud, ob die Dateien angekommen sind</li> 102 </ol> 103 104 <p><strong>Hinweis:</strong> Der Button "Upload manuell starten" wird erst aktiv, wenn mindestens eine Backup-Quelle aktiviert wurde. Große Dateien werden automatisch per Streaming hochgeladen, um Speicher- und Timeout-Probleme zu vermeiden.</p> 105 106 <h3>Schritt 6: Zeitplan einrichten (Optional)</h3> 121 107 122 108 <ol> 123 109 <li>Gehen Sie zum Tab <strong>Zeitplan & Logs</strong></li> 124 110 <li>Aktivieren Sie <strong>Zeitplan aktivieren</strong></li> 125 <li>Wählen Sie die <strong>Frequenz</strong>:</li> 126 <ul> 127 <li><strong>Täglich um:</strong> Upload jeden Tag zur gewählten Uhrzeit</li> 128 <li><strong>Wöchentlich am:</strong> Upload einmal pro Woche an einem bestimmten Wochentag</li> 129 <li><strong>Monatlich am:</strong> Upload einmal pro Monat an einem bestimmten Tag</li> 130 </ul> 131 <li>Wählen Sie die gewünschte <strong>Uhrzeit</strong> (z.B. 03:00 für 3 Uhr nachts)</li> 111 <li>Wählen Sie die <strong>Frequenz</strong>: 112 <ul> 113 <li><strong>Täglich um:</strong> Upload jeden Tag zur gewählten Uhrzeit</li> 114 <li><strong>Wöchentlich am:</strong> Upload einmal pro Woche</li> 115 <li><strong>Monatlich am:</strong> Upload einmal pro Monat</li> 116 </ul> 117 </li> 118 <li>Wählen Sie die gewünschte <strong>Uhrzeit</strong></li> 132 119 <li>Klicken Sie auf <strong>Einstellungen speichern</strong></li> 133 120 </ol> 134 121 135 <p><strong>Wichtig:</strong> WordPress Cron ist traffic-abhängig. Die Ausführung erfolgt, wenn nach der eingestellten Zeit Ihre Website besucht wird. Bei wenig Traffic kann es zu Verzögerungen kommen.</p>122 <p><strong>Wichtig:</strong> WordPress Cron ist traffic-abhängig. Die Ausführung erfolgt, wenn nach der eingestellten Zeit Ihre Website besucht wird.</p> 136 123 137 124 <h4>Rotation einrichten (Optional):</h4> … … 141 128 <ol> 142 129 <li>Aktivieren Sie <strong>Rotation aktivieren</strong></li> 143 <li>Geben Sie an, wie viele Backup-Sets pro Quelle behalten werden sollen (z.B. 10)</li> 144 <li>Ältere Backups werden automatisch gelöscht</li> 145 </ol> 146 147 <h3>Schritt 5: Ersten Upload testen</h3> 148 149 <ol> 150 <li>Stellen Sie sicher, dass Ihr Backup-Plugin mindestens ein Backup erstellt hat</li> 151 <li>Klicken Sie auf <strong>Upload manuell starten</strong></li> 152 <li>Ein Fenster öffnet sich und zeigt den Fortschritt</li> 153 <li>Warten Sie, bis "Upload erfolgreich abgeschlossen!" angezeigt wird</li> 154 <li>Prüfen Sie in Ihrer Nextcloud, ob die Dateien angekommen sind</li> 130 <li>Geben Sie an, wie viele Backup-Sets pro Quelle behalten werden sollen</li> 131 <li>Ältere Backups werden bei jedem Upload-Durchlauf automatisch gelöscht</li> 155 132 </ol> 156 133 … … 160 137 161 138 <pre> 162 WP-Backups/163 ├── ihre -domain_de/139 Ihr-Remote-Ordner/ 140 ├── ihre_domain_de/ 164 141 │ ├── updraftplus/ 165 │ │ ├── 202 50207-1430/166 │ │ │ ├── backup-database. zip142 │ │ ├── 2026-03-17-1430/ 143 │ │ │ ├── backup-database.gz 167 144 │ │ │ ├── backup-plugins.zip 168 │ │ │ └── backup-themes.zip 169 │ │ └── 20250206-1430/ 145 │ │ │ ├── backup-themes.zip 146 │ │ │ └── backup-uploads.zip 147 │ │ └── 2026-03-16-1430/ 170 148 │ │ └── ... 171 149 │ └── backwpup/ … … 173 151 </pre> 174 152 175 <p>Jeder Backup-Durchlauf erhält einen eigenen Unterordner mit Zeitstempel.</p>176 177 153 <h3>Logs und Fehlerbehebung</h3> 178 154 179 <h4>Logs anzeigen:</h4> 180 181 <p>Im Tab <strong>Zeitplan & Logs</strong> finden Sie eine detaillierte Aufzeichnung aller Aktivitäten:</p> 182 183 <ul> 184 <li><strong>Info:</strong> Normale Informationen (z.B. "Backup upload started")</li> 185 <li><strong>Success:</strong> Erfolgreiche Aktionen (z.B. "Uploaded: file.zip")</li> 186 <li><strong>Warning:</strong> Warnungen (z.B. "Source path not found")</li> 187 <li><strong>Error:</strong> Fehler (z.B. "Upload failed")</li> 188 </ul> 155 <p>Im Tab <strong>Zeitplan & Logs</strong> finden Sie eine detaillierte Aufzeichnung aller Aktivitäten.</p> 189 156 190 157 <h4>Häufige Probleme:</h4> 191 158 192 <p><strong> Problem:Verbindungstest schlägt fehl</strong></p>159 <p><strong>Verbindungstest schlägt fehl</strong></p> 193 160 <ul> 194 161 <li>Überprüfen Sie die WebDAV-URL (muss mit / enden)</li> 195 <li>Stellen Sie sicher, dass Sie Ihr <strong>App-Passwort</strong> verwenden, nicht Ihr Hauptpasswort</li> 162 <li>Stellen Sie sicher, dass Sie ein <strong>App-Passwort</strong> verwenden, nicht Ihr Hauptpasswort</li> 163 <li>Nutzen Sie das Auge-Symbol, um zu prüfen, ob Ihr Browser das Passwort-Feld automatisch mit einem falschen Passwort ausgefüllt hat</li> 196 164 <li>Prüfen Sie, ob Ihre Nextcloud von außen erreichbar ist</li> 197 <li>Kontaktieren Sie ggf. Ihren Nextcloud-Administrator</li>198 165 </ul> 199 166 200 <p><strong> Problem: Upload wird nichtgefunden</strong></p>167 <p><strong>Keine Backup-Dateien gefunden</strong></p> 201 168 <ul> 202 169 <li>Prüfen Sie im Tab "Backup-Quellen", ob der lokale Pfad korrekt ist</li> 203 170 <li>Stellen Sie sicher, dass Ihr Backup-Plugin tatsächlich Backups erstellt hat</li> 204 <li>Überprüfen Sie die Dateiendungen (z.B. .zip, .gz)</li>171 <li>Überprüfen Sie die Dateiendungen</li> 205 172 </ul> 206 173 207 <p><strong> Problem: Zeitplan wird nicht ausgeführt</strong></p>174 <p><strong>Upload bleibt hängen</strong></p> 208 175 <ul> 209 <li> WordPress Cron benötigt Website-Traffic zur Ausführung</li>210 <li> Prüfen Sie, ob "Zeitplan aktivieren" aktiviert ist</li>211 <li> Testen Sie zunächst mit "Upload manuell starten"</li>176 <li>Bei sehr großen Dateien (> 200 MB) kann der Upload einige Minuten dauern</li> 177 <li>Das Plugin verwendet für große Dateien automatisch Streaming-Uploads, die wenig RAM benötigen</li> 178 <li>Prüfen Sie die Logs auf Fehlermeldungen</li> 212 179 </ul> 213 180 … … 217 184 <li>Verwenden Sie <strong>immer</strong> ein Nextcloud App-Passwort, nie Ihr Hauptpasswort</li> 218 185 <li>Aktivieren Sie HTTPS/SSL für Ihre Nextcloud-Verbindung</li> 219 <li>Schützen Sie Ihren WordPress-Admin-Bereich</li>220 186 <li>Erstellen Sie regelmäßig Backups und testen Sie die Wiederherstellung</li> 221 <li>Löschen Sie alte App-Passwörter, wenn Sie sie nicht mehr benötigen</li>222 </ul>223 224 <h3>Tipps und Best Practices</h3>225 226 <ul>227 <li><strong>Backup-Zeitplan:</strong> Führen Sie Backups nachts aus (geringere Last)</li>228 <li><strong>Rotation:</strong> Behalten Sie mindestens die letzten 7-14 Backup-Sets</li>229 <li><strong>Speicherplatz:</strong> Überwachen Sie Ihren Nextcloud-Speicher regelmäßig</li>230 <li><strong>Test:</strong> Testen Sie die Wiederherstellung aus Nextcloud</li>231 <li><strong>Mehrere Quellen:</strong> Sie können mehrere Backup-Plugins gleichzeitig verwenden</li>232 187 </ul> 233 188 … … 238 193 <ul> 239 194 <li>Prüfen Sie die Logs im Tab "Zeitplan & Logs"</li> 240 <li>Schicken Sie eine Supportanfrage mit einer ausführlichen Fehlerbeschreibungan <a href="mailto:[email protected]">[email protected]</a></li>195 <li>Schicken Sie eine Supportanfrage an <a href="mailto:[email protected]">[email protected]</a></li> 241 196 </ul> 242 243 <p><strong>Viel Erfolg mit Ihren Backups!</strong></p> -
luzid-backup-to-nextcloud/trunk/howto-en.php
r3481769 r3484887 4 4 */ 5 5 6 // Exit if accessed directly 7 if (!defined('ABSPATH')) { 6 if ( ! defined( 'ABSPATH' ) ) { 8 7 exit; 9 8 } … … 11 10 <h2>Welcome to Luzid Backup to Nextcloud</h2> 12 11 13 <p>This plugin allows you to automatically upload WordPress backup files to your Nextcloud. It is compatible with the most popular backup plugins and offers additionalfeatures such as scheduled uploads and automatic rotation of old backups.</p>12 <p>This plugin allows you to automatically upload WordPress backup files to your Nextcloud. It is compatible with the most popular backup plugins and offers features such as scheduled uploads and automatic rotation of old backups.</p> 14 13 15 14 <h3>Prerequisites</h3> 16 17 <p>Before you begin, make sure you have:</p>18 15 19 16 <ul> … … 25 22 <h3>Step 1: Create Nextcloud App Password</h3> 26 23 27 <p>For security reasons, you should use an <strong>App Password</strong>, not your main password.</p> 28 29 <h4>How to create an App Password:</h4> 24 <p>For security reasons, use an <strong>App Password</strong>, not your main password.</p> 30 25 31 26 <ol> 32 27 <li>Log in to your Nextcloud</li> 33 <li>Click on your profile picture in the top right</li> 34 <li>Select <strong>Settings</strong></li> 35 <li>Navigate to <strong>Security</strong> in the left menu</li> 36 <li>Scroll to <strong>Devices & Sessions</strong></li> 28 <li>Click your profile picture (top right) → <strong>Settings</strong></li> 29 <li>Navigate to <strong>Security</strong> → <strong>Devices & Sessions</strong></li> 37 30 <li>Enter an app name (e.g. "WordPress Backup")</li> 38 31 <li>Click <strong>Create new app password</strong></li> 39 <li>Copy the generated password (you 'll only see it once!)</li>32 <li>Copy the generated password (you will only see it once!)</li> 40 33 </ol> 41 42 <p><strong>Important:</strong> Keep this password safe. You'll need it for the plugin configuration.</p>43 34 44 35 <h3>Step 2: Find Your WebDAV URL</h3> 45 36 46 <p>The WebDAV URL is the address through which the plugin accesses your Nextcloud.</p>47 48 <h4>How to find your WebDAV URL:</h4>49 50 37 <ol> 51 38 <li>Log in to your Nextcloud</li> 52 <li>Click on the <strong>Settings icon</strong> (gear) in the bottom left</li> 53 <li>Go to <strong>Files</strong> → <strong>WebDAV</strong></li> 54 <li>Copy the displayed WebDAV URL</li> 39 <li>Click the <strong>Settings icon</strong> (gear) in the bottom left</li> 40 <li>The WebDAV URL is shown under <strong>Files → WebDAV</strong></li> 55 41 </ol> 56 42 57 <p><strong>The URL should looklike this:</strong></p>43 <p><strong>The URL looks like this:</strong></p> 58 44 <pre>https://your-nextcloud.com/remote.php/dav/files/YOUR-USERNAME/</pre> 59 45 60 <p><strong>Important:</strong> The URL must end with a <code>/</code> (slash)!</p>46 <p><strong>Important:</strong> The URL must end with a <code>/</code>!</p> 61 47 62 <h3>Step 3: Configure the Plugin</h3> 63 64 <h4>Set up Nextcloud connection:</h4> 48 <h3>Step 3: Set Up Connection</h3> 65 49 66 50 <ol> 67 51 <li>In WordPress, go to <strong>Luzid WP Tools → Backup to Nextcloud</strong></li> 68 <li>In the <strong>Nextcloud</strong> tab:</li> 69 <ul> 70 <li><strong>WebDAV Base URL:</strong> Paste your WebDAV URL</li> 71 <li><strong>Username:</strong> Your Nextcloud username</li> 72 <li><strong>App Password:</strong> The app password created in Step 1</li> 73 <li><strong>Remote Folder:</strong> Name of the target folder in Nextcloud (e.g. "WP-Backups")</li> 74 </ul> 52 <li>In the <strong>Setup</strong> tab, fill in all fields: 53 <ul> 54 <li><strong>WebDAV Base URL:</strong> Your WebDAV URL</li> 55 <li><strong>Username:</strong> Your Nextcloud username</li> 56 <li><strong>App Password:</strong> The password from Step 1. Use the eye icon to reveal the password and verify it.</li> 57 <li><strong>Remote Folder:</strong> Path to the target folder in Nextcloud (e.g. "/backups/wordpress")</li> 58 </ul> 59 </li> 75 60 <li>Click <strong>Save & Test</strong></li> 76 <li> If the connection is successful, you'll see aconfirmation</li>61 <li>A successful connection shows a ✓ confirmation</li> 77 62 </ol> 78 63 79 <p><strong>Note:</strong> The target folder will be created automatically if it doesn't exist.</p>64 <p><strong>Note:</strong> The target folder is created automatically if it does not exist. The Setup tab only has the "Save & Test" button, which saves your settings and tests the connection in one step.</p> 80 65 81 66 <h4>Configure Email Reporting (Optional):</h4> 82 67 83 <p>In the <strong>Reporting</strong> section , you can set up email notifications:</p>68 <p>In the <strong>Reporting</strong> section (also in the Setup tab), you can set up email notifications:</p> 84 69 85 70 <ol> … … 88 73 <li>Choose: 89 74 <ul> 90 <li><strong>On successful transfer:</strong> Notification for every successful backupupload</li>75 <li><strong>On successful transfer:</strong> Notification for every successful upload</li> 91 76 <li><strong>On failed transfer:</strong> Notification only for errors (recommended)</li> 92 77 </ul> 93 78 </li> 94 <li>Click <strong>Save Settings</strong></li>95 79 </ol> 96 80 97 <h 4>Activate backup sources:</h4>81 <h3>Step 4: Activate Backup Sources</h3> 98 82 99 83 <ol> 100 84 <li>Switch to the <strong>Backup Sources</strong> tab</li> 101 85 <li>Check the box for your backup plugin (e.g. UpdraftPlus)</li> 102 <li>Verify the <strong>Local Path</strong> (set automatically)</li> 103 <li>Adjust <strong>File Extensions</strong> if needed (e.g. <code>zip,gz,tar.gz</code>)</li> 104 <li>Optional: Enable <strong>Scan real files only</strong> (skips files with permission issues)</li> 86 <li>Verify the <strong>Local Path</strong> (pre-filled automatically)</li> 87 <li>Adjust <strong>File Extensions</strong> if needed</li> 105 88 <li>Click <strong>Save Settings</strong></li> 106 89 </ol> 107 90 108 <p><strong>Supported backup plugins:</strong></p> 109 <ul> 110 <li>UpdraftPlus</li> 111 <li>BackWPup</li> 112 <li>WPvivid</li> 113 <li>All-in-One WP Migration</li> 114 <li>Duplicator</li> 115 <li>Custom (for your own backup folders)</li> 116 </ul> 91 <p><strong>Supported backup plugins:</strong> UpdraftPlus, BackWPup, WPvivid, All-in-One WP Migration, Duplicator, and custom folders.</p> 117 92 118 <h3>Step 4: Set Up Schedule (Optional)</h3>93 <h3>Step 5: Test First Upload</h3> 119 94 120 <p>You can set up automatic uploads at specific times.</p> 95 <ol> 96 <li>Make sure your backup plugin has created at least one backup</li> 97 <li>Switch to the <strong>Backup Sources</strong> or <strong>Schedule & Logs</strong> tab</li> 98 <li>Click <strong>Start Manual Upload</strong></li> 99 <li>A window shows the upload progress</li> 100 <li>Wait until "Upload completed successfully!" is displayed</li> 101 <li>Check your Nextcloud to verify the files arrived</li> 102 </ol> 103 104 <p><strong>Note:</strong> The "Start Manual Upload" button only becomes active once at least one backup source has been enabled. Large files are automatically uploaded via streaming to avoid memory and timeout issues.</p> 105 106 <h3>Step 6: Set Up Schedule (Optional)</h3> 121 107 122 108 <ol> 123 109 <li>Go to the <strong>Schedule & Logs</strong> tab</li> 124 110 <li>Enable <strong>Enable Schedule</strong></li> 125 <li>Choose the <strong>Frequency</strong>:</li> 126 <ul> 127 <li><strong>Daily at:</strong> Upload every day at the chosen time</li> 128 <li><strong>Weekly on:</strong> Upload once per week on a specific weekday</li> 129 <li><strong>Monthly on:</strong> Upload once per month on a specific day</li> 130 </ul> 131 <li>Select the desired <strong>Time</strong> (e.g. 03:00 for 3 AM)</li> 111 <li>Choose the <strong>Frequency</strong>: 112 <ul> 113 <li><strong>Daily at:</strong> Upload every day at the chosen time</li> 114 <li><strong>Weekly on:</strong> Upload once per week</li> 115 <li><strong>Monthly on:</strong> Upload once per month</li> 116 </ul> 117 </li> 118 <li>Select the desired <strong>Time</strong></li> 132 119 <li>Click <strong>Save Settings</strong></li> 133 120 </ol> 134 121 135 <p><strong>Important:</strong> WordPress Cron is traffic-based. Execution occurs when your website is visited after the s et time. With low traffic, delays may occur.</p>122 <p><strong>Important:</strong> WordPress Cron is traffic-based. Execution occurs when your website is visited after the scheduled time.</p> 136 123 137 <h4>Set up rotation (Optional):</h4>124 <h4>Set Up Rotation (Optional):</h4> 138 125 139 126 <p>Rotation automatically deletes old backups in Nextcloud to save storage space.</p> … … 141 128 <ol> 142 129 <li>Enable <strong>Enable Rotation</strong></li> 143 <li>Specify how many backup sets per source should be kept (e.g. 10)</li> 144 <li>Older backups will be automatically deleted</li> 145 </ol> 146 147 <h3>Step 5: Test First Upload</h3> 148 149 <ol> 150 <li>Make sure your backup plugin has created at least one backup</li> 151 <li>Click <strong>Start Manual Upload</strong></li> 152 <li>A window opens showing the progress</li> 153 <li>Wait until "Upload completed successfully!" is displayed</li> 154 <li>Check in your Nextcloud if the files have arrived</li> 130 <li>Specify how many backup sets per source should be kept</li> 131 <li>Older backups are automatically deleted during each upload run</li> 155 132 </ol> 156 133 … … 160 137 161 138 <pre> 162 WP-Backups/163 ├── your -domain_com/139 Your-Remote-Folder/ 140 ├── your_domain_com/ 164 141 │ ├── updraftplus/ 165 │ │ ├── 202 50207-1430/166 │ │ │ ├── backup-database. zip142 │ │ ├── 2026-03-17-1430/ 143 │ │ │ ├── backup-database.gz 167 144 │ │ │ ├── backup-plugins.zip 168 │ │ │ └── backup-themes.zip 169 │ │ └── 20250206-1430/ 145 │ │ │ ├── backup-themes.zip 146 │ │ │ └── backup-uploads.zip 147 │ │ └── 2026-03-16-1430/ 170 148 │ │ └── ... 171 149 │ └── backwpup/ … … 173 151 </pre> 174 152 175 <p>Each backup run gets its own subfolder with timestamp.</p>176 177 153 <h3>Logs and Troubleshooting</h3> 178 154 179 < h4>View logs:</h4>155 <p>In the <strong>Schedule & Logs</strong> tab you will find a detailed record of all activities.</p> 180 156 181 < p>In the <strong>Schedule & Logs</strong> tab you'll find a detailed record of all activities:</p>157 <h4>Common Issues:</h4> 182 158 159 <p><strong>Connection test fails</strong></p> 183 160 <ul> 184 <li> <strong>Info:</strong> Normal information (e.g. "Backup upload started")</li>185 <li> <strong>Success:</strong> Successful actions (e.g. "Uploaded: file.zip")</li>186 <li> <strong>Warning:</strong> Warnings (e.g. "Source path not found")</li>187 <li> <strong>Error:</strong> Errors (e.g. "Upload failed")</li>161 <li>Check the WebDAV URL (must end with /)</li> 162 <li>Make sure you are using an <strong>App Password</strong>, not your main password</li> 163 <li>Use the eye icon to check whether your browser auto-filled the password field with the wrong password</li> 164 <li>Check if your Nextcloud is accessible from the internet</li> 188 165 </ul> 189 166 190 <h4>Common issues:</h4> 191 192 <p><strong>Issue: Connection test fails</strong></p> 193 <ul> 194 <li>Check the WebDAV URL (must end with /)</li> 195 <li>Make sure you're using your <strong>App Password</strong>, not your main password</li> 196 <li>Check if your Nextcloud is accessible from outside</li> 197 <li>Contact your Nextcloud administrator if necessary</li> 198 </ul> 199 200 <p><strong>Issue: Upload not found</strong></p> 167 <p><strong>No backup files found</strong></p> 201 168 <ul> 202 169 <li>Check in the "Backup Sources" tab if the local path is correct</li> 203 170 <li>Make sure your backup plugin has actually created backups</li> 204 <li>Verify the file extensions (e.g. .zip, .gz)</li>171 <li>Verify the file extensions</li> 205 172 </ul> 206 173 207 <p><strong> Issue: Schedule not executing</strong></p>174 <p><strong>Upload gets stuck</strong></p> 208 175 <ul> 209 <li> WordPress Cron requires website traffic to execute</li>210 <li> Check if "Enable Schedule" is enabled</li>211 <li> Test first with "Start Manual Upload"</li>176 <li>Very large files (> 200 MB) may take several minutes to upload</li> 177 <li>The plugin automatically uses streaming uploads for large files, which require very little RAM</li> 178 <li>Check the logs for error messages</li> 212 179 </ul> 213 180 … … 217 184 <li><strong>Always</strong> use a Nextcloud App Password, never your main password</li> 218 185 <li>Enable HTTPS/SSL for your Nextcloud connection</li> 219 <li>Protect your WordPress admin area</li>220 186 <li>Create regular backups and test restoration</li> 221 <li>Delete old app passwords when you no longer need them</li>222 </ul>223 224 <h3>Tips and Best Practices</h3>225 226 <ul>227 <li><strong>Backup schedule:</strong> Run backups at night (lower load)</li>228 <li><strong>Rotation:</strong> Keep at least the last 7-14 backup sets</li>229 <li><strong>Storage:</strong> Monitor your Nextcloud storage regularly</li>230 <li><strong>Test:</strong> Test restoration from Nextcloud</li>231 <li><strong>Multiple sources:</strong> You can use multiple backup plugins simultaneously</li>232 187 </ul> 233 188 … … 238 193 <ul> 239 194 <li>Check the logs in the "Schedule & Logs" tab</li> 240 <li>Send a support request with a detailed error descriptionto <a href="mailto:[email protected]">[email protected]</a></li>195 <li>Send a support request to <a href="mailto:[email protected]">[email protected]</a></li> 241 196 </ul> 242 243 <p><strong>Good luck with your backups!</strong></p> -
luzid-backup-to-nextcloud/trunk/luzid-backup-to-nextcloud.php
r3481809 r3484887 3 3 * Plugin Name: Luzid Backup to Nextcloud 4 4 * Description: Upload WordPress backup files to Nextcloud via WebDAV with rotation and retention management 5 * Version: 1. 2.105 * Version: 1.3.0 6 6 * Author: Luzid Media 7 7 * Text Domain: luzid-backup-to-nextcloud … … 16 16 17 17 // Define plugin constants 18 define('LUZID_BACKUP_VERSION', '1. 2.9');18 define('LUZID_BACKUP_VERSION', '1.3.0'); 19 19 define('LUZID_BACKUP_PLUGIN_FILE', __FILE__); 20 20 define('LUZID_BACKUP_PLUGIN_DIR', plugin_dir_path(__FILE__)); … … 376 376 sanitize_text_field($input['webdav_username']) : ''; 377 377 378 if (isset($input['remove_password']) && $input['remove_password']) { 379 $sanitized['webdav_password'] = ''; 380 } elseif (isset($input['webdav_password']) && !empty($input['webdav_password'])) { 378 if (isset($input['webdav_password']) && !empty($input['webdav_password'])) { 381 379 // Store as entered (app password) 382 380 $sanitized['webdav_password'] = $input['webdav_password']; … … 409 407 $sanitized['sources'][$source_id] = array( 410 408 'enabled' => !empty($posted['enabled']) ? 1 : 0, 411 'path' => isset($posted['path']) ? sanitize_text_field($posted['path']) : $default_config['path'],412 'extensions' => isset($posted['extensions']) ? sanitize_text_field($posted['extensions']) : $default_config['extensions'],409 'path' => !empty($posted['path']) ? sanitize_text_field($posted['path']) : $default_config['path'], 410 'extensions' => !empty($posted['extensions']) ? sanitize_text_field($posted['extensions']) : $default_config['extensions'], 413 411 'scan_real_files' => !empty($posted['scan_real_files']) ? 1 : 0, 414 412 ); … … 480 478 $lang = $this->get_current_lang(); 481 479 $options = get_option('luzid_backup_options', $this->get_default_options()); 480 // Ensure sources always contain default paths and extensions. 481 $default_sources = $this->get_default_sources(); 482 if ( empty($options['sources']) || ! is_array($options['sources']) ) { 483 $options['sources'] = $default_sources; 484 } else { 485 foreach ( $default_sources as $src_id => $src_defaults ) { 486 if ( ! isset($options['sources'][$src_id]) ) { 487 $options['sources'][$src_id] = $src_defaults; 488 } else { 489 $options['sources'][$src_id] = wp_parse_args($options['sources'][$src_id], $src_defaults); 490 } 491 } 492 } 482 493 483 494 $current_url = remove_query_arg('luzid_lang'); … … 492 503 493 504 if (isset($_POST['luzid_backup_save']) && check_admin_referer('luzid_backup_save')) { 494 // Extract only plugin-specific fields to avoid processing entire $_POST 505 // Extract only plugin-specific fields to avoid processing entire $_POST. 506 $luzid_btn_pw_field = isset($_POST['luzid_btn_app_password']) ? sanitize_text_field(wp_unslash($_POST['luzid_btn_app_password'])) : ''; 495 507 $input = array( 496 508 'active_tab' => isset($_POST['active_tab']) ? sanitize_text_field(wp_unslash($_POST['active_tab'])) : '', 497 509 'webdav_url' => isset($_POST['webdav_url']) ? esc_url_raw(wp_unslash($_POST['webdav_url'])) : '', 498 510 'webdav_username' => isset($_POST['webdav_username']) ? sanitize_text_field(wp_unslash($_POST['webdav_username'])) : '', 499 'webdav_password' => isset($_POST['webdav_password']) ? sanitize_text_field(wp_unslash($_POST['webdav_password'])) : '', 500 'remove_password' => isset($_POST['remove_password']) ? sanitize_text_field(wp_unslash($_POST['remove_password'])) : '', 511 'webdav_password' => $luzid_btn_pw_field, 501 512 'remote_folder' => isset($_POST['remote_folder']) ? sanitize_text_field(wp_unslash($_POST['remote_folder'])) : '', 502 513 'email_reporting_enabled' => isset($_POST['email_reporting_enabled']) ? absint($_POST['email_reporting_enabled']) : 0, … … 504 515 'email_on_success' => isset($_POST['email_on_success']) ? absint($_POST['email_on_success']) : 0, 505 516 'email_on_error' => isset($_POST['email_on_error']) ? absint($_POST['email_on_error']) : 0, 506 'sources' => isset($_POST['sources']) && is_array($_POST['sources']) ? map_deep(wp_unslash($_POST['sources']), 'sanitize_text_field') : array(), 517 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nested array; each value is sanitized individually in sanitize_options(). 518 'sources' => isset($_POST['sources']) && is_array($_POST['sources']) ? wp_unslash($_POST['sources']) : array(), 507 519 'schedule_enabled' => isset($_POST['schedule_enabled']) ? absint($_POST['schedule_enabled']) : 0, 508 520 'schedule_frequency' => isset($_POST['schedule_frequency']) ? sanitize_text_field(wp_unslash($_POST['schedule_frequency'])) : '', … … 549 561 'webdav_username' => '', 550 562 'webdav_password' => '', 551 'remote_folder' => ' WP-Backups',563 'remote_folder' => '', 552 564 'email_reporting_enabled' => 0, 553 565 'email_address' => '', … … 631 643 'username' => 'Benutzername', 632 644 'password' => 'App-Passwort', 633 'password_desc' => 'Leer lassen = Passwort behalten. Verwenden Sie ein Nextcloud App-Passwort.', 634 'remove_password' => 'Passwort entfernen', 645 'password_desc' => 'Verwenden Sie ein Nextcloud App-Passwort.', 635 646 'remote_folder' => 'Remote Ordner', 636 647 'remote_folder_desc' => 'Zielordner in Nextcloud, z.B. WP-Backups', … … 649 660 'source_scan_real' => 'Nur echte Dateien scannen', 650 661 'source_scan_real_desc' => 'Wenn aktiv, werden nur lesbare Dateien berücksichtigt (überspringt Dateien mit Berechtigungsproblemen).', 651 'select_source_hint' => ' Bitte wähle eine Backup-Quelle',662 'select_source_hint' => 'Fülle alle Felder aus und drücke anschließend "Speichern & Testen". Wenn die Verbindung erfolgreich ist, wähle anschließend eine Backup-Quelle.', 652 663 'schedule_title' => 'Zeitplan', 653 664 'schedule_enabled' => 'Zeitplan aktivieren', … … 703 714 'username' => 'Username', 704 715 'password' => 'App Password', 705 'password_desc' => 'Leave empty to keep existing. Use Nextcloud app password.', 706 'remove_password' => 'Remove password', 716 'password_desc' => 'Use a Nextcloud app password.', 707 717 'remote_folder' => 'Remote Folder', 708 718 'remote_folder_desc' => 'Target folder in Nextcloud, e.g. WP-Backups', … … 721 731 'source_scan_real' => 'Scan real files only', 722 732 'source_scan_real_desc' => 'If enabled, only readable files are included (skips files with permission issues).', 723 'select_source_hint' => ' Please select a backup source',733 'select_source_hint' => 'Fill in all fields and press "Save & Test". Once the connection is successful, select a backup source.', 724 734 'schedule_title' => 'Schedule', 725 735 'schedule_enabled' => 'Enable Schedule', … … 777 787 } 778 788 779 // Extract only plugin-specific fields to avoid processing entire $_POST 789 // Extract only plugin-specific fields to avoid processing entire $_POST. 790 $luzid_btn_pw_field = isset($_POST['luzid_btn_app_password']) ? sanitize_text_field(wp_unslash($_POST['luzid_btn_app_password'])) : ''; 780 791 $input = array( 781 792 'active_tab' => isset($_POST['active_tab']) ? sanitize_text_field(wp_unslash($_POST['active_tab'])) : '', 782 793 'webdav_url' => isset($_POST['webdav_url']) ? esc_url_raw(wp_unslash($_POST['webdav_url'])) : '', 783 794 'webdav_username' => isset($_POST['webdav_username']) ? sanitize_text_field(wp_unslash($_POST['webdav_username'])) : '', 784 'webdav_password' => isset($_POST['webdav_password']) ? sanitize_text_field(wp_unslash($_POST['webdav_password'])) : '', 785 'remove_password' => isset($_POST['remove_password']) ? sanitize_text_field(wp_unslash($_POST['remove_password'])) : '', 795 'webdav_password' => $luzid_btn_pw_field, 786 796 'remote_folder' => isset($_POST['remote_folder']) ? sanitize_text_field(wp_unslash($_POST['remote_folder'])) : '', 787 797 'email_reporting_enabled' => isset($_POST['email_reporting_enabled']) ? absint($_POST['email_reporting_enabled']) : 0, … … 866 876 } 867 877 868 delete_option('luzid_backup_options');878 // Remove logs, state and cron schedule. 869 879 delete_option('luzid_backup_state'); 870 880 delete_option('luzid_backup_logs'); 881 wp_clear_scheduled_hook('luzid_backup_cron_hook'); 882 883 // Reset options to defaults (preserves source paths and file extensions). 884 update_option('luzid_backup_options', $this->get_default_options()); 871 885 872 886 $lang = $this->get_current_lang(); … … 1185 1199 $remote_url = $options['webdav_url'] . ltrim($remote_path, '/') . $filename; 1186 1200 $auth = base64_encode($options['webdav_username'] . ':' . $options['webdav_password']); 1187 $this->log('info', "Uploading: {$filename}"); 1188 1189 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Large file streaming needed for WebDAV PUT 1201 $filesize = filesize($local_path); 1202 $filesize_mb = round($filesize / 1048576, 1); 1203 $this->log('info', "Uploading: {$filename} ({$filesize_mb} MB)"); 1204 1205 // Attempt to extend PHP limits for large uploads. 1206 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, Squiz.PHP.DiscouragedFunctions.Discouraged -- Graceful timeout extension for large file uploads. 1207 @set_time_limit(600); 1208 1209 // Large files (>2 MB): use cURL streaming to avoid loading entire file into RAM. 1210 if ($filesize > 2 * 1048576 && function_exists('curl_init')) { 1211 return $this->upload_file_curl($local_path, $remote_url, $auth, $filename); 1212 } 1213 1214 // Small files: use wp_remote_request (keeps full WP compatibility). 1215 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Reading local backup file for WebDAV PUT. 1190 1216 $file_data = file_get_contents($local_path); 1191 1217 if ($file_data === false) { … … 1201 1227 ), 1202 1228 'body' => $file_data, 1203 'timeout' => 300, // 5 minutes for large files1229 'timeout' => 300, 1204 1230 )); 1205 1231 … … 1212 1238 if ($http_code >= 200 && $http_code < 300) { 1213 1239 $this->log('success', "Uploaded: {$filename}"); 1240 return true; 1241 } else { 1242 $this->log('error', "Upload failed: {$filename} (HTTP {$http_code})"); 1243 return false; 1244 } 1245 } 1246 1247 /** 1248 * Upload a file via cURL with streaming (reads from disk, never loads entire file into RAM). 1249 */ 1250 private function upload_file_curl($local_path, $remote_url, $auth, $filename) { 1251 // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_init -- Required for streaming large files that exceed PHP memory limits. 1252 $ch = curl_init(); 1253 1254 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Opening local backup file for streaming upload. 1255 $fh = fopen($local_path, 'rb'); 1256 if ( ! $fh ) { 1257 $this->log('error', "Cannot open file for streaming: {$filename}"); 1258 return false; 1259 } 1260 1261 $filesize = filesize($local_path); 1262 1263 // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_setopt_array -- Required for streaming PUT upload with CURLOPT_INFILE; no wp_remote_request equivalent. 1264 curl_setopt_array($ch, array( 1265 CURLOPT_URL => $remote_url, 1266 CURLOPT_PUT => true, 1267 CURLOPT_INFILE => $fh, 1268 CURLOPT_INFILESIZE => $filesize, 1269 CURLOPT_RETURNTRANSFER => true, 1270 CURLOPT_TIMEOUT => 600, 1271 CURLOPT_CONNECTTIMEOUT => 30, 1272 CURLOPT_HTTPHEADER => array( 1273 'Authorization: Basic ' . $auth, 1274 'Content-Type: application/octet-stream', 1275 ), 1276 CURLOPT_SSL_VERIFYPEER => true, 1277 )); 1278 1279 // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_exec 1280 $result = curl_exec($ch); 1281 // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_getinfo 1282 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 1283 // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_error 1284 $curl_error = curl_error($ch); 1285 // phpcs:ignore WordPress.WP.AlternativeFunctions.curl_curl_close 1286 curl_close($ch); 1287 fclose($fh); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Closing file handle opened for cURL streaming. 1288 1289 if ( ! empty($curl_error) ) { 1290 $this->log('error', "Upload failed (cURL): {$filename} - {$curl_error}"); 1291 return false; 1292 } 1293 1294 if ($http_code >= 200 && $http_code < 300) { 1295 $this->log('success', "Uploaded: {$filename} (streamed)"); 1214 1296 return true; 1215 1297 } else { -
luzid-backup-to-nextcloud/trunk/readme.txt
r3481809 r3484887 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 1. 2.107 Stable tag: 1.3.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 17 17 It is designed for two main workflows: 18 18 19 1 ) **Automate offsite copies**: upload backup archives on a schedule (WordPress Cron)20 2 ) **Manual runs + monitoring**: start uploads manually and follow progress + logs19 1. **Automate offsite copies**: upload backup archives on a schedule (WordPress Cron). 20 2. **Manual runs + monitoring**: start uploads manually and follow progress + logs. 21 21 22 22 Typical use cases: 23 23 24 24 * Keep an offsite copy of backup archives generated by popular backup plugins. 25 * Upload backups to a structured Nextcloud folder (per source ).25 * Upload backups to a structured Nextcloud folder (per source, per domain). 26 26 * Reduce storage usage with optional rotation (keep only the last N backup sets per source). 27 27 28 28 Core concepts: 29 29 30 * **Sources**: each source represents a folder where backup files are created (UpdraftPlus, BackWPup, … or custom). 31 * **Upload**: the plugin scans sources for backup files and uploads them to Nextcloud. 32 * **Schedule**: optional daily/weekly/monthly runs at a fixed time (WordPress Cron; traffic-dependent). 33 * **Rotation**: keep the newest N backup sets and remove older ones on Nextcloud. 34 * **Email Reporting**: get notified on successful or failed transfers. 30 * **Sources** – each source represents a folder where backup files are created (UpdraftPlus, BackWPup, WPvivid, All-in-One WP Migration, Duplicator, or custom). 31 * **Upload** – the plugin scans sources for backup files and uploads them to Nextcloud via WebDAV PUT. 32 * **Streaming** – files larger than 2 MB are uploaded via cURL streaming (reads from disk, never loads the entire file into RAM). This prevents memory and timeout issues on shared hosting. 33 * **Schedule** – optional daily/weekly/monthly runs at a fixed time (WordPress Cron; traffic-dependent). 34 * **Rotation** – keep the newest N backup sets and remove older ones on Nextcloud. 35 * **Email Reporting** – get notified on successful or failed transfers. 35 36 36 37 == Features == 37 38 38 * Nextcloud WebDAV connection (URL, username, app password) 39 * Multiple backup sources (known plugins + custom) 39 * Nextcloud WebDAV connection with one-click Save & Test 40 * Password field with visibility toggle and browser autofill protection 41 * Multiple backup sources (known plugins + custom folders) 40 42 * File filters by extension 43 * Streaming uploads for large files (low RAM usage) 41 44 * Manual upload with progress modal 42 45 * Scheduled runs: Daily, Weekly, or Monthly (WordPress Cron) 43 46 * Email notifications for successful/failed transfers 44 * Log s viewer + clear logs47 * Log viewer with clear function 45 48 * Rotation / retention (keep last N backup sets) 46 * German / English admin UI 49 * Context-aware action buttons per tab 50 * German and English admin UI with integrated How-To guide 47 51 48 52 == Installation == … … 54 58 == Usage == 55 59 56 = Configure Nextcloud=60 = 1. Setup (connection) = 57 61 58 1. Enter your WebDAV base URL (must end with `/`). 59 2. Enter username + **Nextcloud App Password**. 60 3. Set a target folder name. 61 4. Click **Save & Test**. 62 1. Fill in WebDAV URL, username, app password, and remote folder. 63 2. Click **Save & Test** – this saves settings and tests the connection in one step. 62 64 63 = Configure sources =65 = 2. Backup Sources = 64 66 65 1. Enable one or more sources.66 2. Adjust local paths (if needed).67 3. Set file extensions to include (comma-separated).67 1. Switch to the **Backup Sources** tab. 68 2. Enable one or more sources (paths and extensions are pre-filled). 69 3. Click **Save Settings**. 68 70 69 = Schedule + rotation=71 = 3. Manual Upload = 70 72 71 * Enable the schedule and set a time. 73 1. Click **Start Manual Upload** (available once a source is enabled). 74 2. A progress modal shows the upload status. 75 76 = 4. Schedule + Rotation = 77 78 * Enable the schedule, choose frequency and time, then save. 72 79 * Enable rotation and choose how many backup sets to keep. 73 80 … … 84 91 = Should I use a normal password or an app password? = 85 92 86 Use a Nextcloud **App Password**. 93 Always use a Nextcloud **App Password** (Settings → Security → Devices & Sessions). 94 95 = My upload gets stuck on large files = 96 97 The plugin streams files larger than 2 MB via cURL to avoid PHP memory limits. If uploads still time out, check your hosting provider's PHP `max_execution_time` setting. 98 99 = The password field is filled with the wrong password = 100 101 Click the eye icon next to the password field to reveal and verify the content. The field uses `autocomplete="new-password"` to prevent browser autofill, but some browsers may still fill it. Clear the field and enter your Nextcloud app password manually. 87 102 88 103 == License == … … 90 105 This plugin is licensed under the **GNU General Public License v2.0 or later**. 91 106 92 Assets: 107 == Changelog == 93 108 94 * Logo + flag icons are shipped as SVG assets in `assets/img/`. 95 96 == Screenshots == 97 98 1. Settings: Configure your Nextcloud WebDAV connection and select auto-detected backup sources. 99 2. Backup Sources: See which backup plugin folders/files were found and what will be uploaded. 100 3. Logs / Status: Review upload results, last run, and any errors. 101 102 == Changelog == 109 = 1.3.0 = 110 * **Streaming uploads**: Files > 2 MB are now uploaded via cURL streaming (reads from disk, never loads entire file into RAM). Solves timeout and memory issues on shared hosting with large backup files. 111 * **Context-aware buttons**: Each tab shows only the relevant action buttons. Setup shows only "Save & Test"; Backup Sources and Schedule show "Save Settings"; How-To hides all buttons. 112 * **Upload button gating**: "Start Manual Upload" is disabled (greyed out) until at least one backup source is enabled. 113 * **Password field improvements**: Visibility toggle (eye icon), `autocomplete="new-password"` to prevent browser autofill, renamed form field name to avoid browser recognition. 114 * **Removed "Remove password" checkbox**: Passwords are now simply replaced by entering a new one. 115 * **Default source paths**: Backup source paths and file extensions always fall back to sensible defaults (even after reset). Empty fields are never saved – defaults are restored automatically. 116 * **"Delete All Settings" preserves defaults**: Resetting the plugin now keeps default source paths and extensions intact, only clears user configuration, logs, and schedule. 117 * **Guided setup flow**: New hint text on Setup tab guides users through the configuration process. 118 * **Updated How-To guides**: German and English documentation rewritten to match current UI and features. 119 * **Fixed**: `$log` / `$source` / `$s` variable reference bugs from prefix renaming. 120 * **Fixed**: Sources tab fields (path, extensions) now save correctly. 103 121 104 122 = 1.2.9 = … … 106 124 * Fixed: Removed invalid Author URI (luzid.app) 107 125 * Fixed: Contributors changed from "luzid" to "luzidmedia" 108 * Fixed: Replaced `WP_CONTENT_DIR` constant with `wp_normalize_path( dirname( wp_upload_dir()['basedir']) )`109 * Fixed: `$_POST` is no longer processed as a whole; explicit field extraction in render_admin_page() and save_and_test_connection()110 * Fixed: J avaScriptobject name `luzidBackup` renamed to `luzid_backup_data` (prefixed)126 * Fixed: Replaced `WP_CONTENT_DIR` constant with `wp_normalize_path( dirname() )` 127 * Fixed: Explicit field extraction from `$_POST` instead of processing the whole stack 128 * Fixed: JS object name `luzidBackup` renamed to `luzid_backup_data` (prefixed) 111 129 * Fixed: All global variables in admin-page.php prefixed with `luzid_btn_` 112 * Fixed: Added proper `phpcs:ignore` annotations with justifications for display-only `$_GET` usage113 * Fixed: `$_GET['test_result']` now sanitized with `sanitize_text_field( wp_unslash() )`114 * Security: `webdav_password` sanitized via `sanitize_text_field()` before processing115 * Security: Nested `$_POST['sources']` array sanitized via `map_deep()` with `sanitize_text_field`116 130 117 131 = 1.2.4 = 118 * **CRITICAL FIX:** Settings now save correctly on first install 119 * Fixed: active_tab defaults to 'tab-nextcloud' instead of empty string 120 * Fixed: WebDAV credentials and settings persist after "Save & Test" 121 * Improved: Better fallback handling for tab detection 132 * Settings now save correctly on first install 133 * WebDAV credentials persist after "Save & Test" 134 * Better fallback handling for tab detection 122 135 123 136 = 1.2.3 = 124 * **CRITICAL:** Replaced cURL with wp_remote_request() for WordPress compliance 125 * Fixed: parse_url() replaced with wp_parse_url() 126 * Fixed: File upload now uses WordPress HTTP API instead of cURL/fopen 127 * Fixed: Added phpcs:ignore for remaining display-only $_GET usage 128 * Performance: Increased timeout to 300s for large file uploads 129 * Compliance: 100% WordPress.org Plugin Check compliant (no ERRORs) 137 * Replaced cURL with wp_remote_request() for WordPress compliance 138 * All PHPCS warnings resolved 130 139 131 140 = 1.2.2 = 132 * Fixed: All date() replaced with gmdate() for timezone safety 133 * Fixed: All wp_redirect() replaced with wp_safe_redirect() 134 * Fixed: Added wp_unslash() before sanitize_text_field() everywhere 135 * Fixed: Added phpcs:ignore comments for legitimate nonce-free $_GET usage 136 * Compliance: Fully WordPress.org Plugin Check compliant 141 * All date() replaced with gmdate() 142 * All wp_redirect() replaced with wp_safe_redirect() 143 * Proper wp_unslash() + sanitize everywhere 137 144 138 145 = 1.2.1 = 139 * Initial public release 140 * **Core Features:** 141 - Automatic backup uploads to Nextcloud via WebDAV 142 - Multiple backup source support (UpdraftPlus, BackWPup, etc.) 143 - Schedule options: Daily, Weekly, Monthly with custom times 144 - Automatic rotation/retention: Keep last N backups 145 - Email notifications for successful/failed transfers 146 - Real-time upload progress tracking 147 - Comprehensive logging system 148 * **Security & Compliance:** 149 - WordPress.org coding standards compliant 150 - Proper nonce verification and data sanitization 151 - Secure credential storage 152 - ABSPATH protection on all files 153 * **User Experience:** 154 - German and English interface 155 - Detailed HowTo guides with screenshots 156 - Conflict resolution for backup files 157 - Easy WebDAV configuration and testing 146 * Initial public release with full feature set 158 147 159 148 = 1.2.0 = 160 * Beta release for testing 161 * Changed: Tab "Nextcloud" renamed to "Setup" 162 * Added: Email Reporting feature with success/error notifications 163 * Changed: "Rotation" heading renamed to "Anzahl Backups" (DE) / "Backup Retention" (EN) 164 * Fixed: Cron scheduling now properly reschedules after each run (important for weekly/monthly) 165 * Fixed: Rotation now logs detailed information for troubleshooting 166 * Updated: Support email changed to [email protected] 167 168 = 1.2.8 = 169 * Added: Schedule frequency options (Daily, Weekly, Monthly) 170 * Fixed: Upload modal now shows "You can close the window now" when complete 171 * Added: Complete German and English HowTo documentation 172 * Fixed: JavaScript code compliance for WordPress.org (wp_add_inline_script) 173 * Improved: Schedule Cron calculations for weekly and monthly backups 174 175 = 1.2.7 = 176 * Fixed: Checkbox values (Sources/Schedule/Rotation) are now parsed robustly, so only selected options are stored (no accidental “all enabled”). 177 178 = 1.2.6 = 179 * Fixed: “Settings saved” modal is now styled like the Content Scheduler modal (OK button, backdrop click, ESC to close). 180 * Fixed: Tab selection is now persisted correctly (active_tab), preventing unintended resets when saving. 181 * Fixed: Backup sources, schedule and rotation checkboxes are now reliably saved (unchecked stays off). 182 * Changed: Backup source fields are no longer greyed out when a source is inactive (only “Aktiviert” controls whether it is used). 183 184 = 1.2.4 = 185 * Fixed: Backup sources are now disabled by default on first install. 186 * Fixed: Source enable/disable state is now reliably persisted after saving. 187 * Added: Hint in the Nextcloud tab if no backup source has been selected yet. 188 * Improved: Clarified what “Scan real files only” does. 189 * Improved: Settings saved feedback now uses a centered modal (consistent with other Luzid plugins). 190 * Improved: Form field styling (select/input) adjusted to avoid clipped text. 191 192 = 1.2.2 = 193 * Changed: Admin UI branding aligned with other Luzid WP Tools plugins (header, tabs, styling). 194 * Changed: Language switcher uses SVG assets from `assets/img/` (no inline data-URI). 195 * Changed: Unified admin CSS to shared `assets/css/luzid.css`. 196 * Added: WordPress-standard `readme.txt`. 197 198 = 1.2.1 = 199 * Previous release. 149 * Beta release 200 150 201 151 == Upgrade Notice == 202 152 203 = 1. 2.6=204 No breaking changes. UI improvements + bugfixes for source selection.153 = 1.3.0 = 154 Streaming uploads for large files, improved UI with context-aware buttons, password field improvements. Recommended update for all users. 205 155 206 = 1.2. 2=207 No breaking changes. UI/branding update only.156 = 1.2.9 = 157 WordPress.org review compliance. Required for plugin directory submission.
Note: See TracChangeset
for help on using the changeset viewer.