refactor: rename the theme brand and every identifier from AKSRT to Jio
Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.
Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)
Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
(dropped 3 stale entries, added the 17 missing ones)
Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
when it is empty
Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.
Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5b16bc6e15
commit
35bb96e82f
997 files changed
+28216
-28188
No files matched your search
+26
-26
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -52,8 +52,8 @@ class AIController extends ControllerBase {
|
||||
if (current_user_can('edit_posts')) {
|
||||
return true;
|
||||
}
|
||||
if (class_exists('\AKSRT\Core\FrontSubmit')) {
|
||||
return (bool) \AKSRT\Core\FrontSubmit::get_instance()->can_submit();
|
||||
if (class_exists('\Jio\Core\FrontSubmit')) {
|
||||
return (bool) \Jio\Core\FrontSubmit::get_instance()->can_submit();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -62,8 +62,8 @@ class AIController extends ControllerBase {
|
||||
* POST /ai/seo-generate — 从 post/term 实体生成 SEO 三字段。
|
||||
*/
|
||||
public function seo_generate(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Ai\AISEO')) {
|
||||
return $this->error(__('AI SEO 模块未加载', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Ai\AISEO')) {
|
||||
return $this->error(__('AI SEO 模块未加载', 'jio'), 500);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
@@ -71,30 +71,30 @@ class AIController extends ControllerBase {
|
||||
$id = absint($params['id'] ?? 0);
|
||||
|
||||
if (!in_array($type, ['post', 'term'], true) || $id <= 0) {
|
||||
return $this->error(__('参数错误', 'aksrt'), 422);
|
||||
return $this->error(__('参数错误', 'jio'), 422);
|
||||
}
|
||||
|
||||
if ($type === 'post') {
|
||||
if (!current_user_can('edit_post', $id)) {
|
||||
return $this->error(__('权限不足,无法编辑此文章', 'aksrt'), 403);
|
||||
return $this->error(__('权限不足,无法编辑此文章', 'jio'), 403);
|
||||
}
|
||||
} else {
|
||||
if (!current_user_can('edit_term', $id)) {
|
||||
return $this->error(__('权限不足,无法编辑此分类/标签', 'aksrt'), 403);
|
||||
return $this->error(__('权限不足,无法编辑此分类/标签', 'jio'), 403);
|
||||
}
|
||||
}
|
||||
|
||||
$result = \AKSRT\Ai\AISEO::get_instance()->generate_fields($type, $id);
|
||||
$result = \Jio\Ai\AISEO::get_instance()->generate_fields($type, $id);
|
||||
if (empty($result['success'])) {
|
||||
return $this->error((string) ($result['message'] ?? __('生成失败', 'aksrt')));
|
||||
return $this->error((string) ($result['message'] ?? __('生成失败', 'jio')));
|
||||
}
|
||||
|
||||
return $this->success(['data' => $result['data']]);
|
||||
}
|
||||
|
||||
public function summary_suggest(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Ai\AI') || !class_exists('\AKSRT\Ai\AIPrompt')) {
|
||||
return $this->error(__('AI 模块未加载', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Ai\AI') || !class_exists('\Jio\Ai\AIPrompt')) {
|
||||
return $this->error(__('AI 模块未加载', 'jio'), 500);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
@@ -114,19 +114,19 @@ class AIController extends ControllerBase {
|
||||
$max_len = max(50, min(500, (int) ($params['max_length'] ?? 200)));
|
||||
|
||||
if ($content === '') {
|
||||
return $this->error(__('内容不能为空,请先在编辑器填写正文后再生成摘要', 'aksrt'), 422);
|
||||
return $this->error(__('内容不能为空,请先在编辑器填写正文后再生成摘要', 'jio'), 422);
|
||||
}
|
||||
|
||||
$ai = \AKSRT\Ai\AI::get_instance();
|
||||
$ai = \Jio\Ai\AI::get_instance();
|
||||
if (!$ai->is_available()) {
|
||||
return $this->error(
|
||||
__('AI 服务不可用,请配置 WordPress AI Connectors。', 'aksrt'),
|
||||
__('AI 服务不可用,请配置 WordPress AI Connectors。', 'jio'),
|
||||
503
|
||||
);
|
||||
}
|
||||
|
||||
// Prefer excerpt-oriented prompt when title is present.
|
||||
$prompt_builder = \AKSRT\Ai\AIPrompt::get_instance();
|
||||
$prompt_builder = \Jio\Ai\AIPrompt::get_instance();
|
||||
if ($title !== '' && method_exists($prompt_builder, 'build_excerpt_prompt')) {
|
||||
$prompt = $prompt_builder->build_excerpt_prompt($title, $content);
|
||||
} else {
|
||||
@@ -136,13 +136,13 @@ class AIController extends ControllerBase {
|
||||
$result = $ai->generate_text($prompt, [
|
||||
'max_tokens' => min(600, max(120, $max_len * 2)),
|
||||
'temperature' => 0.5,
|
||||
'system_instruction' => __('你是中文内容编辑,只输出摘要正文,不要标题、前缀或 Markdown。', 'aksrt'),
|
||||
'system_instruction' => __('你是中文内容编辑,只输出摘要正文,不要标题、前缀或 Markdown。', 'jio'),
|
||||
]);
|
||||
|
||||
if (!empty($result['success'])) {
|
||||
$summary = trim((string) ($result['content'] ?? ''));
|
||||
if ($summary === '') {
|
||||
return $this->error(__('AI 返回内容为空,请稍后重试或检查模型配置', 'aksrt'), 502);
|
||||
return $this->error(__('AI 返回内容为空,请稍后重试或检查模型配置', 'jio'), 502);
|
||||
}
|
||||
// Soft trim to requested length (characters).
|
||||
if (function_exists('mb_substr') && mb_strlen($summary) > $max_len + 20) {
|
||||
@@ -158,7 +158,7 @@ class AIController extends ControllerBase {
|
||||
|
||||
$message = trim((string) ($result['message'] ?? ''));
|
||||
if ($message === '') {
|
||||
$message = __('生成失败', 'aksrt');
|
||||
$message = __('生成失败', 'jio');
|
||||
}
|
||||
|
||||
// Upstream/provider failures are not client validation errors.
|
||||
@@ -173,8 +173,8 @@ class AIController extends ControllerBase {
|
||||
}
|
||||
|
||||
public function tags_suggest(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Ai\AI') || !class_exists('\AKSRT\Ai\AIPrompt')) {
|
||||
return $this->error(__('AI 模块未加载', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Ai\AI') || !class_exists('\Jio\Ai\AIPrompt')) {
|
||||
return $this->error(__('AI 模块未加载', 'jio'), 500);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
@@ -182,11 +182,11 @@ class AIController extends ControllerBase {
|
||||
$content = wp_kses_post((string) ($params['content'] ?? ''));
|
||||
|
||||
if ($title === '' && trim(wp_strip_all_tags($content)) === '') {
|
||||
return $this->error(__('标题或内容不能为空', 'aksrt'));
|
||||
return $this->error(__('标题或内容不能为空', 'jio'));
|
||||
}
|
||||
|
||||
$prompt = \AKSRT\Ai\AIPrompt::get_instance()->build_tags_prompt($title, $content);
|
||||
$result = \AKSRT\Ai\AI::get_instance()->generate_text($prompt, [
|
||||
$prompt = \Jio\Ai\AIPrompt::get_instance()->build_tags_prompt($title, $content);
|
||||
$result = \Jio\Ai\AI::get_instance()->generate_text($prompt, [
|
||||
'max_tokens' => 300,
|
||||
'temperature' => 0.5,
|
||||
]);
|
||||
@@ -200,6 +200,6 @@ class AIController extends ControllerBase {
|
||||
return $this->success(['data' => ['tags' => $tags]]);
|
||||
}
|
||||
|
||||
return $this->error((string) ($result['message'] ?? __('生成失败', 'aksrt')));
|
||||
return $this->error((string) ($result['message'] ?? __('生成失败', 'jio')));
|
||||
}
|
||||
}
|
||||
+38
-38
@@ -1,17 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class Admin {
|
||||
|
||||
private static $instance = null;
|
||||
private const PAGE_SLUG = 'aksrt-settings';
|
||||
private const API_NAMESPACE = 'aksrt/v1';
|
||||
private const PAGE_SLUG = 'jio-settings';
|
||||
private const API_NAMESPACE = 'jio/v1';
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) {
|
||||
@@ -29,16 +29,16 @@ class Admin {
|
||||
add_filter('script_loader_tag', [$this, 'add_module_type_attribute'], 10, 2);
|
||||
add_action('rest_api_init', [$this, 'register_rest_routes']);
|
||||
|
||||
add_action('wp_ajax_aksrt_load_more_posts', [$this, 'ajax_load_more_posts']);
|
||||
add_action('wp_ajax_nopriv_aksrt_load_more_posts', [$this, 'ajax_load_more_posts']);
|
||||
add_action('wp_ajax_aksrt_load_more_comments', [$this, 'ajax_load_more_comments']);
|
||||
add_action('wp_ajax_nopriv_aksrt_load_more_comments', [$this, 'ajax_load_more_comments']);
|
||||
add_action('wp_ajax_jio_load_more_posts', [$this, 'ajax_load_more_posts']);
|
||||
add_action('wp_ajax_nopriv_jio_load_more_posts', [$this, 'ajax_load_more_posts']);
|
||||
add_action('wp_ajax_jio_load_more_comments', [$this, 'ajax_load_more_comments']);
|
||||
add_action('wp_ajax_nopriv_jio_load_more_comments', [$this, 'ajax_load_more_comments']);
|
||||
}
|
||||
|
||||
public function add_admin_menu(): void {
|
||||
add_menu_page(
|
||||
'AKSRT 主题设置',
|
||||
'AKSRT',
|
||||
'Jio 主题设置',
|
||||
'Jio',
|
||||
'manage_options',
|
||||
self::PAGE_SLUG,
|
||||
[$this, 'render_admin_page'],
|
||||
@@ -48,8 +48,8 @@ class Admin {
|
||||
}
|
||||
|
||||
public function render_admin_page(): void {
|
||||
echo '<div class="wrap aksrt-admin-wrap">';
|
||||
echo '<div id="aksrt-admin-app"></div>';
|
||||
echo '<div class="wrap jio-admin-wrap">';
|
||||
echo '<div id="jio-admin-app"></div>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ class Admin {
|
||||
return;
|
||||
}
|
||||
|
||||
$build_dir = trailingslashit(AKSRT_DIR) . 'assets/admin/build/';
|
||||
$build_uri = trailingslashit(AKSRT_URI) . 'assets/admin/build/';
|
||||
$build_dir = trailingslashit(JIO_DIR) . 'assets/admin/build/';
|
||||
$build_uri = trailingslashit(JIO_URI) . 'assets/admin/build/';
|
||||
$entry = $build_dir . 'index.js';
|
||||
|
||||
if (!is_file($entry)) {
|
||||
@@ -69,7 +69,7 @@ class Admin {
|
||||
return;
|
||||
}
|
||||
echo '<div class="notice notice-error"><p>';
|
||||
echo esc_html__('AKSRT 管理后台构建缺失:请在 assets/admin 执行 npm run build,并完整部署 build/ 目录。', 'aksrt');
|
||||
echo esc_html__('Jio 管理后台构建缺失:请在 assets/admin 执行 npm run build,并完整部署 build/ 目录。', 'jio');
|
||||
echo '</p></div>';
|
||||
});
|
||||
return;
|
||||
@@ -82,7 +82,7 @@ class Admin {
|
||||
return;
|
||||
}
|
||||
echo '<div class="notice notice-error"><p>';
|
||||
echo esc_html__('AKSRT 管理后台构建不完整(动态模块缺失),请重新完整部署 assets/admin/build/,不要只覆盖 index.js。缺失:', 'aksrt');
|
||||
echo esc_html__('Jio 管理后台构建不完整(动态模块缺失),请重新完整部署 assets/admin/build/,不要只覆盖 index.js。缺失:', 'jio');
|
||||
echo ' ' . esc_html(implode(', ', array_slice($missing_chunks, 0, 8)));
|
||||
if (count($missing_chunks) > 8) {
|
||||
echo '…';
|
||||
@@ -102,7 +102,7 @@ class Admin {
|
||||
$path = $build_dir . $css_file;
|
||||
if (file_exists($path)) {
|
||||
wp_enqueue_style(
|
||||
'aksrt-admin-' . sanitize_key(str_replace(['.', '/'], '-', $css_file)),
|
||||
'jio-admin-' . sanitize_key(str_replace(['.', '/'], '-', $css_file)),
|
||||
$build_uri . $css_file,
|
||||
[],
|
||||
(string) filemtime($path)
|
||||
@@ -111,12 +111,12 @@ class Admin {
|
||||
}
|
||||
|
||||
// Use the same local FA5 pack as the public stack.
|
||||
aksrt_enqueue_fontawesome();
|
||||
jio_enqueue_fontawesome();
|
||||
|
||||
// Frontend SVG sprite (defines window.aksrt_svgs and injects the symbol
|
||||
// Frontend SVG sprite (defines window.jio_svgs and injects the symbol
|
||||
// sheet) so schema icon fields can preview built-in zibsvg values.
|
||||
wp_enqueue_script(
|
||||
'aksrt-svg-sprite',
|
||||
'jio-svg-sprite',
|
||||
get_template_directory_uri() . '/assets/js/runtime/svg-icon.js',
|
||||
['jquery'],
|
||||
(string) filemtime(get_template_directory() . '/assets/js/runtime/svg-icon.js'),
|
||||
@@ -124,20 +124,20 @@ class Admin {
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'aksrt-admin-app',
|
||||
'jio-admin-app',
|
||||
$build_uri . 'index.js',
|
||||
[],
|
||||
(string) filemtime($entry),
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script('aksrt-admin-app', 'aksrtAdminData', [
|
||||
wp_localize_script('jio-admin-app', 'jioAdminData', [
|
||||
'apiUrl' => esc_url_raw(rest_url(self::API_NAMESPACE)),
|
||||
'nonce' => wp_create_nonce('wp_rest'),
|
||||
'siteUrl' => home_url('/'),
|
||||
'adminUrl' => admin_url('/'),
|
||||
'themeVersion' => wp_get_theme()->get('Version') ?: '3.4.0-dev',
|
||||
'themeUri' => defined('AKSRT_URI') ? AKSRT_URI : get_template_directory_uri(),
|
||||
'themeUri' => defined('JIO_URI') ? JIO_URI : get_template_directory_uri(),
|
||||
'buildOk' => $missing_chunks === [],
|
||||
]);
|
||||
|
||||
@@ -211,14 +211,14 @@ class Admin {
|
||||
}
|
||||
|
||||
public function add_module_type_attribute(string $tag, string $handle): string {
|
||||
if ('aksrt-admin-app' === $handle) {
|
||||
if ('jio-admin-app' === $handle) {
|
||||
return str_replace('<script ', '<script type="module" ', $tag);
|
||||
}
|
||||
return $tag;
|
||||
}
|
||||
|
||||
public function register_rest_routes(): void {
|
||||
// FQCN required after PSR-4 move into AKSRT\Admin (unqualified class_exists skips all routes).
|
||||
// FQCN required after PSR-4 move into Jio\Admin (unqualified class_exists skips all routes).
|
||||
$controllers = [
|
||||
GlobalController::class,
|
||||
PageDisplayController::class,
|
||||
@@ -233,7 +233,7 @@ class Admin {
|
||||
CommentController::class,
|
||||
MessageController::class,
|
||||
AIController::class,
|
||||
\AKSRT\Icons\IconController::class,
|
||||
\Jio\Icons\IconController::class,
|
||||
];
|
||||
|
||||
foreach ($controllers as $class_name) {
|
||||
@@ -325,8 +325,8 @@ class Admin {
|
||||
|
||||
public function ajax_load_more_posts(): void {
|
||||
$nonce = isset($_POST['nonce']) ? (string) wp_unslash($_POST['nonce']) : '';
|
||||
if ($nonce === '' || !wp_verify_nonce($nonce, 'aksrt_public_nonce')) {
|
||||
wp_send_json_error(['message' => __('安全验证失败', 'aksrt')], 403);
|
||||
if ($nonce === '' || !wp_verify_nonce($nonce, 'jio_public_nonce')) {
|
||||
wp_send_json_error(['message' => __('安全验证失败', 'jio')], 403);
|
||||
}
|
||||
|
||||
$page = max(1, (int) ($_POST['page'] ?? 1));
|
||||
@@ -338,7 +338,7 @@ class Admin {
|
||||
if ($context === '') {
|
||||
$context = 'archive';
|
||||
}
|
||||
$resolved = aksrt_resolve_post_list_render_config([
|
||||
$resolved = jio_resolve_post_list_render_config([
|
||||
'context' => $context,
|
||||
'overrides' => $render_config,
|
||||
]);
|
||||
@@ -353,7 +353,7 @@ class Admin {
|
||||
|
||||
$query = new \WP_Query($query_vars);
|
||||
ob_start();
|
||||
aksrt_render_post_list($render_config, $query, true);
|
||||
jio_render_post_list($render_config, $query, true);
|
||||
$html = (string) ob_get_clean();
|
||||
|
||||
wp_send_json_success([
|
||||
@@ -365,30 +365,30 @@ class Admin {
|
||||
|
||||
public function ajax_load_more_comments(): void {
|
||||
$nonce = isset($_POST['nonce']) ? (string) wp_unslash($_POST['nonce']) : '';
|
||||
if ($nonce === '' || !wp_verify_nonce($nonce, 'aksrt_public_nonce')) {
|
||||
wp_send_json_error(['message' => __('安全验证失败', 'aksrt')], 403);
|
||||
if ($nonce === '' || !wp_verify_nonce($nonce, 'jio_public_nonce')) {
|
||||
wp_send_json_error(['message' => __('安全验证失败', 'jio')], 403);
|
||||
}
|
||||
|
||||
$post_id = absint($_POST['post_id'] ?? 0);
|
||||
$page = max(1, (int) ($_POST['page'] ?? 1));
|
||||
if ($post_id <= 0) {
|
||||
wp_send_json_error(['message' => __('无效的文章 ID', 'aksrt')], 400);
|
||||
wp_send_json_error(['message' => __('无效的文章 ID', 'jio')], 400);
|
||||
}
|
||||
|
||||
$order = sanitize_key((string) ($_POST['comment_order'] ?? $_POST['order'] ?? 'newest'));
|
||||
$author_only = !empty($_POST['author_only']) || (isset($_POST['aksrt_author_only']) && (int) $_POST['aksrt_author_only'] === 1);
|
||||
$author_only = !empty($_POST['author_only']) || (isset($_POST['jio_author_only']) && (int) $_POST['jio_author_only'] === 1);
|
||||
|
||||
if (!function_exists('aksrt_comments_query_for_post') || !function_exists('aksrt_comments_render_items_html')) {
|
||||
wp_send_json_error(['message' => __('评论加载不可用', 'aksrt')]);
|
||||
if (!function_exists('jio_comments_query_for_post') || !function_exists('jio_comments_render_items_html')) {
|
||||
wp_send_json_error(['message' => __('评论加载不可用', 'jio')]);
|
||||
}
|
||||
|
||||
$result = aksrt_comments_query_for_post($post_id, [
|
||||
$result = jio_comments_query_for_post($post_id, [
|
||||
'page' => $page,
|
||||
'order' => $order,
|
||||
'author_only' => $author_only,
|
||||
]);
|
||||
|
||||
$html = aksrt_comments_render_items_html($result['comments'], $result['settings'], $result['order']);
|
||||
$html = jio_comments_render_items_html($result['comments'], $result['settings'], $result['order']);
|
||||
|
||||
wp_send_json_success([
|
||||
'html' => $html,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -12,7 +12,7 @@ abstract class ControllerBase {
|
||||
/**
|
||||
* REST API namespace (shared with Admin).
|
||||
*/
|
||||
const API_NAMESPACE = 'aksrt/v1';
|
||||
const API_NAMESPACE = 'jio/v1';
|
||||
|
||||
/**
|
||||
* Register REST routes for this domain.
|
||||
@@ -28,12 +28,12 @@ abstract class ControllerBase {
|
||||
if ($request === null) {
|
||||
return false;
|
||||
}
|
||||
return new \WP_Error('aksrt_admin_forbidden', __('无权执行此操作', 'aksrt'), ['status' => 403]);
|
||||
return new \WP_Error('jio_admin_forbidden', __('无权执行此操作', 'jio'), ['status' => 403]);
|
||||
}
|
||||
if ($request !== null) {
|
||||
$nonce = (string) $request->get_header('X-WP-Nonce');
|
||||
if ($nonce === '' || !wp_verify_nonce($nonce, 'wp_rest')) {
|
||||
return new \WP_Error('aksrt_rest_nonce', __('安全验证失败,请刷新页面后重试', 'aksrt'), ['status' => 403]);
|
||||
return new \WP_Error('jio_rest_nonce', __('安全验证失败,请刷新页面后重试', 'jio'), ['status' => 403]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -109,10 +109,10 @@ abstract class ControllerBase {
|
||||
$out[$key] = array_values(array_unique($ids));
|
||||
break;
|
||||
case 'color':
|
||||
$out[$key] = \AKSRT\Core\Color::sanitize($val);
|
||||
$out[$key] = \Jio\Core\Color::sanitize($val);
|
||||
break;
|
||||
case 'color_alpha':
|
||||
$out[$key] = \AKSRT\Core\Color::sanitize($val, true);
|
||||
$out[$key] = \Jio\Core\Color::sanitize($val, true);
|
||||
break;
|
||||
default:
|
||||
$out[$key] = sanitize_text_field($val);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -70,11 +70,11 @@ class ExtensionController extends ControllerBase {
|
||||
|
||||
// Build message
|
||||
if ($installed === 0) {
|
||||
$message = sprintf(__('已注册 %d 项扩展,均未启用。', 'aksrt'), $available);
|
||||
$message = sprintf(__('已注册 %d 项扩展,均未启用。', 'jio'), $available);
|
||||
} elseif ($unconfigured > 0) {
|
||||
$message = sprintf(__('已启用 %d/%d 项扩展,%d 项待补全配置。', 'aksrt'), $installed, $available, $unconfigured);
|
||||
$message = sprintf(__('已启用 %d/%d 项扩展,%d 项待补全配置。', 'jio'), $installed, $available, $unconfigured);
|
||||
} else {
|
||||
$message = sprintf(__('已启用 %d/%d 项扩展,全部就绪。', 'aksrt'), $installed, $available);
|
||||
$message = sprintf(__('已启用 %d/%d 项扩展,全部就绪。', 'jio'), $installed, $available);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
@@ -140,14 +140,14 @@ class ExtensionController extends ControllerBase {
|
||||
$ip = trim((string) ($params['ip'] ?? ''));
|
||||
|
||||
if ($ip === '' || !filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
return $this->error(__('请输入有效的 IP 地址', 'aksrt'), 422);
|
||||
return $this->error(__('请输入有效的 IP 地址', 'jio'), 422);
|
||||
}
|
||||
|
||||
if (!class_exists('\AKSRT\Extensions\IPGeo')) {
|
||||
return $this->error(__('IP 归属地服务不可用', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Extensions\IPGeo')) {
|
||||
return $this->error(__('IP 归属地服务不可用', 'jio'), 500);
|
||||
}
|
||||
|
||||
$geo = \AKSRT\Extensions\IPGeo::get_instance();
|
||||
$geo = \Jio\Extensions\IPGeo::get_instance();
|
||||
$status = $geo->get_status();
|
||||
$result = $geo->lookup($ip, true);
|
||||
|
||||
@@ -157,12 +157,12 @@ class ExtensionController extends ControllerBase {
|
||||
|
||||
// Persist "verified" once a live lookup succeeds so overview cards leave "待补全".
|
||||
if ($ok) {
|
||||
$all = \AKSRT\Settings\Settings::get_all();
|
||||
$all = \Jio\Settings\Settings::get_all();
|
||||
if (empty($all['ip_geo_verified'])) {
|
||||
$all['ip_geo_verified'] = true;
|
||||
update_option('aksrt_settings', $all);
|
||||
if (method_exists('\AKSRT\Settings\Settings', 'flush_settings_cache')) {
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
update_option('jio_settings', $all);
|
||||
if (method_exists('\Jio\Settings\Settings', 'flush_settings_cache')) {
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
}
|
||||
$status = $geo->get_status();
|
||||
}
|
||||
@@ -172,55 +172,55 @@ class ExtensionController extends ControllerBase {
|
||||
$detail = trim((string) ($result['detail'] ?? ''));
|
||||
switch ($reason) {
|
||||
case 'private_ip':
|
||||
$message = __('该地址为私有/保留 IP(如 127.0.0.1 / 内网),无法查询。请用公网 IP 测试,例如 114.114.114.114。', 'aksrt');
|
||||
$message = __('该地址为私有/保留 IP(如 127.0.0.1 / 内网),无法查询。请用公网 IP 测试,例如 114.114.114.114。', 'jio');
|
||||
break;
|
||||
case 'invalid_ip':
|
||||
$message = __('IP 格式无效。', 'aksrt');
|
||||
$message = __('IP 格式无效。', 'jio');
|
||||
break;
|
||||
case 'missing_key':
|
||||
$message = __('当前服务商 AppKey 未填写,请在「扩展 → IP归属地」补全凭据。', 'aksrt');
|
||||
$message = __('当前服务商 AppKey 未填写,请在「扩展 → IP归属地」补全凭据。', 'jio');
|
||||
break;
|
||||
case 'provider_error':
|
||||
// e.g. USERKEY_PLAT_NOMATCH / INVALID_USER_KEY from Amap
|
||||
if (stripos($detail, 'USERKEY_PLAT_NOMATCH') !== false) {
|
||||
$message = __('高德返回 USERKEY_PLAT_NOMATCH:当前 Key 平台类型不匹配。请在高德开放平台新建「Web服务」类型 Key,并开通 IP 定位,不要用 Web端(JS)/Android/iOS Key。', 'aksrt');
|
||||
$message = __('高德返回 USERKEY_PLAT_NOMATCH:当前 Key 平台类型不匹配。请在高德开放平台新建「Web服务」类型 Key,并开通 IP 定位,不要用 Web端(JS)/Android/iOS Key。', 'jio');
|
||||
} elseif (stripos($detail, 'INVALID_USER_KEY') !== false) {
|
||||
$message = __('高德返回 INVALID_USER_KEY:AppKey 无效或未启用,请核对 Key 是否复制完整、是否已启用。', 'aksrt');
|
||||
$message = __('高德返回 INVALID_USER_KEY:AppKey 无效或未启用,请核对 Key 是否复制完整、是否已启用。', 'jio');
|
||||
} else {
|
||||
$message = $detail !== ''
|
||||
? sprintf(
|
||||
/* translators: %s: provider error code/message */
|
||||
__('服务商拒绝请求:%s。请检查 Key 权限(IP定位)与配额。', 'aksrt'),
|
||||
__('服务商拒绝请求:%s。请检查 Key 权限(IP定位)与配额。', 'jio'),
|
||||
$detail
|
||||
)
|
||||
: __('服务商返回错误,请检查 Key 权限与配额。', 'aksrt');
|
||||
: __('服务商返回错误,请检查 Key 权限与配额。', 'jio');
|
||||
}
|
||||
break;
|
||||
case 'http_error':
|
||||
$message = $detail !== ''
|
||||
? sprintf(
|
||||
/* translators: %s: transport error */
|
||||
__('无法访问归属地接口:%s。请检查服务器出站网络/防火墙。', 'aksrt'),
|
||||
__('无法访问归属地接口:%s。请检查服务器出站网络/防火墙。', 'jio'),
|
||||
$detail
|
||||
)
|
||||
: __('无法访问归属地接口,请检查服务器出站网络。', 'aksrt');
|
||||
: __('无法访问归属地接口,请检查服务器出站网络。', 'jio');
|
||||
break;
|
||||
case 'provider_empty':
|
||||
if (!empty($status['missing_fields'])) {
|
||||
$message = sprintf(
|
||||
/* translators: %s: missing config field keys */
|
||||
__('当前服务商凭据不完整(缺少:%s)。', 'aksrt'),
|
||||
__('当前服务商凭据不完整(缺少:%s)。', 'jio'),
|
||||
implode(', ', (array) $status['missing_fields'])
|
||||
);
|
||||
} else {
|
||||
$message = __('服务商未返回有效归属地,请换公网 IP 或检查 Key 权限。', 'aksrt');
|
||||
$message = __('服务商未返回有效归属地,请换公网 IP 或检查 Key 权限。', 'jio');
|
||||
}
|
||||
break;
|
||||
case 'disabled':
|
||||
$message = __('IP 归属地功能未启用。', 'aksrt');
|
||||
$message = __('IP 归属地功能未启用。', 'jio');
|
||||
break;
|
||||
default:
|
||||
$message = $detail !== '' ? $detail : __('未查询到归属地信息。', 'aksrt');
|
||||
$message = $detail !== '' ? $detail : __('未查询到归属地信息。', 'jio');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,14 +247,14 @@ class ExtensionController extends ControllerBase {
|
||||
$text = trim((string) ($params['text'] ?? ''));
|
||||
|
||||
if ($text === '') {
|
||||
return $this->error(__('请输入要测试的文本内容', 'aksrt'), 422);
|
||||
return $this->error(__('请输入要测试的文本内容', 'jio'), 422);
|
||||
}
|
||||
|
||||
if (!class_exists('\AKSRT\Extensions\ContentAudit')) {
|
||||
return $this->error(__('内容审核服务不可用', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Extensions\ContentAudit')) {
|
||||
return $this->error(__('内容审核服务不可用', 'jio'), 500);
|
||||
}
|
||||
|
||||
$audit = \AKSRT\Extensions\ContentAudit::get_instance();
|
||||
$audit = \Jio\Extensions\ContentAudit::get_instance();
|
||||
$status = $audit->get_status();
|
||||
$result = $audit->audit_text($text, true);
|
||||
|
||||
@@ -273,43 +273,43 @@ class ExtensionController extends ControllerBase {
|
||||
if (!empty($status['missing_fields'])) {
|
||||
$message = sprintf(
|
||||
/* translators: %s: missing config field keys */
|
||||
__('当前审核服务商凭据不完整(缺少:%s),请先在「扩展 → 内容审核」补全配置。', 'aksrt'),
|
||||
__('当前审核服务商凭据不完整(缺少:%s),请先在「扩展 → 内容审核」补全配置。', 'jio'),
|
||||
implode(', ', (array) $status['missing_fields'])
|
||||
);
|
||||
} else {
|
||||
$message = __('审核服务商凭据缺失,请先补全 API Key / Secret。', 'aksrt');
|
||||
$message = __('审核服务商凭据缺失,请先补全 API Key / Secret。', 'jio');
|
||||
}
|
||||
$ok = false;
|
||||
break;
|
||||
case 'api_error':
|
||||
$message = __('调用审核接口失败,请检查凭据、网络出口或服务商状态。', 'aksrt');
|
||||
$message = __('调用审核接口失败,请检查凭据、网络出口或服务商状态。', 'jio');
|
||||
$ok = false;
|
||||
break;
|
||||
case 'disabled':
|
||||
$message = __('内容审核功能未启用。', 'aksrt');
|
||||
$message = __('内容审核功能未启用。', 'jio');
|
||||
$ok = false;
|
||||
break;
|
||||
case 'non_compliant':
|
||||
$message = __('审核结论:不合规(已拦截)。', 'aksrt');
|
||||
$message = __('审核结论:不合规(已拦截)。', 'jio');
|
||||
break;
|
||||
case 'suspected':
|
||||
$message = __('审核结论:疑似违规。', 'aksrt');
|
||||
$message = __('审核结论:疑似违规。', 'jio');
|
||||
break;
|
||||
case 'review_failed':
|
||||
$message = __('审核结论:服务商审核失败。', 'aksrt');
|
||||
$message = __('审核结论:服务商审核失败。', 'jio');
|
||||
break;
|
||||
default:
|
||||
if (!$ok) {
|
||||
$message = $reason !== ''
|
||||
? sprintf(/* translators: %s: provider reason */ __('审核未通过:%s', 'aksrt'), $reason)
|
||||
: __('审核未通过。', 'aksrt');
|
||||
? sprintf(/* translators: %s: provider reason */ __('审核未通过:%s', 'jio'), $reason)
|
||||
: __('审核未通过。', 'jio');
|
||||
}
|
||||
}
|
||||
} elseif ($ok) {
|
||||
if ($reason === 'passthrough' || $reason === '') {
|
||||
$message = __('审核通过。', 'aksrt');
|
||||
$message = __('审核通过。', 'jio');
|
||||
} else {
|
||||
$message = sprintf(/* translators: %s: provider reason */ __('审核通过(%s)。', 'aksrt'), $reason);
|
||||
$message = sprintf(/* translators: %s: provider reason */ __('审核通过(%s)。', 'jio'), $reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,22 +331,22 @@ class ExtensionController extends ControllerBase {
|
||||
$services = [];
|
||||
|
||||
// SMS
|
||||
if (class_exists('\AKSRT\Sms\SMS')) {
|
||||
$services['sms'] = \AKSRT\Sms\SMS::get_instance()->get_provider_status();
|
||||
if (class_exists('\Jio\Sms\SMS')) {
|
||||
$services['sms'] = \Jio\Sms\SMS::get_instance()->get_provider_status();
|
||||
} else {
|
||||
$services['sms'] = ['enabled' => false, 'provider' => 'none', 'configured' => false, 'available' => false, 'verified' => false, 'transport' => '', 'missing_fields' => []];
|
||||
}
|
||||
|
||||
// WeChat
|
||||
if (class_exists('\AKSRT\Payment\WeChatNotify')) {
|
||||
$services['wechat'] = \AKSRT\Payment\WeChatNotify::get_instance()->get_status();
|
||||
if (class_exists('\Jio\Payment\WeChatNotify')) {
|
||||
$services['wechat'] = \Jio\Payment\WeChatNotify::get_instance()->get_status();
|
||||
} else {
|
||||
$services['wechat'] = ['enabled' => false, 'provider' => '', 'configured' => false, 'available' => false, 'verified' => false, 'transport' => '', 'missing_fields' => []];
|
||||
}
|
||||
|
||||
// Captcha — 唯一真相源 aksrt_captcha_config
|
||||
$captcha = function_exists('aksrt_get_captcha_status_summary')
|
||||
? aksrt_get_captcha_status_summary()
|
||||
// Captcha — 唯一真相源 jio_captcha_config
|
||||
$captcha = function_exists('jio_get_captcha_status_summary')
|
||||
? jio_get_captcha_status_summary()
|
||||
: ['enabled' => false, 'provider' => '', 'configured' => false, 'scenes' => []];
|
||||
$services['captcha'] = [
|
||||
'enabled' => !empty($captcha['enabled']) && !empty($captcha['configured']),
|
||||
@@ -360,22 +360,22 @@ class ExtensionController extends ControllerBase {
|
||||
];
|
||||
|
||||
// IP Geo
|
||||
if (class_exists('\AKSRT\Extensions\IPGeo')) {
|
||||
$services['ip_geo'] = \AKSRT\Extensions\IPGeo::get_instance()->get_status();
|
||||
if (class_exists('\Jio\Extensions\IPGeo')) {
|
||||
$services['ip_geo'] = \Jio\Extensions\IPGeo::get_instance()->get_status();
|
||||
} else {
|
||||
$services['ip_geo'] = ['enabled' => false, 'provider' => '', 'configured' => false, 'available' => false, 'verified' => false, 'transport' => '', 'missing_fields' => []];
|
||||
}
|
||||
|
||||
// Content Audit
|
||||
if (class_exists('\AKSRT\Extensions\ContentAudit')) {
|
||||
$services['audit'] = \AKSRT\Extensions\ContentAudit::get_instance()->get_status();
|
||||
if (class_exists('\Jio\Extensions\ContentAudit')) {
|
||||
$services['audit'] = \Jio\Extensions\ContentAudit::get_instance()->get_status();
|
||||
} else {
|
||||
$services['audit'] = ['enabled' => false, 'provider' => '', 'configured' => false, 'available' => false, 'verified' => false, 'transport' => '', 'missing_fields' => []];
|
||||
}
|
||||
|
||||
// Search Push
|
||||
if (class_exists('\AKSRT\Extensions\SearchPush')) {
|
||||
$services['push'] = \AKSRT\Extensions\SearchPush::get_instance()->get_status();
|
||||
if (class_exists('\Jio\Extensions\SearchPush')) {
|
||||
$services['push'] = \Jio\Extensions\SearchPush::get_instance()->get_status();
|
||||
} else {
|
||||
$services['push'] = ['enabled' => false, 'provider' => '', 'configured' => false, 'available' => false, 'verified' => false, 'transport' => '', 'missing_fields' => []];
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -114,7 +114,7 @@ class GlobalController extends ControllerBase {
|
||||
|
||||
public function rest_handle_cms_features(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if ($request->get_method() === 'GET') {
|
||||
return $this->success(['cms_features' => \AKSRT\Settings\Settings::get_cms_features()]);
|
||||
return $this->success(['cms_features' => \Jio\Settings\Settings::get_cms_features()]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
@@ -123,27 +123,27 @@ class GlobalController extends ControllerBase {
|
||||
'search_include_resource' => 'bool',
|
||||
];
|
||||
$sanitized = $this->sanitize_fields($params, $allowed);
|
||||
$settings = \AKSRT\Settings\Settings::get_all();
|
||||
$settings = \Jio\Settings\Settings::get_all();
|
||||
$settings['cms_features'] = array_merge(
|
||||
is_array($settings['cms_features'] ?? null) ? $settings['cms_features'] : [],
|
||||
$sanitized
|
||||
);
|
||||
update_option('aksrt_settings', $settings);
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
update_option('jio_settings', $settings);
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
|
||||
return $this->success(['message' => 'CMS 功能已保存', 'cms_features' => \AKSRT\Settings\Settings::get_cms_features()]);
|
||||
return $this->success(['message' => 'CMS 功能已保存', 'cms_features' => \Jio\Settings\Settings::get_cms_features()]);
|
||||
}
|
||||
|
||||
public function rest_test_mail(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$params = $this->get_json_params($request);
|
||||
$to = sanitize_email($params['to'] ?? '');
|
||||
if (!is_email($to)) {
|
||||
return $this->error(__('请输入有效的邮箱地址', 'aksrt'));
|
||||
return $this->error(__('请输入有效的邮箱地址', 'jio'));
|
||||
}
|
||||
|
||||
$result = \AKSRT\Settings\Mail::send_test_email($to);
|
||||
$result = \Jio\Settings\Mail::send_test_email($to);
|
||||
if (is_wp_error($result)) {
|
||||
return $this->error(__('邮件发送失败,详细原因已写入服务器错误日志', 'aksrt'));
|
||||
return $this->error(__('邮件发送失败,详细原因已写入服务器错误日志', 'jio'));
|
||||
}
|
||||
return $this->success(['message' => '测试邮件已发送']);
|
||||
}
|
||||
@@ -169,12 +169,12 @@ class GlobalController extends ControllerBase {
|
||||
|
||||
public function rest_handle_friend_links(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if ($request->get_method() === 'GET') {
|
||||
$links = aksrt_get_friend_links();
|
||||
$links = jio_get_friend_links();
|
||||
return rest_ensure_response(['code' => 'success', 'data' => $links]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$links = get_option('aksrt_friend_links', []);
|
||||
$links = get_option('jio_friend_links', []);
|
||||
$new_link = [
|
||||
'id' => uniqid('fl_'),
|
||||
'name' => sanitize_text_field($params['name'] ?? ''),
|
||||
@@ -188,29 +188,29 @@ class GlobalController extends ControllerBase {
|
||||
usort($links, function ($a, $b) {
|
||||
return ($a['sort_order'] ?? 0) <=> ($b['sort_order'] ?? 0);
|
||||
});
|
||||
update_option('aksrt_friend_links', $links);
|
||||
do_action('aksrt_friend_link_submitted', $new_link);
|
||||
update_option('jio_friend_links', $links);
|
||||
do_action('jio_friend_link_submitted', $new_link);
|
||||
|
||||
return rest_ensure_response(['code' => 'success', 'data' => $new_link, 'message' => __('Friend link added.', 'aksrt')]);
|
||||
return rest_ensure_response(['code' => 'success', 'data' => $new_link, 'message' => __('Friend link added.', 'jio')]);
|
||||
}
|
||||
|
||||
public function rest_delete_friend_link(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$id = $request->get_param('id');
|
||||
$links = get_option('aksrt_friend_links', []);
|
||||
$links = get_option('jio_friend_links', []);
|
||||
$links = array_filter($links, function ($link) use ($id) {
|
||||
return ($link['id'] ?? '') !== $id;
|
||||
});
|
||||
$links = array_values($links);
|
||||
update_option('aksrt_friend_links', $links);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Friend link deleted.', 'aksrt')]);
|
||||
update_option('jio_friend_links', $links);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Friend link deleted.', 'jio')]);
|
||||
}
|
||||
|
||||
public function rest_reorder_friend_links(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$ordered_ids = $request->get_param('ids');
|
||||
if (!is_array($ordered_ids)) {
|
||||
return new \WP_REST_Response(['code' => 'error', 'message' => __('参数错误', 'aksrt')], 400);
|
||||
return new \WP_REST_Response(['code' => 'error', 'message' => __('参数错误', 'jio')], 400);
|
||||
}
|
||||
$links = get_option('aksrt_friend_links', []);
|
||||
$links = get_option('jio_friend_links', []);
|
||||
$order_map = array_flip($ordered_ids);
|
||||
usort($links, function ($a, $b) use ($order_map) {
|
||||
$idx_a = $order_map[($a['id'] ?? '')] ?? 999;
|
||||
@@ -220,14 +220,14 @@ class GlobalController extends ControllerBase {
|
||||
foreach ($links as $index => &$link) {
|
||||
$link['sort_order'] = $index;
|
||||
}
|
||||
update_option('aksrt_friend_links', $links);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Order saved.', 'aksrt')]);
|
||||
update_option('jio_friend_links', $links);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Order saved.', 'jio')]);
|
||||
}
|
||||
|
||||
public function rest_handle_footer_tabbar(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if ($request->get_method() === 'GET') {
|
||||
$settings = class_exists('\AKSRT\Settings\Settings')
|
||||
? \AKSRT\Settings\Settings::get_footer_tabbar_settings()
|
||||
$settings = class_exists('\Jio\Settings\Settings')
|
||||
? \Jio\Settings\Settings::get_footer_tabbar_settings()
|
||||
: ['enabled' => true, 'scroll_hide' => false, 'scroll_down_hide' => true, 'items' => []];
|
||||
return rest_ensure_response(['code' => 'success', 'data' => $settings]);
|
||||
}
|
||||
@@ -239,7 +239,7 @@ class GlobalController extends ControllerBase {
|
||||
if (count($items) > $max_items) {
|
||||
return new \WP_REST_Response([
|
||||
'code' => 'error',
|
||||
'message' => sprintf(__('Footer tabbar supports at most %d items.', 'aksrt'), $max_items),
|
||||
'message' => sprintf(__('Footer tabbar supports at most %d items.', 'jio'), $max_items),
|
||||
], 400);
|
||||
}
|
||||
|
||||
@@ -251,8 +251,8 @@ class GlobalController extends ControllerBase {
|
||||
'icon_size' => max(16, min(32, (int) ($params['icon_size'] ?? 22))),
|
||||
'text_size' => max(8, min(14, (int) ($params['text_size'] ?? 10))),
|
||||
'show_text' => !empty($params['show_text']),
|
||||
'active_color' => \AKSRT\Core\Color::sanitize($params['active_color'] ?? '') ?: '#f04494',
|
||||
'bg_color' => \AKSRT\Core\Color::sanitize($params['bg_color'] ?? '') ?: '',
|
||||
'active_color' => \Jio\Core\Color::sanitize($params['active_color'] ?? '') ?: '#f04494',
|
||||
'bg_color' => \Jio\Core\Color::sanitize($params['bg_color'] ?? '') ?: '',
|
||||
'border' => !empty($params['border']),
|
||||
'shadow' => !empty($params['shadow']),
|
||||
'animation' => !empty($params['animation']),
|
||||
@@ -260,13 +260,13 @@ class GlobalController extends ControllerBase {
|
||||
'badge_dot' => !empty($params['badge_dot']),
|
||||
'items' => $items,
|
||||
];
|
||||
update_option('aksrt_footer_tabbar', $tabbar);
|
||||
update_option('jio_footer_tabbar', $tabbar);
|
||||
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
if (class_exists('\Jio\Settings\Settings')) {
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
}
|
||||
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Footer tabbar settings saved.', 'aksrt')]);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Footer tabbar settings saved.', 'jio')]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,14 +14,14 @@ trait GlobalControllerPart1 {
|
||||
$ordered_ids = $params['ids'] ?? [];
|
||||
|
||||
if (!is_array($ordered_ids)) {
|
||||
return new \WP_REST_Response(['code' => 'error', 'message' => __('参数错误', 'aksrt')], 400);
|
||||
return new \WP_REST_Response(['code' => 'error', 'message' => __('参数错误', 'jio')], 400);
|
||||
}
|
||||
|
||||
if (count($ordered_ids) > 5) {
|
||||
return new \WP_REST_Response(['code' => 'error', 'message' => __('Footer tabbar supports at most 5 items.', 'aksrt')], 400);
|
||||
return new \WP_REST_Response(['code' => 'error', 'message' => __('Footer tabbar supports at most 5 items.', 'jio')], 400);
|
||||
}
|
||||
|
||||
$tabbar = get_option('aksrt_footer_tabbar', []);
|
||||
$tabbar = get_option('jio_footer_tabbar', []);
|
||||
$items = $tabbar['items'] ?? [];
|
||||
|
||||
$order_map = array_flip($ordered_ids);
|
||||
@@ -36,9 +36,9 @@ trait GlobalControllerPart1 {
|
||||
}
|
||||
|
||||
$tabbar['items'] = $items;
|
||||
update_option('aksrt_footer_tabbar', $tabbar);
|
||||
update_option('jio_footer_tabbar', $tabbar);
|
||||
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Order saved.', 'aksrt')]);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Order saved.', 'jio')]);
|
||||
}
|
||||
|
||||
private function sanitize_tabbar_item(array $item): array {
|
||||
@@ -56,7 +56,7 @@ trait GlobalControllerPart1 {
|
||||
if (!empty($item['add_items']) && is_array($item['add_items'])) {
|
||||
foreach ($item['add_items'] as $sub_item) {
|
||||
$label = sanitize_text_field($sub_item['label'] ?? '');
|
||||
$icon = aksrt_sanitize_icon_value((string) ($sub_item['icon'] ?? 'fa fas fa-pen'));
|
||||
$icon = jio_sanitize_icon_value((string) ($sub_item['icon'] ?? 'fa fas fa-pen'));
|
||||
$url = esc_url_raw($sub_item['url'] ?? '');
|
||||
if ($label === '') {
|
||||
continue;
|
||||
@@ -73,7 +73,7 @@ trait GlobalControllerPart1 {
|
||||
return [
|
||||
'id' => sanitize_text_field($item['id'] ?? uniqid('tab_')),
|
||||
'type' => $type,
|
||||
'icon' => aksrt_sanitize_icon_value((string) ($item['icon'] ?? '')),
|
||||
'icon' => jio_sanitize_icon_value((string) ($item['icon'] ?? '')),
|
||||
'text' => sanitize_text_field($item['text'] ?? ''),
|
||||
'link' => esc_url_raw($item['link'] ?? ''),
|
||||
'badge' => sanitize_text_field($item['badge'] ?? ''),
|
||||
@@ -93,16 +93,16 @@ trait GlobalControllerPart1 {
|
||||
|
||||
public function rest_handle_floating_buttons(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if ($request->get_method() === 'GET') {
|
||||
return rest_ensure_response(['code' => 'success', 'data' => \AKSRT\Settings\FloatButtons::get()]);
|
||||
return rest_ensure_response(['code' => 'success', 'data' => \Jio\Settings\FloatButtons::get()]);
|
||||
}
|
||||
|
||||
$settings = \AKSRT\Settings\FloatButtons::sanitize($this->get_json_params($request));
|
||||
update_option(\AKSRT\Settings\FloatButtons::OPTION, $settings);
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
$settings = \Jio\Settings\FloatButtons::sanitize($this->get_json_params($request));
|
||||
update_option(\Jio\Settings\FloatButtons::OPTION, $settings);
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
|
||||
return rest_ensure_response([
|
||||
'code' => 'success',
|
||||
'message' => __('Floating button settings saved.', 'aksrt'),
|
||||
'message' => __('Floating button settings saved.', 'jio'),
|
||||
'data' => $settings,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -11,7 +11,7 @@ trait GlobalControllerPart2 {
|
||||
|
||||
public function rest_handle_category_recommend(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if ($request->get_method() === 'GET') {
|
||||
$settings = \AKSRT\Settings\Settings::get_category_recommend_settings();
|
||||
$settings = \Jio\Settings\Settings::get_category_recommend_settings();
|
||||
return rest_ensure_response(['code' => 'success', 'data' => $settings]);
|
||||
}
|
||||
|
||||
@@ -30,10 +30,10 @@ trait GlobalControllerPart2 {
|
||||
'category_recommend_show_dates' => !empty($params['category_recommend_show_dates']),
|
||||
'category_recommend_show_more_link' => !empty($params['category_recommend_show_more_link']),
|
||||
];
|
||||
update_option('aksrt_category_recommend', $settings);
|
||||
\AKSRT\Settings\Settings::flush_category_recommend_cache();
|
||||
update_option('jio_category_recommend', $settings);
|
||||
\Jio\Settings\Settings::flush_category_recommend_cache();
|
||||
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Category recommendation settings saved.', 'aksrt')]);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('Category recommendation settings saved.', 'jio')]);
|
||||
}
|
||||
|
||||
public function rest_get_categories(\WP_REST_Request $request): \WP_REST_Response {
|
||||
@@ -62,9 +62,9 @@ trait GlobalControllerPart2 {
|
||||
'taxonomy' => $cat->taxonomy,
|
||||
'count' => (int) $cat->count,
|
||||
'description' => $cat->description,
|
||||
'cover_id' => $is_category ? (int) get_term_meta($cat->term_id, 'aksrt_category_cover_id', true) : 0,
|
||||
'cover_url' => $is_category ? \AKSRT\Settings\Settings::get_category_cover_url((int) $cat->term_id) : '',
|
||||
'recommend_desc' => $is_category ? (string) get_term_meta($cat->term_id, 'aksrt_category_recommend_desc', true) : '',
|
||||
'cover_id' => $is_category ? (int) get_term_meta($cat->term_id, 'jio_category_cover_id', true) : 0,
|
||||
'cover_url' => $is_category ? \Jio\Settings\Settings::get_category_cover_url((int) $cat->term_id) : '',
|
||||
'recommend_desc' => $is_category ? (string) get_term_meta($cat->term_id, 'jio_category_recommend_desc', true) : '',
|
||||
];
|
||||
}, $categories);
|
||||
|
||||
@@ -102,7 +102,7 @@ trait GlobalControllerPart2 {
|
||||
$id = (int) $request->get_param('id');
|
||||
$term = get_term($id, 'category');
|
||||
if (!$term || is_wp_error($term)) {
|
||||
return new \WP_REST_Response(['code' => 'error', 'message' => __('Category does not exist.', 'aksrt')], 404);
|
||||
return new \WP_REST_Response(['code' => 'error', 'message' => __('Category does not exist.', 'jio')], 404);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
@@ -110,19 +110,19 @@ trait GlobalControllerPart2 {
|
||||
$recommend_desc = sanitize_textarea_field($params['recommend_desc'] ?? '');
|
||||
|
||||
if ($cover_id > 0) {
|
||||
update_term_meta($id, 'aksrt_category_cover_id', $cover_id);
|
||||
update_term_meta($id, 'jio_category_cover_id', $cover_id);
|
||||
} else {
|
||||
delete_term_meta($id, 'aksrt_category_cover_id');
|
||||
delete_term_meta($id, 'jio_category_cover_id');
|
||||
}
|
||||
|
||||
if ($recommend_desc !== '') {
|
||||
update_term_meta($id, 'aksrt_category_recommend_desc', $recommend_desc);
|
||||
update_term_meta($id, 'jio_category_recommend_desc', $recommend_desc);
|
||||
} else {
|
||||
delete_term_meta($id, 'aksrt_category_recommend_desc');
|
||||
delete_term_meta($id, 'jio_category_recommend_desc');
|
||||
}
|
||||
|
||||
\AKSRT\Settings\Settings::flush_category_recommend_cache();
|
||||
\Jio\Settings\Settings::flush_category_recommend_cache();
|
||||
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('分类元数据已保存', 'aksrt')]);
|
||||
return rest_ensure_response(['code' => 'success', 'message' => __('分类元数据已保存', 'jio')]);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -13,8 +13,8 @@ class ImportExportController extends ControllerBase {
|
||||
|
||||
|
||||
private static $instance = null;
|
||||
private const SCHEDULED_BACKUP_HOOK = 'aksrt_settings_scheduled_backup';
|
||||
private const MONTHLY_SCHEDULE = 'aksrt_monthly';
|
||||
private const SCHEDULED_BACKUP_HOOK = 'jio_settings_scheduled_backup';
|
||||
private const MONTHLY_SCHEDULE = 'jio_monthly';
|
||||
|
||||
private function __construct() {
|
||||
add_filter('cron_schedules', [$this, 'register_backup_schedules']);
|
||||
@@ -138,37 +138,37 @@ class ImportExportController extends ControllerBase {
|
||||
foreach ($token as $key => $value) {
|
||||
$token[$key] = is_scalar($value) ? sanitize_text_field((string) $value) : '';
|
||||
}
|
||||
$config = function_exists('aksrt_get_captcha_config') ? aksrt_get_captcha_config() : [];
|
||||
$config = function_exists('jio_get_captcha_config') ? jio_get_captcha_config() : [];
|
||||
$stored_provider = (string) ($config['active_provider'] ?? '');
|
||||
|
||||
if ($provider === '' || $provider !== $stored_provider || !in_array($provider, aksrt_captcha_providers(), true)) {
|
||||
return $this->error(__('请先保存并启用要测试的人机验证服务商', 'aksrt'), 422);
|
||||
if ($provider === '' || $provider !== $stored_provider || !in_array($provider, jio_captcha_providers(), true)) {
|
||||
return $this->error(__('请先保存并启用要测试的人机验证服务商', 'jio'), 422);
|
||||
}
|
||||
|
||||
$provider_config = isset($config[$provider]) && is_array($config[$provider]) ? $config[$provider] : [];
|
||||
if (!aksrt_captcha_provider_is_configured($provider, $provider_config)) {
|
||||
return $this->error(__('当前人机验证服务商配置不完整', 'aksrt'), 422);
|
||||
if (!jio_captcha_provider_is_configured($provider, $provider_config)) {
|
||||
return $this->error(__('当前人机验证服务商配置不完整', 'jio'), 422);
|
||||
}
|
||||
|
||||
$scene = 'admin-captcha-test';
|
||||
$action = 'aksrt-admin-test';
|
||||
$result = aksrt_verify_captcha_provider($provider, $provider_config, $token, aksrt_get_client_ip(), $scene, $action);
|
||||
$action = 'jio-admin-test';
|
||||
$result = jio_verify_captcha_provider($provider, $provider_config, $token, jio_get_client_ip(), $scene, $action);
|
||||
if (is_wp_error($result)) {
|
||||
return $this->error($result->get_error_message(), 422);
|
||||
}
|
||||
|
||||
return $this->success(['message' => __('服务端同源验签通过', 'aksrt')]);
|
||||
return $this->success(['message' => __('服务端同源验签通过', 'jio')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all AKSRT settings as a JSON-serializable array.
|
||||
* Export all Jio settings as a JSON-serializable array.
|
||||
*/
|
||||
public function rest_export_settings(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$schema_provider = SchemaProvider::get_instance();
|
||||
$domains = $schema_provider->get_domain_keys();
|
||||
|
||||
$export = [
|
||||
'version' => AKSRT_VERSION ?? '3.4.0',
|
||||
'version' => JIO_VERSION ?? '3.4.0',
|
||||
'exported' => gmdate('Y-m-d H:i:s'),
|
||||
'domains' => [],
|
||||
];
|
||||
@@ -181,7 +181,7 @@ class ImportExportController extends ControllerBase {
|
||||
}
|
||||
|
||||
// Also export standalone options from the registry
|
||||
$standalone_keys = \AKSRT\Settings\Settings::standalone_export_keys();
|
||||
$standalone_keys = \Jio\Settings\Settings::standalone_export_keys();
|
||||
|
||||
$export['standalone'] = [];
|
||||
foreach ($standalone_keys as $key) {
|
||||
@@ -191,7 +191,7 @@ class ImportExportController extends ControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
$export = \AKSRT\Settings\Settings::redact_sensitive_settings($export);
|
||||
$export = \Jio\Settings\Settings::redact_sensitive_settings($export);
|
||||
|
||||
return $this->success(['data' => $export]);
|
||||
}
|
||||
@@ -215,7 +215,7 @@ class ImportExportController extends ControllerBase {
|
||||
|
||||
// Import domain settings
|
||||
if (!$dry_run) {
|
||||
$backup = $this->create_backup_record('manual', __('导入前自动快照', 'aksrt'));
|
||||
$backup = $this->create_backup_record('manual', __('导入前自动快照', 'jio'));
|
||||
$this->persist_setting_backup($backup);
|
||||
}
|
||||
|
||||
@@ -250,12 +250,12 @@ class ImportExportController extends ControllerBase {
|
||||
|
||||
// Import standalone options. Secret-bearing options are never accepted from exports.
|
||||
$standalone = !empty($params['standalone']) && is_array($params['standalone'])
|
||||
? \AKSRT\Settings\Settings::filter_sensitive_import($params['standalone'])
|
||||
? \Jio\Settings\Settings::filter_sensitive_import($params['standalone'])
|
||||
: [];
|
||||
if ($standalone !== []) {
|
||||
foreach ($standalone as $key => $value) {
|
||||
if (!preg_match('/^aksrt_/', $key)) {
|
||||
$results[$key] = ['status' => 'skipped', 'message' => 'Not an AKSRT option'];
|
||||
if (!preg_match('/^jio_/', $key)) {
|
||||
$results[$key] = ['status' => 'skipped', 'message' => 'Not an Jio option'];
|
||||
continue;
|
||||
}
|
||||
if (!$dry_run) {
|
||||
@@ -268,7 +268,7 @@ class ImportExportController extends ControllerBase {
|
||||
}
|
||||
|
||||
if (!$dry_run) {
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
@@ -288,7 +288,7 @@ class ImportExportController extends ControllerBase {
|
||||
return $this->error('未知的设置域: ' . esc_html($domain), 404);
|
||||
}
|
||||
|
||||
$backup = $this->create_backup_record('reset_domain', sprintf(__('重置 %s 前快照', 'aksrt'), $domain));
|
||||
$backup = $this->create_backup_record('reset_domain', sprintf(__('重置 %s 前快照', 'jio'), $domain));
|
||||
$this->persist_setting_backup($backup);
|
||||
|
||||
$schema_provider->reset_to_defaults($domain);
|
||||
@@ -303,17 +303,17 @@ class ImportExportController extends ControllerBase {
|
||||
|
||||
public function rest_reset_all(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$schema_provider = SchemaProvider::get_instance();
|
||||
$backup = $this->create_backup_record('reset_all', __('重置全部前快照', 'aksrt'));
|
||||
$backup = $this->create_backup_record('reset_all', __('重置全部前快照', 'jio'));
|
||||
$this->persist_setting_backup($backup);
|
||||
|
||||
foreach ($schema_provider->get_domain_keys() as $domain) {
|
||||
$schema_provider->reset_to_defaults($domain);
|
||||
}
|
||||
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
|
||||
return $this->success([
|
||||
'message' => __('全部 schema 托管设置已重置为默认值', 'aksrt'),
|
||||
'message' => __('全部 schema 托管设置已重置为默认值', 'jio'),
|
||||
'data' => [
|
||||
'backup_id' => $backup['id'],
|
||||
],
|
||||
@@ -353,12 +353,12 @@ class ImportExportController extends ControllerBase {
|
||||
|
||||
// Database tables
|
||||
$expected_tables = [
|
||||
$wpdb->prefix . 'aksrt_asset_account',
|
||||
$wpdb->prefix . 'aksrt_asset_transaction',
|
||||
$wpdb->prefix . 'aksrt_asset_entry',
|
||||
$wpdb->prefix . 'aksrt_signin_log',
|
||||
$wpdb->prefix . 'aksrt_dl_token',
|
||||
$wpdb->prefix . 'aksrt_notification',
|
||||
$wpdb->prefix . 'jio_asset_account',
|
||||
$wpdb->prefix . 'jio_asset_transaction',
|
||||
$wpdb->prefix . 'jio_asset_entry',
|
||||
$wpdb->prefix . 'jio_signin_log',
|
||||
$wpdb->prefix . 'jio_dl_token',
|
||||
$wpdb->prefix . 'jio_notification',
|
||||
];
|
||||
$missing_tables = [];
|
||||
foreach ($expected_tables as $table) {
|
||||
@@ -374,10 +374,10 @@ class ImportExportController extends ControllerBase {
|
||||
|
||||
// Option count
|
||||
$option_count = (int) $wpdb->get_var(
|
||||
"SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE 'aksrt_%'"
|
||||
"SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE 'jio_%'"
|
||||
);
|
||||
$checks['option_count'] = [
|
||||
'label' => 'AKSRT 选项数',
|
||||
'label' => 'Jio 选项数',
|
||||
'value' => $option_count,
|
||||
'status' => $option_count < 100 ? 'ok' : 'warning',
|
||||
];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -33,7 +33,7 @@ trait ImportExportControllerPart1 {
|
||||
}
|
||||
|
||||
public function list_setting_backups(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$backups = get_option('aksrt_settings_backups', []);
|
||||
$backups = get_option('jio_settings_backups', []);
|
||||
if (!is_array($backups)) {
|
||||
$backups = [];
|
||||
}
|
||||
@@ -64,14 +64,14 @@ trait ImportExportControllerPart1 {
|
||||
$this->persist_setting_backup($backup);
|
||||
|
||||
return $this->success([
|
||||
'message' => __('设置快照已创建', 'aksrt'),
|
||||
'message' => __('设置快照已创建', 'jio'),
|
||||
'data' => $backup,
|
||||
]);
|
||||
}
|
||||
|
||||
public function restore_setting_backup(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$id = (int) $request->get_param('id');
|
||||
$backups = get_option('aksrt_settings_backups', []);
|
||||
$backups = get_option('jio_settings_backups', []);
|
||||
if (!is_array($backups)) {
|
||||
$backups = [];
|
||||
}
|
||||
@@ -85,12 +85,12 @@ trait ImportExportControllerPart1 {
|
||||
}
|
||||
|
||||
if (!$target || !is_array($target['payload'] ?? null)) {
|
||||
return $this->error(__('备份快照不存在', 'aksrt'), 404);
|
||||
return $this->error(__('备份快照不存在', 'jio'), 404);
|
||||
}
|
||||
|
||||
$payload = $target['payload'];
|
||||
$domains = $payload['domains'] ?? [];
|
||||
$standalone = \AKSRT\Settings\Settings::filter_sensitive_import(
|
||||
$standalone = \Jio\Settings\Settings::filter_sensitive_import(
|
||||
is_array($payload['standalone'] ?? null) ? $payload['standalone'] : []
|
||||
);
|
||||
$provider = SchemaProvider::get_instance();
|
||||
@@ -107,15 +107,15 @@ trait ImportExportControllerPart1 {
|
||||
}
|
||||
|
||||
foreach ($standalone as $key => $value) {
|
||||
if (is_string($key) && in_array($key, \AKSRT\Settings\Settings::standalone_export_keys(), true)) {
|
||||
if (is_string($key) && in_array($key, \Jio\Settings\Settings::standalone_export_keys(), true)) {
|
||||
update_option($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
|
||||
return $this->success([
|
||||
'message' => __('设置已从快照恢复', 'aksrt'),
|
||||
'message' => __('设置已从快照恢复', 'jio'),
|
||||
'data' => [
|
||||
'id' => $id,
|
||||
],
|
||||
@@ -124,7 +124,7 @@ trait ImportExportControllerPart1 {
|
||||
|
||||
public function delete_setting_backup(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$id = (int) $request->get_param('id');
|
||||
$backups = get_option('aksrt_settings_backups', []);
|
||||
$backups = get_option('jio_settings_backups', []);
|
||||
if (!is_array($backups)) {
|
||||
$backups = [];
|
||||
}
|
||||
@@ -140,25 +140,25 @@ trait ImportExportControllerPart1 {
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
return $this->error(__('备份快照不存在', 'aksrt'), 404);
|
||||
return $this->error(__('备份快照不存在', 'jio'), 404);
|
||||
}
|
||||
|
||||
update_option('aksrt_settings_backups', array_values($filtered));
|
||||
update_option('jio_settings_backups', array_values($filtered));
|
||||
|
||||
return $this->success([
|
||||
'message' => __('备份快照已删除', 'aksrt'),
|
||||
'message' => __('备份快照已删除', 'jio'),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
private function persist_setting_backup(array $backup): void {
|
||||
$backups = get_option('aksrt_settings_backups', []);
|
||||
$backups = get_option('jio_settings_backups', []);
|
||||
if (!is_array($backups)) {
|
||||
$backups = [];
|
||||
}
|
||||
|
||||
array_unshift($backups, $backup);
|
||||
update_option('aksrt_settings_backups', $this->trim_setting_backups($backups));
|
||||
update_option('jio_settings_backups', $this->trim_setting_backups($backups));
|
||||
}
|
||||
|
||||
private function create_backup_record(string $type, string $label = ''): array {
|
||||
@@ -169,7 +169,7 @@ trait ImportExportControllerPart1 {
|
||||
if (!isset($schedules[self::MONTHLY_SCHEDULE])) {
|
||||
$schedules[self::MONTHLY_SCHEDULE] = [
|
||||
'interval' => MONTH_IN_SECONDS,
|
||||
'display' => __('每月', 'aksrt'),
|
||||
'display' => __('每月', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -30,7 +30,7 @@ trait ImportExportControllerPart2 {
|
||||
$domains = $schema_provider->get_domain_keys();
|
||||
|
||||
$payload = [
|
||||
'version' => AKSRT_VERSION ?? '3.4.0',
|
||||
'version' => JIO_VERSION ?? '3.4.0',
|
||||
'exported' => gmdate('Y-m-d H:i:s'),
|
||||
'domains' => [],
|
||||
'standalone'=> [],
|
||||
@@ -43,14 +43,14 @@ trait ImportExportControllerPart2 {
|
||||
];
|
||||
}
|
||||
|
||||
foreach (\AKSRT\Settings\Settings::standalone_export_keys() as $key) {
|
||||
foreach (\Jio\Settings\Settings::standalone_export_keys() as $key) {
|
||||
$value = get_option($key);
|
||||
if ($value !== false) {
|
||||
$payload['standalone'][$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$payload = \AKSRT\Settings\Settings::redact_sensitive_settings($payload);
|
||||
$payload = \Jio\Settings\Settings::redact_sensitive_settings($payload);
|
||||
|
||||
return [
|
||||
'id' => time(),
|
||||
@@ -62,7 +62,7 @@ trait ImportExportControllerPart2 {
|
||||
}
|
||||
|
||||
private function trim_setting_backups(array $backups): array {
|
||||
$retain = (int) (\AKSRT\Settings\Settings::get_value('backup_retain', 7));
|
||||
$retain = (int) (\Jio\Settings\Settings::get_value('backup_retain', 7));
|
||||
$retain = max(3, min(20, $retain));
|
||||
|
||||
if (count($backups) <= $retain) {
|
||||
@@ -74,13 +74,13 @@ trait ImportExportControllerPart2 {
|
||||
|
||||
private function default_backup_label(string $type): string {
|
||||
$map = [
|
||||
'manual' => __('手动备份', 'aksrt'),
|
||||
'scheduled' => __('定期备份', 'aksrt'),
|
||||
'reset_all' => __('重置全部前快照', 'aksrt'),
|
||||
'reset_domain' => __('重置分区前快照', 'aksrt'),
|
||||
'update_theme' => __('更新主题前快照', 'aksrt'),
|
||||
'manual' => __('手动备份', 'jio'),
|
||||
'scheduled' => __('定期备份', 'jio'),
|
||||
'reset_all' => __('重置全部前快照', 'jio'),
|
||||
'reset_domain' => __('重置分区前快照', 'jio'),
|
||||
'update_theme' => __('更新主题前快照', 'jio'),
|
||||
];
|
||||
|
||||
return $map[$type] ?? __('设置快照', 'aksrt');
|
||||
return $map[$type] ?? __('设置快照', 'jio');
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -63,11 +63,11 @@ class MessageController extends ControllerBase {
|
||||
$per_page = min(100, (int) $request->get_param('per_page'));
|
||||
$page = max(1, (int) $request->get_param('page'));
|
||||
|
||||
if (!class_exists('\AKSRT\Messages\Message')) {
|
||||
if (!class_exists('\Jio\Messages\Message')) {
|
||||
return $this->success(['data' => [], 'total' => 0]);
|
||||
}
|
||||
|
||||
$msg = \AKSRT\Messages\Message::get_instance();
|
||||
$msg = \Jio\Messages\Message::get_instance();
|
||||
$args = [
|
||||
'limit' => $per_page,
|
||||
'offset' => ($page - 1) * $per_page,
|
||||
@@ -87,29 +87,29 @@ class MessageController extends ControllerBase {
|
||||
|
||||
public function get_unread_count(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = get_current_user_id();
|
||||
if (!class_exists('\AKSRT\Messages\Message')) {
|
||||
if (!class_exists('\Jio\Messages\Message')) {
|
||||
return $this->success(['data' => ['unread' => 0]]);
|
||||
}
|
||||
$count = \AKSRT\Messages\Message::get_instance()->get_unread_count($user_id);
|
||||
$count = \Jio\Messages\Message::get_instance()->get_unread_count($user_id);
|
||||
return $this->success(['data' => ['unread' => $count]]);
|
||||
}
|
||||
|
||||
public function mark_read(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = get_current_user_id();
|
||||
$id = (string) $request->get_param('id');
|
||||
if (!class_exists('\AKSRT\Messages\Message')) {
|
||||
if (!class_exists('\Jio\Messages\Message')) {
|
||||
return $this->error('not_available', '消息系统未启用。', 501);
|
||||
}
|
||||
$result = \AKSRT\Messages\Message::get_instance()->mark_read($user_id, $id);
|
||||
$result = \Jio\Messages\Message::get_instance()->mark_read($user_id, $id);
|
||||
return $this->success(['data' => ['success' => $result]]);
|
||||
}
|
||||
|
||||
public function mark_all_read(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = get_current_user_id();
|
||||
if (!class_exists('\AKSRT\Messages\Message')) {
|
||||
if (!class_exists('\Jio\Messages\Message')) {
|
||||
return $this->error('not_available', '消息系统未启用。', 501);
|
||||
}
|
||||
$result = \AKSRT\Messages\Message::get_instance()->mark_all_read($user_id);
|
||||
$result = \Jio\Messages\Message::get_instance()->mark_all_read($user_id);
|
||||
return $this->success(['data' => ['success' => $result]]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* 后台列表页分页条 —— 1:1 复刻「卡密管理」页的 zibpay_admin_pagenavi 结构。
|
||||
*
|
||||
@@ -20,7 +20,7 @@
|
||||
* 这样的口径,四枚按钮全 disabled 也明确了「没有更多页」。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -78,13 +78,13 @@ class AdminListPagination {
|
||||
?>
|
||||
<div class="tablenav bottom">
|
||||
<div class="tablenav-pages">
|
||||
<span class="displaying-num"><?php printf(esc_html__('每页 %1$s 共%2$s', 'aksrt'), esc_html((string) $per_page), esc_html((string) $total_count)); ?></span>
|
||||
<span class="displaying-num"><?php printf(esc_html__('每页 %1$s 共%2$s', 'jio'), esc_html((string) $per_page), esc_html((string) $total_count)); ?></span>
|
||||
<span class="pagination-links">
|
||||
<a class="first-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往第一页', 'aksrt'); ?>" href="<?php echo esc_url($first_page_url); ?>">«</a>
|
||||
<a class="prev-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往上一页', 'aksrt'); ?>" href="<?php echo esc_url($prev_page_url); ?>">‹</a>
|
||||
<span class="paging-input"><?php printf(esc_html__('第 %1$s 页,共 %2$s 页', 'aksrt'), esc_html((string) $current_page), '<span class="total-pages">' . esc_html((string) $total_pages) . '</span>'); ?></span>
|
||||
<a class="next-page button<?php echo $on_last ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往下一页', 'aksrt'); ?>" href="<?php echo esc_url($next_page_url); ?>">›</a>
|
||||
<a class="last-page button<?php echo $on_last ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往最后一页', 'aksrt'); ?>" href="<?php echo esc_url($last_page_url); ?>">»</a>
|
||||
<a class="first-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往第一页', 'jio'); ?>" href="<?php echo esc_url($first_page_url); ?>">«</a>
|
||||
<a class="prev-page button<?php echo $on_first ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往上一页', 'jio'); ?>" href="<?php echo esc_url($prev_page_url); ?>">‹</a>
|
||||
<span class="paging-input"><?php printf(esc_html__('第 %1$s 页,共 %2$s 页', 'jio'), esc_html((string) $current_page), '<span class="total-pages">' . esc_html((string) $total_pages) . '</span>'); ?></span>
|
||||
<a class="next-page button<?php echo $on_last ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往下一页', 'jio'); ?>" href="<?php echo esc_url($next_page_url); ?>">›</a>
|
||||
<a class="last-page button<?php echo $on_last ? ' disabled' : ''; ?>" title="<?php echo esc_attr__('前往最后一页', 'jio'); ?>" href="<?php echo esc_url($last_page_url); ?>">»</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
/**
|
||||
* 原生后台运营页共享 UI。
|
||||
*
|
||||
* 支付中心(aksrt_pay 菜单)与用户子菜单(users.php)的运营页统一使用这里的
|
||||
* 支付中心(jio_pay 菜单)与用户子菜单(users.php)的运营页统一使用这里的
|
||||
* 页面外壳、页头、统计卡、卡片、徽标与空状态。样式在 assets/css/admin/native-ui.css,
|
||||
* 只在 SLUGS 列出的页面入队,规则全部限定在 .aksrt-native 下。
|
||||
* 只在 SLUGS 列出的页面入队,规则全部限定在 .jio-native 下。
|
||||
*
|
||||
* 输出方法直接 echo,参数在内部转义;$html 形参约定为调用方已转义的片段。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -19,22 +19,22 @@ final class NativeUi {
|
||||
|
||||
/** 使用共享 UI 的 admin page 参数。 */
|
||||
public const SLUGS = [
|
||||
'aksrt_pay',
|
||||
'aksrt_orders',
|
||||
'aksrt_pay_products',
|
||||
'aksrt_coupons',
|
||||
'aksrt_charge_cards',
|
||||
'aksrt_income',
|
||||
'aksrt_withdraw',
|
||||
'aksrt_invite_codes',
|
||||
'aksrt_messages',
|
||||
'aksrt_user_vip',
|
||||
'aksrt_user_auth',
|
||||
'aksrt_user_ban',
|
||||
'aksrt_tickets',
|
||||
'jio_pay',
|
||||
'jio_orders',
|
||||
'jio_pay_products',
|
||||
'jio_coupons',
|
||||
'jio_charge_cards',
|
||||
'jio_income',
|
||||
'jio_withdraw',
|
||||
'jio_invite_codes',
|
||||
'jio_messages',
|
||||
'jio_user_vip',
|
||||
'jio_user_auth',
|
||||
'jio_user_ban',
|
||||
'jio_tickets',
|
||||
];
|
||||
|
||||
public const STYLE_HANDLE = 'aksrt-admin-native-ui';
|
||||
public const STYLE_HANDLE = 'jio-admin-native-ui';
|
||||
|
||||
private const TONES = ['success', 'info', 'warning', 'danger', 'neutral', 'primary'];
|
||||
|
||||
@@ -59,8 +59,8 @@ final class NativeUi {
|
||||
return;
|
||||
}
|
||||
$css = '/assets/css/admin/native-ui.css';
|
||||
if (is_file(AKSRT_DIR . $css)) {
|
||||
wp_enqueue_style(self::STYLE_HANDLE, AKSRT_URI . $css, [], (string) filemtime(AKSRT_DIR . $css));
|
||||
if (is_file(JIO_DIR . $css)) {
|
||||
wp_enqueue_style(self::STYLE_HANDLE, JIO_URI . $css, [], (string) filemtime(JIO_DIR . $css));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ final class NativeUi {
|
||||
|
||||
/** 打开 .wrap 外壳。$modifier 追加到 class(如页面专属根类)。 */
|
||||
public static function page_open(string $modifier = ''): void {
|
||||
$class = trim('wrap aksrt-native ' . $modifier);
|
||||
$class = trim('wrap jio-native ' . $modifier);
|
||||
echo '<div class="' . esc_attr($class) . '">';
|
||||
}
|
||||
|
||||
@@ -86,19 +86,19 @@ final class NativeUi {
|
||||
* @param array<int, array{label:string,url?:string,dialog?:string,primary?:bool,icon?:string}> $actions
|
||||
*/
|
||||
public static function header(string $title, string $description = '', string $icon = '', array $actions = []): void {
|
||||
echo '<div class="aksrt-native-header"><div class="aksrt-native-header__main">';
|
||||
echo '<div class="jio-native-header"><div class="jio-native-header__main">';
|
||||
// h1 让 WP 把 admin_notices 挪到标题之后。
|
||||
echo '<h1 class="aksrt-native-header__title">';
|
||||
echo '<h1 class="jio-native-header__title">';
|
||||
if ($icon !== '') {
|
||||
echo '<span class="aksrt-native-header__icon dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>';
|
||||
echo '<span class="jio-native-header__icon dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>';
|
||||
}
|
||||
echo esc_html($title) . '</h1>';
|
||||
if ($description !== '') {
|
||||
echo '<p class="aksrt-native-header__desc">' . esc_html($description) . '</p>';
|
||||
echo '<p class="jio-native-header__desc">' . esc_html($description) . '</p>';
|
||||
}
|
||||
echo '</div>';
|
||||
if ($actions !== []) {
|
||||
echo '<div class="aksrt-native-header__actions">';
|
||||
echo '<div class="jio-native-header__actions">';
|
||||
foreach ($actions as $action) {
|
||||
echo isset($action['dialog'])
|
||||
? self::button_dialog($action['label'], (string) $action['dialog'], !empty($action['primary']), (string) ($action['icon'] ?? ''))
|
||||
@@ -117,11 +117,11 @@ final class NativeUi {
|
||||
return '<a class="' . esc_attr($class) . '" href="' . esc_url($url) . '">' . $icon_html . esc_html($label) . '</a>';
|
||||
}
|
||||
|
||||
/** 打开页面内 <dialog id="$dialog_id"> 的按钮;点击行为由页面脚本按 data-aksrt-dialog-open 绑定。 */
|
||||
/** 打开页面内 <dialog id="$dialog_id"> 的按钮;点击行为由页面脚本按 data-jio-dialog-open 绑定。 */
|
||||
public static function button_dialog(string $label, string $dialog_id, bool $primary = false, string $icon = ''): string {
|
||||
$class = $primary ? 'button button-primary' : 'button';
|
||||
$icon_html = $icon !== '' ? '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>' : '';
|
||||
return '<button type="button" class="' . esc_attr($class) . '" data-aksrt-dialog-open="' . esc_attr($dialog_id) . '" aria-haspopup="dialog" aria-controls="' . esc_attr($dialog_id) . '">'
|
||||
return '<button type="button" class="' . esc_attr($class) . '" data-jio-dialog-open="' . esc_attr($dialog_id) . '" aria-haspopup="dialog" aria-controls="' . esc_attr($dialog_id) . '">'
|
||||
. $icon_html . esc_html($label) . '</button>';
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ final class NativeUi {
|
||||
if ($items === []) {
|
||||
return;
|
||||
}
|
||||
echo '<div class="aksrt-native-stats">';
|
||||
echo '<div class="jio-native-stats">';
|
||||
foreach ($items as $item) {
|
||||
self::stat($item);
|
||||
}
|
||||
@@ -150,19 +150,19 @@ final class NativeUi {
|
||||
$tag = $url !== '' ? 'a' : 'div';
|
||||
$attr = $url !== '' ? ' href="' . esc_url($url) . '"' : '';
|
||||
|
||||
echo '<' . $tag . ' class="aksrt-native-stat aksrt-native-stat--' . esc_attr($tone) . '"' . $attr . '>';
|
||||
echo '<span class="aksrt-native-stat__label">';
|
||||
echo '<' . $tag . ' class="jio-native-stat jio-native-stat--' . esc_attr($tone) . '"' . $attr . '>';
|
||||
echo '<span class="jio-native-stat__label">';
|
||||
if (!empty($item['icon'])) {
|
||||
echo '<span class="dashicons ' . esc_attr((string) $item['icon']) . '" aria-hidden="true"></span>';
|
||||
}
|
||||
echo esc_html($item['label']) . '</span>';
|
||||
echo '<span class="aksrt-native-stat__value">';
|
||||
echo '<span class="jio-native-stat__value">';
|
||||
if (!empty($item['unit'])) {
|
||||
echo '<span class="aksrt-native-stat__unit">' . esc_html((string) $item['unit']) . '</span>';
|
||||
echo '<span class="jio-native-stat__unit">' . esc_html((string) $item['unit']) . '</span>';
|
||||
}
|
||||
echo esc_html((string) $item['value']) . '</span>';
|
||||
if (!empty($item['hint'])) {
|
||||
echo '<span class="aksrt-native-stat__hint">' . esc_html((string) $item['hint']) . '</span>';
|
||||
echo '<span class="jio-native-stat__hint">' . esc_html((string) $item['hint']) . '</span>';
|
||||
}
|
||||
echo '</' . $tag . '>';
|
||||
}
|
||||
@@ -176,19 +176,19 @@ final class NativeUi {
|
||||
* $flush=true 时正文无内边距(承载整表)。
|
||||
*/
|
||||
public static function card_open(string $title = '', string $actions_html = '', bool $flush = false, string $modifier = ''): void {
|
||||
$class = 'aksrt-native-card' . ($flush ? ' aksrt-native-card--flush' : '') . ($modifier !== '' ? ' ' . $modifier : '');
|
||||
$class = 'jio-native-card' . ($flush ? ' jio-native-card--flush' : '') . ($modifier !== '' ? ' ' . $modifier : '');
|
||||
echo '<section class="' . esc_attr($class) . '">';
|
||||
if ($title !== '' || $actions_html !== '') {
|
||||
echo '<div class="aksrt-native-card__head">';
|
||||
echo '<div class="jio-native-card__head">';
|
||||
if ($title !== '') {
|
||||
echo '<h2 class="aksrt-native-card__title">' . esc_html($title) . '</h2>';
|
||||
echo '<h2 class="jio-native-card__title">' . esc_html($title) . '</h2>';
|
||||
}
|
||||
if ($actions_html !== '') {
|
||||
echo '<div class="aksrt-native-card__actions">' . $actions_html . '</div>';
|
||||
echo '<div class="jio-native-card__actions">' . $actions_html . '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '<div class="aksrt-native-card__body">';
|
||||
echo '<div class="jio-native-card__body">';
|
||||
}
|
||||
|
||||
public static function card_close(): void {
|
||||
@@ -198,7 +198,7 @@ final class NativeUi {
|
||||
/** 表格卡:整表铺满卡片,带横向滚动容器。 */
|
||||
public static function table_card_open(string $title = '', string $actions_html = ''): void {
|
||||
self::card_open($title, $actions_html, true);
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
}
|
||||
|
||||
/** 关闭表格卡;分页条应在 table-wrap 关闭后、卡片关闭前输出,故拆成两步。 */
|
||||
@@ -211,7 +211,7 @@ final class NativeUi {
|
||||
* ----------------------------------------------------------------*/
|
||||
|
||||
public static function badge(string $label, string $tone = 'neutral', bool $dot = true): string {
|
||||
$class = 'aksrt-native-badge aksrt-native-badge--' . self::tone($tone) . ($dot ? ' has-dot' : '');
|
||||
$class = 'jio-native-badge jio-native-badge--' . self::tone($tone) . ($dot ? ' has-dot' : '');
|
||||
return '<span class="' . esc_attr($class) . '">' . esc_html($label) . '</span>';
|
||||
}
|
||||
|
||||
@@ -228,9 +228,9 @@ final class NativeUi {
|
||||
}
|
||||
|
||||
public static function empty_state(string $title, string $hint = '', string $icon = 'dashicons-portfolio'): string {
|
||||
$html = '<div class="aksrt-native-empty">';
|
||||
$html = '<div class="jio-native-empty">';
|
||||
$html .= '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>';
|
||||
$html .= '<span class="aksrt-native-empty__title">' . esc_html($title) . '</span>';
|
||||
$html .= '<span class="jio-native-empty__title">' . esc_html($title) . '</span>';
|
||||
if ($hint !== '') {
|
||||
$html .= '<span>' . esc_html($hint) . '</span>';
|
||||
}
|
||||
@@ -239,7 +239,7 @@ final class NativeUi {
|
||||
|
||||
/** 表格内的空状态整行。 */
|
||||
public static function empty_row(int $colspan, string $title, string $hint = ''): string {
|
||||
return '<tr class="no-items"><td class="aksrt-native-empty-cell" colspan="' . esc_attr((string) max(1, $colspan)) . '">'
|
||||
return '<tr class="no-items"><td class="jio-native-empty-cell" colspan="' . esc_attr((string) max(1, $colspan)) . '">'
|
||||
. self::empty_state($title, $hint)
|
||||
. '</td></tr>';
|
||||
}
|
||||
@@ -248,13 +248,13 @@ final class NativeUi {
|
||||
public static function user_cell(int $user_id, string $fallback = ''): string {
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : false;
|
||||
if (!$user) {
|
||||
return '<span class="aksrt-native-muted">' . esc_html($fallback !== '' ? $fallback : ($user_id > 0 ? '#' . $user_id : '—')) . '</span>';
|
||||
return '<span class="jio-native-muted">' . esc_html($fallback !== '' ? $fallback : ($user_id > 0 ? '#' . $user_id : '—')) . '</span>';
|
||||
}
|
||||
$avatar = function_exists('aksrt_get_avatar_url') ? (string) aksrt_get_avatar_url($user_id, 28) : (string) get_avatar_url($user_id, ['size' => 28]);
|
||||
return '<a class="aksrt-native-user" href="' . esc_url(get_edit_user_link($user_id)) . '">'
|
||||
$avatar = function_exists('jio_get_avatar_url') ? (string) jio_get_avatar_url($user_id, 28) : (string) get_avatar_url($user_id, ['size' => 28]);
|
||||
return '<a class="jio-native-user" href="' . esc_url(get_edit_user_link($user_id)) . '">'
|
||||
. ($avatar !== '' ? '<img src="' . esc_url($avatar) . '" width="28" height="28" alt="">' : '')
|
||||
. '<span><span class="aksrt-native-user__name">' . esc_html($user->display_name) . '</span>'
|
||||
. '<span class="aksrt-native-user__meta">#' . esc_html((string) $user_id) . '</span></span></a>';
|
||||
. '<span><span class="jio-native-user__name">' . esc_html($user->display_name) . '</span>'
|
||||
. '<span class="jio-native-user__meta">#' . esc_html((string) $user_id) . '</span></span></a>';
|
||||
}
|
||||
|
||||
private static function tone(string $tone): string {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* 后台「卡密管理」页面 — 1:1 复刻参考主题 zibll 的 zibpay/page/charge-card.php。
|
||||
*
|
||||
* 结构:列表(过滤/搜索/排序/批量删除/分页) + 添加卡密(自动生成 / 导入) + 导出卡密(text / xls)。
|
||||
* 卡密类型与参考主题一致:余额充值 / 会员兑换 / 积分兑换 / 自定义卡密;
|
||||
* 本主题内部类型映射:balance / vip / credits / custom。
|
||||
* 差异点:AKSRT 无商城发货(shipped)子系统,故不含「已售」标记与链接;删除取代参考主题的批量操作语义一致。
|
||||
* 差异点:Jio 无商城发货(shipped)子系统,故不含「已售」标记与链接;删除取代参考主题的批量操作语义一致。
|
||||
*
|
||||
* 外壳与配色走 NativeUi(.aksrt-native),页面专属交互样式仍由 print_assets() 输出。
|
||||
* 外壳与配色走 NativeUi(.jio-native),页面专属交互样式仍由 print_assets() 输出。
|
||||
* 类体按关注点拆成 flat sibling traits(PSR-4 要求 trait 与宿主类同目录,不能放子目录):
|
||||
* - PayChargeCardsList 列表渲染 + 翻页参数
|
||||
* - PayChargeCardsAssets 页面专属内联样式/脚本
|
||||
* - PayChargeCardsForms 添加/导出表单 + 依赖显隐助手
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -27,9 +27,9 @@ class PayChargeCards {
|
||||
use PayChargeCardsForms;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_charge_cards';
|
||||
public const NONCE = 'aksrt_card_pass_page';
|
||||
public const EXPORT_AJAX_ACTION = 'aksrt_card_pass_export';
|
||||
public const PAGE_SLUG = 'jio_charge_cards';
|
||||
public const NONCE = 'jio_card_pass_page';
|
||||
public const EXPORT_AJAX_ACTION = 'jio_card_pass_export';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -54,17 +54,17 @@ class PayChargeCards {
|
||||
$this->ensure_cap();
|
||||
$this->page_url = admin_url('admin.php?page=' . self::PAGE_SLUG);
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\ChargeCard')) {
|
||||
if (!class_exists('\Jio\Payment\ChargeCard')) {
|
||||
NativeUi::page_open();
|
||||
echo '<h1 class="wp-heading-inline">' . esc_html__('卡密管理', 'aksrt') . '</h1>';
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('卡密模块未初始化。', 'aksrt') . '</p></div>';
|
||||
echo '<h1 class="wp-heading-inline">' . esc_html__('卡密管理', 'jio') . '</h1>';
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('卡密模块未初始化。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
|
||||
$tab = isset($_GET['tab']) ? sanitize_key((string) wp_unslash($_GET['tab'])) : '';
|
||||
$s = isset($_REQUEST['s']) ? sanitize_text_field((string) wp_unslash($_REQUEST['s'])) : '';
|
||||
$page_title = __('卡密管理', 'aksrt');
|
||||
$page_title = __('卡密管理', 'jio');
|
||||
$head_but = '';
|
||||
|
||||
// 页面级动作(参考主题在渲染前处理 add / delete)。
|
||||
@@ -78,20 +78,20 @@ class PayChargeCards {
|
||||
// 标题右侧按钮是页面头部的一部分,只在此处输出一次(列表页同样如此,由 render_list 负责列表本体)。
|
||||
switch ($tab) {
|
||||
case 'add':
|
||||
$page_title = __('添加卡密', 'aksrt');
|
||||
$head_but = '<a href="' . esc_url($this->page_url) . '" class="page-title-action">' . esc_html__('返回列表', 'aksrt') . '</a>';
|
||||
$page_title = __('添加卡密', 'jio');
|
||||
$head_but = '<a href="' . esc_url($this->page_url) . '" class="page-title-action">' . esc_html__('返回列表', 'jio') . '</a>';
|
||||
break;
|
||||
|
||||
case 'export':
|
||||
$page_title = __('导出卡密', 'aksrt');
|
||||
$head_but = '<a href="' . esc_url($this->page_url) . '" class="page-title-action">' . esc_html__('返回列表', 'aksrt') . '</a>';
|
||||
$page_title = __('导出卡密', 'jio');
|
||||
$head_but = '<a href="' . esc_url($this->page_url) . '" class="page-title-action">' . esc_html__('返回列表', 'jio') . '</a>';
|
||||
break;
|
||||
|
||||
default:
|
||||
$head_but = '<a href="' . esc_url(add_query_arg('tab', 'add', $this->page_url)) . '" class="page-title-action">' . esc_html__('添加卡密', 'aksrt') . '</a>';
|
||||
$head_but .= '<a href="' . esc_url(add_query_arg('tab', 'export', $this->page_url)) . '" class="page-title-action">' . esc_html__('导出卡密', 'aksrt') . '</a>';
|
||||
$head_but = '<a href="' . esc_url(add_query_arg('tab', 'add', $this->page_url)) . '" class="page-title-action">' . esc_html__('添加卡密', 'jio') . '</a>';
|
||||
$head_but .= '<a href="' . esc_url(add_query_arg('tab', 'export', $this->page_url)) . '" class="page-title-action">' . esc_html__('导出卡密', 'jio') . '</a>';
|
||||
if ($s !== '') {
|
||||
$head_but .= '<div><div class="update-nag" style="margin: 10px 0 0;">' . sprintf(esc_html__('搜索 “%s” 的内容', 'aksrt'), esc_html($s)) . ' </div></div>';
|
||||
$head_but .= '<div><div class="update-nag" style="margin: 10px 0 0;">' . sprintf(esc_html__('搜索 “%s” 的内容', 'jio'), esc_html($s)) . ' </div></div>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -118,7 +118,7 @@ class PayChargeCards {
|
||||
*/
|
||||
public function handle_export(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('暂无此权限', 'aksrt'));
|
||||
wp_die(esc_html__('暂无此权限', 'jio'));
|
||||
}
|
||||
|
||||
@set_time_limit(0);
|
||||
@@ -133,9 +133,9 @@ class PayChargeCards {
|
||||
$status = $status_raw === '0' ? 'active' : $status_raw;
|
||||
}
|
||||
|
||||
$rows = \AKSRT\Payment\ChargeCard::get_instance()->get_export_rows($type, $status);
|
||||
$rows = \Jio\Payment\ChargeCard::get_instance()->get_export_rows($type, $status);
|
||||
if (!$rows) {
|
||||
wp_die(esc_html__('暂无可导出的内容', 'aksrt'));
|
||||
wp_die(esc_html__('暂无可导出的内容', 'jio'));
|
||||
}
|
||||
|
||||
$filename = $type . '_' . gmdate('d_m_Y');
|
||||
@@ -150,24 +150,24 @@ class PayChargeCards {
|
||||
$meta = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
$status_text = $row['status'] === 'used'
|
||||
? __('已使用', 'aksrt')
|
||||
: ($row['status'] === 'expired' ? __('已过期', 'aksrt') : ($row['status'] === 'disabled' ? __('已禁用', 'aksrt') : __('未使用', 'aksrt')));
|
||||
? __('已使用', 'jio')
|
||||
: ($row['status'] === 'expired' ? __('已过期', 'jio') : ($row['status'] === 'disabled' ? __('已禁用', 'jio') : __('未使用', 'jio')));
|
||||
|
||||
switch ($type) {
|
||||
case 'credits': // 积分兑换
|
||||
$title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('面额', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')];
|
||||
$title = [__('卡号', 'jio'), __('密码', 'jio'), __('面额', 'jio'), __('备注', 'jio'), __('状态', 'jio')];
|
||||
$data[] = [(string) $row['card'], (string) $row['password'], (string) (int) ($meta['points'] ?? 0), (string) $row['note'], $status_text];
|
||||
break;
|
||||
case 'vip': // 会员兑换
|
||||
$title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('兑换会员', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')];
|
||||
$title = [__('卡号', 'jio'), __('密码', 'jio'), __('兑换会员', 'jio'), __('备注', 'jio'), __('状态', 'jio')];
|
||||
$data[] = [(string) $row['card'], (string) $row['password'], $this->vip_meta_query($meta), (string) $row['note'], $status_text];
|
||||
break;
|
||||
case 'custom': // 自定义
|
||||
$title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')];
|
||||
$title = [__('卡号', 'jio'), __('密码', 'jio'), __('备注', 'jio'), __('状态', 'jio')];
|
||||
$data[] = [(string) $row['card'], (string) $row['password'], (string) $row['note'], ''];
|
||||
break;
|
||||
default: // 余额充值
|
||||
$title = [__('卡号', 'aksrt'), __('密码', 'aksrt'), __('面额', 'aksrt'), __('备注', 'aksrt'), __('状态', 'aksrt')];
|
||||
$title = [__('卡号', 'jio'), __('密码', 'jio'), __('面额', 'jio'), __('备注', 'jio'), __('状态', 'jio')];
|
||||
$data[] = [(string) $row['card'], (string) $row['password'], $this->balance_amount_text($meta), (string) $row['note'], $status_text];
|
||||
}
|
||||
}
|
||||
@@ -237,7 +237,7 @@ class PayChargeCards {
|
||||
$add_type = isset($_REQUEST['add_type']) ? sanitize_key((string) wp_unslash($_REQUEST['add_type'])) : 'auto';
|
||||
$pass_type = isset($_REQUEST['type']) ? sanitize_key((string) wp_unslash($_REQUEST['type'])) : '';
|
||||
if (!in_array($pass_type, ['balance', 'vip', 'credits', 'custom'], true)) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('参数传入错误', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('参数传入错误', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ class PayChargeCards {
|
||||
// 一并落在区间外,因此用一个区间判定即可覆盖全部非法输入,不另设分支。
|
||||
$auto_num = isset($_REQUEST['auto_num']) ? (int) $_REQUEST['auto_num'] : 0;
|
||||
if ($auto_num < 1 || $auto_num > 1000) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('生成数量需为 1 - 1000 的整数', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('生成数量需为 1 - 1000 的整数', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ class PayChargeCards {
|
||||
// 阻止表单提交),因此下限由服务端兜底。
|
||||
$auto_price = max(0.0, $auto_price);
|
||||
if (!$auto_price) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('请输入卡密的面额', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('请输入卡密的面额', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
$pass_meta = $pass_type === 'credits'
|
||||
@@ -273,7 +273,7 @@ class PayChargeCards {
|
||||
case 'vip':
|
||||
$vip = isset($_REQUEST['vip_exchange']) && is_array($_REQUEST['vip_exchange']) ? wp_unslash($_REQUEST['vip_exchange']) : [];
|
||||
if (empty($vip['time'])) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('请填写兑换会员参数', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('请填写兑换会员参数', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
$pass_meta = $this->vip_meta($vip);
|
||||
@@ -298,15 +298,15 @@ class PayChargeCards {
|
||||
|
||||
$remarks = isset($_REQUEST['auto_remarks']) ? sanitize_text_field((string) wp_unslash($_REQUEST['auto_remarks'])) : '';
|
||||
|
||||
$created = \AKSRT\Payment\ChargeCard::get_instance()->generate_cards($pass_type, $auto_num, $pass_meta, $rand_number, $rand_password, $remarks);
|
||||
$created = \Jio\Payment\ChargeCard::get_instance()->generate_cards($pass_type, $auto_num, $pass_meta, $rand_number, $rand_password, $remarks);
|
||||
|
||||
$this->page_notice(__('完成!', 'aksrt'), sprintf(__('已自动生成%s个卡密', 'aksrt'), (string) $created));
|
||||
$this->page_notice(__('完成!', 'jio'), sprintf(__('已自动生成%s个卡密', 'jio'), (string) $created));
|
||||
}
|
||||
|
||||
private function handle_import(string $pass_type): void {
|
||||
$import_data = isset($_REQUEST['import_data']) ? (string) wp_unslash($_REQUEST['import_data']) : '';
|
||||
if (!$import_data) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('请粘贴您需要导入的数据', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('请粘贴您需要导入的数据', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ class PayChargeCards {
|
||||
|
||||
$success_i = 0;
|
||||
$error_i = 0;
|
||||
$svc = \AKSRT\Payment\ChargeCard::get_instance();
|
||||
$svc = \Jio\Payment\ChargeCard::get_instance();
|
||||
|
||||
foreach ($import_data_array as $line) {
|
||||
$v = explode($import_division, $line);
|
||||
@@ -395,11 +395,11 @@ class PayChargeCards {
|
||||
|
||||
if ($success_i) {
|
||||
$this->page_notice(
|
||||
__('导入完成', 'aksrt'),
|
||||
sprintf(__('成功导入%s个卡密%s', 'aksrt'), (string) $success_i, $error_i ? sprintf(__(',%s个导入失败', 'aksrt'), (string) $error_i) : '')
|
||||
__('导入完成', 'jio'),
|
||||
sprintf(__('成功导入%s个卡密%s', 'jio'), (string) $success_i, $error_i ? sprintf(__(',%s个导入失败', 'jio'), (string) $error_i) : '')
|
||||
);
|
||||
} else {
|
||||
$this->page_notice(__('导入失败', 'aksrt'), sprintf(__('%s个导入失败,数据格式错误', 'aksrt'), (string) $error_i), 'error');
|
||||
$this->page_notice(__('导入失败', 'jio'), sprintf(__('%s个导入失败,数据格式错误', 'jio'), (string) $error_i), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,13 +409,13 @@ class PayChargeCards {
|
||||
$delete_ids = isset($_REQUEST['action_id']) ? array_map('absint', (array) wp_unslash($_REQUEST['action_id'])) : [];
|
||||
$delete_ids = array_filter($delete_ids);
|
||||
if (!$delete_ids) {
|
||||
$this->page_notice(__('错误!', 'aksrt'), __('未选择需要删除的内容', 'aksrt'), 'error');
|
||||
$this->page_notice(__('错误!', 'jio'), __('未选择需要删除的内容', 'jio'), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
$delete_i = \AKSRT\Payment\ChargeCard::get_instance()->delete_by_ids($delete_ids);
|
||||
$delete_i = \Jio\Payment\ChargeCard::get_instance()->delete_by_ids($delete_ids);
|
||||
|
||||
$this->page_notice(__('删除完成', 'aksrt'), sprintf(__('已删除%s个卡密', 'aksrt'), (string) $delete_i));
|
||||
$this->page_notice(__('删除完成', 'jio'), sprintf(__('已删除%s个卡密', 'jio'), (string) $delete_i));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -429,20 +429,20 @@ class PayChargeCards {
|
||||
*/
|
||||
private function type_labels(): array {
|
||||
return [
|
||||
'balance' => __('余额充值', 'aksrt'),
|
||||
'vip' => __('会员兑换', 'aksrt'),
|
||||
'credits' => __('积分兑换', 'aksrt'),
|
||||
'custom' => __('自定义卡密', 'aksrt'),
|
||||
'balance' => __('余额充值', 'jio'),
|
||||
'vip' => __('会员兑换', 'jio'),
|
||||
'credits' => __('积分兑换', 'jio'),
|
||||
'custom' => __('自定义卡密', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
private function vip_level_name(int $level): string {
|
||||
$s = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
$s = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_all() : [];
|
||||
$name = trim((string) ($s['pay_user_vip_' . max(1, min(2, $level)) . '_name'] ?? ''));
|
||||
if ($name !== '') {
|
||||
return $name;
|
||||
}
|
||||
return $level === 2 ? __('二级会员', 'aksrt') : __('一级会员', 'aksrt');
|
||||
return $level === 2 ? __('二级会员', 'jio') : __('一级会员', 'jio');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,13 +454,13 @@ class PayChargeCards {
|
||||
$time = array_key_exists('time', $meta) ? (string) $meta['time'] : null;
|
||||
if ($time !== null && $time !== '') {
|
||||
if ($time === 'Permanent') {
|
||||
return __('永久', 'aksrt');
|
||||
return __('永久', 'jio');
|
||||
}
|
||||
$unit = (isset($meta['unit']) && $meta['unit'] === 'month') ? __('个月', 'aksrt') : __('天', 'aksrt');
|
||||
$unit = (isset($meta['unit']) && $meta['unit'] === 'month') ? __('个月', 'jio') : __('天', 'jio');
|
||||
return (string) ((int) $time) . $unit;
|
||||
}
|
||||
$days = (int) ($meta['days'] ?? 0);
|
||||
return $days > 0 ? $days . __('天', 'aksrt') : __('永久', 'aksrt');
|
||||
return $days > 0 ? $days . __('天', 'jio') : __('永久', 'jio');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,13 +524,13 @@ class PayChargeCards {
|
||||
}
|
||||
|
||||
private function currency_unit(): string {
|
||||
$s = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
return trim((string) ($s['pay_currency_unit'] ?? '')) !== '' ? trim((string) $s['pay_currency_unit']) : __('元', 'aksrt');
|
||||
$s = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_all() : [];
|
||||
return trim((string) ($s['pay_currency_unit'] ?? '')) !== '' ? trim((string) $s['pay_currency_unit']) : __('元', 'jio');
|
||||
}
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* PayChargeCards 页面专属内联样式与脚本:条件依赖显隐 + 点击复制。
|
||||
* 这些是卡密管理页特有的表现(排序箭头基线锚定、按钮组、导入提示块、复制提示),
|
||||
@@ -9,7 +9,7 @@
|
||||
* 作为 flat sibling trait 被 PayChargeCards 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -32,23 +32,23 @@ trait PayChargeCardsAssets {
|
||||
.orderby-but > a + a { bottom: .075em; }
|
||||
.orderby-but:hover a { opacity: .6; }
|
||||
.orderby-but > a:hover { opacity: 1; }
|
||||
.aksrt-cardpass-bulk { margin: 0 0 12px; }
|
||||
.jio-cardpass-bulk { margin: 0 0 12px; }
|
||||
/* 卡密表列多(≥10 列),窄视口下须保持最小宽度并由表格卡片横向滚动,
|
||||
而非让各列挤扁。复刻原先列表表格的内联 style="min-width: 1000px;"。 */
|
||||
.aksrt-cardpass-table { min-width: 1000px; }
|
||||
.aksrt-btnset { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.aksrt-btnset-item { display: inline-block; cursor: pointer; }
|
||||
.aksrt-btnset-item input { position: absolute; opacity: 0; pointer-events: none; }
|
||||
.aksrt-btnset-item span { display: inline-block; padding: 3px 12px; border: 1px solid #c3c4c7; border-radius: 3px; background: #fff; }
|
||||
.aksrt-btnset-item input:checked + span { background: #2271b1; border-color: #2271b1; color: #fff; }
|
||||
.jio-cardpass-table { min-width: 1000px; }
|
||||
.jio-btnset { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.jio-btnset-item { display: inline-block; cursor: pointer; }
|
||||
.jio-btnset-item input { position: absolute; opacity: 0; pointer-events: none; }
|
||||
.jio-btnset-item span { display: inline-block; padding: 3px 12px; border: 1px solid #c3c4c7; border-radius: 3px; background: #fff; }
|
||||
.jio-btnset-item input:checked + span { background: #2271b1; border-color: #2271b1; color: #fff; }
|
||||
/* 导入提示:复刻后台 notice-warning 的外观,但**不用** .notice 类 —— 见 submessage_row()
|
||||
的说明,带 .notice 会被 wp-admin/js/common.js 搬到页面顶部。改这里请连带改那里。 */
|
||||
.aksrt-submsg { margin: 6px 0; padding: 6px 12px; background: #fef8ee; border-left: 4px solid #f0b849; }
|
||||
.jio-submsg { margin: 6px 0; padding: 6px 12px; background: #fef8ee; border-left: 4px solid #f0b849; }
|
||||
/* 提示内的条目列表必须有 <ul> 承载:list-style-position 默认 outside,
|
||||
圆点落在内容盒左侧的 padding 区里;若 <li> 没有列表容器提供缩进,
|
||||
圆点就会溢出到提示块的文字边界之外。这里显式给出缩进,避免依赖 UA/后台的 ul 默认值。 */
|
||||
.aksrt-submsg-list { margin: 6px 0; padding-left: 20px; list-style: disc; }
|
||||
.aksrt-copy-tip { margin-left: 6px; color: #00a32a; font-size: 12px; white-space: nowrap; }
|
||||
.jio-submsg-list { margin: 6px 0; padding-left: 20px; list-style: disc; }
|
||||
.jio-copy-tip { margin-left: 6px; color: #00a32a; font-size: 12px; white-space: nowrap; }
|
||||
</style>
|
||||
<script>
|
||||
(function () {
|
||||
@@ -56,7 +56,7 @@ trait PayChargeCardsAssets {
|
||||
// 含方括号的字段名(如 vip_exchange[time])会让选择器抛异常并被 catch 吞掉,
|
||||
// 从而把依赖行永久留在初始状态。
|
||||
function fieldsByName(key) {
|
||||
var all = document.querySelectorAll('.aksrt-cardpass-form [name]');
|
||||
var all = document.querySelectorAll('.jio-cardpass-form [name]');
|
||||
var out = [];
|
||||
for (var i = 0; i < all.length; i++) {
|
||||
if (all[i].getAttribute('name') === key) out.push(all[i]);
|
||||
@@ -77,7 +77,7 @@ trait PayChargeCardsAssets {
|
||||
}
|
||||
function evalDeps() {
|
||||
// 全文档查找 data-dep 节点:行与提示块各自带状态、各自落 display。
|
||||
// 刻意不做限域(不写 .aksrt-cardpass-form 前缀):提示块一旦被 HTML 解析
|
||||
// 刻意不做限域(不写 .jio-cardpass-form 前缀):提示块一旦被 HTML 解析
|
||||
// 移出表格甚至移出表单子树,限域查找会让它停在初始隐藏状态,表现为「提示不见了」。
|
||||
document.querySelectorAll('[data-dep]').forEach(function (el) {
|
||||
try {
|
||||
@@ -115,9 +115,9 @@ trait PayChargeCardsAssets {
|
||||
if (!text) return;
|
||||
var done = function () {
|
||||
var tip = document.createElement('span');
|
||||
tip.className = 'aksrt-copy-tip';
|
||||
tip.className = 'jio-copy-tip';
|
||||
var tag = el.getAttribute('data-clipboard-tag') || '';
|
||||
tip.textContent = (tag ? tag + ' ' : '') + '<?php echo esc_js(__('已复制', 'aksrt')); ?>';
|
||||
tip.textContent = (tag ? tag + ' ' : '') + '<?php echo esc_js(__('已复制', 'jio')); ?>';
|
||||
el.appendChild(tip);
|
||||
setTimeout(function () { tip.remove(); }, 1200);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* PayChargeCards 的「添加卡密 / 导出卡密」表单渲染与依赖显隐求值。
|
||||
* 表单结构、字段依赖(field_row / submessage_row / deps_visible)、按钮组与数字输入
|
||||
@@ -9,7 +9,7 @@
|
||||
* 作为 flat sibling trait 被 PayChargeCards 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,55 +18,55 @@ trait PayChargeCardsForms {
|
||||
private function render_add_form(): void {
|
||||
$type_labels = $this->type_labels();
|
||||
?>
|
||||
<p><b><?php esc_html_e('在此添加卡密', 'aksrt'); ?></b></p>
|
||||
<p><b><?php esc_html_e('在此添加卡密', 'jio'); ?></b></p>
|
||||
<ul style="list-style:disc;margin:0 0 12px 18px;line-height:1.9">
|
||||
<li><?php esc_html_e('如果您已经准备好了卡密资料,请选择导入的方式添加', 'aksrt'); ?></li>
|
||||
<li><?php esc_html_e('您也可以采用系统生成的方式,自动批量添加卡密', 'aksrt'); ?></li>
|
||||
<li><?php esc_html_e('如果您已经准备好了卡密资料,请选择导入的方式添加', 'jio'); ?></li>
|
||||
<li><?php esc_html_e('您也可以采用系统生成的方式,自动批量添加卡密', 'jio'); ?></li>
|
||||
</ul>
|
||||
<form class="aksrt-cardpass-form" method="post">
|
||||
<form class="jio-cardpass-form" method="post">
|
||||
<?php wp_nonce_field(self::NONCE); ?>
|
||||
<input type="hidden" name="action" value="add" />
|
||||
<table class="form-table" role="presentation">
|
||||
<?php
|
||||
$this->field_row(
|
||||
__('卡密类型', 'aksrt'),
|
||||
__('卡密类型', 'jio'),
|
||||
$this->button_set('type', $type_labels, 'balance'),
|
||||
[]
|
||||
);
|
||||
$this->field_row(
|
||||
__('添加方式', 'aksrt'),
|
||||
__('添加方式', 'jio'),
|
||||
$this->button_set('add_type', [
|
||||
'auto' => __('系统自动生成', 'aksrt'),
|
||||
'import' => __('导入卡密', 'aksrt'),
|
||||
'auto' => __('系统自动生成', 'jio'),
|
||||
'import' => __('导入卡密', 'jio'),
|
||||
], 'auto'),
|
||||
[]
|
||||
);
|
||||
|
||||
// 自动生成
|
||||
$this->field_row(
|
||||
__('生成数量', 'aksrt'),
|
||||
$this->spinner('auto_num', 20, __('张', 'aksrt')),
|
||||
__('生成数量', 'jio'),
|
||||
$this->spinner('auto_num', 20, __('张', 'jio')),
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'auto']],
|
||||
__('需要生成多少张卡密(1 - 1000 的整数,单次生成数量太多可能会对服务器性能造成影响)', 'aksrt')
|
||||
__('需要生成多少张卡密(1 - 1000 的整数,单次生成数量太多可能会对服务器性能造成影响)', 'jio')
|
||||
);
|
||||
$this->field_row(
|
||||
__('标识', 'aksrt'),
|
||||
__('标识', 'jio'),
|
||||
'<input type="text" class="regular-text" name="auto_remarks" value="' . esc_attr('cardpass_' . current_time('YmdHis')) . '" />',
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'auto']],
|
||||
__('对生成的卡密做标记标识,方便后期查找管理', 'aksrt') . '<br>' . sprintf(esc_html__('注意:标识中不能有%1$s字符,系统已占用', 'aksrt'), '<code>shipped</code>')
|
||||
__('对生成的卡密做标记标识,方便后期查找管理', 'jio') . '<br>' . sprintf(esc_html__('注意:标识中不能有%1$s字符,系统已占用', 'jio'), '<code>shipped</code>')
|
||||
);
|
||||
$this->field_row(
|
||||
__('面额', 'aksrt'),
|
||||
__('面额', 'jio'),
|
||||
'<input type="number" step="any" name="auto_price" value="0" />',
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => 'any', 'val' => 'balance,credits'],
|
||||
],
|
||||
__('单张卡密的面额', 'aksrt')
|
||||
__('单张卡密的面额', 'jio')
|
||||
);
|
||||
$this->field_row(
|
||||
__('兑换会员', 'aksrt'),
|
||||
__('会员等级', 'aksrt') . ' ' . $this->button_set('vip_exchange[level]', [
|
||||
__('兑换会员', 'jio'),
|
||||
__('会员等级', 'jio') . ' ' . $this->button_set('vip_exchange[level]', [
|
||||
'1' => $this->vip_level_name(1),
|
||||
'2' => $this->vip_level_name(2),
|
||||
], '1'),
|
||||
@@ -77,18 +77,18 @@ trait PayChargeCardsForms {
|
||||
);
|
||||
$this->field_row(
|
||||
' ',
|
||||
__('兑换会员时长', 'aksrt') . '<br><input type="text" name="vip_exchange[time]" value="" />',
|
||||
__('兑换会员时长', 'jio') . '<br><input type="text" name="vip_exchange[time]" value="" />',
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => '==', 'val' => 'vip'],
|
||||
],
|
||||
__('填', 'aksrt') . '<code>Permanent</code>' . __('为永久', 'aksrt')
|
||||
__('填', 'jio') . '<code>Permanent</code>' . __('为永久', 'jio')
|
||||
);
|
||||
$this->field_row(
|
||||
' ',
|
||||
__('时长单位', 'aksrt') . ' ' . $this->button_set('vip_exchange[unit]', [
|
||||
'day' => __('天', 'aksrt'),
|
||||
'month' => __('个月', 'aksrt'),
|
||||
__('时长单位', 'jio') . ' ' . $this->button_set('vip_exchange[unit]', [
|
||||
'day' => __('天', 'jio'),
|
||||
'month' => __('个月', 'jio'),
|
||||
], 'month'),
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
@@ -97,41 +97,41 @@ trait PayChargeCardsForms {
|
||||
]
|
||||
);
|
||||
$this->field_row(
|
||||
__('自定义显示名称', 'aksrt'),
|
||||
__('卡号显示名称', 'aksrt') . '<br><input type="text" class="regular-text" name="custom_name[card_name]" value="" /><br>' .
|
||||
__('卡密显示名称', 'aksrt') . '<br><input type="text" class="regular-text" name="custom_name[password_name]" value="" />',
|
||||
__('自定义显示名称', 'jio'),
|
||||
__('卡号显示名称', 'jio') . '<br><input type="text" class="regular-text" name="custom_name[card_name]" value="" /><br>' .
|
||||
__('卡密显示名称', 'jio') . '<br><input type="text" class="regular-text" name="custom_name[password_name]" value="" />',
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => '==', 'val' => 'custom'],
|
||||
]
|
||||
);
|
||||
$this->field_row(
|
||||
__('高级选项', 'aksrt'),
|
||||
__('高级选项', 'jio'),
|
||||
'<input type="checkbox" name="auto_top_s" value="1" />',
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'auto']]
|
||||
);
|
||||
$this->field_row(
|
||||
__('自定义卡号位数', 'aksrt'),
|
||||
$this->spinner('auto_rand_number_limit', 20, __('位数', 'aksrt')),
|
||||
__('自定义卡号位数', 'jio'),
|
||||
$this->spinner('auto_rand_number_limit', 20, __('位数', 'jio')),
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'auto_top_s', 'op' => 'checked'],
|
||||
]
|
||||
);
|
||||
$this->field_row(
|
||||
__('自定义密码位数', 'aksrt'),
|
||||
$this->spinner('auto_rand_password_limit', 35, __('位数', 'aksrt')),
|
||||
__('自定义密码位数', 'jio'),
|
||||
$this->spinner('auto_rand_password_limit', 35, __('位数', 'jio')),
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'auto'],
|
||||
['key' => 'auto_top_s', 'op' => 'checked'],
|
||||
],
|
||||
__('自定义自动生成的长度(不能太短,太短可能会出现重复)如果启用了单密码模式,可以将卡号位数设置为0', 'aksrt')
|
||||
__('自定义自动生成的长度(不能太短,太短可能会出现重复)如果启用了单密码模式,可以将卡号位数设置为0', 'jio')
|
||||
);
|
||||
|
||||
// 导入提示统一放在「卡密数据」正上方:切换卡密类型/添加方式时,
|
||||
// 命中的那一条紧邻示例数据与输入框,便于对照填写。
|
||||
$this->field_row(
|
||||
__('单密码模式', 'aksrt'),
|
||||
__('单密码模式', 'jio'),
|
||||
'<input type="checkbox" name="only_pass_s" value="1" />',
|
||||
[
|
||||
['key' => 'add_type', 'op' => '==', 'val' => 'import'],
|
||||
@@ -143,32 +143,32 @@ trait PayChargeCardsForms {
|
||||
['key' => 'add_type', 'op' => '!=', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => 'any', 'val' => 'balance,credits'],
|
||||
],
|
||||
'<p><b>' . esc_html__('导入卡密', 'aksrt') . '</b></p>
|
||||
<ul class="aksrt-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '<code>' . esc_html__('卡号 密码 面额 标识', 'aksrt') . '</code></li>
|
||||
<li>' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '</li><li>' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'aksrt') . '</li>
|
||||
'<p><b>' . esc_html__('导入卡密', 'jio') . '</b></p>
|
||||
<ul class="jio-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'jio') . '<code>' . esc_html__('卡号 密码 面额 标识', 'jio') . '</code></li>
|
||||
<li>' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'jio') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'jio') . '</li><li>' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'jio') . '</li>
|
||||
</ul>
|
||||
<p><b>' . esc_html__('数据示例', 'aksrt') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA 50 ' . sprintf(esc_html__('%1$s余额', 'aksrt'), esc_html('50' . $this->currency_unit())) . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq 100 ' . sprintf(esc_html__('%1$s余额', 'aksrt'), esc_html('100' . $this->currency_unit())) . '</div>'
|
||||
<p><b>' . esc_html__('数据示例', 'jio') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA 50 ' . sprintf(esc_html__('%1$s余额', 'jio'), esc_html('50' . $this->currency_unit())) . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq 100 ' . sprintf(esc_html__('%1$s余额', 'jio'), esc_html('100' . $this->currency_unit())) . '</div>'
|
||||
);
|
||||
$this->submessage_row(
|
||||
[
|
||||
['key' => 'add_type', 'op' => '!=', 'val' => 'auto'],
|
||||
['key' => 'type', 'op' => '==', 'val' => 'vip'],
|
||||
],
|
||||
'<p><b>' . esc_html__('导入会员兑换卡密', 'aksrt') . '</b></p>
|
||||
<ul class="aksrt-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '<code>' . esc_html__('卡号 密码 会员参数 标识', 'aksrt') . '</code></li>
|
||||
<li>' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('会员参数格式为:', 'aksrt') . '<code>level=1&time=5&unit=day</code></li>
|
||||
<li>' . esc_html__('参数说明:level为等级,只能为1或2。time为时长,填Permanent或数字。unit为时长单位,只能填day(天)或者month(月)', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '</li><li>' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'aksrt') . '</li>
|
||||
'<p><b>' . esc_html__('导入会员兑换卡密', 'jio') . '</b></p>
|
||||
<ul class="jio-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'jio') . '<code>' . esc_html__('卡号 密码 会员参数 标识', 'jio') . '</code></li>
|
||||
<li>' . esc_html__('卡号、密码、面额、标识默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'jio') . '</li>
|
||||
<li>' . esc_html__('会员参数格式为:', 'jio') . '<code>level=1&time=5&unit=day</code></li>
|
||||
<li>' . esc_html__('参数说明:level为等级,只能为1或2。time为时长,填Permanent或数字。unit为时长单位,只能填day(天)或者month(月)', 'jio') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'jio') . '</li><li>' . esc_html__('如果使用的是单密码模式,卡号部分随便填什么都行,但不能留空', 'jio') . '</li>
|
||||
</ul>
|
||||
<p><b>' . esc_html__('数据示例', 'aksrt') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA level=1&time=5&unit=day ' . esc_html__('5天一级会员', 'aksrt') . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq level=2&time=Permanent ' . esc_html__('永久2级会员', 'aksrt') . '</div>
|
||||
<p><b>' . esc_html__('数据示例', 'jio') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA level=1&time=5&unit=day ' . esc_html__('5天一级会员', 'jio') . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq level=2&time=Permanent ' . esc_html__('永久2级会员', 'jio') . '</div>
|
||||
'
|
||||
);
|
||||
$this->submessage_row(
|
||||
@@ -177,17 +177,17 @@ trait PayChargeCardsForms {
|
||||
['key' => 'type', 'op' => '==', 'val' => 'custom'],
|
||||
['key' => 'only_pass_s', 'op' => 'unchecked'],
|
||||
],
|
||||
'<p><b>' . esc_html__('自定义卡密导入', 'aksrt') . '</b></p>
|
||||
<ul class="aksrt-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '<code>' . esc_html__('卡号 密码 标识 自定义参数', 'aksrt') . '</code></li>
|
||||
<li>' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('自定义参数格式为:', 'aksrt') . '<code>card_name=授权号&password_name=授权密码</code>,' . esc_html__('card_name为卡号的显示名称,password_name为卡密的显示名称,可以留空', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '</li>
|
||||
'<p><b>' . esc_html__('自定义卡密导入', 'jio') . '</b></p>
|
||||
<ul class="jio-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'jio') . '<code>' . esc_html__('卡号 密码 标识 自定义参数', 'jio') . '</code></li>
|
||||
<li>' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'jio') . '</li>
|
||||
<li>' . esc_html__('自定义参数格式为:', 'jio') . '<code>card_name=授权号&password_name=授权密码</code>,' . esc_html__('card_name为卡号的显示名称,password_name为卡密的显示名称,可以留空', 'jio') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'jio') . '</li>
|
||||
</ul>
|
||||
<p><b>' . esc_html__('数据示例', 'aksrt') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'aksrt') . ' card_name=' . esc_html__('授权号', 'aksrt') . '&password_name=' . esc_html__('授权码', 'aksrt') . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'aksrt') . ' card_name=' . esc_html__('授权号', 'aksrt') . '&password_name=' . esc_html__('授权码', 'aksrt') . '</div>
|
||||
<div>111111@qq.com 11assssddsds ' . esc_html__('某某账号', 'aksrt') . ' card_name=' . esc_html__('账号', 'aksrt') . '&password_name=' . esc_html__('密码', 'aksrt') . '</div>'
|
||||
<p><b>' . esc_html__('数据示例', 'jio') . '</b></p>
|
||||
<div>4849977843617 3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'jio') . ' card_name=' . esc_html__('授权号', 'jio') . '&password_name=' . esc_html__('授权码', 'jio') . '</div>
|
||||
<div>6484027668705 SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'jio') . ' card_name=' . esc_html__('授权号', 'jio') . '&password_name=' . esc_html__('授权码', 'jio') . '</div>
|
||||
<div>111111@qq.com 11assssddsds ' . esc_html__('某某账号', 'jio') . ' card_name=' . esc_html__('账号', 'jio') . '&password_name=' . esc_html__('密码', 'jio') . '</div>'
|
||||
);
|
||||
$this->submessage_row(
|
||||
[
|
||||
@@ -195,72 +195,72 @@ trait PayChargeCardsForms {
|
||||
['key' => 'type', 'op' => '==', 'val' => 'custom'],
|
||||
['key' => 'only_pass_s', 'op' => 'checked'],
|
||||
],
|
||||
'<p><b>' . esc_html__('自定义卡密导入(单密码模式)', 'aksrt') . '</b></p>
|
||||
<ul class="aksrt-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'aksrt') . '<code>' . esc_html__('密码 标识 自定义参数', 'aksrt') . '</code></li>
|
||||
<li>' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'aksrt') . '</li>
|
||||
<li>' . esc_html__('自定义参数格式为:', 'aksrt') . '<code>password_name=授权密码</code>,' . esc_html__('password_name为卡密的显示名称,可以留空', 'aksrt') . '</li>
|
||||
'<p><b>' . esc_html__('自定义卡密导入(单密码模式)', 'jio') . '</b></p>
|
||||
<ul class="jio-submsg-list">
|
||||
<li>' . esc_html__('一行一个卡密,单行格式为:', 'jio') . '<code>' . esc_html__('密码 标识 自定义参数', 'jio') . '</code></li>
|
||||
<li>' . esc_html__('默认使用空格分割,您可以在下方自定义分割符号,与您的数据对应即可', 'jio') . '</li>
|
||||
<li>' . esc_html__('单次导入数量太多可能会对服务器性能造成影响', 'jio') . '</li>
|
||||
<li>' . esc_html__('自定义参数格式为:', 'jio') . '<code>password_name=授权密码</code>,' . esc_html__('password_name为卡密的显示名称,可以留空', 'jio') . '</li>
|
||||
</ul>
|
||||
<p><b>' . esc_html__('数据示例', 'aksrt') . '</b></p>
|
||||
<div>3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'aksrt') . ' password_name=' . esc_html__('授权码', 'aksrt') . '</div>
|
||||
<div>SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'aksrt') . ' password_name=' . esc_html__('授权码', 'aksrt') . '</div>'
|
||||
<p><b>' . esc_html__('数据示例', 'jio') . '</b></p>
|
||||
<div>3w20V4Qe7dRhAIoPYum6KSA ' . esc_html__('平台卡密01', 'jio') . ' password_name=' . esc_html__('授权码', 'jio') . '</div>
|
||||
<div>SkdJZ5vWcpqPxrbs3pqPcpq ' . esc_html__('平台卡密01', 'jio') . ' password_name=' . esc_html__('授权码', 'jio') . '</div>'
|
||||
);
|
||||
$this->field_row(
|
||||
__('卡密数据', 'aksrt'),
|
||||
__('卡密数据', 'jio'),
|
||||
'<textarea name="import_data" rows="10" class="large-text code" style="resize: both;max-width: none;"></textarea>',
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'import']]
|
||||
);
|
||||
$this->field_row(
|
||||
__('自定义分隔符号', 'aksrt'),
|
||||
__('自定义分隔符号', 'jio'),
|
||||
'<input type="text" class="mini-input" style="width:80px;" name="import_division" value=" " />',
|
||||
[['key' => 'add_type', 'op' => '==', 'val' => 'import']],
|
||||
__('卡号和密码之间分割符号(默认为空格分割)', 'aksrt')
|
||||
__('卡号和密码之间分割符号(默认为空格分割)', 'jio')
|
||||
);
|
||||
?>
|
||||
</table>
|
||||
<?php submit_button(__('确认提交', 'aksrt'), 'primary', 'submit', true); ?>
|
||||
<?php submit_button(__('确认提交', 'jio'), 'primary', 'submit', true); ?>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function render_export_form(): void {
|
||||
?>
|
||||
<form class="aksrt-cardpass-form" method="post" action="<?php echo esc_url(admin_url('admin-ajax.php')); ?>">
|
||||
<form class="jio-cardpass-form" method="post" action="<?php echo esc_url(admin_url('admin-ajax.php')); ?>">
|
||||
<input type="hidden" name="action" value="<?php echo esc_attr(self::EXPORT_AJAX_ACTION); ?>" />
|
||||
<table class="form-table" role="presentation">
|
||||
<?php
|
||||
$this->field_row(
|
||||
__('卡密类型', 'aksrt'),
|
||||
__('卡密类型', 'jio'),
|
||||
$this->button_set('type', $this->type_labels(), 'balance'),
|
||||
[]
|
||||
);
|
||||
$this->field_row(
|
||||
__('选择状态', 'aksrt'),
|
||||
__('选择状态', 'jio'),
|
||||
$this->button_set('status', [
|
||||
'all' => __('全部', 'aksrt'),
|
||||
'0' => __('未使用', 'aksrt'),
|
||||
'used' => __('已使用', 'aksrt'),
|
||||
'all' => __('全部', 'jio'),
|
||||
'0' => __('未使用', 'jio'),
|
||||
'used' => __('已使用', 'jio'),
|
||||
], 'all'),
|
||||
[['key' => 'type', 'op' => '!=', 'val' => 'custom']]
|
||||
);
|
||||
$this->field_row(
|
||||
__('导出格式', 'aksrt'),
|
||||
__('导出格式', 'jio'),
|
||||
$this->button_set('export_format', [
|
||||
'text' => __('文本文档', 'aksrt'),
|
||||
'xls' => __('Excel表格', 'aksrt'),
|
||||
'text' => __('文本文档', 'jio'),
|
||||
'xls' => __('Excel表格', 'jio'),
|
||||
], 'xls'),
|
||||
[]
|
||||
);
|
||||
$this->field_row(
|
||||
' ',
|
||||
__('分隔符号', 'aksrt') . '<br><input type="text" class="mini-input" style="width:80px;" name="text_division" value=" " />',
|
||||
__('分隔符号', 'jio') . '<br><input type="text" class="mini-input" style="width:80px;" name="text_division" value=" " />',
|
||||
[['key' => 'export_format', 'op' => '==', 'val' => 'text']],
|
||||
__('卡号和密码之间分割符号(默认为空格分割)', 'aksrt')
|
||||
__('卡号和密码之间分割符号(默认为空格分割)', 'jio')
|
||||
);
|
||||
?>
|
||||
</table>
|
||||
<?php submit_button(__('确认提交', 'aksrt'), 'primary', 'submit', true); ?>
|
||||
<?php submit_button(__('确认提交', 'jio'), 'primary', 'submit', true); ?>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
@@ -294,9 +294,9 @@ trait PayChargeCardsForms {
|
||||
// 把页面上所有「非 inline」的 notice 整块搬到 .wp-header-end(不存在时取 .wrap 内首个
|
||||
// h1/h2)之后。提示块一旦带上 notice,就会被搬到页面顶部、脱离「添加方式—卡密数据」
|
||||
// 之间的字段区,而且搬迁发生在求值之后,行内的显隐逻辑无从纠正。
|
||||
// 外观改由 .aksrt-submsg 自带(见 print_assets),与参考主题 CSF content 字段一致。
|
||||
// 外观改由 .jio-submsg 自带(见 print_assets),与参考主题 CSF content 字段一致。
|
||||
// data-dep 与初始隐藏状态同时挂在 <tr> 和内层提示块上:提示块若脱离所在行,仍能自行显隐。
|
||||
echo '<tr' . $dep_attr . $hidden . '><td colspan="2"><div' . $dep_attr . ' class="aksrt-submsg"' . $hidden . '>' . $content . '</div></td></tr>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 文案已转义。
|
||||
echo '<tr' . $dep_attr . $hidden . '><td colspan="2"><div' . $dep_attr . ' class="jio-submsg"' . $hidden . '>' . $content . '</div></td></tr>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 文案已转义。
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -398,9 +398,9 @@ trait PayChargeCardsForms {
|
||||
* @param array<string,string> $options
|
||||
*/
|
||||
private function button_set(string $name, array $options, string $current): string {
|
||||
$html = '<div class="aksrt-btnset">';
|
||||
$html = '<div class="jio-btnset">';
|
||||
foreach ($options as $value => $label) {
|
||||
$html .= '<label class="aksrt-btnset-item"><input type="radio" name="' . esc_attr($name) . '" value="' . esc_attr((string) $value) . '"' . checked((string) $current, (string) $value, false) . '><span>' . esc_html((string) $label) . '</span></label>';
|
||||
$html .= '<label class="jio-btnset-item"><input type="radio" name="' . esc_attr($name) . '" value="' . esc_attr((string) $value) . '"' . checked((string) $current, (string) $value, false) . '><span>' . esc_html((string) $label) . '</span></label>';
|
||||
}
|
||||
return $html . '</div>';
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*
|
||||
* PayChargeCards 列表渲染(过滤 / 搜索 / 排序 / 批量删除 / 分页)与翻页参数拼装。
|
||||
* 作为 flat sibling trait 被 PayChargeCards 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -16,7 +16,7 @@ trait PayChargeCardsList {
|
||||
* @param string $s 搜索词(由页面头部统一解析后传入,用于提示、过滤链接与查询)。
|
||||
*/
|
||||
private function render_list(string $s): void {
|
||||
$svc = \AKSRT\Payment\ChargeCard::get_instance();
|
||||
$svc = \Jio\Payment\ChargeCard::get_instance();
|
||||
|
||||
$status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
|
||||
$type = isset($_GET['type']) ? sanitize_key((string) wp_unslash($_GET['type'])) : '';
|
||||
@@ -39,11 +39,11 @@ trait PayChargeCardsList {
|
||||
|
||||
// 过滤链接(标题右侧按钮与搜索提示由页面头部统一输出)。
|
||||
$sub_but = [
|
||||
['name' => __('全部', 'aksrt'), 'href' => $this->page_url],
|
||||
['name' => __('全部', 'jio'), 'href' => $this->page_url],
|
||||
];
|
||||
if ($s === '') {
|
||||
$sub_but[] = ['name' => __('未使用', 'aksrt'), 'href' => add_query_arg('status', '0', $this->page_url)];
|
||||
$sub_but[] = ['name' => __('已使用', 'aksrt'), 'href' => add_query_arg('status', 'used', $this->page_url)];
|
||||
$sub_but[] = ['name' => __('未使用', 'jio'), 'href' => add_query_arg('status', '0', $this->page_url)];
|
||||
$sub_but[] = ['name' => __('已使用', 'jio'), 'href' => add_query_arg('status', 'used', $this->page_url)];
|
||||
foreach ($this->type_labels() as $_k => $_v) {
|
||||
$sub_but[] = ['name' => $_v, 'href' => add_query_arg('type', $_k, $this->page_url)];
|
||||
}
|
||||
@@ -56,8 +56,8 @@ trait PayChargeCardsList {
|
||||
|
||||
$search = '<form class="form-inline form-order" method="post">
|
||||
<div class="form-group" style="float: right;">
|
||||
<input type="text" class="form-control" name="s" placeholder="' . esc_attr__('搜索卡密', 'aksrt') . '">
|
||||
<button type="submit" class="button">' . esc_html__('提交', 'aksrt') . '</button>
|
||||
<input type="text" class="form-control" name="s" placeholder="' . esc_attr__('搜索卡密', 'jio') . '">
|
||||
<button type="submit" class="button">' . esc_html__('提交', 'jio') . '</button>
|
||||
</div>
|
||||
</form>';
|
||||
|
||||
@@ -93,8 +93,8 @@ trait PayChargeCardsList {
|
||||
|
||||
if (!$items) {
|
||||
NativeUi::table_card_open();
|
||||
echo '<table class="widefat fixed striped posts table table-bordered aksrt-cardpass-table">';
|
||||
echo '<tbody>' . NativeUi::empty_row(1, esc_html__('未找到对应内容,或暂无内容', 'aksrt')) . '</tbody>';
|
||||
echo '<table class="widefat fixed striped posts table table-bordered jio-cardpass-table">';
|
||||
echo '<tbody>' . NativeUi::empty_row(1, esc_html__('未找到对应内容,或暂无内容', 'jio')) . '</tbody>';
|
||||
echo '</table>';
|
||||
NativeUi::table_wrap_close();
|
||||
NativeUi::card_close();
|
||||
@@ -103,22 +103,22 @@ trait PayChargeCardsList {
|
||||
|
||||
// 表头(与参考主题同列、同排序链接)。
|
||||
$theads = [
|
||||
['width' => '6%', 'orderby' => 'card', 'name' => __('卡号', 'aksrt')],
|
||||
['width' => '10%', 'orderby' => 'password', 'name' => __('密码', 'aksrt')],
|
||||
['width' => '5%', 'orderby' => 'type', 'name' => __('类型', 'aksrt')],
|
||||
['width' => '5%', 'orderby' => 'create_time', 'name' => __('创建时间', 'aksrt')],
|
||||
['width' => '5%', 'orderby' => 'modified_time', 'name' => __('更新时间', 'aksrt')],
|
||||
['width' => '5%', 'orderby' => 'status', 'name' => __('状态', 'aksrt')],
|
||||
['width' => '8%', 'orderby' => 'other', 'name' => __('标识', 'aksrt')],
|
||||
['width' => '6%', 'orderby' => 'card', 'name' => __('卡号', 'jio')],
|
||||
['width' => '10%', 'orderby' => 'password', 'name' => __('密码', 'jio')],
|
||||
['width' => '5%', 'orderby' => 'type', 'name' => __('类型', 'jio')],
|
||||
['width' => '5%', 'orderby' => 'create_time', 'name' => __('创建时间', 'jio')],
|
||||
['width' => '5%', 'orderby' => 'modified_time', 'name' => __('更新时间', 'jio')],
|
||||
['width' => '5%', 'orderby' => 'status', 'name' => __('状态', 'jio')],
|
||||
['width' => '8%', 'orderby' => 'other', 'name' => __('标识', 'jio')],
|
||||
];
|
||||
|
||||
$thead_th = '<td id="cb" class="manage-column column-cb check-column" style="width: 2%;"><label class="screen-reader-text" for="cb-select-all-1">' . esc_html__('全选', 'aksrt') . '</label><input id="cb-select-all-1" type="checkbox"></td>';
|
||||
$thead_th = '<td id="cb" class="manage-column column-cb check-column" style="width: 2%;"><label class="screen-reader-text" for="cb-select-all-1">' . esc_html__('全选', 'jio') . '</label><input id="cb-select-all-1" type="checkbox"></td>';
|
||||
foreach ($theads as $thead) {
|
||||
$orderby_links = '';
|
||||
if ($thead['orderby']) {
|
||||
$orderby_url = add_query_arg('orderby', $thead['orderby']);
|
||||
$orderby_links .= '<a title="' . esc_attr__('降序', 'aksrt') . '" href="' . esc_url(add_query_arg('desc', 'ASC', $orderby_url)) . '"><span class="dashicons dashicons-arrow-up"></span></a>';
|
||||
$orderby_links .= '<a title="' . esc_attr__('升序', 'aksrt') . '" href="' . esc_url(add_query_arg('desc', 'DESC', $orderby_url)) . '"><span class="dashicons dashicons-arrow-down"></span></a>';
|
||||
$orderby_links .= '<a title="' . esc_attr__('降序', 'jio') . '" href="' . esc_url(add_query_arg('desc', 'ASC', $orderby_url)) . '"><span class="dashicons dashicons-arrow-up"></span></a>';
|
||||
$orderby_links .= '<a title="' . esc_attr__('升序', 'jio') . '" href="' . esc_url(add_query_arg('desc', 'DESC', $orderby_url)) . '"><span class="dashicons dashicons-arrow-down"></span></a>';
|
||||
$orderby_links = '<span class="orderby-but">' . $orderby_links . '</span>';
|
||||
}
|
||||
$thead_th .= '<th class="" width="' . esc_attr($thead['width']) . '">' . esc_html($thead['name']) . $orderby_links . '</th>';
|
||||
@@ -135,17 +135,17 @@ trait PayChargeCardsList {
|
||||
// 自定义卡密不展示状态;其余类型按状态着色(类型链接统一在下方链表中生成)。
|
||||
$status_html = '';
|
||||
if ($msg['type'] !== 'custom') {
|
||||
$status_html = '<span style="color: #3d7ffd;">' . esc_html__('未使用', 'aksrt') . '</span>';
|
||||
$status_html = '<span style="color: #3d7ffd;">' . esc_html__('未使用', 'jio') . '</span>';
|
||||
if ($msg['status'] === 'used') {
|
||||
$status_html = '<span style="color: #f93b3b;">' . esc_html__('已使用', 'aksrt') . '</span>';
|
||||
$status_html = '<span style="color: #f93b3b;">' . esc_html__('已使用', 'jio') . '</span>';
|
||||
if (!empty($msg['order_number'])) {
|
||||
$order_link_url = admin_url('admin.php?page=aksrt_orders&s=' . urlencode((string) $msg['order_number']));
|
||||
$status_html .= '<a style="color: #ff6215;" target="_blank" href="' . esc_url($order_link_url) . '"> [' . esc_html__('查看', 'aksrt') . ']</a>';
|
||||
$order_link_url = admin_url('admin.php?page=jio_orders&s=' . urlencode((string) $msg['order_number']));
|
||||
$status_html .= '<a style="color: #ff6215;" target="_blank" href="' . esc_url($order_link_url) . '"> [' . esc_html__('查看', 'jio') . ']</a>';
|
||||
}
|
||||
} elseif ($msg['status'] === 'expired') {
|
||||
$status_html = '<span style="color: #8c8f94;">' . esc_html__('已过期', 'aksrt') . '</span>';
|
||||
$status_html = '<span style="color: #8c8f94;">' . esc_html__('已过期', 'jio') . '</span>';
|
||||
} elseif ($msg['status'] === 'disabled') {
|
||||
$status_html = '<span style="color: #8c8f94;">' . esc_html__('已禁用', 'aksrt') . '</span>';
|
||||
$status_html = '<span style="color: #8c8f94;">' . esc_html__('已禁用', 'jio') . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,28 +155,28 @@ trait PayChargeCardsList {
|
||||
}
|
||||
|
||||
if ($msg['type'] === 'vip') {
|
||||
$type_html = '<a style="color:#975106" href="' . esc_url(add_query_arg('type', 'vip', $this->page_url)) . '">' . esc_html__('会员兑换', 'aksrt') . '</a>';
|
||||
$type_html = '<a style="color:#975106" href="' . esc_url(add_query_arg('type', 'vip', $this->page_url)) . '">' . esc_html__('会员兑换', 'jio') . '</a>';
|
||||
$level = (int) ($meta['level'] ?? 1);
|
||||
$type_html .= '<br>' . esc_html($this->vip_level_name($level)) . ':' . esc_html($this->vip_time_text($meta));
|
||||
} elseif ($msg['type'] === 'credits') {
|
||||
$type_html = '<a style="color:#289d0f" href="' . esc_url(add_query_arg('type', 'credits', $this->page_url)) . '">' . esc_html__('积分兑换', 'aksrt') . '</a>';
|
||||
$type_html .= '<br>' . esc_html__('积分:', 'aksrt') . esc_html((string) (int) ($meta['points'] ?? 0));
|
||||
$type_html = '<a style="color:#289d0f" href="' . esc_url(add_query_arg('type', 'credits', $this->page_url)) . '">' . esc_html__('积分兑换', 'jio') . '</a>';
|
||||
$type_html .= '<br>' . esc_html__('积分:', 'jio') . esc_html((string) (int) ($meta['points'] ?? 0));
|
||||
} elseif ($msg['type'] === 'balance') {
|
||||
$type_html = '<a style="color:#0c76e6" href="' . esc_url(add_query_arg('type', 'balance', $this->page_url)) . '">' . esc_html__('余额充值', 'aksrt') . '</a>';
|
||||
$type_html .= '<br>' . esc_html__('金额:', 'aksrt') . esc_html($this->balance_amount_text($meta));
|
||||
$type_html = '<a style="color:#0c76e6" href="' . esc_url(add_query_arg('type', 'balance', $this->page_url)) . '">' . esc_html__('余额充值', 'jio') . '</a>';
|
||||
$type_html .= '<br>' . esc_html__('金额:', 'jio') . esc_html($this->balance_amount_text($meta));
|
||||
} else {
|
||||
$type_html = '<a style="color:#a42ade" href="' . esc_url(add_query_arg('type', 'custom', $this->page_url)) . '">' . esc_html__('自定义卡密', 'aksrt') . '</a>';
|
||||
$type_html = '<a style="color:#a42ade" href="' . esc_url(add_query_arg('type', 'custom', $this->page_url)) . '">' . esc_html__('自定义卡密', 'jio') . '</a>';
|
||||
}
|
||||
|
||||
$card_name = (string) ($meta['custom_name']['card_name'] ?? '');
|
||||
$password_name = (string) ($meta['custom_name']['password_name'] ?? '');
|
||||
|
||||
$tbody .= '<tr>';
|
||||
$tbody .= '<th scope="row" class="check-column"><label class="screen-reader-text" for="cb-select-' . (int) $msg['id'] . '">' . esc_html__('选择', 'aksrt') . '</label>
|
||||
$tbody .= '<th scope="row" class="check-column"><label class="screen-reader-text" for="cb-select-' . (int) $msg['id'] . '">' . esc_html__('选择', 'jio') . '</label>
|
||||
<input id="cb-select-' . (int) $msg['id'] . '" type="checkbox" name="action_id[]" value="' . (int) $msg['id'] . '">
|
||||
</th>';
|
||||
$tbody .= "<td><span data-clipboard-text='" . esc_attr((string) $msg['card']) . "' data-clipboard-tag=\"" . esc_attr($card_name !== '' ? $card_name : __('卡号', 'aksrt')) . "\">" . esc_html($card_name) . ' ' . esc_html((string) $msg['card']) . '</span></td>';
|
||||
$tbody .= "<td><span data-clipboard-text='" . esc_attr((string) $msg['password']) . "' data-clipboard-tag=\"" . esc_attr($password_name !== '' ? $password_name : __('密码', 'aksrt')) . "\">" . esc_html($password_name) . ' ' . esc_html((string) $msg['password']) . '</span></td>';
|
||||
$tbody .= "<td><span data-clipboard-text='" . esc_attr((string) $msg['card']) . "' data-clipboard-tag=\"" . esc_attr($card_name !== '' ? $card_name : __('卡号', 'jio')) . "\">" . esc_html($card_name) . ' ' . esc_html((string) $msg['card']) . '</span></td>';
|
||||
$tbody .= "<td><span data-clipboard-text='" . esc_attr((string) $msg['password']) . "' data-clipboard-tag=\"" . esc_attr($password_name !== '' ? $password_name : __('密码', 'jio')) . "\">" . esc_html($password_name) . ' ' . esc_html((string) $msg['password']) . '</span></td>';
|
||||
$tbody .= "<td>$type_html</td>";
|
||||
$tbody .= '<td>' . esc_html((string) $msg['created_at']) . '</td>';
|
||||
$tbody .= '<td>' . esc_html((string) ($msg['used_at'] ?: $msg['created_at'])) . '</td>';
|
||||
@@ -185,18 +185,18 @@ trait PayChargeCardsList {
|
||||
$tbody .= '</tr>';
|
||||
}
|
||||
|
||||
echo '<form class="aksrt-cardpass-list-form" method="post">';
|
||||
echo '<form class="jio-cardpass-list-form" method="post">';
|
||||
wp_nonce_field(self::NONCE);
|
||||
echo '<div class="bulkactions aksrt-native-bulk aksrt-cardpass-bulk">
|
||||
<label for="bulk-action-selector-top" class="screen-reader-text">' . esc_html__('选择批量操作', 'aksrt') . '</label><select name="action" id="bulk-action-selector-top">
|
||||
<option value="-1">' . esc_html__('批量操作', 'aksrt') . '</option>
|
||||
<option value="delete">' . esc_html__('删除', 'aksrt') . '</option>
|
||||
echo '<div class="bulkactions jio-native-bulk jio-cardpass-bulk">
|
||||
<label for="bulk-action-selector-top" class="screen-reader-text">' . esc_html__('选择批量操作', 'jio') . '</label><select name="action" id="bulk-action-selector-top">
|
||||
<option value="-1">' . esc_html__('批量操作', 'jio') . '</option>
|
||||
<option value="delete">' . esc_html__('删除', 'jio') . '</option>
|
||||
</select>
|
||||
<input type="submit" class="button action" value="' . esc_attr__('应用', 'aksrt') . '">
|
||||
<input type="submit" class="button action" value="' . esc_attr__('应用', 'jio') . '">
|
||||
</div>';
|
||||
|
||||
NativeUi::table_card_open();
|
||||
echo '<table class="widefat fixed striped posts table table-bordered aksrt-cardpass-table">';
|
||||
echo '<table class="widefat fixed striped posts table table-bordered jio-cardpass-table">';
|
||||
echo '<thead><tr>' . $thead_th . '</tr></thead>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 已转义。
|
||||
echo '<tbody>' . $tbody . '</tbody>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 已转义。
|
||||
echo '</table>';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -12,9 +12,9 @@ class PayCoupons {
|
||||
use PayCouponsForms;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_coupons';
|
||||
public const CREATE_DIALOG = 'aksrt-coupon-create';
|
||||
public const BATCH_DIALOG = 'aksrt-coupon-batch';
|
||||
public const PAGE_SLUG = 'jio_coupons';
|
||||
public const CREATE_DIALOG = 'jio-coupon-create';
|
||||
public const BATCH_DIALOG = 'jio-coupon-batch';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -30,11 +30,11 @@ class PayCoupons {
|
||||
return;
|
||||
}
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
add_action('admin_post_aksrt_pay_create_coupon', [$this, 'handle_create_coupon']);
|
||||
add_action('admin_post_aksrt_pay_batch_coupons', [$this, 'handle_batch_coupons']);
|
||||
add_action('admin_post_aksrt_pay_update_coupon', [$this, 'handle_update_coupon']);
|
||||
add_action('admin_post_aksrt_pay_disable_coupon', [$this, 'handle_disable_coupon']);
|
||||
add_action('admin_post_aksrt_pay_enable_coupon', [$this, 'handle_enable_coupon']);
|
||||
add_action('admin_post_jio_pay_create_coupon', [$this, 'handle_create_coupon']);
|
||||
add_action('admin_post_jio_pay_batch_coupons', [$this, 'handle_batch_coupons']);
|
||||
add_action('admin_post_jio_pay_update_coupon', [$this, 'handle_update_coupon']);
|
||||
add_action('admin_post_jio_pay_disable_coupon', [$this, 'handle_disable_coupon']);
|
||||
add_action('admin_post_jio_pay_enable_coupon', [$this, 'handle_enable_coupon']);
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
@@ -44,56 +44,56 @@ class PayCoupons {
|
||||
}
|
||||
$css = '/assets/css/admin/coupons.css';
|
||||
$js = '/assets/js/admin-coupons.js';
|
||||
wp_enqueue_style('aksrt-admin-coupons', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-coupons', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-coupons', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-coupons', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_page(): void {
|
||||
$this->ensure_cap();
|
||||
|
||||
$ready = class_exists('\AKSRT\Payment\Coupon');
|
||||
NativeUi::page_open('aksrt-coupons-page');
|
||||
$ready = class_exists('\Jio\Payment\Coupon');
|
||||
NativeUi::page_open('jio-coupons-page');
|
||||
NativeUi::header(
|
||||
__('优惠码', 'aksrt'),
|
||||
__('创建、批量生成与管理结算优惠码。配置项仍在 AKSRT 主题设置 SPA。', 'aksrt'),
|
||||
__('优惠码', 'jio'),
|
||||
__('创建、批量生成与管理结算优惠码。配置项仍在 Jio 主题设置 SPA。', 'jio'),
|
||||
'dashicons-tickets-alt',
|
||||
$ready ? [
|
||||
['label' => __('新建优惠码', 'aksrt'), 'dialog' => self::CREATE_DIALOG, 'primary' => true, 'icon' => 'dashicons-plus-alt2'],
|
||||
['label' => __('批量生成', 'aksrt'), 'dialog' => self::BATCH_DIALOG, 'icon' => 'dashicons-randomize'],
|
||||
['label' => __('新建优惠码', 'jio'), 'dialog' => self::CREATE_DIALOG, 'primary' => true, 'icon' => 'dashicons-plus-alt2'],
|
||||
['label' => __('批量生成', 'jio'), 'dialog' => self::BATCH_DIALOG, 'icon' => 'dashicons-randomize'],
|
||||
] : []
|
||||
);
|
||||
$this->render_flash();
|
||||
|
||||
if (!$ready) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('优惠码模块未初始化。', 'aksrt') . '</p></div>';
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('优惠码模块未初始化。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
|
||||
$svc = \AKSRT\Payment\Coupon::get_instance();
|
||||
$svc = \Jio\Payment\Coupon::get_instance();
|
||||
$edit_id = isset($_GET['edit_id']) ? (int) $_GET['edit_id'] : 0;
|
||||
$edit_row = $edit_id > 0 ? $svc->get($edit_id) : null;
|
||||
|
||||
if ($edit_id > 0 && $edit_row) {
|
||||
$this->render_edit_form($edit_row);
|
||||
} elseif ($edit_id > 0) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('优惠码不存在。', 'aksrt') . '</p></div>';
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('优惠码不存在。', 'jio') . '</p></div>';
|
||||
}
|
||||
|
||||
$this->render_generated_codes();
|
||||
$this->render_list($svc);
|
||||
$this->render_create_dialog();
|
||||
$this->render_batch_dialog();
|
||||
echo '<div class="aksrt-coupon-toast" role="status" aria-live="polite" data-aksrt-coupon-toast'
|
||||
. ' data-copied="' . esc_attr__('已复制到剪贴板', 'aksrt') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请手动选中后复制。', 'aksrt') . '"></div>';
|
||||
echo '<div class="jio-coupon-toast" role="status" aria-live="polite" data-jio-coupon-toast'
|
||||
. ' data-copied="' . esc_attr__('已复制到剪贴板', 'jio') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请手动选中后复制。', 'jio') . '"></div>';
|
||||
|
||||
NativeUi::page_close();
|
||||
}
|
||||
|
||||
/** 批量生成后一次性展示本批代码(transient 读取即删),附「复制全部」。 */
|
||||
private function render_generated_codes(): void {
|
||||
$key = 'aksrt_pay_coupon_codes_' . get_current_user_id();
|
||||
$key = 'jio_pay_coupon_codes_' . get_current_user_id();
|
||||
$codes = get_transient($key);
|
||||
if (!is_array($codes) || !$codes) {
|
||||
return;
|
||||
@@ -101,46 +101,46 @@ class PayCoupons {
|
||||
delete_transient($key);
|
||||
$text = implode("\n", array_map('strval', $codes));
|
||||
$copy = '<button type="button" class="button button-small" data-clipboard-text="' . esc_attr($text) . '">'
|
||||
. '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span>' . esc_html__('复制全部', 'aksrt') . '</button>';
|
||||
. '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span>' . esc_html__('复制全部', 'jio') . '</button>';
|
||||
NativeUi::card_open(
|
||||
sprintf(
|
||||
/* translators: %d count */
|
||||
__('本次生成的 %d 个优惠码', 'aksrt'),
|
||||
__('本次生成的 %d 个优惠码', 'jio'),
|
||||
count($codes)
|
||||
),
|
||||
$copy,
|
||||
false,
|
||||
'aksrt-coupon-generated'
|
||||
'jio-coupon-generated'
|
||||
);
|
||||
echo '<p class="aksrt-native-field__hint">' . esc_html__('离开或刷新本页后不再显示,请及时复制保存。', 'aksrt') . '</p>';
|
||||
echo '<textarea class="large-text code" rows="' . esc_attr((string) min(12, max(3, count($codes)))) . '" readonly aria-label="' . esc_attr__('本次生成的优惠码', 'aksrt') . '">' . esc_textarea($text) . '</textarea>';
|
||||
echo '<p class="jio-native-field__hint">' . esc_html__('离开或刷新本页后不再显示,请及时复制保存。', 'jio') . '</p>';
|
||||
echo '<textarea class="large-text code" rows="' . esc_attr((string) min(12, max(3, count($codes)))) . '" readonly aria-label="' . esc_attr__('本次生成的优惠码', 'jio') . '">' . esc_textarea($text) . '</textarea>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
|
||||
private function render_list(\AKSRT\Payment\Coupon $svc): void {
|
||||
private function render_list(\Jio\Payment\Coupon $svc): void {
|
||||
$status = isset($_GET['status']) ? sanitize_key((string) wp_unslash($_GET['status'])) : '';
|
||||
$search = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
|
||||
$page_num = max(1, (int) ($_GET['paged'] ?? 1));
|
||||
$per_page = AdminListPagination::DEFAULT_PER_PAGE;
|
||||
$type_labels = [
|
||||
'fixed' => __('固定金额', 'aksrt'),
|
||||
'percent' => __('折扣百分比', 'aksrt'),
|
||||
'fixed' => __('固定金额', 'jio'),
|
||||
'percent' => __('折扣百分比', 'jio'),
|
||||
];
|
||||
$status_map = [
|
||||
'active' => [__('可用', 'aksrt'), 'success'],
|
||||
'disabled' => [__('已禁用', 'aksrt'), 'neutral'],
|
||||
'active' => [__('可用', 'jio'), 'success'],
|
||||
'disabled' => [__('已禁用', 'jio'), 'neutral'],
|
||||
];
|
||||
|
||||
// 筛选栏(卡片头部的 .aksrt-native-filters 表单)。
|
||||
$filters = '<form method="get" class="aksrt-native-filters">';
|
||||
// 筛选栏(卡片头部的 .jio-native-filters 表单)。
|
||||
$filters = '<form method="get" class="jio-native-filters">';
|
||||
$filters .= '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '" />';
|
||||
$filters .= '<select name="status">';
|
||||
foreach (['' => __('全部状态', 'aksrt'), 'active' => __('可用', 'aksrt'), 'disabled' => __('已禁用', 'aksrt')] as $key => $label) {
|
||||
foreach (['' => __('全部状态', 'jio'), 'active' => __('可用', 'jio'), 'disabled' => __('已禁用', 'jio')] as $key => $label) {
|
||||
$filters .= sprintf('<option value="%s"%s>%s</option>', esc_attr($key), selected($status, $key, false), esc_html($label));
|
||||
}
|
||||
$filters .= '</select>';
|
||||
$filters .= '<input type="search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('搜索代码', 'aksrt') . '" />';
|
||||
$filters .= '<button type="submit" class="button">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
$filters .= '<input type="search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('搜索代码', 'jio') . '" />';
|
||||
$filters .= '<button type="submit" class="button">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
$filters .= '</form>';
|
||||
|
||||
$result = $svc->get_list([
|
||||
@@ -166,12 +166,12 @@ class PayCoupons {
|
||||
$items = $result['items'] ?? [];
|
||||
}
|
||||
|
||||
NativeUi::table_card_open(__('优惠码列表', 'aksrt'), $filters);
|
||||
NativeUi::table_card_open(__('优惠码列表', 'jio'), $filters);
|
||||
echo '<table class="wp-list-table widefat fixed striped"><thead><tr>';
|
||||
echo '<th>ID</th><th>' . esc_html__('代码', 'aksrt') . '</th><th>' . esc_html__('类型', 'aksrt') . '</th><th>' . esc_html__('面值', 'aksrt') . '</th><th>' . esc_html__('门槛/封顶', 'aksrt') . '</th><th>' . esc_html__('范围', 'aksrt') . '</th><th>' . esc_html__('已用/上限/每人', 'aksrt') . '</th><th>' . esc_html__('状态', 'aksrt') . '</th><th>' . esc_html__('过期', 'aksrt') . '</th><th>' . esc_html__('操作', 'aksrt') . '</th>';
|
||||
echo '<th>ID</th><th>' . esc_html__('代码', 'jio') . '</th><th>' . esc_html__('类型', 'jio') . '</th><th>' . esc_html__('面值', 'jio') . '</th><th>' . esc_html__('门槛/封顶', 'jio') . '</th><th>' . esc_html__('范围', 'jio') . '</th><th>' . esc_html__('已用/上限/每人', 'jio') . '</th><th>' . esc_html__('状态', 'jio') . '</th><th>' . esc_html__('过期', 'jio') . '</th><th>' . esc_html__('操作', 'jio') . '</th>';
|
||||
echo '</tr></thead><tbody>';
|
||||
if (!$items) {
|
||||
echo NativeUi::empty_row(10, __('暂无优惠码', 'aksrt'), __('点击右上角「新建优惠码」或「批量生成」添加。', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput -- 内部构造,已转义。
|
||||
echo NativeUi::empty_row(10, __('暂无优惠码', 'jio'), __('点击右上角「新建优惠码」或「批量生成」添加。', 'jio')); // phpcs:ignore WordPress.Security.EscapeOutput -- 内部构造,已转义。
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$id = (int) ($row['id'] ?? 0);
|
||||
@@ -180,7 +180,7 @@ class PayCoupons {
|
||||
echo '<tr>';
|
||||
echo '<td>' . esc_html((string) $id) . '</td>';
|
||||
$code = (string) ($row['code'] ?? '');
|
||||
echo '<td><button type="button" class="aksrt-coupon-code aksrt-native-mono" data-clipboard-text="' . esc_attr($code) . '" aria-label="' . esc_attr(sprintf(__('复制优惠码 %s', 'aksrt'), $code)) . '">'
|
||||
echo '<td><button type="button" class="jio-coupon-code jio-native-mono" data-clipboard-text="' . esc_attr($code) . '" aria-label="' . esc_attr(sprintf(__('复制优惠码 %s', 'jio'), $code)) . '">'
|
||||
. esc_html($code) . '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button></td>';
|
||||
echo '<td>' . esc_html($type_labels[$row_type] ?? $row_type) . '</td>';
|
||||
echo '<td>' . esc_html((string) ($row['value'] ?? '')) . ($row_type === 'percent' ? '%' : '') . '</td>';
|
||||
@@ -189,21 +189,21 @@ class PayCoupons {
|
||||
echo '<td>' . esc_html((string) ((int) ($row['used_count'] ?? 0)) . '/' . (string) ((int) ($row['usage_limit'] ?? 0)) . '/' . (string) ((int) ($row['per_user_limit'] ?? 0))) . '</td>';
|
||||
echo '<td>' . NativeUi::status_badge($st, $status_map) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- 内部构造,已转义。
|
||||
echo '<td>' . esc_html((string) ($row['expires_at'] ?? '')) . '</td>';
|
||||
echo '<td><div class="aksrt-native-inline">';
|
||||
echo '<a class="button button-small" href="' . esc_url($this->page_url(['edit_id' => $id])) . '">' . esc_html__('编辑', 'aksrt') . '</a>';
|
||||
echo '<td><div class="jio-native-inline">';
|
||||
echo '<a class="button button-small" href="' . esc_url($this->page_url(['edit_id' => $id])) . '">' . esc_html__('编辑', 'jio') . '</a>';
|
||||
if ($st === 'active') {
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-native-inline">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_disable_coupon" />';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-native-inline">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_disable_coupon" />';
|
||||
echo '<input type="hidden" name="coupon_id" value="' . esc_attr((string) $id) . '" />';
|
||||
wp_nonce_field('aksrt_pay_disable_coupon_' . $id);
|
||||
submit_button(__('禁用', 'aksrt'), 'small', '', false);
|
||||
wp_nonce_field('jio_pay_disable_coupon_' . $id);
|
||||
submit_button(__('禁用', 'jio'), 'small', '', false);
|
||||
echo '</form>';
|
||||
} elseif ($st === 'disabled') {
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-native-inline">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_enable_coupon" />';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-native-inline">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_enable_coupon" />';
|
||||
echo '<input type="hidden" name="coupon_id" value="' . esc_attr((string) $id) . '" />';
|
||||
wp_nonce_field('aksrt_pay_enable_coupon_' . $id);
|
||||
submit_button(__('启用', 'aksrt'), 'small', '', false);
|
||||
wp_nonce_field('jio_pay_enable_coupon_' . $id);
|
||||
submit_button(__('启用', 'jio'), 'small', '', false);
|
||||
echo '</form>';
|
||||
}
|
||||
echo '</div></td></tr>';
|
||||
@@ -224,13 +224,13 @@ class PayCoupons {
|
||||
|
||||
public function handle_create_coupon(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
check_admin_referer('aksrt_pay_create_coupon');
|
||||
check_admin_referer('jio_pay_create_coupon');
|
||||
|
||||
$ok = false;
|
||||
if (class_exists('\AKSRT\Payment\Coupon')) {
|
||||
$id = \AKSRT\Payment\Coupon::get_instance()->create([
|
||||
if (class_exists('\Jio\Payment\Coupon')) {
|
||||
$id = \Jio\Payment\Coupon::get_instance()->create([
|
||||
'code' => sanitize_text_field((string) wp_unslash($_POST['code'] ?? '')),
|
||||
'type' => sanitize_key((string) wp_unslash($_POST['type'] ?? 'fixed')),
|
||||
'value' => trim((string) wp_unslash($_POST['value'] ?? '0')),
|
||||
@@ -243,19 +243,19 @@ class PayCoupons {
|
||||
]);
|
||||
$ok = $id > 0;
|
||||
}
|
||||
wp_safe_redirect($this->page_url(['aksrt_pay_msg' => $ok ? 'coupon_created' : 'coupon_create_failed']));
|
||||
wp_safe_redirect($this->page_url(['jio_pay_msg' => $ok ? 'coupon_created' : 'coupon_create_failed']));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function handle_batch_coupons(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
check_admin_referer('aksrt_pay_batch_coupons');
|
||||
check_admin_referer('jio_pay_batch_coupons');
|
||||
|
||||
$ok = false;
|
||||
if (class_exists('\AKSRT\Payment\Coupon')) {
|
||||
$result = \AKSRT\Payment\Coupon::get_instance()->batch_create([
|
||||
if (class_exists('\Jio\Payment\Coupon')) {
|
||||
$result = \Jio\Payment\Coupon::get_instance()->batch_create([
|
||||
'type' => sanitize_key((string) wp_unslash($_POST['type'] ?? 'fixed')),
|
||||
'value' => trim((string) wp_unslash($_POST['value'] ?? '0')),
|
||||
'min_amount' => trim((string) wp_unslash($_POST['min_amount'] ?? '0')),
|
||||
@@ -268,24 +268,24 @@ class PayCoupons {
|
||||
$codes = is_array($result['codes'] ?? null) ? $result['codes'] : [];
|
||||
$ok = (int) ($result['created'] ?? 0) > 0;
|
||||
if ($ok) {
|
||||
set_transient('aksrt_pay_coupon_codes_' . get_current_user_id(), $codes, 10 * MINUTE_IN_SECONDS);
|
||||
set_transient('jio_pay_coupon_codes_' . get_current_user_id(), $codes, 10 * MINUTE_IN_SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
wp_safe_redirect($this->page_url(['aksrt_pay_msg' => $ok ? 'coupon_batch_ok' : 'coupon_batch_failed']));
|
||||
wp_safe_redirect($this->page_url(['jio_pay_msg' => $ok ? 'coupon_batch_ok' : 'coupon_batch_failed']));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function handle_update_coupon(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
$id = isset($_POST['coupon_id']) ? (int) $_POST['coupon_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_update_coupon_' . $id);
|
||||
check_admin_referer('jio_pay_update_coupon_' . $id);
|
||||
|
||||
$ok = false;
|
||||
if ($id > 0 && class_exists('\AKSRT\Payment\Coupon')) {
|
||||
$ok = \AKSRT\Payment\Coupon::get_instance()->update($id, [
|
||||
if ($id > 0 && class_exists('\Jio\Payment\Coupon')) {
|
||||
$ok = \Jio\Payment\Coupon::get_instance()->update($id, [
|
||||
'type' => sanitize_key((string) wp_unslash($_POST['type'] ?? 'fixed')),
|
||||
'value' => trim((string) wp_unslash($_POST['value'] ?? '0')),
|
||||
'min_amount' => trim((string) wp_unslash($_POST['min_amount'] ?? '0')),
|
||||
@@ -297,7 +297,7 @@ class PayCoupons {
|
||||
]);
|
||||
}
|
||||
wp_safe_redirect($this->page_url([
|
||||
'aksrt_pay_msg' => $ok ? 'coupon_updated' : 'coupon_update_failed',
|
||||
'jio_pay_msg' => $ok ? 'coupon_updated' : 'coupon_update_failed',
|
||||
'edit_id' => $id,
|
||||
]));
|
||||
exit;
|
||||
@@ -305,23 +305,23 @@ class PayCoupons {
|
||||
|
||||
public function handle_disable_coupon(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
$id = isset($_POST['coupon_id']) ? (int) $_POST['coupon_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_disable_coupon_' . $id);
|
||||
$ok = $id > 0 && class_exists('\AKSRT\Payment\Coupon') && \AKSRT\Payment\Coupon::get_instance()->disable($id);
|
||||
wp_safe_redirect($this->page_url(['aksrt_pay_msg' => $ok ? 'coupon_disabled' : 'coupon_failed']));
|
||||
check_admin_referer('jio_pay_disable_coupon_' . $id);
|
||||
$ok = $id > 0 && class_exists('\Jio\Payment\Coupon') && \Jio\Payment\Coupon::get_instance()->disable($id);
|
||||
wp_safe_redirect($this->page_url(['jio_pay_msg' => $ok ? 'coupon_disabled' : 'coupon_failed']));
|
||||
exit;
|
||||
}
|
||||
|
||||
public function handle_enable_coupon(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
$id = isset($_POST['coupon_id']) ? (int) $_POST['coupon_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_enable_coupon_' . $id);
|
||||
$ok = $id > 0 && class_exists('\AKSRT\Payment\Coupon') && \AKSRT\Payment\Coupon::get_instance()->enable($id);
|
||||
wp_safe_redirect($this->page_url(['aksrt_pay_msg' => $ok ? 'coupon_enabled' : 'coupon_failed']));
|
||||
check_admin_referer('jio_pay_enable_coupon_' . $id);
|
||||
$ok = $id > 0 && class_exists('\Jio\Payment\Coupon') && \Jio\Payment\Coupon::get_instance()->enable($id);
|
||||
wp_safe_redirect($this->page_url(['jio_pay_msg' => $ok ? 'coupon_enabled' : 'coupon_failed']));
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -370,15 +370,15 @@ class PayCoupons {
|
||||
$scope = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
if (!is_array($scope) || empty($scope)) {
|
||||
return __('全部', 'aksrt');
|
||||
return __('全部', 'jio');
|
||||
}
|
||||
$type = sanitize_key((string) ($scope['type'] ?? ''));
|
||||
$ids = array_map('intval', (array) ($scope['ids'] ?? []));
|
||||
$id_text = $ids ? implode(',', $ids) : '—';
|
||||
$map = [
|
||||
'include' => __('含商品', 'aksrt'),
|
||||
'exclude' => __('排除商品', 'aksrt'),
|
||||
'category' => __('分类', 'aksrt'),
|
||||
'include' => __('含商品', 'jio'),
|
||||
'exclude' => __('排除商品', 'jio'),
|
||||
'category' => __('分类', 'jio'),
|
||||
];
|
||||
$label = $map[$type] ?? $type;
|
||||
return $label . ':' . $id_text;
|
||||
@@ -386,25 +386,25 @@ class PayCoupons {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
if (empty($_GET['aksrt_pay_msg'])) {
|
||||
if (empty($_GET['jio_pay_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_pay_msg']));
|
||||
$map = [
|
||||
'coupon_created' => __('优惠码已创建。', 'aksrt'),
|
||||
'coupon_create_failed' => __('优惠码创建失败(可能重复)。', 'aksrt'),
|
||||
'coupon_batch_ok' => __('优惠码批量生成完成。', 'aksrt'),
|
||||
'coupon_batch_failed' => __('优惠码批量生成失败。', 'aksrt'),
|
||||
'coupon_updated' => __('优惠码已更新。', 'aksrt'),
|
||||
'coupon_update_failed' => __('优惠码更新失败。', 'aksrt'),
|
||||
'coupon_disabled' => __('优惠码已禁用。', 'aksrt'),
|
||||
'coupon_enabled' => __('优惠码已启用。', 'aksrt'),
|
||||
'coupon_failed' => __('优惠码操作失败。', 'aksrt'),
|
||||
'coupon_created' => __('优惠码已创建。', 'jio'),
|
||||
'coupon_create_failed' => __('优惠码创建失败(可能重复)。', 'jio'),
|
||||
'coupon_batch_ok' => __('优惠码批量生成完成。', 'jio'),
|
||||
'coupon_batch_failed' => __('优惠码批量生成失败。', 'jio'),
|
||||
'coupon_updated' => __('优惠码已更新。', 'jio'),
|
||||
'coupon_update_failed' => __('优惠码更新失败。', 'jio'),
|
||||
'coupon_disabled' => __('优惠码已禁用。', 'jio'),
|
||||
'coupon_enabled' => __('优惠码已启用。', 'jio'),
|
||||
'coupon_failed' => __('优惠码操作失败。', 'jio'),
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* 优惠码:新建 / 批量生成弹窗与编辑表单。
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayCoupons 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
* 两个弹窗由页头按钮(data-aksrt-dialog-open)打开,交互在 admin-coupons.js;
|
||||
* 两个弹窗由页头按钮(data-jio-dialog-open)打开,交互在 admin-coupons.js;
|
||||
* 三张表单共用 render_rule_fields() 输出的优惠规则字段。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,15 +18,15 @@ trait PayCouponsForms {
|
||||
private function render_create_dialog(): void {
|
||||
$this->dialog_open(
|
||||
self::CREATE_DIALOG,
|
||||
__('新建优惠码', 'aksrt'),
|
||||
__('手动指定一个优惠码,用户结算时输入即可抵扣。', 'aksrt'),
|
||||
'aksrt_pay_create_coupon'
|
||||
__('新建优惠码', 'jio'),
|
||||
__('手动指定一个优惠码,用户结算时输入即可抵扣。', 'jio'),
|
||||
'jio_pay_create_coupon'
|
||||
);
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
$this->field(
|
||||
__('代码', 'aksrt'),
|
||||
'<input type="text" name="code" maxlength="60" required autofocus autocomplete="off" spellcheck="false" class="aksrt-native-mono">',
|
||||
__('需全站唯一,最多 60 个字符。', 'aksrt'),
|
||||
__('代码', 'jio'),
|
||||
'<input type="text" name="code" maxlength="60" required autofocus autocomplete="off" spellcheck="false" class="jio-native-mono">',
|
||||
__('需全站唯一,最多 60 个字符。', 'jio'),
|
||||
true
|
||||
);
|
||||
$this->render_rule_fields([
|
||||
@@ -38,24 +38,24 @@ trait PayCouponsForms {
|
||||
'per_user_limit' => '0',
|
||||
]);
|
||||
echo '</div>';
|
||||
$this->dialog_close(__('创建优惠码', 'aksrt'));
|
||||
$this->dialog_close(__('创建优惠码', 'jio'));
|
||||
}
|
||||
|
||||
private function render_batch_dialog(): void {
|
||||
$this->dialog_open(
|
||||
self::BATCH_DIALOG,
|
||||
__('批量生成优惠码', 'aksrt'),
|
||||
__('按相同规则随机生成一批优惠码,生成后会在页面上列出供复制。', 'aksrt'),
|
||||
'aksrt_pay_batch_coupons'
|
||||
__('批量生成优惠码', 'jio'),
|
||||
__('按相同规则随机生成一批优惠码,生成后会在页面上列出供复制。', 'jio'),
|
||||
'jio_pay_batch_coupons'
|
||||
);
|
||||
echo '<h3 class="aksrt-coupon-section">' . esc_html__('生成规则', 'aksrt') . '</h3>';
|
||||
echo '<div class="aksrt-native-form-grid aksrt-coupon-batch-grid">';
|
||||
$this->field(__('数量', 'aksrt'), '<input type="number" name="count" min="1" max="100" step="1" value="10" required autofocus>', __('单次 1–100 个。', 'aksrt'));
|
||||
$this->field(__('随机位数', 'aksrt'), '<input type="number" name="length" min="6" max="24" step="1" value="10" required>', __('6–24 位,不含前缀。', 'aksrt'));
|
||||
$this->field(__('前缀', 'aksrt'), '<input type="text" name="prefix" maxlength="36" autocomplete="off" spellcheck="false" class="aksrt-native-mono">', __('选填,如 VIP2026-。', 'aksrt'));
|
||||
echo '<h3 class="jio-coupon-section">' . esc_html__('生成规则', 'jio') . '</h3>';
|
||||
echo '<div class="jio-native-form-grid jio-coupon-batch-grid">';
|
||||
$this->field(__('数量', 'jio'), '<input type="number" name="count" min="1" max="100" step="1" value="10" required autofocus>', __('单次 1–100 个。', 'jio'));
|
||||
$this->field(__('随机位数', 'jio'), '<input type="number" name="length" min="6" max="24" step="1" value="10" required>', __('6–24 位,不含前缀。', 'jio'));
|
||||
$this->field(__('前缀', 'jio'), '<input type="text" name="prefix" maxlength="36" autocomplete="off" spellcheck="false" class="jio-native-mono">', __('选填,如 VIP2026-。', 'jio'));
|
||||
echo '</div>';
|
||||
echo '<h3 class="aksrt-coupon-section">' . esc_html__('优惠规则', 'aksrt') . '</h3>';
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<h3 class="jio-coupon-section">' . esc_html__('优惠规则', 'jio') . '</h3>';
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
$this->render_rule_fields([
|
||||
'type' => 'fixed',
|
||||
'value' => '10',
|
||||
@@ -65,7 +65,7 @@ trait PayCouponsForms {
|
||||
'per_user_limit' => '1',
|
||||
]);
|
||||
echo '</div>';
|
||||
$this->dialog_close(__('批量生成', 'aksrt'));
|
||||
$this->dialog_close(__('批量生成', 'jio'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,18 +78,18 @@ trait PayCouponsForms {
|
||||
|
||||
$title = sprintf(
|
||||
/* translators: %d coupon id */
|
||||
__('编辑优惠码 #%d', 'aksrt'),
|
||||
__('编辑优惠码 #%d', 'jio'),
|
||||
$id
|
||||
);
|
||||
$back = NativeUi::button_link(__('返回列表', 'aksrt'), $this->page_url(), false, 'dashicons-arrow-left-alt');
|
||||
$back = NativeUi::button_link(__('返回列表', 'jio'), $this->page_url(), false, 'dashicons-arrow-left-alt');
|
||||
|
||||
NativeUi::card_open($title, $back);
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" data-aksrt-coupon-form data-busy-label="' . esc_attr__('正在保存…', 'aksrt') . '">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_update_coupon" />';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" data-jio-coupon-form data-busy-label="' . esc_attr__('正在保存…', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_update_coupon" />';
|
||||
echo '<input type="hidden" name="coupon_id" value="' . esc_attr((string) $id) . '" />';
|
||||
wp_nonce_field('aksrt_pay_update_coupon_' . $id);
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
$this->field(__('代码', 'aksrt'), '<input type="text" value="' . esc_attr((string) ($row['code'] ?? '')) . '" readonly class="aksrt-native-mono" />', __('代码创建后不可修改。', 'aksrt'));
|
||||
wp_nonce_field('jio_pay_update_coupon_' . $id);
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
$this->field(__('代码', 'jio'), '<input type="text" value="' . esc_attr((string) ($row['code'] ?? '')) . '" readonly class="jio-native-mono" />', __('代码创建后不可修改。', 'jio'));
|
||||
$this->render_rule_fields([
|
||||
'type' => (string) ($row['type'] ?? 'fixed'),
|
||||
'value' => (string) ($row['value'] ?? ''),
|
||||
@@ -102,8 +102,8 @@ trait PayCouponsForms {
|
||||
'expires_at' => $expires !== '' ? str_replace(' ', 'T', substr($expires, 0, 16)) : '',
|
||||
]);
|
||||
echo '</div>';
|
||||
echo '<div class="aksrt-native-form-foot">';
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-coupon-submit>' . esc_html__('保存修改', 'aksrt') . '</button>';
|
||||
echo '<div class="jio-native-form-foot">';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-coupon-submit>' . esc_html__('保存修改', 'jio') . '</button>';
|
||||
echo '</div>';
|
||||
echo '</form>';
|
||||
NativeUi::card_close();
|
||||
@@ -119,39 +119,39 @@ trait PayCouponsForms {
|
||||
$percent = $type === 'percent';
|
||||
$currency = $this->currency_symbol();
|
||||
|
||||
$this->field(__('类型', 'aksrt'), $this->type_select($type));
|
||||
$this->field(__('类型', 'jio'), $this->type_select($type));
|
||||
$this->field(
|
||||
__('面值', 'aksrt'),
|
||||
'<span class="aksrt-coupon-inline"><input type="number" name="value" min="0" step="' . ($percent ? '1' : '0.01') . '"' . ($percent ? ' max="100"' : '')
|
||||
__('面值', 'jio'),
|
||||
'<span class="jio-coupon-inline"><input type="number" name="value" min="0" step="' . ($percent ? '1' : '0.01') . '"' . ($percent ? ' max="100"' : '')
|
||||
. ' value="' . esc_attr($v['value'] ?? '') . '" required>'
|
||||
. '<span class="aksrt-coupon-unit" data-aksrt-coupon-unit data-fixed="' . esc_attr($currency) . '" data-percent="%">' . esc_html($percent ? '%' : $currency) . '</span></span>',
|
||||
__('固定金额直接抵扣;折扣百分比填 20 表示减免 20%(0–100 的整数)。', 'aksrt')
|
||||
. '<span class="jio-coupon-unit" data-jio-coupon-unit data-fixed="' . esc_attr($currency) . '" data-percent="%">' . esc_html($percent ? '%' : $currency) . '</span></span>',
|
||||
__('固定金额直接抵扣;折扣百分比填 20 表示减免 20%(0–100 的整数)。', 'jio')
|
||||
);
|
||||
$this->field(__('最低消费', 'aksrt'), $this->money_input('min_amount', $v['min_amount'] ?? '0'), __('订单金额达到该值才可使用,0 表示无门槛。', 'aksrt'));
|
||||
$this->field(__('最高优惠', 'aksrt'), $this->money_input('max_discount', $v['max_discount'] ?? '0'), __('折扣百分比的封顶金额,0 表示不封顶。', 'aksrt'));
|
||||
$this->field(__('总次数', 'aksrt'), '<input type="number" name="usage_limit" min="0" step="1" value="' . esc_attr($v['usage_limit'] ?? '0') . '">', __('所有用户合计可用次数,0 表示不限。', 'aksrt'));
|
||||
$this->field(__('每人限次', 'aksrt'), '<input type="number" name="per_user_limit" min="0" step="1" value="' . esc_attr($v['per_user_limit'] ?? '0') . '">', __('单个用户可用次数,0 表示不限。', 'aksrt'));
|
||||
$this->field(__('最低消费', 'jio'), $this->money_input('min_amount', $v['min_amount'] ?? '0'), __('订单金额达到该值才可使用,0 表示无门槛。', 'jio'));
|
||||
$this->field(__('最高优惠', 'jio'), $this->money_input('max_discount', $v['max_discount'] ?? '0'), __('折扣百分比的封顶金额,0 表示不封顶。', 'jio'));
|
||||
$this->field(__('总次数', 'jio'), '<input type="number" name="usage_limit" min="0" step="1" value="' . esc_attr($v['usage_limit'] ?? '0') . '">', __('所有用户合计可用次数,0 表示不限。', 'jio'));
|
||||
$this->field(__('每人限次', 'jio'), '<input type="number" name="per_user_limit" min="0" step="1" value="' . esc_attr($v['per_user_limit'] ?? '0') . '">', __('单个用户可用次数,0 表示不限。', 'jio'));
|
||||
|
||||
$select = '<select name="scope_type">';
|
||||
foreach ([
|
||||
'' => __('全部商品', 'aksrt'),
|
||||
'include' => __('仅指定商品 ID', 'aksrt'),
|
||||
'exclude' => __('排除商品 ID', 'aksrt'),
|
||||
'category' => __('指定分类 ID', 'aksrt'),
|
||||
'' => __('全部商品', 'jio'),
|
||||
'include' => __('仅指定商品 ID', 'jio'),
|
||||
'exclude' => __('排除商品 ID', 'jio'),
|
||||
'category' => __('指定分类 ID', 'jio'),
|
||||
] as $k => $label) {
|
||||
$select .= sprintf('<option value="%s"%s>%s</option>', esc_attr($k), selected($v['scope_type'] ?? '', $k, false), esc_html($label));
|
||||
}
|
||||
$select .= '</select>';
|
||||
$this->field(__('适用范围', 'aksrt'), $select);
|
||||
$this->field(__('适用范围', 'jio'), $select);
|
||||
$this->field(
|
||||
__('范围 ID', 'aksrt'),
|
||||
'<input type="text" name="scope_ids" value="' . esc_attr($v['scope_ids'] ?? '') . '" placeholder="' . esc_attr__('如 12,34', 'aksrt') . '" autocomplete="off">',
|
||||
__('逗号分隔;选择「全部商品」时无需填写。', 'aksrt')
|
||||
__('范围 ID', 'jio'),
|
||||
'<input type="text" name="scope_ids" value="' . esc_attr($v['scope_ids'] ?? '') . '" placeholder="' . esc_attr__('如 12,34', 'jio') . '" autocomplete="off">',
|
||||
__('逗号分隔;选择「全部商品」时无需填写。', 'jio')
|
||||
);
|
||||
$this->field(
|
||||
__('过期时间', 'aksrt'),
|
||||
__('过期时间', 'jio'),
|
||||
'<input type="datetime-local" name="expires_at" value="' . esc_attr($v['expires_at'] ?? '') . '">',
|
||||
__('留空表示永不过期。', 'aksrt')
|
||||
__('留空表示永不过期。', 'jio')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -161,40 +161,40 @@ trait PayCouponsForms {
|
||||
|
||||
private function type_select(string $current): string {
|
||||
$html = '<select name="type">';
|
||||
foreach (['fixed' => __('固定金额', 'aksrt'), 'percent' => __('折扣百分比', 'aksrt')] as $k => $label) {
|
||||
foreach (['fixed' => __('固定金额', 'jio'), 'percent' => __('折扣百分比', 'jio')] as $k => $label) {
|
||||
$html .= sprintf('<option value="%s"%s>%s</option>', esc_attr($k), selected($current, $k, false), esc_html($label));
|
||||
}
|
||||
return $html . '</select>';
|
||||
}
|
||||
|
||||
/** 单字段单元(.aksrt-native-field);$html 为调用方已转义的控件片段。 */
|
||||
/** 单字段单元(.jio-native-field);$html 为调用方已转义的控件片段。 */
|
||||
private function field(string $label, string $html, string $hint = '', bool $wide = false): void {
|
||||
echo '<label class="aksrt-native-field' . ($wide ? ' is-wide' : '') . '">';
|
||||
echo '<span class="aksrt-native-field__label">' . esc_html($label) . '</span>';
|
||||
echo '<label class="jio-native-field' . ($wide ? ' is-wide' : '') . '">';
|
||||
echo '<span class="jio-native-field__label">' . esc_html($label) . '</span>';
|
||||
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput -- 调用方已转义。
|
||||
if ($hint !== '') {
|
||||
echo '<small class="aksrt-native-field__hint">' . esc_html($hint) . '</small>';
|
||||
echo '<small class="jio-native-field__hint">' . esc_html($hint) . '</small>';
|
||||
}
|
||||
echo '</label>';
|
||||
}
|
||||
|
||||
private function dialog_open(string $id, string $title, string $description, string $action): void {
|
||||
echo '<dialog class="aksrt-coupon-dialog" id="' . esc_attr($id) . '" aria-labelledby="' . esc_attr($id) . '-title" aria-describedby="' . esc_attr($id) . '-description">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-coupon-dialog__frame" data-aksrt-coupon-form data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">';
|
||||
echo '<dialog class="jio-coupon-dialog" id="' . esc_attr($id) . '" aria-labelledby="' . esc_attr($id) . '-title" aria-describedby="' . esc_attr($id) . '-description">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-coupon-dialog__frame" data-jio-coupon-form data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="' . esc_attr($action) . '">';
|
||||
wp_nonce_field($action);
|
||||
echo '<header class="aksrt-coupon-dialog__head"><div><h2 id="' . esc_attr($id) . '-title">' . esc_html($title) . '</h2><p id="' . esc_attr($id) . '-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="aksrt-coupon-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<div class="aksrt-coupon-dialog__body">';
|
||||
echo '<header class="jio-coupon-dialog__head"><div><h2 id="' . esc_attr($id) . '-title">' . esc_html($title) . '</h2><p id="' . esc_attr($id) . '-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="jio-coupon-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<div class="jio-coupon-dialog__body">';
|
||||
}
|
||||
|
||||
private function dialog_close(string $submit_label): void {
|
||||
echo '</div><footer class="aksrt-coupon-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-coupon-submit>' . esc_html($submit_label) . '</button></footer></form></dialog>';
|
||||
echo '</div><footer class="jio-coupon-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-coupon-submit>' . esc_html($submit_label) . '</button></footer></form></dialog>';
|
||||
}
|
||||
|
||||
private function currency_symbol(): string {
|
||||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||||
$paid = \Jio\Settings\Settings::get_paid_content_settings();
|
||||
return (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,10 @@
|
||||
* admin-post 处理器(结算 / 删除 / 批量操作)与共用的展示助手。页面样式在 assets/css/admin/income.css,
|
||||
* 批量操作、二次确认、全选与防重复提交在 assets/js/admin-income.js。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,7 +18,7 @@ class PayIncome {
|
||||
use PayIncomeList;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_income';
|
||||
public const PAGE_SLUG = 'jio_income';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -34,9 +34,9 @@ class PayIncome {
|
||||
return;
|
||||
}
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
add_action('admin_post_aksrt_pay_settle_income', [$this, 'handle_settle_income']);
|
||||
add_action('admin_post_aksrt_pay_delete_income', [$this, 'handle_delete_income']);
|
||||
add_action('admin_post_aksrt_pay_bulk_income', [$this, 'handle_bulk_income']);
|
||||
add_action('admin_post_jio_pay_settle_income', [$this, 'handle_settle_income']);
|
||||
add_action('admin_post_jio_pay_delete_income', [$this, 'handle_delete_income']);
|
||||
add_action('admin_post_jio_pay_bulk_income', [$this, 'handle_bulk_income']);
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
@@ -46,17 +46,17 @@ class PayIncome {
|
||||
}
|
||||
$css = '/assets/css/admin/income.css';
|
||||
$js = '/assets/js/admin-income.js';
|
||||
wp_enqueue_style('aksrt-admin-income', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-income', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-income', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-income', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_income_page(): void {
|
||||
$this->ensure_cap();
|
||||
NativeUi::page_open('aksrt-income-page');
|
||||
NativeUi::page_open('jio-income-page');
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\Revenue')) {
|
||||
NativeUi::header(__('创作分成', 'aksrt'), '', 'dashicons-chart-pie');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('分成模块未初始化。', 'aksrt') . '</p></div>';
|
||||
if (!class_exists('\Jio\Payment\Revenue')) {
|
||||
NativeUi::header(__('创作分成', 'jio'), '', 'dashicons-chart-pie');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('分成模块未初始化。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -72,28 +72,28 @@ class PayIncome {
|
||||
public function handle_settle_income(): void {
|
||||
$this->ensure_cap();
|
||||
$id = isset($_POST['revenue_id']) ? (int) $_POST['revenue_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_settle_income_' . $id);
|
||||
check_admin_referer('jio_pay_settle_income_' . $id);
|
||||
$operation_key = $this->request_operation_key('admin-revenue-settle');
|
||||
|
||||
$ok = $id > 0 && $operation_key !== ''
|
||||
&& \AKSRT\Payment\Revenue::get_instance()->settle_result($id, get_current_user_id(), $operation_key) instanceof \AKSRT\Ledger\LedgerResult;
|
||||
$this->redirect_back(['aksrt_pay_msg' => $ok ? 'income_settled' : 'income_failed']);
|
||||
&& \Jio\Payment\Revenue::get_instance()->settle_result($id, get_current_user_id(), $operation_key) instanceof \Jio\Ledger\LedgerResult;
|
||||
$this->redirect_back(['jio_pay_msg' => $ok ? 'income_settled' : 'income_failed']);
|
||||
}
|
||||
|
||||
/** 单条删除:只删待结算记录,已结算记录由 Revenue::delete_pending() 的状态条件挡住。 */
|
||||
public function handle_delete_income(): void {
|
||||
$this->ensure_cap();
|
||||
$id = isset($_POST['revenue_id']) ? (int) $_POST['revenue_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_delete_income_' . $id);
|
||||
check_admin_referer('jio_pay_delete_income_' . $id);
|
||||
|
||||
$deleted = $id > 0 ? \AKSRT\Payment\Revenue::get_instance()->delete_pending([$id]) : 0;
|
||||
$this->redirect_back(['aksrt_pay_msg' => $deleted > 0 ? 'income_deleted' : 'income_delete_failed', 'done' => $deleted]);
|
||||
$deleted = $id > 0 ? \Jio\Payment\Revenue::get_instance()->delete_pending([$id]) : 0;
|
||||
$this->redirect_back(['jio_pay_msg' => $deleted > 0 ? 'income_deleted' : 'income_delete_failed', 'done' => $deleted]);
|
||||
}
|
||||
|
||||
/** 批量操作:bulk_action = settle | delete,作用于勾选的记录。 */
|
||||
public function handle_bulk_income(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_pay_bulk_income');
|
||||
check_admin_referer('jio_pay_bulk_income');
|
||||
|
||||
$action = isset($_POST['bulk_action']) ? sanitize_key((string) wp_unslash($_POST['bulk_action'])) : '';
|
||||
$raw_ids = isset($_POST['revenue_ids']) ? wp_unslash($_POST['revenue_ids']) : [];
|
||||
@@ -101,17 +101,17 @@ class PayIncome {
|
||||
? array_values(array_unique(array_filter(array_map('intval', $raw_ids), static fn (int $id): bool => $id > 0)))
|
||||
: [];
|
||||
if (!in_array($action, ['settle', 'delete'], true)) {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'income_bulk_invalid']);
|
||||
$this->redirect_back(['jio_pay_msg' => 'income_bulk_invalid']);
|
||||
}
|
||||
if ($ids === []) {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'income_none_selected']);
|
||||
$this->redirect_back(['jio_pay_msg' => 'income_none_selected']);
|
||||
}
|
||||
|
||||
if ($action === 'delete') {
|
||||
$deleted = \AKSRT\Payment\Revenue::get_instance()->delete_pending($ids);
|
||||
$deleted = \Jio\Payment\Revenue::get_instance()->delete_pending($ids);
|
||||
$failed = count($ids) - $deleted;
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => $deleted === 0 ? 'income_delete_failed' : ($failed > 0 ? 'income_delete_partial' : 'income_deleted'),
|
||||
'jio_pay_msg' => $deleted === 0 ? 'income_delete_failed' : ($failed > 0 ? 'income_delete_partial' : 'income_deleted'),
|
||||
'done' => $deleted,
|
||||
'failed' => $failed,
|
||||
]);
|
||||
@@ -119,13 +119,13 @@ class PayIncome {
|
||||
|
||||
$operation_key = $this->request_operation_key('admin-revenue-settle-batch');
|
||||
$result = $operation_key !== ''
|
||||
? \AKSRT\Payment\Revenue::get_instance()->settle_batch($ids, get_current_user_id(), $operation_key)
|
||||
? \Jio\Payment\Revenue::get_instance()->settle_batch($ids, get_current_user_id(), $operation_key)
|
||||
: ['settled' => [], 'failed' => $ids];
|
||||
$settled = count($result['settled']);
|
||||
$failed = count($result['failed']);
|
||||
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => $settled === 0 ? 'income_failed' : ($failed > 0 ? 'income_batch_partial' : 'income_batch_settled'),
|
||||
'jio_pay_msg' => $settled === 0 ? 'income_failed' : ($failed > 0 ? 'income_batch_partial' : 'income_batch_settled'),
|
||||
'done' => $settled,
|
||||
'failed' => $failed,
|
||||
]);
|
||||
@@ -135,7 +135,7 @@ class PayIncome {
|
||||
private function request_operation_key(string $scope): string {
|
||||
$request_key = sanitize_text_field((string) wp_unslash($_POST['operation_key'] ?? ''));
|
||||
return wp_is_uuid($request_key)
|
||||
? \AKSRT\Ledger\OperationKey::deterministic($scope, [get_current_user_id(), strtolower($request_key)])
|
||||
? \Jio\Ledger\OperationKey::deterministic($scope, [get_current_user_id(), strtolower($request_key)])
|
||||
: '';
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ class PayIncome {
|
||||
if (is_string($referer) && $referer !== '') {
|
||||
parse_str((string) wp_parse_url($referer, PHP_URL_QUERY), $parsed);
|
||||
if (($parsed['page'] ?? '') === self::PAGE_SLUG) {
|
||||
unset($parsed['page'], $parsed['aksrt_pay_msg'], $parsed['done'], $parsed['failed']);
|
||||
unset($parsed['page'], $parsed['jio_pay_msg'], $parsed['done'], $parsed['failed']);
|
||||
$query = $parsed;
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ class PayIncome {
|
||||
|
||||
/** @return array{enabled:bool,cycle_days:int} */
|
||||
private function revenue_policy(): array {
|
||||
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_revenue_settings() : [];
|
||||
$settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_payment_revenue_settings() : [];
|
||||
return [
|
||||
'enabled' => !empty($settings['enabled']),
|
||||
'cycle_days' => max(1, (int) ($settings['cycle_days'] ?? 30)),
|
||||
@@ -174,8 +174,8 @@ class PayIncome {
|
||||
/** @return array<string, string> */
|
||||
private function status_labels(): array {
|
||||
return [
|
||||
'pending' => __('待结算', 'aksrt'),
|
||||
'settled' => __('已结算', 'aksrt'),
|
||||
'pending' => __('待结算', 'jio'),
|
||||
'settled' => __('已结算', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -191,51 +191,51 @@ class PayIncome {
|
||||
/** 分成金额:现金带两位小数与 ¥,积分取整加单位;现金与积分从不相加。 */
|
||||
private function format_amount($value, bool $is_points): string {
|
||||
if ($is_points) {
|
||||
return number_format_i18n(max(0, (int) $value)) . ' ' . __('积分', 'aksrt');
|
||||
return number_format_i18n(max(0, (int) $value)) . ' ' . __('积分', 'jio');
|
||||
}
|
||||
try {
|
||||
return '¥' . \AKSRT\Ledger\AssetAmount::balance(is_string($value) ? $value : '0')->formatted();
|
||||
return '¥' . \Jio\Ledger\AssetAmount::balance(is_string($value) ? $value : '0')->formatted();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '¥0.00';
|
||||
}
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
if (empty($_GET['aksrt_pay_msg'])) {
|
||||
if (empty($_GET['jio_pay_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_pay_msg']));
|
||||
$done = max(0, (int) ($_GET['done'] ?? 0));
|
||||
$failed = max(0, (int) ($_GET['failed'] ?? 0));
|
||||
// 码 => [文案, notice 类型]
|
||||
$map = [
|
||||
'income_settled' => [__('分成已结算,已转入作者钱包。', 'aksrt'), 'success'],
|
||||
'income_settled' => [__('分成已结算,已转入作者钱包。', 'jio'), 'success'],
|
||||
'income_batch_settled' => [sprintf(
|
||||
/* translators: %d settled count */
|
||||
__('已结算 %d 条分成记录。', 'aksrt'),
|
||||
__('已结算 %d 条分成记录。', 'jio'),
|
||||
$done
|
||||
), 'success'],
|
||||
'income_batch_partial' => [sprintf(
|
||||
/* translators: 1 settled count 2 failed count */
|
||||
__('已结算 %1$d 条,%2$d 条失败(记录状态可能已变化),可刷新后重试。', 'aksrt'),
|
||||
__('已结算 %1$d 条,%2$d 条失败(记录状态可能已变化),可刷新后重试。', 'jio'),
|
||||
$done,
|
||||
$failed
|
||||
), 'warning'],
|
||||
'income_none_selected' => [__('请先勾选待结算记录。', 'aksrt'), 'warning'],
|
||||
'income_bulk_invalid' => [__('请先选择批量操作。', 'aksrt'), 'warning'],
|
||||
'income_failed' => [__('分成结算失败:记录可能已被结算,或分成功能未启用。', 'aksrt'), 'error'],
|
||||
'income_none_selected' => [__('请先勾选待结算记录。', 'jio'), 'warning'],
|
||||
'income_bulk_invalid' => [__('请先选择批量操作。', 'jio'), 'warning'],
|
||||
'income_failed' => [__('分成结算失败:记录可能已被结算,或分成功能未启用。', 'jio'), 'error'],
|
||||
'income_deleted' => [sprintf(
|
||||
/* translators: %d deleted count */
|
||||
__('已删除 %d 条待结算记录。', 'aksrt'),
|
||||
__('已删除 %d 条待结算记录。', 'jio'),
|
||||
$done
|
||||
), 'success'],
|
||||
'income_delete_partial' => [sprintf(
|
||||
/* translators: 1 deleted count 2 skipped count */
|
||||
__('已删除 %1$d 条,%2$d 条未删除(仅待结算记录可删除,可能已被结算)。', 'aksrt'),
|
||||
__('已删除 %1$d 条,%2$d 条未删除(仅待结算记录可删除,可能已被结算)。', 'jio'),
|
||||
$done,
|
||||
$failed
|
||||
), 'warning'],
|
||||
'income_delete_failed' => [__('删除失败:仅待结算记录可删除,记录可能已被结算或不存在。', 'aksrt'), 'error'],
|
||||
'income_delete_failed' => [__('删除失败:仅待结算记录可删除,记录可能已被结算或不存在。', 'jio'), 'error'],
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
@@ -246,7 +246,7 @@ class PayIncome {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+100
-100
@@ -4,12 +4,12 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayIncome 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Payment\Revenue;
|
||||
use Jio\Payment\Revenue;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,12 +17,12 @@ trait PayIncomeList {
|
||||
|
||||
private function render_income_list_page(): void {
|
||||
NativeUi::header(
|
||||
__('创作分成', 'aksrt'),
|
||||
__('作者从付费内容订单获得的分成记录。待结算记录到期后自动结算,也可在这里提前手动结算。', 'aksrt'),
|
||||
__('创作分成', 'jio'),
|
||||
__('作者从付费内容订单获得的分成记录。待结算记录到期后自动结算,也可在这里提前手动结算。', 'jio'),
|
||||
'dashicons-chart-pie',
|
||||
[[
|
||||
'label' => __('分成配置', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/payment/revenue'),
|
||||
'label' => __('分成配置', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/payment/revenue'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
]]
|
||||
);
|
||||
@@ -65,20 +65,20 @@ trait PayIncomeList {
|
||||
$has_pending = in_array('pending', array_column($items, 'status'), true);
|
||||
|
||||
// 复选框列与批量栏始终渲染;不可用时禁用并用 title 说明原因,而不是整块隐藏。
|
||||
NativeUi::table_card_open(__('分成记录', 'aksrt'), $this->bulk_form_html($can_settle, $has_pending));
|
||||
echo '<table class="wp-list-table widefat striped aksrt-income-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('分成记录', 'aksrt') . '</caption><thead><tr>';
|
||||
NativeUi::table_card_open(__('分成记录', 'jio'), $this->bulk_form_html($can_settle, $has_pending));
|
||||
echo '<table class="wp-list-table widefat striped jio-income-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('分成记录', 'jio') . '</caption><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column">'
|
||||
. '<input type="checkbox" data-aksrt-income-check-all aria-label="' . esc_attr__('全选本页待结算记录', 'aksrt') . '"'
|
||||
. ($has_pending ? '' : ' disabled title="' . esc_attr__('本页没有待结算记录', 'aksrt') . '"') . ' /></td>';
|
||||
. '<input type="checkbox" data-jio-income-check-all aria-label="' . esc_attr__('全选本页待结算记录', 'jio') . '"'
|
||||
. ($has_pending ? '' : ' disabled title="' . esc_attr__('本页没有待结算记录', 'jio') . '"') . ' /></td>';
|
||||
foreach ([
|
||||
'record' => __('记录 / 产生时间', 'aksrt'),
|
||||
'author' => __('作者', 'aksrt'),
|
||||
'source' => __('来源文章 / 订单', 'aksrt'),
|
||||
'base' => __('订单金额 × 比例', 'aksrt'),
|
||||
'amount' => __('分成', 'aksrt'),
|
||||
'status' => __('状态', 'aksrt'),
|
||||
'actions' => __('操作', 'aksrt'),
|
||||
'record' => __('记录 / 产生时间', 'jio'),
|
||||
'author' => __('作者', 'jio'),
|
||||
'source' => __('来源文章 / 订单', 'jio'),
|
||||
'base' => __('订单金额 × 比例', 'jio'),
|
||||
'amount' => __('分成', 'jio'),
|
||||
'status' => __('状态', 'jio'),
|
||||
'actions' => __('操作', 'jio'),
|
||||
] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
@@ -86,9 +86,9 @@ trait PayIncomeList {
|
||||
|
||||
if (!$items) {
|
||||
$filtered = array_diff_key($query_args, ['status' => true]) !== [];
|
||||
echo NativeUi::empty_row(8, __('暂无分成记录', 'aksrt'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的记录,请调整或重置筛选。', 'aksrt')
|
||||
: __('付费内容订单支付成功后,作者分成会出现在这里。', 'aksrt'));
|
||||
echo NativeUi::empty_row(8, __('暂无分成记录', 'jio'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的记录,请调整或重置筛选。', 'jio')
|
||||
: __('付费内容订单支付成功后,作者分成会出现在这里。', 'jio'));
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_income_row($row, $can_settle, $policy['cycle_days']);
|
||||
@@ -112,47 +112,47 @@ trait PayIncomeList {
|
||||
echo '<tr>';
|
||||
// 已结算记录已入账到钱包,不可结算也不可删除:复选框禁用占位,保持列对齐。
|
||||
echo '<th scope="row" class="check-column">'
|
||||
. '<input type="checkbox" form="aksrt-income-bulk" name="revenue_ids[]" value="' . esc_attr((string) $id) . '"'
|
||||
. ($pending ? ' data-aksrt-income-check' : ' disabled title="' . esc_attr__('已结算记录不可操作', 'aksrt') . '"')
|
||||
. ' aria-label="' . esc_attr(sprintf(__('选择分成记录 #%d', 'aksrt'), $id)) . '" /></th>';
|
||||
. '<input type="checkbox" form="jio-income-bulk" name="revenue_ids[]" value="' . esc_attr((string) $id) . '"'
|
||||
. ($pending ? ' data-jio-income-check' : ' disabled title="' . esc_attr__('已结算记录不可操作', 'jio') . '"')
|
||||
. ' aria-label="' . esc_attr(sprintf(__('选择分成记录 #%d', 'jio'), $id)) . '" /></th>';
|
||||
|
||||
echo '<td class="column-record"><span class="aksrt-income-id">#' . esc_html((string) $id) . '</span>'
|
||||
. '<div class="aksrt-income-sub">' . esc_html((string) ($row['created_at'] ?? '')) . '</div></td>';
|
||||
echo '<td class="column-record"><span class="jio-income-id">#' . esc_html((string) $id) . '</span>'
|
||||
. '<div class="jio-income-sub">' . esc_html((string) ($row['created_at'] ?? '')) . '</div></td>';
|
||||
echo '<td class="column-author">' . NativeUi::user_cell((int) ($row['author_id'] ?? 0)) . '</td>';
|
||||
|
||||
$title = $post_id > 0 ? (string) get_the_title($post_id) : '';
|
||||
$post_html = $post_id > 0
|
||||
? '<a class="aksrt-income-post" href="' . esc_url((string) get_edit_post_link($post_id)) . '">' . esc_html($title !== '' ? $title : '#' . $post_id) . '</a>'
|
||||
: '<span class="aksrt-native-muted">—</span>';
|
||||
? '<a class="jio-income-post" href="' . esc_url((string) get_edit_post_link($post_id)) . '">' . esc_html($title !== '' ? $title : '#' . $post_id) . '</a>'
|
||||
: '<span class="jio-native-muted">—</span>';
|
||||
$buyer = $buyer_id > 0 ? get_userdata($buyer_id) : false;
|
||||
$order_url = admin_url('admin.php?page=aksrt_orders&order=' . rawurlencode($order_number));
|
||||
$order_url = admin_url('admin.php?page=jio_orders&order=' . rawurlencode($order_number));
|
||||
echo '<td class="column-source">' . $post_html // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
|
||||
. '<div class="aksrt-income-sub"><a class="aksrt-income-order" href="' . esc_url($order_url) . '">' . esc_html($order_number) . '</a>'
|
||||
. ($buyer ? ' · ' . esc_html(sprintf(__('买家 %s', 'aksrt'), $buyer->display_name)) : '')
|
||||
. '<div class="jio-income-sub"><a class="jio-income-order" href="' . esc_url($order_url) . '">' . esc_html($order_number) . '</a>'
|
||||
. ($buyer ? ' · ' . esc_html(sprintf(__('买家 %s', 'jio'), $buyer->display_name)) : '')
|
||||
. '</div></td>';
|
||||
|
||||
echo '<td class="column-base">' . esc_html($this->format_amount($row['original_amount'] ?? 0, $is_points))
|
||||
. '<div class="aksrt-income-sub">' . esc_html(sprintf(
|
||||
. '<div class="jio-income-sub">' . esc_html(sprintf(
|
||||
/* translators: 1 share ratio 2 author level label */
|
||||
__('比例 %1$d%% · %2$s', 'aksrt'),
|
||||
__('比例 %1$d%% · %2$s', 'jio'),
|
||||
(int) ($row['share_ratio'] ?? 0),
|
||||
$this->author_level_label((int) ($row['author_level'] ?? 0))
|
||||
)) . '</div></td>';
|
||||
echo '<td class="column-amount"><strong class="aksrt-native-amount' . ($pending ? '' : ' is-positive') . '">'
|
||||
echo '<td class="column-amount"><strong class="jio-native-amount' . ($pending ? '' : ' is-positive') . '">'
|
||||
. esc_html($this->format_amount($row['amount'] ?? 0, $is_points)) . '</strong>'
|
||||
. '<div class="aksrt-income-sub">' . esc_html($is_points ? __('积分钱包', 'aksrt') : __('余额钱包', 'aksrt')) . '</div></td>';
|
||||
. '<div class="jio-income-sub">' . esc_html($is_points ? __('积分钱包', 'jio') : __('余额钱包', 'jio')) . '</div></td>';
|
||||
|
||||
echo '<td class="column-status">' . NativeUi::status_badge($status, $this->status_badge_map())
|
||||
. $this->status_hint($row, $cycle_days) . '</td>';
|
||||
|
||||
echo '<td class="column-actions">';
|
||||
if ($pending) {
|
||||
echo '<div class="aksrt-income-actions">'
|
||||
echo '<div class="jio-income-actions">'
|
||||
. ($can_settle ? $this->settle_form_html($id) : '')
|
||||
. $this->delete_form_html($id)
|
||||
. '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
} else {
|
||||
echo '<span class="aksrt-native-muted">—</span>';
|
||||
echo '<span class="jio-native-muted">—</span>';
|
||||
}
|
||||
echo '</td></tr>';
|
||||
}
|
||||
@@ -161,7 +161,7 @@ trait PayIncomeList {
|
||||
private function status_hint(array $row, int $cycle_days): string {
|
||||
if (($row['status'] ?? '') === 'settled') {
|
||||
$at = (string) ($row['settled_at'] ?? '');
|
||||
return $at !== '' ? '<div class="aksrt-income-sub">' . esc_html(sprintf(__('%s 结算', 'aksrt'), $at)) . '</div>' : '';
|
||||
return $at !== '' ? '<div class="jio-income-sub">' . esc_html(sprintf(__('%s 结算', 'jio'), $at)) . '</div>' : '';
|
||||
}
|
||||
$created = (string) ($row['created_at'] ?? '');
|
||||
if ($created === '') {
|
||||
@@ -173,29 +173,29 @@ trait PayIncomeList {
|
||||
return '';
|
||||
}
|
||||
$text = $due <= new \DateTimeImmutable('now', wp_timezone())
|
||||
? __('已到期,等待自动结算', 'aksrt')
|
||||
? __('已到期,等待自动结算', 'jio')
|
||||
/* translators: %s expected settle time */
|
||||
: sprintf(__('预计 %s 自动结算', 'aksrt'), $due->format('m-d H:i'));
|
||||
return '<div class="aksrt-income-sub">' . esc_html($text) . '</div>';
|
||||
: sprintf(__('预计 %s 自动结算', 'jio'), $due->format('m-d H:i'));
|
||||
return '<div class="jio-income-sub">' . esc_html($text) . '</div>';
|
||||
}
|
||||
|
||||
private function author_level_label(int $level): string {
|
||||
return $level > 0
|
||||
/* translators: %d VIP level */
|
||||
? sprintf(__('VIP%d 作者', 'aksrt'), $level)
|
||||
: __('普通作者', 'aksrt');
|
||||
? sprintf(__('VIP%d 作者', 'jio'), $level)
|
||||
: __('普通作者', 'jio');
|
||||
}
|
||||
|
||||
/** @param array{enabled:bool,cycle_days:int} $policy */
|
||||
private function render_policy_callout(array $policy): void {
|
||||
if (!$policy['enabled']) {
|
||||
echo '<div class="aksrt-native-callout is-warning">' . esc_html__('创作分成功能未启用:不会产生新的分成记录,已有的待结算记录也无法结算。', 'aksrt')
|
||||
. ' <a href="' . esc_url(admin_url('admin.php?page=aksrt-settings#/payment/revenue')) . '">' . esc_html__('前往启用', 'aksrt') . '</a></div>';
|
||||
echo '<div class="jio-native-callout is-warning">' . esc_html__('创作分成功能未启用:不会产生新的分成记录,已有的待结算记录也无法结算。', 'jio')
|
||||
. ' <a href="' . esc_url(admin_url('admin.php?page=jio-settings#/payment/revenue')) . '">' . esc_html__('前往启用', 'jio') . '</a></div>';
|
||||
return;
|
||||
}
|
||||
echo '<div class="aksrt-native-callout">' . esc_html(sprintf(
|
||||
echo '<div class="jio-native-callout">' . esc_html(sprintf(
|
||||
/* translators: %d settlement cycle days */
|
||||
__('结算周期 %d 天:分成产生满周期后由系统每小时自动结算到作者钱包。手动结算会立即入账,不可撤销。', 'aksrt'),
|
||||
__('结算周期 %d 天:分成产生满周期后由系统每小时自动结算到作者钱包。手动结算会立即入账,不可撤销。', 'jio'),
|
||||
$policy['cycle_days']
|
||||
)) . '</div>';
|
||||
}
|
||||
@@ -206,22 +206,22 @@ trait PayIncomeList {
|
||||
$points = (array) ($stats['points'] ?? []);
|
||||
$scope = $user_id > 0
|
||||
/* translators: %d user ID */
|
||||
? sprintf(__('用户 #%d', 'aksrt'), $user_id)
|
||||
: __('全站', 'aksrt');
|
||||
? sprintf(__('用户 #%d', 'jio'), $user_id)
|
||||
: __('全站', 'jio');
|
||||
$count_hint = static function (array $bucket) use ($scope): string {
|
||||
/* translators: 1 scope 2 record count */
|
||||
return sprintf(__('%1$s · %2$s 条记录', 'aksrt'), $scope, number_format_i18n((int) ($bucket['count'] ?? 0)));
|
||||
return sprintf(__('%1$s · %2$s 条记录', 'jio'), $scope, number_format_i18n((int) ($bucket['count'] ?? 0)));
|
||||
};
|
||||
$pending_url = $this->page_url(array_filter(['status' => 'pending', 'user_id' => $user_id ?: null]));
|
||||
$settled_url = $this->page_url(array_filter(['status' => 'settled', 'user_id' => $user_id ?: null]));
|
||||
|
||||
NativeUi::stats([
|
||||
['label' => __('现金待结算', 'aksrt'), 'value' => $this->format_amount($cash['pending'] ?? '0', false), 'url' => add_query_arg('currency', 'cny', $pending_url), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('现金已结算', 'aksrt'), 'value' => $this->format_amount($cash['settled'] ?? '0', false), 'url' => add_query_arg('currency', 'cny', $settled_url), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('现金分成合计', 'aksrt'), 'value' => $this->format_amount($cash['total'] ?? '0', false), 'hint' => $count_hint($cash), 'icon' => 'dashicons-chart-area', 'tone' => 'info'],
|
||||
['label' => __('积分待结算', 'aksrt'), 'value' => $this->format_amount($points['pending'] ?? 0, true), 'url' => add_query_arg('currency', 'points', $pending_url), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('积分已结算', 'aksrt'), 'value' => $this->format_amount($points['settled'] ?? 0, true), 'url' => add_query_arg('currency', 'points', $settled_url), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('积分分成合计', 'aksrt'), 'value' => $this->format_amount($points['total'] ?? 0, true), 'hint' => $count_hint($points), 'icon' => 'dashicons-star-filled', 'tone' => 'info'],
|
||||
['label' => __('现金待结算', 'jio'), 'value' => $this->format_amount($cash['pending'] ?? '0', false), 'url' => add_query_arg('currency', 'cny', $pending_url), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('现金已结算', 'jio'), 'value' => $this->format_amount($cash['settled'] ?? '0', false), 'url' => add_query_arg('currency', 'cny', $settled_url), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('现金分成合计', 'jio'), 'value' => $this->format_amount($cash['total'] ?? '0', false), 'hint' => $count_hint($cash), 'icon' => 'dashicons-chart-area', 'tone' => 'info'],
|
||||
['label' => __('积分待结算', 'jio'), 'value' => $this->format_amount($points['pending'] ?? 0, true), 'url' => add_query_arg('currency', 'points', $pending_url), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('积分已结算', 'jio'), 'value' => $this->format_amount($points['settled'] ?? 0, true), 'url' => add_query_arg('currency', 'points', $settled_url), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('积分分成合计', 'jio'), 'value' => $this->format_amount($points['total'] ?? 0, true), 'hint' => $count_hint($points), 'icon' => 'dashicons-star-filled', 'tone' => 'info'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -232,16 +232,16 @@ trait PayIncomeList {
|
||||
*/
|
||||
private function render_status_views(array $filters, array $query_args, array $counts): void {
|
||||
$base = array_diff_key($query_args, ['status' => true]);
|
||||
$views = ['' => [__('全部', 'aksrt'), array_sum($counts)]];
|
||||
$views = ['' => [__('全部', 'jio'), array_sum($counts)]];
|
||||
foreach ($this->status_labels() as $key => $label) {
|
||||
$views[$key] = [$label, (int) ($counts[$key] ?? 0)];
|
||||
}
|
||||
|
||||
echo '<nav class="aksrt-native-tabs aksrt-income-views" aria-label="' . esc_attr__('按结算状态查看', 'aksrt') . '">';
|
||||
echo '<nav class="jio-native-tabs jio-income-views" aria-label="' . esc_attr__('按结算状态查看', 'jio') . '">';
|
||||
foreach ($views as $key => [$label, $count]) {
|
||||
$url = $this->page_url($base + ($key !== '' ? ['status' => $key] : []));
|
||||
$active = $filters['status'] === $key;
|
||||
echo '<a class="aksrt-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
echo '<a class="jio-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
. esc_html($label) . ' <span class="count">' . esc_html(number_format_i18n($count)) . '</span></a>';
|
||||
}
|
||||
echo '</nav>';
|
||||
@@ -249,30 +249,30 @@ trait PayIncomeList {
|
||||
|
||||
/** @param array<string, mixed> $filters */
|
||||
private function render_filter_card(array $filters): void {
|
||||
NativeUi::card_open('', '', false, 'aksrt-income-filter-card');
|
||||
echo '<form method="get" class="aksrt-income-filters" role="search">';
|
||||
NativeUi::card_open('', '', false, 'jio-income-filter-card');
|
||||
echo '<form method="get" class="jio-income-filters" role="search">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '" />';
|
||||
if ($filters['status'] !== '') {
|
||||
echo '<input type="hidden" name="status" value="' . esc_attr($filters['status']) . '" />';
|
||||
}
|
||||
|
||||
echo '<label class="aksrt-income-filters__search"><span class="screen-reader-text">' . esc_html__('订单号', 'aksrt') . '</span>'
|
||||
. '<input type="search" name="order_number" value="' . esc_attr($filters['order_number']) . '" placeholder="' . esc_attr__('搜索订单号', 'aksrt') . '" /></label>';
|
||||
echo $this->filter_select('currency', __('分成币种', 'aksrt'), $this->currency_options(), $filters['currency']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('order_status', __('订单状态', 'aksrt'), $this->order_status_options(), $filters['order_status']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="aksrt-income-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'aksrt') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
echo '<label class="jio-income-filters__search"><span class="screen-reader-text">' . esc_html__('订单号', 'jio') . '</span>'
|
||||
. '<input type="search" name="order_number" value="' . esc_attr($filters['order_number']) . '" placeholder="' . esc_attr__('搜索订单号', 'jio') . '" /></label>';
|
||||
echo $this->filter_select('currency', __('分成币种', 'jio'), $this->currency_options(), $filters['currency']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('order_status', __('订单状态', 'jio'), $this->order_status_options(), $filters['order_status']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="jio-income-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'jio') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
. '<span aria-hidden="true">–</span>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'aksrt') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'jio') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '</span>';
|
||||
foreach (['user_id' => __('作者 ID', 'aksrt'), 'post_id' => __('文章 ID', 'aksrt')] as $name => $label) {
|
||||
echo '<label><span class="screen-reader-text">' . esc_html($label) . '</span><input type="number" min="1" name="' . esc_attr($name) . '" class="aksrt-income-filters__id" value="'
|
||||
foreach (['user_id' => __('作者 ID', 'jio'), 'post_id' => __('文章 ID', 'jio')] as $name => $label) {
|
||||
echo '<label><span class="screen-reader-text">' . esc_html($label) . '</span><input type="number" min="1" name="' . esc_attr($name) . '" class="jio-income-filters__id" value="'
|
||||
. esc_attr($filters[$name] > 0 ? (string) $filters[$name] : '') . '" placeholder="' . esc_attr($label) . '" /></label>';
|
||||
}
|
||||
echo '<span class="aksrt-income-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '<span class="jio-income-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if (array_diff_key($this->list_query_args($filters), ['status' => true]) !== []) {
|
||||
$reset = $this->page_url($filters['status'] !== '' ? ['status' => $filters['status']] : []);
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'aksrt') . '</a>';
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'jio') . '</a>';
|
||||
}
|
||||
echo '</span></form>';
|
||||
NativeUi::card_close();
|
||||
@@ -290,60 +290,60 @@ trait PayIncomeList {
|
||||
/** @return array<string, string> */
|
||||
private function currency_options(): array {
|
||||
return [
|
||||
'' => __('全部币种', 'aksrt'),
|
||||
'cny' => __('现金(CNY)', 'aksrt'),
|
||||
'points' => __('积分', 'aksrt'),
|
||||
'' => __('全部币种', 'jio'),
|
||||
'cny' => __('现金(CNY)', 'jio'),
|
||||
'points' => __('积分', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
private function order_status_options(): array {
|
||||
return ['' => __('全部订单状态', 'aksrt')] + \AKSRT\Core\DomainModel::order_status_labels();
|
||||
return ['' => __('全部订单状态', 'jio')] + \Jio\Core\DomainModel::order_status_labels();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量操作栏:已选计数 + 「批量操作」下拉 + 应用。表格里的复选框通过 form="aksrt-income-bulk" 关联进来。
|
||||
* 批量操作栏:已选计数 + 「批量操作」下拉 + 应用。表格里的复选框通过 form="jio-income-bulk" 关联进来。
|
||||
* 各操作的确认文案挂在 option 的 data-confirm 上,由 admin-income.js 按所选操作取用。
|
||||
*/
|
||||
private function bulk_form_html(bool $can_settle, bool $has_pending): string {
|
||||
$settle_attrs = $can_settle
|
||||
? ' data-confirm="' . esc_attr__('确认立即结算选中的 {count} 条分成?金额会转入作者钱包,不可撤销。', 'aksrt') . '"'
|
||||
? ' data-confirm="' . esc_attr__('确认立即结算选中的 {count} 条分成?金额会转入作者钱包,不可撤销。', 'jio') . '"'
|
||||
: ' disabled';
|
||||
$settle_label = $can_settle ? __('立即结算', 'aksrt') : __('立即结算(分成功能未启用)', 'aksrt');
|
||||
$settle_label = $can_settle ? __('立即结算', 'jio') : __('立即结算(分成功能未启用)', 'jio');
|
||||
|
||||
return '<form id="aksrt-income-bulk" class="aksrt-native-bulk aksrt-income-bulk" method="post" action="' . esc_url(admin_url('admin-post.php')) . '">'
|
||||
. '<input type="hidden" name="action" value="aksrt_pay_bulk_income" />'
|
||||
return '<form id="jio-income-bulk" class="jio-native-bulk jio-income-bulk" method="post" action="' . esc_url(admin_url('admin-post.php')) . '">'
|
||||
. '<input type="hidden" name="action" value="jio_pay_bulk_income" />'
|
||||
. $this->operation_key_field()
|
||||
. wp_nonce_field('aksrt_pay_bulk_income', '_wpnonce', true, false)
|
||||
. '<span class="aksrt-income-bulk__count" data-aksrt-income-selected aria-live="polite">'
|
||||
. esc_html($has_pending ? __('未选择记录', 'aksrt') : __('本页没有待结算记录', 'aksrt')) . '</span>'
|
||||
. '<label class="screen-reader-text" for="aksrt-income-bulk-action">' . esc_html__('批量操作', 'aksrt') . '</label>'
|
||||
. '<select name="bulk_action" id="aksrt-income-bulk-action" data-aksrt-income-bulk-action>'
|
||||
. '<option value="">' . esc_html__('批量操作', 'aksrt') . '</option>'
|
||||
. wp_nonce_field('jio_pay_bulk_income', '_wpnonce', true, false)
|
||||
. '<span class="jio-income-bulk__count" data-jio-income-selected aria-live="polite">'
|
||||
. esc_html($has_pending ? __('未选择记录', 'jio') : __('本页没有待结算记录', 'jio')) . '</span>'
|
||||
. '<label class="screen-reader-text" for="jio-income-bulk-action">' . esc_html__('批量操作', 'jio') . '</label>'
|
||||
. '<select name="bulk_action" id="jio-income-bulk-action" data-jio-income-bulk-action>'
|
||||
. '<option value="">' . esc_html__('批量操作', 'jio') . '</option>'
|
||||
. '<option value="settle"' . $settle_attrs . '>' . esc_html($settle_label) . '</option>'
|
||||
. '<option value="delete" data-confirm="' . esc_attr__('确认删除选中的 {count} 条待结算分成?删除后作者不会获得这些分成,不可恢复。', 'aksrt') . '">' . esc_html__('删除', 'aksrt') . '</option>'
|
||||
. '<option value="delete" data-confirm="' . esc_attr__('确认删除选中的 {count} 条待结算分成?删除后作者不会获得这些分成,不可恢复。', 'jio') . '">' . esc_html__('删除', 'jio') . '</option>'
|
||||
. '</select>'
|
||||
. '<button type="submit" class="button" disabled data-aksrt-income-bulk-submit>' . esc_html__('应用', 'aksrt') . '</button>'
|
||||
. '<button type="submit" class="button" disabled data-jio-income-bulk-submit>' . esc_html__('应用', 'jio') . '</button>'
|
||||
. '</form>';
|
||||
}
|
||||
|
||||
private function delete_form_html(int $id): string {
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-income-action"'
|
||||
. ' data-aksrt-confirm="' . esc_attr(sprintf(__('确认删除待结算分成记录 #%d?删除后作者不会获得这笔分成,不可恢复。', 'aksrt'), $id)) . '">'
|
||||
. '<input type="hidden" name="action" value="aksrt_pay_delete_income" />'
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-income-action"'
|
||||
. ' data-jio-confirm="' . esc_attr(sprintf(__('确认删除待结算分成记录 #%d?删除后作者不会获得这笔分成,不可恢复。', 'jio'), $id)) . '">'
|
||||
. '<input type="hidden" name="action" value="jio_pay_delete_income" />'
|
||||
. '<input type="hidden" name="revenue_id" value="' . esc_attr((string) $id) . '" />'
|
||||
. wp_nonce_field('aksrt_pay_delete_income_' . $id, '_wpnonce', true, false)
|
||||
. '<button type="submit" class="button button-small is-danger">' . esc_html__('删除', 'aksrt') . '</button></form>';
|
||||
. wp_nonce_field('jio_pay_delete_income_' . $id, '_wpnonce', true, false)
|
||||
. '<button type="submit" class="button button-small is-danger">' . esc_html__('删除', 'jio') . '</button></form>';
|
||||
}
|
||||
|
||||
private function settle_form_html(int $id): string {
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-income-action"'
|
||||
. ' data-aksrt-confirm="' . esc_attr(sprintf(__('确认立即结算分成记录 #%d?金额会转入作者钱包,不可撤销。', 'aksrt'), $id)) . '">'
|
||||
. '<input type="hidden" name="action" value="aksrt_pay_settle_income" />'
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-income-action"'
|
||||
. ' data-jio-confirm="' . esc_attr(sprintf(__('确认立即结算分成记录 #%d?金额会转入作者钱包,不可撤销。', 'jio'), $id)) . '">'
|
||||
. '<input type="hidden" name="action" value="jio_pay_settle_income" />'
|
||||
. '<input type="hidden" name="revenue_id" value="' . esc_attr((string) $id) . '" />'
|
||||
. $this->operation_key_field()
|
||||
. wp_nonce_field('aksrt_pay_settle_income_' . $id, '_wpnonce', true, false)
|
||||
. '<button type="submit" class="button button-small">' . esc_html__('立即结算', 'aksrt') . '</button></form>';
|
||||
. wp_nonce_field('jio_pay_settle_income_' . $id, '_wpnonce', true, false)
|
||||
. '<button type="submit" class="button button-small">' . esc_html__('立即结算', 'jio') . '</button></form>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class PayMenus {
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const MENU_SLUG = 'aksrt_pay';
|
||||
public const MENU_SLUG = 'jio_pay';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -42,85 +42,85 @@ class PayMenus {
|
||||
'slug' => self::MENU_SLUG,
|
||||
'icon' => 'dashicons-cart',
|
||||
'desc' => '',
|
||||
'title' => __('概览', 'aksrt'),
|
||||
'title' => __('概览', 'jio'),
|
||||
'callback' => [$this, 'render_overview_page'],
|
||||
],
|
||||
'orders' => [
|
||||
'slug' => 'aksrt_orders',
|
||||
'slug' => 'jio_orders',
|
||||
'icon' => 'dashicons-list-view',
|
||||
'desc' => __('收款确认、关闭与退款标记', 'aksrt'),
|
||||
'title' => __('订单明细', 'aksrt'),
|
||||
'desc' => __('收款确认、关闭与退款标记', 'jio'),
|
||||
'title' => __('订单明细', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\Admin\Native\PayOrders')) {
|
||||
if (class_exists('Jio\Admin\Native\PayOrders')) {
|
||||
PayOrders::get_instance()->render_orders_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('订单模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('订单模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'products' => [
|
||||
'slug' => 'aksrt_pay_products',
|
||||
'slug' => 'jio_pay_products',
|
||||
'icon' => 'dashicons-media-document',
|
||||
'desc' => __('付费文章定价与销量', 'aksrt'),
|
||||
'title' => __('付费文章', 'aksrt'),
|
||||
'desc' => __('付费文章定价与销量', 'jio'),
|
||||
'title' => __('付费文章', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayProducts')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayProducts')) {
|
||||
PayProducts::get_instance()->render_products_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('付费文章模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('付费文章模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'coupons' => [
|
||||
'slug' => 'aksrt_coupons',
|
||||
'slug' => 'jio_coupons',
|
||||
'icon' => 'dashicons-tickets-alt',
|
||||
'desc' => __('创建与批量生成优惠码', 'aksrt'),
|
||||
'title' => __('优惠码', 'aksrt'),
|
||||
'desc' => __('创建与批量生成优惠码', 'jio'),
|
||||
'title' => __('优惠码', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayCoupons')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayCoupons')) {
|
||||
PayCoupons::get_instance()->render_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('优惠码模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('优惠码模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'charge_cards' => [
|
||||
'slug' => 'aksrt_charge_cards',
|
||||
'slug' => 'jio_charge_cards',
|
||||
'icon' => 'dashicons-id-alt',
|
||||
'desc' => __('充值卡密生成与导出', 'aksrt'),
|
||||
'title' => __('卡密管理', 'aksrt'),
|
||||
'desc' => __('充值卡密生成与导出', 'jio'),
|
||||
'title' => __('卡密管理', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayChargeCards')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayChargeCards')) {
|
||||
PayChargeCards::get_instance()->render_charge_cards_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('卡密模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('卡密模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'income' => [
|
||||
'slug' => 'aksrt_income',
|
||||
'slug' => 'jio_income',
|
||||
'icon' => 'dashicons-chart-pie',
|
||||
'desc' => __('创作者分成结算', 'aksrt'),
|
||||
'title' => __('创作分成', 'aksrt'),
|
||||
'desc' => __('创作者分成结算', 'jio'),
|
||||
'title' => __('创作分成', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayIncome')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayIncome')) {
|
||||
PayIncome::get_instance()->render_income_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('分成模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('分成模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
'withdraw' => [
|
||||
'slug' => 'aksrt_withdraw',
|
||||
'slug' => 'jio_withdraw',
|
||||
'icon' => 'dashicons-money-alt',
|
||||
'desc' => __('提现申请审核与打款', 'aksrt'),
|
||||
'title' => __('提现审核', 'aksrt'),
|
||||
'desc' => __('提现申请审核与打款', 'jio'),
|
||||
'title' => __('提现审核', 'jio'),
|
||||
'callback' => static function (): void {
|
||||
if (class_exists('AKSRT\\Admin\\Native\\PayWithdrawals')) {
|
||||
if (class_exists('Jio\\Admin\\Native\\PayWithdrawals')) {
|
||||
PayWithdrawals::get_instance()->render_withdrawals_page();
|
||||
return;
|
||||
}
|
||||
wp_die(esc_html__('提现模块未初始化。', 'aksrt'));
|
||||
wp_die(esc_html__('提现模块未初始化。', 'jio'));
|
||||
},
|
||||
],
|
||||
];
|
||||
@@ -132,8 +132,8 @@ class PayMenus {
|
||||
}
|
||||
|
||||
add_menu_page(
|
||||
__('支付中心', 'aksrt'),
|
||||
__('支付中心', 'aksrt'),
|
||||
__('支付中心', 'jio'),
|
||||
__('支付中心', 'jio'),
|
||||
self::CAPABILITY,
|
||||
self::MENU_SLUG,
|
||||
[$this, 'render_overview_page'],
|
||||
@@ -160,20 +160,20 @@ class PayMenus {
|
||||
|
||||
$page = isset($_GET['page']) ? sanitize_key((string) wp_unslash($_GET['page'])) : '';
|
||||
|
||||
if ($page !== 'aksrt_withdraw' && class_exists('\AKSRT\Payment\Withdrawal')) {
|
||||
$pending = (int) \AKSRT\Payment\Withdrawal::get_instance()->get_count(['status' => \AKSRT\Payment\Withdrawal::STATUS_PENDING]);
|
||||
$processing = (int) \AKSRT\Payment\Withdrawal::get_instance()->get_count(['status' => \AKSRT\Payment\Withdrawal::STATUS_PROCESSING]);
|
||||
if ($page !== 'jio_withdraw' && class_exists('\Jio\Payment\Withdrawal')) {
|
||||
$pending = (int) \Jio\Payment\Withdrawal::get_instance()->get_count(['status' => \Jio\Payment\Withdrawal::STATUS_PENDING]);
|
||||
$processing = (int) \Jio\Payment\Withdrawal::get_instance()->get_count(['status' => \Jio\Payment\Withdrawal::STATUS_PROCESSING]);
|
||||
$active = $pending + $processing;
|
||||
if ($active > 0) {
|
||||
$url = admin_url('admin.php?page=aksrt_withdraw&status=pending');
|
||||
$url = admin_url('admin.php?page=jio_withdraw&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible">';
|
||||
echo '<h3>' . esc_html__('提现申请待处理', 'aksrt') . '</h3>';
|
||||
echo '<h3>' . esc_html__('提现申请待处理', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(
|
||||
/* translators: %d: open withdrawal count */
|
||||
__('您有 %d 个提现申请待处理', 'aksrt'),
|
||||
__('您有 %d 个提现申请待处理', 'jio'),
|
||||
$active
|
||||
)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* PSR-4 要求与宿主类同目录);这里保留入口、admin-post 处理器与两个视图共用的展示助手。
|
||||
* 页面样式在 assets/css/admin/orders.css,危险操作确认在 assets/js/admin-orders.js。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use Jio\Core\DomainModel;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -21,7 +21,7 @@ class PayOrders {
|
||||
use PayOrdersDetail;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_orders';
|
||||
public const PAGE_SLUG = 'jio_orders';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -40,11 +40,11 @@ class PayOrders {
|
||||
return;
|
||||
}
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
add_action('admin_post_aksrt_pay_update_order_status', [$this, 'handle_update_order_status']);
|
||||
add_action('admin_post_aksrt_pay_update_order_note', [$this, 'handle_update_order_note']);
|
||||
add_action('admin_post_aksrt_pay_clear_closed_orders', [$this, 'handle_clear_closed_orders']);
|
||||
add_action('admin_post_aksrt_pay_delete_order', [$this, 'handle_delete_order']);
|
||||
add_action('admin_post_aksrt_pay_refund_order', [$this, 'handle_refund_order']);
|
||||
add_action('admin_post_jio_pay_update_order_status', [$this, 'handle_update_order_status']);
|
||||
add_action('admin_post_jio_pay_update_order_note', [$this, 'handle_update_order_note']);
|
||||
add_action('admin_post_jio_pay_clear_closed_orders', [$this, 'handle_clear_closed_orders']);
|
||||
add_action('admin_post_jio_pay_delete_order', [$this, 'handle_delete_order']);
|
||||
add_action('admin_post_jio_pay_refund_order', [$this, 'handle_refund_order']);
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
@@ -54,17 +54,17 @@ class PayOrders {
|
||||
}
|
||||
$css = '/assets/css/admin/orders.css';
|
||||
$js = '/assets/js/admin-orders.js';
|
||||
wp_enqueue_style('aksrt-admin-orders', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-orders', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-orders', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-orders', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_orders_page(): void {
|
||||
$this->ensure_cap();
|
||||
NativeUi::page_open('aksrt-orders-page');
|
||||
NativeUi::page_open('jio-orders-page');
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
NativeUi::header(__('订单明细', 'aksrt'), '', 'dashicons-list-view');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'aksrt') . '</p></div>';
|
||||
if (!class_exists('\Jio\Payment\OrderRepository')) {
|
||||
NativeUi::header(__('订单明细', 'jio'), '', 'dashicons-list-view');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -87,13 +87,13 @@ class PayOrders {
|
||||
$this->ensure_cap();
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
$new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
|
||||
check_admin_referer('aksrt_pay_update_order_status_' . $order_number);
|
||||
check_admin_referer('jio_pay_update_order_status_' . $order_number);
|
||||
|
||||
$ok = false;
|
||||
if ($order_number !== '' && $new_status !== '' && class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$ok = \AKSRT\Payment\OrderRepository::get_instance()->update_status($order_number, $new_status);
|
||||
if ($order_number !== '' && $new_status !== '' && class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$ok = \Jio\Payment\OrderRepository::get_instance()->update_status($order_number, $new_status);
|
||||
}
|
||||
$this->redirect_back(['aksrt_pay_msg' => $ok ? 'order_updated' : 'order_failed']);
|
||||
$this->redirect_back(['jio_pay_msg' => $ok ? 'order_updated' : 'order_failed']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,11 +105,11 @@ class PayOrders {
|
||||
public function handle_refund_order(): void {
|
||||
$this->ensure_cap();
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
check_admin_referer('aksrt_pay_refund_order_' . $order_number);
|
||||
check_admin_referer('jio_pay_refund_order_' . $order_number);
|
||||
|
||||
$code = 'order_refund_failed';
|
||||
if ($order_number !== '' && class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$repo = \AKSRT\Payment\OrderRepository::get_instance();
|
||||
if ($order_number !== '' && class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$repo = \Jio\Payment\OrderRepository::get_instance();
|
||||
$order = $repo->get($order_number);
|
||||
if (!is_array($order)) {
|
||||
$code = 'order_refund_missing';
|
||||
@@ -118,37 +118,37 @@ class PayOrders {
|
||||
} else {
|
||||
$ok = $repo->update_status($order_number, DomainModel::ORDER_STATUS_REFUNDED, [
|
||||
'allow_refund' => true,
|
||||
'note' => __('管理员在订单明细中标记退款', 'aksrt'),
|
||||
'note' => __('管理员在订单明细中标记退款', 'jio'),
|
||||
]);
|
||||
$code = $ok ? 'order_refunded' : 'order_refund_failed';
|
||||
}
|
||||
}
|
||||
$this->redirect_back(['aksrt_pay_msg' => $code]);
|
||||
$this->redirect_back(['jio_pay_msg' => $code]);
|
||||
}
|
||||
|
||||
public function handle_update_order_note(): void {
|
||||
$this->ensure_cap();
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
$admin_note = isset($_POST['admin_note']) ? sanitize_textarea_field((string) wp_unslash($_POST['admin_note'])) : '';
|
||||
check_admin_referer('aksrt_pay_update_order_note_' . $order_number);
|
||||
check_admin_referer('jio_pay_update_order_note_' . $order_number);
|
||||
|
||||
$ok = false;
|
||||
if ($order_number !== '' && class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$ok = \AKSRT\Payment\OrderRepository::get_instance()->update_admin_note($order_number, $admin_note);
|
||||
if ($order_number !== '' && class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$ok = \Jio\Payment\OrderRepository::get_instance()->update_admin_note($order_number, $admin_note);
|
||||
}
|
||||
$this->redirect_back(['aksrt_pay_msg' => $ok ? 'order_note_updated' : 'order_note_failed']);
|
||||
$this->redirect_back(['jio_pay_msg' => $ok ? 'order_note_updated' : 'order_note_failed']);
|
||||
}
|
||||
|
||||
public function handle_clear_closed_orders(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_pay_clear_closed_orders');
|
||||
check_admin_referer('jio_pay_clear_closed_orders');
|
||||
$days = isset($_POST['days']) ? max(1, min(3650, (int) $_POST['days'])) : 14;
|
||||
$deleted = 0;
|
||||
if (class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$deleted = \AKSRT\Payment\OrderRepository::get_instance()->clear_closed_orders($days);
|
||||
if (class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$deleted = \Jio\Payment\OrderRepository::get_instance()->clear_closed_orders($days);
|
||||
}
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => $deleted > 0 ? 'orders_cleared' : 'orders_clear_empty',
|
||||
'jio_pay_msg' => $deleted > 0 ? 'orders_cleared' : 'orders_clear_empty',
|
||||
'cleared' => $deleted,
|
||||
], true);
|
||||
}
|
||||
@@ -156,10 +156,10 @@ class PayOrders {
|
||||
public function handle_delete_order(): void {
|
||||
$this->ensure_cap();
|
||||
$order_number = isset($_POST['order_number']) ? sanitize_text_field((string) wp_unslash($_POST['order_number'])) : '';
|
||||
check_admin_referer('aksrt_pay_delete_order_' . $order_number);
|
||||
check_admin_referer('jio_pay_delete_order_' . $order_number);
|
||||
$ok = false;
|
||||
if ($order_number !== '' && class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
$repo = \AKSRT\Payment\OrderRepository::get_instance();
|
||||
if ($order_number !== '' && class_exists('\Jio\Payment\OrderRepository')) {
|
||||
$repo = \Jio\Payment\OrderRepository::get_instance();
|
||||
$order = $repo->get($order_number);
|
||||
// 只允许硬删已关闭订单。
|
||||
if (is_array($order) && (string) ($order['status'] ?? '') === DomainModel::ORDER_STATUS_CLOSED) {
|
||||
@@ -167,7 +167,7 @@ class PayOrders {
|
||||
}
|
||||
}
|
||||
// 删除成功后详情已不存在,回到列表;失败时留在原处。
|
||||
$this->redirect_back(['aksrt_pay_msg' => $ok ? 'order_deleted' : 'order_delete_failed'], $ok);
|
||||
$this->redirect_back(['jio_pay_msg' => $ok ? 'order_deleted' : 'order_delete_failed'], $ok);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +180,7 @@ class PayOrders {
|
||||
if (is_string($referer) && $referer !== '') {
|
||||
parse_str((string) wp_parse_url($referer, PHP_URL_QUERY), $parsed);
|
||||
if (($parsed['page'] ?? '') === self::PAGE_SLUG) {
|
||||
unset($parsed['page'], $parsed['aksrt_pay_msg'], $parsed['cleared']);
|
||||
unset($parsed['page'], $parsed['jio_pay_msg'], $parsed['cleared']);
|
||||
if ($to_list) {
|
||||
unset($parsed['order']);
|
||||
}
|
||||
@@ -198,12 +198,12 @@ class PayOrders {
|
||||
/** 当前状态可执行的动作按钮;没有可执行动作时返回空串。 */
|
||||
private function order_actions_html(string $order_number, string $status, bool $small = true): string {
|
||||
$labels = [
|
||||
DomainModel::ORDER_STATUS_PAID => __('确认收款', 'aksrt'),
|
||||
DomainModel::ORDER_STATUS_CLOSED => __('关闭订单', 'aksrt'),
|
||||
DomainModel::ORDER_STATUS_PAID => __('确认收款', 'jio'),
|
||||
DomainModel::ORDER_STATUS_CLOSED => __('关闭订单', 'jio'),
|
||||
];
|
||||
$confirms = [
|
||||
DomainModel::ORDER_STATUS_PAID => __('确认将该订单标记为已支付?系统会立即为用户发放对应权益。', 'aksrt'),
|
||||
DomainModel::ORDER_STATUS_CLOSED => __('确认关闭该订单?关闭后用户无法继续支付。', 'aksrt'),
|
||||
DomainModel::ORDER_STATUS_PAID => __('确认将该订单标记为已支付?系统会立即为用户发放对应权益。', 'jio'),
|
||||
DomainModel::ORDER_STATUS_CLOSED => __('确认关闭该订单?关闭后用户无法继续支付。', 'jio'),
|
||||
];
|
||||
|
||||
$html = '';
|
||||
@@ -213,8 +213,8 @@ class PayOrders {
|
||||
continue;
|
||||
}
|
||||
$html .= $this->action_form(
|
||||
'aksrt_pay_update_order_status',
|
||||
'aksrt_pay_update_order_status_' . $order_number,
|
||||
'jio_pay_update_order_status',
|
||||
'jio_pay_update_order_status_' . $order_number,
|
||||
['order_number' => $order_number, 'new_status' => $target],
|
||||
$labels[$target],
|
||||
$confirms[$target],
|
||||
@@ -225,22 +225,22 @@ class PayOrders {
|
||||
// 已支付是已退款的唯一前置状态。
|
||||
if ($status === DomainModel::ORDER_STATUS_PAID) {
|
||||
$html .= $this->action_form(
|
||||
'aksrt_pay_refund_order',
|
||||
'aksrt_pay_refund_order_' . $order_number,
|
||||
'jio_pay_refund_order',
|
||||
'jio_pay_refund_order_' . $order_number,
|
||||
['order_number' => $order_number],
|
||||
__('标记退款', 'aksrt'),
|
||||
__('只会把订单标记为已退款,不会自动原路退回款项。确认继续?此操作不可撤销。', 'aksrt'),
|
||||
__('标记退款', 'jio'),
|
||||
__('只会把订单标记为已退款,不会自动原路退回款项。确认继续?此操作不可撤销。', 'jio'),
|
||||
true,
|
||||
$small
|
||||
);
|
||||
}
|
||||
if ($status === DomainModel::ORDER_STATUS_CLOSED) {
|
||||
$html .= $this->action_form(
|
||||
'aksrt_pay_delete_order',
|
||||
'aksrt_pay_delete_order_' . $order_number,
|
||||
'jio_pay_delete_order',
|
||||
'jio_pay_delete_order_' . $order_number,
|
||||
['order_number' => $order_number],
|
||||
__('删除', 'aksrt'),
|
||||
__('确认永久删除此订单?不可恢复。', 'aksrt'),
|
||||
__('删除', 'jio'),
|
||||
__('确认永久删除此订单?不可恢复。', 'jio'),
|
||||
true,
|
||||
$small
|
||||
);
|
||||
@@ -249,13 +249,13 @@ class PayOrders {
|
||||
}
|
||||
|
||||
/**
|
||||
* 单按钮 POST 表单。确认文案放在 data-aksrt-confirm,由 admin-orders.js 拦截提交。
|
||||
* 单按钮 POST 表单。确认文案放在 data-jio-confirm,由 admin-orders.js 拦截提交。
|
||||
*
|
||||
* @param array<string, string> $fields
|
||||
*/
|
||||
private function action_form(string $action, string $nonce_action, array $fields, string $label, string $confirm = '', bool $danger = false, bool $small = true): string {
|
||||
$html = '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-orders-action"'
|
||||
. ($confirm !== '' ? ' data-aksrt-confirm="' . esc_attr($confirm) . '"' : '') . '>';
|
||||
$html = '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-orders-action"'
|
||||
. ($confirm !== '' ? ' data-jio-confirm="' . esc_attr($confirm) . '"' : '') . '>';
|
||||
$html .= '<input type="hidden" name="action" value="' . esc_attr($action) . '" />';
|
||||
foreach ($fields as $name => $value) {
|
||||
$html .= '<input type="hidden" name="' . esc_attr($name) . '" value="' . esc_attr($value) . '" />';
|
||||
@@ -312,11 +312,11 @@ class PayOrders {
|
||||
private function payment_method_label(string $method): string {
|
||||
if ($this->payment_labels === null) {
|
||||
$this->payment_labels = [
|
||||
'points' => __('积分', 'aksrt'),
|
||||
'free' => __('免费', 'aksrt'),
|
||||
'points' => __('积分', 'jio'),
|
||||
'free' => __('免费', 'jio'),
|
||||
];
|
||||
if (class_exists('\AKSRT\Payment\Gateway')) {
|
||||
foreach (\AKSRT\Payment\Gateway::get_instance()->get_all() as $id => $gateway) {
|
||||
if (class_exists('\Jio\Payment\Gateway')) {
|
||||
foreach (\Jio\Payment\Gateway::get_instance()->get_all() as $id => $gateway) {
|
||||
$this->payment_labels[(string) $id] = $gateway->get_name();
|
||||
}
|
||||
}
|
||||
@@ -327,7 +327,7 @@ class PayOrders {
|
||||
/** @return string[] */
|
||||
private function third_party_gateway_ids(): array {
|
||||
return array_values(array_diff(
|
||||
array_keys(\AKSRT\Payment\Gateway::get_instance()->get_all()),
|
||||
array_keys(\Jio\Payment\Gateway::get_instance()->get_all()),
|
||||
['balance', 'points']
|
||||
));
|
||||
}
|
||||
@@ -340,7 +340,7 @@ class PayOrders {
|
||||
/** @param array<string,mixed> $order */
|
||||
private function order_amount(array $order): string {
|
||||
if ($this->is_points_order($order)) {
|
||||
return max(0, (int) ($order['amount'] ?? 0)) . ' ' . __('积分', 'aksrt');
|
||||
return max(0, (int) ($order['amount'] ?? 0)) . ' ' . __('积分', 'jio');
|
||||
}
|
||||
return $this->cash_amount((string) ($order['amount'] ?? '0'), (string) ($order['currency'] ?? ''));
|
||||
}
|
||||
@@ -351,35 +351,35 @@ class PayOrders {
|
||||
|
||||
private function cash_decimal(string $value): string {
|
||||
try {
|
||||
return \AKSRT\Ledger\AssetAmount::balance($value)->decimal();
|
||||
return \Jio\Ledger\AssetAmount::balance($value)->decimal();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '0.00';
|
||||
}
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
if (empty($_GET['aksrt_pay_msg'])) {
|
||||
if (empty($_GET['jio_pay_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_pay_msg']));
|
||||
// 码 => [文案, 是否失败]
|
||||
$map = [
|
||||
'order_updated' => [__('订单状态已更新。', 'aksrt'), false],
|
||||
'order_failed' => [__('订单状态更新失败,订单可能已被其他操作改变,请刷新后重试。', 'aksrt'), true],
|
||||
'order_refunded' => [__('订单已标记为已退款。款项请在支付渠道后台原路退回。', 'aksrt'), false],
|
||||
'order_refund_failed' => [__('订单标记退款失败。', 'aksrt'), true],
|
||||
'order_refund_missing' => [__('订单不存在。', 'aksrt'), true],
|
||||
'order_refund_not_paid' => [__('仅已支付订单可以标记退款。', 'aksrt'), true],
|
||||
'order_note_updated' => [__('运营备注已保存。', 'aksrt'), false],
|
||||
'order_note_failed' => [__('运营备注保存失败。', 'aksrt'), true],
|
||||
'order_updated' => [__('订单状态已更新。', 'jio'), false],
|
||||
'order_failed' => [__('订单状态更新失败,订单可能已被其他操作改变,请刷新后重试。', 'jio'), true],
|
||||
'order_refunded' => [__('订单已标记为已退款。款项请在支付渠道后台原路退回。', 'jio'), false],
|
||||
'order_refund_failed' => [__('订单标记退款失败。', 'jio'), true],
|
||||
'order_refund_missing' => [__('订单不存在。', 'jio'), true],
|
||||
'order_refund_not_paid' => [__('仅已支付订单可以标记退款。', 'jio'), true],
|
||||
'order_note_updated' => [__('运营备注已保存。', 'jio'), false],
|
||||
'order_note_failed' => [__('运营备注保存失败。', 'jio'), true],
|
||||
'orders_cleared' => [sprintf(
|
||||
/* translators: %d deleted count */
|
||||
__('已清理 %d 条关闭订单。', 'aksrt'),
|
||||
__('已清理 %d 条关闭订单。', 'jio'),
|
||||
max(0, (int) ($_GET['cleared'] ?? 0))
|
||||
), false],
|
||||
'orders_clear_empty' => [__('没有可清理的关闭订单。', 'aksrt'), false],
|
||||
'order_deleted' => [__('订单已删除。', 'aksrt'), false],
|
||||
'order_delete_failed' => [__('订单删除失败(仅允许删除已关闭订单)。', 'aksrt'), true],
|
||||
'orders_clear_empty' => [__('没有可清理的关闭订单。', 'jio'), false],
|
||||
'order_deleted' => [__('订单已删除。', 'jio'), false],
|
||||
'order_delete_failed' => [__('订单删除失败(仅允许删除已关闭订单)。', 'jio'), true],
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
@@ -390,7 +390,7 @@ class PayOrders {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,52 +4,52 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayOrders 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use AKSRT\Payment\OrderRepository;
|
||||
use Jio\Core\DomainModel;
|
||||
use Jio\Payment\OrderRepository;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait PayOrdersDetail {
|
||||
|
||||
private function render_order_detail_page(string $order_number): void {
|
||||
$back = NativeUi::button_link(__('返回订单列表', 'aksrt'), $this->list_back_url(), false, 'dashicons-arrow-left-alt');
|
||||
$back = NativeUi::button_link(__('返回订单列表', 'jio'), $this->list_back_url(), false, 'dashicons-arrow-left-alt');
|
||||
|
||||
$repo = OrderRepository::get_instance();
|
||||
$order = $repo->get($order_number);
|
||||
if (!$order) {
|
||||
echo '<div class="aksrt-native-header"><div class="aksrt-native-header__main"><h1 class="aksrt-native-header__title">'
|
||||
. esc_html__('订单详情', 'aksrt') . '</h1></div><div class="aksrt-native-header__actions">' . $back . '</div></div>';
|
||||
echo '<div class="jio-native-header"><div class="jio-native-header__main"><h1 class="jio-native-header__title">'
|
||||
. esc_html__('订单详情', 'jio') . '</h1></div><div class="jio-native-header__actions">' . $back . '</div></div>';
|
||||
echo '<hr class="wp-header-end">';
|
||||
$this->render_flash();
|
||||
echo NativeUi::empty_state(__('订单不存在', 'aksrt'), __('订单可能已被删除,或订单号有误。', 'aksrt'), 'dashicons-search'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo NativeUi::empty_state(__('订单不存在', 'jio'), __('订单可能已被删除,或订单号有误。', 'jio'), 'dashicons-search'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
|
||||
$status = (string) ($order['status'] ?? '');
|
||||
$context = $repo->get_admin_order_context($order_number);
|
||||
|
||||
echo '<div class="aksrt-native-header aksrt-orders-detail__header"><div class="aksrt-native-header__main">';
|
||||
echo '<h1 class="aksrt-native-header__title"><span class="aksrt-native-header__icon dashicons dashicons-media-spreadsheet" aria-hidden="true"></span>'
|
||||
. esc_html__('订单详情', 'aksrt') . '</h1>';
|
||||
echo '<p class="aksrt-native-header__desc"><span class="aksrt-orders-number">' . esc_html($order_number) . '</span> '
|
||||
echo '<div class="jio-native-header jio-orders-detail__header"><div class="jio-native-header__main">';
|
||||
echo '<h1 class="jio-native-header__title"><span class="jio-native-header__icon dashicons dashicons-media-spreadsheet" aria-hidden="true"></span>'
|
||||
. esc_html__('订单详情', 'jio') . '</h1>';
|
||||
echo '<p class="jio-native-header__desc"><span class="jio-orders-number">' . esc_html($order_number) . '</span> '
|
||||
. NativeUi::status_badge($status, $this->status_badge_map()) . '</p>';
|
||||
echo '</div><div class="aksrt-native-header__actions">' . $back . '</div></div>';
|
||||
echo '</div><div class="jio-native-header__actions">' . $back . '</div></div>';
|
||||
echo '<hr class="wp-header-end">';
|
||||
$this->render_flash();
|
||||
|
||||
echo '<div class="aksrt-orders-detail">';
|
||||
echo '<div class="aksrt-orders-detail__main">';
|
||||
echo '<div class="jio-orders-detail">';
|
||||
echo '<div class="jio-orders-detail__main">';
|
||||
$this->render_detail_summary($order, $context);
|
||||
$this->render_detail_timeline($order);
|
||||
$this->render_detail_snapshot($order);
|
||||
echo '</div>';
|
||||
|
||||
echo '<aside class="aksrt-orders-detail__side">';
|
||||
echo '<aside class="jio-orders-detail__side">';
|
||||
$this->render_detail_actions($order_number, $status);
|
||||
$this->render_detail_buyer($order, $context);
|
||||
$this->render_detail_note($order_number, $order, $context);
|
||||
@@ -72,31 +72,31 @@ trait PayOrdersDetail {
|
||||
}
|
||||
}
|
||||
|
||||
NativeUi::card_open(__('订单概要', 'aksrt'));
|
||||
echo '<dl class="aksrt-orders-dl">';
|
||||
$this->dl_row(__('商品', 'aksrt'), $product);
|
||||
$this->dl_row(__('商品类型', 'aksrt'), esc_html($this->product_type_label($type) . ($id > 0 ? ' · #' . $id : '')));
|
||||
$this->dl_row(__('支付方式', 'aksrt'), esc_html($this->payment_method_label((string) ($order['payment_method'] ?? ''))));
|
||||
NativeUi::card_open(__('订单概要', 'jio'));
|
||||
echo '<dl class="jio-orders-dl">';
|
||||
$this->dl_row(__('商品', 'jio'), $product);
|
||||
$this->dl_row(__('商品类型', 'jio'), esc_html($this->product_type_label($type) . ($id > 0 ? ' · #' . $id : '')));
|
||||
$this->dl_row(__('支付方式', 'jio'), esc_html($this->payment_method_label((string) ($order['payment_method'] ?? ''))));
|
||||
$txn = (string) ($order['payment_transaction_id'] ?? '');
|
||||
$this->dl_row(__('渠道交易号', 'aksrt'), $txn !== '' ? '<code class="aksrt-native-mono">' . esc_html($txn) . '</code>' : '<span class="aksrt-native-muted">—</span>');
|
||||
$this->dl_row(__('渠道交易号', 'jio'), $txn !== '' ? '<code class="jio-native-mono">' . esc_html($txn) . '</code>' : '<span class="jio-native-muted">—</span>');
|
||||
echo '</dl>';
|
||||
|
||||
// 金额拆分:只在有折扣 / 运费时展开,否则只显示实付。
|
||||
$discount = (string) ($pricing['discount'] ?? '0');
|
||||
$shipping = (string) ($pricing['shipping_fee'] ?? '0');
|
||||
echo '<div class="aksrt-orders-amounts">';
|
||||
echo '<div class="jio-orders-amounts">';
|
||||
if ($this->has_amount($discount) || $this->has_amount($shipping)) {
|
||||
$this->amount_row(__('商品金额', 'aksrt'), $this->format_amount($order, (string) ($pricing['subtotal'] ?? '0')));
|
||||
$this->amount_row(__('商品金额', 'jio'), $this->format_amount($order, (string) ($pricing['subtotal'] ?? '0')));
|
||||
if ($this->has_amount($shipping)) {
|
||||
$this->amount_row(__('运费', 'aksrt'), $this->format_amount($order, $shipping));
|
||||
$this->amount_row(__('运费', 'jio'), $this->format_amount($order, $shipping));
|
||||
}
|
||||
if ($this->has_amount($discount)) {
|
||||
$code = (string) ($coupon['code'] ?? '');
|
||||
$label = $code !== '' ? sprintf(__('优惠(%s)', 'aksrt'), $code) : __('优惠', 'aksrt');
|
||||
$label = $code !== '' ? sprintf(__('优惠(%s)', 'jio'), $code) : __('优惠', 'jio');
|
||||
$this->amount_row($label, '− ' . $this->format_amount($order, $discount));
|
||||
}
|
||||
}
|
||||
echo '<div class="aksrt-orders-amounts__row is-total"><span>' . esc_html__('实付金额', 'aksrt') . '</span><strong class="aksrt-native-amount">'
|
||||
echo '<div class="jio-orders-amounts__row is-total"><span>' . esc_html__('实付金额', 'jio') . '</span><strong class="jio-native-amount">'
|
||||
. esc_html($this->order_amount($order)) . '</strong></div>';
|
||||
echo '</div>';
|
||||
NativeUi::card_close();
|
||||
@@ -105,26 +105,26 @@ trait PayOrdersDetail {
|
||||
private function render_detail_timeline(array $order): void {
|
||||
// created_at / paid_at / refunded_at 为站点时间;expires_at 为 UTC。
|
||||
$events = [
|
||||
[__('创建订单', 'aksrt'), (string) ($order['created_at'] ?? ''), 'info'],
|
||||
[__('完成支付', 'aksrt'), (string) ($order['paid_at'] ?? ''), 'success'],
|
||||
[__('标记退款', 'aksrt'), (string) ($order['refunded_at'] ?? ''), 'info'],
|
||||
[__('订单关闭', 'aksrt'), (string) ($order['cancelled_at'] ?? ''), 'neutral'],
|
||||
[__('创建订单', 'jio'), (string) ($order['created_at'] ?? ''), 'info'],
|
||||
[__('完成支付', 'jio'), (string) ($order['paid_at'] ?? ''), 'success'],
|
||||
[__('标记退款', 'jio'), (string) ($order['refunded_at'] ?? ''), 'info'],
|
||||
[__('订单关闭', 'jio'), (string) ($order['cancelled_at'] ?? ''), 'neutral'],
|
||||
];
|
||||
$expires = (string) ($order['expires_at'] ?? '');
|
||||
if (($order['status'] ?? '') === DomainModel::ORDER_STATUS_PENDING && $expires !== '') {
|
||||
$events[] = [__('超时自动关闭', 'aksrt'), get_date_from_gmt($expires, 'Y-m-d H:i:s'), 'warning', true];
|
||||
$events[] = [__('超时自动关闭', 'jio'), get_date_from_gmt($expires, 'Y-m-d H:i:s'), 'warning', true];
|
||||
}
|
||||
|
||||
NativeUi::card_open(__('订单进度', 'aksrt'));
|
||||
echo '<ol class="aksrt-orders-timeline">';
|
||||
NativeUi::card_open(__('订单进度', 'jio'));
|
||||
echo '<ol class="jio-orders-timeline">';
|
||||
foreach ($events as $event) {
|
||||
[$label, $time, $tone] = $event;
|
||||
if ($time === '' || str_starts_with($time, '0000-00-00')) {
|
||||
continue;
|
||||
}
|
||||
$planned = !empty($event[3]);
|
||||
echo '<li class="aksrt-orders-timeline__item is-' . esc_attr($tone) . ($planned ? ' is-planned' : '') . '">'
|
||||
. '<span class="aksrt-orders-timeline__label">' . esc_html($label) . ($planned ? ' <span class="aksrt-native-muted">' . esc_html__('(预计)', 'aksrt') . '</span>' : '') . '</span>'
|
||||
echo '<li class="jio-orders-timeline__item is-' . esc_attr($tone) . ($planned ? ' is-planned' : '') . '">'
|
||||
. '<span class="jio-orders-timeline__label">' . esc_html($label) . ($planned ? ' <span class="jio-native-muted">' . esc_html__('(预计)', 'jio') . '</span>' : '') . '</span>'
|
||||
. '<time>' . esc_html($time) . '</time></li>';
|
||||
}
|
||||
echo '</ol>';
|
||||
@@ -138,22 +138,22 @@ trait PayOrdersDetail {
|
||||
if ($meta === []) {
|
||||
return;
|
||||
}
|
||||
echo '<details class="aksrt-native-card aksrt-native-collapse">';
|
||||
echo '<summary><span class="dashicons dashicons-media-code" aria-hidden="true"></span>' . esc_html__('订单快照(原始数据)', 'aksrt') . '</summary>';
|
||||
echo '<div class="aksrt-native-card__body"><pre class="aksrt-orders-snapshot">'
|
||||
echo '<details class="jio-native-card jio-native-collapse">';
|
||||
echo '<summary><span class="dashicons dashicons-media-code" aria-hidden="true"></span>' . esc_html__('订单快照(原始数据)', 'jio') . '</summary>';
|
||||
echo '<div class="jio-native-card__body"><pre class="jio-orders-snapshot">'
|
||||
. esc_html((string) wp_json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT))
|
||||
. '</pre></div></details>';
|
||||
}
|
||||
|
||||
private function render_detail_actions(string $order_number, string $status): void {
|
||||
$actions = $this->order_actions_html($order_number, $status, false);
|
||||
NativeUi::card_open(__('处理订单', 'aksrt'));
|
||||
NativeUi::card_open(__('处理订单', 'jio'));
|
||||
if ($actions === '') {
|
||||
echo '<p class="aksrt-native-muted">' . esc_html__('该订单已结束,没有可执行的操作。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-native-muted">' . esc_html__('该订单已结束,没有可执行的操作。', 'jio') . '</p>';
|
||||
} else {
|
||||
echo '<div class="aksrt-orders-actions is-stacked">' . $actions . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="jio-orders-actions is-stacked">' . $actions . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ($status === DomainModel::ORDER_STATUS_PAID) {
|
||||
echo '<p class="aksrt-native-field__hint">' . esc_html__('「标记退款」只改订单状态;款项需在支付渠道后台原路退回。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-native-field__hint">' . esc_html__('「标记退款」只改订单状态;款项需在支付渠道后台原路退回。', 'jio') . '</p>';
|
||||
}
|
||||
}
|
||||
NativeUi::card_close();
|
||||
@@ -163,15 +163,15 @@ trait PayOrdersDetail {
|
||||
$user_id = (int) ($order['user_id'] ?? 0);
|
||||
$buyer = is_array($context['buyer'] ?? null) ? $context['buyer'] : [];
|
||||
|
||||
NativeUi::card_open(__('买家', 'aksrt'));
|
||||
echo '<div class="aksrt-orders-buyer">' . NativeUi::user_cell($user_id, __('游客', 'aksrt')) . '</div>';
|
||||
NativeUi::card_open(__('买家', 'jio'));
|
||||
echo '<div class="jio-orders-buyer">' . NativeUi::user_cell($user_id, __('游客', 'jio')) . '</div>';
|
||||
if ($user_id > 0) {
|
||||
echo '<dl class="aksrt-orders-dl is-compact">';
|
||||
$this->dl_row(__('用户名', 'aksrt'), esc_html((string) ($buyer['user_login'] ?? '')) ?: '—');
|
||||
$this->dl_row(__('邮箱', 'aksrt'), esc_html((string) ($buyer['user_email'] ?? '')) ?: '—');
|
||||
echo '<dl class="jio-orders-dl is-compact">';
|
||||
$this->dl_row(__('用户名', 'jio'), esc_html((string) ($buyer['user_login'] ?? '')) ?: '—');
|
||||
$this->dl_row(__('邮箱', 'jio'), esc_html((string) ($buyer['user_email'] ?? '')) ?: '—');
|
||||
echo '</dl>';
|
||||
echo '<p><a class="button button-small" href="' . esc_url($this->page_url(self::PAGE_SLUG, ['user_id' => $user_id])) . '">'
|
||||
. esc_html__('查看该用户全部订单', 'aksrt') . '</a></p>';
|
||||
. esc_html__('查看该用户全部订单', 'jio') . '</a></p>';
|
||||
}
|
||||
NativeUi::card_close();
|
||||
}
|
||||
@@ -181,17 +181,17 @@ trait PayOrdersDetail {
|
||||
$admin_note = (string) ($meta['admin_note'] ?? '');
|
||||
$customer_note = (string) ($context['customer_note'] ?? '');
|
||||
|
||||
NativeUi::card_open(__('备注', 'aksrt'));
|
||||
NativeUi::card_open(__('备注', 'jio'));
|
||||
if ($customer_note !== '') {
|
||||
echo '<div class="aksrt-native-callout"><strong>' . esc_html__('买家备注', 'aksrt') . '</strong><br>' . nl2br(esc_html($customer_note)) . '</div>';
|
||||
echo '<div class="jio-native-callout"><strong>' . esc_html__('买家备注', 'jio') . '</strong><br>' . nl2br(esc_html($customer_note)) . '</div>';
|
||||
}
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-orders-note">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_update_order_note" />';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-orders-note">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_update_order_note" />';
|
||||
echo '<input type="hidden" name="order_number" value="' . esc_attr($order_number) . '" />';
|
||||
wp_nonce_field('aksrt_pay_update_order_note_' . $order_number);
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('运营备注', 'aksrt') . '</span>'
|
||||
. '<textarea name="admin_note" rows="4" placeholder="' . esc_attr__('仅管理员可见', 'aksrt') . '">' . esc_textarea($admin_note) . '</textarea></label>';
|
||||
echo '<div class="aksrt-native-form-foot"><button type="submit" class="button">' . esc_html__('保存备注', 'aksrt') . '</button></div>';
|
||||
wp_nonce_field('jio_pay_update_order_note_' . $order_number);
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('运营备注', 'jio') . '</span>'
|
||||
. '<textarea name="admin_note" rows="4" placeholder="' . esc_attr__('仅管理员可见', 'jio') . '">' . esc_textarea($admin_note) . '</textarea></label>';
|
||||
echo '<div class="jio-native-form-foot"><button type="submit" class="button">' . esc_html__('保存备注', 'jio') . '</button></div>';
|
||||
echo '</form>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
@@ -209,7 +209,7 @@ trait PayOrdersDetail {
|
||||
}
|
||||
|
||||
private function amount_row(string $label, string $value): void {
|
||||
echo '<div class="aksrt-orders-amounts__row"><span>' . esc_html($label) . '</span><span>' . esc_html($value) . '</span></div>';
|
||||
echo '<div class="jio-orders-amounts__row"><span>' . esc_html($label) . '</span><span>' . esc_html($value) . '</span></div>';
|
||||
}
|
||||
|
||||
private function has_amount(string $value): bool {
|
||||
@@ -218,7 +218,7 @@ trait PayOrdersDetail {
|
||||
|
||||
private function format_amount(array $order, string $value): string {
|
||||
if ($this->is_points_order($order)) {
|
||||
return max(0, (int) $value) . ' ' . __('积分', 'aksrt');
|
||||
return max(0, (int) $value) . ' ' . __('积分', 'jio');
|
||||
}
|
||||
return $this->cash_amount($value, (string) ($order['currency'] ?? ''));
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayOrders 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use AKSRT\Payment\OrderRepository;
|
||||
use Jio\Core\DomainModel;
|
||||
use Jio\Payment\OrderRepository;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,8 +18,8 @@ trait PayOrdersList {
|
||||
|
||||
private function render_order_list_page(): void {
|
||||
NativeUi::header(
|
||||
__('订单明细', 'aksrt'),
|
||||
__('查看全站付费订单,处理收款确认、关闭订单与退款标记。', 'aksrt'),
|
||||
__('订单明细', 'jio'),
|
||||
__('查看全站付费订单,处理收款确认、关闭订单与退款标记。', 'jio'),
|
||||
'dashicons-list-view'
|
||||
);
|
||||
$this->render_flash();
|
||||
@@ -55,17 +55,17 @@ trait PayOrdersList {
|
||||
$this->render_filter_card($filters);
|
||||
|
||||
$row_args = $query_args + ($page_num > 1 ? ['paged' => $page_num] : []);
|
||||
NativeUi::table_card_open(__('订单列表', 'aksrt'), $this->cleanup_form_html());
|
||||
echo '<table class="wp-list-table widefat striped aksrt-orders-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('订单列表', 'aksrt') . '</caption><thead><tr>';
|
||||
NativeUi::table_card_open(__('订单列表', 'jio'), $this->cleanup_form_html());
|
||||
echo '<table class="wp-list-table widefat striped jio-orders-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('订单列表', 'jio') . '</caption><thead><tr>';
|
||||
foreach ([
|
||||
'order' => __('订单号 / 下单时间', 'aksrt'),
|
||||
'user' => __('用户', 'aksrt'),
|
||||
'product' => __('商品', 'aksrt'),
|
||||
'amount' => __('金额 / 支付方式', 'aksrt'),
|
||||
'status' => __('状态', 'aksrt'),
|
||||
'paid' => __('支付时间', 'aksrt'),
|
||||
'actions' => __('操作', 'aksrt'),
|
||||
'order' => __('订单号 / 下单时间', 'jio'),
|
||||
'user' => __('用户', 'jio'),
|
||||
'product' => __('商品', 'jio'),
|
||||
'amount' => __('金额 / 支付方式', 'jio'),
|
||||
'status' => __('状态', 'jio'),
|
||||
'paid' => __('支付时间', 'jio'),
|
||||
'actions' => __('操作', 'jio'),
|
||||
] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
@@ -73,9 +73,9 @@ trait PayOrdersList {
|
||||
|
||||
if (!$items) {
|
||||
$filtered = array_diff_key($query_args, ['status' => true]) !== [];
|
||||
echo NativeUi::empty_row(7, __('暂无订单', 'aksrt'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的订单,请调整或重置筛选。', 'aksrt')
|
||||
: __('用户下单后,订单会出现在这里。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无订单', 'jio'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的订单,请调整或重置筛选。', 'jio')
|
||||
: __('用户下单后,订单会出现在这里。', 'jio'));
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_order_row($row, $row_args);
|
||||
@@ -98,18 +98,18 @@ trait PayOrdersList {
|
||||
$detail_url = $this->page_url(self::PAGE_SLUG, ['order' => $order_number] + $row_args);
|
||||
|
||||
echo '<tr>';
|
||||
echo '<td class="column-order"><a class="aksrt-orders-number" href="' . esc_url($detail_url) . '">' . esc_html($order_number) . '</a>'
|
||||
. '<div class="aksrt-orders-sub">' . esc_html((string) ($row['created_at'] ?? '')) . '</div></td>';
|
||||
echo '<td class="column-user">' . NativeUi::user_cell((int) ($row['user_id'] ?? 0), __('游客', 'aksrt')) . '</td>';
|
||||
echo '<td class="column-product"><span class="aksrt-orders-product">' . esc_html($title !== '' ? $title : $type_label) . '</span>'
|
||||
. '<div class="aksrt-orders-sub">' . esc_html($type_label . ($product_id > 0 ? ' · #' . $product_id : '')) . '</div></td>';
|
||||
echo '<td class="column-amount"><strong class="aksrt-native-amount">' . esc_html($this->order_amount($row)) . '</strong>'
|
||||
. '<div class="aksrt-orders-sub">' . esc_html($this->payment_method_label((string) ($row['payment_method'] ?? ''))) . '</div></td>';
|
||||
echo '<td class="column-order"><a class="jio-orders-number" href="' . esc_url($detail_url) . '">' . esc_html($order_number) . '</a>'
|
||||
. '<div class="jio-orders-sub">' . esc_html((string) ($row['created_at'] ?? '')) . '</div></td>';
|
||||
echo '<td class="column-user">' . NativeUi::user_cell((int) ($row['user_id'] ?? 0), __('游客', 'jio')) . '</td>';
|
||||
echo '<td class="column-product"><span class="jio-orders-product">' . esc_html($title !== '' ? $title : $type_label) . '</span>'
|
||||
. '<div class="jio-orders-sub">' . esc_html($type_label . ($product_id > 0 ? ' · #' . $product_id : '')) . '</div></td>';
|
||||
echo '<td class="column-amount"><strong class="jio-native-amount">' . esc_html($this->order_amount($row)) . '</strong>'
|
||||
. '<div class="jio-orders-sub">' . esc_html($this->payment_method_label((string) ($row['payment_method'] ?? ''))) . '</div></td>';
|
||||
echo '<td class="column-status">' . NativeUi::status_badge($status, $this->status_badge_map()) . $this->expiry_hint($row) . '</td>';
|
||||
$paid_at = (string) ($row['paid_at'] ?? '');
|
||||
echo '<td class="column-paid">' . ($paid_at !== '' ? esc_html($paid_at) : '<span class="aksrt-native-muted">—</span>') . '</td>';
|
||||
echo '<td class="column-actions"><div class="aksrt-orders-actions">'
|
||||
. '<a class="button button-small" href="' . esc_url($detail_url) . '">' . esc_html__('详情', 'aksrt') . '</a>'
|
||||
echo '<td class="column-paid">' . ($paid_at !== '' ? esc_html($paid_at) : '<span class="jio-native-muted">—</span>') . '</td>';
|
||||
echo '<td class="column-actions"><div class="jio-orders-actions">'
|
||||
. '<a class="button button-small" href="' . esc_url($detail_url) . '">' . esc_html__('详情', 'jio') . '</a>'
|
||||
. $this->order_actions_html($order_number, $status)
|
||||
. '</div></td>';
|
||||
echo '</tr>';
|
||||
@@ -121,9 +121,9 @@ trait PayOrdersList {
|
||||
if (($order['status'] ?? '') !== DomainModel::ORDER_STATUS_PENDING || $expires === '') {
|
||||
return '';
|
||||
}
|
||||
return '<div class="aksrt-orders-sub">' . esc_html(sprintf(
|
||||
return '<div class="jio-orders-sub">' . esc_html(sprintf(
|
||||
/* translators: %s local expiry time */
|
||||
__('%s 前未支付将自动关闭', 'aksrt'),
|
||||
__('%s 前未支付将自动关闭', 'jio'),
|
||||
get_date_from_gmt($expires, 'm-d H:i')
|
||||
)) . '</div>';
|
||||
}
|
||||
@@ -135,16 +135,16 @@ trait PayOrdersList {
|
||||
*/
|
||||
private function render_status_views(array $filters, array $query_args, array $counts): void {
|
||||
$base = array_diff_key($query_args, ['status' => true]);
|
||||
$views = ['' => [__('全部', 'aksrt'), array_sum($counts)]];
|
||||
$views = ['' => [__('全部', 'jio'), array_sum($counts)]];
|
||||
foreach ($this->status_labels() as $key => $label) {
|
||||
$views[$key] = [$label, (int) ($counts[$key] ?? 0)];
|
||||
}
|
||||
|
||||
echo '<nav class="aksrt-native-tabs aksrt-orders-views" aria-label="' . esc_attr__('按订单状态查看', 'aksrt') . '">';
|
||||
echo '<nav class="jio-native-tabs jio-orders-views" aria-label="' . esc_attr__('按订单状态查看', 'jio') . '">';
|
||||
foreach ($views as $key => [$label, $count]) {
|
||||
$url = $this->page_url(self::PAGE_SLUG, $base + ($key !== '' ? ['status' => $key] : []));
|
||||
$active = $filters['status'] === $key;
|
||||
echo '<a class="aksrt-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
echo '<a class="jio-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
. esc_html($label) . ' <span class="count">' . esc_html(number_format_i18n($count)) . '</span></a>';
|
||||
}
|
||||
echo '</nav>';
|
||||
@@ -152,8 +152,8 @@ trait PayOrdersList {
|
||||
|
||||
/** @param array<string, mixed> $filters */
|
||||
private function render_filter_card(array $filters): void {
|
||||
NativeUi::card_open('', '', false, 'aksrt-orders-filter-card');
|
||||
echo '<form method="get" class="aksrt-orders-filters" role="search">';
|
||||
NativeUi::card_open('', '', false, 'jio-orders-filter-card');
|
||||
echo '<form method="get" class="jio-orders-filters" role="search">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '" />';
|
||||
if ($filters['status'] !== '') {
|
||||
echo '<input type="hidden" name="status" value="' . esc_attr($filters['status']) . '" />';
|
||||
@@ -162,21 +162,21 @@ trait PayOrdersList {
|
||||
echo '<input type="hidden" name="product_id" value="' . esc_attr((string) $filters['product_id']) . '" />';
|
||||
}
|
||||
|
||||
echo '<label class="aksrt-orders-filters__search"><span class="screen-reader-text">' . esc_html__('搜索订单', 'aksrt') . '</span>'
|
||||
. '<input type="search" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('订单号 / 用户名 / 邮箱 / 商品名', 'aksrt') . '" /></label>';
|
||||
echo $this->filter_select('product_type', __('商品类型', 'aksrt'), $this->type_options(), $filters['product_type']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('payment_method', __('支付方式', 'aksrt'), $this->method_options(), $filters['payment_method']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="aksrt-orders-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'aksrt') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
echo '<label class="jio-orders-filters__search"><span class="screen-reader-text">' . esc_html__('搜索订单', 'jio') . '</span>'
|
||||
. '<input type="search" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('订单号 / 用户名 / 邮箱 / 商品名', 'jio') . '" /></label>';
|
||||
echo $this->filter_select('product_type', __('商品类型', 'jio'), $this->type_options(), $filters['product_type']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('payment_method', __('支付方式', 'jio'), $this->method_options(), $filters['payment_method']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="jio-orders-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'jio') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
. '<span aria-hidden="true">–</span>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'aksrt') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'jio') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '</span>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('用户 ID', 'aksrt') . '</span><input type="number" min="1" name="user_id" class="aksrt-orders-filters__user" value="'
|
||||
. esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('用户 ID', 'aksrt') . '" /></label>';
|
||||
echo '<span class="aksrt-orders-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('用户 ID', 'jio') . '</span><input type="number" min="1" name="user_id" class="jio-orders-filters__user" value="'
|
||||
. esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('用户 ID', 'jio') . '" /></label>';
|
||||
echo '<span class="jio-orders-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if (array_diff_key($this->list_query_args($filters), ['status' => true]) !== []) {
|
||||
$reset = $this->page_url(self::PAGE_SLUG, $filters['status'] !== '' ? ['status' => $filters['status']] : []);
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'aksrt') . '</a>';
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'jio') . '</a>';
|
||||
}
|
||||
echo '</span></form>';
|
||||
|
||||
@@ -185,9 +185,9 @@ trait PayOrdersList {
|
||||
if ($filters['product_id'] > 0) {
|
||||
$remove = $this->page_url(self::PAGE_SLUG, array_diff_key($this->list_query_args($filters), ['product_id' => true]));
|
||||
$title = (string) get_the_title($filters['product_id']);
|
||||
echo '<p class="aksrt-orders-chips"><span class="aksrt-orders-chip">'
|
||||
. esc_html(sprintf(__('商品:%s', 'aksrt'), $title !== '' ? $title . ' #' . $filters['product_id'] : '#' . $filters['product_id']))
|
||||
. '<a href="' . esc_url($remove) . '" aria-label="' . esc_attr__('移除商品筛选', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></a></span></p>';
|
||||
echo '<p class="jio-orders-chips"><span class="jio-orders-chip">'
|
||||
. esc_html(sprintf(__('商品:%s', 'jio'), $title !== '' ? $title . ' #' . $filters['product_id'] : '#' . $filters['product_id']))
|
||||
. '<a href="' . esc_url($remove) . '" aria-label="' . esc_attr__('移除商品筛选', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></a></span></p>';
|
||||
}
|
||||
NativeUi::card_close();
|
||||
}
|
||||
@@ -203,11 +203,11 @@ trait PayOrdersList {
|
||||
|
||||
private function cleanup_form_html(): string {
|
||||
return $this->action_form(
|
||||
'aksrt_pay_clear_closed_orders',
|
||||
'aksrt_pay_clear_closed_orders',
|
||||
'jio_pay_clear_closed_orders',
|
||||
'jio_pay_clear_closed_orders',
|
||||
['days' => '14'],
|
||||
__('清理 14 天前关闭订单', 'aksrt'),
|
||||
__('将永久删除 14 天前关闭的全部订单,不可恢复。确认清理?', 'aksrt'),
|
||||
__('清理 14 天前关闭订单', 'jio'),
|
||||
__('将永久删除 14 天前关闭的全部订单,不可恢复。确认清理?', 'jio'),
|
||||
true
|
||||
);
|
||||
}
|
||||
@@ -215,24 +215,24 @@ trait PayOrdersList {
|
||||
/** @return array<string, string> */
|
||||
private function type_options(): array {
|
||||
return [
|
||||
'' => __('全部类型', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_READING => __('付费阅读', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_DOWNLOAD => __('付费下载', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_IMAGE => __('付费图片', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_VIDEO => __('付费视频', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_VIP => __('购买会员', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_RECHARGE => __('余额充值', 'aksrt'),
|
||||
DomainModel::ORDER_PRODUCT_POINTS => __('购买积分', 'aksrt'),
|
||||
'' => __('全部类型', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_READING => __('付费阅读', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_DOWNLOAD => __('付费下载', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_IMAGE => __('付费图片', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_VIDEO => __('付费视频', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_VIP => __('购买会员', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_RECHARGE => __('余额充值', 'jio'),
|
||||
DomainModel::ORDER_PRODUCT_POINTS => __('购买积分', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
private function method_options(): array {
|
||||
return [
|
||||
'' => __('支付方式', 'aksrt'),
|
||||
'balance' => __('余额', 'aksrt'),
|
||||
'points' => __('积分', 'aksrt'),
|
||||
'gateway' => __('第三方网关支付', 'aksrt'),
|
||||
'' => __('支付方式', 'jio'),
|
||||
'balance' => __('余额', 'jio'),
|
||||
'points' => __('积分', 'jio'),
|
||||
'gateway' => __('第三方网关支付', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+118
-118
@@ -2,25 +2,25 @@
|
||||
/**
|
||||
* 支付中心「概览」:今日实时、区间指标卡、成交趋势、分类成交、支付方式、转化与退款、热销排行、待办与快捷入口。
|
||||
*
|
||||
* 统计口径见 \AKSRT\Payment\OrderAnalytics。首屏报表(近 30 天)随页面输出为 JSON,
|
||||
* 之后切换区间 / 分类走 REST(aksrt/v1/admin/pay-overview,见 \AKSRT\Admin\PayOverviewController)。
|
||||
* 统计口径见 \Jio\Payment\OrderAnalytics。首屏报表(近 30 天)随页面输出为 JSON,
|
||||
* 之后切换区间 / 分类走 REST(jio/v1/admin/pay-overview,见 \Jio\Admin\PayOverviewController)。
|
||||
* 图表用按需打包的 ECharts(assets/js/runtime/libs/echarts-pay.min.js,tools/build-pay-charts.mjs 生成),
|
||||
* 绘制逻辑在 assets/js/admin-pay-overview.js,样式在 assets/css/admin/pay-overview.css。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Payment\OrderAnalytics;
|
||||
use AKSRT\Payment\OrderAnalyticsDaily;
|
||||
use Jio\Payment\OrderAnalytics;
|
||||
use Jio\Payment\OrderAnalyticsDaily;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class PayOverview {
|
||||
|
||||
private const DEFAULT_DAYS = 30;
|
||||
private const DATA_ID = 'aksrt-pay-overview-data';
|
||||
private const DATA_ID = 'jio-pay-overview-data';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -43,14 +43,14 @@ final class PayOverview {
|
||||
$lib = '/assets/js/runtime/libs/echarts-pay.min.js';
|
||||
$css = '/assets/css/admin/pay-overview.css';
|
||||
$js = '/assets/js/admin-pay-overview.js';
|
||||
wp_enqueue_style('aksrt-admin-pay-overview', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-echarts-pay', AKSRT_URI . $lib, [], (string) filemtime(AKSRT_DIR . $lib), true);
|
||||
wp_enqueue_script('aksrt-admin-pay-overview', AKSRT_URI . $js, ['aksrt-echarts-pay'], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-pay-overview', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-echarts-pay', JIO_URI . $lib, [], (string) filemtime(JIO_DIR . $lib), true);
|
||||
wp_enqueue_script('jio-admin-pay-overview', JIO_URI . $js, ['jio-echarts-pay'], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_page(): void {
|
||||
if (!current_user_can(PayMenus::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
|
||||
$analytics = OrderAnalytics::get_instance();
|
||||
@@ -60,35 +60,35 @@ final class PayOverview {
|
||||
$realtime = $analytics->today_vs_yesterday();
|
||||
$symbol = $this->currency_symbol();
|
||||
|
||||
NativeUi::page_open('aksrt-po-page');
|
||||
NativeUi::page_open('jio-po-page');
|
||||
NativeUi::header(
|
||||
__('支付中心', 'aksrt'),
|
||||
__('成交走势、收入结构与待办一览。收款渠道、价格策略等配置在主题设置中修改。', 'aksrt'),
|
||||
__('支付中心', 'jio'),
|
||||
__('成交走势、收入结构与待办一览。收款渠道、价格策略等配置在主题设置中修改。', 'jio'),
|
||||
'dashicons-cart',
|
||||
[
|
||||
['label' => __('订单明细', 'aksrt'), 'url' => $this->page_url('aksrt_orders'), 'icon' => 'dashicons-list-view'],
|
||||
['label' => __('支付配置', 'aksrt'), 'url' => admin_url('admin.php?page=aksrt-settings#/payment/gateway'), 'icon' => 'dashicons-admin-generic', 'primary' => true],
|
||||
['label' => __('订单明细', 'jio'), 'url' => $this->page_url('jio_orders'), 'icon' => 'dashicons-list-view'],
|
||||
['label' => __('支付配置', 'jio'), 'url' => admin_url('admin.php?page=jio-settings#/payment/gateway'), 'icon' => 'dashicons-admin-generic', 'primary' => true],
|
||||
]
|
||||
);
|
||||
|
||||
$this->render_realtime($realtime, $symbol);
|
||||
$this->render_toolbar($from, $today);
|
||||
echo '<div class="aksrt-po-notice" data-aksrt-po-excluded hidden></div>';
|
||||
echo '<div class="aksrt-po-error notice notice-error" data-aksrt-po-error hidden><p></p></div>';
|
||||
echo '<div class="jio-po-notice" data-jio-po-excluded hidden></div>';
|
||||
echo '<div class="jio-po-error notice notice-error" data-jio-po-error hidden><p></p></div>';
|
||||
$this->render_kpis();
|
||||
|
||||
echo '<div class="aksrt-po-body" data-aksrt-po-body>';
|
||||
echo '<div class="aksrt-po-grid aksrt-po-grid--main">';
|
||||
$this->chart_card('trend', __('成交趋势', 'aksrt'), __('支付金额、净收入、退款金额曲线与支付订单数折线', 'aksrt'), 'lg');
|
||||
echo '<div class="jio-po-body" data-jio-po-body>';
|
||||
echo '<div class="jio-po-grid jio-po-grid--main">';
|
||||
$this->chart_card('trend', __('成交趋势', 'jio'), __('支付金额、净收入、退款金额曲线与支付订单数折线', 'jio'), 'lg');
|
||||
$this->render_funnel_card();
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="aksrt-po-grid aksrt-po-grid--main">';
|
||||
$this->chart_card('groups', __('分类成交', 'aksrt'), __('按商品分类的支付金额曲线', 'aksrt'), 'md');
|
||||
$this->chart_card('methods', __('支付方式', 'aksrt'), __('按支付金额的支付方式占比', 'aksrt'), 'md');
|
||||
echo '<div class="jio-po-grid jio-po-grid--main">';
|
||||
$this->chart_card('groups', __('分类成交', 'jio'), __('按商品分类的支付金额曲线', 'jio'), 'md');
|
||||
$this->chart_card('methods', __('支付方式', 'jio'), __('按支付金额的支付方式占比', 'jio'), 'md');
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="aksrt-po-grid aksrt-po-grid--main">';
|
||||
echo '<div class="jio-po-grid jio-po-grid--main">';
|
||||
$this->render_top_card();
|
||||
$this->render_todo_card();
|
||||
echo '</div>';
|
||||
@@ -100,23 +100,23 @@ final class PayOverview {
|
||||
'currency' => $symbol,
|
||||
'today' => $today,
|
||||
'maxDays' => OrderAnalytics::MAX_RANGE_DAYS,
|
||||
'rest' => ['url' => rest_url('aksrt/v1/admin/pay-overview'), 'nonce' => wp_create_nonce('wp_rest')],
|
||||
'rest' => ['url' => rest_url('jio/v1/admin/pay-overview'), 'nonce' => wp_create_nonce('wp_rest')],
|
||||
'initial' => ['from' => $from, 'to' => $today, 'group' => '', 'report' => $report],
|
||||
'i18n' => [
|
||||
'paid' => __('支付金额', 'aksrt'),
|
||||
'net' => __('净收入', 'aksrt'),
|
||||
'refund' => __('退款金额', 'aksrt'),
|
||||
'count' => __('支付订单数', 'aksrt'),
|
||||
'unit' => __('笔', 'aksrt'),
|
||||
'people' => __('人', 'aksrt'),
|
||||
'empty' => __('所选区间暂无成交', 'aksrt'),
|
||||
'loadError' => __('统计数据加载失败,请稍后重试。', 'aksrt'),
|
||||
'basis' => __('较上一周期', 'aksrt'),
|
||||
'new' => __('新增', 'aksrt'),
|
||||
'total' => __('合计', 'aksrt'),
|
||||
'excluded' => __('以下外币订单未计入统计:', 'aksrt'),
|
||||
'noTop' => __('所选区间暂无付费内容成交', 'aksrt'),
|
||||
'badRange' => __('请选择有效的日期区间。', 'aksrt'),
|
||||
'paid' => __('支付金额', 'jio'),
|
||||
'net' => __('净收入', 'jio'),
|
||||
'refund' => __('退款金额', 'jio'),
|
||||
'count' => __('支付订单数', 'jio'),
|
||||
'unit' => __('笔', 'jio'),
|
||||
'people' => __('人', 'jio'),
|
||||
'empty' => __('所选区间暂无成交', 'jio'),
|
||||
'loadError' => __('统计数据加载失败,请稍后重试。', 'jio'),
|
||||
'basis' => __('较上一周期', 'jio'),
|
||||
'new' => __('新增', 'jio'),
|
||||
'total' => __('合计', 'jio'),
|
||||
'excluded' => __('以下外币订单未计入统计:', 'jio'),
|
||||
'noTop' => __('所选区间暂无付费内容成交', 'jio'),
|
||||
'badRange' => __('请选择有效的日期区间。', 'jio'),
|
||||
],
|
||||
];
|
||||
echo '<script type="application/json" id="' . esc_attr(self::DATA_ID) . '">'
|
||||
@@ -134,17 +134,17 @@ final class PayOverview {
|
||||
private function render_realtime(array $data, string $symbol): void {
|
||||
$amount = (float) $data['today']['amount'];
|
||||
$prev = (float) $data['yesterday']['amount'];
|
||||
echo '<section class="aksrt-po-live" aria-label="' . esc_attr__('今日实时', 'aksrt') . '">';
|
||||
echo '<div class="aksrt-po-live__main"><span class="aksrt-po-live__dot" aria-hidden="true"></span>'
|
||||
. '<span class="aksrt-po-live__label">' . esc_html__('今日支付金额', 'aksrt') . '</span>'
|
||||
. '<strong class="aksrt-po-live__value">' . esc_html($symbol . number_format_i18n($amount, 2)) . '</strong>'
|
||||
echo '<section class="jio-po-live" aria-label="' . esc_attr__('今日实时', 'jio') . '">';
|
||||
echo '<div class="jio-po-live__main"><span class="jio-po-live__dot" aria-hidden="true"></span>'
|
||||
. '<span class="jio-po-live__label">' . esc_html__('今日支付金额', 'jio') . '</span>'
|
||||
. '<strong class="jio-po-live__value">' . esc_html($symbol . number_format_i18n($amount, 2)) . '</strong>'
|
||||
. $this->delta_html($amount, $prev) . '</div>';
|
||||
echo '<div class="aksrt-po-live__meta">';
|
||||
echo '<div class="jio-po-live__meta">';
|
||||
/* translators: %d order count */
|
||||
echo '<span>' . esc_html(sprintf(__('今日 %d 笔', 'aksrt'), (int) $data['today']['count'])) . '</span>';
|
||||
echo '<span>' . esc_html(sprintf(__('今日 %d 笔', 'jio'), (int) $data['today']['count'])) . '</span>';
|
||||
echo '<span>' . esc_html(sprintf(
|
||||
/* translators: 1 time 2 amount 3 order count */
|
||||
__('昨日 00:00–%1$s:%2$s,%3$d 笔', 'aksrt'),
|
||||
__('昨日 00:00–%1$s:%2$s,%3$d 笔', 'jio'),
|
||||
$data['until'],
|
||||
$symbol . number_format_i18n($prev, 2),
|
||||
(int) $data['yesterday']['count']
|
||||
@@ -154,16 +154,16 @@ final class PayOverview {
|
||||
|
||||
/** 同时段环比徽标(服务端渲染,仅今日实时使用;区间指标的环比由前端计算)。 */
|
||||
private function delta_html(float $current, float $previous): string {
|
||||
$basis = __('较昨日同时段', 'aksrt');
|
||||
$basis = __('较昨日同时段', 'jio');
|
||||
if ($previous <= 0) {
|
||||
$text = $current > 0 ? __('新增', 'aksrt') : '—';
|
||||
$text = $current > 0 ? __('新增', 'jio') : '—';
|
||||
$class = $current > 0 ? 'is-up' : 'is-flat';
|
||||
} else {
|
||||
$delta = round(($current - $previous) / $previous * 100, 1);
|
||||
$class = $delta > 0 ? 'is-up' : ($delta < 0 ? 'is-down' : 'is-flat');
|
||||
$text = ($delta > 0 ? '↑' : ($delta < 0 ? '↓' : '')) . number_format_i18n(abs($delta), 1) . '%';
|
||||
}
|
||||
return '<span class="aksrt-po-delta ' . esc_attr($class) . '">' . esc_html($basis . ' ' . $text) . '</span>';
|
||||
return '<span class="jio-po-delta ' . esc_attr($class) . '">' . esc_html($basis . ' ' . $text) . '</span>';
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -172,54 +172,54 @@ final class PayOverview {
|
||||
|
||||
private function render_toolbar(string $from, string $to): void {
|
||||
$presets = [
|
||||
'd7' => __('近 7 天', 'aksrt'),
|
||||
'd30' => __('近 30 天', 'aksrt'),
|
||||
'd90' => __('近 90 天', 'aksrt'),
|
||||
'this_month' => __('本月', 'aksrt'),
|
||||
'last_month' => __('上月', 'aksrt'),
|
||||
'd365' => __('近一年', 'aksrt'),
|
||||
'd7' => __('近 7 天', 'jio'),
|
||||
'd30' => __('近 30 天', 'jio'),
|
||||
'd90' => __('近 90 天', 'jio'),
|
||||
'this_month' => __('本月', 'jio'),
|
||||
'last_month' => __('上月', 'jio'),
|
||||
'd365' => __('近一年', 'jio'),
|
||||
];
|
||||
echo '<div class="aksrt-po-toolbar">';
|
||||
echo '<div class="aksrt-po-seg" role="group" data-aksrt-po-presets aria-label="' . esc_attr__('统计区间', 'aksrt') . '">';
|
||||
echo '<div class="jio-po-toolbar">';
|
||||
echo '<div class="jio-po-seg" role="group" data-jio-po-presets aria-label="' . esc_attr__('统计区间', 'jio') . '">';
|
||||
foreach ($presets as $key => $label) {
|
||||
echo '<button type="button" data-preset="' . esc_attr($key) . '" aria-pressed="' . ($key === 'd30' ? 'true' : 'false') . '">' . esc_html($label) . '</button>';
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
echo '<form class="aksrt-po-range" data-aksrt-po-range>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'aksrt') . '</span>'
|
||||
echo '<form class="jio-po-range" data-jio-po-range>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'jio') . '</span>'
|
||||
. '<input type="date" name="from" value="' . esc_attr($from) . '" max="' . esc_attr($to) . '" required></label>';
|
||||
echo '<span aria-hidden="true">–</span>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'aksrt') . '</span>'
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'jio') . '</span>'
|
||||
. '<input type="date" name="to" value="' . esc_attr($to) . '" max="' . esc_attr($to) . '" required></label>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('商品分类', 'aksrt') . '</span><select name="group">'
|
||||
. '<option value="">' . esc_html__('全部分类', 'aksrt') . '</option>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('商品分类', 'jio') . '</span><select name="group">'
|
||||
. '<option value="">' . esc_html__('全部分类', 'jio') . '</option>';
|
||||
foreach (OrderAnalytics::group_labels() as $key => $label) {
|
||||
echo '<option value="' . esc_attr($key) . '">' . esc_html($label) . '</option>';
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<button type="submit" class="button">' . esc_html__('查询', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button">' . esc_html__('查询', 'jio') . '</button>';
|
||||
echo '</form>';
|
||||
echo '<span class="aksrt-po-range-label" data-aksrt-po-range-label aria-live="polite"></span>';
|
||||
echo '<span class="jio-po-range-label" data-jio-po-range-label aria-live="polite"></span>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
private function render_kpis(): void {
|
||||
$cards = [
|
||||
['paid_amount', __('支付金额', 'aksrt'), 'success'],
|
||||
['net_amount', __('净收入', 'aksrt'), 'info'],
|
||||
['paid_count', __('支付订单数', 'aksrt'), 'primary'],
|
||||
['buyers', __('支付人数', 'aksrt'), 'primary'],
|
||||
['avg_amount', __('客单价', 'aksrt'), 'warning'],
|
||||
['refund_amount', __('退款金额', 'aksrt'), 'danger'],
|
||||
['paid_amount', __('支付金额', 'jio'), 'success'],
|
||||
['net_amount', __('净收入', 'jio'), 'info'],
|
||||
['paid_count', __('支付订单数', 'jio'), 'primary'],
|
||||
['buyers', __('支付人数', 'jio'), 'primary'],
|
||||
['avg_amount', __('客单价', 'jio'), 'warning'],
|
||||
['refund_amount', __('退款金额', 'jio'), 'danger'],
|
||||
];
|
||||
echo '<div class="aksrt-po-kpis" data-aksrt-po-kpis>';
|
||||
echo '<div class="jio-po-kpis" data-jio-po-kpis>';
|
||||
foreach ($cards as [$key, $label, $tone]) {
|
||||
echo '<div class="aksrt-po-kpi aksrt-po-kpi--' . esc_attr($tone) . '" data-kpi="' . esc_attr($key) . '">'
|
||||
. '<span class="aksrt-po-kpi__label">' . esc_html($label) . '</span>'
|
||||
. '<span class="aksrt-po-kpi__value" data-kpi-value>—</span>'
|
||||
. '<span class="aksrt-po-kpi__foot"><span class="aksrt-po-delta is-flat" data-kpi-delta>—</span>'
|
||||
. '<span class="aksrt-po-kpi__meta" data-kpi-meta></span></span>'
|
||||
echo '<div class="jio-po-kpi jio-po-kpi--' . esc_attr($tone) . '" data-kpi="' . esc_attr($key) . '">'
|
||||
. '<span class="jio-po-kpi__label">' . esc_html($label) . '</span>'
|
||||
. '<span class="jio-po-kpi__value" data-kpi-value>—</span>'
|
||||
. '<span class="jio-po-kpi__foot"><span class="jio-po-delta is-flat" data-kpi-delta>—</span>'
|
||||
. '<span class="jio-po-kpi__meta" data-kpi-meta></span></span>'
|
||||
. '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
@@ -230,65 +230,65 @@ final class PayOverview {
|
||||
* ----------------------------------------------------------------*/
|
||||
|
||||
private function chart_card(string $key, string $title, string $label, string $size): void {
|
||||
NativeUi::card_open($title, '', false, 'aksrt-po-card');
|
||||
echo '<div class="aksrt-po-chart aksrt-po-chart--' . esc_attr($size) . '" data-aksrt-po-chart="' . esc_attr($key) . '" role="img" aria-label="' . esc_attr($label) . '"></div>';
|
||||
NativeUi::card_open($title, '', false, 'jio-po-card');
|
||||
echo '<div class="jio-po-chart jio-po-chart--' . esc_attr($size) . '" data-jio-po-chart="' . esc_attr($key) . '" role="img" aria-label="' . esc_attr($label) . '"></div>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
|
||||
private function render_funnel_card(): void {
|
||||
$rows = [
|
||||
['created_count', __('下单数', 'aksrt'), __('区间内创建的订单', 'aksrt')],
|
||||
['conversion', __('支付转化率', 'aksrt'), __('下单后完成支付的比例', 'aksrt')],
|
||||
['close_rate', __('超时关闭率', 'aksrt'), __('未支付而关闭的比例', 'aksrt')],
|
||||
['refund_rate', __('退款率', 'aksrt'), __('退款金额 ÷ 支付金额', 'aksrt')],
|
||||
['created_count', __('下单数', 'jio'), __('区间内创建的订单', 'jio')],
|
||||
['conversion', __('支付转化率', 'jio'), __('下单后完成支付的比例', 'jio')],
|
||||
['close_rate', __('超时关闭率', 'jio'), __('未支付而关闭的比例', 'jio')],
|
||||
['refund_rate', __('退款率', 'jio'), __('退款金额 ÷ 支付金额', 'jio')],
|
||||
];
|
||||
NativeUi::card_open(__('转化与退款', 'aksrt'), '', false, 'aksrt-po-card');
|
||||
echo '<ul class="aksrt-po-funnel" data-aksrt-po-funnel>';
|
||||
NativeUi::card_open(__('转化与退款', 'jio'), '', false, 'jio-po-card');
|
||||
echo '<ul class="jio-po-funnel" data-jio-po-funnel>';
|
||||
foreach ($rows as [$key, $label, $hint]) {
|
||||
echo '<li data-metric="' . esc_attr($key) . '"><div class="aksrt-po-funnel__head">'
|
||||
. '<span class="aksrt-po-funnel__label">' . esc_html($label) . '</span>'
|
||||
. '<strong class="aksrt-po-funnel__value" data-value>—</strong></div>'
|
||||
. '<span class="aksrt-po-funnel__bar" aria-hidden="true"><i data-bar></i></span>'
|
||||
. '<span class="aksrt-po-funnel__hint">' . esc_html($hint) . ' <span data-delta></span></span></li>';
|
||||
echo '<li data-metric="' . esc_attr($key) . '"><div class="jio-po-funnel__head">'
|
||||
. '<span class="jio-po-funnel__label">' . esc_html($label) . '</span>'
|
||||
. '<strong class="jio-po-funnel__value" data-value>—</strong></div>'
|
||||
. '<span class="jio-po-funnel__bar" aria-hidden="true"><i data-bar></i></span>'
|
||||
. '<span class="jio-po-funnel__hint">' . esc_html($hint) . ' <span data-delta></span></span></li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
|
||||
private function render_top_card(): void {
|
||||
NativeUi::card_open(__('付费内容热销 TOP 10', 'aksrt'), '', true, 'aksrt-po-card');
|
||||
echo '<div class="aksrt-native-table-wrap"><table class="widefat aksrt-po-top">'
|
||||
. '<caption class="screen-reader-text">' . esc_html__('付费内容热销排行', 'aksrt') . '</caption>'
|
||||
. '<thead><tr><th scope="col" class="column-rank">#</th><th scope="col">' . esc_html__('文章', 'aksrt') . '</th>'
|
||||
. '<th scope="col" class="column-num">' . esc_html__('销量', 'aksrt') . '</th>'
|
||||
. '<th scope="col" class="column-num">' . esc_html__('支付金额', 'aksrt') . '</th>'
|
||||
. '<th scope="col" class="column-share">' . esc_html__('占比', 'aksrt') . '</th></tr></thead>'
|
||||
. '<tbody data-aksrt-po-top></tbody></table></div>';
|
||||
NativeUi::card_open(__('付费内容热销 TOP 10', 'jio'), '', true, 'jio-po-card');
|
||||
echo '<div class="jio-native-table-wrap"><table class="widefat jio-po-top">'
|
||||
. '<caption class="screen-reader-text">' . esc_html__('付费内容热销排行', 'jio') . '</caption>'
|
||||
. '<thead><tr><th scope="col" class="column-rank">#</th><th scope="col">' . esc_html__('文章', 'jio') . '</th>'
|
||||
. '<th scope="col" class="column-num">' . esc_html__('销量', 'jio') . '</th>'
|
||||
. '<th scope="col" class="column-num">' . esc_html__('支付金额', 'jio') . '</th>'
|
||||
. '<th scope="col" class="column-share">' . esc_html__('占比', 'jio') . '</th></tr></thead>'
|
||||
. '<tbody data-jio-po-top></tbody></table></div>';
|
||||
NativeUi::card_close();
|
||||
}
|
||||
|
||||
private function render_todo_card(): void {
|
||||
$orders = class_exists('\AKSRT\Payment\OrderRepository') ? \AKSRT\Payment\OrderRepository::get_instance()->get_orders_status_counts() : [];
|
||||
$withdraw = class_exists('\AKSRT\Payment\Withdrawal') ? \AKSRT\Payment\Withdrawal::get_instance()->get_stats() : [];
|
||||
$income = class_exists('\AKSRT\Payment\Revenue') ? \AKSRT\Payment\Revenue::get_instance()->get_stats() : [];
|
||||
$cards = class_exists('\AKSRT\Payment\ChargeCard') ? \AKSRT\Payment\ChargeCard::get_instance()->get_stats() : [];
|
||||
$orders = class_exists('\Jio\Payment\OrderRepository') ? \Jio\Payment\OrderRepository::get_instance()->get_orders_status_counts() : [];
|
||||
$withdraw = class_exists('\Jio\Payment\Withdrawal') ? \Jio\Payment\Withdrawal::get_instance()->get_stats() : [];
|
||||
$income = class_exists('\Jio\Payment\Revenue') ? \Jio\Payment\Revenue::get_instance()->get_stats() : [];
|
||||
$cards = class_exists('\Jio\Payment\ChargeCard') ? \Jio\Payment\ChargeCard::get_instance()->get_stats() : [];
|
||||
|
||||
$items = [
|
||||
[__('待审提现', 'aksrt'), (int) ($withdraw['pending'] ?? 0), $this->page_url('aksrt_withdraw', ['status' => 'pending']), 'dashicons-money-alt', 'danger'],
|
||||
[__('打款中提现', 'aksrt'), (int) ($withdraw['processing'] ?? 0), $this->page_url('aksrt_withdraw', ['status' => 'processing']), 'dashicons-update', 'warning'],
|
||||
[__('待结算分成', 'aksrt'), (string) ($income['cash']['pending'] ?? '0.00'), $this->page_url('aksrt_income', ['status' => 'pending']), 'dashicons-chart-pie', 'warning'],
|
||||
[__('待支付订单', 'aksrt'), (int) ($orders['pending'] ?? 0), $this->page_url('aksrt_orders', ['status' => 'pending']), 'dashicons-clock', 'info'],
|
||||
[__('可用卡密', 'aksrt'), (int) ($cards['active'] ?? 0), $this->page_url('aksrt_charge_cards', ['status' => 'active']), 'dashicons-id-alt', 'neutral'],
|
||||
[__('待审提现', 'jio'), (int) ($withdraw['pending'] ?? 0), $this->page_url('jio_withdraw', ['status' => 'pending']), 'dashicons-money-alt', 'danger'],
|
||||
[__('打款中提现', 'jio'), (int) ($withdraw['processing'] ?? 0), $this->page_url('jio_withdraw', ['status' => 'processing']), 'dashicons-update', 'warning'],
|
||||
[__('待结算分成', 'jio'), (string) ($income['cash']['pending'] ?? '0.00'), $this->page_url('jio_income', ['status' => 'pending']), 'dashicons-chart-pie', 'warning'],
|
||||
[__('待支付订单', 'jio'), (int) ($orders['pending'] ?? 0), $this->page_url('jio_orders', ['status' => 'pending']), 'dashicons-clock', 'info'],
|
||||
[__('可用卡密', 'jio'), (int) ($cards['active'] ?? 0), $this->page_url('jio_charge_cards', ['status' => 'active']), 'dashicons-id-alt', 'neutral'],
|
||||
];
|
||||
|
||||
NativeUi::card_open(__('待办事项', 'aksrt'), '', false, 'aksrt-po-card');
|
||||
echo '<ul class="aksrt-po-todo">';
|
||||
NativeUi::card_open(__('待办事项', 'jio'), '', false, 'jio-po-card');
|
||||
echo '<ul class="jio-po-todo">';
|
||||
foreach ($items as [$label, $value, $url, $icon, $tone]) {
|
||||
$idle = (string) $value === '0' || (string) $value === '0.00';
|
||||
echo '<li><a class="aksrt-po-todo__item aksrt-po-todo__item--' . esc_attr($idle ? 'neutral' : $tone) . '" href="' . esc_url($url) . '">'
|
||||
. '<span class="aksrt-po-todo__icon dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>'
|
||||
. '<span class="aksrt-po-todo__label">' . esc_html($label) . '</span>'
|
||||
. '<span class="aksrt-po-todo__value">' . esc_html(is_int($value) ? number_format_i18n($value) : $this->currency_symbol() . $value) . '</span>'
|
||||
echo '<li><a class="jio-po-todo__item jio-po-todo__item--' . esc_attr($idle ? 'neutral' : $tone) . '" href="' . esc_url($url) . '">'
|
||||
. '<span class="jio-po-todo__icon dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>'
|
||||
. '<span class="jio-po-todo__label">' . esc_html($label) . '</span>'
|
||||
. '<span class="jio-po-todo__value">' . esc_html(is_int($value) ? number_format_i18n($value) : $this->currency_symbol() . $value) . '</span>'
|
||||
. '</a></li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
@@ -296,12 +296,12 @@ final class PayOverview {
|
||||
}
|
||||
|
||||
private function render_shortcuts_card(): void {
|
||||
NativeUi::card_open(__('快捷入口', 'aksrt'), '', false, 'aksrt-po-card');
|
||||
echo '<ul class="aksrt-po-links">';
|
||||
NativeUi::card_open(__('快捷入口', 'jio'), '', false, 'jio-po-card');
|
||||
echo '<ul class="jio-po-links">';
|
||||
foreach (PayMenus::get_instance()->shortcuts() as $item) {
|
||||
echo '<li><a href="' . esc_url($this->page_url($item['slug'])) . '">'
|
||||
. '<span class="aksrt-po-links__icon dashicons ' . esc_attr($item['icon']) . '" aria-hidden="true"></span>'
|
||||
. '<span class="aksrt-po-links__text"><strong>' . esc_html($item['title']) . '</strong>'
|
||||
. '<span class="jio-po-links__icon dashicons ' . esc_attr($item['icon']) . '" aria-hidden="true"></span>'
|
||||
. '<span class="jio-po-links__text"><strong>' . esc_html($item['title']) . '</strong>'
|
||||
. ($item['desc'] !== '' ? '<span>' . esc_html($item['desc']) . '</span>' : '')
|
||||
. '</span></a></li>';
|
||||
}
|
||||
@@ -314,7 +314,7 @@ final class PayOverview {
|
||||
* ----------------------------------------------------------------*/
|
||||
|
||||
private function currency_symbol(): string {
|
||||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||||
$paid = \Jio\Settings\Settings::get_paid_content_settings();
|
||||
return (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
* - PayProductsList 列表查询与表格渲染
|
||||
* - PayProductsEditor 快速设置弹窗与保存
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use Jio\Core\DomainModel;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -25,8 +25,8 @@ class PayProducts {
|
||||
use PayProductsEditor;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_pay_products';
|
||||
public const SAVE_ACTION = 'aksrt_pay_save_product';
|
||||
public const PAGE_SLUG = 'jio_pay_products';
|
||||
public const SAVE_ACTION = 'jio_pay_save_product';
|
||||
|
||||
/** 列表纳入的文章状态。 */
|
||||
private const LIST_STATUSES = ['publish', 'future', 'draft', 'pending', 'private'];
|
||||
@@ -74,21 +74,21 @@ class PayProducts {
|
||||
}
|
||||
$css = '/assets/css/admin/pay-products.css';
|
||||
$js = '/assets/js/admin-pay-products.js';
|
||||
wp_enqueue_style('aksrt-admin-pay-products', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-pay-products', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-pay-products', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-pay-products', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_products_page(): void {
|
||||
$this->ensure_cap();
|
||||
NativeUi::page_open('aksrt-products-page');
|
||||
NativeUi::page_open('jio-products-page');
|
||||
NativeUi::header(
|
||||
__('付费文章', 'aksrt'),
|
||||
__('管理付费内容的售卖配置。点击「快速设置」即可在本页修改价格、权限与销量,无需进入文章编辑页。', 'aksrt'),
|
||||
__('付费文章', 'jio'),
|
||||
__('管理付费内容的售卖配置。点击「快速设置」即可在本页修改价格、权限与销量,无需进入文章编辑页。', 'jio'),
|
||||
'dashicons-media-document'
|
||||
);
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'aksrt') . '</p></div>';
|
||||
if (!class_exists('\Jio\Payment\OrderRepository')) {
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('订单服务不可用。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -109,19 +109,19 @@ class PayProducts {
|
||||
/** @return array<int, string> */
|
||||
private function pay_limit_labels(): array {
|
||||
return [
|
||||
0 => __('所有人可购买', 'aksrt'),
|
||||
1 => __('VIP1 及以上可购买', 'aksrt'),
|
||||
2 => __('仅 VIP2 可购买', 'aksrt'),
|
||||
0 => __('所有人可购买', 'jio'),
|
||||
1 => __('VIP1 及以上可购买', 'jio'),
|
||||
2 => __('仅 VIP2 可购买', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
private function currency_symbol(): string {
|
||||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||||
$paid = \Jio\Settings\Settings::get_paid_content_settings();
|
||||
return (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
$code = isset($_GET['aksrt_pay_msg']) ? sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg'])) : '';
|
||||
$code = isset($_GET['jio_pay_msg']) ? sanitize_key((string) wp_unslash($_GET['jio_pay_msg'])) : '';
|
||||
if ($code === '') {
|
||||
return;
|
||||
}
|
||||
@@ -129,11 +129,11 @@ class PayProducts {
|
||||
$title = $post instanceof \WP_Post ? get_the_title($post) : '';
|
||||
// 码 => [文案, 是否失败]
|
||||
$map = [
|
||||
'product_saved' => [sprintf(__('已保存「%s」的商品配置。', 'aksrt'), $title), false],
|
||||
'product_disabled' => [sprintf(__('已关闭「%s」的付费功能,文章已移出本列表。', 'aksrt'), $title), false],
|
||||
'product_invalid_amount' => [__('保存失败:金额最多两位小数,积分须为非负整数。', 'aksrt'), true],
|
||||
'product_vip_price' => [__('保存失败:会员价格不能高于执行价(积分商品不能高于积分售价)。', 'aksrt'), true],
|
||||
'product_missing' => [__('保存失败:文章不存在或您无权编辑。', 'aksrt'), true],
|
||||
'product_saved' => [sprintf(__('已保存「%s」的商品配置。', 'jio'), $title), false],
|
||||
'product_disabled' => [sprintf(__('已关闭「%s」的付费功能,文章已移出本列表。', 'jio'), $title), false],
|
||||
'product_invalid_amount' => [__('保存失败:金额最多两位小数,积分须为非负整数。', 'jio'), true],
|
||||
'product_vip_price' => [__('保存失败:会员价格不能高于执行价(积分商品不能高于积分售价)。', 'jio'), true],
|
||||
'product_missing' => [__('保存失败:文章不存在或您无权编辑。', 'jio'), true],
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
@@ -144,13 +144,13 @@ class PayProducts {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
private function cash_decimal(string $value): string {
|
||||
try {
|
||||
return \AKSRT\Ledger\AssetAmount::balance($value)->decimal();
|
||||
return \Jio\Ledger\AssetAmount::balance($value)->decimal();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '0.00';
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
* 付费文章:快速设置弹窗与保存。
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayProducts 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
* 弹窗是单个 <dialog>,由 admin-pay-products.js 按行内 data-aksrt-product-edit 的 JSON 填充。
|
||||
* 弹窗是单个 <dialog>,由 admin-pay-products.js 按行内 data-jio-product-edit 的 JSON 填充。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use AKSRT\Ledger\AssetAmount;
|
||||
use Jio\Core\DomainModel;
|
||||
use Jio\Ledger\AssetAmount;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -32,7 +32,7 @@ trait PayProductsEditor {
|
||||
$title = html_entity_decode(get_the_title($post), ENT_QUOTES, 'UTF-8');
|
||||
return [
|
||||
'id' => (int) $post->ID,
|
||||
'title' => $title !== '' ? $title : sprintf(__('(无标题 #%d)', 'aksrt'), (int) $post->ID),
|
||||
'title' => $title !== '' ? $title : sprintf(__('(无标题 #%d)', 'jio'), (int) $post->ID),
|
||||
'editUrl' => $edit_url,
|
||||
'fields' => $fields,
|
||||
];
|
||||
@@ -41,12 +41,12 @@ trait PayProductsEditor {
|
||||
/** @param array<string, string|int> $context 列表筛选与页码,保存后据此回到原位置。 */
|
||||
private function render_product_editor(array $context): void {
|
||||
$currency = $this->currency_symbol();
|
||||
echo '<dialog class="aksrt-product-dialog" id="aksrt-product-editor" aria-labelledby="aksrt-product-editor-title" aria-describedby="aksrt-product-editor-subject">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-product-dialog__frame" data-aksrt-product-form'
|
||||
. ' data-discard="' . esc_attr__('有未保存的修改,确定放弃?', 'aksrt') . '"'
|
||||
. ' data-vip-error="' . esc_attr__('会员价格不能高于执行价。', 'aksrt') . '"'
|
||||
. ' data-vip-points-error="' . esc_attr__('会员积分价不能高于积分售价。', 'aksrt') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在保存…', 'aksrt') . '">';
|
||||
echo '<dialog class="jio-product-dialog" id="jio-product-editor" aria-labelledby="jio-product-editor-title" aria-describedby="jio-product-editor-subject">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-product-dialog__frame" data-jio-product-form'
|
||||
. ' data-discard="' . esc_attr__('有未保存的修改,确定放弃?', 'jio') . '"'
|
||||
. ' data-vip-error="' . esc_attr__('会员价格不能高于执行价。', 'jio') . '"'
|
||||
. ' data-vip-points-error="' . esc_attr__('会员积分价不能高于积分售价。', 'jio') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在保存…', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="' . esc_attr(self::SAVE_ACTION) . '">';
|
||||
echo '<input type="hidden" name="post_id" value="">';
|
||||
wp_nonce_field(self::SAVE_ACTION, '_wpnonce', false);
|
||||
@@ -54,83 +54,83 @@ trait PayProductsEditor {
|
||||
echo '<input type="hidden" name="ctx[' . esc_attr((string) $key) . ']" value="' . esc_attr((string) $value) . '">';
|
||||
}
|
||||
|
||||
echo '<header class="aksrt-product-dialog__head"><div>';
|
||||
echo '<h2 id="aksrt-product-editor-title">' . esc_html__('快速设置', 'aksrt') . '</h2>';
|
||||
echo '<p id="aksrt-product-editor-subject" data-aksrt-product-subject></p></div>';
|
||||
echo '<button type="button" class="aksrt-product-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button>';
|
||||
echo '</header><div class="aksrt-product-dialog__body">';
|
||||
echo '<header class="jio-product-dialog__head"><div>';
|
||||
echo '<h2 id="jio-product-editor-title">' . esc_html__('快速设置', 'jio') . '</h2>';
|
||||
echo '<p id="jio-product-editor-subject" data-jio-product-subject></p></div>';
|
||||
echo '<button type="button" class="jio-product-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button>';
|
||||
echo '</header><div class="jio-product-dialog__body">';
|
||||
|
||||
$this->editor_section_open(__('售卖方式', 'aksrt'));
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('付费类型', 'aksrt') . '</span><select name="aksrt_pay[pay_type]">';
|
||||
$this->editor_section_open(__('售卖方式', 'jio'));
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('付费类型', 'jio') . '</span><select name="jio_pay[pay_type]">';
|
||||
foreach ($this->paid_type_labels() as $key => $label) {
|
||||
echo '<option value="' . esc_attr($key) . '">' . esc_html($label) . '</option>';
|
||||
}
|
||||
echo '<option value="' . esc_attr(DomainModel::PAY_TYPE_NO) . '">' . esc_html__('关闭付费', 'aksrt') . '</option></select></label>';
|
||||
$this->editor_radios('pay_modo', __('支付方式', 'aksrt'), ['0' => __('现金', 'aksrt'), 'points' => __('积分', 'aksrt')], 'enabled');
|
||||
echo '<option value="' . esc_attr(DomainModel::PAY_TYPE_NO) . '">' . esc_html__('关闭付费', 'jio') . '</option></select></label>';
|
||||
$this->editor_radios('pay_modo', __('支付方式', 'jio'), ['0' => __('现金', 'jio'), 'points' => __('积分', 'jio')], 'enabled');
|
||||
echo '</div>';
|
||||
echo '<p class="aksrt-native-callout is-warning" data-aksrt-product-pane="disabled" hidden>'
|
||||
. esc_html__('关闭后文章恢复免费阅读并移出本列表,价格等配置会保留,重新开启即可恢复。', 'aksrt') . '</p>';
|
||||
echo '<div data-aksrt-product-pane="enabled">';
|
||||
$this->editor_radios('pay_limit', __('购买权限', 'aksrt'), array_map('strval', $this->pay_limit_labels()));
|
||||
echo '<p class="jio-native-callout is-warning" data-jio-product-pane="disabled" hidden>'
|
||||
. esc_html__('关闭后文章恢复免费阅读并移出本列表,价格等配置会保留,重新开启即可恢复。', 'jio') . '</p>';
|
||||
echo '<div data-jio-product-pane="enabled">';
|
||||
$this->editor_radios('pay_limit', __('购买权限', 'jio'), array_map('strval', $this->pay_limit_labels()));
|
||||
echo '</div>';
|
||||
$this->editor_section_close();
|
||||
|
||||
echo '<div data-aksrt-product-pane="enabled">';
|
||||
$this->editor_section_open(__('价格', 'aksrt'), __('会员价填 0 表示该等级免费,且不能高于售价。', 'aksrt'));
|
||||
echo '<fieldset class="aksrt-native-form-grid aksrt-product-pane" data-aksrt-product-pane="cash"><legend class="screen-reader-text">' . esc_html__('现金价格', 'aksrt') . '</legend>';
|
||||
$this->editor_number('price', __('执行价', 'aksrt'), $currency, '0.01', 'data-aksrt-product-base="cash"');
|
||||
$this->editor_number('original_price', __('原价', 'aksrt'), $currency, '0.01', '', __('高于执行价时划线显示。', 'aksrt'));
|
||||
$this->editor_number('vip1_price', __('VIP1 价格', 'aksrt'), $currency, '0.01', 'data-aksrt-product-vip="cash"');
|
||||
$this->editor_number('vip2_price', __('VIP2 价格', 'aksrt'), $currency, '0.01', 'data-aksrt-product-vip="cash"');
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('促销标签', 'aksrt') . '</span>'
|
||||
. '<input type="text" name="aksrt_pay[promotion_tag]"><small class="aksrt-native-field__hint">' . esc_html__('显示在原价上方,支持安全 HTML,例如图标。', 'aksrt') . '</small></label>';
|
||||
echo '<div data-jio-product-pane="enabled">';
|
||||
$this->editor_section_open(__('价格', 'jio'), __('会员价填 0 表示该等级免费,且不能高于售价。', 'jio'));
|
||||
echo '<fieldset class="jio-native-form-grid jio-product-pane" data-jio-product-pane="cash"><legend class="screen-reader-text">' . esc_html__('现金价格', 'jio') . '</legend>';
|
||||
$this->editor_number('price', __('执行价', 'jio'), $currency, '0.01', 'data-jio-product-base="cash"');
|
||||
$this->editor_number('original_price', __('原价', 'jio'), $currency, '0.01', '', __('高于执行价时划线显示。', 'jio'));
|
||||
$this->editor_number('vip1_price', __('VIP1 价格', 'jio'), $currency, '0.01', 'data-jio-product-vip="cash"');
|
||||
$this->editor_number('vip2_price', __('VIP2 价格', 'jio'), $currency, '0.01', 'data-jio-product-vip="cash"');
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('促销标签', 'jio') . '</span>'
|
||||
. '<input type="text" name="jio_pay[promotion_tag]"><small class="jio-native-field__hint">' . esc_html__('显示在原价上方,支持安全 HTML,例如图标。', 'jio') . '</small></label>';
|
||||
echo '</fieldset>';
|
||||
echo '<fieldset class="aksrt-native-form-grid aksrt-product-pane" data-aksrt-product-pane="points" hidden disabled><legend class="screen-reader-text">' . esc_html__('积分价格', 'aksrt') . '</legend>';
|
||||
$this->editor_number('points_price', __('积分售价', 'aksrt'), __('积分', 'aksrt'), '1', 'data-aksrt-product-base="points"');
|
||||
$this->editor_number('vip1_points', __('VIP1 积分价', 'aksrt'), __('积分', 'aksrt'), '1', 'data-aksrt-product-vip="points"');
|
||||
$this->editor_number('vip2_points', __('VIP2 积分价', 'aksrt'), __('积分', 'aksrt'), '1', 'data-aksrt-product-vip="points"');
|
||||
echo '<fieldset class="jio-native-form-grid jio-product-pane" data-jio-product-pane="points" hidden disabled><legend class="screen-reader-text">' . esc_html__('积分价格', 'jio') . '</legend>';
|
||||
$this->editor_number('points_price', __('积分售价', 'jio'), __('积分', 'jio'), '1', 'data-jio-product-base="points"');
|
||||
$this->editor_number('vip1_points', __('VIP1 积分价', 'jio'), __('积分', 'jio'), '1', 'data-jio-product-vip="points"');
|
||||
$this->editor_number('vip2_points', __('VIP2 积分价', 'jio'), __('积分', 'jio'), '1', 'data-jio-product-vip="points"');
|
||||
echo '</fieldset>';
|
||||
$this->editor_section_close();
|
||||
|
||||
$this->editor_section_open(__('销售', 'aksrt'));
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
$this->editor_number('pay_count', __('销量浮动', 'aksrt'), '', '1', '', __('在真实销量上增加或减少的数量,可填负数。', 'aksrt'), false);
|
||||
echo '<div class="aksrt-product-switches" role="group" aria-label="' . esc_attr__('销售选项', 'aksrt') . '">';
|
||||
$this->editor_checkbox('show_sales', __('前台显示销量', 'aksrt'));
|
||||
$this->editor_checkbox('coupon_s', __('允许使用优惠码', 'aksrt'));
|
||||
$this->editor_checkbox('order_expired_s', __('启用订单有效期', 'aksrt'));
|
||||
$this->editor_section_open(__('销售', 'jio'));
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
$this->editor_number('pay_count', __('销量浮动', 'jio'), '', '1', '', __('在真实销量上增加或减少的数量,可填负数。', 'jio'), false);
|
||||
echo '<div class="jio-product-switches" role="group" aria-label="' . esc_attr__('销售选项', 'jio') . '">';
|
||||
$this->editor_checkbox('show_sales', __('前台显示销量', 'jio'));
|
||||
$this->editor_checkbox('coupon_s', __('允许使用优惠码', 'jio'));
|
||||
$this->editor_checkbox('order_expired_s', __('启用订单有效期', 'jio'));
|
||||
echo '</div></div>';
|
||||
echo '<fieldset class="aksrt-product-pane aksrt-product-expire" data-aksrt-product-pane="expire" hidden disabled><legend class="screen-reader-text">' . esc_html__('订单有效期', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('有效时长', 'aksrt') . '</span><span class="aksrt-product-inline">'
|
||||
. '<input type="number" name="aksrt_pay[order_expired_time]" min="1" step="1" required>'
|
||||
. '<select name="aksrt_pay[order_expired_unit]" aria-label="' . esc_attr__('时长单位', 'aksrt') . '">'
|
||||
. '<option value="day">' . esc_html__('天', 'aksrt') . '</option><option value="hour">' . esc_html__('小时', 'aksrt') . '</option><option value="minute">' . esc_html__('分钟', 'aksrt') . '</option>'
|
||||
. '</select></span><small class="aksrt-native-field__hint">' . esc_html__('购买后仅在有效期内可查看,过期需再次购买。', 'aksrt') . '</small></label></fieldset>';
|
||||
echo '<fieldset class="jio-product-pane jio-product-expire" data-jio-product-pane="expire" hidden disabled><legend class="screen-reader-text">' . esc_html__('订单有效期', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('有效时长', 'jio') . '</span><span class="jio-product-inline">'
|
||||
. '<input type="number" name="jio_pay[order_expired_time]" min="1" step="1" required>'
|
||||
. '<select name="jio_pay[order_expired_unit]" aria-label="' . esc_attr__('时长单位', 'jio') . '">'
|
||||
. '<option value="day">' . esc_html__('天', 'jio') . '</option><option value="hour">' . esc_html__('小时', 'jio') . '</option><option value="minute">' . esc_html__('分钟', 'jio') . '</option>'
|
||||
. '</select></span><small class="jio-native-field__hint">' . esc_html__('购买后仅在有效期内可查看,过期需再次购买。', 'jio') . '</small></label></fieldset>';
|
||||
$this->editor_section_close();
|
||||
|
||||
$this->editor_section_open(__('商品文案', 'aksrt'));
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('商品标题', 'aksrt') . '</span><input type="text" name="aksrt_pay[pay_title]" maxlength="200">'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('留空则使用文章标题。', 'aksrt') . '</small></label>';
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('商品简介', 'aksrt') . '</span><textarea name="aksrt_pay[pay_doc]" rows="2"></textarea></label>';
|
||||
$this->editor_section_open(__('商品文案', 'jio'));
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('商品标题', 'jio') . '</span><input type="text" name="jio_pay[pay_title]" maxlength="200">'
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('留空则使用文章标题。', 'jio') . '</small></label>';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('商品简介', 'jio') . '</span><textarea name="jio_pay[pay_doc]" rows="2"></textarea></label>';
|
||||
echo '</div>';
|
||||
$this->editor_section_close();
|
||||
echo '</div>';
|
||||
|
||||
echo '<p class="aksrt-native-callout aksrt-product-note">' . esc_html__('下载资源、付费图片、视频剧集和隐藏内容请在文章编辑页的「付费功能」面板维护。', 'aksrt')
|
||||
. ' <a href="#" target="_blank" rel="noopener noreferrer" data-aksrt-product-edit-link>' . esc_html__('打开文章编辑页', 'aksrt') . '</a></p>';
|
||||
echo '<p class="jio-native-callout jio-product-note">' . esc_html__('下载资源、付费图片、视频剧集和隐藏内容请在文章编辑页的「付费功能」面板维护。', 'jio')
|
||||
. ' <a href="#" target="_blank" rel="noopener noreferrer" data-jio-product-edit-link>' . esc_html__('打开文章编辑页', 'jio') . '</a></p>';
|
||||
|
||||
echo '</div><footer class="aksrt-product-dialog__foot">';
|
||||
echo '<button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-product-submit>' . esc_html__('保存设置', 'aksrt') . '</button>';
|
||||
echo '</div><footer class="jio-product-dialog__foot">';
|
||||
echo '<button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-product-submit>' . esc_html__('保存设置', 'jio') . '</button>';
|
||||
echo '</footer></form></dialog>';
|
||||
}
|
||||
|
||||
private function editor_section_open(string $title, string $hint = ''): void {
|
||||
echo '<section class="aksrt-product-section"><h3>' . esc_html($title) . '</h3>';
|
||||
echo '<section class="jio-product-section"><h3>' . esc_html($title) . '</h3>';
|
||||
if ($hint !== '') {
|
||||
echo '<p class="aksrt-product-section__hint">' . esc_html($hint) . '</p>';
|
||||
echo '<p class="jio-product-section__hint">' . esc_html($hint) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,29 +140,29 @@ trait PayProductsEditor {
|
||||
|
||||
/** @param array<string, string> $options */
|
||||
private function editor_radios(string $key, string $title, array $options, string $pane = ''): void {
|
||||
echo '<fieldset class="aksrt-native-field aksrt-product-radios"' . ($pane !== '' ? ' data-aksrt-product-pane="' . esc_attr($pane) . '"' : '') . '>';
|
||||
echo '<legend class="aksrt-native-field__label">' . esc_html($title) . '</legend><div>';
|
||||
echo '<fieldset class="jio-native-field jio-product-radios"' . ($pane !== '' ? ' data-jio-product-pane="' . esc_attr($pane) . '"' : '') . '>';
|
||||
echo '<legend class="jio-native-field__label">' . esc_html($title) . '</legend><div>';
|
||||
foreach ($options as $value => $label) {
|
||||
echo '<label><input type="radio" name="aksrt_pay[' . esc_attr($key) . ']" value="' . esc_attr((string) $value) . '"><span>' . esc_html($label) . '</span></label>';
|
||||
echo '<label><input type="radio" name="jio_pay[' . esc_attr($key) . ']" value="' . esc_attr((string) $value) . '"><span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</div></fieldset>';
|
||||
}
|
||||
|
||||
private function editor_number(string $key, string $title, string $unit, string $step, string $attributes = '', string $hint = '', bool $non_negative = true): void {
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html($title) . '</span><span class="aksrt-product-inline">';
|
||||
echo '<input type="number" name="aksrt_pay[' . esc_attr($key) . ']" step="' . esc_attr($step) . '"' . ($non_negative ? ' min="0"' : '') . ' required ' . $attributes . '>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html($title) . '</span><span class="jio-product-inline">';
|
||||
echo '<input type="number" name="jio_pay[' . esc_attr($key) . ']" step="' . esc_attr($step) . '"' . ($non_negative ? ' min="0"' : '') . ' required ' . $attributes . '>';
|
||||
if ($unit !== '') {
|
||||
echo '<span class="aksrt-product-unit">' . esc_html($unit) . '</span>';
|
||||
echo '<span class="jio-product-unit">' . esc_html($unit) . '</span>';
|
||||
}
|
||||
echo '</span>';
|
||||
if ($hint !== '') {
|
||||
echo '<small class="aksrt-native-field__hint">' . esc_html($hint) . '</small>';
|
||||
echo '<small class="jio-native-field__hint">' . esc_html($hint) . '</small>';
|
||||
}
|
||||
echo '</label>';
|
||||
}
|
||||
|
||||
private function editor_checkbox(string $key, string $label): void {
|
||||
echo '<label class="aksrt-product-check"><input type="checkbox" name="aksrt_pay[' . esc_attr($key) . ']" value="1">' . esc_html($label) . '</label>';
|
||||
echo '<label class="jio-product-check"><input type="checkbox" name="jio_pay[' . esc_attr($key) . ']" value="1">' . esc_html($label) . '</label>';
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
@@ -171,7 +171,7 @@ trait PayProductsEditor {
|
||||
|
||||
public function handle_save(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限执行此操作。', 'aksrt'), '', ['response' => 403]);
|
||||
wp_die(esc_html__('您没有权限执行此操作。', 'jio'), '', ['response' => 403]);
|
||||
}
|
||||
check_admin_referer(self::SAVE_ACTION);
|
||||
|
||||
@@ -182,8 +182,8 @@ trait PayProductsEditor {
|
||||
$this->redirect_after_save($context, 'product_missing', 0);
|
||||
}
|
||||
|
||||
$raw = isset($_POST['aksrt_pay']) && is_array($_POST['aksrt_pay']) ? wp_unslash($_POST['aksrt_pay']) : [];
|
||||
$meta = aksrt_get_pay_meta($post);
|
||||
$raw = isset($_POST['jio_pay']) && is_array($_POST['jio_pay']) ? wp_unslash($_POST['jio_pay']) : [];
|
||||
$meta = jio_get_pay_meta($post);
|
||||
|
||||
$pay_type = DomainModel::normalize_pay_type((string) ($raw['pay_type'] ?? ''));
|
||||
if ($pay_type === DomainModel::PAY_TYPE_NO) {
|
||||
@@ -282,7 +282,7 @@ trait PayProductsEditor {
|
||||
|
||||
/** @param array<string, string|int> $context */
|
||||
private function redirect_after_save(array $context, string $code, int $post_id): void {
|
||||
$args = $context + ['aksrt_pay_msg' => $code];
|
||||
$args = $context + ['jio_pay_msg' => $code];
|
||||
if ($post_id > 0) {
|
||||
$args['product'] = $post_id;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayProducts 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Core\DomainModel;
|
||||
use AKSRT\Ledger\AssetAmount;
|
||||
use AKSRT\Payment\OrderRepository;
|
||||
use Jio\Core\DomainModel;
|
||||
use Jio\Ledger\AssetAmount;
|
||||
use Jio\Payment\OrderRepository;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -54,25 +54,25 @@ trait PayProductsList {
|
||||
// 翻页链接显式带上当前筛选条件(不含 paged)。
|
||||
$query_args = array_filter(['pay_type' => $pay_type, 's' => $search], static fn(string $value): bool => $value !== '');
|
||||
|
||||
NativeUi::card_open('', '', true, 'aksrt-products-card');
|
||||
NativeUi::card_open('', '', true, 'jio-products-card');
|
||||
$this->render_product_toolbar($pay_type, $search, $type_labels);
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<table class="wp-list-table widefat fixed striped aksrt-products-table"><thead><tr>';
|
||||
echo '<th scope="col" class="column-product">' . esc_html__('商品', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-type">' . esc_html__('付费类型', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-price">' . esc_html__('价格', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-sales">' . esc_html__('销量', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-revenue">' . esc_html__('销售额', 'aksrt') . '</th>';
|
||||
echo '<th scope="col" class="column-actions">' . esc_html__('操作', 'aksrt') . '</th>';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
echo '<table class="wp-list-table widefat fixed striped jio-products-table"><thead><tr>';
|
||||
echo '<th scope="col" class="column-product">' . esc_html__('商品', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-type">' . esc_html__('付费类型', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-price">' . esc_html__('价格', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-sales">' . esc_html__('销量', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-revenue">' . esc_html__('销售额', 'jio') . '</th>';
|
||||
echo '<th scope="col" class="column-actions">' . esc_html__('操作', 'jio') . '</th>';
|
||||
echo '</tr></thead><tbody>';
|
||||
|
||||
if (!$posts) {
|
||||
echo NativeUi::empty_row(
|
||||
6,
|
||||
__('暂无付费文章', 'aksrt'),
|
||||
__('暂无付费文章', 'jio'),
|
||||
$query_args !== []
|
||||
? __('调整类型或搜索条件后重试。', 'aksrt')
|
||||
: __('在文章编辑页的「付费功能」面板开启付费后,文章会出现在这里。', 'aksrt')
|
||||
? __('调整类型或搜索条件后重试。', 'jio')
|
||||
: __('在文章编辑页的「付费功能」面板开启付费后,文章会出现在这里。', 'jio')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ trait PayProductsList {
|
||||
}
|
||||
|
||||
/**
|
||||
* 付费文章 = `_aksrt_pay_type` 为任一付费类型;所有写入口都同步这个派生键,直接按它查询分页。
|
||||
* 付费文章 = `_jio_pay_type` 为任一付费类型;所有写入口都同步这个派生键,直接按它查询分页。
|
||||
*
|
||||
* @return array{posts: \WP_Post[], total: int}
|
||||
*/
|
||||
@@ -152,9 +152,9 @@ trait PayProductsList {
|
||||
$counts = $this->paid_type_counts();
|
||||
$keep = $search !== '' ? ['s' => $search] : [];
|
||||
|
||||
echo '<div class="aksrt-products-toolbar">';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('付费类型筛选', 'aksrt') . '">';
|
||||
foreach (['' => __('全部', 'aksrt')] + $type_labels as $key => $label) {
|
||||
echo '<div class="jio-products-toolbar">';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('付费类型筛选', 'jio') . '">';
|
||||
foreach (['' => __('全部', 'jio')] + $type_labels as $key => $label) {
|
||||
$key = (string) $key;
|
||||
$count = $key === '' ? array_sum($counts) : (int) ($counts[$key] ?? 0);
|
||||
$url = $this->page_url(self::PAGE_SLUG, $key === '' ? $keep : $keep + ['pay_type' => $key]);
|
||||
@@ -163,17 +163,17 @@ trait PayProductsList {
|
||||
}
|
||||
echo '</ul>';
|
||||
|
||||
echo '<form method="get" action="' . esc_url(admin_url('admin.php')) . '" class="aksrt-native-filters" role="search">';
|
||||
echo '<form method="get" action="' . esc_url(admin_url('admin.php')) . '" class="jio-native-filters" role="search">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '">';
|
||||
if ($pay_type !== '') {
|
||||
echo '<input type="hidden" name="pay_type" value="' . esc_attr($pay_type) . '">';
|
||||
}
|
||||
echo '<label class="screen-reader-text" for="aksrt-products-search">' . esc_html__('搜索标题或文章 ID', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-products-search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('搜索标题或文章 ID', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'aksrt') . '</button>';
|
||||
echo '<label class="screen-reader-text" for="jio-products-search">' . esc_html__('搜索标题或文章 ID', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-products-search" name="s" value="' . esc_attr($search) . '" placeholder="' . esc_attr__('搜索标题或文章 ID', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'jio') . '</button>';
|
||||
if ($search !== '') {
|
||||
$clear = $this->page_url(self::PAGE_SLUG, $pay_type !== '' ? ['pay_type' => $pay_type] : []);
|
||||
echo '<a class="button-link" href="' . esc_url($clear) . '">' . esc_html__('清除搜索', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($clear) . '">' . esc_html__('清除搜索', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form></div>';
|
||||
}
|
||||
@@ -184,11 +184,11 @@ trait PayProductsList {
|
||||
*/
|
||||
private function render_product_row(\WP_Post $post, array $sales, array $type_labels, string $currency): void {
|
||||
$post_id = (int) $post->ID;
|
||||
$meta = aksrt_get_pay_meta($post);
|
||||
$meta = jio_get_pay_meta($post);
|
||||
$pay_type = DomainModel::normalize_pay_type((string) $meta['pay_type']);
|
||||
$title = get_the_title($post);
|
||||
if ($title === '') {
|
||||
$title = sprintf(__('(无标题 #%d)', 'aksrt'), $post_id);
|
||||
$title = sprintf(__('(无标题 #%d)', 'jio'), $post_id);
|
||||
}
|
||||
$edit_url = (string) get_edit_post_link($post_id, 'raw');
|
||||
$permalink = (string) get_permalink($post_id);
|
||||
@@ -201,60 +201,60 @@ trait PayProductsList {
|
||||
echo $edit_url !== ''
|
||||
? '<a class="row-title" href="' . esc_url($edit_url) . '">' . esc_html($title) . '</a>'
|
||||
: esc_html($title);
|
||||
echo '</strong><div class="aksrt-products-sub">#' . esc_html((string) $post_id);
|
||||
echo '</strong><div class="jio-products-sub">#' . esc_html((string) $post_id);
|
||||
if ($post->post_status !== 'publish') {
|
||||
$status = get_post_status_object($post->post_status);
|
||||
echo ' · ' . esc_html($status ? (string) $status->label : $post->post_status);
|
||||
}
|
||||
if ((string) $meta['pay_title'] !== '') {
|
||||
echo ' · ' . esc_html(sprintf(__('商品标题:%s', 'aksrt'), (string) $meta['pay_title']));
|
||||
echo ' · ' . esc_html(sprintf(__('商品标题:%s', 'jio'), (string) $meta['pay_title']));
|
||||
}
|
||||
echo '</div></td>';
|
||||
|
||||
echo '<td class="column-type">' . NativeUi::badge($type_labels[$pay_type] ?? $pay_type, self::TYPE_TONES[$pay_type] ?? 'neutral');
|
||||
$limit = (int) $meta['pay_limit'];
|
||||
if ($limit > 0) {
|
||||
echo '<div class="aksrt-products-sub">' . esc_html($this->pay_limit_labels()[$limit] ?? '') . '</div>';
|
||||
echo '<div class="jio-products-sub">' . esc_html($this->pay_limit_labels()[$limit] ?? '') . '</div>';
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
echo '<td class="column-price">' . $this->price_cell_html($meta, $currency) . '</td>';
|
||||
|
||||
echo '<td class="column-sales"><span class="aksrt-native-amount">' . esc_html(sprintf(__('%d 单', 'aksrt'), $orders)) . '</span>';
|
||||
echo '<div class="aksrt-products-sub">' . esc_html(!empty($meta['show_sales'])
|
||||
? sprintf(__('前台显示 %d', 'aksrt'), aksrt_get_content_sales_count($post, $meta))
|
||||
: __('前台不显示销量', 'aksrt')) . '</div></td>';
|
||||
echo '<td class="column-sales"><span class="jio-native-amount">' . esc_html(sprintf(__('%d 单', 'jio'), $orders)) . '</span>';
|
||||
echo '<div class="jio-products-sub">' . esc_html(!empty($meta['show_sales'])
|
||||
? sprintf(__('前台显示 %d', 'jio'), jio_get_content_sales_count($post, $meta))
|
||||
: __('前台不显示销量', 'jio')) . '</div></td>';
|
||||
|
||||
echo '<td class="column-revenue"><span class="aksrt-native-amount">' . esc_html($this->sales_amount($sales['revenue'] ?? '0.00', $currency)) . '</span>';
|
||||
echo '<td class="column-revenue"><span class="jio-native-amount">' . esc_html($this->sales_amount($sales['revenue'] ?? '0.00', $currency)) . '</span>';
|
||||
if ($orders > 0) {
|
||||
echo '<div class="aksrt-products-sub">' . esc_html(sprintf(__('均价 %s', 'aksrt'), $this->sales_amount($sales['avg_amount'] ?? '0.00', $currency))) . '</div>';
|
||||
echo '<div class="jio-products-sub">' . esc_html(sprintf(__('均价 %s', 'jio'), $this->sales_amount($sales['avg_amount'] ?? '0.00', $currency))) . '</div>';
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
$payload = $this->product_payload($post, $meta, $edit_url);
|
||||
echo '<td class="column-actions">';
|
||||
echo '<button type="button" class="button button-small button-primary" data-aksrt-product-edit="' . esc_attr((string) wp_json_encode($payload)) . '" aria-haspopup="dialog" aria-controls="aksrt-product-editor">'
|
||||
. esc_html__('快速设置', 'aksrt') . '</button>';
|
||||
echo '<div class="aksrt-products-links">';
|
||||
echo '<button type="button" class="button button-small button-primary" data-jio-product-edit="' . esc_attr((string) wp_json_encode($payload)) . '" aria-haspopup="dialog" aria-controls="jio-product-editor">'
|
||||
. esc_html__('快速设置', 'jio') . '</button>';
|
||||
echo '<div class="jio-products-links">';
|
||||
if ($edit_url !== '') {
|
||||
echo '<a href="' . esc_url($edit_url) . '">' . esc_html__('编辑文章', 'aksrt') . '</a>';
|
||||
echo '<a href="' . esc_url($edit_url) . '">' . esc_html__('编辑文章', 'jio') . '</a>';
|
||||
}
|
||||
$orders_url = $this->page_url('aksrt_orders', [
|
||||
$orders_url = $this->page_url('jio_orders', [
|
||||
'product_type' => DomainModel::ORDER_PRODUCT_RESOURCE,
|
||||
'product_id' => $post_id,
|
||||
]);
|
||||
echo '<a href="' . esc_url($orders_url) . '">' . esc_html__('订单', 'aksrt') . '</a>';
|
||||
echo '<a href="' . esc_url($orders_url) . '">' . esc_html__('订单', 'jio') . '</a>';
|
||||
if ($permalink !== '') {
|
||||
echo '<a href="' . esc_url($permalink) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看', 'aksrt') . '</a>';
|
||||
echo '<a href="' . esc_url($permalink) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看', 'jio') . '</a>';
|
||||
}
|
||||
echo '</div></td></tr>';
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $meta */
|
||||
private function price_cell_html(array $meta, string $currency): string {
|
||||
$free = __('免费', 'aksrt');
|
||||
$free = __('免费', 'jio');
|
||||
if ($meta['pay_modo'] === 'points') {
|
||||
$fmt = static fn(int $points): string => $points > 0 ? sprintf(__('%d 积分', 'aksrt'), $points) : $free;
|
||||
$fmt = static fn(int $points): string => $points > 0 ? sprintf(__('%d 积分', 'jio'), $points) : $free;
|
||||
$main = $fmt((int) $meta['points_price']);
|
||||
$original = '';
|
||||
$vip = [$fmt((int) $meta['vip1_points']), $fmt((int) $meta['vip2_points'])];
|
||||
@@ -267,15 +267,15 @@ trait PayProductsList {
|
||||
$vip = [$fmt((string) $meta['vip1_price']), $fmt((string) $meta['vip2_price'])];
|
||||
}
|
||||
|
||||
return '<span class="aksrt-native-amount">' . esc_html($main) . '</span>'
|
||||
. ($original !== '' ? ' <del class="aksrt-native-muted">' . esc_html($original) . '</del>' : '')
|
||||
. '<div class="aksrt-products-sub">' . esc_html(sprintf(__('VIP1 %1$s · VIP2 %2$s', 'aksrt'), $vip[0], $vip[1])) . '</div>';
|
||||
return '<span class="jio-native-amount">' . esc_html($main) . '</span>'
|
||||
. ($original !== '' ? ' <del class="jio-native-muted">' . esc_html($original) . '</del>' : '')
|
||||
. '<div class="jio-products-sub">' . esc_html(sprintf(__('VIP1 %1$s · VIP2 %2$s', 'jio'), $vip[0], $vip[1])) . '</div>';
|
||||
}
|
||||
|
||||
/** 订单统计里积分订单为 int、现金订单为两位小数字符串。 */
|
||||
private function sales_amount($amount, string $currency): string {
|
||||
return is_int($amount)
|
||||
? sprintf(__('%d 积分', 'aksrt'), $amount)
|
||||
? sprintf(__('%d 积分', 'jio'), $amount)
|
||||
: $currency . $this->cash_decimal((string) $amount);
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
* admin-post 处理器与共用的展示助手。页面样式在 assets/css/admin/withdrawals.css,
|
||||
* 处理弹窗、复制收款账户与防重复提交在 assets/js/admin-withdrawals.js。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Payment\Withdrawal;
|
||||
use Jio\Payment\Withdrawal;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -20,7 +20,7 @@ class PayWithdrawals {
|
||||
use PayWithdrawalsList;
|
||||
|
||||
public const CAPABILITY = 'manage_options';
|
||||
public const PAGE_SLUG = 'aksrt_withdraw';
|
||||
public const PAGE_SLUG = 'jio_withdraw';
|
||||
|
||||
/** 后台可发起的目标状态。 */
|
||||
private const TARGET_STATUSES = [Withdrawal::STATUS_PROCESSING, Withdrawal::STATUS_COMPLETED, Withdrawal::STATUS_REJECTED];
|
||||
@@ -39,7 +39,7 @@ class PayWithdrawals {
|
||||
return;
|
||||
}
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
add_action('admin_post_aksrt_pay_update_withdrawal', [$this, 'handle_update_withdrawal']);
|
||||
add_action('admin_post_jio_pay_update_withdrawal', [$this, 'handle_update_withdrawal']);
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
@@ -49,17 +49,17 @@ class PayWithdrawals {
|
||||
}
|
||||
$css = '/assets/css/admin/withdrawals.css';
|
||||
$js = '/assets/js/admin-withdrawals.js';
|
||||
wp_enqueue_style('aksrt-admin-withdrawals', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-withdrawals', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-withdrawals', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-withdrawals', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_withdrawals_page(): void {
|
||||
$this->ensure_cap();
|
||||
NativeUi::page_open('aksrt-wd-page');
|
||||
NativeUi::page_open('jio-wd-page');
|
||||
|
||||
if (!class_exists('\AKSRT\Payment\Withdrawal')) {
|
||||
NativeUi::header(__('提现审核', 'aksrt'), '', 'dashicons-money-alt');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('提现模块未初始化。', 'aksrt') . '</p></div>';
|
||||
if (!class_exists('\Jio\Payment\Withdrawal')) {
|
||||
NativeUi::header(__('提现审核', 'jio'), '', 'dashicons-money-alt');
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__('提现模块未初始化。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -75,39 +75,39 @@ class PayWithdrawals {
|
||||
public function handle_update_withdrawal(): void {
|
||||
$this->ensure_cap();
|
||||
$id = isset($_POST['withdrawal_id']) ? (int) $_POST['withdrawal_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_update_withdrawal_' . $id);
|
||||
check_admin_referer('jio_pay_update_withdrawal_' . $id);
|
||||
|
||||
$new_status = isset($_POST['new_status']) ? sanitize_key((string) wp_unslash($_POST['new_status'])) : '';
|
||||
$note = isset($_POST['note']) ? trim(sanitize_textarea_field((string) wp_unslash($_POST['note']))) : '';
|
||||
$request_key = isset($_POST['idempotency_key']) ? sanitize_text_field((string) wp_unslash($_POST['idempotency_key'])) : '';
|
||||
|
||||
if ($id <= 0 || !in_array($new_status, self::TARGET_STATUSES, true) || !wp_is_uuid($request_key)) {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'withdraw_failed']);
|
||||
$this->redirect_back(['jio_pay_msg' => 'withdraw_failed']);
|
||||
}
|
||||
// 拒绝原因会随站内信通知用户,必须填写。
|
||||
if ($new_status === Withdrawal::STATUS_REJECTED && $note === '') {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'withdraw_note_required']);
|
||||
$this->redirect_back(['jio_pay_msg' => 'withdraw_note_required']);
|
||||
}
|
||||
|
||||
$operation_key = \AKSRT\Ledger\OperationKey::deterministic('admin-withdraw-transition', [get_current_user_id(), strtolower($request_key)]);
|
||||
$operation_key = \Jio\Ledger\OperationKey::deterministic('admin-withdraw-transition', [get_current_user_id(), strtolower($request_key)]);
|
||||
$result = Withdrawal::get_instance()->process_status_change($id, $new_status, $note, $operation_key);
|
||||
|
||||
if (is_array($result)) {
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => 'withdraw_' . $new_status,
|
||||
'jio_pay_msg' => 'withdraw_' . $new_status,
|
||||
'number' => (string) ($result['withdrawal_number'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
$code = is_wp_error($result) ? $result->get_error_code() : '';
|
||||
$msg = [
|
||||
'aksrt_withdraw_transition' => 'withdraw_stale',
|
||||
'aksrt_withdraw_idempotency_conflict' => 'withdraw_stale',
|
||||
'aksrt_balance_insufficient' => 'withdraw_refund_failed',
|
||||
'jio_withdraw_transition' => 'withdraw_stale',
|
||||
'jio_withdraw_idempotency_conflict' => 'withdraw_stale',
|
||||
'jio_balance_insufficient' => 'withdraw_refund_failed',
|
||||
][$code] ?? 'withdraw_failed';
|
||||
|
||||
// 可重试的失败带回同一个幂等键:重提同一操作不会重复入账。
|
||||
$args = ['aksrt_pay_msg' => $msg];
|
||||
$args = ['jio_pay_msg' => $msg];
|
||||
if ($msg !== 'withdraw_stale') {
|
||||
$args['retry_id'] = $id;
|
||||
$args['retry_key'] = $request_key;
|
||||
@@ -122,7 +122,7 @@ class PayWithdrawals {
|
||||
if (is_string($referer) && $referer !== '') {
|
||||
parse_str((string) wp_parse_url($referer, PHP_URL_QUERY), $parsed);
|
||||
if (($parsed['page'] ?? '') === self::PAGE_SLUG) {
|
||||
unset($parsed['page'], $parsed['aksrt_pay_msg'], $parsed['number'], $parsed['retry_id'], $parsed['retry_key']);
|
||||
unset($parsed['page'], $parsed['jio_pay_msg'], $parsed['number'], $parsed['retry_id'], $parsed['retry_key']);
|
||||
$query = $parsed;
|
||||
}
|
||||
}
|
||||
@@ -137,10 +137,10 @@ class PayWithdrawals {
|
||||
/** @return array<string, string> */
|
||||
private function status_labels(): array {
|
||||
return [
|
||||
Withdrawal::STATUS_PENDING => __('待处理', 'aksrt'),
|
||||
Withdrawal::STATUS_PROCESSING => __('处理中', 'aksrt'),
|
||||
Withdrawal::STATUS_COMPLETED => __('已完成', 'aksrt'),
|
||||
Withdrawal::STATUS_REJECTED => __('已拒绝', 'aksrt'),
|
||||
Withdrawal::STATUS_PENDING => __('待处理', 'jio'),
|
||||
Withdrawal::STATUS_PROCESSING => __('处理中', 'jio'),
|
||||
Withdrawal::STATUS_COMPLETED => __('已完成', 'jio'),
|
||||
Withdrawal::STATUS_REJECTED => __('已拒绝', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -171,22 +171,22 @@ class PayWithdrawals {
|
||||
/** @return array<string, string> */
|
||||
private function source_labels(): array {
|
||||
return [
|
||||
Withdrawal::SOURCE_BALANCE => __('余额钱包', 'aksrt'),
|
||||
Withdrawal::SOURCE_COMMISSION => __('推广佣金', 'aksrt'),
|
||||
Withdrawal::SOURCE_BALANCE => __('余额钱包', 'jio'),
|
||||
Withdrawal::SOURCE_COMMISSION => __('推广佣金', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
private function method_labels(): array {
|
||||
return [
|
||||
'alipay' => __('支付宝', 'aksrt'),
|
||||
'wechat' => __('微信', 'aksrt'),
|
||||
'alipay' => __('支付宝', 'jio'),
|
||||
'wechat' => __('微信', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
private function format_money(string $decimal): string {
|
||||
try {
|
||||
return '¥' . \AKSRT\Ledger\AssetAmount::balance($decimal)->formatted();
|
||||
return '¥' . \Jio\Ledger\AssetAmount::balance($decimal)->formatted();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '¥0.00';
|
||||
}
|
||||
@@ -195,11 +195,11 @@ class PayWithdrawals {
|
||||
/** 手续费 = 申请毛额 − 到账净额,按分计算避免浮点误差。 */
|
||||
private function fee_decimal(string $gross, string $net): string {
|
||||
try {
|
||||
$fee = \AKSRT\Ledger\AssetAmount::balance($gross)->minor() - \AKSRT\Ledger\AssetAmount::balance($net)->minor();
|
||||
$fee = \Jio\Ledger\AssetAmount::balance($gross)->minor() - \Jio\Ledger\AssetAmount::balance($net)->minor();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '0.00';
|
||||
}
|
||||
return \AKSRT\Ledger\AssetAmount::decimal_from_minor(max(0, $fee));
|
||||
return \Jio\Ledger\AssetAmount::decimal_from_minor(max(0, $fee));
|
||||
}
|
||||
|
||||
/** 行内操作的幂等键:上次失败的那一行沿用带回的键,其余行每次渲染新生成。 */
|
||||
@@ -208,21 +208,21 @@ class PayWithdrawals {
|
||||
}
|
||||
|
||||
private function render_flash(): void {
|
||||
if (empty($_GET['aksrt_pay_msg'])) {
|
||||
if (empty($_GET['jio_pay_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_pay_msg']));
|
||||
$number = isset($_GET['number']) ? sanitize_text_field((string) wp_unslash($_GET['number'])) : '';
|
||||
$suffix = $number !== '' ? sprintf(__('(订单号 %s)', 'aksrt'), $number) : '';
|
||||
$suffix = $number !== '' ? sprintf(__('(订单号 %s)', 'jio'), $number) : '';
|
||||
// 码 => [文案, notice 类型]
|
||||
$map = [
|
||||
'withdraw_processing' => [__('已标记为处理中,请尽快完成打款。', 'aksrt') . $suffix, 'success'],
|
||||
'withdraw_completed' => [__('已确认打款完成,用户已收到通知。', 'aksrt') . $suffix, 'success'],
|
||||
'withdraw_rejected' => [__('已拒绝提现,冻结金额已退回用户,拒绝原因已通知用户。', 'aksrt') . $suffix, 'success'],
|
||||
'withdraw_note_required' => [__('拒绝提现必须填写拒绝原因。', 'aksrt'), 'warning'],
|
||||
'withdraw_stale' => [__('操作未执行:该申请的状态已被更新,请查看最新状态后再处理。', 'aksrt'), 'warning'],
|
||||
'withdraw_refund_failed' => [__('状态未更新:冻结金额退回失败,请检查用户钱包后重试。', 'aksrt'), 'error'],
|
||||
'withdraw_failed' => [__('提现状态更新失败,请重试。重试不会重复入账。', 'aksrt'), 'error'],
|
||||
'withdraw_processing' => [__('已标记为处理中,请尽快完成打款。', 'jio') . $suffix, 'success'],
|
||||
'withdraw_completed' => [__('已确认打款完成,用户已收到通知。', 'jio') . $suffix, 'success'],
|
||||
'withdraw_rejected' => [__('已拒绝提现,冻结金额已退回用户,拒绝原因已通知用户。', 'jio') . $suffix, 'success'],
|
||||
'withdraw_note_required' => [__('拒绝提现必须填写拒绝原因。', 'jio'), 'warning'],
|
||||
'withdraw_stale' => [__('操作未执行:该申请的状态已被更新,请查看最新状态后再处理。', 'jio'), 'warning'],
|
||||
'withdraw_refund_failed' => [__('状态未更新:冻结金额退回失败,请检查用户钱包后重试。', 'jio'), 'error'],
|
||||
'withdraw_failed' => [__('提现状态更新失败,请重试。重试不会重复入账。', 'jio'), 'error'],
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
@@ -233,7 +233,7 @@ class PayWithdrawals {
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
*
|
||||
* 作为 flat sibling trait 被 PayWithdrawals 使用(PSR-4:trait 必须与宿主类同目录)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Payment\Withdrawal;
|
||||
use Jio\Payment\Withdrawal;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,12 +17,12 @@ trait PayWithdrawalsList {
|
||||
|
||||
private function render_withdrawals_list_page(): void {
|
||||
NativeUi::header(
|
||||
__('提现审核', 'aksrt'),
|
||||
__('用户发起的余额与推广佣金提现申请。按收款信息线下打款后,在这里确认完成;拒绝会把冻结金额退回原资金池。', 'aksrt'),
|
||||
__('提现审核', 'jio'),
|
||||
__('用户发起的余额与推广佣金提现申请。按收款信息线下打款后,在这里确认完成;拒绝会把冻结金额退回原资金池。', 'jio'),
|
||||
'dashicons-money-alt',
|
||||
[[
|
||||
'label' => __('提现策略', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/payment/withdrawal'),
|
||||
'label' => __('提现策略', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/payment/withdrawal'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
]]
|
||||
);
|
||||
@@ -78,17 +78,17 @@ trait PayWithdrawalsList {
|
||||
$this->render_status_views($filters['status'], $query_args, $counts);
|
||||
$this->render_filter_card($filters, $query_args);
|
||||
|
||||
NativeUi::table_card_open(__('提现申请', 'aksrt'));
|
||||
echo '<table class="wp-list-table widefat striped aksrt-wd-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('提现申请', 'aksrt') . '</caption><thead><tr>';
|
||||
NativeUi::table_card_open(__('提现申请', 'jio'));
|
||||
echo '<table class="wp-list-table widefat striped jio-wd-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('提现申请', 'jio') . '</caption><thead><tr>';
|
||||
foreach ([
|
||||
'number' => esc_html__('订单号', 'aksrt'),
|
||||
'user' => esc_html__('用户', 'aksrt'),
|
||||
'amount' => esc_html__('金额', 'aksrt'),
|
||||
'payee' => esc_html__('收款信息', 'aksrt'),
|
||||
'status' => esc_html__('状态', 'aksrt'),
|
||||
'note' => esc_html__('备注', 'aksrt'),
|
||||
'actions' => esc_html__('操作', 'aksrt'),
|
||||
'number' => esc_html__('订单号', 'jio'),
|
||||
'user' => esc_html__('用户', 'jio'),
|
||||
'amount' => esc_html__('金额', 'jio'),
|
||||
'payee' => esc_html__('收款信息', 'jio'),
|
||||
'status' => esc_html__('状态', 'jio'),
|
||||
'note' => esc_html__('备注', 'jio'),
|
||||
'actions' => esc_html__('操作', 'jio'),
|
||||
] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . $label . '</th>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
|
||||
}
|
||||
@@ -96,9 +96,9 @@ trait PayWithdrawalsList {
|
||||
|
||||
if (!$items) {
|
||||
$filtered = array_diff_key($query_args, ['status' => true]) !== [];
|
||||
echo NativeUi::empty_row(7, __('暂无提现记录', 'aksrt'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的申请,请调整或重置筛选。', 'aksrt')
|
||||
: __('用户在用户中心发起余额或佣金提现后,申请会出现在这里。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无提现记录', 'jio'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('没有符合当前筛选条件的申请,请调整或重置筛选。', 'jio')
|
||||
: __('用户在用户中心发起余额或佣金提现后,申请会出现在这里。', 'jio'));
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_withdrawal_row($row, $retry_id, $retry_key);
|
||||
@@ -110,9 +110,9 @@ trait PayWithdrawalsList {
|
||||
NativeUi::card_close();
|
||||
|
||||
$this->render_process_dialog();
|
||||
echo '<div class="aksrt-wd-toast" role="status" aria-live="polite" data-aksrt-wd-toast'
|
||||
. ' data-copied="' . esc_attr__('收款信息已复制', 'aksrt') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请手动选中后复制。', 'aksrt') . '"></div>';
|
||||
echo '<div class="jio-wd-toast" role="status" aria-live="polite" data-jio-wd-toast'
|
||||
. ' data-copied="' . esc_attr__('收款信息已复制', 'jio') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请手动选中后复制。', 'jio') . '"></div>';
|
||||
}
|
||||
|
||||
private function render_withdrawal_row(array $row, int $retry_id, string $retry_key): void {
|
||||
@@ -131,44 +131,44 @@ trait PayWithdrawalsList {
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : false;
|
||||
|
||||
echo '<tr' . ($retry_id === $id ? ' class="is-retry"' : '') . '>';
|
||||
echo '<td class="column-number"><code class="aksrt-native-mono">' . esc_html($number !== '' ? $number : '#' . $id) . '</code>'
|
||||
. '<div class="aksrt-wd-sub">' . esc_html(sprintf(__('%s 申请', 'aksrt'), (string) ($row['created_at'] ?? ''))) . '</div></td>';
|
||||
echo '<td class="column-number"><code class="jio-native-mono">' . esc_html($number !== '' ? $number : '#' . $id) . '</code>'
|
||||
. '<div class="jio-wd-sub">' . esc_html(sprintf(__('%s 申请', 'jio'), (string) ($row['created_at'] ?? ''))) . '</div></td>';
|
||||
echo '<td class="column-user">' . NativeUi::user_cell($user_id) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
. '<div class="aksrt-wd-sub">' . esc_html(sprintf(__('来源:%s', 'aksrt'), $source_label)) . '</div></td>';
|
||||
. '<div class="jio-wd-sub">' . esc_html(sprintf(__('来源:%s', 'jio'), $source_label)) . '</div></td>';
|
||||
|
||||
echo '<td class="column-amount"><strong class="aksrt-native-amount">' . esc_html($this->format_money($net)) . '</strong>'
|
||||
. '<div class="aksrt-wd-sub">' . esc_html(sprintf(
|
||||
echo '<td class="column-amount"><strong class="jio-native-amount">' . esc_html($this->format_money($net)) . '</strong>'
|
||||
. '<div class="jio-wd-sub">' . esc_html(sprintf(
|
||||
/* translators: 1 gross 2 fee 3 fee percent */
|
||||
__('申请 %1$s · 手续费 %2$s(%3$d%%)', 'aksrt'),
|
||||
__('申请 %1$s · 手续费 %2$s(%3$d%%)', 'jio'),
|
||||
$this->format_money($gross),
|
||||
$this->format_money($this->fee_decimal($gross, $net)),
|
||||
(int) ($row['fee_rate'] ?? 0)
|
||||
)) . '</div></td>';
|
||||
|
||||
echo '<td class="column-payee"><span class="aksrt-wd-method aksrt-wd-method--' . esc_attr(sanitize_html_class($method)) . '">' . esc_html($method_label) . '</span>';
|
||||
echo '<td class="column-payee"><span class="jio-wd-method jio-wd-method--' . esc_attr(sanitize_html_class($method)) . '">' . esc_html($method_label) . '</span>';
|
||||
if ($account !== '') {
|
||||
echo '<button type="button" class="aksrt-wd-account" data-clipboard-text="' . esc_attr($account) . '" aria-label="' . esc_attr(sprintf(__('复制收款信息:%s', 'aksrt'), $account)) . '">'
|
||||
echo '<button type="button" class="jio-wd-account" data-clipboard-text="' . esc_attr($account) . '" aria-label="' . esc_attr(sprintf(__('复制收款信息:%s', 'jio'), $account)) . '">'
|
||||
. '<span>' . esc_html($account) . '</span><span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button>';
|
||||
}
|
||||
echo '</td>';
|
||||
|
||||
$processed = (string) ($row['processed_at'] ?? '');
|
||||
echo '<td class="column-status">' . NativeUi::status_badge($status, $this->status_badge_map()) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
. ($processed !== '' ? '<div class="aksrt-wd-sub">' . esc_html(sprintf(__('%s 处理', 'aksrt'), $processed)) . '</div>' : '') . '</td>';
|
||||
. ($processed !== '' ? '<div class="jio-wd-sub">' . esc_html(sprintf(__('%s 处理', 'jio'), $processed)) . '</div>' : '') . '</td>';
|
||||
|
||||
echo '<td class="column-note">' . ($note !== ''
|
||||
? '<div class="aksrt-wd-note">' . nl2br(esc_html($note)) . '</div>'
|
||||
: '<span class="aksrt-native-muted">—</span>') . '</td>';
|
||||
? '<div class="jio-wd-note">' . nl2br(esc_html($note)) . '</div>'
|
||||
: '<span class="jio-native-muted">—</span>') . '</td>';
|
||||
|
||||
echo '<td class="column-actions">';
|
||||
$next = $this->next_statuses($status);
|
||||
if ($next === []) {
|
||||
echo '<span class="aksrt-native-muted">—</span></td></tr>';
|
||||
echo '<span class="jio-native-muted">—</span></td></tr>';
|
||||
return;
|
||||
}
|
||||
$data = [
|
||||
'id' => (string) $id,
|
||||
'nonce' => wp_create_nonce('aksrt_pay_update_withdrawal_' . $id),
|
||||
'nonce' => wp_create_nonce('jio_pay_update_withdrawal_' . $id),
|
||||
'key' => $this->request_key_for($id, $retry_id, $retry_key),
|
||||
'number' => $number !== '' ? $number : '#' . $id,
|
||||
'user' => $user ? $user->display_name . ' #' . $user_id : '#' . $user_id,
|
||||
@@ -182,38 +182,38 @@ trait PayWithdrawalsList {
|
||||
$attrs .= ' data-' . $key . '="' . esc_attr($value) . '"';
|
||||
}
|
||||
$buttons = [
|
||||
Withdrawal::STATUS_PROCESSING => [__('开始处理', 'aksrt'), 'button-primary'],
|
||||
Withdrawal::STATUS_COMPLETED => [__('确认打款', 'aksrt'), 'button-primary'],
|
||||
Withdrawal::STATUS_REJECTED => [__('拒绝', 'aksrt'), 'is-danger'],
|
||||
Withdrawal::STATUS_PROCESSING => [__('开始处理', 'jio'), 'button-primary'],
|
||||
Withdrawal::STATUS_COMPLETED => [__('确认打款', 'jio'), 'button-primary'],
|
||||
Withdrawal::STATUS_REJECTED => [__('拒绝', 'jio'), 'is-danger'],
|
||||
];
|
||||
echo '<div class="aksrt-wd-actions">';
|
||||
echo '<div class="jio-wd-actions">';
|
||||
foreach ($next as $target) {
|
||||
[$label, $class] = $buttons[$target];
|
||||
echo '<button type="button" class="button button-small ' . esc_attr($class) . '" data-aksrt-wd-open="' . esc_attr($target) . '"' . $attrs // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
|
||||
. ' aria-haspopup="dialog" aria-controls="aksrt-wd-dialog">' . esc_html($label) . '</button>';
|
||||
echo '<button type="button" class="button button-small ' . esc_attr($class) . '" data-jio-wd-open="' . esc_attr($target) . '"' . $attrs // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.
|
||||
. ' aria-haspopup="dialog" aria-controls="jio-wd-dialog">' . esc_html($label) . '</button>';
|
||||
}
|
||||
echo '</div></td></tr>';
|
||||
}
|
||||
|
||||
/** 处理流程 + 当前提现策略;有待办时用警示色提醒冻结金额。 */
|
||||
private function render_process_callout(array $stats): void {
|
||||
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_withdrawal_settings() : [];
|
||||
$settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_withdrawal_settings() : [];
|
||||
$open = (int) ($stats['pending'] ?? 0) + (int) ($stats['processing'] ?? 0);
|
||||
$policy = sprintf(
|
||||
/* translators: 1 min amount 2 fee percent 3 daily count 4 daily limit */
|
||||
__('当前策略:最低 %1$s,手续费 %2$d%%,每日 %3$d 次 / %4$s。', 'aksrt'),
|
||||
__('当前策略:最低 %1$s,手续费 %2$d%%,每日 %3$d 次 / %4$s。', 'jio'),
|
||||
$this->format_money((string) ($settings['min_amount'] ?? '0.00')),
|
||||
(int) ($settings['service_charge_rate'] ?? 0),
|
||||
(int) ($settings['daily_count'] ?? 0),
|
||||
$this->format_money((string) ($settings['daily_limit'] ?? '0.00'))
|
||||
);
|
||||
$flow = __('处理流程:开始处理 → 按收款信息线下打款 → 确认打款。确认打款与拒绝都不可撤销。', 'aksrt');
|
||||
$flow = __('处理流程:开始处理 → 按收款信息线下打款 → 确认打款。确认打款与拒绝都不可撤销。', 'jio');
|
||||
|
||||
echo '<div class="aksrt-native-callout' . ($open > 0 ? ' is-warning' : '') . '">';
|
||||
echo '<div class="jio-native-callout' . ($open > 0 ? ' is-warning' : '') . '">';
|
||||
if ($open > 0) {
|
||||
echo '<strong>' . esc_html(sprintf(
|
||||
/* translators: 1 open count 2 frozen amount */
|
||||
__('%1$d 笔申请待办,冻结中 %2$s。', 'aksrt'),
|
||||
__('%1$d 笔申请待办,冻结中 %2$s。', 'jio'),
|
||||
$open,
|
||||
$this->format_money((string) ($stats['open_gross'] ?? '0.00'))
|
||||
)) . '</strong> ';
|
||||
@@ -223,32 +223,32 @@ trait PayWithdrawalsList {
|
||||
|
||||
private function render_withdrawal_stats(array $stats): void {
|
||||
NativeUi::stats([
|
||||
['label' => __('待处理', 'aksrt'), 'value' => number_format_i18n((int) ($stats['pending'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_PENDING]), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('处理中', 'aksrt'), 'value' => number_format_i18n((int) ($stats['processing'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_PROCESSING]), 'icon' => 'dashicons-update', 'tone' => 'info'],
|
||||
['label' => __('冻结中金额', 'aksrt'), 'value' => $this->format_money((string) ($stats['open_gross'] ?? '0.00')), 'hint' => __('待处理 + 处理中的申请毛额', 'aksrt'), 'icon' => 'dashicons-lock', 'tone' => 'warning'],
|
||||
['label' => __('已打款', 'aksrt'), 'value' => $this->format_money((string) ($stats['paid_net'] ?? '0.00')), 'url' => $this->page_url(['status' => Withdrawal::STATUS_COMPLETED]), 'hint' => sprintf(
|
||||
['label' => __('待处理', 'jio'), 'value' => number_format_i18n((int) ($stats['pending'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_PENDING]), 'icon' => 'dashicons-clock', 'tone' => 'warning'],
|
||||
['label' => __('处理中', 'jio'), 'value' => number_format_i18n((int) ($stats['processing'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_PROCESSING]), 'icon' => 'dashicons-update', 'tone' => 'info'],
|
||||
['label' => __('冻结中金额', 'jio'), 'value' => $this->format_money((string) ($stats['open_gross'] ?? '0.00')), 'hint' => __('待处理 + 处理中的申请毛额', 'jio'), 'icon' => 'dashicons-lock', 'tone' => 'warning'],
|
||||
['label' => __('已打款', 'jio'), 'value' => $this->format_money((string) ($stats['paid_net'] ?? '0.00')), 'url' => $this->page_url(['status' => Withdrawal::STATUS_COMPLETED]), 'hint' => sprintf(
|
||||
/* translators: 1 completed count 2 fee total */
|
||||
__('%1$s 笔 · 手续费 %2$s', 'aksrt'),
|
||||
__('%1$s 笔 · 手续费 %2$s', 'jio'),
|
||||
number_format_i18n((int) ($stats['completed'] ?? 0)),
|
||||
$this->format_money((string) ($stats['paid_fee'] ?? '0.00'))
|
||||
), 'icon' => 'dashicons-yes-alt', 'tone' => 'success'],
|
||||
['label' => __('已拒绝', 'aksrt'), 'value' => number_format_i18n((int) ($stats['rejected'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_REJECTED]), 'icon' => 'dashicons-no-alt', 'tone' => 'danger'],
|
||||
['label' => __('已拒绝', 'jio'), 'value' => number_format_i18n((int) ($stats['rejected'] ?? 0)), 'url' => $this->page_url(['status' => Withdrawal::STATUS_REJECTED]), 'icon' => 'dashicons-no-alt', 'tone' => 'danger'],
|
||||
]);
|
||||
}
|
||||
|
||||
/** @param array<string, int> $counts */
|
||||
private function render_status_views(string $current, array $query_args, array $counts): void {
|
||||
$base = array_diff_key($query_args, ['status' => true]);
|
||||
$views = ['' => [__('全部', 'aksrt'), array_sum($counts)]];
|
||||
$views = ['' => [__('全部', 'jio'), array_sum($counts)]];
|
||||
foreach ($this->status_labels() as $key => $label) {
|
||||
$views[$key] = [$label, (int) ($counts[$key] ?? 0)];
|
||||
}
|
||||
|
||||
echo '<nav class="aksrt-native-tabs aksrt-wd-views" aria-label="' . esc_attr__('按处理状态查看', 'aksrt') . '">';
|
||||
echo '<nav class="jio-native-tabs jio-wd-views" aria-label="' . esc_attr__('按处理状态查看', 'jio') . '">';
|
||||
foreach ($views as $key => [$label, $count]) {
|
||||
$url = $this->page_url($base + ($key !== '' ? ['status' => $key] : []));
|
||||
$active = $current === $key;
|
||||
echo '<a class="aksrt-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
echo '<a class="jio-native-tabs__link' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($active ? ' aria-current="page"' : '') . '>'
|
||||
. esc_html($label) . ' <span class="count">' . esc_html(number_format_i18n($count)) . '</span></a>';
|
||||
}
|
||||
echo '</nav>';
|
||||
@@ -259,28 +259,28 @@ trait PayWithdrawalsList {
|
||||
* @param array<string, string|int> $query_args
|
||||
*/
|
||||
private function render_filter_card(array $filters, array $query_args): void {
|
||||
NativeUi::card_open('', '', false, 'aksrt-wd-filter-card');
|
||||
echo '<form method="get" class="aksrt-wd-filters" role="search">';
|
||||
NativeUi::card_open('', '', false, 'jio-wd-filter-card');
|
||||
echo '<form method="get" class="jio-wd-filters" role="search">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::PAGE_SLUG) . '" />';
|
||||
if ($filters['status'] !== '') {
|
||||
echo '<input type="hidden" name="status" value="' . esc_attr($filters['status']) . '" />';
|
||||
}
|
||||
|
||||
echo '<label class="aksrt-wd-filters__search"><span class="screen-reader-text">' . esc_html__('订单号 / 用户 ID', 'aksrt') . '</span>'
|
||||
. '<input type="search" name="s" value="' . esc_attr($filters['keyword']) . '" placeholder="' . esc_attr__('搜索订单号 / 用户 ID', 'aksrt') . '" /></label>';
|
||||
echo $this->filter_select('source', __('提现来源', 'aksrt'), ['' => __('全部来源', 'aksrt')] + $this->source_labels(), $filters['source']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('method', __('收款方式', 'aksrt'), ['' => __('全部收款方式', 'aksrt')] + $this->method_labels(), $filters['method']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="aksrt-wd-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'aksrt') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
echo '<label class="jio-wd-filters__search"><span class="screen-reader-text">' . esc_html__('订单号 / 用户 ID', 'jio') . '</span>'
|
||||
. '<input type="search" name="s" value="' . esc_attr($filters['keyword']) . '" placeholder="' . esc_attr__('搜索订单号 / 用户 ID', 'jio') . '" /></label>';
|
||||
echo $this->filter_select('source', __('提现来源', 'jio'), ['' => __('全部来源', 'jio')] + $this->source_labels(), $filters['source']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->filter_select('method', __('收款方式', 'jio'), ['' => __('全部收款方式', 'jio')] + $this->method_labels(), $filters['method']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="jio-wd-filters__range">'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('开始日期', 'jio') . '</span><input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" /></label>'
|
||||
. '<span aria-hidden="true">–</span>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'aksrt') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '<label><span class="screen-reader-text">' . esc_html__('结束日期', 'jio') . '</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" /></label>'
|
||||
. '</span>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('用户 ID', 'aksrt') . '</span><input type="number" min="1" name="user_id" class="aksrt-wd-filters__id" value="'
|
||||
. esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('用户 ID', 'aksrt') . '" /></label>';
|
||||
echo '<span class="aksrt-wd-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '<label><span class="screen-reader-text">' . esc_html__('用户 ID', 'jio') . '</span><input type="number" min="1" name="user_id" class="jio-wd-filters__id" value="'
|
||||
. esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('用户 ID', 'jio') . '" /></label>';
|
||||
echo '<span class="jio-wd-filters__submit"><button type="submit" class="button button-primary">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if (array_diff_key($query_args, ['status' => true]) !== []) {
|
||||
$reset = $this->page_url($filters['status'] !== '' ? ['status' => $filters['status']] : []);
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'aksrt') . '</a>';
|
||||
echo '<a class="button" href="' . esc_url($reset) . '">' . esc_html__('重置', 'jio') . '</a>';
|
||||
}
|
||||
echo '</span></form>';
|
||||
NativeUi::card_close();
|
||||
@@ -300,9 +300,9 @@ trait PayWithdrawalsList {
|
||||
* 三种目标状态各自的说明与备注要求用 data-mode 区分。
|
||||
*/
|
||||
private function render_process_dialog(): void {
|
||||
echo '<dialog class="aksrt-wd-dialog" id="aksrt-wd-dialog" aria-labelledby="aksrt-wd-dialog-title">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-wd-dialog__frame" data-aksrt-wd-form data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_pay_update_withdrawal" />';
|
||||
echo '<dialog class="jio-wd-dialog" id="jio-wd-dialog" aria-labelledby="jio-wd-dialog-title">';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-wd-dialog__frame" data-jio-wd-form data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="jio_pay_update_withdrawal" />';
|
||||
echo '<input type="hidden" name="withdrawal_id" value="" data-field="id" />';
|
||||
echo '<input type="hidden" name="new_status" value="" data-field="status" />';
|
||||
echo '<input type="hidden" name="idempotency_key" value="" data-field="key" />';
|
||||
@@ -310,52 +310,52 @@ trait PayWithdrawalsList {
|
||||
wp_referer_field();
|
||||
|
||||
$titles = [
|
||||
Withdrawal::STATUS_PROCESSING => __('开始处理提现', 'aksrt'),
|
||||
Withdrawal::STATUS_COMPLETED => __('确认已打款', 'aksrt'),
|
||||
Withdrawal::STATUS_REJECTED => __('拒绝提现', 'aksrt'),
|
||||
Withdrawal::STATUS_PROCESSING => __('开始处理提现', 'jio'),
|
||||
Withdrawal::STATUS_COMPLETED => __('确认已打款', 'jio'),
|
||||
Withdrawal::STATUS_REJECTED => __('拒绝提现', 'jio'),
|
||||
];
|
||||
echo '<header class="aksrt-wd-dialog__head"><h2 id="aksrt-wd-dialog-title">';
|
||||
echo '<header class="jio-wd-dialog__head"><h2 id="jio-wd-dialog-title">';
|
||||
foreach ($titles as $mode => $title) {
|
||||
echo '<span data-mode="' . esc_attr($mode) . '" hidden>' . esc_html($title) . '</span>';
|
||||
}
|
||||
echo '</h2><button type="button" class="aksrt-wd-dialog__close" data-aksrt-wd-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '</h2><button type="button" class="jio-wd-dialog__close" data-jio-wd-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
|
||||
echo '<div class="aksrt-wd-dialog__body"><dl class="aksrt-wd-summary">';
|
||||
echo '<div class="jio-wd-dialog__body"><dl class="jio-wd-summary">';
|
||||
foreach ([
|
||||
'number' => __('订单号', 'aksrt'),
|
||||
'user' => __('用户', 'aksrt'),
|
||||
'net' => __('应打款', 'aksrt'),
|
||||
'gross' => __('申请金额', 'aksrt'),
|
||||
'source' => __('来源', 'aksrt'),
|
||||
'payee' => __('收款信息', 'aksrt'),
|
||||
'number' => __('订单号', 'jio'),
|
||||
'user' => __('用户', 'jio'),
|
||||
'net' => __('应打款', 'jio'),
|
||||
'gross' => __('申请金额', 'jio'),
|
||||
'source' => __('来源', 'jio'),
|
||||
'payee' => __('收款信息', 'jio'),
|
||||
] as $field => $label) {
|
||||
echo '<div class="aksrt-wd-summary__item aksrt-wd-summary__item--' . esc_attr($field) . '"><dt>' . esc_html($label) . '</dt><dd data-summary="' . esc_attr($field) . '"></dd></div>';
|
||||
echo '<div class="jio-wd-summary__item jio-wd-summary__item--' . esc_attr($field) . '"><dt>' . esc_html($label) . '</dt><dd data-summary="' . esc_attr($field) . '"></dd></div>';
|
||||
}
|
||||
echo '</dl>';
|
||||
|
||||
$hints = [
|
||||
Withdrawal::STATUS_PROCESSING => [__('标记为处理中后,请按上方收款信息线下打款,完成后回到这里确认打款。', 'aksrt'), ''],
|
||||
Withdrawal::STATUS_COMPLETED => [__('请确认已向用户实际打出「应打款」金额。确认后状态不可更改,用户会收到完成通知。', 'aksrt'), 'is-warning'],
|
||||
Withdrawal::STATUS_REJECTED => [__('拒绝后,冻结的申请金额会退回用户的原资金池,拒绝原因会通知用户。此操作不可撤销。', 'aksrt'), 'is-danger'],
|
||||
Withdrawal::STATUS_PROCESSING => [__('标记为处理中后,请按上方收款信息线下打款,完成后回到这里确认打款。', 'jio'), ''],
|
||||
Withdrawal::STATUS_COMPLETED => [__('请确认已向用户实际打出「应打款」金额。确认后状态不可更改,用户会收到完成通知。', 'jio'), 'is-warning'],
|
||||
Withdrawal::STATUS_REJECTED => [__('拒绝后,冻结的申请金额会退回用户的原资金池,拒绝原因会通知用户。此操作不可撤销。', 'jio'), 'is-danger'],
|
||||
];
|
||||
foreach ($hints as $mode => [$hint, $tone]) {
|
||||
echo '<p class="aksrt-native-callout ' . esc_attr($tone) . '" data-mode="' . esc_attr($mode) . '" hidden>' . esc_html($hint) . '</p>';
|
||||
echo '<p class="jio-native-callout ' . esc_attr($tone) . '" data-mode="' . esc_attr($mode) . '" hidden>' . esc_html($hint) . '</p>';
|
||||
}
|
||||
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">'
|
||||
. '<span data-mode="' . esc_attr(Withdrawal::STATUS_REJECTED) . '" hidden>' . esc_html__('拒绝原因(必填)', 'aksrt') . '</span>'
|
||||
. '<span data-mode="' . esc_attr(Withdrawal::STATUS_PROCESSING) . ' ' . esc_attr(Withdrawal::STATUS_COMPLETED) . '" hidden>' . esc_html__('备注(选填)', 'aksrt') . '</span>'
|
||||
. '</span><textarea name="note" rows="3" maxlength="1000" data-aksrt-wd-note placeholder="' . esc_attr__('如打款流水号、拒绝原因等,追加记录在申请上', 'aksrt') . '"></textarea></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">'
|
||||
. '<span data-mode="' . esc_attr(Withdrawal::STATUS_REJECTED) . '" hidden>' . esc_html__('拒绝原因(必填)', 'jio') . '</span>'
|
||||
. '<span data-mode="' . esc_attr(Withdrawal::STATUS_PROCESSING) . ' ' . esc_attr(Withdrawal::STATUS_COMPLETED) . '" hidden>' . esc_html__('备注(选填)', 'jio') . '</span>'
|
||||
. '</span><textarea name="note" rows="3" maxlength="1000" data-jio-wd-note placeholder="' . esc_attr__('如打款流水号、拒绝原因等,追加记录在申请上', 'jio') . '"></textarea></label>';
|
||||
echo '</div>';
|
||||
|
||||
$submits = [
|
||||
Withdrawal::STATUS_PROCESSING => [__('开始处理', 'aksrt'), 'button-primary'],
|
||||
Withdrawal::STATUS_COMPLETED => [__('确认已打款', 'aksrt'), 'button-primary'],
|
||||
Withdrawal::STATUS_REJECTED => [__('确认拒绝', 'aksrt'), 'button-primary is-danger'],
|
||||
Withdrawal::STATUS_PROCESSING => [__('开始处理', 'jio'), 'button-primary'],
|
||||
Withdrawal::STATUS_COMPLETED => [__('确认已打款', 'jio'), 'button-primary'],
|
||||
Withdrawal::STATUS_REJECTED => [__('确认拒绝', 'jio'), 'button-primary is-danger'],
|
||||
];
|
||||
echo '<footer class="aksrt-wd-dialog__foot"><button type="button" class="button" data-aksrt-wd-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<footer class="jio-wd-dialog__foot"><button type="button" class="button" data-jio-wd-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
foreach ($submits as $mode => [$label, $class]) {
|
||||
echo '<button type="submit" class="button ' . esc_attr($class) . '" data-mode="' . esc_attr($mode) . '" data-aksrt-wd-submit hidden>' . esc_html($label) . '</button>';
|
||||
echo '<button type="submit" class="button ' . esc_attr($class) . '" data-mode="' . esc_attr($mode) . '" data-jio-wd-submit hidden>' . esc_html($label) . '</button>';
|
||||
}
|
||||
echo '</footer></form></dialog>';
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
* DomainModel::default_post_meta() or the custom-filter settings — the
|
||||
* post-list-quick-edit contract test enforces that pairing.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -27,40 +27,40 @@ class PostListEditFields {
|
||||
[
|
||||
'id' => 'views',
|
||||
'type' => 'number',
|
||||
'title' => __('阅读量', 'aksrt'),
|
||||
'title' => __('阅读量', 'jio'),
|
||||
],
|
||||
[
|
||||
'id' => 'like',
|
||||
'type' => 'number',
|
||||
'title' => __('点赞数', 'aksrt'),
|
||||
'title' => __('点赞数', 'jio'),
|
||||
],
|
||||
[
|
||||
'id' => 'show_layout',
|
||||
'type' => 'radio',
|
||||
'title' => __('显示布局', 'aksrt'),
|
||||
'title' => __('显示布局', 'jio'),
|
||||
'default' => 'false',
|
||||
'options' => [
|
||||
'false' => __('跟随主题', 'aksrt'),
|
||||
'no_sidebar' => __('无侧边栏', 'aksrt'),
|
||||
'sidebar_left' => __('侧边栏靠左', 'aksrt'),
|
||||
'sidebar_right' => __('侧边栏靠右', 'aksrt'),
|
||||
'false' => __('跟随主题', 'jio'),
|
||||
'no_sidebar' => __('无侧边栏', 'jio'),
|
||||
'sidebar_left' => __('侧边栏靠左', 'jio'),
|
||||
'sidebar_right' => __('侧边栏靠右', 'jio'),
|
||||
],
|
||||
],
|
||||
[
|
||||
'id' => 'no_article-navs',
|
||||
'type' => 'checkbox',
|
||||
'label' => __('不显示目录树', 'aksrt'),
|
||||
'label' => __('不显示目录树', 'jio'),
|
||||
],
|
||||
[
|
||||
'id' => 'article_maxheight_xz',
|
||||
'type' => 'checkbox',
|
||||
'label' => __('限制内容最大高度', 'aksrt'),
|
||||
'label' => __('限制内容最大高度', 'jio'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pay parameters (saved into the _aksrt_pay meta bag).
|
||||
* Pay parameters (saved into the _jio_pay meta bag).
|
||||
*
|
||||
* @return array<int,array<string,mixed>>
|
||||
*/
|
||||
@@ -69,132 +69,132 @@ class PostListEditFields {
|
||||
[
|
||||
'id' => 'pay_type',
|
||||
'type' => 'radio',
|
||||
'title' => __('付费模式', 'aksrt'),
|
||||
'options' => \AKSRT\Core\DomainModel::pay_type_labels(),
|
||||
'title' => __('付费模式', 'jio'),
|
||||
'options' => \Jio\Core\DomainModel::pay_type_labels(),
|
||||
],
|
||||
[
|
||||
'id' => 'pay_limit',
|
||||
'type' => 'radio',
|
||||
'title' => __('购买权限', 'aksrt'),
|
||||
'title' => __('购买权限', 'jio'),
|
||||
'options' => [
|
||||
'0' => __('所有人可购买', 'aksrt'),
|
||||
'1' => __('VIP1 及以上会员可购买', 'aksrt'),
|
||||
'2' => __('仅 VIP2 可购买', 'aksrt'),
|
||||
'0' => __('所有人可购买', 'jio'),
|
||||
'1' => __('VIP1 及以上会员可购买', 'jio'),
|
||||
'2' => __('仅 VIP2 可购买', 'jio'),
|
||||
],
|
||||
],
|
||||
[
|
||||
'id' => 'pay_modo',
|
||||
'type' => 'radio',
|
||||
'title' => __('支付类型', 'aksrt'),
|
||||
'title' => __('支付类型', 'jio'),
|
||||
'options' => [
|
||||
'0' => __('普通商品(金钱购买)', 'aksrt'),
|
||||
'points' => __('积分商品(积分购买,依赖于用户积分功能)', 'aksrt'),
|
||||
'0' => __('普通商品(金钱购买)', 'jio'),
|
||||
'points' => __('积分商品(积分购买,依赖于用户积分功能)', 'jio'),
|
||||
],
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '==', 'points']],
|
||||
'id' => 'points_price',
|
||||
'title' => __('积分售价', 'aksrt'),
|
||||
'title' => __('积分售价', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => __('积分', 'aksrt'),
|
||||
'unit' => __('积分', 'jio'),
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '==', 'points']],
|
||||
'title' => __('VIP1 积分售价', 'aksrt'),
|
||||
'title' => __('VIP1 积分售价', 'jio'),
|
||||
'id' => 'vip1_points',
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('填 0 则为 VIP1 免费', 'aksrt'),
|
||||
'subtitle' => __('填 0 则为 VIP1 免费', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => __('积分', 'aksrt'),
|
||||
'unit' => __('积分', 'jio'),
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '==', 'points']],
|
||||
'title' => __('VIP2 积分售价', 'aksrt'),
|
||||
'title' => __('VIP2 积分售价', 'jio'),
|
||||
'id' => 'vip2_points',
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('填 0 则为 VIP2 免费', 'aksrt'),
|
||||
'desc' => __('会员价格不能高于售价', 'aksrt'),
|
||||
'subtitle' => __('填 0 则为 VIP2 免费', 'jio'),
|
||||
'desc' => __('会员价格不能高于售价', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => __('积分', 'aksrt'),
|
||||
'unit' => __('积分', 'jio'),
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '!=', 'points']],
|
||||
'id' => 'price',
|
||||
'title' => __('执行价', 'aksrt'),
|
||||
'title' => __('执行价', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => $currency,
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '!=', 'points']],
|
||||
'id' => 'original_price',
|
||||
'title' => __('原价', 'aksrt'),
|
||||
'title' => __('原价', 'jio'),
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('显示在执行价格前面,并划掉', 'aksrt'),
|
||||
'subtitle' => __('显示在执行价格前面,并划掉', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => $currency,
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '!=', 'points']],
|
||||
'title' => __('VIP1 价格', 'aksrt'),
|
||||
'title' => __('VIP1 价格', 'jio'),
|
||||
'id' => 'vip1_price',
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('填 0 则为 VIP1 免费', 'aksrt'),
|
||||
'subtitle' => __('填 0 则为 VIP1 免费', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => $currency,
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no'], ['pay_modo', '!=', 'points']],
|
||||
'title' => __('VIP2 价格', 'aksrt'),
|
||||
'title' => __('VIP2 价格', 'jio'),
|
||||
'id' => 'vip2_price',
|
||||
'class' => 'compact',
|
||||
'subtitle' => __('填 0 则为 VIP2 免费', 'aksrt'),
|
||||
'desc' => __('会员价格不能高于执行价', 'aksrt'),
|
||||
'subtitle' => __('填 0 则为 VIP2 免费', 'jio'),
|
||||
'desc' => __('会员价格不能高于执行价', 'jio'),
|
||||
'type' => 'number',
|
||||
'unit' => $currency,
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no']],
|
||||
'id' => 'pay_count',
|
||||
'title' => __('销量浮动', 'aksrt'),
|
||||
'subtitle' => __('为真实销量增加或减少的数量', 'aksrt'),
|
||||
'title' => __('销量浮动', 'jio'),
|
||||
'subtitle' => __('为真实销量增加或减少的数量', 'jio'),
|
||||
'type' => 'number',
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no']],
|
||||
'id' => 'show_sales',
|
||||
'title' => __('显示销量', 'aksrt'),
|
||||
'title' => __('显示销量', 'jio'),
|
||||
'default' => true,
|
||||
'type' => 'switcher',
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no']],
|
||||
'id' => 'coupon_s',
|
||||
'title' => __('允许使用优惠码', 'aksrt'),
|
||||
'title' => __('允许使用优惠码', 'jio'),
|
||||
'default' => false,
|
||||
'type' => 'switcher',
|
||||
],
|
||||
[
|
||||
'dependency' => [['pay_type', '!=', 'no']],
|
||||
'id' => 'order_expired_s',
|
||||
'title' => __('订单时效', 'aksrt'),
|
||||
'title' => __('订单时效', 'jio'),
|
||||
'default' => false,
|
||||
'type' => 'switcher',
|
||||
],
|
||||
[
|
||||
'id' => 'order_expired_time',
|
||||
'title' => __('订单时效有效期数值', 'aksrt'),
|
||||
'title' => __('订单时效有效期数值', 'jio'),
|
||||
'default' => 0,
|
||||
'type' => 'number',
|
||||
],
|
||||
[
|
||||
'title' => __('订单时效有效期单位', 'aksrt'),
|
||||
'title' => __('订单时效有效期单位', 'jio'),
|
||||
'type' => 'select',
|
||||
'class' => 'compact',
|
||||
'id' => 'order_expired_unit',
|
||||
'options' => [
|
||||
'day' => __('天', 'aksrt'),
|
||||
'hour' => __('小时', 'aksrt'),
|
||||
'minute' => __('分钟', 'aksrt'),
|
||||
'day' => __('天', 'jio'),
|
||||
'hour' => __('小时', 'jio'),
|
||||
'minute' => __('分钟', 'jio'),
|
||||
],
|
||||
'default' => 'day',
|
||||
],
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
* those boxes onto its topics taxonomy column; this project has no topics
|
||||
* taxonomy, so they hang off the interaction-data column instead.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class PostListEnhance {
|
||||
|
||||
public const COLUMN_STATS = 'aksrt_stats';
|
||||
public const COLUMN_SALES = 'aksrt_sales';
|
||||
public const COLUMN_STATS = 'jio_stats';
|
||||
public const COLUMN_SALES = 'jio_sales';
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
@@ -61,17 +61,17 @@ class PostListEnhance {
|
||||
return;
|
||||
}
|
||||
|
||||
if (function_exists('aksrt_enqueue_fontawesome')) {
|
||||
aksrt_enqueue_fontawesome();
|
||||
if (function_exists('jio_enqueue_fontawesome')) {
|
||||
jio_enqueue_fontawesome();
|
||||
}
|
||||
|
||||
$css = AKSRT_DIR . '/assets/css/admin-main.css';
|
||||
$css = JIO_DIR . '/assets/css/admin-main.css';
|
||||
if (is_file($css)) {
|
||||
wp_enqueue_style('aksrt-admin-main', AKSRT_URI . '/assets/css/admin-main.css', ['aksrt-font-awesome'], (string) filemtime($css));
|
||||
wp_enqueue_style('jio-admin-main', JIO_URI . '/assets/css/admin-main.css', ['jio-font-awesome'], (string) filemtime($css));
|
||||
}
|
||||
$js = AKSRT_DIR . '/assets/js/admin-post-list.js';
|
||||
$js = JIO_DIR . '/assets/js/admin-post-list.js';
|
||||
if (is_file($js)) {
|
||||
wp_enqueue_script('aksrt-post-list', AKSRT_URI . '/assets/js/admin-post-list.js', [], (string) filemtime($js), true);
|
||||
wp_enqueue_script('jio-post-list', JIO_URI . '/assets/js/admin-post-list.js', [], (string) filemtime($js), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,8 +102,8 @@ class PostListEnhance {
|
||||
if ($key !== 'title') {
|
||||
continue;
|
||||
}
|
||||
$new_columns[self::COLUMN_STATS] = __('互动数据', 'aksrt');
|
||||
$new_columns[self::COLUMN_SALES] = '<a href="' . esc_url(add_query_arg(['orderby' => 'sales_volume', 'order' => $order])) . '"><span>' . esc_html__('销售数据', 'aksrt') . ($order_by === 'sales_volume' ? $o_icon : '') . '</span></a>';
|
||||
$new_columns[self::COLUMN_STATS] = __('互动数据', 'jio');
|
||||
$new_columns[self::COLUMN_SALES] = '<a href="' . esc_url(add_query_arg(['orderby' => 'sales_volume', 'order' => $order])) . '"><span>' . esc_html__('销售数据', 'jio') . ($order_by === 'sales_volume' ? $o_icon : '') . '</span></a>';
|
||||
}
|
||||
|
||||
return $new_columns;
|
||||
@@ -124,13 +124,13 @@ class PostListEnhance {
|
||||
}
|
||||
|
||||
private function render_stats_column(int $post_id): void {
|
||||
$views = function_exists('aksrt_get_post_views') ? (int) aksrt_get_post_views($post_id) : (int) get_post_meta($post_id, \AKSRT\Core\DomainModel::POST_VIEW_COUNT_KEY, true);
|
||||
$likes = function_exists('aksrt_get_post_likes') ? (int) aksrt_get_post_likes($post_id) : (int) get_post_meta($post_id, '_aksrt_like_count', true);
|
||||
$favorites = function_exists('aksrt_get_post_favorites') ? (int) aksrt_get_post_favorites($post_id) : (int) get_post_meta($post_id, '_aksrt_favorite_count', true);
|
||||
$views = function_exists('jio_get_post_views') ? (int) jio_get_post_views($post_id) : (int) get_post_meta($post_id, \Jio\Core\DomainModel::POST_VIEW_COUNT_KEY, true);
|
||||
$likes = function_exists('jio_get_post_likes') ? (int) jio_get_post_likes($post_id) : (int) get_post_meta($post_id, '_jio_like_count', true);
|
||||
$favorites = function_exists('jio_get_post_favorites') ? (int) jio_get_post_favorites($post_id) : (int) get_post_meta($post_id, '_jio_favorite_count', true);
|
||||
|
||||
$format = static function (int $n): string {
|
||||
if (function_exists('aksrt_format_number')) {
|
||||
return (string) aksrt_format_number($n);
|
||||
if (function_exists('jio_format_number')) {
|
||||
return (string) jio_format_number($n);
|
||||
}
|
||||
if ($n >= 10000) {
|
||||
return round($n / 10000, 1) . 'W+';
|
||||
@@ -142,45 +142,45 @@ class PostListEnhance {
|
||||
};
|
||||
|
||||
$parts = [];
|
||||
$parts[] = '<span title="' . esc_attr__('浏览', 'aksrt') . '" style="margin-right:4px;"><i class="fa fas fa-eye" style="color:#8a909c;font-size:12px;"></i> ' . esc_html($format($views)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('点赞', 'aksrt') . '" style="margin-right:4px;"><i class="fa fas fa-thumbs-up" style="color:#1890ff;font-size:12px;"></i> ' . esc_html($format($likes)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('收藏', 'aksrt') . '" style="margin-right:4px;"><i class="fa fas fa-star" style="color:#f39c12;font-size:12px;"></i> ' . esc_html($format($favorites)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('浏览', 'jio') . '" style="margin-right:4px;"><i class="fa fas fa-eye" style="color:#8a909c;font-size:12px;"></i> ' . esc_html($format($views)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('点赞', 'jio') . '" style="margin-right:4px;"><i class="fa fas fa-thumbs-up" style="color:#1890ff;font-size:12px;"></i> ' . esc_html($format($likes)) . '</span>';
|
||||
$parts[] = '<span title="' . esc_attr__('收藏', 'jio') . '" style="margin-right:4px;"><i class="fa fas fa-star" style="color:#f39c12;font-size:12px;"></i> ' . esc_html($format($favorites)) . '</span>';
|
||||
|
||||
echo implode(' · ', $parts);
|
||||
}
|
||||
|
||||
private function render_sales_column(int $post_id): void {
|
||||
$pay_meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post_id) : [];
|
||||
$pay_meta = function_exists('jio_get_pay_meta') ? jio_get_pay_meta($post_id) : [];
|
||||
$pay_type = (string) ($pay_meta['pay_type'] ?? '');
|
||||
if ($pay_type === '' || $pay_type === \AKSRT\Core\DomainModel::PAY_TYPE_NO) {
|
||||
echo '<div style="font-size: 12px;">' . esc_html__('未开启付费', 'aksrt') . '</div>';
|
||||
if ($pay_type === '' || $pay_type === \Jio\Core\DomainModel::PAY_TYPE_NO) {
|
||||
echo '<div style="font-size: 12px;">' . esc_html__('未开启付费', 'jio') . '</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$pay_modo = (string) ($pay_meta['pay_modo'] ?? '0');
|
||||
$type_labels = \AKSRT\Core\DomainModel::pay_type_labels();
|
||||
$type_labels = \Jio\Core\DomainModel::pay_type_labels();
|
||||
$type_label = $type_labels[$pay_type] ?? $pay_type;
|
||||
$type_color = $this->pay_type_color($pay_type);
|
||||
|
||||
if ($pay_modo === 'points') {
|
||||
$price_text = sprintf(__('积分:%s', 'aksrt'), (string) max(0, (int) ($pay_meta['points_price'] ?? 0)));
|
||||
$price_text = sprintf(__('积分:%s', 'jio'), (string) max(0, (int) ($pay_meta['points_price'] ?? 0)));
|
||||
} else {
|
||||
$price_text = $this->currency_symbol() . ':' . (string) ($pay_meta['price'] ?? '0.00');
|
||||
}
|
||||
|
||||
// 列表列与前台同一口径:真实订单 + 后台「销量浮动」(pay_count)。
|
||||
// 真实销量取订单表统计,_aksrt_sales_volume 只是订单流水累加的副本。
|
||||
// 真实销量取订单表统计,_jio_sales_volume 只是订单流水累加的副本。
|
||||
$real_sales = $this->real_sales_count($post_id);
|
||||
$meta_sales = max(0, (int) get_post_meta($post_id, \AKSRT\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY, true));
|
||||
$meta_sales = max(0, (int) get_post_meta($post_id, \Jio\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY, true));
|
||||
$sales_volume = max($real_sales, $meta_sales) + max(0, (int) ($pay_meta['pay_count'] ?? 0));
|
||||
$real_html = $real_sales > 0
|
||||
? ' <a title="' . esc_attr__('点击查看销售明细', 'aksrt') . '" href="' . esc_url($this->orders_url($post_id)) . '">[' . esc_html(sprintf(__('真实销量%s', 'aksrt'), $real_sales)) . ']</a>'
|
||||
? ' <a title="' . esc_attr__('点击查看销售明细', 'jio') . '" href="' . esc_url($this->orders_url($post_id)) . '">[' . esc_html(sprintf(__('真实销量%s', 'jio'), $real_sales)) . ']</a>'
|
||||
: '';
|
||||
|
||||
$link = add_query_arg(['aksrt_pay_type' => $pay_type]);
|
||||
$link = add_query_arg(['jio_pay_type' => $pay_type]);
|
||||
echo '<div>';
|
||||
echo '<a style="color: ' . esc_attr($type_color) . ';" href="' . esc_url($link) . '">' . esc_html($type_label) . '</a>';
|
||||
echo '<div style="font-size: 12px;">' . esc_html($price_text) . ' · ' . esc_html__('销量', 'aksrt') . esc_html((string) $sales_volume) . wp_kses_post($real_html) . '</div>';
|
||||
echo '<div style="font-size: 12px;">' . esc_html($price_text) . ' · ' . esc_html__('销量', 'jio') . esc_html((string) $sales_volume) . wp_kses_post($real_html) . '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
@@ -189,11 +189,11 @@ class PostListEnhance {
|
||||
*/
|
||||
private function pay_type_color(string $pay_type): string {
|
||||
$palette = [
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_READING => '#ff4747',
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_DOWNLOAD => '#ee5307',
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_GALLERY => '#1e8608',
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_VIDEO => '#086ae8',
|
||||
\AKSRT\Core\DomainModel::PAY_TYPE_VIP => '#3353fd',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_READING => '#ff4747',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_DOWNLOAD => '#ee5307',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_GALLERY => '#1e8608',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_VIDEO => '#086ae8',
|
||||
\Jio\Core\DomainModel::PAY_TYPE_VIP => '#3353fd',
|
||||
];
|
||||
return $palette[$pay_type] ?? '#464646';
|
||||
}
|
||||
@@ -207,12 +207,12 @@ class PostListEnhance {
|
||||
return;
|
||||
}
|
||||
|
||||
$current = isset($_GET['aksrt_pay_type']) ? sanitize_key((string) wp_unslash($_GET['aksrt_pay_type'])) : '';
|
||||
$labels = \AKSRT\Core\DomainModel::pay_type_labels();
|
||||
unset($labels[\AKSRT\Core\DomainModel::PAY_TYPE_NO]);
|
||||
$current = isset($_GET['jio_pay_type']) ? sanitize_key((string) wp_unslash($_GET['jio_pay_type'])) : '';
|
||||
$labels = \Jio\Core\DomainModel::pay_type_labels();
|
||||
unset($labels[\Jio\Core\DomainModel::PAY_TYPE_NO]);
|
||||
|
||||
echo '<select name="aksrt_pay_type">';
|
||||
echo '<option value="">' . esc_html__('商品类型', 'aksrt') . '</option>';
|
||||
echo '<select name="jio_pay_type">';
|
||||
echo '<option value="">' . esc_html__('商品类型', 'jio') . '</option>';
|
||||
foreach ($labels as $key => $label) {
|
||||
printf('<option %s value="%s">%s</option>', selected($current, $key, false), esc_attr($key), esc_html($label));
|
||||
}
|
||||
@@ -232,25 +232,25 @@ class PostListEnhance {
|
||||
|
||||
$orderby = isset($_GET['orderby']) ? sanitize_key((string) wp_unslash($_GET['orderby'])) : '';
|
||||
if ($orderby === 'sales_volume') {
|
||||
$query->set('meta_key', \AKSRT\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY);
|
||||
$query->set('meta_key', \Jio\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY);
|
||||
$query->set('orderby', 'meta_value_num');
|
||||
} elseif ($orderby === 'views' || $orderby === 'like' || $orderby === 'favorite') {
|
||||
$query->set('meta_key', $this->stats_meta_key($orderby));
|
||||
$query->set('orderby', 'meta_value_num');
|
||||
}
|
||||
|
||||
$pay_type = isset($_GET['aksrt_pay_type']) ? sanitize_key((string) wp_unslash($_GET['aksrt_pay_type'])) : '';
|
||||
if ($pay_type !== '' && isset(\AKSRT\Core\DomainModel::pay_type_labels()[$pay_type])) {
|
||||
$pay_type = isset($_GET['jio_pay_type']) ? sanitize_key((string) wp_unslash($_GET['jio_pay_type'])) : '';
|
||||
if ($pay_type !== '' && isset(\Jio\Core\DomainModel::pay_type_labels()[$pay_type])) {
|
||||
$meta_query = $query->get('meta_query');
|
||||
$meta_query = is_array($meta_query) ? $meta_query : [];
|
||||
$meta_query[] = [
|
||||
'relation' => 'OR',
|
||||
[
|
||||
'key' => \AKSRT\Core\DomainModel::PAY_TYPE_KEY,
|
||||
'key' => \Jio\Core\DomainModel::PAY_TYPE_KEY,
|
||||
'value' => $pay_type,
|
||||
],
|
||||
[
|
||||
'key' => \AKSRT\Core\DomainModel::PAY_META_KEY,
|
||||
'key' => \Jio\Core\DomainModel::PAY_META_KEY,
|
||||
'value' => '"pay_type";s:' . strlen($pay_type) . ':"' . $pay_type . '"',
|
||||
'compare' => 'LIKE',
|
||||
],
|
||||
@@ -264,11 +264,11 @@ class PostListEnhance {
|
||||
*/
|
||||
private function stats_meta_key(string $orderby): string {
|
||||
$map = [
|
||||
'views' => \AKSRT\Core\DomainModel::POST_VIEW_COUNT_KEY,
|
||||
'like' => '_aksrt_like_count',
|
||||
'favorite' => '_aksrt_favorite_count',
|
||||
'views' => \Jio\Core\DomainModel::POST_VIEW_COUNT_KEY,
|
||||
'like' => '_jio_like_count',
|
||||
'favorite' => '_jio_favorite_count',
|
||||
];
|
||||
return $map[$orderby] ?? \AKSRT\Core\DomainModel::POST_VIEW_COUNT_KEY;
|
||||
return $map[$orderby] ?? \Jio\Core\DomainModel::POST_VIEW_COUNT_KEY;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -291,7 +291,7 @@ class PostListEnhance {
|
||||
|
||||
private function edit_boxes_html(): string {
|
||||
$html = $this->fields_html(PostListEditFields::extended(), 'extended');
|
||||
$html .= $this->fields_html(PostListEditFields::pay($this->currency_symbol()), 'pay', __('付费参数', 'aksrt'), '<div class="c-yellow">' . esc_html__('批量设置付费参数,请仔细检查配置内容,更新后无法撤回。请注意配置逻辑,例如会员价不能高于普通价,执行价不能高于原价等', 'aksrt') . '</div>');
|
||||
$html .= $this->fields_html(PostListEditFields::pay($this->currency_symbol()), 'pay', __('付费参数', 'jio'), '<div class="c-yellow">' . esc_html__('批量设置付费参数,请仔细检查配置内容,更新后无法撤回。请注意配置逻辑,例如会员价不能高于普通价,执行价不能高于原价等', 'jio') . '</div>');
|
||||
$html .= $this->filter_fields_html();
|
||||
return $html;
|
||||
}
|
||||
@@ -302,8 +302,8 @@ class PostListEnhance {
|
||||
*/
|
||||
private function filter_fields_html(): string {
|
||||
$dimensions = [];
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
$settings = \AKSRT\Settings\Settings::get_post_filter_settings();
|
||||
if (class_exists('\Jio\Settings\Settings')) {
|
||||
$settings = \Jio\Settings\Settings::get_post_filter_settings();
|
||||
$dimensions = is_array($settings['custom_filter'] ?? null) ? $settings['custom_filter'] : [];
|
||||
}
|
||||
if (!$dimensions) {
|
||||
@@ -332,7 +332,7 @@ class PostListEnhance {
|
||||
'title' => (string) ($dimension['name'] ?? $key),
|
||||
'type' => 'checkbox',
|
||||
'options' => $options,
|
||||
'placeholder' => sprintf(__('请选择%s', 'aksrt'), (string) ($dimension['name'] ?? $key)),
|
||||
'placeholder' => sprintf(__('请选择%s', 'jio'), (string) ($dimension['name'] ?? $key)),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ class PostListEnhance {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->fields_html($fields, 'filter', __('高级筛选', 'aksrt'), '<div class="px12 aksrt-bulk-edit-desc">' . esc_html__('让文章实现更加精细化的分类,同时方便用户进行文章筛选', 'aksrt') . '</div>');
|
||||
return $this->fields_html($fields, 'filter', __('高级筛选', 'jio'), '<div class="px12 jio-bulk-edit-desc">' . esc_html__('让文章实现更加精细化的分类,同时方便用户进行文章筛选', 'jio') . '</div>');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,15 +357,15 @@ class PostListEnhance {
|
||||
$options_html = ' ';
|
||||
switch ($field['type']) {
|
||||
case 'number':
|
||||
$name = 'aksrt_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$name = 'jio_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$options_html = '<div class="flex ac hh">'
|
||||
. '<select name="' . esc_attr($name) . '[operation]">'
|
||||
. '<option value="ignore" selected="selected">' . esc_html__('不更改', 'aksrt') . '</option>'
|
||||
. '<option value="set">' . esc_html__('设置为', 'aksrt') . '</option>'
|
||||
. '<option value="plus">' . esc_html__('加', 'aksrt') . '</option>'
|
||||
. '<option value="subtract">' . esc_html__('减', 'aksrt') . '</option>'
|
||||
. '<option value="multiply">' . esc_html__('乘', 'aksrt') . '</option>'
|
||||
. '<option value="division">' . esc_html__('除', 'aksrt') . '</option>'
|
||||
. '<option value="ignore" selected="selected">' . esc_html__('不更改', 'jio') . '</option>'
|
||||
. '<option value="set">' . esc_html__('设置为', 'jio') . '</option>'
|
||||
. '<option value="plus">' . esc_html__('加', 'jio') . '</option>'
|
||||
. '<option value="subtract">' . esc_html__('减', 'jio') . '</option>'
|
||||
. '<option value="multiply">' . esc_html__('乘', 'jio') . '</option>'
|
||||
. '<option value="division">' . esc_html__('除', 'jio') . '</option>'
|
||||
. '</select>'
|
||||
. '<input type="text" name="' . esc_attr($name) . '[val]" value="">'
|
||||
. '</div>';
|
||||
@@ -374,32 +374,32 @@ class PostListEnhance {
|
||||
case 'checkbox':
|
||||
$select_options = !empty($field['options']) ? $field['options'] : [];
|
||||
if ($select_options) {
|
||||
$name = 'aksrt_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$name = 'jio_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$options_html = $this->checkbox_options_html($name, $select_options);
|
||||
break;
|
||||
}
|
||||
$field['options'] = [
|
||||
'ignore' => __('不更改', 'aksrt'),
|
||||
'1' => __('开启', 'aksrt'),
|
||||
'0' => __('关闭', 'aksrt'),
|
||||
'ignore' => __('不更改', 'jio'),
|
||||
'1' => __('开启', 'jio'),
|
||||
'0' => __('关闭', 'jio'),
|
||||
];
|
||||
// Falls through to the switcher renderer below.
|
||||
|
||||
case 'switcher':
|
||||
$field['options'] = [
|
||||
'ignore' => __('不更改', 'aksrt'),
|
||||
'1' => __('开启', 'aksrt'),
|
||||
'0' => __('关闭', 'aksrt'),
|
||||
'ignore' => __('不更改', 'jio'),
|
||||
'1' => __('开启', 'jio'),
|
||||
'0' => __('关闭', 'jio'),
|
||||
];
|
||||
// Falls through to the select renderer below.
|
||||
|
||||
case 'radio':
|
||||
case 'select':
|
||||
$options = isset($field['options']) && is_array($field['options']) ? $field['options'] : [];
|
||||
$name = 'aksrt_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$name = 'jio_bulk_edit[' . $id . '][' . $field['id'] . ']';
|
||||
$options_html = '<div class="flex ac hh">'
|
||||
. '<select name="' . esc_attr($name) . '">'
|
||||
. '<option selected="selected" value="ignore">--' . esc_html__('不更改', 'aksrt') . '--</option>';
|
||||
. '<option selected="selected" value="ignore">--' . esc_html__('不更改', 'jio') . '--</option>';
|
||||
foreach ($options as $key => $value) {
|
||||
$options_html .= '<option value="' . esc_attr((string) $key) . '">' . esc_html((string) $value) . '</option>';
|
||||
}
|
||||
@@ -412,11 +412,11 @@ class PostListEnhance {
|
||||
}
|
||||
|
||||
$title_html = !empty($field['title']) ? (string) $field['title'] : (!empty($field['label']) ? (string) $field['label'] : '');
|
||||
$desc_html = !empty($field['desc']) ? '<div class="px12 aksrt-bulk-edit-desc">' . esc_html((string) $field['desc']) . '</div>' : '';
|
||||
$subtitle = !empty($field['subtitle']) ? '<div class="px12 aksrt-bulk-edit-desc">' . esc_html((string) $field['subtitle']) . '</div>' : '';
|
||||
$desc_html = !empty($field['desc']) ? '<div class="px12 jio-bulk-edit-desc">' . esc_html((string) $field['desc']) . '</div>' : '';
|
||||
$subtitle = !empty($field['subtitle']) ? '<div class="px12 jio-bulk-edit-desc">' . esc_html((string) $field['subtitle']) . '</div>' : '';
|
||||
$dependency = !empty($field['dependency']) ? ' data-dependency="' . esc_attr(wp_json_encode($field['dependency'])) . '"' : '';
|
||||
|
||||
$item_html .= '<div class="aksrt-bulk-edit-box"' . $dependency . '>'
|
||||
$item_html .= '<div class="jio-bulk-edit-box"' . $dependency . '>'
|
||||
. '<div>' . esc_html($title_html) . '</div>'
|
||||
. $options_html . $subtitle . $desc_html
|
||||
. '</div>';
|
||||
@@ -438,8 +438,8 @@ class PostListEnhance {
|
||||
*/
|
||||
private function checkbox_options_html(string $name, array $options): string {
|
||||
$html = '<select class="mr10" name="' . esc_attr($name) . '[operation]">'
|
||||
. '<option value="ignore" selected="selected">' . esc_html__('不更改', 'aksrt') . '</option>'
|
||||
. '<option value="set">' . esc_html__('设置为', 'aksrt') . '</option>'
|
||||
. '<option value="ignore" selected="selected">' . esc_html__('不更改', 'jio') . '</option>'
|
||||
. '<option value="set">' . esc_html__('设置为', 'jio') . '</option>'
|
||||
. '</select>';
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
@@ -476,7 +476,7 @@ class PostListEnhance {
|
||||
if ($screen !== 'edit-post') {
|
||||
return;
|
||||
}
|
||||
$submitted = isset($_POST['aksrt_bulk_edit']) && is_array($_POST['aksrt_bulk_edit']) ? wp_unslash($_POST['aksrt_bulk_edit']) : [];
|
||||
$submitted = isset($_POST['jio_bulk_edit']) && is_array($_POST['jio_bulk_edit']) ? wp_unslash($_POST['jio_bulk_edit']) : [];
|
||||
if (!$submitted) {
|
||||
return;
|
||||
}
|
||||
@@ -538,9 +538,9 @@ class PostListEnhance {
|
||||
* @param array<string,mixed> $fields
|
||||
*/
|
||||
private function save_pay(int $post_id, array $fields): void {
|
||||
$pay_meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post_id) : [];
|
||||
$pay_meta = function_exists('jio_get_pay_meta') ? jio_get_pay_meta($post_id) : [];
|
||||
if (!is_array($pay_meta) || $pay_meta === []) {
|
||||
$pay_meta = \AKSRT\Core\DomainModel::default_pay_meta();
|
||||
$pay_meta = \Jio\Core\DomainModel::default_pay_meta();
|
||||
}
|
||||
|
||||
foreach ($fields as $field_id => $field_value) {
|
||||
@@ -600,19 +600,19 @@ class PostListEnhance {
|
||||
}
|
||||
$current = (string) ($pay_meta[$field_id] ?? '0.00');
|
||||
try {
|
||||
$current_minor = (float) \AKSRT\Ledger\AssetAmount::balance_minor($current);
|
||||
$current_minor = (float) \Jio\Ledger\AssetAmount::balance_minor($current);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$current_minor = 0.0;
|
||||
}
|
||||
$new = $this->apply_operation($current_minor, (float) $field_value['val'], $operation);
|
||||
if ($new !== null) {
|
||||
$pay_meta[$field_id] = \AKSRT\Ledger\AssetAmount::decimal_from_minor(max(0, (int) round($new)));
|
||||
$pay_meta[$field_id] = \Jio\Ledger\AssetAmount::decimal_from_minor(max(0, (int) round($new)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
\AKSRT\Core\DomainModel::persist_pay_meta($post_id, $pay_meta);
|
||||
\Jio\Core\DomainModel::persist_pay_meta($post_id, $pay_meta);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -620,8 +620,8 @@ class PostListEnhance {
|
||||
*/
|
||||
private function save_filters(int $post_id, array $fields): void {
|
||||
$dimensions = [];
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
$settings = \AKSRT\Settings\Settings::get_post_filter_settings();
|
||||
if (class_exists('\Jio\Settings\Settings')) {
|
||||
$settings = \Jio\Settings\Settings::get_post_filter_settings();
|
||||
$dimensions = is_array($settings['custom_filter'] ?? null) ? $settings['custom_filter'] : [];
|
||||
}
|
||||
if (!$dimensions) {
|
||||
@@ -652,9 +652,9 @@ class PostListEnhance {
|
||||
: [];
|
||||
$values = array_values(array_intersect($allowed_dimensions[$field_id], $values));
|
||||
if ($values) {
|
||||
update_post_meta($post_id, '_aksrt_cf_' . $field_id, $values);
|
||||
update_post_meta($post_id, '_jio_cf_' . $field_id, $values);
|
||||
} else {
|
||||
delete_post_meta($post_id, '_aksrt_cf_' . $field_id);
|
||||
delete_post_meta($post_id, '_jio_cf_' . $field_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -664,7 +664,7 @@ class PostListEnhance {
|
||||
* meta box.
|
||||
*/
|
||||
private function update_post_meta_bag(int $post_id, string $key, string $value): void {
|
||||
$bag_key = \AKSRT\Core\PostMetaBox::META_KEY;
|
||||
$bag_key = \Jio\Core\PostMetaBox::META_KEY;
|
||||
$bag = get_post_meta($post_id, $bag_key, true);
|
||||
$bag = is_array($bag) ? $bag : [];
|
||||
$bag[$key] = $value;
|
||||
@@ -698,8 +698,8 @@ class PostListEnhance {
|
||||
* Configured cash symbol, shared by the sales column and the pay fields.
|
||||
*/
|
||||
private function currency_symbol(): string {
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
$paid = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||||
if (class_exists('\Jio\Settings\Settings')) {
|
||||
$paid = \Jio\Settings\Settings::get_paid_content_settings();
|
||||
return (string) ($paid['display']['currency_symbol'] ?? '¥');
|
||||
}
|
||||
return '¥';
|
||||
@@ -732,7 +732,7 @@ class PostListEnhance {
|
||||
if (!$wp_query instanceof \WP_Query || empty($wp_query->posts)) {
|
||||
return [];
|
||||
}
|
||||
if (!class_exists('\AKSRT\Payment\OrderRepository')) {
|
||||
if (!class_exists('\Jio\Payment\OrderRepository')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -746,9 +746,9 @@ class PostListEnhance {
|
||||
return [];
|
||||
}
|
||||
|
||||
$map = \AKSRT\Payment\OrderRepository::get_instance()->get_content_sales_map(
|
||||
$map = \Jio\Payment\OrderRepository::get_instance()->get_content_sales_map(
|
||||
$ids,
|
||||
\AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE
|
||||
\Jio\Core\DomainModel::ORDER_PRODUCT_RESOURCE
|
||||
);
|
||||
$out = [];
|
||||
foreach ($map as $pid => $row) {
|
||||
@@ -760,8 +760,8 @@ class PostListEnhance {
|
||||
private function orders_url(int $post_id): string {
|
||||
return add_query_arg(
|
||||
[
|
||||
'page' => 'aksrt_orders',
|
||||
'product_type' => \AKSRT\Core\DomainModel::ORDER_PRODUCT_RESOURCE,
|
||||
'page' => 'jio_orders',
|
||||
'product_type' => \Jio\Core\DomainModel::ORDER_PRODUCT_RESOURCE,
|
||||
'product_id' => $post_id,
|
||||
],
|
||||
admin_url('admin.php')
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -35,13 +35,13 @@ class UsersMenus {
|
||||
NativeUi::boot();
|
||||
add_action('admin_menu', [$this, 'register_menus']);
|
||||
add_action('admin_notices', [$this, 'pending_notices']);
|
||||
add_action('admin_post_aksrt_users_send_message', [$this, 'handle_send_message']);
|
||||
add_action('admin_post_aksrt_users_bulk_messages', [$this, 'handle_bulk_messages']);
|
||||
add_action('admin_post_aksrt_users_review_verify', [$this, 'handle_review_verify']);
|
||||
add_action('admin_post_aksrt_users_delete_verify', [$this, 'handle_delete_verify']);
|
||||
add_action('admin_post_aksrt_users_update_report', [$this, 'handle_update_report']);
|
||||
add_action('admin_post_aksrt_users_process_appeal', [$this, 'handle_process_appeal']);
|
||||
add_action('admin_post_aksrt_users_mutate_vip', [$this, 'handle_mutate_vip']);
|
||||
add_action('admin_post_jio_users_send_message', [$this, 'handle_send_message']);
|
||||
add_action('admin_post_jio_users_bulk_messages', [$this, 'handle_bulk_messages']);
|
||||
add_action('admin_post_jio_users_review_verify', [$this, 'handle_review_verify']);
|
||||
add_action('admin_post_jio_users_delete_verify', [$this, 'handle_delete_verify']);
|
||||
add_action('admin_post_jio_users_update_report', [$this, 'handle_update_report']);
|
||||
add_action('admin_post_jio_users_process_appeal', [$this, 'handle_process_appeal']);
|
||||
add_action('admin_post_jio_users_mutate_vip', [$this, 'handle_mutate_vip']);
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_ticket_assets']);
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_message_assets']);
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_invite_assets']);
|
||||
@@ -53,10 +53,10 @@ class UsersMenus {
|
||||
public function register_menus(): void {
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('邀请码管理', 'aksrt'),
|
||||
__('邀请码管理', 'aksrt'),
|
||||
__('邀请码管理', 'jio'),
|
||||
__('邀请码管理', 'jio'),
|
||||
'edit_users',
|
||||
'aksrt_invite_codes',
|
||||
'jio_invite_codes',
|
||||
[$this, 'render_invite_codes_page']
|
||||
);
|
||||
|
||||
@@ -66,42 +66,42 @@ class UsersMenus {
|
||||
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('消息管理', 'aksrt'),
|
||||
__('消息管理', 'aksrt'),
|
||||
__('消息管理', 'jio'),
|
||||
__('消息管理', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_messages',
|
||||
'jio_messages',
|
||||
[$this, 'render_messages_page']
|
||||
);
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('用户会员', 'aksrt'),
|
||||
__('用户会员', 'aksrt'),
|
||||
__('用户会员', 'jio'),
|
||||
__('用户会员', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_user_vip',
|
||||
'jio_user_vip',
|
||||
[$this, 'render_vip_page']
|
||||
);
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('身份认证', 'aksrt'),
|
||||
__('身份认证', 'aksrt'),
|
||||
__('身份认证', 'jio'),
|
||||
__('身份认证', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_user_auth',
|
||||
'jio_user_auth',
|
||||
[$this, 'render_verify_page']
|
||||
);
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('处理举报及禁封申诉', 'aksrt'),
|
||||
__('举报&禁封申诉', 'aksrt'),
|
||||
__('处理举报及禁封申诉', 'jio'),
|
||||
__('举报&禁封申诉', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_user_ban',
|
||||
'jio_user_ban',
|
||||
[$this, 'render_ban_page']
|
||||
);
|
||||
add_submenu_page(
|
||||
'users.php',
|
||||
__('工单管理', 'aksrt'),
|
||||
__('工单管理', 'aksrt'),
|
||||
__('工单管理', 'jio'),
|
||||
__('工单管理', 'jio'),
|
||||
self::CAPABILITY,
|
||||
'aksrt_tickets',
|
||||
'jio_tickets',
|
||||
[$this, 'render_tickets_page']
|
||||
);
|
||||
}
|
||||
@@ -112,115 +112,115 @@ class UsersMenus {
|
||||
}
|
||||
$page = isset($_GET['page']) ? sanitize_key((string) wp_unslash($_GET['page'])) : '';
|
||||
|
||||
if ($page !== 'aksrt_user_auth') {
|
||||
if ($page !== 'jio_user_auth') {
|
||||
$pending_verify = $this->count_verify_status('pending');
|
||||
if ($pending_verify > 0) {
|
||||
$url = admin_url('users.php?page=aksrt_user_auth&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('用户身份认证申请待处理', 'aksrt') . '</h3>';
|
||||
$url = admin_url('users.php?page=jio_user_auth&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('用户身份认证申请待处理', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(
|
||||
/* translators: %d count */
|
||||
__('您有 %d 个用户身份认证申请待处理', 'aksrt'),
|
||||
__('您有 %d 个用户身份认证申请待处理', 'jio'),
|
||||
$pending_verify
|
||||
)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p></div>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($page !== 'aksrt_user_ban' && class_exists('\AKSRT\Messages\ReportCenter')) {
|
||||
$pending_reports = (int) \AKSRT\Messages\ReportCenter::get_instance()->get_pending_count();
|
||||
if ($page !== 'jio_user_ban' && class_exists('\Jio\Messages\ReportCenter')) {
|
||||
$pending_reports = (int) \Jio\Messages\ReportCenter::get_instance()->get_pending_count();
|
||||
if ($pending_reports > 0) {
|
||||
$url = admin_url('users.php?page=aksrt_user_ban&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('举报待处理', 'aksrt') . '</h3>';
|
||||
$url = admin_url('users.php?page=jio_user_ban&status=pending');
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('举报待处理', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(
|
||||
/* translators: %d count */
|
||||
__('您有 %d 条举报待处理', 'aksrt'),
|
||||
__('您有 %d 条举报待处理', 'jio'),
|
||||
$pending_reports
|
||||
)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p></div>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($page !== 'aksrt_user_ban' && class_exists('\AKSRT\User\BanAppeal')) {
|
||||
if ($page !== 'jio_user_ban' && class_exists('\Jio\User\BanAppeal')) {
|
||||
$pending_appeals = count(get_users([
|
||||
'meta_key' => \AKSRT\User\BanAppeal::META_STATUS,
|
||||
'meta_key' => \Jio\User\BanAppeal::META_STATUS,
|
||||
'meta_value' => 'pending',
|
||||
'number' => 50,
|
||||
'fields' => 'ID',
|
||||
]));
|
||||
if ($pending_appeals > 0) {
|
||||
$url = admin_url('users.php?page=aksrt_user_ban&type=appeal&status=pending');
|
||||
echo '<div class="notice notice-warning is-dismissible"><h3>' . esc_html__('禁封申诉待处理', 'aksrt') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(__('您有 %d 条禁封申诉待处理', 'aksrt'), $pending_appeals)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p></div>';
|
||||
$url = admin_url('users.php?page=jio_user_ban&type=appeal&status=pending');
|
||||
echo '<div class="notice notice-warning is-dismissible"><h3>' . esc_html__('禁封申诉待处理', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(__('您有 %d 条禁封申诉待处理', 'jio'), $pending_appeals)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($page !== 'aksrt_tickets' && class_exists('\AKSRT\Ticket\TicketSettings') && \AKSRT\Ticket\TicketSettings::enabled()) {
|
||||
if ($page !== 'jio_tickets' && class_exists('\Jio\Ticket\TicketSettings') && \Jio\Ticket\TicketSettings::enabled()) {
|
||||
$pending_tickets = $this->count_pending_tickets();
|
||||
if ($pending_tickets > 0) {
|
||||
$url = admin_url('users.php?page=aksrt_tickets&status=' . \AKSRT\Ticket\TicketStatusService::KEY_PENDING);
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('工单待接单', 'aksrt') . '</h3>';
|
||||
$url = admin_url('users.php?page=jio_tickets&status=' . \Jio\Ticket\TicketStatusService::KEY_PENDING);
|
||||
echo '<div class="notice notice-info is-dismissible"><h3>' . esc_html__('工单待接单', 'jio') . '</h3>';
|
||||
echo '<p>' . esc_html(sprintf(
|
||||
/* translators: %d count */
|
||||
__('您有 %d 个工单待接单', 'aksrt'),
|
||||
__('您有 %d 个工单待接单', 'jio'),
|
||||
$pending_tickets
|
||||
)) . '</p>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'aksrt') . '</a></p></div>';
|
||||
echo '<p><a class="button button-primary" href="' . esc_url($url) . '">' . esc_html__('立即处理', 'jio') . '</a></p></div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function ensure_cap(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
}
|
||||
|
||||
private function flash(): void {
|
||||
if (empty($_GET['aksrt_users_msg'])) {
|
||||
if (empty($_GET['jio_users_msg'])) {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_users_msg']));
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_users_msg']));
|
||||
$map = [
|
||||
'verify_updated' => __('认证状态已更新。', 'aksrt'),
|
||||
'verify_deleted' => __('认证记录已删除。', 'aksrt'),
|
||||
'verify_failed' => __('认证审核失败。', 'aksrt'),
|
||||
'report_updated' => __('举报状态已更新。', 'aksrt'),
|
||||
'report_failed' => __('举报更新失败。', 'aksrt'),
|
||||
'appeal_approved' => __('申诉已通过,限制已解除。', 'aksrt'),
|
||||
'appeal_rejected' => __('申诉已驳回。', 'aksrt'),
|
||||
'appeal_failed' => __('申诉处理失败。', 'aksrt'),
|
||||
'vip_updated' => __('会员状态已更新。', 'aksrt'),
|
||||
'vip_partial' => __('部分用户的会员状态已更新。', 'aksrt'),
|
||||
'vip_failed' => __('会员操作失败。', 'aksrt'),
|
||||
'verify_updated' => __('认证状态已更新。', 'jio'),
|
||||
'verify_deleted' => __('认证记录已删除。', 'jio'),
|
||||
'verify_failed' => __('认证审核失败。', 'jio'),
|
||||
'report_updated' => __('举报状态已更新。', 'jio'),
|
||||
'report_failed' => __('举报更新失败。', 'jio'),
|
||||
'appeal_approved' => __('申诉已通过,限制已解除。', 'jio'),
|
||||
'appeal_rejected' => __('申诉已驳回。', 'jio'),
|
||||
'appeal_failed' => __('申诉处理失败。', 'jio'),
|
||||
'vip_updated' => __('会员状态已更新。', 'jio'),
|
||||
'vip_partial' => __('部分用户的会员状态已更新。', 'jio'),
|
||||
'vip_failed' => __('会员操作失败。', 'jio'),
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
}
|
||||
$class = str_contains($code, 'failed') ? 'notice-error' : 'notice-success';
|
||||
$message = $map[$code];
|
||||
if ($code === 'verify_failed' && isset($_GET['aksrt_verify_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['aksrt_verify_error']));
|
||||
if ($code === 'verify_failed' && isset($_GET['jio_verify_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['jio_verify_error']));
|
||||
}
|
||||
if (in_array($code, ['report_failed', 'appeal_failed'], true) && isset($_GET['aksrt_ban_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['aksrt_ban_error']));
|
||||
if (in_array($code, ['report_failed', 'appeal_failed'], true) && isset($_GET['jio_ban_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['jio_ban_error']));
|
||||
}
|
||||
if ($code === 'vip_failed' && isset($_GET['aksrt_vip_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['aksrt_vip_error']));
|
||||
if ($code === 'vip_failed' && isset($_GET['jio_vip_error'])) {
|
||||
$message = sanitize_text_field((string) wp_unslash($_GET['jio_vip_error']));
|
||||
}
|
||||
if (in_array($code, ['vip_updated', 'vip_partial'], true)) {
|
||||
$message .= ' ' . sprintf(
|
||||
__('成功:%1$d;失败:%2$d;跳过:%3$d。', 'aksrt'),
|
||||
absint($_GET['aksrt_vip_ok'] ?? 0),
|
||||
absint($_GET['aksrt_vip_failed_count'] ?? 0),
|
||||
absint($_GET['aksrt_vip_skipped'] ?? 0)
|
||||
__('成功:%1$d;失败:%2$d;跳过:%3$d。', 'jio'),
|
||||
absint($_GET['jio_vip_ok'] ?? 0),
|
||||
absint($_GET['jio_vip_failed_count'] ?? 0),
|
||||
absint($_GET['jio_vip_skipped'] ?? 0)
|
||||
);
|
||||
}
|
||||
echo '<div class="notice ' . esc_attr($class) . ' is-dismissible"><p>' . esc_html($message) . '</p></div>';
|
||||
}
|
||||
|
||||
private function redirect(string $page, string $msg, array $extra = []): void {
|
||||
wp_safe_redirect(add_query_arg(array_merge(['page' => $page, 'aksrt_users_msg' => $msg], $extra), admin_url('users.php')));
|
||||
wp_safe_redirect(add_query_arg(array_merge(['page' => $page, 'jio_users_msg' => $msg], $extra), admin_url('users.php')));
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/** Native report and ban appeal queue. @package AKSRT */
|
||||
/** Native report and ban appeal queue. @package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\User\Restriction;
|
||||
use Jio\User\Restriction;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -15,11 +15,11 @@ trait UsersMenusBan {
|
||||
private array $ban_restrictions = [];
|
||||
|
||||
public function enqueue_ban_assets(string $hook): void {
|
||||
if ($hook !== 'users_page_aksrt_user_ban') return;
|
||||
if ($hook !== 'users_page_jio_user_ban') return;
|
||||
$css = '/assets/css/admin/user-ban.css';
|
||||
$js = '/assets/js/admin-user-ban.js';
|
||||
wp_enqueue_style('aksrt-admin-user-ban', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-user-ban', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-user-ban', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-user-ban', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_ban_page(): void {
|
||||
@@ -28,71 +28,71 @@ trait UsersMenusBan {
|
||||
$data = $this->query_ban_requests($filters, max(1, (int) ($_GET['paged'] ?? 1)));
|
||||
$this->cache_ban_targets($data['items']);
|
||||
$this->load_ban_restrictions($data['items']);
|
||||
NativeUi::page_open('aksrt-ban-page');
|
||||
NativeUi::page_open('jio-ban-page');
|
||||
NativeUi::header(
|
||||
__('举报与禁封申诉', 'aksrt'),
|
||||
__('核对举报内容与申诉资料,在审核弹窗中记录处理结果。用户禁封操作仍在前台用户主页完成。', 'aksrt'),
|
||||
__('举报与禁封申诉', 'jio'),
|
||||
__('核对举报内容与申诉资料,在审核弹窗中记录处理结果。用户禁封操作仍在前台用户主页完成。', 'jio'),
|
||||
'dashicons-shield'
|
||||
);
|
||||
$this->flash();
|
||||
NativeUi::card_open('', '', true, 'aksrt-ban-list-card');
|
||||
NativeUi::card_open('', '', true, 'jio-ban-list-card');
|
||||
$this->render_ban_filters($filters, $data['counts']);
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
$this->render_ban_table($data['items'], $filters, $data['page']);
|
||||
NativeUi::table_wrap_close();
|
||||
AdminListPagination::render('aksrt_user_ban', $data['total'], $data['page'], self::BAN_PER_PAGE, $filters, 'users.php');
|
||||
AdminListPagination::render('jio_user_ban', $data['total'], $data['page'], self::BAN_PER_PAGE, $filters, 'users.php');
|
||||
NativeUi::card_close();
|
||||
echo '<dialog id="aksrt-ban-dialog" class="aksrt-ban-dialog" aria-labelledby="aksrt-ban-dialog-title" aria-describedby="aksrt-ban-dialog-description"></dialog>';
|
||||
echo '<noscript><p class="aksrt-native-callout">' . esc_html__('请启用 JavaScript 后查看详情和处理审核。', 'aksrt') . '</p></noscript>';
|
||||
echo '<dialog id="jio-ban-dialog" class="jio-ban-dialog" aria-labelledby="jio-ban-dialog-title" aria-describedby="jio-ban-dialog-description"></dialog>';
|
||||
echo '<noscript><p class="jio-native-callout">' . esc_html__('请启用 JavaScript 后查看详情和处理审核。', 'jio') . '</p></noscript>';
|
||||
NativeUi::page_close();
|
||||
}
|
||||
|
||||
private function ban_page_url(array $args = []): string {
|
||||
return add_query_arg(array_merge(['page' => 'aksrt_user_ban'], $args), admin_url('users.php'));
|
||||
return add_query_arg(array_merge(['page' => 'jio_user_ban'], $args), admin_url('users.php'));
|
||||
}
|
||||
|
||||
private function render_ban_filters(array $filters, array $counts): void {
|
||||
echo '<div class="aksrt-ban-toolbar"><div class="aksrt-ban-toolbar__top"><h2 class="aksrt-native-card__title">' . esc_html__('处理记录', 'aksrt') . '</h2>';
|
||||
echo '<nav class="aksrt-ban-types" aria-label="' . esc_attr__('记录类型', 'aksrt') . '">';
|
||||
foreach (['' => __('全部类型', 'aksrt'), 'report' => __('用户举报', 'aksrt'), 'appeal' => __('禁封申诉', 'aksrt')] as $key => $label) {
|
||||
echo '<div class="jio-ban-toolbar"><div class="jio-ban-toolbar__top"><h2 class="jio-native-card__title">' . esc_html__('处理记录', 'jio') . '</h2>';
|
||||
echo '<nav class="jio-ban-types" aria-label="' . esc_attr__('记录类型', 'jio') . '">';
|
||||
foreach (['' => __('全部类型', 'jio'), 'report' => __('用户举报', 'jio'), 'appeal' => __('禁封申诉', 'jio')] as $key => $label) {
|
||||
echo '<a href="' . esc_url($this->ban_page_url(array_merge($filters, ['type' => $key]))) . '"' . ($key === $filters['type'] ? ' aria-current="page"' : '') . '>' . esc_html($label) . '</a>';
|
||||
}
|
||||
echo '</nav></div><div class="aksrt-ban-toolbar__bottom"><ul class="subsubsub" aria-label="' . esc_attr__('处理状态筛选', 'aksrt') . '">';
|
||||
foreach (['pending' => __('待处理', 'aksrt'), 'resolved' => __('已处理 / 已通过', 'aksrt'), 'dismissed' => __('已驳回', 'aksrt'), '' => __('全部状态', 'aksrt')] as $key => $label) {
|
||||
if ($filters['type'] === 'report' && $key === 'resolved') $label = __('已处理', 'aksrt');
|
||||
if ($filters['type'] === 'appeal' && $key === 'resolved') $label = __('已通过', 'aksrt');
|
||||
echo '</nav></div><div class="jio-ban-toolbar__bottom"><ul class="subsubsub" aria-label="' . esc_attr__('处理状态筛选', 'jio') . '">';
|
||||
foreach (['pending' => __('待处理', 'jio'), 'resolved' => __('已处理 / 已通过', 'jio'), 'dismissed' => __('已驳回', 'jio'), '' => __('全部状态', 'jio')] as $key => $label) {
|
||||
if ($filters['type'] === 'report' && $key === 'resolved') $label = __('已处理', 'jio');
|
||||
if ($filters['type'] === 'appeal' && $key === 'resolved') $label = __('已通过', 'jio');
|
||||
echo '<li><a href="' . esc_url($this->ban_page_url(array_merge($filters, ['status' => $key]))) . '"' . ($key === $filters['status'] ? ' class="current" aria-current="page"' : '') . '>' . esc_html($label)
|
||||
. ' <span class="count">' . esc_html(number_format_i18n($counts[$key === '' ? 'all' : $key])) . '</span></a></li>';
|
||||
}
|
||||
echo '</ul><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="aksrt_user_ban">';
|
||||
echo '</ul><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="jio_user_ban">';
|
||||
foreach (['type', 'status', 'send_user', 'orderby', 'order'] as $key) {
|
||||
echo '<input type="hidden" name="' . esc_attr($key) . '" value="' . esc_attr((string) $filters[$key]) . '">';
|
||||
}
|
||||
echo '<label class="screen-reader-text" for="aksrt-ban-search">' . esc_html__('搜索用户或记录内容', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-ban-search" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('用户 / 目标 ID / 原因 / 申诉资料', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'aksrt') . '</button>';
|
||||
echo '<label class="screen-reader-text" for="jio-ban-search">' . esc_html__('搜索用户或记录内容', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-ban-search" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('用户 / 目标 ID / 原因 / 申诉资料', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'jio') . '</button>';
|
||||
if ($filters['s'] !== '' || $filters['send_user'] > 0) {
|
||||
echo '<a class="button-link" href="' . esc_url($this->ban_page_url(array_merge($filters, ['s' => '', 'send_user' => 0]))) . '">' . esc_html__('清除筛选', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this->ban_page_url(array_merge($filters, ['s' => '', 'send_user' => 0]))) . '">' . esc_html__('清除筛选', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form></div>';
|
||||
if ($filters['send_user'] > 0) {
|
||||
$user = get_userdata($filters['send_user']);
|
||||
echo '<p class="aksrt-ban-filter-hint">' . esc_html(sprintf(__('当前仅显示 %s 提交的记录。', 'aksrt'), $user ? $user->display_name : '#' . $filters['send_user'])) . '</p>';
|
||||
echo '<p class="jio-ban-filter-hint">' . esc_html(sprintf(__('当前仅显示 %s 提交的记录。', 'jio'), $user ? $user->display_name : '#' . $filters['send_user'])) . '</p>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
private function render_ban_table(array $items, array $filters, int $page): void {
|
||||
echo '<table class="widefat fixed striped aksrt-ban-table"><caption class="screen-reader-text">' . esc_html__('举报与禁封申诉处理记录', 'aksrt') . '</caption><thead><tr>';
|
||||
foreach (['type' => __('类型 / 编号', 'aksrt'), 'user' => __('提交用户', 'aksrt'), 'summary' => __('事项摘要', 'aksrt'), 'status' => __('状态', 'aksrt'), 'time' => __('提交时间', 'aksrt'), 'updated' => __('更新时间', 'aksrt'), 'actions' => __('操作', 'aksrt')] as $key => $label) {
|
||||
echo '<table class="widefat fixed striped jio-ban-table"><caption class="screen-reader-text">' . esc_html__('举报与禁封申诉处理记录', 'jio') . '</caption><thead><tr>';
|
||||
foreach (['type' => __('类型 / 编号', 'jio'), 'user' => __('提交用户', 'jio'), 'summary' => __('事项摘要', 'jio'), 'status' => __('状态', 'jio'), 'time' => __('提交时间', 'jio'), 'updated' => __('更新时间', 'jio'), 'actions' => __('操作', 'jio')] as $key => $label) {
|
||||
$sortable = in_array($key, ['type', 'status', 'time', 'updated'], true);
|
||||
$active = $sortable && $filters['orderby'] === $key;
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '"' . ($active ? ' aria-sort="' . ($filters['order'] === 'ASC' ? 'ascending' : 'descending') . '"' : '') . '>';
|
||||
if ($sortable) {
|
||||
$order = $active && $filters['order'] === 'DESC' ? 'ASC' : 'DESC';
|
||||
$title = sprintf($order === 'ASC' ? __('按%s升序排列', 'aksrt') : __('按%s降序排列', 'aksrt'), $label);
|
||||
echo '<a class="aksrt-ban-sort" href="' . esc_url($this->ban_page_url(array_merge($filters, ['orderby' => $key, 'order' => $order]))) . '" aria-label="' . esc_attr($title) . '">' . esc_html($label) . '<span aria-hidden="true">' . ($active ? ($filters['order'] === 'ASC' ? '↑' : '↓') : '↕') . '</span></a>';
|
||||
$title = sprintf($order === 'ASC' ? __('按%s升序排列', 'jio') : __('按%s降序排列', 'jio'), $label);
|
||||
echo '<a class="jio-ban-sort" href="' . esc_url($this->ban_page_url(array_merge($filters, ['orderby' => $key, 'order' => $order]))) . '" aria-label="' . esc_attr($title) . '">' . esc_html($label) . '<span aria-hidden="true">' . ($active ? ($filters['order'] === 'ASC' ? '↑' : '↓') : '↕') . '</span></a>';
|
||||
} else {
|
||||
echo esc_html($label);
|
||||
}
|
||||
@@ -102,8 +102,8 @@ trait UsersMenusBan {
|
||||
if ($items === []) {
|
||||
$filtered = $filters['s'] !== '' || $filters['send_user'] > 0;
|
||||
echo NativeUi::empty_row(7,
|
||||
!$filtered && $filters['status'] === 'pending' ? __('暂无待处理记录', 'aksrt') : __('没有符合条件的记录', 'aksrt'),
|
||||
$filtered ? __('请调整关键词、清除用户筛选或切换类型和状态。', 'aksrt') : __('可切换状态查看处理记录,新举报和申诉将在这里显示。', 'aksrt')
|
||||
!$filtered && $filters['status'] === 'pending' ? __('暂无待处理记录', 'jio') : __('没有符合条件的记录', 'jio'),
|
||||
$filtered ? __('请调整关键词、清除用户筛选或切换类型和状态。', 'jio') : __('可切换状态查看处理记录,新举报和申诉将在这里显示。', 'jio')
|
||||
);
|
||||
}
|
||||
foreach ($items as $row) $this->render_ban_row($row, $filters, $page);
|
||||
@@ -115,31 +115,31 @@ trait UsersMenusBan {
|
||||
$payload = $row['payload'];
|
||||
$key = $row['kind'] . '-' . $row['id'];
|
||||
$user_id = (int) $row['user_id'];
|
||||
echo '<tr><td class="column-type">' . NativeUi::badge($report ? __('用户举报', 'aksrt') : __('禁封申诉', 'aksrt'), $report ? 'info' : 'neutral', false);
|
||||
echo '<div class="aksrt-ban-sub">#' . (int) $row['id'] . '</div></td><td class="column-user">' . NativeUi::user_cell($user_id, __('用户已删除', 'aksrt'));
|
||||
echo '<tr><td class="column-type">' . NativeUi::badge($report ? __('用户举报', 'jio') : __('禁封申诉', 'jio'), $report ? 'info' : 'neutral', false);
|
||||
echo '<div class="jio-ban-sub">#' . (int) $row['id'] . '</div></td><td class="column-user">' . NativeUi::user_cell($user_id, __('用户已删除', 'jio'));
|
||||
if ($user_id > 0) {
|
||||
echo '<a class="aksrt-ban-user-filter" href="' . esc_url($this->ban_page_url(array_merge($filters, ['send_user' => $user_id]))) . '">' . esc_html__('筛选此用户', 'aksrt') . '</a>';
|
||||
echo '<a class="jio-ban-user-filter" href="' . esc_url($this->ban_page_url(array_merge($filters, ['send_user' => $user_id]))) . '">' . esc_html__('筛选此用户', 'jio') . '</a>';
|
||||
}
|
||||
echo '</td><td class="column-summary">';
|
||||
if ($report) {
|
||||
echo '<strong>' . $this->ban_target_link((string) ($payload['target_type'] ?? ''), (int) ($payload['target_id'] ?? 0)) . '</strong>';
|
||||
echo '<div class="aksrt-ban-summary">' . esc_html((string) ($payload['category'] ?? '')) . '</div>';
|
||||
echo '<div class="jio-ban-summary">' . esc_html((string) ($payload['category'] ?? '')) . '</div>';
|
||||
} else {
|
||||
echo '<strong>' . esc_html__('申请解除禁封', 'aksrt') . '</strong>';
|
||||
echo '<strong>' . esc_html__('申请解除禁封', 'jio') . '</strong>';
|
||||
}
|
||||
if ((string) ($payload['reason'] ?? '') !== '') echo '<div class="aksrt-ban-summary">' . esc_html((string) $payload['reason']) . '</div>';
|
||||
if ((string) ($payload['reason'] ?? '') !== '') echo '<div class="jio-ban-summary">' . esc_html((string) $payload['reason']) . '</div>';
|
||||
$this->render_ban_detail_template($row, $filters, $page);
|
||||
echo '</td><td class="column-status">' . $this->ban_status_badge($row) . '</td>';
|
||||
echo '<td class="column-time">' . esc_html($this->ban_time((string) $row['time'])) . '</td>';
|
||||
echo '<td class="column-updated">' . esc_html($this->ban_time((string) $row['updated'])) . '</td>';
|
||||
echo '<td class="column-actions"><button type="button" class="button' . ($row['status'] === 'pending' ? ' button-primary' : '') . '" data-aksrt-ban-view="' . esc_attr($key) . '" aria-haspopup="dialog" aria-controls="aksrt-ban-dialog">'
|
||||
. esc_html($row['status'] === 'pending' ? __('审核', 'aksrt') : __('查看详情', 'aksrt')) . '</button></td></tr>';
|
||||
echo '<td class="column-actions"><button type="button" class="button' . ($row['status'] === 'pending' ? ' button-primary' : '') . '" data-jio-ban-view="' . esc_attr($key) . '" aria-haspopup="dialog" aria-controls="jio-ban-dialog">'
|
||||
. esc_html($row['status'] === 'pending' ? __('审核', 'jio') : __('查看详情', 'jio')) . '</button></td></tr>';
|
||||
}
|
||||
|
||||
private function ban_status_badge(array $row): string {
|
||||
if ($row['status'] === 'pending') return NativeUi::badge($row['raw_status'] === 'reviewing' ? __('审核中', 'aksrt') : __('待处理', 'aksrt'), 'warning');
|
||||
if ($row['status'] === 'resolved') return NativeUi::badge($row['kind'] === 'report' ? __('已处理', 'aksrt') : __('已通过', 'aksrt'), 'success');
|
||||
return NativeUi::badge(__('已驳回', 'aksrt'), 'danger');
|
||||
if ($row['status'] === 'pending') return NativeUi::badge($row['raw_status'] === 'reviewing' ? __('审核中', 'jio') : __('待处理', 'jio'), 'warning');
|
||||
if ($row['status'] === 'resolved') return NativeUi::badge($row['kind'] === 'report' ? __('已处理', 'jio') : __('已通过', 'jio'), 'success');
|
||||
return NativeUi::badge(__('已驳回', 'jio'), 'danger');
|
||||
}
|
||||
|
||||
private function ban_time(string $time): string {
|
||||
@@ -147,12 +147,12 @@ trait UsersMenusBan {
|
||||
}
|
||||
|
||||
private function ban_user_home(int $user_id): string {
|
||||
return function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user_id) : get_author_posts_url($user_id);
|
||||
return function_exists('jio_get_user_home_url') ? jio_get_user_home_url($user_id) : get_author_posts_url($user_id);
|
||||
}
|
||||
|
||||
private function ban_target_link(string $type, int $id): string {
|
||||
$labels = ['post' => __('文章', 'aksrt'), 'comment' => __('评论', 'aksrt'), 'user' => __('用户', 'aksrt')];
|
||||
$label = ($labels[$type] ?? __('目标', 'aksrt')) . ' #' . $id;
|
||||
$labels = ['post' => __('文章', 'jio'), 'comment' => __('评论', 'jio'), 'user' => __('用户', 'jio')];
|
||||
$label = ($labels[$type] ?? __('目标', 'jio')) . ' #' . $id;
|
||||
$url = '';
|
||||
if ($type === 'post' && get_post($id)) {
|
||||
$label = get_the_title($id) ?: $label;
|
||||
@@ -163,7 +163,7 @@ trait UsersMenusBan {
|
||||
$label = $user->display_name . ' #' . $id;
|
||||
$url = $this->ban_user_home($id);
|
||||
}
|
||||
return $url !== '' ? '<a href="' . esc_url($url) . '" target="_blank" rel="noopener noreferrer">' . esc_html($label) . '</a>' : esc_html($label . ' ' . __('(目标已不可用)', 'aksrt'));
|
||||
return $url !== '' ? '<a href="' . esc_url($url) . '" target="_blank" rel="noopener noreferrer">' . esc_html($label) . '</a>' : esc_html($label . ' ' . __('(目标已不可用)', 'jio'));
|
||||
}
|
||||
|
||||
private function cache_ban_targets(array $items): void {
|
||||
@@ -194,7 +194,7 @@ trait UsersMenusBan {
|
||||
global $wpdb;
|
||||
$placeholders = implode(',', array_fill(0, count($ids), '%d'));
|
||||
$rows = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT user_id, type, scope, reason, created_at, expires_at FROM {$wpdb->prefix}aksrt_user_restriction WHERE user_id IN ({$placeholders}) AND (expires_at IS NULL OR expires_at > %s) ORDER BY created_at DESC",
|
||||
"SELECT user_id, type, scope, reason, created_at, expires_at FROM {$wpdb->prefix}jio_user_restriction WHERE user_id IN ({$placeholders}) AND (expires_at IS NULL OR expires_at > %s) ORDER BY created_at DESC",
|
||||
array_merge($ids, [current_time('mysql', true)])
|
||||
), ARRAY_A);
|
||||
foreach ((array) $rows as $row) $this->ban_restrictions[(int) $row['user_id']][] = $row;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -11,87 +11,87 @@ trait UsersMenusBanActions {
|
||||
|
||||
public function handle_update_report(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
|
||||
$report_id = absint($this->ban_post_value('report_id'));
|
||||
$new_status = sanitize_key($this->ban_post_value('new_status'));
|
||||
$note = trim(sanitize_textarea_field($this->ban_post_value('note')));
|
||||
check_admin_referer('aksrt_users_update_report_' . $report_id);
|
||||
check_admin_referer('jio_users_update_report_' . $report_id);
|
||||
|
||||
if ($report_id <= 0 || !in_array($new_status, ['resolved', 'dismissed'], true)) {
|
||||
$this->redirect_ban_action('report_failed', __('举报处理请求无效。', 'aksrt'));
|
||||
$this->redirect_ban_action('report_failed', __('举报处理请求无效。', 'jio'));
|
||||
}
|
||||
if ($new_status === 'dismissed' && $note === '') {
|
||||
$this->redirect_ban_action('report_failed', __('驳回举报时必须填写处理说明。', 'aksrt'));
|
||||
$this->redirect_ban_action('report_failed', __('驳回举报时必须填写处理说明。', 'jio'));
|
||||
}
|
||||
|
||||
$row = $this->find_ban_request('report', $report_id);
|
||||
if (!$this->ban_request_is_current($row, 'pending')) {
|
||||
$this->redirect_ban_action('report_failed', __('举报记录已变化或已处理,请刷新后重试。', 'aksrt'));
|
||||
$this->redirect_ban_action('report_failed', __('举报记录已变化或已处理,请刷新后重试。', 'jio'));
|
||||
}
|
||||
if (!class_exists('\\AKSRT\\Messages\\ReportCenter')) {
|
||||
$this->redirect_ban_action('report_failed', __('举报处理服务不可用。', 'aksrt'));
|
||||
if (!class_exists('\\Jio\\Messages\\ReportCenter')) {
|
||||
$this->redirect_ban_action('report_failed', __('举报处理服务不可用。', 'jio'));
|
||||
}
|
||||
|
||||
$ok = \AKSRT\Messages\ReportCenter::get_instance()->update_status($report_id, $new_status, $note);
|
||||
$ok = \Jio\Messages\ReportCenter::get_instance()->update_status($report_id, $new_status, $note);
|
||||
$this->redirect_ban_action(
|
||||
$ok ? 'report_updated' : 'report_failed',
|
||||
$ok ? '' : __('举报状态更新失败,请稍后重试。', 'aksrt')
|
||||
$ok ? '' : __('举报状态更新失败,请稍后重试。', 'jio')
|
||||
);
|
||||
}
|
||||
|
||||
public function handle_process_appeal(): void {
|
||||
if (!current_user_can(self::CAPABILITY)) {
|
||||
wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
wp_die(esc_html__('权限不足', 'jio'));
|
||||
}
|
||||
|
||||
$user_id = absint($this->ban_post_value('user_id'));
|
||||
$decision = sanitize_key($this->ban_post_value('decision'));
|
||||
$note = trim(sanitize_textarea_field($this->ban_post_value('note')));
|
||||
check_admin_referer('aksrt_users_process_appeal_' . $user_id);
|
||||
check_admin_referer('jio_users_process_appeal_' . $user_id);
|
||||
|
||||
if ($user_id <= 0 || !in_array($decision, ['approve', 'reject'], true)) {
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉处理请求无效。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉处理请求无效。', 'jio'));
|
||||
}
|
||||
if ($decision === 'reject' && $note === '') {
|
||||
$this->redirect_ban_action('appeal_failed', __('驳回申诉时必须填写拒绝原因。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('驳回申诉时必须填写拒绝原因。', 'jio'));
|
||||
}
|
||||
|
||||
$row = $this->find_ban_request('appeal', $user_id);
|
||||
if (!$this->ban_request_is_current($row, 'pending')) {
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉记录已变化或已处理,请刷新后重试。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉记录已变化或已处理,请刷新后重试。', 'jio'));
|
||||
}
|
||||
|
||||
$user = get_userdata($user_id);
|
||||
if (!($user instanceof \WP_User)) {
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉用户不存在,无法处理。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('申诉用户不存在,无法处理。', 'jio'));
|
||||
}
|
||||
|
||||
if ($decision === 'approve') {
|
||||
if (!class_exists('\\AKSRT\\User\\Restriction')) {
|
||||
$this->redirect_ban_action('appeal_failed', __('用户限制服务不可用,无法通过申诉。', 'aksrt'));
|
||||
if (!class_exists('\\Jio\\User\\Restriction')) {
|
||||
$this->redirect_ban_action('appeal_failed', __('用户限制服务不可用,无法通过申诉。', 'jio'));
|
||||
}
|
||||
global $wpdb;
|
||||
$restriction_service = \AKSRT\User\Restriction::get_instance();
|
||||
$restriction_service = \Jio\User\Restriction::get_instance();
|
||||
$restriction_service->lift_user_restriction($user_id);
|
||||
$lift_error = (string) $wpdb->last_error;
|
||||
if ($lift_error !== '') {
|
||||
$this->redirect_ban_action('appeal_failed', __('解除限制时发生数据库错误,申诉未通过。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('解除限制时发生数据库错误,申诉未通过。', 'jio'));
|
||||
}
|
||||
|
||||
$active_restrictions = $restriction_service->get_user_restrictions($user_id, true);
|
||||
$check_error = (string) $wpdb->last_error;
|
||||
if ($check_error !== '') {
|
||||
$this->redirect_ban_action('appeal_failed', __('无法核实限制是否解除,限制可能已解除,请检查用户当前状态。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('无法核实限制是否解除,限制可能已解除,请检查用户当前状态。', 'jio'));
|
||||
}
|
||||
foreach ($active_restrictions as $restriction) {
|
||||
if (in_array($restriction['type'] ?? '', [
|
||||
\AKSRT\User\Restriction::TYPE_BAN,
|
||||
\AKSRT\User\Restriction::TYPE_SUSPEND,
|
||||
\AKSRT\User\Restriction::TYPE_MUTE,
|
||||
\Jio\User\Restriction::TYPE_BAN,
|
||||
\Jio\User\Restriction::TYPE_SUSPEND,
|
||||
\Jio\User\Restriction::TYPE_MUTE,
|
||||
], true)) {
|
||||
$this->redirect_ban_action('appeal_failed', __('仍有生效限制,申诉未通过。', 'aksrt'));
|
||||
$this->redirect_ban_action('appeal_failed', __('仍有生效限制,申诉未通过。', 'jio'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,58 +103,58 @@ trait UsersMenusBanActions {
|
||||
$payload['handled_by'] = get_current_user_id();
|
||||
$payload['decision'] = $raw_status;
|
||||
|
||||
$history = get_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY, true);
|
||||
$history = get_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY, true);
|
||||
$history = is_array($history) ? $history : [];
|
||||
$history[] = $payload;
|
||||
$history = array_slice($history, -30);
|
||||
|
||||
$old_payload = $row['payload'];
|
||||
$old_history = get_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY, true);
|
||||
$payload_updated = update_user_meta($user_id, \AKSRT\User\BanAppeal::META_PAYLOAD, $payload, $old_payload);
|
||||
$old_history = get_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY, true);
|
||||
$payload_updated = update_user_meta($user_id, \Jio\User\BanAppeal::META_PAYLOAD, $payload, $old_payload);
|
||||
if ($payload_updated === false) {
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉处理记录保存失败。', 'aksrt'), $decision));
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉处理记录保存失败。', 'jio'), $decision));
|
||||
}
|
||||
|
||||
$status_updated = update_user_meta($user_id, \AKSRT\User\BanAppeal::META_STATUS, $raw_status, 'pending');
|
||||
$status_updated = update_user_meta($user_id, \Jio\User\BanAppeal::META_STATUS, $raw_status, 'pending');
|
||||
if ($status_updated === false) {
|
||||
update_user_meta($user_id, \AKSRT\User\BanAppeal::META_PAYLOAD, $old_payload);
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉状态保存失败。', 'aksrt'), $decision));
|
||||
update_user_meta($user_id, \Jio\User\BanAppeal::META_PAYLOAD, $old_payload);
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉状态保存失败。', 'jio'), $decision));
|
||||
}
|
||||
|
||||
$history_updated = update_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY, $history);
|
||||
$history_updated = update_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY, $history);
|
||||
if ($history_updated === false) {
|
||||
update_user_meta($user_id, \AKSRT\User\BanAppeal::META_STATUS, 'pending');
|
||||
update_user_meta($user_id, \AKSRT\User\BanAppeal::META_PAYLOAD, $old_payload);
|
||||
update_user_meta($user_id, \Jio\User\BanAppeal::META_STATUS, 'pending');
|
||||
update_user_meta($user_id, \Jio\User\BanAppeal::META_PAYLOAD, $old_payload);
|
||||
if ($old_history === '' || $old_history === null) {
|
||||
delete_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY);
|
||||
delete_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY);
|
||||
} else {
|
||||
update_user_meta($user_id, \AKSRT\User\BanAppeal::META_HISTORY, $old_history);
|
||||
update_user_meta($user_id, \Jio\User\BanAppeal::META_HISTORY, $old_history);
|
||||
}
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉历史保存失败。', 'aksrt'), $decision));
|
||||
$this->redirect_ban_action('appeal_failed', $this->appeal_metadata_error(__('申诉历史保存失败。', 'jio'), $decision));
|
||||
}
|
||||
|
||||
if (class_exists('\\AKSRT\\Messages\\Message')) {
|
||||
if (class_exists('\\Jio\\Messages\\Message')) {
|
||||
$message = $decision === 'approve'
|
||||
? __('你的禁封申诉已通过,限制已解除。', 'aksrt')
|
||||
: __('你的禁封申诉未通过,可修改后重新提交。', 'aksrt');
|
||||
? __('你的禁封申诉已通过,限制已解除。', 'jio')
|
||||
: __('你的禁封申诉未通过,可修改后重新提交。', 'jio');
|
||||
if ($note !== '') {
|
||||
$message .= ' ' . sprintf(__('备注:%s', 'aksrt'), $note);
|
||||
$message .= ' ' . sprintf(__('备注:%s', 'jio'), $note);
|
||||
}
|
||||
\AKSRT\Messages\Message::get_instance()->send(
|
||||
\Jio\Messages\Message::get_instance()->send(
|
||||
$user_id,
|
||||
'ban_appeal',
|
||||
$message,
|
||||
['title' => $decision === 'approve' ? __('申诉通过', 'aksrt') : __('申诉驳回', 'aksrt')]
|
||||
['title' => $decision === 'approve' ? __('申诉通过', 'jio') : __('申诉驳回', 'jio')]
|
||||
);
|
||||
}
|
||||
|
||||
do_action('aksrt_ban_appeal_processed', $user_id, $decision, $note);
|
||||
do_action('jio_ban_appeal_processed', $user_id, $decision, $note);
|
||||
$this->redirect_ban_action($decision === 'approve' ? 'appeal_approved' : 'appeal_rejected');
|
||||
}
|
||||
|
||||
private function appeal_metadata_error(string $message, string $decision): string {
|
||||
if ($decision === 'approve') {
|
||||
return $message . ' ' . __('限制可能已解除,请核对用户当前状态。', 'aksrt');
|
||||
return $message . ' ' . __('限制可能已解除,请核对用户当前状态。', 'jio');
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
@@ -193,8 +193,8 @@ trait UsersMenusBanActions {
|
||||
'paged' => max(1, absint($this->ban_post_value('paged', '1'))),
|
||||
];
|
||||
if ($error !== '') {
|
||||
$extra['aksrt_ban_error'] = $error;
|
||||
$extra['jio_ban_error'] = $error;
|
||||
}
|
||||
$this->redirect('aksrt_user_ban', $message, $extra);
|
||||
$this->redirect('jio_user_ban', $message, $extra);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/** Report and ban appeal detail/review dialog templates. @package AKSRT */
|
||||
/** Report and ban appeal detail/review dialog templates. @package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\User\Restriction;
|
||||
use Jio\User\Restriction;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -13,24 +13,24 @@ trait UsersMenusBanDialogs {
|
||||
$pending = $row['status'] === 'pending';
|
||||
$report = $row['kind'] === 'report';
|
||||
$key = $row['kind'] . '-' . $row['id'];
|
||||
echo '<template id="aksrt-ban-detail-' . esc_attr($key) . '">';
|
||||
echo '<template id="jio-ban-detail-' . esc_attr($key) . '">';
|
||||
if ($pending) {
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-ban-dialog__frame" data-aksrt-ban-review>';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-ban-dialog__frame" data-jio-ban-review>';
|
||||
$this->render_ban_context($row, $filters, $page);
|
||||
} else {
|
||||
echo '<div class="aksrt-ban-dialog__frame">';
|
||||
echo '<div class="jio-ban-dialog__frame">';
|
||||
}
|
||||
$title = $report
|
||||
? ($pending ? __('审核用户举报', 'aksrt') : __('用户举报详情', 'aksrt'))
|
||||
: ($pending ? __('审核禁封申诉', 'aksrt') : __('禁封申诉详情', 'aksrt'));
|
||||
echo '<div class="aksrt-ban-dialog__head"><div><h2 id="aksrt-ban-dialog-title" tabindex="-1" autofocus>' . esc_html($title) . '</h2>';
|
||||
echo '<p id="aksrt-ban-dialog-description">' . esc_html($pending ? __('先核对原始资料,再选择处理结果;提交后将通知用户。', 'aksrt') : __('该记录已处理,以下资料和结果仅供查看。', 'aksrt')) . '</p></div>';
|
||||
echo '<button type="button" class="aksrt-ban-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></div>';
|
||||
echo '<div class="aksrt-ban-dialog__body"><div class="aksrt-ban-applicant">' . NativeUi::user_cell((int) $row['user_id'], __('用户已删除', 'aksrt')) . $this->ban_status_badge($row) . '</div>';
|
||||
echo '<dl class="aksrt-ban-meta">';
|
||||
$this->render_ban_meta_item(__('记录编号', 'aksrt'), ($report ? __('举报', 'aksrt') : __('申诉', 'aksrt')) . ' #' . $row['id']);
|
||||
$this->render_ban_meta_item(__('提交时间', 'aksrt'), $this->ban_time((string) $row['time']));
|
||||
$this->render_ban_meta_item(__('更新时间', 'aksrt'), $this->ban_time((string) $row['updated']));
|
||||
? ($pending ? __('审核用户举报', 'jio') : __('用户举报详情', 'jio'))
|
||||
: ($pending ? __('审核禁封申诉', 'jio') : __('禁封申诉详情', 'jio'));
|
||||
echo '<div class="jio-ban-dialog__head"><div><h2 id="jio-ban-dialog-title" tabindex="-1" autofocus>' . esc_html($title) . '</h2>';
|
||||
echo '<p id="jio-ban-dialog-description">' . esc_html($pending ? __('先核对原始资料,再选择处理结果;提交后将通知用户。', 'jio') : __('该记录已处理,以下资料和结果仅供查看。', 'jio')) . '</p></div>';
|
||||
echo '<button type="button" class="jio-ban-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></div>';
|
||||
echo '<div class="jio-ban-dialog__body"><div class="jio-ban-applicant">' . NativeUi::user_cell((int) $row['user_id'], __('用户已删除', 'jio')) . $this->ban_status_badge($row) . '</div>';
|
||||
echo '<dl class="jio-ban-meta">';
|
||||
$this->render_ban_meta_item(__('记录编号', 'jio'), ($report ? __('举报', 'jio') : __('申诉', 'jio')) . ' #' . $row['id']);
|
||||
$this->render_ban_meta_item(__('提交时间', 'jio'), $this->ban_time((string) $row['time']));
|
||||
$this->render_ban_meta_item(__('更新时间', 'jio'), $this->ban_time((string) $row['updated']));
|
||||
echo '</dl>';
|
||||
if ($report) {
|
||||
$this->render_ban_report_details($row);
|
||||
@@ -39,20 +39,20 @@ trait UsersMenusBanDialogs {
|
||||
}
|
||||
$this->render_ban_review_history($row);
|
||||
if ($pending) $this->render_ban_review_fields($report);
|
||||
echo '</div><div class="aksrt-ban-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html($pending ? __('取消', 'aksrt') : __('关闭', 'aksrt')) . '</button>';
|
||||
echo '</div><div class="jio-ban-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html($pending ? __('取消', 'jio') : __('关闭', 'jio')) . '</button>';
|
||||
if ($pending) {
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-ban-submit disabled'
|
||||
. ' data-idle-label="' . esc_attr__('请选择处理结果', 'aksrt') . '"'
|
||||
. ' data-approve-label="' . esc_attr($report ? __('确认标记已处理', 'aksrt') : __('确认通过并解除限制', 'aksrt')) . '"'
|
||||
. ' data-reject-label="' . esc_attr__('确认驳回', 'aksrt') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">' . esc_html__('请选择处理结果', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-ban-submit disabled'
|
||||
. ' data-idle-label="' . esc_attr__('请选择处理结果', 'jio') . '"'
|
||||
. ' data-approve-label="' . esc_attr($report ? __('确认标记已处理', 'jio') : __('确认通过并解除限制', 'jio')) . '"'
|
||||
. ' data-reject-label="' . esc_attr__('确认驳回', 'jio') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">' . esc_html__('请选择处理结果', 'jio') . '</button>';
|
||||
}
|
||||
echo '</div>' . ($pending ? '</form>' : '</div>') . '</template>';
|
||||
}
|
||||
|
||||
private function render_ban_context(array $row, array $filters, int $page): void {
|
||||
$report = $row['kind'] === 'report';
|
||||
$action = $report ? 'aksrt_users_update_report' : 'aksrt_users_process_appeal';
|
||||
$action = $report ? 'jio_users_update_report' : 'jio_users_process_appeal';
|
||||
$fields = [
|
||||
'action' => $action,
|
||||
$report ? 'report_id' : 'user_id' => (int) $row['id'],
|
||||
@@ -76,7 +76,7 @@ trait UsersMenusBanDialogs {
|
||||
}
|
||||
|
||||
private function render_ban_text_section(string $title, string $text): void {
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html($title) . '</h3><div class="aksrt-ban-copy">';
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html($title) . '</h3><div class="jio-ban-copy">';
|
||||
// Evidence URLs remain links: opening a case must not load reporter-supplied remote images.
|
||||
$parts = preg_split('~(https?://[^\s<>"\x27]+)~u', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
foreach ($parts ?: [$text] as $index => $part) {
|
||||
@@ -84,7 +84,7 @@ trait UsersMenusBanDialogs {
|
||||
? '<a href="' . esc_url($part, ['http', 'https']) . '" target="_blank" rel="noopener noreferrer">' . esc_html($part) . '</a>'
|
||||
: esc_html($part !== '' ? $part : '');
|
||||
}
|
||||
if ($text === '') echo esc_html__('未填写', 'aksrt');
|
||||
if ($text === '') echo esc_html__('未填写', 'jio');
|
||||
echo '</div></section>';
|
||||
}
|
||||
|
||||
@@ -92,59 +92,59 @@ trait UsersMenusBanDialogs {
|
||||
$payload = $row['payload'];
|
||||
$type = (string) ($payload['target_type'] ?? '');
|
||||
$id = (int) ($payload['target_id'] ?? 0);
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html__('举报对象', 'aksrt') . '</h3><div class="aksrt-ban-target">' . $this->ban_target_link($type, $id) . '</div></section>';
|
||||
if ((string) ($payload['category'] ?? '') !== '') $this->render_ban_text_section(__('举报分类', 'aksrt'), (string) $payload['category']);
|
||||
$this->render_ban_text_section(__('举报内容与举证', 'aksrt'), (string) ($payload['reason'] ?? ''));
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html__('举报对象', 'jio') . '</h3><div class="jio-ban-target">' . $this->ban_target_link($type, $id) . '</div></section>';
|
||||
if ((string) ($payload['category'] ?? '') !== '') $this->render_ban_text_section(__('举报分类', 'jio'), (string) $payload['category']);
|
||||
$this->render_ban_text_section(__('举报内容与举证', 'jio'), (string) ($payload['reason'] ?? ''));
|
||||
if ($type === 'user') {
|
||||
$this->render_ban_restriction_section($id);
|
||||
if (get_userdata($id)) {
|
||||
echo '<p class="aksrt-ban-profile-link"><a href="' . esc_url($this->ban_user_home($id)) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看用户主页并管理禁封(新窗口)', 'aksrt') . '</a></p>';
|
||||
echo '<p class="jio-ban-profile-link"><a href="' . esc_url($this->ban_user_home($id)) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看用户主页并管理禁封(新窗口)', 'jio') . '</a></p>';
|
||||
}
|
||||
}
|
||||
if ($row['status'] === 'pending') {
|
||||
echo '<p class="aksrt-native-callout">' . esc_html__('本次审核仅记录举报处理结果,不会自动禁封用户或删除内容。请先核实举证并完成必要处置。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-native-callout">' . esc_html__('本次审核仅记录举报处理结果,不会自动禁封用户或删除内容。请先核实举证并完成必要处置。', 'jio') . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
private function render_ban_appeal_details(array $row): void {
|
||||
$payload = $row['payload'];
|
||||
$this->render_ban_restriction_section((int) $row['user_id']);
|
||||
if (!empty($payload['reason_key'])) $this->render_ban_text_section(__('申诉类型', 'aksrt'), (string) $payload['reason_key']);
|
||||
if (!empty($payload['reason_key'])) $this->render_ban_text_section(__('申诉类型', 'jio'), (string) $payload['reason_key']);
|
||||
$data = isset($payload['data']) && is_array($payload['data']) ? $payload['data'] : [];
|
||||
if ($data !== []) {
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html__('申诉资料', 'aksrt') . '</h3><dl class="aksrt-ban-materials">';
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html__('申诉资料', 'jio') . '</h3><dl class="jio-ban-materials">';
|
||||
foreach ($data as $label => $value) {
|
||||
if (is_scalar($value)) $this->render_ban_meta_item((string) $label, (string) $value);
|
||||
}
|
||||
echo '</dl></section>';
|
||||
}
|
||||
$reason = (string) ($payload['reason'] ?? '');
|
||||
if ($data === [] || !in_array($reason, $data, true)) $this->render_ban_text_section(__('申诉说明', 'aksrt'), $reason);
|
||||
if ($data === [] || !in_array($reason, $data, true)) $this->render_ban_text_section(__('申诉说明', 'jio'), $reason);
|
||||
if (get_userdata((int) $row['user_id'])) {
|
||||
echo '<p class="aksrt-ban-profile-link"><a href="' . esc_url($this->ban_user_home((int) $row['user_id'])) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看申诉用户主页(新窗口)', 'aksrt') . '</a></p>';
|
||||
echo '<p class="jio-ban-profile-link"><a href="' . esc_url($this->ban_user_home((int) $row['user_id'])) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('查看申诉用户主页(新窗口)', 'jio') . '</a></p>';
|
||||
}
|
||||
}
|
||||
|
||||
private function render_ban_restriction_section(int $user_id): void {
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html__('当前限制', 'aksrt') . '</h3>';
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html__('当前限制', 'jio') . '</h3>';
|
||||
if (!class_exists(Restriction::class)) {
|
||||
echo '<p class="aksrt-native-callout is-warning">' . esc_html__('限制服务不可用,暂时无法核对当前状态。', 'aksrt') . '</p></section>';
|
||||
echo '<p class="jio-native-callout is-warning">' . esc_html__('限制服务不可用,暂时无法核对当前状态。', 'jio') . '</p></section>';
|
||||
return;
|
||||
}
|
||||
$rows = $this->ban_restrictions[$user_id] ?? [];
|
||||
if ($rows === []) {
|
||||
echo '<p class="aksrt-ban-normal">' . esc_html__('当前无生效限制。此处仅展示当前状态,不会自动更改处理结果。', 'aksrt') . '</p></section>';
|
||||
echo '<p class="jio-ban-normal">' . esc_html__('当前无生效限制。此处仅展示当前状态,不会自动更改处理结果。', 'jio') . '</p></section>';
|
||||
return;
|
||||
}
|
||||
$labels = [Restriction::TYPE_WARN => __('警告', 'aksrt'), Restriction::TYPE_MUTE => __('禁言', 'aksrt'), Restriction::TYPE_SUSPEND => __('停权', 'aksrt'), Restriction::TYPE_BAN => __('封禁', 'aksrt')];
|
||||
echo '<ul class="aksrt-ban-restrictions">';
|
||||
$labels = [Restriction::TYPE_WARN => __('警告', 'jio'), Restriction::TYPE_MUTE => __('禁言', 'jio'), Restriction::TYPE_SUSPEND => __('停权', 'jio'), Restriction::TYPE_BAN => __('封禁', 'jio')];
|
||||
echo '<ul class="jio-ban-restrictions">';
|
||||
foreach ($rows as $restriction) {
|
||||
echo '<li><div class="aksrt-ban-restriction__title">' . NativeUi::badge($labels[$restriction['type']] ?? (string) $restriction['type'], 'warning', false)
|
||||
. '<span>' . esc_html(($restriction['scope'] ?? '') === Restriction::SCOPE_COMMENT ? __('评论范围', 'aksrt') : __('全站范围', 'aksrt')) . '</span></div>';
|
||||
echo '<div class="aksrt-ban-copy">' . esc_html((string) ($restriction['reason'] ?? '')) . '</div>';
|
||||
echo '<li><div class="jio-ban-restriction__title">' . NativeUi::badge($labels[$restriction['type']] ?? (string) $restriction['type'], 'warning', false)
|
||||
. '<span>' . esc_html(($restriction['scope'] ?? '') === Restriction::SCOPE_COMMENT ? __('评论范围', 'jio') : __('全站范围', 'jio')) . '</span></div>';
|
||||
echo '<div class="jio-ban-copy">' . esc_html((string) ($restriction['reason'] ?? '')) . '</div>';
|
||||
$start = !empty($restriction['created_at']) ? get_date_from_gmt((string) $restriction['created_at'], 'Y-m-d H:i') : '—';
|
||||
$end = !empty($restriction['expires_at']) ? get_date_from_gmt((string) $restriction['expires_at'], 'Y-m-d H:i') : __('永久', 'aksrt');
|
||||
echo '<div class="aksrt-ban-sub">' . esc_html(sprintf(__('开始:%1$s;结束:%2$s', 'aksrt'), $start, $end)) . '</div></li>';
|
||||
$end = !empty($restriction['expires_at']) ? get_date_from_gmt((string) $restriction['expires_at'], 'Y-m-d H:i') : __('永久', 'jio');
|
||||
echo '<div class="jio-ban-sub">' . esc_html(sprintf(__('开始:%1$s;结束:%2$s', 'jio'), $start, $end)) . '</div></li>';
|
||||
}
|
||||
echo '</ul></section>';
|
||||
}
|
||||
@@ -156,30 +156,30 @@ trait UsersMenusBanDialogs {
|
||||
$notes[] = ['note' => $payload['admin_note'] ?? '', 'operator' => $payload['handled_by'] ?? 0, 'timestamp' => $payload['handled_at'], 'status' => $payload['decision'] ?? $row['raw_status']];
|
||||
}
|
||||
if ($notes === []) return;
|
||||
$labels = ['pending' => __('待处理', 'aksrt'), 'reviewing' => __('审核中', 'aksrt'), 'resolved' => __('已处理', 'aksrt'), 'dismissed' => __('已驳回', 'aksrt'), 'approved' => __('已通过', 'aksrt'), 'rejected' => __('已驳回', 'aksrt')];
|
||||
echo '<section class="aksrt-ban-section"><h3>' . esc_html__('处理记录', 'aksrt') . '</h3><ol class="aksrt-ban-history">';
|
||||
$labels = ['pending' => __('待处理', 'jio'), 'reviewing' => __('审核中', 'jio'), 'resolved' => __('已处理', 'jio'), 'dismissed' => __('已驳回', 'jio'), 'approved' => __('已通过', 'jio'), 'rejected' => __('已驳回', 'jio')];
|
||||
echo '<section class="jio-ban-section"><h3>' . esc_html__('处理记录', 'jio') . '</h3><ol class="jio-ban-history">';
|
||||
foreach ($notes as $note) {
|
||||
if (!is_array($note)) continue;
|
||||
$operator = get_userdata((int) ($note['operator'] ?? 0));
|
||||
echo '<li><div class="aksrt-ban-history__meta"><strong>' . esc_html($labels[$note['status'] ?? ''] ?? (string) ($note['status'] ?? '')) . '</strong><span>'
|
||||
. esc_html($operator ? $operator->display_name : __('管理员', 'aksrt')) . '</span><time>' . esc_html((string) ($note['timestamp'] ?? '')) . '</time></div>';
|
||||
echo '<div class="aksrt-ban-copy">' . esc_html((string) ($note['note'] ?? '') !== '' ? (string) $note['note'] : __('未填写处理留言', 'aksrt')) . '</div></li>';
|
||||
echo '<li><div class="jio-ban-history__meta"><strong>' . esc_html($labels[$note['status'] ?? ''] ?? (string) ($note['status'] ?? '')) . '</strong><span>'
|
||||
. esc_html($operator ? $operator->display_name : __('管理员', 'jio')) . '</span><time>' . esc_html((string) ($note['timestamp'] ?? '')) . '</time></div>';
|
||||
echo '<div class="jio-ban-copy">' . esc_html((string) ($note['note'] ?? '') !== '' ? (string) $note['note'] : __('未填写处理留言', 'jio')) . '</div></li>';
|
||||
}
|
||||
echo '</ol></section>';
|
||||
}
|
||||
|
||||
private function render_ban_review_fields(bool $report): void {
|
||||
echo '<section class="aksrt-ban-review"><h3>' . esc_html__('审核处理', 'aksrt') . '</h3><fieldset class="aksrt-ban-decisions"><legend>' . esc_html__('处理结果(必选)', 'aksrt') . '</legend>';
|
||||
$choices = $report ? ['resolved' => __('标记已处理', 'aksrt'), 'dismissed' => __('驳回举报', 'aksrt')] : ['approve' => __('通过申诉', 'aksrt'), 'reject' => __('驳回申诉', 'aksrt')];
|
||||
echo '<section class="jio-ban-review"><h3>' . esc_html__('审核处理', 'jio') . '</h3><fieldset class="jio-ban-decisions"><legend>' . esc_html__('处理结果(必选)', 'jio') . '</legend>';
|
||||
$choices = $report ? ['resolved' => __('标记已处理', 'jio'), 'dismissed' => __('驳回举报', 'jio')] : ['approve' => __('通过申诉', 'jio'), 'reject' => __('驳回申诉', 'jio')];
|
||||
foreach ($choices as $value => $label) {
|
||||
echo '<label><input type="radio" name="' . ($report ? 'new_status' : 'decision') . '" value="' . esc_attr($value) . '" required><span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset>';
|
||||
if (!$report) {
|
||||
echo '<p class="aksrt-native-callout is-warning">' . esc_html__('通过申诉将解除该用户当前生效的封禁、停权和禁言,保留警告记录;驳回不会改变限制。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-native-callout is-warning">' . esc_html__('通过申诉将解除该用户当前生效的封禁、停权和禁言,保留警告记录;驳回不会改变限制。', 'jio') . '</p>';
|
||||
}
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label" data-aksrt-ban-note-label data-optional="' . esc_attr__('处理说明(选填)', 'aksrt') . '" data-required="' . esc_attr__('驳回原因(必填)', 'aksrt') . '">' . esc_html__('处理说明(选填)', 'aksrt') . '</span>';
|
||||
echo '<textarea name="note" rows="3" data-required-error="' . esc_attr__('请填写驳回原因,不能只包含空格', 'aksrt') . '" aria-describedby="aksrt-ban-note-hint"></textarea></label>';
|
||||
echo '<p id="aksrt-ban-note-hint" class="aksrt-native-field__hint">' . esc_html($report ? __('处理结果及说明会反馈给举报人。驳回时请填写具体原因。', 'aksrt') : __('处理结果及说明会反馈给申诉用户。驳回时请说明原因,便于用户补充资料。', 'aksrt')) . '</p></section>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label" data-jio-ban-note-label data-optional="' . esc_attr__('处理说明(选填)', 'jio') . '" data-required="' . esc_attr__('驳回原因(必填)', 'jio') . '">' . esc_html__('处理说明(选填)', 'jio') . '</span>';
|
||||
echo '<textarea name="note" rows="3" data-required-error="' . esc_attr__('请填写驳回原因,不能只包含空格', 'jio') . '" aria-describedby="jio-ban-note-hint"></textarea></label>';
|
||||
echo '<p id="jio-ban-note-hint" class="jio-native-field__hint">' . esc_html($report ? __('处理结果及说明会反馈给举报人。驳回时请填写具体原因。', 'jio') : __('处理结果及说明会反馈给申诉用户。驳回时请说明原因,便于用户补充资料。', 'jio')) . '</p></section>';
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -105,10 +105,10 @@ trait UsersMenusBanQuery {
|
||||
private function query_ban_report_rows(array $filters): array {
|
||||
global $wpdb;
|
||||
|
||||
if (!class_exists('\\AKSRT\\Core\\DBMigrator')) {
|
||||
if (!class_exists('\\Jio\\Core\\DBMigrator')) {
|
||||
return [];
|
||||
}
|
||||
$table = \AKSRT\Core\DBMigrator::table('report');
|
||||
$table = \Jio\Core\DBMigrator::table('report');
|
||||
if ($table === '') {
|
||||
return [];
|
||||
}
|
||||
@@ -147,13 +147,13 @@ trait UsersMenusBanQuery {
|
||||
|
||||
/** @return array<int,array<string,mixed>> */
|
||||
private function query_ban_appeal_rows(array $filters): array {
|
||||
if (!class_exists('\\AKSRT\\User\\BanAppeal')) {
|
||||
if (!class_exists('\\Jio\\User\\BanAppeal')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$args = [
|
||||
'meta_query' => [[
|
||||
'key' => \AKSRT\User\BanAppeal::META_STATUS,
|
||||
'key' => \Jio\User\BanAppeal::META_STATUS,
|
||||
'value' => ['pending', 'approved', 'rejected'],
|
||||
'compare' => 'IN',
|
||||
]],
|
||||
@@ -216,12 +216,12 @@ trait UsersMenusBanQuery {
|
||||
}
|
||||
}
|
||||
$report_ids = array_values(array_unique($report_ids));
|
||||
if ($report_ids === [] || !class_exists('\\AKSRT\\Core\\DBMigrator')) {
|
||||
if ($report_ids === [] || !class_exists('\\Jio\\Core\\DBMigrator')) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('report');
|
||||
$table = \Jio\Core\DBMigrator::table('report');
|
||||
if ($table === '') {
|
||||
return;
|
||||
}
|
||||
@@ -281,12 +281,12 @@ trait UsersMenusBanQuery {
|
||||
if ($kind === 'appeal') {
|
||||
return $this->get_ban_appeal_row($id);
|
||||
}
|
||||
if ($kind !== 'report' || !class_exists('\\AKSRT\\Core\\DBMigrator')) {
|
||||
if ($kind !== 'report' || !class_exists('\\Jio\\Core\\DBMigrator')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('report');
|
||||
$table = \Jio\Core\DBMigrator::table('report');
|
||||
if ($table === '') {
|
||||
return null;
|
||||
}
|
||||
@@ -333,17 +333,17 @@ trait UsersMenusBanQuery {
|
||||
}
|
||||
|
||||
private function get_ban_appeal_row(int $user_id): ?array {
|
||||
if ($user_id <= 0 || !class_exists('\\AKSRT\\User\\BanAppeal')) {
|
||||
if ($user_id <= 0 || !class_exists('\\Jio\\User\\BanAppeal')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$raw_status = \AKSRT\User\BanAppeal::get_status($user_id);
|
||||
$raw_status = \Jio\User\BanAppeal::get_status($user_id);
|
||||
$status = $raw_status === 'approved' ? 'resolved' : ($raw_status === 'rejected' ? 'dismissed' : $raw_status);
|
||||
if (!in_array($status, ['pending', 'resolved', 'dismissed'], true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$payload = \AKSRT\User\BanAppeal::get_payload($user_id);
|
||||
$payload = \Jio\User\BanAppeal::get_payload($user_id);
|
||||
return [
|
||||
'kind' => 'appeal',
|
||||
'id' => $user_id,
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* Native invite-code list and page assets.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -13,44 +13,44 @@ trait UsersMenusInviteCode {
|
||||
use UsersMenusInviteForms;
|
||||
|
||||
public function enqueue_invite_assets(string $hook): void {
|
||||
if ($hook !== 'users_page_aksrt_invite_codes') {
|
||||
if ($hook !== 'users_page_jio_invite_codes') {
|
||||
return;
|
||||
}
|
||||
$css = '/assets/css/admin/invite-codes.css';
|
||||
$js = '/assets/js/admin-invite-codes.js';
|
||||
wp_enqueue_style('aksrt-admin-invite-codes', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-invite-codes', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-invite-codes', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-invite-codes', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
public function render_invite_codes_page(): void {
|
||||
if (!current_user_can('edit_users')) {
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'aksrt'));
|
||||
wp_die(esc_html__('您没有权限访问此页面。', 'jio'));
|
||||
}
|
||||
|
||||
$service = \AKSRT\Payment\InviteCode::get_instance();
|
||||
$this_url = admin_url('users.php?page=aksrt_invite_codes');
|
||||
$nonce = wp_create_nonce('aksrt_invite_admin');
|
||||
NativeUi::page_open('aksrt-inv-page');
|
||||
$service = \Jio\Payment\InviteCode::get_instance();
|
||||
$this_url = admin_url('users.php?page=jio_invite_codes');
|
||||
$nonce = wp_create_nonce('jio_invite_admin');
|
||||
NativeUi::page_open('jio-inv-page');
|
||||
NativeUi::header(
|
||||
__('邀请码管理', 'aksrt'),
|
||||
__('管理注册邀请码与使用奖励,点击邀请码即可复制。', 'aksrt'),
|
||||
__('邀请码管理', 'jio'),
|
||||
__('管理注册邀请码与使用奖励,点击邀请码即可复制。', 'jio'),
|
||||
'dashicons-tickets-alt',
|
||||
[
|
||||
['label' => __('添加邀请码', 'aksrt'), 'dialog' => 'aksrt-inv-add', 'primary' => true, 'icon' => 'dashicons-plus-alt2'],
|
||||
['label' => __('导出邀请码', 'aksrt'), 'dialog' => 'aksrt-inv-export', 'icon' => 'dashicons-download'],
|
||||
['label' => __('添加邀请码', 'jio'), 'dialog' => 'jio-inv-add', 'primary' => true, 'icon' => 'dashicons-plus-alt2'],
|
||||
['label' => __('导出邀请码', 'jio'), 'dialog' => 'jio-inv-export', 'icon' => 'dashicons-download'],
|
||||
]
|
||||
);
|
||||
|
||||
$settings = $service->registration_settings();
|
||||
if (empty($settings['enabled'])) {
|
||||
$settings_url = admin_url('admin.php?page=aksrt-settings#/users/auth');
|
||||
echo '<div class="notice notice-warning"><p>' . esc_html__('邀请码注册尚未启用,已添加的邀请码暂不能用于注册。', 'aksrt')
|
||||
. ' <a href="' . esc_url($settings_url) . '">' . esc_html__('前往登录注册设置', 'aksrt') . '</a></p></div>';
|
||||
$settings_url = admin_url('admin.php?page=jio-settings#/users/auth');
|
||||
echo '<div class="notice notice-warning"><p>' . esc_html__('邀请码注册尚未启用,已添加的邀请码暂不能用于注册。', 'jio')
|
||||
. ' <a href="' . esc_url($settings_url) . '">' . esc_html__('前往登录注册设置', 'jio') . '</a></p></div>';
|
||||
}
|
||||
|
||||
$title = isset($_GET['aksrt_inv_title']) ? sanitize_text_field(rawurldecode((string) wp_unslash($_GET['aksrt_inv_title']))) : '';
|
||||
$message = isset($_GET['aksrt_inv_msg']) ? sanitize_text_field(rawurldecode((string) wp_unslash($_GET['aksrt_inv_msg']))) : '';
|
||||
$type = ($_GET['aksrt_inv_type'] ?? '') === 'error' ? 'error' : 'success';
|
||||
$title = isset($_GET['jio_inv_title']) ? sanitize_text_field(rawurldecode((string) wp_unslash($_GET['jio_inv_title']))) : '';
|
||||
$message = isset($_GET['jio_inv_msg']) ? sanitize_text_field(rawurldecode((string) wp_unslash($_GET['jio_inv_msg']))) : '';
|
||||
$type = ($_GET['jio_inv_type'] ?? '') === 'error' ? 'error' : 'success';
|
||||
if ($title !== '') {
|
||||
echo '<div class="notice notice-' . esc_attr($type) . ' is-dismissible"><p><strong>' . esc_html($title) . '</strong>'
|
||||
. ($message !== '' ? ' ' . esc_html($message) : '') . '</p></div>';
|
||||
@@ -59,14 +59,14 @@ trait UsersMenusInviteCode {
|
||||
$this->render_invite_codes_list($service, $this_url, $nonce);
|
||||
$this->render_invite_codes_add($nonce);
|
||||
$this->render_invite_codes_export($nonce);
|
||||
echo '<div class="aksrt-inv-toast" role="status" aria-live="polite" data-aksrt-inv-toast'
|
||||
. ' data-copied="' . esc_attr__('邀请码已复制', 'aksrt') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请选中邀请码后手动复制。', 'aksrt') . '"'
|
||||
. ' data-exported="' . esc_attr__('下载请求已提交,请查看浏览器下载列表。', 'aksrt') . '"></div>';
|
||||
echo '<div class="jio-inv-toast" role="status" aria-live="polite" data-jio-inv-toast'
|
||||
. ' data-copied="' . esc_attr__('邀请码已复制', 'jio') . '"'
|
||||
. ' data-copy-failed="' . esc_attr__('自动复制失败,请选中邀请码后手动复制。', 'jio') . '"'
|
||||
. ' data-exported="' . esc_attr__('下载请求已提交,请查看浏览器下载列表。', 'jio') . '"></div>';
|
||||
NativeUi::page_close();
|
||||
}
|
||||
|
||||
private function render_invite_codes_list(\AKSRT\Payment\InviteCode $service, string $this_url, string $nonce): void {
|
||||
private function render_invite_codes_list(\Jio\Payment\InviteCode $service, string $this_url, string $nonce): void {
|
||||
$status = in_array(($_GET['status'] ?? 'all'), ['all', 'unused', 'used'], true) ? (string) $_GET['status'] : 'all';
|
||||
$s = isset($_GET['s']) ? sanitize_text_field((string) wp_unslash($_GET['s'])) : '';
|
||||
$remark = isset($_GET['remark']) ? sanitize_text_field((string) wp_unslash($_GET['remark'])) : '';
|
||||
@@ -81,44 +81,44 @@ trait UsersMenusInviteCode {
|
||||
$filters = ['status' => $status, 's' => $s, 'remark' => $remark, 'orderby' => $data['orderby'], 'desc' => $data['desc']];
|
||||
$filter_url = add_query_arg($filters, $this_url);
|
||||
|
||||
NativeUi::card_open('', '', true, 'aksrt-inv-list-card');
|
||||
echo '<div class="aksrt-inv-toolbar"><div class="aksrt-native-toolbar"><div class="aksrt-inv-toolbar__views">';
|
||||
echo '<h2 class="aksrt-native-card__title">' . esc_html__('邀请码列表', 'aksrt') . '</h2>';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('使用状态筛选', 'aksrt') . '">';
|
||||
foreach (['all' => __('全部', 'aksrt'), 'unused' => __('未使用', 'aksrt'), 'used' => __('已使用', 'aksrt')] as $key => $label) {
|
||||
NativeUi::card_open('', '', true, 'jio-inv-list-card');
|
||||
echo '<div class="jio-inv-toolbar"><div class="jio-native-toolbar"><div class="jio-inv-toolbar__views">';
|
||||
echo '<h2 class="jio-native-card__title">' . esc_html__('邀请码列表', 'jio') . '</h2>';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('使用状态筛选', 'jio') . '">';
|
||||
foreach (['all' => __('全部', 'jio'), 'unused' => __('未使用', 'jio'), 'used' => __('已使用', 'jio')] as $key => $label) {
|
||||
$url = add_query_arg('status', $key, $filter_url);
|
||||
echo '<li><a href="' . esc_url($url) . '"' . ($key === $status ? ' class="current" aria-current="page"' : '') . '>' . esc_html($label) . '</a></li>';
|
||||
}
|
||||
echo '</ul></div><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="aksrt_invite_codes">';
|
||||
echo '</ul></div><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="jio_invite_codes">';
|
||||
foreach (array_diff_key($filters, ['s' => '']) as $key => $value) {
|
||||
echo '<input type="hidden" name="' . esc_attr($key) . '" value="' . esc_attr($value) . '">';
|
||||
}
|
||||
echo '<label class="screen-reader-text" for="aksrt-inv-search">' . esc_html__('搜索邀请码或标识', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-inv-search" name="s" value="' . esc_attr($s) . '" placeholder="' . esc_attr__('搜索邀请码或标识', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'aksrt') . '</button>';
|
||||
echo '<label class="screen-reader-text" for="jio-inv-search">' . esc_html__('搜索邀请码或标识', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-inv-search" name="s" value="' . esc_attr($s) . '" placeholder="' . esc_attr__('搜索邀请码或标识', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'jio') . '</button>';
|
||||
if ($remark !== '') {
|
||||
echo '<span class="aksrt-inv-filter-remark">' . esc_html(sprintf(__('标识:%s', 'aksrt'), $remark)) . '</span>';
|
||||
echo '<span class="jio-inv-filter-remark">' . esc_html(sprintf(__('标识:%s', 'jio'), $remark)) . '</span>';
|
||||
}
|
||||
if ($s !== '' || $remark !== '' || $status !== 'all') {
|
||||
echo '<a class="button-link" href="' . esc_url($this_url) . '">' . esc_html__('清除筛选', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this_url) . '">' . esc_html__('清除筛选', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form></div></div>';
|
||||
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" data-aksrt-inv-list'
|
||||
. ' data-confirm="' . esc_attr__('确定删除选中的 %d 个邀请码及其使用记录?此操作不可撤销。', 'aksrt') . '">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_invite_delete"><input type="hidden" name="aksrt_nonce" value="' . esc_attr($nonce) . '">';
|
||||
echo '<div class="aksrt-native-bulk aksrt-inv-bulk"><span class="aksrt-inv-selection" data-aksrt-inv-selection data-label="' . esc_attr__('已选 %d 项', 'aksrt') . '" aria-live="polite"></span>'
|
||||
. '<label class="screen-reader-text" for="bulk-action-selector-top">' . esc_html__('批量操作', 'aksrt') . '</label>'
|
||||
. '<select name="bulk_action" id="bulk-action-selector-top"><option value="-1">' . esc_html__('批量操作', 'aksrt') . '</option><option value="delete">' . esc_html__('删除', 'aksrt') . '</option></select>'
|
||||
. '<button type="submit" class="button" data-aksrt-inv-bulk disabled>' . esc_html__('应用', 'aksrt') . '</button></div>';
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<table class="widefat fixed striped aksrt-inv-table"><thead><tr><td class="manage-column column-cb check-column">';
|
||||
echo '<input type="checkbox" data-aksrt-inv-check-all aria-label="' . esc_attr__('全选本页邀请码', 'aksrt') . '"></td>';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" data-jio-inv-list'
|
||||
. ' data-confirm="' . esc_attr__('确定删除选中的 %d 个邀请码及其使用记录?此操作不可撤销。', 'jio') . '">';
|
||||
echo '<input type="hidden" name="action" value="jio_invite_delete"><input type="hidden" name="jio_nonce" value="' . esc_attr($nonce) . '">';
|
||||
echo '<div class="jio-native-bulk jio-inv-bulk"><span class="jio-inv-selection" data-jio-inv-selection data-label="' . esc_attr__('已选 %d 项', 'jio') . '" aria-live="polite"></span>'
|
||||
. '<label class="screen-reader-text" for="bulk-action-selector-top">' . esc_html__('批量操作', 'jio') . '</label>'
|
||||
. '<select name="bulk_action" id="bulk-action-selector-top"><option value="-1">' . esc_html__('批量操作', 'jio') . '</option><option value="delete">' . esc_html__('删除', 'jio') . '</option></select>'
|
||||
. '<button type="submit" class="button" data-jio-inv-bulk disabled>' . esc_html__('应用', 'jio') . '</button></div>';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
echo '<table class="widefat fixed striped jio-inv-table"><thead><tr><td class="manage-column column-cb check-column">';
|
||||
echo '<input type="checkbox" data-jio-inv-check-all aria-label="' . esc_attr__('全选本页邀请码', 'jio') . '"></td>';
|
||||
$columns = [
|
||||
'code' => __('邀请码', 'aksrt'), 'rewards' => __('使用奖励', 'aksrt'),
|
||||
'created_at' => __('创建时间', 'aksrt'), 'updated_at' => __('更新时间', 'aksrt'),
|
||||
'used_count' => __('使用状态', 'aksrt'), 'remark' => __('标识', 'aksrt'),
|
||||
'code' => __('邀请码', 'jio'), 'rewards' => __('使用奖励', 'jio'),
|
||||
'created_at' => __('创建时间', 'jio'), 'updated_at' => __('更新时间', 'jio'),
|
||||
'used_count' => __('使用状态', 'jio'), 'remark' => __('标识', 'jio'),
|
||||
];
|
||||
foreach ($columns as $key => $label) {
|
||||
$active = $key === $data['orderby'];
|
||||
@@ -130,38 +130,38 @@ trait UsersMenusInviteCode {
|
||||
$next = $active && $data['desc'] === 'ASC' ? 'DESC' : 'ASC';
|
||||
$url = add_query_arg(['orderby' => $key, 'desc' => $next], $filter_url);
|
||||
$icon = $active && $data['desc'] === 'ASC' ? 'dashicons-arrow-up-alt2' : 'dashicons-arrow-down-alt2';
|
||||
echo '<a class="orderby-but' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '" title="' . esc_attr($next === 'ASC' ? __('按此列升序排列', 'aksrt') : __('按此列降序排列', 'aksrt')) . '">'
|
||||
echo '<a class="orderby-but' . ($active ? ' is-active' : '') . '" href="' . esc_url($url) . '" title="' . esc_attr($next === 'ASC' ? __('按此列升序排列', 'jio') : __('按此列降序排列', 'jio')) . '">'
|
||||
. esc_html($label) . '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span></a>';
|
||||
}
|
||||
echo '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($data['items'] === []) {
|
||||
echo NativeUi::empty_row(7, __('暂无符合条件的邀请码', 'aksrt'), __('调整筛选条件,或点击“添加邀请码”生成、导入新的邀请码。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无符合条件的邀请码', 'jio'), __('调整筛选条件,或点击“添加邀请码”生成、导入新的邀请码。', 'jio'));
|
||||
}
|
||||
foreach ($data['items'] as $item) {
|
||||
$used = (int) $item['used_count'];
|
||||
$max_uses = (int) $item['max_uses'];
|
||||
echo '<tr><th scope="row" class="check-column"><input type="checkbox" name="action_id[]" value="' . esc_attr((string) $item['id']) . '" aria-label="' . esc_attr(sprintf(__('选择邀请码 %s', 'aksrt'), $item['code'])) . '"></th>';
|
||||
echo '<td><button type="button" class="aksrt-inv-code aksrt-native-mono" data-clipboard-text="' . esc_attr($item['code']) . '" aria-label="' . esc_attr(sprintf(__('复制邀请码 %s', 'aksrt'), $item['code'])) . '">' . esc_html($item['code']) . '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button></td>';
|
||||
echo '<tr><th scope="row" class="check-column"><input type="checkbox" name="action_id[]" value="' . esc_attr((string) $item['id']) . '" aria-label="' . esc_attr(sprintf(__('选择邀请码 %s', 'jio'), $item['code'])) . '"></th>';
|
||||
echo '<td><button type="button" class="jio-inv-code jio-native-mono" data-clipboard-text="' . esc_attr($item['code']) . '" aria-label="' . esc_attr(sprintf(__('复制邀请码 %s', 'jio'), $item['code'])) . '">' . esc_html($item['code']) . '<span class="dashicons dashicons-admin-page" aria-hidden="true"></span></button></td>';
|
||||
echo '<td>' . wp_kses((string) $item['reward_text'], ['br' => []]) . '</td>';
|
||||
echo '<td class="aksrt-inv-date">' . esc_html($item['created_at']) . '</td><td class="aksrt-inv-date">' . esc_html($item['updated_at']) . '</td><td>';
|
||||
$label = $used > 0 && $max_uses > 0 && $used < $max_uses ? sprintf(__('已使用 %1$d/%2$d', 'aksrt'), $used, $max_uses) : ($used > 0 ? __('已使用', 'aksrt') : __('未使用', 'aksrt'));
|
||||
echo '<td class="jio-inv-date">' . esc_html($item['created_at']) . '</td><td class="jio-inv-date">' . esc_html($item['updated_at']) . '</td><td>';
|
||||
$label = $used > 0 && $max_uses > 0 && $used < $max_uses ? sprintf(__('已使用 %1$d/%2$d', 'jio'), $used, $max_uses) : ($used > 0 ? __('已使用', 'jio') : __('未使用', 'jio'));
|
||||
echo NativeUi::badge($label, $used > 0 ? 'neutral' : 'success');
|
||||
foreach ((array) $item['recent_users'] as $user) {
|
||||
echo '<div class="aksrt-inv-user"><a href="' . esc_url(get_edit_user_link((int) $user['user_id'])) . '">' . esc_html($user['name']) . '</a></div>';
|
||||
echo '<div class="jio-inv-user"><a href="' . esc_url(get_edit_user_link((int) $user['user_id'])) . '">' . esc_html($user['name']) . '</a></div>';
|
||||
}
|
||||
echo '</td><td>';
|
||||
if ($item['remark'] !== '') {
|
||||
echo '<a href="' . esc_url(add_query_arg('remark', $item['remark'], $this_url)) . '">' . esc_html($item['remark']) . '</a>';
|
||||
} else {
|
||||
echo '<span class="aksrt-native-muted">—</span>';
|
||||
echo '<span class="jio-native-muted">—</span>';
|
||||
}
|
||||
echo '</td></tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
NativeUi::table_wrap_close();
|
||||
echo '<div class="tablenav bottom"><div class="tablenav-pages"><span class="displaying-num">' . esc_html(sprintf(__('共 %s 项', 'aksrt'), number_format_i18n((int) $data['total']))) . '</span>';
|
||||
echo '<div class="tablenav bottom"><div class="tablenav-pages"><span class="displaying-num">' . esc_html(sprintf(__('共 %s 项', 'jio'), number_format_i18n((int) $data['total']))) . '</span>';
|
||||
if ((int) $data['max_pages'] > 1) {
|
||||
echo paginate_links([
|
||||
'base' => add_query_arg('paged', '%#%', $filter_url), 'format' => '',
|
||||
|
||||
@@ -2,91 +2,91 @@
|
||||
/**
|
||||
* Invite-code generation, import and export dialogs.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusInviteForms {
|
||||
private function render_invite_codes_add(string $nonce): void {
|
||||
$this->invite_dialog_open('aksrt-inv-add', __('添加邀请码', 'aksrt'), __('批量生成新邀请码,或导入已有的邀请码数据。', 'aksrt'), 'post', 'aksrt_invite_add', $nonce);
|
||||
echo '<fieldset class="aksrt-inv-modes"><legend class="screen-reader-text">' . esc_html__('添加方式', 'aksrt') . '</legend>';
|
||||
foreach (['auto' => __('系统自动生成', 'aksrt'), 'import' => __('导入邀请码', 'aksrt')] as $value => $label) {
|
||||
$this->invite_dialog_open('jio-inv-add', __('添加邀请码', 'jio'), __('批量生成新邀请码,或导入已有的邀请码数据。', 'jio'), 'post', 'jio_invite_add', $nonce);
|
||||
echo '<fieldset class="jio-inv-modes"><legend class="screen-reader-text">' . esc_html__('添加方式', 'jio') . '</legend>';
|
||||
foreach (['auto' => __('系统自动生成', 'jio'), 'import' => __('导入邀请码', 'jio')] as $value => $label) {
|
||||
echo '<label><input type="radio" name="add_type" value="' . esc_attr($value) . '"' . checked($value, 'auto', false) . '><span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset><fieldset class="aksrt-inv-pane" data-aksrt-inv-dep="add_type=auto"><legend class="screen-reader-text">' . esc_html__('自动生成设置', 'aksrt') . '</legend>';
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('生成数量', 'aksrt') . '</span>'
|
||||
echo '</fieldset><fieldset class="jio-inv-pane" data-jio-inv-dep="add_type=auto"><legend class="screen-reader-text">' . esc_html__('自动生成设置', 'jio') . '</legend>';
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('生成数量', 'jio') . '</span>'
|
||||
. '<input type="number" name="auto_num" value="20" min="1" max="1000" step="1" required autofocus>'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('单次可生成 1–1000 个。', 'aksrt') . '</small></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('标识', 'aksrt') . '</span>'
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('单次可生成 1–1000 个。', 'jio') . '</small></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('标识', 'jio') . '</span>'
|
||||
. '<input type="text" name="auto_remarks" value="' . esc_attr('invita_' . current_time('YmdHis')) . '" maxlength="191">'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('为本批邀请码命名,方便查找与管理。', 'aksrt') . '</small></label></div>';
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('为本批邀请码命名,方便查找与管理。', 'jio') . '</small></label></div>';
|
||||
$this->render_invite_reward_fields();
|
||||
echo '<label class="aksrt-inv-advanced"><input type="checkbox" name="auto_top_s" value="1">' . esc_html__('自定义邀请码位数', 'aksrt') . '</label>';
|
||||
echo '<fieldset class="aksrt-inv-pane aksrt-inv-length" data-aksrt-inv-dep="auto_top_s=1" hidden disabled><legend class="screen-reader-text">' . esc_html__('自定义位数', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('邀请码位数', 'aksrt') . '</span>'
|
||||
echo '<label class="jio-inv-advanced"><input type="checkbox" name="auto_top_s" value="1">' . esc_html__('自定义邀请码位数', 'jio') . '</label>';
|
||||
echo '<fieldset class="jio-inv-pane jio-inv-length" data-jio-inv-dep="auto_top_s=1" hidden disabled><legend class="screen-reader-text">' . esc_html__('自定义位数', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('邀请码位数', 'jio') . '</span>'
|
||||
. '<input type="number" name="auto_rand_password_limit" value="10" min="4" max="50" step="1" required>'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('4–50 位;默认 10 位。位数过短容易重复。', 'aksrt') . '</small></label></fieldset></fieldset>';
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('4–50 位;默认 10 位。位数过短容易重复。', 'jio') . '</small></label></fieldset></fieldset>';
|
||||
|
||||
echo '<fieldset class="aksrt-inv-pane" data-aksrt-inv-dep="add_type=import" hidden disabled><legend class="screen-reader-text">' . esc_html__('导入设置', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('邀请码数据', 'aksrt') . '</span>'
|
||||
. '<textarea name="import_data" rows="7" class="code" required data-limit-error="' . esc_attr__('单次最多导入 1000 行,请拆分后重试。', 'aksrt') . '" placeholder="93FW0XAY 无 signup P86NPWKI points=10 signup" aria-describedby="aksrt-inv-import-hint"></textarea>'
|
||||
. '<small class="aksrt-native-field__hint" id="aksrt-inv-import-hint">' . esc_html__('每行一个,格式为“邀请码 奖励 标识”,无奖励填“无”。单次最多 1000 条,空行会忽略,重复的邀请码不会导入。', 'aksrt') . '</small></label>';
|
||||
echo '<label class="aksrt-native-field aksrt-inv-separator"><span class="aksrt-native-field__label">' . esc_html__('分隔符', 'aksrt') . '</span>'
|
||||
echo '<fieldset class="jio-inv-pane" data-jio-inv-dep="add_type=import" hidden disabled><legend class="screen-reader-text">' . esc_html__('导入设置', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('邀请码数据', 'jio') . '</span>'
|
||||
. '<textarea name="import_data" rows="7" class="code" required data-limit-error="' . esc_attr__('单次最多导入 1000 行,请拆分后重试。', 'jio') . '" placeholder="93FW0XAY 无 signup P86NPWKI points=10 signup" aria-describedby="jio-inv-import-hint"></textarea>'
|
||||
. '<small class="jio-native-field__hint" id="jio-inv-import-hint">' . esc_html__('每行一个,格式为“邀请码 奖励 标识”,无奖励填“无”。单次最多 1000 条,空行会忽略,重复的邀请码不会导入。', 'jio') . '</small></label>';
|
||||
echo '<label class="jio-native-field jio-inv-separator"><span class="jio-native-field__label">' . esc_html__('分隔符', 'jio') . '</span>'
|
||||
. '<input type="text" name="import_division" value=" " maxlength="4">'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('默认为空格,也可填写其他分隔符,例如 |。', 'aksrt') . '</small></label>';
|
||||
echo '<details class="aksrt-inv-help"><summary>' . esc_html__('查看奖励格式与示例', 'aksrt') . '</summary><div>'
|
||||
. '<p>' . esc_html__('奖励参数用 & 连接:level_integral 为经验值,points 为积分,balance 为余额(元),vip 为会员等级(1 或 2),vip_time 为会员天数。', 'aksrt') . '</p>'
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('默认为空格,也可填写其他分隔符,例如 |。', 'jio') . '</small></label>';
|
||||
echo '<details class="jio-inv-help"><summary>' . esc_html__('查看奖励格式与示例', 'jio') . '</summary><div>'
|
||||
. '<p>' . esc_html__('奖励参数用 & 连接:level_integral 为经验值,points 为积分,balance 为余额(元),vip 为会员等级(1 或 2),vip_time 为会员天数。', 'jio') . '</p>'
|
||||
. '<pre>93FW0XAY 无 signup P86NPWKI points=10 signup AUF9D2B4 balance=30&vip=2&vip_time=5 signup</pre>'
|
||||
. '<p>' . esc_html__('第三行示例:奖励 30 元余额和 5 天二级会员。标识可省略;导出的文本文件也可按相同分隔符重新导入。', 'aksrt') . '</p></div></details></fieldset>';
|
||||
$this->invite_dialog_close(__('生成邀请码', 'aksrt'), ' data-aksrt-inv-add-submit data-auto-label="' . esc_attr__('生成邀请码', 'aksrt') . '" data-import-label="' . esc_attr__('导入邀请码', 'aksrt') . '" data-busy-label="' . esc_attr__('正在添加…', 'aksrt') . '"');
|
||||
. '<p>' . esc_html__('第三行示例:奖励 30 元余额和 5 天二级会员。标识可省略;导出的文本文件也可按相同分隔符重新导入。', 'jio') . '</p></div></details></fieldset>';
|
||||
$this->invite_dialog_close(__('生成邀请码', 'jio'), ' data-jio-inv-add-submit data-auto-label="' . esc_attr__('生成邀请码', 'jio') . '" data-import-label="' . esc_attr__('导入邀请码', 'jio') . '" data-busy-label="' . esc_attr__('正在添加…', 'jio') . '"');
|
||||
}
|
||||
|
||||
private function render_invite_reward_fields(): void {
|
||||
echo '<fieldset class="aksrt-inv-rewards"><legend>' . esc_html__('使用奖励', 'aksrt') . '</legend><p class="aksrt-inv-hint">' . esc_html__('用户使用邀请码注册后获得以下奖励;数值为 0 表示不赠送。', 'aksrt') . '</p>';
|
||||
echo '<div class="aksrt-native-form-grid aksrt-inv-reward-grid">';
|
||||
foreach (['level_integral' => __('经验值', 'aksrt'), 'points' => __('积分', 'aksrt'), 'balance' => __('余额(元)', 'aksrt')] as $name => $label) {
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html($label) . '</span><input type="number" name="' . esc_attr($name) . '" value="0" min="0" step="' . ($name === 'balance' ? '0.01' : '1') . '"></label>';
|
||||
echo '<fieldset class="jio-inv-rewards"><legend>' . esc_html__('使用奖励', 'jio') . '</legend><p class="jio-inv-hint">' . esc_html__('用户使用邀请码注册后获得以下奖励;数值为 0 表示不赠送。', 'jio') . '</p>';
|
||||
echo '<div class="jio-native-form-grid jio-inv-reward-grid">';
|
||||
foreach (['level_integral' => __('经验值', 'jio'), 'points' => __('积分', 'jio'), 'balance' => __('余额(元)', 'jio')] as $name => $label) {
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html($label) . '</span><input type="number" name="' . esc_attr($name) . '" value="0" min="0" step="' . ($name === 'balance' ? '0.01' : '1') . '"></label>';
|
||||
}
|
||||
echo '</div><fieldset class="aksrt-inv-vip"><legend>' . esc_html__('VIP 会员', 'aksrt') . '</legend><div class="aksrt-inv-radios">';
|
||||
foreach (['0' => __('不赠送', 'aksrt'), '1' => __('一级会员', 'aksrt'), '2' => __('二级会员', 'aksrt')] as $value => $label) {
|
||||
echo '</div><fieldset class="jio-inv-vip"><legend>' . esc_html__('VIP 会员', 'jio') . '</legend><div class="jio-inv-radios">';
|
||||
foreach (['0' => __('不赠送', 'jio'), '1' => __('一级会员', 'jio'), '2' => __('二级会员', 'jio')] as $value => $label) {
|
||||
echo '<label><input type="radio" name="vip" value="' . esc_attr($value) . '"' . checked((string) $value, '0', false) . '>' . esc_html($label) . '</label>';
|
||||
}
|
||||
echo '</div></fieldset><fieldset class="aksrt-inv-pane aksrt-inv-vip-time" data-aksrt-inv-dep="vip=1|2" hidden disabled><legend class="screen-reader-text">' . esc_html__('会员时长', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('会员赠送时长(天)', 'aksrt') . '</span><input type="number" name="vip_time" value="30" min="1" step="1" required></label></fieldset></fieldset>';
|
||||
echo '</div></fieldset><fieldset class="jio-inv-pane jio-inv-vip-time" data-jio-inv-dep="vip=1|2" hidden disabled><legend class="screen-reader-text">' . esc_html__('会员时长', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('会员赠送时长(天)', 'jio') . '</span><input type="number" name="vip_time" value="30" min="1" step="1" required></label></fieldset></fieldset>';
|
||||
}
|
||||
|
||||
private function render_invite_codes_export(string $nonce): void {
|
||||
$status = in_array(($_GET['status'] ?? 'all'), ['all', 'unused', 'used'], true) ? (string) $_GET['status'] : 'all';
|
||||
$this->invite_dialog_open('aksrt-inv-export', __('导出邀请码', 'aksrt'), __('选择使用状态与文件格式,导出后可在本地保存或分发。', 'aksrt'), 'get', 'aksrt_invite_export', $nonce);
|
||||
echo '<fieldset class="aksrt-inv-options"><legend>' . esc_html__('使用状态', 'aksrt') . '</legend><div class="aksrt-inv-radios">';
|
||||
foreach (['all' => __('全部', 'aksrt'), 'unused' => __('未使用', 'aksrt'), 'used' => __('已使用', 'aksrt')] as $value => $label) {
|
||||
$this->invite_dialog_open('jio-inv-export', __('导出邀请码', 'jio'), __('选择使用状态与文件格式,导出后可在本地保存或分发。', 'jio'), 'get', 'jio_invite_export', $nonce);
|
||||
echo '<fieldset class="jio-inv-options"><legend>' . esc_html__('使用状态', 'jio') . '</legend><div class="jio-inv-radios">';
|
||||
foreach (['all' => __('全部', 'jio'), 'unused' => __('未使用', 'jio'), 'used' => __('已使用', 'jio')] as $value => $label) {
|
||||
echo '<label><input type="radio" name="status" value="' . esc_attr($value) . '"' . checked($value, $status, false) . '>' . esc_html($label) . '</label>';
|
||||
}
|
||||
echo '</div></fieldset><fieldset class="aksrt-inv-options"><legend>' . esc_html__('导出格式', 'aksrt') . '</legend><div class="aksrt-inv-formats">';
|
||||
foreach (['csv' => [__('Excel 表格(.csv)', 'aksrt'), __('包含奖励、时间、使用状态与标识,适合整理统计。', 'aksrt')], 'text' => [__('文本文档(.txt)', 'aksrt'), __('每行一个邀请码,包含奖励与标识,可再次导入。', 'aksrt')]] as $value => [$label, $hint]) {
|
||||
echo '</div></fieldset><fieldset class="jio-inv-options"><legend>' . esc_html__('导出格式', 'jio') . '</legend><div class="jio-inv-formats">';
|
||||
foreach (['csv' => [__('Excel 表格(.csv)', 'jio'), __('包含奖励、时间、使用状态与标识,适合整理统计。', 'jio')], 'text' => [__('文本文档(.txt)', 'jio'), __('每行一个邀请码,包含奖励与标识,可再次导入。', 'jio')]] as $value => [$label, $hint]) {
|
||||
echo '<label><input type="radio" name="export_format" value="' . esc_attr($value) . '"' . checked($value, 'csv', false) . '><span><strong>' . esc_html($label) . '</strong><small>' . esc_html($hint) . '</small></span></label>';
|
||||
}
|
||||
echo '</div></fieldset><fieldset class="aksrt-inv-pane" data-aksrt-inv-dep="export_format=text" hidden disabled><legend class="screen-reader-text">' . esc_html__('文本设置', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field aksrt-inv-separator"><span class="aksrt-native-field__label">' . esc_html__('分隔符', 'aksrt') . '</span><input type="text" name="text_division" value=" " maxlength="4">'
|
||||
. '<small class="aksrt-native-field__hint">' . esc_html__('默认为空格;重新导入时请使用相同分隔符。', 'aksrt') . '</small></label></fieldset>';
|
||||
echo '<p class="aksrt-native-callout aksrt-inv-export-note">' . esc_html__('每次最多导出最新的 10,000 条记录。导出仅按上方使用状态筛选,不受列表关键词、标识或勾选项影响。', 'aksrt') . '</p>';
|
||||
$this->invite_dialog_close(__('导出并下载', 'aksrt'));
|
||||
echo '</div></fieldset><fieldset class="jio-inv-pane" data-jio-inv-dep="export_format=text" hidden disabled><legend class="screen-reader-text">' . esc_html__('文本设置', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field jio-inv-separator"><span class="jio-native-field__label">' . esc_html__('分隔符', 'jio') . '</span><input type="text" name="text_division" value=" " maxlength="4">'
|
||||
. '<small class="jio-native-field__hint">' . esc_html__('默认为空格;重新导入时请使用相同分隔符。', 'jio') . '</small></label></fieldset>';
|
||||
echo '<p class="jio-native-callout jio-inv-export-note">' . esc_html__('每次最多导出最新的 10,000 条记录。导出仅按上方使用状态筛选,不受列表关键词、标识或勾选项影响。', 'jio') . '</p>';
|
||||
$this->invite_dialog_close(__('导出并下载', 'jio'));
|
||||
}
|
||||
|
||||
private function invite_dialog_open(string $id, string $title, string $description, string $method, string $action, string $nonce): void {
|
||||
echo '<dialog class="aksrt-inv-dialog" id="' . esc_attr($id) . '" aria-labelledby="' . esc_attr($id) . '-title" aria-describedby="' . esc_attr($id) . '-description">';
|
||||
echo '<form method="' . esc_attr($method) . '" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-inv-dialog__frame">';
|
||||
echo '<input type="hidden" name="action" value="' . esc_attr($action) . '"><input type="hidden" name="aksrt_nonce" value="' . esc_attr($nonce) . '">';
|
||||
echo '<header class="aksrt-inv-dialog__head"><div><h2 id="' . esc_attr($id) . '-title">' . esc_html($title) . '</h2><p id="' . esc_attr($id) . '-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="aksrt-inv-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header><div class="aksrt-inv-dialog__body">';
|
||||
echo '<dialog class="jio-inv-dialog" id="' . esc_attr($id) . '" aria-labelledby="' . esc_attr($id) . '-title" aria-describedby="' . esc_attr($id) . '-description">';
|
||||
echo '<form method="' . esc_attr($method) . '" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-inv-dialog__frame">';
|
||||
echo '<input type="hidden" name="action" value="' . esc_attr($action) . '"><input type="hidden" name="jio_nonce" value="' . esc_attr($nonce) . '">';
|
||||
echo '<header class="jio-inv-dialog__head"><div><h2 id="' . esc_attr($id) . '-title">' . esc_html($title) . '</h2><p id="' . esc_attr($id) . '-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="jio-inv-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header><div class="jio-inv-dialog__body">';
|
||||
}
|
||||
|
||||
private function invite_dialog_close(string $label, string $attributes = ''): void {
|
||||
echo '</div><footer class="aksrt-inv-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '</div><footer class="jio-inv-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary"' . $attributes . '>' . esc_html($label) . '</button></footer></form></dialog>';
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
* 消息管理后台页:页面编排、带权限 / nonce 的写操作、筛选与反馈。
|
||||
* 展示由同目录的 MessagesList / MessagesCompose traits 承载;查询与投递下沉消息服务。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Messages\AdminBroadcast;
|
||||
use AKSRT\Messages\NotificationAdminQuery;
|
||||
use Jio\Messages\AdminBroadcast;
|
||||
use Jio\Messages\NotificationAdminQuery;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,8 +18,8 @@ trait UsersMenusMessages {
|
||||
use UsersMenusMessagesList;
|
||||
use UsersMenusMessagesCompose;
|
||||
|
||||
private const MESSAGE_PAGE_SLUG = 'aksrt_messages';
|
||||
private const MESSAGE_COMPOSE_ID = 'aksrt-msg-compose';
|
||||
private const MESSAGE_PAGE_SLUG = 'jio_messages';
|
||||
private const MESSAGE_COMPOSE_ID = 'jio-msg-compose';
|
||||
|
||||
public function enqueue_message_assets(string $hook): void {
|
||||
if ($hook !== 'users_page_' . self::MESSAGE_PAGE_SLUG) {
|
||||
@@ -27,11 +27,11 @@ trait UsersMenusMessages {
|
||||
}
|
||||
$css = '/assets/css/admin/messages.css';
|
||||
$js = '/assets/js/admin-messages.js';
|
||||
if (is_file(AKSRT_DIR . $css)) {
|
||||
wp_enqueue_style('aksrt-admin-messages', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
if (is_file(JIO_DIR . $css)) {
|
||||
wp_enqueue_style('jio-admin-messages', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
}
|
||||
if (is_file(AKSRT_DIR . $js)) {
|
||||
wp_enqueue_script('aksrt-admin-messages', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
if (is_file(JIO_DIR . $js)) {
|
||||
wp_enqueue_script('jio-admin-messages', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,21 +40,21 @@ trait UsersMenusMessages {
|
||||
$filters = $this->message_filters();
|
||||
$paged = max(1, absint($_GET['paged'] ?? 1));
|
||||
|
||||
NativeUi::page_open('aksrt-messages-admin');
|
||||
NativeUi::page_open('jio-messages-admin');
|
||||
NativeUi::header(
|
||||
__('消息管理', 'aksrt'),
|
||||
__('消息管理', 'jio'),
|
||||
'',
|
||||
'dashicons-email-alt',
|
||||
[
|
||||
[
|
||||
'label' => __('发送系统消息', 'aksrt'),
|
||||
'label' => __('发送系统消息', 'jio'),
|
||||
'dialog' => self::MESSAGE_COMPOSE_ID,
|
||||
'icon' => 'dashicons-megaphone',
|
||||
'primary' => true,
|
||||
],
|
||||
[
|
||||
'label' => __('通知配置', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/users/notifications'),
|
||||
'label' => __('通知配置', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/users/notifications'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
],
|
||||
]
|
||||
@@ -62,7 +62,7 @@ trait UsersMenusMessages {
|
||||
$this->render_message_notices();
|
||||
|
||||
if (NotificationAdminQuery::table() === '') {
|
||||
echo '<div class="notice notice-warning"><p>' . esc_html__('通知表不可用。', 'aksrt') . '</p></div>';
|
||||
echo '<div class="notice notice-warning"><p>' . esc_html__('通知表不可用。', 'jio') . '</p></div>';
|
||||
NativeUi::page_close();
|
||||
return;
|
||||
}
|
||||
@@ -71,8 +71,8 @@ trait UsersMenusMessages {
|
||||
$result = NotificationAdminQuery::paginate($query, $paged);
|
||||
$this->render_message_overview();
|
||||
|
||||
NativeUi::card_open(__('站内通知', 'aksrt'), '', true, 'aksrt-messages-admin__card');
|
||||
echo '<div class="aksrt-messages-admin__toolbar">';
|
||||
NativeUi::card_open(__('站内通知', 'jio'), '', true, 'jio-messages-admin__card');
|
||||
echo '<div class="jio-messages-admin__toolbar">';
|
||||
echo $this->message_views_html($filters, NotificationAdminQuery::status_counts($query)); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$this->render_message_filter_form($filters);
|
||||
echo '</div>';
|
||||
@@ -94,7 +94,7 @@ trait UsersMenusMessages {
|
||||
|
||||
public function handle_send_message(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_users_send_message');
|
||||
check_admin_referer('jio_users_send_message');
|
||||
|
||||
$mode = sanitize_key((string) wp_unslash($_POST['recipient_mode'] ?? 'users'));
|
||||
$title = trim(sanitize_text_field((string) wp_unslash($_POST['title'] ?? '')));
|
||||
@@ -102,16 +102,16 @@ trait UsersMenusMessages {
|
||||
$target_url = esc_url_raw(trim((string) wp_unslash($_POST['target_url'] ?? '')));
|
||||
|
||||
if (!in_array($mode, ['users', 'role'], true)) {
|
||||
$this->message_fail(__('请选择有效的收件人模式。', 'aksrt'));
|
||||
$this->message_fail(__('请选择有效的收件人模式。', 'jio'));
|
||||
}
|
||||
if ($content === '') {
|
||||
$this->message_fail(__('请填写消息内容。', 'aksrt'));
|
||||
$this->message_fail(__('请填写消息内容。', 'jio'));
|
||||
}
|
||||
if (mb_strlen($content) > AdminBroadcast::CONTENT_MAX) {
|
||||
$this->message_fail(sprintf(__('消息内容不能超过 %d 字。', 'aksrt'), AdminBroadcast::CONTENT_MAX));
|
||||
$this->message_fail(sprintf(__('消息内容不能超过 %d 字。', 'jio'), AdminBroadcast::CONTENT_MAX));
|
||||
}
|
||||
if (mb_strlen($title) > AdminBroadcast::TITLE_MAX) {
|
||||
$this->message_fail(sprintf(__('消息标题不能超过 %d 字。', 'aksrt'), AdminBroadcast::TITLE_MAX));
|
||||
$this->message_fail(sprintf(__('消息标题不能超过 %d 字。', 'jio'), AdminBroadcast::TITLE_MAX));
|
||||
}
|
||||
$title = mb_substr($title !== '' ? $title : $content, 0, AdminBroadcast::TITLE_MAX);
|
||||
|
||||
@@ -128,28 +128,28 @@ trait UsersMenusMessages {
|
||||
$this->message_fail($ids->get_error_message());
|
||||
}
|
||||
if ($ids === []) {
|
||||
$this->message_fail(__('没有匹配到任何有效收件人。', 'aksrt'));
|
||||
$this->message_fail(__('没有匹配到任何有效收件人。', 'jio'));
|
||||
}
|
||||
// 确认绑定角色与人数;页面加载后人数变化时拒绝投递,避免按过期预览群发。
|
||||
if ($mode === 'role' && (
|
||||
sanitize_key((string) wp_unslash($_POST['confirmed_role'] ?? '')) !== $role
|
||||
|| absint($_POST['confirmed_count'] ?? 0) !== count($ids)
|
||||
)) {
|
||||
$this->message_fail(__('收件人范围尚未确认或人数已变化,请重新确认后发送。', 'aksrt'));
|
||||
$this->message_fail(__('收件人范围尚未确认或人数已变化,请重新确认后发送。', 'jio'));
|
||||
}
|
||||
|
||||
$sent = AdminBroadcast::send($ids, $title, $content, $target_url);
|
||||
$this->message_redirect([
|
||||
'aksrt_msg' => $sent['sent'] > 0 ? 'sent' : 'send_failed',
|
||||
'aksrt_msg_n' => $sent['sent'],
|
||||
'aksrt_msg_f' => $sent['failed'],
|
||||
'aksrt_msg_u' => $unknown,
|
||||
'jio_msg' => $sent['sent'] > 0 ? 'sent' : 'send_failed',
|
||||
'jio_msg_n' => $sent['sent'],
|
||||
'jio_msg_f' => $sent['failed'],
|
||||
'jio_msg_u' => $unknown,
|
||||
]);
|
||||
}
|
||||
|
||||
public function handle_bulk_messages(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_users_bulk_messages');
|
||||
check_admin_referer('jio_users_bulk_messages');
|
||||
|
||||
$op = sanitize_key((string) wp_unslash($_POST['bulk_action'] ?? ''));
|
||||
$single_id = absint($_POST['message_id'] ?? 0);
|
||||
@@ -161,46 +161,46 @@ trait UsersMenusMessages {
|
||||
}
|
||||
|
||||
if (!in_array($op, ['delete', 'mark_read'], true) || array_filter($ids) === []) {
|
||||
$this->message_redirect(array_merge($back, ['aksrt_msg' => 'bulk_none']));
|
||||
$this->message_redirect(array_merge($back, ['jio_msg' => 'bulk_none']));
|
||||
}
|
||||
$count = $op === 'delete' ? NotificationAdminQuery::delete($ids) : NotificationAdminQuery::mark_read($ids);
|
||||
$this->message_redirect(array_merge($back, [
|
||||
'aksrt_msg' => $op === 'delete' ? 'deleted' : 'marked',
|
||||
'aksrt_msg_n' => $count,
|
||||
'jio_msg' => $op === 'delete' ? 'deleted' : 'marked',
|
||||
'jio_msg_n' => $count,
|
||||
]));
|
||||
}
|
||||
|
||||
private function render_message_notices(): void {
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_msg'] ?? ''));
|
||||
$n = absint($_GET['aksrt_msg_n'] ?? 0);
|
||||
$code = sanitize_key((string) wp_unslash($_GET['jio_msg'] ?? ''));
|
||||
$n = absint($_GET['jio_msg_n'] ?? 0);
|
||||
$error = false;
|
||||
switch ($code) {
|
||||
case 'sent':
|
||||
$text = sprintf(__('已发送 %d 条消息。', 'aksrt'), $n);
|
||||
$failed = absint($_GET['aksrt_msg_f'] ?? 0);
|
||||
$unknown = absint($_GET['aksrt_msg_u'] ?? 0);
|
||||
$text = sprintf(__('已发送 %d 条消息。', 'jio'), $n);
|
||||
$failed = absint($_GET['jio_msg_f'] ?? 0);
|
||||
$unknown = absint($_GET['jio_msg_u'] ?? 0);
|
||||
if ($failed > 0) {
|
||||
$text .= ' ' . sprintf(__('投递失败 %d 条。', 'aksrt'), $failed);
|
||||
$text .= ' ' . sprintf(__('投递失败 %d 条。', 'jio'), $failed);
|
||||
}
|
||||
if ($unknown > 0) {
|
||||
$text .= ' ' . sprintf(__('%d 个收件人未找到,已跳过。', 'aksrt'), $unknown);
|
||||
$text .= ' ' . sprintf(__('%d 个收件人未找到,已跳过。', 'jio'), $unknown);
|
||||
}
|
||||
break;
|
||||
case 'send_failed':
|
||||
$error = true;
|
||||
$text = isset($_GET['aksrt_msg_error'])
|
||||
? sanitize_text_field((string) wp_unslash($_GET['aksrt_msg_error']))
|
||||
: __('消息发送失败。', 'aksrt');
|
||||
$text = isset($_GET['jio_msg_error'])
|
||||
? sanitize_text_field((string) wp_unslash($_GET['jio_msg_error']))
|
||||
: __('消息发送失败。', 'jio');
|
||||
break;
|
||||
case 'deleted':
|
||||
$text = sprintf(__('已删除 %d 条消息。', 'aksrt'), $n);
|
||||
$text = sprintf(__('已删除 %d 条消息。', 'jio'), $n);
|
||||
break;
|
||||
case 'marked':
|
||||
$text = sprintf(__('已将 %d 条消息标记为已读。', 'aksrt'), $n);
|
||||
$text = sprintf(__('已将 %d 条消息标记为已读。', 'jio'), $n);
|
||||
break;
|
||||
case 'bulk_none':
|
||||
$error = true;
|
||||
$text = __('请先勾选消息并选择操作。', 'aksrt');
|
||||
$text = __('请先勾选消息并选择操作。', 'jio');
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -258,7 +258,7 @@ trait UsersMenusMessages {
|
||||
}
|
||||
|
||||
private function message_fail(string $error): void {
|
||||
$this->message_redirect(['aksrt_msg' => 'send_failed', 'aksrt_msg_error' => $error]);
|
||||
$this->message_redirect(['jio_msg' => 'send_failed', 'jio_msg_error' => $error]);
|
||||
}
|
||||
|
||||
private function message_redirect(array $args): void {
|
||||
|
||||
@@ -2,108 +2,108 @@
|
||||
/**
|
||||
* 消息管理弹窗:发送表单、只读详情、删除与角色群发共用的二次确认。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Messages\AdminBroadcast;
|
||||
use Jio\Messages\AdminBroadcast;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusMessagesCompose {
|
||||
|
||||
private function render_message_compose(): void {
|
||||
$open = sanitize_key((string) wp_unslash($_GET['aksrt_msg'] ?? '')) === 'send_failed';
|
||||
$open = sanitize_key((string) wp_unslash($_GET['jio_msg'] ?? '')) === 'send_failed';
|
||||
printf(
|
||||
'<dialog class="aksrt-msg-dialog" id="%s" aria-labelledby="%s-title"%s>',
|
||||
esc_attr(self::MESSAGE_COMPOSE_ID), esc_attr(self::MESSAGE_COMPOSE_ID), $open ? ' data-aksrt-dialog-autoopen' : ''
|
||||
'<dialog class="jio-msg-dialog" id="%s" aria-labelledby="%s-title"%s>',
|
||||
esc_attr(self::MESSAGE_COMPOSE_ID), esc_attr(self::MESSAGE_COMPOSE_ID), $open ? ' data-jio-dialog-autoopen' : ''
|
||||
);
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-msg-dialog__frame aksrt-messages-admin__compose" data-aksrt-msg-compose';
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-msg-dialog__frame jio-messages-admin__compose" data-jio-msg-compose';
|
||||
$strings = [
|
||||
'count-error' => __('最多输入 {count} 字。', 'aksrt'),
|
||||
'role-hint' => __('将发送给 {count} 位用户;提交前需再次确认。', 'aksrt'),
|
||||
'role-title' => __('确认按角色群发', 'aksrt'),
|
||||
'role-confirm' => __('将向「{role}」的 {count} 位用户发送系统消息,并按通知配置触发提醒。请确认收件人范围与消息内容。', 'aksrt'),
|
||||
'role-send' => __('确认发送', 'aksrt'),
|
||||
'count-error' => __('最多输入 {count} 字。', 'jio'),
|
||||
'role-hint' => __('将发送给 {count} 位用户;提交前需再次确认。', 'jio'),
|
||||
'role-title' => __('确认按角色群发', 'jio'),
|
||||
'role-confirm' => __('将向「{role}」的 {count} 位用户发送系统消息,并按通知配置触发提醒。请确认收件人范围与消息内容。', 'jio'),
|
||||
'role-send' => __('确认发送', 'jio'),
|
||||
];
|
||||
foreach ($strings as $key => $text) {
|
||||
echo ' data-i18n-' . esc_attr($key) . '="' . esc_attr($text) . '"';
|
||||
}
|
||||
echo '>';
|
||||
echo '<input type="hidden" name="action" value="aksrt_users_send_message" />';
|
||||
echo '<input type="hidden" name="action" value="jio_users_send_message" />';
|
||||
echo '<input type="hidden" name="confirmed_role" value="" /><input type="hidden" name="confirmed_count" value="" />';
|
||||
wp_nonce_field('aksrt_users_send_message');
|
||||
$this->render_message_dialog_head(self::MESSAGE_COMPOSE_ID, __('发送系统消息', 'aksrt'), 'dashicons-megaphone');
|
||||
echo '<div class="aksrt-msg-dialog__body">';
|
||||
wp_nonce_field('jio_users_send_message');
|
||||
$this->render_message_dialog_head(self::MESSAGE_COMPOSE_ID, __('发送系统消息', 'jio'), 'dashicons-megaphone');
|
||||
echo '<div class="jio-msg-dialog__body">';
|
||||
if ($open) {
|
||||
$error = sanitize_text_field((string) wp_unslash($_GET['aksrt_msg_error'] ?? __('消息发送失败,请检查后重试。', 'aksrt')));
|
||||
echo '<p class="aksrt-native-callout is-danger">' . esc_html($error) . '</p>';
|
||||
$error = sanitize_text_field((string) wp_unslash($_GET['jio_msg_error'] ?? __('消息发送失败,请检查后重试。', 'jio')));
|
||||
echo '<p class="jio-native-callout is-danger">' . esc_html($error) . '</p>';
|
||||
}
|
||||
echo '<fieldset class="aksrt-msg-modes"><legend class="aksrt-native-field__label">' . esc_html__('收件人', 'aksrt') . '</legend>';
|
||||
foreach (['users' => [__('指定用户', 'aksrt'), 'dashicons-admin-users'], 'role' => [__('按角色群发', 'aksrt'), 'dashicons-groups']] as $value => [$label, $icon]) {
|
||||
echo '<label class="aksrt-msg-mode"><input type="radio" name="recipient_mode" value="' . esc_attr($value) . '"' . checked($value, 'users', false) . ' />';
|
||||
echo '<fieldset class="jio-msg-modes"><legend class="jio-native-field__label">' . esc_html__('收件人', 'jio') . '</legend>';
|
||||
foreach (['users' => [__('指定用户', 'jio'), 'dashicons-admin-users'], 'role' => [__('按角色群发', 'jio'), 'dashicons-groups']] as $value => [$label, $icon]) {
|
||||
echo '<label class="jio-msg-mode"><input type="radio" name="recipient_mode" value="' . esc_attr($value) . '"' . checked($value, 'users', false) . ' />';
|
||||
echo '<span><span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset><div class="aksrt-native-form-grid">';
|
||||
echo '</fieldset><div class="jio-native-form-grid">';
|
||||
printf(
|
||||
'<label class="aksrt-native-field is-wide" data-aksrt-msg-pane="users"><span class="aksrt-native-field__label">%s</span><textarea name="users" rows="2" required placeholder="%s"></textarea><small class="aksrt-native-field__hint">%s</small></label>',
|
||||
esc_html__('用户', 'aksrt'), esc_attr__('用户 ID / 用户名 / 邮箱,逗号或换行分隔', 'aksrt'), esc_html(sprintf(__('单次最多 %d 位。', 'aksrt'), AdminBroadcast::MAX_USERS))
|
||||
'<label class="jio-native-field is-wide" data-jio-msg-pane="users"><span class="jio-native-field__label">%s</span><textarea name="users" rows="2" required placeholder="%s"></textarea><small class="jio-native-field__hint">%s</small></label>',
|
||||
esc_html__('用户', 'jio'), esc_attr__('用户 ID / 用户名 / 邮箱,逗号或换行分隔', 'jio'), esc_html(sprintf(__('单次最多 %d 位。', 'jio'), AdminBroadcast::MAX_USERS))
|
||||
);
|
||||
echo '<label class="aksrt-native-field is-wide" data-aksrt-msg-pane="role" hidden><span class="aksrt-native-field__label">' . esc_html__('角色', 'aksrt') . '</span>';
|
||||
echo '<select name="role" required disabled><option value="" selected disabled>' . esc_html__('请选择角色', 'aksrt') . '</option>';
|
||||
echo '<label class="jio-native-field is-wide" data-jio-msg-pane="role" hidden><span class="jio-native-field__label">' . esc_html__('角色', 'jio') . '</span>';
|
||||
echo '<select name="role" required disabled><option value="" selected disabled>' . esc_html__('请选择角色', 'jio') . '</option>';
|
||||
$counts = count_users('time');
|
||||
foreach (wp_roles()->get_names() as $role => $name) {
|
||||
$count = (int) ($counts['avail_roles'][$role] ?? 0);
|
||||
$label = translate_user_role($name);
|
||||
$disabled = $count === 0 || $count > AdminBroadcast::MAX_ROLE_USERS;
|
||||
$suffix = $count > AdminBroadcast::MAX_ROLE_USERS ? __('(超过发送上限)', 'aksrt') : ($count === 0 ? __('(暂无用户)', 'aksrt') : '');
|
||||
$suffix = $count > AdminBroadcast::MAX_ROLE_USERS ? __('(超过发送上限)', 'jio') : ($count === 0 ? __('(暂无用户)', 'jio') : '');
|
||||
printf(
|
||||
'<option value="%s" data-count="%d" data-label="%s"%s>%s</option>',
|
||||
esc_attr($role), $count, esc_attr($label), $disabled ? ' disabled' : '',
|
||||
esc_html(sprintf(__('%1$s · %2$s 人', 'aksrt'), $label, number_format_i18n($count)) . $suffix)
|
||||
esc_html(sprintf(__('%1$s · %2$s 人', 'jio'), $label, number_format_i18n($count)) . $suffix)
|
||||
);
|
||||
}
|
||||
echo '</select><small class="aksrt-native-field__hint">' . esc_html(sprintf(__('单个角色最多 %d 人;空角色和超限角色不可选。', 'aksrt'), AdminBroadcast::MAX_ROLE_USERS)) . '</small>';
|
||||
echo '<small class="aksrt-native-field__hint" data-aksrt-msg-role-hint aria-live="polite"></small></label>';
|
||||
echo '</select><small class="jio-native-field__hint">' . esc_html(sprintf(__('单个角色最多 %d 人;空角色和超限角色不可选。', 'jio'), AdminBroadcast::MAX_ROLE_USERS)) . '</small>';
|
||||
echo '<small class="jio-native-field__hint" data-jio-msg-role-hint aria-live="polite"></small></label>';
|
||||
printf(
|
||||
'<label class="aksrt-native-field"><span class="aksrt-native-field__label">%s</span><input type="text" name="title" data-count-limit="%d" aria-describedby="aksrt-msg-title-count" placeholder="%s" /><small class="aksrt-msg-counter" id="aksrt-msg-title-count" data-count-for="title"></small></label>',
|
||||
esc_html__('标题', 'aksrt'), AdminBroadcast::TITLE_MAX, esc_attr__('留空则取内容开头', 'aksrt')
|
||||
'<label class="jio-native-field"><span class="jio-native-field__label">%s</span><input type="text" name="title" data-count-limit="%d" aria-describedby="jio-msg-title-count" placeholder="%s" /><small class="jio-msg-counter" id="jio-msg-title-count" data-count-for="title"></small></label>',
|
||||
esc_html__('标题', 'jio'), AdminBroadcast::TITLE_MAX, esc_attr__('留空则取内容开头', 'jio')
|
||||
);
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('跳转链接(可选)', 'aksrt') . '</span><input type="url" name="target_url" placeholder="https://" /></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('跳转链接(可选)', 'jio') . '</span><input type="url" name="target_url" placeholder="https://" /></label>';
|
||||
printf(
|
||||
'<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">%s</span><textarea name="content" rows="5" data-count-limit="%d" required aria-describedby="aksrt-msg-content-count"></textarea><small class="aksrt-msg-counter" id="aksrt-msg-content-count" data-count-for="content"></small></label>',
|
||||
esc_html__('内容', 'aksrt'), AdminBroadcast::CONTENT_MAX
|
||||
'<label class="jio-native-field is-wide"><span class="jio-native-field__label">%s</span><textarea name="content" rows="5" data-count-limit="%d" required aria-describedby="jio-msg-content-count"></textarea><small class="jio-msg-counter" id="jio-msg-content-count" data-count-for="content"></small></label>',
|
||||
esc_html__('内容', 'jio'), AdminBroadcast::CONTENT_MAX
|
||||
);
|
||||
echo '</div><p class="aksrt-native-muted aksrt-msg-dialog__note">' . esc_html__('以「系统通知」类型逐人投递;邮件 / 公众号提醒按通知配置同步触发。', 'aksrt') . '</p></div>';
|
||||
echo '<footer class="aksrt-msg-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-msg-send>' . esc_html__('发送', 'aksrt') . '</button></footer></form></dialog>';
|
||||
echo '</div><p class="jio-native-muted jio-msg-dialog__note">' . esc_html__('以「系统通知」类型逐人投递;邮件 / 公众号提醒按通知配置同步触发。', 'jio') . '</p></div>';
|
||||
echo '<footer class="jio-msg-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-msg-send>' . esc_html__('发送', 'jio') . '</button></footer></form></dialog>';
|
||||
}
|
||||
|
||||
private function render_message_dialogs(): void {
|
||||
echo '<dialog class="aksrt-msg-dialog" id="aksrt-msg-detail" aria-labelledby="aksrt-msg-detail-title"><div class="aksrt-msg-dialog__frame">';
|
||||
$this->render_message_dialog_head('aksrt-msg-detail', __('消息详情', 'aksrt'), 'dashicons-email-alt');
|
||||
echo '<div class="aksrt-msg-dialog__body" data-aksrt-msg-detail-body></div>';
|
||||
echo '<footer class="aksrt-msg-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('关闭', 'aksrt') . '</button></footer></div></dialog>';
|
||||
echo '<dialog class="jio-msg-dialog" id="jio-msg-detail" aria-labelledby="jio-msg-detail-title"><div class="jio-msg-dialog__frame">';
|
||||
$this->render_message_dialog_head('jio-msg-detail', __('消息详情', 'jio'), 'dashicons-email-alt');
|
||||
echo '<div class="jio-msg-dialog__body" data-jio-msg-detail-body></div>';
|
||||
echo '<footer class="jio-msg-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('关闭', 'jio') . '</button></footer></div></dialog>';
|
||||
|
||||
echo '<dialog class="aksrt-msg-dialog aksrt-msg-dialog--confirm" id="aksrt-msg-confirm" aria-labelledby="aksrt-msg-confirm-title" aria-describedby="aksrt-msg-confirm-text"';
|
||||
echo '<dialog class="jio-msg-dialog jio-msg-dialog--confirm" id="jio-msg-confirm" aria-labelledby="jio-msg-confirm-title" aria-describedby="jio-msg-confirm-text"';
|
||||
foreach ([
|
||||
'delete-title' => __('删除消息', 'aksrt'),
|
||||
'delete-text' => __('确定删除 {count} 条消息?删除后用户收件箱中也将不再显示,此操作不可撤销。', 'aksrt'),
|
||||
'delete-send' => __('确认删除', 'aksrt'),
|
||||
'delete-title' => __('删除消息', 'jio'),
|
||||
'delete-text' => __('确定删除 {count} 条消息?删除后用户收件箱中也将不再显示,此操作不可撤销。', 'jio'),
|
||||
'delete-send' => __('确认删除', 'jio'),
|
||||
] as $key => $text) {
|
||||
echo ' data-i18n-' . esc_attr($key) . '="' . esc_attr($text) . '"';
|
||||
}
|
||||
echo '><div class="aksrt-msg-dialog__frame">';
|
||||
$this->render_message_dialog_head('aksrt-msg-confirm', __('确认操作', 'aksrt'), 'dashicons-warning');
|
||||
echo '<div class="aksrt-msg-dialog__body"><p id="aksrt-msg-confirm-text"></p></div>';
|
||||
echo '<footer class="aksrt-msg-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close autofocus>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="button" class="button button-primary" data-aksrt-msg-confirm>' . esc_html__('确认', 'aksrt') . '</button></footer></div></dialog>';
|
||||
echo '><div class="jio-msg-dialog__frame">';
|
||||
$this->render_message_dialog_head('jio-msg-confirm', __('确认操作', 'jio'), 'dashicons-warning');
|
||||
echo '<div class="jio-msg-dialog__body"><p id="jio-msg-confirm-text"></p></div>';
|
||||
echo '<footer class="jio-msg-dialog__foot"><button type="button" class="button" data-jio-dialog-close autofocus>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="button" class="button button-primary" data-jio-msg-confirm>' . esc_html__('确认', 'jio') . '</button></footer></div></dialog>';
|
||||
}
|
||||
|
||||
private function render_message_dialog_head(string $id, string $title, string $icon): void {
|
||||
echo '<header class="aksrt-msg-dialog__head"><h2 id="' . esc_attr($id) . '-title" class="aksrt-msg-dialog__title">';
|
||||
echo '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span><span data-aksrt-dialog-title>' . esc_html($title) . '</span></h2>';
|
||||
echo '<button type="button" class="aksrt-msg-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<header class="jio-msg-dialog__head"><h2 id="' . esc_attr($id) . '-title" class="jio-msg-dialog__title">';
|
||||
echo '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span><span data-jio-dialog-title>' . esc_html($title) . '</span></h2>';
|
||||
echo '<button type="button" class="jio-msg-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,13 @@
|
||||
/**
|
||||
* 消息管理展示:概览、筛选、列表与按行预渲染的只读详情。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Messages\MessageTypes;
|
||||
use AKSRT\Messages\NotificationAdminQuery;
|
||||
use Jio\Messages\MessageTypes;
|
||||
use Jio\Messages\NotificationAdminQuery;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,22 +17,22 @@ trait UsersMenusMessagesList {
|
||||
private function render_message_overview(): void {
|
||||
$stats = NotificationAdminQuery::overview();
|
||||
NativeUi::stats([
|
||||
['label' => __('消息总数', 'aksrt'), 'value' => number_format_i18n($stats['total']), 'icon' => 'dashicons-email-alt', 'tone' => 'info', 'url' => $this->message_page_url()],
|
||||
['label' => __('未读', 'aksrt'), 'value' => number_format_i18n($stats['unread']), 'icon' => 'dashicons-marker', 'tone' => 'danger', 'url' => $this->message_page_url(['status' => 'unread'])],
|
||||
['label' => __('今日新增', 'aksrt'), 'value' => number_format_i18n($stats['today']), 'icon' => 'dashicons-calendar-alt', 'tone' => 'success', 'url' => $this->message_page_url(['period' => 'today'])],
|
||||
['label' => __('涉及用户', 'aksrt'), 'value' => number_format_i18n($stats['recipients']), 'icon' => 'dashicons-groups', 'tone' => 'primary', 'url' => admin_url('users.php'), 'hint' => __('前往用户管理', 'aksrt')],
|
||||
['label' => __('消息总数', 'jio'), 'value' => number_format_i18n($stats['total']), 'icon' => 'dashicons-email-alt', 'tone' => 'info', 'url' => $this->message_page_url()],
|
||||
['label' => __('未读', 'jio'), 'value' => number_format_i18n($stats['unread']), 'icon' => 'dashicons-marker', 'tone' => 'danger', 'url' => $this->message_page_url(['status' => 'unread'])],
|
||||
['label' => __('今日新增', 'jio'), 'value' => number_format_i18n($stats['today']), 'icon' => 'dashicons-calendar-alt', 'tone' => 'success', 'url' => $this->message_page_url(['period' => 'today'])],
|
||||
['label' => __('涉及用户', 'jio'), 'value' => number_format_i18n($stats['recipients']), 'icon' => 'dashicons-groups', 'tone' => 'primary', 'url' => admin_url('users.php'), 'hint' => __('前往用户管理', 'jio')],
|
||||
]);
|
||||
}
|
||||
|
||||
private function message_views_html(array $filters, array $counts): string {
|
||||
$views = [
|
||||
'' => [__('全部', 'aksrt'), $counts['all']],
|
||||
'unread' => [__('未读', 'aksrt'), $counts['unread']],
|
||||
'read' => [__('已读', 'aksrt'), $counts['read']],
|
||||
'' => [__('全部', 'jio'), $counts['all']],
|
||||
'unread' => [__('未读', 'jio'), $counts['unread']],
|
||||
'read' => [__('已读', 'jio'), $counts['read']],
|
||||
];
|
||||
$args = $this->message_filter_args($filters);
|
||||
unset($args['status']);
|
||||
$html = '<ul class="subsubsub" aria-label="' . esc_attr__('消息状态', 'aksrt') . '">';
|
||||
$html = '<ul class="subsubsub" aria-label="' . esc_attr__('消息状态', 'jio') . '">';
|
||||
foreach ($views as $key => [$label, $count]) {
|
||||
$html .= sprintf(
|
||||
'<li><a href="%s"%s>%s <span class="count">(%s)</span></a></li>',
|
||||
@@ -46,33 +46,33 @@ trait UsersMenusMessagesList {
|
||||
}
|
||||
|
||||
private function render_message_filter_form(array $filters): void {
|
||||
echo '<form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::MESSAGE_PAGE_SLUG) . '" />';
|
||||
if ($filters['status'] !== '') {
|
||||
echo '<input type="hidden" name="status" value="' . esc_attr($filters['status']) . '" />';
|
||||
}
|
||||
echo '<label class="screen-reader-text" for="aksrt-msg-filter-type">' . esc_html__('按类型筛选', 'aksrt') . '</label>';
|
||||
echo '<select name="type" id="aksrt-msg-filter-type"><option value="">' . esc_html__('全部类型', 'aksrt') . '</option>';
|
||||
echo '<label class="screen-reader-text" for="jio-msg-filter-type">' . esc_html__('按类型筛选', 'jio') . '</label>';
|
||||
echo '<select name="type" id="jio-msg-filter-type"><option value="">' . esc_html__('全部类型', 'jio') . '</option>';
|
||||
foreach (NotificationAdminQuery::types() as $type) {
|
||||
printf('<option value="%s"%s>%s</option>', esc_attr($type), selected($filters['type'], $type, false), esc_html(MessageTypes::label($type)));
|
||||
}
|
||||
echo '</select>';
|
||||
echo '<label class="screen-reader-text" for="aksrt-msg-filter-period">' . esc_html__('按发送时间筛选', 'aksrt') . '</label>';
|
||||
echo '<select name="period" id="aksrt-msg-filter-period"><option value="">' . esc_html__('全部时间', 'aksrt') . '</option>';
|
||||
echo '<option value="today"' . selected($filters['period'], 'today', false) . '>' . esc_html__('今日新增', 'aksrt') . '</option></select>';
|
||||
echo '<label class="screen-reader-text" for="jio-msg-filter-period">' . esc_html__('按发送时间筛选', 'jio') . '</label>';
|
||||
echo '<select name="period" id="jio-msg-filter-period"><option value="">' . esc_html__('全部时间', 'jio') . '</option>';
|
||||
echo '<option value="today"' . selected($filters['period'], 'today', false) . '>' . esc_html__('今日新增', 'jio') . '</option></select>';
|
||||
printf(
|
||||
'<label class="screen-reader-text" for="aksrt-msg-filter-user">%s</label><input type="text" id="aksrt-msg-filter-user" name="user" value="%s" placeholder="%s" />',
|
||||
esc_html__('按收件人筛选', 'aksrt'), esc_attr($filters['user']), esc_attr__('收件人 ID / 用户名 / 邮箱', 'aksrt')
|
||||
'<label class="screen-reader-text" for="jio-msg-filter-user">%s</label><input type="text" id="jio-msg-filter-user" name="user" value="%s" placeholder="%s" />',
|
||||
esc_html__('按收件人筛选', 'jio'), esc_attr($filters['user']), esc_attr__('收件人 ID / 用户名 / 邮箱', 'jio')
|
||||
);
|
||||
printf(
|
||||
'<label class="screen-reader-text" for="aksrt-msg-filter-s">%s</label><input type="search" id="aksrt-msg-filter-s" name="s" value="%s" placeholder="%s" />',
|
||||
esc_html__('搜索消息', 'aksrt'), esc_attr($filters['s']), esc_attr__('标题 / 内容', 'aksrt')
|
||||
'<label class="screen-reader-text" for="jio-msg-filter-s">%s</label><input type="search" id="jio-msg-filter-s" name="s" value="%s" placeholder="%s" />',
|
||||
esc_html__('搜索消息', 'jio'), esc_attr($filters['s']), esc_attr__('标题 / 内容', 'jio')
|
||||
);
|
||||
submit_button(__('筛选', 'aksrt'), '', '', false);
|
||||
submit_button(__('筛选', 'jio'), '', '', false);
|
||||
$other = $filters;
|
||||
unset($other['status']);
|
||||
if ($this->message_filter_args($other) !== []) {
|
||||
echo '<a class="button-link" href="' . esc_url($this->message_page_url($filters['status'] === '' ? [] : ['status' => $filters['status']])) . '">' . esc_html__('清除筛选', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this->message_page_url($filters['status'] === '' ? [] : ['status' => $filters['status']])) . '">' . esc_html__('清除筛选', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form>';
|
||||
}
|
||||
@@ -82,29 +82,29 @@ trait UsersMenusMessagesList {
|
||||
if ($user_ids !== []) {
|
||||
cache_users($user_ids);
|
||||
}
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-messages-admin__list" data-aksrt-msg-list>';
|
||||
echo '<input type="hidden" name="action" value="aksrt_users_bulk_messages" />';
|
||||
wp_nonce_field('aksrt_users_bulk_messages');
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-messages-admin__list" data-jio-msg-list>';
|
||||
echo '<input type="hidden" name="action" value="jio_users_bulk_messages" />';
|
||||
wp_nonce_field('jio_users_bulk_messages');
|
||||
foreach ($this->message_filter_args($filters) as $key => $value) {
|
||||
printf('<input type="hidden" name="%s" value="%s" />', esc_attr($key), esc_attr((string) $value));
|
||||
}
|
||||
echo '<input type="hidden" name="paged" value="' . esc_attr((string) $page) . '" />';
|
||||
echo '<input type="hidden" name="bulk_action" value="" /><input type="hidden" name="message_id" value="" />';
|
||||
echo '<div class="aksrt-messages-admin__bulk">';
|
||||
echo '<span class="aksrt-messages-admin__selection" data-aksrt-msg-selection role="status" aria-live="polite" data-label="' . esc_attr__('已选 %d 条', 'aksrt') . '"></span>';
|
||||
echo '<button type="button" class="button" data-aksrt-msg-action="mark_read" disabled>' . esc_html__('标记已读', 'aksrt') . '</button>';
|
||||
echo '<button type="button" class="button aksrt-messages-admin__danger" data-aksrt-msg-action="delete" disabled>' . esc_html__('删除', 'aksrt') . '</button>';
|
||||
echo '</div><div class="aksrt-native-table-wrap">';
|
||||
echo '<table class="wp-list-table widefat fixed striped aksrt-messages-admin__table"><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column"><label class="screen-reader-text" for="aksrt-msg-check-all">' . esc_html__('全选本页消息', 'aksrt') . '</label><input id="aksrt-msg-check-all" type="checkbox" data-aksrt-msg-check-all /></td>';
|
||||
foreach (['title' => __('消息', 'aksrt'), 'type' => __('类型', 'aksrt'), 'user' => __('收件人', 'aksrt'), 'read' => __('状态', 'aksrt'), 'time' => __('发送时间', 'aksrt'), 'actions' => __('操作', 'aksrt')] as $column => $label) {
|
||||
echo '<div class="jio-messages-admin__bulk">';
|
||||
echo '<span class="jio-messages-admin__selection" data-jio-msg-selection role="status" aria-live="polite" data-label="' . esc_attr__('已选 %d 条', 'jio') . '"></span>';
|
||||
echo '<button type="button" class="button" data-jio-msg-action="mark_read" disabled>' . esc_html__('标记已读', 'jio') . '</button>';
|
||||
echo '<button type="button" class="button jio-messages-admin__danger" data-jio-msg-action="delete" disabled>' . esc_html__('删除', 'jio') . '</button>';
|
||||
echo '</div><div class="jio-native-table-wrap">';
|
||||
echo '<table class="wp-list-table widefat fixed striped jio-messages-admin__table"><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column"><label class="screen-reader-text" for="jio-msg-check-all">' . esc_html__('全选本页消息', 'jio') . '</label><input id="jio-msg-check-all" type="checkbox" data-jio-msg-check-all /></td>';
|
||||
foreach (['title' => __('消息', 'jio'), 'type' => __('类型', 'jio'), 'user' => __('收件人', 'jio'), 'read' => __('状态', 'jio'), 'time' => __('发送时间', 'jio'), 'actions' => __('操作', 'jio')] as $column => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($column) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($items === []) {
|
||||
$hint = $user_missing ? __('没有找到该收件人。', 'aksrt')
|
||||
: ($this->message_filter_args($filters) === [] ? __('站内还没有任何通知。', 'aksrt') : __('当前筛选条件下没有消息。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无消息', 'aksrt'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$hint = $user_missing ? __('没有找到该收件人。', 'jio')
|
||||
: ($this->message_filter_args($filters) === [] ? __('站内还没有任何通知。', 'jio') : __('当前筛选条件下没有消息。', 'jio'));
|
||||
echo NativeUi::empty_row(7, __('暂无消息', 'jio'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_message_row($row, $filters);
|
||||
@@ -120,77 +120,77 @@ trait UsersMenusMessagesList {
|
||||
$unread = empty($row['is_read']);
|
||||
echo '<tr class="' . ($unread ? 'is-unread' : 'is-read') . '">';
|
||||
printf(
|
||||
'<th scope="row" class="check-column"><label class="screen-reader-text" for="aksrt-msg-%1$d">%2$s</label><input id="aksrt-msg-%1$d" type="checkbox" name="ids[]" value="%1$d" data-aksrt-msg-check /></th>',
|
||||
$id, esc_html(sprintf(__('选择消息 #%d', 'aksrt'), $id))
|
||||
'<th scope="row" class="check-column"><label class="screen-reader-text" for="jio-msg-%1$d">%2$s</label><input id="jio-msg-%1$d" type="checkbox" name="ids[]" value="%1$d" data-jio-msg-check /></th>',
|
||||
$id, esc_html(sprintf(__('选择消息 #%d', 'jio'), $id))
|
||||
);
|
||||
echo '<td class="column-title"><strong class="row-title">' . esc_html($title) . '</strong>';
|
||||
if ($desc !== '' && $desc !== $title) {
|
||||
echo '<div class="aksrt-messages-admin__desc">' . esc_html(mb_strimwidth($desc, 0, 160, '…')) . '</div>';
|
||||
echo '<div class="jio-messages-admin__desc">' . esc_html(mb_strimwidth($desc, 0, 160, '…')) . '</div>';
|
||||
}
|
||||
echo '<div class="aksrt-messages-admin__sub"><code>#' . esc_html((string) $id) . '</code></div>';
|
||||
echo '<div class="jio-messages-admin__sub"><code>#' . esc_html((string) $id) . '</code></div>';
|
||||
$this->render_message_detail_template($row);
|
||||
echo '</td>';
|
||||
printf(
|
||||
'<td class="column-type"><a class="aksrt-msg-badge is-%s" href="%s">%s</a></td>',
|
||||
'<td class="column-type"><a class="jio-msg-badge is-%s" href="%s">%s</a></td>',
|
||||
esc_attr(MessageTypes::tone($type)), esc_url($this->message_page_url(array_merge($this->message_filter_args($filters), ['type' => $type]))), esc_html(MessageTypes::label($type))
|
||||
);
|
||||
echo '<td class="column-user">' . $this->message_user_cell((int) $row['user_id'], $filters) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$read_at = (string) ($row['read_at'] ?? '');
|
||||
printf(
|
||||
'<td class="column-read"><span class="aksrt-msg-state %s"%s>%s</span></td>',
|
||||
'<td class="column-read"><span class="jio-msg-state %s"%s>%s</span></td>',
|
||||
$unread ? 'is-unread' : 'is-read',
|
||||
!$unread && $read_at !== '' ? ' title="' . esc_attr(sprintf(__('阅读于 %s', 'aksrt'), $this->message_time($read_at))) . '"' : '',
|
||||
$unread ? esc_html__('未读', 'aksrt') : esc_html__('已读', 'aksrt')
|
||||
!$unread && $read_at !== '' ? ' title="' . esc_attr(sprintf(__('阅读于 %s', 'jio'), $this->message_time($read_at))) . '"' : '',
|
||||
$unread ? esc_html__('未读', 'jio') : esc_html__('已读', 'jio')
|
||||
);
|
||||
$created = (string) $row['created_at'];
|
||||
$ts = $created !== '' ? strtotime($created . ' UTC') : false;
|
||||
printf(
|
||||
'<td class="column-time"><time datetime="%s">%s</time>%s</td>',
|
||||
esc_attr($ts ? gmdate('c', $ts) : ''), esc_html($this->message_time($created)),
|
||||
$ts ? '<div class="aksrt-messages-admin__sub">' . esc_html(sprintf(__('%s前', 'aksrt'), human_time_diff($ts))) . '</div>' : ''
|
||||
$ts ? '<div class="jio-messages-admin__sub">' . esc_html(sprintf(__('%s前', 'jio'), human_time_diff($ts))) . '</div>' : ''
|
||||
);
|
||||
echo '<td class="column-actions"><div class="aksrt-msg-row-actions">';
|
||||
printf('<button type="button" class="button-link" data-aksrt-msg-view="%d" aria-haspopup="dialog" aria-controls="aksrt-msg-detail">%s</button>', $id, esc_html__('查看', 'aksrt'));
|
||||
echo '<td class="column-actions"><div class="jio-msg-row-actions">';
|
||||
printf('<button type="button" class="button-link" data-jio-msg-view="%d" aria-haspopup="dialog" aria-controls="jio-msg-detail">%s</button>', $id, esc_html__('查看', 'jio'));
|
||||
if ($unread) {
|
||||
printf('<button type="button" class="button-link" data-aksrt-msg-action="mark_read" data-id="%d">%s</button>', $id, esc_html__('标记已读', 'aksrt'));
|
||||
printf('<button type="button" class="button-link" data-jio-msg-action="mark_read" data-id="%d">%s</button>', $id, esc_html__('标记已读', 'jio'));
|
||||
}
|
||||
printf('<button type="button" class="button-link button-link-delete" data-aksrt-msg-action="delete" data-id="%d">%s</button>', $id, esc_html__('删除', 'aksrt'));
|
||||
printf('<button type="button" class="button-link button-link-delete" data-jio-msg-action="delete" data-id="%d">%s</button>', $id, esc_html__('删除', 'jio'));
|
||||
echo '</div></td></tr>';
|
||||
}
|
||||
|
||||
private function render_message_detail_template(array $row): void {
|
||||
$user = get_userdata((int) $row['user_id']);
|
||||
$recipient = $user ? $user->display_name : __('已删除用户', 'aksrt');
|
||||
echo '<template id="aksrt-msg-detail-' . esc_attr((string) $row['id']) . '">';
|
||||
echo '<h3 class="aksrt-msg-detail__title">' . esc_html((string) $row['title']) . '</h3><dl class="aksrt-msg-detail__meta">';
|
||||
$recipient = $user ? $user->display_name : __('已删除用户', 'jio');
|
||||
echo '<template id="jio-msg-detail-' . esc_attr((string) $row['id']) . '">';
|
||||
echo '<h3 class="jio-msg-detail__title">' . esc_html((string) $row['title']) . '</h3><dl class="jio-msg-detail__meta">';
|
||||
$meta = [
|
||||
__('消息编号', 'aksrt') => '#' . $row['id'],
|
||||
__('收件人', 'aksrt') => $recipient . ' · #' . $row['user_id'],
|
||||
__('消息类型', 'aksrt') => MessageTypes::label((string) $row['type']),
|
||||
__('发送时间', 'aksrt') => $this->message_time((string) $row['created_at']),
|
||||
__('阅读状态', 'aksrt') => empty($row['is_read']) ? __('未读', 'aksrt') : __('已读', 'aksrt'),
|
||||
__('阅读时间', 'aksrt') => empty($row['is_read']) || empty($row['read_at']) ? '—' : $this->message_time((string) $row['read_at']),
|
||||
__('消息编号', 'jio') => '#' . $row['id'],
|
||||
__('收件人', 'jio') => $recipient . ' · #' . $row['user_id'],
|
||||
__('消息类型', 'jio') => MessageTypes::label((string) $row['type']),
|
||||
__('发送时间', 'jio') => $this->message_time((string) $row['created_at']),
|
||||
__('阅读状态', 'jio') => empty($row['is_read']) ? __('未读', 'jio') : __('已读', 'jio'),
|
||||
__('阅读时间', 'jio') => empty($row['is_read']) || empty($row['read_at']) ? '—' : $this->message_time((string) $row['read_at']),
|
||||
];
|
||||
foreach ($meta as $label => $value) {
|
||||
echo '<div><dt>' . esc_html($label) . '</dt><dd>' . esc_html($value) . '</dd></div>';
|
||||
}
|
||||
echo '</dl><h4>' . esc_html__('完整内容', 'aksrt') . '</h4><div class="aksrt-msg-detail__content">' . esc_html((string) $row['description']) . '</div>';
|
||||
echo '</dl><h4>' . esc_html__('完整内容', 'jio') . '</h4><div class="jio-msg-detail__content">' . esc_html((string) $row['description']) . '</div>';
|
||||
if (!empty($row['target_url'])) {
|
||||
printf('<a class="aksrt-msg-detail__link" href="%s" target="_blank" rel="noopener noreferrer">%s<span class="screen-reader-text">%s</span></a>', esc_url((string) $row['target_url']), esc_html((string) $row['target_url']), esc_html__('(在新窗口打开)', 'aksrt'));
|
||||
printf('<a class="jio-msg-detail__link" href="%s" target="_blank" rel="noopener noreferrer">%s<span class="screen-reader-text">%s</span></a>', esc_url((string) $row['target_url']), esc_html((string) $row['target_url']), esc_html__('(在新窗口打开)', 'jio'));
|
||||
}
|
||||
echo '<p class="aksrt-native-field__hint">' . esc_html__('查看详情不会改变收件人的已读状态。', 'aksrt') . '</p></template>';
|
||||
echo '<p class="jio-native-field__hint">' . esc_html__('查看详情不会改变收件人的已读状态。', 'jio') . '</p></template>';
|
||||
}
|
||||
|
||||
private function message_user_cell(int $user_id, array $filters): string {
|
||||
$user = $user_id > 0 ? get_userdata($user_id) : false;
|
||||
if (!$user) {
|
||||
return '<span class="aksrt-messages-admin__muted">' . esc_html(sprintf(__('已删除用户 #%d', 'aksrt'), $user_id)) . '</span>';
|
||||
return '<span class="jio-messages-admin__muted">' . esc_html(sprintf(__('已删除用户 #%d', 'jio'), $user_id)) . '</span>';
|
||||
}
|
||||
$avatar = function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user_id, 48) : get_avatar_url($user_id, ['size' => 48]);
|
||||
$avatar = function_exists('jio_get_avatar_url') ? jio_get_avatar_url($user_id, 48) : get_avatar_url($user_id, ['size' => 48]);
|
||||
$filter_url = $this->message_page_url(array_merge($this->message_filter_args($filters), ['user' => (string) $user_id]));
|
||||
return '<a class="aksrt-msg-user" href="' . esc_url($filter_url) . '" title="' . esc_attr__('只看该用户', 'aksrt') . '">'
|
||||
return '<a class="jio-msg-user" href="' . esc_url($filter_url) . '" title="' . esc_attr__('只看该用户', 'jio') . '">'
|
||||
. '<img src="' . esc_url($avatar) . '" width="24" height="24" alt="" />'
|
||||
. '<span>' . esc_html($user->display_name) . '</span></a>'
|
||||
. '<div class="aksrt-messages-admin__sub">#' . esc_html((string) $user_id) . ' · <a href="' . esc_url(get_edit_user_link($user_id)) . '">' . esc_html__('编辑', 'aksrt') . '</a></div>';
|
||||
. '<div class="jio-messages-admin__sub">#' . esc_html((string) $user_id) . ' · <a href="' . esc_url(get_edit_user_link($user_id)) . '">' . esc_html__('编辑', 'jio') . '</a></div>';
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
* 工单管理:列表(含代客提交弹窗)与处理工作区(?id=)两个页内视图。
|
||||
* 写操作统一使用 TicketRestAdmin;此处只负责服务端渲染。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Ticket\TicketRepository;
|
||||
use AKSRT\Ticket\TicketStatusService;
|
||||
use Jio\Ticket\TicketRepository;
|
||||
use Jio\Ticket\TicketStatusService;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -19,7 +19,7 @@ trait UsersMenusTicketsCenter {
|
||||
use UsersMenusTicketsDetail;
|
||||
use UsersMenusTicketsCreate;
|
||||
|
||||
private const TICKET_PAGE_SLUG = 'aksrt_tickets';
|
||||
private const TICKET_PAGE_SLUG = 'jio_tickets';
|
||||
private const TICKET_PER_PAGE = 20;
|
||||
|
||||
private function count_pending_tickets(): int {
|
||||
@@ -35,18 +35,18 @@ trait UsersMenusTicketsCenter {
|
||||
}
|
||||
$css = '/assets/css/admin/tickets.css';
|
||||
$js = '/assets/js/admin-tickets.js';
|
||||
wp_enqueue_style('aksrt-admin-tickets', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-tickets', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_localize_script('aksrt-admin-tickets', 'AKSRTAdminTickets', [
|
||||
wp_enqueue_style('jio-admin-tickets', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-tickets', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
wp_localize_script('jio-admin-tickets', 'JioAdminTickets', [
|
||||
'restNonce' => wp_create_nonce('wp_rest'),
|
||||
'currentUser' => get_current_user_id(),
|
||||
'strings' => [
|
||||
'saving' => __('正在保存…', 'aksrt'),
|
||||
'saveFailed' => __('保存失败,请检查填写内容后重试。', 'aksrt'),
|
||||
'uncertain' => __('连接中断,提交结果尚未确认。请先重新加载核对,避免重复提交。', 'aksrt'),
|
||||
'required' => __('请填写内容,不能只包含空格。', 'aksrt'),
|
||||
'tooLong' => __('插入后将超过字数限制,请先精简回复内容。', 'aksrt'),
|
||||
'discard' => __('有尚未保存的内容。关闭后将丢弃,是否继续?', 'aksrt'),
|
||||
'saving' => __('正在保存…', 'jio'),
|
||||
'saveFailed' => __('保存失败,请检查填写内容后重试。', 'jio'),
|
||||
'uncertain' => __('连接中断,提交结果尚未确认。请先重新加载核对,避免重复提交。', 'jio'),
|
||||
'required' => __('请填写内容,不能只包含空格。', 'jio'),
|
||||
'tooLong' => __('插入后将超过字数限制,请先精简回复内容。', 'jio'),
|
||||
'discard' => __('有尚未保存的内容。关闭后将丢弃,是否继续?', 'jio'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -56,7 +56,7 @@ trait UsersMenusTicketsCenter {
|
||||
$filters = $this->ticket_filters($_GET);
|
||||
$paged = max(1, absint($_GET['paged'] ?? 1));
|
||||
$id = absint($_GET['id'] ?? 0);
|
||||
NativeUi::page_open('aksrt-tickets-admin');
|
||||
NativeUi::page_open('jio-tickets-admin');
|
||||
if ($id > 0) {
|
||||
$this->render_ticket_detail_page($id, $this->ticket_context($filters, $paged));
|
||||
} else {
|
||||
@@ -85,12 +85,12 @@ trait UsersMenusTicketsCenter {
|
||||
|
||||
/** 写操作成功后脚本跳转回工作区并带上结果码,这里转成原生通知。 */
|
||||
private function render_ticket_notice(): void {
|
||||
$code = isset($_GET['aksrt_ticket_msg']) && is_scalar($_GET['aksrt_ticket_msg']) ? sanitize_key((string) wp_unslash($_GET['aksrt_ticket_msg'])) : '';
|
||||
$code = isset($_GET['jio_ticket_msg']) && is_scalar($_GET['jio_ticket_msg']) ? sanitize_key((string) wp_unslash($_GET['jio_ticket_msg'])) : '';
|
||||
$map = [
|
||||
'created' => __('工单已创建,用户将收到通知。', 'aksrt'),
|
||||
'reply' => __('回复已发送,用户将收到通知。', 'aksrt'),
|
||||
'status' => __('工单信息已保存。', 'aksrt'),
|
||||
'note' => __('内部备注已添加,仅管理员可见。', 'aksrt'),
|
||||
'created' => __('工单已创建,用户将收到通知。', 'jio'),
|
||||
'reply' => __('回复已发送,用户将收到通知。', 'jio'),
|
||||
'status' => __('工单信息已保存。', 'jio'),
|
||||
'note' => __('内部备注已添加,仅管理员可见。', 'jio'),
|
||||
];
|
||||
if (isset($map[$code])) {
|
||||
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html($map[$code]) . '</p></div>';
|
||||
@@ -99,21 +99,21 @@ trait UsersMenusTicketsCenter {
|
||||
|
||||
/** 提交中与失败提示;失败且结果不确定时显示「重新加载」。 */
|
||||
private function ticket_feedback(): string {
|
||||
return '<div class="aksrt-ticket-feedback" hidden data-aksrt-ticket-feedback><p role="status" aria-live="polite" aria-atomic="true"></p>'
|
||||
. '<button type="button" class="button" data-aksrt-ticket-reload hidden>' . esc_html__('重新加载', 'aksrt') . '</button></div>';
|
||||
return '<div class="jio-ticket-feedback" hidden data-jio-ticket-feedback><p role="status" aria-live="polite" aria-atomic="true"></p>'
|
||||
. '<button type="button" class="button" data-jio-ticket-reload hidden>' . esc_html__('重新加载', 'jio') . '</button></div>';
|
||||
}
|
||||
|
||||
/** 代客提交与内部备注弹窗共用的头部:标题、说明与关闭按钮。 */
|
||||
private function ticket_dialog_head(string $key, string $title, string $description): string {
|
||||
return '<header class="aksrt-ticket-dialog__head"><div><h2 id="aksrt-ticket-' . esc_attr($key) . '-title" tabindex="-1">' . esc_html($title) . '</h2>'
|
||||
. '<p id="aksrt-ticket-' . esc_attr($key) . '-description">' . esc_html($description) . '</p></div>'
|
||||
. '<button type="button" class="aksrt-ticket-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
return '<header class="jio-ticket-dialog__head"><div><h2 id="jio-ticket-' . esc_attr($key) . '-title" tabindex="-1">' . esc_html($title) . '</h2>'
|
||||
. '<p id="jio-ticket-' . esc_attr($key) . '-description">' . esc_html($description) . '</p></div>'
|
||||
. '<button type="button" class="jio-ticket-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
}
|
||||
|
||||
/** 所有表单只提交到现有 REST 写路由,不保留 admin_post 分支。 */
|
||||
private function ticket_form_open(string $action, int $id, string $class = ''): string {
|
||||
$path = $action === 'create' ? '' : '/' . $id . '/' . ($action === 'note' ? 'notes' : $action);
|
||||
return '<form method="post" action="' . esc_url(rest_url('aksrt/v1/admin/tickets' . $path)) . '"'
|
||||
. ' class="' . esc_attr($class) . '" data-aksrt-ticket-form="' . esc_attr($action) . '">';
|
||||
return '<form method="post" action="' . esc_url(rest_url('jio/v1/admin/tickets' . $path)) . '"'
|
||||
. ' class="' . esc_attr($class) . '" data-jio-ticket-form="' . esc_attr($action) . '">';
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,48 @@
|
||||
<?php
|
||||
/** 代客提交工单:列表页上的原生 <dialog>,创建成功后跳转到新工单的工作区。@package AKSRT */
|
||||
/** 代客提交工单:列表页上的原生 <dialog>,创建成功后跳转到新工单的工作区。@package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Ticket\TicketSettings;
|
||||
use AKSRT\Ticket\TicketTypeService;
|
||||
use Jio\Ticket\TicketSettings;
|
||||
use Jio\Ticket\TicketTypeService;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusTicketsCreate {
|
||||
|
||||
private const TICKET_CREATE_DIALOG = 'aksrt-ticket-create-dialog';
|
||||
private const TICKET_CREATE_DIALOG = 'jio-ticket-create-dialog';
|
||||
|
||||
private function render_ticket_create_dialog(): void {
|
||||
$types = TicketTypeService::all();
|
||||
echo '<dialog id="' . esc_attr(self::TICKET_CREATE_DIALOG) . '" class="aksrt-ticket-dialog" aria-labelledby="aksrt-ticket-create-title" aria-describedby="aksrt-ticket-create-description">';
|
||||
echo $types === [] ? '<div class="aksrt-ticket-dialog__frame">' : $this->ticket_form_open('create', 0, 'aksrt-ticket-dialog__frame'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->ticket_dialog_head('create', __('代客提交工单', 'aksrt'), __('为指定用户创建工单,创建后由你跟进,用户会收到通知。', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="aksrt-ticket-dialog__body">' . $this->ticket_feedback(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<dialog id="' . esc_attr(self::TICKET_CREATE_DIALOG) . '" class="jio-ticket-dialog" aria-labelledby="jio-ticket-create-title" aria-describedby="jio-ticket-create-description">';
|
||||
echo $types === [] ? '<div class="jio-ticket-dialog__frame">' : $this->ticket_form_open('create', 0, 'jio-ticket-dialog__frame'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->ticket_dialog_head('create', __('代客提交工单', 'jio'), __('为指定用户创建工单,创建后由你跟进,用户会收到通知。', 'jio')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="jio-ticket-dialog__body">' . $this->ticket_feedback(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ($types === []) {
|
||||
echo NativeUi::empty_state(__('尚未配置工单部门', 'aksrt'), __('请先添加部门,再为用户创建工单。', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<p><a class="button" href="' . esc_url(admin_url('admin.php?page=aksrt-settings#/users/ticket')) . '">' . esc_html__('前往工单配置', 'aksrt') . '</a></p>';
|
||||
echo NativeUi::empty_state(__('尚未配置工单部门', 'jio'), __('请先添加部门,再为用户创建工单。', 'jio')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<p><a class="button" href="' . esc_url(admin_url('admin.php?page=jio-settings#/users/ticket')) . '">' . esc_html__('前往工单配置', 'jio') . '</a></p>';
|
||||
} else {
|
||||
echo '<div class="aksrt-native-form-grid">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('用户 ID', 'aksrt') . '</span><input type="number" name="user_id" min="1" step="1" required placeholder="' . esc_attr__('填写工单归属用户的 ID', 'aksrt') . '"></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('工单部门', 'aksrt') . '</span><select name="ticket_type_id" required><option value="">' . esc_html__('请选择部门', 'aksrt') . '</option>';
|
||||
echo '<div class="jio-native-form-grid">';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('用户 ID', 'jio') . '</span><input type="number" name="user_id" min="1" step="1" required placeholder="' . esc_attr__('填写工单归属用户的 ID', 'jio') . '"></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('工单部门', 'jio') . '</span><select name="ticket_type_id" required><option value="">' . esc_html__('请选择部门', 'jio') . '</option>';
|
||||
foreach ($types as $type) {
|
||||
echo '<option value="' . (int) $type['id'] . '">' . esc_html((string) $type['name']) . '</option>';
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('工单标题', 'aksrt') . '</span><input type="text" name="title" maxlength="' . TicketSettings::title_max() . '" required placeholder="' . esc_attr__('简要说明用户遇到的问题', 'aksrt') . '"></label>';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('工单标题', 'jio') . '</span><input type="text" name="title" maxlength="' . TicketSettings::title_max() . '" required placeholder="' . esc_attr__('简要说明用户遇到的问题', 'jio') . '"></label>';
|
||||
if (TicketSettings::order_link_enabled()) {
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('关联订单(可选)', 'aksrt') . '</span><input type="text" name="order_number" placeholder="' . esc_attr__('填写订单号', 'aksrt') . '"></label>';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('关联订单(可选)', 'jio') . '</span><input type="text" name="order_number" placeholder="' . esc_attr__('填写订单号', 'jio') . '"></label>';
|
||||
}
|
||||
echo '<label class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('问题描述', 'aksrt') . '</span><textarea id="aksrt-ticket-create-content" name="content" rows="7" maxlength="' . TicketSettings::content_max() . '" required data-aksrt-ticket-editor aria-describedby="aksrt-ticket-create-count" placeholder="' . esc_attr__('描述问题、发生时间和已尝试的处理方式', 'aksrt') . '"></textarea>';
|
||||
echo '<span id="aksrt-ticket-create-count" class="aksrt-ticket-counter" data-aksrt-ticket-counter="aksrt-ticket-create-content">0 / ' . TicketSettings::content_max() . '</span></label></div>';
|
||||
echo '<label class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('问题描述', 'jio') . '</span><textarea id="jio-ticket-create-content" name="content" rows="7" maxlength="' . TicketSettings::content_max() . '" required data-jio-ticket-editor aria-describedby="jio-ticket-create-count" placeholder="' . esc_attr__('描述问题、发生时间和已尝试的处理方式', 'jio') . '"></textarea>';
|
||||
echo '<span id="jio-ticket-create-count" class="jio-ticket-counter" data-jio-ticket-counter="jio-ticket-create-content">0 / ' . TicketSettings::content_max() . '</span></label></div>';
|
||||
}
|
||||
echo '</div><footer class="aksrt-ticket-dialog__foot">';
|
||||
echo '</div><footer class="jio-ticket-dialog__foot">';
|
||||
if ($types !== []) {
|
||||
echo '<span class="aksrt-ticket-hint">' . esc_html__('新工单状态为「已回复」', 'aksrt') . '</span>';
|
||||
echo '<span class="jio-ticket-hint">' . esc_html__('新工单状态为「已回复」', 'jio') . '</span>';
|
||||
}
|
||||
echo '<button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>';
|
||||
if ($types !== []) {
|
||||
echo '<button type="submit" class="button button-primary">' . esc_html__('创建工单并通知用户', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary">' . esc_html__('创建工单并通知用户', 'jio') . '</button>';
|
||||
}
|
||||
echo '</footer>' . ($types === [] ? '</div>' : '</form>') . '</dialog>';
|
||||
}
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
<?php
|
||||
/** 工单处理工作区(?id=):左侧会话面板(含底部回复框),右侧处理信息与内部备注(弹窗添加)。@package AKSRT */
|
||||
/** 工单处理工作区(?id=):左侧会话面板(含底部回复框),右侧处理信息与内部备注(弹窗添加)。@package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Ticket\TicketNoteService;
|
||||
use AKSRT\Ticket\TicketPrereplyService;
|
||||
use AKSRT\Ticket\TicketRepository;
|
||||
use AKSRT\Ticket\TicketSettings;
|
||||
use AKSRT\Ticket\TicketStatusService;
|
||||
use AKSRT\Ticket\TicketTypeService;
|
||||
use Jio\Ticket\TicketNoteService;
|
||||
use Jio\Ticket\TicketPrereplyService;
|
||||
use Jio\Ticket\TicketRepository;
|
||||
use Jio\Ticket\TicketSettings;
|
||||
use Jio\Ticket\TicketStatusService;
|
||||
use Jio\Ticket\TicketTypeService;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusTicketsDetail {
|
||||
|
||||
private const TICKET_NOTE_DIALOG = 'aksrt-ticket-note-dialog';
|
||||
private const TICKET_NOTE_DIALOG = 'jio-ticket-note-dialog';
|
||||
|
||||
/** 详情按 ID 查询,不受列表筛选影响;$context 只用于「返回工单列表」。 */
|
||||
private function render_ticket_detail_page(int $id, array $context): void {
|
||||
$back = NativeUi::button_link(__('返回工单列表', 'aksrt'), $this->ticket_page_url($context), false, 'dashicons-arrow-left-alt');
|
||||
$back = NativeUi::button_link(__('返回工单列表', 'jio'), $this->ticket_page_url($context), false, 'dashicons-arrow-left-alt');
|
||||
$ticket = TicketRepository::get_instance()->find($id);
|
||||
echo '<div class="aksrt-native-header aksrt-tickets-admin__detail-header"><div class="aksrt-native-header__main"><h1 class="aksrt-native-header__title">';
|
||||
echo '<div class="jio-native-header jio-tickets-admin__detail-header"><div class="jio-native-header__main"><h1 class="jio-native-header__title">';
|
||||
if ($ticket === null) {
|
||||
echo esc_html__('工单详情', 'aksrt') . '</h1></div><div class="aksrt-native-header__actions">' . $back . '</div></div><hr class="wp-header-end">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo NativeUi::empty_state(__('工单不存在', 'aksrt'), __('工单可能已被删除,或链接有误。', 'aksrt'), 'dashicons-search'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo esc_html__('工单详情', 'jio') . '</h1></div><div class="jio-native-header__actions">' . $back . '</div></div><hr class="wp-header-end">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo NativeUi::empty_state(__('工单不存在', 'jio'), __('工单可能已被删除,或链接有误。', 'jio'), 'dashicons-search'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
echo '<span class="aksrt-native-header__icon dashicons dashicons-tickets-alt" aria-hidden="true"></span>' . esc_html((string) $ticket['title']) . '</h1>';
|
||||
echo '<p class="aksrt-native-header__desc"><span>' . esc_html((string) $ticket['ticket_num']) . '</span> ' . $this->ticket_status_badge($ticket) . $this->ticket_urge_badge($ticket) . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '</div><div class="aksrt-native-header__actions">' . $back . '</div></div><hr class="wp-header-end">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="jio-native-header__icon dashicons dashicons-tickets-alt" aria-hidden="true"></span>' . esc_html((string) $ticket['title']) . '</h1>';
|
||||
echo '<p class="jio-native-header__desc"><span>' . esc_html((string) $ticket['ticket_num']) . '</span> ' . $this->ticket_status_badge($ticket) . $this->ticket_urge_badge($ticket) . '</p>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '</div><div class="jio-native-header__actions">' . $back . '</div></div><hr class="wp-header-end">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$this->render_ticket_notice();
|
||||
echo $this->ticket_feedback(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $this->render_ticket_detail_panel($ticket); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
@@ -38,11 +38,11 @@ trait UsersMenusTicketsDetail {
|
||||
private function render_ticket_detail_panel(array $ticket): string {
|
||||
$id = (int) $ticket['id'];
|
||||
$footer = !empty($ticket['is_closed'])
|
||||
? '<p class="aksrt-ticket-detail__closed"><span class="dashicons dashicons-lock" aria-hidden="true"></span>' . esc_html__('工单已关闭,不能继续回复或变更处理信息;仍可添加内部备注。', 'aksrt') . '</p>'
|
||||
? '<p class="jio-ticket-detail__closed"><span class="dashicons dashicons-lock" aria-hidden="true"></span>' . esc_html__('工单已关闭,不能继续回复或变更处理信息;仍可添加内部备注。', 'jio') . '</p>'
|
||||
: $this->ticket_reply_form($id);
|
||||
$html = '<div class="aksrt-ticket-detail" data-aksrt-ticket-detail="' . $id . '"><div class="aksrt-ticket-detail__main">';
|
||||
$html = '<div class="jio-ticket-detail" data-jio-ticket-detail="' . $id . '"><div class="jio-ticket-detail__main">';
|
||||
$html .= $this->ticket_conversation($ticket, TicketRepository::get_instance()->replies($id), $footer);
|
||||
$html .= '</div><aside class="aksrt-ticket-detail__side" aria-label="' . esc_attr__('工单信息与内部备注', 'aksrt') . '">';
|
||||
$html .= '</div><aside class="jio-ticket-detail__side" aria-label="' . esc_attr__('工单信息与内部备注', 'jio') . '">';
|
||||
return $html . $this->ticket_info_card($ticket) . $this->ticket_notes_card($id) . '</aside></div>';
|
||||
}
|
||||
|
||||
@@ -52,48 +52,48 @@ trait UsersMenusTicketsDetail {
|
||||
* 原始问题与回复同属一条时间线:用户在左、客服在右、系统事件居中。
|
||||
*/
|
||||
private function ticket_conversation(array $ticket, array $replies, string $footer): string {
|
||||
$html = '<section class="aksrt-ticket-conversation" aria-label="' . esc_attr__('问题与沟通记录', 'aksrt') . '">';
|
||||
$html .= '<div class="aksrt-ticket-section-head"><h3>' . esc_html__('沟通记录', 'aksrt') . ' <span class="aksrt-ticket-pill">' . esc_html(sprintf(__('%d 条记录', 'aksrt'), count($replies))) . '</span></h3>';
|
||||
$html = '<section class="jio-ticket-conversation" aria-label="' . esc_attr__('问题与沟通记录', 'jio') . '">';
|
||||
$html .= '<div class="jio-ticket-section-head"><h3>' . esc_html__('沟通记录', 'jio') . ' <span class="jio-ticket-pill">' . esc_html(sprintf(__('%d 条记录', 'jio'), count($replies))) . '</span></h3>';
|
||||
if ($replies !== []) {
|
||||
$html .= '<button type="button" class="button-link" data-aksrt-ticket-latest hidden>' . esc_html__('查看最新记录', 'aksrt') . '</button>';
|
||||
$html .= '<button type="button" class="button-link" data-jio-ticket-latest hidden>' . esc_html__('查看最新记录', 'jio') . '</button>';
|
||||
}
|
||||
$html .= '</div><div class="aksrt-ticket-conversation__scroll" tabindex="0" role="region" aria-label="' . esc_attr__('可滚动的沟通记录', 'aksrt') . '"><ol class="aksrt-ticket-detail__thread">';
|
||||
$html .= $this->ticket_message('origin', (int) $ticket['user_id'], (string) $ticket['user_name'], !empty($ticket['post_admin_id']) ? __('代客提交', 'aksrt') : __('提交了工单', 'aksrt'), (string) $ticket['post_time'], (string) $ticket['content'], (array) $ticket['attachment']);
|
||||
$html .= '</div><div class="jio-ticket-conversation__scroll" tabindex="0" role="region" aria-label="' . esc_attr__('可滚动的沟通记录', 'jio') . '"><ol class="jio-ticket-detail__thread">';
|
||||
$html .= $this->ticket_message('origin', (int) $ticket['user_id'], (string) $ticket['user_name'], !empty($ticket['post_admin_id']) ? __('代客提交', 'jio') : __('提交了工单', 'jio'), (string) $ticket['post_time'], (string) $ticket['content'], (array) $ticket['attachment']);
|
||||
if ($replies === []) {
|
||||
$html .= '<li class="aksrt-ticket-detail__empty">' . esc_html__('暂无回复,请核对问题后联系用户。', 'aksrt') . '</li>';
|
||||
$html .= '<li class="jio-ticket-detail__empty">' . esc_html__('暂无回复,请核对问题后联系用户。', 'jio') . '</li>';
|
||||
}
|
||||
foreach ($replies as $reply) {
|
||||
if ((string) $reply['author_type'] === 'system') {
|
||||
$html .= '<li class="aksrt-ticket-detail__event"><span>' . esc_html((string) $reply['content']) . '</span><time>' . esc_html((string) $reply['created_at']) . '</time></li>';
|
||||
$html .= '<li class="jio-ticket-detail__event"><span>' . esc_html((string) $reply['content']) . '</span><time>' . esc_html((string) $reply['created_at']) . '</time></li>';
|
||||
continue;
|
||||
}
|
||||
$admin = (string) $reply['author_type'] === 'admin';
|
||||
$quote_html = '';
|
||||
if (is_array($reply['quote'] ?? null)) {
|
||||
$quote = $reply['quote'];
|
||||
$quote_html = '<blockquote class="aksrt-ticket-detail__quote">' . esc_html(!empty($quote['is_deleted']) ? __('原回复已删除', 'aksrt') : (string) $quote['content']) . '</blockquote>';
|
||||
$quote_html = '<blockquote class="jio-ticket-detail__quote">' . esc_html(!empty($quote['is_deleted']) ? __('原回复已删除', 'jio') : (string) $quote['content']) . '</blockquote>';
|
||||
}
|
||||
$html .= $this->ticket_message($admin ? 'admin' : 'user', (int) $reply['author_id'], (string) $reply['author_name'], $admin ? __('客服', 'aksrt') : __('用户', 'aksrt'), (string) $reply['created_at'], (string) $reply['content'], (array) $reply['attachment'], $quote_html);
|
||||
$html .= $this->ticket_message($admin ? 'admin' : 'user', (int) $reply['author_id'], (string) $reply['author_name'], $admin ? __('客服', 'jio') : __('用户', 'jio'), (string) $reply['created_at'], (string) $reply['content'], (array) $reply['attachment'], $quote_html);
|
||||
}
|
||||
return $html . '</ol></div>' . $footer . '</section>';
|
||||
}
|
||||
|
||||
private function ticket_message(string $variant, int $user_id, string $name, string $role, string $time, string $content, array $attachments, string $quote_html = ''): string {
|
||||
$avatar = function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user_id, 64) : '';
|
||||
return '<li class="aksrt-ticket-detail__message is-' . esc_attr($variant) . '">'
|
||||
. ($avatar !== '' ? '<img class="aksrt-ticket-detail__avatar" src="' . esc_url($avatar) . '" alt="" width="32" height="32">' : '<span class="aksrt-ticket-detail__avatar" aria-hidden="true"></span>')
|
||||
. '<div class="aksrt-ticket-detail__bubble"><header class="aksrt-ticket-detail__meta"><strong>' . esc_html($name !== '' ? $name : '#' . $user_id) . '</strong>'
|
||||
. '<span class="aksrt-ticket-detail__role">' . esc_html($role) . '</span><time>' . esc_html($time) . '</time></header>'
|
||||
. $quote_html . '<div class="aksrt-ticket-detail__body">' . wpautop(esc_html($content)) . '</div>'
|
||||
$avatar = function_exists('jio_get_avatar_url') ? jio_get_avatar_url($user_id, 64) : '';
|
||||
return '<li class="jio-ticket-detail__message is-' . esc_attr($variant) . '">'
|
||||
. ($avatar !== '' ? '<img class="jio-ticket-detail__avatar" src="' . esc_url($avatar) . '" alt="" width="32" height="32">' : '<span class="jio-ticket-detail__avatar" aria-hidden="true"></span>')
|
||||
. '<div class="jio-ticket-detail__bubble"><header class="jio-ticket-detail__meta"><strong>' . esc_html($name !== '' ? $name : '#' . $user_id) . '</strong>'
|
||||
. '<span class="jio-ticket-detail__role">' . esc_html($role) . '</span><time>' . esc_html($time) . '</time></header>'
|
||||
. $quote_html . '<div class="jio-ticket-detail__body">' . wpautop(esc_html($content)) . '</div>'
|
||||
. $this->ticket_attachments_html($attachments) . '</div></li>';
|
||||
}
|
||||
|
||||
private function ticket_reply_form(int $id): string {
|
||||
$field = 'aksrt-ticket-reply-' . $id;
|
||||
$field = 'jio-ticket-reply-' . $id;
|
||||
$tools = '';
|
||||
$prereplies = TicketPrereplyService::all();
|
||||
if ($prereplies !== []) {
|
||||
$tools = '<label class="screen-reader-text" for="' . $field . '-pre">' . esc_html__('插入预设回复', 'aksrt') . '</label><select id="' . $field . '-pre" data-aksrt-ticket-prereply="' . $field . '"><option value="">' . esc_html__('插入预设回复…', 'aksrt') . '</option>';
|
||||
$tools = '<label class="screen-reader-text" for="' . $field . '-pre">' . esc_html__('插入预设回复', 'jio') . '</label><select id="' . $field . '-pre" data-jio-ticket-prereply="' . $field . '"><option value="">' . esc_html__('插入预设回复…', 'jio') . '</option>';
|
||||
foreach ($prereplies as $row) {
|
||||
$tools .= '<option value="' . esc_attr((string) $row['content']) . '">' . esc_html(mb_strimwidth((string) $row['content'], 0, 40, '…')) . '</option>';
|
||||
}
|
||||
@@ -101,13 +101,13 @@ trait UsersMenusTicketsDetail {
|
||||
}
|
||||
$max = TicketSettings::reply_max();
|
||||
// 输入框与工具栏合为一个整体:预设回复、字数与发送按钮同排;Ctrl/⌘ + Enter 发送。
|
||||
return $this->ticket_form_open('reply', $id, 'aksrt-ticket-composer')
|
||||
. '<label class="screen-reader-text" for="' . esc_attr($field) . '">' . esc_html__('回复用户', 'aksrt') . '</label>'
|
||||
. '<div class="aksrt-ticket-composer__box"><textarea id="' . esc_attr($field) . '" name="content" rows="3" maxlength="' . $max . '" required data-aksrt-ticket-editor data-aksrt-ticket-send-shortcut aria-describedby="' . esc_attr($field) . '-count ' . esc_attr($field) . '-hint" placeholder="' . esc_attr__('回复用户,发送后用户将收到通知…', 'aksrt') . '"></textarea>'
|
||||
. '<div class="aksrt-ticket-composer__bar">' . $tools
|
||||
. '<span id="' . esc_attr($field) . '-hint" class="aksrt-ticket-hint aksrt-ticket-composer__shortcut">' . esc_html__('Ctrl + Enter 发送', 'aksrt') . '</span>'
|
||||
. '<span id="' . esc_attr($field) . '-count" class="aksrt-ticket-counter" data-aksrt-ticket-counter="' . esc_attr($field) . '">0 / ' . $max . '</span>'
|
||||
. '<button type="submit" class="button button-primary"><span class="dashicons dashicons-email-alt" aria-hidden="true"></span>' . esc_html__('发送回复', 'aksrt') . '</button></div></div></form>';
|
||||
return $this->ticket_form_open('reply', $id, 'jio-ticket-composer')
|
||||
. '<label class="screen-reader-text" for="' . esc_attr($field) . '">' . esc_html__('回复用户', 'jio') . '</label>'
|
||||
. '<div class="jio-ticket-composer__box"><textarea id="' . esc_attr($field) . '" name="content" rows="3" maxlength="' . $max . '" required data-jio-ticket-editor data-jio-ticket-send-shortcut aria-describedby="' . esc_attr($field) . '-count ' . esc_attr($field) . '-hint" placeholder="' . esc_attr__('回复用户,发送后用户将收到通知…', 'jio') . '"></textarea>'
|
||||
. '<div class="jio-ticket-composer__bar">' . $tools
|
||||
. '<span id="' . esc_attr($field) . '-hint" class="jio-ticket-hint jio-ticket-composer__shortcut">' . esc_html__('Ctrl + Enter 发送', 'jio') . '</span>'
|
||||
. '<span id="' . esc_attr($field) . '-count" class="jio-ticket-counter" data-jio-ticket-counter="' . esc_attr($field) . '">0 / ' . $max . '</span>'
|
||||
. '<button type="submit" class="button button-primary"><span class="dashicons dashicons-email-alt" aria-hidden="true"></span>' . esc_html__('发送回复', 'jio') . '</button></div></div></form>';
|
||||
}
|
||||
|
||||
private function ticket_info_card(array $ticket): string {
|
||||
@@ -115,40 +115,40 @@ trait UsersMenusTicketsDetail {
|
||||
$editable = empty($ticket['is_closed']);
|
||||
$controls = $editable ? $this->ticket_info_controls($ticket) : [];
|
||||
$rows = [
|
||||
[__('工单号', 'aksrt'), esc_html((string) $ticket['ticket_num'])],
|
||||
[__('状态', 'aksrt'), $controls['status'] ?? $this->ticket_status_badge($ticket), 'status'],
|
||||
[__('用户', 'aksrt'), $this->ticket_user_chip((int) $ticket['user_id'], (string) $ticket['user_name'])],
|
||||
[__('部门', 'aksrt'), $controls['ticket_type_id'] ?? esc_html((string) $ticket['ticket_type_name'] !== '' ? (string) $ticket['ticket_type_name'] : '—'), 'ticket_type_id'],
|
||||
[__('跟进人', 'aksrt'), $controls['admin_id'] ?? esc_html((int) $ticket['admin_id'] > 0 ? (string) $ticket['admin_name'] : __('未指派', 'aksrt')), 'admin_id'],
|
||||
[__('工单号', 'jio'), esc_html((string) $ticket['ticket_num'])],
|
||||
[__('状态', 'jio'), $controls['status'] ?? $this->ticket_status_badge($ticket), 'status'],
|
||||
[__('用户', 'jio'), $this->ticket_user_chip((int) $ticket['user_id'], (string) $ticket['user_name'])],
|
||||
[__('部门', 'jio'), $controls['ticket_type_id'] ?? esc_html((string) $ticket['ticket_type_name'] !== '' ? (string) $ticket['ticket_type_name'] : '—'), 'ticket_type_id'],
|
||||
[__('跟进人', 'jio'), $controls['admin_id'] ?? esc_html((int) $ticket['admin_id'] > 0 ? (string) $ticket['admin_name'] : __('未指派', 'jio')), 'admin_id'],
|
||||
];
|
||||
if ((string) $ticket['order_number'] !== '') {
|
||||
$rows[] = [__('关联订单', 'aksrt'), esc_html((string) $ticket['order_number'])];
|
||||
$rows[] = [__('关联订单', 'jio'), esc_html((string) $ticket['order_number'])];
|
||||
}
|
||||
$rows[] = [__('提交时间', 'aksrt'), esc_html((string) $ticket['post_time'])];
|
||||
$rows[] = [__('提交时间', 'jio'), esc_html((string) $ticket['post_time'])];
|
||||
if ((int) $ticket['urge_count'] > 0) {
|
||||
$rows[] = [__('催单', 'aksrt'), '<span class="aksrt-ticket-urge">' . esc_html(sprintf(__('%1$d 次,最近 %2$s', 'aksrt'), (int) $ticket['urge_count'], (string) $ticket['last_urge_time'])) . '</span>'];
|
||||
$rows[] = [__('催单', 'jio'), '<span class="jio-ticket-urge">' . esc_html(sprintf(__('%1$d 次,最近 %2$s', 'jio'), (int) $ticket['urge_count'], (string) $ticket['last_urge_time'])) . '</span>'];
|
||||
}
|
||||
$html = '<section class="aksrt-ticket-detail__card"><div class="aksrt-ticket-section-head"><h3>' . esc_html__('工单信息', 'aksrt') . '</h3>';
|
||||
$html = '<section class="jio-ticket-detail__card"><div class="jio-ticket-section-head"><h3>' . esc_html__('工单信息', 'jio') . '</h3>';
|
||||
if ($editable) {
|
||||
$html .= '<button type="button" class="button-link" data-aksrt-ticket-assign>' . esc_html__('由我跟进', 'aksrt') . '</button>';
|
||||
$html .= '<button type="button" class="button-link" data-jio-ticket-assign>' . esc_html__('由我跟进', 'jio') . '</button>';
|
||||
}
|
||||
$html .= '</div>';
|
||||
if ($editable) {
|
||||
$html .= $this->ticket_form_open('status', $id, 'aksrt-ticket-detail__status-form');
|
||||
$html .= $this->ticket_form_open('status', $id, 'jio-ticket-detail__status-form');
|
||||
}
|
||||
$html .= '<dl class="aksrt-ticket-detail__info">';
|
||||
$html .= '<dl class="jio-ticket-detail__info">';
|
||||
foreach ($rows as $row) {
|
||||
[$label, $value] = $row;
|
||||
$label_html = esc_html($label);
|
||||
if ($editable && isset($row[2])) {
|
||||
$label_html = '<label for="aksrt-ticket-' . esc_attr($row[2]) . '-' . $id . '">' . $label_html . '</label>';
|
||||
$label_html = '<label for="jio-ticket-' . esc_attr($row[2]) . '-' . $id . '">' . $label_html . '</label>';
|
||||
}
|
||||
$html .= '<dt>' . $label_html . '</dt><dd>' . $value . '</dd>';
|
||||
}
|
||||
$html .= '</dl>';
|
||||
if ($editable) {
|
||||
$html .= '<div class="aksrt-ticket-close-warning" data-aksrt-ticket-close-warning hidden><label><input type="checkbox" data-aksrt-ticket-close-confirm> <span>' . esc_html__('确认关闭工单:关闭后无法再次回复,尚未发送的回复不会保存。', 'aksrt') . '</span></label></div>';
|
||||
$html .= '<button type="submit" class="button">' . esc_html__('保存工单信息', 'aksrt') . '</button></form>';
|
||||
$html .= '<div class="jio-ticket-close-warning" data-jio-ticket-close-warning hidden><label><input type="checkbox" data-jio-ticket-close-confirm> <span>' . esc_html__('确认关闭工单:关闭后无法再次回复,尚未发送的回复不会保存。', 'jio') . '</span></label></div>';
|
||||
$html .= '<button type="submit" class="button">' . esc_html__('保存工单信息', 'jio') . '</button></form>';
|
||||
}
|
||||
return $html . '</section>';
|
||||
}
|
||||
@@ -157,7 +157,7 @@ trait UsersMenusTicketsDetail {
|
||||
private function ticket_info_controls(array $ticket): array {
|
||||
$id = (int) $ticket['id'];
|
||||
$from = (string) $ticket['status'];
|
||||
$controls = ['status' => '<select id="aksrt-ticket-status-' . $id . '" name="status" data-aksrt-ticket-status>'];
|
||||
$controls = ['status' => '<select id="jio-ticket-status-' . $id . '" name="status" data-jio-ticket-status>'];
|
||||
foreach (TicketStatusService::map() as $row) {
|
||||
$key = (string) $row['status_key'];
|
||||
if (TicketStatusService::can_transition($from, $key)) {
|
||||
@@ -165,19 +165,19 @@ trait UsersMenusTicketsDetail {
|
||||
}
|
||||
}
|
||||
$controls['status'] .= '</select>';
|
||||
$controls['ticket_type_id'] = '<select id="aksrt-ticket-ticket_type_id-' . $id . '" name="ticket_type_id" required>';
|
||||
$controls['ticket_type_id'] = '<select id="jio-ticket-ticket_type_id-' . $id . '" name="ticket_type_id" required>';
|
||||
foreach (TicketTypeService::all() as $row) {
|
||||
$controls['ticket_type_id'] .= '<option value="' . (int) $row['id'] . '"' . selected((int) $ticket['ticket_type_id'], (int) $row['id'], false) . '>' . esc_html((string) $row['name']) . '</option>';
|
||||
}
|
||||
$controls['ticket_type_id'] .= '</select>';
|
||||
$controls['admin_id'] = '<select id="aksrt-ticket-admin_id-' . $id . '" name="admin_id"><option value="0">' . esc_html__('未指派', 'aksrt') . '</option>';
|
||||
$controls['admin_id'] = '<select id="jio-ticket-admin_id-' . $id . '" name="admin_id"><option value="0">' . esc_html__('未指派', 'jio') . '</option>';
|
||||
$current = (int) $ticket['admin_id'];
|
||||
$admins = $this->ticket_admin_options();
|
||||
if ($current > 0 && !isset($admins[$current])) {
|
||||
$admins[$current] = (string) $ticket['admin_name'];
|
||||
}
|
||||
foreach ($admins as $admin_id => $name) {
|
||||
$controls['admin_id'] .= '<option value="' . $admin_id . '"' . selected($current, $admin_id, false) . '>' . esc_html($admin_id === get_current_user_id() ? sprintf(__('%s(我)', 'aksrt'), $name) : $name) . '</option>';
|
||||
$controls['admin_id'] .= '<option value="' . $admin_id . '"' . selected($current, $admin_id, false) . '>' . esc_html($admin_id === get_current_user_id() ? sprintf(__('%s(我)', 'jio'), $name) : $name) . '</option>';
|
||||
}
|
||||
$controls['admin_id'] .= '</select>';
|
||||
return $controls;
|
||||
@@ -203,42 +203,42 @@ trait UsersMenusTicketsDetail {
|
||||
private function ticket_notes_card(int $id): string {
|
||||
$notes = TicketNoteService::for_ticket($id);
|
||||
$visible = 3;
|
||||
$html = '<section class="aksrt-ticket-detail__card aksrt-ticket-detail__private"><div class="aksrt-ticket-section-head"><h3>' . esc_html__('内部备注', 'aksrt');
|
||||
$html = '<section class="jio-ticket-detail__card jio-ticket-detail__private"><div class="jio-ticket-section-head"><h3>' . esc_html__('内部备注', 'jio');
|
||||
if ($notes !== []) {
|
||||
$html .= ' <span class="aksrt-ticket-pill">' . count($notes) . '</span>';
|
||||
$html .= ' <span class="jio-ticket-pill">' . count($notes) . '</span>';
|
||||
}
|
||||
$html .= '</h3><button type="button" class="button button-small" data-aksrt-dialog-open="' . esc_attr(self::TICKET_NOTE_DIALOG) . '" aria-haspopup="dialog" aria-controls="' . esc_attr(self::TICKET_NOTE_DIALOG) . '">'
|
||||
. '<span class="dashicons dashicons-plus-alt2" aria-hidden="true"></span>' . esc_html__('添加备注', 'aksrt') . '</button></div>';
|
||||
$html .= '<p class="aksrt-ticket-hint">' . esc_html__('仅管理员可见,不会发送给用户。', 'aksrt') . '</p>';
|
||||
$html .= '</h3><button type="button" class="button button-small" data-jio-dialog-open="' . esc_attr(self::TICKET_NOTE_DIALOG) . '" aria-haspopup="dialog" aria-controls="' . esc_attr(self::TICKET_NOTE_DIALOG) . '">'
|
||||
. '<span class="dashicons dashicons-plus-alt2" aria-hidden="true"></span>' . esc_html__('添加备注', 'jio') . '</button></div>';
|
||||
$html .= '<p class="jio-ticket-hint">' . esc_html__('仅管理员可见,不会发送给用户。', 'jio') . '</p>';
|
||||
if ($notes === []) {
|
||||
return $html . '<p class="aksrt-ticket-detail__notes-empty">' . esc_html__('暂无备注', 'aksrt') . '</p></section>';
|
||||
return $html . '<p class="jio-ticket-detail__notes-empty">' . esc_html__('暂无备注', 'jio') . '</p></section>';
|
||||
}
|
||||
$older = array_slice($notes, 0, -$visible);
|
||||
$html .= '<div class="aksrt-ticket-detail__notes">';
|
||||
$html .= '<div class="jio-ticket-detail__notes">';
|
||||
if ($older !== []) {
|
||||
$html .= '<details class="aksrt-ticket-detail__notes-older"><summary>' . esc_html(sprintf(__('更早的 %d 条备注', 'aksrt'), count($older))) . '</summary>' . $this->ticket_notes_list($older) . '</details>';
|
||||
$html .= '<details class="jio-ticket-detail__notes-older"><summary>' . esc_html(sprintf(__('更早的 %d 条备注', 'jio'), count($older))) . '</summary>' . $this->ticket_notes_list($older) . '</details>';
|
||||
}
|
||||
return $html . $this->ticket_notes_list(array_slice($notes, -$visible)) . '</div></section>';
|
||||
}
|
||||
|
||||
private function ticket_note_dialog(int $id): string {
|
||||
$field = 'aksrt-ticket-note-' . $id;
|
||||
$field = 'jio-ticket-note-' . $id;
|
||||
$max = TicketSettings::reply_max();
|
||||
return '<dialog id="' . esc_attr(self::TICKET_NOTE_DIALOG) . '" class="aksrt-ticket-dialog aksrt-ticket-dialog--note" aria-labelledby="aksrt-ticket-note-title" aria-describedby="aksrt-ticket-note-description">'
|
||||
. $this->ticket_form_open('note', $id, 'aksrt-ticket-dialog__frame')
|
||||
. $this->ticket_dialog_head('note', __('添加内部备注', 'aksrt'), __('仅管理员可见,不会发送给用户。', 'aksrt'))
|
||||
. '<div class="aksrt-ticket-dialog__body">' . $this->ticket_feedback()
|
||||
. '<label class="screen-reader-text" for="' . esc_attr($field) . '">' . esc_html__('备注内容', 'aksrt') . '</label>'
|
||||
. '<textarea id="' . esc_attr($field) . '" name="content" rows="6" maxlength="' . $max . '" required autofocus data-aksrt-ticket-editor aria-describedby="' . esc_attr($field) . '-count" placeholder="' . esc_attr__('记录处理进展、排查结论…', 'aksrt') . '"></textarea>'
|
||||
. '<span id="' . esc_attr($field) . '-count" class="aksrt-ticket-counter aksrt-ticket-dialog__counter" data-aksrt-ticket-counter="' . esc_attr($field) . '">0 / ' . $max . '</span></div>'
|
||||
. '<footer class="aksrt-ticket-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close>' . esc_html__('取消', 'aksrt') . '</button>'
|
||||
. '<button type="submit" class="button button-primary">' . esc_html__('保存备注', 'aksrt') . '</button></footer></form></dialog>';
|
||||
return '<dialog id="' . esc_attr(self::TICKET_NOTE_DIALOG) . '" class="jio-ticket-dialog jio-ticket-dialog--note" aria-labelledby="jio-ticket-note-title" aria-describedby="jio-ticket-note-description">'
|
||||
. $this->ticket_form_open('note', $id, 'jio-ticket-dialog__frame')
|
||||
. $this->ticket_dialog_head('note', __('添加内部备注', 'jio'), __('仅管理员可见,不会发送给用户。', 'jio'))
|
||||
. '<div class="jio-ticket-dialog__body">' . $this->ticket_feedback()
|
||||
. '<label class="screen-reader-text" for="' . esc_attr($field) . '">' . esc_html__('备注内容', 'jio') . '</label>'
|
||||
. '<textarea id="' . esc_attr($field) . '" name="content" rows="6" maxlength="' . $max . '" required autofocus data-jio-ticket-editor aria-describedby="' . esc_attr($field) . '-count" placeholder="' . esc_attr__('记录处理进展、排查结论…', 'jio') . '"></textarea>'
|
||||
. '<span id="' . esc_attr($field) . '-count" class="jio-ticket-counter jio-ticket-dialog__counter" data-jio-ticket-counter="' . esc_attr($field) . '">0 / ' . $max . '</span></div>'
|
||||
. '<footer class="jio-ticket-dialog__foot"><button type="button" class="button" data-jio-dialog-close>' . esc_html__('取消', 'jio') . '</button>'
|
||||
. '<button type="submit" class="button button-primary">' . esc_html__('保存备注', 'jio') . '</button></footer></form></dialog>';
|
||||
}
|
||||
|
||||
private function ticket_notes_list(array $notes): string {
|
||||
$html = '<ol>';
|
||||
foreach ($notes as $note) {
|
||||
$html .= '<li><div class="aksrt-ticket-detail__note-head"><strong>' . esc_html((string) $note['admin_name']) . '</strong><time>' . esc_html((string) $note['created_at']) . '</time></div><div>' . nl2br(esc_html((string) $note['content'])) . '</div></li>';
|
||||
$html .= '<li><div class="jio-ticket-detail__note-head"><strong>' . esc_html((string) $note['admin_name']) . '</strong><time>' . esc_html((string) $note['created_at']) . '</time></div><div>' . nl2br(esc_html((string) $note['content'])) . '</div></li>';
|
||||
}
|
||||
return $html . '</ol>';
|
||||
}
|
||||
@@ -247,9 +247,9 @@ trait UsersMenusTicketsDetail {
|
||||
if ($urls === []) {
|
||||
return '';
|
||||
}
|
||||
$html = '<ul class="aksrt-ticket-detail__attachments">';
|
||||
$html = '<ul class="jio-ticket-detail__attachments">';
|
||||
foreach ($urls as $index => $url) {
|
||||
$html .= '<li><a href="' . esc_url((string) $url) . '" target="_blank" rel="noopener noreferrer"><img src="' . esc_url((string) $url) . '" alt="' . esc_attr(sprintf(__('查看附件 %d(新窗口)', 'aksrt'), $index + 1)) . '"></a></li>';
|
||||
$html .= '<li><a href="' . esc_url((string) $url) . '" target="_blank" rel="noopener noreferrer"><img src="' . esc_url((string) $url) . '" alt="' . esc_attr(sprintf(__('查看附件 %d(新窗口)', 'jio'), $index + 1)) . '"></a></li>';
|
||||
}
|
||||
return $html . '</ul>';
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
/** 工单筛选与列表;标题与操作直接链接到处理工作区(?id=)。@package AKSRT */
|
||||
/** 工单筛选与列表;标题与操作直接链接到处理工作区(?id=)。@package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Ticket\TicketRepository;
|
||||
use AKSRT\Ticket\TicketStatusService;
|
||||
use AKSRT\Ticket\TicketTypeService;
|
||||
use Jio\Ticket\TicketRepository;
|
||||
use Jio\Ticket\TicketStatusService;
|
||||
use Jio\Ticket\TicketTypeService;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -15,19 +15,19 @@ trait UsersMenusTicketsList {
|
||||
// 先查询并夹取页码,创建成功后跳转的工作区链接才能带上实际页码。
|
||||
[$result, $paged] = $this->ticket_list_result($filters, $paged);
|
||||
NativeUi::header(
|
||||
__('工单管理', 'aksrt'),
|
||||
__('查看用户问题、跟进沟通并记录处理结果。', 'aksrt'),
|
||||
__('工单管理', 'jio'),
|
||||
__('查看用户问题、跟进沟通并记录处理结果。', 'jio'),
|
||||
'dashicons-tickets-alt',
|
||||
[
|
||||
[
|
||||
'label' => __('代客提交工单', 'aksrt'),
|
||||
'label' => __('代客提交工单', 'jio'),
|
||||
'dialog' => self::TICKET_CREATE_DIALOG,
|
||||
'primary' => true,
|
||||
'icon' => 'dashicons-plus-alt2',
|
||||
],
|
||||
[
|
||||
'label' => __('工单配置', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/users/ticket'),
|
||||
'label' => __('工单配置', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/users/ticket'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
],
|
||||
]
|
||||
@@ -57,27 +57,27 @@ trait UsersMenusTicketsList {
|
||||
}
|
||||
|
||||
private function render_ticket_list(array $filters, int $paged, array $result): void {
|
||||
NativeUi::card_open('', '', true, 'aksrt-tickets-admin__list-card');
|
||||
NativeUi::card_open('', '', true, 'jio-tickets-admin__list-card');
|
||||
$this->render_ticket_toolbar($filters);
|
||||
echo '<div class="aksrt-native-table-wrap"><table class="widefat striped aksrt-tickets-admin__table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('工单处理列表', 'aksrt') . '</caption><thead><tr>';
|
||||
echo '<div class="jio-native-table-wrap"><table class="widefat striped jio-tickets-admin__table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('工单处理列表', 'jio') . '</caption><thead><tr>';
|
||||
foreach ([
|
||||
'title' => __('工单 / 问题', 'aksrt'),
|
||||
'status' => __('状态', 'aksrt'),
|
||||
'type' => __('部门', 'aksrt'),
|
||||
'user' => __('用户', 'aksrt'),
|
||||
'admin' => __('跟进人', 'aksrt'),
|
||||
'time' => __('最后活动', 'aksrt'),
|
||||
'actions' => __('操作', 'aksrt'),
|
||||
'title' => __('工单 / 问题', 'jio'),
|
||||
'status' => __('状态', 'jio'),
|
||||
'type' => __('部门', 'jio'),
|
||||
'user' => __('用户', 'jio'),
|
||||
'admin' => __('跟进人', 'jio'),
|
||||
'time' => __('最后活动', 'jio'),
|
||||
'actions' => __('操作', 'jio'),
|
||||
] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($result['items'] === []) {
|
||||
$filtered = $filters['status'] !== '' || $filters['ticket_type_id'] > 0 || $filters['s'] !== '';
|
||||
echo NativeUi::empty_row(7, __('暂无工单', 'aksrt'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('请调整筛选条件,或使用标题右侧的「代客提交工单」。', 'aksrt')
|
||||
: __('暂无未关闭工单。可切换状态查看历史记录,或代用户创建工单。', 'aksrt'));
|
||||
echo NativeUi::empty_row(7, __('暂无工单', 'jio'), $filtered // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
? __('请调整筛选条件,或使用标题右侧的「代客提交工单」。', 'jio')
|
||||
: __('暂无未关闭工单。可切换状态查看历史记录,或代用户创建工单。', 'jio'));
|
||||
}
|
||||
$context = $this->ticket_context($filters, $paged);
|
||||
foreach ($result['items'] as $ticket) {
|
||||
@@ -106,13 +106,13 @@ trait UsersMenusTicketsList {
|
||||
}
|
||||
$views[] = [$key, (string) $row['name'], $count, (string) $row['color']];
|
||||
}
|
||||
array_unshift($views, ['', __('未关闭', 'aksrt'), $open_total, '']);
|
||||
echo '<div class="aksrt-tickets-admin__toolbar"><h2 class="aksrt-native-card__title">' . esc_html__('工单列表', 'aksrt') . '</h2>';
|
||||
echo '<nav aria-label="' . esc_attr__('按工单状态筛选', 'aksrt') . '"><ul class="subsubsub">';
|
||||
array_unshift($views, ['', __('未关闭', 'jio'), $open_total, '']);
|
||||
echo '<div class="jio-tickets-admin__toolbar"><h2 class="jio-native-card__title">' . esc_html__('工单列表', 'jio') . '</h2>';
|
||||
echo '<nav aria-label="' . esc_attr__('按工单状态筛选', 'jio') . '"><ul class="subsubsub">';
|
||||
foreach ($views as [$key, $label, $count, $color]) {
|
||||
echo '<li><a href="' . esc_url($this->ticket_page_url(array_merge($filters, ['status' => $key]))) . '"' . ($filters['status'] === $key ? ' class="current" aria-current="page"' : '') . '>';
|
||||
if ($color !== '') {
|
||||
echo '<span class="aksrt-ticket-dot" aria-hidden="true" style="--aksrt-ticket-color:' . esc_attr($color) . '"></span>';
|
||||
echo '<span class="jio-ticket-dot" aria-hidden="true" style="--jio-ticket-color:' . esc_attr($color) . '"></span>';
|
||||
}
|
||||
echo esc_html($label) . ' <span class="count">' . esc_html(number_format_i18n($count)) . '</span></a></li>';
|
||||
}
|
||||
@@ -122,17 +122,17 @@ trait UsersMenusTicketsList {
|
||||
}
|
||||
|
||||
private function render_ticket_filter_form(array $filters): void {
|
||||
echo '<form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::TICKET_PAGE_SLUG) . '"><input type="hidden" name="status" value="' . esc_attr($filters['status']) . '">';
|
||||
echo '<label class="screen-reader-text" for="aksrt-ticket-filter-type">' . esc_html__('按部门筛选', 'aksrt') . '</label><select name="ticket_type_id" id="aksrt-ticket-filter-type"><option value="">' . esc_html__('全部部门', 'aksrt') . '</option>';
|
||||
echo '<label class="screen-reader-text" for="jio-ticket-filter-type">' . esc_html__('按部门筛选', 'jio') . '</label><select name="ticket_type_id" id="jio-ticket-filter-type"><option value="">' . esc_html__('全部部门', 'jio') . '</option>';
|
||||
foreach (TicketTypeService::all() as $row) {
|
||||
echo '<option value="' . (int) $row['id'] . '"' . selected($filters['ticket_type_id'], (int) $row['id'], false) . '>' . esc_html((string) $row['name']) . '</option>';
|
||||
}
|
||||
echo '</select><label class="screen-reader-text" for="aksrt-ticket-filter-s">' . esc_html__('搜索工单', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-ticket-filter-s" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('工单号 / 标题', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '</select><label class="screen-reader-text" for="jio-ticket-filter-s">' . esc_html__('搜索工单', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-ticket-filter-s" name="s" value="' . esc_attr($filters['s']) . '" placeholder="' . esc_attr__('工单号 / 标题', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if ($filters['ticket_type_id'] > 0 || $filters['s'] !== '') {
|
||||
echo '<a class="button-link" href="' . esc_url($this->ticket_page_url(['status' => $filters['status']])) . '">' . esc_html__('清除筛选', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this->ticket_page_url(['status' => $filters['status']])) . '">' . esc_html__('清除筛选', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form>';
|
||||
}
|
||||
@@ -142,22 +142,22 @@ trait UsersMenusTicketsList {
|
||||
$url = esc_url($this->ticket_page_url(['id' => $id] + $context));
|
||||
$last = (string) $ticket['last_reply_time'] !== '' ? (string) $ticket['last_reply_time'] : (string) $ticket['post_time'];
|
||||
$awaiting = in_array((string) $ticket['status'], [TicketStatusService::KEY_PENDING, TicketStatusService::KEY_USER_REPLIED], true);
|
||||
echo '<tr class="aksrt-tickets-admin__row' . ($awaiting ? ' is-awaiting' : '') . '">';
|
||||
echo '<tr class="jio-tickets-admin__row' . ($awaiting ? ' is-awaiting' : '') . '">';
|
||||
echo '<td class="column-title"><a class="row-title" href="' . $url . '">' . esc_html((string) $ticket['title']) . '</a>' . $this->ticket_urge_badge($ticket); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="aksrt-tickets-admin__sub">' . esc_html((string) $ticket['ticket_num']) . '</div>';
|
||||
echo '<div class="jio-tickets-admin__sub">' . esc_html((string) $ticket['ticket_num']) . '</div>';
|
||||
if ((string) $ticket['order_number'] !== '') {
|
||||
echo '<div class="aksrt-tickets-admin__sub">' . esc_html__('订单:', 'aksrt') . esc_html((string) $ticket['order_number']) . '</div>';
|
||||
echo '<div class="jio-tickets-admin__sub">' . esc_html__('订单:', 'jio') . esc_html((string) $ticket['order_number']) . '</div>';
|
||||
}
|
||||
echo '</td><td class="column-status">' . $this->ticket_status_badge($ticket) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<td class="column-type">' . esc_html((string) $ticket['ticket_type_name'] !== '' ? (string) $ticket['ticket_type_name'] : '—') . '</td>';
|
||||
echo '<td class="column-user">' . $this->ticket_user_chip((int) $ticket['user_id'], (string) $ticket['user_name']) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<td class="column-admin">' . ((int) $ticket['admin_id'] > 0 ? esc_html((string) $ticket['admin_name']) : '<span class="aksrt-ticket-hint">' . esc_html__('未指派', 'aksrt') . '</span>') . '</td>';
|
||||
echo '<td class="column-time"><time>' . esc_html($last) . '</time><div class="aksrt-tickets-admin__sub">' . esc_html(sprintf(__('提交于 %s', 'aksrt'), (string) $ticket['post_time'])) . '</div></td>';
|
||||
echo '<td class="column-actions"><a class="button' . ($awaiting ? ' button-primary' : '') . '" href="' . $url . '">' . esc_html(!empty($ticket['is_closed']) ? __('查看记录', 'aksrt') : __('处理工单', 'aksrt')) . '</a></td></tr>';
|
||||
echo '<td class="column-admin">' . ((int) $ticket['admin_id'] > 0 ? esc_html((string) $ticket['admin_name']) : '<span class="jio-ticket-hint">' . esc_html__('未指派', 'jio') . '</span>') . '</td>';
|
||||
echo '<td class="column-time"><time>' . esc_html($last) . '</time><div class="jio-tickets-admin__sub">' . esc_html(sprintf(__('提交于 %s', 'jio'), (string) $ticket['post_time'])) . '</div></td>';
|
||||
echo '<td class="column-actions"><a class="button' . ($awaiting ? ' button-primary' : '') . '" href="' . $url . '">' . esc_html(!empty($ticket['is_closed']) ? __('查看记录', 'jio') : __('处理工单', 'jio')) . '</a></td></tr>';
|
||||
}
|
||||
|
||||
private function ticket_status_badge(array $ticket): string {
|
||||
return '<span class="aksrt-ticket-badge" style="--aksrt-ticket-color:' . esc_attr((string) $ticket['status_color']) . '">' . esc_html((string) $ticket['status_label']) . '</span>';
|
||||
return '<span class="jio-ticket-badge" style="--jio-ticket-color:' . esc_attr((string) $ticket['status_color']) . '">' . esc_html((string) $ticket['status_label']) . '</span>';
|
||||
}
|
||||
|
||||
private function ticket_urge_badge(array $ticket): string {
|
||||
@@ -165,13 +165,13 @@ trait UsersMenusTicketsList {
|
||||
if ($count <= 0 || !empty($ticket['is_closed'])) {
|
||||
return '';
|
||||
}
|
||||
return ' <span class="aksrt-ticket-urge" title="' . esc_attr(sprintf(__('最近催单:%s', 'aksrt'), (string) $ticket['last_urge_time'])) . '">' . esc_html(sprintf(__('催单 ×%d', 'aksrt'), $count)) . '</span>';
|
||||
return ' <span class="jio-ticket-urge" title="' . esc_attr(sprintf(__('最近催单:%s', 'jio'), (string) $ticket['last_urge_time'])) . '">' . esc_html(sprintf(__('催单 ×%d', 'jio'), $count)) . '</span>';
|
||||
}
|
||||
|
||||
private function ticket_user_chip(int $user_id, string $name): string {
|
||||
$avatar = function_exists('aksrt_get_avatar_url') ? aksrt_get_avatar_url($user_id, 48) : '';
|
||||
$avatar = function_exists('jio_get_avatar_url') ? jio_get_avatar_url($user_id, 48) : '';
|
||||
$label = $name !== '' ? $name : '#' . $user_id;
|
||||
return '<a class="aksrt-ticket-user" href="' . esc_url(get_edit_user_link($user_id)) . '" target="_blank" rel="noopener noreferrer" aria-label="' . esc_attr(sprintf(__('%s 的用户资料(新窗口)', 'aksrt'), $label)) . '">'
|
||||
return '<a class="jio-ticket-user" href="' . esc_url(get_edit_user_link($user_id)) . '" target="_blank" rel="noopener noreferrer" aria-label="' . esc_attr(sprintf(__('%s 的用户资料(新窗口)', 'jio'), $label)) . '">'
|
||||
. ($avatar !== '' ? '<img src="' . esc_url($avatar) . '" alt="" width="24" height="24">' : '')
|
||||
. '<span>' . esc_html($label) . '</span></a>';
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
/** Native WordPress identity application review. @package AKSRT */
|
||||
/** Native WordPress identity application review. @package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
use AKSRT\Identity\ApplicationRepository;
|
||||
use AKSRT\Identity\EvidenceService;
|
||||
use AKSRT\Identity\EventRepository;
|
||||
use AKSRT\User\AuthApply;
|
||||
use Jio\Identity\ApplicationRepository;
|
||||
use Jio\Identity\EvidenceService;
|
||||
use Jio\Identity\EventRepository;
|
||||
use Jio\User\AuthApply;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,16 +17,16 @@ trait UsersMenusVerify {
|
||||
private const VERIFY_STATUSES = ['pending', 'approved', 'rejected', ''];
|
||||
|
||||
public function enqueue_verify_assets(string $hook): void {
|
||||
if ($hook !== 'users_page_aksrt_user_auth') return;
|
||||
if ($hook !== 'users_page_jio_user_auth') return;
|
||||
$css = '/assets/css/admin/user-auth.css';
|
||||
$js = '/assets/js/admin-user-auth.js';
|
||||
wp_enqueue_style('aksrt-admin-user-auth', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
wp_enqueue_script('aksrt-admin-user-auth', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
wp_enqueue_style('jio-admin-user-auth', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
wp_enqueue_script('jio-admin-user-auth', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
|
||||
private function count_verify_status(string $status): int {
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('identity_application');
|
||||
$table = \Jio\Core\DBMigrator::table('identity_application');
|
||||
return (int) $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$table} WHERE type = %s AND channel = %s AND status = %s",
|
||||
'general', 'manual', $status === 'pending' ? 'manual_pending' : $status
|
||||
@@ -38,23 +38,23 @@ trait UsersMenusVerify {
|
||||
$filters = $this->get_verify_filters($_GET);
|
||||
$data = $this->query_verify_requests($filters, max(1, (int) ($_GET['paged'] ?? 1)));
|
||||
$counts = $this->get_verify_status_counts($filters);
|
||||
NativeUi::page_open('aksrt-verify-page');
|
||||
NativeUi::page_open('jio-verify-page');
|
||||
NativeUi::header(
|
||||
__('身份认证', 'aksrt'),
|
||||
__('查看申请资料与证明材料,在审核弹窗中确认处理结果。', 'aksrt'),
|
||||
__('身份认证', 'jio'),
|
||||
__('查看申请资料与证明材料,在审核弹窗中确认处理结果。', 'jio'),
|
||||
'dashicons-id',
|
||||
[['label' => __('认证设置', 'aksrt'), 'url' => admin_url('admin.php?page=aksrt-settings#/users/verify'), 'icon' => 'dashicons-admin-settings']]
|
||||
[['label' => __('认证设置', 'jio'), 'url' => admin_url('admin.php?page=jio-settings#/users/verify'), 'icon' => 'dashicons-admin-settings']]
|
||||
);
|
||||
$this->flash();
|
||||
NativeUi::card_open('', '', true, 'aksrt-verify-list-card');
|
||||
NativeUi::card_open('', '', true, 'jio-verify-list-card');
|
||||
$this->render_verify_filters($filters, $counts);
|
||||
echo '<div class="aksrt-native-table-wrap">';
|
||||
echo '<div class="jio-native-table-wrap">';
|
||||
$this->render_verify_table($data['items'], $filters, $data['page']);
|
||||
NativeUi::table_wrap_close();
|
||||
AdminListPagination::render('aksrt_user_auth', $data['total'], $data['page'], self::VERIFY_PER_PAGE, $filters, 'users.php');
|
||||
AdminListPagination::render('jio_user_auth', $data['total'], $data['page'], self::VERIFY_PER_PAGE, $filters, 'users.php');
|
||||
NativeUi::card_close();
|
||||
echo '<dialog id="aksrt-verify-dialog" class="aksrt-verify-dialog" aria-labelledby="aksrt-verify-dialog-title" aria-describedby="aksrt-verify-dialog-description"></dialog>';
|
||||
echo '<noscript><p class="aksrt-native-callout">' . esc_html__('请启用 JavaScript 后查看申请资料和处理审核。', 'aksrt') . '</p></noscript>';
|
||||
echo '<dialog id="jio-verify-dialog" class="jio-verify-dialog" aria-labelledby="jio-verify-dialog-title" aria-describedby="jio-verify-dialog-description"></dialog>';
|
||||
echo '<noscript><p class="jio-native-callout">' . esc_html__('请启用 JavaScript 后查看申请资料和处理审核。', 'jio') . '</p></noscript>';
|
||||
NativeUi::page_close();
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ trait UsersMenusVerify {
|
||||
/** @return array{0:string,1:array} */
|
||||
private function verify_query_base(array $filters): array {
|
||||
global $wpdb;
|
||||
$table = \AKSRT\Core\DBMigrator::table('identity_application');
|
||||
$table = \Jio\Core\DBMigrator::table('identity_application');
|
||||
$where = ['a.type = %s', 'a.channel = %s'];
|
||||
$params = ['general', 'manual'];
|
||||
if ($filters['status'] !== '') {
|
||||
@@ -128,38 +128,38 @@ trait UsersMenusVerify {
|
||||
}
|
||||
|
||||
private function verify_page_url(array $args = []): string {
|
||||
return add_query_arg(array_merge(['page' => 'aksrt_user_auth'], $args), admin_url('users.php'));
|
||||
return add_query_arg(array_merge(['page' => 'jio_user_auth'], $args), admin_url('users.php'));
|
||||
}
|
||||
|
||||
private function render_verify_filters(array $filters, array $counts): void {
|
||||
echo '<div class="aksrt-verify-toolbar"><div class="aksrt-verify-toolbar__views">';
|
||||
echo '<h2 class="aksrt-native-card__title">' . esc_html__('认证申请', 'aksrt') . '</h2>';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('认证状态筛选', 'aksrt') . '">';
|
||||
foreach (['pending' => __('待审核', 'aksrt'), 'approved' => __('已通过', 'aksrt'), 'rejected' => __('已拒绝', 'aksrt'), '' => __('全部', 'aksrt')] as $key => $label) {
|
||||
echo '<div class="jio-verify-toolbar"><div class="jio-verify-toolbar__views">';
|
||||
echo '<h2 class="jio-native-card__title">' . esc_html__('认证申请', 'jio') . '</h2>';
|
||||
echo '<ul class="subsubsub" aria-label="' . esc_attr__('认证状态筛选', 'jio') . '">';
|
||||
foreach (['pending' => __('待审核', 'jio'), 'approved' => __('已通过', 'jio'), 'rejected' => __('已拒绝', 'jio'), '' => __('全部', 'jio')] as $key => $label) {
|
||||
$url = $this->verify_page_url(['status' => $key, 'keyword' => $filters['keyword']]);
|
||||
echo '<li><a href="' . esc_url($url) . '"' . ($key === $filters['status'] ? ' class="current" aria-current="page"' : '') . '>' . esc_html($label)
|
||||
. ' <span class="count">' . esc_html(number_format_i18n($counts[$key === '' ? 'all' : $key])) . '</span></a></li>';
|
||||
}
|
||||
echo '</ul></div><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="aksrt-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="aksrt_user_auth"><input type="hidden" name="status" value="' . esc_attr($filters['status']) . '">';
|
||||
echo '<label class="screen-reader-text" for="aksrt-verify-search">' . esc_html__('搜索用户或认证资料', 'aksrt') . '</label>';
|
||||
echo '<input type="search" id="aksrt-verify-search" name="keyword" value="' . esc_attr($filters['keyword']) . '" placeholder="' . esc_attr__('用户 ID / 昵称 / 邮箱 / 认证名称', 'aksrt') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'aksrt') . '</button>';
|
||||
echo '</ul></div><form method="get" action="' . esc_url(admin_url('users.php')) . '" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="jio_user_auth"><input type="hidden" name="status" value="' . esc_attr($filters['status']) . '">';
|
||||
echo '<label class="screen-reader-text" for="jio-verify-search">' . esc_html__('搜索用户或认证资料', 'jio') . '</label>';
|
||||
echo '<input type="search" id="jio-verify-search" name="keyword" value="' . esc_attr($filters['keyword']) . '" placeholder="' . esc_attr__('用户 ID / 昵称 / 邮箱 / 认证名称', 'jio') . '">';
|
||||
echo '<button type="submit" class="button">' . esc_html__('搜索', 'jio') . '</button>';
|
||||
if ($filters['keyword'] !== '') {
|
||||
echo '<a class="button-link" href="' . esc_url($this->verify_page_url(['status' => $filters['status']])) . '">' . esc_html__('清除搜索', 'aksrt') . '</a>';
|
||||
echo '<a class="button-link" href="' . esc_url($this->verify_page_url(['status' => $filters['status']])) . '">' . esc_html__('清除搜索', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form></div>';
|
||||
}
|
||||
|
||||
private function render_verify_table(array $items, array $filters, int $page): void {
|
||||
echo '<table class="widefat fixed striped aksrt-verify-table"><thead><tr>';
|
||||
foreach (['user' => __('申请用户', 'aksrt'), 'profile' => __('认证资料', 'aksrt'), 'evidence' => __('证明材料', 'aksrt'), 'status' => __('状态', 'aksrt'), 'time' => __('申请时间', 'aksrt'), 'actions' => __('操作', 'aksrt')] as $key => $label) {
|
||||
echo '<table class="widefat fixed striped jio-verify-table"><thead><tr>';
|
||||
foreach (['user' => __('申请用户', 'jio'), 'profile' => __('认证资料', 'jio'), 'evidence' => __('证明材料', 'jio'), 'status' => __('状态', 'jio'), 'time' => __('申请时间', 'jio'), 'actions' => __('操作', 'jio')] as $key => $label) {
|
||||
echo '<th scope="col" class="column-' . esc_attr($key) . '">' . esc_html($label) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
if ($items === []) {
|
||||
$title = $filters['status'] === 'pending' && $filters['keyword'] === '' ? __('暂无待审核申请', 'aksrt') : __('没有符合条件的认证申请', 'aksrt');
|
||||
$hint = $filters['keyword'] !== '' ? __('请调整搜索关键词或切换状态。', 'aksrt') : __('可切换状态查看其他申请,新申请将在这里显示。', 'aksrt');
|
||||
$title = $filters['status'] === 'pending' && $filters['keyword'] === '' ? __('暂无待审核申请', 'jio') : __('没有符合条件的认证申请', 'jio');
|
||||
$hint = $filters['keyword'] !== '' ? __('请调整搜索关键词或切换状态。', 'jio') : __('可切换状态查看其他申请,新申请将在这里显示。', 'jio');
|
||||
echo NativeUi::empty_row(6, $title, $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
}
|
||||
foreach ($items as $row) $this->render_verify_row($row, $filters, $page);
|
||||
@@ -171,23 +171,23 @@ trait UsersMenusVerify {
|
||||
$pending = $row['status'] === 'manual_pending';
|
||||
$evidence = (new EvidenceService())->items($row, true);
|
||||
echo '<tr><td class="column-user">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<div class="aksrt-verify-sub">' . esc_html((string) $row['user_email']) . '</div></td>';
|
||||
echo '<td class="column-profile"><strong>' . esc_html((string) $row['public_name']) . '</strong><div class="aksrt-verify-summary">' . esc_html((string) $row['description']) . '</div>';
|
||||
echo '<div class="jio-verify-sub">' . esc_html((string) $row['user_email']) . '</div></td>';
|
||||
echo '<td class="column-profile"><strong>' . esc_html((string) $row['public_name']) . '</strong><div class="jio-verify-summary">' . esc_html((string) $row['description']) . '</div>';
|
||||
$this->render_verify_detail_template($row, $evidence, $filters, $page);
|
||||
$this->render_verify_delete_template($row, $filters, $page);
|
||||
echo '</td><td class="column-evidence">' . esc_html($evidence === [] ? __('未提供材料', 'aksrt') : sprintf(__('%d 份材料', 'aksrt'), count($evidence))) . '</td>';
|
||||
echo '</td><td class="column-evidence">' . esc_html($evidence === [] ? __('未提供材料', 'jio') : sprintf(__('%d 份材料', 'jio'), count($evidence))) . '</td>';
|
||||
echo '<td class="column-status">' . $this->verify_status_badge((string) $row['status']) . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td class="column-time">' . esc_html($this->verify_time((string) $row['created_at'])) . '</td>';
|
||||
echo '<td class="column-actions"><div class="aksrt-verify-row-actions">';
|
||||
echo '<button type="button" class="button' . ($pending ? ' button-primary' : '') . '" data-aksrt-verify-view="' . $id . '" aria-haspopup="dialog" aria-controls="aksrt-verify-dialog">' . esc_html($pending ? __('审核', 'aksrt') : __('查看详情', 'aksrt')) . '</button>';
|
||||
echo '<button type="button" class="button-link button-link-delete" data-aksrt-verify-delete="' . $id . '" aria-haspopup="dialog" aria-controls="aksrt-verify-dialog">' . esc_html__('删除记录', 'aksrt') . '</button></div></td></tr>';
|
||||
echo '<td class="column-actions"><div class="jio-verify-row-actions">';
|
||||
echo '<button type="button" class="button' . ($pending ? ' button-primary' : '') . '" data-jio-verify-view="' . $id . '" aria-haspopup="dialog" aria-controls="jio-verify-dialog">' . esc_html($pending ? __('审核', 'jio') : __('查看详情', 'jio')) . '</button>';
|
||||
echo '<button type="button" class="button-link button-link-delete" data-jio-verify-delete="' . $id . '" aria-haspopup="dialog" aria-controls="jio-verify-dialog">' . esc_html__('删除记录', 'jio') . '</button></div></td></tr>';
|
||||
}
|
||||
|
||||
private function verify_status_badge(string $status): string {
|
||||
return NativeUi::status_badge($status, [
|
||||
'manual_pending' => [__('待审核', 'aksrt'), 'warning'],
|
||||
'verified' => [__('已通过', 'aksrt'), 'success'],
|
||||
'rejected' => [__('已拒绝', 'aksrt'), 'danger'],
|
||||
'manual_pending' => [__('待审核', 'jio'), 'warning'],
|
||||
'verified' => [__('已通过', 'jio'), 'success'],
|
||||
'rejected' => [__('已拒绝', 'jio'), 'danger'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -203,9 +203,9 @@ trait UsersMenusVerify {
|
||||
}
|
||||
|
||||
public function handle_review_verify(): void {
|
||||
if (!current_user_can('aksrt_review_identity') && !current_user_can(self::CAPABILITY)) wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
if (!current_user_can('jio_review_identity') && !current_user_can(self::CAPABILITY)) wp_die(esc_html__('权限不足', 'jio'));
|
||||
$uuid = sanitize_text_field((string) wp_unslash($_POST['application_uuid'] ?? ''));
|
||||
check_admin_referer('aksrt_users_review_verify_' . $uuid);
|
||||
check_admin_referer('jio_users_review_verify_' . $uuid);
|
||||
$user_id = absint($_POST['user_id'] ?? 0);
|
||||
$status = sanitize_key((string) wp_unslash($_POST['status'] ?? ''));
|
||||
$note = sanitize_textarea_field((string) wp_unslash($_POST['note'] ?? ''));
|
||||
@@ -215,27 +215,27 @@ trait UsersMenusVerify {
|
||||
];
|
||||
$result = AuthApply::review($user_id, $uuid, $status, $note, get_current_user_id(), $fields);
|
||||
$extra = $this->verify_return_args();
|
||||
if (is_wp_error($result)) $extra['aksrt_verify_error'] = $result->get_error_message();
|
||||
$this->redirect('aksrt_user_auth', is_wp_error($result) ? 'verify_failed' : 'verify_updated', $extra);
|
||||
if (is_wp_error($result)) $extra['jio_verify_error'] = $result->get_error_message();
|
||||
$this->redirect('jio_user_auth', is_wp_error($result) ? 'verify_failed' : 'verify_updated', $extra);
|
||||
}
|
||||
|
||||
public function handle_delete_verify(): void {
|
||||
if (!current_user_can('aksrt_review_identity') && !current_user_can(self::CAPABILITY)) wp_die(esc_html__('权限不足', 'aksrt'));
|
||||
if (!current_user_can('jio_review_identity') && !current_user_can(self::CAPABILITY)) wp_die(esc_html__('权限不足', 'jio'));
|
||||
$uuid = sanitize_text_field((string) wp_unslash($_POST['application_uuid'] ?? ''));
|
||||
check_admin_referer('aksrt_users_delete_verify_' . $uuid);
|
||||
check_admin_referer('jio_users_delete_verify_' . $uuid);
|
||||
$extra = $this->verify_return_args();
|
||||
$repo = new ApplicationRepository();
|
||||
$application = $repo->find_by_uuid($uuid);
|
||||
if (!$application) {
|
||||
$this->redirect('aksrt_user_auth', 'verify_failed', array_merge($extra, ['aksrt_verify_error' => __('认证记录不存在', 'aksrt')]));
|
||||
$this->redirect('jio_user_auth', 'verify_failed', array_merge($extra, ['jio_verify_error' => __('认证记录不存在', 'jio')]));
|
||||
}
|
||||
if (!$repo->delete((int) $application['id'])) {
|
||||
$this->redirect('aksrt_user_auth', 'verify_failed', array_merge($extra, ['aksrt_verify_error' => __('认证记录删除失败', 'aksrt')]));
|
||||
$this->redirect('jio_user_auth', 'verify_failed', array_merge($extra, ['jio_verify_error' => __('认证记录删除失败', 'jio')]));
|
||||
}
|
||||
(new EvidenceService())->delete((array) $application['evidence_ids']);
|
||||
if (!(new EventRepository())->delete_for_application((int) $application['id'])) {
|
||||
$this->redirect('aksrt_user_auth', 'verify_failed', array_merge($extra, ['aksrt_verify_error' => __('认证申请记录已删除,但审核事件清理失败。', 'aksrt')]));
|
||||
$this->redirect('jio_user_auth', 'verify_failed', array_merge($extra, ['jio_verify_error' => __('认证申请记录已删除,但审核事件清理失败。', 'jio')]));
|
||||
}
|
||||
$this->redirect('aksrt_user_auth', 'verify_deleted', $extra);
|
||||
$this->redirect('jio_user_auth', 'verify_deleted', $extra);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
/** Identity application detail, review and deletion dialogs. @package AKSRT */
|
||||
/** Identity application detail, review and deletion dialogs. @package Jio */
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UsersMenusVerifyDialogs {
|
||||
|
||||
private function render_verify_dialog_head(string $title, string $description): void {
|
||||
echo '<div class="aksrt-verify-dialog__head"><div><h2 id="aksrt-verify-dialog-title">' . esc_html($title) . '</h2>';
|
||||
echo '<p id="aksrt-verify-dialog-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="aksrt-verify-dialog__close" data-aksrt-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></div>';
|
||||
echo '<div class="jio-verify-dialog__head"><div><h2 id="jio-verify-dialog-title">' . esc_html($title) . '</h2>';
|
||||
echo '<p id="jio-verify-dialog-description">' . esc_html($description) . '</p></div>';
|
||||
echo '<button type="button" class="jio-verify-dialog__close" data-jio-dialog-close aria-label="' . esc_attr__('关闭弹窗', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></div>';
|
||||
}
|
||||
|
||||
private function render_verify_context(array $row, array $filters, int $page, string $action): void {
|
||||
@@ -24,70 +24,70 @@ trait UsersMenusVerifyDialogs {
|
||||
|
||||
private function render_verify_detail_template(array $row, array $evidence, array $filters, int $page): void {
|
||||
$pending = $row['status'] === 'manual_pending';
|
||||
echo '<template id="aksrt-verify-detail-' . (int) $row['id'] . '">';
|
||||
echo '<template id="jio-verify-detail-' . (int) $row['id'] . '">';
|
||||
if ($pending) {
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-verify-dialog__frame" data-aksrt-verify-review>';
|
||||
$this->render_verify_context($row, $filters, $page, 'aksrt_users_review_verify');
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-verify-dialog__frame" data-jio-verify-review>';
|
||||
$this->render_verify_context($row, $filters, $page, 'jio_users_review_verify');
|
||||
echo '<input type="hidden" name="user_id" value="' . (int) $row['user_id'] . '">';
|
||||
} else {
|
||||
echo '<div class="aksrt-verify-dialog__frame">';
|
||||
echo '<div class="jio-verify-dialog__frame">';
|
||||
}
|
||||
$this->render_verify_dialog_head(
|
||||
$pending ? __('审核认证申请', 'aksrt') : __('认证申请详情', 'aksrt'),
|
||||
$pending ? __('先核对申请资料与证明材料,再选择审核结果。', 'aksrt') : __('该申请已处理,以下为申请资料与审核结果。', 'aksrt')
|
||||
$pending ? __('审核认证申请', 'jio') : __('认证申请详情', 'jio'),
|
||||
$pending ? __('先核对申请资料与证明材料,再选择审核结果。', 'jio') : __('该申请已处理,以下为申请资料与审核结果。', 'jio')
|
||||
);
|
||||
echo '<div class="aksrt-verify-dialog__body">';
|
||||
echo '<div class="aksrt-verify-applicant">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<div class="jio-verify-dialog__body">';
|
||||
echo '<div class="jio-verify-applicant">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo $this->verify_status_badge((string) $row['status']) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$meta = [
|
||||
__('用户账号', 'aksrt') => (string) $row['user_login'],
|
||||
__('邮箱', 'aksrt') => (string) $row['user_email'],
|
||||
__('申请时间', 'aksrt') => $this->verify_time((string) $row['created_at']),
|
||||
__('用户账号', 'jio') => (string) $row['user_login'],
|
||||
__('邮箱', 'jio') => (string) $row['user_email'],
|
||||
__('申请时间', 'jio') => $this->verify_time((string) $row['created_at']),
|
||||
];
|
||||
if (!$pending) {
|
||||
$reviewer = get_userdata((int) $row['reviewer_id']);
|
||||
$meta[__('审核人', 'aksrt')] = $reviewer ? (string) $reviewer->display_name : '—';
|
||||
$meta[__('审核时间', 'aksrt')] = $this->verify_time((string) ($row['completed_at'] ?? ''));
|
||||
$meta[__('审核人', 'jio')] = $reviewer ? (string) $reviewer->display_name : '—';
|
||||
$meta[__('审核时间', 'jio')] = $this->verify_time((string) ($row['completed_at'] ?? ''));
|
||||
}
|
||||
echo '<dl class="aksrt-verify-meta">';
|
||||
echo '<dl class="jio-verify-meta">';
|
||||
foreach ($meta as $label => $value) {
|
||||
echo '<div><dt>' . esc_html($label) . '</dt><dd>' . esc_html($value !== '' ? $value : '—') . '</dd></div>';
|
||||
}
|
||||
echo '</dl>';
|
||||
$this->render_verify_text_section(__('认证名称', 'aksrt'), (string) $row['public_name']);
|
||||
$this->render_verify_text_section(__('认证简介', 'aksrt'), (string) $row['description']);
|
||||
$this->render_verify_text_section(__('申请说明', 'aksrt'), (string) $row['manual_reason']);
|
||||
$this->render_verify_text_section(__('认证名称', 'jio'), (string) $row['public_name']);
|
||||
$this->render_verify_text_section(__('认证简介', 'jio'), (string) $row['description']);
|
||||
$this->render_verify_text_section(__('申请说明', 'jio'), (string) $row['manual_reason']);
|
||||
$this->render_verify_evidence($evidence);
|
||||
if ($pending) {
|
||||
$this->render_verify_review_fields($row);
|
||||
} else {
|
||||
$this->render_verify_text_section($row['status'] === 'rejected' ? __('拒绝原因', 'aksrt') : __('处理留言', 'aksrt'), (string) $row['review_note']);
|
||||
$this->render_verify_text_section($row['status'] === 'rejected' ? __('拒绝原因', 'jio') : __('处理留言', 'jio'), (string) $row['review_note']);
|
||||
}
|
||||
echo '</div><div class="aksrt-verify-dialog__foot">';
|
||||
echo '<button type="button" class="button" data-aksrt-dialog-close>' . esc_html($pending ? __('取消', 'aksrt') : __('关闭', 'aksrt')) . '</button>';
|
||||
echo '</div><div class="jio-verify-dialog__foot">';
|
||||
echo '<button type="button" class="button" data-jio-dialog-close>' . esc_html($pending ? __('取消', 'jio') : __('关闭', 'jio')) . '</button>';
|
||||
if ($pending) {
|
||||
echo '<button type="submit" class="button button-primary" data-aksrt-verify-submit disabled'
|
||||
. ' data-idle-label="' . esc_attr__('请选择审核结果', 'aksrt') . '"'
|
||||
. ' data-approve-label="' . esc_attr__('确认通过', 'aksrt') . '"'
|
||||
. ' data-reject-label="' . esc_attr__('确认拒绝', 'aksrt') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">' . esc_html__('请选择审核结果', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button button-primary" data-jio-verify-submit disabled'
|
||||
. ' data-idle-label="' . esc_attr__('请选择审核结果', 'jio') . '"'
|
||||
. ' data-approve-label="' . esc_attr__('确认通过', 'jio') . '"'
|
||||
. ' data-reject-label="' . esc_attr__('确认拒绝', 'jio') . '"'
|
||||
. ' data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">' . esc_html__('请选择审核结果', 'jio') . '</button>';
|
||||
}
|
||||
echo '</div>' . ($pending ? '</form>' : '</div>') . '</template>';
|
||||
}
|
||||
|
||||
private function render_verify_text_section(string $title, string $text): void {
|
||||
echo '<section class="aksrt-verify-section"><h3>' . esc_html($title) . '</h3><div class="aksrt-verify-copy">' . esc_html($text !== '' ? $text : __('未填写', 'aksrt')) . '</div></section>';
|
||||
echo '<section class="jio-verify-section"><h3>' . esc_html($title) . '</h3><div class="jio-verify-copy">' . esc_html($text !== '' ? $text : __('未填写', 'jio')) . '</div></section>';
|
||||
}
|
||||
|
||||
private function render_verify_evidence(array $evidence): void {
|
||||
echo '<section class="aksrt-verify-section"><h3>' . esc_html__('证明材料', 'aksrt') . '</h3>';
|
||||
echo '<section class="jio-verify-section"><h3>' . esc_html__('证明材料', 'jio') . '</h3>';
|
||||
if ($evidence === []) {
|
||||
echo '<p class="aksrt-verify-evidence-empty">' . esc_html__('未提供可查看的证明材料,请结合申请说明判断。', 'aksrt') . '</p>';
|
||||
echo '<p class="jio-verify-evidence-empty">' . esc_html__('未提供可查看的证明材料,请结合申请说明判断。', 'jio') . '</p>';
|
||||
} else {
|
||||
echo '<ul class="aksrt-verify-evidence">';
|
||||
echo '<ul class="jio-verify-evidence">';
|
||||
foreach ($evidence as $index => $item) {
|
||||
$label = sprintf(__('材料 %d(新窗口查看原图)', 'aksrt'), $index + 1);
|
||||
echo '<li><a href="' . esc_url($item['url']) . '" target="_blank" rel="noopener noreferrer"><img src="' . esc_url($item['thumbnail_url']) . '" alt="' . esc_attr(sprintf(__('证明材料 %d', 'aksrt'), $index + 1)) . '"><span>' . esc_html($label) . '</span></a></li>';
|
||||
$label = sprintf(__('材料 %d(新窗口查看原图)', 'jio'), $index + 1);
|
||||
echo '<li><a href="' . esc_url($item['url']) . '" target="_blank" rel="noopener noreferrer"><img src="' . esc_url($item['thumbnail_url']) . '" alt="' . esc_attr(sprintf(__('证明材料 %d', 'jio'), $index + 1)) . '"><span>' . esc_html($label) . '</span></a></li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
@@ -95,30 +95,30 @@ trait UsersMenusVerifyDialogs {
|
||||
}
|
||||
|
||||
private function render_verify_review_fields(array $row): void {
|
||||
echo '<section class="aksrt-verify-review"><h3>' . esc_html__('审核处理', 'aksrt') . '</h3>';
|
||||
echo '<fieldset class="aksrt-verify-decisions"><legend>' . esc_html__('审核结果(必选)', 'aksrt') . '</legend>';
|
||||
foreach (['verified' => __('通过申请', 'aksrt'), 'rejected' => __('拒绝申请', 'aksrt')] as $status => $label) {
|
||||
echo '<section class="jio-verify-review"><h3>' . esc_html__('审核处理', 'jio') . '</h3>';
|
||||
echo '<fieldset class="jio-verify-decisions"><legend>' . esc_html__('审核结果(必选)', 'jio') . '</legend>';
|
||||
foreach (['verified' => __('通过申请', 'jio'), 'rejected' => __('拒绝申请', 'jio')] as $status => $label) {
|
||||
echo '<label><input type="radio" name="status" value="' . esc_attr($status) . '" required><span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset><fieldset class="aksrt-verify-profile" data-aksrt-verify-profile hidden disabled><legend>' . esc_html__('通过后公开展示的认证资料', 'aksrt') . '</legend>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('认证名称(必填)', 'aksrt') . '</span>';
|
||||
echo '<input type="text" name="name" maxlength="64" value="' . esc_attr((string) $row['public_name']) . '" required data-required-error="' . esc_attr__('请填写认证名称', 'aksrt') . '"></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('认证简介', 'aksrt') . '</span><textarea name="description" rows="3">' . esc_textarea((string) $row['description']) . '</textarea></label>';
|
||||
echo '<p class="aksrt-native-field__hint">' . esc_html__('通过审核前可修订名称与简介,原申请说明和证明材料不会被修改。', 'aksrt') . '</p></fieldset>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label" data-aksrt-verify-note-label data-optional="' . esc_attr__('处理留言(选填)', 'aksrt') . '" data-required="' . esc_attr__('拒绝原因(必填)', 'aksrt') . '">' . esc_html__('处理留言(选填)', 'aksrt') . '</span>';
|
||||
echo '<textarea name="note" rows="3" data-required-error="' . esc_attr__('请填写拒绝原因', 'aksrt') . '" aria-describedby="aksrt-verify-note-hint"></textarea></label>';
|
||||
echo '<p id="aksrt-verify-note-hint" class="aksrt-native-field__hint">' . esc_html__('审核结果会通知申请用户;拒绝时请说明原因,便于用户修改后重新申请。', 'aksrt') . '</p></section>';
|
||||
echo '</fieldset><fieldset class="jio-verify-profile" data-jio-verify-profile hidden disabled><legend>' . esc_html__('通过后公开展示的认证资料', 'jio') . '</legend>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('认证名称(必填)', 'jio') . '</span>';
|
||||
echo '<input type="text" name="name" maxlength="64" value="' . esc_attr((string) $row['public_name']) . '" required data-required-error="' . esc_attr__('请填写认证名称', 'jio') . '"></label>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('认证简介', 'jio') . '</span><textarea name="description" rows="3">' . esc_textarea((string) $row['description']) . '</textarea></label>';
|
||||
echo '<p class="jio-native-field__hint">' . esc_html__('通过审核前可修订名称与简介,原申请说明和证明材料不会被修改。', 'jio') . '</p></fieldset>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label" data-jio-verify-note-label data-optional="' . esc_attr__('处理留言(选填)', 'jio') . '" data-required="' . esc_attr__('拒绝原因(必填)', 'jio') . '">' . esc_html__('处理留言(选填)', 'jio') . '</span>';
|
||||
echo '<textarea name="note" rows="3" data-required-error="' . esc_attr__('请填写拒绝原因', 'jio') . '" aria-describedby="jio-verify-note-hint"></textarea></label>';
|
||||
echo '<p id="jio-verify-note-hint" class="jio-native-field__hint">' . esc_html__('审核结果会通知申请用户;拒绝时请说明原因,便于用户修改后重新申请。', 'jio') . '</p></section>';
|
||||
}
|
||||
|
||||
private function render_verify_delete_template(array $row, array $filters, int $page): void {
|
||||
echo '<template id="aksrt-verify-delete-' . (int) $row['id'] . '"><form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-verify-dialog__frame" data-aksrt-verify-delete-form>';
|
||||
$this->render_verify_context($row, $filters, $page, 'aksrt_users_delete_verify');
|
||||
$this->render_verify_dialog_head(__('删除认证申请记录', 'aksrt'), __('请核对申请信息。此操作不可撤销。', 'aksrt'));
|
||||
echo '<div class="aksrt-verify-dialog__body"><div class="aksrt-verify-applicant">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<template id="jio-verify-delete-' . (int) $row['id'] . '"><form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-verify-dialog__frame" data-jio-verify-delete-form>';
|
||||
$this->render_verify_context($row, $filters, $page, 'jio_users_delete_verify');
|
||||
$this->render_verify_dialog_head(__('删除认证申请记录', 'jio'), __('请核对申请信息。此操作不可撤销。', 'jio'));
|
||||
echo '<div class="jio-verify-dialog__body"><div class="jio-verify-applicant">' . NativeUi::user_cell((int) $row['user_id'], (string) $row['display_name']); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo $this->verify_status_badge((string) $row['status']) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$this->render_verify_text_section(__('认证名称', 'aksrt'), (string) $row['public_name']);
|
||||
echo '<p class="aksrt-native-callout is-danger">' . esc_html__('将永久删除该申请记录、证明材料及审核事件。删除记录不会撤销用户已获得的认证。', 'aksrt') . '</p>';
|
||||
echo '</div><div class="aksrt-verify-dialog__foot"><button type="button" class="button" data-aksrt-dialog-close autofocus>' . esc_html__('取消', 'aksrt') . '</button>';
|
||||
echo '<button type="submit" class="button is-danger" data-aksrt-verify-submit data-idle-label="' . esc_attr__('确认删除', 'aksrt') . '" data-busy-label="' . esc_attr__('正在提交…', 'aksrt') . '">' . esc_html__('确认删除', 'aksrt') . '</button></div></form></template>';
|
||||
$this->render_verify_text_section(__('认证名称', 'jio'), (string) $row['public_name']);
|
||||
echo '<p class="jio-native-callout is-danger">' . esc_html__('将永久删除该申请记录、证明材料及审核事件。删除记录不会撤销用户已获得的认证。', 'jio') . '</p>';
|
||||
echo '</div><div class="jio-verify-dialog__foot"><button type="button" class="button" data-jio-dialog-close autofocus>' . esc_html__('取消', 'jio') . '</button>';
|
||||
echo '<button type="submit" class="button is-danger" data-jio-verify-submit data-idle-label="' . esc_attr__('确认删除', 'jio') . '" data-busy-label="' . esc_attr__('正在提交…', 'jio') . '">' . esc_html__('确认删除', 'jio') . '</button></div></form></template>';
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* 用户会员后台页(users.php → aksrt_user_vip):页面编排、写操作与查询。
|
||||
* 用户会员后台页(users.php → jio_user_vip):页面编排、写操作与查询。
|
||||
*
|
||||
* 列表展示由 UsersMenusVipView 输出;所有会员变更都通过同一个弹窗表单提交到
|
||||
* admin_post_aksrt_users_mutate_vip(单人调整与批量调整共用),交互脚本见
|
||||
* admin_post_jio_users_mutate_vip(单人调整与批量调整共用),交互脚本见
|
||||
* assets/js/admin-vip.js。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,7 +17,7 @@ trait UsersMenusVip {
|
||||
|
||||
use UsersMenusVipView;
|
||||
|
||||
private const VIP_PAGE_SLUG = 'aksrt_user_vip';
|
||||
private const VIP_PAGE_SLUG = 'jio_user_vip';
|
||||
private const VIP_PER_PAGE = 20;
|
||||
private const VIP_FILTER_KEYS = ['user_id', 'level', 'state', 'date_from', 'date_to', 'orderby', 'order'];
|
||||
|
||||
@@ -27,11 +27,11 @@ trait UsersMenusVip {
|
||||
}
|
||||
$css = '/assets/css/admin/vip.css';
|
||||
$js = '/assets/js/admin-vip.js';
|
||||
if (is_file(AKSRT_DIR . $css)) {
|
||||
wp_enqueue_style('aksrt-admin-vip', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
|
||||
if (is_file(JIO_DIR . $css)) {
|
||||
wp_enqueue_style('jio-admin-vip', JIO_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(JIO_DIR . $css));
|
||||
}
|
||||
if (is_file(AKSRT_DIR . $js)) {
|
||||
wp_enqueue_script('aksrt-admin-vip', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
|
||||
if (is_file(JIO_DIR . $js)) {
|
||||
wp_enqueue_script('jio-admin-vip', JIO_URI . $js, [], (string) filemtime(JIO_DIR . $js), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,14 +39,14 @@ trait UsersMenusVip {
|
||||
$this->ensure_cap();
|
||||
$tab = $this->vip_tab();
|
||||
$page = max(1, absint($_GET['paged'] ?? 1));
|
||||
NativeUi::page_open('aksrt-vip');
|
||||
NativeUi::page_open('jio-vip');
|
||||
NativeUi::header(
|
||||
__('用户会员', 'aksrt'),
|
||||
__('查看会员档位与有效期,单人或批量调整会员;每次变更都会记录原因与结果。', 'aksrt'),
|
||||
__('用户会员', 'jio'),
|
||||
__('查看会员档位与有效期,单人或批量调整会员;每次变更都会记录原因与结果。', 'jio'),
|
||||
'dashicons-awards',
|
||||
[[
|
||||
'label' => __('会员配置', 'aksrt'),
|
||||
'url' => admin_url('admin.php?page=aksrt-settings#/payment/vip'),
|
||||
'label' => __('会员配置', 'jio'),
|
||||
'url' => admin_url('admin.php?page=jio-settings#/payment/vip'),
|
||||
'icon' => 'dashicons-admin-generic',
|
||||
]]
|
||||
);
|
||||
@@ -67,18 +67,18 @@ trait UsersMenusVip {
|
||||
|
||||
public function handle_mutate_vip(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_users_mutate_vip');
|
||||
check_admin_referer('jio_users_mutate_vip');
|
||||
$ids = isset($_POST['user_ids']) ? wp_unslash($_POST['user_ids']) : [];
|
||||
$ids = is_array($ids) ? array_values(array_unique(array_filter(array_map('absint', $ids)))) : [];
|
||||
if ($ids === [] || count($ids) > 100) {
|
||||
$this->redirect_vip_result('vip_failed', ['aksrt_vip_error' => __('请选择 1 至 100 名用户。', 'aksrt')]);
|
||||
$this->redirect_vip_result('vip_failed', ['jio_vip_error' => __('请选择 1 至 100 名用户。', 'jio')]);
|
||||
}
|
||||
|
||||
$operation = sanitize_key((string) wp_unslash($_POST['vip_operation'] ?? ''));
|
||||
$reason = sanitize_text_field((string) wp_unslash($_POST['reason'] ?? ''));
|
||||
$payload = $this->vip_mutation_payload($operation);
|
||||
if (is_wp_error($payload)) {
|
||||
$this->redirect_vip_result('vip_failed', ['aksrt_vip_error' => $payload->get_error_message()]);
|
||||
$this->redirect_vip_result('vip_failed', ['jio_vip_error' => $payload->get_error_message()]);
|
||||
}
|
||||
|
||||
$batch = count($ids) > 1 ? wp_generate_uuid4() : '';
|
||||
@@ -89,14 +89,14 @@ trait UsersMenusVip {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
$result = class_exists('\AKSRT\Content\VipService')
|
||||
? \AKSRT\Content\VipService::get_instance()->mutate_membership($user_id, $operation, $payload, [
|
||||
$result = class_exists('\Jio\Content\VipService')
|
||||
? \Jio\Content\VipService::get_instance()->mutate_membership($user_id, $operation, $payload, [
|
||||
'actor_user_id' => get_current_user_id(),
|
||||
'source' => 'admin',
|
||||
'reason' => $reason,
|
||||
'batch_uuid' => $batch,
|
||||
])
|
||||
: new \WP_Error('aksrt_vip_unavailable', __('会员服务不可用。', 'aksrt'));
|
||||
: new \WP_Error('jio_vip_unavailable', __('会员服务不可用。', 'jio'));
|
||||
if (is_wp_error($result)) {
|
||||
$failed++;
|
||||
$last_error = $result->get_error_message();
|
||||
@@ -105,12 +105,12 @@ trait UsersMenusVip {
|
||||
}
|
||||
}
|
||||
if ($ok === 0) {
|
||||
$this->redirect_vip_result('vip_failed', $last_error !== '' ? ['aksrt_vip_error' => $last_error] : []);
|
||||
$this->redirect_vip_result('vip_failed', $last_error !== '' ? ['jio_vip_error' => $last_error] : []);
|
||||
}
|
||||
$this->redirect_vip_result($failed > 0 || $skipped > 0 ? 'vip_partial' : 'vip_updated', [
|
||||
'aksrt_vip_ok' => $ok,
|
||||
'aksrt_vip_failed_count' => $failed,
|
||||
'aksrt_vip_skipped' => $skipped,
|
||||
'jio_vip_ok' => $ok,
|
||||
'jio_vip_failed_count' => $failed,
|
||||
'jio_vip_skipped' => $skipped,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -164,13 +164,13 @@ trait UsersMenusVip {
|
||||
private function vip_sql_parts(): array {
|
||||
global $wpdb;
|
||||
$meta = "(SELECT user_id,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_level' THEN meta_value END) level,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_expires' THEN meta_value END) expires,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_base_level' THEN meta_value END) base_level,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_base_expires' THEN meta_value END) base_expires,
|
||||
MAX(CASE WHEN meta_key = 'aksrt_vip_level_expired' THEN meta_value END) expired_level
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_level' THEN meta_value END) level,
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_expires' THEN meta_value END) expires,
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_base_level' THEN meta_value END) base_level,
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_base_expires' THEN meta_value END) base_expires,
|
||||
MAX(CASE WHEN meta_key = 'jio_vip_level_expired' THEN meta_value END) expired_level
|
||||
FROM {$wpdb->usermeta}
|
||||
WHERE meta_key IN ('aksrt_vip_level', 'aksrt_vip_expires', 'aksrt_vip_base_level', 'aksrt_vip_base_expires', 'aksrt_vip_level_expired')
|
||||
WHERE meta_key IN ('jio_vip_level', 'jio_vip_expires', 'jio_vip_base_level', 'jio_vip_base_expires', 'jio_vip_level_expired')
|
||||
GROUP BY user_id) m";
|
||||
$raw_level = 'CAST(COALESCE(m.level, 0) AS UNSIGNED)';
|
||||
$raw_expires = 'CAST(COALESCE(m.expires, 0) AS UNSIGNED)';
|
||||
@@ -274,7 +274,7 @@ trait UsersMenusVip {
|
||||
/** @return array<string,mixed>|\WP_Error */
|
||||
private function vip_mutation_payload(string $operation): array|\WP_Error {
|
||||
if (!in_array($operation, ['set', 'extend', 'disable', 'restore'], true)) {
|
||||
return new \WP_Error('aksrt_vip_operation_invalid', __('会员操作无效。', 'aksrt'));
|
||||
return new \WP_Error('jio_vip_operation_invalid', __('会员操作无效。', 'jio'));
|
||||
}
|
||||
if ($operation === 'disable' || $operation === 'restore') {
|
||||
return [];
|
||||
@@ -290,7 +290,7 @@ trait UsersMenusVip {
|
||||
}
|
||||
$date = $this->vip_date($_POST['expires_date'] ?? '');
|
||||
if ($date === '') {
|
||||
return new \WP_Error('aksrt_vip_expiry_required', __('请设置永久会员或有效的到期日期。', 'aksrt'));
|
||||
return new \WP_Error('jio_vip_expiry_required', __('请设置永久会员或有效的到期日期。', 'jio'));
|
||||
}
|
||||
$expires = (new \DateTimeImmutable($date . ' 23:59:59', wp_timezone()))->getTimestamp();
|
||||
return ['level' => absint($_POST['level'] ?? 0), 'expires' => $expires];
|
||||
@@ -298,10 +298,10 @@ trait UsersMenusVip {
|
||||
|
||||
/** @return array<int,string> */
|
||||
private function vip_level_names(): array {
|
||||
$levels = class_exists('\AKSRT\Settings\Settings') ? (array) (\AKSRT\Settings\Settings::get_payment_vip_settings()['levels'] ?? []) : [];
|
||||
$levels = class_exists('\Jio\Settings\Settings') ? (array) (\Jio\Settings\Settings::get_payment_vip_settings()['levels'] ?? []) : [];
|
||||
return [
|
||||
1 => (string) ($levels[1]['name'] ?? __('一级会员', 'aksrt')),
|
||||
2 => (string) ($levels[2]['name'] ?? __('二级会员', 'aksrt')),
|
||||
1 => (string) ($levels[1]['name'] ?? __('一级会员', 'jio')),
|
||||
2 => (string) ($levels[2]['name'] ?? __('二级会员', 'jio')),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
* 弹窗是页面上唯一的写表单:行内「调整」与工具栏「批量调整」都只负责把目标用户
|
||||
* 填进弹窗(assets/js/admin-vip.js),提交由 UsersMenusVip::handle_mutate_vip() 处理。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Native;
|
||||
namespace Jio\Admin\Native;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -21,29 +21,29 @@ trait UsersMenusVipView {
|
||||
$base = admin_url('users.php?page=' . self::VIP_PAGE_SLUG);
|
||||
NativeUi::stats([
|
||||
[
|
||||
'label' => __('限时会员', 'aksrt'),
|
||||
'label' => __('限时会员', 'jio'),
|
||||
'value' => $stats['active'],
|
||||
'url' => add_query_arg('state', 'active', $base),
|
||||
'icon' => 'dashicons-clock',
|
||||
'hint' => __('有效期内', 'aksrt'),
|
||||
'hint' => __('有效期内', 'jio'),
|
||||
],
|
||||
[
|
||||
'label' => __('永久会员', 'aksrt'),
|
||||
'label' => __('永久会员', 'jio'),
|
||||
'value' => $stats['permanent'],
|
||||
'url' => add_query_arg('state', 'permanent', $base),
|
||||
'tone' => 'primary',
|
||||
'icon' => 'dashicons-awards',
|
||||
],
|
||||
[
|
||||
'label' => __('7 天内到期', 'aksrt'),
|
||||
'label' => __('7 天内到期', 'jio'),
|
||||
'value' => $stats['expiring'],
|
||||
'url' => add_query_arg(['state' => 'active', 'date_from' => $window['from'], 'date_to' => $window['to'], 'orderby' => 'expires', 'order' => 'ASC'], $base),
|
||||
'tone' => $stats['expiring'] > 0 ? 'warning' : 'neutral',
|
||||
'icon' => 'dashicons-warning',
|
||||
'hint' => __('建议提醒续费', 'aksrt'),
|
||||
'hint' => __('建议提醒续费', 'jio'),
|
||||
],
|
||||
[
|
||||
'label' => __('已过期', 'aksrt'),
|
||||
'label' => __('已过期', 'jio'),
|
||||
'value' => $stats['expired'],
|
||||
'url' => add_query_arg('state', 'expired', $base),
|
||||
'tone' => 'neutral',
|
||||
@@ -54,14 +54,14 @@ trait UsersMenusVipView {
|
||||
'value' => $stats['level_1'],
|
||||
'tone' => 'info',
|
||||
'icon' => 'dashicons-star-half',
|
||||
'hint' => __('当前有效人数', 'aksrt'),
|
||||
'hint' => __('当前有效人数', 'jio'),
|
||||
],
|
||||
[
|
||||
'label' => $names[2],
|
||||
'value' => $stats['level_2'],
|
||||
'tone' => 'info',
|
||||
'icon' => 'dashicons-star-filled',
|
||||
'hint' => __('当前有效人数', 'aksrt'),
|
||||
'hint' => __('当前有效人数', 'jio'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -69,13 +69,13 @@ trait UsersMenusVipView {
|
||||
private function render_vip_tabs(string $tab): void {
|
||||
$base = admin_url('users.php?page=' . self::VIP_PAGE_SLUG);
|
||||
$tabs = [
|
||||
'members' => [__('会员列表', 'aksrt'), $base, 'dashicons-groups'],
|
||||
'history' => [__('变更记录', 'aksrt'), add_query_arg('tab', 'history', $base), 'dashicons-backup'],
|
||||
'members' => [__('会员列表', 'jio'), $base, 'dashicons-groups'],
|
||||
'history' => [__('变更记录', 'jio'), add_query_arg('tab', 'history', $base), 'dashicons-backup'],
|
||||
];
|
||||
echo '<nav class="aksrt-native-tabs" aria-label="' . esc_attr__('会员视图', 'aksrt') . '">';
|
||||
echo '<nav class="jio-native-tabs" aria-label="' . esc_attr__('会员视图', 'jio') . '">';
|
||||
foreach ($tabs as $key => [$label, $url, $icon]) {
|
||||
$current = $key === $tab;
|
||||
echo '<a class="aksrt-native-tabs__link' . ($current ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($current ? ' aria-current="page"' : '') . '>'
|
||||
echo '<a class="jio-native-tabs__link' . ($current ? ' is-active' : '') . '" href="' . esc_url($url) . '"' . ($current ? ' aria-current="page"' : '') . '>'
|
||||
. '<span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>' . esc_html($label) . '</a>';
|
||||
}
|
||||
echo '</nav>';
|
||||
@@ -83,25 +83,25 @@ trait UsersMenusVipView {
|
||||
|
||||
private function render_vip_filters(array $filters): void {
|
||||
$names = $this->vip_level_names();
|
||||
NativeUi::card_open('', '', false, 'aksrt-native-toolbar aksrt-vip-filters');
|
||||
echo '<form method="get" class="aksrt-native-filters">';
|
||||
NativeUi::card_open('', '', false, 'jio-native-toolbar jio-vip-filters');
|
||||
echo '<form method="get" class="jio-native-filters">';
|
||||
echo '<input type="hidden" name="page" value="' . esc_attr(self::VIP_PAGE_SLUG) . '">';
|
||||
echo '<input type="hidden" name="orderby" value="' . esc_attr($filters['orderby']) . '"><input type="hidden" name="order" value="' . esc_attr($filters['order']) . '">';
|
||||
echo '<label>UID<input type="number" min="1" name="user_id" class="small-text" value="' . esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('全部', 'aksrt') . '"></label>';
|
||||
echo '<label>' . esc_html__('档位', 'aksrt') . '<select name="level">';
|
||||
foreach (['all' => __('全部', 'aksrt'), '0' => __('普通用户', 'aksrt'), '1' => $names[1], '2' => $names[2]] as $key => $label) {
|
||||
echo '<label>UID<input type="number" min="1" name="user_id" class="small-text" value="' . esc_attr($filters['user_id'] > 0 ? (string) $filters['user_id'] : '') . '" placeholder="' . esc_attr__('全部', 'jio') . '"></label>';
|
||||
echo '<label>' . esc_html__('档位', 'jio') . '<select name="level">';
|
||||
foreach (['all' => __('全部', 'jio'), '0' => __('普通用户', 'jio'), '1' => $names[1], '2' => $names[2]] as $key => $label) {
|
||||
printf('<option value="%s"%s>%s</option>', esc_attr((string) $key), selected($filters['level'], (string) $key, false), esc_html($label));
|
||||
}
|
||||
echo '</select></label><label>' . esc_html__('状态', 'aksrt') . '<select name="state">';
|
||||
echo '</select></label><label>' . esc_html__('状态', 'jio') . '<select name="state">';
|
||||
foreach ($this->vip_state_labels() as $key => $label) {
|
||||
printf('<option value="%s"%s>%s</option>', esc_attr($key), selected($filters['state'], $key, false), esc_html($label));
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<label>' . esc_html__('到期日', 'aksrt') . '<input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" aria-label="' . esc_attr__('到期日起', 'aksrt') . '">'
|
||||
. '<span aria-hidden="true">—</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" aria-label="' . esc_attr__('到期日止', 'aksrt') . '"></label>';
|
||||
echo '<button type="submit" class="button button-primary">' . esc_html__('筛选', 'aksrt') . '</button>';
|
||||
echo '<label>' . esc_html__('到期日', 'jio') . '<input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '" aria-label="' . esc_attr__('到期日起', 'jio') . '">'
|
||||
. '<span aria-hidden="true">—</span><input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '" aria-label="' . esc_attr__('到期日止', 'jio') . '"></label>';
|
||||
echo '<button type="submit" class="button button-primary">' . esc_html__('筛选', 'jio') . '</button>';
|
||||
if ($this->vip_filters_active($filters)) {
|
||||
echo '<a class="button" href="' . esc_url(admin_url('users.php?page=' . self::VIP_PAGE_SLUG)) . '">' . esc_html__('重置', 'aksrt') . '</a>';
|
||||
echo '<a class="button" href="' . esc_url(admin_url('users.php?page=' . self::VIP_PAGE_SLUG)) . '">' . esc_html__('重置', 'jio') . '</a>';
|
||||
}
|
||||
echo '</form>';
|
||||
NativeUi::card_close();
|
||||
@@ -110,28 +110,28 @@ trait UsersMenusVipView {
|
||||
/** @return array<string,string> */
|
||||
private function vip_state_labels(): array {
|
||||
return [
|
||||
'all' => __('全部', 'aksrt'),
|
||||
'active' => __('限时有效', 'aksrt'),
|
||||
'permanent' => __('永久', 'aksrt'),
|
||||
'expired' => __('已过期', 'aksrt'),
|
||||
'none' => __('无会员', 'aksrt'),
|
||||
'all' => __('全部', 'jio'),
|
||||
'active' => __('限时有效', 'jio'),
|
||||
'permanent' => __('永久', 'jio'),
|
||||
'expired' => __('已过期', 'jio'),
|
||||
'none' => __('无会员', 'jio'),
|
||||
];
|
||||
}
|
||||
|
||||
private function render_vip_table(array $items, array $filters, int $pages, int $page): void {
|
||||
$actions = '<span class="aksrt-vip-selection" data-aksrt-vip-selection hidden></span>'
|
||||
. '<button type="button" class="button button-primary" data-aksrt-vip-bulk disabled>'
|
||||
. '<span class="dashicons dashicons-edit" aria-hidden="true"></span>' . esc_html__('批量调整', 'aksrt') . '</button>';
|
||||
NativeUi::table_card_open(__('会员列表', 'aksrt'), $actions);
|
||||
echo '<table class="wp-list-table widefat fixed striped aksrt-vip-table"><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column"><input type="checkbox" data-aksrt-vip-check-all aria-label="' . esc_attr__('选择本页全部用户', 'aksrt') . '"></td>';
|
||||
echo '<th class="column-user">' . esc_html__('用户', 'aksrt') . '</th>';
|
||||
echo '<th>' . esc_html__('当前会员', 'aksrt') . '</th>';
|
||||
$actions = '<span class="jio-vip-selection" data-jio-vip-selection hidden></span>'
|
||||
. '<button type="button" class="button button-primary" data-jio-vip-bulk disabled>'
|
||||
. '<span class="dashicons dashicons-edit" aria-hidden="true"></span>' . esc_html__('批量调整', 'jio') . '</button>';
|
||||
NativeUi::table_card_open(__('会员列表', 'jio'), $actions);
|
||||
echo '<table class="wp-list-table widefat fixed striped jio-vip-table"><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column"><input type="checkbox" data-jio-vip-check-all aria-label="' . esc_attr__('选择本页全部用户', 'jio') . '"></td>';
|
||||
echo '<th class="column-user">' . esc_html__('用户', 'jio') . '</th>';
|
||||
echo '<th>' . esc_html__('当前会员', 'jio') . '</th>';
|
||||
echo '<th>' . $this->vip_sort_link($filters) . '</th>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<th class="column-actions">' . esc_html__('操作', 'aksrt') . '</th></tr></thead><tbody>';
|
||||
echo '<th class="column-actions">' . esc_html__('操作', 'jio') . '</th></tr></thead><tbody>';
|
||||
if ($items === []) {
|
||||
$hint = $this->vip_filters_active($filters) ? __('试试调整或重置筛选条件。', 'aksrt') : '';
|
||||
echo NativeUi::empty_row(5, __('没有符合条件的用户', 'aksrt'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
$hint = $this->vip_filters_active($filters) ? __('试试调整或重置筛选条件。', 'jio') : '';
|
||||
echo NativeUi::empty_row(5, __('没有符合条件的用户', 'jio'), $hint); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
}
|
||||
foreach ($items as $row) {
|
||||
$this->render_vip_row($row);
|
||||
@@ -146,8 +146,8 @@ trait UsersMenusVipView {
|
||||
$active = $filters['orderby'] === 'expires';
|
||||
$next = $active && $filters['order'] === 'ASC' ? 'DESC' : 'ASC';
|
||||
$state = $active ? ($filters['order'] === 'ASC' ? ' is-asc' : ' is-desc') : '';
|
||||
return '<a class="aksrt-vip-sort' . $state . '" href="' . esc_url($this->vip_list_url($filters, ['orderby' => 'expires', 'order' => $next])) . '">'
|
||||
. esc_html__('到期时间', 'aksrt') . '<span class="aksrt-vip-sort__icon" aria-hidden="true"><i></i><i></i></span></a>';
|
||||
return '<a class="jio-vip-sort' . $state . '" href="' . esc_url($this->vip_list_url($filters, ['orderby' => 'expires', 'order' => $next])) . '">'
|
||||
. esc_html__('到期时间', 'jio') . '<span class="jio-vip-sort__icon" aria-hidden="true"><i></i><i></i></span></a>';
|
||||
}
|
||||
|
||||
private function render_vip_row(array $row): void {
|
||||
@@ -160,27 +160,27 @@ trait UsersMenusVipView {
|
||||
$fell_back = $level > 0 && ((int) $row['raw_level'] !== $level || (int) $row['raw_expires'] !== $expires);
|
||||
$base_level = (int) $row['base_level'];
|
||||
$base_expires = (int) $row['base_expires'];
|
||||
$label = $level > 0 ? ($names[$level] ?? ('VIP' . $level)) : __('普通用户', 'aksrt');
|
||||
$label = $level > 0 ? ($names[$level] ?? ('VIP' . $level)) : __('普通用户', 'jio');
|
||||
|
||||
if ($expired) {
|
||||
$lapsed = max((int) $row['raw_level'], (int) $row['expired_level']);
|
||||
$badge = NativeUi::badge(sprintf(__('%s · 已过期', 'aksrt'), $names[$lapsed] ?? __('会员', 'aksrt')), 'neutral');
|
||||
$badge = NativeUi::badge(sprintf(__('%s · 已过期', 'jio'), $names[$lapsed] ?? __('会员', 'jio')), 'neutral');
|
||||
} else {
|
||||
$badge = NativeUi::badge($label, $level > 0 ? ($fell_back ? 'warning' : ($expires === 0 ? 'primary' : 'success')) : 'neutral', $level > 0);
|
||||
}
|
||||
$note = '';
|
||||
if ($fell_back) {
|
||||
$note = __('限时档位已到期,按基础档位计', 'aksrt');
|
||||
$note = __('限时档位已到期,按基础档位计', 'jio');
|
||||
} elseif ($level > 0 && $base_level > 0) {
|
||||
$note = sprintf(__('到期后回退:%s', 'aksrt'), $names[$base_level] ?? ('VIP' . $base_level));
|
||||
$note = sprintf(__('到期后回退:%s', 'jio'), $names[$base_level] ?? ('VIP' . $base_level));
|
||||
}
|
||||
|
||||
$expiry_label = $this->vip_expiry_label($level, $expires, $expired);
|
||||
$expiry_html = esc_html($expiry_label);
|
||||
if ($level > 0 && $expires > 0 && !$expired) {
|
||||
$days = (int) ceil(($expires - $now) / DAY_IN_SECONDS);
|
||||
$expiry_html .= '<span class="aksrt-vip-remaining ' . ($days <= 7 ? 'aksrt-native-text--warning' : 'aksrt-native-muted') . '">'
|
||||
. esc_html(sprintf(__('剩余 %s', 'aksrt'), human_time_diff($now, $expires))) . '</span>';
|
||||
$expiry_html .= '<span class="jio-vip-remaining ' . ($days <= 7 ? 'jio-native-text--warning' : 'jio-native-muted') . '">'
|
||||
. esc_html(sprintf(__('剩余 %s', 'jio'), human_time_diff($now, $expires))) . '</span>';
|
||||
}
|
||||
|
||||
$can_extend = $level > 0 && $expires > 0 && !$expired;
|
||||
@@ -189,7 +189,7 @@ trait UsersMenusVipView {
|
||||
$data = [
|
||||
'user-id' => (string) $id,
|
||||
'user-name' => $user_name,
|
||||
'current' => $expired ? __('已过期', 'aksrt') : $label . ($level > 0 ? ' · ' . $expiry_label : ''),
|
||||
'current' => $expired ? __('已过期', 'jio') : $label . ($level > 0 ? ' · ' . $expiry_label : ''),
|
||||
'level' => (string) max(1, $level),
|
||||
'can-extend' => $can_extend ? '1' : '0',
|
||||
'can-restore' => $can_restore ? '1' : '0',
|
||||
@@ -200,130 +200,130 @@ trait UsersMenusVipView {
|
||||
}
|
||||
|
||||
echo '<tr>';
|
||||
echo '<th scope="row" class="check-column"><input type="checkbox" class="aksrt-vip-check" value="' . esc_attr((string) $id) . '"' . $attrs . ' aria-label="' . esc_attr(sprintf(__('选择 %s', 'aksrt'), $user_name)) . '"></th>'; // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
echo '<td class="column-user">' . NativeUi::user_cell($id, $user_name) . '<span class="aksrt-native-muted aksrt-vip-login">@' . esc_html((string) $row['user_login']) . '</span></td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td>' . $badge . ($note !== '' ? '<span class="aksrt-vip-note aksrt-native-muted">' . esc_html($note) . '</span>' : '') . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td class="aksrt-vip-expiry">' . $expiry_html . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- escaped above.
|
||||
echo '<td class="column-actions"><button type="button" class="button button-small" data-aksrt-vip-edit' . $attrs . '>' // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
. '<span class="dashicons dashicons-edit" aria-hidden="true"></span>' . esc_html__('调整', 'aksrt') . '</button></td>';
|
||||
echo '<th scope="row" class="check-column"><input type="checkbox" class="jio-vip-check" value="' . esc_attr((string) $id) . '"' . $attrs . ' aria-label="' . esc_attr(sprintf(__('选择 %s', 'jio'), $user_name)) . '"></th>'; // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
echo '<td class="column-user">' . NativeUi::user_cell($id, $user_name) . '<span class="jio-native-muted jio-vip-login">@' . esc_html((string) $row['user_login']) . '</span></td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td>' . $badge . ($note !== '' ? '<span class="jio-vip-note jio-native-muted">' . esc_html($note) . '</span>' : '') . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo '<td class="jio-vip-expiry">' . $expiry_html . '</td>'; // phpcs:ignore WordPress.Security.EscapeOutput -- escaped above.
|
||||
echo '<td class="column-actions"><button type="button" class="button button-small" data-jio-vip-edit' . $attrs . '>' // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
. '<span class="dashicons dashicons-edit" aria-hidden="true"></span>' . esc_html__('调整', 'jio') . '</button></td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
|
||||
private function vip_expiry_label(int $level, int $expires, bool $expired): string {
|
||||
if ($expired) {
|
||||
return __('已过期', 'aksrt');
|
||||
return __('已过期', 'jio');
|
||||
}
|
||||
if ($level <= 0) {
|
||||
return '—';
|
||||
}
|
||||
return $expires === 0 ? __('永久有效', 'aksrt') : wp_date('Y-m-d H:i', $expires, wp_timezone());
|
||||
return $expires === 0 ? __('永久有效', 'jio') : wp_date('Y-m-d H:i', $expires, wp_timezone());
|
||||
}
|
||||
|
||||
private function render_vip_dialog(array $filters): void {
|
||||
$names = $this->vip_level_names();
|
||||
$today = (new \DateTimeImmutable('today', wp_timezone()))->format('Y-m-d');
|
||||
$strings = [
|
||||
'single' => __('调整会员:%s', 'aksrt'),
|
||||
'bulk' => __('批量调整会员(%d 人)', 'aksrt'),
|
||||
'selected' => __('已选 %d 人', 'aksrt'),
|
||||
'bulk-note' => __('不满足条件的用户会被跳过并计入失败,结果会在提交后汇总显示。', 'aksrt'),
|
||||
'extend-off' => __('该用户不是限时有效会员,无法延长。', 'aksrt'),
|
||||
'restore-off' => __('该用户没有可恢复的回退会员。', 'aksrt'),
|
||||
'single' => __('调整会员:%s', 'jio'),
|
||||
'bulk' => __('批量调整会员(%d 人)', 'jio'),
|
||||
'selected' => __('已选 %d 人', 'jio'),
|
||||
'bulk-note' => __('不满足条件的用户会被跳过并计入失败,结果会在提交后汇总显示。', 'jio'),
|
||||
'extend-off' => __('该用户不是限时有效会员,无法延长。', 'jio'),
|
||||
'restore-off' => __('该用户没有可恢复的回退会员。', 'jio'),
|
||||
];
|
||||
$attrs = '';
|
||||
foreach ($strings as $key => $value) {
|
||||
$attrs .= ' data-i18n-' . $key . '="' . esc_attr($value) . '"';
|
||||
}
|
||||
echo '<dialog class="aksrt-vip-dialog" id="aksrt-vip-dialog" aria-labelledby="aksrt-vip-dialog-title"' . $attrs . '>'; // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-vip-dialog__form">';
|
||||
echo '<input type="hidden" name="action" value="aksrt_users_mutate_vip">';
|
||||
wp_nonce_field('aksrt_users_mutate_vip');
|
||||
echo '<dialog class="jio-vip-dialog" id="jio-vip-dialog" aria-labelledby="jio-vip-dialog-title"' . $attrs . '>'; // phpcs:ignore WordPress.Security.EscapeOutput -- attrs escaped above.
|
||||
echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="jio-vip-dialog__form">';
|
||||
echo '<input type="hidden" name="action" value="jio_users_mutate_vip">';
|
||||
wp_nonce_field('jio_users_mutate_vip');
|
||||
$this->render_vip_return_fields($filters);
|
||||
echo '<div data-aksrt-vip-ids></div>';
|
||||
echo '<div data-jio-vip-ids></div>';
|
||||
|
||||
echo '<header class="aksrt-vip-dialog__head"><h2 id="aksrt-vip-dialog-title" class="aksrt-vip-dialog__title" data-aksrt-vip-title></h2>'
|
||||
. '<button type="button" class="aksrt-vip-dialog__close" data-aksrt-vip-close aria-label="' . esc_attr__('关闭', 'aksrt') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<div class="aksrt-vip-dialog__body">';
|
||||
echo '<p class="aksrt-vip-dialog__target" data-aksrt-vip-target></p>';
|
||||
echo '<header class="jio-vip-dialog__head"><h2 id="jio-vip-dialog-title" class="jio-vip-dialog__title" data-jio-vip-title></h2>'
|
||||
. '<button type="button" class="jio-vip-dialog__close" data-jio-vip-close aria-label="' . esc_attr__('关闭', 'jio') . '"><span class="dashicons dashicons-no-alt" aria-hidden="true"></span></button></header>';
|
||||
echo '<div class="jio-vip-dialog__body">';
|
||||
echo '<p class="jio-vip-dialog__target" data-jio-vip-target></p>';
|
||||
|
||||
echo '<fieldset class="aksrt-vip-ops"><legend class="aksrt-native-field__label">' . esc_html__('操作', 'aksrt') . '</legend>';
|
||||
echo '<fieldset class="jio-vip-ops"><legend class="jio-native-field__label">' . esc_html__('操作', 'jio') . '</legend>';
|
||||
$ops = [
|
||||
'set' => [__('设置会员', 'aksrt'), 'dashicons-awards'],
|
||||
'extend' => [__('延长有效期', 'aksrt'), 'dashicons-clock'],
|
||||
'restore' => [__('恢复回退会员', 'aksrt'), 'dashicons-undo'],
|
||||
'disable' => [__('停用会员', 'aksrt'), 'dashicons-dismiss'],
|
||||
'set' => [__('设置会员', 'jio'), 'dashicons-awards'],
|
||||
'extend' => [__('延长有效期', 'jio'), 'dashicons-clock'],
|
||||
'restore' => [__('恢复回退会员', 'jio'), 'dashicons-undo'],
|
||||
'disable' => [__('停用会员', 'jio'), 'dashicons-dismiss'],
|
||||
];
|
||||
foreach ($ops as $key => [$label, $icon]) {
|
||||
echo '<label class="aksrt-vip-op' . ($key === 'disable' ? ' is-danger' : '') . '"><input type="radio" name="vip_operation" value="' . esc_attr($key) . '"' . checked($key, 'set', false) . '>'
|
||||
echo '<label class="jio-vip-op' . ($key === 'disable' ? ' is-danger' : '') . '"><input type="radio" name="vip_operation" value="' . esc_attr($key) . '"' . checked($key, 'set', false) . '>'
|
||||
. '<span><span class="dashicons ' . esc_attr($icon) . '" aria-hidden="true"></span>' . esc_html($label) . '</span></label>';
|
||||
}
|
||||
echo '</fieldset>';
|
||||
echo '<p class="aksrt-vip-op-hint aksrt-native-muted" data-aksrt-vip-op-hint hidden></p>';
|
||||
echo '<p class="jio-vip-op-hint jio-native-muted" data-jio-vip-op-hint hidden></p>';
|
||||
|
||||
echo '<div class="aksrt-native-form-grid aksrt-vip-pane" data-aksrt-vip-pane="set">';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('会员档位', 'aksrt') . '</span><select name="level" data-aksrt-vip-level>';
|
||||
echo '<div class="jio-native-form-grid jio-vip-pane" data-jio-vip-pane="set">';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('会员档位', 'jio') . '</span><select name="level" data-jio-vip-level>';
|
||||
foreach ($names as $value => $label) {
|
||||
echo '<option value="' . esc_attr((string) $value) . '">' . esc_html($label) . '</option>';
|
||||
}
|
||||
echo '</select></label>';
|
||||
echo '<label class="aksrt-native-field"><span class="aksrt-native-field__label">' . esc_html__('到期日', 'aksrt') . '</span><input type="date" name="expires_date" min="' . esc_attr($today) . '" required data-aksrt-vip-date>'
|
||||
. '<span class="aksrt-native-field__hint">' . esc_html__('当天 23:59:59 到期', 'aksrt') . '</span></label>';
|
||||
echo '<label class="aksrt-vip-switch is-wide"><input type="checkbox" name="permanent" value="1" data-aksrt-vip-permanent>' . esc_html__('永久会员(不设到期日)', 'aksrt') . '</label>';
|
||||
echo '<p class="aksrt-native-field__hint is-wide">' . esc_html__('设置会员会直接覆盖当前档位与到期时间,并清除回退会员。', 'aksrt') . '</p>';
|
||||
echo '<label class="jio-native-field"><span class="jio-native-field__label">' . esc_html__('到期日', 'jio') . '</span><input type="date" name="expires_date" min="' . esc_attr($today) . '" required data-jio-vip-date>'
|
||||
. '<span class="jio-native-field__hint">' . esc_html__('当天 23:59:59 到期', 'jio') . '</span></label>';
|
||||
echo '<label class="jio-vip-switch is-wide"><input type="checkbox" name="permanent" value="1" data-jio-vip-permanent>' . esc_html__('永久会员(不设到期日)', 'jio') . '</label>';
|
||||
echo '<p class="jio-native-field__hint is-wide">' . esc_html__('设置会员会直接覆盖当前档位与到期时间,并清除回退会员。', 'jio') . '</p>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="aksrt-native-form-grid aksrt-vip-pane" data-aksrt-vip-pane="extend" hidden>';
|
||||
echo '<div class="aksrt-native-field is-wide"><span class="aksrt-native-field__label">' . esc_html__('延长时长', 'aksrt') . '</span><div class="aksrt-vip-duration">'
|
||||
. '<input type="number" name="amount" min="1" max="3650" value="30" required aria-label="' . esc_attr__('延长数量', 'aksrt') . '">'
|
||||
. '<select name="unit" aria-label="' . esc_attr__('延长单位', 'aksrt') . '"><option value="day">' . esc_html__('天', 'aksrt') . '</option><option value="month">' . esc_html__('月', 'aksrt') . '</option></select></div>'
|
||||
. '<span class="aksrt-native-field__hint">' . esc_html__('从当前到期时间起顺延;仅对限时有效会员生效。', 'aksrt') . '</span></div>';
|
||||
echo '<div class="jio-native-form-grid jio-vip-pane" data-jio-vip-pane="extend" hidden>';
|
||||
echo '<div class="jio-native-field is-wide"><span class="jio-native-field__label">' . esc_html__('延长时长', 'jio') . '</span><div class="jio-vip-duration">'
|
||||
. '<input type="number" name="amount" min="1" max="3650" value="30" required aria-label="' . esc_attr__('延长数量', 'jio') . '">'
|
||||
. '<select name="unit" aria-label="' . esc_attr__('延长单位', 'jio') . '"><option value="day">' . esc_html__('天', 'jio') . '</option><option value="month">' . esc_html__('月', 'jio') . '</option></select></div>'
|
||||
. '<span class="jio-native-field__hint">' . esc_html__('从当前到期时间起顺延;仅对限时有效会员生效。', 'jio') . '</span></div>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="aksrt-native-callout is-warning aksrt-vip-pane" data-aksrt-vip-pane="restore" hidden>' . esc_html__('将取消当前限时升级,恢复为回退会员(档位与到期时间按升级前保存的记录)。', 'aksrt') . '</div>';
|
||||
echo '<div class="aksrt-native-callout is-danger aksrt-vip-pane" data-aksrt-vip-pane="disable" hidden>' . esc_html__('停用后立即撤销前端会员权益,并清除回退会员记录。此操作无法撤销,只能重新设置会员。', 'aksrt') . '</div>';
|
||||
echo '<div class="jio-native-callout is-warning jio-vip-pane" data-jio-vip-pane="restore" hidden>' . esc_html__('将取消当前限时升级,恢复为回退会员(档位与到期时间按升级前保存的记录)。', 'jio') . '</div>';
|
||||
echo '<div class="jio-native-callout is-danger jio-vip-pane" data-jio-vip-pane="disable" hidden>' . esc_html__('停用后立即撤销前端会员权益,并清除回退会员记录。此操作无法撤销,只能重新设置会员。', 'jio') . '</div>';
|
||||
|
||||
echo '<label class="aksrt-native-field aksrt-vip-reason"><span class="aksrt-native-field__label">' . esc_html__('变更原因', 'aksrt') . ' <span class="aksrt-native-text--danger" aria-hidden="true">*</span></span>'
|
||||
. '<input type="text" name="reason" required maxlength="255" placeholder="' . esc_attr__('例如:活动赠送、客服补偿、退款撤销', 'aksrt') . '">'
|
||||
. '<span class="aksrt-native-field__hint">' . esc_html__('写入变更记录,便于后续追溯。', 'aksrt') . '</span></label>';
|
||||
echo '<p class="aksrt-native-muted aksrt-vip-bulk-note" data-aksrt-vip-bulk-note hidden></p>';
|
||||
echo '<label class="jio-native-field jio-vip-reason"><span class="jio-native-field__label">' . esc_html__('变更原因', 'jio') . ' <span class="jio-native-text--danger" aria-hidden="true">*</span></span>'
|
||||
. '<input type="text" name="reason" required maxlength="255" placeholder="' . esc_attr__('例如:活动赠送、客服补偿、退款撤销', 'jio') . '">'
|
||||
. '<span class="jio-native-field__hint">' . esc_html__('写入变更记录,便于后续追溯。', 'jio') . '</span></label>';
|
||||
echo '<p class="jio-native-muted jio-vip-bulk-note" data-jio-vip-bulk-note hidden></p>';
|
||||
echo '</div>';
|
||||
|
||||
echo '<footer class="aksrt-vip-dialog__foot"><button type="button" class="button" data-aksrt-vip-close>' . esc_html__('取消', 'aksrt') . '</button>'
|
||||
. '<button type="submit" class="button button-primary" data-aksrt-vip-submit>' . esc_html__('确认变更', 'aksrt') . '</button></footer>';
|
||||
echo '<footer class="jio-vip-dialog__foot"><button type="button" class="button" data-jio-vip-close>' . esc_html__('取消', 'jio') . '</button>'
|
||||
. '<button type="submit" class="button button-primary" data-jio-vip-submit>' . esc_html__('确认变更', 'jio') . '</button></footer>';
|
||||
echo '</form></dialog>';
|
||||
}
|
||||
|
||||
private function render_vip_history(int $page): void {
|
||||
$result = (new \AKSRT\Content\VipMembershipEventRepository())->query($page, self::VIP_PER_PAGE);
|
||||
$result = (new \Jio\Content\VipMembershipEventRepository())->query($page, self::VIP_PER_PAGE);
|
||||
$ops = [
|
||||
'set' => [__('设置会员', 'aksrt'), 'success'],
|
||||
'extend' => [__('延长有效期', 'aksrt'), 'info'],
|
||||
'restore' => [__('恢复回退会员', 'aksrt'), 'warning'],
|
||||
'disable' => [__('停用会员', 'aksrt'), 'danger'],
|
||||
'expire_recover' => [__('到期自动回退', 'aksrt'), 'neutral'],
|
||||
'set' => [__('设置会员', 'jio'), 'success'],
|
||||
'extend' => [__('延长有效期', 'jio'), 'info'],
|
||||
'restore' => [__('恢复回退会员', 'jio'), 'warning'],
|
||||
'disable' => [__('停用会员', 'jio'), 'danger'],
|
||||
'expire_recover' => [__('到期自动回退', 'jio'), 'neutral'],
|
||||
];
|
||||
$statuses = [
|
||||
'succeeded' => [__('成功', 'aksrt'), 'success'],
|
||||
'failed' => [__('失败', 'aksrt'), 'danger'],
|
||||
'pending' => [__('处理中', 'aksrt'), 'warning'],
|
||||
'succeeded' => [__('成功', 'jio'), 'success'],
|
||||
'failed' => [__('失败', 'jio'), 'danger'],
|
||||
'pending' => [__('处理中', 'jio'), 'warning'],
|
||||
];
|
||||
NativeUi::table_card_open(__('变更记录', 'aksrt'), '<span class="aksrt-native-muted">' . esc_html(sprintf(__('共 %d 条', 'aksrt'), (int) $result['total'])) . '</span>');
|
||||
echo '<table class="wp-list-table widefat fixed striped aksrt-vip-history"><thead><tr><th class="column-time">' . esc_html__('时间', 'aksrt') . '</th><th class="column-user">' . esc_html__('用户', 'aksrt') . '</th><th>' . esc_html__('操作', 'aksrt') . '</th><th>' . esc_html__('操作人', 'aksrt') . '</th><th class="column-reason">' . esc_html__('原因', 'aksrt') . '</th><th>' . esc_html__('结果', 'aksrt') . '</th></tr></thead><tbody>';
|
||||
NativeUi::table_card_open(__('变更记录', 'jio'), '<span class="jio-native-muted">' . esc_html(sprintf(__('共 %d 条', 'jio'), (int) $result['total'])) . '</span>');
|
||||
echo '<table class="wp-list-table widefat fixed striped jio-vip-history"><thead><tr><th class="column-time">' . esc_html__('时间', 'jio') . '</th><th class="column-user">' . esc_html__('用户', 'jio') . '</th><th>' . esc_html__('操作', 'jio') . '</th><th>' . esc_html__('操作人', 'jio') . '</th><th class="column-reason">' . esc_html__('原因', 'jio') . '</th><th>' . esc_html__('结果', 'jio') . '</th></tr></thead><tbody>';
|
||||
if ($result['items'] === []) {
|
||||
echo NativeUi::empty_row(6, __('暂无变更记录', 'aksrt'), __('在会员列表中调整会员后会出现在这里。', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
echo NativeUi::empty_row(6, __('暂无变更记录', 'jio'), __('在会员列表中调整会员后会出现在这里。', 'jio')); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
}
|
||||
foreach ($result['items'] as $event) {
|
||||
$created = (string) $event['created_at'];
|
||||
$time = $created !== '' ? get_date_from_gmt($created, 'Y-m-d H:i') : '—';
|
||||
$actor = (string) ($event['actor_name'] ?? '');
|
||||
if ($actor === '') {
|
||||
$actor = ((string) ($event['source'] ?? '') === 'admin') ? '—' : __('系统', 'aksrt');
|
||||
$actor = ((string) ($event['source'] ?? '') === 'admin') ? '—' : __('系统', 'jio');
|
||||
}
|
||||
$result_html = NativeUi::status_badge((string) $event['status'], $statuses);
|
||||
if ((string) $event['status'] === 'failed' && (string) ($event['error_message'] ?? '') !== '') {
|
||||
$result_html .= '<span class="aksrt-vip-note aksrt-native-text--danger">' . esc_html((string) $event['error_message']) . '</span>';
|
||||
$result_html .= '<span class="jio-vip-note jio-native-text--danger">' . esc_html((string) $event['error_message']) . '</span>';
|
||||
}
|
||||
echo '<tr><td class="column-time">' . esc_html($time) . (!empty($event['batch_uuid']) ? '<span class="aksrt-vip-note aksrt-native-muted">' . esc_html__('批量', 'aksrt') . '</span>' : '') . '</td>'
|
||||
echo '<tr><td class="column-time">' . esc_html($time) . (!empty($event['batch_uuid']) ? '<span class="jio-vip-note jio-native-muted">' . esc_html__('批量', 'jio') . '</span>' : '') . '</td>'
|
||||
. '<td class="column-user">' . NativeUi::user_cell((int) $event['user_id'], (string) ($event['display_name'] ?: $event['user_login'] ?: '')) . '</td>' // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
. '<td>' . NativeUi::status_badge((string) $event['operation'], $ops) . '</td>' // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
|
||||
. '<td>' . esc_html($actor) . '</td>'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Native WP nav-menu item options using legacy's aksrt_menu_options meta contract.
|
||||
* Native WP nav-menu item options using legacy's jio_menu_options meta contract.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -22,11 +22,11 @@ class NavMenuOptions {
|
||||
return;
|
||||
}
|
||||
wp_enqueue_media();
|
||||
aksrt_icon_picker_assets();
|
||||
jio_icon_picker_assets();
|
||||
$path = get_template_directory() . '/assets/js/admin-nav-menu-senior.js';
|
||||
if (file_exists($path)) {
|
||||
wp_enqueue_script(
|
||||
'aksrt-admin-nav-menu-senior',
|
||||
'jio-admin-nav-menu-senior',
|
||||
get_template_directory_uri() . '/assets/js/admin-nav-menu-senior.js',
|
||||
['jquery'],
|
||||
(string) filemtime($path),
|
||||
@@ -41,7 +41,7 @@ class NavMenuOptions {
|
||||
*/
|
||||
public static function render_fields($item_id, $item): void {
|
||||
$item_id = (int) $item_id;
|
||||
$opts = get_post_meta($item_id, 'aksrt_menu_options', true);
|
||||
$opts = get_post_meta($item_id, 'jio_menu_options', true);
|
||||
$opts = is_array($opts) ? $opts : [];
|
||||
$icon = (string) ($opts['icon'] ?? '');
|
||||
$badge = (string) ($opts['badge'] ?? '');
|
||||
@@ -55,63 +55,63 @@ class NavMenuOptions {
|
||||
? wp_json_encode($opts['multi_column_links_opts'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||||
: self::sample_multi_json();
|
||||
?>
|
||||
<p class="field-aksrt-icon description description-wide">
|
||||
<label for="edit-menu-item-aksrt-icon-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('图标', 'aksrt'); ?>
|
||||
<p class="field-jio-icon description description-wide">
|
||||
<label for="edit-menu-item-jio-icon-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('图标', 'jio'); ?>
|
||||
</label>
|
||||
<?php
|
||||
echo aksrt_icon_picker_field(
|
||||
sprintf('aksrt_menu_options[%d][icon]', $item_id),
|
||||
sprintf('edit-menu-item-aksrt-icon-%d', $item_id),
|
||||
echo jio_icon_picker_field(
|
||||
sprintf('jio_menu_options[%d][icon]', $item_id),
|
||||
sprintf('edit-menu-item-jio-icon-%d', $item_id),
|
||||
$icon
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<p class="field-aksrt-badge description description-thin">
|
||||
<label for="edit-menu-item-aksrt-badge-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('徽章文字', 'aksrt'); ?><br>
|
||||
<input type="text" id="edit-menu-item-aksrt-badge-<?php echo esc_attr((string) $item_id); ?>"
|
||||
class="widefat" name="aksrt_menu_options[<?php echo esc_attr((string) $item_id); ?>][badge]"
|
||||
<p class="field-jio-badge description description-thin">
|
||||
<label for="edit-menu-item-jio-badge-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('徽章文字', 'jio'); ?><br>
|
||||
<input type="text" id="edit-menu-item-jio-badge-<?php echo esc_attr((string) $item_id); ?>"
|
||||
class="widefat" name="jio_menu_options[<?php echo esc_attr((string) $item_id); ?>][badge]"
|
||||
value="<?php echo esc_attr($badge); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p class="field-aksrt-badge-class description description-thin">
|
||||
<label for="edit-menu-item-aksrt-badge-class-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('徽章 class', 'aksrt'); ?><br>
|
||||
<input type="text" id="edit-menu-item-aksrt-badge-class-<?php echo esc_attr((string) $item_id); ?>"
|
||||
class="widefat" name="aksrt_menu_options[<?php echo esc_attr((string) $item_id); ?>][badge_class]"
|
||||
<p class="field-jio-badge-class description description-thin">
|
||||
<label for="edit-menu-item-jio-badge-class-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('徽章 class', 'jio'); ?><br>
|
||||
<input type="text" id="edit-menu-item-jio-badge-class-<?php echo esc_attr((string) $item_id); ?>"
|
||||
class="widefat" name="jio_menu_options[<?php echo esc_attr((string) $item_id); ?>][badge_class]"
|
||||
value="<?php echo esc_attr($badge_class); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p class="field-aksrt-submenu description description-wide">
|
||||
<p class="field-jio-submenu description description-wide">
|
||||
<label>
|
||||
<input type="checkbox" name="aksrt_menu_options[<?php echo esc_attr((string) $item_id); ?>][submenu_s]" value="1" <?php checked($submenu_s); ?>>
|
||||
<?php esc_html_e('启用高级子菜单(仅一级菜单)', 'aksrt'); ?>
|
||||
<input type="checkbox" name="jio_menu_options[<?php echo esc_attr((string) $item_id); ?>][submenu_s]" value="1" <?php checked($submenu_s); ?>>
|
||||
<?php esc_html_e('启用高级子菜单(仅一级菜单)', 'jio'); ?>
|
||||
</label>
|
||||
</p>
|
||||
<p class="field-aksrt-submenu-type description description-wide">
|
||||
<label for="edit-menu-item-aksrt-submenu-type-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('高级子菜单类型', 'aksrt'); ?><br>
|
||||
<select id="edit-menu-item-aksrt-submenu-type-<?php echo esc_attr((string) $item_id); ?>"
|
||||
name="aksrt_menu_options[<?php echo esc_attr((string) $item_id); ?>][submenu_type]">
|
||||
<option value="" <?php selected($submenu_type, ''); ?>><?php esc_html_e('无', 'aksrt'); ?></option>
|
||||
<option value="graphic_card" <?php selected($submenu_type, 'graphic_card'); ?>><?php esc_html_e('图文卡片', 'aksrt'); ?></option>
|
||||
<option value="multi_column_links" <?php selected($submenu_type, 'multi_column_links'); ?>><?php esc_html_e('多列链接', 'aksrt'); ?></option>
|
||||
<p class="field-jio-submenu-type description description-wide">
|
||||
<label for="edit-menu-item-jio-submenu-type-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('高级子菜单类型', 'jio'); ?><br>
|
||||
<select id="edit-menu-item-jio-submenu-type-<?php echo esc_attr((string) $item_id); ?>"
|
||||
name="jio_menu_options[<?php echo esc_attr((string) $item_id); ?>][submenu_type]">
|
||||
<option value="" <?php selected($submenu_type, ''); ?>><?php esc_html_e('无', 'jio'); ?></option>
|
||||
<option value="graphic_card" <?php selected($submenu_type, 'graphic_card'); ?>><?php esc_html_e('图文卡片', 'jio'); ?></option>
|
||||
<option value="multi_column_links" <?php selected($submenu_type, 'multi_column_links'); ?>><?php esc_html_e('多列链接', 'jio'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p class="field-aksrt-graphic description description-wide">
|
||||
<label for="edit-menu-item-aksrt-graphic-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('图文卡片 JSON(type=graphic_card)', 'aksrt'); ?><br>
|
||||
<textarea rows="8" class="widefat code" id="edit-menu-item-aksrt-graphic-<?php echo esc_attr((string) $item_id); ?>"
|
||||
name="aksrt_menu_options[<?php echo esc_attr((string) $item_id); ?>][graphic_card_opts_json]"><?php echo esc_textarea((string) $graphic_json); ?></textarea>
|
||||
<p class="field-jio-graphic description description-wide">
|
||||
<label for="edit-menu-item-jio-graphic-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('图文卡片 JSON(type=graphic_card)', 'jio'); ?><br>
|
||||
<textarea rows="8" class="widefat code" id="edit-menu-item-jio-graphic-<?php echo esc_attr((string) $item_id); ?>"
|
||||
name="jio_menu_options[<?php echo esc_attr((string) $item_id); ?>][graphic_card_opts_json]"><?php echo esc_textarea((string) $graphic_json); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p class="field-aksrt-multi description description-wide">
|
||||
<label for="edit-menu-item-aksrt-multi-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('多列链接 JSON(type=multi_column_links)', 'aksrt'); ?><br>
|
||||
<textarea rows="8" class="widefat code" id="edit-menu-item-aksrt-multi-<?php echo esc_attr((string) $item_id); ?>"
|
||||
name="aksrt_menu_options[<?php echo esc_attr((string) $item_id); ?>][multi_column_links_opts_json]"><?php echo esc_textarea((string) $multi_json); ?></textarea>
|
||||
<p class="field-jio-multi description description-wide">
|
||||
<label for="edit-menu-item-jio-multi-<?php echo esc_attr((string) $item_id); ?>">
|
||||
<?php esc_html_e('多列链接 JSON(type=multi_column_links)', 'jio'); ?><br>
|
||||
<textarea rows="8" class="widefat code" id="edit-menu-item-jio-multi-<?php echo esc_attr((string) $item_id); ?>"
|
||||
name="jio_menu_options[<?php echo esc_attr((string) $item_id); ?>][multi_column_links_opts_json]"><?php echo esc_textarea((string) $multi_json); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
@@ -124,14 +124,14 @@ class NavMenuOptions {
|
||||
public static function save($menu_id, $menu_item_db_id): void {
|
||||
$menu_item_db_id = (int) $menu_item_db_id;
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
if (empty($_POST['aksrt_menu_options'][ $menu_item_db_id ]) || !is_array($_POST['aksrt_menu_options'][ $menu_item_db_id ])) {
|
||||
if (empty($_POST['jio_menu_options'][ $menu_item_db_id ]) || !is_array($_POST['jio_menu_options'][ $menu_item_db_id ])) {
|
||||
return;
|
||||
}
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$raw = wp_unslash($_POST['aksrt_menu_options'][ $menu_item_db_id ]);
|
||||
$raw = wp_unslash($_POST['jio_menu_options'][ $menu_item_db_id ]);
|
||||
|
||||
$clean = [
|
||||
'icon' => aksrt_sanitize_icon_value((string) ($raw['icon'] ?? '')),
|
||||
'icon' => jio_sanitize_icon_value((string) ($raw['icon'] ?? '')),
|
||||
'badge' => sanitize_text_field((string) ($raw['badge'] ?? '')),
|
||||
'badge_class' => sanitize_html_class((string) ($raw['badge_class'] ?? '')),
|
||||
'submenu_s' => !empty($raw['submenu_s']),
|
||||
@@ -147,7 +147,7 @@ class NavMenuOptions {
|
||||
$clean['multi_column_links_opts'] = self::sanitize_multi_opts($multi);
|
||||
}
|
||||
|
||||
update_post_meta($menu_item_db_id, 'aksrt_menu_options', $clean);
|
||||
update_post_meta($menu_item_db_id, 'jio_menu_options', $clean);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
/**
|
||||
* Per-page legacy header slider configuration.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class PageConfigMetaBox {
|
||||
|
||||
private const META_KEY = 'page_config';
|
||||
private const NONCE_ACTION = 'aksrt_page_config_save';
|
||||
private const NONCE_NAME = 'aksrt_page_config_nonce';
|
||||
private const NONCE_ACTION = 'jio_page_config_save';
|
||||
private const NONCE_NAME = 'jio_page_config_nonce';
|
||||
|
||||
public static function init(): void {
|
||||
add_action('add_meta_boxes_page', [self::class, 'register']);
|
||||
@@ -31,13 +31,13 @@ final class PageConfigMetaBox {
|
||||
}
|
||||
wp_enqueue_style('wp-color-picker');
|
||||
wp_enqueue_script('wp-color-picker');
|
||||
wp_add_inline_script('wp-color-picker', "jQuery(function($){ $('.aksrt-page-config-color').wpColorPicker(); });");
|
||||
wp_add_inline_script('wp-color-picker', "jQuery(function($){ $('.jio-page-config-color').wpColorPicker(); });");
|
||||
}
|
||||
|
||||
public static function register(): void {
|
||||
add_meta_box(
|
||||
'aksrt-page-config',
|
||||
__('页面配置', 'aksrt'),
|
||||
'jio-page-config',
|
||||
__('页面配置', 'jio'),
|
||||
[self::class, 'render'],
|
||||
'page',
|
||||
'normal',
|
||||
@@ -58,40 +58,40 @@ final class PageConfigMetaBox {
|
||||
];
|
||||
?>
|
||||
<style>
|
||||
.aksrt-page-config-grid{display:grid;grid-template-columns:220px minmax(0,1fr);gap:14px 20px;align-items:start}
|
||||
.aksrt-page-config-grid>label{font-weight:600;padding-top:7px}.aksrt-page-config-grid textarea{width:100%;font-family:Consolas,monospace}
|
||||
.aksrt-page-config-grid .description{margin:4px 0 0}.aksrt-page-config-wide{grid-column:1/-1}
|
||||
@media(max-width:782px){.aksrt-page-config-grid{grid-template-columns:1fr}.aksrt-page-config-wide{grid-column:auto}}
|
||||
.jio-page-config-grid{display:grid;grid-template-columns:220px minmax(0,1fr);gap:14px 20px;align-items:start}
|
||||
.jio-page-config-grid>label{font-weight:600;padding-top:7px}.jio-page-config-grid textarea{width:100%;font-family:Consolas,monospace}
|
||||
.jio-page-config-grid .description{margin:4px 0 0}.jio-page-config-wide{grid-column:1/-1}
|
||||
@media(max-width:782px){.jio-page-config-grid{grid-template-columns:1fr}.jio-page-config-wide{grid-column:auto}}
|
||||
</style>
|
||||
<div class="aksrt-page-config-grid">
|
||||
<label for="aksrt-header-slider-show"><?php esc_html_e('独立配置顶部多功能组件', 'aksrt'); ?></label>
|
||||
<div class="jio-page-config-grid">
|
||||
<label for="jio-header-slider-show"><?php esc_html_e('独立配置顶部多功能组件', 'jio'); ?></label>
|
||||
<div>
|
||||
<label><input id="aksrt-header-slider-show" type="checkbox" name="page_config[header_slider_show]" value="1" <?php checked(!empty($config['header_slider_show'])); ?>> <?php esc_html_e('为当前页面使用独立内容和显示规则', 'aksrt'); ?></label>
|
||||
<p class="description"><?php esc_html_e('仅当主题设置的“顶部多功能组件”已勾选当前页面所属类型时生效;此处不能单独启用组件。', 'aksrt'); ?></p>
|
||||
<label><input id="jio-header-slider-show" type="checkbox" name="page_config[header_slider_show]" value="1" <?php checked(!empty($config['header_slider_show'])); ?>> <?php esc_html_e('为当前页面使用独立内容和显示规则', 'jio'); ?></label>
|
||||
<p class="description"><?php esc_html_e('仅当主题设置的“顶部多功能组件”已勾选当前页面所属类型时生效;此处不能单独启用组件。', 'jio'); ?></p>
|
||||
</div>
|
||||
|
||||
<label><?php esc_html_e('显示规则', 'aksrt'); ?></label>
|
||||
<label><?php esc_html_e('显示规则', 'jio'); ?></label>
|
||||
<div>
|
||||
<?php foreach (['' => __('全部显示', 'aksrt'), 'only_pc' => __('仅在 PC 端显示', 'aksrt'), 'only_sm' => __('仅在移动端显示', 'aksrt')] as $value => $label) : ?>
|
||||
<?php foreach (['' => __('全部显示', 'jio'), 'only_pc' => __('仅在 PC 端显示', 'jio'), 'only_sm' => __('仅在移动端显示', 'jio')] as $value => $label) : ?>
|
||||
<label style="margin-right:16px"><input type="radio" name="page_config[header_slider_show_type]" value="<?php echo esc_attr($value); ?>" <?php checked($show_type, $value); ?>> <?php echo esc_html($label); ?></label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<label for="aksrt-header-slider-nav-color"><?php esc_html_e('导航栏文字颜色', 'aksrt'); ?></label>
|
||||
<div><input id="aksrt-header-slider-nav-color" class="aksrt-page-config-color" type="text" name="page_config[header_slider_nav_color]" value="<?php echo esc_attr((string) ($config['header_slider_nav_color'] ?? '#ffffff')); ?>" data-default-color="#ffffff"></div>
|
||||
<label for="jio-header-slider-nav-color"><?php esc_html_e('导航栏文字颜色', 'jio'); ?></label>
|
||||
<div><input id="jio-header-slider-nav-color" class="jio-page-config-color" type="text" name="page_config[header_slider_nav_color]" value="<?php echo esc_attr((string) ($config['header_slider_nav_color'] ?? '#ffffff')); ?>" data-default-color="#ffffff"></div>
|
||||
|
||||
<?php self::render_json_field('header_slider', __('背景内容', 'aksrt'), $config['header_slider'] ?? self::slide_defaults(), 16); ?>
|
||||
<?php self::render_json_field('header_slider_option', __('背景显示设置', 'aksrt'), $config['header_slider_option'] ?? self::slider_defaults(), 12); ?>
|
||||
<?php self::render_json_field('header_slider', __('背景内容', 'jio'), $config['header_slider'] ?? self::slide_defaults(), 16); ?>
|
||||
<?php self::render_json_field('header_slider_option', __('背景显示设置', 'jio'), $config['header_slider_option'] ?? self::slider_defaults(), 12); ?>
|
||||
|
||||
<label><?php esc_html_e('叠加搜索组件', 'aksrt'); ?></label>
|
||||
<label><?php esc_html_e('叠加搜索组件', 'jio'); ?></label>
|
||||
<div><?php self::render_devices('header_slider_search_s', $devices['header_slider_search_s']); ?></div>
|
||||
<?php self::render_json_field('header_slider_search_option', __('搜索组件设置', 'aksrt'), $config['header_slider_search_option'] ?? self::search_defaults(), 12); ?>
|
||||
<?php self::render_json_field('header_slider_search_option', __('搜索组件设置', 'jio'), $config['header_slider_search_option'] ?? self::search_defaults(), 12); ?>
|
||||
|
||||
<label><?php esc_html_e('叠加封面卡片组件', 'aksrt'); ?></label>
|
||||
<label><?php esc_html_e('叠加封面卡片组件', 'jio'); ?></label>
|
||||
<div><?php self::render_devices('header_slider_card_s', $devices['header_slider_card_s']); ?></div>
|
||||
<?php self::render_json_field('header_slider_card_option', __('图标卡片设置', 'aksrt'), $config['header_slider_card_option'] ?? self::card_defaults(), 16); ?>
|
||||
<?php self::render_json_field('header_slider_card_option', __('图标卡片设置', 'jio'), $config['header_slider_card_option'] ?? self::card_defaults(), 16); ?>
|
||||
</div>
|
||||
<p class="description"><?php esc_html_e('复杂字段使用参考主题原始数组结构的 JSON;保存后统一写入 page_config 序列化元数据,不读取任何旧字段。', 'aksrt'); ?></p>
|
||||
<p class="description"><?php esc_html_e('复杂字段使用参考主题原始数组结构的 JSON;保存后统一写入 page_config 序列化元数据,不读取任何旧字段。', 'jio'); ?></p>
|
||||
<?php
|
||||
}
|
||||
|
||||
@@ -99,14 +99,14 @@ final class PageConfigMetaBox {
|
||||
private static function render_json_field(string $key, string $label, $value, int $rows): void {
|
||||
$json = wp_json_encode(is_array($value) ? $value : [], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
?>
|
||||
<label for="aksrt-<?php echo esc_attr($key); ?>"><?php echo esc_html($label); ?></label>
|
||||
<div><textarea id="aksrt-<?php echo esc_attr($key); ?>" name="page_config[<?php echo esc_attr($key); ?>]" rows="<?php echo (int) $rows; ?>"><?php echo esc_textarea((string) $json); ?></textarea></div>
|
||||
<label for="jio-<?php echo esc_attr($key); ?>"><?php echo esc_html($label); ?></label>
|
||||
<div><textarea id="jio-<?php echo esc_attr($key); ?>" name="page_config[<?php echo esc_attr($key); ?>]" rows="<?php echo (int) $rows; ?>"><?php echo esc_textarea((string) $json); ?></textarea></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/** @param list<string> $selected */
|
||||
private static function render_devices(string $key, array $selected): void {
|
||||
foreach (['pc_s' => __('PC 端显示', 'aksrt'), 'm_s' => __('移动端显示', 'aksrt')] as $value => $label) {
|
||||
foreach (['pc_s' => __('PC 端显示', 'jio'), 'm_s' => __('移动端显示', 'jio')] as $value => $label) {
|
||||
printf(
|
||||
'<label style="margin-right:16px"><input type="checkbox" name="page_config[%1$s][]" value="%2$s" %3$s> %4$s</label>',
|
||||
esc_attr($key),
|
||||
@@ -136,7 +136,7 @@ final class PageConfigMetaBox {
|
||||
$config = [
|
||||
'header_slider_show' => !empty($raw['header_slider_show']),
|
||||
'header_slider_show_type' => self::enum((string) ($raw['header_slider_show_type'] ?? ''), ['', 'only_pc', 'only_sm']),
|
||||
'header_slider_nav_color' => \AKSRT\Core\Color::sanitize((string) ($raw['header_slider_nav_color'] ?? '')) ?: '#ffffff',
|
||||
'header_slider_nav_color' => \Jio\Core\Color::sanitize((string) ($raw['header_slider_nav_color'] ?? '')) ?: '#ffffff',
|
||||
'header_slider' => self::sanitize_slides(self::decode_json($raw['header_slider'] ?? '')),
|
||||
'header_slider_option' => self::sanitize_slider_options(self::decode_json($raw['header_slider_option'] ?? '')),
|
||||
'header_slider_search_s' => self::sanitize_devices($raw['header_slider_search_s'] ?? []),
|
||||
@@ -266,12 +266,12 @@ final class PageConfigMetaBox {
|
||||
$cards[] = [
|
||||
// legacy echoes card title/desc raw (admin authors <badge> etc.);
|
||||
// wp_kses_post() would strip the custom badge element.
|
||||
'title' => aksrt_icon_card_kses((string) ($card['title'] ?? '')),
|
||||
'desc' => aksrt_icon_card_kses((string) ($card['desc'] ?? '')),
|
||||
'title' => jio_icon_card_kses((string) ($card['title'] ?? '')),
|
||||
'desc' => jio_icon_card_kses((string) ($card['desc'] ?? '')),
|
||||
// Icon picker values may carry an inline "customsvg-<svg>" payload;
|
||||
// sanitize_text_field would strip it down to a bare prefix.
|
||||
'icon' => function_exists('aksrt_sanitize_icon_value')
|
||||
? aksrt_sanitize_icon_value((string) ($card['icon'] ?? ''))
|
||||
'icon' => function_exists('jio_sanitize_icon_value')
|
||||
? jio_sanitize_icon_value((string) ($card['icon'] ?? ''))
|
||||
: sanitize_text_field((string) ($card['icon'] ?? '')),
|
||||
'customize_icon' => wp_kses_post((string) ($card['customize_icon'] ?? '')),
|
||||
'icon_class' => sanitize_text_field((string) ($card['icon_class'] ?? '')),
|
||||
@@ -296,7 +296,7 @@ final class PageConfigMetaBox {
|
||||
|
||||
/** @return list<array<string,mixed>> */
|
||||
private static function slide_defaults(): array {
|
||||
$base = defined('AKSRT_URI') ? trailingslashit(AKSRT_URI) . 'assets/img/' : '';
|
||||
$base = defined('JIO_URI') ? trailingslashit(JIO_URI) . 'assets/img/' : '';
|
||||
$slides = [];
|
||||
foreach (['slider-bg.jpg', 'user_t.jpg'] as $image) {
|
||||
$slides[] = [
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* Page/display schema option search.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* 支付中心概览的数据接口:GET aksrt/v1/admin/pay-overview?from=Y-m-d&to=Y-m-d&group=
|
||||
* 支付中心概览的数据接口:GET jio/v1/admin/pay-overview?from=Y-m-d&to=Y-m-d&group=
|
||||
*
|
||||
* 仅管理员(manage_options + wp_rest nonce,见 ControllerBase::check_permission)。
|
||||
* 统计口径见 \AKSRT\Payment\OrderAnalytics。
|
||||
* 统计口径见 \Jio\Payment\OrderAnalytics。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
use AKSRT\Payment\OrderAnalytics;
|
||||
use AKSRT\Payment\OrderAnalyticsDaily;
|
||||
use Jio\Payment\OrderAnalytics;
|
||||
use Jio\Payment\OrderAnalyticsDaily;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -49,7 +49,7 @@ class PayOverviewController extends ControllerBase {
|
||||
return $this->error($error, 400);
|
||||
}
|
||||
if ($group !== '' && !isset(OrderAnalytics::group_labels()[$group])) {
|
||||
return $this->error(__('商品分类无效。', 'aksrt'), 400);
|
||||
return $this->error(__('商品分类无效。', 'jio'), 400);
|
||||
}
|
||||
|
||||
return $this->success(['data' => OrderAnalytics::get_instance()->report($from, $to, $group)]);
|
||||
@@ -58,17 +58,17 @@ class PayOverviewController extends ControllerBase {
|
||||
/** 区间校验:合法日期、起止有序、不晚于今天、不超过上限天数。返回空串表示通过。 */
|
||||
public static function validate_range(string $from, string $to): string {
|
||||
if (!OrderAnalyticsDaily::is_date($from) || !OrderAnalyticsDaily::is_date($to)) {
|
||||
return __('日期格式无效。', 'aksrt');
|
||||
return __('日期格式无效。', 'jio');
|
||||
}
|
||||
if ($from > $to) {
|
||||
return __('开始日期不能晚于结束日期。', 'aksrt');
|
||||
return __('开始日期不能晚于结束日期。', 'jio');
|
||||
}
|
||||
if ($to > current_time('Y-m-d')) {
|
||||
return __('结束日期不能晚于今天。', 'aksrt');
|
||||
return __('结束日期不能晚于今天。', 'jio');
|
||||
}
|
||||
if (count(OrderAnalyticsDaily::dates($from, $to)) > OrderAnalytics::MAX_RANGE_DAYS) {
|
||||
/* translators: %d max days */
|
||||
return sprintf(__('统计区间最长 %d 天。', 'aksrt'), OrderAnalytics::MAX_RANGE_DAYS);
|
||||
return sprintf(__('统计区间最长 %d 天。', 'jio'), OrderAnalytics::MAX_RANGE_DAYS);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -32,13 +32,13 @@ class PermissionsController extends ControllerBase {
|
||||
}
|
||||
|
||||
public function get_capabilities(): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Settings\Settings')) {
|
||||
return $this->error(__('能力矩阵模块不可用', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Settings\Settings')) {
|
||||
return $this->error(__('能力矩阵模块不可用', 'jio'), 500);
|
||||
}
|
||||
|
||||
$caps = \AKSRT\Settings\Settings::get_user_capabilities();
|
||||
$defs = \AKSRT\Settings\Settings::cap_definitions();
|
||||
$columns = \AKSRT\Settings\Settings::cap_role_columns();
|
||||
$caps = \Jio\Settings\Settings::get_user_capabilities();
|
||||
$defs = \Jio\Settings\Settings::cap_definitions();
|
||||
$columns = \Jio\Settings\Settings::cap_role_columns();
|
||||
|
||||
return $this->success([
|
||||
'data' => [
|
||||
@@ -54,19 +54,19 @@ class PermissionsController extends ControllerBase {
|
||||
}
|
||||
|
||||
public function save_capabilities(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Settings\Settings')) {
|
||||
return $this->error(__('能力矩阵模块不可用', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Settings\Settings')) {
|
||||
return $this->error(__('能力矩阵模块不可用', 'jio'), 500);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$input = $params['user_caps'] ?? null;
|
||||
|
||||
if (!is_array($input)) {
|
||||
return $this->error(__('无效的能力矩阵数据', 'aksrt'), 422);
|
||||
return $this->error(__('无效的能力矩阵数据', 'jio'), 422);
|
||||
}
|
||||
|
||||
$defs = \AKSRT\Settings\Settings::cap_definitions();
|
||||
$columns = \AKSRT\Settings\Settings::cap_role_columns();
|
||||
$defs = \Jio\Settings\Settings::cap_definitions();
|
||||
$columns = \Jio\Settings\Settings::cap_role_columns();
|
||||
$clean = [];
|
||||
|
||||
foreach ($defs as $cap_id => $def) {
|
||||
@@ -84,13 +84,13 @@ class PermissionsController extends ControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
$all = \AKSRT\Settings\Settings::get_all();
|
||||
$all = \Jio\Settings\Settings::get_all();
|
||||
$all['user_caps'] = $clean;
|
||||
update_option('aksrt_settings', $all);
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
update_option('jio_settings', $all);
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
|
||||
return $this->success([
|
||||
'message' => __('能力矩阵已保存', 'aksrt'),
|
||||
'message' => __('能力矩阵已保存', 'jio'),
|
||||
'data' => [
|
||||
'user_caps' => $clean,
|
||||
],
|
||||
@@ -101,27 +101,27 @@ class PermissionsController extends ControllerBase {
|
||||
$buckets = [
|
||||
'user' => [
|
||||
'key' => 'user',
|
||||
'title' => __('用户治理', 'aksrt'),
|
||||
'title' => __('用户治理', 'jio'),
|
||||
'items' => [],
|
||||
],
|
||||
'post' => [
|
||||
'key' => 'post',
|
||||
'title' => __('投稿权限', 'aksrt'),
|
||||
'title' => __('投稿权限', 'jio'),
|
||||
'items' => [],
|
||||
],
|
||||
'comment' => [
|
||||
'key' => 'comment',
|
||||
'title' => __('评论权限', 'aksrt'),
|
||||
'title' => __('评论权限', 'jio'),
|
||||
'items' => [],
|
||||
],
|
||||
'upload' => [
|
||||
'key' => 'upload',
|
||||
'title' => __('上传权限', 'aksrt'),
|
||||
'title' => __('上传权限', 'jio'),
|
||||
'items' => [],
|
||||
],
|
||||
'download' => [
|
||||
'key' => 'download',
|
||||
'title' => __('下载权限', 'aksrt'),
|
||||
'title' => __('下载权限', 'jio'),
|
||||
'items' => [],
|
||||
],
|
||||
];
|
||||
@@ -162,11 +162,11 @@ class PermissionsController extends ControllerBase {
|
||||
|
||||
private function get_capability_column_labels(): array {
|
||||
return [
|
||||
'all' => __('所有人', 'aksrt'),
|
||||
'logged' => __('已登录', 'aksrt'),
|
||||
'level' => __('等级≥', 'aksrt'),
|
||||
'vip' => __('VIP≥', 'aksrt'),
|
||||
'auth' => __('已认证', 'aksrt'),
|
||||
'all' => __('所有人', 'jio'),
|
||||
'logged' => __('已登录', 'jio'),
|
||||
'level' => __('等级≥', 'jio'),
|
||||
'vip' => __('VIP≥', 'jio'),
|
||||
'auth' => __('已认证', 'jio'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -18,7 +18,7 @@ class GlobalSchema {
|
||||
'key' => 'common',
|
||||
'title' => '常用功能',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'performance', 'title' => '图像异步懒加载', 'fields' => [
|
||||
['key' => 'lazy_posts_thumb', 'label' => '文章缩略图懒加载', 'type' => 'switch', 'default' => true],
|
||||
@@ -41,7 +41,7 @@ class GlobalSchema {
|
||||
'key' => 'logo',
|
||||
'title' => 'LOGO图像',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'logo', 'title' => 'Logo 设置', 'fields' => [
|
||||
['key' => 'site_logo', 'label' => '站点 Logo', 'type' => 'media', 'default' => ''],
|
||||
@@ -59,7 +59,7 @@ class GlobalSchema {
|
||||
'key' => 'seo',
|
||||
'title' => 'SEO优化',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => 'SEO基础', 'fields' => [
|
||||
['key' => 'seo_enabled', 'label' => '启用核心SEO优化', 'type' => 'switch', 'default' => true, 'help' => '关闭后主题不再输出 SEO head 标签。'],
|
||||
@@ -98,7 +98,7 @@ class GlobalSchema {
|
||||
'key' => 'layout',
|
||||
'title' => '显示&布局',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'color', 'title' => '色彩方案', 'fields' => [
|
||||
['key' => 'theme_mode', 'label' => '主题模式', 'type' => 'radio', 'default' => 'time-auto', 'help' => '此处设置为默认风格,实际显示风格以用户设置优先。如需固定风格,则关闭还需下方主题切换按钮', 'options' => [
|
||||
@@ -164,7 +164,7 @@ class GlobalSchema {
|
||||
'key' => 'code-highlight',
|
||||
'title' => '代码高亮',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '代码高亮设置', 'fields' => [
|
||||
['key' => 'highlight_kg', 'label' => '代码高亮开关', 'type' => 'switch', 'default' => true],
|
||||
@@ -202,7 +202,7 @@ class GlobalSchema {
|
||||
'key' => 'system-notice',
|
||||
'title' => '系统公告',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '公告设置', 'fields' => [
|
||||
['key' => 'system_notice_enabled', 'label' => '启用系统公告', 'type' => 'switch', 'default' => true],
|
||||
@@ -292,7 +292,7 @@ class GlobalSchema {
|
||||
'key' => 'search',
|
||||
'title' => '搜索功能',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '搜索基础', 'fields' => [
|
||||
['key' => 'search_placeholder', 'label' => '搜索框提示文字', 'type' => 'input', 'default' => '开启精彩搜索'],
|
||||
@@ -335,12 +335,12 @@ class GlobalSchema {
|
||||
['key' => 'meilisearch', 'title' => 'Meilisearch 智能搜索', 'fields' => [
|
||||
['key' => 'search_ms_s', 'label' => '启用 Meilisearch', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'search_ms_opt', 'label' => 'Meilisearch 参数', 'type' => 'fieldset', 'sanitize_type' => 'array', 'default' => [
|
||||
'api_url' => 'http://127.0.0.1:7700', 'api_key' => '', 'index_prefix' => 'aksrt',
|
||||
'api_url' => 'http://127.0.0.1:7700', 'api_key' => '', 'index_prefix' => 'jio',
|
||||
'synonyms' => [['key' => 'wordpress', 'val' => 'wp,wordp,wordpre']],
|
||||
], 'dependsOn' => ['key' => 'search_ms_s', 'value' => true], 'fields' => [
|
||||
['key' => 'api_url', 'label' => 'API 地址', 'type' => 'input', 'default' => 'http://127.0.0.1:7700'],
|
||||
['key' => 'api_key', 'label' => 'API 密钥', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'index_prefix', 'label' => '索引前缀', 'type' => 'input', 'default' => 'aksrt'],
|
||||
['key' => 'index_prefix', 'label' => '索引前缀', 'type' => 'input', 'default' => 'jio'],
|
||||
['key' => 'synonyms', 'label' => '同义词', 'type' => 'repeater', 'default' => [['key' => 'wordpress', 'val' => 'wp,wordp,wordpre']], 'addText' => '添加同义词', 'fields' => [
|
||||
['key' => 'key', 'label' => '关键词', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'val', 'label' => '同义词', 'type' => 'textarea', 'default' => ''],
|
||||
@@ -353,7 +353,7 @@ class GlobalSchema {
|
||||
'key' => 'share',
|
||||
'title' => '社交分享',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [[
|
||||
'key' => 'share',
|
||||
'title' => '社交分享',
|
||||
@@ -385,12 +385,12 @@ class GlobalSchema {
|
||||
],
|
||||
]],
|
||||
],
|
||||
'email' => \AKSRT\Settings\MailTemplates::build_schema(),
|
||||
'email' => \Jio\Settings\MailTemplates::build_schema(),
|
||||
'custom-code' => [
|
||||
'key' => 'custom-code',
|
||||
'title' => '自定义代码',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'css', 'title' => '自定义 CSS', 'fields' => [
|
||||
['key' => 'custom_css', 'label' => '自定义 CSS', 'type' => 'code', 'language' => 'css', 'default' => ''],
|
||||
@@ -408,7 +408,7 @@ class GlobalSchema {
|
||||
'key' => 'i18n',
|
||||
'title' => '多语言翻译',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '基础配置', 'fields' => [
|
||||
['key' => 'i18n_enabled', 'label' => '启用多语言', 'type' => 'switch', 'default' => false],
|
||||
@@ -423,7 +423,7 @@ class GlobalSchema {
|
||||
['label' => 'Español', 'value' => 'es'],
|
||||
['label' => 'Русский', 'value' => 'ru'],
|
||||
]],
|
||||
['key' => 'i18n_auto_detect', 'label' => '自动检测浏览器语言', 'type' => 'switch', 'default' => true, 'help' => '基于浏览器 Accept-Language 自动切换(AKSRT 方案,区别于 legacy IP 定位方案)'],
|
||||
['key' => 'i18n_auto_detect', 'label' => '自动检测浏览器语言', 'type' => 'switch', 'default' => true, 'help' => '基于浏览器 Accept-Language 自动切换(Jio 方案,区别于 legacy IP 定位方案)'],
|
||||
]],
|
||||
['key' => 'languages', 'title' => '语言管理', 'fields' => [
|
||||
['key' => 'i18n_available_langs', 'label' => '可用语言列表', 'type' => 'textarea', 'default' => "zh-CN|简体中文\nen|English\nzh-TW|繁體中文\nja|日本語\nko|한국어\nfr|Français\nde|Deutsch\nes|Español\nru|Русский", 'help' => '每行一个语言,格式:代码|名称(9 种内置语言)'],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema\PostList;
|
||||
namespace Jio\Admin\Schema\PostList;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,7 +14,7 @@ class SchemaPostListList {
|
||||
'key' => 'list',
|
||||
'title' => '文章列表',
|
||||
'version' => '9.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
[
|
||||
'key' => 'order',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,7 +14,7 @@ class SchemaExtensionsAudit {
|
||||
'key' => 'audit',
|
||||
'title' => 'API内容审核',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'provider', 'title' => '审核接口', 'fields' => [
|
||||
['key' => 'content_audit_enabled', 'label' => '启用内容审核', 'type' => 'switch', 'default' => false],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,7 +14,7 @@ class SchemaExtensionsIP {
|
||||
'key' => 'ip',
|
||||
'title' => 'IP归属地',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '基础配置', 'fields' => [
|
||||
['key' => 'ip_geo_enabled', 'label' => '启用 IP 归属地', 'type' => 'switch', 'default' => false],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,7 +14,7 @@ class SchemaExtensionsPush {
|
||||
'key' => 'push',
|
||||
'title' => '百度推送',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '推送配置', 'fields' => [
|
||||
['key' => 'baidu_push_enabled', 'label' => '启用百度推送', 'type' => 'switch', 'default' => false],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,7 +14,7 @@ class SchemaExtensionsSecurity {
|
||||
'key' => 'security',
|
||||
'title' => '网站安全',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'brush_post', 'title' => '[防刷机制]发布限制', 'description' => '限制单个用户发布文章、帖子、板块的总数量(为0则不限制)', 'fields' => [
|
||||
['key' => 'security_brush_post_10min', 'label' => '10分钟内最多发布 (篇)', 'type' => 'number', 'default' => 20, 'min' => 0, 'max' => 200],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,7 +14,7 @@ class SchemaExtensionsSystem {
|
||||
'key' => 'system',
|
||||
'title' => '系统工具',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'optimize', 'title' => 'WordPress 优化', 'fields' => [
|
||||
['key' => 'hide_admin_bar', 'label' => '前台关闭顶部admin_bar', 'type' => 'switch', 'default' => true, 'help' => '开启后前台不显示WordPress顶部黑条'],
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* legacy V9.0 page/display settings contract.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -33,7 +33,7 @@ class SchemaPageDisplay {
|
||||
'key' => $key,
|
||||
'title' => $title,
|
||||
'version' => '9.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [['key' => $key, 'fields' => $fields]],
|
||||
];
|
||||
}
|
||||
@@ -52,7 +52,7 @@ class SchemaPageDisplay {
|
||||
self::field('nav_scroll_down_hide', '页面向下滚动时隐藏', 'switcher', true, ['dependsOn' => self::enabled('nav_fixed'), 'description' => '下滚动隐藏,上滚动显示。']),
|
||||
self::field('nav_newposts', 'Add发布按钮', 'switcher', true, ['description' => '顶部导航显示文章投稿按钮。']),
|
||||
self::field('nav_newposts_radius', '按钮圆角', 'switcher', true, ['dependsOn' => self::enabled('nav_newposts')]),
|
||||
self::field('nav_newposts_class', '按钮颜色', 'palette', 'jb-blue', ['dependsOn' => self::enabled('nav_newposts'), 'options' => self::aksrt_palette()]),
|
||||
self::field('nav_newposts_class', '按钮颜色', 'palette', 'jb-blue', ['dependsOn' => self::enabled('nav_newposts'), 'options' => self::jio_palette()]),
|
||||
self::field('nav_newposts_text', '按钮文案', 'textarea', '<i class="fa fas fa-fw fa-pencil-alt"></i>发布', ['dependsOn' => self::enabled('nav_newposts'), 'sanitize_type' => 'html', 'rows' => 1]),
|
||||
]);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ class SchemaPageDisplay {
|
||||
self::field('footer_t1_img', '日间模式图片', 'upload', $img . 'logo.png'),
|
||||
self::field('footer_t1_img_dark', '夜间模式图片', 'upload', $img . 'logo_dark.png'),
|
||||
self::field('footer_t1_t', '首行文字', 'text', ''),
|
||||
self::field('fcode_t1_code', '更多内容', 'textarea', 'AKSRT 主题专为博客、自媒体、资讯类的网站设计开发,简约优雅的设计风格,全面的前端用户功能,简单的模块化配置,欢迎您的体验', ['sanitize_type' => 'html', 'rows' => 3]),
|
||||
self::field('fcode_t1_code', '更多内容', 'textarea', 'Jio 主题专为博客、自媒体、资讯类的网站设计开发,简约优雅的设计风格,全面的前端用户功能,简单的模块化配置,欢迎您的体验', ['sanitize_type' => 'html', 'rows' => 3]),
|
||||
self::field('fcode_t2_code_1', '版块二第一行', 'textarea', '<a href="#">友链申请</a>' . "\n" . '<a href="#">免责声明</a>' . "\n" . '<a href="#">广告合作</a>' . "\n" . '<a href="#">关于我们</a>', ['sanitize_type' => 'html', 'rows' => 4]),
|
||||
self::field('fcode_t2_code_2', '版块二第二行', 'textarea', self::copyright_default(), ['sanitize_type' => 'html', 'rows' => 3]),
|
||||
self::field('footer_contact_m_s', '图标联系方式移动端显示', 'switcher', true),
|
||||
@@ -181,7 +181,7 @@ class SchemaPageDisplay {
|
||||
}
|
||||
|
||||
private static function card_fields(): array {
|
||||
return [self::field('show_widget_bg', '模块背景', 'switcher', false, ['description' => '开启后整个模块显示统一背景;关闭后每张卡片单独显示背景。']), self::field('icon_radius4', '方形图标', 'switcher', false), self::field('cards', '添加卡片', 'group', [], ['button_title' => '添加内容', 'fields' => [self::field('title', '文案标题', 'textarea', '', ['sanitize_type' => 'rich_html']), self::field('desc', '文案简介', 'textarea', '', ['sanitize_type' => 'rich_html']), self::field('icon', '选择图标', 'icon', 'fa fas fa-magic'), self::field('customize_icon', '自定义图标', 'textarea', '', ['sanitize_type' => 'html']), self::field('icon_class', '图标颜色', 'palette', 'c-yellow', ['options' => self::aksrt_palette(true)]), self::field('link', '跳转链接', 'link', [])]])];
|
||||
return [self::field('show_widget_bg', '模块背景', 'switcher', false, ['description' => '开启后整个模块显示统一背景;关闭后每张卡片单独显示背景。']), self::field('icon_radius4', '方形图标', 'switcher', false), self::field('cards', '添加卡片', 'group', [], ['button_title' => '添加内容', 'fields' => [self::field('title', '文案标题', 'textarea', '', ['sanitize_type' => 'rich_html']), self::field('desc', '文案简介', 'textarea', '', ['sanitize_type' => 'rich_html']), self::field('icon', '选择图标', 'icon', 'fa fas fa-magic'), self::field('customize_icon', '自定义图标', 'textarea', '', ['sanitize_type' => 'html']), self::field('icon_class', '图标颜色', 'palette', 'c-yellow', ['options' => self::jio_palette(true)]), self::field('link', '跳转链接', 'link', [])]])];
|
||||
}
|
||||
|
||||
private static function orderby_fields(): array { return [self::field('lists', '显示排序方式', 'select', ['modified', 'views', 'like', 'comment_count'], ['multiple' => true, 'sortable' => true, 'options' => self::post_order_options(false)]), self::field('dropdown', '更多排序方式', 'switcher', false)]; }
|
||||
@@ -201,7 +201,7 @@ class SchemaPageDisplay {
|
||||
private static function options(array $options): array { $out = []; foreach ($options as $value => $label) { $out[] = ['label' => $label, 'value' => (string) $value]; } return $out; }
|
||||
private static function device_options(): array { return self::options(['pc_s' => 'PC端显示', 'm_s' => '移动端显示']); }
|
||||
private static function solid_palette(): array { return self::options(['ff648f' => '#ff648f', 'c246f5' => '#c246f5', '469cf5' => '#469cf5', '27bf41' => '#27bf41', 'fd6b4e' => '#fd6b4e', '2d2422' => '#2d2422']); }
|
||||
private static function aksrt_palette(bool $transparent = false): array { $colors = $transparent ? ['transparent' => 'rgba(114,114,114,.1)'] : []; $colors += ['c-red' => 'rgba(255,84,115,.4)', 'c-yellow' => 'rgba(255,111,6,.4)', 'c-cyan' => 'rgba(8,196,193,.4)', 'c-blue' => 'rgba(41,151,247,.4)', 'c-blue-2' => 'rgba(77,130,249,.4)', 'c-green' => 'rgba(18,185,40,.4)', 'c-purple' => 'rgba(213,72,245,.4)', 'jb-red' => '#ff5473', 'jb-yellow' => '#ff8a00', 'jb-cyan' => '#08c4c1', 'jb-blue' => '#2997f7', 'jb-green' => '#12b928', 'jb-purple' => '#d548f5', 'jb-pink' => '#ff648f']; return self::options($colors); }
|
||||
private static function jio_palette(bool $transparent = false): array { $colors = $transparent ? ['transparent' => 'rgba(114,114,114,.1)'] : []; $colors += ['c-red' => 'rgba(255,84,115,.4)', 'c-yellow' => 'rgba(255,111,6,.4)', 'c-cyan' => 'rgba(8,196,193,.4)', 'c-blue' => 'rgba(41,151,247,.4)', 'c-blue-2' => 'rgba(77,130,249,.4)', 'c-green' => 'rgba(18,185,40,.4)', 'c-purple' => 'rgba(213,72,245,.4)', 'jb-red' => '#ff5473', 'jb-yellow' => '#ff8a00', 'jb-cyan' => '#08c4c1', 'jb-blue' => '#2997f7', 'jb-green' => '#12b928', 'jb-purple' => '#d548f5', 'jb-pink' => '#ff648f']; return self::options($colors); }
|
||||
private static function post_order_options(bool $with_default): array { $options = $with_default ? ['' => '保持默认'] : []; $options += ['modified' => '更新', 'date' => '发布', 'views' => '浏览', 'like' => '点赞', 'comment_count' => '评论', 'favorite' => '收藏', 'rand' => '随机']; return self::options($options); }
|
||||
private static function header_layout_options(): array { $base = self::asset('assets/images/header-layout/'); return [['label' => '布局一', 'value' => '1', 'image' => $base . 'header_layout_1.png'], ['label' => '布局二', 'value' => '2', 'image' => $base . 'header_layout_2.png'], ['label' => '布局三', 'value' => '3', 'image' => $base . 'header_layout_3.png']]; }
|
||||
private static function mobile_header_layout_options(): array { $base = self::asset('assets/images/header-layout/'); return [['label' => '居中', 'value' => 'center', 'image' => $base . 'mobile_header_layout_center.png'], ['label' => '居左', 'value' => 'left', 'image' => $base . 'mobile_header_layout_left.png']]; }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -30,7 +30,7 @@ class SchemaPayment {
|
||||
'key' => 'knowledge',
|
||||
'title' => '知识付费',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'notice', 'title' => '说明', 'fields' => [
|
||||
['key' => 'knowledge_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<p><b>知识付费</b></p><ul><li>知识付费功能主要分为文章付费、付费会员、余额系统、推广返佣、创作分成等功能组成,您可以根据您的需要开启对应功能</li><li>使用支付功能请先在「收款接口」页配置好收款通道</li></ul>'],
|
||||
@@ -122,7 +122,7 @@ class SchemaPayment {
|
||||
'key' => 'recharge',
|
||||
'title' => '余额充值',
|
||||
'version' => '1.2.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'notice', 'title' => '说明', 'fields' => [
|
||||
['key' => 'recharge_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<p><b>用户余额功能:</b></p><ul><li>开启后用户可充值到余额,余额可用于全站消费</li><li>在下方设置默认的分成比例,同时支持单独为每一个用户设置独立的分成比例</li><li>可以在用户管理中,为某一个用户手动赠送余额</li></ul>'],
|
||||
@@ -179,7 +179,7 @@ class SchemaPayment {
|
||||
'key' => 'vip',
|
||||
'title' => 'VIP会员',
|
||||
'version' => '1.2.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'notice', 'title' => '说明', 'fields' => [
|
||||
['key' => 'vip_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<ul><li>开启付费会员功能之前,请先配置好收款接口,确保网站收款正常</li><li>会员功能的设置项目较多,请仔细核对,避免出现价格、时间的问题</li><li>配合设置名称、有效期等可搭配出不同类型的会员</li><li>管理员可在后台为用户单独开启会员</li></ul>'],
|
||||
@@ -322,7 +322,7 @@ class SchemaPayment {
|
||||
'key' => 'revenue',
|
||||
'title' => '创作分成',
|
||||
'version' => '2.1.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'notice', 'title' => '说明', 'fields' => [
|
||||
['key' => 'revenue_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<ul><li>开启创作分成后,用户发布的付费内容则按设置的比例收入与用户分成</li><li>在下方设置默认的分成比例,同时支持单独为每一个用户设置独立的分成比例</li><li>用户分成比例是按照订单实收现金并扣除推广佣金后,再按设置的比例分成</li><li>禁用此功能后,所有收款将不会与作者分成</li></ul>'],
|
||||
@@ -357,7 +357,7 @@ class SchemaPayment {
|
||||
'key' => 'affiliate',
|
||||
'title' => '推广返佣',
|
||||
'version' => '1.1.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'notice', 'title' => '说明', 'fields' => [
|
||||
['key' => 'affiliate_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<ul><li>识别模式「绑定注册」:用户只要是通过任意推广链接注册的,今后此用户消费均会给推荐人返佣</li><li>识别模式「仅推广链接」:只有通过推广链接购买的才会给推荐人返佣,不会考虑是否是推荐注册的</li><li>开启的订单类型如果一个都不勾选,则代表该用户类型不参与此功能</li><li>您可以根据网站的VIP设置相互配合设置相应的规则</li><li>下面的设置为全局设置,还可以单独为每一个用户设置规则和比例</li></ul>'],
|
||||
@@ -436,10 +436,10 @@ class SchemaPayment {
|
||||
'key' => 'gateway',
|
||||
'title' => '收款接口',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'notice', 'title' => '说明', 'fields' => [
|
||||
['key' => 'gateway_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<ul><li>以下收款接口,主题仅提供API接入服务,收款平台的可靠性请自行斟酌!</li><li>涉及到资金及信息安全,有相关执照的商家推荐使用官方接口</li><li>微信 / 支付宝接口可在「官方」与「易支付」之间选择;PayPal 与 Stripe 独立开关</li><li>异步回调由各网关验签,无需额外口令;回调地址固定为 /wp-json/aksrt/v1/payment/{gateway}/{notify|return}</li></ul>'],
|
||||
['key' => 'gateway_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<ul><li>以下收款接口,主题仅提供API接入服务,收款平台的可靠性请自行斟酌!</li><li>涉及到资金及信息安全,有相关执照的商家推荐使用官方接口</li><li>微信 / 支付宝接口可在「官方」与「易支付」之间选择;PayPal 与 Stripe 独立开关</li><li>异步回调由各网关验签,无需额外口令;回调地址固定为 /wp-json/jio/v1/payment/{gateway}/{notify|return}</li></ul>'],
|
||||
]],
|
||||
['key' => 'channels', 'title' => '收款接口选择', 'fields' => [
|
||||
['key' => 'pay_wechat_sdk_options', 'label' => '微信收款接口', 'type' => 'select', 'default' => 'null', 'options' => [
|
||||
@@ -469,7 +469,7 @@ class SchemaPayment {
|
||||
['key' => 'settle_unit', 'label' => '结算货币单位', 'type' => 'input', 'default' => '元', 'help' => '如果接入的收款方式与本地的货币类型不同,则在此配置结算汇率及单位'],
|
||||
]],
|
||||
['key' => 'pay_sdk_custom_title_s', 'label' => '自定义收款标题', 'type' => 'switch', 'default' => false, 'help' => '默认为付款类型加网站名,如需自定义请开启'],
|
||||
['key' => 'pay_sdk_custom_title', 'label' => '自定义收款标题', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'pay_sdk_custom_title_s', 'value' => true], 'help' => '可使用变量:订单类型 %order_type%,例如:%order_type%-AKSRT;注意控制总长度,太长会导致部分接口报错'],
|
||||
['key' => 'pay_sdk_custom_title', 'label' => '自定义收款标题', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'pay_sdk_custom_title_s', 'value' => true], 'help' => '可使用变量:订单类型 %order_type%,例如:%order_type%-Jio;注意控制总长度,太长会导致部分接口报错'],
|
||||
]],
|
||||
['key' => 'order', 'title' => '订单与编号', 'fields' => [
|
||||
['key' => 'payment_order_expiry', 'label' => '订单支付时效(分钟)', 'type' => 'number', 'default' => 30, 'min' => 5, 'max' => 1440, 'help' => '用户下单后,最大支付时间,超过后订单将自动关闭(不能低于5分钟)'],
|
||||
@@ -486,7 +486,7 @@ class SchemaPayment {
|
||||
], 'help' => '收银台支付方式显示顺序;仅显示已开启的接口'],
|
||||
]],
|
||||
['key' => 'alipay', 'title' => '支付宝官方', 'collapsible' => true, 'defaultCollapsed' => true, 'dependsOn' => ['key' => 'pay_alipay_sdk_options', 'value' => 'official_alipay'], 'fields' => [
|
||||
['key' => 'alipay_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<ul><li>支付宝公钥为必填项目(公钥错误可以成功付款,但会回调失败)</li><li>回调地址固定为 /wp-json/aksrt/v1/payment/alipay/{notify|return}</li><li>电脑端按「电脑端收款」选择站内二维码或跳转;手机端开启 H5 时跳转,否则显示二维码</li></ul>'],
|
||||
['key' => 'alipay_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<ul><li>支付宝公钥为必填项目(公钥错误可以成功付款,但会回调失败)</li><li>回调地址固定为 /wp-json/jio/v1/payment/alipay/{notify|return}</li><li>电脑端按「电脑端收款」选择站内二维码或跳转;手机端开启 H5 时跳转,否则显示二维码</li></ul>'],
|
||||
['key' => 'alipay_appid', 'label' => '当面付:APPID', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'alipay_privatekey', 'label' => '当面付:应用私钥', 'type' => 'textarea', 'sanitize_type' => 'code', 'default' => '', 'rows' => 4, 'secret' => true],
|
||||
['key' => 'alipay_pc_mode', 'label' => '电脑端收款', 'type' => 'select', 'default' => 'facepay', 'options' => [
|
||||
@@ -499,7 +499,7 @@ class SchemaPayment {
|
||||
['key' => 'alipay_root_cert_path', 'label' => '支付宝根证书路径', 'type' => 'input', 'default' => '', 'help' => 'alipayRootCert.crt 绝对路径。'],
|
||||
]],
|
||||
['key' => 'wechat', 'title' => '微信官方', 'collapsible' => true, 'defaultCollapsed' => true, 'dependsOn' => ['key' => 'pay_wechat_sdk_options', 'value' => 'official_wechat'], 'fields' => [
|
||||
['key' => 'wechat_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<ul><li>微信企业支付:官方接口,商家可申请,有年费,申请较为复杂</li><li>回调地址固定为 /wp-json/aksrt/v1/payment/wechatpay/{notify|return},请配置 Native 支付回调链接</li><li>电脑端显示站内二维码;手机端按下方开关选择 H5 跳转或微信内 JSAPI,未开启时同样显示二维码</li></ul>'],
|
||||
['key' => 'wechat_notice', 'label' => '', 'type' => 'content', 'ui_only' => true, 'content' => '<ul><li>微信企业支付:官方接口,商家可申请,有年费,申请较为复杂</li><li>回调地址固定为 /wp-json/jio/v1/payment/wechatpay/{notify|return},请配置 Native 支付回调链接</li><li>电脑端显示站内二维码;手机端按下方开关选择 H5 跳转或微信内 JSAPI,未开启时同样显示二维码</li></ul>'],
|
||||
['key' => 'wechat_merchantid', 'label' => '商户号 PartnerID', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'wechat_appid', 'label' => '授权绑定的AppID', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'wechat_h5', 'label' => '移动端 H5 跳转', 'type' => 'switch', 'default' => false, 'help' => '需开通 H5 支付。开启后手机浏览器(非微信内)跳转微信付款;关闭则显示站内二维码'],
|
||||
@@ -545,7 +545,7 @@ class SchemaPayment {
|
||||
'key' => 'withdrawal',
|
||||
'title' => '提现策略',
|
||||
'version' => '1.1.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '提现门槛', 'fields' => [
|
||||
['key' => 'affiliate_min_withdrawal', 'label' => '提现最低金额限制', 'type' => 'input', 'sanitize_type' => 'money_decimal', 'default' => '50.00', 'help' => '当用户可提现总金额(推广佣金、余额、创作分成)高于多少时候,才能发起提现'],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -16,7 +16,7 @@ class SchemaPermissions {
|
||||
*/
|
||||
private static function upload_user_type_options(): array {
|
||||
$options = [];
|
||||
foreach (\AKSRT\Attachment\UploadLimits::user_type_options() as $value => $label) {
|
||||
foreach (\Jio\Attachment\UploadLimits::user_type_options() as $value => $label) {
|
||||
$options[] = ['label' => $label, 'value' => $value];
|
||||
}
|
||||
return $options;
|
||||
@@ -29,16 +29,16 @@ class SchemaPermissions {
|
||||
*/
|
||||
private static function upload_mime_user_type_options(): array {
|
||||
$options = [
|
||||
['label' => __('所有用户', 'aksrt'), 'value' => 'all'],
|
||||
['label' => __('所有用户', 'jio'), 'value' => 'all'],
|
||||
];
|
||||
foreach (\AKSRT\Attachment\UploadLimits::user_type_options() as $value => $label) {
|
||||
foreach (\Jio\Attachment\UploadLimits::user_type_options() as $value => $label) {
|
||||
$options[] = ['label' => $label, 'value' => $value];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
public static function build(): array {
|
||||
$provider_meta = function_exists('aksrt_captcha_provider_metadata') ? aksrt_captcha_provider_metadata() : [];
|
||||
$provider_meta = function_exists('jio_captcha_provider_metadata') ? jio_captcha_provider_metadata() : [];
|
||||
$provider_options = [['label' => '关闭', 'value' => '']];
|
||||
$provider_fields = [];
|
||||
foreach ($provider_meta as $provider => $meta) {
|
||||
@@ -50,7 +50,7 @@ class SchemaPermissions {
|
||||
'key' => $provider,
|
||||
'label' => (string) ($meta['label'] ?? $provider) . ' 配置',
|
||||
'type' => 'custom',
|
||||
'default' => function_exists('aksrt_captcha_provider_defaults') ? aksrt_captcha_provider_defaults($provider) : [],
|
||||
'default' => function_exists('jio_captcha_provider_defaults') ? jio_captcha_provider_defaults($provider) : [],
|
||||
'captcha_provider' => $provider,
|
||||
];
|
||||
}
|
||||
@@ -63,7 +63,7 @@ class SchemaPermissions {
|
||||
'key' => 'login',
|
||||
'title' => '人机验证',
|
||||
'version' => '1.1.0',
|
||||
'option_key' => 'aksrt_captcha_config',
|
||||
'option_key' => 'jio_captcha_config',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '基础开关', 'fields' => [
|
||||
['key' => 'enabled', 'label' => '启用人机验证', 'type' => 'switch', 'default' => false],
|
||||
@@ -92,7 +92,7 @@ class SchemaPermissions {
|
||||
'key' => 'attachment',
|
||||
'title' => '附件配置',
|
||||
'version' => '1.2.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'size-limits', 'title' => '上传大小限制', 'fields' => [
|
||||
['key' => 'size_limit_notice', 'label' => '服务器环境提示', 'type' => 'notice', 'alertType' => 'warning', 'alertContent' => '设置上传大小,请考虑服务器负荷、硬盘,以及服务器最大能支持的范围。实际上传大小取「主题限制」与「PHP 环境限制」的较小值,超过 PHP 环境限制时请调整 php.ini 的 upload_max_filesize 与 post_max_size。'],
|
||||
@@ -139,7 +139,7 @@ class SchemaPermissions {
|
||||
'key' => 'moderation',
|
||||
'title' => '评论审核',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_moderation_config',
|
||||
'option_key' => 'jio_moderation_config',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '审核总开关', 'fields' => [
|
||||
['key' => 'enabled', 'label' => '启用审核系统', 'type' => 'switch', 'default' => false],
|
||||
@@ -174,7 +174,7 @@ class SchemaPermissions {
|
||||
'key' => 'capabilities',
|
||||
'title' => '基本权限',
|
||||
'version' => '1.1.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -19,7 +19,7 @@ class SchemaPostList {
|
||||
'key' => 'thumb',
|
||||
'title' => '列表缩略图',
|
||||
'version' => '9.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'source', 'title' => '图片来源', 'description' => '缩略图获取优先级:文章特色图像 > 外链特色图像 > 文章首图 > 分类封面图 > 备用缩略图 > 懒加载预载图。', 'fields' => [
|
||||
['key' => 'list_thumb_slides_s', 'label' => '幻灯片缩略图', 'type' => 'switch', 'default' => true, 'description' => '允许幻灯片作为列表缩略图'],
|
||||
@@ -47,7 +47,7 @@ class SchemaPostList {
|
||||
'key' => 'page',
|
||||
'title' => '文章页面',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'post_page', 'title' => '文章页面', 'fields' => [
|
||||
['key' => 'article_image_cover', 'label' => '图片封面', 'type' => 'switcher', 'default' => true, 'description' => '允许设置文章页顶部显示封面图'],
|
||||
@@ -130,7 +130,7 @@ class SchemaPostList {
|
||||
'key' => 'filter',
|
||||
'title' => '高级筛选',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'advanced_filter', 'title' => '高级筛选', 'fields' => [
|
||||
['key' => 'custom_filter_show', 'label' => '高级筛选默认显示', 'type' => 'checkbox', 'default' => [], 'options' => [
|
||||
@@ -180,7 +180,7 @@ class SchemaPostList {
|
||||
'key' => 'feature',
|
||||
'title' => '文章功能',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'feature', 'title' => '文章功能', 'fields' => [
|
||||
['key' => 'article_nav', 'label' => '文章目录树', 'type' => 'switcher', 'default' => true, 'description' => '默认开关,每篇文章可单独设置。开启后请自行添加文章目录树模块到侧边栏(文章内标题超过3个才会显示)'],
|
||||
@@ -222,7 +222,7 @@ class SchemaPostList {
|
||||
'key' => 'comment',
|
||||
'title' => '评论设置',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'comment', 'title' => '评论设置', 'fields' => [
|
||||
['key' => 'close_comments', 'label' => '关闭文章评论功能', 'type' => 'switcher', 'default' => false, 'description' => '部分网站无需交互,或需备案审核,可在此关闭所有文章的评论功能。同时每一篇文章可单独关闭评论功能'],
|
||||
@@ -258,7 +258,7 @@ class SchemaPostList {
|
||||
'key' => 'pay',
|
||||
'title' => '付费内容',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '付费设置', 'fields' => [
|
||||
['key' => 'post_pay_enable', 'label' => '启用付费内容', 'type' => 'switch', 'default' => false],
|
||||
@@ -284,7 +284,7 @@ class SchemaPostList {
|
||||
'key' => 'submit',
|
||||
'title' => '前台投稿',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'submit', 'title' => '前台投稿', 'fields' => [
|
||||
['key' => 'post_article_s', 'label' => '启用前台投稿', 'type' => 'switcher', 'default' => true, 'description' => '此功能启用后,可以在权限管理中设置用户的发布、审核权限'],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,7 +14,7 @@ class SchemaToolsBackup {
|
||||
'key' => 'backup',
|
||||
'title' => '数据备份',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'backup', 'title' => '备份设置', 'fields' => [
|
||||
['key' => 'backup_auto', 'label' => '自动备份', 'type' => 'switch', 'help' => '启用自动定时备份'],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,7 +14,7 @@ class SchemaToolsHealth {
|
||||
'key' => 'health',
|
||||
'title' => '健康检查',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'check', 'title' => '环境检查', 'fields' => [
|
||||
['key' => 'php_version', 'label' => 'PHP 版本', 'type' => 'display', 'help' => '建议 7.4+'],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -14,11 +14,11 @@ class SchemaToolsImportExport {
|
||||
'key' => 'import-export',
|
||||
'title' => '导入导出',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'export', 'title' => '导出配置', 'fields' => [
|
||||
['key' => 'export_settings', 'label' => '导出主题设置', 'type' => 'button', 'help' => '导出当前所有主题配置为 JSON 文件'],
|
||||
['key' => 'export_options', 'label' => '导出 WordPress 选项', 'type' => 'button', 'help' => '导出 aksrt_settings 等选项'],
|
||||
['key' => 'export_options', 'label' => '导出 WordPress 选项', 'type' => 'button', 'help' => '导出 jio_settings 等选项'],
|
||||
]],
|
||||
['key' => 'import', 'title' => '导入配置', 'fields' => [
|
||||
['key' => 'import_settings', 'label' => '导入主题设置', 'type' => 'file', 'accept' => '.json', 'help' => '导入主题配置文件(将覆盖当前设置)'],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema;
|
||||
namespace Jio\Admin\Schema;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -111,7 +111,7 @@ class SchemaUsers {
|
||||
'key' => 'auth',
|
||||
'title' => '注册登录',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'register', 'title' => '注册设置', 'fields' => [
|
||||
['key' => 'close_sign', 'label' => '一键禁用登录注册', 'type' => 'switch', 'default' => false, 'desc' => '开启后前台禁用全部注册/登录入口(含社交登录),不影响后台登录'],
|
||||
@@ -139,7 +139,7 @@ class SchemaUsers {
|
||||
['key' => 'sign_modal_auto_waiting_time', 'label' => '弹窗等待秒数', 'type' => 'number', 'default' => 5, 'min' => 1, 'max' => 100, 'desc' => '页面加载多少秒后弹出登录窗'],
|
||||
['key' => 'sign_modal_auto_expires', 'label' => '弹窗周期(小时)', 'type' => 'number', 'default' => 1, 'min' => 0, 'max' => 2000, 'desc' => '关闭弹窗后多少小时内不再自动弹出,0=每次刷新都弹'],
|
||||
['key' => 'replace_wp_login', 'label' => '替换WP登录页', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'admin_sign_key', 'label' => '应急登录 Key', 'type' => 'input', 'default' => '', 'desc' => '留空为禁用。管理员可通过 /wp-login.php?aksrt_bypass=<key> 绕过前台替换'],
|
||||
['key' => 'admin_sign_key', 'label' => '应急登录 Key', 'type' => 'input', 'default' => '', 'desc' => '留空为禁用。管理员可通过 /wp-login.php?jio_bypass=<key> 绕过前台替换'],
|
||||
['key' => 'user_signin_phone_s', 'label' => '手机号登录', 'type' => 'switch', 'default' => false, 'desc' => '允许用手机号作为用户名登录'],
|
||||
['key' => 'user_signin_nopas_s', 'label' => '免密登录', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'user_signin_nopas_type', 'label' => '免密登录方式', 'type' => 'radio', 'default' => 'email', 'options' => [
|
||||
@@ -234,7 +234,7 @@ class SchemaUsers {
|
||||
'key' => 'notifications',
|
||||
'title' => '消息通知',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '基础设置', 'fields' => [
|
||||
['key' => 'message_s', 'label' => '站内通知总开关', 'type' => 'switch', 'default' => true],
|
||||
@@ -292,7 +292,7 @@ class SchemaUsers {
|
||||
'key' => 'oauth',
|
||||
'title' => '第三方登录',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '基础设置', 'fields' => [
|
||||
['key' => 'oauth_s', 'label' => '启用第三方登录', 'type' => 'switch', 'default' => false],
|
||||
@@ -390,7 +390,7 @@ class SchemaUsers {
|
||||
'key' => 'sms',
|
||||
'title' => '短信接口',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'provider', 'title' => '短信接口', 'fields' => [
|
||||
['key' => 'sms_sdk', 'label' => '设置短信接口', 'type' => 'select', 'default' => 'null', 'options' => [
|
||||
@@ -432,7 +432,7 @@ class SchemaUsers {
|
||||
'key' => 'credits',
|
||||
'title' => '积分设置',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => '积分基础', 'fields' => [
|
||||
['key' => 'points_s', 'label' => '启用积分系统', 'type' => 'switch', 'default' => true],
|
||||
@@ -477,7 +477,7 @@ class SchemaUsers {
|
||||
'key' => 'features',
|
||||
'title' => '用户功能',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'profile-media', 'title' => '用户资料', 'fields' => [
|
||||
['key' => 'avatar_upload_s', 'label' => '允许用户上传头像', 'type' => 'switch', 'default' => true],
|
||||
@@ -510,7 +510,7 @@ class SchemaUsers {
|
||||
'key' => 'verify',
|
||||
'title' => '身份认证',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'apply', 'title' => '申请设置', 'fields' => [
|
||||
['key' => 'user_auth_s', 'label' => '启用身份认证', 'type' => 'switch', 'default' => true],
|
||||
@@ -525,7 +525,7 @@ class SchemaUsers {
|
||||
'key' => 'level',
|
||||
'title' => '用户等级',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'display', 'title' => '等级显示', 'fields' => [
|
||||
['key' => 'user_level_s', 'label' => '启用用户等级', 'type' => 'switch', 'default' => true],
|
||||
@@ -545,7 +545,7 @@ class SchemaUsers {
|
||||
'key' => 'badge',
|
||||
'title' => '用户徽章',
|
||||
'version' => '2.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'display', 'title' => '徽章展示', 'fields' => [
|
||||
['key' => 'user_medal_s', 'label' => '启用徽章/勋章系统', 'type' => 'switch', 'default' => false],
|
||||
@@ -571,7 +571,7 @@ class SchemaUsers {
|
||||
'key' => 'report',
|
||||
'title' => '禁封/举报',
|
||||
'version' => '1.1.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
// 字段集合、顺序、文案、默认值 1:1 对齐参考主题"用户&互动/禁封举报"
|
||||
['key' => 'main', 'title' => '', 'fields' => [
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
* assets/admin/src/schema/sections/users/ticket.js 保持 1:1
|
||||
* (tests/widget-system/ticket-schema-contract.test.cjs 守护)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin\Schema\Users;
|
||||
namespace Jio\Admin\Schema\Users;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -20,7 +20,7 @@ class SchemaUsersTicket {
|
||||
'key' => 'ticket',
|
||||
'title' => '工单中心',
|
||||
'version' => '1.0.0',
|
||||
'option_key' => 'aksrt_settings',
|
||||
'option_key' => 'jio_settings',
|
||||
'groups' => [
|
||||
['key' => 'main', 'title' => '工单功能', 'icon' => 'fa fas fa-headset', 'fields' => [
|
||||
['key' => 'ticket_s', 'label' => '工单功能', 'type' => 'switch', 'default' => true, 'help' => '开启后用户可在用户中心提交工单;关闭后工单中心不再开放,后台工单列表仍可查看历史工单'],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -81,21 +81,21 @@ class SchemaProvider {
|
||||
public function get_option_key(string $domain, string $section = ''): string {
|
||||
if ($section !== '') {
|
||||
$schema = $this->get_section_schema($domain, $section);
|
||||
return (string) ($schema['option_key'] ?? 'aksrt_settings');
|
||||
return (string) ($schema['option_key'] ?? 'jio_settings');
|
||||
}
|
||||
|
||||
$primary = [
|
||||
'global' => 'aksrt_settings',
|
||||
'page_display' => 'aksrt_settings',
|
||||
'post_list' => 'aksrt_settings',
|
||||
'permissions' => 'aksrt_settings',
|
||||
'users' => 'aksrt_settings',
|
||||
'payment' => 'aksrt_settings',
|
||||
'extensions' => 'aksrt_settings',
|
||||
'tools' => 'aksrt_settings',
|
||||
'global' => 'jio_settings',
|
||||
'page_display' => 'jio_settings',
|
||||
'post_list' => 'jio_settings',
|
||||
'permissions' => 'jio_settings',
|
||||
'users' => 'jio_settings',
|
||||
'payment' => 'jio_settings',
|
||||
'extensions' => 'jio_settings',
|
||||
'tools' => 'jio_settings',
|
||||
];
|
||||
|
||||
return $primary[$this->normalize_domain($domain)] ?? 'aksrt_settings';
|
||||
return $primary[$this->normalize_domain($domain)] ?? 'jio_settings';
|
||||
}
|
||||
|
||||
public function get_defaults(string $domain, string $section = ''): array {
|
||||
@@ -271,7 +271,7 @@ class SchemaProvider {
|
||||
}
|
||||
|
||||
if ($domain === 'users' && ($section === 'oauth' || $section === '')) {
|
||||
$stored = get_option('aksrt_settings', []);
|
||||
$stored = get_option('jio_settings', []);
|
||||
$merged = is_array($stored) ? array_merge($stored, $data) : $data;
|
||||
foreach ((array) ($data['__clear_secrets'] ?? []) as $secret_key) {
|
||||
$merged[sanitize_key((string) $secret_key)] = '';
|
||||
@@ -280,27 +280,27 @@ class SchemaProvider {
|
||||
}
|
||||
|
||||
if ($domain === 'payment' && $section === 'withdrawal') {
|
||||
$stored = get_option('aksrt_settings', []);
|
||||
$stored = get_option('jio_settings', []);
|
||||
$merged = is_array($stored) ? array_merge($stored, $data) : $data;
|
||||
$count = $merged['affiliate_daily_withdrawal_count'] ?? 3;
|
||||
$min_raw = $merged['affiliate_min_withdrawal'] ?? '50.00';
|
||||
$limit_raw = $merged['affiliate_daily_withdrawal_limit'] ?? '500.00';
|
||||
$time = (string) ($merged['affiliate_withdrawal_reset_time'] ?? '00:00');
|
||||
if (filter_var($count, FILTER_VALIDATE_INT) === false || (int) $count < 0) {
|
||||
$errors[] = __('每日可提现次数必须是非负整数', 'aksrt');
|
||||
$errors[] = __('每日可提现次数必须是非负整数', 'jio');
|
||||
}
|
||||
try {
|
||||
$min_minor = \AKSRT\Ledger\AssetAmount::balance(is_string($min_raw) ? $min_raw : '')->minor();
|
||||
$limit_minor = \AKSRT\Ledger\AssetAmount::balance(is_string($limit_raw) ? $limit_raw : '')->minor();
|
||||
$min_minor = \Jio\Ledger\AssetAmount::balance(is_string($min_raw) ? $min_raw : '')->minor();
|
||||
$limit_minor = \Jio\Ledger\AssetAmount::balance(is_string($limit_raw) ? $limit_raw : '')->minor();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$min_minor = 0;
|
||||
$limit_minor = 0;
|
||||
}
|
||||
if ($limit_minor < $min_minor) {
|
||||
$errors[] = __('每日提现上限不得低于最低提现金额', 'aksrt');
|
||||
$errors[] = __('每日提现上限不得低于最低提现金额', 'jio');
|
||||
}
|
||||
if (preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', $time) !== 1) {
|
||||
$errors[] = __('重置时间必须为 HH:MM', 'aksrt');
|
||||
$errors[] = __('重置时间必须为 HH:MM', 'jio');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,8 +366,8 @@ class SchemaProvider {
|
||||
}
|
||||
|
||||
private function get_schema_builder_map(): array {
|
||||
// Builders live under AKSRT\Admin\Schema (Composer PSR-4). Relative
|
||||
// Schema\*::class resolves correctly from this AKSRT\Admin namespace.
|
||||
// Builders live under Jio\Admin\Schema (Composer PSR-4). Relative
|
||||
// Schema\*::class resolves correctly from this Jio\Admin namespace.
|
||||
return [
|
||||
'global' => Schema\GlobalSchema::class,
|
||||
'page_display' => Schema\SchemaPageDisplay::class,
|
||||
@@ -381,7 +381,7 @@ class SchemaProvider {
|
||||
}
|
||||
|
||||
private function read_section_settings(array $schema): array {
|
||||
$option_key = (string) ($schema['option_key'] ?? 'aksrt_settings');
|
||||
$option_key = (string) ($schema['option_key'] ?? 'jio_settings');
|
||||
$stored = get_option($option_key, []);
|
||||
if (!is_array($stored)) {
|
||||
$stored = [];
|
||||
@@ -426,14 +426,14 @@ class SchemaProvider {
|
||||
}
|
||||
|
||||
// Captcha: always surface the normalized positive scene list.
|
||||
if ($option_key === 'aksrt_captcha_config' && function_exists('aksrt_get_captcha_config')) {
|
||||
$normalized = aksrt_get_captcha_config();
|
||||
if ($option_key === 'jio_captcha_config' && function_exists('jio_get_captcha_config')) {
|
||||
$normalized = jio_get_captcha_config();
|
||||
foreach (['enabled', 'active_provider', 'enabled_scenarios'] as $key) {
|
||||
if (array_key_exists($key, $normalized)) {
|
||||
$data[$key] = $normalized[$key];
|
||||
}
|
||||
}
|
||||
foreach (function_exists('aksrt_captcha_providers') ? aksrt_captcha_providers() : [] as $provider) {
|
||||
foreach (function_exists('jio_captcha_providers') ? jio_captcha_providers() : [] as $provider) {
|
||||
$data[$provider] = $this->mask_captcha_provider_secrets($provider, (array) ($normalized[$provider] ?? []));
|
||||
}
|
||||
}
|
||||
@@ -442,7 +442,7 @@ class SchemaProvider {
|
||||
}
|
||||
|
||||
private function write_section_settings(array $schema, array $data): bool {
|
||||
$option_key = (string) ($schema['option_key'] ?? 'aksrt_settings');
|
||||
$option_key = (string) ($schema['option_key'] ?? 'jio_settings');
|
||||
$stored = get_option($option_key, []);
|
||||
if (!is_array($stored)) {
|
||||
$stored = [];
|
||||
@@ -482,28 +482,28 @@ class SchemaProvider {
|
||||
}
|
||||
|
||||
// Captcha stores a single positive scene list.
|
||||
if ($option_key === 'aksrt_captcha_config' && function_exists('aksrt_captcha_scenes')) {
|
||||
if ($option_key === 'jio_captcha_config' && function_exists('jio_captcha_scenes')) {
|
||||
$stored['enabled'] = !empty($stored['enabled']);
|
||||
$active_provider = sanitize_key((string) ($stored['active_provider'] ?? ''));
|
||||
$stored['active_provider'] = in_array($active_provider, aksrt_captcha_providers(), true) ? $active_provider : '';
|
||||
$stored['enabled_scenarios'] = function_exists('aksrt_captcha_sanitize_scene_list')
|
||||
? aksrt_captcha_sanitize_scene_list($stored['enabled_scenarios'] ?? [])
|
||||
$stored['active_provider'] = in_array($active_provider, jio_captcha_providers(), true) ? $active_provider : '';
|
||||
$stored['enabled_scenarios'] = function_exists('jio_captcha_sanitize_scene_list')
|
||||
? jio_captcha_sanitize_scene_list($stored['enabled_scenarios'] ?? [])
|
||||
: (array) ($stored['enabled_scenarios'] ?? []);
|
||||
foreach (aksrt_captcha_providers() as $provider) {
|
||||
$stored[$provider] = function_exists('aksrt_captcha_sanitize_provider_config')
|
||||
? aksrt_captcha_sanitize_provider_config($provider, $stored[$provider] ?? [])
|
||||
foreach (jio_captcha_providers() as $provider) {
|
||||
$stored[$provider] = function_exists('jio_captcha_sanitize_provider_config')
|
||||
? jio_captcha_sanitize_provider_config($provider, $stored[$provider] ?? [])
|
||||
: (array) ($stored[$provider] ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
$updated = update_option($option_key, $stored);
|
||||
if ($option_key === 'aksrt_captcha_config' && function_exists('aksrt_captcha_config_reset')) {
|
||||
aksrt_captcha_config_reset();
|
||||
if ($option_key === 'jio_captcha_config' && function_exists('jio_captcha_config_reset')) {
|
||||
jio_captcha_config_reset();
|
||||
}
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
|
||||
if (isset($stored['site_favicon'])) {
|
||||
\AKSRT\Settings\Settings::sync_native_site_icon((string) $stored['site_favicon']);
|
||||
\Jio\Settings\Settings::sync_native_site_icon((string) $stored['site_favicon']);
|
||||
}
|
||||
|
||||
return $updated || true;
|
||||
@@ -525,8 +525,8 @@ class SchemaProvider {
|
||||
if (!empty($field['secret'])) {
|
||||
$paths[] = $key;
|
||||
}
|
||||
if (!empty($field['captcha_provider']) && function_exists('aksrt_captcha_provider_secret_fields')) {
|
||||
foreach (aksrt_captcha_provider_secret_fields((string) $field['captcha_provider']) as $secret) {
|
||||
if (!empty($field['captcha_provider']) && function_exists('jio_captcha_provider_secret_fields')) {
|
||||
foreach (jio_captcha_provider_secret_fields((string) $field['captcha_provider']) as $secret) {
|
||||
$paths[] = $key . '.' . sanitize_key((string) $secret);
|
||||
}
|
||||
}
|
||||
@@ -535,21 +535,21 @@ class SchemaProvider {
|
||||
}
|
||||
|
||||
private function mask_captcha_provider_secrets(string $provider, array $config): array {
|
||||
$clean = function_exists('aksrt_captcha_sanitize_provider_config')
|
||||
? aksrt_captcha_sanitize_provider_config($provider, $config)
|
||||
$clean = function_exists('jio_captcha_sanitize_provider_config')
|
||||
? jio_captcha_sanitize_provider_config($provider, $config)
|
||||
: $config;
|
||||
foreach (function_exists('aksrt_captcha_provider_secret_fields') ? aksrt_captcha_provider_secret_fields($provider) : [] as $secret) {
|
||||
foreach (function_exists('jio_captcha_provider_secret_fields') ? jio_captcha_provider_secret_fields($provider) : [] as $secret) {
|
||||
$clean[$secret] = trim((string) ($config[$secret] ?? '')) !== '' ? '***' : '';
|
||||
}
|
||||
return $clean;
|
||||
}
|
||||
|
||||
private function merge_captcha_provider_config(string $provider, array $stored, array $incoming, array $clear_paths): array {
|
||||
$current = function_exists('aksrt_captcha_sanitize_provider_config')
|
||||
? aksrt_captcha_sanitize_provider_config($provider, $stored)
|
||||
$current = function_exists('jio_captcha_sanitize_provider_config')
|
||||
? jio_captcha_sanitize_provider_config($provider, $stored)
|
||||
: $stored;
|
||||
$defaults = function_exists('aksrt_captcha_provider_defaults') ? aksrt_captcha_provider_defaults($provider) : [];
|
||||
$secret_fields = function_exists('aksrt_captcha_provider_secret_fields') ? aksrt_captcha_provider_secret_fields($provider) : [];
|
||||
$defaults = function_exists('jio_captcha_provider_defaults') ? jio_captcha_provider_defaults($provider) : [];
|
||||
$secret_fields = function_exists('jio_captcha_provider_secret_fields') ? jio_captcha_provider_secret_fields($provider) : [];
|
||||
foreach (array_keys($defaults) as $key) {
|
||||
$path = $provider . '.' . $key;
|
||||
if (in_array($path, $clear_paths, true)) {
|
||||
@@ -565,8 +565,8 @@ class SchemaProvider {
|
||||
}
|
||||
$current[$key] = $value;
|
||||
}
|
||||
return function_exists('aksrt_captcha_sanitize_provider_config')
|
||||
? aksrt_captcha_sanitize_provider_config($provider, $current)
|
||||
return function_exists('jio_captcha_sanitize_provider_config')
|
||||
? jio_captcha_sanitize_provider_config($provider, $current)
|
||||
: $current;
|
||||
}
|
||||
|
||||
@@ -659,7 +659,7 @@ class SchemaProvider {
|
||||
}
|
||||
|
||||
try {
|
||||
return \AKSRT\Ledger\AssetAmount::balance(trim($value))->decimal();
|
||||
return \Jio\Ledger\AssetAmount::balance(trim($value))->decimal();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return '';
|
||||
}
|
||||
@@ -679,8 +679,8 @@ class SchemaProvider {
|
||||
// Inline whitelist for card title/desc: `badge` is a custom
|
||||
// element that wp_kses_post() would strip, but legacy (Zibll)
|
||||
// renders such markup verbatim.
|
||||
return function_exists('aksrt_icon_card_kses')
|
||||
? aksrt_icon_card_kses((string) $value)
|
||||
return function_exists('jio_icon_card_kses')
|
||||
? jio_icon_card_kses((string) $value)
|
||||
: wp_kses_post((string) $value);
|
||||
case 'textarea':
|
||||
return sanitize_textarea_field((string) $value);
|
||||
@@ -716,13 +716,13 @@ class SchemaProvider {
|
||||
}
|
||||
return $out;
|
||||
case 'icon':
|
||||
return function_exists('aksrt_sanitize_icon_value')
|
||||
? aksrt_sanitize_icon_value((string) $value)
|
||||
return function_exists('jio_sanitize_icon_value')
|
||||
? jio_sanitize_icon_value((string) $value)
|
||||
: sanitize_text_field((string) $value);
|
||||
case 'color':
|
||||
return \AKSRT\Core\Color::sanitize($value);
|
||||
return \Jio\Core\Color::sanitize($value);
|
||||
case 'color_alpha':
|
||||
return \AKSRT\Core\Color::sanitize($value, true);
|
||||
return \Jio\Core\Color::sanitize($value, true);
|
||||
default:
|
||||
return sanitize_text_field((string) $value);
|
||||
}
|
||||
@@ -730,8 +730,8 @@ class SchemaProvider {
|
||||
|
||||
private function sanitize_field_value($value, array $field) {
|
||||
if (!empty($field['captcha_provider'])) {
|
||||
return function_exists('aksrt_captcha_sanitize_provider_config')
|
||||
? aksrt_captcha_sanitize_provider_config((string) $field['captcha_provider'], $value)
|
||||
return function_exists('jio_captcha_sanitize_provider_config')
|
||||
? jio_captcha_sanitize_provider_config((string) $field['captcha_provider'], $value)
|
||||
: [];
|
||||
}
|
||||
$type = (string) ($field['type'] ?? 'text');
|
||||
@@ -749,8 +749,8 @@ class SchemaProvider {
|
||||
// fields already route through sanitize_value('icon'); group/repeater
|
||||
// sub-fields (e.g. header_slider_card_option.cards[].icon) must too.
|
||||
if ($type === 'icon') {
|
||||
return function_exists('aksrt_sanitize_icon_value')
|
||||
? aksrt_sanitize_icon_value((string) $value)
|
||||
return function_exists('jio_sanitize_icon_value')
|
||||
? jio_sanitize_icon_value((string) $value)
|
||||
: $this->sanitize_value($value, 'icon');
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* SchemaProvider OAuth 设置校验(代理模式 / 各平台凭据 / HTTPS 地址)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -23,24 +23,24 @@ trait SchemaProviderOAuthValidation {
|
||||
if ($mode === 'client') {
|
||||
$url = trim((string) ($settings['oauth_proxy_server_url'] ?? ''));
|
||||
if (!$this->is_secure_oauth_url($url)) {
|
||||
$errors[] = __('客户端代理模式需要有效的 HTTPS 服务端地址', 'aksrt');
|
||||
$errors[] = __('客户端代理模式需要有效的 HTTPS 服务端地址', 'jio');
|
||||
}
|
||||
if (!$secret_configured) {
|
||||
$errors[] = __('客户端代理模式需要代理 HMAC 密钥', 'aksrt');
|
||||
$errors[] = __('客户端代理模式需要代理 HMAC 密钥', 'jio');
|
||||
}
|
||||
if (empty($settings['oauth_proxy_client_providers']) || !is_array($settings['oauth_proxy_client_providers'])) {
|
||||
$errors[] = __('客户端代理模式至少需要启用一个登录平台', 'aksrt');
|
||||
$errors[] = __('客户端代理模式至少需要启用一个登录平台', 'jio');
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
if ($mode === 'server') {
|
||||
if (!$secret_configured) {
|
||||
$errors[] = __('服务端代理模式需要代理 HMAC 密钥', 'aksrt');
|
||||
$errors[] = __('服务端代理模式需要代理 HMAC 密钥', 'jio');
|
||||
}
|
||||
$selected = is_array($settings['oauth_proxy_server_providers'] ?? null) ? $settings['oauth_proxy_server_providers'] : [];
|
||||
if ($selected === []) {
|
||||
$errors[] = __('服务端代理模式至少需要允许一个登录平台', 'aksrt');
|
||||
$errors[] = __('服务端代理模式至少需要允许一个登录平台', 'jio');
|
||||
}
|
||||
$provider_fields = [
|
||||
'github' => ['GitHub', 'github_appid', 'github_appkey'],
|
||||
@@ -56,7 +56,7 @@ trait SchemaProviderOAuthValidation {
|
||||
}
|
||||
[$label, $id_key, $secret_key] = $provider_fields[$provider];
|
||||
if (trim((string) ($settings[$id_key] ?? '')) === '' || !$this->oauth_secret_is_configured($settings[$secret_key] ?? '', $stored[$secret_key] ?? '')) {
|
||||
$errors[] = sprintf(__('代理服务端的 %s 平台缺少完整凭据', 'aksrt'), $label);
|
||||
$errors[] = sprintf(__('代理服务端的 %s 平台缺少完整凭据', 'jio'), $label);
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
@@ -77,7 +77,7 @@ trait SchemaProviderOAuthValidation {
|
||||
}
|
||||
$enabled++;
|
||||
if (trim((string) ($settings[$id_key] ?? '')) === '' || !$this->oauth_secret_is_configured($settings[$secret_key] ?? '', $stored[$secret_key] ?? '')) {
|
||||
$errors[] = sprintf(__('%s 登录已启用,请填写完整凭据', 'aksrt'), $label);
|
||||
$errors[] = sprintf(__('%s 登录已启用,请填写完整凭据', 'jio'), $label);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,8 +92,8 @@ trait SchemaProviderOAuthValidation {
|
||||
: $this->oauth_secret_is_configured($settings['weixingzh_appsecret'] ?? '', $stored['weixingzh_appsecret'] ?? '');
|
||||
if (!$base_ready || !$mode_ready) {
|
||||
$errors[] = $gzh_type === 'subscribe'
|
||||
? __('微信公众号订阅号登录已启用,请填写 Token、AppID、二维码和回复关键词', 'aksrt')
|
||||
: __('微信公众号服务号登录已启用,请填写 Token、AppID 和 AppSecret', 'aksrt');
|
||||
? __('微信公众号订阅号登录已启用,请填写 Token、AppID、二维码和回复关键词', 'jio')
|
||||
: __('微信公众号服务号登录已启用,请填写 Token、AppID 和 AppSecret', 'jio');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,12 +104,12 @@ trait SchemaProviderOAuthValidation {
|
||||
|| !$this->oauth_secret_is_configured($settings['clogin_key'] ?? '', $stored['clogin_key'] ?? '')
|
||||
|| empty($settings['clogin_oauth_s'])
|
||||
) {
|
||||
$errors[] = __('彩虹聚合登录已启用,请填写 HTTPS 接口地址、AppID、AppKey 并选择平台', 'aksrt');
|
||||
$errors[] = __('彩虹聚合登录已启用,请填写 HTTPS 接口地址、AppID、AppKey 并选择平台', 'jio');
|
||||
}
|
||||
}
|
||||
|
||||
if ($enabled === 0) {
|
||||
$errors[] = __('启用第三方登录后,至少需要启用一个本地登录提供商', 'aksrt');
|
||||
$errors[] = __('启用第三方登录后,至少需要启用一个本地登录提供商', 'jio');
|
||||
}
|
||||
|
||||
return $errors;
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* SchemaProvider 枚举字段取值归一化(大小写不敏感匹配 / 选项声明值回写)。
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -41,7 +41,7 @@ class ToolsController extends ControllerBase {
|
||||
$theme = wp_get_theme();
|
||||
$info = [
|
||||
'current_version' => $theme->get('Version') ?: '0.0.0',
|
||||
'name' => $theme->get('Name') ?: 'AKSRT',
|
||||
'name' => $theme->get('Name') ?: 'Jio',
|
||||
'update_available'=> false,
|
||||
'remote_version' => '',
|
||||
'download_url' => '',
|
||||
@@ -50,28 +50,28 @@ class ToolsController extends ControllerBase {
|
||||
'dismissed' => '',
|
||||
];
|
||||
|
||||
if (class_exists('\AKSRT\Core\UpdateChecker')) {
|
||||
$stored = \AKSRT\Core\UpdateChecker::get_instance()->get_update_info();
|
||||
if (class_exists('\Jio\Core\UpdateChecker')) {
|
||||
$stored = \Jio\Core\UpdateChecker::get_instance()->get_update_info();
|
||||
if (is_array($stored)) {
|
||||
$info = array_merge($info, $stored);
|
||||
}
|
||||
}
|
||||
|
||||
$info['dismissed'] = (string) get_user_meta(get_current_user_id(), 'aksrt_dismissed_update', true);
|
||||
$info['dismissed'] = (string) get_user_meta(get_current_user_id(), 'jio_dismissed_update', true);
|
||||
|
||||
return $this->success(['data' => $info]);
|
||||
}
|
||||
|
||||
public function detect_update(): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Core\UpdateChecker')) {
|
||||
return $this->error(__('更新检查器不可用', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Core\UpdateChecker')) {
|
||||
return $this->error(__('更新检查器不可用', 'jio'), 500);
|
||||
}
|
||||
|
||||
\AKSRT\Core\UpdateChecker::get_instance()->do_check();
|
||||
$stored = \AKSRT\Core\UpdateChecker::get_instance()->get_update_info();
|
||||
\Jio\Core\UpdateChecker::get_instance()->do_check();
|
||||
$stored = \Jio\Core\UpdateChecker::get_instance()->get_update_info();
|
||||
|
||||
return $this->success([
|
||||
'message' => __('已完成更新检测', 'aksrt'),
|
||||
'message' => __('已完成更新检测', 'jio'),
|
||||
'data' => is_array($stored) ? $stored : [],
|
||||
]);
|
||||
}
|
||||
@@ -81,13 +81,13 @@ class ToolsController extends ControllerBase {
|
||||
$version = sanitize_text_field((string) ($params['version'] ?? ''));
|
||||
|
||||
if ($version === '') {
|
||||
return $this->error(__('缺少版本号', 'aksrt'), 422);
|
||||
return $this->error(__('缺少版本号', 'jio'), 422);
|
||||
}
|
||||
|
||||
update_user_meta(get_current_user_id(), 'aksrt_dismissed_update', $version);
|
||||
update_user_meta(get_current_user_id(), 'jio_dismissed_update', $version);
|
||||
|
||||
return $this->success([
|
||||
'message' => __('已忽略此版本提醒', 'aksrt'),
|
||||
'message' => __('已忽略此版本提醒', 'jio'),
|
||||
'data' => ['version' => $version],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -61,10 +61,10 @@ class UserController extends ControllerBase {
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'rest_upload_comment_image'],
|
||||
'permission_callback' => static function (): bool {
|
||||
if (!is_user_logged_in() || !class_exists('\AKSRT\Settings\UserRuntime')) {
|
||||
if (!is_user_logged_in() || !class_exists('\Jio\Settings\UserRuntime')) {
|
||||
return false;
|
||||
}
|
||||
$report = \AKSRT\Settings\UserRuntime::report_settings();
|
||||
$report = \Jio\Settings\UserRuntime::report_settings();
|
||||
return !empty($report['enabled']) && !empty($report['img_enabled']);
|
||||
},
|
||||
]);
|
||||
@@ -331,11 +331,11 @@ class UserController extends ControllerBase {
|
||||
* 状态形状与 WeChatNotify::get_status() 对齐(该方法的既定用途即 REST 与 AJAX 复用)。
|
||||
*/
|
||||
public function get_wechat_status(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Payment\WeChatNotify')) {
|
||||
return $this->error(__('微信通知组件未加载', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Payment\WeChatNotify')) {
|
||||
return $this->error(__('微信通知组件未加载', 'jio'), 500);
|
||||
}
|
||||
|
||||
return $this->success(['data' => \AKSRT\Payment\WeChatNotify::get_instance()->get_status()]);
|
||||
return $this->success(['data' => \Jio\Payment\WeChatNotify::get_instance()->get_status()]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,7 +346,7 @@ class UserController extends ControllerBase {
|
||||
*/
|
||||
public function update_wechat_settings(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$params = $this->get_json_params($request);
|
||||
$current = \AKSRT\Settings\Settings::get_wechat_settings();
|
||||
$current = \Jio\Settings\Settings::get_wechat_settings();
|
||||
|
||||
$enabled = array_key_exists('enabled', $params) ? !empty($params['enabled']) : !empty($current['enabled']);
|
||||
$app_id = array_key_exists('app_id', $params)
|
||||
@@ -363,31 +363,31 @@ class UserController extends ControllerBase {
|
||||
$app_secret = (string) ($current['app_secret'] ?? '');
|
||||
}
|
||||
|
||||
\AKSRT\Settings\Settings::update_wechat_settings([
|
||||
\Jio\Settings\Settings::update_wechat_settings([
|
||||
'enabled' => $enabled,
|
||||
'app_id' => $app_id,
|
||||
'app_secret' => $app_secret,
|
||||
'template_id' => $template_id,
|
||||
]);
|
||||
|
||||
return $this->success(['message' => __('微信通知配置已保存', 'aksrt')]);
|
||||
return $this->success(['message' => __('微信通知配置已保存', 'jio')]);
|
||||
}
|
||||
|
||||
public function rest_handle_credit_settings(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if ($request->get_method() === 'GET') {
|
||||
$config = \AKSRT\Settings\CreditsRuntime::get_credit_settings();
|
||||
$config = \Jio\Settings\CreditsRuntime::get_credit_settings();
|
||||
return $this->success(['data' => $config]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$current = get_option('aksrt_credit_settings', []);
|
||||
$current = get_option('jio_credit_settings', []);
|
||||
if (!is_array($current)) {
|
||||
$current = [];
|
||||
}
|
||||
$merged = array_replace_recursive($current, $params);
|
||||
update_option('aksrt_credit_settings', $merged);
|
||||
update_option('jio_credit_settings', $merged);
|
||||
|
||||
return $this->success(['message' => __('积分配置已保存', 'aksrt')]);
|
||||
return $this->success(['message' => __('积分配置已保存', 'jio')]);
|
||||
}
|
||||
|
||||
public function rest_handle_auth_settings(\WP_REST_Request $request): \WP_REST_Response {
|
||||
@@ -395,19 +395,19 @@ class UserController extends ControllerBase {
|
||||
$allowed = $provider->get_allowed_fields('users', 'auth');
|
||||
|
||||
if ($request->get_method() === 'GET') {
|
||||
$data = \AKSRT\Settings\Settings::get_all();
|
||||
$data = \Jio\Settings\Settings::get_all();
|
||||
return $this->success(['data' => array_intersect_key($data, $allowed)]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$settings = \AKSRT\Settings\Settings::get_all();
|
||||
$settings = \Jio\Settings\Settings::get_all();
|
||||
$sanitized = $this->sanitize_fields($params, $allowed);
|
||||
$settings = array_merge($settings, $sanitized);
|
||||
|
||||
update_option('aksrt_settings', $settings);
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
update_option('jio_settings', $settings);
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
|
||||
return $this->success(['message' => __('注册登录配置已保存', 'aksrt')]);
|
||||
return $this->success(['message' => __('注册登录配置已保存', 'jio')]);
|
||||
}
|
||||
|
||||
public function rest_handle_notification_settings(\WP_REST_Request $request): \WP_REST_Response {
|
||||
@@ -415,17 +415,17 @@ class UserController extends ControllerBase {
|
||||
$allowed = $provider->get_allowed_fields('users', 'notifications');
|
||||
|
||||
if ($request->get_method() === 'GET') {
|
||||
$data = \AKSRT\Settings\Settings::get_all();
|
||||
$data = \Jio\Settings\Settings::get_all();
|
||||
return $this->success(['data' => array_intersect_key($data, $allowed)]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$settings = \AKSRT\Settings\Settings::get_all();
|
||||
$settings = \Jio\Settings\Settings::get_all();
|
||||
$settings = array_merge($settings, $this->sanitize_fields($params, $allowed));
|
||||
update_option('aksrt_settings', $settings);
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
update_option('jio_settings', $settings);
|
||||
\Jio\Settings\Settings::flush_settings_cache();
|
||||
|
||||
return $this->success(['message' => __('消息通知配置已保存', 'aksrt')]);
|
||||
return $this->success(['message' => __('消息通知配置已保存', 'jio')]);
|
||||
}
|
||||
|
||||
public function rest_get_notification_overview(\WP_REST_Request $request): \WP_REST_Response {
|
||||
@@ -436,19 +436,19 @@ class UserController extends ControllerBase {
|
||||
|
||||
$items = [];
|
||||
$unread = 0;
|
||||
if (class_exists('\AKSRT\Messages\Message')) {
|
||||
if (class_exists('\Jio\Messages\Message')) {
|
||||
$args = ['limit' => $per_page, 'offset' => ($page - 1) * $per_page];
|
||||
if ($type !== '') {
|
||||
$args['type'] = $type;
|
||||
}
|
||||
$items = \AKSRT\Messages\Message::get_instance()->get_notifications(get_current_user_id(), $args);
|
||||
$items = \Jio\Messages\Message::get_instance()->get_notifications(get_current_user_id(), $args);
|
||||
if ($is_read !== null && $is_read !== '') {
|
||||
$want_read = (string) $is_read === '1';
|
||||
$items = array_values(array_filter($items, static function ($item) use ($want_read) {
|
||||
return !empty($item['is_read']) === $want_read;
|
||||
}));
|
||||
}
|
||||
$unread = \AKSRT\Messages\Message::get_instance()->get_unread_count(get_current_user_id());
|
||||
$unread = \Jio\Messages\Message::get_instance()->get_unread_count(get_current_user_id());
|
||||
}
|
||||
|
||||
$total = count($items);
|
||||
@@ -486,12 +486,12 @@ class UserController extends ControllerBase {
|
||||
}
|
||||
|
||||
public function rest_mark_notification_read(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Messages\Message')) {
|
||||
return $this->success(['message' => __('消息系统未启用', 'aksrt')]);
|
||||
if (!class_exists('\Jio\Messages\Message')) {
|
||||
return $this->success(['message' => __('消息系统未启用', 'jio')]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$msg = \AKSRT\Messages\Message::get_instance();
|
||||
$msg = \Jio\Messages\Message::get_instance();
|
||||
if (!empty($params['mark_all'])) {
|
||||
$msg->mark_all_read(get_current_user_id());
|
||||
} else {
|
||||
@@ -501,54 +501,54 @@ class UserController extends ControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success(['message' => __('已更新消息状态', 'aksrt')]);
|
||||
return $this->success(['message' => __('已更新消息状态', 'jio')]);
|
||||
}
|
||||
|
||||
public function rest_delete_notification_item(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = get_current_user_id();
|
||||
if ($user_id <= 0) {
|
||||
return $this->error(__('请先登录', 'aksrt'), 401);
|
||||
return $this->error(__('请先登录', 'jio'), 401);
|
||||
}
|
||||
$params = $this->get_json_params($request);
|
||||
$id = sanitize_text_field((string) ($params['id'] ?? $request->get_param('id') ?? ''));
|
||||
if ($id === '') {
|
||||
return $this->error(__('参数错误', 'aksrt'), 400);
|
||||
return $this->error(__('参数错误', 'jio'), 400);
|
||||
}
|
||||
if (function_exists('aksrt_delete_user_notification')) {
|
||||
aksrt_delete_user_notification($user_id, $id);
|
||||
} elseif (class_exists('\AKSRT\Messages\Message')) {
|
||||
\AKSRT\Messages\Message::get_instance()->mark_read($user_id, $id);
|
||||
if (function_exists('jio_delete_user_notification')) {
|
||||
jio_delete_user_notification($user_id, $id);
|
||||
} elseif (class_exists('\Jio\Messages\Message')) {
|
||||
\Jio\Messages\Message::get_instance()->mark_read($user_id, $id);
|
||||
}
|
||||
return $this->success([
|
||||
'message' => __('消息已删除', 'aksrt'),
|
||||
'unread' => function_exists('aksrt_get_user_unread_notification_count')
|
||||
? aksrt_get_user_unread_notification_count($user_id)
|
||||
'message' => __('消息已删除', 'jio'),
|
||||
'unread' => function_exists('jio_get_user_unread_notification_count')
|
||||
? jio_get_user_unread_notification_count($user_id)
|
||||
: 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function rest_run_notification_cleanup(\WP_REST_Request $request): \WP_REST_Response {
|
||||
return $this->success(['message' => __('消息清理任务已受理', 'aksrt')]);
|
||||
return $this->success(['message' => __('消息清理任务已受理', 'jio')]);
|
||||
}
|
||||
|
||||
public function check_comment_image_upload_permission(?\WP_REST_Request $request = null) {
|
||||
if (!is_user_logged_in()) {
|
||||
return new \WP_Error('aksrt_auth_required', __('请先登录', 'aksrt'), ['status' => 401]);
|
||||
return new \WP_Error('jio_auth_required', __('请先登录', 'jio'), ['status' => 401]);
|
||||
}
|
||||
if ($request !== null) {
|
||||
$nonce = (string) $request->get_header('X-WP-Nonce');
|
||||
if ($nonce === '' || !wp_verify_nonce($nonce, 'wp_rest')) {
|
||||
return new \WP_Error('aksrt_rest_nonce', __('安全验证失败,请刷新页面后重试', 'aksrt'), ['status' => 403]);
|
||||
return new \WP_Error('jio_rest_nonce', __('安全验证失败,请刷新页面后重试', 'jio'), ['status' => 403]);
|
||||
}
|
||||
}
|
||||
$settings = class_exists('\\AKSRT\\Settings\\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
$settings = class_exists('\\Jio\\Settings\\Settings') ? \Jio\Settings\Settings::get_all() : [];
|
||||
$image_enabled = !isset($settings['comment_img']) || !empty($settings['comment_img']);
|
||||
if (!$image_enabled || empty($settings['comment_upload_img'])) {
|
||||
return new \WP_Error('aksrt_comment_upload_disabled', __('评论图片上传功能未开启', 'aksrt'), ['status' => 403]);
|
||||
return new \WP_Error('jio_comment_upload_disabled', __('评论图片上传功能未开启', 'jio'), ['status' => 403]);
|
||||
}
|
||||
$user_id = get_current_user_id();
|
||||
if (!function_exists('aksrt_user_can') || !aksrt_user_can('upload_comment_image', $user_id)) {
|
||||
return new \WP_Error('aksrt_comment_upload_forbidden', __('当前账号没有上传评论图片的权限', 'aksrt'), ['status' => 403]);
|
||||
if (!function_exists('jio_user_can') || !jio_user_can('upload_comment_image', $user_id)) {
|
||||
return new \WP_Error('jio_comment_upload_forbidden', __('当前账号没有上传评论图片的权限', 'jio'), ['status' => 403]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -556,7 +556,7 @@ class UserController extends ControllerBase {
|
||||
public function rest_upload_comment_image(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = get_current_user_id();
|
||||
if (empty($_FILES['image'])) {
|
||||
return $this->error(__('请选择要上传的图片', 'aksrt'), 400);
|
||||
return $this->error(__('请选择要上传的图片', 'jio'), 400);
|
||||
}
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/media.php';
|
||||
@@ -566,15 +566,15 @@ class UserController extends ControllerBase {
|
||||
$file = $_FILES[$file_key];
|
||||
$upload_error = isset($file['error']) ? (int) $file['error'] : UPLOAD_ERR_NO_FILE;
|
||||
if ($upload_error !== UPLOAD_ERR_OK) {
|
||||
return $this->error(__('图片文件上传失败,请重新选择', 'aksrt'), 400);
|
||||
return $this->error(__('图片文件上传失败,请重新选择', 'jio'), 400);
|
||||
}
|
||||
|
||||
$max_size = class_exists('\AKSRT\Attachment\UploadLimits')
|
||||
? \AKSRT\Attachment\UploadLimits::limit_bytes('image', $user_id)
|
||||
$max_size = class_exists('\Jio\Attachment\UploadLimits')
|
||||
? \Jio\Attachment\UploadLimits::limit_bytes('image', $user_id)
|
||||
: (int) wp_max_upload_size();
|
||||
$actual_size = !empty($file['tmp_name']) && is_readable($file['tmp_name']) ? (int) filesize($file['tmp_name']) : 0;
|
||||
if ($actual_size <= 0 || $actual_size > $max_size) {
|
||||
return $this->error(sprintf(__('图片大小不能超过 %s。', 'aksrt'), size_format($max_size)), 413);
|
||||
return $this->error(sprintf(__('图片大小不能超过 %s。', 'jio'), size_format($max_size)), 413);
|
||||
}
|
||||
|
||||
$mimes = [
|
||||
@@ -586,12 +586,12 @@ class UserController extends ControllerBase {
|
||||
$checked = wp_check_filetype_and_ext($file['tmp_name'], $file['name'], $mimes);
|
||||
$image_size = wp_getimagesize($file['tmp_name']);
|
||||
if (empty($checked['type']) || empty($checked['ext']) || !wp_match_mime_types('image', $checked['type']) || !$image_size || ((int) $image_size[0] * (int) $image_size[1]) > 40000000) {
|
||||
return $this->error(__('仅支持有效的 JPG、PNG、GIF 或 WebP 图片', 'aksrt'), 415);
|
||||
return $this->error(__('仅支持有效的 JPG、PNG、GIF 或 WebP 图片', 'jio'), 415);
|
||||
}
|
||||
if (class_exists('\AKSRT\Extensions\ContentAudit')) {
|
||||
$audit = \AKSRT\Extensions\ContentAudit::get_instance()->audit_image((string) $file['tmp_name']);
|
||||
if (class_exists('\Jio\Extensions\ContentAudit')) {
|
||||
$audit = \Jio\Extensions\ContentAudit::get_instance()->audit_image((string) $file['tmp_name']);
|
||||
if (empty($audit['passed'])) {
|
||||
return $this->error(__('图片未通过内容审核', 'aksrt'), 422);
|
||||
return $this->error(__('图片未通过内容审核', 'jio'), 422);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,17 +605,17 @@ class UserController extends ControllerBase {
|
||||
wp_update_post(['ID' => (int) $attachment_id, 'post_author' => $user_id]);
|
||||
if (!wp_attachment_is_image((int) $attachment_id)) {
|
||||
wp_delete_attachment((int) $attachment_id, true);
|
||||
return $this->error(__('上传文件不是有效图片', 'aksrt'), 415);
|
||||
return $this->error(__('上传文件不是有效图片', 'jio'), 415);
|
||||
}
|
||||
$url = wp_get_attachment_url((int) $attachment_id);
|
||||
if (!$url) {
|
||||
wp_delete_attachment((int) $attachment_id, true);
|
||||
return $this->error(__('上传失败', 'aksrt'), 500);
|
||||
return $this->error(__('上传失败', 'jio'), 500);
|
||||
}
|
||||
// 选图即上传:提交成功前处于待认领状态,× 移除或超时未提交会被清理。
|
||||
\AKSRT\Attachment\PendingUploads::mark((int) $attachment_id);
|
||||
\Jio\Attachment\PendingUploads::mark((int) $attachment_id);
|
||||
return $this->success([
|
||||
'message' => __('图片上传成功', 'aksrt'),
|
||||
'message' => __('图片上传成功', 'jio'),
|
||||
'data' => [
|
||||
'id' => (int) $attachment_id,
|
||||
'url' => $url,
|
||||
@@ -627,18 +627,18 @@ class UserController extends ControllerBase {
|
||||
$user_id = get_current_user_id();
|
||||
return $this->success(['data' => [
|
||||
'user_id' => $user_id,
|
||||
'points' => \AKSRT\Ledger\LedgerService::get_instance()->balance($user_id, \AKSRT\Ledger\AssetType::POINTS),
|
||||
'balance' => \AKSRT\Ledger\LedgerService::get_instance()->balance_decimal($user_id),
|
||||
'points' => \Jio\Ledger\LedgerService::get_instance()->balance($user_id, \Jio\Ledger\AssetType::POINTS),
|
||||
'balance' => \Jio\Ledger\LedgerService::get_instance()->balance_decimal($user_id),
|
||||
]]);
|
||||
}
|
||||
|
||||
public function check_asset_self_permission(\WP_REST_Request $request) {
|
||||
if (!is_user_logged_in()) {
|
||||
return new \WP_Error('aksrt_auth_required', __('请先登录', 'aksrt'), ['status' => 401]);
|
||||
return new \WP_Error('jio_auth_required', __('请先登录', 'jio'), ['status' => 401]);
|
||||
}
|
||||
$nonce = (string) $request->get_header('X-WP-Nonce');
|
||||
if ($nonce === '' || !wp_verify_nonce($nonce, 'wp_rest')) {
|
||||
return new \WP_Error('aksrt_rest_nonce', __('安全验证失败,请刷新页面后重试', 'aksrt'), ['status' => 403]);
|
||||
return new \WP_Error('jio_rest_nonce', __('安全验证失败,请刷新页面后重试', 'jio'), ['status' => 403]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -666,7 +666,7 @@ class UserController extends ControllerBase {
|
||||
$rows = $wpdb->get_results($wpdb->prepare($sql, array_merge($params, [$per_page, ($page - 1) * $per_page])), ARRAY_A);
|
||||
$items = [];
|
||||
foreach ((array) $rows as $row) {
|
||||
try { $balance = \AKSRT\Ledger\AssetAmount::balance((string) ($row['balance_value'] ?? '0'))->decimal(); } catch (\InvalidArgumentException $e) { $balance = '0.00'; }
|
||||
try { $balance = \Jio\Ledger\AssetAmount::balance((string) ($row['balance_value'] ?? '0'))->decimal(); } catch (\InvalidArgumentException $e) { $balance = '0.00'; }
|
||||
$items[] = [
|
||||
'id' => (int) $row['id'], 'login' => (string) $row['login'], 'email' => (string) $row['email'],
|
||||
'display_name' => (string) $row['display_name'], 'registered' => (string) $row['registered'],
|
||||
@@ -684,10 +684,10 @@ class UserController extends ControllerBase {
|
||||
$raw_delta = $params['delta'] ?? null;
|
||||
$remark = sanitize_text_field((string) ($params['remark'] ?? ''));
|
||||
if ($user_id <= 0 || !in_array($asset, ['points', 'balance'], true) || !get_userdata($user_id)) {
|
||||
return $this->error(__('用户或资产类型无效', 'aksrt'), 400);
|
||||
return $this->error(__('用户或资产类型无效', 'jio'), 400);
|
||||
}
|
||||
if (!current_user_can('edit_user', $user_id)) {
|
||||
return $this->error(__('无权调整该用户资产', 'aksrt'), 403);
|
||||
return $this->error(__('无权调整该用户资产', 'jio'), 403);
|
||||
}
|
||||
$negative = is_string($raw_delta) && str_starts_with($raw_delta, '-');
|
||||
$unsigned = $negative ? substr($raw_delta, 1) : $raw_delta;
|
||||
@@ -698,15 +698,15 @@ class UserController extends ControllerBase {
|
||||
$negative = $raw_delta < 0;
|
||||
} else {
|
||||
if (!is_string($unsigned)) throw new \InvalidArgumentException();
|
||||
$minor = \AKSRT\Ledger\AssetAmount::balance($unsigned)->minor();
|
||||
$minor = \Jio\Ledger\AssetAmount::balance($unsigned)->minor();
|
||||
}
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return $this->error(__('资产变动金额格式无效', 'aksrt'), 400);
|
||||
return $this->error(__('资产变动金额格式无效', 'jio'), 400);
|
||||
}
|
||||
if ($minor <= 0) return $this->error(__('资产变动金额必须非零', 'aksrt'), 400);
|
||||
$operation_key = \AKSRT\Ledger\OperationKey::from_request($request, 'admin-adjustment', get_current_user_id());
|
||||
if ($minor <= 0) return $this->error(__('资产变动金额必须非零', 'jio'), 400);
|
||||
$operation_key = \Jio\Ledger\OperationKey::from_request($request, 'admin-adjustment', get_current_user_id());
|
||||
if (is_wp_error($operation_key)) return new \WP_REST_Response(['success' => false, 'message' => $operation_key->get_error_message()], 400);
|
||||
$service = \AKSRT\Ledger\LedgerService::get_instance();
|
||||
$service = \Jio\Ledger\LedgerService::get_instance();
|
||||
$result = $negative
|
||||
? $service->debit($user_id, $asset, $minor, $operation_key, 'admin_adjustment', ['type' => 'user', 'id' => $user_id], $remark, get_current_user_id())
|
||||
: $service->credit($user_id, $asset, $minor, $operation_key, 'admin_adjustment', ['type' => 'user', 'id' => $user_id], $remark, get_current_user_id());
|
||||
@@ -720,10 +720,10 @@ class UserController extends ControllerBase {
|
||||
'user_id' => $user_id,
|
||||
'asset' => $asset,
|
||||
'balance' => $asset === 'balance'
|
||||
? \AKSRT\Ledger\AssetAmount::from_minor(\AKSRT\Ledger\AssetType::BALANCE, $result->balance($user_id, $asset))->decimal()
|
||||
? \Jio\Ledger\AssetAmount::from_minor(\Jio\Ledger\AssetType::BALANCE, $result->balance($user_id, $asset))->decimal()
|
||||
: $result->balance($user_id, $asset),
|
||||
'replayed' => $result->replayed,
|
||||
], 'message' => __('资产调整已保存', 'aksrt')]);
|
||||
], 'message' => __('资产调整已保存', 'jio')]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* Shared public authentication REST operations.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -30,7 +30,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
}
|
||||
|
||||
private function auth_require_captcha(string $scene, array $payload): ?\WP_REST_Response {
|
||||
$result = aksrt_require_captcha($scene, $payload);
|
||||
$result = jio_require_captcha($scene, $payload);
|
||||
if ($result === true) {
|
||||
return null;
|
||||
}
|
||||
@@ -38,24 +38,24 @@ trait UserControllerAuthPhoneOperations {
|
||||
if (is_wp_error($result)) {
|
||||
$data = $result->get_error_data();
|
||||
$status = is_array($data) ? (int) ($data['status'] ?? 400) : 400;
|
||||
$message = $result->get_error_message() ?: __('人机验证失败', 'aksrt');
|
||||
$message = $result->get_error_message() ?: __('人机验证失败', 'jio');
|
||||
return new \WP_REST_Response([
|
||||
'success' => false,
|
||||
'code' => $result->get_error_code() ?: 'aksrt_captcha_failed',
|
||||
'code' => $result->get_error_code() ?: 'jio_captcha_failed',
|
||||
'message' => $message,
|
||||
], $status > 0 ? $status : 400);
|
||||
}
|
||||
|
||||
return new \WP_REST_Response([
|
||||
'success' => false,
|
||||
'code' => 'aksrt_captcha_failed',
|
||||
'message' => __('人机验证失败', 'aksrt'),
|
||||
'code' => 'jio_captcha_failed',
|
||||
'message' => __('人机验证失败', 'jio'),
|
||||
], 400);
|
||||
}
|
||||
|
||||
private function auth_rate_key(string $action, string $subject = ''): string {
|
||||
$ip = sanitize_text_field((string) ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'));
|
||||
return 'aksrt_auth_rate_' . md5($action . '|' . strtolower($subject) . '|' . $ip);
|
||||
return 'jio_auth_rate_' . md5($action . '|' . strtolower($subject) . '|' . $ip);
|
||||
}
|
||||
|
||||
private function auth_rate_limited(string $action, string $subject = '', int $limit = 8, int $ttl = 600): bool {
|
||||
@@ -69,7 +69,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
}
|
||||
|
||||
private function auth_code_key(string $scene, string $target): string {
|
||||
return 'aksrt_auth_code_' . md5($scene . '|' . strtolower(trim($target)));
|
||||
return 'jio_auth_code_' . md5($scene . '|' . strtolower(trim($target)));
|
||||
}
|
||||
|
||||
private function auth_store_code(string $scene, string $target, string $code, int $ttl = 10 * MINUTE_IN_SECONDS): void {
|
||||
@@ -108,7 +108,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
}
|
||||
|
||||
private function auth_channel(string $target, string $requested = ''): string {
|
||||
if ($requested === 'phone' || (class_exists('\\AKSRT\\Sms\\SMS') && \AKSRT\Sms\SMS::is_phonenumber($target))) {
|
||||
if ($requested === 'phone' || (class_exists('\\Jio\\Sms\\SMS') && \Jio\Sms\SMS::is_phonenumber($target))) {
|
||||
return 'phone';
|
||||
}
|
||||
return 'email';
|
||||
@@ -125,7 +125,7 @@ trait UserControllerAuthPhoneOperations {
|
||||
|
||||
private function auth_registration_error(string $error): string {
|
||||
if (str_contains($error, '已被') || str_contains($error, '已注册') || str_contains($error, '已使用')) {
|
||||
return __('注册信息不可用,请检查后重试', 'aksrt');
|
||||
return __('注册信息不可用,请检查后重试', 'jio');
|
||||
}
|
||||
return $error;
|
||||
}
|
||||
@@ -147,30 +147,30 @@ trait UserControllerAuthPhoneOperations {
|
||||
public function auth_code(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'jio'), 403);
|
||||
}
|
||||
|
||||
$scene = sanitize_key((string) ($payload['scene'] ?? ''));
|
||||
$target = trim(sanitize_text_field((string) ($payload['target'] ?? '')));
|
||||
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
||||
$obk = sanitize_text_field((string) ($payload['obk'] ?? ''));
|
||||
$oauth_bind = $obk !== '' && class_exists('\AKSRT\Auth\Service') && is_array(\AKSRT\Auth\Service::get_oauth_bind_context($obk));
|
||||
$oauth_bind = $obk !== '' && class_exists('\Jio\Auth\Service') && is_array(\Jio\Auth\Service::get_oauth_bind_context($obk));
|
||||
if (!in_array($scene, ['login', 'register', 'resetpassword'], true)) {
|
||||
return $this->error(__('请求类型无效', 'aksrt'));
|
||||
return $this->error(__('请求类型无效', 'jio'));
|
||||
}
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
if (\AKSRT\Settings\Settings::close_sign()) return $this->error(__('登录注册已关闭', 'aksrt'), 403);
|
||||
if (($scene === 'login' || $scene === 'resetpassword') && \AKSRT\Settings\Settings::close_signin() && !$oauth_bind) return $this->error(__('当前登录方式已关闭', 'aksrt'), 403);
|
||||
if ($scene === 'register' && !\AKSRT\Settings\Settings::registration_enabled() && !$oauth_bind) return $this->error(__('当前已关闭新用户注册', 'aksrt'), 403);
|
||||
$type = $scene === 'login' ? \AKSRT\Settings\Settings::passwordless_login_type() : ($scene === 'register' ? \AKSRT\Settings\Settings::signup_verification_type() : \AKSRT\Settings\Settings::reset_password_verification_type());
|
||||
$enabled = $scene === 'login' ? \AKSRT\Settings\Settings::passwordless_login_enabled() : ($scene === 'register' ? \AKSRT\Settings\Settings::signup_verification_enabled() : true);
|
||||
if (!$enabled || ($type !== 'email_phone' && $type !== $channel)) return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
||||
if (class_exists('\Jio\Settings\Settings')) {
|
||||
if (\Jio\Settings\Settings::close_sign()) return $this->error(__('登录注册已关闭', 'jio'), 403);
|
||||
if (($scene === 'login' || $scene === 'resetpassword') && \Jio\Settings\Settings::close_signin() && !$oauth_bind) return $this->error(__('当前登录方式已关闭', 'jio'), 403);
|
||||
if ($scene === 'register' && !\Jio\Settings\Settings::registration_enabled() && !$oauth_bind) return $this->error(__('当前已关闭新用户注册', 'jio'), 403);
|
||||
$type = $scene === 'login' ? \Jio\Settings\Settings::passwordless_login_type() : ($scene === 'register' ? \Jio\Settings\Settings::signup_verification_type() : \Jio\Settings\Settings::reset_password_verification_type());
|
||||
$enabled = $scene === 'login' ? \Jio\Settings\Settings::passwordless_login_enabled() : ($scene === 'register' ? \Jio\Settings\Settings::signup_verification_enabled() : true);
|
||||
if (!$enabled || ($type !== 'email_phone' && $type !== $channel)) return $this->error(__('当前验证方式不可用', 'jio'), 403);
|
||||
}
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !\AKSRT\Sms\SMS::is_phonenumber($target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !\Jio\Sms\SMS::is_phonenumber($target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'jio'));
|
||||
}
|
||||
if ($this->auth_rate_limited('code:' . $scene, $target, 5, HOUR_IN_SECONDS)) {
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'jio'), 429);
|
||||
}
|
||||
$captcha_scene = $scene === 'resetpassword' ? 'lostpassword' : $scene;
|
||||
$captcha_error = $this->auth_require_captcha($captcha_scene, $payload);
|
||||
@@ -178,27 +178,27 @@ trait UserControllerAuthPhoneOperations {
|
||||
return $captcha_error;
|
||||
}
|
||||
|
||||
$generic = __('如果信息有效,验证码将发送到该账号', 'aksrt');
|
||||
$generic = __('如果信息有效,验证码将发送到该账号', 'jio');
|
||||
if ($scene !== 'register' && !$this->auth_find_user($target, $channel)) {
|
||||
return $this->success(['message' => $generic, 'countdown' => 60]);
|
||||
}
|
||||
|
||||
if ($channel === 'phone') {
|
||||
if (!class_exists('\AKSRT\Sms\SMS')) {
|
||||
return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
||||
if (!class_exists('\Jio\Sms\SMS')) {
|
||||
return $this->error(__('短信服务不可用', 'jio'), 503);
|
||||
}
|
||||
$code = (string) random_int(100000, 999999);
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->send_code($target, $code);
|
||||
$result = \Jio\Sms\SMS::get_instance()->send_code($target, $code);
|
||||
if (empty($result['success'])) {
|
||||
return $this->error((string) ($result['message'] ?? __('验证码发送失败', 'aksrt')));
|
||||
return $this->error((string) ($result['message'] ?? __('验证码发送失败', 'jio')));
|
||||
}
|
||||
$this->auth_store_code($scene, $target, $code, 30 * MINUTE_IN_SECONDS);
|
||||
return $this->success(['message' => $generic, 'countdown' => (int) ($result['remaining_time'] ?? 60)]);
|
||||
}
|
||||
|
||||
// [防刷机制]验证码发送限制(短信通道由 SMS::send_code 内部接入)。
|
||||
if (class_exists('\AKSRT\Extensions\SendLimit')) {
|
||||
$limit = \AKSRT\Extensions\SendLimit::check($target);
|
||||
if (class_exists('\Jio\Extensions\SendLimit')) {
|
||||
$limit = \Jio\Extensions\SendLimit::check($target);
|
||||
if (empty($limit['ok'])) {
|
||||
return $this->error((string) $limit['message'], 429);
|
||||
}
|
||||
@@ -218,8 +218,8 @@ trait UserControllerAuthPhoneOperations {
|
||||
'{site_name}' => $site_name,
|
||||
'{code}' => $code,
|
||||
];
|
||||
$sent = $scene_event !== '' && class_exists('\\AKSRT\\Settings\\Settings')
|
||||
? \AKSRT\Settings\Settings::send_event_email($scene_event, $target, $placeholders)
|
||||
$sent = $scene_event !== '' && class_exists('\\Jio\\Settings\\Settings')
|
||||
? \Jio\Settings\Settings::send_event_email($scene_event, $target, $placeholders)
|
||||
: false;
|
||||
|
||||
if (!$sent) {
|
||||
@@ -227,9 +227,9 @@ trait UserControllerAuthPhoneOperations {
|
||||
if ($scene !== 'register') {
|
||||
return $this->success(['message' => $generic, 'countdown' => 60]);
|
||||
}
|
||||
return $this->error(__('验证码发送失败,请稍后再试', 'aksrt'), 500);
|
||||
return $this->error(__('验证码发送失败,请稍后再试', 'jio'), 500);
|
||||
}
|
||||
$cooldown = class_exists('\AKSRT\Extensions\SendLimit') ? (int) \AKSRT\Extensions\SendLimit::record($target) : 0;
|
||||
$cooldown = class_exists('\Jio\Extensions\SendLimit') ? (int) \Jio\Extensions\SendLimit::record($target) : 0;
|
||||
$this->auth_store_code($scene, $target, $code);
|
||||
return $this->success(['message' => $generic, 'countdown' => $cooldown > 0 ? $cooldown : 60]);
|
||||
|
||||
@@ -238,27 +238,27 @@ trait UserControllerAuthPhoneOperations {
|
||||
public function auth_login(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'jio'), 403);
|
||||
}
|
||||
$target = trim(sanitize_text_field((string) ($payload['target'] ?? $payload['log'] ?? '')));
|
||||
$obk = sanitize_text_field((string) ($payload['obk'] ?? ''));
|
||||
$oauth_data = $obk !== '' && class_exists('\AKSRT\Auth\Service')
|
||||
? \AKSRT\Auth\Service::get_oauth_bind_context($obk)
|
||||
$oauth_data = $obk !== '' && class_exists('\Jio\Auth\Service')
|
||||
? \Jio\Auth\Service::get_oauth_bind_context($obk)
|
||||
: null;
|
||||
if ($obk !== '' && !$oauth_data) {
|
||||
return $this->error(__('绑定会话已过期,请重新使用第三方登录', 'aksrt'), 410);
|
||||
return $this->error(__('绑定会话已过期,请重新使用第三方登录', 'jio'), 410);
|
||||
}
|
||||
if (
|
||||
class_exists('\AKSRT\Settings\Settings')
|
||||
&& (\AKSRT\Settings\Settings::close_sign() || (!$oauth_data && \AKSRT\Settings\Settings::close_signin()))
|
||||
class_exists('\Jio\Settings\Settings')
|
||||
&& (\Jio\Settings\Settings::close_sign() || (!$oauth_data && \Jio\Settings\Settings::close_signin()))
|
||||
) {
|
||||
return $this->error(__('当前登录方式已关闭', 'aksrt'), 403);
|
||||
return $this->error(__('当前登录方式已关闭', 'jio'), 403);
|
||||
}
|
||||
if ($target === '' || (\AKSRT\Sms\SMS::is_phonenumber($target) && empty($payload['code']) && class_exists('\AKSRT\Settings\Settings') && !\AKSRT\Settings\Settings::signin_phone_enabled())) {
|
||||
return $this->error(__('账号或密码错误', 'aksrt'), 401);
|
||||
if ($target === '' || (\Jio\Sms\SMS::is_phonenumber($target) && empty($payload['code']) && class_exists('\Jio\Settings\Settings') && !\Jio\Settings\Settings::signin_phone_enabled())) {
|
||||
return $this->error(__('账号或密码错误', 'jio'), 401);
|
||||
}
|
||||
if ($this->auth_rate_limited('login', $target)) {
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'jio'), 429);
|
||||
}
|
||||
$captcha_error = $this->auth_require_captcha($oauth_data ? 'bind' : 'login', $payload);
|
||||
if ($captcha_error) {
|
||||
@@ -267,104 +267,104 @@ trait UserControllerAuthPhoneOperations {
|
||||
$redirect = $this->auth_safe_redirect((string) ($payload['redirect_to'] ?? ''), home_url('/'));
|
||||
|
||||
if (!empty($payload['code'])) {
|
||||
if (!class_exists('\AKSRT\Settings\Settings') || !\AKSRT\Settings\Settings::passwordless_login_enabled()) {
|
||||
return $this->error(__('当前未开启免密登录', 'aksrt'));
|
||||
if (!class_exists('\Jio\Settings\Settings') || !\Jio\Settings\Settings::passwordless_login_enabled()) {
|
||||
return $this->error(__('当前未开启免密登录', 'jio'));
|
||||
}
|
||||
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
||||
$configured = \AKSRT\Settings\Settings::passwordless_login_type();
|
||||
$configured = \Jio\Settings\Settings::passwordless_login_type();
|
||||
if (!$this->auth_channel_allowed($configured, $channel)) {
|
||||
return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
||||
return $this->error(__('当前验证方式不可用', 'jio'), 403);
|
||||
}
|
||||
$user = $this->auth_find_user($target, $channel);
|
||||
if (!$user || !$this->auth_consume_code('login', $target, sanitize_text_field((string) $payload['code']))) {
|
||||
return $this->error(__('账号或验证码错误', 'aksrt'), 401);
|
||||
return $this->error(__('账号或验证码错误', 'jio'), 401);
|
||||
}
|
||||
if ($oauth_data) {
|
||||
$oauth_data = \AKSRT\Auth\Service::claim_oauth_bind_context($obk);
|
||||
$oauth_data = \Jio\Auth\Service::claim_oauth_bind_context($obk);
|
||||
if (!$oauth_data) {
|
||||
return $this->error(__('绑定会话已被使用,请重新授权', 'aksrt'), 410);
|
||||
return $this->error(__('绑定会话已被使用,请重新授权', 'jio'), 410);
|
||||
}
|
||||
$binding = \AKSRT\Auth\Service::finalize_oauth_binding((int) $user->ID, $oauth_data, $obk);
|
||||
$binding = \Jio\Auth\Service::finalize_oauth_binding((int) $user->ID, $oauth_data, $obk);
|
||||
if (is_wp_error($binding)) {
|
||||
return $this->error($binding->get_error_message(), 409);
|
||||
}
|
||||
\AKSRT\Auth\OAuthAccountService::complete_login($user, !empty($payload['rememberme']));
|
||||
$redirect = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : $redirect;
|
||||
return $this->success(['message' => __('账号绑定成功', 'aksrt'), 'redirect' => $redirect]);
|
||||
\Jio\Auth\OAuthAccountService::complete_login($user, !empty($payload['rememberme']));
|
||||
$redirect = function_exists('jio_get_user_center_url') ? jio_get_user_center_url() : $redirect;
|
||||
return $this->success(['message' => __('账号绑定成功', 'jio'), 'redirect' => $redirect]);
|
||||
}
|
||||
wp_set_current_user((int) $user->ID);
|
||||
wp_set_auth_cookie((int) $user->ID, !empty($payload['rememberme']));
|
||||
do_action('wp_login', $user->user_login, $user);
|
||||
return $this->success(['message' => __('登录成功', 'aksrt'), 'redirect' => $redirect]);
|
||||
return $this->success(['message' => __('登录成功', 'jio'), 'redirect' => $redirect]);
|
||||
}
|
||||
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Auth\Service')) {
|
||||
return $this->error(__('认证服务不可用', 'jio'), 500);
|
||||
}
|
||||
$payload['log'] = $target;
|
||||
$payload['pwd'] = (string) ($payload['password'] ?? $payload['pwd'] ?? '');
|
||||
$payload['redirect_to'] = $redirect;
|
||||
if ($oauth_data) {
|
||||
$resolved_login = \AKSRT\Auth\Service::resolve_email_to_username($target);
|
||||
$resolved_login = \Jio\Auth\Service::resolve_email_to_username($target);
|
||||
$user = wp_authenticate($resolved_login, (string) ($payload['password'] ?? $payload['pwd'] ?? ''));
|
||||
if (is_wp_error($user) || !$user instanceof \WP_User) {
|
||||
// 透传具体失败原因,不得泛化为“账号或密码错误”。
|
||||
$bind_error = $user instanceof \WP_Error
|
||||
? \AKSRT\Auth\Service::friendly_login_error($user->get_error_message())
|
||||
? \Jio\Auth\Service::friendly_login_error($user->get_error_message())
|
||||
: '';
|
||||
return $this->error($bind_error !== '' ? $bind_error : __('账号或密码错误', 'aksrt'), 401);
|
||||
return $this->error($bind_error !== '' ? $bind_error : __('账号或密码错误', 'jio'), 401);
|
||||
}
|
||||
$oauth_data = \AKSRT\Auth\Service::claim_oauth_bind_context($obk);
|
||||
$oauth_data = \Jio\Auth\Service::claim_oauth_bind_context($obk);
|
||||
if (!$oauth_data) {
|
||||
return $this->error(__('绑定会话已被使用,请重新授权', 'aksrt'), 410);
|
||||
return $this->error(__('绑定会话已被使用,请重新授权', 'jio'), 410);
|
||||
}
|
||||
$binding = \AKSRT\Auth\Service::finalize_oauth_binding((int) $user->ID, $oauth_data, $obk);
|
||||
$binding = \Jio\Auth\Service::finalize_oauth_binding((int) $user->ID, $oauth_data, $obk);
|
||||
if (is_wp_error($binding)) {
|
||||
return $this->error($binding->get_error_message(), 409);
|
||||
}
|
||||
\AKSRT\Auth\OAuthAccountService::complete_login($user, !empty($payload['rememberme']));
|
||||
$redirect = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : $redirect;
|
||||
return $this->success(['message' => __('账号绑定成功', 'aksrt'), 'redirect' => $redirect]);
|
||||
\Jio\Auth\OAuthAccountService::complete_login($user, !empty($payload['rememberme']));
|
||||
$redirect = function_exists('jio_get_user_center_url') ? jio_get_user_center_url() : $redirect;
|
||||
return $this->success(['message' => __('账号绑定成功', 'jio'), 'redirect' => $redirect]);
|
||||
}
|
||||
$result = \AKSRT\Auth\Service::login_user($payload);
|
||||
$result = \Jio\Auth\Service::login_user($payload);
|
||||
if (empty($result['success'])) {
|
||||
// 透传具体失败原因:禁封等场景不得泛化为“账号或密码错误”。
|
||||
$login_error = trim((string) ($result['error'] ?? ''));
|
||||
return $this->error($login_error !== '' ? $login_error : __('账号或密码错误', 'aksrt'), 401);
|
||||
return $this->error($login_error !== '' ? $login_error : __('账号或密码错误', 'jio'), 401);
|
||||
}
|
||||
return $this->success(['message' => $oauth_data ? __('账号绑定成功', 'aksrt') : __('登录成功', 'aksrt'), 'redirect' => $redirect]);
|
||||
return $this->success(['message' => $oauth_data ? __('账号绑定成功', 'jio') : __('登录成功', 'jio'), 'redirect' => $redirect]);
|
||||
}
|
||||
|
||||
public function auth_register(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'jio'), 403);
|
||||
}
|
||||
$obk = sanitize_text_field((string) ($payload['obk'] ?? ''));
|
||||
$oauth_data = $obk !== '' && class_exists('\AKSRT\Auth\Service')
|
||||
? \AKSRT\Auth\Service::get_oauth_bind_context($obk)
|
||||
$oauth_data = $obk !== '' && class_exists('\Jio\Auth\Service')
|
||||
? \Jio\Auth\Service::get_oauth_bind_context($obk)
|
||||
: null;
|
||||
if ($obk !== '' && !$oauth_data) {
|
||||
return $this->error(__('绑定会话已过期,请重新使用第三方登录', 'aksrt'), 410);
|
||||
return $this->error(__('绑定会话已过期,请重新使用第三方登录', 'jio'), 410);
|
||||
}
|
||||
if (class_exists('\AKSRT\Settings\Settings') && (
|
||||
\AKSRT\Settings\Settings::close_sign()
|
||||
|| ($oauth_data ? !\AKSRT\Settings\Settings::oauth_registration_enabled() : !\AKSRT\Settings\Settings::registration_enabled())
|
||||
if (class_exists('\Jio\Settings\Settings') && (
|
||||
\Jio\Settings\Settings::close_sign()
|
||||
|| ($oauth_data ? !\Jio\Settings\Settings::oauth_registration_enabled() : !\Jio\Settings\Settings::registration_enabled())
|
||||
)) {
|
||||
return $this->error(__('当前已关闭新用户注册', 'aksrt'), 403);
|
||||
return $this->error(__('当前已关闭新用户注册', 'jio'), 403);
|
||||
}
|
||||
if ($this->auth_rate_limited('register')) {
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'jio'), 429);
|
||||
}
|
||||
$target = trim(sanitize_text_field((string) ($payload['target'] ?? $payload['user_email'] ?? $payload['phone'] ?? '')));
|
||||
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
||||
$signup_verification = class_exists('\AKSRT\Settings\Settings') && \AKSRT\Settings\Settings::signup_verification_enabled();
|
||||
$configured = $signup_verification ? \AKSRT\Settings\Settings::signup_verification_type() : 'email';
|
||||
$signup_verification = class_exists('\Jio\Settings\Settings') && \Jio\Settings\Settings::signup_verification_enabled();
|
||||
$configured = $signup_verification ? \Jio\Settings\Settings::signup_verification_type() : 'email';
|
||||
if (!$this->auth_channel_allowed($configured, $channel)) {
|
||||
return $this->error(__('当前注册方式不可用', 'aksrt'), 403);
|
||||
return $this->error(__('当前注册方式不可用', 'jio'), 403);
|
||||
}
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !\AKSRT\Sms\SMS::is_phonenumber($target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !\Jio\Sms\SMS::is_phonenumber($target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'jio'));
|
||||
}
|
||||
$captcha_error = $this->auth_require_captcha('register', $payload);
|
||||
if ($captcha_error) {
|
||||
@@ -373,43 +373,43 @@ trait UserControllerAuthPhoneOperations {
|
||||
$verification_completed = false;
|
||||
if ($signup_verification) {
|
||||
if (!$this->auth_consume_code('register', $target, sanitize_text_field((string) ($payload['code'] ?? '')))) {
|
||||
return $this->error(__('验证码错误或已过期', 'aksrt'));
|
||||
return $this->error(__('验证码错误或已过期', 'jio'));
|
||||
}
|
||||
$verification_completed = true;
|
||||
}
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Auth\Service')) {
|
||||
return $this->error(__('认证服务不可用', 'jio'), 500);
|
||||
}
|
||||
if ($channel === 'phone') {
|
||||
$payload['phone'] = $target;
|
||||
$result = \AKSRT\Auth\Service::register_by_phone($payload, $verification_completed, !empty($oauth_data));
|
||||
$result = \Jio\Auth\Service::register_by_phone($payload, $verification_completed, !empty($oauth_data));
|
||||
if (empty($result['success'])) {
|
||||
return $this->error($this->auth_registration_error((string) ($result['error'] ?? __('注册失败', 'aksrt'))));
|
||||
return $this->error($this->auth_registration_error((string) ($result['error'] ?? __('注册失败', 'jio'))));
|
||||
}
|
||||
$user_id = (int) ($result['user_id'] ?? 0);
|
||||
if ($oauth_data && $user_id > 0) {
|
||||
$binding = \AKSRT\Auth\Service::finalize_oauth_binding($user_id, $oauth_data, $obk);
|
||||
$binding = \Jio\Auth\Service::finalize_oauth_binding($user_id, $oauth_data, $obk);
|
||||
if (is_wp_error($binding)) {
|
||||
$this->delete_oauth_registration_user($user_id);
|
||||
return $this->error($binding->get_error_message(), 409);
|
||||
}
|
||||
$user = get_user_by('id', $user_id);
|
||||
if ($user instanceof \WP_User) {
|
||||
\AKSRT\Auth\OAuthAccountService::complete_login($user, true);
|
||||
\Jio\Auth\OAuthAccountService::complete_login($user, true);
|
||||
}
|
||||
}
|
||||
return $this->success(['message' => $oauth_data ? __('注册并绑定成功', 'aksrt') : __('注册成功', 'aksrt'), 'redirect' => $result['redirect'] ?? home_url('/'), 'notice' => (string) ($result['notice'] ?? '')]);
|
||||
return $this->success(['message' => $oauth_data ? __('注册并绑定成功', 'jio') : __('注册成功', 'jio'), 'redirect' => $result['redirect'] ?? home_url('/'), 'notice' => (string) ($result['notice'] ?? '')]);
|
||||
}
|
||||
$payload['user_email'] = sanitize_email($target);
|
||||
$payload['user_pass'] = (string) ($payload['password'] ?? '');
|
||||
$payload['user_pass_confirm'] = (string) ($payload['password_confirm'] ?? $payload['password'] ?? '');
|
||||
$result = \AKSRT\Auth\Service::register_user($payload, $verification_completed, !empty($oauth_data));
|
||||
$result = \Jio\Auth\Service::register_user($payload, $verification_completed, !empty($oauth_data));
|
||||
if (empty($result['success'])) {
|
||||
return $this->error($this->auth_registration_error((string) ($result['error'] ?? __('注册失败', 'aksrt'))));
|
||||
return $this->error($this->auth_registration_error((string) ($result['error'] ?? __('注册失败', 'jio'))));
|
||||
}
|
||||
$user_id = (int) ($result['user_id'] ?? 0);
|
||||
if ($oauth_data && $user_id > 0) {
|
||||
$binding = \AKSRT\Auth\Service::finalize_oauth_binding($user_id, $oauth_data, $obk);
|
||||
$binding = \Jio\Auth\Service::finalize_oauth_binding($user_id, $oauth_data, $obk);
|
||||
if (is_wp_error($binding)) {
|
||||
$this->delete_oauth_registration_user($user_id);
|
||||
return $this->error($binding->get_error_message(), 409);
|
||||
@@ -417,13 +417,13 @@ trait UserControllerAuthPhoneOperations {
|
||||
$user = get_user_by('id', $user_id);
|
||||
if ($user instanceof \WP_User) {
|
||||
if ($channel === 'email' && $verification_completed) {
|
||||
\AKSRT\Auth\Service::mark_verified_email($user_id);
|
||||
\Jio\Auth\Service::mark_verified_email($user_id);
|
||||
}
|
||||
\AKSRT\Auth\OAuthAccountService::complete_login($user, true);
|
||||
\Jio\Auth\OAuthAccountService::complete_login($user, true);
|
||||
}
|
||||
}
|
||||
return $this->success([
|
||||
'message' => $oauth_data ? __('注册并绑定成功', 'aksrt') : __('注册成功', 'aksrt'),
|
||||
'message' => $oauth_data ? __('注册并绑定成功', 'jio') : __('注册成功', 'jio'),
|
||||
'redirect' => $result['redirect'] ?? home_url('/'),
|
||||
'notice' => (string) ($result['notice'] ?? ''),
|
||||
]);
|
||||
@@ -432,20 +432,20 @@ trait UserControllerAuthPhoneOperations {
|
||||
public function auth_reset_password(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'jio'), 403);
|
||||
}
|
||||
$target = trim(sanitize_text_field((string) ($payload['target'] ?? '')));
|
||||
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
if (\AKSRT\Settings\Settings::close_sign() || \AKSRT\Settings\Settings::close_signin() || !$this->auth_channel_allowed(\AKSRT\Settings\Settings::reset_password_verification_type(), $channel)) {
|
||||
return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
||||
if (class_exists('\Jio\Settings\Settings')) {
|
||||
if (\Jio\Settings\Settings::close_sign() || \Jio\Settings\Settings::close_signin() || !$this->auth_channel_allowed(\Jio\Settings\Settings::reset_password_verification_type(), $channel)) {
|
||||
return $this->error(__('当前验证方式不可用', 'jio'), 403);
|
||||
}
|
||||
}
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !\AKSRT\Sms\SMS::is_phonenumber($target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !\Jio\Sms\SMS::is_phonenumber($target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'jio'));
|
||||
}
|
||||
if ($this->auth_rate_limited('reset-password', $target, 8, HOUR_IN_SECONDS)) {
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'jio'), 429);
|
||||
}
|
||||
$captcha_error = $this->auth_require_captcha('lostpassword', $payload);
|
||||
if ($captcha_error) {
|
||||
@@ -454,25 +454,25 @@ trait UserControllerAuthPhoneOperations {
|
||||
$user = $this->auth_find_user($target, $channel);
|
||||
$password = (string) ($payload['password'] ?? '');
|
||||
$confirm = (string) ($payload['password_confirm'] ?? '');
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
||||
if (!class_exists('\Jio\Auth\Service')) {
|
||||
return $this->error(__('认证服务不可用', 'jio'), 500);
|
||||
}
|
||||
$password_check = \AKSRT\Auth\Service::validate_password($password, $confirm);
|
||||
$password_check = \Jio\Auth\Service::validate_password($password, $confirm);
|
||||
if ($password_check !== true) {
|
||||
return $this->error((string) $password_check);
|
||||
}
|
||||
if (!$user || !$this->auth_consume_code('resetpassword', $target, sanitize_text_field((string) ($payload['code'] ?? '')))) {
|
||||
return $this->error(__('账号或验证码错误', 'aksrt'));
|
||||
return $this->error(__('账号或验证码错误', 'jio'));
|
||||
}
|
||||
wp_set_password($password, (int) $user->ID);
|
||||
return $this->success(['message' => __('密码已重置,请重新登录', 'aksrt'), 'redirect' => function_exists('aksrt_get_sign_page_url') ? aksrt_get_sign_page_url('signin') : wp_login_url()]);
|
||||
return $this->success(['message' => __('密码已重置,请重新登录', 'jio'), 'redirect' => function_exists('jio_get_sign_page_url') ? jio_get_sign_page_url('signin') : wp_login_url()]);
|
||||
}
|
||||
|
||||
public function user_phone_code(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'aksrt'), 403);
|
||||
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
if (empty($settings['bind_phone'])) return $this->error(__('手机号绑定功能暂未开启', 'aksrt'), 403);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'jio'), 403);
|
||||
$settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_all() : [];
|
||||
if (empty($settings['bind_phone'])) return $this->error(__('手机号绑定功能暂未开启', 'jio'), 403);
|
||||
$stage = sanitize_key((string) ($payload['stage'] ?? 'bind_new_phone'));
|
||||
$user_id = get_current_user_id();
|
||||
$current_phone = (string) get_user_meta($user_id, 'phone_number', true);
|
||||
@@ -482,49 +482,49 @@ trait UserControllerAuthPhoneOperations {
|
||||
if ($current_phone !== '') {
|
||||
delete_transient($this->auth_code_key('account_verify_old_phone_' . $user_id, $current_phone));
|
||||
}
|
||||
return $this->success(['message' => __('已取消本次验证', 'aksrt')]);
|
||||
return $this->success(['message' => __('已取消本次验证', 'jio')]);
|
||||
}
|
||||
|
||||
if ($stage === 'verify_old_phone') {
|
||||
// 老号验证只接收脱敏中间位,发送目标恒为服务端绑定号,
|
||||
// 避免客户端提交任意号码导致向第三方手机号发码。
|
||||
if ($current_phone === '') return $this->error(__('当前绑定手机号已变化,请刷新页面重试', 'aksrt'), 409);
|
||||
$parts = function_exists('aksrt_phone_mask_parts') ? aksrt_phone_mask_parts($current_phone) : null;
|
||||
if ($current_phone === '') return $this->error(__('当前绑定手机号已变化,请刷新页面重试', 'jio'), 409);
|
||||
$parts = function_exists('jio_phone_mask_parts') ? jio_phone_mask_parts($current_phone) : null;
|
||||
if ($parts !== null) {
|
||||
$middle = sanitize_text_field((string) ($payload['middle'] ?? ''));
|
||||
if (!$this->auth_middle_matches($parts, $middle)) {
|
||||
if (class_exists('\AKSRT\Extensions\SecurityHardening')) {
|
||||
\AKSRT\Extensions\SecurityHardening::error_frequency_record();
|
||||
if (class_exists('\Jio\Extensions\SecurityHardening')) {
|
||||
\Jio\Extensions\SecurityHardening::error_frequency_record();
|
||||
}
|
||||
return $this->error(__('填写的号码与绑定手机号不一致', 'aksrt'), 400);
|
||||
return $this->error(__('填写的号码与绑定手机号不一致', 'jio'), 400);
|
||||
}
|
||||
}
|
||||
$phone = $current_phone;
|
||||
} elseif ($stage === 'bind_new_phone') {
|
||||
$phone = sanitize_text_field((string) ($payload['phone'] ?? ''));
|
||||
if (!\AKSRT\Sms\SMS::is_phonenumber($phone)) return $this->error(__('手机号格式不正确', 'aksrt'));
|
||||
if (!\Jio\Sms\SMS::is_phonenumber($phone)) return $this->error(__('手机号格式不正确', 'jio'));
|
||||
if ($current_phone !== '' && !hash_equals($current_phone, $phone)) {
|
||||
$proof = get_transient('aksrt_account_phone_old_' . $user_id);
|
||||
if (!is_array($proof) || !hash_equals($current_phone, (string) ($proof['phone'] ?? ''))) return $this->error(__('请先验证老手机号', 'aksrt'), 403);
|
||||
$proof = get_transient('jio_account_phone_old_' . $user_id);
|
||||
if (!is_array($proof) || !hash_equals($current_phone, (string) ($proof['phone'] ?? ''))) return $this->error(__('请先验证老手机号', 'jio'), 403);
|
||||
}
|
||||
$users = get_users(['meta_key' => 'phone_number', 'meta_value' => $phone, 'number' => 1, 'fields' => 'ID']);
|
||||
if (!empty($users) && (int) $users[0] !== $user_id) return $this->error(__('该手机号已被其他账号绑定', 'aksrt'), 409);
|
||||
if ($current_phone !== '' && hash_equals($current_phone, $phone)) return $this->error(__('手机号不能与当前绑定号码相同', 'aksrt'), 409);
|
||||
if (!empty($users) && (int) $users[0] !== $user_id) return $this->error(__('该手机号已被其他账号绑定', 'jio'), 409);
|
||||
if ($current_phone !== '' && hash_equals($current_phone, $phone)) return $this->error(__('手机号不能与当前绑定号码相同', 'jio'), 409);
|
||||
} else {
|
||||
return $this->error(__('无效的验证步骤', 'aksrt'));
|
||||
return $this->error(__('无效的验证步骤', 'jio'));
|
||||
}
|
||||
$captcha_error = $this->auth_require_captcha('bind_phone', $payload);
|
||||
if ($captcha_error) return $captcha_error;
|
||||
if (class_exists('\AKSRT\Extensions\SecurityHardening')) {
|
||||
$wait = \AKSRT\Extensions\SecurityHardening::error_frequency_wait();
|
||||
if ($wait > 0) return $this->error(sprintf(__('验证错误次数过多,请 %d 秒后再试', 'aksrt'), $wait), 429);
|
||||
if (class_exists('\Jio\Extensions\SecurityHardening')) {
|
||||
$wait = \Jio\Extensions\SecurityHardening::error_frequency_wait();
|
||||
if ($wait > 0) return $this->error(sprintf(__('验证错误次数过多,请 %d 秒后再试', 'jio'), $wait), 429);
|
||||
}
|
||||
if (!class_exists('\AKSRT\Sms\SMS')) return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
||||
if (!class_exists('\Jio\Sms\SMS')) return $this->error(__('短信服务不可用', 'jio'), 503);
|
||||
$code = (string) random_int(100000, 999999);
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->send_code($phone, $code);
|
||||
if (empty($result['success'])) return $this->error((string) ($result['message'] ?? __('发送失败', 'aksrt')));
|
||||
$result = \Jio\Sms\SMS::get_instance()->send_code($phone, $code);
|
||||
if (empty($result['success'])) return $this->error((string) ($result['message'] ?? __('发送失败', 'jio')));
|
||||
$this->auth_store_code('account_' . $stage . '_' . $user_id, $phone, $code, 30 * MINUTE_IN_SECONDS);
|
||||
return $this->success(['message' => __('验证码短信已发送', 'aksrt'), 'countdown' => (int) ($result['remaining_time'] ?? 60)]);
|
||||
return $this->success(['message' => __('验证码短信已发送', 'jio'), 'countdown' => (int) ($result['remaining_time'] ?? 60)]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -532,33 +532,33 @@ trait UserControllerAuthPhoneOperations {
|
||||
*/
|
||||
public function user_phone_match(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'aksrt'), 403);
|
||||
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
if (empty($settings['bind_phone'])) return $this->error(__('手机号绑定功能暂未开启', 'aksrt'), 403);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'jio'), 403);
|
||||
$settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_all() : [];
|
||||
if (empty($settings['bind_phone'])) return $this->error(__('手机号绑定功能暂未开启', 'jio'), 403);
|
||||
$user_id = get_current_user_id();
|
||||
$current_phone = (string) get_user_meta($user_id, 'phone_number', true);
|
||||
$parts = function_exists('aksrt_phone_mask_parts') ? aksrt_phone_mask_parts($current_phone) : null;
|
||||
if ($parts === null) return $this->error(__('当前绑定手机号不支持此验证方式', 'aksrt'), 409);
|
||||
$parts = function_exists('jio_phone_mask_parts') ? jio_phone_mask_parts($current_phone) : null;
|
||||
if ($parts === null) return $this->error(__('当前绑定手机号不支持此验证方式', 'jio'), 409);
|
||||
|
||||
// 中间位是可枚举面:错误频率限制 + 每用户/IP 每小时尝试上限双保险。
|
||||
if (class_exists('\AKSRT\Extensions\SecurityHardening')) {
|
||||
$wait = \AKSRT\Extensions\SecurityHardening::error_frequency_wait();
|
||||
if ($wait > 0) return $this->error(sprintf(__('验证错误次数过多,请 %d 秒后再试', 'aksrt'), $wait), 429);
|
||||
if (class_exists('\Jio\Extensions\SecurityHardening')) {
|
||||
$wait = \Jio\Extensions\SecurityHardening::error_frequency_wait();
|
||||
if ($wait > 0) return $this->error(sprintf(__('验证错误次数过多,请 %d 秒后再试', 'jio'), $wait), 429);
|
||||
}
|
||||
$ip = function_exists('aksrt_get_client_ip') ? aksrt_get_client_ip() : (string) ($_SERVER['REMOTE_ADDR'] ?? '');
|
||||
$ip = function_exists('jio_get_client_ip') ? jio_get_client_ip() : (string) ($_SERVER['REMOTE_ADDR'] ?? '');
|
||||
if ($this->auth_rate_limited('phone-match', (string) $user_id . '|' . $ip, 20, HOUR_IN_SECONDS)) {
|
||||
return $this->error(__('尝试次数过多,请稍后再试', 'aksrt'), 429);
|
||||
return $this->error(__('尝试次数过多,请稍后再试', 'jio'), 429);
|
||||
}
|
||||
|
||||
$middle = sanitize_text_field((string) ($payload['middle'] ?? ''));
|
||||
if (!$this->auth_middle_matches($parts, $middle)) {
|
||||
if (class_exists('\AKSRT\Extensions\SecurityHardening')) {
|
||||
\AKSRT\Extensions\SecurityHardening::error_frequency_record();
|
||||
if (class_exists('\Jio\Extensions\SecurityHardening')) {
|
||||
\Jio\Extensions\SecurityHardening::error_frequency_record();
|
||||
}
|
||||
return $this->error(__('填写的号码与绑定手机号不一致', 'aksrt'), 400);
|
||||
return $this->error(__('填写的号码与绑定手机号不一致', 'jio'), 400);
|
||||
}
|
||||
if (class_exists('\AKSRT\Extensions\SecurityHardening')) {
|
||||
\AKSRT\Extensions\SecurityHardening::error_frequency_clear();
|
||||
if (class_exists('\Jio\Extensions\SecurityHardening')) {
|
||||
\Jio\Extensions\SecurityHardening::error_frequency_clear();
|
||||
}
|
||||
return $this->success(['match' => true]);
|
||||
}
|
||||
@@ -576,76 +576,76 @@ trait UserControllerAuthPhoneOperations {
|
||||
|
||||
public function user_verify_phone(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'aksrt'), 403);
|
||||
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
if (empty($settings['bind_phone'])) return $this->error(__('手机号绑定功能暂未开启', 'aksrt'), 403);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'jio'), 403);
|
||||
$settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_all() : [];
|
||||
if (empty($settings['bind_phone'])) return $this->error(__('手机号绑定功能暂未开启', 'jio'), 403);
|
||||
$captcha_error = $this->auth_require_captcha('bind_phone', $payload);
|
||||
if ($captcha_error) return $captcha_error;
|
||||
$user_id = get_current_user_id();
|
||||
$current_phone = (string) get_user_meta($user_id, 'phone_number', true);
|
||||
$code = sanitize_text_field((string) ($payload['code'] ?? ''));
|
||||
if ($current_phone === '') return $this->error(__('当前绑定手机号已变化,请刷新页面重试', 'aksrt'), 409);
|
||||
if ($current_phone === '') return $this->error(__('当前绑定手机号已变化,请刷新页面重试', 'jio'), 409);
|
||||
if (!$this->auth_consume_code('account_verify_old_phone_' . $user_id, $current_phone, $code)) {
|
||||
if (class_exists('\AKSRT\Extensions\SecurityHardening')) {
|
||||
\AKSRT\Extensions\SecurityHardening::error_frequency_record();
|
||||
if (class_exists('\Jio\Extensions\SecurityHardening')) {
|
||||
\Jio\Extensions\SecurityHardening::error_frequency_record();
|
||||
}
|
||||
return $this->error(__('验证码错误或已过期', 'aksrt'));
|
||||
return $this->error(__('验证码错误或已过期', 'jio'));
|
||||
}
|
||||
if (class_exists('\AKSRT\Extensions\SecurityHardening')) {
|
||||
\AKSRT\Extensions\SecurityHardening::error_frequency_clear();
|
||||
if (class_exists('\Jio\Extensions\SecurityHardening')) {
|
||||
\Jio\Extensions\SecurityHardening::error_frequency_clear();
|
||||
}
|
||||
set_transient('aksrt_account_phone_old_' . $user_id, ['phone' => $current_phone], 30 * MINUTE_IN_SECONDS);
|
||||
return $this->success(['message' => __('手机号验证成功', 'aksrt')]);
|
||||
set_transient('jio_account_phone_old_' . $user_id, ['phone' => $current_phone], 30 * MINUTE_IN_SECONDS);
|
||||
return $this->success(['message' => __('手机号验证成功', 'jio')]);
|
||||
}
|
||||
|
||||
public function user_bind_phone(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'aksrt'), 403);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'jio'), 403);
|
||||
$captcha_error = $this->auth_require_captcha('bind_phone', $payload);
|
||||
if ($captcha_error) return $captcha_error;
|
||||
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||||
if (empty($settings['bind_phone'])) return $this->error(__('手机号绑定功能暂未开启', 'aksrt'), 403);
|
||||
$settings = class_exists('\Jio\Settings\Settings') ? \Jio\Settings\Settings::get_all() : [];
|
||||
if (empty($settings['bind_phone'])) return $this->error(__('手机号绑定功能暂未开启', 'jio'), 403);
|
||||
$user_id = get_current_user_id();
|
||||
$current_phone = (string) get_user_meta($user_id, 'phone_number', true);
|
||||
$phone = sanitize_text_field((string) ($payload['phone'] ?? ''));
|
||||
$code = sanitize_text_field((string) ($payload['code'] ?? ''));
|
||||
if (!\AKSRT\Sms\SMS::is_phonenumber($phone)) return $this->error(__('手机号格式不正确', 'aksrt'));
|
||||
if (!\Jio\Sms\SMS::is_phonenumber($phone)) return $this->error(__('手机号格式不正确', 'jio'));
|
||||
if ($current_phone !== '') {
|
||||
$proof = get_transient('aksrt_account_phone_old_' . $user_id);
|
||||
if (!is_array($proof) || !hash_equals($current_phone, (string) ($proof['phone'] ?? ''))) return $this->error(__('请先验证老手机号', 'aksrt'), 403);
|
||||
if (hash_equals($current_phone, $phone)) return $this->error(__('手机号不能与当前绑定号码相同', 'aksrt'), 409);
|
||||
$proof = get_transient('jio_account_phone_old_' . $user_id);
|
||||
if (!is_array($proof) || !hash_equals($current_phone, (string) ($proof['phone'] ?? ''))) return $this->error(__('请先验证老手机号', 'jio'), 403);
|
||||
if (hash_equals($current_phone, $phone)) return $this->error(__('手机号不能与当前绑定号码相同', 'jio'), 409);
|
||||
}
|
||||
$users = get_users(['meta_key' => 'phone_number', 'meta_value' => $phone, 'number' => 1, 'fields' => 'ID']);
|
||||
if (!empty($users) && (int) $users[0] !== $user_id) return $this->error(__('该手机号已被其他账号绑定', 'aksrt'), 409);
|
||||
$agreement = function_exists('aksrt_get_auth_agreement_view') ? aksrt_get_auth_agreement_view() : ['enabled' => false, 'required' => false];
|
||||
if (!empty($agreement['required']) && empty($payload['agree_terms'])) return $this->error(__('请先同意相关协议', 'aksrt'), 400);
|
||||
if (!empty($users) && (int) $users[0] !== $user_id) return $this->error(__('该手机号已被其他账号绑定', 'jio'), 409);
|
||||
$agreement = function_exists('jio_get_auth_agreement_view') ? jio_get_auth_agreement_view() : ['enabled' => false, 'required' => false];
|
||||
if (!empty($agreement['required']) && empty($payload['agree_terms'])) return $this->error(__('请先同意相关协议', 'jio'), 400);
|
||||
$code_scene = 'account_bind_new_phone_' . $user_id;
|
||||
if (!$this->auth_consume_code($code_scene, $phone, $code, false)) {
|
||||
if (class_exists('\AKSRT\Extensions\SecurityHardening')) {
|
||||
\AKSRT\Extensions\SecurityHardening::error_frequency_record();
|
||||
if (class_exists('\Jio\Extensions\SecurityHardening')) {
|
||||
\Jio\Extensions\SecurityHardening::error_frequency_record();
|
||||
}
|
||||
return $this->error(__('验证码错误或已过期', 'aksrt'));
|
||||
return $this->error(__('验证码错误或已过期', 'jio'));
|
||||
}
|
||||
if (!class_exists('\AKSRT\Auth\Service')) return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
||||
$result = \AKSRT\Auth\Service::bind_phone($payload, true);
|
||||
if (empty($result['success'])) return $this->error((string) ($result['error'] ?? __('绑定失败', 'aksrt')));
|
||||
if (class_exists('\AKSRT\Extensions\SecurityHardening')) {
|
||||
\AKSRT\Extensions\SecurityHardening::error_frequency_clear();
|
||||
if (!class_exists('\Jio\Auth\Service')) return $this->error(__('认证服务不可用', 'jio'), 500);
|
||||
$result = \Jio\Auth\Service::bind_phone($payload, true);
|
||||
if (empty($result['success'])) return $this->error((string) ($result['error'] ?? __('绑定失败', 'jio')));
|
||||
if (class_exists('\Jio\Extensions\SecurityHardening')) {
|
||||
\Jio\Extensions\SecurityHardening::error_frequency_clear();
|
||||
}
|
||||
delete_transient($this->auth_code_key($code_scene, $phone));
|
||||
delete_transient('aksrt_account_phone_old_' . $user_id);
|
||||
return $this->success(['message' => $result['message'] ?? __('绑定成功', 'aksrt')]);
|
||||
delete_transient('jio_account_phone_old_' . $user_id);
|
||||
return $this->success(['message' => $result['message'] ?? __('绑定成功', 'jio')]);
|
||||
}
|
||||
|
||||
public function test_sms_send(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$params = $this->get_json_params($request);
|
||||
$phone = sanitize_text_field((string) ($params['phone'] ?? ''));
|
||||
if (!class_exists('\\AKSRT\\Sms\\SMS')) {
|
||||
return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
||||
if (!class_exists('\\Jio\\Sms\\SMS')) {
|
||||
return $this->error(__('短信服务不可用', 'jio'), 503);
|
||||
}
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->test_send($phone);
|
||||
$result = \Jio\Sms\SMS::get_instance()->test_send($phone);
|
||||
return !empty($result['success'])
|
||||
? $this->success(['message' => (string) ($result['message'] ?? __('测试短信已发送', 'aksrt'))])
|
||||
: $this->error((string) ($result['message'] ?? __('测试短信发送失败', 'aksrt')));
|
||||
? $this->success(['message' => (string) ($result['message'] ?? __('测试短信已发送', 'jio'))])
|
||||
: $this->error((string) ($result['message'] ?? __('测试短信发送失败', 'jio')));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -13,7 +13,7 @@ trait UserControllerFollowOperations {
|
||||
* POST /follow/toggle — 切换关注状态
|
||||
*/
|
||||
public function follow_toggle(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\User\Follow')) {
|
||||
if (!class_exists('\Jio\User\Follow')) {
|
||||
return $this->error('关注系统未启用', 500);
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ trait UserControllerFollowOperations {
|
||||
return $this->error('用户不存在', 404);
|
||||
}
|
||||
|
||||
$is_following = \AKSRT\User\Follow::get_instance()->toggle($current_user_id, $target_user_id);
|
||||
$is_following = \Jio\User\Follow::get_instance()->toggle($current_user_id, $target_user_id);
|
||||
|
||||
return $this->success([
|
||||
'data' => [
|
||||
'is_following' => $is_following,
|
||||
'follower_count' => \AKSRT\User\Follow::get_instance()->get_follower_count($target_user_id),
|
||||
'follower_count' => \Jio\User\Follow::get_instance()->get_follower_count($target_user_id),
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ trait UserControllerFollowOperations {
|
||||
* GET /follow/status — 查询关注状态
|
||||
*/
|
||||
public function follow_status(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\User\Follow')) {
|
||||
if (!class_exists('\Jio\User\Follow')) {
|
||||
return $this->success(['data' => ['is_following' => false]]);
|
||||
}
|
||||
|
||||
@@ -55,13 +55,13 @@ trait UserControllerFollowOperations {
|
||||
return $this->error('参数无效');
|
||||
}
|
||||
|
||||
$is_following = \AKSRT\User\Follow::get_instance()->is_following($current_user_id, $target_user_id);
|
||||
$is_following = \Jio\User\Follow::get_instance()->is_following($current_user_id, $target_user_id);
|
||||
|
||||
return $this->success([
|
||||
'data' => [
|
||||
'is_following' => $is_following,
|
||||
'follower_count' => \AKSRT\User\Follow::get_instance()->get_follower_count($target_user_id),
|
||||
'following_count' => \AKSRT\User\Follow::get_instance()->get_following_count($target_user_id),
|
||||
'follower_count' => \Jio\User\Follow::get_instance()->get_follower_count($target_user_id),
|
||||
'following_count' => \Jio\User\Follow::get_instance()->get_following_count($target_user_id),
|
||||
],
|
||||
]);
|
||||
}
|
||||
@@ -70,7 +70,7 @@ trait UserControllerFollowOperations {
|
||||
* GET /follow/counts — 获取关注/粉丝数
|
||||
*/
|
||||
public function follow_counts(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\User\Follow')) {
|
||||
if (!class_exists('\Jio\User\Follow')) {
|
||||
return $this->success(['data' => ['followers' => 0, 'following' => 0]]);
|
||||
}
|
||||
|
||||
@@ -81,8 +81,8 @@ trait UserControllerFollowOperations {
|
||||
|
||||
return $this->success([
|
||||
'data' => [
|
||||
'followers' => \AKSRT\User\Follow::get_instance()->get_follower_count($user_id),
|
||||
'following' => \AKSRT\User\Follow::get_instance()->get_following_count($user_id),
|
||||
'followers' => \Jio\User\Follow::get_instance()->get_follower_count($user_id),
|
||||
'following' => \Jio\User\Follow::get_instance()->get_following_count($user_id),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
/**
|
||||
* Logged-in user profile media uploads.
|
||||
*
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -13,8 +13,8 @@ trait UserControllerProfileMediaOperations {
|
||||
public function rest_upload_user_avatar(\WP_REST_Request $request): \WP_REST_Response {
|
||||
return $this->upload_profile_image(
|
||||
['avatar', 'file'],
|
||||
'aksrt_avatar_attachment_id',
|
||||
__('头像已更新', 'aksrt'),
|
||||
'jio_avatar_attachment_id',
|
||||
__('头像已更新', 'jio'),
|
||||
true
|
||||
);
|
||||
}
|
||||
@@ -22,25 +22,25 @@ trait UserControllerProfileMediaOperations {
|
||||
public function rest_delete_user_avatar(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = get_current_user_id();
|
||||
if ($user_id <= 0) {
|
||||
return $this->error(__('请先登录', 'aksrt'), 401);
|
||||
return $this->error(__('请先登录', 'jio'), 401);
|
||||
}
|
||||
|
||||
$attachment_id = (int) get_user_meta($user_id, 'aksrt_avatar_attachment_id', true);
|
||||
delete_user_meta($user_id, 'aksrt_avatar_attachment_id');
|
||||
$attachment_id = (int) get_user_meta($user_id, 'jio_avatar_attachment_id', true);
|
||||
delete_user_meta($user_id, 'jio_avatar_attachment_id');
|
||||
$this->delete_owned_profile_attachment($attachment_id, $user_id);
|
||||
do_action('aksrt_user_avatar_changed', $user_id, 0, '');
|
||||
do_action('jio_user_avatar_changed', $user_id, 0, '');
|
||||
|
||||
return $this->success([
|
||||
'message' => __('已恢复默认头像', 'aksrt'),
|
||||
'url' => function_exists('aksrt_get_default_avatar_url') ? aksrt_get_default_avatar_url() : '',
|
||||
'message' => __('已恢复默认头像', 'jio'),
|
||||
'url' => function_exists('jio_get_default_avatar_url') ? jio_get_default_avatar_url() : '',
|
||||
]);
|
||||
}
|
||||
|
||||
public function rest_upload_user_background(\WP_REST_Request $request): \WP_REST_Response {
|
||||
return $this->upload_profile_image(
|
||||
['background', 'file'],
|
||||
'aksrt_background_attachment_id',
|
||||
__('背景图已更新', 'aksrt'),
|
||||
'jio_background_attachment_id',
|
||||
__('背景图已更新', 'jio'),
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -51,16 +51,16 @@ trait UserControllerProfileMediaOperations {
|
||||
private function upload_profile_image(array $file_keys, string $meta_key, string $message, bool $is_avatar): \WP_REST_Response {
|
||||
$user_id = get_current_user_id();
|
||||
if ($user_id <= 0) {
|
||||
return $this->error(__('请先登录', 'aksrt'), 401);
|
||||
return $this->error(__('请先登录', 'jio'), 401);
|
||||
}
|
||||
|
||||
$upload_enabled = $is_avatar
|
||||
? function_exists('aksrt_is_avatar_upload_enabled') && aksrt_is_avatar_upload_enabled()
|
||||
: function_exists('aksrt_is_user_cover_upload_enabled') && aksrt_is_user_cover_upload_enabled();
|
||||
? function_exists('jio_is_avatar_upload_enabled') && jio_is_avatar_upload_enabled()
|
||||
: function_exists('jio_is_user_cover_upload_enabled') && jio_is_user_cover_upload_enabled();
|
||||
if (!$upload_enabled) {
|
||||
return $this->error($is_avatar
|
||||
? __('当前账号没有上传头像的权限', 'aksrt')
|
||||
: __('当前账号没有上传用户资料卡背景图的权限', 'aksrt'), 403);
|
||||
? __('当前账号没有上传头像的权限', 'jio')
|
||||
: __('当前账号没有上传用户资料卡背景图的权限', 'jio'), 403);
|
||||
}
|
||||
|
||||
$file_key = '';
|
||||
@@ -71,7 +71,7 @@ trait UserControllerProfileMediaOperations {
|
||||
}
|
||||
}
|
||||
if ($file_key === '') {
|
||||
return $this->error(__('请选择要上传的图片', 'aksrt'), 400);
|
||||
return $this->error(__('请选择要上传的图片', 'jio'), 400);
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
@@ -81,16 +81,16 @@ trait UserControllerProfileMediaOperations {
|
||||
$file = $_FILES[$file_key];
|
||||
$upload_error = isset($file['error']) ? (int) $file['error'] : UPLOAD_ERR_NO_FILE;
|
||||
if ($upload_error !== UPLOAD_ERR_OK) {
|
||||
return $this->error(__('头像文件上传失败,请重新选择', 'aksrt'), 400);
|
||||
return $this->error(__('头像文件上传失败,请重新选择', 'jio'), 400);
|
||||
}
|
||||
|
||||
// 头像与资料卡背景图并入「前端图片上传限制」统一治理(参考主题同源)。
|
||||
$max_size = class_exists('\AKSRT\Attachment\UploadLimits')
|
||||
? \AKSRT\Attachment\UploadLimits::limit_bytes('image', $user_id)
|
||||
$max_size = class_exists('\Jio\Attachment\UploadLimits')
|
||||
? \Jio\Attachment\UploadLimits::limit_bytes('image', $user_id)
|
||||
: (int) wp_max_upload_size();
|
||||
$actual_size = !empty($file['tmp_name']) && is_readable($file['tmp_name']) ? (int) filesize($file['tmp_name']) : 0;
|
||||
if ($actual_size <= 0 || $actual_size > $max_size) {
|
||||
return $this->error(sprintf(__('图片大小不能超过 %s。', 'aksrt'), size_format($max_size)), 413);
|
||||
return $this->error(sprintf(__('图片大小不能超过 %s。', 'jio'), size_format($max_size)), 413);
|
||||
}
|
||||
|
||||
$checked = wp_check_filetype_and_ext($file['tmp_name'], $file['name'], [
|
||||
@@ -100,17 +100,17 @@ trait UserControllerProfileMediaOperations {
|
||||
'webp' => 'image/webp',
|
||||
]);
|
||||
if (empty($checked['type']) || empty($checked['ext']) || !wp_match_mime_types('image', $checked['type'])) {
|
||||
return $this->error(__('仅支持 JPG、PNG、GIF 或 WebP 图片', 'aksrt'), 415);
|
||||
return $this->error(__('仅支持 JPG、PNG、GIF 或 WebP 图片', 'jio'), 415);
|
||||
}
|
||||
|
||||
$image_size = wp_getimagesize($file['tmp_name']);
|
||||
if (!$image_size || empty($image_size[0]) || empty($image_size[1]) || ((int) $image_size[0] * (int) $image_size[1]) > 40000000) {
|
||||
return $this->error(__('图片无效或尺寸过大', 'aksrt'), 415);
|
||||
return $this->error(__('图片无效或尺寸过大', 'jio'), 415);
|
||||
}
|
||||
if (!$is_avatar && class_exists('\AKSRT\Extensions\ContentAudit')) {
|
||||
$audit = \AKSRT\Extensions\ContentAudit::get_instance()->audit_image((string) $file['tmp_name']);
|
||||
if (!$is_avatar && class_exists('\Jio\Extensions\ContentAudit')) {
|
||||
$audit = \Jio\Extensions\ContentAudit::get_instance()->audit_image((string) $file['tmp_name']);
|
||||
if (empty($audit['passed'])) {
|
||||
return $this->error(__('图片未通过内容审核', 'aksrt'), 422);
|
||||
return $this->error(__('图片未通过内容审核', 'jio'), 422);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,21 +131,21 @@ trait UserControllerProfileMediaOperations {
|
||||
wp_update_post(['ID' => $attachment_id, 'post_author' => $user_id]);
|
||||
if (!wp_attachment_is_image($attachment_id)) {
|
||||
wp_delete_attachment($attachment_id, true);
|
||||
return $this->error(__('上传文件不是有效图片', 'aksrt'), 415);
|
||||
return $this->error(__('上传文件不是有效图片', 'jio'), 415);
|
||||
}
|
||||
update_post_meta($attachment_id, '_aksrt_profile_media_user_id', $user_id);
|
||||
update_post_meta($attachment_id, '_aksrt_profile_media_type', $is_avatar ? 'avatar' : 'background');
|
||||
update_post_meta($attachment_id, '_jio_profile_media_user_id', $user_id);
|
||||
update_post_meta($attachment_id, '_jio_profile_media_type', $is_avatar ? 'avatar' : 'background');
|
||||
|
||||
$url = wp_get_attachment_image_url($attachment_id, $is_avatar ? 'thumbnail' : 'full');
|
||||
if (!$url) {
|
||||
wp_delete_attachment($attachment_id, true);
|
||||
return $this->error(__('上传失败', 'aksrt'), 500);
|
||||
return $this->error(__('上传失败', 'jio'), 500);
|
||||
}
|
||||
|
||||
$previous_id = (int) get_user_meta($user_id, $meta_key, true);
|
||||
update_user_meta($user_id, $meta_key, $attachment_id);
|
||||
if (!$is_avatar) {
|
||||
update_user_meta($user_id, 'aksrt_background_image', esc_url_raw($url));
|
||||
update_user_meta($user_id, 'jio_background_image', esc_url_raw($url));
|
||||
}
|
||||
|
||||
if ($previous_id > 0 && $previous_id !== $attachment_id) {
|
||||
@@ -153,7 +153,7 @@ trait UserControllerProfileMediaOperations {
|
||||
}
|
||||
|
||||
if ($is_avatar) {
|
||||
do_action('aksrt_user_avatar_changed', $user_id, $attachment_id, $url);
|
||||
do_action('jio_user_avatar_changed', $user_id, $attachment_id, $url);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
@@ -176,7 +176,7 @@ trait UserControllerProfileMediaOperations {
|
||||
return false;
|
||||
}
|
||||
|
||||
$managed_user_id = (int) get_post_meta($attachment_id, '_aksrt_profile_media_user_id', true);
|
||||
$managed_user_id = (int) get_post_meta($attachment_id, '_jio_profile_media_user_id', true);
|
||||
if ($managed_user_id > 0 && $managed_user_id !== $user_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait UserControllerReportOperations {
|
||||
|
||||
public function get_report_list(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Messages\ReportCenter')) {
|
||||
if (!class_exists('\Jio\Messages\ReportCenter')) {
|
||||
return $this->success([
|
||||
'data' => [
|
||||
'items' => [],
|
||||
@@ -27,7 +27,7 @@ trait UserControllerReportOperations {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$table = \AKSRT\Core\DBMigrator::table('report');
|
||||
$table = \Jio\Core\DBMigrator::table('report');
|
||||
if (!$table) {
|
||||
return $this->success([
|
||||
'data' => [
|
||||
@@ -92,7 +92,7 @@ trait UserControllerReportOperations {
|
||||
$total = count($items);
|
||||
$offset = ($page - 1) * $per_page;
|
||||
$paged = array_slice($items, $offset, $per_page);
|
||||
$counts = \AKSRT\Messages\ReportCenter::get_instance()->get_status_counts();
|
||||
$counts = \Jio\Messages\ReportCenter::get_instance()->get_status_counts();
|
||||
$categories = [];
|
||||
foreach ((array) $rows as $row) {
|
||||
$cat = sanitize_text_field((string) ($row['category'] ?? ''));
|
||||
@@ -125,15 +125,15 @@ trait UserControllerReportOperations {
|
||||
global $wpdb;
|
||||
|
||||
$report_id = (int) $request->get_param('id');
|
||||
$table = \AKSRT\Core\DBMigrator::table('report');
|
||||
$table = \Jio\Core\DBMigrator::table('report');
|
||||
|
||||
if ($report_id <= 0 || !$table) {
|
||||
return $this->error(__('举报工单不存在', 'aksrt'), 404);
|
||||
return $this->error(__('举报工单不存在', 'jio'), 404);
|
||||
}
|
||||
|
||||
$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE id = %d", $report_id), ARRAY_A);
|
||||
if (!$row) {
|
||||
return $this->error(__('举报工单不存在', 'aksrt'), 404);
|
||||
return $this->error(__('举报工单不存在', 'jio'), 404);
|
||||
}
|
||||
|
||||
$related = (int) $wpdb->get_var($wpdb->prepare(
|
||||
@@ -157,7 +157,7 @@ trait UserControllerReportOperations {
|
||||
public function process_report(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$report_id = (int) $request->get_param('id');
|
||||
if ($report_id <= 0) {
|
||||
return $this->error(__('参数无效', 'aksrt'));
|
||||
return $this->error(__('参数无效', 'jio'));
|
||||
}
|
||||
|
||||
return $this->handle_report_process($report_id, $this->get_json_params($request));
|
||||
@@ -166,9 +166,9 @@ trait UserControllerReportOperations {
|
||||
private function handle_report_process(int $report_id, array $payload): \WP_REST_Response {
|
||||
global $wpdb;
|
||||
|
||||
$table = \AKSRT\Core\DBMigrator::table('report');
|
||||
$table = \Jio\Core\DBMigrator::table('report');
|
||||
if (!$table) {
|
||||
return $this->error(__('举报系统未启用', 'aksrt'));
|
||||
return $this->error(__('举报系统未启用', 'jio'));
|
||||
}
|
||||
|
||||
$status = sanitize_key((string) ($payload['status'] ?? ''));
|
||||
@@ -179,7 +179,7 @@ trait UserControllerReportOperations {
|
||||
$valid_actions = array_column($this->get_report_action_options(), 'value');
|
||||
|
||||
if ($status === '' || !in_array($status, $valid_statuses, true)) {
|
||||
return $this->error(__('处理状态无效', 'aksrt'));
|
||||
return $this->error(__('处理状态无效', 'jio'));
|
||||
}
|
||||
if (!in_array($action_taken, $valid_actions, true)) {
|
||||
$action_taken = 'none';
|
||||
@@ -187,7 +187,7 @@ trait UserControllerReportOperations {
|
||||
|
||||
$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE id = %d", $report_id), ARRAY_A);
|
||||
if (!$row) {
|
||||
return $this->error(__('举报工单不存在', 'aksrt'), 404);
|
||||
return $this->error(__('举报工单不存在', 'jio'), 404);
|
||||
}
|
||||
|
||||
$action_reason = (string) ($row['category'] ?? '');
|
||||
@@ -226,11 +226,11 @@ trait UserControllerReportOperations {
|
||||
);
|
||||
|
||||
if ($updated === false) {
|
||||
return $this->error(__('处理失败', 'aksrt'));
|
||||
return $this->error(__('处理失败', 'jio'));
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'message' => __('举报工单已处理', 'aksrt'),
|
||||
'message' => __('举报工单已处理', 'jio'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ trait UserControllerReportOperations {
|
||||
if ($user instanceof \WP_User) {
|
||||
$summary['display_name'] = $user->display_name;
|
||||
$summary['title'] = $user->display_name;
|
||||
$summary['url'] = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($target_id) : '';
|
||||
$summary['url'] = function_exists('jio_get_user_home_url') ? jio_get_user_home_url($target_id) : '';
|
||||
$summary['ban_state'] = $this->get_user_restriction_state($target_id);
|
||||
}
|
||||
}
|
||||
@@ -340,64 +340,64 @@ trait UserControllerReportOperations {
|
||||
* 用户当前限制状态摘要(banned|restricted|muted|空),与 Restriction 表实时同步。
|
||||
*/
|
||||
private function get_user_restriction_state(int $user_id): string {
|
||||
if ($user_id <= 0 || !class_exists('\AKSRT\User\Restriction')) {
|
||||
if ($user_id <= 0 || !class_exists('\Jio\User\Restriction')) {
|
||||
return '';
|
||||
}
|
||||
$svc = \AKSRT\User\Restriction::get_instance();
|
||||
$svc = \Jio\User\Restriction::get_instance();
|
||||
if ($svc->is_banned($user_id)) {
|
||||
return 'banned';
|
||||
}
|
||||
if ($svc->is_restricted($user_id, \AKSRT\User\Restriction::TYPE_SUSPEND)) {
|
||||
if ($svc->is_restricted($user_id, \Jio\User\Restriction::TYPE_SUSPEND)) {
|
||||
return 'restricted';
|
||||
}
|
||||
if ($svc->is_restricted($user_id, \AKSRT\User\Restriction::TYPE_MUTE)) {
|
||||
if ($svc->is_restricted($user_id, \Jio\User\Restriction::TYPE_MUTE)) {
|
||||
return 'muted';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private function apply_report_action(string $target_type, int $target_id, string $action_taken, string $reason = ''): string {
|
||||
if (!class_exists('\AKSRT\Messages\ReportCenter')) {
|
||||
if (!class_exists('\Jio\Messages\ReportCenter')) {
|
||||
return '';
|
||||
}
|
||||
return \AKSRT\Messages\ReportCenter::get_instance()->apply_action($target_type, $target_id, $action_taken, $reason);
|
||||
return \Jio\Messages\ReportCenter::get_instance()->apply_action($target_type, $target_id, $action_taken, $reason);
|
||||
}
|
||||
|
||||
private function get_report_status_options(): array {
|
||||
return [
|
||||
['label' => __('待处理', 'aksrt'), 'value' => 'pending'],
|
||||
['label' => __('处理中', 'aksrt'), 'value' => 'reviewing'],
|
||||
['label' => __('已解决', 'aksrt'), 'value' => 'resolved'],
|
||||
['label' => __('已驳回', 'aksrt'), 'value' => 'dismissed'],
|
||||
['label' => __('待处理', 'jio'), 'value' => 'pending'],
|
||||
['label' => __('处理中', 'jio'), 'value' => 'reviewing'],
|
||||
['label' => __('已解决', 'jio'), 'value' => 'resolved'],
|
||||
['label' => __('已驳回', 'jio'), 'value' => 'dismissed'],
|
||||
];
|
||||
}
|
||||
|
||||
private function get_report_target_type_options(): array {
|
||||
return [
|
||||
['label' => __('文章', 'aksrt'), 'value' => 'post'],
|
||||
['label' => __('评论', 'aksrt'), 'value' => 'comment'],
|
||||
['label' => __('用户', 'aksrt'), 'value' => 'user'],
|
||||
['label' => __('文章', 'jio'), 'value' => 'post'],
|
||||
['label' => __('评论', 'jio'), 'value' => 'comment'],
|
||||
['label' => __('用户', 'jio'), 'value' => 'user'],
|
||||
];
|
||||
}
|
||||
|
||||
private function get_report_action_options(string $target_type = ''): array {
|
||||
$common = [
|
||||
['label' => __('仅更新状态', 'aksrt'), 'value' => 'none'],
|
||||
['label' => __('升级人工处理', 'aksrt'), 'value' => 'escalated'],
|
||||
['label' => __('仅更新状态', 'jio'), 'value' => 'none'],
|
||||
['label' => __('升级人工处理', 'jio'), 'value' => 'escalated'],
|
||||
];
|
||||
|
||||
if ($target_type === 'user' || $target_type === '') {
|
||||
$common = array_merge($common, [
|
||||
['label' => __('发送警告', 'aksrt'), 'value' => 'warn'],
|
||||
['label' => __('禁言', 'aksrt'), 'value' => 'mute'],
|
||||
['label' => __('停权', 'aksrt'), 'value' => 'suspend'],
|
||||
['label' => __('封禁账号', 'aksrt'), 'value' => 'ban'],
|
||||
['label' => __('解除限制', 'aksrt'), 'value' => 'clear'],
|
||||
['label' => __('发送警告', 'jio'), 'value' => 'warn'],
|
||||
['label' => __('禁言', 'jio'), 'value' => 'mute'],
|
||||
['label' => __('停权', 'jio'), 'value' => 'suspend'],
|
||||
['label' => __('封禁账号', 'jio'), 'value' => 'ban'],
|
||||
['label' => __('解除限制', 'jio'), 'value' => 'clear'],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($target_type === 'post' || $target_type === 'comment' || $target_type === '') {
|
||||
$common[] = ['label' => __('转为待审内容', 'aksrt'), 'value' => 'content_removed'];
|
||||
$common[] = ['label' => __('转为待审内容', 'jio'), 'value' => 'content_removed'];
|
||||
}
|
||||
|
||||
return $common;
|
||||
@@ -407,7 +407,7 @@ trait UserControllerReportOperations {
|
||||
* POST /report/submit — 前台举报提交(登录用户)
|
||||
*/
|
||||
public function submit_report(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Messages\ReportCenter')) {
|
||||
if (!class_exists('\Jio\Messages\ReportCenter')) {
|
||||
return $this->error('举报系统未启用', 500);
|
||||
}
|
||||
|
||||
@@ -421,13 +421,13 @@ trait UserControllerReportOperations {
|
||||
}
|
||||
|
||||
$other_category = '其他原因';
|
||||
$categories = array_column(\AKSRT\Settings\UserRuntime::ban_settings()['reasons'], 'name');
|
||||
$categories = array_column(\Jio\Settings\UserRuntime::ban_settings()['reasons'], 'name');
|
||||
$categories[] = $other_category;
|
||||
if (!in_array($category, $categories, true)) {
|
||||
return $this->error(__('举报原因无效或已更新,请刷新页面后重新选择', 'aksrt'));
|
||||
return $this->error(__('举报原因无效或已更新,请刷新页面后重新选择', 'jio'));
|
||||
}
|
||||
if ($category === $other_category && $reason === '') {
|
||||
return $this->error(__('请填写举报原因的具体说明', 'aksrt'));
|
||||
return $this->error(__('请填写举报原因的具体说明', 'jio'));
|
||||
}
|
||||
|
||||
$valid_types = ['post', 'comment', 'user'];
|
||||
@@ -437,23 +437,23 @@ trait UserControllerReportOperations {
|
||||
|
||||
$reporter_id = get_current_user_id();
|
||||
if ($reporter_id <= 0) {
|
||||
return $this->error(__('请先登录', 'aksrt'), 401);
|
||||
return $this->error(__('请先登录', 'jio'), 401);
|
||||
}
|
||||
|
||||
// 同一人对同一目标的待处理工单只允许存在一条
|
||||
if (\AKSRT\Messages\ReportCenter::get_instance()->has_pending_report($reporter_id, $target_type, $target_id)) {
|
||||
return $this->error(__('你已提交过相同的举报,请等待处理结果', 'aksrt'), 409);
|
||||
if (\Jio\Messages\ReportCenter::get_instance()->has_pending_report($reporter_id, $target_type, $target_id)) {
|
||||
return $this->error(__('你已提交过相同的举报,请等待处理结果', 'jio'), 409);
|
||||
}
|
||||
if ($target_type === 'user') {
|
||||
if ($target_id === $reporter_id) {
|
||||
return $this->error(__('不能举报自己', 'aksrt'), 400);
|
||||
return $this->error(__('不能举报自己', 'jio'), 400);
|
||||
}
|
||||
if (!function_exists('aksrt_user_can') || !aksrt_user_can('user_report', $reporter_id, $target_id)) {
|
||||
return $this->error(__('当前账号没有举报用户的权限', 'aksrt'), 403);
|
||||
if (!function_exists('jio_user_can') || !jio_user_can('user_report', $reporter_id, $target_id)) {
|
||||
return $this->error(__('当前账号没有举报用户的权限', 'jio'), 403);
|
||||
}
|
||||
}
|
||||
|
||||
$report_id = \AKSRT\Messages\ReportCenter::get_instance()->submit(
|
||||
$report_id = \Jio\Messages\ReportCenter::get_instance()->submit(
|
||||
$reporter_id,
|
||||
$target_type,
|
||||
$target_id,
|
||||
@@ -463,13 +463,13 @@ trait UserControllerReportOperations {
|
||||
|
||||
if ($report_id > 0) {
|
||||
// 举证图片以 URL 拼入举报内容,提交成功即认领。
|
||||
\AKSRT\Attachment\PendingUploads::claim_text($reason, $reporter_id);
|
||||
\Jio\Attachment\PendingUploads::claim_text($reason, $reporter_id);
|
||||
return $this->success([
|
||||
'message' => __('举报已提交,我们会尽快处理', 'aksrt'),
|
||||
'message' => __('举报已提交,我们会尽快处理', 'jio'),
|
||||
'data' => ['report_id' => $report_id],
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->error(__('举报提交失败', 'aksrt'));
|
||||
return $this->error(__('举报提交失败', 'jio'));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
namespace Jio\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -16,11 +16,11 @@ trait UserControllerRestrictionOperations {
|
||||
return $this->error('用户不存在', 404);
|
||||
}
|
||||
|
||||
if (!class_exists('\AKSRT\User\Restriction')) {
|
||||
if (!class_exists('\Jio\User\Restriction')) {
|
||||
return $this->success(['data' => ['restrictions' => []]]);
|
||||
}
|
||||
|
||||
$restrictions = \AKSRT\User\Restriction::get_instance()->get_user_restrictions($user_id, false);
|
||||
$restrictions = \Jio\User\Restriction::get_instance()->get_user_restrictions($user_id, false);
|
||||
return $this->success(['data' => ['restrictions' => $restrictions]]);
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ trait UserControllerRestrictionOperations {
|
||||
return $this->error('请选择限制类型');
|
||||
}
|
||||
|
||||
if (!class_exists('\AKSRT\User\Restriction')) {
|
||||
if (!class_exists('\Jio\User\Restriction')) {
|
||||
return $this->error('限制系统未启用', 501);
|
||||
}
|
||||
|
||||
$result = \AKSRT\User\Restriction::get_instance()->restrict(
|
||||
$result = \Jio\User\Restriction::get_instance()->restrict(
|
||||
$user_id, $type, $reason, $scope, $expires_at, get_current_user_id()
|
||||
);
|
||||
|
||||
@@ -59,11 +59,11 @@ trait UserControllerRestrictionOperations {
|
||||
public function remove_user_restriction(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$restriction_id = (int) $request->get_param('id');
|
||||
|
||||
if (!class_exists('\AKSRT\User\Restriction')) {
|
||||
if (!class_exists('\Jio\User\Restriction')) {
|
||||
return $this->error('限制系统未启用', 501);
|
||||
}
|
||||
|
||||
$result = \AKSRT\User\Restriction::get_instance()->remove_restriction($restriction_id);
|
||||
$result = \Jio\User\Restriction::get_instance()->remove_restriction($restriction_id);
|
||||
if (!$result) {
|
||||
return $this->error('移除失败', 500);
|
||||
}
|
||||
@@ -72,27 +72,27 @@ trait UserControllerRestrictionOperations {
|
||||
}
|
||||
|
||||
public function get_restriction_stats(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\User\Restriction')) {
|
||||
if (!class_exists('\Jio\User\Restriction')) {
|
||||
return $this->success(['data' => ['total' => 0, 'active' => 0, 'banned' => 0]]);
|
||||
}
|
||||
|
||||
$stats = \AKSRT\User\Restriction::get_instance()->get_stats();
|
||||
$stats = \Jio\User\Restriction::get_instance()->get_stats();
|
||||
return $this->success(['data' => $stats]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 前台禁封弹窗内容(1:1 复刻参考主题 zib_get_set_user_ban_modal,
|
||||
* 经 aksrt_get_user_ban_modal 渲染;参考主题 refresh_modal 每次刷新同语义)。
|
||||
* 经 jio_get_user_ban_modal 渲染;参考主题 refresh_modal 每次刷新同语义)。
|
||||
*/
|
||||
public function get_user_ban_modal(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = (int) $request->get_param('user_id');
|
||||
if ($user_id <= 0 || !get_userdata($user_id)) {
|
||||
return $this->error('用户不存在', 404);
|
||||
}
|
||||
if (!function_exists('aksrt_get_user_ban_modal')) {
|
||||
if (!function_exists('jio_get_user_ban_modal')) {
|
||||
return $this->error('禁封组件未加载', 501);
|
||||
}
|
||||
$html = aksrt_get_user_ban_modal($user_id);
|
||||
$html = jio_get_user_ban_modal($user_id);
|
||||
if ($html === '') {
|
||||
return $this->error('抱歉,您暂时没有此权限', 403);
|
||||
}
|
||||
@@ -101,17 +101,17 @@ trait UserControllerRestrictionOperations {
|
||||
|
||||
/**
|
||||
* 前台查看禁封状态弹窗内容(1:1 复刻参考主题 zib_get_ban_info_modal,
|
||||
* 经 aksrt_get_user_ban_info_modal 渲染;本人或管理员可见)。
|
||||
* 经 jio_get_user_ban_info_modal 渲染;本人或管理员可见)。
|
||||
*/
|
||||
public function get_user_ban_info_modal(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = (int) $request->get_param('user_id');
|
||||
if ($user_id <= 0 || !get_userdata($user_id)) {
|
||||
return $this->error('用户不存在', 404);
|
||||
}
|
||||
if (!function_exists('aksrt_get_user_ban_info_modal')) {
|
||||
if (!function_exists('jio_get_user_ban_info_modal')) {
|
||||
return $this->error('禁封组件未加载', 501);
|
||||
}
|
||||
$html = aksrt_get_user_ban_info_modal($user_id);
|
||||
$html = jio_get_user_ban_info_modal($user_id);
|
||||
if ($html === '') {
|
||||
return $this->error('抱歉,您暂时没有此权限', 403);
|
||||
}
|
||||
@@ -120,17 +120,17 @@ trait UserControllerRestrictionOperations {
|
||||
|
||||
/**
|
||||
* 前台禁封申诉弹窗内容(1:1 复刻参考主题 zib_get_user_ban_appeal_modal,
|
||||
* 经 aksrt_get_user_ban_appeal_modal 渲染;仅本人可见)。
|
||||
* 经 jio_get_user_ban_appeal_modal 渲染;仅本人可见)。
|
||||
*/
|
||||
public function get_user_ban_appeal_modal(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = (int) $request->get_param('user_id');
|
||||
if ($user_id <= 0 || !get_userdata($user_id)) {
|
||||
return $this->error('用户不存在', 404);
|
||||
}
|
||||
if (!function_exists('aksrt_get_user_ban_appeal_modal')) {
|
||||
if (!function_exists('jio_get_user_ban_appeal_modal')) {
|
||||
return $this->error('申诉组件未加载', 501);
|
||||
}
|
||||
$html = aksrt_get_user_ban_appeal_modal($user_id);
|
||||
$html = jio_get_user_ban_appeal_modal($user_id);
|
||||
if ($html === '') {
|
||||
return $this->error('抱歉,您暂时没有此权限', 403);
|
||||
}
|
||||
@@ -139,17 +139,17 @@ trait UserControllerRestrictionOperations {
|
||||
|
||||
/**
|
||||
* 前台禁封记录弹窗内容(1:1 复刻参考主题 zib_get_ban_log_modal,
|
||||
* 经 aksrt_get_user_ban_log_modal 渲染;仅具备禁封权限的管理员可见)。
|
||||
* 经 jio_get_user_ban_log_modal 渲染;仅具备禁封权限的管理员可见)。
|
||||
*/
|
||||
public function get_user_ban_log_modal(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = (int) $request->get_param('user_id');
|
||||
if ($user_id <= 0 || !get_userdata($user_id)) {
|
||||
return $this->error('用户不存在', 404);
|
||||
}
|
||||
if (!function_exists('aksrt_get_user_ban_log_modal')) {
|
||||
if (!function_exists('jio_get_user_ban_log_modal')) {
|
||||
return $this->error('禁封组件未加载', 501);
|
||||
}
|
||||
$html = aksrt_get_user_ban_log_modal($user_id);
|
||||
$html = jio_get_user_ban_log_modal($user_id);
|
||||
if ($html === '') {
|
||||
return $this->error('抱歉,您暂时没有此权限', 403);
|
||||
}
|
||||
@@ -167,34 +167,34 @@ trait UserControllerRestrictionOperations {
|
||||
if ($user_id <= 0 || !get_userdata($user_id)) {
|
||||
return $this->error('用户不存在或参数传入错误', 404);
|
||||
}
|
||||
if (!function_exists('aksrt_current_user_can_ban') || !aksrt_current_user_can_ban($user_id)) {
|
||||
if (!function_exists('jio_current_user_can_ban') || !jio_current_user_can_ban($user_id)) {
|
||||
return $this->error('抱歉,您暂时没有此权限', 403);
|
||||
}
|
||||
if (!class_exists('\AKSRT\User\Restriction')) {
|
||||
if (!class_exists('\Jio\User\Restriction')) {
|
||||
return $this->error('限制系统未启用', 501);
|
||||
}
|
||||
|
||||
$svc = \AKSRT\User\Restriction::get_instance();
|
||||
$svc = \Jio\User\Restriction::get_instance();
|
||||
$ban = (int) ($params['ban'] ?? 0);
|
||||
$desc = isset($params['desc']) ? sanitize_textarea_field((string) wp_unslash($params['desc'])) : '';
|
||||
|
||||
// ── 解封 ──
|
||||
if (0 === $ban) {
|
||||
$svc->lift_user_restriction($user_id);
|
||||
delete_user_meta($user_id, '_aksrt_no_appeal');
|
||||
$this->send_ban_notice($user_id, __('您的账号限制已解除。', 'aksrt'), __('限制解除', 'aksrt'), $desc);
|
||||
return $this->success(['message' => __('已解封此用户', 'aksrt'), 'data' => ['banned' => false]]);
|
||||
delete_user_meta($user_id, '_jio_no_appeal');
|
||||
$this->send_ban_notice($user_id, __('您的账号限制已解除。', 'jio'), __('限制解除', 'jio'), $desc);
|
||||
return $this->success(['message' => __('已解封此用户', 'jio'), 'data' => ['banned' => false]]);
|
||||
}
|
||||
|
||||
// ── 禁封 ──
|
||||
$type = 2 === $ban ? \AKSRT\User\Restriction::TYPE_BAN : \AKSRT\User\Restriction::TYPE_SUSPEND;
|
||||
$type = 2 === $ban ? \Jio\User\Restriction::TYPE_BAN : \Jio\User\Restriction::TYPE_SUSPEND;
|
||||
|
||||
$reason = isset($params['reason']) ? sanitize_text_field((string) wp_unslash($params['reason'])) : '';
|
||||
if ($reason === '' || $reason === 'other') {
|
||||
$reason = isset($params['reason_other']) ? sanitize_text_field((string) wp_unslash($params['reason_other'])) : '';
|
||||
}
|
||||
if ($reason === '') {
|
||||
return $this->error(__('请输入禁封原因', 'aksrt'));
|
||||
return $this->error(__('请输入禁封原因', 'jio'));
|
||||
}
|
||||
|
||||
$time_day = isset($params['time_day']) ? (int) $params['time_day'] : 0;
|
||||
@@ -203,34 +203,34 @@ trait UserControllerRestrictionOperations {
|
||||
$no_appeal = !empty($params['no_appeal']);
|
||||
$no_appeal_desc = isset($params['no_appeal_desc']) ? sanitize_textarea_field((string) wp_unslash($params['no_appeal_desc'])) : '';
|
||||
if ($no_appeal && $no_appeal_desc === '') {
|
||||
return $this->error(__('请输入禁止用户申诉的说明或原因', 'aksrt'));
|
||||
return $this->error(__('请输入禁止用户申诉的说明或原因', 'jio'));
|
||||
}
|
||||
|
||||
// 禁封说明并入原因列(与后台工单禁封表单同存储语义)。
|
||||
if ($desc !== '') {
|
||||
$reason .= "\n" . sprintf(__('禁封说明:%s', 'aksrt'), $desc);
|
||||
$reason .= "\n" . sprintf(__('禁封说明:%s', 'jio'), $desc);
|
||||
}
|
||||
|
||||
$result = $svc->restrict($user_id, $type, $reason, \AKSRT\User\Restriction::SCOPE_GLOBAL, $expires, get_current_user_id());
|
||||
$result = $svc->restrict($user_id, $type, $reason, \Jio\User\Restriction::SCOPE_GLOBAL, $expires, get_current_user_id());
|
||||
if (empty($result['success'])) {
|
||||
return $this->error((string) ($result['message'] ?? __('操作失败', 'aksrt')));
|
||||
return $this->error((string) ($result['message'] ?? __('操作失败', 'jio')));
|
||||
}
|
||||
|
||||
if ($no_appeal) {
|
||||
update_user_meta($user_id, '_aksrt_no_appeal', [
|
||||
update_user_meta($user_id, '_jio_no_appeal', [
|
||||
'desc' => $no_appeal_desc,
|
||||
'at' => current_time('mysql'),
|
||||
]);
|
||||
}
|
||||
|
||||
// 站内信(后台禁封表单 / 参考主题 zib_updata_user_ban_msg 同语义)。
|
||||
$notice = $type === \AKSRT\User\Restriction::TYPE_BAN
|
||||
? __('您的账号已被禁封,将无法登录。', 'aksrt')
|
||||
: __('您已被拉入小黑屋,发布等相关权限已被限制。', 'aksrt');
|
||||
$notice = $type === \Jio\User\Restriction::TYPE_BAN
|
||||
? __('您的账号已被禁封,将无法登录。', 'jio')
|
||||
: __('您已被拉入小黑屋,发布等相关权限已被限制。', 'jio');
|
||||
if ($time_day > 0) {
|
||||
$notice .= ' ' . sprintf(__('期限:%1$d 天,%2$s 起自动解封。', 'aksrt'), $time_day, $expires);
|
||||
$notice .= ' ' . sprintf(__('期限:%1$d 天,%2$s 起自动解封。', 'jio'), $time_day, $expires);
|
||||
}
|
||||
$this->send_ban_notice($user_id, $notice, __('账号限制', 'aksrt'), $desc);
|
||||
$this->send_ban_notice($user_id, $notice, __('账号限制', 'jio'), $desc);
|
||||
|
||||
// 删除全部内容:全部公开内容与已审核评论移至回收站(参考主题同行为)。
|
||||
if (!empty($params['delete_content'])) {
|
||||
@@ -249,7 +249,7 @@ trait UserControllerRestrictionOperations {
|
||||
}
|
||||
}
|
||||
|
||||
$send_msg = 2 === $ban ? __('已禁封此用户', 'aksrt') : __('已将此用户拉入小黑屋', 'aksrt');
|
||||
$send_msg = 2 === $ban ? __('已禁封此用户', 'jio') : __('已将此用户拉入小黑屋', 'jio');
|
||||
return $this->success(['message' => $send_msg, 'data' => ['banned' => true]]);
|
||||
}
|
||||
|
||||
@@ -258,13 +258,13 @@ trait UserControllerRestrictionOperations {
|
||||
*/
|
||||
private function send_ban_notice(int $user_id, string $notice, string $title, string $desc = ''): void
|
||||
{
|
||||
if (!class_exists('\AKSRT\Messages\Message')) {
|
||||
if (!class_exists('\Jio\Messages\Message')) {
|
||||
return;
|
||||
}
|
||||
$msg = $notice;
|
||||
if ($desc !== '') {
|
||||
$msg .= ' ' . sprintf(__('留言:%s', 'aksrt'), $desc);
|
||||
$msg .= ' ' . sprintf(__('留言:%s', 'jio'), $desc);
|
||||
}
|
||||
\AKSRT\Messages\Message::get_instance()->send($user_id, 'system', $msg, ['title' => $title]);
|
||||
\Jio\Messages\Message::get_instance()->send($user_id, 'system', $msg, ['title' => $title]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user