Files
wordpress-kratos-yang/inc/theme-core.php
T

282 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');
}
wp_enqueue_style('kratos', ASSET_PATH . '/assets/css/kratos.min.css', array(), THEME_VERSION);
// 站点级公共样式:原「自定义 CSS」的内容已并入本文件(3.2.29)。
// 依赖 kratos ⇒ 一定排在主题样式之后,才压得住主题里的同名规则;
// 它自己也作为「自定义 CSS」的挂载点,保证站长临时覆盖仍排在最末。
wp_enqueue_style('kratos-site', ASSET_PATH . '/assets/css/site.css', array('kratos'), 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);
}
}
// 自定义样式:存在主题选项里(数据库),排在主题样式之后,优先级最高。
// 放选项而非主题目录,是为了让它在主题升级(升级会清空主题目录)后依然存在。
// 挂在 kratos-site 上(而非 kratos):站点公共样式 site.css 排在 kratos 之后,
// 自定义 CSS 必须继续排在 site.css 之后,否则临时覆盖会被公共样式反压。
$custom_css = yang_custom_css();
if (trim($custom_css) !== '') {
wp_add_inline_style('kratos-site', $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' => yang_option_image('g_donate_alipay'),
'wechat' => yang_option_image('g_donate_wechat'),
'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');
}