Files
wordpress-theme-aksrt/inc/core-hooks/part-2.php
T

480 lines
20 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
defined('ABSPATH') || exit;
// 前端隐藏 WordPress Admin Bar
// ============================================================
add_filter('show_admin_bar', '__return_false');
// ============================================================
// 隐藏不需要的默认菜单项
// ============================================================
add_action('admin_menu', function () {
remove_menu_page('link-manager.php');
});
// ============================================================
// Gravatar 头像源替换(4层拦截:get_avatar_url / pre_get_avatar_data / avatar_defaults / ob_start 兜底)
// ============================================================
function aksrt_get_gravatar_source() {
if (class_exists('AKSRT_Settings')) {
return AKSRT_Settings::avatar_source();
}
$source = aksrt_get_setting('avatar_source', 'cn.cravatar.com');
$allowed = array('cn.cravatar.com', 'cn.gravatar.com', 'gravatar.com');
return in_array($source, $allowed, true) ? $source : 'cn.cravatar.com';
}
function aksrt_replace_gravatar_domain($url) {
$source = aksrt_get_gravatar_source();
if ($source === 'gravatar.com') return $url;
return preg_replace('#^https?://([0-9]+\.)?(secure\.)?gravatar\.com#i', 'https://' . $source, $url);
}
add_filter('get_avatar_url', function ($url) {
return aksrt_replace_gravatar_domain($url);
});
add_filter('pre_get_avatar_data', function ($args) {
if (!empty($args['url'])) {
$args['url'] = aksrt_replace_gravatar_domain($args['url']);
}
return $args;
}, 10, 1);
add_filter('avatar_defaults', function ($defaults) {
foreach ($defaults as &$url) {
$url = aksrt_replace_gravatar_domain($url);
}
unset($url);
return $defaults;
});
// 全局输出缓冲兜底(覆盖 JS/API 直接请求的 URL)
add_action('init', function () {
if (is_admin()) ob_start(function ($html) {
return preg_replace_callback(
'#https?://([0-9]+\.)?(secure\.)?gravatar\.com#i',
function () { return 'https://' . aksrt_get_gravatar_source(); },
$html
);
});
});
// ============================================================
// SMTP 邮件配置注入
// ============================================================
add_action('phpmailer_init', function ($phpmailer) {
$mail = get_option('aksrt_mail_smtp', array());
if (empty($mail['enabled']) || empty($mail['host'])) return;
$phpmailer->isSMTP();
$phpmailer->Host = $mail['host'];
$phpmailer->Port = (int) (isset($mail['port']) ? $mail['port'] : 465);
$phpmailer->SMTPAuth = !empty($mail['username']);
$phpmailer->Username = $mail['username'];
$phpmailer->Password = $mail['password'];
$phpmailer->SMTPSecure = isset($mail['encryption']) ? $mail['encryption'] : 'ssl';
// 性能优化:超时 + 连接保持
$phpmailer->Timeout = 10; // 连接/读取超时 10 秒
$phpmailer->Timelimit = 15; // 总执行上限 15 秒
if (!empty($mail['from_address'])) {
$from_name = isset($mail['from_name']) ? $mail['from_name'] : '';
// mail_showname 覆盖 from_name
if (class_exists('AKSRT_Settings')) {
$branding = AKSRT_Settings::get_mail_branding();
if ($branding['showname'] !== '') {
$from_name = $branding['showname'];
}
}
$phpmailer->setFrom($mail['from_address'], $from_name);
}
if (!empty($mail['from_name'])) {
$phpmailer->FromName = $mail['from_name'];
}
// mail_showname 全局覆盖 FromName
if (class_exists('AKSRT_Settings')) {
$branding = AKSRT_Settings::get_mail_branding();
if ($branding['showname'] !== '') {
$phpmailer->FromName = $branding['showname'];
}
}
});
// mail_title_prefix:为所有邮件主题添加前缀
add_filter('wp_mail', function ($atts) {
if (!class_exists('AKSRT_Settings') || empty($atts['subject'])) return $atts;
$branding = AKSRT_Settings::get_mail_branding();
if ($branding['title_prefix'] !== '' && strpos($atts['subject'], $branding['title_prefix']) !== 0) {
$atts['subject'] = $branding['title_prefix'] . ' ' . $atts['subject'];
}
return $atts;
});
// ============================================================
// 分类 Term Meta 字段(封面图 + 推荐描述)
// ============================================================
/**
* 编辑分类页面 - 显示自定义字段
*/
function aksrt_category_meta_fields($term) {
$cover_id = (int) get_term_meta($term->term_id, 'aksrt_category_cover_id', true);
$cover_url = $cover_id > 0 ? wp_get_attachment_url($cover_id) : '';
$recommend_desc = (string) get_term_meta($term->term_id, 'aksrt_category_recommend_desc', true);
?>
<tr class="form-field">
<th scope="row"><label for="aksrt_category_cover_id"><?php esc_html_e('分类封面图', 'aksrt'); ?></label></th>
<td>
<div id="aksrt-cover-preview" style="margin-bottom:8px;">
<?php if ($cover_url) : ?>
<img src="<?php echo esc_url($cover_url); ?>" style="max-width:200px;height:auto;border-radius:6px;" alt="">
<?php endif; ?>
</div>
<input type="hidden" id="aksrt_category_cover_id" name="aksrt_category_cover_id" value="<?php echo esc_attr((string) $cover_id); ?>">
<button type="button" class="button" id="aksrt-pick-cover"><?php esc_html_e('选择图片', 'aksrt'); ?></button>
<button type="button" class="button" id="aksrt-remove-cover" <?php echo $cover_id > 0 ? '' : 'style="display:none;"'; ?>><?php esc_html_e('移除', 'aksrt'); ?></button>
<p class="description"><?php esc_html_e('分类推荐模块的封面图片。', 'aksrt'); ?></p>
</td>
</tr>
<tr class="form-field">
<th scope="row"><label for="aksrt_category_recommend_desc"><?php esc_html_e('推荐描述', 'aksrt'); ?></label></th>
<td>
<textarea id="aksrt_category_recommend_desc" name="aksrt_category_recommend_desc" rows="3" class="large-text"><?php echo esc_textarea($recommend_desc); ?></textarea>
<p class="description"><?php esc_html_e('分类推荐模块中显示的描述文字。留空则使用分类默认描述。', 'aksrt'); ?></p>
</td>
</tr>
<?php
}
/**
* 新增分类页面 - 显示自定义字段
*/
function aksrt_category_meta_fields_add() {
?>
<div class="form-field">
<label for="aksrt_category_cover_id"><?php esc_html_e('分类封面图', 'aksrt'); ?></label>
<div id="aksrt-cover-preview" style="margin-bottom:8px;"></div>
<input type="hidden" id="aksrt_category_cover_id" name="aksrt_category_cover_id" value="">
<button type="button" class="button" id="aksrt-pick-cover"><?php esc_html_e('选择图片', 'aksrt'); ?></button>
<button type="button" class="button" id="aksrt-remove-cover" style="display:none;"><?php esc_html_e('移除', 'aksrt'); ?></button>
<p class="description"><?php esc_html_e('分类推荐模块的封面图片。', 'aksrt'); ?></p>
</div>
<div class="form-field">
<label for="aksrt_category_recommend_desc"><?php esc_html_e('推荐描述', 'aksrt'); ?></label>
<textarea id="aksrt_category_recommend_desc" name="aksrt_category_recommend_desc" rows="3"></textarea>
<p class="description"><?php esc_html_e('分类推荐模块中显示的描述文字。留空则使用分类默认描述。', 'aksrt'); ?></p>
</div>
<?php
}
add_action('category_edit_form_fields', 'aksrt_category_meta_fields', 10, 1);
add_action('category_add_form_fields', 'aksrt_category_meta_fields_add', 10, 1);
/**
* 保存分类 term meta
*/
function aksrt_save_category_meta(int $term_id) {
if (!current_user_can('manage_categories')) return;
$cover_id = isset($_POST['aksrt_category_cover_id']) ? absint($_POST['aksrt_category_cover_id']) : 0;
$recommend_desc = isset($_POST['aksrt_category_recommend_desc']) ? sanitize_textarea_field(wp_unslash($_POST['aksrt_category_recommend_desc'])) : '';
if ($cover_id > 0) {
update_term_meta($term_id, 'aksrt_category_cover_id', $cover_id);
} else {
delete_term_meta($term_id, 'aksrt_category_cover_id');
}
if ($recommend_desc !== '') {
update_term_meta($term_id, 'aksrt_category_recommend_desc', $recommend_desc);
} else {
delete_term_meta($term_id, 'aksrt_category_recommend_desc');
}
if (class_exists('AKSRT_Settings')) {
AKSRT_Settings::flush_category_recommend_cache();
}
}
add_action('edited_category', 'aksrt_save_category_meta', 10, 1);
add_action('created_category', 'aksrt_save_category_meta', 10, 1);
/**
* 分类编辑页加载媒体库脚本(wp_enqueue_media 仅在 AKSRT 面板加载,此处补上)
*/
add_action('admin_enqueue_scripts', function (string $hook_suffix) {
if ($hook_suffix === 'edit-tags.php' || $hook_suffix === 'term.php') {
wp_enqueue_media();
}
});
/**
* 分类编辑页内联 JS - 媒体选择器
*/
function aksrt_category_meta_admin_js() {
$screen = get_current_screen();
if (!$screen || $screen->taxonomy !== 'category') return;
?>
<script>
jQuery(function($) {
var frame;
$(document).on('click', '#aksrt-pick-cover', function(e) {
e.preventDefault();
if (typeof wp === 'undefined' || !wp.media) return;
if (frame) { frame.open(); return; }
frame = wp.media({
title: '<?php echo esc_js(__('选择分类封面图', 'aksrt')); ?>',
button: { text: '<?php echo esc_js(__('使用此图片', 'aksrt')); ?>' },
multiple: false
});
frame.on('select', function() {
var attachment = frame.state().get('selection').first().toJSON();
$('#aksrt_category_cover_id').val(attachment.id);
$('#aksrt-cover-preview').html('<img src="' + attachment.url + '" style="max-width:200px;height:auto;border-radius:6px;" alt="">');
$('#aksrt-remove-cover').show();
});
frame.open();
});
$(document).on('click', '#aksrt-remove-cover', function(e) {
e.preventDefault();
$('#aksrt_category_cover_id').val('');
$('#aksrt-cover-preview').html('');
$(this).hide();
});
});
</script>
<?php
}
add_action('admin_footer', 'aksrt_category_meta_admin_js');
// ============================================================
// 资源归档:/resources 重写规则 + 排序参数注入
// 站内搜索:包含「资源」(post + _aksrt_is_resource)
// ============================================================
// 注册 /resources 虚拟归档
add_action('init', function () {
add_rewrite_rule('^resources/?$', 'index.php?aksrt_is_resource_archive=1', 'top');
add_rewrite_rule('^resources/page/([0-9]+)/?$', 'index.php?aksrt_is_resource_archive=1&paged=$matches[1]', 'top');
// 兼容历史 /resource/{slug}:301 到 /{slug}(默认 post 永久链接)
add_rewrite_rule('^resource/([^/]+)/?$', 'index.php?aksrt_legacy_resource=$matches[1]', 'top');
});
add_filter('query_vars', function ($vars) {
$vars[] = 'aksrt_is_resource_archive';
$vars[] = 'aksrt_legacy_resource';
$vars[] = 'aksrt_search_type';
return $vars;
});
// 旧资源链接 301 跳转
add_action('template_redirect', function () {
$slug = get_query_var('aksrt_legacy_resource');
if (!$slug) return;
// 先按 post 查
$page = get_page_by_path((string) $slug, OBJECT, 'post');
if (!$page && post_type_exists('aksrt_resource')) {
$page = get_page_by_path((string) $slug, OBJECT, 'aksrt_resource');
}
if ($page) {
wp_redirect(get_permalink($page->ID), 301);
exit;
}
});
add_action('pre_get_posts', function ($query) {
if (is_admin() || !$query->is_main_query()) return;
$is_resource_archive = (bool) $query->get('aksrt_is_resource_archive')
|| (post_type_exists('aksrt_resource') && $query->is_post_type_archive('aksrt_resource'));
// 资源归档页:把查询锁定到「post + _aksrt_is_resource = 1」(含历史 CPT 兼容)
if ($is_resource_archive) {
$query->set('post_type', function_exists('aksrt_resource_post_types') ? aksrt_resource_post_types() : ['post']);
$query->set('post_status', 'publish');
$orderby = isset($_GET['orderby']) ? sanitize_key((string) $_GET['orderby']) : 'date';
if ($orderby === 'downloads') {
$query->set('meta_key', AKSRT_Domain_Model::RESOURCE_DOWNLOAD_COUNT_KEY);
$query->set('orderby', 'meta_value_num');
$query->set('order', 'DESC');
} elseif ($orderby === 'views') {
$query->set('meta_key', AKSRT_Domain_Model::RESOURCE_VIEW_COUNT_KEY);
$query->set('orderby', 'meta_value_num');
$query->set('order', 'DESC');
}
// 价格筛选:?price=free | paid | all
// 价格区间:?price_min=0&price_max=99
$price_filter = isset($_GET['price']) ? sanitize_key((string) $_GET['price']) : '';
$price_min = isset($_GET['price_min']) && $_GET['price_min'] !== '' ? (float) $_GET['price_min'] : null;
$price_max = isset($_GET['price_max']) && $_GET['price_max'] !== '' ? (float) $_GET['price_max'] : null;
// 「是资源」基础约束:_aksrt_is_resource=1(兼容旧 CPT 时由 post_type 兜底)
$meta_query = [
'relation' => 'AND',
[
'relation' => 'OR',
// 新形态:post + meta=1
[
'key' => AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
'value' => '1',
'compare' => '=',
],
// 历史 CPT 兼容:通过 post_type 已被包含,meta 缺失也算资源
[
'key' => AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
'compare' => 'NOT EXISTS',
],
],
];
if ($price_filter === 'free') {
$meta_query[] = [
'relation' => 'OR',
[
'key' => AKSRT_Domain_Model::PAY_PRICE_KEY,
'value' => 0,
'compare' => '=',
'type' => 'NUMERIC',
],
[
'key' => AKSRT_Domain_Model::PAY_PRICE_KEY,
'compare' => 'NOT EXISTS',
],
];
} elseif ($price_filter === 'paid') {
$meta_query[] = [
'key' => AKSRT_Domain_Model::PAY_PRICE_KEY,
'value' => 0,
'compare' => '>',
'type' => 'NUMERIC',
];
}
if ($price_min !== null || $price_max !== null) {
if ($price_min !== null && $price_max !== null) {
$meta_query[] = [
'key' => AKSRT_Domain_Model::PAY_PRICE_KEY,
'value' => [$price_min, $price_max],
'compare' => 'BETWEEN',
'type' => 'NUMERIC',
];
} elseif ($price_min !== null) {
$meta_query[] = [
'key' => AKSRT_Domain_Model::PAY_PRICE_KEY,
'value' => $price_min,
'compare' => '>=',
'type' => 'NUMERIC',
];
} elseif ($price_max !== null) {
$meta_query[] = [
'key' => AKSRT_Domain_Model::PAY_PRICE_KEY,
'value' => $price_max,
'compare' => '<=',
'type' => 'NUMERIC',
];
}
}
// 仅在新形态(post)下加 _aksrt_is_resource=1 强约束;
// 旧 CPT 仍在站点时,post_type 已含 aksrt_resource,配合上面 OR 兼容逻辑即可。
if (!post_type_exists('aksrt_resource')) {
// 替换 OR 兼容片段为强等于
$meta_query[0] = [
'key' => AKSRT_Domain_Model::RESOURCE_MARKER_KEY,
'value' => '1',
'compare' => '=',
];
}
if (!empty($meta_query)) {
$existing = $query->get('meta_query');
if (!is_array($existing)) $existing = [];
$query->set('meta_query', array_merge($existing, $meta_query));
}
// 资源归档不要分类/标签限制
$query->set('cat', 0);
$query->set('tag', '');
}
// 站内搜索:post 已默认覆盖(资源就是 post),仅在仍有旧 CPT 时合并
if ($query->is_search() && empty($query->get('post_type'))) {
$search_type = sanitize_key((string) $query->get('aksrt_search_type'));
if (in_array($search_type, ['category', 'tag'], true)) {
$taxonomy = $search_type === 'category' ? 'category' : 'post_tag';
$term_ids = get_terms([
'taxonomy' => $taxonomy,
'hide_empty' => false,
'fields' => 'ids',
'number' => 20,
'search' => (string) $query->get('s'),
]);
$query->set('s', '');
if (is_wp_error($term_ids) || empty($term_ids)) {
$query->set('post__in', [0]);
} else {
$query->set('tax_query', [[
'taxonomy' => $taxonomy,
'field' => 'term_id',
'terms' => array_map('absint', $term_ids),
]]);
}
}
if (post_type_exists('aksrt_resource')) {
// 单一数据选择策略:搜索的内容类型与索引/推送共用同一 helper。
$search_types = function_exists('aksrt_searchable_post_types')
? aksrt_searchable_post_types()
: ['post'];
if (count($search_types) > 1) {
$query->set('post_type', $search_types);
}
}
}
// 分类 / 标签归档:post 已自然包含资源(meta 标记),仅当历史 CPT 仍在时才需合并 post_type
if (($query->is_category() || $query->is_tag()) && post_type_exists('aksrt_resource')) {
$existing = $query->get('post_type');
if (empty($existing)) {
$query->set('post_type', ['post', 'aksrt_resource']);
}
}
// v4 起主题统一为 CMS / 资源站形态,所有归档都允许包含资源帖。
// 历史的 blog/cms 双流分流(按 site_profile + term flow 排除资源)已移除。
});
// 资源归档页:套 archive-aksrt_resource.php 模板(v3.4 起作为「资源虚拟归档」复用)
add_filter('template_include', function ($template) {
if (!get_query_var('aksrt_is_resource_archive')) return $template;
$candidate = locate_template(['archive-aksrt_resource.php']);
return $candidate ?: $template;
});
// 资源归档页:标题
add_filter('document_title_parts', function ($parts) {
if (get_query_var('aksrt_is_resource_archive')) {
$parts['title'] = '资源';
}
return $parts;
});
// ============================================================