PluginProbe
Sync QCloud COS / 2.6.5
Sync QCloud COS v2.6.5
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 2.6.5, at sync-qcloud-cos.php

1,952 lines 72.7 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.5
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.5');
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
912 return <<<HTML
913 <form method="post">
914 <table class="form-table">
915 <tr>
916 <th>
917 <legend>数据库�
918 容替换</legend>
919 </th>
920 <td>
921 <input type="text" name="old_url" size="50" placeholder="请输�
922 �要替换的�
923 容"/>
924 <p><b>可能会填�
925 �:<code>{$old_url}</code></b></p>
926 <p>例如:<code>https://qq52o.me/wp-content/uploads</code></p>
927 </td>
928 </tr>
929 <tr>
930 <th>
931 <legend></legend>
932 </th>
933 <td>
934 <input type="text" name="new_url" size="50" placeholder="请输�
935 �要替换为的�
936 容"/>
937 <p><b>可能会填�
938 �:<code>{$new_url}</code></b></p>
939 <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>
940 </td>
941 </tr>
942 <tr>
943 <th>
944 <legend></legend>
945 </th>
946 <input type="hidden" name="type" value="qcloud_cos_replace">
947 {$nonce}
948 <td>
949 <input type="submit" class="button button-secondary" value="开始替换"/>
950 <p><b>注意:如果是首次替换,请注意备份!此功能会替换文章以及设置的特色图片(题图)等使用的资源链接,也可用于�
951 �他需要替换文章�
952 容的场景。</b></p>
953 </td>
954 </tr>
955 </table>
956 </form>
957 <form method="post">
958 <table class="form-table">
959 <tr>
960 <th>
961 <legend>同步历史附件</legend>
962 </th>
963 <input type="hidden" name="type" value="qcloud_cos_all">
964 <td>
965 <input type="submit" class="button button-secondary" value="开始同步"/>
966 <p><b>注意:如果是首次同步,执行时间将会非常长(根据你的历史附件数量),有可能会因为执行时间过长,导致页面显示�
967 时或�
968 报错。<br> 所以建议附件数量过多的用户,直接使用官方的<a target="_blank" rel="nofollow" href="https://cloud.tencent.com/document/product/436/63143">COSCLI 工�
969 �</a>进行迁移,�
970 �体可参考<a target="_blank" rel="nofollow" href="https://qq52o.me/2809.html">使用 COSCLI 快速迁移本地数据到 COS</a></b></p>
971 </td>
972 </tr>
973 </table>
974 </form>
975 HTML;
976 }
977
978 /**
979 * @param array $content
980 * @return bool
981 */
982 function cos_ci_image_slim_setting($content)
983 {
984 $cos_options = get_option('cos_options', cos_get_default_options());
985
986 if (!cos_validate_configuration($cos_options)) {
987 return false;
988 }
989
990 $slim = !empty($content['ci_image_slim']) ? sanitize_text_field($content['ci_image_slim']) : 'off';
991 $mode = !empty($content['ci_image_slim_mode']) ? implode(',', $content['ci_image_slim_mode']) : '';
992 $suffix = !empty($content['ci_image_slim_suffix']) ? implode(',', $content['ci_image_slim_suffix']) : '';
993
994 if ($slim == 'on') {
995 if (empty($mode)) {
996 echo '<div class="error"><p><strong>图片极智压缩模式不能为空!</strong></p></div>';
997 return false;
998 }
999 if (strpos($mode, 'Auto') !== false && empty($suffix)) {
1000 echo '<div class="error"><p><strong>图片极智压缩使用模式�
1001 含自动时,图片格式不能为空!</strong></p></div>';
1002 return false;
1003 }
1004 }
1005
1006 try {
1007 $client = cos_get_client($cos_options);
1008 $bucket = cos_get_bucket_name($cos_options);
1009 $client = cos_replace_client_region($client, $bucket);
1010 ImageSlim::checkStatus($client, $bucket);
1011 if ($slim == 'on') {
1012 ImageSlim::open($client, $bucket, $mode, $suffix);
1013 } else {
1014 ImageSlim::close($client, $bucket);
1015 }
1016 } catch (ServiceResponseException $e) {
1017 $msg = (string)$e;
1018 if ($e->getExceptionCode() === ErrorCode::NO_BIND_CI) {
1019 $msg = "存储桶 {$bucket} 未绑定数据万象,若要开启极智压缩,请�
1020 � <a href='https://console.cloud.tencent.com/ci' target='_blank'>绑定数据万象服务</a >";
1021 }
1022 if ($e->getExceptionCode() === ErrorCode::REGION_UNSUPPORTED) {
1023 $msg = "存储桶所在地域 {$cos_options['regional']} 暂不支持图片极智压缩";
1024 }
1025 echo "<div class='error'><p><strong>{$msg}</strong></p></div>";
1026 return false;
1027 } catch (\Throwable $e) {
1028 $message = (string)$e;
1029 echo "<div class='error'><p><strong>{$message}</strong></p></div>";
1030 return false;
1031 }
1032
1033 $cos_options['ci_image_slim'] = $slim;
1034 $cos_options['ci_image_slim_mode'] = $mode;
1035 $cos_options['ci_image_slim_suffix'] = $suffix;
1036
1037 update_option('cos_options', $cos_options);
1038
1039 echo '<div class="updated"><p><strong>图片极智压缩设置已保存!</strong></p></div>';
1040 return true;
1041 }
1042
1043 function cos_ci_image_slim_page($options)
1044 {
1045 cos_validate_configuration($options);
1046
1047 $ci_image_slim = esc_attr($options['ci_image_slim']);
1048 $checked_ci_image_slim = $ci_image_slim == 'on' ? 'checked="checked"' : '';
1049 $ci_image_slim_mode = explode(',', esc_attr($options['ci_image_slim_mode']));
1050 $checked_mode_api = in_array('API', $ci_image_slim_mode) ? 'checked="checked"' : '';
1051 $checked_mode_auto = in_array('Auto', $ci_image_slim_mode) ? 'checked="checked"' : '';
1052 $ci_image_slim_suffix = explode(',', esc_attr($options['ci_image_slim_suffix']));
1053 $checked_suffix_jpg = in_array('jpg', $ci_image_slim_suffix) ? 'checked="checked"' : '';
1054 $checked_suffix_png = in_array('png', $ci_image_slim_suffix) ? 'checked="checked"' : '';
1055 $checked_suffix_gif = in_array('gif', $ci_image_slim_suffix) ? 'checked="checked"' : '';
1056
1057 $remoteStatus = '';
1058 if (!empty($options['bucket']) && !empty($options['app_id']) && !empty($options['secret_id']) && !empty($options['secret_key'])) {
1059 try {
1060 $bucket = cos_get_bucket_name($options);
1061 $client = cos_get_client($options);
1062 $client = cos_replace_client_region($client, $bucket);
1063 $result = ImageSlim::checkStatus($client, $bucket);
1064 cos_sync_image_slim_config($result, $options);
1065 $status = $result['Status'];
1066
1067 $checked_ci_image_slim = $status == 'on' ? 'checked="checked"' : '';
1068 $remoteStatus = $status == 'on' ? '云端状态:<span class="open">已开启</span>' : '云端状态:<span class="close">已�
1069 �闭</span>';
1070
1071 $remoteMode = explode(',', $result['SlimMode']);
1072 $checked_mode_api = in_array('API', $remoteMode) ? 'checked="checked"' : '';
1073 $checked_mode_auto = in_array('Auto', $remoteMode) ? 'checked="checked"' : '';
1074 } catch (ServiceResponseException $e) {
1075 $msg = (string)$e;
1076 if ($e->getExceptionCode() === ErrorCode::NO_BIND_CI) {
1077 $msg = "存储桶 {$bucket} 未绑定数据万象,若要开启极智压缩,请�
1078 � <a href='https://console.cloud.tencent.com/ci' target='_blank'>绑定数据万象服务</a>";
1079 }
1080 if ($e->getExceptionCode() === ErrorCode::REGION_UNSUPPORTED) {
1081 $msg = "存储桶所在地域 '{$options['regional']}' 暂不支持图片极智压缩";
1082 }
1083 echo "<div class='error'><p><strong>{$msg}</strong></p></div>";
1084 } catch (\Throwable $e) {
1085 $message = (string)$e;
1086 echo "<div class='error'><p><strong>{$message}</strong></p></div>";
1087 }
1088 }
1089
1090 return <<<EOF
1091 <form method="post">
1092 <table class="form-table">
1093 <tr>
1094 <th>
1095 <legend>简介</legend>
1096 </th>
1097 <td>
1098 <p>图片极智压缩开启后,通过智能判断图片的主观质量进行自动调节,在不改变图片原格式的基础上,使图片体积相比原图有显著的降低,<br> 同时在视觉效果上可以最大程度贴近原图。更多详�
1099 请查看:<a href="https://cloud.tencent.com/document/product/460/86438" target="_blank">�
1100 �讯云文档</a></p>
1101 </td>
1102 </tr>
1103 <tr>
1104 <th>
1105 <legend>是否启用</legend>
1106 </th>
1107 <td>
1108 <label class="switch">
1109 <input type="checkbox" name="ci_image_slim" {$checked_ci_image_slim} />
1110 <span class="slider round"></span>
1111 </label>
1112 <p>{$remoteStatus}</p>
1113 <p>极智压缩支持两种模式自动压缩和API模式,请按需选择。</p>
1114 </td>
1115 </tr>
1116 <tr>
1117 <th>
1118 <legend>自动压缩</legend>
1119 </th>
1120 <td>
1121 <label class="switch">
1122 <input type="checkbox" name="ci_image_slim_mode[]" value="Auto" {$checked_mode_auto} />
1123 <span class="slider round"></span>
1124 </label>
1125
1126 <p>开通极智压缩的自动使用方式,开通后无需携带任何参数,存储桶�
1127 指定格式的图片将在访问时自动进行极智压缩。</p>
1128 <p>需要选择自动进行压缩的图片格式:</p>
1129 <input type="checkbox" name="ci_image_slim_suffix[]" value="jpg" {$checked_suffix_jpg} /> jpg(�
1130 含<code>jpg</code>、<code>jpeg</code>)<br>
1131 <input type="checkbox" name="ci_image_slim_suffix[]" value="png" {$checked_suffix_png} /> png <br>
1132 <input type="checkbox" name="ci_image_slim_suffix[]" value="gif" {$checked_suffix_gif} /> gif <br>
1133 </td>
1134 </tr>
1135 <tr>
1136 <th>
1137 <legend>API 模式</legend>
1138 </th>
1139 <td>
1140 <label class="switch">
1141 <input type="checkbox" name="ci_image_slim_mode[]" value="API" {$checked_mode_api} />
1142 <span class="slider round"></span>
1143 </label>
1144 <p>开通极智压缩的 API 使用方式,开通后可在图片时通过极智压缩参数(需要�
1145 �置图片处理样式<code>?imageSlim</code>)对图片进行压缩;</p>
1146 </td>
1147 </tr>
1148 <tr>
1149 <th></th>
1150 <input type="hidden" name="type" value="qcloud_cos_ci_image_slim">
1151 <td><input type="submit" class="button button-primary" value="保存"/></td>
1152 </tr>
1153 </table>
1154 </form>
1155 EOF;
1156 }
1157
1158 function cos_get_user_roles()
1159 {
1160 $result = [];
1161
1162 $editable_roles = array_reverse(get_editable_roles());
1163 foreach ($editable_roles as $role => $details) {
1164 $result[$role] = translate_user_role($details['name']);
1165 }
1166
1167 return $result;
1168 }
1169
1170 function cos_ci_text_page($options)
1171 {
1172 cos_validate_configuration($options);
1173
1174 $checked_skip_comment_validation_on_login = esc_attr($options['skip_comment_validation_on_login'] ?? 'off') !== 'off' ? 'checked="checked"' : '';
1175 $checked_text_comments = esc_attr($options['ci_text_comments'] ?? 'off') !== 'off' ? 'checked="checked"' : '';
1176 $ci_text_comments_strategy = esc_attr($options['ci_text_comments_strategy'] ?? '');
1177
1178 $roles = cos_get_user_roles();
1179 $check_roles = explode(',', esc_attr($options['ci_text_comments_check_roles'] ?? ''));
1180 $select_roles = '';
1181 foreach ($roles as $role => $name) {
1182 $check = '';
1183 if (in_array($role, $check_roles)) {
1184 $check = 'checked="checked"';
1185 }
1186 $select_roles .= '<input type="checkbox" name="ci_text_comments_check_roles[]" value="' . $role . '" ' . $check . '>' . $name . '<br>';
1187 }
1188
1189 return <<<EOF
1190 <form method="post">
1191 <table class="form-table">
1192 <tr>
1193 <th>
1194 <legend>评论审核</legend>
1195 </th>
1196 <td>
1197 <label class="switch">
1198 <input type="checkbox" name="ci_text_comments" {$checked_text_comments} />
1199 <span class="slider round"></span>
1200 </label>
1201 </td>
1202 </tr>
1203 <tr>
1204 <th>
1205 <legend>评论审核策略</legend>
1206 </th>
1207 <td>
1208 <input type="text" name="ci_text_comments_strategy" value="{$ci_text_comments_strategy}" size="50" placeholder="请填写对应的 Biztype 名称" />
1209 <p>填写需要使用的文本审核策略 <code>Biztype</code> 名称,为空时使用默认策略,详�
1210 查看 <a href="https://cloud.tencent.com/document/product/436/55206" target="_blank">设置审核策略</a>。</p>
1211 </td>
1212 </tr>
1213 <tr>
1214 <th>
1215 <legend>跳过登录态验证</legend>
1216 </th>
1217 <td>
1218 <label class="switch">
1219 <input type="checkbox" name="skip_comment_validation_on_login" {$checked_skip_comment_validation_on_login} />
1220 <span class="slider round"></span>
1221 </label>
1222 <p>启用后如果是登录态则会跳过该用户评论�
1223 容,不去验证。</p>
1224 </td>
1225 </tr>
1226 <tr>
1227 <th>
1228 <legend>需要验证的登录角色</legend>
1229 </th>
1230 <td>
1231 {$select_roles}
1232 <p>选择需要在登录态下验证的角色,选择后即使选择了<strong>跳过登录态验证</strong>,属于该角色的用户评论也会进行验证。</p>
1233 </td>
1234 </tr>
1235 <tr>
1236 <th></th>
1237 <input type="hidden" name="type" value="qcloud_cos_ci_text">
1238 <td><input type="submit" class="button button-primary" value="保存"/></td>
1239 </tr>
1240 </table>
1241 </form>
1242 EOF;
1243 }
1244
1245 function cos_ci_text_setting($content)
1246 {
1247 $cos_options = get_option('cos_options', cos_get_default_options());
1248 if (!cos_validate_configuration($cos_options)) {
1249 return false;
1250 }
1251
1252 $client = cos_get_client($cos_options);
1253 $bucket = cos_get_bucket_name($cos_options);
1254 $ciService = Service::checkStatus($client, $bucket);
1255 if (!$ciService) {
1256 echo "<div class='error'><p><strong>存储桶 {$bucket} 未绑定数据万象,若要开启文本审核,请�
1257 � <a href='https://console.cloud.tencent.com/ci' target='_blank'>绑定数据万象服务</a ></strong></p></div>";
1258 return false;
1259 }
1260
1261 $ci_text_comments = isset($content['ci_text_comments']) ? sanitize_text_field($content['ci_text_comments']) : 'off';
1262 $skip_comment_validation_on_login = isset($content['skip_comment_validation_on_login']) ? sanitize_text_field($content['skip_comment_validation_on_login']) : 'off';
1263 $ci_text_comments_strategy = isset($content['ci_text_comments_strategy']) ? sanitize_text_field($content['ci_text_comments_strategy']) : '';
1264 $ci_text_comments_check_roles = isset($content['ci_text_comments_check_roles']) ? implode(',', $content['ci_text_comments_check_roles']) : '';
1265
1266 $cos_options['ci_text_comments'] = $ci_text_comments;
1267 $cos_options['skip_comment_validation_on_login'] = $skip_comment_validation_on_login;
1268 $cos_options['ci_text_comments_strategy'] = $ci_text_comments_strategy;
1269 $cos_options['ci_text_comments_check_roles'] = $ci_text_comments_check_roles;
1270 update_option('cos_options', $cos_options);
1271
1272 echo '<div class="updated"><p><strong>文本审核设置已保存!</strong></p></div>';
1273 return true;
1274 }
1275
1276 function cos_contact_page()
1277 {
1278 return <<<EOF
1279 <table class="form-table">
1280 <tbody>
1281 <tr>
1282 <th>GitHub</th>
1283 <td><a href="https://github.com/sy-records" target="_blank">@sy-records</a></td>
1284 </tr>
1285 <tr>
1286 <th>QQ 群</th>
1287 <td>887595381 <a href="https://go.qq52o.me/qm/ccs" target="_blank">点击加�
1288 �</a></td>
1289 </tr>
1290 <tr>
1291 <th>微信�
1292 �众号</th>
1293 <td><img width="150px" src="https://open.weixin.qq.com/qr/code?username=sy-records" alt="鲁飞"></td>
1294 </tr>
1295 <tr>
1296 <th>打赏一杯咖啡或一杯香茗</th>
1297 <td><img height="290px" src="https://img.qq52o.me/staticfiles/donate.png?t=cos&f={$_SERVER['HTTP_HOST']}"></td>
1298 </tr>
1299 </tbody>
1300 </table>
1301 EOF;
1302 }
1303
1304 if (!function_exists('is_user_logged_in')) {
1305 require_once ABSPATH . WPINC . '/pluggable.php';
1306 }
1307
1308 function cos_process_comments($comment_data)
1309 {
1310 $options = get_option('cos_options', cos_get_default_options());
1311
1312 // If CI text for comments is not enabled
1313 if (($options['ci_text_comments'] ?? 'off') !== 'on') {
1314 return $comment_data;
1315 }
1316
1317 // If 'skip_comment_validation_on_login' option is not enabled
1318 if (($options['skip_comment_validation_on_login'] ?? 'off') !== 'on') {
1319 cos_request_txt_check($options, $comment_data['comment_content']);
1320 return $comment_data;
1321 }
1322
1323 // If User is not logged in
1324 if (!is_user_logged_in()) {
1325 cos_request_txt_check($options, $comment_data['comment_content']);
1326 return $comment_data;
1327 }
1328
1329 $roles = explode(',', $options['ci_text_comments_check_roles'] ?? '');
1330 global $current_user;
1331 // Check if one of the user roles is in the defined roles
1332 foreach ($roles as $role) {
1333 if (in_array($role, $current_user->roles)) {
1334 cos_request_txt_check($options, $comment_data['comment_content']);
1335 break;
1336 }
1337 }
1338
1339 return $comment_data;
1340 }
1341
1342 add_filter('preprocess_comment', 'cos_process_comments');
1343
1344 function cos_request_txt_check($options, $comment)
1345 {
1346 $client = cos_get_client($options);
1347 $bucket = cos_get_bucket_name($options);
1348 $client = cos_replace_client_region($client, $bucket);
1349 $result = Audit::comment($client, $bucket, $comment, $options['ci_text_comments_strategy'] ?? '');
1350 if (!$result['state'] || $result['result'] === 2) {
1351 // 人工审核
1352 add_filter('pre_comment_approved', '__return_zero');
1353 }
1354
1355 if ($result['state'] && $result['result'] === 1) {
1356 wp_die("评论�
1357 容{$result['message']}涉嫌违规,请重新评论", 409);
1358 }
1359 }
1360
1361 /**
1362 * @param array $content
1363 * @return bool
1364 */
1365 function cos_ci_attachment_preview_setting($content)
1366 {
1367 $cos_options = get_option('cos_options', cos_get_default_options());
1368 if (!cos_validate_configuration($cos_options)) {
1369 return false;
1370 }
1371
1372 $attachment_preview = !empty($content['attachment_preview']) ? sanitize_text_field($content['attachment_preview']) : 'off';
1373
1374 $cos_options['attachment_preview'] = $attachment_preview;
1375 update_option('cos_options', $cos_options);
1376
1377 echo '<div class="updated"><p><strong>文档处理设置已保存!</strong></p></div>';
1378 return true;
1379 }
1380
1381 /**
1382 * @param array $options
1383 * @return bool
1384 */
1385 function cos_validate_configuration($options)
1386 {
1387 if (empty($options['bucket']) || empty($options['app_id']) || empty($options['secret_id']) || empty($options['secret_key'])) {
1388 echo '<div class="error"><p><strong>请�
1389 �保存存储桶名称、地域、APP ID、SecretID、SecretKey 参数!</strong></p></div>';
1390 return false;
1391 }
1392
1393 return true;
1394 }
1395
1396 function cos_document_page($options)
1397 {
1398 cos_validate_configuration($options);
1399
1400 $ci_attachment_preview = esc_attr($options['attachment_preview'] ?? 'off');
1401 $checked_attachment_preview = $ci_attachment_preview == 'on' ? 'checked="checked"' : '';
1402 $bucket = cos_get_bucket_name($options);
1403
1404 $remoteStatus = '';
1405 $status = false;
1406 if (!empty($options['bucket']) && !empty($options['app_id']) && !empty($options['secret_id']) && !empty($options['secret_key'])) {
1407 try {
1408 $client = cos_get_client($options);
1409 $client = cos_replace_client_region($client, $bucket);
1410 $status = FilePreview::checkStatus($client, $bucket);
1411
1412 if ($ci_attachment_preview == 'on' && !$status) {
1413 cos_update_config_parameters(['attachment_preview' => 'off'], $options);
1414 $checked_attachment_preview = '';
1415 }
1416
1417 $remoteStatus = $status ? '云端状态:<span class="open">已开启</span>' : '云端状态:<span class="close">已�
1418 �闭</span>';
1419 } catch (ServiceResponseException $e) {
1420 $msg = (string)$e;
1421 if ($e->getExceptionCode() === ErrorCode::NO_BIND_CI) {
1422 $msg = "存储桶 {$bucket} 未绑定数据万象,若要开启文档处理,请�
1423 � <a href='https://console.cloud.tencent.com/ci' target='_blank'>绑定数据万象服务</a>";
1424 }
1425 echo "<div class='error'><p><strong>{$msg}</strong></p></div>";
1426 } catch (\Throwable $e) {
1427 $message = (string)$e;
1428 echo "<div class='error'><p><strong>{$message}</strong></p></div>";
1429 }
1430 }
1431
1432 $disableSubmit = !$status ? 'disabled=disabled' : '';
1433 $disableMessage = !$status ? "<p>如需使用请�
1434 �访问 <a href='https://console.cloud.tencent.com/ci/bucket?bucket={$bucket}&region={$options['regional']}&type=document' target='_blank'>�
1435 �讯云控制台</a> 开启。</p>" : '';
1436
1437 return <<<EOF
1438 <form method="post">
1439 <table class="form-table">
1440 <tr>
1441 <th>
1442 <legend>文档预览</legend>
1443 </th>
1444 <td>
1445 <label class="switch">
1446 <input type="checkbox" name="attachment_preview" {$checked_attachment_preview} />
1447 <span class="slider round"></span>
1448 </label>
1449 <p>{$remoteStatus}</p>
1450 <p>文档预览支持对多种文件类型生成预览,可以解决文档�
1451 容的页面展示问题,满足 PC、App 等多个用户端的文档在线浏览需求。更多详�
1452 请查看:<a href="https://cloud.tencent.com/document/product/460/47495" target="_blank">�
1453 �讯云文档</a></p>
1454 </td>
1455 </tr>
1456 <tr>
1457 <th></th>
1458 <input type="hidden" name="type" value="qcloud_cos_ci_attachment_preview">
1459 <td>
1460 <input type="submit" class="button button-primary" {$disableSubmit} value="保存"/>
1461 {$disableMessage}
1462 </td>
1463 </tr>
1464 </table>
1465 </form>
1466 EOF;
1467 }
1468
1469 /**
1470 * @return string[]
1471 */
1472 function cos_setting_page_tabs()
1473 {
1474 return [
1475 'config' => '�
1476 �置',
1477 'sync' => '数据迁移',
1478 'slim' => '图片极智压缩',
1479 'document' => '文档处理',
1480 'text' => '文本审核',
1481 'metric' => '数据监控',
1482 'contact' => '联系作�
1483 '
1484 ];
1485 }
1486
1487 /**
1488 * @return string
1489 */
1490 function cos_get_current_tab()
1491 {
1492 if (isset($_GET['tab'])) {
1493 return $_GET['tab'];
1494 }
1495
1496 global $pagenow;
1497 if ($pagenow == 'admin.php' && $_GET['page'] !== COS_PLUGIN_SLUG) {
1498 $parts = explode('-', $_GET['page']);
1499 return end($parts);
1500 }
1501
1502 return 'config';
1503 }
1504
1505 function cos_get_user_color_scheme()
1506 {
1507 // Get the user data
1508 $user_info = get_userdata(get_current_user_id());
1509
1510 // Get the admin color scheme name
1511 $color_scheme_name = $user_info->admin_color;
1512
1513 // Get the admin color scheme object
1514 global $_wp_admin_css_colors;
1515 $color_scheme = $_wp_admin_css_colors[$color_scheme_name];
1516
1517 // Return the color scheme
1518 return $color_scheme;
1519 }
1520
1521 // 在导航栏“设置”中添加条目
1522 function cos_add_setting_page()
1523 {
1524 add_options_page('�
1525 �讯云 COS', '�
1526 �讯云 COS', 'manage_options', __FILE__, 'cos_setting_page');
1527
1528 add_menu_page('�
1529 �讯云 COS', '�
1530 �讯云 COS', 'manage_options', COS_PLUGIN_SLUG, 'cos_setting_page', 'dashicons-cloud-upload');
1531 foreach (cos_setting_page_tabs() as $tab => $name) {
1532 add_submenu_page(COS_PLUGIN_SLUG, $name, $name, 'manage_options', COS_PLUGIN_SLUG . "-{$tab}", 'cos_setting_page');
1533 }
1534 }
1535
1536 add_action('admin_menu', 'cos_add_setting_page');
1537
1538 // 插件设置页面
1539 function cos_setting_page()
1540 {
1541 if (!current_user_can('manage_options')) {
1542 wp_die('Insufficient privileges!');
1543 }
1544 $options = [];
1545 if (!empty($_POST) and $_POST['type'] == 'cos_set') {
1546 $options['bucket'] = isset($_POST['bucket']) ? sanitize_text_field($_POST['bucket']) : '';
1547 $options['regional'] = isset($_POST['regional']) ? sanitize_text_field($_POST['regional']) : '';
1548 $options['app_id'] = isset($_POST['app_id']) ? sanitize_text_field($_POST['app_id']) : '';
1549 $options['secret_id'] = isset($_POST['secret_id']) ? sanitize_text_field($_POST['secret_id']) : '';
1550 $options['secret_key'] = isset($_POST['secret_key']) ? sanitize_text_field($_POST['secret_key']) : '';
1551 $options['nothumb'] = isset($_POST['nothumb']) ? 'true' : 'false';
1552 $options['nolocalsaving'] = isset($_POST['nolocalsaving']) ? 'true' : 'false';
1553 $options['delete_options'] = isset($_POST['delete_options']) ? 'true' : 'false';
1554
1555 //�
1556 用于插件卸载时比较使用
1557 $options['upload_url_path'] = isset($_POST['upload_url_path']) ? sanitize_text_field(stripslashes($_POST['upload_url_path'])) : '';
1558
1559 $options['upload_subdirectory'] = isset($_POST['upload_subdirectory']) ? sanitize_text_field(trim($_POST['upload_subdirectory'], '/')) : '';
1560 $options['ci_style'] = isset($_POST['ci_style']) ? sanitize_text_field($_POST['ci_style']) : '';
1561 $options['update_file_name'] = isset($_POST['update_file_name']) ? sanitize_text_field($_POST['update_file_name']) : 'false';
1562 $options['origin_protect'] = isset($_POST['origin_protect']) ? sanitize_text_field($_POST['origin_protect']) : 'off';
1563
1564 $options = cos_append_options($options);
1565 }
1566
1567 if (!empty($_POST) and $_POST['type'] == 'qcloud_cos_all') {
1568 if (cos_validate_configuration(get_option('cos_options', cos_get_default_options()))) {
1569 $files = cos_read_dir_queue(get_home_path(), cos_get_option('upload_path'));
1570 foreach ($files as $file) {
1571 cos_file_upload($file['key'], $file['filepath']);
1572 }
1573 echo '<div class="updated"><p><strong>本次操作成功同步' . count($files) . '个文件</strong></p></div>';
1574 }
1575 }
1576
1577 // 替换数据库链接
1578 if (!empty($_POST) and $_POST['type'] == 'qcloud_cos_replace') {
1579 $nonce = $_POST['qcloud_cos_replace-nonce'] ?? '';
1580 if (empty($nonce) || !wp_verify_nonce($nonce, 'qcloud_cos_replace')) {
1581 wp_die('Illegal requests!');
1582 }
1583
1584 $old_url = esc_url_raw($_POST['old_url']);
1585 $new_url = esc_url_raw($_POST['new_url']);
1586 if (!empty($old_url) && !empty($new_url)) {
1587 global $wpdb;
1588 // 文章�
1589
1590 $posts_name = $wpdb->prefix . 'posts';
1591 $posts_result = $wpdb->query($wpdb->prepare("UPDATE $posts_name SET post_content = REPLACE(post_content, '%s', '%s')", [$old_url, $new_url]));
1592
1593 // 修改题图之类的
1594 $postmeta_name = $wpdb->prefix . 'postmeta';
1595 $postmeta_result = $wpdb->query($wpdb->prepare("UPDATE $postmeta_name SET meta_value = REPLACE(meta_value, '%s', '%s')", [$old_url, $new_url]));
1596
1597 echo '<div class="updated"><p><strong>替换成功!�
1598 �替换文章�
1599 ' . $posts_result . '条、题图链接' . $postmeta_result . '条!</strong></p></div>';
1600 } else {
1601 echo '<div class="error"><p><strong>请填写资源链接URL地址!</strong></p></div>';
1602 }
1603 }
1604
1605 if (!empty($_POST) and $_POST['type'] == 'qcloud_cos_ci_image_slim') {
1606 cos_ci_image_slim_setting($_POST);
1607 }
1608
1609 if (!empty($_POST) and $_POST['type'] == 'qcloud_cos_ci_text') {
1610 cos_ci_text_setting($_POST);
1611 }
1612
1613 if (!empty($_POST) and $_POST['type'] == 'qcloud_cos_ci_attachment_preview') {
1614 cos_ci_attachment_preview_setting($_POST);
1615 }
1616
1617 // 若$options不为空数组,则更新数据
1618 if ($options !== []) {
1619 $check_status = true;
1620 if (!empty($options['bucket']) && !empty($options['app_id']) && !empty($options['secret_id']) && !empty($options['secret_key'])) {
1621 $check_status = cos_check_bucket($options);
1622 }
1623
1624 if ($options['origin_protect'] === 'on') {
1625 $check_origin_protect = OriginProtect::checkStatus(cos_get_client($options), cos_get_bucket_name($options));
1626 if (!$check_origin_protect) {
1627 $options['origin_protect'] = 'off';
1628 echo '<div class="error"><p><strong>未开启原图保护,请�
1629 �访问�
1630 �讯云对象存储控制台开启!</strong></p></div>';
1631 }
1632 }
1633
1634 if ($check_status) {
1635 //更新数据库
1636 update_option('cos_options', $options);
1637
1638 $upload_path = sanitize_text_field(trim(stripslashes($_POST['upload_path']), '/'));
1639 $upload_path = $upload_path == '' ? 'wp-content/uploads' : $upload_path;
1640 update_option('upload_path', $upload_path);
1641 $upload_url_path = sanitize_text_field(trim(stripslashes($_POST['upload_url_path']), '/'));
1642 update_option('upload_url_path', $upload_url_path);
1643 echo '<div class="updated"><p><strong>设置已保存!</strong></p></div>';
1644 }
1645 }
1646
1647 $cos_options = get_option('cos_options', cos_get_default_options());
1648 $cos_regional = esc_attr($cos_options['regional']);
1649
1650 $cos_nothumb = esc_attr($cos_options['nothumb']);
1651 $check_nothumb = $cos_nothumb == 'true' ? 'checked="checked"' : '';
1652
1653 $cos_nolocalsaving = esc_attr($cos_options['nolocalsaving']);
1654 $check_nolocalsaving = $cos_nolocalsaving == 'true' ? 'checked="checked"' : '';
1655
1656 $cos_delete_options = esc_attr($cos_options['delete_options']);
1657 $check_delete_options = $cos_delete_options == 'true' ? 'checked="checked"' : '';
1658
1659 $check_origin_protect = esc_attr($cos_options['origin_protect'] ?? 'off') !== 'off' ? 'checked="checked"' : '';
1660
1661 $cos_update_file_name = esc_attr($cos_options['update_file_name']);
1662 $cos_upload_subdirectory = esc_attr($cos_options['upload_subdirectory'] ?? '');
1663
1664 $protocol = cos_get_url_scheme();
1665
1666 $current_tab = cos_get_current_tab();
1667
1668 $color_scheme = cos_get_user_color_scheme();
1669 ?>
1670 <style>
1671 .new-tab{margin-left: 5px;padding: 3px;border-radius: 10px;font-size: 10px;}
1672 .open{color: #007017;}
1673 .close{color: #b32d2e;}
1674 .charts-container{display: flex;flex-wrap: wrap;margin-top: 10px;}
1675 .cos-chart{flex-basis: calc(50% - 20px);}
1676 @media(max-width:600px){.cos-chart{flex-basis:100%}}
1677 .switch{position:relative;display:inline-block;width:60px;height:30px}
1678 .switch input{opacity:0;width:0;height:0}
1679 .slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;transition:.4s}
1680 .slider:before{position:absolute;content:"";height:25px;width:25px;left:4px;bottom:2.5px;background-color:white;transition:.4s}
1681 input:checked+.slider{background-color: <?php echo $color_scheme->colors[2]; ?>;}
1682 input:checked+.slider:before{transform:translateX(25px)}
1683 .slider.round{border-radius:30px}
1684 .slider.round:before{border-radius:50%}
1685 </style>
1686 <div class="wrap" style="margin: 10px;">
1687 <h1>�
1688 �讯云 COS <span style="font-size: 13px;">当前版本:<?php echo COS_VERSION; ?></span></h1>
1689 <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>
1690 <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>
1691 <h3 class="nav-tab-wrapper">
1692 <?php global $pagenow; ?>
1693 <?php foreach (cos_setting_page_tabs() as $tab => $label): ?>
1694 <?php $href = $pagenow === 'admin.php' ? COS_PLUGIN_SLUG . '-' . $tab : COS_PLUGIN_PAGE . '&tab=' . $tab; ?>
1695 <?php $label = $tab == 'contact' ? $label . '<span class="wp-ui-notification new-tab">NEW</span>' : $label; ?>
1696 <a class="nav-tab <?php echo $current_tab == $tab ? 'nav-tab-active' : '' ?>" href="?page=<?php echo $href;?>"><?php echo $label; ?></a>
1697 <?php endforeach; ?>
1698 </h3>
1699 <?php if ($current_tab == 'config'): ?>
1700 <form method="post">
1701 <table class="form-table">
1702 <tr>
1703 <th>
1704 <legend>存储桶名称</legend>
1705 </th>
1706 <td>
1707 <input type="text" name="bucket" required value="<?php echo esc_attr($cos_options['bucket']); ?>" size="50" placeholder="请填写存储桶名称"/>
1708
1709 <p>请�
1710 �访问 <a href="https://console.cloud.tencent.com/cos5/bucket" target="_blank">�
1711 �讯云控制台</a> 创建<code>存储桶</code>,再填写以上�
1712 容。</p>
1713 </td>
1714 </tr>
1715 <tr>
1716 <th>
1717 <legend>存储桶地域</legend>
1718 </th>
1719 <td>
1720 <select name="regional"><?php cos_get_regional($cos_regional); ?></select>
1721 <p>请选择<code>存储桶</code>对应的所在地域。</p>
1722 </td>
1723 </tr>
1724 <tr>
1725 <th>
1726 <legend>APP ID</legend>
1727 </th>
1728 <td>
1729 <input type="text" name="app_id" required value="<?php echo esc_attr($cos_options['app_id']); ?>" size="50" placeholder="APP ID"/>
1730
1731 <p>请�
1732 �访问 <a href="https://console.cloud.tencent.com/cos5/key" target="_blank">�
1733 �讯云控制台</a> 获取 <code>APP ID、SecretID、SecretKey</code>。</p>
1734 </td>
1735 </tr>
1736 <tr>
1737 <th>
1738 <legend>SecretID</legend>
1739 </th>
1740 <td><input type="text" name="secret_id" required value="<?php echo esc_attr($cos_options['secret_id']); ?>" size="50" placeholder="SecretID"/></td>
1741 </tr>
1742 <tr>
1743 <th>
1744 <legend>SecretKey</legend>
1745 </th>
1746 <td>
1747 <input type="password" name="secret_key" required value="<?php echo esc_attr($cos_options['secret_key']); ?>" size="50" placeholder="SecretKey"/>
1748 </td>
1749 </tr>
1750 <tr>
1751 <th>
1752 <legend>不上传缩略图</legend>
1753 </th>
1754 <td>
1755 <label class="switch">
1756 <input type="checkbox" name="nothumb" <?php echo $check_nothumb; ?> />
1757 <span class="slider round"></span>
1758 </label>
1759
1760 <p>建议不启用。</p>
1761 </td>
1762 </tr>
1763 <tr>
1764 <th>
1765 <legend>不在本地保留备份</legend>
1766 </th>
1767 <td>
1768 <label class="switch">
1769 <input type="checkbox" name="nolocalsaving" <?php echo $check_nolocalsaving; ?> />
1770 <span class="slider round"></span>
1771 </label>
1772
1773 <p>建议不启用。</p>
1774 </td>
1775 </tr>
1776 <tr>
1777 <th>
1778 <legend>删除�
1779 �置信息</legend>
1780 </th>
1781 <td>
1782 <label class="switch">
1783 <input type="checkbox" name="delete_options" <?php echo $check_delete_options; ?> />
1784 <span class="slider round"></span>
1785 </label>
1786
1787 <p>默认启用,删除插件时会删除当前�
1788 �置信息。</p>
1789 <p>如果不启用,删除插件时只会重置URL前缀为空,保留当前�
1790 �置信息。</p>
1791 </td>
1792 </tr>
1793 <tr>
1794 <th>
1795 <legend>自动重命名文件</legend>
1796 </th>
1797 <td>
1798 <select name="update_file_name">
1799 <option <?php echo $cos_update_file_name == 'false' ? 'selected="selected"' : '';?> value="false">不处理</option>
1800 <option <?php echo $cos_update_file_name == 'md5' ? 'selected="selected"' : '';?> value="md5">MD5</option>
1801 <option <?php echo $cos_update_file_name == 'time' ? 'selected="selected"' : '';?> value="time">时间戳+随机数</option>
1802 </select>
1803 </td>
1804 </tr>
1805 <tr>
1806 <th>
1807 <legend>本地文件夹</legend>
1808 </th>
1809 <td>
1810 <input type="text" name="upload_path" required value="<?php echo cos_get_option('upload_path'); ?>" size="50" placeholder="请输�
1811 �本地文件夹"/>
1812
1813 <p>附件在服务器上的存储位置,例如:<code>wp-content/uploads</code>(注意不要以<code>/</code>开头和结尾),根目录请输�
1814 �<code>.</code>。</p>
1815 </td>
1816 </tr>
1817 <tr>
1818 <th>
1819 <legend>URL前缀</legend>
1820 </th>
1821 <td>
1822 <input type="text" name="upload_url_path" required value="<?php echo cos_get_option('upload_url_path'); ?>" size="50" placeholder="请输�
1823 �URL前缀"/>
1824
1825 <p><b>注意:</b></p>
1826
1827 <p>1)URL前缀的格式为 <code><?php echo $protocol;?>{cos域名}/{本地文件夹}</code> ,“本地文件夹”务�
1828 与上面保持一致(结尾无 <code>/</code> ),或�
1829 “本地文件夹”为 <code>.</code> 时 <code><?php echo $protocol;?>{cos域名}</code> 。</p>
1830
1831 <p>2)COS中的存放路径(即“文件夹”)与上述 <code>本地文件夹</code> 中定义的路径是相同的(出于方便切换考虑)。</p>
1832
1833 <p>3)如果需要使用 <code>独立域名</code> ,直接将 <code>{cos域名}</code> 替换为 <code>独立域名</code> 即可。</p>
1834 </td>
1835 </tr>
1836 <tr>
1837 <th>
1838 <legend>上传至子目录</legend>
1839 </th>
1840 <td>
1841 <input type="text" name="upload_subdirectory" value="<?php echo $cos_upload_subdirectory; ?>" size="50" placeholder="请输�
1842 �子目录地址,不使用请留空"/>
1843
1844 <p>如果需要多个站点�
1845 �用一个存储桶时设置,例如:<code>sub1</code>、<code>sub1/sub2</code>(注意不要以<code>/</code>开头和结尾),支持多级;</p>
1846 <p>如果设置了上传至子目录,需要在 <b>URL前缀</b> 中增加对应的子目录,例如:<code><?php echo $protocol;?>{cos域名}/{子目录}/{本地文件夹}</code>。</p>
1847 </td>
1848 </tr>
1849 <tr>
1850 <th>
1851 <legend>图片处理样式</legend>
1852 </th>
1853 <td>
1854 <input type="text" name="ci_style" value="<?php echo esc_attr($cos_options['ci_style']); ?>" size="50" placeholder="请输�
1855 �图片处理样式,不使用请留空"/>
1856
1857 <p><b>获取样式:</b></p>
1858
1859 <p>1)在 <a href="https://console.cloud.tencent.com/cos5/bucket" target="_blank">存储桶列表</a> 中对应桶的 <code>图片处理</code> 处添加。�
1860 �体样式设置参考<a href="https://cloud.tencent.com/document/product/460/6936" target="_blank">�
1861 �讯云文档</a>。</p>
1862
1863 <p>2)填写时需要将<code>分隔符</code>和对应的<code>名称</code>或 <code>描述</code>进行拼接,例如:</p>
1864
1865 <p><code>分隔符</code>为<code>!</code>(感叹号),<code>名称</code>为<code>blog</code>,<code>描述</code>为 <code> imageMogr2/format/webp/interlace/0/quality/100</code></p>
1866 <p>则填写为 <code>!blog</code> 或 <code>?imageMogr2/format/webp/interlace/0/quality/100</code></p>
1867 </td>
1868 </tr>
1869 <tr>
1870 <th>
1871 <legend>原图保护</legend>
1872 </th>
1873 <td>
1874 <label class="switch">
1875 <input type="checkbox" name="origin_protect" <?php echo $check_origin_protect; ?> />
1876 <span class="slider round"></span>
1877 </label>
1878
1879 <p>开启原图保护功能后,存储桶中的图片文件�
1880 能以带样式的 URL 进行访问,能够阻止恶意用户对源文件的请求。</p>
1881 <p>使用时请�
1882 �访问�
1883 �讯云对象存储控制台<b>开启原图保护</b>并设置<b>图片处理样式</b>!</p>
1884 <p>注:此功能为实验性功能,如遇错误或不可用,请�
1885 �闭后联系作�
1886 反馈。</p>
1887 </td>
1888 </tr>
1889 <tr>
1890 <th></th>
1891 <td><input type="submit" class="button button-primary" value="保存更改"/></td>
1892 </tr>
1893 </table>
1894 <input type="hidden" name="type" value="cos_set">
1895 </form>
1896 <?php elseif ($current_tab == 'sync'): ?>
1897 <?php echo cos_sync_setting_form($cos_options); ?>
1898 <?php elseif ($current_tab == 'slim'): ?>
1899 <?php echo cos_ci_image_slim_page($cos_options); ?>
1900 <?php elseif ($current_tab == 'document'): ?>
1901 <?php echo cos_document_page($cos_options); ?>
1902 <?php elseif ($current_tab == 'text'): ?>
1903 <?php echo cos_ci_text_page($cos_options); ?>
1904 <?php elseif ($current_tab == 'metric'): ?>
1905 <script src="//cdnjs.cloudflare.com/ajax/libs/apexcharts/3.41.1/apexcharts.min.js"></script>
1906 <div class="charts-container">
1907 <?php
1908 $bucket = cos_get_bucket_name($cos_options);
1909 $disableCharts = defined('COS_DISABLE_CHARTS') && COS_DISABLE_CHARTS;
1910 if (!empty($bucket) && !$disableCharts) {
1911 $styleChart = !empty($cos_options['ci_style']);
1912 $previewChart = !empty($cos_options['attachment_preview']) && $cos_options['attachment_preview'] == 'on';
1913 $textChart = !empty($cos_options['ci_text_comments']) && $cos_options['ci_text_comments'] == 'on';
1914 if (defined('COS_ENABLE_STYLE_CHART')) {
1915 $styleChart = COS_ENABLE_STYLE_CHART;
1916 }
1917 if (defined('COS_ENABLE_PREVIEW_CHART')) {
1918 $previewChart = COS_ENABLE_PREVIEW_CHART;
1919 }
1920 if (defined('COS_ENABLE_TEXT_CHART')) {
1921 $textChart = COS_ENABLE_TEXT_CHART;
1922 }
1923 $monitor = new DataPoints($bucket, $cos_options);
1924 Charts::setColors($color_scheme->colors);
1925 echo Charts::storage($monitor->getStorage());
1926 echo Charts::objectNumber($monitor->getObjectNumber());
1927 echo Charts::requests($monitor->getRequests());
1928 echo Charts::traffic($monitor->getTraffic());
1929
1930 if ($styleChart) {
1931 echo Charts::ciStyle($monitor->getImageBasicsRequests());
1932 echo Charts::ciTraffic($monitor->getCITraffic());
1933 }
1934
1935 if ($previewChart) {
1936 echo Charts::ciDocumentHtml($monitor->getDocumentHtmlRequests());
1937 }
1938
1939 if ($textChart) {
1940 echo Charts::ciTextAuditing($monitor->getTextAuditing());
1941 }
1942 }
1943 ?>
1944 </div>
1945 <?php elseif ($current_tab == 'contact'): ?>
1946 <?php echo cos_contact_page(); ?>
1947 <?php endif; ?>
1948 </div>
1949 <?php
1950 }
1951 ?>
1952