进行了一些小修复
This commit is contained in:
+78
-37
@@ -63,34 +63,22 @@ class DomainModel {
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function default_post_meta(): array {
|
||||
return [
|
||||
'cover_images' => '',
|
||||
'custom_excerpt' => '',
|
||||
'header_image_mode' => '',
|
||||
'show_reading_progress' => '',
|
||||
'enable_lightbox' => '',
|
||||
'show_related' => '',
|
||||
'related_count' => 0,
|
||||
'enable_like' => '',
|
||||
'enable_favorite' => '',
|
||||
'enable_share' => '',
|
||||
'enable_print' => '',
|
||||
'enable_pdf' => '',
|
||||
'copyright_text' => '',
|
||||
'is_featured' => false,
|
||||
// §3.8 文章扩展字段
|
||||
'thumbnail_url' => '',
|
||||
'featured_slide' => '',
|
||||
'featured_video' => '',
|
||||
// Episode list: [ ['url' => string, 'title' => string], ... ]
|
||||
'featured_video_episode' => [],
|
||||
'subtitle' => '',
|
||||
'views' => 0,
|
||||
'likes' => 0,
|
||||
'maxheight' => 0,
|
||||
];
|
||||
}
|
||||
public static function default_post_meta(): array {
|
||||
return [
|
||||
'thumbnail_url' => '',
|
||||
'cover_image' => '',
|
||||
'featured_slide' => '',
|
||||
'featured_video' => '',
|
||||
'featured_video_title' => '',
|
||||
'featured_video_episode' => [],
|
||||
'subtitle' => '',
|
||||
'views' => 0,
|
||||
'like' => 0,
|
||||
'show_layout' => 'false',
|
||||
'no_article-navs' => false,
|
||||
'article_maxheight_xz' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
@@ -112,23 +100,37 @@ class DomainModel {
|
||||
return [
|
||||
'pay_type' => self::PAY_TYPE_NO,
|
||||
'pay_modo' => '0', // '0' cash | 'points' credits (legacy)
|
||||
'order_expired_s' => false,
|
||||
'order_expired_time' => 0,
|
||||
'order_expired_unit' => 'day',
|
||||
'price' => '0.00',
|
||||
'points_price' => 0,
|
||||
'original_price' => '0.00',
|
||||
'promotion_tag' => '',
|
||||
'vip1_price' => '0.00',
|
||||
'vip2_price' => '0.00',
|
||||
'vip1_points' => 0,
|
||||
'vip2_points' => 0,
|
||||
'pay_limit' => 0, // 0 all | 1 VIP1+ | 2 VIP2 only (legacy)
|
||||
'pay_count' => 0,
|
||||
'version' => '',
|
||||
'size' => '',
|
||||
'license' => '',
|
||||
'compat' => '',
|
||||
'demo_url' => '',
|
||||
'video_url' => '',
|
||||
'video_pic' => '',
|
||||
'video_title' => '',
|
||||
'video_episode' => [],
|
||||
'video_scale_height' => 0,
|
||||
'downloads' => [],
|
||||
'attributes' => [],
|
||||
'gallery' => [],
|
||||
'gallery_show' => 1,
|
||||
'pay_title' => '',
|
||||
'pay_doc' => '',
|
||||
'pay_details' => '',
|
||||
'pay_extra_hide' => '',
|
||||
'require_login' => false,
|
||||
'require_captcha' => false,
|
||||
// §3.8 电商增强字段
|
||||
@@ -150,15 +152,14 @@ class DomainModel {
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function pay_type_labels(): array {
|
||||
return [
|
||||
self::PAY_TYPE_NO => '不付费',
|
||||
self::PAY_TYPE_READING => '付费阅读',
|
||||
self::PAY_TYPE_DOWNLOAD => '付费下载',
|
||||
self::PAY_TYPE_GALLERY => '付费图库',
|
||||
self::PAY_TYPE_VIDEO => '付费视频',
|
||||
self::PAY_TYPE_VIP => 'VIP 专属',
|
||||
];
|
||||
public static function pay_type_labels(): array {
|
||||
return [
|
||||
self::PAY_TYPE_NO => '关闭',
|
||||
self::PAY_TYPE_READING => '付费阅读',
|
||||
self::PAY_TYPE_DOWNLOAD => '付费下载',
|
||||
self::PAY_TYPE_GALLERY => '付费图片',
|
||||
self::PAY_TYPE_VIDEO => '付费视频',
|
||||
];
|
||||
}
|
||||
|
||||
public static function normalize_pay_type(string $pay_type): string {
|
||||
@@ -315,24 +316,39 @@ class DomainModel {
|
||||
$meta['pay_type'] = self::normalize_pay_type((string) ($raw['pay_type'] ?? $meta['pay_type']));
|
||||
$pay_modo = sanitize_key((string) ($raw['pay_modo'] ?? '0'));
|
||||
$meta['pay_modo'] = $pay_modo === 'points' ? 'points' : '0';
|
||||
$meta['order_expired_s'] = !empty($raw['order_expired_s']);
|
||||
$meta['order_expired_time'] = max(0, (int) ($raw['order_expired_time'] ?? 0));
|
||||
$order_expired_unit = sanitize_key((string) ($raw['order_expired_unit'] ?? 'day'));
|
||||
$meta['order_expired_unit'] = in_array($order_expired_unit, ['day', 'hour', 'minute'], true) ? $order_expired_unit : 'day';
|
||||
$meta['price'] = self::sanitize_balance_decimal($raw['price'] ?? '0');
|
||||
$meta['points_price'] = self::sanitize_points($raw['points_price'] ?? 0);
|
||||
$meta['original_price'] = self::sanitize_balance_decimal($raw['original_price'] ?? '0');
|
||||
$meta['promotion_tag'] = wp_kses_post((string) ($raw['promotion_tag'] ?? ''));
|
||||
$meta['vip1_price'] = self::sanitize_balance_decimal($raw['vip1_price'] ?? '0');
|
||||
$meta['vip2_price'] = self::sanitize_balance_decimal($raw['vip2_price'] ?? '0');
|
||||
$meta['vip1_points'] = self::sanitize_points($raw['vip1_points'] ?? 0);
|
||||
$meta['vip2_points'] = self::sanitize_points($raw['vip2_points'] ?? 0);
|
||||
$pay_limit = (int) ($raw['pay_limit'] ?? 0);
|
||||
$meta['pay_limit'] = in_array($pay_limit, [0, 1, 2], true) ? $pay_limit : 0;
|
||||
$meta['pay_count'] = (int) ($raw['pay_count'] ?? 0);
|
||||
$meta['version'] = sanitize_text_field((string) ($raw['version'] ?? ''));
|
||||
$meta['size'] = sanitize_text_field((string) ($raw['size'] ?? ''));
|
||||
$meta['license'] = sanitize_text_field((string) ($raw['license'] ?? ''));
|
||||
$meta['compat'] = sanitize_text_field((string) ($raw['compat'] ?? ''));
|
||||
$meta['demo_url'] = esc_url_raw((string) ($raw['demo_url'] ?? ''));
|
||||
$meta['video_url'] = esc_url_raw((string) ($raw['video_url'] ?? ''));
|
||||
$meta['video_pic'] = esc_url_raw((string) ($raw['video_pic'] ?? ''));
|
||||
$meta['video_title'] = sanitize_text_field((string) ($raw['video_title'] ?? ''));
|
||||
$meta['video_episode'] = self::sanitize_video_episodes($raw['video_episode'] ?? []);
|
||||
$meta['video_scale_height'] = max(0, min(200, (int) ($raw['video_scale_height'] ?? 0)));
|
||||
$meta['downloads'] = self::sanitize_downloads($raw['downloads'] ?? []);
|
||||
$meta['attributes'] = self::sanitize_attributes($raw['attributes'] ?? []);
|
||||
$meta['gallery'] = self::sanitize_gallery($raw['gallery'] ?? []);
|
||||
$meta['gallery_show'] = max(0, (int) ($raw['gallery_show'] ?? 1));
|
||||
$meta['pay_title'] = sanitize_text_field((string) ($raw['pay_title'] ?? ''));
|
||||
$meta['pay_doc'] = wp_kses_post((string) ($raw['pay_doc'] ?? ''));
|
||||
$meta['pay_details'] = wp_kses_post((string) ($raw['pay_details'] ?? ''));
|
||||
$meta['pay_extra_hide'] = wp_kses_post((string) ($raw['pay_extra_hide'] ?? ''));
|
||||
$meta['require_login'] = !empty($raw['require_login']);
|
||||
$meta['require_captcha'] = !empty($raw['require_captcha']);
|
||||
|
||||
@@ -353,6 +369,31 @@ class DomainModel {
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Raw video episode rows.
|
||||
* @return array<int, array{title:string,url:string}>
|
||||
*/
|
||||
private static function sanitize_video_episodes($value): array {
|
||||
if (!is_array($value)) {
|
||||
return [];
|
||||
}
|
||||
$episodes = [];
|
||||
foreach ($value as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$url = esc_url_raw((string) ($row['url'] ?? ''));
|
||||
if ($url === '') {
|
||||
continue;
|
||||
}
|
||||
$episodes[] = [
|
||||
'title' => sanitize_text_field((string) ($row['title'] ?? '')),
|
||||
'url' => $url,
|
||||
];
|
||||
}
|
||||
return array_slice($episodes, 0, 50);
|
||||
}
|
||||
|
||||
private static function sanitize_balance_decimal($value): string {
|
||||
if (!is_string($value)) {
|
||||
return '0.00';
|
||||
|
||||
+157
-156
@@ -12,172 +12,173 @@ defined('ABSPATH') || exit;
|
||||
*/
|
||||
trait FrontSubmitFeatured {
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function save_featured_meta(int $post_id, array $data, int $user_id): void {
|
||||
if (empty($data['featured_data']) && empty($data['featured_type'])) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function save_featured_meta(int $post_id, array $data, int $user_id): void {
|
||||
if (empty($data['featured_data'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$featured = $data['featured_data'] ?? null;
|
||||
if (is_string($featured)) {
|
||||
$decoded = json_decode(wp_unslash($featured), true);
|
||||
$featured = is_array($decoded) ? $decoded : null;
|
||||
}
|
||||
if (!is_array($featured)) {
|
||||
// Flat payload: featured_type + fields.
|
||||
$featured = [
|
||||
'type' => sanitize_key((string) ($data['featured_type'] ?? '')),
|
||||
'data' => $data['featured_payload'] ?? $data,
|
||||
];
|
||||
}
|
||||
$featured = $data['featured_data'] ?? null;
|
||||
if (is_string($featured)) {
|
||||
$decoded = json_decode(wp_unslash($featured), true);
|
||||
$featured = is_array($decoded) ? $decoded : null;
|
||||
}
|
||||
if (!is_array($featured)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = sanitize_key((string) ($featured['type'] ?? ''));
|
||||
$payload = is_array($featured['data'] ?? null) ? $featured['data'] : $featured;
|
||||
$type = sanitize_key((string) ($featured['type'] ?? ''));
|
||||
$payload = is_array($featured['data'] ?? null) ? $featured['data'] : $featured;
|
||||
|
||||
$post_meta = get_post_meta($post_id, DomainModel::POST_META_KEY, true);
|
||||
if (!is_array($post_meta)) {
|
||||
$post_meta = DomainModel::default_post_meta();
|
||||
}
|
||||
$post_meta = get_post_meta($post_id, DomainModel::POST_META_KEY, true);
|
||||
if (!is_array($post_meta)) {
|
||||
$post_meta = DomainModel::default_post_meta();
|
||||
}
|
||||
$post_meta = array_intersect_key($post_meta, DomainModel::default_post_meta());
|
||||
|
||||
switch ($type) {
|
||||
case 'video':
|
||||
if (!$this->user_can_cap('new_post_video_cover', $user_id) && !$this->user_can_cap('new_post_image_cover', $user_id)) {
|
||||
return;
|
||||
}
|
||||
$video = esc_url_raw((string) ($payload['url'] ?? $payload['video'] ?? ''));
|
||||
$pic = esc_url_raw((string) ($payload['pic'] ?? $payload['poster'] ?? $payload['cover'] ?? ''));
|
||||
if ($pic !== '' && !$this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
$pic = '';
|
||||
}
|
||||
$post_meta['featured_video'] = $video;
|
||||
$post_meta['featured_slide'] = '';
|
||||
if ($pic !== '' && $this->can_use_front_submit_image_url($pic, $user_id)) {
|
||||
$post_meta['thumbnail_url'] = $pic;
|
||||
$post_meta['cover_images'] = $pic;
|
||||
$this->maybe_set_thumbnail_url($post_id, $pic);
|
||||
}
|
||||
break;
|
||||
switch ($type) {
|
||||
case 'video':
|
||||
if (!$this->user_can_cap('new_post_video_cover', $user_id) && !$this->user_can_cap('new_post_image_cover', $user_id)) {
|
||||
return;
|
||||
}
|
||||
$video = esc_url_raw((string) ($payload['url'] ?? ''));
|
||||
$pic = esc_url_raw((string) ($payload['pic'] ?? ''));
|
||||
if ($pic !== '' && !$this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
$pic = '';
|
||||
}
|
||||
$post_meta['featured_video'] = $video;
|
||||
$post_meta['featured_slide'] = '';
|
||||
if ($pic !== '' && $this->can_use_front_submit_image_url($pic, $user_id)) {
|
||||
$post_meta['thumbnail_url'] = $pic;
|
||||
$post_meta['cover_image'] = $pic;
|
||||
$this->maybe_set_thumbnail_url($post_id, $pic);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'slide':
|
||||
if (!$this->user_can_cap('new_post_slide_cover', $user_id) && !$this->user_can_cap('new_post_image_cover', $user_id)) {
|
||||
return;
|
||||
}
|
||||
$ids = $payload['ids'] ?? $payload['slides'] ?? $payload['images'] ?? [];
|
||||
$urls = [];
|
||||
if (is_string($ids)) {
|
||||
$ids = array_filter(array_map('trim', explode(',', $ids)));
|
||||
}
|
||||
if (is_array($ids)) {
|
||||
foreach ($ids as $item) {
|
||||
if (is_array($item)) {
|
||||
$u = esc_url_raw((string) ($item['url'] ?? ''));
|
||||
$attachment_id = absint($item['id'] ?? 0);
|
||||
if ($attachment_id > 0 && $this->can_use_front_submit_image($attachment_id, $user_id)) {
|
||||
$src = wp_get_attachment_image_url($attachment_id, 'full');
|
||||
if ($src) {
|
||||
$urls[] = $src;
|
||||
}
|
||||
} elseif ($u !== '' && $this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
$urls[] = $u;
|
||||
}
|
||||
} elseif (is_numeric($item)) {
|
||||
$attachment_id = (int) $item;
|
||||
$src = $this->can_use_front_submit_image($attachment_id, $user_id)
|
||||
? wp_get_attachment_image_url($attachment_id, 'full')
|
||||
: false;
|
||||
if ($src) {
|
||||
$urls[] = $src;
|
||||
}
|
||||
} else {
|
||||
$u = esc_url_raw((string) $item);
|
||||
if ($u !== '' && $this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
$urls[] = $u;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$post_meta['featured_slide'] = implode(',', $urls);
|
||||
$post_meta['featured_video'] = '';
|
||||
if (!empty($urls[0]) && $this->can_use_front_submit_image_url((string) $urls[0], $user_id)) {
|
||||
$post_meta['thumbnail_url'] = $urls[0];
|
||||
$post_meta['cover_images'] = $urls[0];
|
||||
$this->maybe_set_thumbnail_url($post_id, $urls[0]);
|
||||
}
|
||||
break;
|
||||
case 'slide':
|
||||
if (!$this->user_can_cap('new_post_slide_cover', $user_id) && !$this->user_can_cap('new_post_image_cover', $user_id)) {
|
||||
return;
|
||||
}
|
||||
$ids = array_is_list($payload) ? $payload : [];
|
||||
$attachment_ids = [];
|
||||
$urls = [];
|
||||
if (is_string($ids)) {
|
||||
$ids = array_filter(array_map('trim', explode(',', $ids)));
|
||||
}
|
||||
if (is_array($ids)) {
|
||||
foreach ($ids as $item) {
|
||||
if (is_array($item)) {
|
||||
$u = esc_url_raw((string) ($item['url'] ?? ''));
|
||||
$attachment_id = absint($item['id'] ?? 0);
|
||||
if ($attachment_id > 0 && $this->can_use_front_submit_image($attachment_id, $user_id)) {
|
||||
$src = wp_get_attachment_image_url($attachment_id, 'full');
|
||||
if ($src) {
|
||||
$attachment_ids[] = $attachment_id;
|
||||
$urls[] = $src;
|
||||
}
|
||||
} elseif ($u !== '' && $this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
$urls[] = $u;
|
||||
}
|
||||
} elseif (is_numeric($item)) {
|
||||
$attachment_id = (int) $item;
|
||||
$src = $this->can_use_front_submit_image($attachment_id, $user_id)
|
||||
? wp_get_attachment_image_url($attachment_id, 'full')
|
||||
: false;
|
||||
if ($src) {
|
||||
$attachment_ids[] = $attachment_id;
|
||||
$urls[] = $src;
|
||||
}
|
||||
} else {
|
||||
$u = esc_url_raw((string) $item);
|
||||
if ($u !== '' && $this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
$urls[] = $u;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$post_meta['featured_slide'] = implode(',', array_values(array_unique($attachment_ids)));
|
||||
$post_meta['featured_video'] = '';
|
||||
if (!empty($urls[0]) && $this->can_use_front_submit_image_url((string) $urls[0], $user_id)) {
|
||||
$post_meta['thumbnail_url'] = $urls[0];
|
||||
$post_meta['cover_image'] = $urls[0];
|
||||
$this->maybe_set_thumbnail_url($post_id, $urls[0]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'image':
|
||||
if (!$this->user_can_cap('new_post_image_cover', $user_id)) {
|
||||
return;
|
||||
}
|
||||
$url = esc_url_raw((string) ($payload['url'] ?? $payload['image'] ?? ''));
|
||||
$id = absint($payload['id'] ?? 0);
|
||||
if ($id > 0 && !$this->can_use_front_submit_image($id, $user_id)) {
|
||||
return;
|
||||
}
|
||||
if ($url === '' && $id > 0) {
|
||||
$src = wp_get_attachment_image_url($id, 'full');
|
||||
if ($src) {
|
||||
$url = $src;
|
||||
}
|
||||
}
|
||||
$post_meta['featured_video'] = '';
|
||||
$post_meta['featured_slide'] = '';
|
||||
if ($url !== '' && $this->can_use_front_submit_image_url($url, $user_id)) {
|
||||
$post_meta['thumbnail_url'] = $url;
|
||||
$post_meta['cover_images'] = $url;
|
||||
if ($id > 0) {
|
||||
set_post_thumbnail($post_id, $id);
|
||||
} elseif ($this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
$this->maybe_set_thumbnail_url($post_id, $url);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'image':
|
||||
if (!$this->user_can_cap('new_post_image_cover', $user_id)) {
|
||||
return;
|
||||
}
|
||||
$url = esc_url_raw((string) ($payload['url'] ?? ''));
|
||||
$id = absint($payload['id'] ?? 0);
|
||||
if ($id > 0 && !$this->can_use_front_submit_image($id, $user_id)) {
|
||||
return;
|
||||
}
|
||||
if ($url === '' && $id > 0) {
|
||||
$src = wp_get_attachment_image_url($id, 'full');
|
||||
if ($src) {
|
||||
$url = $src;
|
||||
}
|
||||
}
|
||||
$post_meta['featured_video'] = '';
|
||||
$post_meta['featured_slide'] = '';
|
||||
if ($url !== '' && $this->can_use_front_submit_image_url($url, $user_id)) {
|
||||
$post_meta['thumbnail_url'] = $url;
|
||||
$post_meta['cover_image'] = $url;
|
||||
if ($id > 0) {
|
||||
set_post_thumbnail($post_id, $id);
|
||||
} elseif ($this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
$this->maybe_set_thumbnail_url($post_id, $url);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'close':
|
||||
case 'none':
|
||||
case 'clear':
|
||||
$post_meta['featured_video'] = '';
|
||||
$post_meta['featured_slide'] = '';
|
||||
$post_meta['thumbnail_url'] = '';
|
||||
$post_meta['cover_images'] = '';
|
||||
delete_post_thumbnail($post_id);
|
||||
break;
|
||||
case 'close':
|
||||
case 'none':
|
||||
case 'clear':
|
||||
$post_meta['featured_video'] = '';
|
||||
$post_meta['featured_slide'] = '';
|
||||
$post_meta['thumbnail_url'] = '';
|
||||
$post_meta['cover_image'] = '';
|
||||
delete_post_thumbnail($post_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
update_post_meta($post_id, DomainModel::POST_META_KEY, $post_meta);
|
||||
}
|
||||
unset($post_meta['views'], $post_meta['like']);
|
||||
update_post_meta($post_id, DomainModel::POST_META_KEY, $post_meta);
|
||||
}
|
||||
|
||||
private function maybe_set_thumbnail_url(int $post_id, string $url): void {
|
||||
if ($url === '') {
|
||||
return;
|
||||
}
|
||||
// Prefer attachment if URL is local media.
|
||||
$attach_id = attachment_url_to_postid($url);
|
||||
if ($attach_id > 0) {
|
||||
set_post_thumbnail($post_id, $attach_id);
|
||||
return;
|
||||
}
|
||||
$this->set_thumbnail_from_url($post_id, $url);
|
||||
}
|
||||
private function maybe_set_thumbnail_url(int $post_id, string $url): void {
|
||||
if ($url === '') {
|
||||
return;
|
||||
}
|
||||
// Prefer attachment if URL is local media.
|
||||
$attach_id = attachment_url_to_postid($url);
|
||||
if ($attach_id > 0) {
|
||||
set_post_thumbnail($post_id, $attach_id);
|
||||
return;
|
||||
}
|
||||
$this->set_thumbnail_from_url($post_id, $url);
|
||||
}
|
||||
|
||||
private function can_use_front_submit_image(int $attachment_id, int $user_id): bool {
|
||||
if ($attachment_id <= 0 || !$this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
return false;
|
||||
}
|
||||
$attachment = get_post($attachment_id);
|
||||
return $attachment instanceof \WP_Post
|
||||
&& $attachment->post_type === 'attachment'
|
||||
&& (int) $attachment->post_author === $user_id
|
||||
&& wp_attachment_is_image($attachment_id);
|
||||
}
|
||||
private function can_use_front_submit_image(int $attachment_id, int $user_id): bool {
|
||||
if ($attachment_id <= 0 || !$this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
return false;
|
||||
}
|
||||
$attachment = get_post($attachment_id);
|
||||
return $attachment instanceof \WP_Post
|
||||
&& $attachment->post_type === 'attachment'
|
||||
&& (int) $attachment->post_author === $user_id
|
||||
&& wp_attachment_is_image($attachment_id);
|
||||
}
|
||||
|
||||
private function can_use_front_submit_image_url(string $url, int $user_id): bool {
|
||||
$attachment_id = attachment_url_to_postid($url);
|
||||
return $attachment_id > 0 && $this->can_use_front_submit_image($attachment_id, $user_id);
|
||||
}
|
||||
private function can_use_front_submit_image_url(string $url, int $user_id): bool {
|
||||
$attachment_id = attachment_url_to_postid($url);
|
||||
return $attachment_id > 0 && $this->can_use_front_submit_image($attachment_id, $user_id);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-28
@@ -23,13 +23,13 @@ trait FrontSubmitPay {
|
||||
}
|
||||
|
||||
// Only touch pay meta when the form sent pay fields.
|
||||
$has_pay_payload = isset($data['aksrt_pay']) || isset($data['pay_enabled']) || isset($data['aksrtpay_s']) || isset($data['pay_type']);
|
||||
$has_pay_payload = isset($data['aksrt_pay']) || isset($data['pay_enabled']);
|
||||
if (!$has_pay_payload) {
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
$raw = is_array($data['aksrt_pay'] ?? null) ? $data['aksrt_pay'] : [];
|
||||
$enabled = !empty($data['pay_enabled']) || !empty($data['aksrtpay_s']);
|
||||
$enabled = !empty($data['pay_enabled']);
|
||||
|
||||
$existing = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post_id) : DomainModel::default_pay_meta();
|
||||
$meta = is_array($existing) ? $existing : DomainModel::default_pay_meta();
|
||||
@@ -40,22 +40,15 @@ trait FrontSubmitPay {
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
$pay_modo = sanitize_key((string) ($raw['pay_modo'] ?? $data['pay_modo'] ?? '0'));
|
||||
$pay_modo = sanitize_key((string) ($raw['pay_modo'] ?? '0'));
|
||||
$pay_modo = $pay_modo === 'points' ? 'points' : '0';
|
||||
|
||||
$pay_type = sanitize_key((string) ($raw['pay_type'] ?? $data['pay_type'] ?? DomainModel::PAY_TYPE_READING));
|
||||
if ($pay_type === '1' || $pay_type === 'reading' || $pay_type === 'payshow') {
|
||||
$pay_type = DomainModel::normalize_pay_type((string) ($raw['pay_type'] ?? DomainModel::PAY_TYPE_READING));
|
||||
if ($pay_type === DomainModel::PAY_TYPE_NO) {
|
||||
$pay_type = DomainModel::PAY_TYPE_READING;
|
||||
} elseif ($pay_type === '2' || $pay_type === 'download') {
|
||||
$pay_type = DomainModel::PAY_TYPE_DOWNLOAD;
|
||||
} else {
|
||||
$pay_type = DomainModel::normalize_pay_type($pay_type);
|
||||
if ($pay_type === DomainModel::PAY_TYPE_NO) {
|
||||
$pay_type = DomainModel::PAY_TYPE_READING;
|
||||
}
|
||||
}
|
||||
|
||||
$downloads = $this->normalize_front_downloads($raw['downloads'] ?? $data['pay_download'] ?? [], $user_id);
|
||||
$downloads = $this->normalize_front_downloads($raw['downloads'] ?? [], $user_id);
|
||||
if (!empty($downloads) && $this->user_can_cap('new_post_pay_download', $user_id)) {
|
||||
$pay_type = DomainModel::PAY_TYPE_DOWNLOAD;
|
||||
$meta['downloads'] = $downloads;
|
||||
@@ -93,10 +86,10 @@ trait FrontSubmitPay {
|
||||
|
||||
if ($vip_manual) {
|
||||
$vip1 = $vip1_enabled
|
||||
? $this->parse_front_points($raw['vip1_points'] ?? $raw['vip_1_points'] ?? null)
|
||||
? $this->parse_front_points($raw['vip1_points'] ?? null)
|
||||
: 0;
|
||||
$vip2 = $vip2_enabled
|
||||
? $this->parse_front_points($raw['vip2_points'] ?? $raw['vip_2_points'] ?? null)
|
||||
? $this->parse_front_points($raw['vip2_points'] ?? null)
|
||||
: 0;
|
||||
if (($vip1_enabled && $vip1 === null) || ($vip2_enabled && $vip2 === null)) {
|
||||
return ['success' => false, 'message' => __('请设置正确的会员积分价格', 'aksrt')];
|
||||
@@ -121,7 +114,7 @@ trait FrontSubmitPay {
|
||||
$meta['vip1_price'] = '0.00';
|
||||
$meta['vip2_price'] = '0.00';
|
||||
} else {
|
||||
$price_minor = $this->parse_front_cash_minor($raw['price'] ?? $raw['pay_price'] ?? $data['pay_price'] ?? null);
|
||||
$price_minor = $this->parse_front_cash_minor($raw['price'] ?? null);
|
||||
$min_minor = $this->parse_front_cash_minor($settings['pay_price_min'] ?? '0') ?? 0;
|
||||
$max_minor = $this->parse_front_cash_minor($settings['pay_price_max'] ?? '0') ?? 0;
|
||||
if ($price_minor === null) {
|
||||
@@ -139,10 +132,10 @@ trait FrontSubmitPay {
|
||||
|
||||
if ($vip_manual) {
|
||||
$vip1_minor = $vip1_enabled
|
||||
? $this->parse_front_cash_minor($raw['vip1_price'] ?? $raw['vip_1_price'] ?? null)
|
||||
? $this->parse_front_cash_minor($raw['vip1_price'] ?? null)
|
||||
: 0;
|
||||
$vip2_minor = $vip2_enabled
|
||||
? $this->parse_front_cash_minor($raw['vip2_price'] ?? $raw['vip_2_price'] ?? null)
|
||||
? $this->parse_front_cash_minor($raw['vip2_price'] ?? null)
|
||||
: 0;
|
||||
if (($vip1_enabled && $vip1_minor === null) || ($vip2_enabled && $vip2_minor === null)) {
|
||||
return ['success' => false, 'message' => __('请设置正确的会员价格', 'aksrt')];
|
||||
@@ -168,13 +161,8 @@ trait FrontSubmitPay {
|
||||
$meta['vip2_points'] = 0;
|
||||
}
|
||||
|
||||
$pay_limit = (int) ($raw['pay_limit'] ?? $data['pay_limit'] ?? 0);
|
||||
$pay_limit = (int) ($raw['pay_limit'] ?? 0);
|
||||
$meta['pay_limit'] = in_array($pay_limit, [0, 1, 2], true) ? $pay_limit : 0;
|
||||
$meta['require_login'] = !empty($raw['require_login']) || !empty($data['require_login']);
|
||||
$meta['coupon_s'] = !empty($raw['coupon_s']) || !empty($data['coupon_s']);
|
||||
if (isset($raw['coupon_desc']) || isset($data['coupon_desc'])) {
|
||||
$meta['coupon_desc'] = sanitize_textarea_field((string) ($raw['coupon_desc'] ?? $data['coupon_desc'] ?? ''));
|
||||
}
|
||||
|
||||
$this->write_pay_meta($post_id, $meta);
|
||||
return ['success' => true];
|
||||
@@ -227,8 +215,7 @@ trait FrontSubmitPay {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
// Accept legacy-ish keys + AKSRT keys.
|
||||
$url = (string) ($row['url'] ?? $row['link'] ?? '');
|
||||
$url = (string) ($row['url'] ?? '');
|
||||
$url = esc_url_raw(trim($url));
|
||||
if ($url === '') {
|
||||
continue;
|
||||
@@ -238,10 +225,10 @@ trait FrontSubmitPay {
|
||||
continue;
|
||||
}
|
||||
$out[] = [
|
||||
'label' => sanitize_text_field((string) ($row['label'] ?? $row['name'] ?? '')),
|
||||
'label' => sanitize_text_field((string) ($row['label'] ?? '')),
|
||||
'url' => $url,
|
||||
'type' => $attachment_id > 0 ? 'direct' : 'external',
|
||||
'extract_code' => sanitize_text_field((string) ($row['extract_code'] ?? $row['copy_val'] ?? $row['more'] ?? '')),
|
||||
'extract_code' => sanitize_text_field((string) ($row['extract_code'] ?? '')),
|
||||
];
|
||||
}
|
||||
return DomainModel::sanitize_downloads($out);
|
||||
|
||||
@@ -181,16 +181,6 @@ trait FrontSubmitSave {
|
||||
|
||||
$this->save_featured_meta((int) $in_id, $data, $user_id);
|
||||
|
||||
// Legacy thumbnail URL fallback.
|
||||
$thumbnail_url = esc_url_raw((string) ($data['thumbnail'] ?? ''));
|
||||
if ($thumbnail_url !== '' && empty($data['featured_data']) && $this->user_can_cap('upload_submit_image', $user_id)) {
|
||||
$attachment_id = attachment_url_to_postid($thumbnail_url);
|
||||
$attachment = $attachment_id > 0 ? get_post($attachment_id) : null;
|
||||
if ($attachment instanceof \WP_Post && (int) $attachment->post_author === $user_id && wp_attachment_is_image($attachment_id)) {
|
||||
set_post_thumbnail((int) $in_id, $attachment_id);
|
||||
}
|
||||
}
|
||||
|
||||
$post = get_post((int) $in_id);
|
||||
if ($post instanceof \WP_Post) {
|
||||
do_action($is_edit ? 'aksrt_new_edit_posts' : 'aksrt_new_add_posts', $post);
|
||||
|
||||
+304
-788
File diff suppressed because it is too large
Load Diff
+231
-518
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Article paid-content fields shared by the block and classic editors.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
@@ -9,550 +11,261 @@ defined('ABSPATH') || exit;
|
||||
|
||||
class ResourceMetaBox {
|
||||
|
||||
private static $instance = null;
|
||||
private static $instance = null;
|
||||
|
||||
private function __construct() {}
|
||||
private function __construct() {}
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) self::$instance = new self();
|
||||
return self::$instance;
|
||||
}
|
||||
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 init(): void {
|
||||
add_action('add_meta_boxes', [$this, 'register_meta_box']);
|
||||
add_action('save_post_post', [$this, 'save'], 10, 2);
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
}
|
||||
|
||||
public function register_meta_box(): void {
|
||||
add_meta_box(
|
||||
'aksrt_pay_meta',
|
||||
__('付费内容设置', 'aksrt'),
|
||||
[$this, 'render'],
|
||||
'post',
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
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');
|
||||
public function enqueue_assets(string $hook): void {
|
||||
if (!in_array($hook, ['post.php', 'post-new.php'], true)) {
|
||||
return;
|
||||
}
|
||||
$screen = function_exists('get_current_screen') ? get_current_screen() : null;
|
||||
if (!$screen || $screen->post_type !== 'post') {
|
||||
return;
|
||||
}
|
||||
|
||||
$meta = function_exists('aksrt_get_pay_meta')
|
||||
? aksrt_get_pay_meta($post)
|
||||
: DomainModel::default_pay_meta();
|
||||
wp_enqueue_media();
|
||||
$css = AKSRT_DIR . '/assets/css/admin/pay-meta-box.css';
|
||||
$js = AKSRT_DIR . '/assets/js/admin-pay-meta-box.js';
|
||||
if (is_file($css)) {
|
||||
wp_enqueue_style('aksrt-pay-meta-box', AKSRT_URI . '/assets/css/admin/pay-meta-box.css', [], (string) filemtime($css));
|
||||
}
|
||||
if (is_file($js)) {
|
||||
wp_enqueue_script('aksrt-pay-meta-box', AKSRT_URI . '/assets/js/admin-pay-meta-box.js', [], (string) filemtime($js), true);
|
||||
}
|
||||
}
|
||||
|
||||
$pay_types = aksrt_get_pay_types();
|
||||
$is_paid = ($meta['pay_type'] ?? 'no') !== 'no';
|
||||
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();
|
||||
$gallery_ids = [];
|
||||
foreach ((array) ($meta['gallery'] ?? []) as $url) {
|
||||
$id = attachment_url_to_postid((string) $url);
|
||||
if ($id > 0) {
|
||||
$gallery_ids[] = $id;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="aksrt-pay-meta" data-aksrt-pay-meta>
|
||||
<?php $this->radio('pay_type', __('付费模式', 'aksrt'), (string) ($meta['pay_type'] ?? 'no'), aksrt_get_pay_types()); ?>
|
||||
<div data-pay-enabled>
|
||||
<?php
|
||||
$this->radio('pay_limit', __('购买权限', 'aksrt'), (string) ($meta['pay_limit'] ?? 0), [
|
||||
'0' => __('所有人可购买', 'aksrt'),
|
||||
'1' => __('VIP1 及以上会员可购买', 'aksrt'),
|
||||
'2' => __('仅 VIP2 可购买', 'aksrt'),
|
||||
], __('设置会员专享资源时,请同时配置对应的会员价格。', 'aksrt'));
|
||||
$this->radio('pay_modo', __('支付类型', 'aksrt'), (string) ($meta['pay_modo'] ?? '0'), [
|
||||
'0' => __('普通商品(金钱购买)', 'aksrt'),
|
||||
'points' => __('积分商品(积分购买,依赖于用户积分功能)', 'aksrt'),
|
||||
]);
|
||||
?>
|
||||
|
||||
$downloads_json = !empty($meta['downloads'])
|
||||
? wp_json_encode($meta['downloads'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||||
: '';
|
||||
<div class="aksrt-pay-meta__field">
|
||||
<div class="aksrt-pay-meta__title"><?php esc_html_e('订单时效', 'aksrt'); ?></div>
|
||||
<div class="aksrt-pay-meta__control">
|
||||
<label><input type="checkbox" name="aksrt_pay[order_expired_s]" value="1" <?php checked(!empty($meta['order_expired_s'])); ?> data-order-expired> <?php esc_html_e('启用订单有效期', 'aksrt'); ?></label>
|
||||
<p class="description"><?php esc_html_e('购买后仅在有效期内可查看内容,超时后需再次购买。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aksrt-pay-meta__field compact" data-order-expired-fields>
|
||||
<div class="aksrt-pay-meta__title"><?php esc_html_e('有效时间', 'aksrt'); ?></div>
|
||||
<div class="aksrt-pay-meta__control aksrt-pay-meta__inline">
|
||||
<input type="number" min="0" step="1" name="aksrt_pay[order_expired_time]" value="<?php echo esc_attr((string) ($meta['order_expired_time'] ?? 0)); ?>">
|
||||
<select name="aksrt_pay[order_expired_unit]">
|
||||
<option value="day" <?php selected((string) ($meta['order_expired_unit'] ?? 'day'), 'day'); ?>><?php esc_html_e('天', 'aksrt'); ?></option>
|
||||
<option value="hour" <?php selected((string) ($meta['order_expired_unit'] ?? 'day'), 'hour'); ?>><?php esc_html_e('小时', 'aksrt'); ?></option>
|
||||
<option value="minute" <?php selected((string) ($meta['order_expired_unit'] ?? 'day'), 'minute'); ?>><?php esc_html_e('分钟', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
$attributes_json = !empty($meta['attributes'])
|
||||
? wp_json_encode($meta['attributes'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||||
: '';
|
||||
<div data-pay-modo="points">
|
||||
<?php $this->number('points_price', __('积分售价', 'aksrt'), (string) ($meta['points_price'] ?? 0), '1', __('积分', 'aksrt')); ?>
|
||||
<?php $this->number('vip1_points', __('VIP1 积分售价', 'aksrt'), (string) ($meta['vip1_points'] ?? 0), '1', __('积分', 'aksrt'), __('填 0 则为 VIP1 免费', 'aksrt')); ?>
|
||||
<?php $this->number('vip2_points', __('VIP2 积分售价', 'aksrt'), (string) ($meta['vip2_points'] ?? 0), '1', __('积分', 'aksrt'), __('会员价格不能高于售价,填 0 则为 VIP2 免费。', 'aksrt')); ?>
|
||||
</div>
|
||||
<div data-pay-modo="0">
|
||||
<?php $this->number('price', __('执行价', 'aksrt'), (string) ($meta['price'] ?? '0.00'), '0.01', '', __('填 0 则为免费', 'aksrt')); ?>
|
||||
<?php $this->number('original_price', __('原价', 'aksrt'), (string) ($meta['original_price'] ?? '0.00'), '0.01', '', __('显示在执行价格前面,并划掉。', 'aksrt')); ?>
|
||||
<?php $this->textarea('promotion_tag', __('促销标签', 'aksrt'), (string) ($meta['promotion_tag'] ?? ''), 1); ?>
|
||||
<?php $this->number('vip1_price', __('VIP1 价格', 'aksrt'), (string) ($meta['vip1_price'] ?? '0.00'), '0.01', '', __('填 0 则为 VIP1 免费', 'aksrt')); ?>
|
||||
<?php $this->number('vip2_price', __('VIP2 价格', 'aksrt'), (string) ($meta['vip2_price'] ?? '0.00'), '0.01', '', __('会员价格不能高于执行价,填 0 则为 VIP2 免费。', 'aksrt')); ?>
|
||||
</div>
|
||||
|
||||
$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>
|
||||
<?php $this->number('pay_count', __('销量浮动', 'aksrt'), (string) ($meta['pay_count'] ?? 0), '1', '', __('为真实销量增加或减少的数量。', 'aksrt'), false); ?>
|
||||
|
||||
<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 data-pay-type="gallery">
|
||||
<div class="aksrt-pay-meta__field">
|
||||
<div class="aksrt-pay-meta__title"><?php esc_html_e('付费图片', 'aksrt'); ?></div>
|
||||
<div class="aksrt-pay-meta__control">
|
||||
<input type="hidden" name="aksrt_pay[gallery_ids]" value="<?php echo esc_attr(implode(',', $gallery_ids)); ?>" data-gallery-input>
|
||||
<div class="aksrt-pay-meta__gallery" data-gallery-preview></div>
|
||||
<div class="aksrt-pay-meta__buttons"><button type="button" class="button" data-gallery-select><?php esc_html_e('新增图片', 'aksrt'); ?></button><button type="button" class="button-link-delete" data-gallery-clear><?php esc_html_e('清空图片', 'aksrt'); ?></button></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php $this->number('gallery_show', __('免费查看', 'aksrt'), (string) ($meta['gallery_show'] ?? 1), '1', __('张', 'aksrt'), __('不能大于付费图片数量,否则无效。', 'aksrt')); ?>
|
||||
</div>
|
||||
|
||||
<p class="aksrt-pmb__hint" id="aksrt-pmb-reading-hint" hidden>
|
||||
<?php esc_html_e('付费阅读价格只作用于正文中的“付费可见”片段;未插入片段时,正文保持公开。', 'aksrt'); ?>
|
||||
</p>
|
||||
<div data-pay-type="video">
|
||||
<?php $this->upload('video_url', __('视频资源', 'aksrt'), (string) ($meta['video_url'] ?? ''), 'video', __('输入视频地址或选择、上传本地视频。', 'aksrt')); ?>
|
||||
<?php $this->upload('video_pic', __('视频封面(可选)', 'aksrt'), (string) ($meta['video_pic'] ?? ''), 'image'); ?>
|
||||
<?php $this->text('video_title', __('本集标题', 'aksrt'), (string) ($meta['video_title'] ?? ''), __('如需添加剧集则需填写此处', 'aksrt'), __('第1集', 'aksrt')); ?>
|
||||
<?php $this->group('video_episode', __('更多剧集', 'aksrt'), (array) ($meta['video_episode'] ?? []), 'video_row', __('添加剧集', 'aksrt'), __('为付费视频添加更多剧集。', 'aksrt')); ?>
|
||||
<?php $this->number('video_scale_height', __('固定长宽比例', 'aksrt'), (string) ($meta['video_scale_height'] ?? 0), '5', '%', __('为 0 则不固定长宽比例。', 'aksrt')); ?>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-pmb__fields" id="aksrt-pmb-fields" data-disabled="<?php echo $is_paid ? '0' : '1'; ?>">
|
||||
<div data-pay-type="download">
|
||||
<?php $this->group('downloads', __('资源下载', 'aksrt'), (array) ($meta['downloads'] ?? []), 'download_row', __('添加资源', 'aksrt')); ?>
|
||||
<?php $this->group('attributes', __('资源属性', 'aksrt'), (array) ($meta['attributes'] ?? []), 'attribute_row', __('添加属性', 'aksrt')); ?>
|
||||
<?php $this->text('demo_url', __('演示地址', 'aksrt'), (string) ($meta['demo_url'] ?? ''), '', 'https://', 'url'); ?>
|
||||
</div>
|
||||
|
||||
<!-- 基础价格 -->
|
||||
<div class="aksrt-pmb__section">
|
||||
<div class="aksrt-pmb__section-title"><?php esc_html_e('价格设置', 'aksrt'); ?></div>
|
||||
<?php $this->text('pay_title', __('商品标题', 'aksrt'), (string) ($meta['pay_title'] ?? ''), __('可选,如需单独显示付费内容标题请填写。', 'aksrt')); ?>
|
||||
<?php $this->textarea('pay_doc', __('商品简介', 'aksrt'), (string) ($meta['pay_doc'] ?? ''), 1, __('可选,如需单独显示付费内容介绍请填写。', 'aksrt')); ?>
|
||||
<?php $this->textarea('pay_details', __('更多详情', 'aksrt'), (string) ($meta['pay_details'] ?? ''), 3, __('显示在付费内容卡片下方,支持安全 HTML。', 'aksrt')); ?>
|
||||
<?php $this->textarea('pay_extra_hide', __('额外隐藏内容', 'aksrt'), (string) ($meta['pay_extra_hide'] ?? ''), 3, __('付费后显示的额外隐藏内容,支持安全 HTML。', 'aksrt')); ?>
|
||||
|
||||
<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('对齐 legacy pay_modo:现金价或积分价结算。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aksrt-pay-meta__notice"><strong><?php esc_html_e('付费阅读', 'aksrt'); ?></strong> <?php esc_html_e('需配合古腾堡“隐藏内容块”或经典编辑器短代码使用:', 'aksrt'); ?> <code>[hidecontent type="payshow"]</code> <?php esc_html_e('隐藏内容', 'aksrt'); ?> <code>[/hidecontent]</code></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
<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>
|
||||
/** @param array<string, string> $options */
|
||||
private function radio(string $id, string $title, string $value, array $options, string $description = ''): void {
|
||||
?>
|
||||
<fieldset class="aksrt-pay-meta__field"><legend class="aksrt-pay-meta__title"><?php echo esc_html($title); ?></legend><div class="aksrt-pay-meta__control aksrt-pay-meta__radios">
|
||||
<?php foreach ($options as $option => $label) : ?><label><input type="radio" name="aksrt_pay[<?php echo esc_attr($id); ?>]" value="<?php echo esc_attr((string) $option); ?>" <?php checked($value, (string) $option); ?>> <?php echo esc_html($label); ?></label><?php endforeach; ?>
|
||||
<?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?>
|
||||
</div></fieldset>
|
||||
<?php
|
||||
}
|
||||
|
||||
<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>
|
||||
private function number(string $id, string $title, string $value, string $step, string $unit = '', string $description = '', bool $non_negative = true): void {
|
||||
?>
|
||||
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><label for="aksrt-pay-<?php echo esc_attr($id); ?>"><?php echo esc_html($title); ?></label></div><div class="aksrt-pay-meta__control"><div class="aksrt-pay-meta__number"><input id="aksrt-pay-<?php echo esc_attr($id); ?>" type="number" <?php echo $non_negative ? 'min="0"' : ''; ?> step="<?php echo esc_attr($step); ?>" name="aksrt_pay[<?php echo esc_attr($id); ?>]" value="<?php echo esc_attr($value); ?>"><?php if ($unit !== '') : ?><span><?php echo esc_html($unit); ?></span><?php endif; ?></div><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?></div></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
<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>
|
||||
private function text(string $id, string $title, string $value, string $description = '', string $placeholder = '', string $type = 'text'): void {
|
||||
?>
|
||||
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><label for="aksrt-pay-<?php echo esc_attr($id); ?>"><?php echo esc_html($title); ?></label></div><div class="aksrt-pay-meta__control"><input id="aksrt-pay-<?php echo esc_attr($id); ?>" type="<?php echo esc_attr($type); ?>" name="aksrt_pay[<?php echo esc_attr($id); ?>]" value="<?php echo esc_attr($value); ?>" placeholder="<?php echo esc_attr($placeholder); ?>"><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?></div></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
<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('对齐 legacy pay_limit:限制购买人群(会员专属资源)。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
private function textarea(string $id, string $title, string $value, int $rows, string $description = ''): void {
|
||||
?>
|
||||
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><label for="aksrt-pay-<?php echo esc_attr($id); ?>"><?php echo esc_html($title); ?></label></div><div class="aksrt-pay-meta__control"><textarea id="aksrt-pay-<?php echo esc_attr($id); ?>" rows="<?php echo esc_attr((string) $rows); ?>" name="aksrt_pay[<?php echo esc_attr($id); ?>]"><?php echo esc_textarea($value); ?></textarea><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?></div></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
<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>
|
||||
private function upload(string $id, string $title, string $value, string $library, string $description = ''): void {
|
||||
?>
|
||||
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><label for="aksrt-pay-<?php echo esc_attr($id); ?>"><?php echo esc_html($title); ?></label></div><div class="aksrt-pay-meta__control"><div class="aksrt-pay-meta__upload"><input id="aksrt-pay-<?php echo esc_attr($id); ?>" type="url" name="aksrt_pay[<?php echo esc_attr($id); ?>]" value="<?php echo esc_attr($value); ?>" data-media-input><button type="button" class="button" data-media-select data-library="<?php echo esc_attr($library); ?>"><?php esc_html_e('选择', 'aksrt'); ?></button><button type="button" class="button-link-delete" data-media-clear><?php esc_html_e('移除', 'aksrt'); ?></button></div><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?></div></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
<!-- 资源信息(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>
|
||||
/** @param array<int, array<string, mixed>> $rows */
|
||||
private function group(string $id, string $title, array $rows, string $renderer, string $button, string $description = ''): void {
|
||||
?>
|
||||
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><?php echo esc_html($title); ?></div><div class="aksrt-pay-meta__control"><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?><div data-group-list="<?php echo esc_attr($id); ?>"><?php foreach ($rows as $index => $row) { $this->{$renderer}((int) $index, (array) $row); } ?></div><button type="button" class="button" data-group-add="<?php echo esc_attr($id); ?>"><?php echo esc_html($button); ?></button><template data-group-template="<?php echo esc_attr($id); ?>"><?php $this->{$renderer}('__INDEX__', []); ?></template></div></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
<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>
|
||||
/** @param int|string $index */
|
||||
private function video_row($index, array $row): void {
|
||||
?>
|
||||
<div class="aksrt-pay-meta__group" data-group-row><input type="text" name="aksrt_pay[video_episode][<?php echo esc_attr((string) $index); ?>][title]" value="<?php echo esc_attr((string) ($row['title'] ?? '')); ?>" placeholder="<?php esc_attr_e('剧集标题', 'aksrt'); ?>"><div class="aksrt-pay-meta__upload"><input type="url" name="aksrt_pay[video_episode][<?php echo esc_attr((string) $index); ?>][url]" value="<?php echo esc_attr((string) ($row['url'] ?? '')); ?>" placeholder="<?php esc_attr_e('选择视频或填写视频地址', 'aksrt'); ?>" data-media-input><button type="button" class="button" data-media-select data-library="video"><?php esc_html_e('选择', 'aksrt'); ?></button></div><button type="button" class="button-link-delete" data-group-remove><?php esc_html_e('移除', 'aksrt'); ?></button></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
<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>
|
||||
/** @param int|string $index */
|
||||
private function download_row($index, array $row): void {
|
||||
?>
|
||||
<div class="aksrt-pay-meta__group" data-group-row><div class="aksrt-pay-meta__upload"><input type="url" name="aksrt_pay[downloads][<?php echo esc_attr((string) $index); ?>][url]" value="<?php echo esc_attr((string) ($row['url'] ?? '')); ?>" placeholder="<?php esc_attr_e('上传文件或输入下载地址', 'aksrt'); ?>" data-media-input><button type="button" class="button" data-media-select data-library=""><?php esc_html_e('选择', 'aksrt'); ?></button></div><input type="text" name="aksrt_pay[downloads][<?php echo esc_attr((string) $index); ?>][label]" value="<?php echo esc_attr((string) ($row['label'] ?? '')); ?>" placeholder="<?php esc_attr_e('按钮文案', 'aksrt'); ?>"><textarea rows="1" name="aksrt_pay[downloads][<?php echo esc_attr((string) $index); ?>][extract_code]" placeholder="<?php esc_attr_e('资源备注、提取码或解压密码', 'aksrt'); ?>"><?php echo esc_textarea((string) ($row['extract_code'] ?? '')); ?></textarea><button type="button" class="button-link-delete" data-group-remove><?php esc_html_e('移除', 'aksrt'); ?></button></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
<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>
|
||||
/** @param int|string $index */
|
||||
private function attribute_row($index, array $row): void {
|
||||
?>
|
||||
<div class="aksrt-pay-meta__group aksrt-pay-meta__group--inline" data-group-row><input type="text" name="aksrt_pay[attributes][<?php echo esc_attr((string) $index); ?>][key]" value="<?php echo esc_attr((string) ($row['key'] ?? '')); ?>" placeholder="<?php esc_attr_e('属性名称', 'aksrt'); ?>"><input type="text" name="aksrt_pay[attributes][<?php echo esc_attr((string) $index); ?>][value]" value="<?php echo esc_attr((string) ($row['value'] ?? '')); ?>" placeholder="<?php esc_attr_e('属性内容', 'aksrt'); ?>"><button type="button" class="button-link-delete" data-group-remove><?php esc_html_e('移除', 'aksrt'); ?></button></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
<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>
|
||||
public function save(int $post_id, \WP_Post $post): void {
|
||||
if ($post->post_type !== 'post' || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || wp_is_post_revision($post_id) || !current_user_can('edit_post', $post_id)) {
|
||||
return;
|
||||
}
|
||||
if (empty($_POST['aksrt_pay_meta_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash((string) $_POST['aksrt_pay_meta_nonce'])), 'aksrt_pay_meta_save')) {
|
||||
return;
|
||||
}
|
||||
|
||||
<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>
|
||||
$raw = isset($_POST['aksrt_pay']) && is_array($_POST['aksrt_pay']) ? wp_unslash($_POST['aksrt_pay']) : [];
|
||||
$gallery = [];
|
||||
$ids = array_values(array_filter(array_map('absint', preg_split('/[\s,]+/', (string) ($raw['gallery_ids'] ?? '')) ?: [])));
|
||||
foreach (array_slice($ids, 0, 100) as $attachment_id) {
|
||||
$url = wp_get_attachment_image_url($attachment_id, 'full');
|
||||
if ($url) {
|
||||
$gallery[] = $url;
|
||||
}
|
||||
}
|
||||
|
||||
<!-- 自定义属性 -->
|
||||
<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>
|
||||
$meta = DomainModel::sanitize_pay_meta([
|
||||
'pay_type' => $raw['pay_type'] ?? 'no',
|
||||
'pay_limit' => $raw['pay_limit'] ?? 0,
|
||||
'pay_modo' => $raw['pay_modo'] ?? '0',
|
||||
'order_expired_s' => !empty($raw['order_expired_s']),
|
||||
'order_expired_time' => $raw['order_expired_time'] ?? 0,
|
||||
'order_expired_unit' => $raw['order_expired_unit'] ?? 'day',
|
||||
'price' => (string) ($raw['price'] ?? '0'),
|
||||
'points_price' => $raw['points_price'] ?? 0,
|
||||
'original_price' => (string) ($raw['original_price'] ?? '0'),
|
||||
'promotion_tag' => (string) ($raw['promotion_tag'] ?? ''),
|
||||
'vip1_price' => (string) ($raw['vip1_price'] ?? '0'),
|
||||
'vip2_price' => (string) ($raw['vip2_price'] ?? '0'),
|
||||
'vip1_points' => $raw['vip1_points'] ?? 0,
|
||||
'vip2_points' => $raw['vip2_points'] ?? 0,
|
||||
'pay_count' => $raw['pay_count'] ?? 0,
|
||||
'gallery' => $gallery,
|
||||
'gallery_show' => $raw['gallery_show'] ?? 1,
|
||||
'video_url' => (string) ($raw['video_url'] ?? ''),
|
||||
'video_pic' => (string) ($raw['video_pic'] ?? ''),
|
||||
'video_title' => (string) ($raw['video_title'] ?? ''),
|
||||
'video_episode' => $raw['video_episode'] ?? [],
|
||||
'video_scale_height' => $raw['video_scale_height'] ?? 0,
|
||||
'downloads' => $raw['downloads'] ?? [],
|
||||
'attributes' => $raw['attributes'] ?? [],
|
||||
'demo_url' => (string) ($raw['demo_url'] ?? ''),
|
||||
'pay_title' => (string) ($raw['pay_title'] ?? ''),
|
||||
'pay_doc' => (string) ($raw['pay_doc'] ?? ''),
|
||||
'pay_details' => (string) ($raw['pay_details'] ?? ''),
|
||||
'pay_extra_hide' => (string) ($raw['pay_extra_hide'] ?? ''),
|
||||
]);
|
||||
|
||||
<!-- 下载源(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 = 不允许超额购买。对齐 legacy 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('对齐 legacy 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('有有效推荐人且开启推广返佣时,现金购买可先减此金额(买家侧推广折扣,对齐 legacy 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');
|
||||
|
||||
var readingHint = document.getElementById('aksrt-pmb-reading-hint');
|
||||
if (readingHint) readingHint.hidden = t !== 'reading';
|
||||
|
||||
// 根据付费类型显示/隐藏相关字段
|
||||
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 = trim((string) wp_unslash($_POST['aksrt_pay_price'] ?? '0'));
|
||||
$points_price = max(0, (int) ($_POST['aksrt_pay_points_price'] ?? 0));
|
||||
$original_price = trim((string) wp_unslash($_POST['aksrt_pay_original_price'] ?? '0'));
|
||||
$vip1_price = trim((string) wp_unslash($_POST['aksrt_pay_vip1_price'] ?? '0'));
|
||||
$vip2_price = trim((string) wp_unslash($_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('AKSRT\\Core\\Setup') && method_exists('AKSRT\\Core\\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' => trim((string) wp_unslash($_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' => trim((string) wp_unslash($_POST['aksrt_pay_rebate_discount'] ?? '0')),
|
||||
]);
|
||||
|
||||
update_post_meta($post_id, DomainModel::PAY_META_KEY, $pay_meta);
|
||||
|
||||
// 同步标量字段用于查询(模仿 legacy 的 aksrtpay_type 同步)
|
||||
update_post_meta($post_id, DomainModel::PAY_TYPE_KEY, (string) $pay_meta['pay_type']);
|
||||
$query_price = ($pay_meta['pay_modo'] === 'points')
|
||||
? (int) $pay_meta['points_price']
|
||||
: (string) $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;
|
||||
}
|
||||
update_post_meta($post_id, DomainModel::PAY_META_KEY, $meta);
|
||||
update_post_meta($post_id, DomainModel::PAY_TYPE_KEY, (string) $meta['pay_type']);
|
||||
update_post_meta($post_id, DomainModel::PAY_PRICE_KEY, $meta['pay_modo'] === 'points' ? (int) $meta['points_price'] : (string) $meta['price']);
|
||||
update_post_meta($post_id, '_aksrt_pay_modo', (string) $meta['pay_modo']);
|
||||
update_post_meta($post_id, '_aksrt_pay_limit', (int) $meta['pay_limit']);
|
||||
update_post_meta($post_id, DomainModel::RESOURCE_MARKER_KEY, DomainModel::is_resource_pay_type((string) $meta['pay_type']) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
+40
-18
@@ -38,7 +38,7 @@ class SEOMetaBox {
|
||||
foreach (['post', 'page'] as $screen) {
|
||||
add_meta_box(
|
||||
'aksrt_seo_meta',
|
||||
__('独立 SEO', 'aksrt'),
|
||||
__('独立SEO', 'aksrt'),
|
||||
[$this, 'render_meta_box'],
|
||||
$screen,
|
||||
'normal',
|
||||
@@ -57,20 +57,35 @@ class SEOMetaBox {
|
||||
$ai_btn = $this->render_ai_button('post', (int) $post->ID);
|
||||
$summary_btn = $this->render_summary_button((int) $post->ID);
|
||||
?>
|
||||
<div class="aksrt-seo-meta-box">
|
||||
<p>
|
||||
<label for="aksrt_seo_title"><strong><?php esc_html_e('SEO 标题', 'aksrt'); ?></strong></label>
|
||||
<input type="text" class="widefat" id="aksrt_seo_title" name="aksrt_seo_title" value="<?php echo esc_attr($title); ?>" />
|
||||
</p>
|
||||
<p>
|
||||
<label for="aksrt_seo_keywords"><strong><?php esc_html_e('SEO 关键词', 'aksrt'); ?></strong></label>
|
||||
<input type="text" class="widefat" id="aksrt_seo_keywords" name="aksrt_seo_keywords" value="<?php echo esc_attr($keywords); ?>" placeholder="<?php esc_attr_e('多个关键词用英文逗号分隔', 'aksrt'); ?>" />
|
||||
</p>
|
||||
<p>
|
||||
<label for="aksrt_seo_description"><strong><?php esc_html_e('SEO 描述', 'aksrt'); ?></strong></label>
|
||||
<textarea class="widefat" rows="3" id="aksrt_seo_description" name="aksrt_seo_description"><?php echo esc_textarea($description); ?></textarea>
|
||||
<span class="description"><?php esc_html_e('建议 50–150 字。', 'aksrt'); ?><?php echo $ai_btn; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
|
||||
</p>
|
||||
<div class="aksrt-seo-meta-box">
|
||||
<div class="notice notice-warning inline"><p><?php esc_html_e('系统默认调用标题、分类标签、描述等自动生成SEO内容,一般无需单独设置SEO内容;如需覆盖,请在下方自定义。', 'aksrt'); ?></p></div>
|
||||
<div class="aksrt-seo-preview">
|
||||
<strong><?php esc_html_e('SEO预览', 'aksrt'); ?></strong>
|
||||
<div class="aksrt-seo-preview__title"><?php echo esc_html($title !== '' ? $title : get_the_title($post)); ?></div>
|
||||
<div class="aksrt-seo-preview__url"><?php echo esc_html(get_permalink($post)); ?></div>
|
||||
<div class="aksrt-seo-preview__description"><?php echo esc_html($description !== '' ? $description : wp_trim_words(wp_strip_all_tags((string) $post->post_content), 38, '…')); ?></div>
|
||||
</div>
|
||||
<p>
|
||||
<label for="aksrt_seo_title"><strong><?php esc_html_e('自定义SEO标题', 'aksrt'); ?></strong></label>
|
||||
<input type="text" class="widefat" id="aksrt_seo_title" name="aksrt_seo_title" value="<?php echo esc_attr($title); ?>" />
|
||||
<span class="description"><?php esc_html_e('Title 一般建议15到30个字符', 'aksrt'); ?></span>
|
||||
</p>
|
||||
<p>
|
||||
<label for="aksrt_seo_keywords"><strong><?php esc_html_e('自定义SEO关键词', 'aksrt'); ?></strong></label>
|
||||
<input type="text" class="widefat" id="aksrt_seo_keywords" name="aksrt_seo_keywords" value="<?php echo esc_attr($keywords); ?>" placeholder="<?php esc_attr_e('多个关键词用英文逗号分隔', 'aksrt'); ?>" />
|
||||
<span class="description"><?php esc_html_e('Keywords 每个关键词用逗号隔开', 'aksrt'); ?></span>
|
||||
</p>
|
||||
<p>
|
||||
<label for="aksrt_seo_description"><strong><?php esc_html_e('自定义SEO描述', 'aksrt'); ?></strong></label>
|
||||
<textarea class="widefat" rows="3" id="aksrt_seo_description" name="aksrt_seo_description"><?php echo esc_textarea($description); ?></textarea>
|
||||
<span class="description"><?php esc_html_e('Description 一般建议50到150个字符', 'aksrt'); ?><?php echo $ai_btn; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
|
||||
</p>
|
||||
<details class="aksrt-seo-advice"><summary><span class="dashicons dashicons-star-filled"></span> <?php esc_html_e('SEO优化建议', 'aksrt'); ?></summary><div>
|
||||
<p><strong><?php esc_html_e('SEO标题:', 'aksrt'); ?></strong><?php esc_html_e('标题应紧扣主要内容,重要关键词靠前,避免关键词堆砌。', 'aksrt'); ?></p>
|
||||
<p><strong><?php esc_html_e('SEO关键词:', 'aksrt'); ?></strong><?php esc_html_e('一般建议4到8个,并与网站定位及正文保持一致。', 'aksrt'); ?></p>
|
||||
<p><strong><?php esc_html_e('SEO描述:', 'aksrt'); ?></strong><?php esc_html_e('使用一段通顺、有吸引力的文字精炼概括页面内容。', 'aksrt'); ?></p>
|
||||
<p class="description"><?php esc_html_e('优化建议来自互联网,仅供参考。', 'aksrt'); ?></p>
|
||||
</div></details>
|
||||
<?php if ($summary_btn !== '') : ?>
|
||||
<p class="aksrt-ai-summary-row">
|
||||
<span class="description"><?php esc_html_e('写作辅助:根据正文生成摘要并填入「摘要」字段(不自动保存)。', 'aksrt'); ?></span>
|
||||
@@ -220,15 +235,22 @@ class SEOMetaBox {
|
||||
],
|
||||
]);
|
||||
|
||||
wp_add_inline_style('wp-admin', '
|
||||
wp_add_inline_style('wp-admin', '
|
||||
.aksrt-ai-seo-button{display:inline-block;margin-left:8px;vertical-align:middle}
|
||||
.aksrt-ai-seo-generate,.aksrt-ai-summary-generate{cursor:pointer}
|
||||
.aksrt-ai-seo-generate.is-loading,.aksrt-ai-summary-generate.is-loading{opacity:.6;pointer-events:none}
|
||||
.aksrt-ai-seo-msg{display:block;margin-top:6px;font-size:12px}
|
||||
.aksrt-ai-seo-msg.is-error{color:#b32d2e}
|
||||
.aksrt-ai-seo-msg.is-ok{color:#007017}
|
||||
.aksrt-ai-summary-row{margin-top:8px;padding-top:8px;border-top:1px solid #eee}
|
||||
');
|
||||
.aksrt-ai-summary-row{margin-top:8px;padding-top:8px;border-top:1px solid #eee}
|
||||
.aksrt-seo-preview{margin:12px 0;padding:14px;border:1px solid #e5e5e5;border-radius:4px;background:#fff}
|
||||
.aksrt-seo-preview__title{margin-top:8px;color:#1a0dab;font-size:18px;line-height:1.3}
|
||||
.aksrt-seo-preview__url{margin:4px 0;color:#188038;font-size:13px;word-break:break-all}
|
||||
.aksrt-seo-preview__description{color:#4d5156;line-height:1.5}
|
||||
.aksrt-seo-advice{margin-top:12px;border:1px solid #e5e5e5;border-radius:4px}
|
||||
.aksrt-seo-advice summary{padding:10px 12px;cursor:pointer;font-weight:600}
|
||||
.aksrt-seo-advice>div{padding:0 12px 10px}
|
||||
');
|
||||
}
|
||||
|
||||
private function render_ai_button(string $type, int $id): string {
|
||||
|
||||
Reference in New Issue
Block a user