From e47ee1b029e01cac9eb61ff2de4f739921809dcb Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 28 Jul 2026 12:22:52 +0800 Subject: [PATCH] feat(user-center): deepen zibll parity and profile media --- assets/js/author.js | 60 +++++++++ inc/bootstrap.php | 1 + inc/helpers/aksrt-avatar.php | 127 ++++++++++++++++++ inc/helpers/aksrt-user-center.php | 56 +++++++- .../user-center-renderers/profile-header.php | 11 +- .../user-center-renderers/profile-sidebar.php | 1 + .../user-center-renderers/sidebar-creator.php | 47 +++++++ .../user-center-renderers/sidebar-nav.php | 2 + .../user-center-renderers/sidebar-stats.php | 2 + inc/modules/user-parts/template-registry.php | 41 ++++-- inc/modules/user-parts/urls.php | 30 ++--- src/AKSRT/Admin/UserController.php | 40 +----- .../UserControllerProfileMediaOperations.php | 112 +++++++++++++++ templates/forum/forum-compose.php | 6 + templates/pages/page-product-edit.php | 6 + uninstall.php | 2 + 16 files changed, 479 insertions(+), 65 deletions(-) create mode 100644 inc/helpers/aksrt-avatar.php create mode 100644 inc/helpers/user-center-renderers/sidebar-creator.php create mode 100644 src/AKSRT/Admin/UserControllerProfileMediaOperations.php diff --git a/assets/js/author.js b/assets/js/author.js index 999a190b..f42a30de 100644 --- a/assets/js/author.js +++ b/assets/js/author.js @@ -199,9 +199,69 @@ }); } + function initAvatarUpload() { + var upload = (window.aksrtUserCenter || {}).avatarUpload; + if (!upload || !upload.enabled || !upload.restUrl) return; + + var button = document.getElementById(upload.buttonId || 'aksrt-upload-avatar-btn'); + var input = document.getElementById(upload.inputId || 'aksrt-avatar-file-input'); + var image = document.getElementById(upload.imageId || 'aksrt-user-avatar-image'); + if (!button || !input || button.dataset.aksrtAvatarBound === '1') return; + button.dataset.aksrtAvatarBound = '1'; + + var label = button.querySelector('span'); + var idleText = upload.idleText || (label ? label.textContent : '更换头像'); + + function setBusy(busy) { + button.disabled = !!busy; + if (label) label.textContent = busy ? (upload.loadingText || '上传中…') : idleText; + } + + button.addEventListener('click', function () { input.click(); }); + input.addEventListener('change', function () { + var file = input.files && input.files[0]; + if (!file) return; + if (upload.maxSize && file.size > upload.maxSize) { + notify(upload.tooLargeText || '图片大小不能超过 2MB', 'warning'); + input.value = ''; + return; + } + + var body = new FormData(); + body.append(upload.fieldName || 'avatar', file); + setBusy(true); + fetch(upload.restUrl, { + method: 'POST', + credentials: 'same-origin', + headers: { 'X-WP-Nonce': upload.restNonce || '' }, + body: body + }) + .then(function (response) { + return response.json().then(function (payload) { + if (!response.ok || !payload || !payload.success) throw payload || {}; + return payload; + }); + }) + .then(function (payload) { + var url = payload.url || (payload.data && payload.data.url) || ''; + if (!url) throw payload; + if (image) image.src = url; + notify(payload.message || upload.successText || '头像已更新', 'success'); + }) + .catch(function (error) { + notify((error && error.message) || upload.errorText || '上传失败', 'error'); + }) + .finally(function () { + setBusy(false); + input.value = ''; + }); + }); + } + function init() { initPublicDashboards(); initCoverUpload(); + initAvatarUpload(); } if (document.readyState === 'loading') { diff --git a/inc/bootstrap.php b/inc/bootstrap.php index 9f515c28..10e8ee03 100644 --- a/inc/bootstrap.php +++ b/inc/bootstrap.php @@ -13,6 +13,7 @@ defined('ABSPATH') || exit; // Stage 1: helpers require_once AKSRT_DIR . '/inc/helpers/aksrt-icons.php'; +require_once AKSRT_DIR . '/inc/helpers/aksrt-avatar.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-domain-model.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-template-tags.php'; require_once AKSRT_DIR . '/inc/helpers/aksrt-frontend-tags.php'; diff --git a/inc/helpers/aksrt-avatar.php b/inc/helpers/aksrt-avatar.php new file mode 100644 index 00000000..d2a25b9f --- /dev/null +++ b/inc/helpers/aksrt-avatar.php @@ -0,0 +1,127 @@ +ID; + } + if ($subject instanceof WP_Post) { + return (int) $subject->post_author; + } + if ($subject instanceof WP_Comment) { + return (int) $subject->user_id; + } + if (is_object($subject) && isset($subject->user_id)) { + return (int) $subject->user_id; + } + if (is_numeric($subject)) { + return max(0, (int) $subject); + } + if (is_string($subject) && is_email($subject)) { + $user = get_user_by('email', $subject); + return $user instanceof WP_User ? (int) $user->ID : 0; + } + return 0; + } +} + +if (!function_exists('aksrt_get_custom_avatar_url')) { + /** + * Return the locally uploaded or OAuth avatar URL. + */ + function aksrt_get_custom_avatar_url(int $user_id, int $size = 96): string { + if ($user_id <= 0) { + return ''; + } + + $attachment_id = (int) get_user_meta($user_id, 'aksrt_avatar_attachment_id', true); + if ($attachment_id > 0) { + $image = wp_get_attachment_image_url($attachment_id, [$size, $size]); + if (is_string($image) && $image !== '') { + return $image; + } + } + + $oauth_avatar = esc_url_raw((string) get_user_meta($user_id, 'aksrt_oauth_avatar', true)); + return $oauth_avatar; + } +} + +if (!function_exists('aksrt_get_avatar_url')) { + /** + * Resolve the canonical avatar URL used by all AKSRT frontend surfaces. + * + * @param mixed $subject User subject accepted by get_avatar_url(). + * @param array|int $args WordPress avatar args or a numeric size. + */ + function aksrt_get_avatar_url($subject, $args = []): string { + if (is_numeric($args)) { + $args = ['size' => (int) $args]; + } + $args = is_array($args) ? $args : []; + $size = max(16, min(512, (int) ($args['size'] ?? 96))); + $user_id = aksrt_resolve_avatar_user_id($subject); + $custom = aksrt_get_custom_avatar_url($user_id, $size); + if ($custom !== '') { + return $custom; + } + return (string) get_avatar_url($subject, array_merge($args, ['size' => $size])); + } +} + +if (!function_exists('aksrt_get_avatar_html')) { + /** + * Render a canonical avatar image. + * + * @param mixed $subject User subject. + * @param int $size Pixel size. + * @param array $attrs Additional safe image attributes. + */ + function aksrt_get_avatar_html($subject, int $size = 96, array $attrs = []): string { + $size = max(16, min(512, $size)); + $url = aksrt_get_avatar_url($subject, ['size' => $size]); + if ($url === '') { + return ''; + } + + $defaults = [ + 'class' => 'avatar avatar-' . $size . ' photo', + 'alt' => '', + 'width' => (string) $size, + 'height' => (string) $size, + 'loading' => 'lazy', + ]; + $attrs = array_merge($defaults, $attrs); + $html_attrs = ''; + foreach ($attrs as $name => $value) { + $name = sanitize_key((string) $name); + if ($name === '' || $value === false || $value === null) { + continue; + } + $html_attrs .= ' ' . $name . '="' . esc_attr((string) $value) . '"'; + } + return ''; + } +} + +add_filter('get_avatar_data', static function (array $args, $subject): array { + $user_id = aksrt_resolve_avatar_user_id($subject); + $custom = aksrt_get_custom_avatar_url($user_id, (int) ($args['size'] ?? 96)); + if ($custom !== '') { + $args['url'] = $custom; + $args['found_avatar'] = true; + } + return $args; +}, 20, 2); diff --git a/inc/helpers/aksrt-user-center.php b/inc/helpers/aksrt-user-center.php index 01dc099e..09777192 100644 --- a/inc/helpers/aksrt-user-center.php +++ b/inc/helpers/aksrt-user-center.php @@ -388,6 +388,32 @@ if (!function_exists('aksrt_user_center_runtime_config')) { } } +if (!function_exists('aksrt_user_center_avatar_upload_config')) { + /** + * Avatar upload config for the private user center. + */ + function aksrt_user_center_avatar_upload_config(bool $enabled): array { + if (!$enabled) { + return []; + } + return [ + 'enabled' => true, + 'restUrl' => rest_url('aksrt/v1/user/avatar'), + 'restNonce' => wp_create_nonce('wp_rest'), + 'buttonId' => 'aksrt-upload-avatar-btn', + 'inputId' => 'aksrt-avatar-file-input', + 'imageId' => 'aksrt-user-avatar-image', + 'fieldName' => 'avatar', + 'maxSize' => 2 * MB_IN_BYTES, + 'idleText' => __('更换头像', 'aksrt'), + 'loadingText' => __('上传中…', 'aksrt'), + 'tooLargeText' => __('图片大小不能超过 2MB', 'aksrt'), + 'successText' => __('头像已更新', 'aksrt'), + 'errorText' => __('头像上传失败', 'aksrt'), + ]; + } +} + if (!function_exists('aksrt_user_center_cover_upload_config')) { /** * Cover upload config for the private user-center hero. @@ -450,7 +476,9 @@ if (!function_exists('aksrt_get_user_center_render_context')) { $user_contacts = $dashboard['user_contacts'] ?? get_user_meta($user_id, 'aksrt_user_contacts', true); $user_contacts = is_array($user_contacts) ? $user_contacts : []; $user_background_url = (string) ($dashboard['background_url'] ?? get_user_meta($user_id, 'aksrt_background_image', true)); + $allow_avatar_upload = !empty($user_center_settings['avatar_upload']); $allow_bg_upload = !empty($user_center_settings['cover_upload']) && class_exists('\AKSRT\Settings\Settings') && aksrt_get_setting('allow_background_upload', false); + $messages_enabled = !class_exists('\AKSRT\Settings\Settings') || \AKSRT\Settings\Settings::message_enabled(); $post_status_filter = isset($_GET['post_status']) ? sanitize_key(wp_unslash($_GET['post_status'])) : 'publish'; if (!in_array($post_status_filter, ['all', 'publish', 'draft', 'pending', 'private'], true)) { @@ -472,6 +500,23 @@ if (!function_exists('aksrt_get_user_center_render_context')) { $currency_name = (string) ($dashboard['currency_name'] ?? __('积分', 'aksrt')); $balance_enabled = (bool) ($dashboard['balance_enabled'] ?? false); $wallet_balance = (float) ($dashboard['wallet_balance'] ?? 0.0); + $runtime_config = aksrt_user_center_runtime_config($user_id, $tabs['default'], $active_tab, $tabs['urls']); + $runtime_config['avatarUpload'] = aksrt_user_center_avatar_upload_config($allow_avatar_upload); + $shop_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_shop_settings() : []; + $forum_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_forum_settings() : []; + $revenue_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_revenue_settings() : []; + $shop_enabled = !empty($shop_settings['enabled']); + $forum_enabled = !empty($forum_settings['enabled']); + $new_post_url = function_exists('aksrt_get_new_post_url') ? aksrt_get_new_post_url() : ''; + $new_product_url = $shop_enabled && function_exists('aksrt_get_page_url_by_template') + ? aksrt_get_page_url_by_template('templates/pages/page-product-edit.php') + : ''; + $forum_compose_url = $forum_enabled && function_exists('aksrt_get_page_url_by_template') + ? aksrt_get_page_url_by_template('templates/forum/forum-compose.php') + : ''; + $revenue_url = (!empty($revenue_settings['enabled']) || !empty($affiliate_settings['enabled'])) && function_exists('aksrt_get_revenue_page_url') + ? aksrt_get_revenue_page_url() + : ''; $render_context = [ 'user_center_settings' => $user_center_settings, @@ -490,10 +535,13 @@ if (!function_exists('aksrt_get_user_center_render_context')) { 'profile_signals' => $dashboard['profile_signals'] ?? [], 'user_background_url' => $user_background_url, 'allow_bg_upload' => $allow_bg_upload, - 'show_avatar_edit' => !empty($user_center_settings['avatar_upload']), - 'avatar_settings_url' => admin_url('profile.php'), + 'show_avatar_edit' => $allow_avatar_upload, 'notification_unread_count' => function_exists('aksrt_get_user_unread_notification_count') ? aksrt_get_user_unread_notification_count($user_id) : 0, - 'messages_url' => function_exists('aksrt_get_messages_page_url') ? aksrt_get_messages_page_url() : '', + 'messages_url' => $messages_enabled && function_exists('aksrt_get_messages_page_url') ? aksrt_get_messages_page_url() : '', + 'new_post_url' => $new_post_url, + 'new_product_url' => $new_product_url, + 'forum_compose_url' => $forum_compose_url, + 'revenue_url' => $revenue_url, 'per_page' => 10, 'posts_page' => max(1, isset($_GET['posts_page']) ? (int) $_GET['posts_page'] : 1), 'comments_page' => max(1, isset($_GET['comments_page']) ? (int) $_GET['comments_page'] : 1), @@ -520,7 +568,7 @@ if (!function_exists('aksrt_get_user_center_render_context')) { && \AKSRT\Settings\Settings::invite_enabled() && \AKSRT\Settings\Settings::invite_registration_enabled(), 'user_email' => (string) $current_user->user_email, - 'runtime_config' => aksrt_user_center_runtime_config($user_id, $tabs['default'], $active_tab, $tabs['urls']), + 'runtime_config' => $runtime_config, 'cover_upload_config' => aksrt_user_center_cover_upload_config($allow_bg_upload), ]; diff --git a/inc/helpers/user-center-renderers/profile-header.php b/inc/helpers/user-center-renderers/profile-header.php index de889ae9..f006d1ef 100644 --- a/inc/helpers/user-center-renderers/profile-header.php +++ b/inc/helpers/user-center-renderers/profile-header.php @@ -24,7 +24,7 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_
- display_name); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> + 'aksrt-user-avatar-image', 'alt' => $target_user->display_name, 'loading' => 'eager']); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>

@@ -82,16 +82,19 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_ ?> + 0) : ?> + - - - + + diff --git a/inc/helpers/user-center-renderers/profile-sidebar.php b/inc/helpers/user-center-renderers/profile-sidebar.php index 589bc99f..7a5e96d7 100644 --- a/inc/helpers/user-center-renderers/profile-sidebar.php +++ b/inc/helpers/user-center-renderers/profile-sidebar.php @@ -12,6 +12,7 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_ require __DIR__ . '/sidebar-stats.php'; require __DIR__ . '/sidebar-nav.php'; +require __DIR__ . '/sidebar-creator.php'; require __DIR__ . '/sidebar-commerce.php'; ?>

diff --git a/inc/helpers/user-center-renderers/sidebar-stats.php b/inc/helpers/user-center-renderers/sidebar-stats.php index 43548b18..faff54ae 100644 --- a/inc/helpers/user-center-renderers/sidebar-stats.php +++ b/inc/helpers/user-center-renderers/sidebar-stats.php @@ -24,9 +24,11 @@ defined('ABSPATH') || exit; + + diff --git a/inc/modules/user-parts/template-registry.php b/inc/modules/user-parts/template-registry.php index 53c3a3bc..263f6178 100644 --- a/inc/modules/user-parts/template-registry.php +++ b/inc/modules/user-parts/template-registry.php @@ -125,6 +125,32 @@ $_aksrt_special_templates = [ 'title' => '收益中心', 'fallback' => '/revenue/', ], + 'messages' => [ + 'file' => 'templates/pages/page-messages.php', + 'slug' => 'messages', + 'title' => '消息中心', + 'fallback' => '/messages/', + ], + 'submit' => [ + 'file' => 'templates/pages/page-submit.php', + 'slug' => 'submit', + 'title' => '发布文章', + 'fallback' => '/submit/', + ], + 'product-edit' => [ + 'file' => 'templates/pages/page-product-edit.php', + 'slug' => 'product-edit', + 'title' => '发布商品', + 'fallback' => '/product-edit/', + 'auto_create' => false, + ], + 'forum-compose' => [ + 'file' => 'templates/forum/forum-compose.php', + 'slug' => 'forum-compose', + 'title' => '论坛发帖', + 'fallback' => '/forum-compose/', + 'auto_create' => false, + ], ]; /** @var array 缓存 key=模板文件路径 => value=WP_Post|null */ @@ -486,6 +512,9 @@ function aksrt_ensure_template_page(string $key): void { if (!isset($_aksrt_special_templates[$key])) return; $cfg = $_aksrt_special_templates[$key]; + if (array_key_exists('auto_create', $cfg) && empty($cfg['auto_create'])) { + return; + } $desired_slug = sanitize_title($cfg['slug'] ?? $key); // 已有页面使用此模板?仍校验模板 meta,避免空白默认页。 @@ -502,15 +531,8 @@ function aksrt_ensure_template_page(string $key): void { if ($desired_slug !== '') { $existing_page = get_page_by_path($desired_slug, OBJECT, 'page'); if ($existing_page instanceof WP_Post) { - if ($existing_page->post_status !== 'publish') { - wp_update_post([ - 'ID' => $existing_page->ID, - 'post_status' => 'publish', - ]); - } - - update_post_meta($existing_page->ID, '_wp_page_template', $cfg['file']); - aksrt_clear_template_page_cache(); + // Slug collisions belong to the administrator. System pages are + // created only when the slug is completely free. return; } } @@ -528,6 +550,7 @@ function aksrt_ensure_template_page(string $key): void { if (!is_wp_error($page_id) && $page_id > 0) { update_post_meta($page_id, '_wp_page_template', $cfg['file']); + update_post_meta($page_id, '_aksrt_managed_template_page', (string) $key); aksrt_clear_template_page_cache(); } } diff --git a/inc/modules/user-parts/urls.php b/inc/modules/user-parts/urls.php index e546b95f..bbb546e3 100644 --- a/inc/modules/user-parts/urls.php +++ b/inc/modules/user-parts/urls.php @@ -58,12 +58,9 @@ function aksrt_get_new_post_url(int $edit_id = 0): string { } } - $url = function_exists('aksrt_get_page_url_by_template') - ? aksrt_get_page_url_by_template('templates/pages/page-submit.php') - : ''; - if ($url === '') { - return ''; - } + $url = function_exists('aksrt_get_template_url') + ? aksrt_get_template_url('submit') + : home_url('/submit/'); if ($edit_id > 0) { $url = add_query_arg('edit', $edit_id, $url); } @@ -92,9 +89,9 @@ if (!function_exists('aksrt_get_cart_page_url')) : * 购物车页面 URL(模板 templates/pages/page-cart.php)。 */ function aksrt_get_cart_page_url(): string { - return function_exists('aksrt_get_page_url_by_template') - ? aksrt_get_page_url_by_template('templates/pages/page-cart.php') - : ''; + return function_exists('aksrt_get_template_url') + ? aksrt_get_template_url('cart') + : home_url('/cart/'); } endif; @@ -103,9 +100,9 @@ if (!function_exists('aksrt_get_revenue_page_url')) : * 收益中心页面 URL(模板 templates/pages/page-revenue.php)。 */ function aksrt_get_revenue_page_url(): string { - return function_exists('aksrt_get_page_url_by_template') - ? aksrt_get_page_url_by_template('templates/pages/page-revenue.php') - : ''; + return function_exists('aksrt_get_template_url') + ? aksrt_get_template_url('revenue') + : home_url('/revenue/'); } endif; @@ -114,9 +111,12 @@ if (!function_exists('aksrt_get_messages_page_url')) : * 消息中心页面 URL(模板 templates/pages/page-messages.php)。 */ function aksrt_get_messages_page_url(): string { - return function_exists('aksrt_get_page_url_by_template') - ? aksrt_get_page_url_by_template('templates/pages/page-messages.php') - : ''; + if (class_exists('\AKSRT\Settings\Settings') && !\AKSRT\Settings\Settings::message_enabled()) { + return ''; + } + return function_exists('aksrt_get_template_url') + ? aksrt_get_template_url('messages') + : home_url('/messages/'); } endif; diff --git a/src/AKSRT/Admin/UserController.php b/src/AKSRT/Admin/UserController.php index 25a79e5b..15b602d4 100644 --- a/src/AKSRT/Admin/UserController.php +++ b/src/AKSRT/Admin/UserController.php @@ -14,6 +14,7 @@ class UserController extends ControllerBase { use UserControllerAuthPhoneOperations; use UserControllerRestrictionOperations; use UserControllerFollowOperations; + use UserControllerProfileMediaOperations; private static $instance = null; @@ -44,12 +45,17 @@ class UserController extends ControllerBase { 'permission_callback' => [$this, 'check_permission'], ]); - // User background upload (logged-in users) + // User profile media uploads (logged-in users). register_rest_route(self::API_NAMESPACE, '/user/background', [ 'methods' => 'POST', 'callback' => [$this, 'rest_upload_user_background'], 'permission_callback' => function () { return is_user_logged_in(); }, ]); + register_rest_route(self::API_NAMESPACE, '/user/avatar', [ + 'methods' => 'POST', + 'callback' => [$this, 'rest_upload_user_avatar'], + 'permission_callback' => function () { return is_user_logged_in(); }, + ]); // Comment inline image upload (logged-in users) register_rest_route(self::API_NAMESPACE, '/comment/upload-image', [ @@ -650,38 +656,6 @@ class UserController extends ControllerBase { return $this->success(['message' => __('OAuth 代理配置已保存', 'aksrt')]); } - public function rest_upload_user_background(\WP_REST_Request $request): \WP_REST_Response { - $user_id = get_current_user_id(); - if ($user_id <= 0) { - return $this->error(__('请先登录', 'aksrt'), 401); - } - if (empty($_FILES['background']) && empty($_FILES['file'])) { - return $this->error(__('请选择要上传的图片', 'aksrt'), 400); - } - require_once ABSPATH . 'wp-admin/includes/file.php'; - require_once ABSPATH . 'wp-admin/includes/media.php'; - require_once ABSPATH . 'wp-admin/includes/image.php'; - - $file_key = !empty($_FILES['background']) ? 'background' : 'file'; - $attachment_id = media_handle_upload($file_key, 0); - if (is_wp_error($attachment_id)) { - return $this->error($attachment_id->get_error_message(), 400); - } - $url = wp_get_attachment_url((int) $attachment_id); - if (!$url) { - return $this->error(__('上传失败', 'aksrt'), 500); - } - update_user_meta($user_id, 'aksrt_background_image', esc_url_raw($url)); - update_user_meta($user_id, 'aksrt_background_attachment_id', (int) $attachment_id); - return $this->success([ - 'message' => __('背景图已更新', 'aksrt'), - 'data' => [ - 'url' => $url, - 'attachment_id' => (int) $attachment_id, - ], - ]); - } - public function rest_upload_comment_image(\WP_REST_Request $request): \WP_REST_Response { $user_id = get_current_user_id(); if ($user_id <= 0) { diff --git a/src/AKSRT/Admin/UserControllerProfileMediaOperations.php b/src/AKSRT/Admin/UserControllerProfileMediaOperations.php new file mode 100644 index 00000000..446c2f4a --- /dev/null +++ b/src/AKSRT/Admin/UserControllerProfileMediaOperations.php @@ -0,0 +1,112 @@ +upload_profile_image( + ['avatar', 'file'], + 'aksrt_avatar_attachment_id', + __('头像已更新', 'aksrt'), + true + ); + } + + 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'), + false + ); + } + + /** + * @param string[] $file_keys Accepted multipart field names. + */ + 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); + } + + $center_settings = class_exists('\AKSRT\Settings\Settings') + ? \AKSRT\Settings\Settings::get_user_center_settings() + : ['avatar_upload' => true, 'cover_upload' => true]; + if ($is_avatar && empty($center_settings['avatar_upload'])) { + return $this->error(__('头像上传功能未开启', 'aksrt'), 403); + } + if (!$is_avatar && (empty($center_settings['cover_upload']) || !aksrt_get_setting('allow_background_upload', false))) { + return $this->error(__('背景图上传功能未开启', 'aksrt'), 403); + } + + $file_key = ''; + foreach ($file_keys as $candidate) { + if (!empty($_FILES[$candidate])) { + $file_key = $candidate; + break; + } + } + if ($file_key === '') { + return $this->error(__('请选择要上传的图片', 'aksrt'), 400); + } + + $file = $_FILES[$file_key]; + if (!empty($file['size']) && (int) $file['size'] > 2 * MB_IN_BYTES) { + return $this->error(__('图片大小不能超过 2MB', 'aksrt'), 413); + } + + require_once ABSPATH . 'wp-admin/includes/file.php'; + require_once ABSPATH . 'wp-admin/includes/media.php'; + require_once ABSPATH . 'wp-admin/includes/image.php'; + + $attachment_id = media_handle_upload($file_key, 0, [], [ + 'test_form' => false, + 'mimes' => [ + 'jpg|jpeg|jpe' => 'image/jpeg', + 'png' => 'image/png', + 'gif' => 'image/gif', + 'webp' => 'image/webp', + ], + ]); + if (is_wp_error($attachment_id)) { + return $this->error($attachment_id->get_error_message(), 400); + } + + $attachment_id = (int) $attachment_id; + $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); + } + + $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)); + } + + if ($previous_id > 0 && $previous_id !== $attachment_id) { + $previous = get_post($previous_id); + if ($previous instanceof \WP_Post && (int) $previous->post_author === $user_id) { + wp_delete_attachment($previous_id, true); + } + } + + return $this->success([ + 'message' => $message, + 'url' => $url, + 'data' => [ + 'url' => $url, + 'attachment_id' => $attachment_id, + ], + ]); + } +} diff --git a/templates/forum/forum-compose.php b/templates/forum/forum-compose.php index 7a09320b..edbdf2a1 100644 --- a/templates/forum/forum-compose.php +++ b/templates/forum/forum-compose.php @@ -18,6 +18,12 @@ if (!is_user_logged_in()) { exit; } +$forum_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_forum_settings() : []; +if (empty($forum_settings['enabled'])) { + wp_safe_redirect(home_url('/')); + exit; +} + $forum = class_exists('\AKSRT\Forum\Forum') ? \AKSRT\Forum\Forum::get_instance() : null; $current_user_id = get_current_user_id(); $board_id = isset($_GET['board']) ? (int) $_GET['board'] : 0; diff --git a/templates/pages/page-product-edit.php b/templates/pages/page-product-edit.php index c4897d9a..174ce9c0 100644 --- a/templates/pages/page-product-edit.php +++ b/templates/pages/page-product-edit.php @@ -16,6 +16,12 @@ if (!is_user_logged_in()) { exit; } +$shop_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_shop_settings() : []; +if (empty($shop_settings['enabled'])) { + wp_safe_redirect(home_url('/')); + exit; +} + $product_id = (int) ($_GET['product_id'] ?? 0); $edit_view = function_exists('aksrt_get_product_edit_page_view') ? aksrt_get_product_edit_page_view(get_current_user_id(), $product_id) diff --git a/uninstall.php b/uninstall.php index af787cc3..79186837 100644 --- a/uninstall.php +++ b/uninstall.php @@ -57,6 +57,8 @@ if ($options) { // ── Delete AKSRT user meta ── $meta_keys = [ 'aksrt_user_contacts', + 'aksrt_avatar_attachment_id', + 'aksrt_background_attachment_id', 'aksrt_background_image', 'aksrt_email_verification_status', 'aksrt_email_verified_at',