Changeset 3397669
- Timestamp:
- 11/18/2025 12:10:58 AM (4 months ago)
- Location:
- tiny-backup
- Files:
-
- 22 added
- 8 edited
-
tags/1.1.0 (added)
-
tags/1.1.0/assets (added)
-
tags/1.1.0/assets/css (added)
-
tags/1.1.0/assets/css/admin.css (added)
-
tags/1.1.0/assets/js (added)
-
tags/1.1.0/assets/js/admin.js (added)
-
tags/1.1.0/includes (added)
-
tags/1.1.0/includes/class-tnbu-admin-interface.php (added)
-
tags/1.1.0/includes/class-tnbu-ajax-handler.php (added)
-
tags/1.1.0/includes/class-tnbu-core.php (added)
-
tags/1.1.0/includes/class-tnbu-database-backup.php (added)
-
tags/1.1.0/includes/class-tnbu-file-backup.php (added)
-
tags/1.1.0/includes/class-tnbu-progress-manager.php (added)
-
tags/1.1.0/includes/class-tnbu-utilities.php (added)
-
tags/1.1.0/languages (added)
-
tags/1.1.0/languages/tiny-backup-ja-tnbu-admin.json (added)
-
tags/1.1.0/languages/tiny-backup-ja.mo (added)
-
tags/1.1.0/languages/tiny-backup-ja.po (added)
-
tags/1.1.0/languages/tiny-backup.pot (added)
-
tags/1.1.0/readme.txt (added)
-
tags/1.1.0/tiny-backup.php (added)
-
tags/1.1.0/uninstall.php (added)
-
trunk/includes/class-tnbu-admin-interface.php (modified) (5 diffs)
-
trunk/includes/class-tnbu-ajax-handler.php (modified) (5 diffs)
-
trunk/includes/class-tnbu-core.php (modified) (3 diffs)
-
trunk/includes/class-tnbu-database-backup.php (modified) (5 diffs)
-
trunk/includes/class-tnbu-file-backup.php (modified) (4 diffs)
-
trunk/includes/class-tnbu-utilities.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/tiny-backup.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
tiny-backup/trunk/includes/class-tnbu-admin-interface.php
r3397190 r3397669 39 39 ); 40 40 } 41 // インラインスクリプトでJavaScript変数を設定42 $tnbu_files_nonce = wp_create_nonce( 'tnbu_files_ops' );43 $tnbu_ajax_nonce = wp_create_nonce( 'tnbu_start_backup' );44 $opts = TNBU_Core::get_options();45 $preset = isset( $opts['selected_items'] ) && is_array( $opts['selected_items'] ) ? array_values( array_map( 'strval', $opts['selected_items'] ) ) : array();46 $inline_script = sprintf(47 'window.tnbuFilesNonce = %s;' . "\n" .48 'window.tnbuAjaxNonce = %s;' . "\n" .49 'window.ajaxurl = %s;' . "\n" .50 'window.tnbuOptionKey = %s;' . "\n" .51 'window.tnbuPresetItems = %s;',52 wp_json_encode( $tnbu_files_nonce ),53 wp_json_encode( $tnbu_ajax_nonce ),54 wp_json_encode( admin_url( 'admin-ajax.php' ) ),55 wp_json_encode( TNBU_Core::OPTION_KEY ),56 wp_json_encode( $preset )57 );58 wp_add_inline_script( 'tnbu-admin', $inline_script, 'before' );59 41 } 60 42 } … … 162 144 */ 163 145 public static function field_files_selection() { 146 $tnbu_files_nonce = wp_create_nonce( 'tnbu_files_ops' ); 147 $tnbu_ajax_nonce = wp_create_nonce( 'tnbu_start_backup' ); 164 148 ?> 165 149 <div id="tnbu-files-ui" class="tnbu-file-related"> … … 168 152 </div> 169 153 </div> 154 155 <script> 156 // JavaScript変数をグローバルに設定 157 window.tnbuFilesNonce = '<?php echo esc_js( $tnbu_files_nonce ); ?>'; 158 window.tnbuAjaxNonce = '<?php echo esc_js( $tnbu_ajax_nonce ); ?>'; 159 window.ajaxurl = '<?php echo esc_js( admin_url( 'admin-ajax.php' ) ); ?>'; 160 window.tnbuOptionKey = '<?php echo esc_js( TNBU_Core::OPTION_KEY ); ?>'; 161 window.tnbuPresetItems = 162 <?php 163 $opts = TNBU_Core::get_options(); 164 $preset = isset( $opts['selected_items'] ) && is_array( $opts['selected_items'] ) ? array_values( array_map( 'strval', $opts['selected_items'] ) ) : array(); 165 echo wp_json_encode( $preset ); 166 ?> 167 ; 168 </script> 170 169 <?php 171 170 } … … 225 224 </p> 226 225 <?php 227 $save_dir = TNBU_Core::get_backup_dir(); 226 $opts = wp_parse_args( get_option( TNBU_Core::OPTION_KEY, array() ), TNBU_Core::defaults() ); 227 $save_dir = trailingslashit( WP_CONTENT_DIR ) . ltrim( $opts['target_dir'], '/' ); 228 228 ?> 229 229 <p class="description"><?php echo esc_html( __( 'Backup destination: ', 'tiny-backup' ) ); ?><code><?php echo esc_html( $save_dir ); ?></code></p> … … 253 253 */ 254 254 public static function render_backup_list() { 255 $backup_dir = TNBU_Core::get_backup_dir(); 255 $opts = wp_parse_args( get_option( TNBU_Core::OPTION_KEY, array() ), TNBU_Core::defaults() ); 256 $backup_dir = trailingslashit( WP_CONTENT_DIR ) . ltrim( $opts['target_dir'], '/' ); 256 257 257 258 if ( ! is_dir( $backup_dir ) ) { -
tiny-backup/trunk/includes/class-tnbu-ajax-handler.php
r3397190 r3397669 177 177 } 178 178 179 $files_text = implode( ', ', $backup_files ); 180 /* translators: %s: backup file names */ 179 $dump_method = get_transient( 'tnbu_last_dump_method' ); 180 $method_text = $dump_method ? " ({$dump_method})" : ''; 181 $files_text = implode( ', ', $backup_files ); 182 /* translators: 1: backup file names, 2: dump method suffix e.g. (mysqldump) */ 181 183 set_transient( 182 184 'tnbu_flash_' . get_current_user_id(), 183 185 array( 184 186 'type' => 'success', 185 /* translators: %s: backup file names*/186 'text' => sprintf( __( 'Backup completed: % s', 'tiny-backup' ), $files_text ),187 /* translators: 1: backup file names, 2: dump method suffix e.g. (mysqldump) */ 188 'text' => sprintf( __( 'Backup completed: %1$s%2$s', 'tiny-backup' ), $files_text, $method_text ), 187 189 ), 188 190 60 … … 306 308 } 307 309 308 // nonce検証(入力処理の前に実行)309 if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['tnbu_download_nonce'] ?? '' ) ), 'tnbu_download_backup' ) ) {310 wp_die( esc_html__( 'Security check failed.', 'tiny-backup' ) );311 }312 313 310 $filename = sanitize_text_field( wp_unslash( $_GET['file'] ?? '' ) ); 314 311 if ( empty( $filename ) ) { … … 321 318 } 322 319 323 $backup_dir = TNBU_Core::get_backup_dir(); 324 $file_path = trailingslashit( $backup_dir ) . $filename; 320 // nonce検証 321 if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['tnbu_download_nonce'] ?? '' ) ), 'tnbu_download_backup' ) ) { 322 wp_die( esc_html__( 'Security check failed.', 'tiny-backup' ) ); 323 } 324 325 $opts = wp_parse_args( get_option( TNBU_Core::OPTION_KEY, array() ), TNBU_Core::defaults() ); 326 $file_path = trailingslashit( trailingslashit( WP_CONTENT_DIR ) . ltrim( $opts['target_dir'], '/' ) ) . $filename; 325 327 326 328 // ファイルの存在確認 … … 331 333 // ファイルがバックアップディレクトリ内にあることを確認(ディレクトリトラバーサル攻撃対策) 332 334 $real_file_path = realpath( $file_path ); 333 $real_backup_dir = realpath( $backup_dir);335 $real_backup_dir = realpath( trailingslashit( WP_CONTENT_DIR ) . ltrim( $opts['target_dir'], '/' ) ); 334 336 if ( strpos( $real_file_path, $real_backup_dir ) !== 0 ) { 335 337 wp_die( esc_html__( 'Invalid file path.', 'tiny-backup' ) ); … … 391 393 wp_die( esc_html__( 'Security check failed.', 'tiny-backup' ) ); 392 394 } 393 $files = isset( $_POST['files'] ) && is_array( $_POST['files'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['files'] ) ) : array(); 394 $base = trailingslashit( TNBU_Core::get_backup_dir() ); 395 $files = isset( $_POST['files'] ) && is_array( $_POST['files'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['files'] ) ) : array(); 396 $opts = wp_parse_args( get_option( TNBU_Core::OPTION_KEY, array() ), TNBU_Core::defaults() ); 397 $base = trailingslashit( trailingslashit( WP_CONTENT_DIR ) . ltrim( $opts['target_dir'], '/' ) ); 395 398 $deleted = 0; 396 399 foreach ( $files as $filename ) { -
tiny-backup/trunk/includes/class-tnbu-core.php
r3397190 r3397669 19 19 const DEFAULT_DB_FILENAME_TEMPLATE = '{db}-{date}-db'; 20 20 const DEFAULT_FILES_FILENAME_TEMPLATE = '{db}-{date}-files'; 21 const DEFAULT_DIR_SUFFIX = 'tiny-backup-datas'; // uploadsにこのサブフォルダを作成21 const DEFAULT_DIR_SUFFIX = 'tiny-backup'; // wp-content直下にこのサブフォルダを作成 22 22 23 23 // 圧縮・I/O のチューニング値(フィルタで上書き可能) … … 32 32 public static function get_zip_level() { 33 33 return (int) apply_filters( 'tnbu_zip_level', self::ZIP_LEVEL ); 34 }35 36 /**37 * バックアップディレクトリのパスを取得する38 * wp_upload_dir()を使用してアップロードディレクトリに保存する39 *40 * @return string バックアップディレクトリの絶対パス41 */42 public static function get_backup_dir() {43 $upload_dir = wp_upload_dir();44 $backup_dir = trailingslashit( $upload_dir['basedir'] ) . self::DEFAULT_DIR_SUFFIX;45 return $backup_dir;46 34 } 47 35 … … 96 84 update_option( self::OPTION_KEY, $opts ); 97 85 // Ensure directory exists 98 $abs_dir = self::get_backup_dir(); 99 wp_mkdir_p( $abs_dir ); 86 if ( ! empty( $opts['target_dir'] ) ) { 87 $abs_dir = trailingslashit( WP_CONTENT_DIR ) . ltrim( $opts['target_dir'], '/' ); 88 wp_mkdir_p( $abs_dir ); 89 } 100 90 } 101 91 -
tiny-backup/trunk/includes/class-tnbu-database-backup.php
r3397190 r3397669 14 14 /** 15 15 * データベースのバックアップを実行する 16 * PHPによるダンプを実行し、指定された圧縮形式で保存する16 * mysqldumpまたはPHPによるダンプを実行し、指定された圧縮形式で保存する 17 17 * 18 18 * @return string|WP_Error 成功時はファイルパス、失敗時はWP_Error … … 20 20 public static function perform_backup() { 21 21 global $wpdb; 22 $abs_dir = TNBU_Core::get_backup_dir(); 22 $opts = wp_parse_args( get_option( TNBU_Core::OPTION_KEY, array() ), TNBU_Core::defaults() ); 23 $abs_dir = trailingslashit( WP_CONTENT_DIR ) . ltrim( $opts['target_dir'], '/' ); 23 24 $dir_check = TNBU_Utilities::ensure_directory( $abs_dir ); 24 25 if ( is_wp_error( $dir_check ) ) { … … 32 33 $sql_path = $base_path . $base_filename . '.sql'; 33 34 34 //export SQL via PHP dumper 35 // 使用したダンプ方法を記録 36 $dump_method = ''; 37 38 // 1) export SQL via mysqldump if available, fallback to PHP dumper 35 39 TNBU_Progress_Manager::set_progress( __( 'Starting database dump', 'tiny-backup' ), false, null ); 36 $php_dump = self::dump_database_with_php( $sql_path ); 37 if ( is_wp_error( $php_dump ) ) { 38 return $php_dump; 39 } 40 TNBU_Progress_Manager::set_progress( __( 'Database dump completed', 'tiny-backup' ), false, null ); 40 $dump_result = self::dump_database_with_mysqldump( $sql_path ); 41 if ( is_wp_error( $dump_result ) ) { 42 TNBU_Progress_Manager::set_progress( __( 'mysqldump failed, switching to PHP dump', 'tiny-backup' ), false, null ); 43 $php_dump = self::dump_database_with_php( $sql_path ); 44 if ( is_wp_error( $php_dump ) ) { 45 return $dump_result; // mysqldumpのエラーを返す(詳細がわかりやすい) 46 } 47 $dump_method = 'PHPdump'; 48 } else { 49 TNBU_Progress_Manager::set_progress( __( 'mysqldump completed', 'tiny-backup' ), false, null ); 50 $dump_method = 'mysqldump'; 51 } 52 53 // ダンプ方法を一時的に保存(リダイレクト後に表示するため) 54 set_transient( 'tnbu_last_dump_method', $dump_method, 300 ); // 5分間保存 41 55 42 56 $final_path = $sql_path; … … 56 70 return new WP_Error( 'sbwp_zip', __( 'ZIP extension is not available', 'tiny-backup' ) ); 57 71 } 58 $zip_path = $base_path . $base_filename . '. sql.zip';72 $zip_path = $base_path . $base_filename . '.zip'; 59 73 $tmp_path = $zip_path . '.part'; 60 74 $zip = new ZipArchive(); … … 195 209 } 196 210 211 /** 212 * mysqldumpコマンドを使用してデータベースをダンプする 213 * 214 * @param string $sql_path 出力先SQLファイルパス 215 * @return true|WP_Error 成功時はtrue、失敗時はWP_Error 216 */ 217 protected static function dump_database_with_mysqldump( $sql_path ) { 218 // Validate exec availability 219 if ( ! function_exists( 'escapeshellarg' ) || ! function_exists( 'exec' ) ) { 220 return new WP_Error( 'sbwp_exec', __( 'Command execution is not allowed on this server', 'tiny-backup' ) ); 221 } 222 // Build mysqldump command 223 $db_host = defined( 'DB_HOST' ) ? DB_HOST : 'localhost'; 224 $db_name = defined( 'DB_NAME' ) ? DB_NAME : ''; 225 $db_user = defined( 'DB_USER' ) ? DB_USER : ''; 226 $db_pass = defined( 'DB_PASSWORD' ) ? DB_PASSWORD : ''; 227 228 $host = $db_host; 229 $port = ''; 230 if ( strpos( $db_host, ':' ) !== false ) { 231 list($host_part, $port_part) = explode( ':', $db_host, 2 ); 232 $host = $host_part; 233 $port = $port_part; 234 } 235 236 $cmd = 'mysqldump'; 237 $parts = array(); 238 $parts[] = $cmd; 239 $parts[] = '-h' . escapeshellarg( $host ); 240 if ( '' !== $port ) { 241 $parts[] = '-P' . escapeshellarg( $port ); 242 } 243 $parts[] = '-u' . escapeshellarg( $db_user ); 244 if ( '' !== $db_pass ) { 245 $parts[] = '-p' . escapeshellarg( $db_pass ); 246 } 247 $parts[] = '--single-transaction'; 248 $parts[] = '--quick'; 249 $parts[] = '--hex-blob'; 250 $maxpkt = apply_filters( 'tnbu_dump_max_allowed_packet', TNBU_Core::DUMP_MAX_ALLOWED_PACKET ); 251 if ( is_string( $maxpkt ) && '' !== $maxpkt ) { 252 $parts[] = '--max-allowed-packet=' . escapeshellarg( $maxpkt ); 253 } 254 $parts[] = escapeshellarg( $db_name ); 255 256 // 標準エラー出力を一時ファイルにリダイレクト(SQLファイルに混入させないため) 257 $stderr_file = tempnam( sys_get_temp_dir(), 'tnbu_mysqldump_err_' ); 258 $command = implode( ' ', $parts ) . ' > ' . escapeshellarg( $sql_path ) . ' 2>' . escapeshellarg( $stderr_file ); 259 260 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- mysqldump command execution requires error suppression 261 @exec( $command, $output, $code ); 262 263 // エラー出力を読み取り 264 $stderr = ''; 265 if ( file_exists( $stderr_file ) ) { 266 267 $stderr = file_get_contents( $stderr_file ); 268 // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink -- Cleanup temporary file 269 @unlink( $stderr_file ); 270 } 271 272 if ( 0 !== (int) $code || ! file_exists( $sql_path ) || 0 === filesize( $sql_path ) ) { 273 $error_msg = ''; 274 if ( ! empty( $stderr ) ) { 275 $error_msg = $stderr; 276 } elseif ( ! empty( $output ) ) { 277 $error_msg = implode( "\n", (array) $output ); 278 } else { 279 $error_msg = __( 'Unknown error', 'tiny-backup' ); 280 } 281 /* translators: %s: error output from mysqldump */ 282 return new WP_Error( 'sbwp_dump', sprintf( __( 'mysqldump failed: %s', 'tiny-backup' ), $error_msg ) ); 283 } 284 return true; 285 } 197 286 } -
tiny-backup/trunk/includes/class-tnbu-file-backup.php
r3397190 r3397669 16 16 */ 17 17 public static function perform_files_backup_selected( array $items ) { 18 $backup_dir = TNBU_Core::get_backup_dir(); 18 $opts = TNBU_Core::get_options(); 19 $backup_dir = trailingslashit( WP_CONTENT_DIR ) . ltrim( $opts['target_dir'], '/' ); 19 20 $ensure = TNBU_Utilities::ensure_directory( $backup_dir ); 20 21 if ( is_wp_error( $ensure ) ) { … … 43 44 $items = array_values( array_unique( $items ) ); 44 45 $compressed = TNBU_Utilities::optimize_targets_by_parent( $items, $base ); 46 // CLI zip が使える場合は高速ルートを先に試す(成功時はここで完了) 47 TNBU_Progress_Manager::set_progress( __( 'Trying CLI compression', 'tiny-backup' ), false, null ); 48 $cli_ok = self::try_zip_with_cli( $compressed, $zip_path ); 49 if ( true === $cli_ok ) { 50 TNBU_Progress_Manager::set_progress( __( 'Files compression completed (CLI)', 'tiny-backup' ), true, null ); 51 return $zip_path; 52 } 53 TNBU_Progress_Manager::set_progress( __( 'CLI compression failed, falling back to PHP', 'tiny-backup' ), false, null ); 45 54 // ファイルをZIPに追加 46 55 TNBU_Progress_Manager::set_progress( __( 'Starting file scan', 'tiny-backup' ), false, null ); … … 84 93 $count = 0; 85 94 $real_base = realpath( $base_root ); 86 $real_backup_dir = realpath( TNBU_Core::get_backup_dir() );95 $real_backup_dir = realpath( trailingslashit( WP_CONTENT_DIR ) . ltrim( TNBU_Core::get_options()['target_dir'], '/' ) ); 87 96 88 97 foreach ( $targets as $rel ) { … … 139 148 } 140 149 return $count; 150 } 151 152 /** 153 * 可能ならzipコマンドで高速にZIPを作成(fallbackはZipArchive) 154 * 選択された targets(wp-content 相対)をまとめてZIPする。 155 */ 156 private static function try_zip_with_cli( array $targets, $dest_zip_path ) { 157 if ( ! function_exists( 'exec' ) ) { 158 return false; } 159 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- CLI zip command availability check requires error suppression 160 @exec( 'command -v zip 2>/dev/null', $out, $code ); 161 if ( 0 !== $code || empty( $out ) ) { 162 return false; } 163 $zip_bin = trim( (string) $out[0] ); 164 $dir = WP_CONTENT_DIR; 165 $rel_targets = array(); 166 // 親優先最適化を適用 167 $optimized_targets = TNBU_Utilities::optimize_targets_by_parent( $targets, $dir ); 168 169 foreach ( $optimized_targets as $rel ) { 170 $rel = ltrim( (string) $rel, '/' ); 171 if ( '' === $rel ) { 172 continue; 173 } 174 $full_path = $dir . '/' . $rel; 175 if ( TNBU_Utilities::should_exclude_file( $full_path, $dir ) ) { 176 continue; 177 } 178 $rel_targets[] = escapeshellarg( $rel ); 179 } 180 if ( empty( $rel_targets ) ) { 181 return false; } 182 $dest_tmp = $dest_zip_path . '.part'; 183 $parent = dirname( $dest_tmp ); 184 if ( ! is_dir( $parent ) ) { 185 wp_mkdir_p( $parent ); } 186 // zip -r -9 -q <destTmp> <targets...> (-x で除外) 187 $exclude = TNBU_Utilities::get_exclude_patterns(); 188 $exclude[] = trim( str_replace( WP_CONTENT_DIR . '/', '', trailingslashit( $parent ) ), '/' ) . '/*'; 189 $exclude_args = ''; 190 foreach ( $exclude as $ex ) { 191 $exclude_args .= ' -x ' . escapeshellarg( $ex ); 192 } 193 $cmd = 'cd ' . escapeshellarg( $dir ) . ' && ' . escapeshellarg( $zip_bin ) . ' -r -9 -q ' . escapeshellarg( $dest_tmp ) . ' ' . implode( ' ', $rel_targets ) . $exclude_args; 194 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- CLI zip command execution requires error suppression 195 @exec( $cmd . ' 2>&1', $o2, $c2 ); 196 if ( 0 === (int) $c2 && file_exists( $dest_tmp ) && filesize( $dest_tmp ) > 0 ) { 197 // phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename -- wp_move_file() doesn't exist 198 if ( @rename( $dest_tmp, $dest_zip_path ) ) { 199 return true; } 200 wp_delete_file( $dest_tmp ); 201 } 202 return false; 141 203 } 142 204 -
tiny-backup/trunk/includes/class-tnbu-utilities.php
r3397190 r3397669 35 35 36 36 // バックアップディレクトリ除外(自己再帰防止強化) 37 $backup_dir = TNBU_Core::get_backup_dir(); 37 $opts = TNBU_Core::get_options(); 38 $backup_dir = trailingslashit( WP_CONTENT_DIR ) . ltrim( $opts['target_dir'], '/' ); 38 39 $real_backup_dir = realpath( $backup_dir ); 39 40 $real_file_path = realpath( $file_path ); -
tiny-backup/trunk/readme.txt
r3397190 r3397669 1 1 === Tiny Backup === 2 Contributors: ejointjp2 Contributors: Takashi Fujisaki 3 3 Tags: backup, database, files, zip, admin 4 4 Requires at least: 6.0 5 5 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 1.0.07 Stable tag: 0.1.1 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 46 46 == Screenshots == 47 47 48 1. Settings screen - Choose what to backup: database and/or files, and select specific folders/plugins/themes under wp-content49 2. Backup management screen - View all backup files with creation date and size, download or delete backups48 1. Backup screen and progress indicator 49 2. Backup files list with download and delete options 50 50 51 51 == Changelog == 52 52 53 = 1.0.0 =54 * Initial Release.53 = 0.1.0 = 54 * Initial release. 55 55 56 56 == Upgrade Notice == 57 57 58 = 1.0.0 =58 = 0.1.0 = 59 59 Initial release. 60 60 -
tiny-backup/trunk/tiny-backup.php
r3397190 r3397669 3 3 Plugin Name: Tiny Backup 4 4 Description: Create database and files backups with minimal setup. 5 Version: 1.0.06 Author: Takashi Fujisaki 5 Version: 0.1.1 6 Author: Takashi Fujisaki 7 7 Plugin URI: https://wordpress.org/plugins/tiny-backup/ 8 8 Author URI: https://yuiami.jp
Note: See TracChangeset
for help on using the changeset viewer.