diff --git a/assets/admin/src/schema/sections/payment/paid.js b/assets/admin/src/schema/sections/payment/paid.js index dddb4c88..0112c630 100644 --- a/assets/admin/src/schema/sections/payment/paid.js +++ b/assets/admin/src/schema/sections/payment/paid.js @@ -15,6 +15,8 @@ export const paidSchema = { pay_show_paycount: true, pay_box_position: 'top', payment_download_enable: false, + payment_down_alone_page: false, + pay_free_logged_show: false, payment_download_remote_s: true, payment_download_verify_nonce: true, payment_download_user_log: 50, @@ -52,6 +54,7 @@ export const paidSchema = { { key: 'pay_show_user', label: '显示购买用户', type: 'switch', default: true }, { key: 'pay_show_paycount', label: '显示购买次数', type: 'switch', default: true }, { key: 'pay_box_position', label: '付费框位置', type: 'radio', default: 'top', options: [{ label: '文章框顶部(封面后)', value: 'box_top' }, { label: '正文顶部', value: 'top' }, { label: '正文底部', value: 'bottom' }, { label: '文章框底部(相关前)', value: 'box_bottom' }] }, + { key: 'pay_free_logged_show', label: '免费资源需登录后下载', type: 'switch', default: false, help: '开启后,价格为 0 的下载资源也要求登录才可下载(对齐 zibll pay_free_logged_show)。' }, ], }, { @@ -60,6 +63,7 @@ export const paidSchema = { icon: 'fas fa-cloud-download-alt', fields: [ { key: 'payment_download_enable', label: '启用下载功能', type: 'switch', default: false }, + { key: 'payment_down_alone_page', label: '独立下载页面', type: 'switch', default: false, help: '开启后,已获权限的下载入口跳转到独立下载页(模板:单篇资源下载),而不是在正文内联显示全部下载按钮。', dependsOn: { key: 'payment_download_enable', value: true } }, { key: 'payment_download_remote_s', label: '远程文件下载', type: 'switch', default: true, dependsOn: { key: 'payment_download_enable', value: true } }, { key: 'payment_download_verify_nonce', label: '下载鉴权', type: 'switch', default: true, dependsOn: { key: 'payment_download_enable', value: true } }, { key: 'payment_download_user_log', label: '用户下载记录条数', type: 'number', default: 50, min: 0, max: 9999, help: '0 为不保存', dependsOn: { key: 'payment_download_enable', value: true } }, diff --git a/inc/classes/admin/schema/class-aksrt-admin-schema-payment.php b/inc/classes/admin/schema/class-aksrt-admin-schema-payment.php index d2c102c0..9b652a40 100644 --- a/inc/classes/admin/schema/class-aksrt-admin-schema-payment.php +++ b/inc/classes/admin/schema/class-aksrt-admin-schema-payment.php @@ -40,9 +40,11 @@ class AKSRT_Admin_Schema_Payment { ['label' => '正文底部', 'value' => 'bottom'], ['label' => '文章框底部', 'value' => 'box_bottom'], ]], + ['key' => 'pay_free_logged_show', 'label' => '免费资源需登录后下载', 'type' => 'switch', 'default' => false], ]], ['key' => 'download', 'title' => '下载能力', 'fields' => [ ['key' => 'payment_download_enable', 'label' => '启用下载能力', 'type' => 'switch', 'default' => false], + ['key' => 'payment_down_alone_page', 'label' => '独立下载页面', 'type' => 'switch', 'default' => false], ['key' => 'payment_download_remote_s', 'label' => '远程文件下载', 'type' => 'switch', 'default' => true], ['key' => 'payment_download_verify_nonce', 'label' => '下载鉴权', 'type' => 'switch', 'default' => true], ['key' => 'payment_download_user_log', 'label' => '用户下载记录条数', 'type' => 'number', 'default' => 50, 'min' => 0, 'max' => 9999], diff --git a/inc/classes/settings/class-aksrt-settings-payment-runtime.php b/inc/classes/settings/class-aksrt-settings-payment-runtime.php index bd805076..bb25cd25 100644 --- a/inc/classes/settings/class-aksrt-settings-payment-runtime.php +++ b/inc/classes/settings/class-aksrt-settings-payment-runtime.php @@ -53,6 +53,8 @@ class AKSRT_Settings_Payment_Runtime { 'catalog_note' => $catalog_note, 'manual_delivery_note' => $manual_note !== '' ? $manual_note : __('付费或人工确认内容请在订单备注中记录交付方式。', 'aksrt'), 'no_logged_in' => !isset($settings['pay_no_logged_in']) || !empty($settings['pay_no_logged_in']), + 'free_logged_show' => !empty($settings['pay_free_logged_show']), + 'down_alone_page' => !empty($settings['payment_down_alone_page']), 'display' => [ 'mark' => sanitize_text_field((string) ($settings['pay_mark'] ?? '付费')), 'currency_symbol' => sanitize_text_field((string) ($settings['payment_currency_symbol'] ?? '¥')), @@ -68,6 +70,7 @@ class AKSRT_Settings_Payment_Runtime { 'post_log' => max(0, min(9999, (int) ($settings['payment_download_post_log'] ?? 50))), 'remote_size' => max(1, (int) ($settings['payment_download_remote_size'] ?? 2048)), 'remote_ext' => sanitize_text_field((string) ($settings['payment_download_remote_ext'] ?? 'zip,rar,7z')), + 'alone_page' => !empty($settings['payment_down_alone_page']), ], ]; } diff --git a/inc/helpers/aksrt-pay.php b/inc/helpers/aksrt-pay.php index 59bb9d02..83da69da 100644 --- a/inc/helpers/aksrt-pay.php +++ b/inc/helpers/aksrt-pay.php @@ -328,6 +328,45 @@ if (!function_exists('aksrt_get_download_cost')) { } } +/** + * URL of the Zibll-style single-post download page (page template + ?post=ID). + */ +if (!function_exists('aksrt_get_single_download_page_url')) { + function aksrt_get_single_download_page_url($post = null): string { + $p = get_post($post); + if (!($p instanceof WP_Post)) { + return ''; + } + + $page_url = ''; + if (function_exists('aksrt_find_template_page')) { + $page = aksrt_find_template_page('templates/pages/page-download-file.php'); + if ($page instanceof WP_Post) { + $page_url = get_permalink($page); + } + } + if ($page_url === '') { + // Fallback: look for any page using the template via slug convention. + $page_url = home_url('/download-file/'); + } + + return add_query_arg('post', (int) $p->ID, $page_url); + } +} + +/** + * Whether independent download page mode is enabled (Zibll down_alone_page). + */ +if (!function_exists('aksrt_is_download_alone_page_enabled')) { + function aksrt_is_download_alone_page_enabled(): bool { + if (!class_exists('AKSRT_Settings')) { + return false; + } + $paid = AKSRT_Settings::get_paid_content_settings(); + return !empty($paid['down_alone_page']) || !empty($paid['download']['alone_page']); + } +} + // ============================================================ // 价格显示 // ============================================================ diff --git a/inc/helpers/aksrt-resource.php b/inc/helpers/aksrt-resource.php index f3d6798c..102ea911 100644 --- a/inc/helpers/aksrt-resource.php +++ b/inc/helpers/aksrt-resource.php @@ -145,28 +145,54 @@ if (!function_exists('aksrt_resource_price_amount')) { if (!function_exists('aksrt_resource_card_badge_text')) { /** - * Resource badge text used in mixed post cards. + * List-card pay badge (Zibll zib_get_posts_list_pay_tags equivalent). * - * Only resource-type posts should render this badge. + * Renders for any paid post type, not only download resources. + * Returns plain text; card template wraps with link + class. * * @param int|WP_Post|null $post * @return string */ function aksrt_resource_card_badge_text($post = null): string { - if (!function_exists('aksrt_is_resource_post') || !aksrt_is_resource_post($post)) { + $p = get_post($post); + if (!($p instanceof WP_Post)) { return ''; } - $meta = aksrt_get_resource_meta($post); - $price = (float) $meta['price']; - - if ($price <= 0) { - // 免费资源不在卡片上显示价格 chip。 + $meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($p) : null; + if (!is_array($meta)) { return ''; } + $pay_type = (string) ($meta['pay_type'] ?? 'no'); + if ($pay_type === '' || $pay_type === 'no') { + return ''; + } + + $price = (float) ($meta['price'] ?? 0); + $type_labels = function_exists('aksrt_get_pay_types') ? aksrt_get_pay_types() : []; + $type_name = $type_labels[$pay_type] ?? __('付费内容', 'aksrt'); + + // VIP-only posts. + if ($pay_type === 'vip') { + return __('会员专属', 'aksrt'); + } + + // Free paid-type (price 0): Zibll shows "免费资源" / free type name. + if ($price <= 0) { + if ($pay_type === 'download') { + return __('免费资源', 'aksrt'); + } + // e.g. 付费阅读 → 免费阅读 + return str_replace(__('付费', 'aksrt'), __('免费', 'aksrt'), (string) $type_name); + } + $currency = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name() : __('积分', 'aksrt'); - return sprintf(__('付费 %s %s', 'aksrt'), aksrt_resource_price_amount($price), $currency); + $amount = function_exists('aksrt_resource_price_amount') + ? aksrt_resource_price_amount($price) + : (string) $price; + + return sprintf('%s %s%s', $type_name, $amount, $currency); } } diff --git a/templates/pages/page-download-file.php b/templates/pages/page-download-file.php new file mode 100644 index 00000000..50a02c44 --- /dev/null +++ b/templates/pages/page-download-file.php @@ -0,0 +1,154 @@ + 0 ? get_post($post_id) : null; + +if (!$post instanceof WP_Post || $post->post_status !== 'publish') { + get_header(); + echo '
'; + echo '
' . esc_html__('无效的下载请求', 'aksrt') . '
'; + echo '
'; + get_footer(); + exit; +} + +$pay_meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : ['pay_type' => 'no', 'downloads' => []]; +$pay_type = (string) ($pay_meta['pay_type'] ?? 'no'); +$is_download = $pay_type === 'download' || (function_exists('aksrt_is_downloadable_content') && aksrt_is_downloadable_content($post)); + +if (!$is_download) { + get_header(); + echo '
'; + echo '
' . esc_html__('该内容不是可下载资源', 'aksrt') . '
'; + echo '

' . esc_html__('返回文章', 'aksrt') . '

'; + echo '
'; + get_footer(); + exit; +} + +$article_url = get_permalink($post) . '#posts-pay'; +$pay_title = get_the_title($post); +$subtitle = ''; +if (function_exists('get_the_subtitle')) { + // noop — AKSRT may not have zibll subtitle helper +} +$has_access = function_exists('aksrt_has_paid_access') ? aksrt_has_paid_access($post) : false; +$gate = function_exists('aksrt_can_download') ? aksrt_can_download($post) : true; +$can_dl = $gate === true; +$need_login = is_wp_error($gate) && $gate->get_error_code() === 'aksrt_need_login'; + +// Free download + login required (Zibll pay_free_logged_show). +$price = (float) ($pay_meta['price'] ?? 0); +$order_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_order_settings() : []; +$free_logged_show = !empty($order_settings['free_logged_show']); +if ($price <= 0 && $free_logged_show && !is_user_logged_in()) { + $can_dl = false; + $need_login = true; + $has_access = true; // free "access" but must login to download +} + +$paid_name = ''; +if ($has_access || $can_dl) { + if ($price <= 0) { + $paid_name = __('免费资源', 'aksrt'); + } else { + $paid_name = __('已获得下载权限', 'aksrt'); + } +} + +get_header(); +?> + +
+
+
+
+
+
+
+ +
+ + + + + +
+ + +
+ +
+ +
+ +
+
+ 登录 后再下载。', 'aksrt')), + esc_url($login_url) + ); + ?> +
+ + + + + +
+ +
+ + + + + +
+
+
+
+
+ $mode, - 'context' => $context, - ]); - - echo aksrt_render_post($config); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - return; -} - -// ============================================================ -// Fallback: self-contained rendering when shared renderer unavailable -// ============================================================ - -$args = isset($args) && is_array($args) ? $args : []; - -$lazy_cfg = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_lazyload_config() : ['enabled' => false, 'placeholder_url' => '']; -$lazy_on = !empty($lazy_cfg['enabled']); -$placeholder_url = !empty($lazy_cfg['placeholder_url']) ? (string) $lazy_cfg['placeholder_url'] : (get_template_directory_uri() . '/assets/img/placeholder.svg'); - -$forced_layout = (string) get_query_var('aksrt_post_card_layout', ''); -$layout = 'card'; -if (is_string($forced_layout) && in_array($forced_layout, ['card', 'list', 'masonry', 'grid-cms'], true)) { - $layout = $forced_layout; -} elseif (function_exists('aksrt_get_post_flow') && aksrt_get_post_flow(get_the_ID()) === 'cms') { - $layout = 'grid-cms'; -} elseif (class_exists('AKSRT_Settings')) { - $layout = AKSRT_Settings::get_archive_layout(); -} - -$post_id = get_the_ID(); -$author_id = get_the_author_meta('ID'); -$author = get_the_author(); -$avatar_url = get_avatar_url($author_id, ['size' => 40]); -$time_ago = human_time_diff(get_the_time('U'), current_time('timestamp')) . __('前', 'aksrt'); -$excerpt = wp_trim_words(get_the_excerpt(), 'list' === $layout ? 30 : 20, '...'); -$tags = get_the_tags() ?: []; -$categories = get_the_category() ?: []; -$comment_count = (int) get_comments_number(); -$views = function_exists('aksrt_get_post_views') ? aksrt_get_post_views($post_id) : 0; -$is_sticky = is_sticky($post_id); -$title_html = isset($args['title_html']) ? (string) $args['title_html'] : get_the_title(); -$resource_badge = function_exists('aksrt_resource_card_badge_text') ? aksrt_resource_card_badge_text($post_id) : ''; - -$img_url = ''; -if (has_post_thumbnail()) { - $img_url = get_the_post_thumbnail_url($post_id, 'medium_large'); -} elseif (function_exists('aksrt_get_first_content_image_url')) { - $img_url = (string) aksrt_get_first_content_image_url($post_id); -} -$has_img = !empty($img_url); - -$scene = (string) get_query_var('aksrt_post_card_scene', ''); -$is_home_scene = $scene === 'home' || is_front_page() || (is_home() && !is_paged()); -$category_limit = $is_home_scene ? 1 : 3; -$tag_limit = $is_home_scene ? 2 : 3; - -// Badges HTML -ob_start(); -?> -
- - - - - name); ?> - - - # name); ?> - -
- -
- - - - - -
- - -
-
- -
> -
- - - - <?php echo esc_attr(get_the_title()); ?> - - <?php echo esc_attr(get_the_title()); ?> - - - - - - - -
-
-

-

- - -
-
- -
> -
- - - - <?php echo esc_attr(get_the_title()); ?> - - <?php echo esc_attr(get_the_title()); ?> - - - - - - - -
-
-

- - -
-
- + $mode, + 'context' => $context, + ]); + + echo aksrt_render_post($config); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + return; +} + +// ============================================================ +// Fallback: self-contained rendering when shared renderer unavailable +// ============================================================ + +$args = isset($args) && is_array($args) ? $args : []; + +$lazy_cfg = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_lazyload_config() : ['enabled' => false, 'placeholder_url' => '']; +$lazy_on = !empty($lazy_cfg['enabled']); +$placeholder_url = !empty($lazy_cfg['placeholder_url']) ? (string) $lazy_cfg['placeholder_url'] : (get_template_directory_uri() . '/assets/img/placeholder.svg'); + +$forced_layout = (string) get_query_var('aksrt_post_card_layout', ''); +$layout = 'card'; +if (is_string($forced_layout) && in_array($forced_layout, ['card', 'list', 'masonry', 'grid-cms'], true)) { + $layout = $forced_layout; +} elseif (function_exists('aksrt_get_post_flow') && aksrt_get_post_flow(get_the_ID()) === 'cms') { + $layout = 'grid-cms'; +} elseif (class_exists('AKSRT_Settings')) { + $layout = AKSRT_Settings::get_archive_layout(); +} + +$post_id = get_the_ID(); +$author_id = get_the_author_meta('ID'); +$author = get_the_author(); +$avatar_url = get_avatar_url($author_id, ['size' => 40]); +$time_ago = human_time_diff(get_the_time('U'), current_time('timestamp')) . __('前', 'aksrt'); +$excerpt = wp_trim_words(get_the_excerpt(), 'list' === $layout ? 30 : 20, '...'); +$tags = get_the_tags() ?: []; +$categories = get_the_category() ?: []; +$comment_count = (int) get_comments_number(); +$views = function_exists('aksrt_get_post_views') ? aksrt_get_post_views($post_id) : 0; +$is_sticky = is_sticky($post_id); +$title_html = isset($args['title_html']) ? (string) $args['title_html'] : get_the_title(); +$resource_badge = function_exists('aksrt_resource_card_badge_text') ? aksrt_resource_card_badge_text($post_id) : ''; + +$img_url = ''; +if (has_post_thumbnail()) { + $img_url = get_the_post_thumbnail_url($post_id, 'medium_large'); +} elseif (function_exists('aksrt_get_first_content_image_url')) { + $img_url = (string) aksrt_get_first_content_image_url($post_id); +} +$has_img = !empty($img_url); + +$scene = (string) get_query_var('aksrt_post_card_scene', ''); +$is_home_scene = $scene === 'home' || is_front_page() || (is_home() && !is_paged()); +$category_limit = $is_home_scene ? 1 : 3; +$tag_limit = $is_home_scene ? 2 : 3; + +// Badges HTML +ob_start(); +?> +
+ + + + + name); ?> + + + # name); ?> + +
+ +
+ + + + + +
+ + +
+
+ +
> +
+ + + + <?php echo esc_attr(get_the_title()); ?> + + <?php echo esc_attr(get_the_title()); ?> + + + + + + + +
+
+

+

+ + +
+
+ +
> +
+ + + + <?php echo esc_attr(get_the_title()); ?> + + <?php echo esc_attr(get_the_title()); ?> + + + + + + + +
+
+

+ + +
+
+ diff --git a/templates/parts/resource-download-box.php b/templates/parts/resource-download-box.php index f98e77e5..2786a4dd 100644 --- a/templates/parts/resource-download-box.php +++ b/templates/parts/resource-download-box.php @@ -55,7 +55,26 @@ $balance = (int) ($gate_data['balance'] ?? 0); $login_url = (string) ($gate_data['login_url'] ?? wp_login_url(get_permalink($post_id))); $is_need_login = $gate_blocked && $gate_code === 'aksrt_need_login'; -$is_low_credit = $gate_blocked && $gate_code === 'aksrt_credits_insufficient'; + $is_low_credit = $gate_blocked && $gate_code === 'aksrt_credits_insufficient'; + + // Zibll pay_free_logged_show: free downloads still require login. + $free_logged_show = !empty($paid_settings['free_logged_show']); + $price_amount = (float) ($meta['price'] ?? 0); + if ($price_amount <= 0 && $free_logged_show && !is_user_logged_in()) { + $is_need_login = true; + $gate_blocked = true; + } + + // Zibll down_alone_page: single entry to independent download page. + $alone_page = !empty($paid_settings['down_alone_page']) || !empty($paid_settings['download']['alone_page']); + $alone_url = ''; + if ($alone_page && function_exists('aksrt_get_single_download_page_url')) { + $alone_url = aksrt_get_single_download_page_url($post_id); + } + $use_alone_entry = $alone_page && $alone_url !== '' && !$is_need_login && !$is_low_credit; + if (!empty($GLOBALS['aksrt_on_download_file_page'])) { + $use_alone_entry = false; + } // 缩略图 $thumbnail = get_the_post_thumbnail_url($post_id, 'medium') ?: ''; @@ -160,7 +179,21 @@ $currency = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_currency_name()

-