/** * ============================================ * Modou COS 全局删除桥接器(必须全局加载) * 放到你的主插件入口文件中 * ============================================ */ // 你可以把这个开关改成 false,表示“仅永久删除时删 COS” if (!defined('MODOU_COS_DELETE_ON_TRASH')) { define('MODOU_COS_DELETE_ON_TRASH', false); } /** * 判断一个附件是否“可能在 COS 上” * - 1) 我们自己的外链索引标记 * - 2) URL 命中你配置的域名替换规则 * - 3) 你也可以按需扩展 */ function modou_cos_is_cos_attachment($post_id) { if (get_post_meta($post_id, '_modou_cos_is_external', true)) { return true; } $url = wp_get_attachment_url($post_id); if (!$url) return false; $bound = trim((string) get_option('modou_cos_bound_domain', '')); $from = get_option('modou_cos_replace_from', []); if (!is_array($from)) { $from = array_filter(array_map('trim', explode("\n", (string)$from))); } $candidates = array_filter(array_merge( $bound ? [$bound] : [], $from )); if (empty($candidates)) { // 没有域名规则就不做“猜测判定” return false; } foreach ($candidates as $d) { $d = trim($d); if (!$d) continue; $d = rtrim($d, '/'); if (strpos($url, $d) === 0) { return true; } } return false; } /** * 获取 COS Key(尽量稳) */ function modou_cos_get_object_key($post_id) { $key = trim((string) get_post_meta($post_id, 'modou_cos_key', true)); if ($key !== '') { return $key; } $attached = trim((string) get_post_meta($post_id, '_wp_attached_file', true)); if ($attached !== '') { return ltrim($attached, '/'); } $url = (string) get_post_meta($post_id, 'modou_cos_external_url', true); if (!$url) { $url = wp_get_attachment_url($post_id); } if ($url) { $path = wp_parse_url($url, PHP_URL_PATH); if ($path) { return ltrim($path, '/'); } } return ''; } /** * 使用 COS PHP SDK v5 删除(如果存在) */ function modou_cos_try_delete_by_sdk($key) { if (!class_exists('\\Qcloud\\Cos\\Client')) { return [false, 'SDK not found']; } $bucket = trim((string) get_option('modou_cos_bucket', '')); $region = trim((string) get_option('modou_cos_region', '')); $sid = trim((string) get_option('modou_cos_secret_id', '')); $skey = trim((string) get_option('modou_cos_secret_key', '')); if ($bucket === '' || $region === '' || $sid === '' || $skey === '') { return [false, 'Missing COS config']; } try { $client = new \Qcloud\Cos\Client([ 'region' => $region, 'schema' => 'https', 'credentials' => [ 'secretId' => $sid, 'secretKey' => $skey, ], ]); $client->deleteObject([ 'Bucket' => $bucket, 'Key' => $key, ]); return [true, 'Deleted by SDK']; } catch (\Throwable $e) { return [false, 'SDK error: ' . $e->getMessage()]; } } /** * 真正执行“尝试删 COS” */ function modou_cos_delete_remote_object_for_attachment($post_id, $reason = 'delete') { // 是否开启“同步删除”开关 $enabled = (int) get_option('modou_cos_delete_remote_enabled', 0); if (!$enabled) { return; } if (get_post_type($post_id) !== 'attachment') { return; } // 不是我们能识别的 COS 附件就不动 if (!modou_cos_is_cos_attachment($post_id)) { return; } $key = modou_cos_get_object_key($post_id); if ($key === '') { error_log('[MODOU COS] Skip delete: empty key, post_id=' . $post_id); return; } // 1) 先试 SDK [$ok, $msg] = modou_cos_try_delete_by_sdk($key); if ($ok) { error_log('[MODOU COS] ' . $msg . ' key=' . $key . ' post_id=' . $post_id); return; } // 2) 没 SDK / 配置不全 / SDK 报错 // 交给你现有 COS 插件接管 do_action('modou_cos_delete_object', $key, $post_id, $reason, $msg); error_log('[MODOU COS] Fallback action fired. key=' . $key . ' post_id=' . $post_id . ' reason=' . $reason . ' note=' . $msg); } /** * ✅ 关键:只要你在“媒体库永久删除附件”,这里一定会触发 */ add_action('before_delete_post', function($post_id){ if (get_post_type($post_id) !== 'attachment') return; modou_cos_delete_remote_object_for_attachment($post_id, 'before_delete_post'); }, 20); /** * 可选:如果你希望“丢进回收站就删 COS” * 默认关闭,避免误删 */ add_action('wp_trash_post', function($post_id){ if (!MODOU_COS_DELETE_ON_TRASH) return; if (get_post_type($post_id) !== 'attachment') return; modou_cos_delete_remote_object_for_attachment($post_id, 'wp_trash_post'); }, 20);