PluginProbe
Sync QCloud COS / trunk
Sync QCloud COS vtrunk
2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.6.7 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 All 65 releases
sync-qcloud-cos / sync-qcloud-cos.php

sync-qcloud-cos.php in Sync QCloud COS trunk, at sync-qcloud-cos.php

1,960 lines 73.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Sync QCloud COS
4 Plugin URI: https://qq52o.me/2518.html
5 Description: 使用�
6 �讯云对象存储服务 COS 作为附件存储空间。(Using Tencent Cloud Object Storage Service COS as Attachment Storage Space.)
7 Version: 2.6.7
8 Author: 沈唁
9 Author URI: https://qq52o.me
10 License: Apache2.0
11 */
12
13 if (!defined('ABSPATH')) {
14 exit;
15 }
16
17 require_once 'cos-sdk-v5/vendor/autoload.php';
18
19 use Qcloud\Cos\Client;
20 use Qcloud\Cos\Exception\ServiceResponseException;
21 use SyncQcloudCos\CI\Audit;
22 use SyncQcloudCos\CI\FilePreview;
23 use SyncQcloudCos\CI\ImageSlim;
24 use SyncQcloudCos\CI\OriginProtect;
25 use SyncQcloudCos\CI\Service;
26 use SyncQcloudCos\ErrorCode;
27 use SyncQcloudCos\Monitor\Charts;
28 use SyncQcloudCos\Monitor\DataPoints;
29 use SyncQcloudCos\Object\Head;
30
31 define('COS_VERSION', '2.6.6');
32 define('COS_PLUGIN_SLUG', 'sync-qcloud-cos');
33 define('COS_PLUGIN_PAGE', plugin_basename(dirname(__FILE__)) . '%2F' . basename(__FILE__));
34
35 if (!function_exists('get_home_path')) {
36 require_once ABSPATH . 'wp-admin/includes/file.php';
37 }
38
39 if (defined('WP_CLI') && WP_CLI) {
40 require_once plugin_dir_path(__FILE__) . 'cos-commands.php';
41 }
42
43 // 初始化选项
44 register_activation_hook(__FILE__, 'cos_set_options');
45 function cos_get_default_options()
46 {
47 return [
48 'bucket' => '',
49 'regional' => 'ap-beijing',
50 'app_id' => '',
51 'secret_id' => '',
52 'secret_key' => '',
53 'nothumb' => 'false', // 是否上传缩略图
54 'nolocalsaving' => 'false', // 是否保留本地备份
55 'delete_options' => 'true',
56 'upload_subdirectory' => '',
57 'upload_url_path' => '', // URL前缀
58 'update_file_name' => 'false', // 是否重命名文件名
59 'ci_style' => '',
60 'ci_image_slim' => 'off',
61 'ci_image_slim_mode' => '',
62 'ci_image_slim_suffix' => '',
63 'attachment_preview' => 'off',
64 'ci_text_comments' => 'off',
65 'skip_comment_validation_on_login' => 'off',
66 'ci_text_comments_strategy' => '',
67 'ci_text_comments_check_roles' => '',
68 'origin_protect' => 'off',
69 ];
70 }
71 function cos_set_options()
72 {
73 add_option('cos_options', cos_get_default_options(), '', 'yes');
74 }
75
76 /**
77 * @param array $cos_options
78 * @return Client
79 */
80 function cos_get_client($cos_options = null)
81 {
82 if ($cos_options === null) {
83 $cos_options = get_option('cos_options', cos_get_default_options());
84 }
85 $config = [
86 'region' => esc_attr($cos_options['regional']),
87 'scheme' => cos_get_url_scheme(''),
88 'credentials' => [
89 'secretId' => esc_attr($cos_options['secret_id']),
90 'secretKey' => esc_attr($cos_options['secret_key'])
91 ],
92 'userAgent' => 'WordPress v' . $GLOBALS['wp_version'] . '; SyncQCloudCOS v' . COS_VERSION . '; SDK v' . Client::VERSION,
93 ];
94 return new Client($config);
95 }
96
97 function cos_get_bucket_name($cos_options = null)
98 {
99 if ($cos_options === null) {
100 $cos_options = get_option('cos_options', cos_get_default_options());
101 }
102 $cos_bucket = esc_attr($cos_options['bucket']);
103 $cos_app_id = esc_attr($cos_options['app_id']);
104 if (empty($cos_bucket) && empty($cos_app_id)) {
105 return '';
106 }
107
108 $needle = '-' . $cos_app_id;
109 if (strpos($cos_bucket, $needle) !== false) {
110 return $cos_bucket;
111 }
112 return $cos_bucket . $needle;
113 }
114
115 function cos_check_bucket($cos_options)
116 {
117 try {
118 $client = cos_get_client($cos_options);
119 $bucket = cos_get_bucket_name($cos_options);
120 $client->HeadBucket(['Bucket' => $bucket]);
121 $upload = $client->upload($bucket, 'sync-qcloud-cos.txt', COS_PLUGIN_SLUG);
122 if ($upload) {
123 $client->DeleteObject(['Bucket' => $bucket, 'Key' => 'sync-qcloud-cos.txt']);
124 }
125
126 return true;
127 } catch (ServiceResponseException $e) {
128 $message = (string)$e;
129 $errorCode = $e->getCosErrorCode();
130 if ($errorCode == ErrorCode::NO_SUCH_BUCKET) {
131 $message = '<code>Bucket</code> 不存在,请检查存储桶名称和 <code>APP ID</code> 参数!';
132 } elseif ($errorCode == ErrorCode::ACCESS_DENIED) {
133 $message = '<code>SecretID</code> 或 <code>SecretKey</code> 有误,请检查�
134 �置信息!';
135 }
136 } catch (\Throwable $e) {
137 $message = (string)$e;
138 }
139
140 echo "<div class='error'><p><strong>{$message}</strong></p></div>";
141 return false;
142 }
143
144 /**
145 * @param Client $client
146 * @param string $bucket
147 * @return Client
148 */
149 function cos_replace_client_region($client, $bucket)
150 {
151 if ($client->getCosConfig('region') != 'accelerate') {
152 return $client;
153 }
154
155 $list = $client->listBuckets();
156 $buckets = $list['Buckets'][0]['Bucket'];
157 $buckets = array_column($buckets, null, 'Name');
158
159 if (isset($buckets[$bucket])) {
160 $client->setCosConfig('region', $buckets[$bucket]['Location']);
161 }
162
163 return $client;
164 }
165
166 $cos_options = get_option('cos_options', cos_get_default_options());
167 if (!empty($cos_options['origin_protect']) && esc_attr($cos_options['origin_protect']) === 'on' && !empty(esc_attr($cos_options['ci_style']))) {
168 add_filter('wp_get_attachment_url', 'cos_add_suffix_to_attachment_url', 10, 2);
169 add_filter('wp_get_attachment_thumb_url', 'cos_add_suffix_to_attachment_url', 10, 2);
170 add_filter('wp_get_original_image_url', 'cos_add_suffix_to_attachment_url', 10, 2);
171 add_filter('wp_prepare_attachment_for_js', 'cos_add_suffix_to_attachment', 10, 2);
172 add_filter('image_get_intermediate_size', 'cos_add_suffix_for_media_send_to_editor');
173 }
174
175 /**
176 * @param string $url
177 * @param int $post_id
178 * @return string
179 */
180 function cos_add_suffix_to_attachment_url($url, $post_id)
181 {
182 if (cos_is_image_type($url)) {
183 $url .= cos_get_image_style();
184 }
185
186 return $url;
187 }
188
189 /**
190 * @param array $response
191 * @param array $attachment
192 * @return array
193 */
194 function cos_add_suffix_to_attachment($response, $attachment)
195 {
196 if ($response['type'] != 'image') {
197 return $response;
198 }
199
200 $style = cos_get_image_style();
201 if (!empty($response['sizes'])) {
202 foreach ($response['sizes'] as $size_key => $size_file) {
203 if (cos_is_image_type($size_file['url'])) {
204 $response['sizes'][$size_key]['url'] .= $style;
205 }
206 }
207 }
208
209 if(!empty($response['originalImageURL'])) {
210 if (cos_is_image_type($response['originalImageURL'])) {
211 $response['originalImageURL'] .= $style;
212 }
213 }
214
215 return $response;
216 }
217
218 /**
219 * @param array $data
220 * @return array
221 */
222 function cos_add_suffix_for_media_send_to_editor($data)
223 {
224 // https://github.com/WordPress/wordpress-develop/blob/43d2455dc68072fdd43c3c800cc8c32590f23cbe/src/wp-includes/media.php#L239
225 if (cos_is_image_type($data['file'])) {
226 $data['file'] .= cos_get_image_style();
227 }
228
229 return $data;
230 }
231
232 /**
233 * @param string $url
234 * @return bool
235 */
236 function cos_is_image_type($url)
237 {
238 return (bool) preg_match('/\.(jpg|jpeg|jpe|gif|png|bmp|webp|heic|heif)$/i', $url);
239 }
240
241 /**
242 * @return string
243 */
244 function cos_get_image_style()
245 {
246 $cos_options = get_option('cos_options', cos_get_default_options());
247
248 return esc_attr($cos_options['ci_style']);
249 }
250
251 /**
252 * @param string $object
253 * @param string $filename
254 * @param bool $no_local_file
255 * @return bool
256 */
257 function cos_file_upload($object, $filename, $no_local_file = false)
258 {
259 //如果文件不存在,直接返回false
260 if (!@file_exists($filename)) {
261 return false;
262 }
263 $options = get_option('cos_options', cos_get_default_options());
264 $bucket = cos_get_bucket_name($options);
265 try {
266 $file = fopen($filename, 'rb');
267 if ($file) {
268 if (!empty($options['upload_subdirectory'])) {
269 $object = '/' . esc_attr($options['upload_subdirectory']) . $object;
270 }
271 $cosClient = cos_get_client($options);
272 $cosClient->upload($bucket, $object, $file);
273
274 if (is_resource($file)) {
275 fclose($file);
276 }
277
278 if ($no_local_file) {
279 cos_delete_local_file($filename);
280 }
281
282 return true;
283 }
284 } catch (\Throwable $e) {
285 error_log($e->getMessage());
286 }
287
288 return false;
289 }
290
291 /**
292 * 是否需要删除本地文件
293 *
294 * @return bool
295 */
296 function cos_is_delete_local_file()
297 {
298 $cos_options = get_option('cos_options', cos_get_default_options());
299 return esc_attr($cos_options['nolocalsaving']) == 'true';
300 }
301
302 /**
303 * 删除本地文件
304 *
305 * @param string $file
306 * @return bool
307 */
308 function cos_delete_local_file($file)
309 {
310 try {
311 //文件不存在
312 if (!@file_exists($file)) {
313 return true;
314 }
315
316 //删除文件
317 if (!@unlink($file)) {
318 return false;
319 }
320
321 return true;
322 } catch (Exception $ex) {
323 return false;
324 }
325 }
326
327 /**
328 * 删除cos中的单个文件
329 * @param string $file
330 */
331 function cos_delete_cos_file($file)
332 {
333 $options = get_option('cos_options', cos_get_default_options());
334 $bucket = cos_get_bucket_name($options);
335 $cosClient = cos_get_client($options);
336 if (!empty($options['upload_subdirectory'])) {
337 $file = esc_attr($options['upload_subdirectory']) . '/' . ltrim($file, '/');
338 }
339 $cosClient->deleteObject(['Bucket' => $bucket, 'Key' => $file]);
340 }
341
342 /**
343 * 批量删除cos中的文件
344 * @param array $files
345 */
346 function cos_delete_cos_files(array $files)
347 {
348 $options = get_option('cos_options', cos_get_default_options());
349 $subdirectory = !empty($options['upload_subdirectory']) ? esc_attr($options['upload_subdirectory']) . '/' : '';
350
351 $deleteObjects = [];
352 foreach ($files as $file) {
353 $fileKey = str_replace(["\\", './'], ['/', ''], $subdirectory . $file);
354 $deleteObjects[] = ['Key' => $fileKey];
355 }
356
357 $bucket = cos_get_bucket_name($options);
358 $cosClient = cos_get_client($options);
359 $cosClient->deleteObjects(['Bucket' => $bucket, 'Objects' => $deleteObjects]);
360 }
361
362 function cos_get_option($key)
363 {
364 return esc_attr(get_option($key));
365 }
366
367 /**
368 * 上传附件(�
369 括图片的原图)
370 *
371 * @param $metadata
372 * @return array
373 */
374 function cos_upload_attachments($metadata)
375 {
376 $mime_types = wp_get_mime_types();
377 $image_mime_types = [
378 $mime_types['jpg|jpeg|jpe'],
379 $mime_types['gif'],
380 $mime_types['png'],
381 $mime_types['bmp'],
382 $mime_types['tiff|tif'],
383 $mime_types['webp'],
384 $mime_types['ico'],
385 ];
386 // 例如mp4等格式 上传后根据�
387 �置选择是否删除 删除后媒体库会显示默认图片 点开�
388 容是正常的
389 // 图片在缩略图处理
390 if (!in_array($metadata['type'], $image_mime_types)) {
391 //生成object在COS中的存储路径
392 if (cos_get_option('upload_path') == '.') {
393 $metadata['file'] = str_replace('./', '', $metadata['file']);
394 }
395 $object = str_replace("\\", '/', $metadata['file']);
396 $home_path = get_home_path();
397 $object = str_replace($home_path, '', $object);
398
399 //在本地的存储路径
400 $file = $home_path . $object; //向上�
401 �容,较早的WordPress版本上$metadata['file']存放的是相对路径
402 //执行上传操作
403 cos_file_upload('/' . $object, $file, cos_is_delete_local_file());
404 }
405
406 return $metadata;
407 }
408
409 //避�
410 �上传插件/主题时出现同步到COS的�
411
412 if (substr_count($_SERVER['REQUEST_URI'], '/update.php') <= 0) {
413 add_filter('wp_handle_upload', 'cos_upload_attachments', 50);
414 add_filter('wp_generate_attachment_metadata', 'cos_upload_thumbs', 100);
415 add_filter('wp_save_image_editor_file', 'cos_save_image_editor_file', 101);
416 }
417
418 /**
419 * 上传图片的缩略图
420 */
421 function cos_upload_thumbs($metadata)
422 {
423 if (empty($metadata['file'])) {
424 return $metadata;
425 }
426
427 //获取上传路径
428 $wp_uploads = wp_upload_dir();
429 $basedir = $wp_uploads['basedir'];
430 $upload_path = cos_get_option('upload_path');
431
432 $cos_options = get_option('cos_options', cos_get_default_options());
433 $no_local_file = esc_attr($cos_options['nolocalsaving']) == 'true';
434 $no_thumb = esc_attr($cos_options['nothumb']) == 'true';
435
436 // Maybe there is a problem with the old version
437 $file = $basedir . '/' . $metadata['file'];
438 if ($upload_path != '.') {
439 $path_array = explode($upload_path, $file);
440 if (count($path_array) >= 2) {
441 $object = '/' . $upload_path . end($path_array);
442 }
443 } else {
444 $object = '/' . $metadata['file'];
445 $file = str_replace('./', '', $file);
446 }
447
448 cos_file_upload($object, $file, $no_local_file);
449
450 //得到本地文件夹和远端文件夹
451 $dirname = dirname($metadata['file']);
452 $file_path = $dirname != '.' ? "{$basedir}/{$dirname}/" : "{$basedir}/";
453 $file_path = str_replace("\\", '/', $file_path);
454 if ($upload_path == '.') {
455 $file_path = str_replace('./', '', $file_path);
456 }
457 $object_path = str_replace(get_home_path(), '', $file_path);
458
459 if (!empty($metadata['original_image'])) {
460 cos_file_upload("/{$object_path}{$metadata['original_image']}", "{$file_path}{$metadata['original_image']}", $no_local_file);
461 }
462
463 //如果禁止上传缩略图,就不用继续执行了
464 if ($no_thumb) {
465 return $metadata;
466 }
467
468 //上传所有缩略图
469 if (!empty($metadata['sizes'])) {
470 //there may be duplicated filenames,so ....
471 foreach ($metadata['sizes'] as $val) {
472 //生成object在COS中的存储路径
473 $object = '/' . $object_path . $val['file'];
474 //生成本地存储路径
475 $file = $file_path . $val['file'];
476
477 cos_file_upload($object, $file, $no_local_file);
478 }
479 }
480
481 return $metadata;
482 }
483
484 /**
485 * @param $override
486 * @return mixed
487 */
488 function cos_save_image_editor_file($override)
489 {
490 add_filter('wp_update_attachment_metadata', 'cos_image_editor_file_do');
491 return $override;
492 }
493
494 /**
495 * @param $metadata
496 * @return mixed
497 */
498 function cos_image_editor_file_do($metadata)
499 {
500 return cos_upload_thumbs($metadata);
501 }
502
503 /**
504 * 删除远端文件,删除文件时触发
505 * @param $post_id
506 */
507 function cos_delete_remote_attachment($post_id)
508 {
509 $wp_uploads = wp_upload_dir();
510 $basedir = $wp_uploads['basedir'];
511 $upload_path = str_replace(get_home_path(), '', $basedir);
512 // 获取图片类附件的meta信息
513 $meta = wp_get_attachment_metadata($post_id);
514
515 if (!empty($meta['file'])) {
516 $deleteObjects = [];
517
518 // meta['file']的格式为 "2020/01/wp-bg.png"
519 $file_path = $upload_path . '/' . $meta['file'];
520 $dirname = dirname($file_path) . '/';
521
522 $deleteObjects[] = $file_path;
523
524 // �
525 大图原图
526 if (!empty($meta['original_image'])) {
527 $deleteObjects[] = $dirname . $meta['original_image'];
528 }
529
530 // 删除缩略图
531 if (!empty($meta['sizes'])) {
532 foreach ($meta['sizes'] as $val) {
533 $deleteObjects[] = $dirname . $val['file'];
534 }
535 }
536
537 $backup_sizes = get_post_meta($post_id, '_wp_attachment_backup_sizes', true);
538 if (is_array($backup_sizes)) {
539 foreach ($backup_sizes as $size) {
540 $deleteObjects[] = $dirname . $size['file'];
541 }
542 }
543
544 cos_delete_cos_files($deleteObjects);
545 } else {
546 // 获取链接删除
547 $link = wp_get_attachment_url($post_id);
548 if ($link) {
549 $cos_options = get_option('cos_options', cos_get_default_options());
550 $subdirectory = !empty($cos_options['upload_subdirectory']) ? '/' . esc_attr($cos_options['upload_subdirectory']) : '';
551 if ($upload_path != '.') {
552 $file_info = explode($upload_path, $link);
553 if (count($file_info) >= 2) {
554 $file = $subdirectory . $upload_path . end($file_info);
555 }
556 } else {
557 $cos_upload_url = esc_attr($cos_options['upload_url_path']);
558 $file_info = explode($cos_upload_url, $link);
559 if (count($file_info) >= 2) {
560 $file = $subdirectory . end($file_info);
561 }
562 }
563
564 cos_delete_cos_file($file);
565 }
566 }
567 }
568
569 add_action('delete_attachment', 'cos_delete_remote_attachment');
570
571 // 当upload_path为根目录时,需要移除URL中出现的“绝对路径”
572 function cos_modify_img_url($url, $post_id)
573 {
574 // 移除 ./ 和 项目根路径
575 return str_replace(['./', get_home_path()], '', $url);
576 }
577
578 if (cos_get_option('upload_path') == '.') {
579 add_filter('wp_get_attachment_url', 'cos_modify_img_url', 30, 2);
580 }
581
582 function cos_sanitize_file_name($filename)
583 {
584 $cos_options = get_option('cos_options', cos_get_default_options());
585 switch ($cos_options['update_file_name']) {
586 case 'md5':
587 return md5($filename) . '.' . pathinfo($filename, PATHINFO_EXTENSION);
588 case 'time':
589 return gmdate('YmdHis', current_time('timestamp')) . wp_rand(100, 999) . '.' . pathinfo($filename, PATHINFO_EXTENSION);
590 default:
591 return $filename;
592 }
593 }
594
595 add_filter('sanitize_file_name', 'cos_sanitize_file_name', 10, 1);
596
597 /**
598 * @param string $homePath
599 * @param string $uploadPath
600 * @return array
601 */
602 function cos_read_dir_queue($homePath, $uploadPath)
603 {
604 $dir = $homePath . $uploadPath;
605 $dirsToProcess = new SplQueue();
606 $dirsToProcess->enqueue([$dir, '']);
607 $foundFiles = [];
608
609 while (!$dirsToProcess->isEmpty()) {
610 [$currentDir, $relativeDir] = $dirsToProcess->dequeue();
611
612 foreach (new DirectoryIterator($currentDir) as $fileInfo) {
613 if ($fileInfo->isDot()) continue;
614
615 $filepath = $fileInfo->getRealPath();
616
617 // Compute the relative path of the file/directory with respect to upload path
618 $currentRelativeDir = "{$relativeDir}/{$fileInfo->getFilename()}";
619
620 if ($fileInfo->isDir()) {
621 $dirsToProcess->enqueue([$filepath, $currentRelativeDir]);
622 } else {
623 // Add file path and key to the result array
624 $foundFiles[] = [
625 'filepath' => $filepath,
626 'key' => '/' . $uploadPath . $currentRelativeDir
627 ];
628 }
629 }
630 }
631
632 return $foundFiles;
633 }
634
635 // 在插件列表页添加设置按钮
636 function cos_plugin_action_links($links, $file)
637 {
638 if ($file == urldecode(COS_PLUGIN_PAGE)) {
639 $page = COS_PLUGIN_SLUG;
640 $links[] = "<a href='admin.php?page={$page}'>设置</a>";
641 }
642 return $links;
643 }
644
645 add_filter('plugin_action_links', 'cos_plugin_action_links', 10, 2);
646
647 add_filter('the_content', 'cos_setting_content_ci');
648 add_filter('post_thumbnail_html', 'cos_setting_post_thumbnail_ci', 10, 3);
649 add_filter('wp_calculate_image_srcset', 'cos_custom_image_srcset', 10, 5);
650 add_filter('wp_prepare_attachment_for_js', 'cos_wp_prepare_attachment_for_js');
651
652 function cos_wp_prepare_attachment_for_js($response)
653 {
654 if (empty($response['filesizeInBytes']) || empty($response['filesizeHumanReadable'])) {
655 $cos_options = get_option('cos_options', cos_get_default_options());
656 $upload_url_path = esc_attr($cos_options['upload_url_path']);
657 $upload_path = get_option('upload_path');
658 $object = str_replace($upload_url_path, $upload_path, $response['url']);
659 $contentLength = Head::getContentLength(cos_get_client($cos_options), cos_get_bucket_name($cos_options), $object);
660 if (!empty($contentLength)) {
661 $response['filesizeInBytes'] = $contentLength;
662 $response['filesizeHumanReadable'] = size_format($contentLength);
663 }
664 }
665
666 return $response;
667 }
668
669 function cos_custom_image_srcset($sources, $size_array, $image_src, $image_meta, $attachment_id)
670 {
671 $option = get_option('cos_options', cos_get_default_options());
672 $style = !empty($option['ci_style']) ? esc_attr($option['ci_style']) : '';
673 $upload_url_path = esc_attr($option['upload_url_path']);
674 if (empty($style)) {
675 return $sources;
676 }
677
678 foreach ($sources as $index => $source) {
679 if (strpos($source['url'], $upload_url_path) !== false && substr($source['url'], -strlen($style)) !== $style) {
680 $sources[$index]['url'] .= $style;
681 }
682 }
683
684 return $sources;
685 }
686
687 function cos_setting_content_ci($content)
688 {
689 $option = get_option('cos_options', cos_get_default_options());
690 $style = esc_attr($option['ci_style']);
691 $upload_url_path = esc_attr($option['upload_url_path']);
692 if (!empty($style)) {
693 preg_match_all('/<img.*?(?: |\\t|\\r|\\n)?src=[\'"]?(.+?)[\'"]?(?:(?: |\\t|\\r|\\n)+.*?)?>/sim', $content, $images);
694 if (!empty($images) && isset($images[1])) {
695 $images[1] = array_unique($images[1]);
696 foreach ($images[1] as $item) {
697 if (strpos($item, $upload_url_path) !== false && substr($item, -strlen($style)) !== $style) {
698 $content = str_replace($item, $item . $style, $content);
699 }
700 }
701
702 $content = str_replace($style . $style, $style, $content);
703 }
704 }
705
706 if (!empty($option['attachment_preview']) && $option['attachment_preview'] == 'on') {
707 preg_match_all('/<a.*?href="(.*?)".*?\/a>/is', $content, $matches);
708 if (!empty($matches)) {
709 [$tags, $links] = $matches;
710 $handledLinks = [];
711 foreach ($links as $index => $link) {
712 if (in_array($link, $handledLinks)) {
713 continue;
714 }
715
716 if (FilePreview::isFileExtensionSupported($link, $option['upload_url_path'])) {
717 $iframe = '<iframe src="' . $link . '?ci-process=doc-preview&dstType=html" width="100%" allowFullScreen="true" height="800"></iframe>';
718 $content = str_replace($tags[$index], $iframe, $content);
719 $handledLinks[] = $link;
720 }
721 }
722 }
723 }
724
725 return $content;
726 }
727
728 function cos_setting_post_thumbnail_ci($html, $post_id, $post_image_id)
729 {
730 $option = get_option('cos_options', cos_get_default_options());
731 $style = esc_attr($option['ci_style']);
732 $upload_url_path = esc_attr($option['upload_url_path']);
733 if (!empty($style) && has_post_thumbnail()) {
734 preg_match_all('/<img.*?(?: |\\t|\\r|\\n)?src=[\'"]?(.+?)[\'"]?(?:(?: |\\t|\\r|\\n)+.*?)?>/sim', $html, $images);
735 if (!empty($images) && isset($images[1])) {
736 $images[1] = array_unique($images[1]);
737 foreach ($images[1] as $item) {
738 if (strpos($item, $upload_url_path) !== false && substr($item, -strlen($style)) !== $style) {
739 $html = str_replace($item, $item . $style, $html);
740 }
741 }
742
743 $html = str_replace($style . $style, $style, $html);
744 }
745 }
746 return $html;
747 }
748
749 /**
750 * @param array $options
751 * @return array
752 */
753 function cos_append_options($options)
754 {
755 $cos_options = get_option('cos_options', cos_get_default_options());
756
757 $options['ci_image_slim'] = $cos_options['ci_image_slim'] ?? 'off';
758 $options['ci_image_slim_mode'] = $cos_options['ci_image_slim_mode'] ?? '';
759 $options['ci_image_slim_suffix'] = $cos_options['ci_image_slim_suffix'] ?? '';
760 $options['attachment_preview'] = $cos_options['attachment_preview'] ?? 'off';
761 $options['ci_text_comments'] = $cos_options['ci_text_comments'] ?? 'off';
762 $options['skip_comment_validation_on_login'] = $cos_options['skip_comment_validation_on_login'] ?? 'off';
763 $options['ci_text_comments_strategy'] = $cos_options['ci_text_comments_strategy'] ?? '';
764 $options['ci_text_comments_check_roles'] = $cos_options['ci_text_comments_check_roles'] ?? '';
765
766 return $options;
767 }
768
769 /**
770 * @param array $parametersToUpdate
771 * @param array|null $currentParameters
772 * @return array
773 */
774 function cos_update_config_parameters($parametersToUpdate, $currentOptions = null)
775 {
776 $currentOptions = $currentOptions ?: get_option('cos_options', cos_get_default_options());
777
778 $options = array_merge($currentOptions, $parametersToUpdate);
779
780 update_option('cos_options', $options);
781
782 return $options;
783 }
784
785 /**
786 * @param array $slimConfigData
787 * @param array $currentOptions
788 * @return array
789 */
790 function cos_sync_image_slim_config($slimConfigData, $currentOptions)
791 {
792 $sanitizedConfig = [
793 'ci_image_slim' => sanitize_text_field($slimConfigData['Status']),
794 'ci_image_slim_mode' => sanitize_text_field($slimConfigData['SlimMode']),
795 'ci_image_slim_suffix' => implode(',', ($slimConfigData['Suffixs']['Suffix'] ?? []))
796 ];
797
798 return cos_update_config_parameters($sanitizedConfig, $currentOptions);
799 }
800
801 /**
802 * @param string $url
803 * @param array|null $options
804 * @return string
805 */
806 function cos_append_ci_style($url, $options = null)
807 {
808 if (empty($options)) $options = get_option('cos_options', cos_get_default_options());
809
810 if (!empty($options['ci_style']) && !empty($options['upload_url_path']) && strpos($url, esc_attr($options['upload_url_path'])) !== false) {
811 $url .= esc_attr($options['ci_style']);
812 }
813
814 return $url;
815 }
816
817 /**
818 * @param string $url
819 * @param array|null $options
820 * @return string
821 */
822 function cos_local2remote($url, $options = null)
823 {
824 if (empty($options)) $options = get_option('cos_options', cos_get_default_options());
825
826 $upload_path = get_option('upload_path');
827
828 if ($upload_path != '.' && !empty($options['upload_url_path']) && strpos($url, $upload_path) !== false) {
829 return $options['upload_url_path'] . explode($upload_path, $url)[1];
830 }
831
832 return $url;
833 }
834
835 function cos_get_regional($regional)
836 {
837 $options = [
838 'ap-beijing-1' => ['tj', '北京一区(华北)'],
839 'ap-beijing' => ['bj', '北京'],
840 'ap-nanjing' => ['ap-nanjing', '南京'],
841 'ap-shanghai' => ['sh', '上海(华东)'],
842 'ap-guangzhou' => ['gz', '广州(华南)'],
843 'ap-chengdu' => ['cd', '成都(西南)'],
844 'ap-chongqing' => ['ap-chongqing', '重庆'],
845 'ap-shenzhen-fsi' => ['ap-shenzhen-fsi', '深圳金融'],
846 'ap-shanghai-fsi' => ['ap-shanghai-fsi', '上海金融'],
847 'ap-beijing-fsi' => ['ap-beijing-fsi', '北京金融'],
848
849 'ap-hongkong' => ['hk', '中国香港'],
850 'ap-singapore' => ['sgp', '新加坡'],
851 'ap-mumbai' => ['ap-mumbai', '孟买'],
852 'ap-jakarta' => ['ap-jakarta', '�
853 加达'],
854 'ap-seoul' => ['ap-seoul', '首尔'],
855 'ap-bangkok' => ['ap-bangkok', '曼谷'],
856 'ap-tokyo' => ['ap-tokyo', '东京'],
857
858 'na-siliconvalley' => ['na-siliconvalley', '�
859 谷(美西)'],
860 'na-ashburn' => ['na-ashburn', '弗吉尼亚(美东)'],
861 'na-toronto' => ['ca', '多伦多'],
862
863 'sa-saopaulo' => ['sa-saopaulo', '圣保罗'],
864
865 'eu-frankfurt' => ['ger', '法�
866 ��
867 �福'],
868
869 'eu-moscow' => ['eu-moscow', '莫斯科'],
870
871 'accelerate' => ['accelerate', '�
872 �球加速']
873 ];
874
875 foreach ($options as $value => $info) {
876 $selected = ($regional == $info[0] || $regional == $value) ? ' selected="selected"' : '';
877 echo '<option value="' . $value . '"' . $selected . '>' . $info[1] . '</option>';
878 }
879 }
880
881 /**
882 * Generate URL scheme
883 *
884 * Decides whether 'http' or 'https' should be used based on the server configuration.
885 *
886 * @param string $separator separator used between the schema and the rest of the URL.
887 * @return string 'http' or 'https' followed by the separator.
888 */
889 function cos_get_url_scheme($separator = '://')
890 {
891 $isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443);
892 $scheme = $isHttps ? 'https' : 'http';
893
894 return $scheme . $separator;
895 }
896
897 function cos_sync_setting_form($cos_options)
898 {
899 $protocol = cos_get_url_scheme();
900
901 $upload_path = cos_get_option('upload_path');
902 $upload_path = $upload_path == '.' ? '' : $upload_path;
903
904 $old_url = "{$protocol}{$_SERVER['HTTP_HOST']}/{$upload_path}";
905 $new_url = $cos_options['upload_url_path'];
906 if (!empty($upload_path) && strpos($new_url, $upload_path) === false) {
907 $new_url = "{$new_url}/{$upload_path}";
908 }
909
910 $nonce = wp_nonce_field('qcloud_cos_replace', 'qcloud_cos_replace-nonce', true, false);
911 $replaceNonce = wp_nonce_field('qcloud_cos_all', 'qcloud_cos_all-nonce', true, false);
912
913 return <<<HTML
914 <form method="post">
915 <table class="form-table">
916 <tr>
917 <th>
918 <legend>数据库�
919 容替换</legend>
920 </th>
921 <td>
922 <input type="text" required name="old_url" size="50" placeholder="请输�
923 �要替换的�
924 容"/>
925 <p><b>可能会填�
926 �:<code>{$old_url}</code></b></p>
927 <p>例如:<code>https://qq52o.me/wp-content/uploads</code></p>
928 </td>
929 </tr>
930 <tr>
931 <th>
932 <legend></legend>
933 </th>
934 <td>
935 <input type="text" name="new_url" size="50" placeholder="请输�
936 �要替换为的�
937 容"/>
938 <p><b>可能会填�
939 �:<code>{$new_url}</code></b></p>
940 <p>例如:COS访问域名<code>https://bucket-appid.cos.ap-xxx.myqcloud.com/wp-content/uploads</code>或自定义域名<code>https://resources.qq52o.me/wp-content/uploads</code></p>
941 </td>
942 </tr>
943 <tr>
944 <th>
945 <legend></legend>
946 </th>
947 <input type="hidden" name="type" value="qcloud_cos_replace">
948 {$nonce}
949 <td>
950 <input type="submit" class="button button-secondary" value="开始替换"/>
951 <p><b>注意:如果是首次替换,请注意备份!此功能会替换文章以及设置的特色图片(题图)等使用的资源链接,也可用于�
952 �他需要替换文章�
953 容的场景。</b></p>
954 </td>
955 </tr>
956 </table>
957 </form>
958 <form method="post">
959 <table class="form-table">
960 <tr>
961 <th>
962 <legend>同步历史附件</legend>
963 </th>
964 <input type="hidden" name="type" value="qcloud_cos_all">
965 {$replaceNonce}
966 <td>
967 <input type="submit" class="button button-secondary" value="开始同步"/>
968 <p><b>注意:如果是首次同步,执行时间将会非常长(根据你的历史附件数量),有可能会因为执行时间过长,导致页面显示�
969 时或�
970 报错。<br> 所以建议附件数量过多的用户,直接使用官方的<a target="_blank" rel="nofollow" href="https://cloud.tencent.com/document/product/436/63143">COSCLI 工�
971 �</a>进行迁移,�
972 �体可参考<a target="_blank" rel="nofollow" href="https://qq52o.me/2809.html">使用 COSCLI 快速迁移本地数据到 COS</a></b></p>
973 </td>
974 </tr>
975 </table>
976 </form>
977 HTML;
978 }
979
980 /**
981 * @param array $content
982 * @return bool
983 */
984 function cos_ci_image_slim_setting($content)
985 {
986 $cos_options = get_option('cos_options', cos_get_default_options());
987
988 if (!cos_validate_configuration($cos_options)) {
989 return false;
990 }
991
992 $slim = !empty($content['ci_image_slim']) ? sanitize_text_field($content['ci_image_slim']) : 'off';
993 $mode = !empty($content['ci_image_slim_mode']) ? implode(',', $content['ci_image_slim_mode']) : '';
994 $suffix = !empty($content['ci_image_slim_suffix']) ? implode(',', $content['ci_image_slim_suffix']) : '';
995
996 if ($slim == 'on') {
997 if (empty($mode)) {
998 echo '<div class="error"><p><strong>图片极智压缩模式不能为空!</strong></p></div>';
999 return false;
1000 }
1001 if (strpos($mode, 'Auto') !== false && empty($suffix)) {
1002 echo '<div class="error"><p><strong>图片极智压缩使用模式�
1003 含自动时,图片格式不能为空!</strong></p></div>';
1004 return false;
1005 }
1006 }
1007
1008 try {
1009 $client = cos_get_client($cos_options);
1010 $bucket = cos_get_bucket_name($cos_options);
1011 $client = cos_replace_client_region($client, $bucket);
1012 ImageSlim::checkStatus($client, $bucket);
1013 if ($slim == 'on') {
1014 ImageSlim::open($client, $bucket, $mode, $suffix);
1015 } else {
1016 ImageSlim::close($client, $bucket);
1017 }
1018 } catch (ServiceResponseException $e) {
1019 $msg = (string)$e;
1020 if ($e->getExceptionCode() === ErrorCode::NO_BIND_CI) {
1021 $msg = "存储桶 {$bucket} 未绑定数据万象,若要开启极智压缩,请�
1022 � <a href='https://console.cloud.tencent.com/ci' target='_blank'>绑定数据万象服务</a >";
1023 }
1024 if ($e->getExceptionCode() === ErrorCode::REGION_UNSUPPORTED) {
1025 $msg = "存储桶所在地域 {$cos_options['regional']} 暂不支持图片极智压缩";
1026 }
1027 echo "<div class='error'><p><strong>{$msg}</strong></p></div>";
1028 return false;
1029 } catch (\Throwable $e) {
1030 $message = (string)$e;
1031 echo "<div class='error'><p><strong>{$message}</strong></p></div>";
1032 return false;
1033 }
1034
1035 $cos_options['ci_image_slim'] = $slim;
1036 $cos_options['ci_image_slim_mode'] = $mode;
1037 $cos_options['ci_image_slim_suffix'] = $suffix;
1038
1039 update_option('cos_options', $cos_options);
1040
1041 echo '<div class="updated"><p><strong>图片极智压缩设置已保存!</strong></p></div>';
1042 return true;
1043 }
1044
1045 function cos_ci_image_slim_page($options)
1046 {
1047 cos_validate_configuration($options);
1048
1049 $ci_image_slim = esc_attr($options['ci_image_slim']);
1050 $checked_ci_image_slim = $ci_image_slim == 'on' ? 'checked="checked"' : '';
1051 $ci_image_slim_mode = explode(',', esc_attr($options['ci_image_slim_mode']));
1052 $checked_mode_api = in_array('API', $ci_image_slim_mode) ? 'checked="checked"' : '';
1053 $checked_mode_auto = in_array('Auto', $ci_image_slim_mode) ? 'checked="checked"' : '';
1054 $ci_image_slim_suffix = explode(',', esc_attr($options['ci_image_slim_suffix']));
1055 $checked_suffix_jpg = in_array('jpg', $ci_image_slim_suffix) ? 'checked="checked"' : '';
1056 $checked_suffix_png = in_array('png', $ci_image_slim_suffix) ? 'checked="checked"' : '';
1057 $checked_suffix_gif = in_array('gif', $ci_image_slim_suffix) ? 'checked="checked"' : '';
1058
1059 $remoteStatus = '';
1060 if (!empty($options['bucket']) && !empty($options['app_id']) && !empty($options['secret_id']) && !empty($options['secret_key'])) {
1061 try {
1062 $bucket = cos_get_bucket_name($options);
1063 $client = cos_get_client($options);
1064 $client = cos_replace_client_region($client, $bucket);
1065 $result = ImageSlim::checkStatus($client, $bucket);
1066 cos_sync_image_slim_config($result, $options);
1067 $status = $result['Status'];
1068
1069 $checked_ci_image_slim = $status == 'on' ? 'checked="checked"' : '';
1070 $remoteStatus = $status == 'on' ? '云端状态:<span class="open">已开启</span>' : '云端状态:<span class="close">已�
1071 �闭</span>';
1072
1073 $remoteMode = explode(',', $result['SlimMode']);
1074 $checked_mode_api = in_array('API', $remoteMode) ? 'checked="checked"' : '';
1075 $checked_mode_auto = in_array('Auto', $remoteMode) ? 'checked="checked"' : '';
1076 } catch (ServiceResponseException $e) {
1077 $msg = (string)$e;
1078 if ($e->getExceptionCode() === ErrorCode::NO_BIND_CI) {
1079 $msg = "存储桶 {$bucket} 未绑定数据万象,若要开启极智压缩,请�
1080 � <a href='https://console.cloud.tencent.com/ci' target='_blank'>绑定数据万象服务</a>";
1081 }
1082 if ($e->getExceptionCode() === ErrorCode::REGION_UNSUPPORTED) {
1083 $msg = "存储桶所在地域 '{$options['regional']}' 暂不支持图片极智压缩";
1084 }
1085 echo "<div class='error'><p><strong>{$msg}</strong></p></div>";
1086 } catch (\Throwable $e) {
1087 $message = (string)$e;
1088 echo "<div class='error'><p><strong>{$message}</strong></p></div>";
1089 }
1090 }
1091
1092 $nonce = wp_nonce_field('qcloud_cos_ci_image_slim', 'qcloud_cos_ci_image_slim-nonce', true, false);
1093
1094 return <<<EOF
1095 <form method="post">
1096 <table class="form-table">
1097 <tr>
1098 <th>
1099 <legend>简介</legend>
1100 </th>
1101 <td>
1102 <p>图片极智压缩开启后,通过智能判断图片的主观质量进行自动调节,在不改变图片原格式的基础上,使图片体积相比原图有显著的降低,<br> 同时在视觉效果上可以最大程度贴近原图。更多详�
1103 请查看:<a href="https://cloud.tencent.com/document/product/460/86438" target="_blank">�
1104 �讯云文档</a></p>
1105 </td>
1106 </tr>
1107 <tr>
1108 <th>
1109 <legend>是否启用</legend>
1110 </th>
1111 <td>
1112 <label class="switch">
1113 <input type="checkbox" name="ci_image_slim" {$checked_ci_image_slim} />
1114 <span class="slider round"></span>
1115 </label>
1116 <p>{$remoteStatus}</p>
1117 <p>极智压缩支持两种模式自动压缩和API模式,请按需选择。</p>
1118 </td>
1119 </tr>
1120 <tr>
1121 <th>
1122 <legend>自动压缩</legend>
1123 </th>
1124 <td>
1125 <label class="switch">
1126 <input type="checkbox" name="ci_image_slim_mode[]" value="Auto" {$checked_mode_auto} />
1127 <span class="slider round"></span>
1128 </label>
1129
1130 <p>开通极智压缩的自动使用方式,开通后无需携带任何参数,存储桶�
1131 指定格式的图片将在访问时自动进行极智压缩。</p>
1132 <p>需要选择自动进行压缩的图片格式:</p>
1133 <input type="checkbox" name="ci_image_slim_suffix[]" value="jpg" {$checked_suffix_jpg} /> jpg(�
1134 含<code>jpg</code>、<code>jpeg</code>)<br>
1135 <input type="checkbox" name="ci_image_slim_suffix[]" value="png" {$checked_suffix_png} /> png <br>
1136 <input type="checkbox" name="ci_image_slim_suffix[]" value="gif" {$checked_suffix_gif} /> gif <br>
1137 </td>
1138 </tr>
1139 <tr>
1140 <th>
1141 <legend>API 模式</legend>
1142 </th>
1143 <td>
1144 <label class="switch">
1145 <input type="checkbox" name="ci_image_slim_mode[]" value="API" {$checked_mode_api} />
1146 <span class="slider round"></span>
1147 </label>
1148 <p>开通极智压缩的 API 使用方式,开通后可在图片时通过极智压缩参数(需要�
1149 �置图片处理样式<code>?imageSlim</code>)对图片进行压缩;</p>
1150 </td>
1151 </tr>
1152 <tr>
1153 <th></th>
1154 <input type="hidden" name="type" value="qcloud_cos_ci_image_slim">
1155 {$nonce}
1156 <td><input type="submit" class="button button-primary" value="保存"/></td>
1157 </tr>
1158 </table>
1159 </form>
1160 EOF;
1161 }
1162
1163 function cos_get_user_roles()
1164 {
1165 $result = [];
1166
1167 $editable_roles = array_reverse(get_editable_roles());
1168 foreach ($editable_roles as $role => $details) {
1169 $result[$role] = translate_user_role($details['name']);
1170 }
1171
1172 return $result;
1173 }
1174
1175 function cos_ci_text_page($options)
1176 {
1177 cos_validate_configuration($options);
1178
1179 $checked_skip_comment_validation_on_login = esc_attr($options['skip_comment_validation_on_login'] ?? 'off') !== 'off' ? 'checked="checked"' : '';
1180 $checked_text_comments = esc_attr($options['ci_text_comments'] ?? 'off') !== 'off' ? 'checked="checked"' : '';
1181 $ci_text_comments_strategy = esc_attr($options['ci_text_comments_strategy'] ?? '');
1182
1183 $roles = cos_get_user_roles();
1184 $check_roles = explode(',', esc_attr($options['ci_text_comments_check_roles'] ?? ''));
1185 $select_roles = '';
1186 foreach ($roles as $role => $name) {
1187 $check = '';
1188 if (in_array($role, $check_roles)) {
1189 $check = 'checked="checked"';
1190 }
1191 $select_roles .= '<input type="checkbox" name="ci_text_comments_check_roles[]" value="' . $role . '" ' . $check . '>' . $name . '<br>';
1192 }
1193
1194 $nonce = wp_nonce_field('qcloud_cos_ci_text', 'qcloud_cos_ci_text-nonce', true, false);
1195
1196 return <<<EOF
1197 <form method="post">
1198 <table class="form-table">
1199 <tr>
1200 <th>
1201 <legend>评论审核</legend>
1202 </th>
1203 <td>
1204 <label class="switch">
1205 <input type="checkbox" name="ci_text_comments" {$checked_text_comments} />
1206 <span class="slider round"></span>
1207 </label>
1208 </td>
1209 </tr>
1210 <tr>
1211 <th>
1212 <legend>评论审核策略</legend>
1213 </th>
1214 <td>
1215 <input type="text" name="ci_text_comments_strategy" value="{$ci_text_comments_strategy}" size="50" placeholder="请填写对应的 Biztype 名称" />
1216 <p>填写需要使用的文本审核策略 <code>Biztype</code> 名称,为空时使用默认策略,详�
1217 查看 <a href="https://cloud.tencent.com/document/product/436/55206" target="_blank">设置审核策略</a>。</p>
1218 </td>
1219 </tr>
1220 <tr>
1221 <th>
1222 <legend>跳过登录态验证</legend>
1223 </th>
1224 <td>
1225 <label class="switch">
1226 <input type="checkbox" name="skip_comment_validation_on_login" {$checked_skip_comment_validation_on_login} />
1227 <span class="slider round"></span>
1228 </label>
1229 <p>启用后如果是登录态则会跳过该用户评论�
1230 容,不去验证。</p>
1231 </td>
1232 </tr>
1233 <tr>
1234 <th>
1235 <legend>需要验证的登录角色</legend>
1236 </th>
1237 <td>
1238 {$select_roles}
1239 <p>选择需要在登录态下验证的角色,选择后即使选择了<strong>跳过登录态验证</strong>,属于该角色的用户评论也会进行验证。</p>
1240 </td>
1241 </tr>
1242 <tr>
1243 <th></th>
1244 <input type="hidden" name="type" value="qcloud_cos_ci_text">
1245 {$nonce}
1246 <td><input type="submit" class="button button-primary" value="保存"/></td>
1247 </tr>
1248 </table>
1249 </form>
1250 EOF;
1251 }
1252
1253 function cos_ci_text_setting($content)
1254 {
1255 $cos_options = get_option('cos_options', cos_get_default_options());
1256 if (!cos_validate_configuration($cos_options)) {
1257 return false;
1258 }
1259
1260 $client = cos_get_client($cos_options);
1261 $bucket = cos_get_bucket_name($cos_options);
1262 $ciService = Service::checkStatus($client, $bucket);
1263 if (!$ciService) {
1264 echo "<div class='error'><p><strong>存储桶 {$bucket} 未绑定数据万象,若要开启文本审核,请�
1265 � <a href='https://console.cloud.tencent.com/ci' target='_blank'>绑定数据万象服务</a ></strong></p></div>";
1266 return false;
1267 }
1268
1269 $ci_text_comments = isset($content['ci_text_comments']) ? sanitize_text_field($content['ci_text_comments']) : 'off';
1270 $skip_comment_validation_on_login = isset($content['skip_comment_validation_on_login']) ? sanitize_text_field($content['skip_comment_validation_on_login']) : 'off';
1271 $ci_text_comments_strategy = isset($content['ci_text_comments_strategy']) ? sanitize_text_field($content['ci_text_comments_strategy']) : '';
1272 $ci_text_comments_check_roles = isset($content['ci_text_comments_check_roles']) ? implode(',', $content['ci_text_comments_check_roles']) : '';
1273
1274 $cos_options['ci_text_comments'] = $ci_text_comments;
1275 $cos_options['skip_comment_validation_on_login'] = $skip_comment_validation_on_login;
1276 $cos_options['ci_text_comments_strategy'] = $ci_text_comments_strategy;
1277 $cos_options['ci_text_comments_check_roles'] = $ci_text_comments_check_roles;
1278 update_option('cos_options', $cos_options);
1279
1280 echo '<div class="updated"><p><strong>文本审核设置已保存!</strong></p></div>';
1281 return true;
1282 }
1283
1284 function cos_contact_page()
1285 {
1286 return <<<EOF
1287 <table class="form-table">
1288 <tbody>
1289 <tr>
1290 <th>GitHub</th>
1291 <td><a href="https://github.com/sy-records" target="_blank">@sy-records</a></td>
1292 </tr>
1293 <tr>
1294 <th>QQ 群</th>
1295 <td>887595381 <a href="https://go.qq52o.me/qm/ccs" target="_blank">点击加�
1296 �</a></td>
1297 </tr>
1298 <tr>
1299 <th>微信�
1300 �众号</th>
1301 <td><img width="150px" src="https://open.weixin.qq.com/qr/code?username=sy-records" alt="鲁飞"></td>
1302 </tr>
1303 <tr>
1304 <th>打赏一杯咖啡或一杯香茗</th>
1305 <td><img height="290px" src="https://img.qq52o.me/staticfiles/donate.png?t=cos&f={$_SERVER['HTTP_HOST']}"></td>
1306 </tr>
1307 </tbody>
1308 </table>
1309 EOF;
1310 }
1311
1312 if (!function_exists('is_user_logged_in')) {
1313 require_once ABSPATH . WPINC . '/pluggable.php';
1314 }
1315
1316 function cos_process_comments($comment_data)
1317 {
1318 $options = get_option('cos_options', cos_get_default_options());
1319
1320 // If CI text for comments is not enabled
1321 if (($options['ci_text_comments'] ?? 'off') !== 'on') {
1322 return $comment_data;
1323 }
1324
1325 // If 'skip_comment_validation_on_login' option is not enabled
1326 if (($options['skip_comment_validation_on_login'] ?? 'off') !== 'on') {
1327 cos_request_txt_check($options, $comment_data['comment_content']);
1328 return $comment_data;
1329 }
1330
1331 // If User is not logged in
1332 if (!is_user_logged_in()) {
1333 cos_request_txt_check($options, $comment_data['comment_content']);
1334 return $comment_data;
1335 }
1336
1337 $roles = explode(',', $options['ci_text_comments_check_roles'] ?? '');
1338 global $current_user;
1339 // Check if one of the user roles is in the defined roles
1340 foreach ($roles as $role) {
1341 if (in_array($role, $current_user->roles)) {
1342 cos_request_txt_check($options, $comment_data['comment_content']);
1343 break;
1344 }
1345 }
1346
1347 return $comment_data;
1348 }
1349
1350 add_filter('preprocess_comment', 'cos_process_comments', 99);
1351
1352 function cos_request_txt_check($options, $comment)
1353 {
1354 $client = cos_get_client($options);
1355 $bucket = cos_get_bucket_name($options);
1356 $client = cos_replace_client_region($client, $bucket);
1357 $result = Audit::comment($client, $bucket, $comment, $options['ci_text_comments_strategy'] ?? '');
1358 if (!$result['state'] || $result['result'] === 2) {
1359 // 人工审核
1360 add_filter('pre_comment_approved', '__return_zero');
1361 }
1362
1363 if ($result['state'] && $result['result'] === 1) {
1364 wp_die("评论�
1365 容{$result['message']}涉嫌违规,请重新评论", 409);
1366 }
1367 }
1368
1369 /**
1370 * @param array $content
1371 * @return bool
1372 */
1373 function cos_ci_attachment_preview_setting($content)
1374 {
1375 $cos_options = get_option('cos_options', cos_get_default_options());
1376 if (!cos_validate_configuration($cos_options)) {
1377 return false;
1378 }
1379
1380 $attachment_preview = !empty($content['attachment_preview']) ? sanitize_text_field($content['attachment_preview']) : 'off';
1381
1382 $cos_options['attachment_preview'] = $attachment_preview;
1383 update_option('cos_options', $cos_options);
1384
1385 echo '<div class="updated"><p><strong>文档处理设置已保存!</strong></p></div>';
1386 return true;
1387 }
1388
1389 /**
1390 * @param array $options
1391 * @return bool
1392 */
1393 function cos_validate_configuration($options)
1394 {
1395 if (empty($options['bucket']) || empty($options['app_id']) || empty($options['secret_id']) || empty($options['secret_key'])) {
1396 echo '<div class="error"><p><strong>请�
1397 �保存存储桶名称、地域、APP ID、SecretID、SecretKey 参数!</strong></p></div>';
1398 return false;
1399 }
1400
1401 return true;
1402 }
1403
1404 function cos_document_page($options)
1405 {
1406 cos_validate_configuration($options);
1407
1408 $ci_attachment_preview = esc_attr($options['attachment_preview'] ?? 'off');
1409 $checked_attachment_preview = $ci_attachment_preview == 'on' ? 'checked="checked"' : '';
1410 $bucket = cos_get_bucket_name($options);
1411
1412 $remoteStatus = '';
1413 $status = false;
1414 if (!empty($options['bucket']) && !empty($options['app_id']) && !empty($options['secret_id']) && !empty($options['secret_key'])) {
1415 try {
1416 $client = cos_get_client($options);
1417 $client = cos_replace_client_region($client, $bucket);
1418 $status = FilePreview::checkStatus($client, $bucket);
1419
1420 if ($ci_attachment_preview == 'on' && !$status) {
1421 cos_update_config_parameters(['attachment_preview' => 'off'], $options);
1422 $checked_attachment_preview = '';
1423 }
1424
1425 $remoteStatus = $status ? '云端状态:<span class="open">已开启</span>' : '云端状态:<span class="close">已�
1426 �闭</span>';
1427 } catch (ServiceResponseException $e) {
1428 $msg = (string)$e;
1429 if ($e->getExceptionCode() === ErrorCode::NO_BIND_CI) {
1430 $msg = "存储桶 {$bucket} 未绑定数据万象,若要开启文档处理,请�
1431 � <a href='https://console.cloud.tencent.com/ci' target='_blank'>绑定数据万象服务</a>";
1432 }
1433 echo "<div class='error'><p><strong>{$msg}</strong></p></div>";
1434 } catch (\Throwable $e) {
1435 $message = (string)$e;
1436 echo "<div class='error'><p><strong>{$message}</strong></p></div>";
1437 }
1438 }
1439
1440 $disableSubmit = !$status ? 'disabled=disabled' : '';
1441 $disableMessage = !$status ? "<p>如需使用请�
1442 �访问 <a href='https://console.cloud.tencent.com/ci/bucket?bucket={$bucket}&region={$options['regional']}&type=document' target='_blank'>�
1443 �讯云控制台</a> 开启。</p>" : '';
1444
1445 $nonce = wp_nonce_field('qcloud_cos_ci_attachment_preview', 'qcloud_cos_ci_attachment_preview-nonce', true, false);
1446
1447 return <<<EOF
1448 <form method="post">
1449 <table class="form-table">
1450 <tr>
1451 <th>
1452 <legend>文档预览</legend>
1453 </th>
1454 <td>
1455 <label class="switch">
1456 <input type="checkbox" name="attachment_preview" {$checked_attachment_preview} />
1457 <span class="slider round"></span>
1458 </label>
1459 <p>{$remoteStatus}</p>
1460 <p>文档预览支持对多种文件类型生成预览,可以解决文档�
1461 容的页面展示问题,满足 PC、App 等多个用户端的文档在线浏览需求。更多详�
1462 请查看:<a href="https://cloud.tencent.com/document/product/460/47495" target="_blank">�
1463 �讯云文档</a></p>
1464 </td>
1465 </tr>
1466 <tr>
1467 <th></th>
1468 <input type="hidden" name="type" value="qcloud_cos_ci_attachment_preview">
1469 {$nonce}
1470 <td>
1471 <input type="submit" class="button button-primary" {$disableSubmit} value="保存"/>
1472 {$disableMessage}
1473 </td>
1474 </tr>
1475 </table>
1476 </form>
1477 EOF;
1478 }
1479
1480 /**
1481 * @return string[]
1482 */
1483 function cos_setting_page_tabs()
1484 {
1485 return [
1486 'config' => '�
1487 �置',
1488 'sync' => '数据迁移',
1489 'slim' => '图片极智压缩',
1490 'document' => '文档处理',
1491 'text' => '文本审核',
1492 'metric' => '数据监控',
1493 'contact' => '联系作�
1494 '
1495 ];
1496 }
1497
1498 /**
1499 * @return string
1500 */
1501 function cos_get_current_tab()
1502 {
1503 if (isset($_GET['tab'])) {
1504 return $_GET['tab'];
1505 }
1506
1507 global $pagenow;
1508 if ($pagenow == 'admin.php' && $_GET['page'] !== COS_PLUGIN_SLUG) {
1509 $parts = explode('-', $_GET['page']);
1510 return end($parts);
1511 }
1512
1513 return 'config';
1514 }
1515
1516 function cos_get_user_color_scheme()
1517 {
1518 // Get the user data
1519 $user_info = get_userdata(get_current_user_id());
1520
1521 // Get the admin color scheme name
1522 $color_scheme_name = $user_info->admin_color;
1523
1524 // Get the admin color scheme object
1525 global $_wp_admin_css_colors;
1526 $color_scheme = $_wp_admin_css_colors[$color_scheme_name];
1527
1528 // Return the color scheme
1529 return $color_scheme;
1530 }
1531
1532 // 在导航栏“设置”中添加条目
1533 function cos_add_setting_page()
1534 {
1535 add_options_page('�
1536 �讯云 COS', '�
1537 �讯云 COS', 'manage_options', __FILE__, 'cos_setting_page');
1538
1539 add_menu_page('�
1540 �讯云 COS', '�
1541 �讯云 COS', 'manage_options', COS_PLUGIN_SLUG, 'cos_setting_page', 'dashicons-cloud-upload');
1542 foreach (cos_setting_page_tabs() as $tab => $name) {
1543 add_submenu_page(COS_PLUGIN_SLUG, $name, $name, 'manage_options', COS_PLUGIN_SLUG . "-{$tab}", 'cos_setting_page');
1544 }
1545 }
1546
1547 add_action('admin_menu', 'cos_add_setting_page');
1548
1549 // 插件设置页面
1550 function cos_setting_page()
1551 {
1552 if (!current_user_can('manage_options')) {
1553 wp_die('Insufficient privileges!');
1554 }
1555 if (!empty($_POST) && !empty($_POST['type'])) {
1556 $nonce = $_POST["{$_POST['type']}-nonce"] ?? '';
1557 if (empty($nonce) || !wp_verify_nonce($nonce, $_POST['type'])) {
1558 wp_die('Illegal requests!');
1559 }
1560 }
1561 $options = [];
1562 if (!empty($_POST) && $_POST['type'] == 'qcloud_cos_set') {
1563 $options['bucket'] = isset($_POST['bucket']) ? sanitize_text_field($_POST['bucket']) : '';
1564 $options['regional'] = isset($_POST['regional']) ? sanitize_text_field($_POST['regional']) : '';
1565 $options['app_id'] = isset($_POST['app_id']) ? sanitize_text_field($_POST['app_id']) : '';
1566 $options['secret_id'] = isset($_POST['secret_id']) ? sanitize_text_field($_POST['secret_id']) : '';
1567 $options['secret_key'] = isset($_POST['secret_key']) ? sanitize_text_field($_POST['secret_key']) : '';
1568 $options['nothumb'] = isset($_POST['nothumb']) ? 'true' : 'false';
1569 $options['nolocalsaving'] = isset($_POST['nolocalsaving']) ? 'true' : 'false';
1570 $options['delete_options'] = isset($_POST['delete_options']) ? 'true' : 'false';
1571 $options['upload_url_path'] = isset($_POST['upload_url_path']) ? sanitize_text_field(trim(stripslashes($_POST['upload_url_path']), '/')) : '';
1572 $options['upload_subdirectory'] = isset($_POST['upload_subdirectory']) ? sanitize_text_field(trim($_POST['upload_subdirectory'], '/')) : '';
1573 $options['ci_style'] = isset($_POST['ci_style']) ? sanitize_text_field($_POST['ci_style']) : '';
1574 $options['update_file_name'] = isset($_POST['update_file_name']) ? sanitize_text_field($_POST['update_file_name']) : 'false';
1575 $options['origin_protect'] = isset($_POST['origin_protect']) ? sanitize_text_field($_POST['origin_protect']) : 'off';
1576
1577 $options = cos_append_options($options);
1578 }
1579
1580 if (!empty($_POST) and $_POST['type'] == 'qcloud_cos_all') {
1581 if (cos_validate_configuration(get_option('cos_options', cos_get_default_options()))) {
1582 $files = cos_read_dir_queue(get_home_path(), cos_get_option('upload_path'));
1583 foreach ($files as $file) {
1584 cos_file_upload($file['key'], $file['filepath']);
1585 }
1586 echo '<div class="updated"><p><strong>本次操作成功同步' . count($files) . '个文件</strong></p></div>';
1587 }
1588 }
1589
1590 // 替换数据库链接
1591 if (!empty($_POST) && $_POST['type'] == 'qcloud_cos_replace') {
1592 $old_url = esc_url_raw($_POST['old_url']);
1593 $new_url = esc_url_raw($_POST['new_url']);
1594 if (!empty($old_url)) {
1595 global $wpdb;
1596 // 文章�
1597
1598 $posts_name = $wpdb->prefix . 'posts';
1599 $posts_result = $wpdb->query($wpdb->prepare("UPDATE $posts_name SET post_content = REPLACE(post_content, '%s', '%s')", [$old_url, $new_url]));
1600
1601 // 修改题图之类的
1602 $postmeta_name = $wpdb->prefix . 'postmeta';
1603 $postmeta_result = $wpdb->query($wpdb->prepare("UPDATE $postmeta_name SET meta_value = REPLACE(meta_value, '%s', '%s')", [$old_url, $new_url]));
1604
1605 echo '<div class="updated"><p><strong>替换成功!�
1606 �替换文章�
1607 ' . $posts_result . '条、题图链接' . $postmeta_result . '条!</strong></p></div>';
1608 } else {
1609 echo '<div class="error"><p><strong>请填写资源链接URL地址!</strong></p></div>';
1610 }
1611 }
1612
1613 if (!empty($_POST) && $_POST['type'] == 'qcloud_cos_ci_image_slim') {
1614 cos_ci_image_slim_setting($_POST);
1615 }
1616
1617 if (!empty($_POST) && $_POST['type'] == 'qcloud_cos_ci_text') {
1618 cos_ci_text_setting($_POST);
1619 }
1620
1621 if (!empty($_POST) && $_POST['type'] == 'qcloud_cos_ci_attachment_preview') {
1622 cos_ci_attachment_preview_setting($_POST);
1623 }
1624
1625 // 若$options不为空数组,则更新数据
1626 if ($options !== []) {
1627 $check_status = true;
1628 if (!empty($options['bucket']) && !empty($options['app_id']) && !empty($options['secret_id']) && !empty($options['secret_key'])) {
1629 $check_status = cos_check_bucket($options);
1630 }
1631
1632 if ($options['origin_protect'] === 'on') {
1633 $check_origin_protect = OriginProtect::checkStatus(cos_get_client($options), cos_get_bucket_name($options));
1634 if (!$check_origin_protect) {
1635 $options['origin_protect'] = 'off';
1636 echo '<div class="error"><p><strong>未开启原图保护,请�
1637 �访问�
1638 �讯云对象存储控制台开启!</strong></p></div>';
1639 }
1640 }
1641
1642 if ($check_status) {
1643 //更新数据库
1644 update_option('cos_options', $options);
1645
1646 $upload_path = sanitize_text_field(trim(stripslashes($_POST['upload_path']), '/'));
1647 $upload_path = $upload_path == '' ? 'wp-content/uploads' : $upload_path;
1648 update_option('upload_path', $upload_path);
1649 update_option('upload_url_path', $options['upload_url_path']);
1650 echo '<div class="updated"><p><strong>设置已保存!</strong></p></div>';
1651 }
1652 }
1653
1654 $cos_options = get_option('cos_options', cos_get_default_options());
1655 $cos_regional = esc_attr($cos_options['regional']);
1656
1657 $cos_nothumb = esc_attr($cos_options['nothumb']);
1658 $check_nothumb = $cos_nothumb == 'true' ? 'checked="checked"' : '';
1659
1660 $cos_nolocalsaving = esc_attr($cos_options['nolocalsaving']);
1661 $check_nolocalsaving = $cos_nolocalsaving == 'true' ? 'checked="checked"' : '';
1662
1663 $cos_delete_options = esc_attr($cos_options['delete_options']);
1664 $check_delete_options = $cos_delete_options == 'true' ? 'checked="checked"' : '';
1665
1666 $check_origin_protect = esc_attr($cos_options['origin_protect'] ?? 'off') !== 'off' ? 'checked="checked"' : '';
1667
1668 $cos_update_file_name = esc_attr($cos_options['update_file_name']);
1669 $cos_upload_subdirectory = esc_attr($cos_options['upload_subdirectory'] ?? '');
1670
1671 $protocol = cos_get_url_scheme();
1672
1673 $current_tab = cos_get_current_tab();
1674
1675 $color_scheme = cos_get_user_color_scheme();
1676 ?>
1677 <style>
1678 .new-tab{margin-left: 5px;padding: 3px;border-radius: 10px;font-size: 10px;}
1679 .open{color: #007017;}
1680 .close{color: #b32d2e;}
1681 .charts-container{display: flex;flex-wrap: wrap;margin-top: 10px;}
1682 .cos-chart{flex-basis: calc(50% - 20px);}
1683 @media(max-width:600px){.cos-chart{flex-basis:100%}}
1684 .switch{position:relative;display:inline-block;width:60px;height:30px}
1685 .switch input{opacity:0;width:0;height:0}
1686 .slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;transition:.4s}
1687 .slider:before{position:absolute;content:"";height:25px;width:25px;left:4px;bottom:2.5px;background-color:white;transition:.4s}
1688 input:checked+.slider{background-color: <?php echo $color_scheme->colors[2]; ?>;}
1689 input:checked+.slider:before{transform:translateX(25px)}
1690 .slider.round{border-radius:30px}
1691 .slider.round:before{border-radius:50%}
1692 </style>
1693 <div class="wrap" style="margin: 10px;">
1694 <h1>�
1695 �讯云 COS <span style="font-size: 13px;">当前版本:<?php echo COS_VERSION; ?></span></h1>
1696 <p>插件网站:<a href="https://qq52o.me/" target="_blank">沈唁志</a> / <a href="https://qq52o.me/2518.html" target="_blank">Sync QCloud COS发布页面</a> / <a href="https://qq52o.me/2722.html" target="_blank">详细使用教程</a>;</p>
1697 <p>如果觉得此插件对你有所帮助,不妨到 <a href="https://github.com/sy-records/sync-qcloud-cos" target="_blank">GitHub</a> 上点个<code>Star</code>;有 WordPress 账号?给个 <a href="https://wordpress.org/support/plugin/sync-qcloud-cos/reviews/#new-post" target="_blank">五星好评</a>;<a href="?page=sync-qcloud-cos-contact">打赏一杯咖啡或一杯香茗</a></p>
1698 <h3 class="nav-tab-wrapper">
1699 <?php global $pagenow; ?>
1700 <?php foreach (cos_setting_page_tabs() as $tab => $label): ?>
1701 <?php $href = $pagenow === 'admin.php' ? COS_PLUGIN_SLUG . '-' . $tab : COS_PLUGIN_PAGE . '&tab=' . $tab; ?>
1702 <?php $label = $tab == 'contact' ? $label . '<span class="wp-ui-notification new-tab">NEW</span>' : $label; ?>
1703 <a class="nav-tab <?php echo $current_tab == $tab ? 'nav-tab-active' : '' ?>" href="?page=<?php echo $href;?>"><?php echo $label; ?></a>
1704 <?php endforeach; ?>
1705 </h3>
1706 <?php if ($current_tab == 'config'): ?>
1707 <form method="post">
1708 <table class="form-table">
1709 <tr>
1710 <th>
1711 <legend>存储桶名称</legend>
1712 </th>
1713 <td>
1714 <input type="text" name="bucket" required value="<?php echo esc_attr($cos_options['bucket']); ?>" size="50" placeholder="请填写存储桶名称"/>
1715
1716 <p>请�
1717 �访问 <a href="https://console.cloud.tencent.com/cos5/bucket" target="_blank">�
1718 �讯云控制台</a> 创建<code>存储桶</code>,再填写以上�
1719 容。</p>
1720 </td>
1721 </tr>
1722 <tr>
1723 <th>
1724 <legend>存储桶地域</legend>
1725 </th>
1726 <td>
1727 <select name="regional"><?php cos_get_regional($cos_regional); ?></select>
1728 <p>请选择<code>存储桶</code>对应的所在地域。</p>
1729 </td>
1730 </tr>
1731 <tr>
1732 <th>
1733 <legend>APP ID</legend>
1734 </th>
1735 <td>
1736 <input type="text" name="app_id" required value="<?php echo esc_attr($cos_options['app_id']); ?>" size="50" placeholder="APP ID"/>
1737
1738 <p>请�
1739 �访问 <a href="https://console.cloud.tencent.com/cos5/key" target="_blank">�
1740 �讯云控制台</a> 获取 <code>APP ID、SecretID、SecretKey</code>。</p>
1741 </td>
1742 </tr>
1743 <tr>
1744 <th>
1745 <legend>SecretID</legend>
1746 </th>
1747 <td><input type="text" name="secret_id" required value="<?php echo esc_attr($cos_options['secret_id']); ?>" size="50" placeholder="SecretID"/></td>
1748 </tr>
1749 <tr>
1750 <th>
1751 <legend>SecretKey</legend>
1752 </th>
1753 <td>
1754 <input type="password" name="secret_key" required value="<?php echo esc_attr($cos_options['secret_key']); ?>" size="50" placeholder="SecretKey"/>
1755 </td>
1756 </tr>
1757 <tr>
1758 <th>
1759 <legend>不上传缩略图</legend>
1760 </th>
1761 <td>
1762 <label class="switch">
1763 <input type="checkbox" name="nothumb" <?php echo $check_nothumb; ?> />
1764 <span class="slider round"></span>
1765 </label>
1766
1767 <p>建议不启用。</p>
1768 </td>
1769 </tr>
1770 <tr>
1771 <th>
1772 <legend>不在本地保留备份</legend>
1773 </th>
1774 <td>
1775 <label class="switch">
1776 <input type="checkbox" name="nolocalsaving" <?php echo $check_nolocalsaving; ?> />
1777 <span class="slider round"></span>
1778 </label>
1779
1780 <p>建议不启用。</p>
1781 </td>
1782 </tr>
1783 <tr>
1784 <th>
1785 <legend>删除�
1786 �置信息</legend>
1787 </th>
1788 <td>
1789 <label class="switch">
1790 <input type="checkbox" name="delete_options" <?php echo $check_delete_options; ?> />
1791 <span class="slider round"></span>
1792 </label>
1793
1794 <p>默认启用,删除插件时会删除当前�
1795 �置信息。</p>
1796 <p>如果不启用,删除插件时只会重置URL前缀为空,保留当前�
1797 �置信息。</p>
1798 </td>
1799 </tr>
1800 <tr>
1801 <th>
1802 <legend>自动重命名文件</legend>
1803 </th>
1804 <td>
1805 <select name="update_file_name">
1806 <option <?php echo $cos_update_file_name == 'false' ? 'selected="selected"' : '';?> value="false">不处理</option>
1807 <option <?php echo $cos_update_file_name == 'md5' ? 'selected="selected"' : '';?> value="md5">MD5</option>
1808 <option <?php echo $cos_update_file_name == 'time' ? 'selected="selected"' : '';?> value="time">时间戳+随机数</option>
1809 </select>
1810 </td>
1811 </tr>
1812 <tr>
1813 <th>
1814 <legend>本地文件夹</legend>
1815 </th>
1816 <td>
1817 <input type="text" name="upload_path" required value="<?php echo cos_get_option('upload_path'); ?>" size="50" placeholder="请输�
1818 �本地文件夹"/>
1819
1820 <p>附件在服务器上的存储位置,例如:<code>wp-content/uploads</code>(注意不要以<code>/</code>开头和结尾),根目录请输�
1821 �<code>.</code>。</p>
1822 </td>
1823 </tr>
1824 <tr>
1825 <th>
1826 <legend>URL前缀</legend>
1827 </th>
1828 <td>
1829 <input type="text" name="upload_url_path" required value="<?php echo cos_get_option('upload_url_path'); ?>" size="50" placeholder="请输�
1830 �URL前缀"/>
1831
1832 <p><b>注意:</b></p>
1833
1834 <p>1)URL前缀的格式为 <code><?php echo $protocol;?>{cos域名}/{本地文件夹}</code> ,“本地文件夹”务�
1835 与上面保持一致(结尾无 <code>/</code> ),或�
1836 “本地文件夹”为 <code>.</code> 时 <code><?php echo $protocol;?>{cos域名}</code> 。</p>
1837
1838 <p>2)COS中的存放路径(即“文件夹”)与上述 <code>本地文件夹</code> 中定义的路径是相同的(出于方便切换考虑)。</p>
1839
1840 <p>3)如果需要使用 <code>独立域名</code> ,直接将 <code>{cos域名}</code> 替换为 <code>独立域名</code> 即可。</p>
1841 </td>
1842 </tr>
1843 <tr>
1844 <th>
1845 <legend>上传至子目录</legend>
1846 </th>
1847 <td>
1848 <input type="text" name="upload_subdirectory" value="<?php echo $cos_upload_subdirectory; ?>" size="50" placeholder="请输�
1849 �子目录地址,不使用请留空"/>
1850
1851 <p>如果需要多个站点�
1852 �用一个存储桶时设置,例如:<code>sub1</code>、<code>sub1/sub2</code>(注意不要以<code>/</code>开头和结尾),支持多级;</p>
1853 <p>如果设置了上传至子目录,需要在 <b>URL前缀</b> 中增加对应的子目录,例如:<code><?php echo $protocol;?>{cos域名}/{子目录}/{本地文件夹}</code>。</p>
1854 </td>
1855 </tr>
1856 <tr>
1857 <th>
1858 <legend>图片处理样式</legend>
1859 </th>
1860 <td>
1861 <input type="text" name="ci_style" value="<?php echo esc_attr($cos_options['ci_style']); ?>" size="50" placeholder="请输�
1862 �图片处理样式,不使用请留空"/>
1863
1864 <p><b>获取样式:</b></p>
1865
1866 <p>1)在 <a href="https://console.cloud.tencent.com/cos5/bucket" target="_blank">存储桶列表</a> 中对应桶的 <code>图片处理</code> 处添加。�
1867 �体样式设置参考<a href="https://cloud.tencent.com/document/product/460/6936" target="_blank">�
1868 �讯云文档</a>。</p>
1869
1870 <p>2)填写时需要将<code>分隔符</code>和对应的<code>名称</code>或 <code>描述</code>进行拼接,例如:</p>
1871
1872 <p><code>分隔符</code>为<code>!</code>(感叹号),<code>名称</code>为<code>blog</code>,<code>描述</code>为 <code> imageMogr2/format/webp/interlace/0/quality/100</code></p>
1873 <p>则填写为 <code>!blog</code> 或 <code>?imageMogr2/format/webp/interlace/0/quality/100</code></p>
1874 </td>
1875 </tr>
1876 <tr>
1877 <th>
1878 <legend>原图保护</legend>
1879 </th>
1880 <td>
1881 <label class="switch">
1882 <input type="checkbox" name="origin_protect" <?php echo $check_origin_protect; ?> />
1883 <span class="slider round"></span>
1884 </label>
1885
1886 <p>开启原图保护功能后,存储桶中的图片文件�
1887 能以带样式的 URL 进行访问,能够阻止恶意用户对源文件的请求。</p>
1888 <p>使用时请�
1889 �访问�
1890 �讯云对象存储控制台<b>开启原图保护</b>并设置<b>图片处理样式</b>!</p>
1891 <p>注:此功能为实验性功能,如遇错误或不可用,请�
1892 �闭后联系作�
1893 反馈。</p>
1894 </td>
1895 </tr>
1896 <tr>
1897 <th></th>
1898 <td><input type="submit" class="button button-primary" value="保存更改"/></td>
1899 </tr>
1900 </table>
1901 <input type="hidden" name="type" value="qcloud_cos_set">
1902 <?php wp_nonce_field('qcloud_cos_set', 'qcloud_cos_set-nonce'); ?>
1903 </form>
1904 <?php elseif ($current_tab == 'sync'): ?>
1905 <?php echo cos_sync_setting_form($cos_options); ?>
1906 <?php elseif ($current_tab == 'slim'): ?>
1907 <?php echo cos_ci_image_slim_page($cos_options); ?>
1908 <?php elseif ($current_tab == 'document'): ?>
1909 <?php echo cos_document_page($cos_options); ?>
1910 <?php elseif ($current_tab == 'text'): ?>
1911 <?php echo cos_ci_text_page($cos_options); ?>
1912 <?php elseif ($current_tab == 'metric'): ?>
1913 <script src="//cdnjs.cloudflare.com/ajax/libs/apexcharts/3.41.1/apexcharts.min.js"></script>
1914 <div class="charts-container">
1915 <?php
1916 $bucket = cos_get_bucket_name($cos_options);
1917 $disableCharts = defined('COS_DISABLE_CHARTS') && COS_DISABLE_CHARTS;
1918 if (!empty($bucket) && !$disableCharts) {
1919 $styleChart = !empty($cos_options['ci_style']);
1920 $previewChart = !empty($cos_options['attachment_preview']) && $cos_options['attachment_preview'] == 'on';
1921 $textChart = !empty($cos_options['ci_text_comments']) && $cos_options['ci_text_comments'] == 'on';
1922 if (defined('COS_ENABLE_STYLE_CHART')) {
1923 $styleChart = COS_ENABLE_STYLE_CHART;
1924 }
1925 if (defined('COS_ENABLE_PREVIEW_CHART')) {
1926 $previewChart = COS_ENABLE_PREVIEW_CHART;
1927 }
1928 if (defined('COS_ENABLE_TEXT_CHART')) {
1929 $textChart = COS_ENABLE_TEXT_CHART;
1930 }
1931 $monitor = new DataPoints($bucket, $cos_options);
1932 Charts::setColors($color_scheme->colors);
1933 echo Charts::storage($monitor->getStorage());
1934 echo Charts::objectNumber($monitor->getObjectNumber());
1935 echo Charts::requests($monitor->getRequests());
1936 echo Charts::traffic($monitor->getTraffic());
1937
1938 if ($styleChart) {
1939 echo Charts::ciStyle($monitor->getImageBasicsRequests());
1940 echo Charts::ciTraffic($monitor->getCITraffic());
1941 }
1942
1943 if ($previewChart) {
1944 echo Charts::ciDocumentHtml($monitor->getDocumentHtmlRequests());
1945 }
1946
1947 if ($textChart) {
1948 echo Charts::ciTextAuditing($monitor->getTextAuditing());
1949 }
1950 }
1951 ?>
1952 </div>
1953 <?php elseif ($current_tab == 'contact'): ?>
1954 <?php echo cos_contact_page(); ?>
1955 <?php endif; ?>
1956 </div>
1957 <?php
1958 }
1959 ?>
1960