diff --git a/feedback.php b/feedback.php index 8ce87bd..6b96f94 100644 --- a/feedback.php +++ b/feedback.php @@ -3,310 +3,533 @@ * 问题反馈页面 * @package Argon */ + +// 在加载 WordPress 之前初始化 session +if (session_status() === PHP_SESSION_NONE) { + session_start(); +} + $wp_load_path = dirname(dirname(dirname(dirname(__FILE__)))) . '/wp-load.php'; if (!file_exists($wp_load_path)) $wp_load_path = $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php'; require_once($wp_load_path); +// 引入反馈邮件模板 +require_once(get_template_directory() . '/email-templates/feedback-notify.php'); + // 权限检查 $is_admin = current_user_can('manage_options'); +// 处理验证码刷新请求(AJAX) +if (isset($_GET['action']) && $_GET['action'] === 'refresh_captcha') { + header('Content-Type: application/json; charset=utf-8'); + if (function_exists('get_comment_captcha_seed')) { + get_comment_captcha_seed(true); + } + $captcha = function_exists('get_comment_captcha') ? get_comment_captcha() : ''; + echo json_encode(['success' => true, 'captcha' => $captcha]); + exit; +} + +// 授权链接访问检查 +$auth_view_id = null; +$auth_view_token = null; +$is_auth_view = false; +$can_view_private = false; // 是否可以查看私密留言 + +if (isset($_GET['id']) && isset($_GET['token'])) { + $auth_view_id = sanitize_text_field($_GET['id']); + $auth_view_token = sanitize_text_field($_GET['token']); + if (argon_verify_feedback_token($auth_view_id, $auth_view_token)) { + $is_auth_view = true; + $can_view_private = true; // 授权链接可以查看私密留言 + } +} + +// 管理员也可以查看私密留言 +if ($is_admin) { + $can_view_private = true; +} + +// AJAX 处理图片上传 +if (isset($_POST['feedback_action']) && $_POST['feedback_action'] === 'upload_image') { + header('Content-Type: application/json; charset=utf-8'); + + // 验证 nonce(防止 CSRF 攻击) + if (!isset($_POST['feedback_upload_nonce']) || !wp_verify_nonce($_POST['feedback_upload_nonce'], 'argon_feedback_upload')) { + echo json_encode(['success' => false, 'message' => '安全验证失败']); + exit; + } + + // IP 黑名单检查 + if (argon_is_ip_blocked_global()) { + echo json_encode(['success' => false, 'message' => '您的 IP 已被限制访问']); + exit; + } + + // 频率限制(防止滥用) + $user_identifier = argon_get_feedback_user_identifier(); + $rate_limit_key = 'feedback_upload_' . $user_identifier; + $upload_count = get_transient($rate_limit_key); + $max_uploads = intval(get_option('argon_feedback_upload_limit', 20)); + if ($upload_count !== false && $upload_count >= $max_uploads) { + echo json_encode(['success' => false, 'message' => '上传过于频繁,请稍后再试']); + exit; + } + + $image_data = $_POST['image_data'] ?? ''; + if (empty($image_data)) { + echo json_encode(['success' => false, 'message' => '图片数据为空']); + exit; + } + + $saved_url = argon_save_feedback_image($image_data); + if ($saved_url) { + // 更新频率限制计数 + $limit_period = intval(get_option('argon_feedback_upload_period', 3600)); + if ($upload_count === false) { + set_transient($rate_limit_key, 1, $limit_period); + } else { + set_transient($rate_limit_key, $upload_count + 1, $limit_period); + } + + echo json_encode(['success' => true, 'url' => $saved_url]); + } else { + echo json_encode(['success' => false, 'message' => '图片保存失败']); + } + exit; +} + // AJAX 处理反馈管理 if (isset($_POST['feedback_action'])) { - header('Content-Type: application/json; charset=utf-8'); - $response = ['success' => false, 'message' => '']; - $action = sanitize_text_field($_POST['feedback_action']); - - // 管理员操作 - if (in_array($action, ['delete', 'toggle_public', 'reply', 'update_status'])) { - if (!$is_admin) { - echo json_encode(['success' => false, 'message' => '权限不足']); - exit; - } - if (!isset($_POST['feedback_nonce']) || !wp_verify_nonce($_POST['feedback_nonce'], 'argon_feedback_manage')) { - echo json_encode(['success' => false, 'message' => '安全验证失败']); - exit; - } - } - - switch ($action) { - case 'submit': - $response = argon_handle_feedback_submit($_POST); - break; - case 'delete': - $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; - $response = ['success' => argon_delete_feedback($id)]; - break; - case 'toggle_public': - $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; - $response = argon_toggle_feedback_public($id); - break; - case 'reply': - $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; - $reply = isset($_POST['reply']) ? sanitize_textarea_field($_POST['reply']) : ''; - $response = argon_reply_feedback($id, $reply); - break; - case 'update_status': - $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; - $status = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : ''; - $response = argon_update_feedback_status($id, $status); - break; - } - echo json_encode($response); - exit; + header('Content-Type: application/json; charset=utf-8'); + $response = ['success' => false, 'message' => '']; + $action = sanitize_text_field($_POST['feedback_action']); + + // 管理员操作 + if (in_array($action, ['delete', 'toggle_public', 'reply', 'update_status', 'get', 'reply_and_resolve'])) { + if (!$is_admin) { + echo json_encode(['success' => false, 'message' => '权限不足']); + exit; + } + if (!isset($_POST['feedback_nonce']) || !wp_verify_nonce($_POST['feedback_nonce'], 'argon_feedback_manage')) { + echo json_encode(['success' => false, 'message' => '安全验证失败']); + exit; + } + } + + switch ($action) { + case 'submit': + $response = argon_handle_feedback_submit($_POST); + break; + case 'delete': + $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; + $response = ['success' => argon_delete_feedback($id)]; + break; + case 'toggle_public': + $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; + $response = argon_toggle_feedback_public($id); + break; + case 'reply': + $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; + $reply = isset($_POST['reply']) ? sanitize_textarea_field($_POST['reply']) : ''; + $is_private = !empty($_POST['is_private']); + $response = argon_reply_feedback($id, $reply, false, $is_private); + break; + case 'reply_and_resolve': + $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; + $reply = isset($_POST['reply']) ? sanitize_textarea_field($_POST['reply']) : ''; + $is_private = !empty($_POST['is_private']); + $response = argon_reply_feedback($id, $reply, true, $is_private); + break; + case 'update_status': + $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; + $status = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : ''; + $response = argon_update_feedback_status($id, $status, false); + break; + case 'get': + $id = isset($_POST['id']) ? sanitize_text_field($_POST['id']) : ''; + $feedback = argon_get_feedback($id); + if ($feedback) { + // 如果是未查看状态,自动改为已查看 + if ($feedback['status'] === 'pending') { + argon_update_feedback_status($id, 'viewed', false); + $feedback['status'] = 'viewed'; + } + // 确保 images 字段存在(兼容旧数据) + if (!isset($feedback['images'])) { + $feedback['images'] = []; + } + $response = ['success' => true, 'data' => $feedback]; + } else { + $response = ['success' => false, 'message' => '反馈不存在']; + } + break; + } + echo json_encode($response); + exit; } // ==================== 反馈数据操作函数 ==================== -/** - * 获取用户唯一标识 - */ function argon_get_feedback_user_identifier() { - if (is_user_logged_in()) { - return 'user_' . get_current_user_id(); - } - $ip = ''; - if (!empty($_SERVER['HTTP_CLIENT_IP'])) { - $ip = $_SERVER['HTTP_CLIENT_IP']; - } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $ip_list = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); - $ip = trim($ip_list[0]); - } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) { - $ip = $_SERVER['HTTP_X_REAL_IP']; - } else { - $ip = $_SERVER['REMOTE_ADDR']; - } - $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; - return substr(hash('sha256', $ip . '|' . $user_agent), 0, 16); + if (is_user_logged_in()) { + return 'user_' . get_current_user_id(); + } + $ip = $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? ''; + if (strpos($ip, ',') !== false) $ip = trim(explode(',', $ip)[0]); + $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? ''; + return substr(hash('sha256', $ip . '|' . $user_agent), 0, 16); } -/** - * 获取所有反馈 - */ function argon_get_feedbacks($filter = 'all') { - $feedbacks = get_option('argon_feedbacks', []); - if (!is_array($feedbacks)) $feedbacks = []; - - // 按时间倒序 - usort($feedbacks, function($a, $b) { - return ($b['created_at'] ?? 0) - ($a['created_at'] ?? 0); - }); - - if ($filter === 'public') { - return array_filter($feedbacks, function($f) { - return !empty($f['is_public']); - }); - } - return $feedbacks; + $feedbacks = get_option('argon_feedbacks', []); + if (!is_array($feedbacks)) $feedbacks = []; + usort($feedbacks, function($a, $b) { return ($b['created_at'] ?? 0) - ($a['created_at'] ?? 0); }); + if ($filter === 'public') { + return array_values(array_filter($feedbacks, function($f) { return !empty($f['is_public']); })); + } + return $feedbacks; } -/** - * 获取单个反馈 - */ function argon_get_feedback($id) { - $feedbacks = argon_get_feedbacks(); - foreach ($feedbacks as $f) { - if ($f['id'] === $id) return $f; - } - return null; + foreach (argon_get_feedbacks() as $f) { + if ($f['id'] === $id) return $f; + } + return null; } +function argon_get_feedback_stats() { + $feedbacks = argon_get_feedbacks(); + $stats = ['total' => count($feedbacks), 'pending' => 0, 'viewed' => 0, 'processing' => 0, 'resolved' => 0, 'closed' => 0]; + foreach ($feedbacks as $f) { + $status = $f['status'] ?? 'pending'; + if (isset($stats[$status])) $stats[$status]++; + } + return $stats; +} -/** - * 添加反馈 - */ function argon_add_feedback($data) { - $feedbacks = get_option('argon_feedbacks', []); - if (!is_array($feedbacks)) $feedbacks = []; - - $id = 'fb_' . uniqid(); - $feedback = [ - 'id' => $id, - 'title' => sanitize_text_field($data['title'] ?? ''), - 'content' => sanitize_textarea_field($data['content'] ?? ''), - 'type' => sanitize_text_field($data['type'] ?? 'suggestion'), - 'name' => sanitize_text_field($data['name'] ?? ''), - 'email' => sanitize_email($data['email'] ?? ''), - 'url' => esc_url_raw($data['url'] ?? ''), - 'is_public' => !empty($data['is_public']), - 'status' => 'pending', // pending, processing, resolved, closed - 'user_identifier' => argon_get_feedback_user_identifier(), - 'user_id' => is_user_logged_in() ? get_current_user_id() : 0, - 'ip' => $_SERVER['REMOTE_ADDR'] ?? '', - 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', - 'created_at' => time(), - 'reply' => '', - 'reply_at' => 0, - ]; - - $feedbacks[] = $feedback; - update_option('argon_feedbacks', $feedbacks); - - return $id; + $feedbacks = get_option('argon_feedbacks', []); + if (!is_array($feedbacks)) $feedbacks = []; + $id = 'fb_' . uniqid(); + // 处理图片上传 + $images = []; + if (!empty($data['feedback_images']) && is_array($data['feedback_images'])) { + $upload_dir = wp_upload_dir(); + $feedback_url_prefix = $upload_dir['baseurl'] . '/feedback-images/'; + foreach ($data['feedback_images'] as $image_url) { + if (!empty($image_url) && count($images) < 10) { + // 验证是否是本站上传的图片 URL(已经通过 upload_image 接口上传) + if (strpos($image_url, $feedback_url_prefix) === 0) { + $images[] = $image_url; + } + } + } + } + + $feedback = [ + 'id' => $id, + 'title' => sanitize_text_field($data['title'] ?? ''), + 'content' => sanitize_textarea_field($data['content'] ?? ''), + 'type' => sanitize_text_field($data['type'] ?? 'suggestion'), + 'name' => sanitize_text_field($data['name'] ?? ''), + 'email' => sanitize_email($data['email'] ?? ''), + 'url' => esc_url_raw($data['url'] ?? ''), + 'is_public' => !empty($data['is_public']), + 'status' => 'pending', + 'user_identifier' => argon_get_feedback_user_identifier(), + 'user_id' => is_user_logged_in() ? get_current_user_id() : 0, + 'ip' => $_SERVER['REMOTE_ADDR'] ?? '', + 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', + 'created_at' => time(), + 'replies' => [], + 'images' => $images, + ]; + $feedbacks[] = $feedback; + update_option('argon_feedbacks', $feedbacks); + argon_send_feedback_new_notify($feedback); + return $id; } -/** - * 删除反馈 - */ function argon_delete_feedback($id) { - $feedbacks = get_option('argon_feedbacks', []); - if (!is_array($feedbacks)) return false; - - foreach ($feedbacks as $key => $f) { - if ($f['id'] === $id) { - unset($feedbacks[$key]); - update_option('argon_feedbacks', array_values($feedbacks)); - return true; - } - } - return false; + $feedbacks = get_option('argon_feedbacks', []); + if (!is_array($feedbacks)) return false; + foreach ($feedbacks as $key => $f) { + if ($f['id'] === $id) { + unset($feedbacks[$key]); + update_option('argon_feedbacks', array_values($feedbacks)); + return true; + } + } + return false; } -/** - * 切换公开状态 - */ function argon_toggle_feedback_public($id) { - $feedbacks = get_option('argon_feedbacks', []); - if (!is_array($feedbacks)) return ['success' => false]; - - foreach ($feedbacks as &$f) { - if ($f['id'] === $id) { - $f['is_public'] = !$f['is_public']; - update_option('argon_feedbacks', $feedbacks); - return ['success' => true, 'is_public' => $f['is_public']]; - } - } - return ['success' => false]; + $feedbacks = get_option('argon_feedbacks', []); + if (!is_array($feedbacks)) return ['success' => false]; + foreach ($feedbacks as &$f) { + if ($f['id'] === $id) { + $f['is_public'] = !$f['is_public']; + update_option('argon_feedbacks', $feedbacks); + return ['success' => true, 'is_public' => $f['is_public']]; + } + } + return ['success' => false]; } - -/** - * 回复反馈 - */ -function argon_reply_feedback($id, $reply) { - $feedbacks = get_option('argon_feedbacks', []); - if (!is_array($feedbacks)) return ['success' => false]; - - foreach ($feedbacks as &$f) { - if ($f['id'] === $id) { - $f['reply'] = $reply; - $f['reply_at'] = time(); - if ($f['status'] === 'pending') { - $f['status'] = 'processing'; - } - update_option('argon_feedbacks', $feedbacks); - - // 发送邮件通知 - if (!empty($f['email']) && !empty($reply)) { - $subject = sprintf('[%s] 您的反馈已收到回复', get_bloginfo('name')); - $message = sprintf( - "您好 %s!\n\n您提交的反馈「%s」已收到回复:\n\n%s\n\n感谢您的反馈!\n\n%s", - $f['name'], - $f['title'], - $reply, - home_url() - ); - wp_mail($f['email'], $subject, $message); - } - - return ['success' => true]; - } - } - return ['success' => false]; +function argon_reply_feedback($id, $reply, $resolve = false, $is_private = false) { + $feedbacks = get_option('argon_feedbacks', []); + if (!is_array($feedbacks)) return ['success' => false]; + foreach ($feedbacks as &$f) { + if ($f['id'] === $id) { + // 检查是否已完结,已完结的反馈不允许修改状态,只能追加留言 + $is_archived = in_array($f['status'], ['resolved', 'closed']); + + // 初始化 replies 数组(兼容旧数据) + if (!isset($f['replies'])) { + $f['replies'] = []; + // 迁移旧的 reply 字段 + if (!empty($f['reply'])) { + $f['replies'][] = [ + 'content' => $f['reply'], + 'time' => $f['reply_at'] ?? time(), + 'is_private' => false, + ]; + } + } + + // 添加新留言 + if (!empty($reply)) { + $new_reply = [ + 'content' => $reply, + 'time' => time(), + 'is_private' => $is_private, + ]; + $f['replies'][] = $new_reply; + + // 只有未完结的反馈才能修改状态 + if (!$is_archived) { + if ($resolve) { + $f['status'] = 'resolved'; + } elseif ($f['status'] === 'pending' || $f['status'] === 'viewed') { + $f['status'] = 'processing'; + } + } + + update_option('argon_feedbacks', $feedbacks); + + // 发送邮件通知 + if ($resolve) { + argon_send_feedback_resolved_notify($f); + } elseif (!$is_private) { + // 只有非私密留言才发送通知 + if ($is_archived) { + // 已完结的反馈发送留言追加通知 + argon_send_feedback_reply_append_notify($f, $reply); + } else { + // 未完结的反馈发送普通回复通知 + argon_send_feedback_reply_notify($f, $reply); + } + } + } else { + // 没有留言内容,只修改状态 + if (!$is_archived && $resolve) { + $f['status'] = 'resolved'; + update_option('argon_feedbacks', $feedbacks); + argon_send_feedback_resolved_notify($f); + } + } + + return ['success' => true, 'is_archived' => $is_archived]; + } + } + return ['success' => false]; } -/** - * 更新反馈状态 - */ -function argon_update_feedback_status($id, $status) { - $valid_statuses = ['pending', 'processing', 'resolved', 'closed']; - if (!in_array($status, $valid_statuses)) { - return ['success' => false, 'message' => '无效状态']; - } - - $feedbacks = get_option('argon_feedbacks', []); - if (!is_array($feedbacks)) return ['success' => false]; - - foreach ($feedbacks as &$f) { - if ($f['id'] === $id) { - $f['status'] = $status; - update_option('argon_feedbacks', $feedbacks); - return ['success' => true]; - } - } - return ['success' => false]; +function argon_update_feedback_status($id, $status, $send_notify = true) { + $valid_statuses = ['pending', 'viewed', 'processing', 'resolved', 'closed']; + if (!in_array($status, $valid_statuses)) return ['success' => false, 'message' => '无效状态']; + $feedbacks = get_option('argon_feedbacks', []); + if (!is_array($feedbacks)) return ['success' => false]; + foreach ($feedbacks as &$f) { + if ($f['id'] === $id) { + // 检查是否已完结 + $is_archived = in_array($f['status'], ['resolved', 'closed']); + if ($is_archived) { + return ['success' => false, 'message' => '已完结的反馈不允许修改状态']; + } + + $old_status = $f['status']; + $f['status'] = $status; + update_option('argon_feedbacks', $feedbacks); + + // 只有明确要求发送通知且状态变为 resolved 时才发送 + if ($send_notify && $status === 'resolved' && $old_status !== 'resolved') { + argon_send_feedback_resolved_notify($f); + } + return ['success' => true]; + } + } + return ['success' => false]; +} + +function argon_is_feedback_captcha_enabled() { + $mode = get_option('argon_feedback_captcha_mode', 'global'); + if ($mode === 'enabled') return true; + if ($mode === 'disabled') return false; + return function_exists('argon_is_captcha_enabled') && argon_is_captcha_enabled(); +} + +function argon_save_feedback_image($base64_data) { + // 验证 base64 数据 + if (strpos($base64_data, 'data:image/') !== 0) { + return false; + } + + // 解析 base64 + $data_parts = explode(',', $base64_data); + if (count($data_parts) !== 2) { + return false; + } + + $image_data = base64_decode($data_parts[1]); + if ($image_data === false) { + return false; + } + + // 检查大小(250KB) + if (strlen($image_data) > 250 * 1024) { + return false; + } + + // 获取 MIME 类型 + preg_match('/data:image\/(\w+);/', $data_parts[0], $matches); + $ext = $matches[1] ?? 'jpg'; + if (!in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) { + $ext = 'jpg'; + } + + // 生成文件名 + $upload_dir = wp_upload_dir(); + $feedback_dir = $upload_dir['basedir'] . '/feedback-images'; + if (!file_exists($feedback_dir)) { + wp_mkdir_p($feedback_dir); + } + + $filename = 'fb_' . uniqid() . '_' . time() . '.' . $ext; + $filepath = $feedback_dir . '/' . $filename; + + // 保存文件 + if (file_put_contents($filepath, $image_data) === false) { + return false; + } + + // 返回相对 URL + return $upload_dir['baseurl'] . '/feedback-images/' . $filename; } -/** - * 处理反馈提交 - */ function argon_handle_feedback_submit($data) { - // 验证必填字段 - if (empty($data['feedback_email'])) { - return ['success' => false, 'message' => __('请填写邮箱', 'argon')]; - } - if (empty($data['feedback_name'])) { - return ['success' => false, 'message' => __('请填写昵称', 'argon')]; - } - if (empty($data['feedback_content'])) { - return ['success' => false, 'message' => __('请填写反馈内容', 'argon')]; - } - - // 验证邮箱格式 - if (!is_email($data['feedback_email'])) { - return ['success' => false, 'message' => __('邮箱格式错误', 'argon')]; - } - - // 验证码检查 - $captcha_enabled = get_option('argon_feedback_captcha', 'true') === 'true'; - if ($captcha_enabled && function_exists('check_comment_captcha')) { - $captcha_input = $data['feedback_captcha'] ?? ''; - if (!check_comment_captcha($captcha_input)) { - return ['success' => false, 'message' => __('验证码错误', 'argon')]; - } - } - - // 添加反馈 - $id = argon_add_feedback([ - 'title' => $data['feedback_title'] ?? '', - 'content' => $data['feedback_content'], - 'type' => $data['feedback_type'] ?? 'suggestion', - 'name' => $data['feedback_name'], - 'email' => $data['feedback_email'], - 'url' => $data['feedback_url'] ?? '', - 'is_public' => !empty($data['feedback_public']), - ]); - - if ($id) { - return ['success' => true, 'message' => __('反馈提交成功,感谢您的建议!', 'argon')]; - } - return ['success' => false, 'message' => __('提交失败,请稍后重试', 'argon')]; + // IP 黑名单检查 + if (argon_is_ip_blocked_global()) { + return ['success' => false, 'message' => __('您的 IP 已被限制访问', 'argon')]; + } + + // 提交频率限制 + $user_identifier = argon_get_feedback_user_identifier(); + $submit_limit_key = 'feedback_submit_' . $user_identifier; + $submit_count = get_transient($submit_limit_key); + $max_submits = intval(get_option('argon_feedback_submit_limit', 5)); + $limit_period = intval(get_option('argon_feedback_submit_period', 3600)); + if ($submit_count !== false && $submit_count >= $max_submits) { + return ['success' => false, 'message' => __('提交过于频繁,请稍后再试', 'argon')]; + } + + if (empty($data['feedback_email'])) return ['success' => false, 'message' => __('请填写邮箱', 'argon')]; + if (empty($data['feedback_name'])) return ['success' => false, 'message' => __('请填写昵称', 'argon')]; + if (empty($data['feedback_content'])) return ['success' => false, 'message' => __('请填写反馈内容', 'argon')]; + if (!is_email($data['feedback_email'])) return ['success' => false, 'message' => __('邮箱格式错误', 'argon')]; + + // 验证码检查 + if (argon_is_feedback_captcha_enabled()) { + $captcha_type = get_option('argon_captcha_type', 'math'); + if ($captcha_type === 'geetest') { + if (function_exists('geetest_validate')) { + $lot_number = $data['geetest_lot_number'] ?? ''; + $captcha_output = $data['geetest_captcha_output'] ?? ''; + $pass_token = $data['geetest_pass_token'] ?? ''; + $gen_time = $data['geetest_gen_time'] ?? ''; + if (empty($lot_number) || empty($captcha_output)) { + return ['success' => false, 'message' => __('请完成验证码验证', 'argon')]; + } + $result = geetest_validate($lot_number, $captcha_output, $pass_token, $gen_time); + if ($result !== true) return ['success' => false, 'message' => __('验证码验证失败', 'argon')]; + } + } else { + $captcha_input = $data['feedback_captcha'] ?? ''; + if (empty($captcha_input)) { + return ['success' => false, 'message' => __('请输入验证码', 'argon')]; + } + if (function_exists('get_comment_captcha_answer')) { + $correct_answer = get_comment_captcha_answer(); + if ($captcha_input != $correct_answer) { + if (function_exists('get_comment_captcha_seed')) get_comment_captcha_seed(true); + return ['success' => false, 'message' => __('验证码错误', 'argon')]; + } + if (function_exists('get_comment_captcha_seed')) get_comment_captcha_seed(true); + } + } + } + + $id = argon_add_feedback([ + 'title' => $data['feedback_title'] ?? '', + 'content' => $data['feedback_content'], + 'type' => $data['feedback_type'] ?? 'suggestion', + 'name' => $data['feedback_name'], + 'email' => $data['feedback_email'], + 'url' => $data['feedback_url'] ?? '', + 'is_public' => !empty($data['feedback_public']), + 'feedback_images' => $data['feedback_images'] ?? [], + ]); + + if ($id) { + // 更新提交频率计数 + if ($submit_count === false) { + set_transient($submit_limit_key, 1, $limit_period); + } else { + set_transient($submit_limit_key, $submit_count + 1, $limit_period); + } + return ['success' => true, 'message' => __('反馈提交成功,感谢您的建议!', 'argon')]; + } + return ['success' => false, 'message' => __('提交失败,请稍后重试', 'argon')]; } - // ==================== 页面渲染 ==================== - -// 获取反馈数据 $all_feedbacks = argon_get_feedbacks(); $public_feedbacks = argon_get_feedbacks('public'); -$pending_count = count(array_filter($all_feedbacks, function($f) { return $f['status'] === 'pending'; })); +$stats = argon_get_feedback_stats(); +$captcha_enabled = argon_is_feedback_captcha_enabled(); +$captcha_type = get_option('argon_captcha_type', 'math'); -// 验证码设置 -$captcha_enabled = get_option('argon_feedback_captcha', 'true') === 'true'; +$saved_name = $_COOKIE['comment_author_' . COOKIEHASH] ?? ''; +$saved_email = $_COOKIE['comment_author_email_' . COOKIEHASH] ?? ''; +$saved_url = $_COOKIE['comment_author_url_' . COOKIEHASH] ?? ''; -// 从 Cookie 获取用户信息 -$saved_name = isset($_COOKIE['comment_author_' . COOKIEHASH]) ? $_COOKIE['comment_author_' . COOKIEHASH] : ''; -$saved_email = isset($_COOKIE['comment_author_email_' . COOKIEHASH]) ? $_COOKIE['comment_author_email_' . COOKIEHASH] : ''; -$saved_url = isset($_COOKIE['comment_author_url_' . COOKIEHASH]) ? $_COOKIE['comment_author_url_' . COOKIEHASH] : ''; - -// 如果已登录,使用用户信息 if (is_user_logged_in()) { - $current_user = wp_get_current_user(); - $saved_name = $current_user->display_name; - $saved_email = $current_user->user_email; - $saved_url = $current_user->user_url; + $current_user = wp_get_current_user(); + $saved_name = $current_user->display_name; + $saved_email = $current_user->user_email; + $saved_url = $current_user->user_url; +} + +$single_feedback = null; +if ($is_auth_view && $auth_view_id) { + $single_feedback = argon_get_feedback($auth_view_id); } get_header(); + +$type_labels = ['bug' => __('Bug', 'argon'), 'suggestion' => __('建议', 'argon'), 'question' => __('问题', 'argon'), 'other' => __('其他', 'argon')]; +$status_labels = ['pending' => __('未查看', 'argon'), 'viewed' => __('已查看', 'argon'), 'processing' => __('进行中', 'argon'), 'resolved' => __('已完成', 'argon'), 'closed' => __('已关闭', 'argon')]; ?>
@@ -314,497 +537,1228 @@ get_header();