'
. ''
. '
' . esc_html__('在此编辑此页面参数', 'aksrt') . '
';
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 = '';
$footer = '';
$body = ''
. ($type === 'tax' ? $this->term_inputs($object_id, $taxonomy) : $this->post_inputs($object_id))
. ''
. ''
. ''
. ''
. ''
. wp_nonce_field('aksrt_frontend_set', '_wpnonce', true, false)
. '
';
echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- 各片段已转义
}
/** 关闭按钮图标(本站 FA5 惯例,对齐参考主题 close 按钮位)。 */
private function close_icon(): string {
return '';
}
/** 保存(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') . '
' . __('保存页面后即可进入小工具或模块配置添加模块', 'aksrt') . '' . __('注意:开启此功能的页面不能太多,太多会影响性能,建议控制在10个以内', 'aksrt') . '
',
'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 '' . esc_html__('未找到可用term数据', 'aksrt') . '
';
}
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 = '';
} elseif ($type === 'number') {
$control = '';
} elseif ($type === 'select') {
$control = '';
} elseif ($type === 'radio') {
foreach ((array) ($meta['options'] ?? []) as $key => $option) {
$control .= '';
}
} 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 .= '';
}
} else {
$control = '';
}
}
$output .= '' . "\n";
$output .= '
' . esc_html((string) ($meta['name'] ?? '')) . '
' . "\n";
$output .= '
' . "\n";
$output .= $control;
if ($desc !== '' && $type !== 'checkbox') {
$output .= '
' . $this->rich_desc($desc) . '
' . "\n";
}
$output .= '
' . "\n";
$output .= '
' . "\n";
}
return $output;
}
/**
* 描述文案:允许
、强调标签与来源可信的主题文案(对齐参考主题的富文本描述)。
*
* @param string $desc 内部生成的描述文案。
*/
private function rich_desc(string $desc): string {
return wp_kses($desc, [
'br' => [],
'div' => ['class' => []],
'span' => ['class' => []],
'em' => [],
'strong' => [],
'b' => [],
'i' => [],
]);
}
}