Plugin Directory

Changeset 3487170


Ignore:
Timestamp:
03/20/2026 11:54:32 AM (11 days ago)
Author:
ejointjp
Message:

Update to version 1.3.0 from GitHub

Location:
tiny-backup
Files:
24 added
2 deleted
28 edited
1 copied

Legend:

Unmodified
Added
Removed
  • tiny-backup/assets/screenshot-1.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • tiny-backup/assets/screenshot-2.png

    • Property svn:mime-type changed from application/octet-stream to image/png
  • tiny-backup/tags/1.3.0/includes/class-tnbu-admin-interface.php

    r3397691 r3487170  
    1818        $screen = get_current_screen();
    1919        if ( 'tools_page_tnbu' === $screen->id ) {
     20            $asset_file = plugin_dir_path( __DIR__ ) . 'build/admin.asset.php';
     21            $asset      = file_exists( $asset_file ) ? require $asset_file : array(
     22                'dependencies' => array(),
     23                'version'      => TNBU_Utilities::get_version(),
     24            );
     25            $deps = array_unique( array_merge( $asset['dependencies'], array( 'wp-i18n' ) ) );
     26
    2027            wp_enqueue_style(
    2128                'tnbu-style',
    22                 plugin_dir_url( __DIR__ ) . 'assets/css/admin.css',
     29                plugin_dir_url( __DIR__ ) . 'build/admin.css',
    2330                array(),
    24                 TNBU_Utilities::get_version()
     31                $asset['version']
    2532            );
    2633            wp_enqueue_script(
    2734                'tnbu-admin',
    28                 plugin_dir_url( __DIR__ ) . 'assets/js/admin.js',
    29                 array( 'wp-i18n' ),
    30                 TNBU_Utilities::get_version(),
     35                plugin_dir_url( __DIR__ ) . 'build/admin.js',
     36                $deps,
     37                $asset['version'],
    3138                true
    3239            );
     
    5764            );
    5865            wp_add_inline_script( 'tnbu-admin', $inline_script, 'before' );
     66
     67            // 自動バックアップUI:スケジュール選択に応じて曜日/日付/時刻の表示切替
     68            $auto_backup_script = <<<'JS'
     69document.addEventListener('DOMContentLoaded', function() {
     70    document.querySelectorAll('.tnbu-auto-schedule').forEach(function(sel) {
     71        sel.addEventListener('change', function() {
     72            var row = this.closest('.tnbu-auto-backup-row');
     73            var val = this.value;
     74            var dow = row.querySelector('.tnbu-auto-dow');
     75            var day = row.querySelector('.tnbu-auto-day');
     76            var time = row.querySelector('.tnbu-auto-time');
     77            dow.style.display = (val === 'weekly') ? '' : 'none';
     78            day.style.display = (val === 'monthly') ? '' : 'none';
     79            time.style.display = (val === '' || val === 'every_minute') ? 'none' : '';
     80        });
     81    });
     82});
     83JS;
     84            wp_add_inline_script( 'tnbu-admin', $auto_backup_script, 'after' );
    5985        }
    6086    }
     
    100126        register_setting( 'tnbu_settings_group', TNBU_Core::OPTION_KEY, array( __CLASS__, 'sanitize_settings' ) );
    101127        // 単一セクション(見出しなし)
    102         add_settings_section( 'tnbu_section_settings', __( 'Settings', 'tiny-backup' ), '__return_false', 'tnbu' );
    103 
    104         // 最上部: 横並びチェックボックス(DB/ファイル)
    105         add_settings_field( 'backup_toggles', __( 'Backup targets', 'tiny-backup' ), array( __CLASS__, 'field_backup_toggles' ), 'tnbu', 'tnbu_section_settings' );
    106 
    107         // ファイル設定: ファイル名テンプレート(固定のため表示なし)
    108         add_settings_field(
    109             'files_selection',
    110             __( 'Select backup items', 'tiny-backup' ) . '<br>' . __( 'only under wp-content', 'tiny-backup' ),
    111             array( __CLASS__, 'field_files_selection' ),
    112             'tnbu',
    113             'tnbu_section_settings'
    114         );
     128        add_settings_section( 'tnbu_section_settings', '', '__return_false', 'tnbu' );
     129
     130        // ファイルバックアップ対象選択セクション
     131        add_settings_field( 'files_selection', '', array( __CLASS__, 'field_files_selection' ), 'tnbu', 'tnbu_section_files' );
     132
     133        // 自動バックアップセクション
     134        add_settings_section( 'tnbu_section_auto', __( 'Auto Backup', 'tiny-backup' ), array( __CLASS__, 'section_auto_description' ), 'tnbu' );
     135        add_settings_field( 'auto_backup_db', __( 'Database', 'tiny-backup' ), array( __CLASS__, 'field_auto_backup_db' ), 'tnbu', 'tnbu_section_auto' );
     136        add_settings_field( 'auto_backup_files', _x( 'Files', 'auto backup setting', 'tiny-backup' ), array( __CLASS__, 'field_auto_backup_files' ), 'tnbu', 'tnbu_section_auto' );
     137        add_settings_field( 'auto_backup_email', __( 'Email notification', 'tiny-backup' ), array( __CLASS__, 'field_auto_backup_email' ), 'tnbu', 'tnbu_section_auto' );
    115138
    116139        // 共通設定: 保存先ディレクトリ(固定化のためUI非表示)
    117     }
    118 
    119     /**
    120      * バックアップ対象のトグルボタンを表示
    121      * データベースとファイルのバックアップ有効/無効を切り替え
    122      */
    123     public static function field_backup_toggles() {
    124         $opts                    = TNBU_Core::get_options();
    125         $is_db_backup_enabled    = ! empty( $opts['backup_db'] );
    126         $is_files_backup_enabled = ! empty( $opts['backup_files'] );
    127         ?>
    128         <div class="tnbu-backup-toggles">
    129             <label>
    130                 <input type="checkbox" name="<?php echo esc_attr( TNBU_Core::OPTION_KEY ); ?>[backup_db]" value="1" <?php checked( $is_db_backup_enabled, true ); ?> /> <?php echo esc_html( __( 'Backup database', 'tiny-backup' ) ); ?>
    131             </label>
    132             <label>
    133                 <input type="checkbox" name="<?php echo esc_attr( TNBU_Core::OPTION_KEY ); ?>[backup_files]" value="1" <?php checked( $is_files_backup_enabled, true ); ?> /> <?php echo esc_html( _x( 'Backup files', 'checkbox label', 'tiny-backup' ) ); ?>
    134             </label>
    135         </div>
    136         <?php
    137140    }
    138141
     
    149152        $rel                  = ltrim( $rel, '/' );
    150153        $output['target_dir'] = ( '' !== $rel ) ? $rel : TNBU_Core::DEFAULT_DIR_SUFFIX;
    151         // do not store files filename template
    152         // backup switches (checkboxes)
    153         $output['backup_db']    = ! empty( $input['backup_db'] ) ? 1 : 0;
    154         $output['backup_files'] = ! empty( $input['backup_files'] ) ? 1 : 0;
    155154        // save selected items from settings form (if present)
    156155        $sel                      = isset( $input['selected_items'] ) && is_array( $input['selected_items'] ) ? array_map( 'sanitize_text_field', $input['selected_items'] ) : array();
     
    170169        );
    171170        $output['selected_items'] = $sel;
     171
     172        // 自動バックアップ設定のサニタイズ
     173        $valid_schedules = array( '', 'daily', 'weekly', 'monthly' );
     174        if ( defined( 'TNBU_CRON_DEBUG' ) && TNBU_CRON_DEBUG ) {
     175            $valid_schedules[] = 'every_minute';
     176        }
     177        foreach ( array( 'auto_backup_db', 'auto_backup_files' ) as $prefix ) {
     178            $sched = isset( $input[ $prefix . '_schedule' ] ) ? sanitize_text_field( $input[ $prefix . '_schedule' ] ) : '';
     179            $output[ $prefix . '_schedule' ] = in_array( $sched, $valid_schedules, true ) ? $sched : '';
     180
     181            $time = isset( $input[ $prefix . '_time' ] ) ? sanitize_text_field( $input[ $prefix . '_time' ] ) : '03:00';
     182            if ( preg_match( '/^(\d{1,2}):(\d{2})$/', $time, $m ) ) {
     183                $output[ $prefix . '_time' ] = sprintf( '%02d:%s', max( 0, min( 23, (int) $m[1] ) ), ( (int) $m[2] >= 30 ? '30' : '00' ) );
     184            } else {
     185                $output[ $prefix . '_time' ] = '03:00';
     186            }
     187
     188            $dow = isset( $input[ $prefix . '_day_of_week' ] ) ? (int) $input[ $prefix . '_day_of_week' ] : 0;
     189            $output[ $prefix . '_day_of_week' ] = (string) max( 0, min( 6, $dow ) );
     190
     191            $day = isset( $input[ $prefix . '_day' ] ) ? (int) $input[ $prefix . '_day' ] : 1;
     192            $output[ $prefix . '_day' ] = (string) max( 1, min( 28, $day ) );
     193        }
     194
     195        $max_gen = isset( $input['auto_backup_db_max_generations'] ) ? (int) $input['auto_backup_db_max_generations'] : TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     196        $output['auto_backup_db_max_generations'] = max( 1, min( 10, $max_gen ) );
     197
     198        $max_gen = isset( $input['auto_backup_files_max_generations'] ) ? (int) $input['auto_backup_files_max_generations'] : TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     199        $output['auto_backup_files_max_generations'] = max( 1, min( 10, $max_gen ) );
     200
     201        $output['auto_backup_email_success'] = ! empty( $input['auto_backup_email_success'] );
     202        $output['auto_backup_email_failure'] = ! empty( $input['auto_backup_email_failure'] );
     203
    172204        return $output;
    173205    }
     
    175207    /**
    176208     * ファイルバックアップ対象選択UIを表示
    177      * wp-content配下のファイル・フォルダをツリー形式で選択可能
    178209     */
    179210    public static function field_files_selection() {
     
    184215            </div>
    185216        </div>
     217
     218        <hr class="tnbu-divider" />
     219       
     220        <?php
     221    }
     222
     223    /**
     224     * 自動バックアップセクションの説明文
     225     */
     226    public static function section_auto_description() {
     227        echo '<p class="description">' . esc_html__( 'Automatic backups use the same targets as manual backups. Actual execution timing may vary depending on site traffic.', 'tiny-backup' ) . '</p>';
     228    }
     229
     230    /**
     231     * スケジュール選択肢を返す
     232     *
     233     * @return array
     234     */
     235    private static function get_schedule_options() {
     236        $options = array(
     237            ''        => __( 'Disabled', 'tiny-backup' ),
     238            'daily'   => __( 'Daily', 'tiny-backup' ),
     239            'weekly'  => __( 'Weekly', 'tiny-backup' ),
     240            'monthly' => __( 'Monthly', 'tiny-backup' ),
     241        );
     242        if ( defined( 'TNBU_CRON_DEBUG' ) && TNBU_CRON_DEBUG ) {
     243            $options['every_minute'] = __( 'Every Minute (debug)', 'tiny-backup' );
     244        }
     245        return $options;
     246    }
     247
     248    /**
     249     * 時刻選択肢を返す(0〜23時)
     250     *
     251     * @return array
     252     */
     253    private static function get_time_options() {
     254        $times = array();
     255        for ( $h = 0; $h < 24; $h++ ) {
     256            foreach ( array( '00', '30' ) as $m ) {
     257                $key           = sprintf( '%02d:%s', $h, $m );
     258                $times[ $key ] = $key;
     259            }
     260        }
     261        return $times;
     262    }
     263
     264    /**
     265     * 曜日選択肢を返す
     266     *
     267     * @return array
     268     */
     269    private static function get_dow_options() {
     270        return array(
     271            '0' => __( 'Sunday', 'tiny-backup' ),
     272            '1' => __( 'Monday', 'tiny-backup' ),
     273            '2' => __( 'Tuesday', 'tiny-backup' ),
     274            '3' => __( 'Wednesday', 'tiny-backup' ),
     275            '4' => __( 'Thursday', 'tiny-backup' ),
     276            '5' => __( 'Friday', 'tiny-backup' ),
     277            '6' => __( 'Saturday', 'tiny-backup' ),
     278        );
     279    }
     280
     281    /**
     282     * 自動バックアップ行(スケジュール+時刻+曜日/日付)をレンダリング
     283     *
     284     * @param string $prefix 設定キーのプレフィックス('auto_backup_db' or 'auto_backup_files')
     285     */
     286    private static function render_auto_backup_row( $prefix ) {
     287        $opts     = TNBU_Core::get_options();
     288        $opt_key  = TNBU_Core::OPTION_KEY;
     289        $schedule = $opts[ $prefix . '_schedule' ] ?? '';
     290        $time     = $opts[ $prefix . '_time' ] ?? '03';
     291        $dow      = $opts[ $prefix . '_day_of_week' ] ?? '0';
     292        $day      = $opts[ $prefix . '_day' ] ?? '1';
     293        ?>
     294        <div class="tnbu-auto-backup-row" data-prefix="<?php echo esc_attr( $prefix ); ?>">
     295            <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $prefix . '_schedule' ); ?>]" class="tnbu-auto-schedule">
     296                <?php foreach ( self::get_schedule_options() as $val => $label ) : ?>
     297                    <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $schedule, $val ); ?>><?php echo esc_html( $label ); ?></option>
     298                <?php endforeach; ?>
     299            </select>
     300
     301            <span class="tnbu-auto-dow" <?php echo 'weekly' !== $schedule ? 'style="display:none;"' : ''; ?>>
     302                <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $prefix . '_day_of_week' ); ?>]">
     303                    <?php foreach ( self::get_dow_options() as $val => $label ) : ?>
     304                        <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $dow, $val ); ?>><?php echo esc_html( $label ); ?></option>
     305                    <?php endforeach; ?>
     306                </select>
     307            </span>
     308
     309            <span class="tnbu-auto-day" <?php echo 'monthly' !== $schedule ? 'style="display:none;"' : ''; ?>>
     310                <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $prefix . '_day' ); ?>]">
     311                    <?php for ( $d = 1; $d <= 28; $d++ ) : ?>
     312                        <option value="<?php echo esc_attr( $d ); ?>" <?php selected( $day, (string) $d ); ?>><?php echo esc_html( $d ); ?></option>
     313                    <?php endfor; ?>
     314                </select>
     315                <?php echo esc_html__( 'day', 'tiny-backup' ); ?>
     316            </span>
     317
     318            <span class="tnbu-auto-time" <?php echo ( '' === $schedule || 'every_minute' === $schedule ) ? 'style="display:none;"' : ''; ?>>
     319                <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $prefix . '_time' ); ?>]">
     320                    <?php foreach ( self::get_time_options() as $val => $label ) : ?>
     321                        <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $time, $val ); ?>><?php echo esc_html( $label ); ?></option>
     322                    <?php endforeach; ?>
     323                </select>
     324            </span>
     325
     326            <?php
     327            $max_key = $prefix . '_max_generations';
     328            $max_val = $opts[ $max_key ] ?? TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     329            ?>
     330            <span class="tnbu-auto-max-gen" <?php echo '' === $schedule ? 'style="display:none;"' : 'style="margin-left:2rem;"'; ?>>
     331                <?php echo esc_html__( 'Max generations', 'tiny-backup' ); ?>:
     332                <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $max_key ); ?>]">
     333                    <?php for ( $i = 1; $i <= 10; $i++ ) : ?>
     334                        <option value="<?php echo esc_attr( $i ); ?>" <?php selected( (int) $max_val, $i ); ?>><?php echo esc_html( $i ); ?></option>
     335                    <?php endfor; ?>
     336                </select>
     337            </span>
     338        </div>
     339        <?php
     340    }
     341
     342    /**
     343     * DB自動バックアップ設定フィールド
     344     */
     345    public static function field_auto_backup_db() {
     346        self::render_auto_backup_row( 'auto_backup_db' );
     347    }
     348
     349    /**
     350     * ファイル自動バックアップ設定フィールド
     351     */
     352    public static function field_auto_backup_files() {
     353        self::render_auto_backup_row( 'auto_backup_files' );
     354    }
     355
     356    /**
     357     * DB保持世代数設定フィールド
     358     */
     359    public static function field_auto_backup_db_max_generations() {
     360        $opts = TNBU_Core::get_options();
     361        $max  = $opts['auto_backup_db_max_generations'] ?? TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     362        ?>
     363        <select name="<?php echo esc_attr( TNBU_Core::OPTION_KEY ); ?>[auto_backup_db_max_generations]">
     364            <?php for ( $i = 1; $i <= 10; $i++ ) : ?>
     365                <option value="<?php echo esc_attr( $i ); ?>" <?php selected( (int) $max, $i ); ?>><?php echo esc_html( $i ); ?></option>
     366            <?php endfor; ?>
     367        </select>
     368        <?php
     369    }
     370
     371    /**
     372     * ファイル保持世代数設定フィールド
     373     */
     374    public static function field_auto_backup_files_max_generations() {
     375        $opts = TNBU_Core::get_options();
     376        $max  = $opts['auto_backup_files_max_generations'] ?? TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     377        ?>
     378        <select name="<?php echo esc_attr( TNBU_Core::OPTION_KEY ); ?>[auto_backup_files_max_generations]">
     379            <?php for ( $i = 1; $i <= 10; $i++ ) : ?>
     380                <option value="<?php echo esc_attr( $i ); ?>" <?php selected( (int) $max, $i ); ?>><?php echo esc_html( $i ); ?></option>
     381            <?php endfor; ?>
     382        </select>
     383        <?php
     384    }
     385
     386    /**
     387     * メール通知設定フィールド
     388     */
     389    public static function field_auto_backup_email() {
     390        $opts    = TNBU_Core::get_options();
     391        $key     = esc_attr( TNBU_Core::OPTION_KEY );
     392        $email   = get_option( 'admin_email' );
     393        $success = ! empty( $opts['auto_backup_email_success'] );
     394        $failure = ! empty( $opts['auto_backup_email_failure'] );
     395        ?>
     396        <p class="description" style="margin-bottom: 8px;">
     397            <?php echo esc_html( sprintf(
     398                /* translators: %s: admin email address */
     399                __( 'Send notification to %s', 'tiny-backup' ),
     400                $email
     401            ) ); ?>
     402        </p>
     403        <label style="margin-right: 16px;">
     404            <input type="hidden" name="<?php echo $key; ?>[auto_backup_email_success]" value="0" />
     405            <input type="checkbox" name="<?php echo $key; ?>[auto_backup_email_success]" value="1" <?php checked( $success ); ?> />
     406            <?php echo esc_html__( 'On success', 'tiny-backup' ); ?>
     407        </label>
     408        <label>
     409            <input type="hidden" name="<?php echo $key; ?>[auto_backup_email_failure]" value="0" />
     410            <input type="checkbox" name="<?php echo $key; ?>[auto_backup_email_failure]" value="1" <?php checked( $failure ); ?> />
     411            <?php echo esc_html__( 'On failure', 'tiny-backup' ); ?>
     412        </label>
    186413        <?php
    187414    }
     
    222449
    223450            <form id="tnbu-settings-form" class="tnbu-settings-form" method="post" action="options.php">
     451                <h3><?php echo esc_html( __( 'Choose what to backup from your uploads folder.', 'tiny-backup' ) ); ?></h3>
     452                <?php self::field_files_selection(); ?>
     453
    224454                <?php settings_fields( 'tnbu_settings_group' ); ?>
    225455                <?php do_settings_sections( 'tnbu' ); ?>
    226                 <?php /* ファイル選択はセクションフィールド側で描画 */ ?>
    227456                <?php submit_button( __( 'Save Settings', 'tiny-backup' ) ); ?>
    228457            </form>
     458
    229459            <form id="tnbu-reset-form" class="tnbu-reset-form" method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
    230460                <input type="hidden" name="action" value="tnbu_reset_settings" />
     
    236466            <hr class="tnbu-divider" />
    237467           
    238             <h2><?php echo esc_html( __( 'Backup', 'tiny-backup' ) ); ?></h2>
    239             <p>
    240                 <button type="button" class="button button-primary" id="tnbu-backup-button"><?php echo esc_html( __( 'Run backup now', 'tiny-backup' ) ); ?></button>
     468            <h2><?php echo esc_html( __( 'Run backup now', 'tiny-backup' ) ); ?></h2>
     469            <p class="tnbu-backup-buttons">
     470                <button type="button" class="button button-primary tnbu-backup-trigger" data-backup-type="db"><?php echo esc_html( __( 'Database backup', 'tiny-backup' ) ); ?></button>
     471                <button type="button" class="button button-primary tnbu-backup-trigger" data-backup-type="files"><?php echo esc_html( __( 'Files backup', 'tiny-backup' ) ); ?></button>
    241472            </p>
    242473            <?php
     
    251482            </div>
    252483
    253 
     484            <hr class="tnbu-divider" />
    254485
    255486            <!-- バックアップファイル一覧 -->
    256487            <div class="tnbu-backup-list">
    257                 <h3><?php echo esc_html( _x( 'Backup files', 'section title', 'tiny-backup' ) ); ?></h3>
     488                <h2><?php echo esc_html( _x( 'Backup files', 'section title', 'tiny-backup' ) ); ?></h2>
    258489                <?php self::render_backup_list(); ?>
    259490            </div>
  • tiny-backup/tags/1.3.0/includes/class-tnbu-ajax-handler.php

    r3397691 r3487170  
    2424        }
    2525
    26         // バックアップ実行前に設定を保存(変更された設定があれば保存する)
    27         if ( isset( $_POST['tnbu_backup_db'] ) || isset( $_POST['tnbu_backup_files'] ) ) {
    28             $current_opts = TNBU_Core::get_options();
    29             $updated_opts = $current_opts;
    30 
    31             // チェックボックスの状態を更新
    32             if ( isset( $_POST['tnbu_backup_db'] ) ) {
    33                 $updated_opts['backup_db'] = ( sanitize_text_field( wp_unslash( $_POST['tnbu_backup_db'] ) ) === '1' ) ? 1 : 0;
    34             }
    35             if ( isset( $_POST['tnbu_backup_files'] ) ) {
    36                 $updated_opts['backup_files'] = ( sanitize_text_field( wp_unslash( $_POST['tnbu_backup_files'] ) ) === '1' ) ? 1 : 0;
    37             }
    38 
    39             // 選択されたアイテムを更新(常に更新する、空でも)
    40         // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in array_map below
    41             $selected = isset( $_POST['tnbu_selected_items'] ) ? (array) wp_unslash( $_POST['tnbu_selected_items'] ) : array();
    42             $selected = array_map(
    43                 function ( $p ) {
    44                     return ltrim( (string) sanitize_text_field( $p ), '/' );
    45                 },
    46                 $selected
    47             );
    48             $selected = array_values(
    49                 array_filter(
    50                     array_unique( $selected ),
    51                     function ( $p ) {
    52                         return '' !== $p && false === strpos( $p, '..' ) && false === strpos( $p, "\0" );
    53                     }
    54                 )
    55             );
    56             // 空の配列でも保存する(選択を解除した場合のため)
    57             $updated_opts['selected_items'] = $selected;
    58 
    59             // 設定を保存
    60             update_option( TNBU_Core::OPTION_KEY, $updated_opts );
    61 
    62             /* translators: 1: DB backup enabled (0/1), 2: Files backup enabled (0/1) */
    63             TNBU_Progress_Manager::set_progress( sprintf( __( 'Settings saved: DB=%1$s, Files=%2$s', 'tiny-backup' ), $updated_opts['backup_db'], $updated_opts['backup_files'] ), false, null );
     26        // バックアップ種別を取得('db' or 'files')
     27        $backup_type = isset( $_POST['backup_type'] ) ? sanitize_text_field( wp_unslash( $_POST['backup_type'] ) ) : '';
     28        if ( ! in_array( $backup_type, array( 'db', 'files' ), true ) ) {
     29            wp_send_json_error( array( 'message' => 'invalid_backup_type' ), 400 );
    6430        }
    6531
    6632        TNBU_Progress_Manager::set_progress( __( 'Backup started', 'tiny-backup' ), false, null );
    67         // 設定に応じてDB/ファイルのバックアップを実行(キャッシュを強制リフレッシュ)
    68         $opts              = TNBU_Core::get_options( true );
    69         $last_result_path  = '';
    70         $db_result_path    = '';
    71         $files_result_path = '';
    72         $error             = null;
    73         $total_steps       = 0;
    74         $current_step      = 0;
    75 
    76         // 実行するバックアップの種類をカウント
    77         if ( ! empty( $opts['backup_db'] ) ) {
    78             ++$total_steps;
    79         }
    80         if ( ! empty( $opts['backup_files'] ) ) {
    81             ++$total_steps;
    82         }
    83 
    84         /* translators: 1: whether DB backup is enabled (0/1), 2: whether Files backup is enabled (0/1) */
    85         TNBU_Progress_Manager::set_progress( sprintf( __( 'Checking backup settings: DB=%1$s, Files=%2$s', 'tiny-backup' ), $opts['backup_db'], $opts['backup_files'] ), false, null );
    86 
    87         if ( ! empty( $opts['backup_db'] ) ) {
    88             ++$current_step;
     33
     34        $result_path = '';
     35        $error       = null;
     36
     37        if ( 'db' === $backup_type ) {
    8938            TNBU_Progress_Manager::set_progress( __( 'Starting database backup', 'tiny-backup' ), false, null );
    9039            $res = TNBU_Database_Backup::perform_backup();
     
    9443                TNBU_Progress_Manager::set_progress( sprintf( __( 'Database backup error: %s', 'tiny-backup' ), $res->get_error_message() ), false, null );
    9544            } else {
    96                 $db_result_path   = $res;
    97                 $last_result_path = $res;
     45                $result_path = $res;
    9846                TNBU_Progress_Manager::set_progress( __( 'Database backup completed', 'tiny-backup' ), false, null );
    9947            }
    10048        }
    10149
    102         if ( ! empty( $opts['backup_files'] ) ) {
    103             ++$current_step;
     50        if ( 'files' === $backup_type ) {
    10451            TNBU_Progress_Manager::set_progress( __( 'Starting files backup', 'tiny-backup' ), false, null );
    10552
    106             // ユーザーが選択した項目があればそれを優先、なければ自動スキャン
    107     // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in array_map below
    108             $selected = isset( $_POST['tnbu_selected_items'] ) ? (array) wp_unslash( $_POST['tnbu_selected_items'] ) : array();
    109             $selected = array_map(
    110                 function ( $p ) {
    111                     return ltrim( (string) sanitize_text_field( $p ), '/' );
    112                 },
    113                 $selected
    114             );
    115             $selected = array_values(
    116                 array_filter(
    117                     array_unique( $selected ),
    118                     function ( $p ) {
    119                         return '' !== $p && false === strpos( $p, '..' ) && false === strpos( $p, "\0" );
    120                     }
    121                 )
    122             );
    123             if ( empty( $selected ) && ! empty( $opts['selected_items'] ) && is_array( $opts['selected_items'] ) ) {
    124                 $selected = array_values(
    125                     array_filter(
    126                         array_map(
    127                             function ( $p ) {
    128                                 return ltrim( (string) $p, '/' );
    129                             },
    130                             $opts['selected_items']
     53            // POSTから選択アイテムを取得、なければ保存済み設定から取得
     54            $selected = self::sanitize_selected_items_from_post();
     55            if ( empty( $selected ) ) {
     56                $opts = TNBU_Core::get_options( true );
     57                if ( ! empty( $opts['selected_items'] ) && is_array( $opts['selected_items'] ) ) {
     58                    $selected = array_values(
     59                        array_filter(
     60                            array_map(
     61                                function ( $p ) {
     62                                    return ltrim( (string) $p, '/' );
     63                                },
     64                                $opts['selected_items']
     65                            )
    13166                        )
    132                     )
    133                 );
    134             }
    135 
    136             /* translators: %d: number of selected items */
    137             TNBU_Progress_Manager::set_progress( sprintf( __( 'Selected files check: %d items', 'tiny-backup' ), count( $selected ) ), false, null );
     67                    );
     68                }
     69            }
    13870
    13971            if ( empty( $selected ) ) {
    140                 $fres = new WP_Error( 'tnbu_files_none', __( 'No file targets selected for backup', 'tiny-backup' ) );
     72                $error = new WP_Error( 'tnbu_files_none', __( 'No file targets selected for backup', 'tiny-backup' ) );
     73                TNBU_Progress_Manager::set_progress( $error->get_error_message(), false, null );
    14174            } else {
    14275                $fres = TNBU_File_Backup::perform_files_backup_selected( $selected );
    143             }
    144             if ( is_wp_error( $fres ) ) {
    145                 $error = $error ? $error : $fres;
    146                 /* translators: %s: error message */
    147                 TNBU_Progress_Manager::set_progress( sprintf( __( 'Files backup error: %s', 'tiny-backup' ), $fres->get_error_message() ), false, null );
    148             } else {
    149                 $files_result_path = $fres;
    150                 $last_result_path  = $fres;
    151                 TNBU_Progress_Manager::set_progress( __( 'Files backup completed: ', 'tiny-backup' ), false, null );
    152             }
    153         }
    154         $result = $error ? $error : ( $last_result_path ? $last_result_path : '' );
    155         if ( is_wp_error( $result ) ) {
     76                if ( is_wp_error( $fres ) ) {
     77                    $error = $fres;
     78                    /* translators: %s: error message */
     79                    TNBU_Progress_Manager::set_progress( sprintf( __( 'Files backup error: %s', 'tiny-backup' ), $fres->get_error_message() ), false, null );
     80                } else {
     81                    $result_path = $fres;
     82                    TNBU_Progress_Manager::set_progress( __( 'Files backup completed', 'tiny-backup' ), false, null );
     83                }
     84            }
     85        }
     86
     87        if ( $error ) {
    15688            set_transient(
    15789                'tnbu_flash_' . get_current_user_id(),
     
    15991                    'type' => 'error',
    16092                    /* translators: %s: error message */
    161                     'text' => sprintf( __( 'Backup failed: %s', 'tiny-backup' ), $result->get_error_message() ),
     93                    'text' => sprintf( __( 'Backup failed: %s', 'tiny-backup' ), $error->get_error_message() ),
    16294                ),
    16395                60
    16496            );
    16597            /* translators: %s: error message */
    166             TNBU_Progress_Manager::set_progress( sprintf( __( 'Error: %s', 'tiny-backup' ), $result->get_error_message() ), true, null );
    167             wp_send_json_error( array( 'message' => $result->get_error_message() ) );
    168         }
    169 
    170         // バックアップ完了メッセージの作成
    171         $backup_files = array();
    172         if ( $db_result_path ) {
    173             $backup_files[] = basename( $db_result_path );
    174         }
    175         if ( $files_result_path ) {
    176             $backup_files[] = basename( $files_result_path );
    177         }
    178 
    179         $files_text = implode( ', ', $backup_files );
    180         /* translators: %s: backup file names */
     98            TNBU_Progress_Manager::set_progress( sprintf( __( 'Error: %s', 'tiny-backup' ), $error->get_error_message() ), true, null );
     99            wp_send_json_error( array( 'message' => $error->get_error_message() ) );
     100        }
     101
     102        /* translators: %s: backup file name */
    181103        set_transient(
    182104            'tnbu_flash_' . get_current_user_id(),
    183105            array(
    184106                'type' => 'success',
    185                 /* translators: %s: backup file names */
    186                 'text' => sprintf( __( 'Backup completed: %s', 'tiny-backup' ), $files_text ),
     107                /* translators: %s: backup file name */
     108                'text' => sprintf( __( 'Backup completed: %s', 'tiny-backup' ), basename( $result_path ) ),
    187109            ),
    188110            60
    189111        );
    190112        TNBU_Progress_Manager::set_progress( __( 'Completed', 'tiny-backup' ), true, null );
    191         wp_send_json_success( array( 'path' => $result ) );
     113        wp_send_json_success( array( 'path' => $result_path ) );
    192114    }
    193115
     
    214136            wp_send_json_error( array( 'message' => 'bad_nonce' ), 403 );
    215137        }
    216         $rel         = isset( $_POST['path'] ) ? sanitize_text_field( wp_unslash( $_POST['path'] ) ) : '';
    217         $rel         = ltrim( $rel, '/' );
    218         $base        = trailingslashit( WP_CONTENT_DIR );
    219         $target      = $base . $rel;
     138        $rel   = isset( $_POST['path'] ) ? sanitize_text_field( wp_unslash( $_POST['path'] ) ) : '';
     139        $rel   = ltrim( $rel, '/' );
     140        $base  = trailingslashit( WP_CONTENT_DIR . '/uploads' );
     141
     142        // 1階層のみ表示。pathが空以外の場合は許可しない
     143        if ( '' !== $rel ) {
     144            wp_send_json_error( array( 'message' => 'depth_limit' ), 400 );
     145        }
     146
     147        $target      = $base;
    220148        $real_base   = realpath( $base );
    221         $real_target = realpath( $target ) ? $target : $target; // 未存在でも操作しないのでOK
     149        $real_target = realpath( $target ) ? $target : $target;
    222150        if ( $real_base && $real_target && 0 !== strpos( wp_normalize_path( $real_target ), wp_normalize_path( $real_base ) ) ) {
    223151            wp_send_json_error( array( 'message' => 'invalid_path' ), 400 );
    224         }
    225         if ( ! is_dir( $target ) ) {
    226             $target = $base; // ルート
    227152        }
    228153        $items = array();
     
    246171                $full    = $target . DIRECTORY_SEPARATOR . $entry;
    247172                $type    = is_dir( $full ) ? 'dir' : 'file';
    248                 $relp    = ltrim( trim( $rel . '/' . $entry, '/' ) );
     173                $relp    = 'uploads/' . $entry;
    249174                $items[] = array(
    250175                    'name' => $entry,
     
    353278        header( 'Pragma: no-cache' );
    354279
    355         // WordPress Filesystem APIを使用してファイル送信
    356         if ( ! function_exists( 'WP_Filesystem' ) ) {
    357             require_once ABSPATH . 'wp-admin/includes/file.php';
    358         }
    359         WP_Filesystem();
    360         global $wp_filesystem;
    361 
    362         if ( ! $wp_filesystem->exists( $file_path ) ) {
    363             wp_die( esc_html__( 'File not found.', 'tiny-backup' ) );
    364         }
    365 
    366         $file_content = $wp_filesystem->get_contents( $file_path );
    367         if ( false === $file_content ) {
    368             wp_die( esc_html__( 'File read error.', 'tiny-backup' ) );
    369         }
    370 
    371         // チャンクごとに出力
    372         $chunk_size  = 1024 * 1024; // 1MB
    373         $file_length = strlen( $file_content );
    374         for ( $i = 0; $i < $file_length; $i += $chunk_size ) {
    375             // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Binary file download, escaping would corrupt the data
    376             echo substr( $file_content, $i, $chunk_size );
    377             flush();
    378         }
     280        // ストリーミングでファイル送信(メモリ節約)
     281        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Binary file streaming for download, WP_Filesystem::get_contents() would load entire file into memory
     282        readfile( $file_path );
    379283        exit;
    380284    }
    381285
     286
     287    /**
     288     * POSTデータから選択アイテムをサニタイズして取得する
     289     *
     290     * @return array サニタイズ済みの選択アイテム配列
     291     */
     292    private static function sanitize_selected_items_from_post() {
     293        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in array_map below
     294        $selected = isset( $_POST['tnbu_selected_items'] ) ? (array) wp_unslash( $_POST['tnbu_selected_items'] ) : array();
     295        $selected = array_map(
     296            function ( $p ) {
     297                return ltrim( (string) sanitize_text_field( $p ), '/' );
     298            },
     299            $selected
     300        );
     301        return array_values(
     302            array_filter(
     303                array_unique( $selected ),
     304                function ( $p ) {
     305                    return '' !== $p && false === strpos( $p, '..' ) && false === strpos( $p, "\0" );
     306                }
     307            )
     308        );
     309    }
    382310
    383311    /**
     
    393321        $files = isset( $_POST['files'] ) && is_array( $_POST['files'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['files'] ) ) : array();
    394322        $base  = trailingslashit( TNBU_Core::get_backup_dir() );
    395         $deleted = 0;
    396323        foreach ( $files as $filename ) {
    397324            if ( ! TNBU_Utilities::validate_backup_filename( $filename ) ) {
     
    399326            }
    400327            $path = $base . $filename;
    401             if ( is_file( $path ) && strpos( realpath( $path ), realpath( $base ) ) === 0 && wp_delete_file( $path ) ) {
    402                 ++$deleted;
     328            if ( is_file( $path ) && strpos( realpath( $path ), realpath( $base ) ) === 0 ) {
     329                wp_delete_file( $path );
    403330            }
    404331        }
  • tiny-backup/tags/1.3.0/includes/class-tnbu-core.php

    r3397691 r3487170  
    7272     * @return array デフォルト設定の配列
    7373     */
     74    // 自動バックアップのファイル名テンプレート(-auto 付き)
     75    const DEFAULT_AUTO_DB_FILENAME_TEMPLATE    = '{db}-{date}-auto-db';
     76    const DEFAULT_AUTO_FILES_FILENAME_TEMPLATE = '{db}-{date}-auto-files';
     77
     78    // 自動バックアップの保持世代数デフォルト
     79    const DEFAULT_AUTO_MAX_GENERATIONS = 3;
     80
    7481    public static function defaults() {
    7582        return array(
    7683            // filename_template is fixed by code to DEFAULT_DB_FILENAME_TEMPLATE
    7784            'target_dir'     => self::DEFAULT_DIR_SUFFIX,
    78             'backup_db'      => 1,
    79             'backup_files'   => 1,
    8085            // files filename template is fixed by code to DEFAULT_FILES_FILENAME_TEMPLATE
    8186            'selected_items' => array( 'uploads' ),
     87
     88            // 自動バックアップ設定
     89            'auto_backup_db_schedule'       => '',   // '' | 'daily' | 'weekly' | 'monthly'
     90            'auto_backup_db_time'           => '03:00', // '00:00'〜'23:30'
     91            'auto_backup_db_day_of_week'    => '0',  // 0(日)〜6(土)
     92            'auto_backup_db_day'            => '1',  // 1〜28
     93            'auto_backup_files_schedule'    => '',
     94            'auto_backup_files_time'        => '03:00',
     95            'auto_backup_files_day_of_week' => '0',
     96            'auto_backup_files_day'         => '1',
     97            'auto_backup_db_max_generations'    => self::DEFAULT_AUTO_MAX_GENERATIONS,
     98            'auto_backup_files_max_generations' => self::DEFAULT_AUTO_MAX_GENERATIONS,
     99            'auto_backup_email_success'     => false,
     100            'auto_backup_email_failure'     => false,
    82101        );
    83102    }
     
    116135    }
    117136
    118     /**
    119      * 設定オプションのキャッシュをクリアする
    120      * 設定を更新した後に呼び出すことで、次回の get_options() で最新の値を取得できる
    121      *
    122      * @return void
    123      */
    124     public static function clear_options_cache() {
    125         // 次回の get_options() で強制的に再読み込みさせるため、引数で制御
    126         // このメソッドは互換性のために残しているが、get_options(true) を推奨
    127     }
    128137}
  • tiny-backup/tags/1.3.0/includes/class-tnbu-database-backup.php

    r3397691 r3487170  
    1818     * @return string|WP_Error 成功時はファイルパス、失敗時はWP_Error
    1919     */
    20     public static function perform_backup() {
     20    /**
     21     * データベースのバックアップを実行する
     22     *
     23     * @param string|null $filename_template ファイル名テンプレート(null の場合はデフォルト)
     24     * @return string|WP_Error 成功時はファイルパス、失敗時はWP_Error
     25     */
     26    public static function perform_backup( $filename_template = null ) {
    2127        global $wpdb;
    2228        $abs_dir   = TNBU_Core::get_backup_dir();
     
    2632        }
    2733
    28         $base_filename = TNBU_Utilities::replace_template_vars( TNBU_Core::DEFAULT_DB_FILENAME_TEMPLATE );
     34        if ( null === $filename_template ) {
     35            $filename_template = TNBU_Core::DEFAULT_DB_FILENAME_TEMPLATE;
     36        }
     37        $base_filename = TNBU_Utilities::replace_template_vars( $filename_template );
    2938        // remove known extensions if user accidentally included them
    3039        $base_filename = preg_replace( '/\.(sql\.gz|zip)$/i', '', $base_filename );
     
    5463            if ( ! class_exists( 'ZipArchive' ) ) {
    5564                wp_delete_file( $sql_path );
    56                 return new WP_Error( 'sbwp_zip', __( 'ZIP extension is not available', 'tiny-backup' ) );
     65                return new WP_Error( 'tnbu_zip', __( 'ZIP extension is not available', 'tiny-backup' ) );
    5766            }
    5867            $zip_path = $base_path . $base_filename . '.sql.zip';
     
    6170            if ( true !== $zip->open( $tmp_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
    6271                wp_delete_file( $sql_path );
    63                 return new WP_Error( 'sbwp_zip_open', __( 'Failed to create ZIP file', 'tiny-backup' ) );
     72                return new WP_Error( 'tnbu_zip_open', __( 'Failed to create ZIP file', 'tiny-backup' ) );
    6473            }
    6574            $zip->addFile( $sql_path, basename( $sql_path ) );
     
    7079            if ( ! @rename( $tmp_path, $zip_path ) ) {
    7180                wp_delete_file( $tmp_path );
    72                 return new WP_Error( 'sbwp_zip_rename', __( 'Failed to finalize ZIP file', 'tiny-backup' ) );
     81                return new WP_Error( 'tnbu_zip_rename', __( 'Failed to finalize ZIP file', 'tiny-backup' ) );
    7382            }
    7483            TNBU_Progress_Manager::set_progress( __( 'ZIP compression completed', 'tiny-backup' ), false, null );
     
    9099        $handle = @fopen( $sql_path, 'wb' );
    91100        if ( ! $handle ) {
    92             return new WP_Error( 'sbwp_phpdump_open', __( 'Unable to write SQL file', 'tiny-backup' ) );
     101            return new WP_Error( 'tnbu_phpdump_open', __( 'Unable to write SQL file', 'tiny-backup' ) );
    93102        }
    94103        $site    = site_url();
     
    105114            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Direct file writing for SQL dump
    106115            fclose( $handle );
    107             return new WP_Error( 'sbwp_phpdump_tables', __( 'No tables found', 'tiny-backup' ) );
     116            return new WP_Error( 'tnbu_phpdump_tables', __( 'No tables found', 'tiny-backup' ) );
    108117        }
    109118
     
    190199        fclose( $handle );
    191200        if ( ! file_exists( $sql_path ) || filesize( $sql_path ) === 0 ) {
    192             return new WP_Error( 'sbwp_phpdump_empty', __( 'PHP dump failed (empty file)', 'tiny-backup' ) );
     201            return new WP_Error( 'tnbu_phpdump_empty', __( 'PHP dump failed (empty file)', 'tiny-backup' ) );
    193202        }
    194203        return true;
  • tiny-backup/tags/1.3.0/includes/class-tnbu-file-backup.php

    r3397691 r3487170  
    1515     * 指定されたファイル・フォルダをZIP圧縮してバックアップ
    1616     */
    17     public static function perform_files_backup_selected( array $items ) {
     17    /**
     18     * 指定されたファイル・フォルダをZIP圧縮してバックアップ
     19     *
     20     * @param array       $items             バックアップ対象のアイテム
     21     * @param string|null $filename_template ファイル名テンプレート(null の場合はデフォルト)
     22     * @return string|WP_Error 成功時はファイルパス、失敗時はWP_Error
     23     */
     24    public static function perform_files_backup_selected( array $items, $filename_template = null ) {
    1825        $backup_dir = TNBU_Core::get_backup_dir();
    1926        $ensure     = TNBU_Utilities::ensure_directory( $backup_dir );
     
    2936            @set_time_limit( 0 ); }
    3037        if ( ! class_exists( 'ZipArchive' ) ) {
    31             return new WP_Error( 'sbwp_zip', __( 'ZIP extension is not available', 'tiny-backup' ) );
     38            return new WP_Error( 'tnbu_zip', __( 'ZIP extension is not available', 'tiny-backup' ) );
    3239        }
    3340        $base     = trailingslashit( WP_CONTENT_DIR );
    34         $zip_tmpl = TNBU_Core::DEFAULT_FILES_FILENAME_TEMPLATE;
     41        $zip_tmpl = null !== $filename_template ? $filename_template : TNBU_Core::DEFAULT_FILES_FILENAME_TEMPLATE;
    3542        $zip_base = TNBU_Utilities::replace_template_vars( strtr( $zip_tmpl, array( '{db}' => ( defined( 'DB_NAME' ) ? DB_NAME : 'wp' ) ) ) );
    3643        $zip_path = trailingslashit( $backup_dir ) . $zip_base . '.zip';
     
    3845        $zip      = new ZipArchive();
    3946        if ( true !== $zip->open( $tmp, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
    40             return new WP_Error( 'sbwp_zip_open', __( 'Failed to create ZIP file', 'tiny-backup' ) );
     47            return new WP_Error( 'tnbu_zip_open', __( 'Failed to create ZIP file', 'tiny-backup' ) );
    4148        }
    4249        $added_any  = false;
    4350        $items      = array_values( array_unique( $items ) );
    44         $compressed = TNBU_Utilities::optimize_targets_by_parent( $items, $base );
     51        $compressed = TNBU_Utilities::optimize_targets_by_parent( $items );
    4552        // ファイルをZIPに追加
    4653        TNBU_Progress_Manager::set_progress( __( 'Starting file scan', 'tiny-backup' ), false, null );
     
    6774        if ( ! $added_any ) {
    6875            wp_delete_file( $tmp );
    69             return new WP_Error( 'sbwp_files_empty', __( 'No files to add', 'tiny-backup' ) ); }
     76            return new WP_Error( 'tnbu_files_empty', __( 'No files to add', 'tiny-backup' ) ); }
    7077        // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename -- wp_move_file() doesn't exist
    7178        // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename -- wp_move_file() doesn't exist
    7279        if ( ! @rename( $tmp, $zip_path ) ) {
    7380            wp_delete_file( $tmp );
    74             return new WP_Error( 'sbwp_zip_finalize', __( 'Failed to finalize ZIP file', 'tiny-backup' ) ); }
     81            return new WP_Error( 'tnbu_zip_finalize', __( 'Failed to finalize ZIP file', 'tiny-backup' ) ); }
    7582
    7683        TNBU_Progress_Manager::set_progress( __( 'Files compression completed', 'tiny-backup' ), true, null );
     
    115122        $count    = 0;
    116123        $iterator = new RecursiveIteratorIterator(
    117             new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS ),
     124            new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS ),
    118125            RecursiveIteratorIterator::LEAVES_ONLY
    119126        );
     
    151158
    152159        // 親優先ロジックに基づく最適化:重複する子パスを除去
    153         $optimized_targets = TNBU_Utilities::optimize_targets_by_parent( $targets, $base_root );
     160        $optimized_targets = TNBU_Utilities::optimize_targets_by_parent( $targets );
    154161
    155162        foreach ( $optimized_targets as $rel ) {
     
    183190        $files    = array();
    184191        $iterator = new RecursiveIteratorIterator(
    185             new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS ),
     192            new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS ),
    186193            RecursiveIteratorIterator::LEAVES_ONLY // ファイルのみ(ディレクトリエントリは不要)
    187194        );
  • tiny-backup/tags/1.3.0/includes/class-tnbu-utilities.php

    r3397691 r3487170  
    156156        if ( ! is_dir( $dir ) || ! wp_is_writable( $dir ) ) {
    157157            /* translators: %s: directory path */
    158             return new WP_Error( 'sbwp_dir', sprintf( __( 'Cannot write to backup directory: %s', 'tiny-backup' ), $dir ) );
     158            return new WP_Error( 'tnbu_dir', sprintf( __( 'Cannot write to backup directory: %s', 'tiny-backup' ), $dir ) );
    159159        }
    160160        return true;
  • tiny-backup/tags/1.3.0/languages/tiny-backup-ja-tnbu-admin.json

    r3397190 r3487170  
    2323  }
    2424}
    25 
  • tiny-backup/tags/1.3.0/languages/tiny-backup-ja.po

    r3397190 r3487170  
    33"Project-Id-Version: Tiny Backup 0.1.0\n"
    44"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/tiny-backup\n"
    5 "POT-Creation-Date: 2025-10-09 00:00+0000\n"
    6 "PO-Revision-Date: 2025-10-09 00:00+0000\n"
     5"POT-Creation-Date: 2026-03-20 00:00+0000\n"
     6"PO-Revision-Date: 2026-03-20 00:00+0000\n"
    77"Last-Translator: \n"
    88"Language-Team: Japanese\n"
     
    2121msgstr "最小限の設定でデータベースとファイルのバックアップを作成します。"
    2222
    23 #: includes/class-admin-interface.php:60
     23#: includes/class-tnbu-admin-interface.php
    2424msgid "Settings"
    2525msgstr "設定"
    2626
    27 #: includes/class-admin-interface.php:63
    28 msgid "Backup targets"
    29 msgstr "バックアップ対象"
    30 
    31 #: includes/class-admin-interface.php:68
    32 msgid "Select backup items"
    33 msgstr "バックアップ項目を選択"
    34 
    35 #: includes/class-admin-interface.php:68
    36 msgid "only under wp-content"
    37 msgstr "wp-content 配下のみ"
    38 
    39 #: includes/class-admin-interface.php:88
    40 msgid "Backup database"
    41 msgstr "データベースをバックアップ"
    42 
    43 #: includes/class-admin-interface.php:91 (context: checkbox label)
    44 msgctxt "checkbox label"
    45 msgid "Backup files"
    46 msgstr "ファイルをバックアップ"
    47 
    48 #: includes/class-admin-interface.php:151
     27#: includes/class-tnbu-admin-interface.php
     28msgid "Run backup now"
     29msgstr "今すぐバックアップ"
     30
     31#: includes/class-tnbu-cron.php
     32msgid "[%1$s] Tiny Backup: %2$s backup %3$s"
     33msgstr "[%1$s] Tiny Backup: %2$s バックアップ %3$s"
     34
     35#: includes/class-tnbu-cron.php
     36msgid "success"
     37msgstr "成功"
     38
     39#: includes/class-tnbu-cron.php
     40msgid "failed"
     41msgstr "失敗"
     42
     43#: includes/class-tnbu-cron.php
     44msgid "Site: %s"
     45msgstr "サイト: %s"
     46
     47#: includes/class-tnbu-cron.php
     48msgid "Backup type: %s"
     49msgstr "バックアップ種別: %s"
     50
     51#: includes/class-tnbu-cron.php
     52msgid "Status: %s"
     53msgstr "ステータス: %s"
     54
     55#: includes/class-tnbu-cron.php
     56msgid "Time: %s"
     57msgstr "実行日時: %s"
     58
     59
     60#: includes/class-tnbu-admin-interface.php
     61msgid "Choose what to backup from your uploads folder."
     62msgstr "uploads フォルダからバックアップするファイルを選択"
     63
     64#: includes/class-tnbu-admin-interface.php
     65msgid "Auto Backup"
     66msgstr "自動バックアップ"
     67
     68#: includes/class-tnbu-admin-interface.php
     69msgid "Database"
     70msgstr "データベース"
     71
     72#: includes/class-tnbu-admin-interface.php
     73msgctxt "auto backup setting"
     74msgid "Files"
     75msgstr "ファイル"
     76
     77#: includes/class-tnbu-admin-interface.php
     78msgid "Max generations"
     79msgstr "保持世代数"
     80
     81#: includes/class-tnbu-admin-interface.php
     82msgid "Email notification"
     83msgstr "メール通知"
     84
     85#: includes/class-tnbu-admin-interface.php
     86msgid "On success"
     87msgstr "成功時"
     88
     89#: includes/class-tnbu-admin-interface.php
     90msgid "On failure"
     91msgstr "失敗時"
     92
     93#: includes/class-tnbu-admin-interface.php
     94msgid "Automatic backups use the same targets as manual backups. Actual execution timing may vary depending on site traffic."
     95msgstr "自動バックアップは手動バックアップと同じ対象を使用します。実際の実行タイミングはサイトへのアクセス状況により前後します。"
     96
     97#: includes/class-tnbu-admin-interface.php
     98msgid "Disabled"
     99msgstr "無効"
     100
     101#: includes/class-tnbu-admin-interface.php
     102msgid "Daily"
     103msgstr "毎日"
     104
     105#: includes/class-tnbu-admin-interface.php
     106msgid "Weekly"
     107msgstr "毎週"
     108
     109#: includes/class-tnbu-admin-interface.php
     110msgid "Monthly"
     111msgstr "毎月"
     112
     113#: includes/class-tnbu-admin-interface.php
     114msgid "Every Minute (debug)"
     115msgstr "毎分 (デバッグ)"
     116
     117#: includes/class-tnbu-admin-interface.php
     118msgid "Sunday"
     119msgstr "日曜日"
     120
     121#: includes/class-tnbu-admin-interface.php
     122msgid "Monday"
     123msgstr "月曜日"
     124
     125#: includes/class-tnbu-admin-interface.php
     126msgid "Tuesday"
     127msgstr "火曜日"
     128
     129#: includes/class-tnbu-admin-interface.php
     130msgid "Wednesday"
     131msgstr "水曜日"
     132
     133#: includes/class-tnbu-admin-interface.php
     134msgid "Thursday"
     135msgstr "木曜日"
     136
     137#: includes/class-tnbu-admin-interface.php
     138msgid "Friday"
     139msgstr "金曜日"
     140
     141#: includes/class-tnbu-admin-interface.php
     142msgid "Saturday"
     143msgstr "土曜日"
     144
     145#: includes/class-tnbu-admin-interface.php
     146msgid "day"
     147msgstr "日"
     148
     149#. translators: %s: admin email address
     150#: includes/class-tnbu-admin-interface.php
     151msgid "Send notification to %s"
     152msgstr "%s に通知を送信する"
     153
     154#: includes/class-tnbu-admin-interface.php
    49155msgid "Network error occurred"
    50156msgstr "ネットワークエラーが発生しました"
    51157
    52 #: includes/class-admin-interface.php:152
     158#: includes/class-tnbu-admin-interface.php
    53159msgid "Backup failed"
    54160msgstr "バックアップに失敗しました"
    55161
    56 #: includes/class-admin-interface.php:153
     162#: includes/class-tnbu-admin-interface.php
    57163msgid "Backing up"
    58164msgstr "バックアップ中"
    59165
    60 #: includes/class-admin-interface.php:154
     166#: includes/class-tnbu-admin-interface.php
    61167msgid "Starting backup"
    62168msgstr "バックアップを開始しています"
    63169
    64 #: includes/class-admin-interface.php:176
     170#: includes/class-tnbu-admin-interface.php
    65171msgid "Enter a relative path under wp-content (e.g. "
    66172msgstr "wp-content 配下の相対パスを入力してください (例: "
    67173
    68 #: includes/class-admin-interface.php:198
     174#: includes/class-tnbu-admin-interface.php
    69175msgid "Save Settings"
    70176msgstr "設定を保存"
    71177
    72 #: includes/class-admin-interface.php:203
     178#: includes/class-tnbu-admin-interface.php
    73179msgid "Reset settings to defaults? Backup files will not be deleted."
    74180msgstr "設定を初期値に戻しますか?バックアップファイルは削除されません。"
    75181
    76 #: includes/class-admin-interface.php:203
     182#: includes/class-tnbu-admin-interface.php
    77183msgid "Reset settings"
    78184msgstr "設定をリセット"
    79185
    80 #: includes/class-admin-interface.php:209
     186#: includes/class-tnbu-admin-interface.php
    81187msgid "Backup"
    82188msgstr "バックアップ"
    83189
    84 #: includes/class-admin-interface.php:211
    85 msgid "Run backup now"
    86 msgstr "今すぐバックアップを実行"
    87 
    88 #: includes/class-admin-interface.php:217
     190#: includes/class-tnbu-admin-interface.php
     191msgid "Database backup"
     192msgstr "データベースバックアップ"
     193
     194#: includes/class-tnbu-admin-interface.php
     195msgid "Files backup"
     196msgstr "ファイルバックアップ"
     197
     198#: includes/class-tnbu-admin-interface.php
    89199msgid "Backup destination: "
    90200msgstr "バックアップ保存先: "
    91201
    92 #: includes/class-admin-interface.php:221
     202#: includes/class-tnbu-admin-interface.php
    93203msgid "Preparing"
    94204msgstr "準備中"
    95205
    96 #: includes/class-admin-interface.php:229 (context: section title)
     206#: includes/class-tnbu-admin-interface.php
    97207msgctxt "section title"
    98208msgid "Backup files"
    99209msgstr "バックアップファイル"
    100210
    101 #: includes/class-admin-interface.php:247
    102 #: includes/class-admin-interface.php:255
     211#: includes/class-tnbu-admin-interface.php
    103212msgid "No backup files found"
    104213msgstr "バックアップファイルが見つかりません"
    105214
    106 #: includes/class-admin-interface.php:271
     215#: includes/class-tnbu-admin-interface.php
    107216msgid "Delete the selected files. Are you sure?"
    108217msgstr "選択したファイルを削除します。よろしいですか?"
    109218
    110 #: includes/class-admin-interface.php:277
     219#: includes/class-tnbu-admin-interface.php
    111220msgid "File name"
    112221msgstr "ファイル名"
    113222
    114 #: includes/class-admin-interface.php:278
     223#: includes/class-tnbu-admin-interface.php
    115224msgid "Modified"
    116225msgstr "更新日時"
    117226
    118 #: includes/class-admin-interface.php:279
     227#: includes/class-tnbu-admin-interface.php
    119228msgid "Size"
    120229msgstr "サイズ"
    121230
    122 #: includes/class-admin-interface.php:280
    123 #: includes/class-admin-interface.php:298
     231#: includes/class-tnbu-admin-interface.php
    124232msgid "Download"
    125233msgstr "ダウンロード"
    126234
    127 #: includes/class-admin-interface.php:304
     235#: includes/class-tnbu-admin-interface.php
    128236msgid "Delete selected files"
    129237msgstr "選択したファイルを削除"
    130238
    131 #: includes/class-admin-interface.php:334
     239#: includes/class-tnbu-admin-interface.php
    132240msgid "Settings saved."
    133241msgstr "設定を保存しました。"
    134242
    135 #: includes/class-admin-interface.php:336
     243#: includes/class-tnbu-admin-interface.php
    136244msgid "Dismiss this notice"
    137245msgstr "この通知を非表示"
    138246
    139 #: includes/class-ajax-handler.php:55
    140 msgid "Settings saved: DB=%1$s, Files=%2$s"
    141 msgstr "設定を保存しました: DB=%1$s, ファイル=%2$s"
    142 
    143 #: includes/class-ajax-handler.php:58
     247#: includes/class-tnbu-ajax-handler.php
    144248msgid "Backup started"
    145249msgstr "バックアップを開始しました"
    146250
    147 #: includes/class-ajax-handler.php:73
    148 msgid "Checking backup settings: DB=%1$s, Files=%2$s"
    149 msgstr "バックアップ設定を確認中: DB=%1$s, ファイル=%2$s"
    150 
    151 #: includes/class-ajax-handler.php:76
     251#: includes/class-tnbu-ajax-handler.php
    152252msgid "Starting database backup"
    153253msgstr "データベースのバックアップを開始"
    154254
    155 #: includes/class-ajax-handler.php:81
     255#. translators: %s: error message
     256#: includes/class-tnbu-ajax-handler.php
    156257msgid "Database backup error: %s"
    157258msgstr "データベースのバックアップエラー: %s"
    158259
    159 #: includes/class-ajax-handler.php:85
     260#: includes/class-tnbu-ajax-handler.php
    160261msgid "Database backup completed"
    161262msgstr "データベースのバックアップが完了しました"
    162263
    163 #: includes/class-ajax-handler.php:91
     264#: includes/class-tnbu-ajax-handler.php
    164265msgid "Starting files backup"
    165266msgstr "ファイルのバックアップを開始"
    166267
    167 #: includes/class-ajax-handler.php:107
    168 msgid "Selected files check: %d items"
    169 msgstr "選択ファイルの確認: %d 件"
    170 
    171 #: includes/class-ajax-handler.php:110
     268#: includes/class-tnbu-ajax-handler.php
    172269msgid "No file targets selected for backup"
    173270msgstr "バックアップ対象ファイルが選択されていません"
    174271
    175 #: includes/class-ajax-handler.php:117
     272#. translators: %s: error message
     273#: includes/class-tnbu-ajax-handler.php
    176274msgid "Files backup error: %s"
    177275msgstr "ファイルバックアップのエラー: %s"
    178276
    179 #: includes/class-ajax-handler.php:121
    180 msgid "Files backup completed: "
    181 msgstr "ファイルバックアップ完了: "
    182 
    183 #: includes/class-ajax-handler.php:127
     277#: includes/class-tnbu-ajax-handler.php
     278msgid "Files backup completed"
     279msgstr "ファイルバックアップが完了しました"
     280
     281#. translators: %s: error message
     282#: includes/class-tnbu-ajax-handler.php
    184283msgid "Backup failed: %s"
    185284msgstr "バックアップに失敗しました: %s"
    186285
    187 #: includes/class-ajax-handler.php:129
     286#. translators: %s: error message
     287#: includes/class-tnbu-ajax-handler.php
    188288msgid "Error: %s"
    189289msgstr "エラー: %s"
    190290
    191 #: includes/class-ajax-handler.php:146
    192 msgid "Backup completed: %1$s%2$s"
    193 msgstr "バックアップが完了しました: %1$s%2$s"
    194 
    195 #: includes/class-ajax-handler.php:147
     291#. translators: %s: backup file name
     292#: includes/class-tnbu-ajax-handler.php
     293msgid "Backup completed: %s"
     294msgstr "バックアップが完了しました: %s"
     295
     296#: includes/class-tnbu-ajax-handler.php
    196297msgid "Completed"
    197298msgstr "完了"
    198299
    199 #: includes/class-ajax-handler.php:221
     300#: includes/class-tnbu-ajax-handler.php
    200301msgid "You do not have permission."
    201302msgstr "権限がありません。"
    202303
    203 #: includes/class-ajax-handler.php:224
     304#: includes/class-tnbu-ajax-handler.php
    204305msgid "Security check failed."
    205306msgstr "セキュリティチェックに失敗しました。"
    206307
    207 #: includes/class-ajax-handler.php:228
     308#: includes/class-tnbu-ajax-handler.php
    208309msgid "Settings have been reset."
    209310msgstr "設定を初期化しました。"
    210311
    211 #: includes/class-ajax-handler.php:247
     312#: includes/class-tnbu-ajax-handler.php
    212313msgid "No file name specified."
    213314msgstr "ファイル名が指定されていません。"
    214315
    215 #: includes/class-ajax-handler.php:252
     316#: includes/class-tnbu-ajax-handler.php
    216317msgid "Invalid file name."
    217318msgstr "ファイル名が不正です。"
    218319
    219 #: includes/class-ajax-handler.php:265
     320#: includes/class-tnbu-ajax-handler.php
    220321msgid "File not found."
    221322msgstr "ファイルが見つかりません。"
    222323
    223 #: includes/class-ajax-handler.php:272
     324#: includes/class-tnbu-ajax-handler.php
    224325msgid "Invalid file path."
    225326msgstr "ファイルパスが不正です。"
    226327
    227 #: includes/class-database-backup.php:39
     328#: includes/class-tnbu-ajax-handler.php
     329msgid "Selected files have been deleted."
     330msgstr "選択したファイルを削除しました。"
     331
     332#: includes/class-tnbu-database-backup.php
    228333msgid "Starting database dump"
    229334msgstr "データベースのダンプを開始"
    230335
    231 #: includes/class-database-backup.php:42
    232 msgid "mysqldump failed, switching to PHP dump"
    233 msgstr "mysqldump に失敗したため、PHP ダンプに切り替えます"
    234 
    235 #: includes/class-database-backup.php:49
    236 msgid "mysqldump completed"
    237 msgstr "mysqldump が完了しました"
    238 
    239 #: includes/class-database-backup.php:59
     336#: includes/class-tnbu-database-backup.php
     337msgid "Database dump completed"
     338msgstr "データベースのダンプが完了しました"
     339
     340#: includes/class-tnbu-database-backup.php
    240341msgid "Starting ZIP compression"
    241342msgstr "ZIP 圧縮を開始"
    242343
    243 #: includes/class-database-backup.php:65
     344#: includes/class-tnbu-database-backup.php
    244345msgid "ZIP extension is not available"
    245346msgstr "ZIP 拡張が利用できません"
    246347
    247 #: includes/class-database-backup.php:73
     348#: includes/class-tnbu-database-backup.php
    248349msgid "Failed to create ZIP file"
    249350msgstr "ZIP ファイルの作成に失敗しました"
    250351
    251 #: includes/class-database-backup.php:82
     352#: includes/class-tnbu-database-backup.php
    252353msgid "Failed to finalize ZIP file"
    253354msgstr "ZIP ファイルの確定に失敗しました"
    254355
    255 #: includes/class-database-backup.php:84
     356#: includes/class-tnbu-database-backup.php
    256357msgid "ZIP compression completed"
    257358msgstr "ZIP 圧縮が完了しました"
    258359
    259 #: includes/class-database-backup.php:102
     360#: includes/class-tnbu-database-backup.php
    260361msgid "Unable to write SQL file"
    261362msgstr "SQL ファイルを書き込めません"
    262363
    263 #: includes/class-database-backup.php:117
     364#: includes/class-tnbu-database-backup.php
    264365msgid "No tables found"
    265366msgstr "テーブルが見つかりません"
    266367
    267 #: includes/class-database-backup.php:126
     368#. translators: 1: table name, 2: current index, 3: total tables
     369#: includes/class-tnbu-database-backup.php
    268370msgid "Processing table: %1$s (%2$d/%3$d)"
    269371msgstr "テーブルを処理中: %1$s (%2$d/%3$d)"
    270372
    271 #: includes/class-database-backup.php:185
     373#. translators: 1: table name, 2: processed rows, 3: total rows
     374#: includes/class-tnbu-database-backup.php
    272375msgid "Processing table: %1$s (%2$d/%3$d rows)"
    273376msgstr "テーブルを処理中: %1$s (%2$d/%3$d 行)"
    274377
    275 #: includes/class-database-backup.php:193
     378#: includes/class-tnbu-database-backup.php
    276379msgid "PHP dump failed (empty file)"
    277380msgstr "PHP ダンプに失敗しました(空のファイル)"
    278381
    279 #: includes/class-database-backup.php:207
     382#: includes/class-tnbu-database-backup.php
    280383msgid "Command execution is not allowed on this server"
    281384msgstr "このサーバーではコマンド実行が許可されていません"
    282385
    283 #: includes/class-database-backup.php:247
     386#. translators: %s: error output from mysqldump
     387#: includes/class-tnbu-database-backup.php
    284388msgid "mysqldump failed: %s"
    285389msgstr "mysqldump に失敗しました: %s"
    286390
    287  
    288 
    289 #: includes/class-file-backup.php:44
    290 msgid "Trying CLI compression"
    291 msgstr "CLI 圧縮を試行中"
    292 
    293 #: includes/class-file-backup.php:47
    294 msgid "Files compression completed (CLI)"
    295 msgstr "ファイル圧縮が完了しました(CLI)"
    296 
    297 #: includes/class-file-backup.php:50
    298 msgid "CLI compression failed, falling back to PHP"
    299 msgstr "CLI 圧縮に失敗したため PHP にフォールバックします"
    300 
    301 #: includes/class-file-backup.php:52
     391#: includes/class-tnbu-file-backup.php
    302392msgid "Starting file scan"
    303393msgstr "ファイルのスキャンを開始"
    304394
    305 #: includes/class-file-backup.php:56
     395#. translators: %d: number of files
     396#: includes/class-tnbu-file-backup.php
    306397msgid "File scan completed: %d files"
    307398msgstr "ファイルスキャンが完了: %d 件"
    308399
    309 #: includes/class-file-backup.php:63
     400#. translators: 1: processed file count, 2: total files
     401#: includes/class-tnbu-file-backup.php
    310402msgid "Compressing files: %1$d/%2$d"
    311403msgstr "ファイルを圧縮中: %1$d/%2$d"
    312404
    313 #: includes/class-file-backup.php:68
     405#: includes/class-tnbu-file-backup.php
    314406msgid "No files to add"
    315407msgstr "追加するファイルがありません"
    316408
    317  
    318 
    319 #: includes/class-file-backup.php:72
     409#: includes/class-tnbu-file-backup.php
    320410msgid "Files compression completed"
    321411msgstr "ファイル圧縮が完了しました"
    322412
    323 #: includes/class-progress-manager.php:32
     413#: includes/class-tnbu-cron.php
     414msgid "Once Monthly"
     415msgstr "月1回"
     416
     417#: includes/class-tnbu-progress-manager.php
    324418msgid "Idle"
    325419msgstr "待機中"
    326420
    327 #: includes/class-utilities.php:156
     421#. translators: %s: directory path
     422#: includes/class-tnbu-utilities.php
    328423msgid "Cannot write to backup directory: %s"
    329424msgstr "バックアップディレクトリに書き込めません: %s"
    330 
    331 #: includes/class-ajax-handler.php:345
    332 msgid "Selected files have been deleted."
    333 msgstr "選択したファイルを削除しました。"
    334 
    335 
  • tiny-backup/tags/1.3.0/languages/tiny-backup.pot

    r3397190 r3487170  
    4848msgstr ""
    4949
    50 #: includes/class-admin-interface.php:68
    51 msgid "Select backup items"
    52 msgstr ""
    53 
    54 #: includes/class-admin-interface.php:68
    55 msgid "only under wp-content"
     50#: includes/class-admin-interface.php:199
     51msgid "Choose what to backup from your uploads folder."
    5652msgstr ""
    5753
  • tiny-backup/tags/1.3.0/readme.txt

    r3397693 r3487170  
    33Tags: backup, database, files, zip, admin
    44Requires at least: 6.0
    5 Tested up to: 6.8
     5Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 1.1.1
     7Stable tag: 1.3.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1010
    11 Simple and minimal backup plugin for WordPress. Create database and files backups with one click.
     11Simple and minimal backup plugin for WordPress. Create database and files backups manually or automatically on a schedule.
    1212
    1313== Description ==
     
    1616You can create and download the bare minimum backup with just one click, stress-free.
    1717
    18 - Database backup (SQL inside ZIP)
    19 - Files backup (select folders under `wp-content`)
    20 - Clear progress indicator and logs
     18**Manual Backup**
     19
     20- Database backup (SQL inside ZIP) with a single click
     21- Files backup from your uploads folder — select specific subfolders as needed
     22- Clear progress indicator during backup
     23
     24**Auto Backup (WP-Cron)**
     25
     26- Set separate schedules for database and files backups (daily / weekly / monthly)
     27- Configure time, day of week, or day of month for each schedule
     28- Manage retention: set max generations for database and files independently
     29- Email notifications on success and/or failure (sent to the admin email address)
     30
     31**General**
     32
    2133- No external services; everything runs on your server
     34- Backup files are stored in `wp-content/tiny-backup`
    2235
    23 This plugin is ideal for small to medium sites that need quick on-demand backups.
     36This plugin is ideal for small to medium sites that need simple, low-overhead backups.
    2437
    2538== Installation ==
     
    28412. Activate the plugin through the 'Plugins' screen in WordPress.
    29423. Go to Tools → Tiny Backup.
    30 4. Choose backup targets and click "Run backup now".
     434. Click "Database Backup" or "Files Backup" to run a manual backup, or configure Auto Backup settings.
    3144
    3245== Frequently Asked Questions ==
     
    3447= Where are backups saved? =
    3548
    36 By default, backups are saved to `wp-content/tiny-backup`.
     49Backups are saved to `wp-content/tiny-backup` by default.
    3750
    3851= Can I change the destination directory? =
     
    4255= Are scheduled backups supported? =
    4356
    44 Not currently supported. This initial version only supports manual backups. Scheduled backups may be added in a future version.
     57Yes. As of version 1.3.0, you can configure automatic backups for database and files independently using WP-Cron. Options include daily, weekly, and monthly schedules.
     58
     59= Does scheduled backup run at the exact time I set? =
     60
     61WP-Cron is triggered by site visits, so the actual execution time may differ slightly from the configured time on low-traffic sites. The backup will run on the next page load after the scheduled time.
     62
     63= What folders can I select for file backup? =
     64
     65You can select the entire uploads folder or individual subfolders directly under it. Subfolders of subfolders are not selectable.
    4566
    4667== Screenshots ==
    4768
    48 1. Settings screen - Choose what to backup: database and/or files, and select specific folders/plugins/themes under wp-content
    49 2. Backup management screen - View all backup files with creation date and size, download or delete backups
     691. Settings screen — Select backup items from the uploads folder
     702. Auto Backup settings — Configure separate schedules, retention, and email notifications for database and files
     713. Backup screen — Run manual backups and manage backup files
    5072
    5173== Changelog ==
    5274
    53 = 1.1.1 =
     75= 1.3.0 =
     76
     77- Added auto backup feature using WP-Cron (daily / weekly / monthly)
     78- Database and files can be scheduled independently
     79- Configurable retention (max generations) per backup type
     80- Email notifications on success and/or failure
     81- Split manual backup into separate "Database Backup" and "Files Backup" buttons
     82- Limited file backup scope to uploads folder (direct subfolders selectable)
     83- Introduced wp-scripts build pipeline for admin assets
     84
     85= 1.2.0 =
     86
     87- Internal improvements and refactoring.
     88
     89= 1.1.1 =
    5490
    5591- Bug fix.
     
    5995- Added an action link to the plugin list for quick access to the settings page.
    6096
    61 = 1.0.0 = 
     97= 1.0.0 =
    6298
    6399- Initial Release.
     
    65101== Upgrade Notice ==
    66102
    67 = 1.0.0 =
     103= 1.3.0 =
    68104
    69 - Initial release.
    70 
    71 
     105Auto backup, separate DB/files schedules, email notifications, and a redesigned backup UI are now available.
  • tiny-backup/tags/1.3.0/tiny-backup.php

    r3397693 r3487170  
    33Plugin Name: Tiny Backup
    44Description: Create database and files backups with minimal setup.
    5 Version: 1.1.1
    6 Author: Takashi Fujisaki 
     5Version: 1.3.0
     6Author: Takashi Fujisaki
    77Plugin URI: https://wordpress.org/plugins/tiny-backup/
    88Author URI: https://yuiami.jp
     
    1111Requires at least: 6.0
    1212Requires PHP: 7.4
    13 Tested up to: 6.8
     13Tested up to: 6.9
    1414License: GPLv2 or later
    1515License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    2828require_once plugin_dir_path( __FILE__ ) . 'includes/class-tnbu-ajax-handler.php';
    2929require_once plugin_dir_path( __FILE__ ) . 'includes/class-tnbu-core.php';
     30require_once plugin_dir_path( __FILE__ ) . 'includes/class-tnbu-cron.php';
    3031
    3132
    3233// プラグイン初期化
    3334TNBU_Core::init();
     35TNBU_Cron::init();
     36
     37// カスタム cron スケジュール(monthly)を登録
     38add_filter( 'cron_schedules', array( 'TNBU_Cron', 'add_cron_schedules' ) );
    3439
    3540// 有効化フック(WPの定石としてメインファイルで登録)
    3641register_activation_hook( __FILE__, array( 'TNBU_Core', 'on_activate' ) );
     42
     43// 無効化フック(cronスケジュール解除)
     44register_deactivation_hook( __FILE__, array( 'TNBU_Cron', 'unschedule_all' ) );
  • tiny-backup/tags/1.3.0/uninstall.php

    r3397693 r3487170  
    1414}
    1515
    16 // 定数を利用するためコアクラスを読み込み
     16// 定数を利用するためクラスを読み込み
    1717require_once plugin_dir_path(__FILE__) . 'includes/class-tnbu-core.php';
     18require_once plugin_dir_path(__FILE__) . 'includes/class-tnbu-cron.php';
     19
     20// cronスケジュールを解除
     21TNBU_Cron::unschedule_all();
    1822
    1923// プラグインの設定を削除
  • tiny-backup/trunk/includes/class-tnbu-admin-interface.php

    r3397691 r3487170  
    1818        $screen = get_current_screen();
    1919        if ( 'tools_page_tnbu' === $screen->id ) {
     20            $asset_file = plugin_dir_path( __DIR__ ) . 'build/admin.asset.php';
     21            $asset      = file_exists( $asset_file ) ? require $asset_file : array(
     22                'dependencies' => array(),
     23                'version'      => TNBU_Utilities::get_version(),
     24            );
     25            $deps = array_unique( array_merge( $asset['dependencies'], array( 'wp-i18n' ) ) );
     26
    2027            wp_enqueue_style(
    2128                'tnbu-style',
    22                 plugin_dir_url( __DIR__ ) . 'assets/css/admin.css',
     29                plugin_dir_url( __DIR__ ) . 'build/admin.css',
    2330                array(),
    24                 TNBU_Utilities::get_version()
     31                $asset['version']
    2532            );
    2633            wp_enqueue_script(
    2734                'tnbu-admin',
    28                 plugin_dir_url( __DIR__ ) . 'assets/js/admin.js',
    29                 array( 'wp-i18n' ),
    30                 TNBU_Utilities::get_version(),
     35                plugin_dir_url( __DIR__ ) . 'build/admin.js',
     36                $deps,
     37                $asset['version'],
    3138                true
    3239            );
     
    5764            );
    5865            wp_add_inline_script( 'tnbu-admin', $inline_script, 'before' );
     66
     67            // 自動バックアップUI:スケジュール選択に応じて曜日/日付/時刻の表示切替
     68            $auto_backup_script = <<<'JS'
     69document.addEventListener('DOMContentLoaded', function() {
     70    document.querySelectorAll('.tnbu-auto-schedule').forEach(function(sel) {
     71        sel.addEventListener('change', function() {
     72            var row = this.closest('.tnbu-auto-backup-row');
     73            var val = this.value;
     74            var dow = row.querySelector('.tnbu-auto-dow');
     75            var day = row.querySelector('.tnbu-auto-day');
     76            var time = row.querySelector('.tnbu-auto-time');
     77            dow.style.display = (val === 'weekly') ? '' : 'none';
     78            day.style.display = (val === 'monthly') ? '' : 'none';
     79            time.style.display = (val === '' || val === 'every_minute') ? 'none' : '';
     80        });
     81    });
     82});
     83JS;
     84            wp_add_inline_script( 'tnbu-admin', $auto_backup_script, 'after' );
    5985        }
    6086    }
     
    100126        register_setting( 'tnbu_settings_group', TNBU_Core::OPTION_KEY, array( __CLASS__, 'sanitize_settings' ) );
    101127        // 単一セクション(見出しなし)
    102         add_settings_section( 'tnbu_section_settings', __( 'Settings', 'tiny-backup' ), '__return_false', 'tnbu' );
    103 
    104         // 最上部: 横並びチェックボックス(DB/ファイル)
    105         add_settings_field( 'backup_toggles', __( 'Backup targets', 'tiny-backup' ), array( __CLASS__, 'field_backup_toggles' ), 'tnbu', 'tnbu_section_settings' );
    106 
    107         // ファイル設定: ファイル名テンプレート(固定のため表示なし)
    108         add_settings_field(
    109             'files_selection',
    110             __( 'Select backup items', 'tiny-backup' ) . '<br>' . __( 'only under wp-content', 'tiny-backup' ),
    111             array( __CLASS__, 'field_files_selection' ),
    112             'tnbu',
    113             'tnbu_section_settings'
    114         );
     128        add_settings_section( 'tnbu_section_settings', '', '__return_false', 'tnbu' );
     129
     130        // ファイルバックアップ対象選択セクション
     131        add_settings_field( 'files_selection', '', array( __CLASS__, 'field_files_selection' ), 'tnbu', 'tnbu_section_files' );
     132
     133        // 自動バックアップセクション
     134        add_settings_section( 'tnbu_section_auto', __( 'Auto Backup', 'tiny-backup' ), array( __CLASS__, 'section_auto_description' ), 'tnbu' );
     135        add_settings_field( 'auto_backup_db', __( 'Database', 'tiny-backup' ), array( __CLASS__, 'field_auto_backup_db' ), 'tnbu', 'tnbu_section_auto' );
     136        add_settings_field( 'auto_backup_files', _x( 'Files', 'auto backup setting', 'tiny-backup' ), array( __CLASS__, 'field_auto_backup_files' ), 'tnbu', 'tnbu_section_auto' );
     137        add_settings_field( 'auto_backup_email', __( 'Email notification', 'tiny-backup' ), array( __CLASS__, 'field_auto_backup_email' ), 'tnbu', 'tnbu_section_auto' );
    115138
    116139        // 共通設定: 保存先ディレクトリ(固定化のためUI非表示)
    117     }
    118 
    119     /**
    120      * バックアップ対象のトグルボタンを表示
    121      * データベースとファイルのバックアップ有効/無効を切り替え
    122      */
    123     public static function field_backup_toggles() {
    124         $opts                    = TNBU_Core::get_options();
    125         $is_db_backup_enabled    = ! empty( $opts['backup_db'] );
    126         $is_files_backup_enabled = ! empty( $opts['backup_files'] );
    127         ?>
    128         <div class="tnbu-backup-toggles">
    129             <label>
    130                 <input type="checkbox" name="<?php echo esc_attr( TNBU_Core::OPTION_KEY ); ?>[backup_db]" value="1" <?php checked( $is_db_backup_enabled, true ); ?> /> <?php echo esc_html( __( 'Backup database', 'tiny-backup' ) ); ?>
    131             </label>
    132             <label>
    133                 <input type="checkbox" name="<?php echo esc_attr( TNBU_Core::OPTION_KEY ); ?>[backup_files]" value="1" <?php checked( $is_files_backup_enabled, true ); ?> /> <?php echo esc_html( _x( 'Backup files', 'checkbox label', 'tiny-backup' ) ); ?>
    134             </label>
    135         </div>
    136         <?php
    137140    }
    138141
     
    149152        $rel                  = ltrim( $rel, '/' );
    150153        $output['target_dir'] = ( '' !== $rel ) ? $rel : TNBU_Core::DEFAULT_DIR_SUFFIX;
    151         // do not store files filename template
    152         // backup switches (checkboxes)
    153         $output['backup_db']    = ! empty( $input['backup_db'] ) ? 1 : 0;
    154         $output['backup_files'] = ! empty( $input['backup_files'] ) ? 1 : 0;
    155154        // save selected items from settings form (if present)
    156155        $sel                      = isset( $input['selected_items'] ) && is_array( $input['selected_items'] ) ? array_map( 'sanitize_text_field', $input['selected_items'] ) : array();
     
    170169        );
    171170        $output['selected_items'] = $sel;
     171
     172        // 自動バックアップ設定のサニタイズ
     173        $valid_schedules = array( '', 'daily', 'weekly', 'monthly' );
     174        if ( defined( 'TNBU_CRON_DEBUG' ) && TNBU_CRON_DEBUG ) {
     175            $valid_schedules[] = 'every_minute';
     176        }
     177        foreach ( array( 'auto_backup_db', 'auto_backup_files' ) as $prefix ) {
     178            $sched = isset( $input[ $prefix . '_schedule' ] ) ? sanitize_text_field( $input[ $prefix . '_schedule' ] ) : '';
     179            $output[ $prefix . '_schedule' ] = in_array( $sched, $valid_schedules, true ) ? $sched : '';
     180
     181            $time = isset( $input[ $prefix . '_time' ] ) ? sanitize_text_field( $input[ $prefix . '_time' ] ) : '03:00';
     182            if ( preg_match( '/^(\d{1,2}):(\d{2})$/', $time, $m ) ) {
     183                $output[ $prefix . '_time' ] = sprintf( '%02d:%s', max( 0, min( 23, (int) $m[1] ) ), ( (int) $m[2] >= 30 ? '30' : '00' ) );
     184            } else {
     185                $output[ $prefix . '_time' ] = '03:00';
     186            }
     187
     188            $dow = isset( $input[ $prefix . '_day_of_week' ] ) ? (int) $input[ $prefix . '_day_of_week' ] : 0;
     189            $output[ $prefix . '_day_of_week' ] = (string) max( 0, min( 6, $dow ) );
     190
     191            $day = isset( $input[ $prefix . '_day' ] ) ? (int) $input[ $prefix . '_day' ] : 1;
     192            $output[ $prefix . '_day' ] = (string) max( 1, min( 28, $day ) );
     193        }
     194
     195        $max_gen = isset( $input['auto_backup_db_max_generations'] ) ? (int) $input['auto_backup_db_max_generations'] : TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     196        $output['auto_backup_db_max_generations'] = max( 1, min( 10, $max_gen ) );
     197
     198        $max_gen = isset( $input['auto_backup_files_max_generations'] ) ? (int) $input['auto_backup_files_max_generations'] : TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     199        $output['auto_backup_files_max_generations'] = max( 1, min( 10, $max_gen ) );
     200
     201        $output['auto_backup_email_success'] = ! empty( $input['auto_backup_email_success'] );
     202        $output['auto_backup_email_failure'] = ! empty( $input['auto_backup_email_failure'] );
     203
    172204        return $output;
    173205    }
     
    175207    /**
    176208     * ファイルバックアップ対象選択UIを表示
    177      * wp-content配下のファイル・フォルダをツリー形式で選択可能
    178209     */
    179210    public static function field_files_selection() {
     
    184215            </div>
    185216        </div>
     217
     218        <hr class="tnbu-divider" />
     219       
     220        <?php
     221    }
     222
     223    /**
     224     * 自動バックアップセクションの説明文
     225     */
     226    public static function section_auto_description() {
     227        echo '<p class="description">' . esc_html__( 'Automatic backups use the same targets as manual backups. Actual execution timing may vary depending on site traffic.', 'tiny-backup' ) . '</p>';
     228    }
     229
     230    /**
     231     * スケジュール選択肢を返す
     232     *
     233     * @return array
     234     */
     235    private static function get_schedule_options() {
     236        $options = array(
     237            ''        => __( 'Disabled', 'tiny-backup' ),
     238            'daily'   => __( 'Daily', 'tiny-backup' ),
     239            'weekly'  => __( 'Weekly', 'tiny-backup' ),
     240            'monthly' => __( 'Monthly', 'tiny-backup' ),
     241        );
     242        if ( defined( 'TNBU_CRON_DEBUG' ) && TNBU_CRON_DEBUG ) {
     243            $options['every_minute'] = __( 'Every Minute (debug)', 'tiny-backup' );
     244        }
     245        return $options;
     246    }
     247
     248    /**
     249     * 時刻選択肢を返す(0〜23時)
     250     *
     251     * @return array
     252     */
     253    private static function get_time_options() {
     254        $times = array();
     255        for ( $h = 0; $h < 24; $h++ ) {
     256            foreach ( array( '00', '30' ) as $m ) {
     257                $key           = sprintf( '%02d:%s', $h, $m );
     258                $times[ $key ] = $key;
     259            }
     260        }
     261        return $times;
     262    }
     263
     264    /**
     265     * 曜日選択肢を返す
     266     *
     267     * @return array
     268     */
     269    private static function get_dow_options() {
     270        return array(
     271            '0' => __( 'Sunday', 'tiny-backup' ),
     272            '1' => __( 'Monday', 'tiny-backup' ),
     273            '2' => __( 'Tuesday', 'tiny-backup' ),
     274            '3' => __( 'Wednesday', 'tiny-backup' ),
     275            '4' => __( 'Thursday', 'tiny-backup' ),
     276            '5' => __( 'Friday', 'tiny-backup' ),
     277            '6' => __( 'Saturday', 'tiny-backup' ),
     278        );
     279    }
     280
     281    /**
     282     * 自動バックアップ行(スケジュール+時刻+曜日/日付)をレンダリング
     283     *
     284     * @param string $prefix 設定キーのプレフィックス('auto_backup_db' or 'auto_backup_files')
     285     */
     286    private static function render_auto_backup_row( $prefix ) {
     287        $opts     = TNBU_Core::get_options();
     288        $opt_key  = TNBU_Core::OPTION_KEY;
     289        $schedule = $opts[ $prefix . '_schedule' ] ?? '';
     290        $time     = $opts[ $prefix . '_time' ] ?? '03';
     291        $dow      = $opts[ $prefix . '_day_of_week' ] ?? '0';
     292        $day      = $opts[ $prefix . '_day' ] ?? '1';
     293        ?>
     294        <div class="tnbu-auto-backup-row" data-prefix="<?php echo esc_attr( $prefix ); ?>">
     295            <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $prefix . '_schedule' ); ?>]" class="tnbu-auto-schedule">
     296                <?php foreach ( self::get_schedule_options() as $val => $label ) : ?>
     297                    <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $schedule, $val ); ?>><?php echo esc_html( $label ); ?></option>
     298                <?php endforeach; ?>
     299            </select>
     300
     301            <span class="tnbu-auto-dow" <?php echo 'weekly' !== $schedule ? 'style="display:none;"' : ''; ?>>
     302                <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $prefix . '_day_of_week' ); ?>]">
     303                    <?php foreach ( self::get_dow_options() as $val => $label ) : ?>
     304                        <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $dow, $val ); ?>><?php echo esc_html( $label ); ?></option>
     305                    <?php endforeach; ?>
     306                </select>
     307            </span>
     308
     309            <span class="tnbu-auto-day" <?php echo 'monthly' !== $schedule ? 'style="display:none;"' : ''; ?>>
     310                <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $prefix . '_day' ); ?>]">
     311                    <?php for ( $d = 1; $d <= 28; $d++ ) : ?>
     312                        <option value="<?php echo esc_attr( $d ); ?>" <?php selected( $day, (string) $d ); ?>><?php echo esc_html( $d ); ?></option>
     313                    <?php endfor; ?>
     314                </select>
     315                <?php echo esc_html__( 'day', 'tiny-backup' ); ?>
     316            </span>
     317
     318            <span class="tnbu-auto-time" <?php echo ( '' === $schedule || 'every_minute' === $schedule ) ? 'style="display:none;"' : ''; ?>>
     319                <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $prefix . '_time' ); ?>]">
     320                    <?php foreach ( self::get_time_options() as $val => $label ) : ?>
     321                        <option value="<?php echo esc_attr( $val ); ?>" <?php selected( $time, $val ); ?>><?php echo esc_html( $label ); ?></option>
     322                    <?php endforeach; ?>
     323                </select>
     324            </span>
     325
     326            <?php
     327            $max_key = $prefix . '_max_generations';
     328            $max_val = $opts[ $max_key ] ?? TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     329            ?>
     330            <span class="tnbu-auto-max-gen" <?php echo '' === $schedule ? 'style="display:none;"' : 'style="margin-left:2rem;"'; ?>>
     331                <?php echo esc_html__( 'Max generations', 'tiny-backup' ); ?>:
     332                <select name="<?php echo esc_attr( $opt_key ); ?>[<?php echo esc_attr( $max_key ); ?>]">
     333                    <?php for ( $i = 1; $i <= 10; $i++ ) : ?>
     334                        <option value="<?php echo esc_attr( $i ); ?>" <?php selected( (int) $max_val, $i ); ?>><?php echo esc_html( $i ); ?></option>
     335                    <?php endfor; ?>
     336                </select>
     337            </span>
     338        </div>
     339        <?php
     340    }
     341
     342    /**
     343     * DB自動バックアップ設定フィールド
     344     */
     345    public static function field_auto_backup_db() {
     346        self::render_auto_backup_row( 'auto_backup_db' );
     347    }
     348
     349    /**
     350     * ファイル自動バックアップ設定フィールド
     351     */
     352    public static function field_auto_backup_files() {
     353        self::render_auto_backup_row( 'auto_backup_files' );
     354    }
     355
     356    /**
     357     * DB保持世代数設定フィールド
     358     */
     359    public static function field_auto_backup_db_max_generations() {
     360        $opts = TNBU_Core::get_options();
     361        $max  = $opts['auto_backup_db_max_generations'] ?? TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     362        ?>
     363        <select name="<?php echo esc_attr( TNBU_Core::OPTION_KEY ); ?>[auto_backup_db_max_generations]">
     364            <?php for ( $i = 1; $i <= 10; $i++ ) : ?>
     365                <option value="<?php echo esc_attr( $i ); ?>" <?php selected( (int) $max, $i ); ?>><?php echo esc_html( $i ); ?></option>
     366            <?php endfor; ?>
     367        </select>
     368        <?php
     369    }
     370
     371    /**
     372     * ファイル保持世代数設定フィールド
     373     */
     374    public static function field_auto_backup_files_max_generations() {
     375        $opts = TNBU_Core::get_options();
     376        $max  = $opts['auto_backup_files_max_generations'] ?? TNBU_Core::DEFAULT_AUTO_MAX_GENERATIONS;
     377        ?>
     378        <select name="<?php echo esc_attr( TNBU_Core::OPTION_KEY ); ?>[auto_backup_files_max_generations]">
     379            <?php for ( $i = 1; $i <= 10; $i++ ) : ?>
     380                <option value="<?php echo esc_attr( $i ); ?>" <?php selected( (int) $max, $i ); ?>><?php echo esc_html( $i ); ?></option>
     381            <?php endfor; ?>
     382        </select>
     383        <?php
     384    }
     385
     386    /**
     387     * メール通知設定フィールド
     388     */
     389    public static function field_auto_backup_email() {
     390        $opts    = TNBU_Core::get_options();
     391        $key     = esc_attr( TNBU_Core::OPTION_KEY );
     392        $email   = get_option( 'admin_email' );
     393        $success = ! empty( $opts['auto_backup_email_success'] );
     394        $failure = ! empty( $opts['auto_backup_email_failure'] );
     395        ?>
     396        <p class="description" style="margin-bottom: 8px;">
     397            <?php echo esc_html( sprintf(
     398                /* translators: %s: admin email address */
     399                __( 'Send notification to %s', 'tiny-backup' ),
     400                $email
     401            ) ); ?>
     402        </p>
     403        <label style="margin-right: 16px;">
     404            <input type="hidden" name="<?php echo $key; ?>[auto_backup_email_success]" value="0" />
     405            <input type="checkbox" name="<?php echo $key; ?>[auto_backup_email_success]" value="1" <?php checked( $success ); ?> />
     406            <?php echo esc_html__( 'On success', 'tiny-backup' ); ?>
     407        </label>
     408        <label>
     409            <input type="hidden" name="<?php echo $key; ?>[auto_backup_email_failure]" value="0" />
     410            <input type="checkbox" name="<?php echo $key; ?>[auto_backup_email_failure]" value="1" <?php checked( $failure ); ?> />
     411            <?php echo esc_html__( 'On failure', 'tiny-backup' ); ?>
     412        </label>
    186413        <?php
    187414    }
     
    222449
    223450            <form id="tnbu-settings-form" class="tnbu-settings-form" method="post" action="options.php">
     451                <h3><?php echo esc_html( __( 'Choose what to backup from your uploads folder.', 'tiny-backup' ) ); ?></h3>
     452                <?php self::field_files_selection(); ?>
     453
    224454                <?php settings_fields( 'tnbu_settings_group' ); ?>
    225455                <?php do_settings_sections( 'tnbu' ); ?>
    226                 <?php /* ファイル選択はセクションフィールド側で描画 */ ?>
    227456                <?php submit_button( __( 'Save Settings', 'tiny-backup' ) ); ?>
    228457            </form>
     458
    229459            <form id="tnbu-reset-form" class="tnbu-reset-form" method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
    230460                <input type="hidden" name="action" value="tnbu_reset_settings" />
     
    236466            <hr class="tnbu-divider" />
    237467           
    238             <h2><?php echo esc_html( __( 'Backup', 'tiny-backup' ) ); ?></h2>
    239             <p>
    240                 <button type="button" class="button button-primary" id="tnbu-backup-button"><?php echo esc_html( __( 'Run backup now', 'tiny-backup' ) ); ?></button>
     468            <h2><?php echo esc_html( __( 'Run backup now', 'tiny-backup' ) ); ?></h2>
     469            <p class="tnbu-backup-buttons">
     470                <button type="button" class="button button-primary tnbu-backup-trigger" data-backup-type="db"><?php echo esc_html( __( 'Database backup', 'tiny-backup' ) ); ?></button>
     471                <button type="button" class="button button-primary tnbu-backup-trigger" data-backup-type="files"><?php echo esc_html( __( 'Files backup', 'tiny-backup' ) ); ?></button>
    241472            </p>
    242473            <?php
     
    251482            </div>
    252483
    253 
     484            <hr class="tnbu-divider" />
    254485
    255486            <!-- バックアップファイル一覧 -->
    256487            <div class="tnbu-backup-list">
    257                 <h3><?php echo esc_html( _x( 'Backup files', 'section title', 'tiny-backup' ) ); ?></h3>
     488                <h2><?php echo esc_html( _x( 'Backup files', 'section title', 'tiny-backup' ) ); ?></h2>
    258489                <?php self::render_backup_list(); ?>
    259490            </div>
  • tiny-backup/trunk/includes/class-tnbu-ajax-handler.php

    r3397691 r3487170  
    2424        }
    2525
    26         // バックアップ実行前に設定を保存(変更された設定があれば保存する)
    27         if ( isset( $_POST['tnbu_backup_db'] ) || isset( $_POST['tnbu_backup_files'] ) ) {
    28             $current_opts = TNBU_Core::get_options();
    29             $updated_opts = $current_opts;
    30 
    31             // チェックボックスの状態を更新
    32             if ( isset( $_POST['tnbu_backup_db'] ) ) {
    33                 $updated_opts['backup_db'] = ( sanitize_text_field( wp_unslash( $_POST['tnbu_backup_db'] ) ) === '1' ) ? 1 : 0;
    34             }
    35             if ( isset( $_POST['tnbu_backup_files'] ) ) {
    36                 $updated_opts['backup_files'] = ( sanitize_text_field( wp_unslash( $_POST['tnbu_backup_files'] ) ) === '1' ) ? 1 : 0;
    37             }
    38 
    39             // 選択されたアイテムを更新(常に更新する、空でも)
    40         // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in array_map below
    41             $selected = isset( $_POST['tnbu_selected_items'] ) ? (array) wp_unslash( $_POST['tnbu_selected_items'] ) : array();
    42             $selected = array_map(
    43                 function ( $p ) {
    44                     return ltrim( (string) sanitize_text_field( $p ), '/' );
    45                 },
    46                 $selected
    47             );
    48             $selected = array_values(
    49                 array_filter(
    50                     array_unique( $selected ),
    51                     function ( $p ) {
    52                         return '' !== $p && false === strpos( $p, '..' ) && false === strpos( $p, "\0" );
    53                     }
    54                 )
    55             );
    56             // 空の配列でも保存する(選択を解除した場合のため)
    57             $updated_opts['selected_items'] = $selected;
    58 
    59             // 設定を保存
    60             update_option( TNBU_Core::OPTION_KEY, $updated_opts );
    61 
    62             /* translators: 1: DB backup enabled (0/1), 2: Files backup enabled (0/1) */
    63             TNBU_Progress_Manager::set_progress( sprintf( __( 'Settings saved: DB=%1$s, Files=%2$s', 'tiny-backup' ), $updated_opts['backup_db'], $updated_opts['backup_files'] ), false, null );
     26        // バックアップ種別を取得('db' or 'files')
     27        $backup_type = isset( $_POST['backup_type'] ) ? sanitize_text_field( wp_unslash( $_POST['backup_type'] ) ) : '';
     28        if ( ! in_array( $backup_type, array( 'db', 'files' ), true ) ) {
     29            wp_send_json_error( array( 'message' => 'invalid_backup_type' ), 400 );
    6430        }
    6531
    6632        TNBU_Progress_Manager::set_progress( __( 'Backup started', 'tiny-backup' ), false, null );
    67         // 設定に応じてDB/ファイルのバックアップを実行(キャッシュを強制リフレッシュ)
    68         $opts              = TNBU_Core::get_options( true );
    69         $last_result_path  = '';
    70         $db_result_path    = '';
    71         $files_result_path = '';
    72         $error             = null;
    73         $total_steps       = 0;
    74         $current_step      = 0;
    75 
    76         // 実行するバックアップの種類をカウント
    77         if ( ! empty( $opts['backup_db'] ) ) {
    78             ++$total_steps;
    79         }
    80         if ( ! empty( $opts['backup_files'] ) ) {
    81             ++$total_steps;
    82         }
    83 
    84         /* translators: 1: whether DB backup is enabled (0/1), 2: whether Files backup is enabled (0/1) */
    85         TNBU_Progress_Manager::set_progress( sprintf( __( 'Checking backup settings: DB=%1$s, Files=%2$s', 'tiny-backup' ), $opts['backup_db'], $opts['backup_files'] ), false, null );
    86 
    87         if ( ! empty( $opts['backup_db'] ) ) {
    88             ++$current_step;
     33
     34        $result_path = '';
     35        $error       = null;
     36
     37        if ( 'db' === $backup_type ) {
    8938            TNBU_Progress_Manager::set_progress( __( 'Starting database backup', 'tiny-backup' ), false, null );
    9039            $res = TNBU_Database_Backup::perform_backup();
     
    9443                TNBU_Progress_Manager::set_progress( sprintf( __( 'Database backup error: %s', 'tiny-backup' ), $res->get_error_message() ), false, null );
    9544            } else {
    96                 $db_result_path   = $res;
    97                 $last_result_path = $res;
     45                $result_path = $res;
    9846                TNBU_Progress_Manager::set_progress( __( 'Database backup completed', 'tiny-backup' ), false, null );
    9947            }
    10048        }
    10149
    102         if ( ! empty( $opts['backup_files'] ) ) {
    103             ++$current_step;
     50        if ( 'files' === $backup_type ) {
    10451            TNBU_Progress_Manager::set_progress( __( 'Starting files backup', 'tiny-backup' ), false, null );
    10552
    106             // ユーザーが選択した項目があればそれを優先、なければ自動スキャン
    107     // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in array_map below
    108             $selected = isset( $_POST['tnbu_selected_items'] ) ? (array) wp_unslash( $_POST['tnbu_selected_items'] ) : array();
    109             $selected = array_map(
    110                 function ( $p ) {
    111                     return ltrim( (string) sanitize_text_field( $p ), '/' );
    112                 },
    113                 $selected
    114             );
    115             $selected = array_values(
    116                 array_filter(
    117                     array_unique( $selected ),
    118                     function ( $p ) {
    119                         return '' !== $p && false === strpos( $p, '..' ) && false === strpos( $p, "\0" );
    120                     }
    121                 )
    122             );
    123             if ( empty( $selected ) && ! empty( $opts['selected_items'] ) && is_array( $opts['selected_items'] ) ) {
    124                 $selected = array_values(
    125                     array_filter(
    126                         array_map(
    127                             function ( $p ) {
    128                                 return ltrim( (string) $p, '/' );
    129                             },
    130                             $opts['selected_items']
     53            // POSTから選択アイテムを取得、なければ保存済み設定から取得
     54            $selected = self::sanitize_selected_items_from_post();
     55            if ( empty( $selected ) ) {
     56                $opts = TNBU_Core::get_options( true );
     57                if ( ! empty( $opts['selected_items'] ) && is_array( $opts['selected_items'] ) ) {
     58                    $selected = array_values(
     59                        array_filter(
     60                            array_map(
     61                                function ( $p ) {
     62                                    return ltrim( (string) $p, '/' );
     63                                },
     64                                $opts['selected_items']
     65                            )
    13166                        )
    132                     )
    133                 );
    134             }
    135 
    136             /* translators: %d: number of selected items */
    137             TNBU_Progress_Manager::set_progress( sprintf( __( 'Selected files check: %d items', 'tiny-backup' ), count( $selected ) ), false, null );
     67                    );
     68                }
     69            }
    13870
    13971            if ( empty( $selected ) ) {
    140                 $fres = new WP_Error( 'tnbu_files_none', __( 'No file targets selected for backup', 'tiny-backup' ) );
     72                $error = new WP_Error( 'tnbu_files_none', __( 'No file targets selected for backup', 'tiny-backup' ) );
     73                TNBU_Progress_Manager::set_progress( $error->get_error_message(), false, null );
    14174            } else {
    14275                $fres = TNBU_File_Backup::perform_files_backup_selected( $selected );
    143             }
    144             if ( is_wp_error( $fres ) ) {
    145                 $error = $error ? $error : $fres;
    146                 /* translators: %s: error message */
    147                 TNBU_Progress_Manager::set_progress( sprintf( __( 'Files backup error: %s', 'tiny-backup' ), $fres->get_error_message() ), false, null );
    148             } else {
    149                 $files_result_path = $fres;
    150                 $last_result_path  = $fres;
    151                 TNBU_Progress_Manager::set_progress( __( 'Files backup completed: ', 'tiny-backup' ), false, null );
    152             }
    153         }
    154         $result = $error ? $error : ( $last_result_path ? $last_result_path : '' );
    155         if ( is_wp_error( $result ) ) {
     76                if ( is_wp_error( $fres ) ) {
     77                    $error = $fres;
     78                    /* translators: %s: error message */
     79                    TNBU_Progress_Manager::set_progress( sprintf( __( 'Files backup error: %s', 'tiny-backup' ), $fres->get_error_message() ), false, null );
     80                } else {
     81                    $result_path = $fres;
     82                    TNBU_Progress_Manager::set_progress( __( 'Files backup completed', 'tiny-backup' ), false, null );
     83                }
     84            }
     85        }
     86
     87        if ( $error ) {
    15688            set_transient(
    15789                'tnbu_flash_' . get_current_user_id(),
     
    15991                    'type' => 'error',
    16092                    /* translators: %s: error message */
    161                     'text' => sprintf( __( 'Backup failed: %s', 'tiny-backup' ), $result->get_error_message() ),
     93                    'text' => sprintf( __( 'Backup failed: %s', 'tiny-backup' ), $error->get_error_message() ),
    16294                ),
    16395                60
    16496            );
    16597            /* translators: %s: error message */
    166             TNBU_Progress_Manager::set_progress( sprintf( __( 'Error: %s', 'tiny-backup' ), $result->get_error_message() ), true, null );
    167             wp_send_json_error( array( 'message' => $result->get_error_message() ) );
    168         }
    169 
    170         // バックアップ完了メッセージの作成
    171         $backup_files = array();
    172         if ( $db_result_path ) {
    173             $backup_files[] = basename( $db_result_path );
    174         }
    175         if ( $files_result_path ) {
    176             $backup_files[] = basename( $files_result_path );
    177         }
    178 
    179         $files_text = implode( ', ', $backup_files );
    180         /* translators: %s: backup file names */
     98            TNBU_Progress_Manager::set_progress( sprintf( __( 'Error: %s', 'tiny-backup' ), $error->get_error_message() ), true, null );
     99            wp_send_json_error( array( 'message' => $error->get_error_message() ) );
     100        }
     101
     102        /* translators: %s: backup file name */
    181103        set_transient(
    182104            'tnbu_flash_' . get_current_user_id(),
    183105            array(
    184106                'type' => 'success',
    185                 /* translators: %s: backup file names */
    186                 'text' => sprintf( __( 'Backup completed: %s', 'tiny-backup' ), $files_text ),
     107                /* translators: %s: backup file name */
     108                'text' => sprintf( __( 'Backup completed: %s', 'tiny-backup' ), basename( $result_path ) ),
    187109            ),
    188110            60
    189111        );
    190112        TNBU_Progress_Manager::set_progress( __( 'Completed', 'tiny-backup' ), true, null );
    191         wp_send_json_success( array( 'path' => $result ) );
     113        wp_send_json_success( array( 'path' => $result_path ) );
    192114    }
    193115
     
    214136            wp_send_json_error( array( 'message' => 'bad_nonce' ), 403 );
    215137        }
    216         $rel         = isset( $_POST['path'] ) ? sanitize_text_field( wp_unslash( $_POST['path'] ) ) : '';
    217         $rel         = ltrim( $rel, '/' );
    218         $base        = trailingslashit( WP_CONTENT_DIR );
    219         $target      = $base . $rel;
     138        $rel   = isset( $_POST['path'] ) ? sanitize_text_field( wp_unslash( $_POST['path'] ) ) : '';
     139        $rel   = ltrim( $rel, '/' );
     140        $base  = trailingslashit( WP_CONTENT_DIR . '/uploads' );
     141
     142        // 1階層のみ表示。pathが空以外の場合は許可しない
     143        if ( '' !== $rel ) {
     144            wp_send_json_error( array( 'message' => 'depth_limit' ), 400 );
     145        }
     146
     147        $target      = $base;
    220148        $real_base   = realpath( $base );
    221         $real_target = realpath( $target ) ? $target : $target; // 未存在でも操作しないのでOK
     149        $real_target = realpath( $target ) ? $target : $target;
    222150        if ( $real_base && $real_target && 0 !== strpos( wp_normalize_path( $real_target ), wp_normalize_path( $real_base ) ) ) {
    223151            wp_send_json_error( array( 'message' => 'invalid_path' ), 400 );
    224         }
    225         if ( ! is_dir( $target ) ) {
    226             $target = $base; // ルート
    227152        }
    228153        $items = array();
     
    246171                $full    = $target . DIRECTORY_SEPARATOR . $entry;
    247172                $type    = is_dir( $full ) ? 'dir' : 'file';
    248                 $relp    = ltrim( trim( $rel . '/' . $entry, '/' ) );
     173                $relp    = 'uploads/' . $entry;
    249174                $items[] = array(
    250175                    'name' => $entry,
     
    353278        header( 'Pragma: no-cache' );
    354279
    355         // WordPress Filesystem APIを使用してファイル送信
    356         if ( ! function_exists( 'WP_Filesystem' ) ) {
    357             require_once ABSPATH . 'wp-admin/includes/file.php';
    358         }
    359         WP_Filesystem();
    360         global $wp_filesystem;
    361 
    362         if ( ! $wp_filesystem->exists( $file_path ) ) {
    363             wp_die( esc_html__( 'File not found.', 'tiny-backup' ) );
    364         }
    365 
    366         $file_content = $wp_filesystem->get_contents( $file_path );
    367         if ( false === $file_content ) {
    368             wp_die( esc_html__( 'File read error.', 'tiny-backup' ) );
    369         }
    370 
    371         // チャンクごとに出力
    372         $chunk_size  = 1024 * 1024; // 1MB
    373         $file_length = strlen( $file_content );
    374         for ( $i = 0; $i < $file_length; $i += $chunk_size ) {
    375             // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Binary file download, escaping would corrupt the data
    376             echo substr( $file_content, $i, $chunk_size );
    377             flush();
    378         }
     280        // ストリーミングでファイル送信(メモリ節約)
     281        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Binary file streaming for download, WP_Filesystem::get_contents() would load entire file into memory
     282        readfile( $file_path );
    379283        exit;
    380284    }
    381285
     286
     287    /**
     288     * POSTデータから選択アイテムをサニタイズして取得する
     289     *
     290     * @return array サニタイズ済みの選択アイテム配列
     291     */
     292    private static function sanitize_selected_items_from_post() {
     293        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in array_map below
     294        $selected = isset( $_POST['tnbu_selected_items'] ) ? (array) wp_unslash( $_POST['tnbu_selected_items'] ) : array();
     295        $selected = array_map(
     296            function ( $p ) {
     297                return ltrim( (string) sanitize_text_field( $p ), '/' );
     298            },
     299            $selected
     300        );
     301        return array_values(
     302            array_filter(
     303                array_unique( $selected ),
     304                function ( $p ) {
     305                    return '' !== $p && false === strpos( $p, '..' ) && false === strpos( $p, "\0" );
     306                }
     307            )
     308        );
     309    }
    382310
    383311    /**
     
    393321        $files = isset( $_POST['files'] ) && is_array( $_POST['files'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['files'] ) ) : array();
    394322        $base  = trailingslashit( TNBU_Core::get_backup_dir() );
    395         $deleted = 0;
    396323        foreach ( $files as $filename ) {
    397324            if ( ! TNBU_Utilities::validate_backup_filename( $filename ) ) {
     
    399326            }
    400327            $path = $base . $filename;
    401             if ( is_file( $path ) && strpos( realpath( $path ), realpath( $base ) ) === 0 && wp_delete_file( $path ) ) {
    402                 ++$deleted;
     328            if ( is_file( $path ) && strpos( realpath( $path ), realpath( $base ) ) === 0 ) {
     329                wp_delete_file( $path );
    403330            }
    404331        }
  • tiny-backup/trunk/includes/class-tnbu-core.php

    r3397691 r3487170  
    7272     * @return array デフォルト設定の配列
    7373     */
     74    // 自動バックアップのファイル名テンプレート(-auto 付き)
     75    const DEFAULT_AUTO_DB_FILENAME_TEMPLATE    = '{db}-{date}-auto-db';
     76    const DEFAULT_AUTO_FILES_FILENAME_TEMPLATE = '{db}-{date}-auto-files';
     77
     78    // 自動バックアップの保持世代数デフォルト
     79    const DEFAULT_AUTO_MAX_GENERATIONS = 3;
     80
    7481    public static function defaults() {
    7582        return array(
    7683            // filename_template is fixed by code to DEFAULT_DB_FILENAME_TEMPLATE
    7784            'target_dir'     => self::DEFAULT_DIR_SUFFIX,
    78             'backup_db'      => 1,
    79             'backup_files'   => 1,
    8085            // files filename template is fixed by code to DEFAULT_FILES_FILENAME_TEMPLATE
    8186            'selected_items' => array( 'uploads' ),
     87
     88            // 自動バックアップ設定
     89            'auto_backup_db_schedule'       => '',   // '' | 'daily' | 'weekly' | 'monthly'
     90            'auto_backup_db_time'           => '03:00', // '00:00'〜'23:30'
     91            'auto_backup_db_day_of_week'    => '0',  // 0(日)〜6(土)
     92            'auto_backup_db_day'            => '1',  // 1〜28
     93            'auto_backup_files_schedule'    => '',
     94            'auto_backup_files_time'        => '03:00',
     95            'auto_backup_files_day_of_week' => '0',
     96            'auto_backup_files_day'         => '1',
     97            'auto_backup_db_max_generations'    => self::DEFAULT_AUTO_MAX_GENERATIONS,
     98            'auto_backup_files_max_generations' => self::DEFAULT_AUTO_MAX_GENERATIONS,
     99            'auto_backup_email_success'     => false,
     100            'auto_backup_email_failure'     => false,
    82101        );
    83102    }
     
    116135    }
    117136
    118     /**
    119      * 設定オプションのキャッシュをクリアする
    120      * 設定を更新した後に呼び出すことで、次回の get_options() で最新の値を取得できる
    121      *
    122      * @return void
    123      */
    124     public static function clear_options_cache() {
    125         // 次回の get_options() で強制的に再読み込みさせるため、引数で制御
    126         // このメソッドは互換性のために残しているが、get_options(true) を推奨
    127     }
    128137}
  • tiny-backup/trunk/includes/class-tnbu-database-backup.php

    r3397691 r3487170  
    1818     * @return string|WP_Error 成功時はファイルパス、失敗時はWP_Error
    1919     */
    20     public static function perform_backup() {
     20    /**
     21     * データベースのバックアップを実行する
     22     *
     23     * @param string|null $filename_template ファイル名テンプレート(null の場合はデフォルト)
     24     * @return string|WP_Error 成功時はファイルパス、失敗時はWP_Error
     25     */
     26    public static function perform_backup( $filename_template = null ) {
    2127        global $wpdb;
    2228        $abs_dir   = TNBU_Core::get_backup_dir();
     
    2632        }
    2733
    28         $base_filename = TNBU_Utilities::replace_template_vars( TNBU_Core::DEFAULT_DB_FILENAME_TEMPLATE );
     34        if ( null === $filename_template ) {
     35            $filename_template = TNBU_Core::DEFAULT_DB_FILENAME_TEMPLATE;
     36        }
     37        $base_filename = TNBU_Utilities::replace_template_vars( $filename_template );
    2938        // remove known extensions if user accidentally included them
    3039        $base_filename = preg_replace( '/\.(sql\.gz|zip)$/i', '', $base_filename );
     
    5463            if ( ! class_exists( 'ZipArchive' ) ) {
    5564                wp_delete_file( $sql_path );
    56                 return new WP_Error( 'sbwp_zip', __( 'ZIP extension is not available', 'tiny-backup' ) );
     65                return new WP_Error( 'tnbu_zip', __( 'ZIP extension is not available', 'tiny-backup' ) );
    5766            }
    5867            $zip_path = $base_path . $base_filename . '.sql.zip';
     
    6170            if ( true !== $zip->open( $tmp_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
    6271                wp_delete_file( $sql_path );
    63                 return new WP_Error( 'sbwp_zip_open', __( 'Failed to create ZIP file', 'tiny-backup' ) );
     72                return new WP_Error( 'tnbu_zip_open', __( 'Failed to create ZIP file', 'tiny-backup' ) );
    6473            }
    6574            $zip->addFile( $sql_path, basename( $sql_path ) );
     
    7079            if ( ! @rename( $tmp_path, $zip_path ) ) {
    7180                wp_delete_file( $tmp_path );
    72                 return new WP_Error( 'sbwp_zip_rename', __( 'Failed to finalize ZIP file', 'tiny-backup' ) );
     81                return new WP_Error( 'tnbu_zip_rename', __( 'Failed to finalize ZIP file', 'tiny-backup' ) );
    7382            }
    7483            TNBU_Progress_Manager::set_progress( __( 'ZIP compression completed', 'tiny-backup' ), false, null );
     
    9099        $handle = @fopen( $sql_path, 'wb' );
    91100        if ( ! $handle ) {
    92             return new WP_Error( 'sbwp_phpdump_open', __( 'Unable to write SQL file', 'tiny-backup' ) );
     101            return new WP_Error( 'tnbu_phpdump_open', __( 'Unable to write SQL file', 'tiny-backup' ) );
    93102        }
    94103        $site    = site_url();
     
    105114            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Direct file writing for SQL dump
    106115            fclose( $handle );
    107             return new WP_Error( 'sbwp_phpdump_tables', __( 'No tables found', 'tiny-backup' ) );
     116            return new WP_Error( 'tnbu_phpdump_tables', __( 'No tables found', 'tiny-backup' ) );
    108117        }
    109118
     
    190199        fclose( $handle );
    191200        if ( ! file_exists( $sql_path ) || filesize( $sql_path ) === 0 ) {
    192             return new WP_Error( 'sbwp_phpdump_empty', __( 'PHP dump failed (empty file)', 'tiny-backup' ) );
     201            return new WP_Error( 'tnbu_phpdump_empty', __( 'PHP dump failed (empty file)', 'tiny-backup' ) );
    193202        }
    194203        return true;
  • tiny-backup/trunk/includes/class-tnbu-file-backup.php

    r3397691 r3487170  
    1515     * 指定されたファイル・フォルダをZIP圧縮してバックアップ
    1616     */
    17     public static function perform_files_backup_selected( array $items ) {
     17    /**
     18     * 指定されたファイル・フォルダをZIP圧縮してバックアップ
     19     *
     20     * @param array       $items             バックアップ対象のアイテム
     21     * @param string|null $filename_template ファイル名テンプレート(null の場合はデフォルト)
     22     * @return string|WP_Error 成功時はファイルパス、失敗時はWP_Error
     23     */
     24    public static function perform_files_backup_selected( array $items, $filename_template = null ) {
    1825        $backup_dir = TNBU_Core::get_backup_dir();
    1926        $ensure     = TNBU_Utilities::ensure_directory( $backup_dir );
     
    2936            @set_time_limit( 0 ); }
    3037        if ( ! class_exists( 'ZipArchive' ) ) {
    31             return new WP_Error( 'sbwp_zip', __( 'ZIP extension is not available', 'tiny-backup' ) );
     38            return new WP_Error( 'tnbu_zip', __( 'ZIP extension is not available', 'tiny-backup' ) );
    3239        }
    3340        $base     = trailingslashit( WP_CONTENT_DIR );
    34         $zip_tmpl = TNBU_Core::DEFAULT_FILES_FILENAME_TEMPLATE;
     41        $zip_tmpl = null !== $filename_template ? $filename_template : TNBU_Core::DEFAULT_FILES_FILENAME_TEMPLATE;
    3542        $zip_base = TNBU_Utilities::replace_template_vars( strtr( $zip_tmpl, array( '{db}' => ( defined( 'DB_NAME' ) ? DB_NAME : 'wp' ) ) ) );
    3643        $zip_path = trailingslashit( $backup_dir ) . $zip_base . '.zip';
     
    3845        $zip      = new ZipArchive();
    3946        if ( true !== $zip->open( $tmp, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
    40             return new WP_Error( 'sbwp_zip_open', __( 'Failed to create ZIP file', 'tiny-backup' ) );
     47            return new WP_Error( 'tnbu_zip_open', __( 'Failed to create ZIP file', 'tiny-backup' ) );
    4148        }
    4249        $added_any  = false;
    4350        $items      = array_values( array_unique( $items ) );
    44         $compressed = TNBU_Utilities::optimize_targets_by_parent( $items, $base );
     51        $compressed = TNBU_Utilities::optimize_targets_by_parent( $items );
    4552        // ファイルをZIPに追加
    4653        TNBU_Progress_Manager::set_progress( __( 'Starting file scan', 'tiny-backup' ), false, null );
     
    6774        if ( ! $added_any ) {
    6875            wp_delete_file( $tmp );
    69             return new WP_Error( 'sbwp_files_empty', __( 'No files to add', 'tiny-backup' ) ); }
     76            return new WP_Error( 'tnbu_files_empty', __( 'No files to add', 'tiny-backup' ) ); }
    7077        // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename -- wp_move_file() doesn't exist
    7178        // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename -- wp_move_file() doesn't exist
    7279        if ( ! @rename( $tmp, $zip_path ) ) {
    7380            wp_delete_file( $tmp );
    74             return new WP_Error( 'sbwp_zip_finalize', __( 'Failed to finalize ZIP file', 'tiny-backup' ) ); }
     81            return new WP_Error( 'tnbu_zip_finalize', __( 'Failed to finalize ZIP file', 'tiny-backup' ) ); }
    7582
    7683        TNBU_Progress_Manager::set_progress( __( 'Files compression completed', 'tiny-backup' ), true, null );
     
    115122        $count    = 0;
    116123        $iterator = new RecursiveIteratorIterator(
    117             new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS ),
     124            new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS ),
    118125            RecursiveIteratorIterator::LEAVES_ONLY
    119126        );
     
    151158
    152159        // 親優先ロジックに基づく最適化:重複する子パスを除去
    153         $optimized_targets = TNBU_Utilities::optimize_targets_by_parent( $targets, $base_root );
     160        $optimized_targets = TNBU_Utilities::optimize_targets_by_parent( $targets );
    154161
    155162        foreach ( $optimized_targets as $rel ) {
     
    183190        $files    = array();
    184191        $iterator = new RecursiveIteratorIterator(
    185             new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS ),
     192            new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS ),
    186193            RecursiveIteratorIterator::LEAVES_ONLY // ファイルのみ(ディレクトリエントリは不要)
    187194        );
  • tiny-backup/trunk/includes/class-tnbu-utilities.php

    r3397691 r3487170  
    156156        if ( ! is_dir( $dir ) || ! wp_is_writable( $dir ) ) {
    157157            /* translators: %s: directory path */
    158             return new WP_Error( 'sbwp_dir', sprintf( __( 'Cannot write to backup directory: %s', 'tiny-backup' ), $dir ) );
     158            return new WP_Error( 'tnbu_dir', sprintf( __( 'Cannot write to backup directory: %s', 'tiny-backup' ), $dir ) );
    159159        }
    160160        return true;
  • tiny-backup/trunk/languages/tiny-backup-ja-tnbu-admin.json

    r3397190 r3487170  
    2323  }
    2424}
    25 
  • tiny-backup/trunk/languages/tiny-backup-ja.po

    r3397190 r3487170  
    33"Project-Id-Version: Tiny Backup 0.1.0\n"
    44"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/tiny-backup\n"
    5 "POT-Creation-Date: 2025-10-09 00:00+0000\n"
    6 "PO-Revision-Date: 2025-10-09 00:00+0000\n"
     5"POT-Creation-Date: 2026-03-20 00:00+0000\n"
     6"PO-Revision-Date: 2026-03-20 00:00+0000\n"
    77"Last-Translator: \n"
    88"Language-Team: Japanese\n"
     
    2121msgstr "最小限の設定でデータベースとファイルのバックアップを作成します。"
    2222
    23 #: includes/class-admin-interface.php:60
     23#: includes/class-tnbu-admin-interface.php
    2424msgid "Settings"
    2525msgstr "設定"
    2626
    27 #: includes/class-admin-interface.php:63
    28 msgid "Backup targets"
    29 msgstr "バックアップ対象"
    30 
    31 #: includes/class-admin-interface.php:68
    32 msgid "Select backup items"
    33 msgstr "バックアップ項目を選択"
    34 
    35 #: includes/class-admin-interface.php:68
    36 msgid "only under wp-content"
    37 msgstr "wp-content 配下のみ"
    38 
    39 #: includes/class-admin-interface.php:88
    40 msgid "Backup database"
    41 msgstr "データベースをバックアップ"
    42 
    43 #: includes/class-admin-interface.php:91 (context: checkbox label)
    44 msgctxt "checkbox label"
    45 msgid "Backup files"
    46 msgstr "ファイルをバックアップ"
    47 
    48 #: includes/class-admin-interface.php:151
     27#: includes/class-tnbu-admin-interface.php
     28msgid "Run backup now"
     29msgstr "今すぐバックアップ"
     30
     31#: includes/class-tnbu-cron.php
     32msgid "[%1$s] Tiny Backup: %2$s backup %3$s"
     33msgstr "[%1$s] Tiny Backup: %2$s バックアップ %3$s"
     34
     35#: includes/class-tnbu-cron.php
     36msgid "success"
     37msgstr "成功"
     38
     39#: includes/class-tnbu-cron.php
     40msgid "failed"
     41msgstr "失敗"
     42
     43#: includes/class-tnbu-cron.php
     44msgid "Site: %s"
     45msgstr "サイト: %s"
     46
     47#: includes/class-tnbu-cron.php
     48msgid "Backup type: %s"
     49msgstr "バックアップ種別: %s"
     50
     51#: includes/class-tnbu-cron.php
     52msgid "Status: %s"
     53msgstr "ステータス: %s"
     54
     55#: includes/class-tnbu-cron.php
     56msgid "Time: %s"
     57msgstr "実行日時: %s"
     58
     59
     60#: includes/class-tnbu-admin-interface.php
     61msgid "Choose what to backup from your uploads folder."
     62msgstr "uploads フォルダからバックアップするファイルを選択"
     63
     64#: includes/class-tnbu-admin-interface.php
     65msgid "Auto Backup"
     66msgstr "自動バックアップ"
     67
     68#: includes/class-tnbu-admin-interface.php
     69msgid "Database"
     70msgstr "データベース"
     71
     72#: includes/class-tnbu-admin-interface.php
     73msgctxt "auto backup setting"
     74msgid "Files"
     75msgstr "ファイル"
     76
     77#: includes/class-tnbu-admin-interface.php
     78msgid "Max generations"
     79msgstr "保持世代数"
     80
     81#: includes/class-tnbu-admin-interface.php
     82msgid "Email notification"
     83msgstr "メール通知"
     84
     85#: includes/class-tnbu-admin-interface.php
     86msgid "On success"
     87msgstr "成功時"
     88
     89#: includes/class-tnbu-admin-interface.php
     90msgid "On failure"
     91msgstr "失敗時"
     92
     93#: includes/class-tnbu-admin-interface.php
     94msgid "Automatic backups use the same targets as manual backups. Actual execution timing may vary depending on site traffic."
     95msgstr "自動バックアップは手動バックアップと同じ対象を使用します。実際の実行タイミングはサイトへのアクセス状況により前後します。"
     96
     97#: includes/class-tnbu-admin-interface.php
     98msgid "Disabled"
     99msgstr "無効"
     100
     101#: includes/class-tnbu-admin-interface.php
     102msgid "Daily"
     103msgstr "毎日"
     104
     105#: includes/class-tnbu-admin-interface.php
     106msgid "Weekly"
     107msgstr "毎週"
     108
     109#: includes/class-tnbu-admin-interface.php
     110msgid "Monthly"
     111msgstr "毎月"
     112
     113#: includes/class-tnbu-admin-interface.php
     114msgid "Every Minute (debug)"
     115msgstr "毎分 (デバッグ)"
     116
     117#: includes/class-tnbu-admin-interface.php
     118msgid "Sunday"
     119msgstr "日曜日"
     120
     121#: includes/class-tnbu-admin-interface.php
     122msgid "Monday"
     123msgstr "月曜日"
     124
     125#: includes/class-tnbu-admin-interface.php
     126msgid "Tuesday"
     127msgstr "火曜日"
     128
     129#: includes/class-tnbu-admin-interface.php
     130msgid "Wednesday"
     131msgstr "水曜日"
     132
     133#: includes/class-tnbu-admin-interface.php
     134msgid "Thursday"
     135msgstr "木曜日"
     136
     137#: includes/class-tnbu-admin-interface.php
     138msgid "Friday"
     139msgstr "金曜日"
     140
     141#: includes/class-tnbu-admin-interface.php
     142msgid "Saturday"
     143msgstr "土曜日"
     144
     145#: includes/class-tnbu-admin-interface.php
     146msgid "day"
     147msgstr "日"
     148
     149#. translators: %s: admin email address
     150#: includes/class-tnbu-admin-interface.php
     151msgid "Send notification to %s"
     152msgstr "%s に通知を送信する"
     153
     154#: includes/class-tnbu-admin-interface.php
    49155msgid "Network error occurred"
    50156msgstr "ネットワークエラーが発生しました"
    51157
    52 #: includes/class-admin-interface.php:152
     158#: includes/class-tnbu-admin-interface.php
    53159msgid "Backup failed"
    54160msgstr "バックアップに失敗しました"
    55161
    56 #: includes/class-admin-interface.php:153
     162#: includes/class-tnbu-admin-interface.php
    57163msgid "Backing up"
    58164msgstr "バックアップ中"
    59165
    60 #: includes/class-admin-interface.php:154
     166#: includes/class-tnbu-admin-interface.php
    61167msgid "Starting backup"
    62168msgstr "バックアップを開始しています"
    63169
    64 #: includes/class-admin-interface.php:176
     170#: includes/class-tnbu-admin-interface.php
    65171msgid "Enter a relative path under wp-content (e.g. "
    66172msgstr "wp-content 配下の相対パスを入力してください (例: "
    67173
    68 #: includes/class-admin-interface.php:198
     174#: includes/class-tnbu-admin-interface.php
    69175msgid "Save Settings"
    70176msgstr "設定を保存"
    71177
    72 #: includes/class-admin-interface.php:203
     178#: includes/class-tnbu-admin-interface.php
    73179msgid "Reset settings to defaults? Backup files will not be deleted."
    74180msgstr "設定を初期値に戻しますか?バックアップファイルは削除されません。"
    75181
    76 #: includes/class-admin-interface.php:203
     182#: includes/class-tnbu-admin-interface.php
    77183msgid "Reset settings"
    78184msgstr "設定をリセット"
    79185
    80 #: includes/class-admin-interface.php:209
     186#: includes/class-tnbu-admin-interface.php
    81187msgid "Backup"
    82188msgstr "バックアップ"
    83189
    84 #: includes/class-admin-interface.php:211
    85 msgid "Run backup now"
    86 msgstr "今すぐバックアップを実行"
    87 
    88 #: includes/class-admin-interface.php:217
     190#: includes/class-tnbu-admin-interface.php
     191msgid "Database backup"
     192msgstr "データベースバックアップ"
     193
     194#: includes/class-tnbu-admin-interface.php
     195msgid "Files backup"
     196msgstr "ファイルバックアップ"
     197
     198#: includes/class-tnbu-admin-interface.php
    89199msgid "Backup destination: "
    90200msgstr "バックアップ保存先: "
    91201
    92 #: includes/class-admin-interface.php:221
     202#: includes/class-tnbu-admin-interface.php
    93203msgid "Preparing"
    94204msgstr "準備中"
    95205
    96 #: includes/class-admin-interface.php:229 (context: section title)
     206#: includes/class-tnbu-admin-interface.php
    97207msgctxt "section title"
    98208msgid "Backup files"
    99209msgstr "バックアップファイル"
    100210
    101 #: includes/class-admin-interface.php:247
    102 #: includes/class-admin-interface.php:255
     211#: includes/class-tnbu-admin-interface.php
    103212msgid "No backup files found"
    104213msgstr "バックアップファイルが見つかりません"
    105214
    106 #: includes/class-admin-interface.php:271
     215#: includes/class-tnbu-admin-interface.php
    107216msgid "Delete the selected files. Are you sure?"
    108217msgstr "選択したファイルを削除します。よろしいですか?"
    109218
    110 #: includes/class-admin-interface.php:277
     219#: includes/class-tnbu-admin-interface.php
    111220msgid "File name"
    112221msgstr "ファイル名"
    113222
    114 #: includes/class-admin-interface.php:278
     223#: includes/class-tnbu-admin-interface.php
    115224msgid "Modified"
    116225msgstr "更新日時"
    117226
    118 #: includes/class-admin-interface.php:279
     227#: includes/class-tnbu-admin-interface.php
    119228msgid "Size"
    120229msgstr "サイズ"
    121230
    122 #: includes/class-admin-interface.php:280
    123 #: includes/class-admin-interface.php:298
     231#: includes/class-tnbu-admin-interface.php
    124232msgid "Download"
    125233msgstr "ダウンロード"
    126234
    127 #: includes/class-admin-interface.php:304
     235#: includes/class-tnbu-admin-interface.php
    128236msgid "Delete selected files"
    129237msgstr "選択したファイルを削除"
    130238
    131 #: includes/class-admin-interface.php:334
     239#: includes/class-tnbu-admin-interface.php
    132240msgid "Settings saved."
    133241msgstr "設定を保存しました。"
    134242
    135 #: includes/class-admin-interface.php:336
     243#: includes/class-tnbu-admin-interface.php
    136244msgid "Dismiss this notice"
    137245msgstr "この通知を非表示"
    138246
    139 #: includes/class-ajax-handler.php:55
    140 msgid "Settings saved: DB=%1$s, Files=%2$s"
    141 msgstr "設定を保存しました: DB=%1$s, ファイル=%2$s"
    142 
    143 #: includes/class-ajax-handler.php:58
     247#: includes/class-tnbu-ajax-handler.php
    144248msgid "Backup started"
    145249msgstr "バックアップを開始しました"
    146250
    147 #: includes/class-ajax-handler.php:73
    148 msgid "Checking backup settings: DB=%1$s, Files=%2$s"
    149 msgstr "バックアップ設定を確認中: DB=%1$s, ファイル=%2$s"
    150 
    151 #: includes/class-ajax-handler.php:76
     251#: includes/class-tnbu-ajax-handler.php
    152252msgid "Starting database backup"
    153253msgstr "データベースのバックアップを開始"
    154254
    155 #: includes/class-ajax-handler.php:81
     255#. translators: %s: error message
     256#: includes/class-tnbu-ajax-handler.php
    156257msgid "Database backup error: %s"
    157258msgstr "データベースのバックアップエラー: %s"
    158259
    159 #: includes/class-ajax-handler.php:85
     260#: includes/class-tnbu-ajax-handler.php
    160261msgid "Database backup completed"
    161262msgstr "データベースのバックアップが完了しました"
    162263
    163 #: includes/class-ajax-handler.php:91
     264#: includes/class-tnbu-ajax-handler.php
    164265msgid "Starting files backup"
    165266msgstr "ファイルのバックアップを開始"
    166267
    167 #: includes/class-ajax-handler.php:107
    168 msgid "Selected files check: %d items"
    169 msgstr "選択ファイルの確認: %d 件"
    170 
    171 #: includes/class-ajax-handler.php:110
     268#: includes/class-tnbu-ajax-handler.php
    172269msgid "No file targets selected for backup"
    173270msgstr "バックアップ対象ファイルが選択されていません"
    174271
    175 #: includes/class-ajax-handler.php:117
     272#. translators: %s: error message
     273#: includes/class-tnbu-ajax-handler.php
    176274msgid "Files backup error: %s"
    177275msgstr "ファイルバックアップのエラー: %s"
    178276
    179 #: includes/class-ajax-handler.php:121
    180 msgid "Files backup completed: "
    181 msgstr "ファイルバックアップ完了: "
    182 
    183 #: includes/class-ajax-handler.php:127
     277#: includes/class-tnbu-ajax-handler.php
     278msgid "Files backup completed"
     279msgstr "ファイルバックアップが完了しました"
     280
     281#. translators: %s: error message
     282#: includes/class-tnbu-ajax-handler.php
    184283msgid "Backup failed: %s"
    185284msgstr "バックアップに失敗しました: %s"
    186285
    187 #: includes/class-ajax-handler.php:129
     286#. translators: %s: error message
     287#: includes/class-tnbu-ajax-handler.php
    188288msgid "Error: %s"
    189289msgstr "エラー: %s"
    190290
    191 #: includes/class-ajax-handler.php:146
    192 msgid "Backup completed: %1$s%2$s"
    193 msgstr "バックアップが完了しました: %1$s%2$s"
    194 
    195 #: includes/class-ajax-handler.php:147
     291#. translators: %s: backup file name
     292#: includes/class-tnbu-ajax-handler.php
     293msgid "Backup completed: %s"
     294msgstr "バックアップが完了しました: %s"
     295
     296#: includes/class-tnbu-ajax-handler.php
    196297msgid "Completed"
    197298msgstr "完了"
    198299
    199 #: includes/class-ajax-handler.php:221
     300#: includes/class-tnbu-ajax-handler.php
    200301msgid "You do not have permission."
    201302msgstr "権限がありません。"
    202303
    203 #: includes/class-ajax-handler.php:224
     304#: includes/class-tnbu-ajax-handler.php
    204305msgid "Security check failed."
    205306msgstr "セキュリティチェックに失敗しました。"
    206307
    207 #: includes/class-ajax-handler.php:228
     308#: includes/class-tnbu-ajax-handler.php
    208309msgid "Settings have been reset."
    209310msgstr "設定を初期化しました。"
    210311
    211 #: includes/class-ajax-handler.php:247
     312#: includes/class-tnbu-ajax-handler.php
    212313msgid "No file name specified."
    213314msgstr "ファイル名が指定されていません。"
    214315
    215 #: includes/class-ajax-handler.php:252
     316#: includes/class-tnbu-ajax-handler.php
    216317msgid "Invalid file name."
    217318msgstr "ファイル名が不正です。"
    218319
    219 #: includes/class-ajax-handler.php:265
     320#: includes/class-tnbu-ajax-handler.php
    220321msgid "File not found."
    221322msgstr "ファイルが見つかりません。"
    222323
    223 #: includes/class-ajax-handler.php:272
     324#: includes/class-tnbu-ajax-handler.php
    224325msgid "Invalid file path."
    225326msgstr "ファイルパスが不正です。"
    226327
    227 #: includes/class-database-backup.php:39
     328#: includes/class-tnbu-ajax-handler.php
     329msgid "Selected files have been deleted."
     330msgstr "選択したファイルを削除しました。"
     331
     332#: includes/class-tnbu-database-backup.php
    228333msgid "Starting database dump"
    229334msgstr "データベースのダンプを開始"
    230335
    231 #: includes/class-database-backup.php:42
    232 msgid "mysqldump failed, switching to PHP dump"
    233 msgstr "mysqldump に失敗したため、PHP ダンプに切り替えます"
    234 
    235 #: includes/class-database-backup.php:49
    236 msgid "mysqldump completed"
    237 msgstr "mysqldump が完了しました"
    238 
    239 #: includes/class-database-backup.php:59
     336#: includes/class-tnbu-database-backup.php
     337msgid "Database dump completed"
     338msgstr "データベースのダンプが完了しました"
     339
     340#: includes/class-tnbu-database-backup.php
    240341msgid "Starting ZIP compression"
    241342msgstr "ZIP 圧縮を開始"
    242343
    243 #: includes/class-database-backup.php:65
     344#: includes/class-tnbu-database-backup.php
    244345msgid "ZIP extension is not available"
    245346msgstr "ZIP 拡張が利用できません"
    246347
    247 #: includes/class-database-backup.php:73
     348#: includes/class-tnbu-database-backup.php
    248349msgid "Failed to create ZIP file"
    249350msgstr "ZIP ファイルの作成に失敗しました"
    250351
    251 #: includes/class-database-backup.php:82
     352#: includes/class-tnbu-database-backup.php
    252353msgid "Failed to finalize ZIP file"
    253354msgstr "ZIP ファイルの確定に失敗しました"
    254355
    255 #: includes/class-database-backup.php:84
     356#: includes/class-tnbu-database-backup.php
    256357msgid "ZIP compression completed"
    257358msgstr "ZIP 圧縮が完了しました"
    258359
    259 #: includes/class-database-backup.php:102
     360#: includes/class-tnbu-database-backup.php
    260361msgid "Unable to write SQL file"
    261362msgstr "SQL ファイルを書き込めません"
    262363
    263 #: includes/class-database-backup.php:117
     364#: includes/class-tnbu-database-backup.php
    264365msgid "No tables found"
    265366msgstr "テーブルが見つかりません"
    266367
    267 #: includes/class-database-backup.php:126
     368#. translators: 1: table name, 2: current index, 3: total tables
     369#: includes/class-tnbu-database-backup.php
    268370msgid "Processing table: %1$s (%2$d/%3$d)"
    269371msgstr "テーブルを処理中: %1$s (%2$d/%3$d)"
    270372
    271 #: includes/class-database-backup.php:185
     373#. translators: 1: table name, 2: processed rows, 3: total rows
     374#: includes/class-tnbu-database-backup.php
    272375msgid "Processing table: %1$s (%2$d/%3$d rows)"
    273376msgstr "テーブルを処理中: %1$s (%2$d/%3$d 行)"
    274377
    275 #: includes/class-database-backup.php:193
     378#: includes/class-tnbu-database-backup.php
    276379msgid "PHP dump failed (empty file)"
    277380msgstr "PHP ダンプに失敗しました(空のファイル)"
    278381
    279 #: includes/class-database-backup.php:207
     382#: includes/class-tnbu-database-backup.php
    280383msgid "Command execution is not allowed on this server"
    281384msgstr "このサーバーではコマンド実行が許可されていません"
    282385
    283 #: includes/class-database-backup.php:247
     386#. translators: %s: error output from mysqldump
     387#: includes/class-tnbu-database-backup.php
    284388msgid "mysqldump failed: %s"
    285389msgstr "mysqldump に失敗しました: %s"
    286390
    287  
    288 
    289 #: includes/class-file-backup.php:44
    290 msgid "Trying CLI compression"
    291 msgstr "CLI 圧縮を試行中"
    292 
    293 #: includes/class-file-backup.php:47
    294 msgid "Files compression completed (CLI)"
    295 msgstr "ファイル圧縮が完了しました(CLI)"
    296 
    297 #: includes/class-file-backup.php:50
    298 msgid "CLI compression failed, falling back to PHP"
    299 msgstr "CLI 圧縮に失敗したため PHP にフォールバックします"
    300 
    301 #: includes/class-file-backup.php:52
     391#: includes/class-tnbu-file-backup.php
    302392msgid "Starting file scan"
    303393msgstr "ファイルのスキャンを開始"
    304394
    305 #: includes/class-file-backup.php:56
     395#. translators: %d: number of files
     396#: includes/class-tnbu-file-backup.php
    306397msgid "File scan completed: %d files"
    307398msgstr "ファイルスキャンが完了: %d 件"
    308399
    309 #: includes/class-file-backup.php:63
     400#. translators: 1: processed file count, 2: total files
     401#: includes/class-tnbu-file-backup.php
    310402msgid "Compressing files: %1$d/%2$d"
    311403msgstr "ファイルを圧縮中: %1$d/%2$d"
    312404
    313 #: includes/class-file-backup.php:68
     405#: includes/class-tnbu-file-backup.php
    314406msgid "No files to add"
    315407msgstr "追加するファイルがありません"
    316408
    317  
    318 
    319 #: includes/class-file-backup.php:72
     409#: includes/class-tnbu-file-backup.php
    320410msgid "Files compression completed"
    321411msgstr "ファイル圧縮が完了しました"
    322412
    323 #: includes/class-progress-manager.php:32
     413#: includes/class-tnbu-cron.php
     414msgid "Once Monthly"
     415msgstr "月1回"
     416
     417#: includes/class-tnbu-progress-manager.php
    324418msgid "Idle"
    325419msgstr "待機中"
    326420
    327 #: includes/class-utilities.php:156
     421#. translators: %s: directory path
     422#: includes/class-tnbu-utilities.php
    328423msgid "Cannot write to backup directory: %s"
    329424msgstr "バックアップディレクトリに書き込めません: %s"
    330 
    331 #: includes/class-ajax-handler.php:345
    332 msgid "Selected files have been deleted."
    333 msgstr "選択したファイルを削除しました。"
    334 
    335 
  • tiny-backup/trunk/languages/tiny-backup.pot

    r3397190 r3487170  
    4848msgstr ""
    4949
    50 #: includes/class-admin-interface.php:68
    51 msgid "Select backup items"
    52 msgstr ""
    53 
    54 #: includes/class-admin-interface.php:68
    55 msgid "only under wp-content"
     50#: includes/class-admin-interface.php:199
     51msgid "Choose what to backup from your uploads folder."
    5652msgstr ""
    5753
  • tiny-backup/trunk/readme.txt

    r3397693 r3487170  
    33Tags: backup, database, files, zip, admin
    44Requires at least: 6.0
    5 Tested up to: 6.8
     5Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 1.1.1
     7Stable tag: 1.3.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1010
    11 Simple and minimal backup plugin for WordPress. Create database and files backups with one click.
     11Simple and minimal backup plugin for WordPress. Create database and files backups manually or automatically on a schedule.
    1212
    1313== Description ==
     
    1616You can create and download the bare minimum backup with just one click, stress-free.
    1717
    18 - Database backup (SQL inside ZIP)
    19 - Files backup (select folders under `wp-content`)
    20 - Clear progress indicator and logs
     18**Manual Backup**
     19
     20- Database backup (SQL inside ZIP) with a single click
     21- Files backup from your uploads folder — select specific subfolders as needed
     22- Clear progress indicator during backup
     23
     24**Auto Backup (WP-Cron)**
     25
     26- Set separate schedules for database and files backups (daily / weekly / monthly)
     27- Configure time, day of week, or day of month for each schedule
     28- Manage retention: set max generations for database and files independently
     29- Email notifications on success and/or failure (sent to the admin email address)
     30
     31**General**
     32
    2133- No external services; everything runs on your server
     34- Backup files are stored in `wp-content/tiny-backup`
    2235
    23 This plugin is ideal for small to medium sites that need quick on-demand backups.
     36This plugin is ideal for small to medium sites that need simple, low-overhead backups.
    2437
    2538== Installation ==
     
    28412. Activate the plugin through the 'Plugins' screen in WordPress.
    29423. Go to Tools → Tiny Backup.
    30 4. Choose backup targets and click "Run backup now".
     434. Click "Database Backup" or "Files Backup" to run a manual backup, or configure Auto Backup settings.
    3144
    3245== Frequently Asked Questions ==
     
    3447= Where are backups saved? =
    3548
    36 By default, backups are saved to `wp-content/tiny-backup`.
     49Backups are saved to `wp-content/tiny-backup` by default.
    3750
    3851= Can I change the destination directory? =
     
    4255= Are scheduled backups supported? =
    4356
    44 Not currently supported. This initial version only supports manual backups. Scheduled backups may be added in a future version.
     57Yes. As of version 1.3.0, you can configure automatic backups for database and files independently using WP-Cron. Options include daily, weekly, and monthly schedules.
     58
     59= Does scheduled backup run at the exact time I set? =
     60
     61WP-Cron is triggered by site visits, so the actual execution time may differ slightly from the configured time on low-traffic sites. The backup will run on the next page load after the scheduled time.
     62
     63= What folders can I select for file backup? =
     64
     65You can select the entire uploads folder or individual subfolders directly under it. Subfolders of subfolders are not selectable.
    4566
    4667== Screenshots ==
    4768
    48 1. Settings screen - Choose what to backup: database and/or files, and select specific folders/plugins/themes under wp-content
    49 2. Backup management screen - View all backup files with creation date and size, download or delete backups
     691. Settings screen — Select backup items from the uploads folder
     702. Auto Backup settings — Configure separate schedules, retention, and email notifications for database and files
     713. Backup screen — Run manual backups and manage backup files
    5072
    5173== Changelog ==
    5274
    53 = 1.1.1 =
     75= 1.3.0 =
     76
     77- Added auto backup feature using WP-Cron (daily / weekly / monthly)
     78- Database and files can be scheduled independently
     79- Configurable retention (max generations) per backup type
     80- Email notifications on success and/or failure
     81- Split manual backup into separate "Database Backup" and "Files Backup" buttons
     82- Limited file backup scope to uploads folder (direct subfolders selectable)
     83- Introduced wp-scripts build pipeline for admin assets
     84
     85= 1.2.0 =
     86
     87- Internal improvements and refactoring.
     88
     89= 1.1.1 =
    5490
    5591- Bug fix.
     
    5995- Added an action link to the plugin list for quick access to the settings page.
    6096
    61 = 1.0.0 = 
     97= 1.0.0 =
    6298
    6399- Initial Release.
     
    65101== Upgrade Notice ==
    66102
    67 = 1.0.0 =
     103= 1.3.0 =
    68104
    69 - Initial release.
    70 
    71 
     105Auto backup, separate DB/files schedules, email notifications, and a redesigned backup UI are now available.
  • tiny-backup/trunk/tiny-backup.php

    r3397693 r3487170  
    33Plugin Name: Tiny Backup
    44Description: Create database and files backups with minimal setup.
    5 Version: 1.1.1
    6 Author: Takashi Fujisaki 
     5Version: 1.3.0
     6Author: Takashi Fujisaki
    77Plugin URI: https://wordpress.org/plugins/tiny-backup/
    88Author URI: https://yuiami.jp
     
    1111Requires at least: 6.0
    1212Requires PHP: 7.4
    13 Tested up to: 6.8
     13Tested up to: 6.9
    1414License: GPLv2 or later
    1515License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    2828require_once plugin_dir_path( __FILE__ ) . 'includes/class-tnbu-ajax-handler.php';
    2929require_once plugin_dir_path( __FILE__ ) . 'includes/class-tnbu-core.php';
     30require_once plugin_dir_path( __FILE__ ) . 'includes/class-tnbu-cron.php';
    3031
    3132
    3233// プラグイン初期化
    3334TNBU_Core::init();
     35TNBU_Cron::init();
     36
     37// カスタム cron スケジュール(monthly)を登録
     38add_filter( 'cron_schedules', array( 'TNBU_Cron', 'add_cron_schedules' ) );
    3439
    3540// 有効化フック(WPの定石としてメインファイルで登録)
    3641register_activation_hook( __FILE__, array( 'TNBU_Core', 'on_activate' ) );
     42
     43// 無効化フック(cronスケジュール解除)
     44register_deactivation_hook( __FILE__, array( 'TNBU_Cron', 'unschedule_all' ) );
  • tiny-backup/trunk/uninstall.php

    r3397693 r3487170  
    1414}
    1515
    16 // 定数を利用するためコアクラスを読み込み
     16// 定数を利用するためクラスを読み込み
    1717require_once plugin_dir_path(__FILE__) . 'includes/class-tnbu-core.php';
     18require_once plugin_dir_path(__FILE__) . 'includes/class-tnbu-cron.php';
     19
     20// cronスケジュールを解除
     21TNBU_Cron::unschedule_all();
    1822
    1923// プラグインの設定を削除
Note: See TracChangeset for help on using the changeset viewer.