进行了一些小修复

This commit is contained in:
2026-08-26 15:29:37 +08:00
parent ae10b3d3fd
commit 562beb15cf
6 changed files with 206 additions and 43 deletions
+8 -14
View File
@@ -228,14 +228,8 @@
// ===== 主流程 =====
function collectCaptchaPayload(sourceEl) {
// 优先从触发按钮所属的下载卡片中查找验证码容器(支持双下载框场景:侧栏+移动端)
var container = null;
if (sourceEl) {
var box = sourceEl.closest('.resource-download-box');
if (box) container = box.querySelector('[data-aksrt-download-captcha]');
}
// 回退到全局查找
if (!container) container = document.querySelector('[data-aksrt-download-captcha]');
const box = sourceEl ? sourceEl.closest('[data-aksrt-download-box]') : null;
const container = box ? box.querySelector('[data-aksrt-download-captcha]') : null;
if (!container) return {};
const out = {};
const keys = [
@@ -252,14 +246,14 @@
return out;
}
function isCaptchaActive() {
const card = document.querySelector('.resource-download-box');
function isCaptchaActive(sourceEl) {
const card = sourceEl ? sourceEl.closest('[data-aksrt-download-box]') : null;
if (!card) return false;
return card.getAttribute('data-captcha') === '1';
}
function ensureCaptchaThen(callback) {
if (!isCaptchaActive()) { callback(); return; }
function ensureCaptchaThen(sourceEl, callback) {
if (!isCaptchaActive(sourceEl)) { callback(); return; }
const state = window.AKSRTCaptchaState;
const scene = state && state.scenes && state.scenes.download;
if (!scene || typeof scene.verify !== 'function') {
@@ -274,7 +268,7 @@
}
function performDownloadRequest(button) {
const card = button.closest('.resource-download-box');
const card = button.closest('[data-aksrt-download-box]');
if (!card) return;
const postId = parseInt(card.getAttribute('data-post-id'), 10);
const sourceIndex = parseInt(button.getAttribute('data-source-index'), 10);
@@ -323,7 +317,7 @@
}
function requestDownload(button) {
ensureCaptchaThen(function () {
ensureCaptchaThen(button, function () {
performDownloadRequest(button);
});
}
+183 -11
View File
@@ -1300,6 +1300,176 @@ if (!function_exists('aksrt_get_paywall_html')) {
}
}
/**
* Resolve the paid order that granted access to a post, when access came from
* an actual purchase rather than a free or VIP entitlement.
*
* @return array<string,mixed>|null
*/
if (!function_exists('aksrt_get_current_content_paid_order')) {
function aksrt_get_current_content_paid_order(WP_Post $post): ?array {
if (is_user_logged_in()) {
return aksrt_get_user_content_paid_order($post->ID);
}
return aksrt_get_guest_content_paid_order($post->ID);
}
}
/**
* Reference-theme download actions used inside the purchased article card.
*/
if (!function_exists('aksrt_get_paid_download_actions_html')) {
function aksrt_get_paid_download_actions_html(array $meta): string {
$downloads = is_array($meta['downloads'] ?? null) ? array_values($meta['downloads']) : [];
if ($downloads === []) {
return '<div class="muted-2-color text-center">' . esc_html__('暂无可下载资源', 'aksrt') . '</div>';
}
$buttons = '';
foreach ($downloads as $index => $download) {
if (!is_array($download)) {
continue;
}
$url = (string) ($download['url'] ?? '');
$label = trim((string) ($download['label'] ?? ''));
$service = function_exists('aksrt_detect_cloud_service')
? aksrt_detect_cloud_service($url)
: ['key' => 'other', 'name' => __('本地下载', 'aksrt')];
if ($label === '') {
$label = (string) ($service['name'] ?? __('本地下载', 'aksrt'));
}
$type = sanitize_key((string) ($download['type'] ?? 'direct'));
$has_extract = trim((string) ($download['extract_code'] ?? '')) !== '';
$service_key = sanitize_html_class((string) ($service['key'] ?? 'other')) ?: 'other';
$buttons .= '<div class="but-download flex ac">';
$buttons .= '<button type="button" class="mr10 but b-theme ' . esc_attr($service_key) . '" data-action="aksrt-download" data-source-index="' . esc_attr((string) $index) . '" data-source-type="' . esc_attr($type) . '" data-has-extract="' . ($has_extract ? '1' : '0') . '">';
$buttons .= '<i class="fa fa-download" aria-hidden="true"></i>' . esc_html($label) . '</button>';
$buttons .= '</div>';
}
return $buttons !== ''
? '<div class="flex ac hh">' . $buttons . '</div>'
: '<div class="muted-2-color text-center">' . esc_html__('暂无可下载资源', 'aksrt') . '</div>';
}
}
/**
* Purchased article card matching Zibll's paid-box structure.
*/
if (!function_exists('aksrt_get_paid_box_html')) {
function aksrt_get_paid_box_html(WP_Post $post): string {
if (!aksrt_has_paid_access($post)) {
return '';
}
$meta = aksrt_get_pay_meta($post);
$pay_type = (string) ($meta['pay_type'] ?? AKSRT_PAY_TYPE_NO);
if ($pay_type === AKSRT_PAY_TYPE_NO) {
return '';
}
$order_types = [
AKSRT_PAY_TYPE_READING => 1,
AKSRT_PAY_TYPE_DOWNLOAD => 2,
AKSRT_PAY_TYPE_GALLERY => 5,
AKSRT_PAY_TYPE_VIDEO => 6,
AKSRT_PAY_TYPE_VIP => 4,
];
$type_labels = [
AKSRT_PAY_TYPE_READING => __('付费阅读', 'aksrt'),
AKSRT_PAY_TYPE_DOWNLOAD => __('付费下载', 'aksrt'),
AKSRT_PAY_TYPE_GALLERY => __('付费图库', 'aksrt'),
AKSRT_PAY_TYPE_VIDEO => __('付费视频', 'aksrt'),
AKSRT_PAY_TYPE_VIP => __('VIP 专属', 'aksrt'),
];
$order_type = (int) ($order_types[$pay_type] ?? 1);
$type_label = (string) ($type_labels[$pay_type] ?? __('付费内容', 'aksrt'));
$title = get_the_title($post);
$sales = max(0, (int) ($meta['virtual_sales'] ?? 0));
$sales += max(0, (int) get_post_meta($post->ID, \AKSRT\Core\DomainModel::RESOURCE_SALES_VOLUME_KEY, true));
$count = $sales > 0 ? '<badge class="img-badge hot jb-blue px12">' . esc_html(sprintf(__('已售 %s', 'aksrt'), $sales)) . '</badge>' : '';
$order = aksrt_get_current_content_paid_order($post);
$priced = aksrt_get_user_pay_price($post);
$vip_level = max(0, min(2, (int) ($priced['vip_level'] ?? 0)));
$header = '';
$paid_box = '';
if (is_array($order)) {
$order_number = (string) ($order['order_number'] ?? '');
$paid_at = (string) ($order['paid_at'] ?? $order['created_at'] ?? '');
$order_currency = (string) ($order['currency'] ?? '');
$order_amount = $order['amount'] ?? '0.00';
if ($order_currency === 'points') {
$currency_name = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
$amount_text = aksrt_format_price($order_amount) . ' ' . $currency_name;
} else {
$cash_symbol = '¥';
if (class_exists('\AKSRT\Settings\Settings')) {
$settings = \AKSRT\Settings\Settings::get_paid_content_settings();
$cash_symbol = (string) ($settings['display']['currency_symbol'] ?? '¥');
}
$amount_text = $cash_symbol . aksrt_format_price($order_amount);
}
$paid_info = '<div class="flex jsb"><span>' . esc_html__('订单号', 'aksrt') . '</span><span>' . esc_html($order_number) . '</span></div>';
if ($paid_at !== '') {
$paid_info .= '<div class="flex jsb"><span>' . esc_html__('支付时间', 'aksrt') . '</span><span>' . esc_html($paid_at) . '</span></div>';
}
$paid_info .= '<div class="flex jsb"><span>' . esc_html__('支付金额', 'aksrt') . '</span><span>' . esc_html($amount_text) . '</span></div>';
$paid_box = '<div class="flex ac jb-green padding-10 em09"><div class="text-center flex-auto"><div class="mb6"><i class="fa fa-shopping-bag fa-2x" aria-hidden="true"></i></div><b class="em12">' . esc_html__('已购买', 'aksrt') . '</b></div><div class="em09 paid-info flex-auto">' . $paid_info . '</div></div>';
} elseif ($vip_level > 0 || $pay_type === AKSRT_PAY_TYPE_VIP) {
$level = max(1, $vip_level);
$price_label = aksrt_get_pay_price_label($post);
$price = '<div class="price-box"><div class="text-center"><b class="em3x">' . esc_html((string) ($price_label['text'] ?? '')) . '</b></div></div>';
$paid_box = '<div class="flex jsb ac payvip-icon box-body vipbg-v' . esc_attr((string) $level) . '">' . $price . '<div class="flex0"><b class="em12"><i class="fa fa-certificate mr10 em12" aria-hidden="true"></i>' . esc_html(sprintf(__('VIP%d 会员免费', 'aksrt'), $level)) . '</b></div></div>';
} else {
$type_label = str_replace(__('付费', 'aksrt'), __('免费', 'aksrt'), $type_label);
}
if ($pay_type === AKSRT_PAY_TYPE_GALLERY) {
$header = aksrt_render_paid_gallery($post);
} elseif ($pay_type === AKSRT_PAY_TYPE_VIDEO) {
$header = aksrt_render_paid_video($post);
}
$download_box = '';
$root_data = '';
$captcha = '';
if ($pay_type === AKSRT_PAY_TYPE_DOWNLOAD) {
$captcha_active = function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('download');
$root_data = ' data-aksrt-download-box data-post-id="' . esc_attr((string) $post->ID) . '" data-captcha="' . ($captcha_active ? '1' : '0') . '"';
$download_box = '<div class="hidden-box show"><div class="hidden-text"><i class="fa fa-download mr6" aria-hidden="true"></i>' . esc_html__('资源下载', 'aksrt') . '</div>' . aksrt_get_paid_download_actions_html($meta) . '</div>';
$demo = aksrt_get_demo_link_html($post, 'but jb-yellow padding-lg');
if ($demo !== '') {
$download_box .= '<div class="mt10">' . $demo . '</div>';
}
$download_box .= aksrt_get_pay_attributes_html($post, 'pay-attr');
if ($captcha_active && function_exists('aksrt_render_captcha')) {
ob_start();
aksrt_render_captcha('download');
$captcha = '<div class="resource-download-box__captcha resource-download-box__captcha--hidden" data-aksrt-download-captcha>' . (string) ob_get_clean() . '</div>';
}
}
$service = aksrt_get_pay_service_html('inline-block ml10');
$service = $service !== '' ? '<div class="px12 muted-2-color mt10 text-right">' . $service . '</div>' : '';
$title_style = $count !== '' ? ' style="padding-right:48px"' : '';
$body_class = 'box-body relative' . ($pay_type === AKSRT_PAY_TYPE_VIDEO ? ' dplayer-featured' : '');
return '<div class="pay-box zib-widget paid-box order-type-' . $order_type . '" id="posts-pay"' . $root_data . '>'
. $header
. $paid_box
. '<div class="' . esc_attr($body_class) . '">'
. $count
. '<div' . $title_style . '><span class="badg c-red hollow badg-sm mr6">' . esc_html($type_label) . '</span><b>' . esc_html($title) . '</b></div>'
. $download_box
. $service
. $captcha
. '</div></div>';
}
}
// ============================================================
// 付费图库轮播(Swiper)
// ============================================================
@@ -1313,19 +1483,25 @@ if (!function_exists('aksrt_render_paid_gallery')) {
if (empty($images)) return '';
if (!aksrt_has_paid_access($post)) return '';
$count = count($images);
$backdrop = (string) end($images);
ob_start();
?>
<div class="aksrt-paid-gallery" data-post-id="<?php echo esc_attr((string) $post->ID); ?>">
<div class="new-swiper aksrt-paid-gallery__swiper" data-loop="true">
<div class="relative-h paid-gallery aksrt-paid-gallery" data-post-id="<?php echo esc_attr((string) $post->ID); ?>">
<div class="absolute blur-10 opacity3"><img<?php echo aksrt_get_lazy_attr('lazy_other', $backdrop, 'fit-cover', aksrt_get_lazy_thumb('lg')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> alt=""></div>
<div style="margin-top:-20px" class="relative mb6"><span class="badg b-theme badg-sm"><?php echo esc_html(sprintf(__('共%s张图片', 'aksrt'), $count)); ?></span></div>
<div class="new-swiper swiper-container swiper-scroll aksrt-paid-gallery__swiper" data-loop="true">
<div class="swiper-wrapper">
<?php foreach ($images as $img_url) : ?>
<div class="swiper-slide">
<img<?php echo aksrt_get_lazy_attr('lazy_other', (string) $img_url, '', aksrt_get_lazy_thumb('lg')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> alt="">
<?php foreach ($images as $index => $img_url) : ?>
<div class="swiper-slide mr10" style="width:150px">
<div class="graphic" style="padding-bottom:100%!important">
<img<?php echo aksrt_get_lazy_attr('lazy_other', (string) $img_url, 'fit-cover', aksrt_get_lazy_thumb('lg')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> alt="">
<div class="abs-center right-top"><badge class="b-black opacity8 mr6 mt6"><?php echo esc_html(($index + 1) . '/' . $count); ?></badge></div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="swiper-pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
@@ -1418,11 +1594,7 @@ add_action('wp_head', function (): void {
if (!in_array($meta['pay_type'], [AKSRT_PAY_TYPE_GALLERY, AKSRT_PAY_TYPE_VIDEO], true)) return;
?>
<style>
.aksrt-paid-gallery{margin:24px 0}
.aksrt-paid-gallery__swiper{border-radius:var(--radius-lg);overflow:hidden}
.aksrt-paid-gallery__swiper img{width:100%;height:auto;display:block}
.aksrt-paid-video{margin:24px 0}
.aksrt-paid-video{margin:0}
.aksrt-paid-video__embed{position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:var(--radius-lg)}
.aksrt-paid-video__embed iframe{position:absolute;top:0;left:0;width:100%;height:100%;border:none}
</style>
+2 -15
View File
@@ -759,21 +759,8 @@ if (!function_exists('aksrt_single_render_pay_box')) :
$has_access = function_exists('aksrt_has_paid_access') ? aksrt_has_paid_access($post) : false;
if ($has_access) {
if ($pay_type === 'download') {
get_template_part('templates/parts/resource-download-box');
return;
}
$order_type = $pay_type === 'gallery' ? 5 : ($pay_type === 'video' ? 6 : 4);
echo '<div class="zib-widget pay-box paid-box order-type-' . esc_attr((string) $order_type) . '" id="posts-pay">';
if ($pay_type === 'gallery') {
echo aksrt_render_paid_gallery($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} elseif ($pay_type === 'video') {
echo aksrt_render_paid_video($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo '<div class="box-body"><span class="badg c-green hollow badg-sm mr6">' . esc_html__('已解锁', 'aksrt') . '</span><b>' . esc_html(get_the_title($post)) . '</b></div>';
}
echo '</div>';
} else {
echo aksrt_get_paid_box_html($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} elseif ($pay_type !== 'reading') {
echo aksrt_get_paywall_html($post); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
+3 -3
View File
@@ -72,9 +72,9 @@ if (!function_exists('aksrt_single_build_context')) :
$pay_meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post_id) : ['pay_type' => 'no'];
$pay_type = (string) ($pay_meta['pay_type'] ?? 'no');
$is_paid_post = function_exists('aksrt_is_paid_post') && aksrt_is_paid_post($post_id);
// Paid-reading purchase is rendered inline by each payshow shortcode.
// Keep the article-level module for download, gallery, video and VIP only.
$show_pay_box = $is_paid_post && !in_array($pay_type, ['no', 'reading'], true);
// Unpaid reading fragments own their inline purchase card. Once access is
// granted, the article-level slot shows the canonical purchased card.
$show_pay_box = $is_paid_post && $pay_type !== 'no';
$breadcrumbs_html = '';
if (!empty($settings['breadcrumbs']) && function_exists('aksrt_breadcrumbs')) {
@@ -150,6 +150,7 @@ if ($is_need_purchase && $user_amount > 0) {
?>
<section class="widget resource-download-box resource-download-box--v2<?php echo $gate_blocked ? ' is-blocked' : ''; ?><?php echo $has_paid_access ? ' is-purchased' : ''; ?> resource-download-box--<?php echo esc_attr($context_variant); ?>"
data-aksrt-download-box
data-post-id="<?php echo esc_attr((string) $post_id); ?>"
data-price="<?php echo esc_attr((string) $base_price); ?>"
data-amount="<?php echo esc_attr((string) $user_amount); ?>"
@@ -23,9 +23,18 @@ test('paid article and sidebar widget use the Zibll purchase-card DOM', () => {
assert.match(pay, /price-box/);
assert.match(pay, /vip-price-buts but-average inline/);
assert.match(pay, /cashier-link/);
assert.match(pay, /function aksrt_get_paid_box_html/);
assert.match(pay, /pay-box zib-widget paid-box order-type-/);
assert.match(pay, /flex ac jb-green padding-10 em09/);
assert.match(pay, /订单号/);
assert.match(pay, /支付时间/);
assert.match(pay, /支付金额/);
assert.match(pay, /hidden-box show/);
assert.match(widget, /'layout' => 'widget'/);
assert.match(shell, /\['post_pay', 'tab_posts'/);
assert.doesNotMatch(single, /aksrt-panel pay-box pay-type-download/);
assert.doesNotMatch(single, /get_template_part\('templates\/parts\/resource-download-box'\)/);
assert.match(single, /aksrt_get_paid_box_html\(\$post\)/);
assert.doesNotMatch(hooks, /aksrt_paid_content_enhance/);
});