' . '' . '
' . 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 = ''; 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' => [], ]); } }