feat(users): govern profiles and access

Normalize fixed profile fields, profile actions, and the five-column capability matrix for comments, uploads, and downloads.
This commit is contained in:
2026-08-13 00:24:54 +08:00
parent 0b06b2c7df
commit 8241570ca2
67 changed files with 780 additions and 1127 deletions
+12 -9
View File
@@ -91,6 +91,15 @@ class Download {
}
public function check_rest_nonce(\WP_REST_Request $request) {
// The capability matrix can explicitly allow resource downloads for
// everyone. A WordPress REST nonce only exists for authenticated
// sessions, so requiring one from visitors would make the "all"
// column ineffective. Anonymous requests still pass the unified
// download gate (capability, payment and captcha) in rest_request().
if (get_current_user_id() <= 0) {
return true;
}
$nonce = $request->get_header('x-wp-nonce');
if (!$nonce) {
return new \WP_Error('aksrt_rest_no_nonce', __('缺少安全令牌', 'aksrt'), ['status' => 403]);
@@ -109,7 +118,6 @@ class Download {
}
$meta = function_exists('aksrt_get_download_box_meta') ? aksrt_get_download_box_meta($post) : [];
$paid_settings = \AKSRT\Settings\Settings::get_paid_content_settings();
$cost = aksrt_resource_download_cost($post);
$uid = get_current_user_id();
$priced = function_exists('aksrt_get_user_pay_price') ? aksrt_get_user_pay_price($post, $uid) : null;
@@ -122,10 +130,7 @@ class Download {
? Visibility::get_post_access_state($post, $uid)
: [];
$need_captcha = !empty($paid_settings['download_captcha']) || !empty($meta['require_captcha']);
if ($need_captcha && function_exists('aksrt_is_captcha_enabled_for') && !aksrt_is_captcha_enabled_for('download')) {
$need_captcha = false;
}
$need_captcha = function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('download');
return new \WP_REST_Response([
'post_id' => $post_id,
@@ -134,7 +139,7 @@ class Download {
'pay_modo' => $pay_modo,
'pay_limit' => (int) ($meta['pay_limit'] ?? 0),
'cost' => $cost,
'require_login' => !empty($paid_settings['download_login']) || !empty($meta['require_login']) || (!empty($paid_settings['free_logged_show']) && $cost <= 0 && $display_price <= 0),
'require_login' => !empty($access['need_login']),
'require_captcha' => $need_captcha,
'logged_in' => $uid > 0,
'balance' => $uid > 0 && class_exists('\AKSRT\Credits\Credits') ? \AKSRT\Credits\Credits::balance($uid) : 0,
@@ -181,7 +186,6 @@ class Download {
$gate = aksrt_can_download($post);
if (is_wp_error($gate)) return $gate;
$paid_settings = \AKSRT\Settings\Settings::get_paid_content_settings();
$uid = get_current_user_id();
$cost = aksrt_resource_download_cost($post);
@@ -193,8 +197,7 @@ class Download {
}
}
$need_captcha = !empty($paid_settings['download_captcha']) || !empty($meta['require_captcha']);
// 下载双开关:cms_features.download_captcha(或单资源强制)且场景未停用。
$need_captcha = function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('download');
if ($need_captcha && function_exists('aksrt_require_captcha')) {
$cap_payload = isset($params['captcha']) && is_array($params['captcha']) ? $params['captcha'] : [];
// 场景未启用时 require 直接放行;启用时强制校验 token。
+14 -28
View File
@@ -47,14 +47,6 @@ class Visibility {
: (($pay_modo === 'points')
? (float) ($pay_meta['points_price'] ?? 0)
: (float) ($pay_meta['price'] ?? 0));
$paid_settings = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_paid_content_settings()
: [
'download_login' => true,
'download_credit' => true,
'download_captcha' => false,
];
$state = [
'post_id' => $resource_post instanceof \WP_Post ? (int) $resource_post->ID : 0,
'pay_type' => $pay_type,
@@ -134,12 +126,13 @@ class Visibility {
if ($allowed_member && $price <= 0) {
$download_cost = 0;
}
$can_download = function_exists('aksrt_user_can') && aksrt_user_can('resource_download', $uid, (int) $resource_post->ID);
$state['download_cost'] = max(0, $download_cost);
$state['need_login'] = $uid <= 0;
$state['need_login'] = !$can_download && $uid <= 0;
$state['need_purchase'] = $allowed_member && $price > 0 && !$has_paid_order;
$state['has_paid_access'] = $allowed_member && ($has_paid_order || $price <= 0);
$state['can_view'] = true;
$state['can_download'] = $allowed_member && ($uid > 0) && ($has_paid_order || $price <= 0);
$state['can_download'] = $can_download && $allowed_member && ($has_paid_order || $price <= 0);
if (!$allowed_member) {
$state['can_download'] = false;
$state['has_paid_access'] = false;
@@ -172,16 +165,14 @@ class Visibility {
}
$state['download_cost'] = max(0, $download_cost);
$free_logged_show = !empty($paid_settings['free_logged_show']);
$state['need_login'] = !empty($paid_settings['download_login'])
|| !empty($pay_meta['require_login'])
|| ($free_logged_show && $price <= 0);
$state['require_captcha'] = !empty($paid_settings['download_captcha']) || !empty($pay_meta['require_captcha']);
$can_download = function_exists('aksrt_user_can') && aksrt_user_can('resource_download', $uid, (int) $resource_post->ID);
$state['need_login'] = !$can_download && $uid <= 0;
$state['require_captcha'] = function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('download');
$state['need_purchase'] = !$has_paid_order && $price > 0;
$state['has_paid_access'] = $has_paid_order || $price <= 0 || $has_free_access;
$state['can_view'] = true;
$state['can_download'] = (!$state['need_login'] || $uid > 0) && ($has_paid_order || $price <= 0);
$state['can_download'] = $can_download && ($has_paid_order || $price <= 0);
return $state;
}
@@ -227,6 +218,13 @@ class Visibility {
$state = self::get_post_access_state($resource_post, $user_id);
$uid = $user_id > 0 ? $user_id : get_current_user_id();
if (!function_exists('aksrt_user_can') || !aksrt_user_can('resource_download', $uid, (int) $resource_post->ID)) {
return new \WP_Error('aksrt_download_forbidden', __('当前账号没有下载资源的权限。', 'aksrt'), [
'status' => $uid > 0 ? 403 : 401,
'login_url' => wp_login_url(get_permalink($resource_post)),
]);
}
if (($state['pay_type'] ?? '') !== \AKSRT\Core\DomainModel::PAY_TYPE_DOWNLOAD) {
return new \WP_Error('aksrt_not_downloadable', __('该内容未启用下载能力。', 'aksrt'));
}
@@ -238,19 +236,7 @@ class Visibility {
]);
}
// Free download + pay_free_logged_show (belt-and-suspenders with state.need_login).
$cost = (int) ($state['download_cost'] ?? 0);
$paid_settings = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_paid_content_settings()
: ['download_credit' => true, 'free_logged_show' => false];
if ($cost <= 0 && !empty($paid_settings['free_logged_show']) && $uid <= 0) {
return new \WP_Error('aksrt_need_login', __('免费资源,请登录后下载。', 'aksrt'), [
'status' => 401,
'login_url' => wp_login_url(get_permalink($resource_post)),
]);
}
// Member-only (pay_limit).
if (!empty($state['need_vip']) && empty($state['has_paid_access']) && empty($state['can_download'])) {
$need_level = (int) ($state['pay_limit'] ?? 1);