526 lines
21 KiB
PHP
526 lines
21 KiB
PHP
<?php
|
||
/**
|
||
* 前端页面编辑(后台「扩展&增强 → 系统工具」的 admin_frontend_set)。
|
||
*
|
||
* 对齐参考主题 zib-frontend-set.php:wp_footer 直出 #modal_admin_set 弹窗
|
||
* (bootstrap modal + wp-ajax-submit 走 vendored main.js 的 aksrt_ajax 契约),
|
||
* 管理员在前台快速修改当前文章/页面的参数或分类术语的名称与描述。
|
||
*
|
||
* @package AKSRT
|
||
*/
|
||
|
||
namespace AKSRT\Frontend;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
class PageAdminSet {
|
||
|
||
private static $instance = null;
|
||
|
||
public static function get_instance(): self {
|
||
if (null === self::$instance) {
|
||
self::$instance = new self();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
public function init(): void {
|
||
add_filter('aksrt_float_right_buttons', [$this, 'float_button'], 10, 2);
|
||
add_action('wp_footer', [$this, 'render_modal'], 10);
|
||
add_action('wp_footer', [self::class, 'enqueue_hint_script'], 9);
|
||
add_action('wp_ajax_aksrt_frontend_set_save', [$this, 'ajax_save']);
|
||
}
|
||
|
||
/**
|
||
* 提示横条的自动淡出(对齐参考主题 js/page-edit.js)。
|
||
*
|
||
* 参考主题在 2 秒后把 .admin-set-info 淡出 1 秒,避免提示常驻遮挡悬浮栏。
|
||
* 挂在 wp_footer 早期:wp_print_footer_scripts 在此之前执行,脚本仍能入队。
|
||
*/
|
||
public static function enqueue_hint_script(): void {
|
||
if (!self::enabled()) {
|
||
return;
|
||
}
|
||
$path = get_template_directory() . '/assets/js/front-page-admin-set.js';
|
||
if (!file_exists($path)) {
|
||
return;
|
||
}
|
||
wp_enqueue_script(
|
||
'aksrt-front-page-admin-set',
|
||
get_template_directory_uri() . '/assets/js/front-page-admin-set.js',
|
||
['jquery'],
|
||
(string) filemtime($path),
|
||
true
|
||
);
|
||
}
|
||
|
||
/** 开启条件:设置开关 + 管理员 + 单页/术语页 + 有效对象(排除投稿与导航页模板)。 */
|
||
public static function enabled(): bool {
|
||
if (!class_exists('\AKSRT\Settings\ExtensionRuntime') || !is_user_logged_in()) {
|
||
return false;
|
||
}
|
||
$settings = \AKSRT\Settings\ExtensionRuntime::get_system_tools_settings();
|
||
if (empty($settings['admin_frontend_set']) || !current_user_can('manage_options')) {
|
||
return false;
|
||
}
|
||
if (!get_queried_object_id() || (!is_single() && !is_page() && !is_category() && !is_tag() && !is_tax())) {
|
||
return false;
|
||
}
|
||
if (is_page_template('templates/pages/page-submit.php') || is_page_template('templates/pages/page-posts-navs.php')) {
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 悬浮按钮:追加到右侧悬浮按钮栏(对齐参考主题 zib_admin_frontend_set_botton)。
|
||
*
|
||
* 参考主题对移动端与桌面端共用同一按钮,$is_mobile 仅用于保持 filter 签名。
|
||
*/
|
||
public function float_button($buttons, $is_mobile = null) {
|
||
if (!self::enabled()) {
|
||
return $buttons;
|
||
}
|
||
|
||
$button = '<a href="javascript:;" data-toggle="modal" data-target="#modal_admin_set"'
|
||
. ' title="' . esc_attr__('设置页面参数', 'aksrt') . '" class="admin-set-page float-btn">'
|
||
. '<i class="fa fas fa-cog fa-spin" aria-hidden="true"></i>'
|
||
. '<div class="abs-right badg c-red px12 admin-set-info" style="width:187px;">' . esc_html__('在此编辑此页面参数', 'aksrt') . '</div></a>';
|
||
|
||
return (string) $buttons . $button;
|
||
}
|
||
|
||
/** wp_footer 输出 #modal_admin_set(对齐参考主题 zib_frontend_set_modal)。 */
|
||
public function render_modal(): void {
|
||
if (!self::enabled()) {
|
||
return;
|
||
}
|
||
|
||
$object_id = (int) get_queried_object_id();
|
||
if (is_category() || is_tag() || is_tax()) {
|
||
$type = 'tax';
|
||
$taxonomy = (string) get_queried_object()->taxonomy;
|
||
$post_type = '';
|
||
$edit_link = get_edit_term_link($object_id, $taxonomy);
|
||
$type_label = (string) get_taxonomy($taxonomy)->labels->singular_name;
|
||
} else {
|
||
$type = 'post';
|
||
$taxonomy = '';
|
||
$post_type = (string) get_post_type();
|
||
$edit_link = (string) get_edit_post_link($object_id);
|
||
$type_label = (string) get_post_type_object($post_type)->labels->singular_name;
|
||
}
|
||
|
||
// 对齐参考主题 zib_get_customize_widgets_url:跳转定制器小工具面板并返回当前页。
|
||
$current_url = (string) home_url(add_query_arg(null, null));
|
||
$widgets_url = add_query_arg([
|
||
'url' => rawurlencode($current_url),
|
||
'autofocus' => ['panel' => 'widgets'],
|
||
'return' => rawurlencode($current_url),
|
||
], admin_url('customize.php'));
|
||
|
||
$header = '<div class="modal-header"><strong class="modal-title">'
|
||
. '<i class="fa fas fa-sliders-h mr10" aria-hidden="true"></i>' . esc_html($type_label . __('设置', 'aksrt'))
|
||
. '</strong><button class="close" data-dismiss="modal">' . $this->close_icon() . '</button></div>';
|
||
|
||
$footer = '<div class="modal-footer">'
|
||
. '<a target="_blank" title="' . esc_attr__('使用可视化布局配置此页面', 'aksrt') . '" data-toggle="tooltip" href="' . esc_url($widgets_url) . '" class="but c-yellow-2 padding-lg"><i class="fa fas fa-chart-pie" aria-hidden="true"></i>' . esc_html__('模块布局', 'aksrt') . '</a>'
|
||
. ($edit_link ? '<a target="_blank" href="' . esc_url($edit_link) . '" class="but c-yellow padding-lg"><i class="fa fab fa-wordpress" aria-hidden="true"></i>' . esc_html__('后台编辑', 'aksrt') . '</a>' : '')
|
||
. '<button class="but jb-blue padding-lg wp-ajax-submit"><i class="fa fas fa-check" aria-hidden="true"></i>' . esc_html__('确认修改', 'aksrt') . '</button>'
|
||
. '</div>';
|
||
|
||
$body = '<div class="modal-body">'
|
||
. ($type === 'tax' ? $this->term_inputs($object_id, $taxonomy) : $this->post_inputs($object_id))
|
||
. '<input type="hidden" name="action" value="aksrt_frontend_set_save">'
|
||
. '<input type="hidden" name="type" value="' . esc_attr($type) . '">'
|
||
. '<input type="hidden" name="taxonomy" value="' . esc_attr($taxonomy) . '">'
|
||
. '<input type="hidden" name="post_type" value="' . esc_attr($post_type) . '">'
|
||
. '<input type="hidden" name="id" value="' . esc_attr((string) $object_id) . '">'
|
||
. wp_nonce_field('aksrt_frontend_set', '_wpnonce', true, false)
|
||
. '</div>';
|
||
|
||
echo '<div class="modal fade" id="modal_admin_set" tabindex="-1" role="dialog" aria-hidden="false" win-ajax-replace="modal_admin_set">'
|
||
. '<div class="modal-dialog" role="document">'
|
||
. '<div class="modal-content page-set-modal"><form>' . $header . $body . $footer . '</form></div>'
|
||
. '</div></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- 各片段已转义
|
||
}
|
||
|
||
/** 关闭按钮图标(本站 FA5 惯例,对齐参考主题 close 按钮位)。 */
|
||
private function close_icon(): string {
|
||
return '<i class="fa fas fa-times" aria-hidden="true"></i>';
|
||
}
|
||
|
||
/** 保存(legacy aksrt_ajax 契约:{msg, reload} / {error, ys, msg})。 */
|
||
public function ajax_save(): void {
|
||
check_ajax_referer('aksrt_frontend_set', '_wpnonce');
|
||
|
||
$settings = class_exists('\AKSRT\Settings\ExtensionRuntime')
|
||
? \AKSRT\Settings\ExtensionRuntime::get_system_tools_settings()
|
||
: [];
|
||
if (empty($settings['admin_frontend_set']) || !current_user_can('manage_options')) {
|
||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('权限不足', 'aksrt')]);
|
||
}
|
||
|
||
$object_id = isset($_POST['id']) ? absint($_POST['id']) : 0;
|
||
if (!$object_id) {
|
||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('页面数据出错', 'aksrt')]);
|
||
}
|
||
|
||
$type = isset($_POST['type']) ? sanitize_key((string) $_POST['type']) : '';
|
||
if ($type === 'tax') {
|
||
$this->save_term($object_id, isset($_POST['taxonomy']) ? sanitize_key((string) wp_unslash($_POST['taxonomy'])) : '');
|
||
} elseif ($type === 'post') {
|
||
$this->save_post($object_id);
|
||
} else {
|
||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('页面数据出错', 'aksrt')]);
|
||
}
|
||
|
||
wp_send_json(['msg' => __('保存成功,正在刷新页面', 'aksrt'), 'reload' => true]);
|
||
}
|
||
|
||
private function save_post(int $post_id): void {
|
||
$post = get_post($post_id);
|
||
if (!$post) {
|
||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('未找到可用post数据', 'aksrt')]);
|
||
}
|
||
if (!current_user_can('edit_post', $post_id)) {
|
||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('权限不足', 'aksrt')]);
|
||
}
|
||
|
||
$postarr = ['ID' => $post_id];
|
||
if (isset($_POST['post_title'])) {
|
||
$title = sanitize_text_field(wp_unslash((string) $_POST['post_title']));
|
||
if ($title !== '') {
|
||
$postarr['post_title'] = $title;
|
||
}
|
||
}
|
||
if (isset($_POST['comments_open'])) {
|
||
$postarr['comment_status'] = $_POST['comments_open'] === 'open' ? 'open' : 'closed';
|
||
}
|
||
if (count($postarr) > 1) {
|
||
$result = wp_update_post($postarr, true);
|
||
if (is_wp_error($result)) {
|
||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => $result->get_error_message()]);
|
||
}
|
||
}
|
||
|
||
if (isset($_POST['post_format']) && post_type_supports($post->post_type, 'post-formats')) {
|
||
$format = sanitize_key((string) wp_unslash($_POST['post_format']));
|
||
set_post_format($post_id, $format === 'standard' ? '' : $format);
|
||
}
|
||
|
||
if ($post->post_type === 'post' && class_exists('\AKSRT\Core\PostMetaBox')) {
|
||
// 保留白名单之外的既有配置键(封面、视频等),仅覆盖弹窗提交的字段。
|
||
// 复选框未勾选时浏览器不会提交键名,两个开关必须无条件写入。
|
||
$meta = get_post_meta($post_id, \AKSRT\Core\PostMetaBox::META_KEY, true);
|
||
$meta = is_array($meta) ? $meta : [];
|
||
|
||
$layout = isset($_POST['show_layout']) ? sanitize_key((string) wp_unslash($_POST['show_layout'])) : 'false';
|
||
if (!in_array($layout, ['false', 'no_sidebar', 'sidebar_left', 'sidebar_right'], true)) {
|
||
$layout = 'false';
|
||
}
|
||
$meta['show_layout'] = $layout;
|
||
|
||
if (isset($_POST['subtitle'])) {
|
||
$meta['subtitle'] = sanitize_text_field(wp_unslash((string) $_POST['subtitle']));
|
||
}
|
||
|
||
$meta['no_article-navs'] = !empty($_POST['no_article-navs']);
|
||
$meta['article_maxheight_xz'] = !empty($_POST['article_maxheight_xz']);
|
||
update_post_meta($post_id, \AKSRT\Core\PostMetaBox::META_KEY, $meta);
|
||
|
||
if (isset($_POST['views'])) {
|
||
update_post_meta($post_id, \AKSRT\Core\DomainModel::POST_VIEW_COUNT_KEY, max(0, (int) $_POST['views']));
|
||
}
|
||
if (isset($_POST['like'])) {
|
||
update_post_meta($post_id, '_aksrt_like_count', max(0, (int) $_POST['like']));
|
||
}
|
||
}
|
||
|
||
if ($post->post_type === 'page') {
|
||
if (isset($_POST['page_header_style'])) {
|
||
$style = sanitize_key((string) wp_unslash($_POST['page_header_style']));
|
||
update_post_meta($post_id, 'page_header_style', in_array($style, ['', 'not', '1', '2', '3'], true) ? $style : '');
|
||
}
|
||
|
||
$register = isset($_POST['widgets_register']) && $_POST['widgets_register'] === '1';
|
||
update_post_meta($post_id, 'widgets_register', $register ? '1' : '');
|
||
|
||
// 关闭小工具容器时清空容器位置;容器位置与参考主题的注册语义保持一致。
|
||
$containers = [];
|
||
if ($register && !empty($_POST['widgets_register_container']) && is_array($_POST['widgets_register_container'])) {
|
||
$allowed = ['sidebar', 'top_fluid', 'top_content', 'bottom_content', 'bottom_fluid'];
|
||
$containers = array_values(array_unique(array_intersect(
|
||
array_map('sanitize_key', wp_unslash($_POST['widgets_register_container'])),
|
||
$allowed
|
||
)));
|
||
}
|
||
update_post_meta($post_id, 'widgets_register_container', $containers);
|
||
}
|
||
}
|
||
|
||
private function save_term(int $term_id, string $taxonomy = ''): void {
|
||
$term = $taxonomy !== '' ? get_term($term_id, $taxonomy) : get_term($term_id);
|
||
if (!$term || is_wp_error($term)) {
|
||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('未找到可用term数据', 'aksrt')]);
|
||
}
|
||
if (!current_user_can('manage_categories') && !current_user_can('manage_terms')) {
|
||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('权限不足', 'aksrt')]);
|
||
}
|
||
|
||
$data = [];
|
||
if (isset($_POST['term_name'])) {
|
||
$name = sanitize_text_field(wp_unslash((string) $_POST['term_name']));
|
||
if ($name !== '') {
|
||
$data['name'] = $name;
|
||
}
|
||
}
|
||
if (isset($_POST['term_description'])) {
|
||
$data['description'] = sanitize_textarea_field(wp_unslash((string) $_POST['term_description']));
|
||
}
|
||
if (!$data) {
|
||
return;
|
||
}
|
||
|
||
$result = wp_update_term($term_id, (string) $term->taxonomy, $data);
|
||
if (is_wp_error($result)) {
|
||
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => $result->get_error_message()]);
|
||
}
|
||
}
|
||
|
||
/** 文章/页面的弹窗字段(对齐参考主题 zib_get_frontend_set_input)。 */
|
||
private function post_inputs(int $post_id): string {
|
||
$meta = class_exists('\AKSRT\Core\PostMetaBox')
|
||
? \AKSRT\Core\PostMetaBox::get_meta($post_id)
|
||
: [];
|
||
$inputs = [];
|
||
|
||
$inputs[] = [
|
||
'name' => __('显示布局', 'aksrt'),
|
||
'id' => 'show_layout',
|
||
'std' => (string) ($meta['show_layout'] ?? 'false'),
|
||
'type' => 'radio',
|
||
'options' => [
|
||
'false' => __('跟随主题', 'aksrt'),
|
||
'no_sidebar' => __('无侧边栏', 'aksrt'),
|
||
'sidebar_left' => __('侧边栏靠左', 'aksrt'),
|
||
'sidebar_right' => __('侧边栏靠右', 'aksrt'),
|
||
],
|
||
];
|
||
|
||
$inputs[] = [
|
||
'name' => __('标题', 'aksrt'),
|
||
'id' => 'post_title',
|
||
'std' => (string) get_the_title($post_id),
|
||
'type' => 'text',
|
||
];
|
||
|
||
if (is_single()) {
|
||
$inputs[] = [
|
||
'name' => __('副标题', 'aksrt'),
|
||
'id' => 'subtitle',
|
||
'std' => (string) ($meta['subtitle'] ?? ''),
|
||
'type' => 'text',
|
||
];
|
||
$inputs[] = [
|
||
'name' => __('文章格式', 'aksrt'),
|
||
'id' => 'post_format',
|
||
'std' => (string) get_post_format($post_id),
|
||
'type' => 'select',
|
||
'options' => $this->post_format_options(),
|
||
];
|
||
$inputs[] = [
|
||
'name' => __('点赞数', 'aksrt'),
|
||
'id' => 'like',
|
||
'std' => (int) ($meta['like'] ?? 0),
|
||
'type' => 'number',
|
||
];
|
||
$inputs[] = [
|
||
'name' => __('阅读数', 'aksrt'),
|
||
'id' => 'views',
|
||
'std' => (int) ($meta['views'] ?? 0),
|
||
'type' => 'number',
|
||
];
|
||
$inputs[] = [
|
||
'name' => __('目录树', 'aksrt'),
|
||
'id' => 'no_article-navs',
|
||
'std' => !empty($meta['no_article-navs']),
|
||
'type' => 'checkbox',
|
||
'desc' => __('不显示', 'aksrt'),
|
||
];
|
||
$inputs[] = [
|
||
'name' => __('文章高度', 'aksrt'),
|
||
'id' => 'article_maxheight_xz',
|
||
'std' => !empty($meta['article_maxheight_xz']),
|
||
'type' => 'checkbox',
|
||
'desc' => __('限制文章最大高度', 'aksrt'),
|
||
];
|
||
}
|
||
|
||
$inputs[] = [
|
||
'name' => __('评论', 'aksrt'),
|
||
'id' => 'comments_open',
|
||
'std' => comments_open($post_id) ? 'open' : '',
|
||
'type' => 'radio',
|
||
'options' => [
|
||
'open' => __('开启', 'aksrt'),
|
||
'' => __('关闭', 'aksrt'),
|
||
],
|
||
];
|
||
|
||
if (is_page()) {
|
||
$inputs[] = [
|
||
'name' => __('标题样式', 'aksrt'),
|
||
'id' => 'page_header_style',
|
||
'std' => (string) get_post_meta($post_id, 'page_header_style', true),
|
||
'type' => 'radio',
|
||
'options' => [
|
||
'' => __('跟随主题', 'aksrt'),
|
||
'not' => __('不显示', 'aksrt'),
|
||
'1' => __('简单样式', 'aksrt'),
|
||
'2' => __('卡片样式', 'aksrt'),
|
||
'3' => __('封面图样式', 'aksrt'),
|
||
],
|
||
];
|
||
$inputs[] = [
|
||
'name' => __('模块布局', 'aksrt'),
|
||
'id' => 'widgets_register',
|
||
'type' => 'radio',
|
||
'options' => [
|
||
'1' => __('开启', 'aksrt'),
|
||
'0' => __('关闭', 'aksrt'),
|
||
],
|
||
'std' => (string) get_post_meta($post_id, 'widgets_register', true),
|
||
];
|
||
$inputs[] = [
|
||
'id' => 'widgets_register_container',
|
||
'type' => 'checkbox',
|
||
'class' => 'compact',
|
||
'std' => (array) get_post_meta($post_id, 'widgets_register_container', true),
|
||
'name' => __('创建容器位置', 'aksrt'),
|
||
'desc' => __('请根据需要合理开启,如果用不到则不要开启', 'aksrt') . '<br>' . __('保存页面后即可进入小工具或模块配置添加模块', 'aksrt') . '<div class="c-yellow">' . __('注意:开启此功能的页面不能太多,太多会影响性能,建议控制在10个以内', 'aksrt') . '</div>',
|
||
'options' => [
|
||
'sidebar' => __('侧边栏', 'aksrt'),
|
||
'top_fluid' => __('顶部全宽度', 'aksrt'),
|
||
'top_content' => __('主内容上面', 'aksrt'),
|
||
'bottom_content' => __('主内容下面', 'aksrt'),
|
||
'bottom_fluid' => __('底部全宽度', 'aksrt'),
|
||
],
|
||
];
|
||
}
|
||
|
||
return $this->render_inputs($inputs);
|
||
}
|
||
|
||
/**
|
||
* 文章格式选项(1:1 对齐参考主题的固定列表)。
|
||
*
|
||
* 参考主题把标准/图像/画廊/视频四个格式写死在弹窗里,不读
|
||
* get_theme_support('post-formats')。本站注册的格式比参考主题多
|
||
* (aside/quote/link 等),动态枚举会让选项集偏离参考主题,
|
||
* 因此同样固定这四项。
|
||
*/
|
||
private function post_format_options(): array {
|
||
return [
|
||
'standard' => __('标准', 'aksrt'),
|
||
'image' => __('图像', 'aksrt'),
|
||
'gallery' => __('画廊', 'aksrt'),
|
||
'video' => __('视频', 'aksrt'),
|
||
];
|
||
}
|
||
|
||
/** 分类术语的弹窗字段(对齐参考主题 tax 分支)。 */
|
||
private function term_inputs(int $term_id, string $taxonomy): string {
|
||
$term = get_term($term_id, $taxonomy);
|
||
if (!$term || is_wp_error($term)) {
|
||
return '<p class="muted-3-color">' . esc_html__('未找到可用term数据', 'aksrt') . '</p>';
|
||
}
|
||
|
||
return $this->render_inputs([
|
||
[
|
||
'name' => __('名称', 'aksrt'),
|
||
'id' => 'term_name',
|
||
'std' => (string) $term->name,
|
||
'type' => 'text',
|
||
],
|
||
[
|
||
'name' => __('描述', 'aksrt'),
|
||
'id' => 'term_description',
|
||
'std' => (string) $term->description,
|
||
'type' => 'text',
|
||
],
|
||
]);
|
||
}
|
||
|
||
/** 表单控件构建(对齐参考主题 zib_edit_input_construct)。 */
|
||
private function render_inputs(array $inputs): string {
|
||
$output = '';
|
||
foreach ($inputs as $meta) {
|
||
$type = (string) ($meta['type'] ?? '');
|
||
$value = $meta['std'] ?? '';
|
||
$desc = (string) ($meta['desc'] ?? '');
|
||
$class = $type !== '' ? ' option-' . $type : '';
|
||
|
||
if (isset($meta['class'])) {
|
||
$class .= ' ' . $meta['class'];
|
||
}
|
||
|
||
$control = '';
|
||
if ($type === 'text') {
|
||
$control = '<input class="form-control" name="' . esc_attr((string) ($meta['id'] ?? '')) . '" type="text" value="' . esc_attr((string) $value) . '"/>';
|
||
} elseif ($type === 'number') {
|
||
$control = '<input class="form-control" name="' . esc_attr((string) ($meta['id'] ?? '')) . '" type="number" value="' . esc_attr((string) $value) . '"/>';
|
||
} elseif ($type === 'select') {
|
||
$control = '<div class="form-select"><select class="form-control" name="' . esc_attr((string) ($meta['id'] ?? '')) . '">';
|
||
foreach ((array) ($meta['options'] ?? []) as $key => $option) {
|
||
$control .= '<option' . selected((string) $value, (string) $key, false) . ' value="' . esc_attr((string) $key) . '">' . esc_html((string) $option) . '</option>';
|
||
}
|
||
$control .= '</select></div>';
|
||
} elseif ($type === 'radio') {
|
||
foreach ((array) ($meta['options'] ?? []) as $key => $option) {
|
||
$control .= '<label class="mr10"><input type="radio" name="' . esc_attr((string) ($meta['id'] ?? '')) . '" value="' . esc_attr((string) $key) . '" ' . checked((string) $value, (string) $key, false) . ' /><span class="ml6 em09 muted-color" style=" font-weight: normal; ">' . esc_html((string) $option) . '</span></label>';
|
||
}
|
||
} elseif ($type === 'checkbox') {
|
||
$options = (array) ($meta['options'] ?? []);
|
||
if ($options) {
|
||
foreach ($options as $key => $option) {
|
||
$checked = is_array($value) ? (in_array($key, $value, true) ? ' checked' : '') : ((string) $value === (string) $key ? ' checked' : '');
|
||
$control .= '<span class="form-checkbox"><input name="' . esc_attr((string) ($meta['id'] ?? '')) . '[]" id="' . esc_attr((string) ($meta['id'] ?? '') . $key) . '" type="checkbox"' . $checked . ' value="' . esc_attr((string) $key) . '"/><label for="' . esc_attr((string) ($meta['id'] ?? '') . $key) . '" class="em09 muted-color ml6 mr10" style=" font-weight: normal; ">' . esc_html((string) $option) . '</label></span>';
|
||
}
|
||
} else {
|
||
$control = '<span class="form-checkbox"><input value="1" name="' . esc_attr((string) ($meta['id'] ?? '')) . '" id="' . esc_attr((string) ($meta['id'] ?? '')) . '" type="checkbox" ' . checked(!empty($value), true, false) . '/><label for="' . esc_attr((string) ($meta['id'] ?? '')) . '" class="em09 muted-color ml6" style=" font-weight: normal; ">' . ($desc !== '' ? $this->rich_desc($desc) : '') . '</label></span>';
|
||
}
|
||
}
|
||
|
||
$output .= '<div class="mb10 row' . esc_attr($class) . '">' . "\n";
|
||
$output .= '<div class="heading col-xs-3 text-right">' . esc_html((string) ($meta['name'] ?? '')) . '</div>' . "\n";
|
||
$output .= '<div class="option col-xs-8">' . "\n";
|
||
$output .= $control;
|
||
if ($desc !== '' && $type !== 'checkbox') {
|
||
$output .= '<div class="em09 muted-2-color">' . $this->rich_desc($desc) . '</div>' . "\n";
|
||
}
|
||
$output .= '</div>' . "\n";
|
||
$output .= '</div>' . "\n";
|
||
}
|
||
|
||
return $output;
|
||
}
|
||
|
||
/**
|
||
* 描述文案:允许 <br>、强调标签与来源可信的主题文案(对齐参考主题的富文本描述)。
|
||
*
|
||
* @param string $desc 内部生成的描述文案。
|
||
*/
|
||
private function rich_desc(string $desc): string {
|
||
return wp_kses($desc, [
|
||
'br' => [],
|
||
'div' => ['class' => []],
|
||
'span' => ['class' => []],
|
||
'em' => [],
|
||
'strong' => [],
|
||
'b' => [],
|
||
'i' => [],
|
||
]);
|
||
}
|
||
}
|