convert to plugins+theme

This commit is contained in:
OldTaoge
2026-07-21 09:55:39 +08:00
parent 803841cd12
commit aea6095c0a
57 changed files with 2742 additions and 1619 deletions
@@ -0,0 +1,67 @@
<?php
/**
* 归档扩展搜索:在新闻/公告归档搜索时,额外按作者显示名/登录名/昵称匹配。
* 由主题模板通过查询变量 itstudio_archive_extend_search / itstudio_archive_keyword 触发。
* 抽离自主题 functions.php。
*/
if (!defined('ABSPATH')) {
exit;
}
function itstudio_archive_extended_search_enabled($query) {
if (!($query instanceof WP_Query)) {
return false;
}
if (!$query->get('itstudio_archive_extend_search')) {
return false;
}
$keyword = trim((string) $query->get('itstudio_archive_keyword'));
return $keyword !== '';
}
function itstudio_archive_extended_search_join($join, $query) {
if (!itstudio_archive_extended_search_enabled($query)) {
return $join;
}
global $wpdb;
if (strpos($join, $wpdb->users) !== false) {
return $join;
}
return $join . " LEFT JOIN {$wpdb->users} ON ({$wpdb->posts}.post_author = {$wpdb->users}.ID) ";
}
add_filter('posts_join', 'itstudio_archive_extended_search_join', 10, 2);
function itstudio_archive_extended_search_where($where, $query) {
if (!itstudio_archive_extended_search_enabled($query)) {
return $where;
}
global $wpdb;
$keyword = trim((string) $query->get('itstudio_archive_keyword'));
$like = '%' . $wpdb->esc_like($keyword) . '%';
$search_sql = $wpdb->prepare(
" AND (
{$wpdb->posts}.post_title LIKE %s
OR {$wpdb->posts}.post_excerpt LIKE %s
OR {$wpdb->posts}.post_content LIKE %s
OR {$wpdb->users}.display_name LIKE %s
OR {$wpdb->users}.user_login LIKE %s
OR {$wpdb->users}.user_nicename LIKE %s
)",
$like,
$like,
$like,
$like,
$like,
$like
);
return $where . $search_sql;
}
add_filter('posts_where', 'itstudio_archive_extended_search_where', 10, 2);
@@ -0,0 +1,145 @@
<?php
/**
* 新闻 / 公告文章类型与内容权重字段。
* 抽离自主题 functions.php 的 itstudio_custom_post_types() 与 itstudio_register_acf_fields()。
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* 注册新闻、公告文章类型,并把标签绑定到它们。
* 便民服务(service / service_category)不在此插件,由 itstudio-services 负责。
*/
function itstudio_content_register_post_types() {
register_post_type('announcement', array(
'labels' => array(
'name' => __('公告', 'itstudio'),
'singular_name' => __('公告', 'itstudio'),
),
'public' => true,
'has_archive' => true,
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'custom-fields'),
'taxonomies' => array('post_tag'),
'menu_icon' => 'dashicons-megaphone',
'show_in_rest' => true,
));
register_post_type('news', array(
'labels' => array(
'name' => __('新闻', 'itstudio'),
'singular_name' => __('新闻', 'itstudio'),
),
'public' => true,
'has_archive' => 'news',
'rewrite' => array(
'slug' => 'news',
'with_front' => false,
),
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'custom-fields'),
'taxonomies' => array('post_tag'),
'menu_icon' => 'dashicons-media-document',
'show_in_rest' => true,
));
register_taxonomy_for_object_type('post_tag', 'announcement');
register_taxonomy_for_object_type('post_tag', 'news');
}
add_action('init', 'itstudio_content_register_post_types');
/**
* 内容权重 ACF 字段组:数值越大文章在侧栏排序越靠前。
* 招新文章标记(itstudio_is_recruitment_article)不在此插件,由 itstudio-recruitment 负责。
*/
function itstudio_content_register_acf_fields() {
if (!function_exists('acf_add_local_field_group')) {
return;
}
acf_add_local_field_group(array(
'key' => 'group_itstudio_content_priority',
'title' => '内容权重',
'fields' => array(
array(
'key' => 'field_itstudio_weight',
'label' => '权重',
'name' => 'itstudio_weight',
'type' => 'number',
'instructions' => '数值越大,文章在侧栏排序越靠前。',
'required' => 0,
'default_value' => 0,
'min' => 0,
'step' => 1,
),
),
'location' => array(
array(
array(
'param' => 'post_type',
'operator' => '==',
'value' => 'post',
),
),
array(
array(
'param' => 'post_type',
'operator' => '==',
'value' => 'announcement',
),
),
array(
array(
'param' => 'post_type',
'operator' => '==',
'value' => 'news',
),
),
),
'position' => 'side',
'style' => 'default',
'active' => true,
'show_in_rest' => 1,
));
}
add_action('acf/init', 'itstudio_content_register_acf_fields');
/**
* 新闻/公告在只有日期格式时补上时:分,提升列表时间精度。
*/
function itstudio_expand_news_notice_date_precision($the_date, $format, $post) {
$post_obj = $post instanceof WP_Post ? $post : get_post($post);
if (!($post_obj instanceof WP_Post)) {
return $the_date;
}
if (!in_array((string) $post_obj->post_type, array('announcement', 'news'), true)) {
return $the_date;
}
$format = (string) $format;
if ($format === '') {
$format = (string) get_option('date_format');
}
$lower = strtolower($format);
if (in_array($lower, array('c', 'r', 'u'), true)) {
return $the_date;
}
// Already has time precision.
if (preg_match('/[HhGgis]/', $format)) {
return $the_date;
}
$timestamp = function_exists('get_post_timestamp')
? get_post_timestamp($post_obj, 'date')
: false;
if (!$timestamp) {
return $the_date;
}
$format_with_time = rtrim($format) . ' H:i';
return wp_date($format_with_time, (int) $timestamp, wp_timezone());
}
add_filter('get_the_date', 'itstudio_expand_news_notice_date_precision', 10, 3);
@@ -0,0 +1,203 @@
<?php
/**
* 文章浏览量统计(含防机器人与去重 cookie)。
* 抽离自主题 functions.php。
*/
if (!defined('ABSPATH')) {
exit;
}
function itstudio_get_post_views($post_id) {
$post_id = (int) $post_id;
if ($post_id <= 0) {
return 0;
}
$meta_keys = array(
'post_views_count',
'itstudio_views',
'views',
'post_views',
'view_count',
'views_count',
);
$max_views = 0;
foreach ($meta_keys as $meta_key) {
$raw = get_post_meta($post_id, $meta_key, true);
if ($raw !== '' && is_numeric($raw)) {
$max_views = max($max_views, (int) $raw);
}
}
return max(0, $max_views);
}
function itstudio_is_probably_bot_request() {
$user_agent = strtolower(trim((string) ($_SERVER['HTTP_USER_AGENT'] ?? '')));
if ($user_agent === '') {
return true;
}
$bot_signatures = array(
'bot',
'spider',
'crawl',
'slurp',
'headless',
'preview',
'facebookexternalhit',
'discordbot',
'telegrambot',
'linkedinbot',
'applebot',
'googlebot',
'bingbot',
'wget',
'curl',
'python-requests',
'postmanruntime',
);
foreach ($bot_signatures as $signature) {
if (strpos($user_agent, $signature) !== false) {
return true;
}
}
$purpose = strtolower((string) ($_SERVER['HTTP_PURPOSE'] ?? ''));
$sec_purpose = strtolower((string) ($_SERVER['HTTP_SEC_PURPOSE'] ?? ''));
$x_moz = strtolower((string) ($_SERVER['HTTP_X_MOZ'] ?? ''));
if (strpos($purpose, 'prefetch') !== false || strpos($sec_purpose, 'prefetch') !== false || $x_moz === 'prefetch') {
return true;
}
return false;
}
function itstudio_get_view_cookie_name() {
return 'itstudio_viewed_posts';
}
function itstudio_read_view_cookie_map() {
$cookie_name = itstudio_get_view_cookie_name();
$raw_cookie = isset($_COOKIE[$cookie_name]) ? wp_unslash((string) $_COOKIE[$cookie_name]) : '';
if ($raw_cookie === '') {
return array();
}
$decoded = json_decode($raw_cookie, true);
if (!is_array($decoded)) {
return array();
}
$clean = array();
foreach ($decoded as $post_id => $timestamp) {
$post_id = (int) $post_id;
$timestamp = (int) $timestamp;
if ($post_id > 0 && $timestamp > 0) {
$clean[(string) $post_id] = $timestamp;
}
}
return $clean;
}
function itstudio_write_view_cookie_map($map, $window_seconds) {
if (!headers_sent()) {
$cookie_name = itstudio_get_view_cookie_name();
$expire_at = time() + max(DAY_IN_SECONDS, (int) $window_seconds * 2);
$path = defined('COOKIEPATH') && COOKIEPATH ? COOKIEPATH : '/';
$domain = defined('COOKIE_DOMAIN') ? COOKIE_DOMAIN : '';
$secure = is_ssl();
$http_only = true;
$encoded = wp_json_encode($map);
if (!is_string($encoded)) {
return;
}
if (PHP_VERSION_ID >= 70300) {
setcookie($cookie_name, $encoded, array(
'expires' => $expire_at,
'path' => $path,
'domain' => $domain,
'secure' => $secure,
'httponly' => $http_only,
'samesite' => 'Lax',
));
} else {
setcookie($cookie_name, $encoded, $expire_at, $path, $domain, $secure, $http_only);
}
$_COOKIE[$cookie_name] = $encoded;
}
}
function itstudio_should_count_post_view($post_id) {
$post_id = (int) $post_id;
if ($post_id <= 0) {
return false;
}
if (itstudio_is_probably_bot_request()) {
return false;
}
$window_seconds = (int) apply_filters('itstudio_post_view_window_seconds', 12 * HOUR_IN_SECONDS);
$window_seconds = max(60, $window_seconds);
$max_entries = (int) apply_filters('itstudio_post_view_cookie_max_entries', 120);
$max_entries = max(10, $max_entries);
$now = time();
$key = (string) $post_id;
$map = itstudio_read_view_cookie_map();
foreach ($map as $id => $timestamp) {
if (($now - (int) $timestamp) > $window_seconds) {
unset($map[$id]);
}
}
if (isset($map[$key]) && ($now - (int) $map[$key]) < $window_seconds) {
return false;
}
$map[$key] = $now;
if (count($map) > $max_entries) {
asort($map, SORT_NUMERIC);
$map = array_slice($map, -$max_entries, null, true);
}
itstudio_write_view_cookie_map($map, $window_seconds);
return true;
}
function itstudio_track_post_views() {
if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
return;
}
if (!is_singular(array('post', 'announcement', 'news')) || is_preview() || is_feed() || is_trackback() || is_embed()) {
return;
}
if (!isset($_SERVER['REQUEST_METHOD']) || strtoupper((string) $_SERVER['REQUEST_METHOD']) !== 'GET') {
return;
}
$post_id = (int) get_queried_object_id();
if ($post_id <= 0) {
return;
}
if (!itstudio_should_count_post_view($post_id)) {
return;
}
$views = itstudio_get_post_views($post_id) + 1;
update_post_meta($post_id, 'post_views_count', $views);
update_post_meta($post_id, 'itstudio_views', $views);
}
add_action('template_redirect', 'itstudio_track_post_views', 20);
@@ -0,0 +1,115 @@
<?php
/**
* 供主题模板调用的内容助手函数(权重、字数、摘要、归档 URL)。
* 函数名保持与主题历史一致,模板正在按原名调用它们。
* 抽离自主题 functions.php。
*/
if (!defined('ABSPATH')) {
exit;
}
function itstudio_get_post_weight($post_id, $field_name = 'itstudio_weight') {
$post_id = (int) $post_id;
if ($post_id <= 0) {
return 0;
}
if (!function_exists('get_field')) {
return 0;
}
$raw = get_field($field_name, $post_id, false);
if ($raw === '' || $raw === null || !is_numeric($raw)) {
return 0;
}
return (int) $raw;
}
function itstudio_get_post_char_count($post_id) {
$post_id = (int) $post_id;
if ($post_id <= 0) {
return 0;
}
$content = (string) get_post_field('post_content', $post_id);
$plain_text = trim(wp_strip_all_tags($content));
if ($plain_text === '') {
return 0;
}
if (function_exists('mb_strlen')) {
return (int) mb_strlen($plain_text, 'UTF-8');
}
return (int) strlen($plain_text);
}
function itstudio_get_post_excerpt_chars($post_id, $limit = 200) {
$post_id = (int) $post_id;
$limit = max(1, (int) $limit);
if ($post_id <= 0) {
return '';
}
$excerpt = (string) get_post_field('post_excerpt', $post_id);
if (trim($excerpt) === '') {
$excerpt = (string) get_post_field('post_content', $post_id);
}
$excerpt = trim(wp_strip_all_tags($excerpt));
if ($excerpt === '') {
return '';
}
return wp_html_excerpt($excerpt, $limit, '...');
}
function itstudio_get_archive_url_by_post_type($post_type) {
$post_type = sanitize_key((string) $post_type);
if ($post_type === '') {
return home_url('/');
}
$archive_url = get_post_type_archive_link($post_type);
if ($archive_url) {
return $archive_url;
}
if ($post_type === 'news') {
return home_url('/news/');
}
if ($post_type === 'announcement') {
return home_url('/announcement/');
}
return home_url('/');
}
function itstudio_get_archive_tag_filter_url($term, $post_type) {
$term_obj = null;
if ($term instanceof WP_Term) {
$term_obj = $term;
} else {
$term_obj = get_term($term, 'post_tag');
}
if (!($term_obj instanceof WP_Term) || is_wp_error($term_obj)) {
return '';
}
$post_type = sanitize_key((string) $post_type);
if (!in_array($post_type, array('news', 'announcement'), true)) {
return '';
}
$archive_url = itstudio_get_archive_url_by_post_type($post_type);
return add_query_arg(
array(
'tag' => $term_obj->slug,
),
$archive_url
);
}