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

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