- 删除 AksrtFrontend registry/rehydrate 双路架构及 build/public.js 拼包入口,
前台唯一初始路径为 PHP 本地栈(jQuery 1.9.1 → Bootstrap 3.4.1)
→ loader_js(_win/aksrtData 唯一注入)→ tbquire(['main']) → runtime/main.min.js
- window.auto_fun = debounce(z_auto_fun, 100) 为唯一重扫入口;所有模块改为
执行一次 + off/on('auto_fun.aksrt-*') 命名空间重扫,data/class 标记幂等
- 局部更新协议统一:HTML 注入 → post_ajax.ed → auto_fun();
远程 modal → loaded.bs.modal → auto_fun();JSON 成功 → zib_ajax.success
- 灯箱恢复 Miniimgbox/imgbox 唯一 owner(imgbox-container、a[data-imgbox]、
imgbox_data),删除 AksrtLightbox/data-aksrt-lightbox 第二实现及 CSS/overlay,
_win.imgbox* 重新接上 post-display 设置
- 死代码清理:runtime 下 11 组被自有实现替代的旧模块副本、无消费 vendored 库、
顶层 grade.min.js、revenue.js、无调用方的 aksrt_get_win_runtime_config(),
以及 loader 中 16 条死 mapping
- PHP 入队改为本地栈 + loader_js,routed/widget/submit 依赖统一换绑;
用户卡片发布按钮恢复 nav_user_newposts_btn 设置门控
- 测试对齐:新增 frontend-lifecycle-contract,重写 navigation/lazyload/toast/
auth-source/comment/page-display/post-visual 等断言为目标架构契约
验证:node --test widget-system+navigation 223 通过 0 失败;
构建脚本重建 main/imgbox min 与 CSS bundles;改动 PHP php -l、JS node --check
全部通过;git diff --check 干净;credits 重入 harness 通过。
253 lines
9.1 KiB
PHP
253 lines
9.1 KiB
PHP
<?php
|
||
defined('ABSPATH') || exit;
|
||
|
||
// EnlighterJS 代码高亮 — 白名单 data-enlighter-* 属性
|
||
// ============================================================
|
||
|
||
add_filter('wp_kses_allowed_html', function ($allowed, $context) {
|
||
if ($context !== 'post') {
|
||
return $allowed;
|
||
}
|
||
|
||
// Allow data-enlighter-* attributes on <pre> and <code>
|
||
$enlighter_attrs = [
|
||
'data-enlighter-language' => true,
|
||
'data-enlighter-theme' => true,
|
||
'data-enlighter-group' => true,
|
||
'data-enlighter-title' => true,
|
||
'data-enlighter-linenumbers' => true,
|
||
'data-enlighter-highlight' => true,
|
||
'data-enlighter-lineoffset' => true,
|
||
];
|
||
|
||
if (isset($allowed['pre'])) {
|
||
$allowed['pre'] = array_merge($allowed['pre'], $enlighter_attrs);
|
||
}
|
||
if (isset($allowed['code'])) {
|
||
$allowed['code'] = array_merge($allowed['code'], $enlighter_attrs);
|
||
}
|
||
|
||
return $allowed;
|
||
}, 10, 2);
|
||
|
||
// ============================================================
|
||
// WP 后台文章列表增强列 — 浏览/点赞/收藏/下载
|
||
// ============================================================
|
||
|
||
add_filter('manage_post_posts_columns', function ($columns) {
|
||
$new_columns = [];
|
||
foreach ($columns as $key => $label) {
|
||
$new_columns[$key] = $label;
|
||
if ($key === 'title') {
|
||
$new_columns['aksrt_stats'] = __('互动数据', 'aksrt');
|
||
}
|
||
}
|
||
return $new_columns;
|
||
});
|
||
|
||
add_action('admin_enqueue_scripts', function ($hook) {
|
||
if ($hook !== 'edit.php') return;
|
||
wp_enqueue_style(
|
||
'aksrt-font-awesome',
|
||
get_template_directory_uri() . '/assets/fontawesome5/css/all.min.css',
|
||
[],
|
||
'5.15.4'
|
||
);
|
||
});
|
||
|
||
add_action('manage_post_posts_custom_column', function ($column, $post_id) {
|
||
if ($column !== 'aksrt_stats') return;
|
||
|
||
$views = (int) get_post_meta($post_id, 'views', true);
|
||
$likes = (int) get_post_meta($post_id, 'like', true);
|
||
$favorites = (int) get_post_meta($post_id, 'favorite', true);
|
||
$downloads = (int) get_post_meta($post_id, 'download_count', true);
|
||
|
||
$format = function (int $n): string {
|
||
if ($n >= 10000) return round($n / 10000, 1) . '万';
|
||
if ($n >= 1000) return round($n / 1000, 1) . 'k';
|
||
return (string) $n;
|
||
};
|
||
|
||
$parts = [];
|
||
$parts[] = '<span title="' . esc_attr__('浏览', 'aksrt') . '" style="margin-right:4px;"><i class="fa fas fa-eye" style="color:#8a909c;font-size:12px;"></i> ' . esc_html($format($views)) . '</span>';
|
||
$parts[] = '<span title="' . esc_attr__('点赞', 'aksrt') . '" style="margin-right:4px;"><i class="fa fas fa-thumbs-up" style="color:#1890ff;font-size:12px;"></i> ' . esc_html($format($likes)) . '</span>';
|
||
$parts[] = '<span title="' . esc_attr__('收藏', 'aksrt') . '" style="margin-right:4px;"><i class="fa fas fa-star" style="color:#f39c12;font-size:12px;"></i> ' . esc_html($format($favorites)) . '</span>';
|
||
|
||
if ($downloads > 0) {
|
||
$parts[] = '<span title="' . esc_attr__('下载', 'aksrt') . '"><i class="fa fas fa-download" style="color:#52c41a;font-size:12px;"></i> ' . esc_html($format($downloads)) . '</span>';
|
||
}
|
||
|
||
echo implode(' · ', $parts);
|
||
}, 10, 2);
|
||
|
||
// Support sorting by views/likes/favorites in admin
|
||
add_action('pre_get_posts', function ($query) {
|
||
if (!is_admin() || !$query->is_main_query()) return;
|
||
|
||
$orderby = $query->get('orderby');
|
||
$allowed = ['views', 'like', 'favorite', 'download_count'];
|
||
|
||
if (in_array($orderby, $allowed, true)) {
|
||
$query->set('meta_key', $orderby);
|
||
$query->set('orderby', 'meta_value_num');
|
||
}
|
||
});
|
||
|
||
// ============================================================
|
||
// 评论提交:单一合同
|
||
// ------------------------------------------------------------
|
||
// 主路径:前端 comments.js → REST /comment/submit(aksrt_require_captcha)。
|
||
// 兜底 / 原生 wp-comments-post.php 经 preprocess_comment 做人机验证,避免绕过。
|
||
// 列表仍由 aksrt_comment_callback 统一渲染。
|
||
// ============================================================
|
||
|
||
// ============================================================
|
||
// Gutenberg 自定义块注册
|
||
// ============================================================
|
||
|
||
// Register category server-side so it exists before JS registerBlockType runs.
|
||
// Keep untyped + defensive: some plugins pass unexpected shapes; typed params can 500 the editor.
|
||
add_filter('block_categories_all', static function ($categories, $context = null) {
|
||
if (!is_array($categories)) {
|
||
return $categories;
|
||
}
|
||
|
||
foreach ($categories as $category) {
|
||
$slug = '';
|
||
if (is_array($category)) {
|
||
$slug = (string) ($category['slug'] ?? '');
|
||
} elseif (is_object($category) && isset($category->slug)) {
|
||
$slug = (string) $category->slug;
|
||
}
|
||
if ($slug === 'aksrt-blocks') {
|
||
return $categories;
|
||
}
|
||
}
|
||
|
||
$categories[] = [
|
||
'slug' => 'aksrt-blocks',
|
||
'title' => 'AKSRT',
|
||
'icon' => null,
|
||
];
|
||
|
||
return $categories;
|
||
}, 10, 2);
|
||
|
||
add_action('enqueue_block_editor_assets', function () {
|
||
$base_uri = get_template_directory_uri();
|
||
$version = wp_get_theme()->get('Version');
|
||
|
||
$blocks_js = get_template_directory() . '/assets/js/gutenberg-blocks.js';
|
||
if (file_exists($blocks_js)) {
|
||
wp_enqueue_script(
|
||
'aksrt-gutenberg-blocks',
|
||
$base_uri . '/assets/js/gutenberg-blocks.js',
|
||
// wp-media-utils: MediaUpload for carousel block
|
||
['wp-blocks', 'wp-element', 'wp-components', 'wp-block-editor', 'wp-hooks', 'wp-media-utils', 'wp-data'],
|
||
(string) filemtime($blocks_js),
|
||
true
|
||
);
|
||
}
|
||
|
||
$blocks_css = get_template_directory() . '/assets/css/gutenberg-blocks.css';
|
||
if (file_exists($blocks_css)) {
|
||
wp_enqueue_style(
|
||
'aksrt-gutenberg-blocks',
|
||
$base_uri . '/assets/css/gutenberg-blocks.css',
|
||
[],
|
||
(string) filemtime($blocks_css)
|
||
);
|
||
}
|
||
});
|
||
|
||
// Front-end styles for Gutenberg blocks
|
||
add_action('enqueue_block_assets', function () {
|
||
if (is_admin()) return; // Already loaded in editor
|
||
|
||
$posts = [];
|
||
$queried = get_queried_object();
|
||
if ($queried instanceof \WP_Post) {
|
||
$posts[$queried->ID] = $queried;
|
||
}
|
||
|
||
global $wp_query;
|
||
if ($wp_query instanceof \WP_Query) {
|
||
foreach ((array) $wp_query->posts as $post) {
|
||
if ($post instanceof \WP_Post) {
|
||
$posts[$post->ID] = $post;
|
||
}
|
||
}
|
||
}
|
||
|
||
$has_carousel = false;
|
||
foreach ($posts as $post) {
|
||
if (has_block('aksrt/carousel', $post)) {
|
||
$has_carousel = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!$has_carousel) return;
|
||
|
||
$base_uri = get_template_directory_uri();
|
||
$blocks_css = get_template_directory() . '/assets/css/gutenberg-blocks.css';
|
||
if (file_exists($blocks_css)) {
|
||
wp_enqueue_style(
|
||
'aksrt-gutenberg-blocks-front',
|
||
$base_uri . '/assets/css/gutenberg-blocks.css',
|
||
[],
|
||
(string) filemtime($blocks_css)
|
||
);
|
||
}
|
||
});
|
||
|
||
// ============================================================
|
||
// Gutenberg Carousel Block 渲染回调
|
||
// ============================================================
|
||
add_action('init', function () {
|
||
if (!function_exists('register_block_type')) return;
|
||
|
||
register_block_type('aksrt/carousel', [
|
||
'render_callback' => function (array $attributes): string {
|
||
$images = $attributes['images'] ?? [];
|
||
if (empty($images)) return '';
|
||
|
||
$autoplay = !empty($attributes['autoplay']) ? 'true' : 'false';
|
||
$delay = absint($attributes['delay'] ?? 3000);
|
||
$effect = sanitize_text_field($attributes['effect'] ?? 'slide');
|
||
|
||
$slides = '';
|
||
foreach ($images as $img) {
|
||
if (empty($img['url'])) continue;
|
||
$alt = esc_attr($img['alt'] ?? '');
|
||
$url = esc_url($img['url']);
|
||
$slides .= '<div class="swiper-slide"><img' . aksrt_get_lazy_attr('lazy_sider', (string) $url, '', aksrt_get_lazy_thumb('lg')) . ' alt="' . $alt . '"></div>';
|
||
}
|
||
|
||
if (empty($slides)) return '';
|
||
|
||
return '<div class="aksrt-carousel-block swiper" data-autoplay="' . $autoplay . '" data-delay="' . $delay . '" data-effect="' . $effect . '">'
|
||
. '<div class="swiper-wrapper">' . $slides . '</div>'
|
||
. '<div class="swiper-pagination"></div>'
|
||
. '<div class="swiper-button-prev"></div><div class="swiper-button-next"></div>'
|
||
. '</div>';
|
||
},
|
||
]);
|
||
});
|
||
|
||
// ============================================================
|
||
// 付费内容系统:the_content 过滤器
|
||
// ============================================================
|
||
|
||
/**
|
||
* 未购买的图库/视频正文只保留公开预览;购买卡由 single 模板渲染。
|
||
*/
|
||
add_filter('the_content', 'aksrt_paid_content_filter', 20);
|
||
|
||
/**
|
||
* 付费/资源文章使用统一的 single.php。
|
||
*/
|
||
add_filter('single_template', function (string $template): string {
|
||
return $template;
|
||
});
|