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
+112
View File
@@ -36,12 +36,124 @@ class FrontSubmit {
// Guest path (when enabled).
add_action('wp_ajax_nopriv_aksrt_submit_post', [$this, 'ajax_submit_post']);
add_action('rest_api_init', [$this, 'register_rest_routes']);
add_filter('body_class', [$this, 'body_class']);
add_filter('wp_robots', [$this, 'robots_noindex']);
add_filter('aksrt_floating_buttons_enabled', [$this, 'disable_chrome_on_submit']);
add_filter('aksrt_footer_tabbar_enabled', [$this, 'disable_chrome_on_submit']);
}
public function register_rest_routes(): void {
register_rest_route('aksrt/v1', '/front-submit/upload/(?P<kind>image|resource)', [
'methods' => 'POST',
'callback' => [$this, 'rest_upload_attachment'],
'permission_callback' => [$this, 'can_upload_attachment'],
'args' => [
'kind' => [
'validate_callback' => static function ($value): bool {
return in_array((string) $value, ['image', 'resource'], true);
},
],
],
]);
}
public function can_upload_attachment(\WP_REST_Request $request): bool {
$user_id = get_current_user_id();
if ($user_id <= 0) {
return false;
}
if (!$this->can_submit($user_id)) {
return false;
}
$kind = (string) $request->get_param('kind');
$cap = $kind === 'resource' ? 'upload_resource_attachment' : 'upload_submit_image';
return $this->user_can_cap($cap, $user_id);
}
public function rest_upload_attachment(\WP_REST_Request $request) {
$kind = (string) $request->get_param('kind');
$user_id = get_current_user_id();
$files = $request->get_file_params();
$file = $files['file'] ?? null;
if (!is_array($file) || empty($file['tmp_name']) || !empty($file['error'])) {
return new \WP_Error('aksrt_upload_missing_file', __('请选择要上传的文件。', 'aksrt'), ['status' => 400]);
}
$settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
$max_mb = $kind === 'resource'
? max(1, min(200, (int) ($settings['resource_max_size'] ?? 50)))
: max(1, min(20, (int) ($settings['submit_image_max_size'] ?? 5)));
$max_size = $max_mb * MB_IN_BYTES;
$size = (int) ($file['size'] ?? 0);
if ($size <= 0 || $size > $max_size) {
return new \WP_Error(
'aksrt_upload_too_large',
sprintf(__('文件大小不能超过 %dMB。', 'aksrt'), $max_mb),
['status' => 400]
);
}
$filename = sanitize_file_name((string) ($file['name'] ?? ''));
$checked = wp_check_filetype_and_ext((string) $file['tmp_name'], $filename, get_allowed_mime_types());
$mime = (string) ($checked['type'] ?? '');
if ($filename === '' || $mime === '') {
return new \WP_Error('aksrt_upload_invalid_type', __('不支持的文件类型。', 'aksrt'), ['status' => 400]);
}
if ($kind === 'image') {
$image_info = @getimagesize((string) $file['tmp_name']);
if ($image_info === false || strpos($mime, 'image/') !== 0) {
return new \WP_Error('aksrt_upload_invalid_image', __('请上传有效的图片文件。', 'aksrt'), ['status' => 400]);
}
if (((int) $image_info[0] * (int) $image_info[1]) > 40000000) {
return new \WP_Error('aksrt_upload_image_too_large', __('图片尺寸过大。', 'aksrt'), ['status' => 400]);
}
if (class_exists('\AKSRT\Extensions\ContentAudit')) {
$audit = \AKSRT\Extensions\ContentAudit::get_instance()->audit_image((string) $file['tmp_name']);
if (empty($audit['passed'])) {
return new \WP_Error('aksrt_upload_image_rejected', __('图片未通过内容审核。', 'aksrt'), ['status' => 422]);
}
}
}
if (!function_exists('media_handle_upload')) {
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
}
$_FILES['aksrt_front_submit_file'] = $file;
$attachment_id = media_handle_upload('aksrt_front_submit_file', 0, [], ['test_form' => false]);
unset($_FILES['aksrt_front_submit_file']);
if (is_wp_error($attachment_id)) {
return new \WP_Error('aksrt_upload_failed', $attachment_id->get_error_message(), ['status' => 400]);
}
$attachment_id = (int) $attachment_id;
wp_update_post(['ID' => $attachment_id, 'post_author' => $user_id]);
update_post_meta($attachment_id, '_aksrt_front_submit_upload_type', $kind);
update_post_meta($attachment_id, '_aksrt_front_submit_upload_user_id', $user_id);
$url = wp_get_attachment_url($attachment_id);
if (!$url) {
wp_delete_attachment($attachment_id, true);
return new \WP_Error('aksrt_upload_failed', __('上传后无法读取文件。', 'aksrt'), ['status' => 500]);
}
return new \WP_REST_Response([
'success' => true,
'attachment_id' => $attachment_id,
'url' => $url,
'name' => get_the_title($attachment_id),
'mime' => $mime,
], 201);
}
/**
* @return array<string, mixed>
*/