refactor(autoload): migrate theme classes to Composer PSR-4
Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
This commit is contained in:
@@ -0,0 +1,558 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Core;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class ResourceMetaBox {
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) self::$instance = new self();
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function init(): void {
|
||||
add_action('add_meta_boxes', [$this, 'register_meta_box']);
|
||||
add_action('save_post_post', [$this, 'save'], 10, 2);
|
||||
}
|
||||
|
||||
public function register_meta_box(): void {
|
||||
add_meta_box(
|
||||
'aksrt_pay_meta',
|
||||
__('付费内容设置', 'aksrt'),
|
||||
[$this, 'render'],
|
||||
'post',
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
|
||||
public function render(\WP_Post $post): void {
|
||||
wp_nonce_field('aksrt_pay_meta_save', 'aksrt_pay_meta_nonce');
|
||||
|
||||
$meta = function_exists('aksrt_get_pay_meta')
|
||||
? aksrt_get_pay_meta($post)
|
||||
: DomainModel::default_pay_meta();
|
||||
|
||||
$pay_types = aksrt_get_pay_types();
|
||||
$is_paid = ($meta['pay_type'] ?? 'no') !== 'no';
|
||||
|
||||
$downloads_json = !empty($meta['downloads'])
|
||||
? wp_json_encode($meta['downloads'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||||
: '';
|
||||
|
||||
$attributes_json = !empty($meta['attributes'])
|
||||
? wp_json_encode($meta['attributes'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||||
: '';
|
||||
|
||||
$gallery_json = !empty($meta['gallery'])
|
||||
? wp_json_encode($meta['gallery'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||||
: '';
|
||||
?>
|
||||
<style>
|
||||
.aksrt-pmb__row { display: grid; grid-template-columns: 140px 1fr; gap: 12px 16px; align-items: start; margin-bottom: 12px; }
|
||||
.aksrt-pmb__row label { font-weight: 600; color: #1f2937; padding-top: 6px; font-size: 13px; }
|
||||
.aksrt-pmb__row input[type="text"], .aksrt-pmb__row input[type="number"], .aksrt-pmb__row input[type="url"], .aksrt-pmb__row textarea, .aksrt-pmb__row select { width: 100%; max-width: 560px; }
|
||||
.aksrt-pmb__row textarea { min-height: 120px; font-family: Consolas, "Liberation Mono", monospace; font-size: 12px; }
|
||||
.aksrt-pmb__hint { color: #6b7280; font-size: 12px; margin-top: 4px; line-height: 1.5; }
|
||||
.aksrt-pmb__section { margin-top: 16px; padding-top: 16px; border-top: 1px solid #e5e7eb; }
|
||||
.aksrt-pmb__section-title { font-size: 14px; font-weight: 700; color: #111827; margin-bottom: 12px; }
|
||||
.aksrt-pmb__toggle { display: flex; align-items: center; gap: 8px; padding: 12px; background: #f3f4f6; border-radius: 6px; margin-bottom: 16px; }
|
||||
.aksrt-pmb__toggle label { font-weight: 600; }
|
||||
.aksrt-pmb__vip-row { display: flex; gap: 12px; flex-wrap: wrap; }
|
||||
.aksrt-pmb__vip-row > div { flex: 1; min-width: 120px; }
|
||||
.aksrt-pmb__vip-row label { font-weight: 400; font-size: 12px; color: #6b7280; }
|
||||
.aksrt-pmb__tools { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
.aksrt-pmb__tools button { font-size: 12px; }
|
||||
.aksrt-pmb__error { color: #dc2626; font-size: 12px; display: none; margin-top: 4px; }
|
||||
.aksrt-pmb__fields[data-disabled="1"] { opacity: 0.45; pointer-events: none; }
|
||||
</style>
|
||||
|
||||
<div class="aksrt-pmb__toggle">
|
||||
<label for="aksrt_pay_type"><?php esc_html_e('付费类型', 'aksrt'); ?></label>
|
||||
<select id="aksrt_pay_type" name="aksrt_pay_type" style="min-width:160px;">
|
||||
<?php foreach ($pay_types as $key => $label) : ?>
|
||||
<option value="<?php echo esc_attr($key); ?>" <?php selected($meta['pay_type'], $key); ?>>
|
||||
<?php echo esc_html($label); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<span class="aksrt-pmb__hint">
|
||||
<?php esc_html_e('选择「不付费」则为普通文章;选择其他类型将启用对应的付费功能。', 'aksrt'); ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__fields" id="aksrt-pmb-fields" data-disabled="<?php echo $is_paid ? '0' : '1'; ?>">
|
||||
|
||||
<!-- 基础价格 -->
|
||||
<div class="aksrt-pmb__section">
|
||||
<div class="aksrt-pmb__section-title"><?php esc_html_e('价格设置', 'aksrt'); ?></div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_modo"><?php esc_html_e('支付方式', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<select id="aksrt_pay_modo" name="aksrt_pay_modo" style="min-width:160px;">
|
||||
<option value="0" <?php selected((string) ($meta['pay_modo'] ?? '0'), '0'); ?>><?php esc_html_e('现金', 'aksrt'); ?></option>
|
||||
<option value="points" <?php selected((string) ($meta['pay_modo'] ?? '0'), 'points'); ?>><?php esc_html_e('积分', 'aksrt'); ?></option>
|
||||
</select>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('对齐 zibll pay_modo:现金价或积分价结算。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_price"><?php esc_html_e('现金价格', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<input type="number" id="aksrt_pay_price" name="aksrt_pay_price" value="<?php echo esc_attr((string) $meta['price']); ?>" min="0" step="0.01">
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('支付方式为「现金」时使用。0 = 免费。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_points_price"><?php esc_html_e('积分价格', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<input type="number" id="aksrt_pay_points_price" name="aksrt_pay_points_price" value="<?php echo esc_attr((string) ($meta['points_price'] ?? 0)); ?>" min="0" step="1">
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('支付方式为「积分」时使用。0 = 免费。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_original_price"><?php esc_html_e('原价(划线价)', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<input type="number" id="aksrt_pay_original_price" name="aksrt_pay_original_price" value="<?php echo esc_attr((string) ($meta['original_price'] ?? '')); ?>" min="0" step="0.01" placeholder="<?php esc_attr_e('可选,显示划线效果', 'aksrt'); ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_limit"><?php esc_html_e('购买限制', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<select id="aksrt_pay_limit" name="aksrt_pay_limit" style="min-width:200px;">
|
||||
<option value="0" <?php selected((int) ($meta['pay_limit'] ?? 0), 0); ?>><?php esc_html_e('所有用户可购买', 'aksrt'); ?></option>
|
||||
<option value="1" <?php selected((int) ($meta['pay_limit'] ?? 0), 1); ?>><?php esc_html_e('仅 VIP1 及以上', 'aksrt'); ?></option>
|
||||
<option value="2" <?php selected((int) ($meta['pay_limit'] ?? 0), 2); ?>><?php esc_html_e('仅 VIP2', 'aksrt'); ?></option>
|
||||
</select>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('对齐 zibll pay_limit:限制购买人群(会员专属资源)。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label><?php esc_html_e('VIP 专属价', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<div class="aksrt-pmb__vip-row">
|
||||
<div>
|
||||
<label for="aksrt_pay_vip1_price"><?php esc_html_e('VIP1 现金价', 'aksrt'); ?></label>
|
||||
<input type="number" id="aksrt_pay_vip1_price" name="aksrt_pay_vip1_price" value="<?php echo esc_attr((string) ($meta['vip1_price'] ?? '')); ?>" min="0" step="0.01" placeholder="0">
|
||||
</div>
|
||||
<div>
|
||||
<label for="aksrt_pay_vip2_price"><?php esc_html_e('VIP2 现金价', 'aksrt'); ?></label>
|
||||
<input type="number" id="aksrt_pay_vip2_price" name="aksrt_pay_vip2_price" value="<?php echo esc_attr((string) ($meta['vip2_price'] ?? '')); ?>" min="0" step="0.01" placeholder="0">
|
||||
</div>
|
||||
<div>
|
||||
<label for="aksrt_pay_vip1_points"><?php esc_html_e('VIP1 积分价', 'aksrt'); ?></label>
|
||||
<input type="number" id="aksrt_pay_vip1_points" name="aksrt_pay_vip1_points" value="<?php echo esc_attr((string) ($meta['vip1_points'] ?? '')); ?>" min="0" step="1" placeholder="0">
|
||||
</div>
|
||||
<div>
|
||||
<label for="aksrt_pay_vip2_points"><?php esc_html_e('VIP2 积分价', 'aksrt'); ?></label>
|
||||
<input type="number" id="aksrt_pay_vip2_points" name="aksrt_pay_vip2_points" value="<?php echo esc_attr((string) ($meta['vip2_points'] ?? '')); ?>" min="0" step="1" placeholder="0">
|
||||
</div>
|
||||
</div>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('VIP 用户专属价。留空或 0 表示不启用该档位优惠。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 资源信息(download/gallery/video 类型显示) -->
|
||||
<div class="aksrt-pmb__section" id="aksrt-pmb-resource-fields">
|
||||
<div class="aksrt-pmb__section-title"><?php esc_html_e('资源信息', 'aksrt'); ?></div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_version"><?php esc_html_e('版本', 'aksrt'); ?></label>
|
||||
<input type="text" id="aksrt_pay_version" name="aksrt_pay_version" value="<?php echo esc_attr((string) $meta['version']); ?>" placeholder="v1.0.0">
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_size"><?php esc_html_e('文件大小', 'aksrt'); ?></label>
|
||||
<input type="text" id="aksrt_pay_size" name="aksrt_pay_size" value="<?php echo esc_attr((string) $meta['size']); ?>" placeholder="210 MB">
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_compat"><?php esc_html_e('兼容性', 'aksrt'); ?></label>
|
||||
<input type="text" id="aksrt_pay_compat" name="aksrt_pay_compat" value="<?php echo esc_attr((string) $meta['compat']); ?>" placeholder="WordPress 6.4+">
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_license"><?php esc_html_e('授权', 'aksrt'); ?></label>
|
||||
<input type="text" id="aksrt_pay_license" name="aksrt_pay_license" value="<?php echo esc_attr((string) $meta['license']); ?>" placeholder="MIT / GPL / 自用">
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_demo_url"><?php esc_html_e('演示地址', 'aksrt'); ?></label>
|
||||
<input type="url" id="aksrt_pay_demo_url" name="aksrt_pay_demo_url" value="<?php echo esc_attr((string) $meta['demo_url']); ?>" placeholder="https://">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 自定义属性 -->
|
||||
<div class="aksrt-pmb__section">
|
||||
<div class="aksrt-pmb__section-title"><?php esc_html_e('自定义属性', 'aksrt'); ?></div>
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_attributes"><?php esc_html_e('属性列表', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<textarea id="aksrt_pay_attributes" name="aksrt_pay_attributes" placeholder='[
|
||||
{"key": "格式", "value": "PSD"},
|
||||
{"key": "分辨率", "value": "4K"}
|
||||
]'><?php echo esc_textarea($attributes_json); ?></textarea>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('JSON 数组。每项:key(属性名)/ value(属性值)。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下载源(download 类型显示) -->
|
||||
<div class="aksrt-pmb__section" id="aksrt-pmb-download-fields">
|
||||
<div class="aksrt-pmb__section-title"><?php esc_html_e('下载源', 'aksrt'); ?></div>
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_downloads"><?php esc_html_e('下载链接', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<textarea id="aksrt_pay_downloads" name="aksrt_pay_downloads" placeholder='[
|
||||
{
|
||||
"label": "百度网盘",
|
||||
"url": "https://pan.baidu.com/s/xxx",
|
||||
"type": "net_disk",
|
||||
"extract_code": "abcd"
|
||||
}
|
||||
]'><?php echo esc_textarea($downloads_json); ?></textarea>
|
||||
<div class="aksrt-pmb__error" id="aksrt-pmb-dl-err"></div>
|
||||
<div class="aksrt-pmb__tools">
|
||||
<button type="button" class="button button-small" id="aksrt-pmb-add-dl"><?php esc_html_e('追加示例条目', 'aksrt'); ?></button>
|
||||
<button type="button" class="button button-small" id="aksrt-pmb-fmt-dl"><?php esc_html_e('格式化 JSON', 'aksrt'); ?></button>
|
||||
</div>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('JSON 数组。每项:label / url / type(direct | net_disk | external)/ extract_code。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图库 URL(gallery 类型显示) -->
|
||||
<div class="aksrt-pmb__section" id="aksrt-pmb-gallery-fields" style="display:none;">
|
||||
<div class="aksrt-pmb__section-title"><?php esc_html_e('付费图库', 'aksrt'); ?></div>
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_gallery"><?php esc_html_e('图片 URL 列表', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<textarea id="aksrt_pay_gallery" name="aksrt_pay_gallery" placeholder='[
|
||||
"https://example.com/img1.jpg",
|
||||
"https://example.com/img2.jpg"
|
||||
]'><?php echo esc_textarea($gallery_json); ?></textarea>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('JSON 数组。每项为图片 URL。付费前图片将被模糊遮罩。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 视频 URL(video 类型显示) -->
|
||||
<div class="aksrt-pmb__section" id="aksrt-pmb-video-fields" style="display:none;">
|
||||
<div class="aksrt-pmb__section-title"><?php esc_html_e('付费视频', 'aksrt'); ?></div>
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_video_url"><?php esc_html_e('视频 URL', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<input type="url" id="aksrt_pay_video_url" name="aksrt_pay_video_url" value="<?php echo esc_attr((string) ($meta['video_url'] ?? '')); ?>" placeholder="https://example.com/video.mp4">
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('支持 MP4 直链或视频平台嵌入链接。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 选项覆盖 -->
|
||||
<div class="aksrt-pmb__section">
|
||||
<div class="aksrt-pmb__section-title"><?php esc_html_e('选项覆盖', 'aksrt'); ?></div>
|
||||
<div class="aksrt-pmb__row">
|
||||
<label><?php esc_html_e('单条覆盖', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<div style="display:flex; align-items:center; gap:6px; margin-bottom:6px;">
|
||||
<input type="checkbox" id="aksrt_pay_require_login" name="aksrt_pay_require_login" value="1" <?php checked($meta['require_login']); ?>>
|
||||
<label for="aksrt_pay_require_login" style="font-weight:400;padding:0;"><?php esc_html_e('强制登录后才可访问', 'aksrt'); ?></label>
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:6px;">
|
||||
<input type="checkbox" id="aksrt_pay_require_captcha" name="aksrt_pay_require_captcha" value="1" <?php checked($meta['require_captcha']); ?>>
|
||||
<label for="aksrt_pay_require_captcha" style="font-weight:400;padding:0;"><?php esc_html_e('强制需要人机验证', 'aksrt'); ?></label>
|
||||
</div>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('全局开关在后台统一控制,这里仅作为单条强制覆盖。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- §3.8 电商增强 -->
|
||||
<div class="aksrt-pmb__section">
|
||||
<div class="aksrt-pmb__section-title"><?php esc_html_e('电商增强', 'aksrt'); ?></div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_virtual_sales"><?php esc_html_e('虚拟销量', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<input type="number" id="aksrt_pay_virtual_sales" name="aksrt_pay_virtual_sales" value="<?php echo esc_attr((string) ($meta['virtual_sales'] ?? 0)); ?>" min="0" step="1">
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('在前端展示的销量基础上叠加此数值,用于展示效果。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_limit_over_price"><?php esc_html_e('超额下载补价', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<input type="number" id="aksrt_pay_limit_over_price" name="aksrt_pay_limit_over_price" value="<?php echo esc_attr((string) ($meta['download_limit_over_price'] ?? 0)); ?>" min="0" step="0.01">
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('免费日限额用尽后允许购买的补价(现金/积分随支付方式)。0 = 不允许超额购买。对齐 zibll download_limit_over_price。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label><?php esc_html_e('优惠码', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<div style="display:flex; align-items:center; gap:6px; margin-bottom:8px;">
|
||||
<input type="checkbox" id="aksrt_pay_coupon_s" name="aksrt_pay_coupon_s" value="1" <?php checked(!empty($meta['coupon_s'])); ?>>
|
||||
<label for="aksrt_pay_coupon_s" style="font-weight:400;padding:0;"><?php esc_html_e('允许使用优惠码(全站开关关闭时仍可对本篇启用)', 'aksrt'); ?></label>
|
||||
</div>
|
||||
<textarea id="aksrt_pay_coupon_desc" name="aksrt_pay_coupon_desc" style="min-height:60px;" placeholder="<?php esc_attr_e('本篇优惠码说明,可留空', 'aksrt'); ?>"><?php echo esc_textarea((string) ($meta['coupon_desc'] ?? '')); ?></textarea>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('对齐 zibll coupon_s / coupon_desc。积分支付不使用优惠码。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_rebate_discount"><?php esc_html_e('推广优惠金额', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<input type="number" id="aksrt_pay_rebate_discount" name="aksrt_pay_rebate_discount" value="<?php echo esc_attr((string) ($meta['pay_rebate_discount'] ?? 0)); ?>" min="0" step="0.01">
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('有有效推荐人且开启推广返佣时,现金购买可先减此金额(买家侧推广折扣,对齐 zibll pay_rebate_discount)。0 = 不启用。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_stock"><?php esc_html_e('库存', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<input type="number" id="aksrt_pay_stock" name="aksrt_pay_stock" value="<?php echo esc_attr((string) ($meta['stock'] ?? -1)); ?>" min="-1" step="1">
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('-1 表示无限库存;>0 表示限量,售完即止。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_purchase_limit"><?php esc_html_e('限购数量', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<input type="number" id="aksrt_pay_purchase_limit" name="aksrt_pay_purchase_limit" value="<?php echo esc_attr((string) ($meta['purchase_limit'] ?? 0)); ?>" min="0" step="1">
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('每用户最多可购买次数,0 表示不限购。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_card_keys"><?php esc_html_e('自动发货卡密', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<textarea id="aksrt_pay_card_keys" name="aksrt_pay_card_keys" placeholder="<?php esc_attr_e('每行一个卡密/激活码,购买后自动发放一个', 'aksrt'); ?>"><?php echo esc_textarea(implode("\n", $meta['card_keys'] ?? [])); ?></textarea>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('每行一个卡密,购买后自动消耗一个并发给用户。留空则不自动发货。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label for="aksrt_pay_free_preview"><?php esc_html_e('免费试读段落', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<textarea id="aksrt_pay_free_preview" name="aksrt_pay_free_preview" style="min-height:80px;" placeholder="<?php esc_attr_e('付费内容的免费预览段落(支持 HTML)', 'aksrt'); ?>"><?php echo esc_textarea($meta['free_preview'] ?? ''); ?></textarea>
|
||||
<p class="aksrt-pmb__hint"><?php esc_html_e('未付费用户可见的预览内容,支持基础 HTML。留空则不显示预览。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__row">
|
||||
<label><?php esc_html_e('销量显示', 'aksrt'); ?></label>
|
||||
<div style="display:flex; align-items:center; gap:6px;">
|
||||
<input type="checkbox" id="aksrt_pay_show_sales" name="aksrt_pay_show_sales" value="1" <?php checked($meta['show_sales'] ?? true); ?>>
|
||||
<label for="aksrt_pay_show_sales" style="font-weight:400;padding:0;"><?php esc_html_e('在前端显示销量', 'aksrt'); ?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var typeSelect = document.getElementById('aksrt_pay_type');
|
||||
var fields = document.getElementById('aksrt-pmb-fields');
|
||||
var dlFields = document.getElementById('aksrt-pmb-download-fields');
|
||||
var galleryFields = document.getElementById('aksrt-pmb-gallery-fields');
|
||||
var videoFields = document.getElementById('aksrt-pmb-video-fields');
|
||||
var resourceFields = document.getElementById('aksrt-pmb-resource-fields');
|
||||
|
||||
function updateVisibility() {
|
||||
var t = typeSelect.value;
|
||||
var isPaid = t !== 'no';
|
||||
fields.setAttribute('data-disabled', isPaid ? '0' : '1');
|
||||
|
||||
// 根据付费类型显示/隐藏相关字段
|
||||
if (dlFields) dlFields.style.display = (t === 'download') ? '' : 'none';
|
||||
if (galleryFields) galleryFields.style.display = (t === 'gallery') ? '' : 'none';
|
||||
if (videoFields) videoFields.style.display = (t === 'video') ? '' : 'none';
|
||||
if (resourceFields) resourceFields.style.display = (t === 'download' || t === 'gallery' || t === 'video') ? '' : 'none';
|
||||
}
|
||||
|
||||
if (typeSelect) {
|
||||
typeSelect.addEventListener('change', updateVisibility);
|
||||
updateVisibility();
|
||||
}
|
||||
|
||||
// 下载源 JSON 工具
|
||||
var dlTa = document.getElementById('aksrt_pay_downloads');
|
||||
var dlErr = document.getElementById('aksrt-pmb-dl-err');
|
||||
var addDlBtn = document.getElementById('aksrt-pmb-add-dl');
|
||||
var fmtDlBtn = document.getElementById('aksrt-pmb-fmt-dl');
|
||||
|
||||
if (addDlBtn && dlTa) {
|
||||
addDlBtn.addEventListener('click', function () {
|
||||
var sample = {label: '百度网盘', url: 'https://pan.baidu.com/s/xxx', type: 'net_disk', extract_code: ''};
|
||||
try {
|
||||
var arr = dlTa.value.trim() ? JSON.parse(dlTa.value) : [];
|
||||
arr.push(sample);
|
||||
dlTa.value = JSON.stringify(arr, null, 2);
|
||||
if (dlErr) dlErr.style.display = 'none';
|
||||
} catch (e) {
|
||||
if (dlErr) { dlErr.textContent = 'JSON 解析错误: ' + e.message; dlErr.style.display = 'block'; }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (fmtDlBtn && dlTa) {
|
||||
fmtDlBtn.addEventListener('click', function () {
|
||||
try {
|
||||
var arr = JSON.parse(dlTa.value);
|
||||
dlTa.value = JSON.stringify(arr, null, 2);
|
||||
if (dlErr) dlErr.style.display = 'none';
|
||||
} catch (e) {
|
||||
if (dlErr) { dlErr.textContent = 'JSON 解析错误: ' + e.message; dlErr.style.display = 'block'; }
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function save(int $post_id, \WP_Post $post): void {
|
||||
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
|
||||
if (wp_is_post_revision($post_id)) return;
|
||||
if ($post->post_type !== 'post') return;
|
||||
|
||||
if (empty($_POST['aksrt_pay_meta_nonce'])
|
||||
|| !wp_verify_nonce((string) $_POST['aksrt_pay_meta_nonce'], 'aksrt_pay_meta_save')) {
|
||||
return;
|
||||
}
|
||||
if (!current_user_can('edit_post', $post_id)) return;
|
||||
|
||||
// 付费类型
|
||||
$allowed_types = array_keys(aksrt_get_pay_types());
|
||||
$pay_type = sanitize_key((string) ($_POST['aksrt_pay_type'] ?? 'no'));
|
||||
if (!in_array($pay_type, $allowed_types, true)) {
|
||||
$pay_type = 'no';
|
||||
}
|
||||
|
||||
// 价格字段
|
||||
$pay_modo = sanitize_key((string) ($_POST['aksrt_pay_modo'] ?? '0'));
|
||||
if ($pay_modo !== 'points') {
|
||||
$pay_modo = '0';
|
||||
}
|
||||
$price = max(0, (float) ($_POST['aksrt_pay_price'] ?? 0));
|
||||
$points_price = max(0, (int) ($_POST['aksrt_pay_points_price'] ?? 0));
|
||||
// Keep legacy single-price editors usable: if points field empty and modo points, mirror price.
|
||||
if ($pay_modo === 'points' && $points_price <= 0 && $price > 0) {
|
||||
$points_price = (int) ceil($price);
|
||||
}
|
||||
if ($pay_modo !== 'points' && $price <= 0 && $points_price > 0) {
|
||||
$price = (float) $points_price;
|
||||
}
|
||||
$original_price = max(0, (float) ($_POST['aksrt_pay_original_price'] ?? 0));
|
||||
$vip1_price = max(0, (float) ($_POST['aksrt_pay_vip1_price'] ?? 0));
|
||||
$vip2_price = max(0, (float) ($_POST['aksrt_pay_vip2_price'] ?? 0));
|
||||
$vip1_points = max(0, (int) ($_POST['aksrt_pay_vip1_points'] ?? 0));
|
||||
$vip2_points = max(0, (int) ($_POST['aksrt_pay_vip2_points'] ?? 0));
|
||||
$pay_limit = (int) ($_POST['aksrt_pay_limit'] ?? 0);
|
||||
if (!in_array($pay_limit, [0, 1, 2], true)) {
|
||||
$pay_limit = 0;
|
||||
}
|
||||
|
||||
// 资源信息
|
||||
$version = sanitize_text_field((string) ($_POST['aksrt_pay_version'] ?? ''));
|
||||
$size = sanitize_text_field((string) ($_POST['aksrt_pay_size'] ?? ''));
|
||||
$compat = sanitize_text_field((string) ($_POST['aksrt_pay_compat'] ?? ''));
|
||||
$license = sanitize_text_field((string) ($_POST['aksrt_pay_license'] ?? ''));
|
||||
$demo_url = esc_url_raw((string) ($_POST['aksrt_pay_demo_url'] ?? ''));
|
||||
$video_url = esc_url_raw((string) ($_POST['aksrt_pay_video_url'] ?? ''));
|
||||
|
||||
// 布尔字段
|
||||
$require_login = !empty($_POST['aksrt_pay_require_login']);
|
||||
$require_captcha = !empty($_POST['aksrt_pay_require_captcha']);
|
||||
|
||||
// JSON 字段
|
||||
$downloads = $this->parse_json_array($_POST['aksrt_pay_downloads'] ?? '');
|
||||
$attributes = $this->parse_json_array($_POST['aksrt_pay_attributes'] ?? '');
|
||||
$gallery = $this->parse_json_array($_POST['aksrt_pay_gallery'] ?? '');
|
||||
|
||||
// 清洗下载源
|
||||
if ($downloads !== null && class_exists('Setup') && method_exists('Setup', 'sanitize_downloads_meta')) {
|
||||
$downloads = Setup::sanitize_downloads_meta($downloads);
|
||||
}
|
||||
|
||||
// 保存统一元数据
|
||||
$pay_meta = DomainModel::sanitize_pay_meta([
|
||||
'pay_type' => $pay_type,
|
||||
'pay_modo' => $pay_modo,
|
||||
'price' => $price,
|
||||
'points_price' => $points_price,
|
||||
'original_price' => $original_price,
|
||||
'vip1_price' => $vip1_price,
|
||||
'vip2_price' => $vip2_price,
|
||||
'vip1_points' => $vip1_points,
|
||||
'vip2_points' => $vip2_points,
|
||||
'pay_limit' => $pay_limit,
|
||||
'version' => $version,
|
||||
'size' => $size,
|
||||
'compat' => $compat,
|
||||
'license' => $license,
|
||||
'demo_url' => $demo_url,
|
||||
'video_url' => $video_url,
|
||||
'downloads' => $downloads ?? [],
|
||||
'attributes' => $attributes ?? [],
|
||||
'gallery' => $gallery ?? [],
|
||||
'require_login' => $require_login,
|
||||
'require_captcha' => $require_captcha,
|
||||
// §3.8 电商增强字段
|
||||
'virtual_sales' => $_POST['aksrt_pay_virtual_sales'] ?? 0,
|
||||
'stock' => $_POST['aksrt_pay_stock'] ?? -1,
|
||||
'purchase_limit' => $_POST['aksrt_pay_purchase_limit'] ?? 0,
|
||||
'card_keys' => isset($_POST['aksrt_pay_card_keys']) ? array_filter(array_map('trim', explode("\n", (string) $_POST['aksrt_pay_card_keys']))) : [],
|
||||
'free_preview' => $_POST['aksrt_pay_free_preview'] ?? '',
|
||||
'show_sales' => !empty($_POST['aksrt_pay_show_sales']),
|
||||
'download_limit_over_price' => max(0, (float) ($_POST['aksrt_pay_limit_over_price'] ?? 0)),
|
||||
'coupon_s' => !empty($_POST['aksrt_pay_coupon_s']),
|
||||
'coupon_desc' => (string) ($_POST['aksrt_pay_coupon_desc'] ?? ''),
|
||||
'pay_rebate_discount' => max(0, (float) ($_POST['aksrt_pay_rebate_discount'] ?? 0)),
|
||||
]);
|
||||
|
||||
update_post_meta($post_id, DomainModel::PAY_META_KEY, $pay_meta);
|
||||
|
||||
// 同步标量字段用于查询(模仿 zibll 的 zibpay_type 同步)
|
||||
update_post_meta($post_id, DomainModel::PAY_TYPE_KEY, (string) $pay_meta['pay_type']);
|
||||
$query_price = ($pay_meta['pay_modo'] === 'points')
|
||||
? (float) $pay_meta['points_price']
|
||||
: (float) $pay_meta['price'];
|
||||
update_post_meta($post_id, DomainModel::PAY_PRICE_KEY, $query_price);
|
||||
update_post_meta($post_id, '_aksrt_pay_modo', (string) $pay_meta['pay_modo']);
|
||||
update_post_meta($post_id, '_aksrt_pay_limit', (int) $pay_meta['pay_limit']);
|
||||
|
||||
// 清理旧的 _aksrt_is_resource 标记
|
||||
update_post_meta(
|
||||
$post_id,
|
||||
DomainModel::RESOURCE_MARKER_KEY,
|
||||
DomainModel::is_resource_pay_type((string) $pay_meta['pay_type']) ? 1 : 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全解析 JSON 数组。
|
||||
*/
|
||||
private function parse_json_array(string $raw): ?array {
|
||||
$raw = trim($raw);
|
||||
if ($raw === '') return [];
|
||||
|
||||
$decoded = json_decode($raw, true);
|
||||
return is_array($decoded) ? $decoded : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user