Files
wordpress-kratos-yang/inc/theme-core.php
T
laoyang 4f0d19fb91 版本号 3.2.4 -> 3.2.5(修 Prism 报错,项目地址改为自建 Git)
## 修 Prism is not defined

首页控制台报 `Uncaught ReferenceError: Prism is not defined`,来源是
wp-editormd 插件(不是本主题):它在 Prism 脚本加载之前就无条件执行了
`Prism.plugins.autoloader.languages_path = "..."`。

线上页面里的真实顺序:
    第 668 行  该赋值语句
    第 695 行  prism-core.min.js
    第 696 行  prism-autoloader.min.js

在 `wp_head` 优先级 0 垫一个最小空壳 `window.Prism`(含 plugins.autoloader
这条访问链路),让那句赋值有地方可写。已核对 prism-core.min.js 源码:它开头
只读 `u.Prism.manual` 与 `u.Prism.disableWorkerMessageHandler`,结尾
`global.Prism = Prism` 整体覆盖 —— 空壳必定被真实对象替换,不承担持久职责,
只为吸收那条过早的赋值。已用 jsdom 验证:去掉空壳重现原始报错,加上后消失,
且之后 `Prism.highlightAll` 仍可正常调用。

## 项目地址统一到 code.yangyq.net

原作者署名、原主题地址、README、语言包、JS 里的控制台横幅全部换成自建仓库地址。
LICENSE 文件保持原样不动(MIT 要求保留原始版权声明)。

## CDN 分支改成拿不到就回退本地

原来的 `g_cdn` 分支指向 `cdn.jsdelivr.net/gh/vtrois/kratos`,本仓库在自建
Gitea 上,jsDelivr 只认 github/npm/wordpress,实测该地址 404。现改为仅在
`YANG_THEME_CDN_REPO` 常量存在时才走 CDN,否则一律用
`get_template_directory_uri()`。留一个 404 的地址比留空更危险:页面能开、
样式全丢,极难排查。

## 顺带

- `.gitattributes` 补 `*.mo binary`:原来是靠"含 NUL"启发式判定,不是契约
- `update.json` 的版本号跟进到 3.2.5
- `kratos.min.js` / `kratos.min.css` 用压缩器重新生成,不手工改产物
2026-10-23 12:55:41 +08:00

279 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 核心函数
* @author Yongquan Yang <yangyq.net>
* @license MIT License
* @version 2020.08.04
*/
// CDN 资源分支。
//
// ⚠️ 这里的地址必须是**能被 jsDelivr 拉取的 GitHub 仓库**(jsDelivr 只认
// github / npm / wordpress 等白名单源,不支持自建 Gitea/GitLab)。
// 本项目的主仓库在 https://code.yangyq.net/laoyang/wordpress-kratos-yang,
// 它**不能**直出给 jsDelivr(实测 404)。
// 因此:
// · 线上 g_cdn 选项默认关闭,走 get_template_directory_uri() 本地资源;
// · 若将来要启用 CDN,必须先把仓库镜像到 GitHub,再把下面的 owner/repo 换成镜像地址。
// 留一个 404 的地址比留空更危险:页面能打开、样式却全丢,排查成本极高。
if (kratos_option('g_cdn', false) && defined('YANG_THEME_CDN_REPO')) {
$asset_path = 'https://cdn.jsdelivr.net/gh/' . YANG_THEME_CDN_REPO . '@' . THEME_VERSION;
} else {
$asset_path = get_template_directory_uri();
}
define('ASSET_PATH', $asset_path);
// 自动跳转主题设置
function init_theme()
{
global $pagenow;
if ('themes.php' == $pagenow && isset($_GET['activated'])) {
wp_redirect(admin_url('admin.php?page=kratos_options'));
exit;
}
}
add_action('load-themes.php', 'init_theme');
// 语言国际化
function theme_languages()
{
load_theme_textdomain('kratos', get_template_directory() . '/languages');
}
add_action('after_setup_theme', 'theme_languages');
// 资源加载
function theme_autoload()
{
if (!is_admin()) {
// css
wp_enqueue_style('bootstrap', ASSET_PATH . '/assets/css/bootstrap.min.css', array(), '4.5.0');
wp_enqueue_style('kicon', ASSET_PATH . '/assets/css/iconfont.min.css', array(), THEME_VERSION);
wp_enqueue_style('layer', ASSET_PATH . '/assets/css/layer.min.css', array(), '3.1.1');
if (kratos_option('g_animate', false)) {
wp_enqueue_style('animate', ASSET_PATH . '/assets/css/animate.min.css', array(), '3.7.2');
}
if (kratos_option('g_fontawesome', false)) {
wp_enqueue_style('fontawesome', ASSET_PATH . '/assets/css/fontawesome.min.css', array(), '5.13.0');
}
wp_enqueue_style('kratos', ASSET_PATH . '/assets/css/kratos.min.css', array(), THEME_VERSION);
if (kratos_option('g_adminbar', true)) {
$admin_bar_css = "
@media screen and (max-width: 782px) {
.k-nav{
padding-top: 46px;
}
}
@media screen and (min-width: 782px) {
.k-nav{
padding-top: 32px;
}
}
";
if (current_user_can('level_10')) {
wp_add_inline_style('kratos', $admin_bar_css);
}
}
// 自定义样式:存在主题选项里(数据库),排在主题样式之后,优先级最高。
// 放选项而非主题目录,是为了让它在主题升级(升级会清空主题目录)后依然存在。
$custom_css = yang_custom_css();
if (trim($custom_css) !== '') {
wp_add_inline_style('kratos', $custom_css);
}
// js
wp_deregister_script('jquery');
wp_enqueue_script('jquery', ASSET_PATH . '/assets/js/jquery.min.js', array(), '3.4.1', false);
wp_enqueue_script('bootstrap-bundle', ASSET_PATH . '/assets/js/bootstrap.bundle.min.js', array(), '4.5.0', true);
wp_enqueue_script('layer', ASSET_PATH . '/assets/js/layer.min.js', array(), '3.1.1', true);
wp_enqueue_script('kratos', ASSET_PATH . '/assets/js/kratos.min.js', array(), THEME_VERSION, true);
// 自定义脚本:同样存在主题选项里,挂在 kratos 之后(页脚输出)。
$custom_js = yang_custom_js();
if (trim($custom_js) !== '') {
wp_add_inline_script('kratos', $custom_js);
}
$data = array(
'site' => home_url(),
'directory' => get_stylesheet_directory_uri(),
'alipay' => kratos_option('g_donate_alipay', ASSET_PATH . '/assets/img/donate.png'),
'wechat' => kratos_option('g_donate_wechat', ASSET_PATH . '/assets/img/donate.png'),
'repeat' => __('您已经赞过了', 'kratos'),
'thanks' => __('感谢您的支持', 'kratos'),
'donate' => __('打赏作者', 'kratos'),
'scan' => __('扫码支付', 'kratos'),
);
wp_localize_script('kratos', 'kratos', $data);
}
}
add_action('wp_enqueue_scripts', 'theme_autoload');
/**
* Prism 前置兜底(修 Uncaught ReferenceError: Prism is not defined)
*
* 病因在 wp-editormd 插件,不在本主题。实测线上页面顺序:
* 668 行 Prism.plugins.autoloader.languages_path = "..."; ← 无条件赋值
* 695 行 <script src="prism-core.min.js">
* 696 行 <script src="prism-autoloader.min.js">
* 赋值语句在 Prism 加载**之前**执行 ⇒ 抛 ReferenceError,且这条赋值实际上
* 也完全没生效(真正的 languages_path 靠 footer.php 里那段带 if 防护的兜底设置)。
*
* 插件自己在页尾另一处写了 `if (window.Prism && ...)`,说明是它自己没改干净。
*
* 修法:在 <head> 极早位置(wp_head 优先级 0)先垫一个空壳 window.Prism,
* 把 plugins.autoloader 这条访问链路铺好,让插件那句赋值有地方可写,不再抛错。
*
* 关于「会不会被真实 Prism 冲掉」——已核对 prism-core.min.js 源码:
* 开头 `C={manual: u.Prism && u.Prism.manual, ...}` 只读取 manual /
* disableWorkerMessageHandler 两个开关;结尾 `global.Prism = Prism` 整体覆盖。
* 也就是说空壳对象**确实会被替换**,autoloader 也是随后由
* prism-autoloader.min.js 重新挂到新对象上的。
* 所以这里垫的对象只负责「让第 668 行不报错」,不承担持久职责 —— 正合适。
* 为避免误导后来人,这里刻意只做最小空壳,不塞任何逻辑。
*
* 用 var 而非 window.Prism= 是为了在严格模式下也能安全创建全局;
* 且仅在尚未存在时才创建,插件/主题自己先初始化时本段什么都不做。
*/
function yang_prism_early_shim()
{
if (is_admin()) {
return;
}
// 注意:这里是给浏览器执行的 JS,不是 PHP 字符串拼接出错也无妨,
// 但保持单行数组 + implode,避免 HEREDOC 里出现 <? 之类被误解析。
$lines = array(
"if (typeof window.Prism === 'undefined') {",
"\twindow.Prism = {};",
"\twindow.Prism.plugins = {};",
"\twindow.Prism.plugins.autoloader = {};",
"\twindow.Prism.plugins.autoloader.languages_path = '';",
"}",
);
echo '<script id="yang-prism-shim">' . "\n"
. implode("\n", $lines) . "\n"
. '</script>' . "\n";
}
add_action('wp_head', 'yang_prism_early_shim', 0);
// Admin Bar
if (! kratos_option('g_adminbar', true)) {
add_filter('show_admin_bar', '__return_false');
}
// 移除自动保存、修订版本
remove_action('post_updated', 'wp_save_post_revision');
// 添加友情链接
add_filter('pre_option_link_manager_enabled', '__return_true');
// 禁用转义
$qmr_work_tags = array('the_title', 'the_excerpt', 'single_post_title', 'comment_author', 'comment_text', 'link_description', 'bloginfo', 'wp_title', 'term_description', 'category_description', 'widget_title', 'widget_text');
foreach ($qmr_work_tags as $qmr_work_tag) {
remove_filter($qmr_work_tag, 'wptexturize');
}
remove_filter('the_content', 'wptexturize');
add_filter('run_wptexturize', '__return_false');
// 禁用 Emoji
add_filter('emoji_svg_url', '__return_false');
remove_action('admin_print_scripts', 'print_emoji_detection_script');
remove_action('admin_print_styles', 'print_emoji_styles');
remove_filter('the_content', 'wptexturize');
remove_filter('comment_text', 'wptexturize');
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');
remove_action('embed_head', 'print_emoji_detection_script');
remove_filter('the_content_feed', 'wp_staticize_emoji');
remove_filter('comment_text_rss', 'wp_staticize_emoji');
remove_filter('wp_mail', 'wp_staticize_emoji_for_email');
// 禁用 Trackbacks
add_filter('xmlrpc_methods', function ($methods) {
$methods['pingback.ping'] = '__return_false';
$methods['pingback.extensions.getPingbacks'] = '__return_false';
return $methods;
});
remove_action('do_pings', 'do_all_pings', 10);
remove_action('publish_post', '_publish_post_hook', 5);
// 优化 wp_head() 内容
foreach (array('rss2_head', 'commentsrss2_head', 'rss_head', 'rdf_header', 'atom_head', 'comments_atom_head', 'opml_head', 'app_head') as $action) {
remove_action($action, 'the_generator');
}
remove_action('wp_head', 'wp_print_head_scripts', 9);
remove_action('wp_head', 'rel_canonical');
remove_action('wp_head', 'wp_generator');
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
remove_action('wp_head', 'feed_links_extra', 3);
remove_action('wp_head', 'feed_links', 2);
remove_action('wp_head', 'index_rel_link');
remove_action('wp_head', 'parent_post_rel_link', 10);
remove_action('wp_head', 'start_post_rel_link', 10);
remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10);
remove_action('wp_head', 'wp_shortlink_wp_head', 10, 0);
remove_action('wp_head', 'rest_output_link_wp_head', 10);
remove_action('template_redirect', 'wp_shortlink_header', 11);
remove_action('template_redirect', 'rest_output_link_header', 11);
// 禁用 WordPress 拼写修正
remove_filter('the_title', 'capital_P_dangit', 11);
remove_filter('the_content', 'capital_P_dangit', 11);
remove_filter('comment_text', 'capital_P_dangit', 31);
// 禁用后台 Google Fonts
add_filter('style_loader_src', function ($href) {
if (strpos($href, "fonts.googleapis.com") === false) {
return $href;
}
return false;
});
// 禁用 Auto Embeds
remove_filter('the_content', array($GLOBALS['wp_embed'], 'autoembed'), 8);
// 替换国内 Gravatar 源
function get_https_avatar($avatar)
{
if (kratos_option('g_gravatar', false)) {
$cdn = "gravatar.loli.net";
} else {
$cdn = "cn.gravatar.com";
}
$avatar = str_replace(array("www.gravatar.com", "0.gravatar.com", "1.gravatar.com", "2.gravatar.com", "3.gravatar.com", "secure.gravatar.com"), $cdn, $avatar);
$avatar = str_replace("http://", "https://", $avatar);
return $avatar;
}
add_filter('get_avatar', 'get_https_avatar');
// 主题更新检测
//
// 更新元数据放在站点自己的域名下(/theme-update/update.json),不再单独占一个子域名。
// 好处是复用主站的证书、备案与 CDN,少维护一套站点。
// 注意:update.json 必须能被无条件公开访问。它落在 WordPress 根目录下,但主站 .htaccess
// 的重写规则带了 !-f 条件,真实存在的文件不会被送进 index.php,所以能正常直出。
// 若以后装了拦截直链的安全插件,先确认这个路径没被拦。
if (!defined('YANG_THEME_UPDATE_URL')) {
define('YANG_THEME_UPDATE_URL', 'https://yangyq.net/theme-update/update.json');
}
$myUpdateChecker = Puc_v4_Factory::buildUpdateChecker(
YANG_THEME_UPDATE_URL,
get_template_directory() . '/functions.php',
'Kratos_Yang'
);
// 禁止生成多种尺寸图片
if (kratos_option('g_removeimgsize', false)) {
function remove_default_images($sizes)
{
unset($sizes['thumbnail']);
unset($sizes['medium']);
unset($sizes['large']);
unset($sizes['medium_large']);
return $sizes;
}
add_filter('intermediate_image_sizes_advanced', 'remove_default_images');
}