├── .distignore ├── .github └── workflows │ ├── asset-update.yaml │ └── deploy.yml ├── .wordpress-org ├── banner-772x250.png ├── blueprints │ └── blueprint.json ├── icon-128x128.png ├── screenshot-1.png └── screenshot-2.png ├── LICENSE ├── README.md ├── kodo-commands.php ├── qiniu-kodo-wordpress.php ├── readme.txt ├── sdk └── composer.json └── uninstall.php /.distignore: -------------------------------------------------------------------------------- 1 | *.github 2 | *.gitignore 3 | *.gitattributes 4 | *.yml 5 | *examples 6 | *tests 7 | /.git 8 | /.distignore 9 | /.wordpress-org 10 | /sdk/vendor/qiniu/php-sdk/phpunit.xml.dist 11 | /sdk/vendor/qiniu/php-sdk/test-env.sh 12 | /sdk/composer.lock 13 | -------------------------------------------------------------------------------- /.github/workflows/asset-update.yaml: -------------------------------------------------------------------------------- 1 | name: Plugin asset/readme update 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - master 7 | jobs: 8 | update: 9 | uses: sy-records/.github/.github/workflows/wordpress-plugin-asset-update.yaml@main 10 | with: 11 | skip_assets: false 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to WordPress.org 2 | on: 3 | pull_request: 4 | release: 5 | types: [ published ] 6 | jobs: 7 | deploy: 8 | uses: sy-records/.github/.github/workflows/wordpress-plugin-deploy.yaml@main 9 | with: 10 | build: true 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.wordpress-org/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sy-records/qiniu-kodo-wordpress/7ff9ae767d1d0deebafa734d0f7bfeeae46ddbcf/.wordpress-org/banner-772x250.png -------------------------------------------------------------------------------- /.wordpress-org/blueprints/blueprint.json: -------------------------------------------------------------------------------- 1 | { 2 | "landingPage": "/wp-admin/admin.php?page=kodo-qiniu%2Fqiniu-kodo-wordpress.php", 3 | "preferredVersions": { 4 | "php": "latest", 5 | "wp": "latest" 6 | }, 7 | "phpExtensionBundles": [ 8 | "kitchen-sink" 9 | ], 10 | "steps": [ 11 | { 12 | "step": "login", 13 | "username": "admin", 14 | "password": "password" 15 | }, 16 | { 17 | "step": "installPlugin", 18 | "pluginZipFile": { 19 | "resource": "wordpress.org/plugins", 20 | "slug": "kodo-qiniu" 21 | }, 22 | "options": { 23 | "activate": true 24 | } 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.wordpress-org/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sy-records/qiniu-kodo-wordpress/7ff9ae767d1d0deebafa734d0f7bfeeae46ddbcf/.wordpress-org/icon-128x128.png -------------------------------------------------------------------------------- /.wordpress-org/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sy-records/qiniu-kodo-wordpress/7ff9ae767d1d0deebafa734d0f7bfeeae46ddbcf/.wordpress-org/screenshot-1.png -------------------------------------------------------------------------------- /.wordpress-org/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sy-records/qiniu-kodo-wordpress/7ff9ae767d1d0deebafa734d0f7bfeeae46ddbcf/.wordpress-org/screenshot-2.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KODO Qiniu 2 | 3 | 使用七牛云海量存储系统 KODO 作为附件存储空间的 WordPress 插件 4 | 5 | [![Latest Version](https://img.shields.io/github/release/sy-records/qiniu-kodo-wordpress.svg)](https://github.com/sy-records/qiniu-kodo-wordpress/releases) 6 | [![License](https://img.shields.io/github/license/sy-records/qiniu-kodo-wordpress?color=red)](LICENSE) 7 | [![website](https://img.shields.io/badge/website-qq52o.me-blue)](https://qq52o.me) 8 | [![contributors](https://img.shields.io/github/contributors/sy-records/qiniu-kodo-wordpress?color=blue)](https://github.com/sy-records/qiniu-kodo-wordpress/graphs/contributors) 9 | [![plugin stars](https://img.shields.io/wordpress/plugin/stars/kodo-qiniu)](https://wordpress.org/plugins/kodo-qiniu/) 10 | [![plugin installs](https://img.shields.io/wordpress/plugin/installs/kodo-qiniu)](https://wordpress.org/plugins/kodo-qiniu/) 11 | [![Deploy to WordPress.org](https://github.com/sy-records/qiniu-kodo-wordpress/actions/workflows/deploy.yml/badge.svg)](https://github.com/sy-records/qiniu-kodo-wordpress/actions/workflows/deploy.yml) 12 | 13 | > 使用 WordPress Playground 在线预览 [KODO Qiniu](https://wordpress.org/plugins/kodo-qiniu/?preview=1) 插件 14 | 15 | ## 插件特色 16 | 17 | - [x] 可配置是否上传缩略图和是否保留本地备份 18 | - [x] 本地删除可同步删除七牛云海量存储系统 KODO 中的文件 19 | - [x] 支持七牛云海量存储系统 KODO 绑定的个性域名 20 | - [x] 支持替换数据库中旧的资源链接地址 21 | - [x] 支持七牛云海量存储系统 KODO 完整地域使用 22 | - [x] 支持同步历史附件到七牛云海量存储系统 KODO 23 | - [x] 支持七牛云图片样式 24 | - [x] 支持七牛云原图保护 25 | - [x] 支持媒体库编辑 26 | - [x] 支持上传文件自动重命名 27 | - [x] 支持图片裁剪编辑等操作后的上传 28 | - [x] 支持多站点 29 | - [x] 支持使用 wp-cli 命令上传/删除文件 30 | 31 | ## 安装 32 | 33 | ### 后台安装(推荐使用) 34 | 35 | WordPress 后台安装插件页面搜索`KODO Qiniu`,点击安装 36 | 37 | ### 下载源码 38 | 39 | 从 GitHub 下载源码,通过 WordPress 后台上传安装,或者直接将源码上传到 WordPress 插件目录`wp-content/plugins`,然后在后台启用 40 | 41 | GitHub 下载节点:[https://github.com/sy-records/qiniu-kodo-wordpress/releases/latest](https://github.com/sy-records/qiniu-kodo-wordpress/releases/latest) 42 | 43 | ## 修改配置 44 | 45 | - 方法一:在 WordPress 插件管理页面有设置按钮,进行设置 46 | - 方法二:在 WordPress 后台管理左侧导航栏`设置`下`七牛云 KODO`,点击进入设置页面 47 | 48 | ## 插件截图 49 | 50 | ![设置页面](.wordpress-org/screenshot-1.png) 51 | 52 | ![数据库替换](.wordpress-org/screenshot-2.png) 53 | -------------------------------------------------------------------------------- /kodo-commands.php: -------------------------------------------------------------------------------- 1 | 15 | * : 要同步的文件夹 16 | * 17 | * ## EXAMPLES 18 | * 19 | * wp kodo upload wp-content/uploads 20 | * 21 | * @when after_wp_load 22 | */ 23 | public function upload($args, $assoc_args) 24 | { 25 | [$path] = $args; 26 | $dir = ABSPATH . $path; 27 | if (!is_dir($dir)) { 28 | WP_CLI::error("Directory not found: [{$dir}]"); 29 | } 30 | 31 | WP_CLI::line("Uploading files from [{$dir}] to KODO..."); 32 | 33 | $files = kodo_read_dir_queue(ABSPATH, $path); 34 | if (empty($files)) { 35 | WP_CLI::success('No files to upload.'); 36 | return; 37 | } 38 | 39 | foreach ($files as $file) { 40 | $status = kodo_file_upload($file['key'], $file['filepath']); 41 | if ($status) { 42 | WP_CLI::line("Uploaded: {$file['key']}"); 43 | } else { 44 | WP_CLI::line("Failed: {$file['key']}"); 45 | } 46 | } 47 | 48 | $total = count($files); 49 | WP_CLI::success("Uploaded {$total} files."); 50 | } 51 | 52 | /** 53 | * 同步文件到 KODO 54 | * 55 | * ## OPTIONS 56 | * 57 | * 58 | * : 要同步的文件 59 | * 60 | * [--delete] 61 | * : 如果设置,上传后会删除本地文件 62 | * [--key=] 63 | * : 指定上传到 KODO 的 key,默认和文件路径一致 64 | * 65 | * ## EXAMPLES 66 | * 67 | * wp kodo upload-file wp-content/uploads/2021/01/1.jpg 68 | * wp kodo upload-file wp-content/uploads/2021/01/1.jpg --delete 69 | * wp kodo upload-file wp-content/uploads/2021/01/1.jpg --key=2021/01/1.jpg 70 | * 71 | * @when after_wp_load 72 | * @subcommand upload-file 73 | */ 74 | public function upload_file($args, $assoc_args) 75 | { 76 | [$path] = $args; 77 | $file = ABSPATH . $path; 78 | if (!is_file($file)) { 79 | WP_CLI::error("File not found: {$file}"); 80 | } 81 | 82 | $delete = false; 83 | if (isset($assoc_args['delete'])) { 84 | $delete = true; 85 | } 86 | 87 | $key = isset($assoc_args['key']) ? $assoc_args['key'] : $path; 88 | 89 | WP_CLI::line("Uploading file [{$file}] to KODO with key [$key]..."); 90 | 91 | $status = kodo_file_upload("/{$key}", $file, $delete); 92 | if ($status) { 93 | WP_CLI::success("Uploaded: {$path}"); 94 | } else { 95 | WP_CLI::error("Failed: {$path}"); 96 | } 97 | } 98 | 99 | /** 100 | * 删除 KODO 中的文件 101 | * 102 | * ## OPTIONS 103 | * 104 | * 105 | * : 需要删除 KODO 中的文件 key 106 | * 107 | * ## EXAMPLES 108 | * 109 | * wp kodo delete-file 2021/01/1.jpg 110 | * 111 | * @when after_wp_load 112 | * @subcommand delete-file 113 | */ 114 | public function delete_file($args, $assoc_args) 115 | { 116 | [$key] = $args; 117 | WP_CLI::line("Deleting file [{$key}] from KODO..."); 118 | 119 | kodo_delete_file($key); 120 | } 121 | } 122 | 123 | WP_CLI::add_command('kodo', 'KODO_CLI_Commands', ['shortdesc' => 'Commands used to operate KODO.']); 124 | -------------------------------------------------------------------------------- /qiniu-kodo-wordpress.php: -------------------------------------------------------------------------------- 1 | '', 39 | 'accessKey' => '', 40 | 'secretKey' => '', 41 | 'nothumb' => 'false', // 是否上传缩略图 42 | 'nolocalsaving' => 'false', // 是否保留本地备份 43 | 'upload_url_path' => '', // URL前缀 44 | 'image_style' => '', 45 | 'origin_protect' => 'false', // 原图保护 46 | 'update_file_name' => 'false', // 是否重命名文件名 47 | ]; 48 | } 49 | function kodo_set_options() 50 | { 51 | add_option('kodo_options', kodo_get_default_options(), '', 'yes'); 52 | } 53 | 54 | function kodo_get_auth() 55 | { 56 | $kodo_options = get_option('kodo_options', kodo_get_default_options()); 57 | $accessKey = esc_attr($kodo_options['accessKey']); 58 | $secretKey = esc_attr($kodo_options['secretKey']); 59 | // 构建鉴权对象 60 | return new Auth($accessKey, $secretKey); 61 | } 62 | 63 | function kodo_get_auth_token() 64 | { 65 | // 生成上传 Token 66 | return kodo_get_auth()->uploadToken(kodo_get_bucket_name()); 67 | } 68 | 69 | function kodo_get_bucket_name() 70 | { 71 | $kodo_options = get_option('kodo_options', kodo_get_default_options()); 72 | return esc_attr($kodo_options['bucket']); 73 | } 74 | 75 | /** 76 | * @param string $object 77 | * @param string $file 78 | * @param bool $no_local_file 79 | * @return bool 80 | */ 81 | function kodo_file_upload($object, $file, $no_local_file = false) 82 | { 83 | //如果文件不存在,直接返回false 84 | if (!@file_exists($file)) { 85 | return false; 86 | } 87 | 88 | // Fix fread(): Length parameter must be greater than 0 89 | $filesize = @filesize($file); 90 | if ($filesize === 0 || $filesize === false) { 91 | return false; 92 | } 93 | 94 | $token = kodo_get_auth_token(); 95 | // 要上传文件的本地路径 96 | $filePath = $file; 97 | // 上传到七牛后保存的文件名 98 | $key = ltrim($object, '/'); 99 | // 初始化 UploadManager 对象并进行文件的上传。 100 | $uploadMgr = new UploadManager(); 101 | // 调用 UploadManager 的 putFile 方法进行文件的上传。 102 | $uploadMgr->putFile($token, $key, $filePath); 103 | [$_, $err] = $uploadMgr->putFile($token, $key, $filePath); 104 | if ($err !== null) { 105 | error_log($err->message()); 106 | return false; 107 | } 108 | 109 | if ($no_local_file) { 110 | kodo_delete_local_file($file); 111 | } 112 | 113 | return true; 114 | } 115 | 116 | /** 117 | * 是否需要删除本地文件 118 | * 119 | * @return bool 120 | */ 121 | function kodo_is_delete_local_file() 122 | { 123 | $kodo_options = get_option('kodo_options', kodo_get_default_options()); 124 | return esc_attr($kodo_options['nolocalsaving']) == 'true'; 125 | } 126 | 127 | /** 128 | * 删除本地文件 129 | * 130 | * @param $file 131 | * @return bool 132 | */ 133 | function kodo_delete_local_file($file) 134 | { 135 | try { 136 | //文件不存在 137 | if (!@file_exists($file)) { 138 | return true; 139 | } 140 | 141 | //删除文件 142 | if (!@unlink($file)) { 143 | return false; 144 | } 145 | 146 | return true; 147 | } catch (Exception $ex) { 148 | return false; 149 | } 150 | } 151 | 152 | /** 153 | * 删除kodo中的文件 154 | * @param string $file 155 | * @return bool 156 | */ 157 | function kodo_delete_file($file) 158 | { 159 | $bucket = kodo_get_bucket_name(); 160 | $bucketManager = new BucketManager(kodo_get_auth()); 161 | [$_, $err] = $bucketManager->delete($bucket, $file); 162 | if ($err !== null) { 163 | error_log($err->message()); 164 | return false; 165 | } 166 | 167 | return true; 168 | } 169 | 170 | /** 171 | * 批量删除文件 172 | * @param $bucket 173 | * @param array $files 174 | */ 175 | function kodo_delete_files($bucket, array $files) 176 | { 177 | $deleteObjects = []; 178 | foreach ($files as $file) { 179 | $deleteObjects[] = str_replace(["\\", './'], ['/', ''], $file); 180 | } 181 | 182 | $bucketManager = new BucketManager(kodo_get_auth()); 183 | $ops = $bucketManager->buildBatchDelete($bucket, $deleteObjects); 184 | [$_, $err] = $bucketManager->batch($ops); 185 | if ($err !== null) { 186 | error_log($err->message()); 187 | } 188 | } 189 | 190 | function kodo_get_option($key) 191 | { 192 | return esc_attr(get_option($key)); 193 | } 194 | 195 | $kodo_options = get_option('kodo_options', kodo_get_default_options()); 196 | if (isset($kodo_options['origin_protect']) && esc_attr($kodo_options['origin_protect']) == 'true' && !empty(esc_attr($kodo_options['image_style']))) { 197 | add_filter('wp_get_attachment_url', 'kodo_add_suffix_to_attachment_url', 10, 2); 198 | add_filter('wp_get_attachment_thumb_url', 'kodo_add_suffix_to_attachment_url', 10, 2); 199 | add_filter('wp_get_original_image_url', 'kodo_add_suffix_to_attachment_url', 10, 2); 200 | add_filter('wp_prepare_attachment_for_js', 'kodo_add_suffix_to_attachment', 10, 2); 201 | add_filter('image_get_intermediate_size', 'kodo_add_suffix_for_media_send_to_editor'); 202 | } 203 | 204 | /** 205 | * @param string $url 206 | * @param int $post_id 207 | * @return string 208 | */ 209 | function kodo_add_suffix_to_attachment_url($url, $post_id) 210 | { 211 | if (kodo_is_image_type($url)) { 212 | $url .= kodo_get_image_style(); 213 | } 214 | 215 | return $url; 216 | } 217 | 218 | /** 219 | * @param array $response 220 | * @param array $attachment 221 | * @return array 222 | */ 223 | function kodo_add_suffix_to_attachment($response, $attachment) 224 | { 225 | if ($response['type'] != 'image') { 226 | return $response; 227 | } 228 | 229 | $style = kodo_get_image_style(); 230 | if (!empty($response['sizes'])) { 231 | foreach ($response['sizes'] as $size_key => $size_file) { 232 | if (kodo_is_image_type($size_file['url']) && strpos($size_file['url'], $style) === false) { 233 | $response['sizes'][$size_key]['url'] .= $style; 234 | } 235 | } 236 | } 237 | 238 | if(!empty($response['originalImageURL'])) { 239 | if (kodo_is_image_type($response['originalImageURL']) && strpos($response['originalImageURL'], $style) === false) { 240 | $response['originalImageURL'] .= $style; 241 | } 242 | } 243 | 244 | return $response; 245 | } 246 | 247 | /** 248 | * @param array $data 249 | * @return array 250 | */ 251 | function kodo_add_suffix_for_media_send_to_editor($data) 252 | { 253 | // https://github.com/WordPress/wordpress-develop/blob/43d2455dc68072fdd43c3c800cc8c32590f23cbe/src/wp-includes/media.php#L239 254 | if (kodo_is_image_type($data['file'])) { 255 | $data['file'] .= kodo_get_image_style(); 256 | } 257 | 258 | return $data; 259 | } 260 | 261 | /** 262 | * @param string $url 263 | * @return bool 264 | */ 265 | function kodo_is_image_type($url) 266 | { 267 | return (bool) preg_match('/\.(jpg|jpeg|jpe|gif|png|bmp|tiff|tif|webp|ico|heic)$/i', $url); 268 | } 269 | 270 | /** 271 | * @return string 272 | */ 273 | function kodo_get_image_style() 274 | { 275 | $kodo_options = get_option('kodo_options', kodo_get_default_options()); 276 | 277 | return esc_attr($kodo_options['image_style']); 278 | } 279 | 280 | /** 281 | * 上传附件(包括图片的原图) 282 | * 283 | * @param $metadata 284 | * @return array 285 | */ 286 | function kodo_upload_attachments($metadata) 287 | { 288 | $mime_types = wp_get_mime_types(); 289 | $image_mime_types = [ 290 | $mime_types['jpg|jpeg|jpe'], 291 | $mime_types['gif'], 292 | $mime_types['png'], 293 | $mime_types['bmp'], 294 | $mime_types['tiff|tif'], 295 | $mime_types['webp'], 296 | $mime_types['ico'], 297 | ]; 298 | 299 | // 例如mp4等格式 上传后根据配置选择是否删除 删除后媒体库会显示默认图片 点开内容是正常的 300 | // 图片在缩略图处理 301 | if (!in_array($metadata['type'], $image_mime_types)) { 302 | //生成object在kodo中的存储路径 303 | if (kodo_get_option('upload_path') == '.') { 304 | $metadata['file'] = str_replace("./", '', $metadata['file']); 305 | } 306 | $object = str_replace("\\", '/', $metadata['file']); 307 | $home_path = get_home_path(); 308 | $object = str_replace($home_path, '', $object); 309 | 310 | //在本地的存储路径 311 | $file = $home_path . $object; //向上兼容,较早的WordPress版本上$metadata['file']存放的是相对路径 312 | 313 | //执行上传操作 314 | kodo_file_upload('/' . $object, $file, kodo_is_delete_local_file()); 315 | } 316 | 317 | return $metadata; 318 | } 319 | 320 | //避免上传插件/主题时出现同步到kodo的情况 321 | if (substr_count($_SERVER['REQUEST_URI'], '/update.php') <= 0) { 322 | add_filter('wp_handle_upload', 'kodo_upload_attachments', 50); 323 | add_filter('wp_generate_attachment_metadata', 'kodo_upload_thumbs', 100); 324 | add_filter('wp_save_image_editor_file', 'kodo_save_image_editor_file', 101); 325 | } 326 | 327 | /** 328 | * 上传图片的缩略图 329 | */ 330 | function kodo_upload_thumbs($metadata) 331 | { 332 | if (empty($metadata['file'])) { 333 | return $metadata; 334 | } 335 | 336 | //获取上传路径 337 | $wp_uploads = wp_upload_dir(); 338 | $basedir = $wp_uploads['basedir']; 339 | $upload_path = kodo_get_option('upload_path'); 340 | 341 | //获取kodo插件的配置信息 342 | $kodo_options = get_option('kodo_options', kodo_get_default_options()); 343 | $no_local_file = esc_attr($kodo_options['nolocalsaving']) == 'true'; 344 | $no_thumb = esc_attr($kodo_options['nothumb']) == 'true'; 345 | 346 | // Maybe there is a problem with the old version 347 | $file = $basedir . '/' . $metadata['file']; 348 | if ($upload_path != '.') { 349 | $path_array = explode($upload_path, $file); 350 | if (count($path_array) >= 2) { 351 | $object = '/' . $upload_path . end($path_array); 352 | } 353 | } else { 354 | $object = '/' . $metadata['file']; 355 | $file = str_replace('./', '', $file); 356 | } 357 | 358 | kodo_file_upload($object, $file, $no_local_file); 359 | 360 | //得到本地文件夹和远端文件夹 361 | $dirname = dirname($metadata['file']); 362 | $file_path = $dirname != '.' ? "{$basedir}/{$dirname}/" : "{$basedir}/"; 363 | $file_path = str_replace("\\", '/', $file_path); 364 | if ($upload_path == '.') { 365 | $file_path = str_replace('./', '', $file_path); 366 | } 367 | $object_path = str_replace(get_home_path(), '', $file_path); 368 | 369 | if (!empty($metadata['original_image'])) { 370 | kodo_file_upload("/{$object_path}{$metadata['original_image']}", "{$file_path}{$metadata['original_image']}", $no_local_file); 371 | } 372 | 373 | //如果禁止上传缩略图,就不用继续执行了 374 | if ($no_thumb) { 375 | return $metadata; 376 | } 377 | 378 | //上传所有缩略图 379 | if (!empty($metadata['sizes'])) { 380 | //there may be duplicated filenames 381 | foreach ($metadata['sizes'] as $val) { 382 | //生成object在kodo中的存储路径 383 | $object = '/' . $object_path . $val['file']; 384 | //生成本地存储路径 385 | $file = $file_path . $val['file']; 386 | 387 | //执行上传操作 388 | kodo_file_upload($object, $file, $no_local_file); 389 | } 390 | } 391 | 392 | return $metadata; 393 | } 394 | 395 | /** 396 | * @param $override 397 | * @return mixed 398 | */ 399 | function kodo_save_image_editor_file($override) 400 | { 401 | add_filter('wp_update_attachment_metadata', 'kodo_image_editor_file_do'); 402 | return $override; 403 | } 404 | 405 | /** 406 | * @param $metadata 407 | * @return mixed 408 | */ 409 | function kodo_image_editor_file_do($metadata) 410 | { 411 | return kodo_upload_thumbs($metadata); 412 | } 413 | 414 | /** 415 | * 删除远端文件,删除文件时触发 416 | * @param $post_id 417 | */ 418 | function kodo_delete_remote_attachment($post_id) 419 | { 420 | $wp_uploads = wp_upload_dir(); 421 | $basedir = $wp_uploads['basedir']; 422 | $upload_path = str_replace(get_home_path(), '', $basedir); 423 | $meta = wp_get_attachment_metadata($post_id); 424 | $kodo_options = get_option('kodo_options', kodo_get_default_options()); 425 | 426 | if (!empty($meta['file'])) { 427 | $deleteObjects = []; 428 | // meta['file']的格式为 "2020/01/wp-bg.png" 429 | $file_path = $upload_path . '/' . $meta['file']; 430 | $dirname = dirname($file_path) . '/'; 431 | 432 | $deleteObjects[] = $file_path; 433 | 434 | // 超大图原图 435 | if (!empty($meta['original_image'])) { 436 | $deleteObjects[] = $dirname . $meta['original_image']; 437 | } 438 | 439 | // 删除缩略图 440 | if (!empty($meta['sizes'])) { 441 | foreach ($meta['sizes'] as $val) { 442 | $deleteObjects[] = $dirname . $val['file']; 443 | } 444 | } 445 | 446 | $backup_sizes = get_post_meta($post_id, '_wp_attachment_backup_sizes', true); 447 | if (is_array($backup_sizes)) { 448 | foreach ($backup_sizes as $size) { 449 | $deleteObjects[] = $dirname . $size['file']; 450 | } 451 | } 452 | 453 | kodo_delete_files($kodo_options['bucket'], $deleteObjects); 454 | } else { 455 | // 获取链接删除 456 | $link = wp_get_attachment_url($post_id); 457 | if ($link) { 458 | $upload_path = kodo_get_option('upload_path'); 459 | if ($upload_path != '.') { 460 | $file_info = explode($upload_path, $link); 461 | if (count($file_info) >= 2) { 462 | kodo_delete_file(end($file_info)); 463 | } 464 | } else { 465 | $kodo_upload_url = esc_attr($kodo_options['upload_url_path']); 466 | $file_info = explode($kodo_upload_url, $link); 467 | if (isset($file_info[1])) { 468 | kodo_delete_file($file_info[1]); 469 | } 470 | } 471 | } 472 | } 473 | } 474 | 475 | add_action('delete_attachment', 'kodo_delete_remote_attachment'); 476 | 477 | // 当upload_path为根目录时,需要移除URL中出现的“绝对路径” 478 | function kodo_modify_img_url($url, $post_id) 479 | { 480 | // 移除 ./ 和 项目根路径 481 | return str_replace(['./', get_home_path()], '', $url); 482 | } 483 | 484 | if (kodo_get_option('upload_path') == '.') { 485 | add_filter('wp_get_attachment_url', 'kodo_modify_img_url', 30, 2); 486 | } 487 | 488 | function kodo_sanitize_file_name($filename) 489 | { 490 | $kodo_options = get_option('kodo_options', kodo_get_default_options()); 491 | switch ($kodo_options['update_file_name']) { 492 | case 'md5': 493 | return md5($filename) . '.' . pathinfo($filename, PATHINFO_EXTENSION); 494 | case 'time': 495 | return date('YmdHis', current_time('timestamp')) . mt_rand(100, 999) . '.' . pathinfo($filename, PATHINFO_EXTENSION); 496 | default: 497 | return $filename; 498 | } 499 | } 500 | 501 | add_filter('sanitize_file_name', 'kodo_sanitize_file_name', 10, 1); 502 | 503 | /** 504 | * @param string $homePath 505 | * @param string $uploadPath 506 | * @return array 507 | */ 508 | function kodo_read_dir_queue($homePath, $uploadPath) 509 | { 510 | $dir = $homePath . $uploadPath; 511 | $dirsToProcess = new SplQueue(); 512 | $dirsToProcess->enqueue([$dir, '']); 513 | $foundFiles = []; 514 | 515 | while (!$dirsToProcess->isEmpty()) { 516 | [$currentDir, $relativeDir] = $dirsToProcess->dequeue(); 517 | 518 | foreach (new DirectoryIterator($currentDir) as $fileInfo) { 519 | if ($fileInfo->isDot()) continue; 520 | 521 | $filepath = $fileInfo->getRealPath(); 522 | 523 | // Compute the relative path of the file/directory with respect to upload path 524 | $currentRelativeDir = "{$relativeDir}/{$fileInfo->getFilename()}"; 525 | 526 | if ($fileInfo->isDir()) { 527 | $dirsToProcess->enqueue([$filepath, $currentRelativeDir]); 528 | } else { 529 | // Add file path and key to the result array 530 | $foundFiles[] = [ 531 | 'filepath' => $filepath, 532 | 'key' => '/' . $uploadPath . $currentRelativeDir 533 | ]; 534 | } 535 | } 536 | } 537 | 538 | return $foundFiles; 539 | } 540 | 541 | // 在插件列表页添加设置按钮 542 | function kodo_plugin_action_links($links, $file) 543 | { 544 | if ($file == plugin_basename(dirname(__FILE__) . '/qiniu-kodo-wordpress.php')) { 545 | $links[] = '设置'; 546 | $links[] = '赞赏'; 547 | } 548 | return $links; 549 | } 550 | 551 | add_filter('plugin_action_links', 'kodo_plugin_action_links', 10, 2); 552 | 553 | function kodo_custom_image_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id) 554 | { 555 | $option = get_option('kodo_options', kodo_get_default_options()); 556 | $style = !empty($option['image_style']) ? esc_attr($option['image_style']) : ''; 557 | $upload_url_path = esc_attr($option['upload_url_path']); 558 | if (empty($style)) { 559 | return $sources; 560 | } 561 | 562 | foreach ($sources as $index => $source) { 563 | if (strpos($source['url'], $upload_url_path) !== false && strpos($source['url'], $style) === false) { 564 | $sources[$index]['url'] .= $style; 565 | } 566 | } 567 | 568 | return $sources; 569 | } 570 | 571 | add_filter('wp_calculate_image_srcset', 'kodo_custom_image_srcset', 10, 5); 572 | 573 | add_filter('wp_prepare_attachment_for_js', 'kodo_wp_prepare_attachment_for_js', 10); 574 | function kodo_wp_prepare_attachment_for_js($response) 575 | { 576 | if (empty($response['filesizeInBytes']) || empty($response['filesizeHumanReadable'])) { 577 | $upload_url_path = kodo_get_option('upload_url_path'); 578 | $upload_path = kodo_get_option('upload_path'); 579 | $object = str_replace($upload_url_path, $upload_path, $response['url']); 580 | $meta = kodo_get_file_meta($object); 581 | if (!empty($meta['fsize'])) { 582 | $response['filesizeInBytes'] = $meta['fsize']; 583 | $response['filesizeHumanReadable'] = size_format($meta['fsize']); 584 | } 585 | } 586 | 587 | return $response; 588 | } 589 | 590 | /** 591 | * @param string $object 592 | * @return array 593 | */ 594 | function kodo_get_file_meta($object) 595 | { 596 | try { 597 | $bucket = kodo_get_bucket_name(); 598 | $bucketManager = new BucketManager(kodo_get_auth()); 599 | [$meta, $err] = $bucketManager->stat($bucket, $object); 600 | if ($err !== null) { 601 | error_log($err->message()); 602 | } 603 | 604 | return $meta; 605 | } catch (\Throwable $e) { 606 | error_log($e->getMessage()); 607 | return ['fsize' => 0]; 608 | } 609 | } 610 | 611 | add_filter('the_content', 'kodo_setting_content_style'); 612 | function kodo_setting_content_style($content) 613 | { 614 | $option = get_option('kodo_options', kodo_get_default_options()); 615 | $upload_url_path = esc_attr($option['upload_url_path']); 616 | $style = esc_attr($option['image_style']); 617 | if (!empty($style)) { 618 | preg_match_all('//sim', $content, $images); 619 | if (!empty($images) && isset($images[1])) { 620 | $images[1] = array_unique($images[1]); 621 | foreach ($images[1] as $item) { 622 | if (strpos($item, $upload_url_path) !== false && strpos($item, $style) === false) { 623 | $content = str_replace($item, $item . $style, $content); 624 | } 625 | } 626 | $content = str_replace($style . $style, $style, $content); 627 | } 628 | } 629 | return $content; 630 | } 631 | 632 | add_filter('post_thumbnail_html', 'kodo_setting_post_thumbnail_style', 10, 3); 633 | function kodo_setting_post_thumbnail_style($html, $post_id, $post_image_id) 634 | { 635 | $option = get_option('kodo_options', kodo_get_default_options()); 636 | $upload_url_path = esc_attr($option['upload_url_path']); 637 | $style = esc_attr($option['image_style']); 638 | if (!empty($style) && has_post_thumbnail()) { 639 | preg_match_all('//sim', $html, $images); 640 | if (!empty($images) && isset($images[1])) { 641 | $images[1] = array_unique($images[1]); 642 | foreach ($images[1] as $item) { 643 | if (strpos($item, $upload_url_path) !== false && strpos($item, $style) === false) { 644 | $html = str_replace($item, $item . $style, $html); 645 | } 646 | } 647 | $html = str_replace($style . $style, $style, $html); 648 | } 649 | } 650 | return $html; 651 | } 652 | 653 | // 在导航栏“设置”中添加条目 654 | function kodo_add_setting_page() 655 | { 656 | add_options_page('七牛云 KODO', '七牛云 KODO', 'manage_options', __FILE__, 'kodo_setting_page'); 657 | } 658 | 659 | add_action('admin_menu', 'kodo_add_setting_page'); 660 | 661 | // 插件设置页面 662 | function kodo_setting_page() 663 | { 664 | if (!current_user_can('manage_options')) { 665 | wp_die('Insufficient privileges!'); 666 | } 667 | $options = []; 668 | if (!empty($_POST) and $_POST['type'] == 'kodo_set') { 669 | $options['bucket'] = isset($_POST['bucket']) ? sanitize_text_field($_POST['bucket']) : ''; 670 | $options['accessKey'] = isset($_POST['accessKey']) ? sanitize_text_field($_POST['accessKey']) : ''; 671 | $options['secretKey'] = isset($_POST['secretKey']) ? sanitize_text_field($_POST['secretKey']) : ''; 672 | $options['nothumb'] = isset($_POST['nothumb']) ? 'true' : 'false'; 673 | $options['nolocalsaving'] = isset($_POST['nolocalsaving']) ? 'true' : 'false'; 674 | $options['origin_protect'] = isset($_POST['origin_protect']) ? 'true' : 'false'; 675 | //仅用于插件卸载时比较使用 676 | $options['upload_url_path'] = isset($_POST['upload_url_path']) ? sanitize_text_field(stripslashes($_POST['upload_url_path'])) : ''; 677 | $options['image_style'] = isset($_POST['image_style']) ? sanitize_text_field($_POST['image_style']) : ''; 678 | $options['update_file_name'] = isset($_POST['update_file_name']) ? sanitize_text_field($_POST['update_file_name']) : 'false'; 679 | } 680 | 681 | if (!empty($_POST) and $_POST['type'] == 'qiniu_kodo_all') { 682 | $files = kodo_read_dir_queue(get_home_path(), kodo_get_option('upload_path')); 683 | foreach ($files as $file) { 684 | kodo_file_upload($file['key'], $file['filepath']); 685 | } 686 | echo '

本次操作成功同步' . count($files) . '个文件

'; 687 | } 688 | 689 | // 替换数据库链接 690 | if (!empty($_POST) and $_POST['type'] == 'qiniu_kodo_replace') { 691 | $nonce = $_POST['qiniu_kodo_replace-nonce'] ?? ''; 692 | if (empty($nonce) || !wp_verify_nonce($nonce, 'qiniu_kodo_replace')) { 693 | wp_die('Illegal requests!'); 694 | } 695 | 696 | $old_url = esc_url_raw($_POST['old_url']); 697 | $new_url = esc_url_raw($_POST['new_url']); 698 | if (!empty($old_url) && !empty($new_url)) { 699 | global $wpdb; 700 | // 文章内容 701 | $posts_name = $wpdb->prefix . 'posts'; 702 | $posts_result = $wpdb->query($wpdb->prepare("UPDATE $posts_name SET post_content = REPLACE(post_content, '%s', '%s')", [$old_url, $new_url])); 703 | 704 | // 修改题图之类的 705 | $postmeta_name = $wpdb->prefix . 'postmeta'; 706 | $postmeta_result = $wpdb->query($wpdb->prepare("UPDATE $postmeta_name SET meta_value = REPLACE(meta_value, '%s', '%s')", [$old_url, $new_url])); 707 | 708 | echo '

替换成功!共替换文章内链'.$posts_result.'条、题图链接'.$postmeta_result.'条!

'; 709 | } else { 710 | echo '

请填写资源链接URL地址!

'; 711 | } 712 | } 713 | 714 | // 若$options不为空数组,则更新数据 715 | if ($options !== []) { 716 | //更新数据库 717 | update_option('kodo_options', $options); 718 | 719 | $upload_path = sanitize_text_field(trim(stripslashes($_POST['upload_path']), '/')); 720 | $upload_path = ($upload_path == '') ? 'wp-content/uploads' : $upload_path; 721 | update_option('upload_path', $upload_path); 722 | $upload_url_path = sanitize_text_field(trim(stripslashes($_POST['upload_url_path']), '/')); 723 | update_option('upload_url_path', $upload_url_path); 724 | echo '

设置已保存!

'; 725 | } 726 | 727 | $kodo_options = get_option('kodo_options', kodo_get_default_options()); 728 | 729 | $kodo_nothumb = esc_attr($kodo_options['nothumb']) == 'true'; 730 | $kodo_nolocalsaving = esc_attr($kodo_options['nolocalsaving']) == 'true'; 731 | $kodo_origin_protect = esc_attr($kodo_options['origin_protect'] ?? '') == 'true'; 732 | 733 | $kodo_update_file_name = esc_attr($kodo_options['update_file_name']); 734 | 735 | $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? 'https://' : 'http://'; 736 | ?> 737 |
738 |

七牛云 KODO 当前版本:

739 |

如果觉得此插件对你有所帮助,不妨到 GitHub 上点个StarWatch关注更新;欢迎加入云存储插件交流群,QQ群号:887595381

740 |
741 |
742 | 743 | 744 | 747 | 751 | 752 | 753 | 756 | 757 | 758 | 759 | 762 | 765 | 766 | 767 | 770 | 774 | 775 | 776 | 779 | 783 | 784 | 785 | 788 | 795 | 796 | 797 | 800 | 804 | 805 | 806 | 809 | 820 | 821 | 822 | 825 | 837 | 838 | 839 | 842 | 847 | 848 | 849 | 852 | 853 | 854 |
745 | 空间名称 746 | 748 | 749 |

请先访问 七牛云控制台 创建存储空间,再填写以上内容。

750 |
754 | accessKey 755 |
760 | secretKey 761 | 763 | 764 |
768 | 不上传缩略图 769 | 771 | /> 772 |

建议不勾选

773 |
777 | 不在本地保留备份 778 | 780 | /> 781 |

建议不勾选

782 |
786 | 自动重命名文件 787 | 789 | 794 |
798 | 本地文件夹 799 | 801 | 802 |

附件在服务器上的存储位置,例如: wp-content/uploads (注意不要以“/”开头和结尾),根目录请输入.

803 |
807 | URL前缀 808 | 810 | 811 | 812 |

注意:

813 | 814 |

1)URL前缀的格式为 {加速域名}/{本地文件夹} ,“本地文件夹”务必与上面保持一致(结尾无 815 | / ),或者“本地文件夹”为 .{加速域名} 。 816 |

817 | 818 |

2)七牛云中没有文件夹的概念,所以本地文件夹对应七牛云中的文件名。

819 |
823 | 图片样式 824 | 826 | 827 | 828 |

获取图片样式:

829 | 830 |

1)在 空间管理 中对应空间的 图片样式 处添加。

831 | 832 |

2)填写时需要将分隔符和对应的名称处理接口进行拼接,例如:

833 | 834 |

分隔符!(感叹号),名称webp处理接口imageView2/0/format/webp/q/75

835 |

则填写为 !webp?imageView2/0/format/webp/q/75

836 |
840 | 原图保护 841 | 843 | /> 844 |

在七牛云启用原图保护后勾选启用,需要先配置图片样式。

845 |

注:此功能为实验性功能,如遇错误或不可用,请关闭后联系作者反馈。

846 |
850 | 保存/更新选项 851 |
855 | 856 |
857 |
858 | 859 | 860 | 863 | 864 | 868 | 869 |
861 | 同步历史附件 862 | 865 | 866 |

注意:如果是首次同步,执行时间将会非常长(根据你的历史附件数量),有可能会因为执行时间过长,导致页面显示超时或者报错。
所以,建议附件数量过多的用户,直接使用官方的 同步工具

867 |
870 |
871 |
872 |
873 | 874 | 875 | 878 | 881 | 882 | 883 | 886 | 889 | 890 | 891 | 894 | 895 | 896 | 900 | 901 |
876 | 数据库原链接替换 877 | 879 | 880 |
884 | 885 | 887 | 888 |
892 | 893 | 897 | 898 |

注意:如果是首次替换,请注意备份!此功能会替换文章以及设置的特色图片(题图)等使用的资源链接

899 |
902 |
903 |
904 | 907 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === KODO Qiniu === 2 | Contributors: shenyanzhi 3 | Donate link: https://qq52o.me/sponsor.html 4 | Tags: KODO, 七牛云, qiniu, 对象存储, 海量存储 5 | Requires at least: 4.6 6 | Tested up to: 6.8 7 | Requires PHP: 7.1 8 | Stable tag: 1.5.6 9 | License: Apache2.0 10 | License URI: http://www.apache.org/licenses/LICENSE-2.0.html 11 | 12 | 使用七牛云海量存储系统 KODO 作为附件存储空间。(This is a plugin that uses Qiniu Cloud KODO for attachments remote saving.) 13 | 14 | == Description == 15 | 16 | 使用七牛云海量存储系统 KODO 作为附件存储空间。(This is a plugin that uses Qiniu Cloud KODO for attachments remote saving.) 17 | 18 | - 依赖七牛云海量存储系统 KODO 服务:https://www.qiniu.com/products/kodo 19 | - 使用说明:https://developer.qiniu.com/kodo?ref=www.qq52o.me 20 | 21 | ## 插件特点 22 | 23 | 1. 可配置是否上传缩略图和是否保留本地备份 24 | 2. 本地删除可同步删除七牛云海量存储系统 KODO 中的文件 25 | 3. 支持七牛云海量存储系统 KODO 绑定的个性域名 26 | 4. 支持替换数据库中旧的资源链接地址 27 | 5. 支持七牛云海量存储系统 KODO 完整地域使用 28 | 6. 支持同步历史附件到七牛云海量存储系统 KODO 29 | 7. 支持七牛云图片样式 30 | 8. 支持七牛云原图保护 31 | 9. 支持媒体库编辑 32 | 10. 支持上传文件自动重命名 33 | 11. 支持图片裁剪编辑等操作后的上传 34 | 12. 支持多站点 35 | 13. 支持使用 wp-cli 命令上传/删除文件 36 | 14. 插件更多详细介绍和安装:[https://github.com/sy-records/qiniu-kodo-wordpress](https://github.com/sy-records/qiniu-kodo-wordpress) 37 | 38 | ## 其他插件 39 | 40 | 腾讯云 COS:[GitHub](https://github.com/sy-records/sync-qcloud-cos),[WordPress Plugins](https://wordpress.org/plugins/sync-qcloud-cos) 41 | 华为云 OBS:[GitHub](https://github.com/sy-records/huaweicloud-obs-wordpress),[WordPress Plugins](https://wordpress.org/plugins/obs-huaweicloud) 42 | 阿里云 OSS:[GitHub](https://github.com/sy-records/aliyun-oss-wordpress),[WordPress Plugins](https://wordpress.org/plugins/oss-aliyun) 43 | 又拍云 USS:[GitHub](https://github.com/sy-records/upyun-uss-wordpress),[WordPress Plugins](https://wordpress.org/plugins/uss-upyun) 44 | 45 | ## 作者博客 46 | 47 | [沈唁志](https://qq52o.me "沈唁志") 48 | 49 | QQ 交流群:887595381 50 | 51 | == Installation == 52 | 53 | 1. Upload the folder `qiniu-kodo-wordpress` or `kodo-qiniu` to the `/wp-content/plugins/` directory 54 | 2. Activate the plugin through the 'Plugins' menu in WordPress 55 | 3. That's all 56 | 57 | == Screenshots == 58 | 59 | 1. 设置页面 60 | 2. 数据库替换 61 | 62 | == Frequently Asked Questions == 63 | 64 | = 怎么替换文章中之前的旧资源地址链接 = 65 | 66 | 这个插件已经加上了替换数据库中之前的旧资源地址链接功能,只需要填好对应的链接即可 67 | 68 | == Changelog == 69 | 70 | = 1.5.6 = 71 | 72 | - Fix typo. 73 | 74 | = 1.5.5 = 75 | 76 | - 支持使用 `wp-cli` 命令上传/删除文件 77 | - 修复多站点时删除子站点远程图片失败问题 78 | - 修复 `get_option` 获取默认值错误 79 | 80 | = 1.5.4 = 81 | 82 | - 修复`不在本地保留备份`时获取不到非图片文件大小 83 | 84 | = 1.5.3 = 85 | 86 | - 修复原图保护开启后媒体库图片重复追加规则 87 | - 不再支持 PHP7.0 88 | - 修复 heic 格式图片上传 89 | 90 | = 1.5.2 = 91 | 92 | - Optimize wpdb query 93 | 94 | = 1.5.1 = 95 | 96 | - Fix CSRF error 97 | 98 | = 1.5.0 = 99 | 100 | - 支持原图保护 101 | 102 | = 1.4.6 = 103 | 104 | - 修复图片处理参数重复追加 105 | 106 | = 1.4.5 = 107 | 108 | - 修复 `upload_url_path` 设置为 `.` 时删除失败 109 | - 优化图片处理参数追加 110 | 111 | = 1.4.4 = 112 | 113 | - 修复上传 PDF 等文件格式时的错误 114 | 115 | = 1.4.3 = 116 | 117 | - 修复超大图片上传原图丢失问题 118 | - 优化同步上传逻辑代码 119 | 120 | = 1.4.2 = 121 | 122 | - 修复 webp、heic 格式图片缩略图未上传问题 123 | 124 | = 1.4.1 = 125 | 126 | - 更新依赖 https://github.com/qiniu/php-sdk/releases/tag/v7.10.1 127 | 128 | = 1.4.0 = 129 | 130 | - 更新依赖 131 | - 支持 WordPress 6.3 版本 132 | 133 | = 1.3.2 = 134 | 135 | - 支持媒体库编辑 136 | - 支持上传文件自动重命名 137 | - 移除 esc_html 138 | 139 | = 1.3.0 = 140 | 141 | - 修复 XSS 142 | - 优化 isset 判断 143 | - 优化访问权限 144 | - 修复存在同名 path 时截取错误 145 | - 修复禁用年/月目录格式时上传缩略图错误 146 | 147 | = 1.2.5 = 148 | 149 | - 修正版本号 150 | 151 | = 1.2.4 = 152 | 153 | - 添加 get_home_path 方法判断 154 | - 支持 WordPress 5.7 版本 155 | 156 | = 1.2.3 = 157 | 158 | - 优化远端文件删除 159 | - 修复同步文件上传报错`fread(): Length parameter must be greater than 0` 160 | 161 | = 1.2.2 = 162 | 163 | - 修复缩略图删除获取配置错误 164 | - 升级 SDK 165 | - 增加图片样式处理 166 | 167 | = 1.2.1 = 168 | 169 | - 优化缩略图删除 170 | - 支持 WordPress 5.6 171 | 172 | = 1.2.0 = 173 | 174 | - 优化同步上传路径获取 175 | - 修复多站点上传原图失败,缩略图正常问题 176 | - 优化上传路径获取 177 | - 增加数据库题图链接替换 178 | 179 | = 1.1.0 = 180 | 181 | - 优化删除文件为批量删除 182 | - 修复勾选不在本地保存图片后媒体库显示默认图片问题 183 | - 修复本地文件夹为根目录时路径错误 184 | 185 | = 1.0.1 = 186 | 187 | - 修复勾选不在本地保存图片后媒体库显示默认图片问题 188 | 189 | = 1.0 = 190 | 191 | - First version 192 | -------------------------------------------------------------------------------- /sdk/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "qiniu/php-sdk": "^7.2" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 |