__('Primary Menu', 'itstudio'), )); load_theme_textdomain('itstudio', get_template_directory() . '/languages'); } add_action('after_setup_theme', 'itstudio_theme_setup'); add_filter('comments_open', '__return_false', 20, 2); add_filter('pings_open', '__return_false', 20, 2); add_filter('comments_array', '__return_empty_array', 10, 2); function itstudio_disable_comments_post_types() { $post_types = array('post', 'page', 'announcement', 'news', 'service'); foreach ($post_types as $post_type) { if (post_type_supports($post_type, 'comments')) { remove_post_type_support($post_type, 'comments'); } if (post_type_supports($post_type, 'trackbacks')) { remove_post_type_support($post_type, 'trackbacks'); } } } add_action('init', 'itstudio_disable_comments_post_types', 100); function itstudio_hide_comments_menu() { remove_menu_page('edit-comments.php'); } add_action('admin_menu', 'itstudio_hide_comments_menu', 999); function itstudio_hide_admin_bar_comments() { remove_action('admin_bar_menu', 'wp_admin_bar_comments_menu', 60); } add_action('init', 'itstudio_hide_admin_bar_comments'); function itstudio_redirect_comments_admin_pages() { global $pagenow; if ($pagenow === 'edit-comments.php' || $pagenow === 'comment.php') { wp_safe_redirect(admin_url()); exit; } } add_action('admin_init', 'itstudio_redirect_comments_admin_pages'); function itstudio_remove_comments_dashboard_widget() { remove_meta_box('dashboard_recent_comments', 'dashboard', 'normal'); } add_action('wp_dashboard_setup', 'itstudio_remove_comments_dashboard_widget'); function itstudio_apply_site_identity() { $site_name = '爱特工作室'; $site_tagline = '爱特工作室官方网站'; if (get_option('blogname') !== $site_name) { update_option('blogname', $site_name); } if (get_option('blogdescription') !== $site_tagline) { update_option('blogdescription', $site_tagline); } } add_action('init', 'itstudio_apply_site_identity', 20); function itstudio_output_favicon() { $favicon_url = get_template_directory_uri() . '/resources/it_logo_2024.svg'; echo '' . "\n"; echo '' . "\n"; } function itstudio_output_theme_bootstrap_script() { echo '' . "\n"; echo '' . "\n"; } function itstudio_output_lang_bootstrap_script() { echo '' . "\n"; echo '' . "\n"; } function itstudio_disable_default_site_icon() { remove_action('wp_head', 'wp_site_icon', 99); remove_action('admin_head', 'wp_site_icon', 99); remove_action('login_head', 'wp_site_icon', 99); } add_action('init', 'itstudio_disable_default_site_icon'); add_action('wp_head', 'itstudio_output_theme_bootstrap_script', 0); add_action('wp_head', 'itstudio_output_lang_bootstrap_script', 1); add_action('wp_head', 'itstudio_output_favicon', 1); add_action('admin_head', 'itstudio_output_favicon', 1); add_action('login_head', 'itstudio_output_favicon', 1); function itstudio_enqueue_scripts() { // 基础样式 (Style.css) wp_enqueue_style('itstudio-style', get_stylesheet_uri(), array(), '2.1.2'); wp_enqueue_style('itstudio-header', get_template_directory_uri() . '/assets/css/header.css', array('itstudio-style'), '2.1.2'); wp_enqueue_style('itstudio-footer', get_template_directory_uri() . '/assets/css/footer.css', array('itstudio-style'), '2.1.2'); wp_enqueue_style('itstudio-content', get_template_directory_uri() . '/assets/css/content.css', array('itstudio-style'), '2.1.2'); // 仅在首页加载首页样式 if (is_front_page() || is_home()) { wp_enqueue_style('itstudio-front-page', get_template_directory_uri() . '/assets/css/front-page.css', array('itstudio-style'), '2.1.2'); wp_enqueue_script('itstudio-landing-hero-canvas', get_template_directory_uri() . '/assets/js/landing-hero-canvas.js', array(), '1.0.0', true); } // 仅在工作室介绍页加载(包含 /about fallback) $is_about = is_page('about') || is_page_template('page-about.php'); if (!$is_about && is_404()) { global $wp; $request = isset($wp->request) ? trim($wp->request, '/') : ''; $is_about = ($request === 'about'); } if ($is_about) { wp_enqueue_style('itstudio-about-hero', get_template_directory_uri() . '/assets/css/about-hero.css', array('itstudio-content'), '2.1.2'); wp_enqueue_script('itstudio-animejs', get_template_directory_uri() . '/assets/js/vendor/anime.min.js', array(), '3.2.2', true); wp_enqueue_script('itstudio-about-hero-waves', get_template_directory_uri() . '/assets/js/hero-waves.js', array('itstudio-animejs'), '1.0.0', true); wp_enqueue_script('itstudio-about-hero', get_template_directory_uri() . '/assets/js/home-hero.js', array('itstudio-animejs'), '1.0.0', true); } // 仅在便民服务页加载(包含 /services fallback) $is_services = is_page('services') || is_page_template('page-services.php'); if (!$is_services && is_404()) { global $wp; $request = isset($wp->request) ? trim($wp->request, '/') : ''; $is_services = ($request === 'services'); } if ($is_services) { wp_enqueue_style('itstudio-services-page', get_template_directory_uri() . '/assets/css/services-page.css', array('itstudio-content'), '1.0.0'); } // Scripts wp_enqueue_script('itstudio-theme-toggle', get_template_directory_uri() . '/assets/js/theme-toggle.js', array(), '1.0.0', true); wp_enqueue_script('itstudio-lang-toggle', get_template_directory_uri() . '/assets/js/lang-toggle.js', array(), '1.0.0', true); wp_enqueue_script('itstudio-main', get_template_directory_uri() . '/assets/js/main.js', array(), '1.0.0', true); if (is_singular(array('post', 'announcement', 'news'))) { wp_enqueue_script('itstudio-single-title-fit', get_template_directory_uri() . '/assets/js/single-title-fit.js', array(), '1.0.0', true); } } add_action('wp_enqueue_scripts', 'itstudio_enqueue_scripts'); function itstudio_register_sidebars() { register_sidebar(array( 'name' => __('Footer - Column 1', 'itstudio'), 'id' => 'footer-1', 'description' => __('Footer widget area 1', 'itstudio'), 'before_widget' => '', 'before_title' => '

', 'after_title' => '

', )); register_sidebar(array( 'name' => __('Footer - Column 2', 'itstudio'), 'id' => 'footer-2', 'description' => __('Footer widget area 2', 'itstudio'), 'before_widget' => '', 'before_title' => '

', 'after_title' => '

', )); } add_action('widgets_init', 'itstudio_register_sidebars'); function itstudio_intro_body_class($classes) { $is_about = is_page('about') || is_page_template('page-about.php'); if (!$is_about && is_404()) { global $wp; $request = isset($wp->request) ? trim($wp->request, '/') : ''; $is_about = ($request === 'about'); } if ($is_about) { $classes[] = 'intro-about'; } return $classes; } add_filter('body_class', 'itstudio_intro_body_class'); function itstudio_custom_post_types() { register_post_type('announcement', array( 'labels' => array( 'name' => __('Announcements', 'itstudio'), 'singular_name' => __('Announcement', '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' => __('News', 'itstudio'), 'singular_name' => __('News', '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('service_category', array('service'), array( 'labels' => array( 'name' => __('服务分类', 'itstudio'), 'singular_name' => __('服务分类', 'itstudio'), ), 'hierarchical' => true, 'public' => true, 'show_ui' => true, 'show_admin_column' => false, 'show_in_rest' => true, 'rewrite' => array( 'slug' => 'service-category', 'with_front' => false, ), )); register_post_type('service', array( 'labels' => array( 'name' => __('便民服务', 'itstudio'), 'singular_name' => __('便民服务', 'itstudio'), 'menu_name' => __('便民服务', 'itstudio'), 'add_new_item' => __('新增便民服务', 'itstudio'), 'edit_item' => __('编辑便民服务', 'itstudio'), ), 'public' => true, 'show_ui' => true, 'show_in_menu' => true, 'show_in_nav_menus' => false, 'exclude_from_search' => true, 'publicly_queryable' => true, 'has_archive' => false, 'rewrite' => array( 'slug' => 'service', 'with_front' => false, ), 'supports' => array('title', 'excerpt', 'thumbnail', 'page-attributes'), 'taxonomies' => array('service_category'), 'menu_icon' => 'dashicons-admin-tools', 'show_in_rest' => true, )); register_taxonomy_for_object_type('post_tag', 'announcement'); register_taxonomy_for_object_type('post_tag', 'news'); } add_action('init', 'itstudio_custom_post_types'); function itstudio_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, )); acf_add_local_field_group(array( 'key' => 'group_itstudio_recruitment_article', 'title' => '招新文章标记', 'fields' => array( array( 'key' => 'field_itstudio_is_recruitment_article', 'label' => '是否为招新文章', 'name' => 'itstudio_is_recruitment_article', 'type' => 'true_false', 'instructions' => '勾选后,该文章会在“加入我们”页面顶部新闻条中显示(按发布时间排序)。', 'required' => 0, 'default_value' => 0, 'ui' => 1, 'ui_on_text' => '是', 'ui_off_text' => '否', ), ), 'location' => array( array( array( 'param' => 'post_type', 'operator' => '==', 'value' => 'announcement', ), ), array( array( 'param' => 'post_type', 'operator' => '==', 'value' => 'news', ), ), array( array( 'param' => 'post_type', 'operator' => '==', 'value' => 'post', ), ), ), 'position' => 'side', 'style' => 'default', 'active' => true, 'show_in_rest' => 1, )); } add_action('acf/init', 'itstudio_register_acf_fields'); function itstudio_get_recruitment_article_meta_key() { return 'itstudio_is_recruitment_article'; } function itstudio_normalize_recruitment_flag($value) { if (is_bool($value)) { return $value; } if (is_numeric($value)) { return ((int) $value) > 0; } $value = strtolower(trim((string) $value)); return in_array($value, array('1', 'true', 'yes', 'on', 'y'), true); } function itstudio_is_recruitment_article($post_id) { $post_id = (int) $post_id; if ($post_id <= 0) { return false; } $meta_key = itstudio_get_recruitment_article_meta_key(); $meta_keys = array( $meta_key, '_itstudio_is_recruitment_article', 'itstudio_join_article', 'join_recruitment_article', 'is_recruitment_article', ); foreach ($meta_keys as $key) { $raw = get_post_meta($post_id, $key, true); if ($raw !== '' && $raw !== null && itstudio_normalize_recruitment_flag($raw)) { return true; } } if (function_exists('get_field')) { $acf_keys = array( 'itstudio_is_recruitment_article', 'is_recruitment_article', 'itstudio_join_article', ); foreach ($acf_keys as $acf_key) { $raw = get_field($acf_key, $post_id, false); if ($raw !== '' && $raw !== null && itstudio_normalize_recruitment_flag($raw)) { return true; } } } return false; } function itstudio_add_recruitment_meta_boxes() { $screens = array('announcement', 'news', 'post'); foreach ($screens as $screen) { add_meta_box( 'itstudio_recruitment_flag', __('招新文章', 'itstudio'), 'itstudio_render_recruitment_meta_box', $screen, 'side', 'high' ); } } add_action('add_meta_boxes', 'itstudio_add_recruitment_meta_boxes'); function itstudio_render_recruitment_meta_box($post) { $meta_key = itstudio_get_recruitment_article_meta_key(); $checked = itstudio_is_recruitment_article((int) $post->ID); wp_nonce_field('itstudio_save_recruitment_meta', 'itstudio_recruitment_meta_nonce'); ?>

format('Y'); $display_year = isset($join_runtime['recruitment_year']) && is_numeric($join_runtime['recruitment_year']) ? (int) $join_runtime['recruitment_year'] : $now_year; $season_start_ts = null; $season_end_ts = null; $stages = isset($join_runtime['stages']) && is_array($join_runtime['stages']) ? $join_runtime['stages'] : array(); foreach ($stages as $stage) { if (!is_array($stage)) { continue; } $start_ts = isset($stage['start_ts']) && is_numeric($stage['start_ts']) ? (int) $stage['start_ts'] : null; $end_ts = isset($stage['end_ts']) && is_numeric($stage['end_ts']) ? (int) $stage['end_ts'] : null; if ($start_ts !== null && ($season_start_ts === null || $start_ts < $season_start_ts)) { $season_start_ts = $start_ts; } if ($end_ts !== null && ($season_end_ts === null || $end_ts > $season_end_ts)) { $season_end_ts = $end_ts; } } $now_ts = (int) (isset($join_runtime['now_ts']) && is_numeric($join_runtime['now_ts']) ? $join_runtime['now_ts'] : ((int) $now->format('U') * 1000)); if ($season_start_ts !== null && $now_ts < $season_start_ts && $display_year > $now_year) { // 下年招新未开始:继续展示本年招新资讯 $display_year = $now_year; } $query = new WP_Query(array( 'post_type' => array('announcement', 'news', 'post'), 'post_status' => 'publish', 'posts_per_page' => 80, 'orderby' => 'date', 'order' => 'DESC', 'no_found_rows' => true, 'ignore_sticky_posts' => true, 'date_query' => array( array( 'year' => $display_year, ), ), )); $items = array(); if ($query->have_posts()) { foreach ($query->posts as $post) { if (count($items) >= $limit) { break; } $post_id = (int) $post->ID; if (!itstudio_is_recruitment_article($post_id)) { continue; } $title = trim((string) get_the_title($post_id)); $url = (string) get_permalink($post_id); if ($title === '' || $url === '') { continue; } $excerpt = function_exists('itstudio_get_post_excerpt_chars') ? itstudio_get_post_excerpt_chars($post_id, 72) : wp_html_excerpt(trim(wp_strip_all_tags((string) get_post_field('post_excerpt', $post_id))), 72, '...'); if ($excerpt === '') { $excerpt = '...'; } $items[] = array( 'id' => $post_id, 'title' => $title, 'excerpt' => $excerpt, 'url' => $url, 'date' => get_the_date('Y-m-d', $post_id), 'type' => get_post_type($post_id), ); } } wp_reset_postdata(); return array( 'display_year' => $display_year, 'items' => $items, ); } function itstudio_get_service_url_meta_key() { return '_itstudio_service_url'; } function itstudio_get_service_title_cn_meta_key() { return '_itstudio_service_title_cn'; } function itstudio_get_service_title_en_meta_key() { return '_itstudio_service_title_en'; } function itstudio_get_service_excerpt_cn_meta_key() { return '_itstudio_service_excerpt_cn'; } function itstudio_get_service_excerpt_en_meta_key() { return '_itstudio_service_excerpt_en'; } function itstudio_get_service_category_name_cn_meta_key() { return 'itstudio_service_category_name_cn'; } function itstudio_get_service_category_name_en_meta_key() { return 'itstudio_service_category_name_en'; } function itstudio_get_service_category_i18n_labels($term) { if (!$term || is_wp_error($term)) { return array( 'cn' => '未分类', 'en' => 'Uncategorized', ); } $term_id = (int) $term->term_id; $name_cn = trim((string) get_term_meta($term_id, itstudio_get_service_category_name_cn_meta_key(), true)); $name_en = trim((string) get_term_meta($term_id, itstudio_get_service_category_name_en_meta_key(), true)); if ($name_cn === '') { $name_cn = (string) $term->name; } if ($name_en === '') { $name_en = $name_cn; } return array( 'cn' => $name_cn, 'en' => $name_en, ); } function itstudio_get_service_i18n_content($service_id, $excerpt_limit = 90) { $service_id = (int) $service_id; if ($service_id <= 0) { return array( 'title_cn' => '', 'title_en' => '', 'excerpt_cn' => '', 'excerpt_en' => '', ); } $title_cn = trim((string) get_post_meta($service_id, itstudio_get_service_title_cn_meta_key(), true)); $title_en = trim((string) get_post_meta($service_id, itstudio_get_service_title_en_meta_key(), true)); $excerpt_cn = trim((string) get_post_meta($service_id, itstudio_get_service_excerpt_cn_meta_key(), true)); $excerpt_en = trim((string) get_post_meta($service_id, itstudio_get_service_excerpt_en_meta_key(), true)); $fallback_title = trim((string) get_the_title($service_id)); if ($fallback_title === '') { $fallback_title = '未命名服务'; } $fallback_excerpt = function_exists('itstudio_get_post_excerpt_chars') ? itstudio_get_post_excerpt_chars($service_id, $excerpt_limit) : wp_html_excerpt(wp_strip_all_tags((string) get_post_field('post_excerpt', $service_id)), $excerpt_limit, '...'); $fallback_excerpt = trim((string) $fallback_excerpt); if ($fallback_excerpt === '') { $fallback_excerpt = '暂无简介'; } if ($title_cn === '') { $title_cn = $fallback_title; } if ($title_en === '') { $title_en = $title_cn; } if ($excerpt_cn === '') { $excerpt_cn = $fallback_excerpt; } if ($excerpt_en === '') { $excerpt_en = $excerpt_cn; } return array( 'title_cn' => $title_cn, 'title_en' => $title_en, 'excerpt_cn' => $excerpt_cn, 'excerpt_en' => $excerpt_en, ); } function itstudio_add_service_meta_boxes() { add_meta_box( 'itstudio_service_link', __('服务双语与链接', 'itstudio'), 'itstudio_render_service_link_meta_box', 'service', 'normal', 'high' ); } add_action('add_meta_boxes', 'itstudio_add_service_meta_boxes'); function itstudio_render_service_link_meta_box($post) { $service_url = (string) get_post_meta($post->ID, itstudio_get_service_url_meta_key(), true); $title_cn = (string) get_post_meta($post->ID, itstudio_get_service_title_cn_meta_key(), true); $title_en = (string) get_post_meta($post->ID, itstudio_get_service_title_en_meta_key(), true); $excerpt_cn = (string) get_post_meta($post->ID, itstudio_get_service_excerpt_cn_meta_key(), true); $excerpt_en = (string) get_post_meta($post->ID, itstudio_get_service_excerpt_en_meta_key(), true); wp_nonce_field('itstudio_save_service_meta', 'itstudio_service_meta_nonce'); ?>

trim($title_cn), itstudio_get_service_title_en_meta_key() => trim($title_en), itstudio_get_service_excerpt_cn_meta_key() => trim($excerpt_cn), itstudio_get_service_excerpt_en_meta_key() => trim($excerpt_en), ); foreach ($meta_updates as $meta_key => $value) { if ($value === '') { delete_post_meta($post_id, $meta_key); } else { update_post_meta($post_id, $meta_key, $value); } } $url_meta_key = itstudio_get_service_url_meta_key(); $raw_url = isset($_POST['itstudio_service_url']) ? trim((string) wp_unslash($_POST['itstudio_service_url'])) : ''; if ($raw_url === '') { delete_post_meta($post_id, $url_meta_key); return; } if (strpos($raw_url, '/') === 0) { $raw_url = home_url($raw_url); } $service_url = esc_url_raw($raw_url); if ($service_url === '') { delete_post_meta($post_id, $url_meta_key); return; } update_post_meta($post_id, $url_meta_key, $service_url); } add_action('save_post_service', 'itstudio_save_service_meta'); function itstudio_service_category_add_fields($taxonomy) { wp_nonce_field('itstudio_save_service_category_meta', 'itstudio_service_category_meta_nonce'); ?>
term_id; $name_cn = (string) get_term_meta($term_id, itstudio_get_service_category_name_cn_meta_key(), true); $name_en = (string) get_term_meta($term_id, itstudio_get_service_category_name_en_meta_key(), true); wp_nonce_field('itstudio_save_service_category_meta', 'itstudio_service_category_meta_nonce'); ?> trim($name_cn), itstudio_get_service_category_name_en_meta_key() => trim($name_en), ); foreach ($term_meta as $meta_key => $value) { if ($value === '') { delete_term_meta($term_id, $meta_key); } else { update_term_meta($term_id, $meta_key, $value); } } } add_action('created_service_category', 'itstudio_save_service_category_meta'); add_action('edited_service_category', 'itstudio_save_service_category_meta'); function itstudio_get_service_target_url($service_id) { $service_id = (int) $service_id; if ($service_id <= 0) { return ''; } $meta_key = itstudio_get_service_url_meta_key(); $url = (string) get_post_meta($service_id, $meta_key, true); if ($url !== '') { return $url; } return (string) get_permalink($service_id); } function itstudio_service_admin_columns($columns) { $columns['service_category'] = __('分类', 'itstudio'); $columns['service_url'] = __('跳转链接', 'itstudio'); return $columns; } add_filter('manage_service_posts_columns', 'itstudio_service_admin_columns'); function itstudio_service_admin_custom_column($column, $post_id) { if ($column === 'service_category') { $terms = get_the_terms($post_id, 'service_category'); if (empty($terms) || is_wp_error($terms)) { echo '' . esc_html__('未分类', 'itstudio') . ''; return; } $labels = array(); foreach ($terms as $term) { $i18n = itstudio_get_service_category_i18n_labels($term); $label = $i18n['cn']; if ($i18n['en'] !== $i18n['cn']) { $label .= ' / ' . $i18n['en']; } $labels[] = $label; } echo esc_html(implode(' | ', $labels)); return; } if ($column === 'service_url') { $url = itstudio_get_service_target_url($post_id); if ($url === '') { echo '-'; return; } echo '' . esc_html($url) . ''; } } add_action('manage_service_posts_custom_column', 'itstudio_service_admin_custom_column', 10, 2); function itstudio_archive_document_title($parts) { if (is_post_type_archive('announcement')) { $parts['title'] = '公告通知'; } elseif (is_post_type_archive('news')) { $parts['title'] = '社团新闻'; } return $parts; } add_filter('document_title_parts', 'itstudio_archive_document_title', 20); // Fallback: render /about even if the page isn't created in WP admin. function itstudio_about_fallback() { if (!is_404()) { return; } global $wp; $request = isset($wp->request) ? trim($wp->request, '/') : ''; if ($request !== 'about') { return; } $template = locate_template('page-about.php'); if ($template) { global $wp_query; if ($wp_query) { $wp_query->is_404 = false; $wp_query->is_page = true; $wp_query->is_singular = true; $virtual_post = new WP_Post((object) array( 'ID' => 0, 'post_type' => 'page', 'post_parent' => 0, 'post_title' => __('工作室介绍', 'itstudio'), 'post_status' => 'publish', 'post_name' => 'about', 'post_content' => '', )); $wp_query->post = $virtual_post; $wp_query->posts = array($virtual_post); $wp_query->queried_object = $virtual_post; $wp_query->queried_object_id = 0; $wp_query->post_count = 1; $wp_query->found_posts = 1; $wp_query->max_num_pages = 1; global $post; $post = $virtual_post; setup_postdata($post); } add_filter('document_title_parts', function ($parts) { $parts['title'] = __('工作室介绍', 'itstudio'); return $parts; }); status_header(200); nocache_headers(); include $template; exit; } } add_action('template_redirect', 'itstudio_about_fallback'); // Fallback: render /news via archive.php even if the page isn't created in WP admin. function itstudio_news_fallback() { if (!is_404()) { return; } global $wp; $request = isset($wp->request) ? trim($wp->request, '/') : ''; if ($request !== 'news') { return; } $template = locate_template('archive.php'); if ($template) { global $wp_query; if ($wp_query) { $wp_query->is_404 = false; $wp_query->is_page = true; $wp_query->is_singular = true; $virtual_post = new WP_Post((object) array( 'ID' => 0, 'post_type' => 'page', 'post_parent' => 0, 'post_title' => __('社团新闻', 'itstudio'), 'post_status' => 'publish', 'post_name' => 'news', 'post_content' => '', )); $wp_query->post = $virtual_post; $wp_query->posts = array($virtual_post); $wp_query->queried_object = $virtual_post; $wp_query->queried_object_id = 0; $wp_query->post_count = 1; $wp_query->found_posts = 1; $wp_query->max_num_pages = 1; global $post; $post = $virtual_post; setup_postdata($post); } add_filter('document_title_parts', function ($parts) { $parts['title'] = __('社团新闻', 'itstudio'); return $parts; }); $GLOBALS['itstudio_archive_mode'] = 'news'; status_header(200); nocache_headers(); include $template; unset($GLOBALS['itstudio_archive_mode']); exit; } } add_action('template_redirect', 'itstudio_news_fallback'); // Fallback: render /services even if the page isn't created in WP admin. function itstudio_services_fallback() { if (!is_404()) { return; } global $wp; $request = isset($wp->request) ? trim($wp->request, '/') : ''; if ($request !== 'services') { return; } $template = locate_template('page-services.php'); if ($template) { global $wp_query; if ($wp_query) { $wp_query->is_404 = false; $wp_query->is_page = true; $wp_query->is_singular = true; $virtual_post = new WP_Post((object) array( 'ID' => 0, 'post_type' => 'page', 'post_parent' => 0, 'post_title' => __('便民服务', 'itstudio'), 'post_status' => 'publish', 'post_name' => 'services', 'post_content' => '', )); $wp_query->post = $virtual_post; $wp_query->posts = array($virtual_post); $wp_query->queried_object = $virtual_post; $wp_query->queried_object_id = 0; $wp_query->post_count = 1; $wp_query->found_posts = 1; $wp_query->max_num_pages = 1; global $post; $post = $virtual_post; setup_postdata($post); } add_filter('document_title_parts', function ($parts) { $parts['title'] = __('便民服务', 'itstudio'); return $parts; }); status_header(200); nocache_headers(); include $template; exit; } } add_action('template_redirect', 'itstudio_services_fallback'); 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_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_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); function itstudio_is_join_page_context() { $is_join = is_page('join') || is_page_template('page-join.php'); if (!$is_join && is_404()) { global $wp; $request = isset($wp->request) ? trim((string) $wp->request, '/') : ''; $is_join = ($request === 'join'); } return $is_join; } function itstudio_join_get_default_settings() { return array( 'registration_start' => '', 'registration_end' => '', 'first_interview_date' => '', 'first_interview_end' => '', 'first_interview_location_cn' => '', 'first_interview_location_en' => '', 'second_interview_date' => '', 'second_interview_end' => '', 'second_interview_location_cn' => '', 'second_interview_location_en' => '', 'assessment_start_date' => '', 'assessment_end_date' => '', 'notice_start_date' => '', 'result_registration_records' => '', 'result_first_interview_records' => '', 'result_assessment_records' => '', 'result_second_interview_records' => '', 'result_admission_records' => '', 'photo_registration' => 0, 'photo_first_interview' => 0, 'photo_assessment' => 0, 'photo_second_interview' => 0, 'photo_public_notice' => 0, 'photo_inactive' => 0, 'signup_form_shortcode' => '', ); } function itstudio_join_get_photo_field_map() { return array( 'registration' => 'photo_registration', 'first_interview' => 'photo_first_interview', 'assessment' => 'photo_assessment', 'second_interview' => 'photo_second_interview', 'public_notice' => 'photo_public_notice', 'inactive' => 'photo_inactive', ); } function itstudio_join_sanitize_datetime_local_value($value) { $value = trim((string) $value); if ($value === '') { return ''; } $value = preg_replace('/\s+/', 'T', $value); // 兼容旧数据:仅日期 if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) { return $value; } // 兼容输入:YYYY-MM-DDTHH:MM 或 YYYY-MM-DDTHH:MM:SS if (preg_match('/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})(:\d{2})?$/', $value, $matches)) { return $matches[1] . 'T' . $matches[2]; } return ''; } function itstudio_join_sanitize_date_value($value) { $value = trim((string) $value); if ($value === '') { return ''; } if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) { return ''; } return $value; } function itstudio_join_sanitize_shortcode_value($value) { if (!is_string($value)) { return ''; } return trim(sanitize_text_field(wp_unslash($value))); } function itstudio_join_sanitize_records_value($value) { if (!is_string($value)) { return ''; } $value = sanitize_textarea_field(wp_unslash($value)); $value = str_replace(array("\r\n", "\r"), "\n", $value); $lines = explode("\n", $value); $clean_lines = array(); foreach ($lines as $line) { $line = trim((string) $line); if ($line === '') { continue; } $clean_lines[] = $line; } return implode("\n", $clean_lines); } function itstudio_join_sanitize_settings($input) { $defaults = itstudio_join_get_default_settings(); $input = is_array($input) ? $input : array(); $sanitized = array( 'registration_start' => itstudio_join_sanitize_datetime_local_value($input['registration_start'] ?? ''), 'registration_end' => itstudio_join_sanitize_datetime_local_value($input['registration_end'] ?? ''), 'first_interview_date' => itstudio_join_sanitize_datetime_local_value($input['first_interview_date'] ?? ''), 'first_interview_end' => itstudio_join_sanitize_datetime_local_value($input['first_interview_end'] ?? ''), 'first_interview_location_cn' => itstudio_join_sanitize_shortcode_value($input['first_interview_location_cn'] ?? ''), 'first_interview_location_en' => itstudio_join_sanitize_shortcode_value($input['first_interview_location_en'] ?? ''), 'second_interview_date' => itstudio_join_sanitize_datetime_local_value($input['second_interview_date'] ?? ''), 'second_interview_end' => itstudio_join_sanitize_datetime_local_value($input['second_interview_end'] ?? ''), 'second_interview_location_cn' => itstudio_join_sanitize_shortcode_value($input['second_interview_location_cn'] ?? ''), 'second_interview_location_en' => itstudio_join_sanitize_shortcode_value($input['second_interview_location_en'] ?? ''), 'assessment_start_date' => itstudio_join_sanitize_date_value($input['assessment_start_date'] ?? ''), 'assessment_end_date' => itstudio_join_sanitize_date_value($input['assessment_end_date'] ?? ''), 'notice_start_date' => itstudio_join_sanitize_date_value($input['notice_start_date'] ?? ''), 'result_registration_records' => itstudio_join_sanitize_records_value($input['result_registration_records'] ?? ''), 'result_first_interview_records' => itstudio_join_sanitize_records_value($input['result_first_interview_records'] ?? ''), 'result_assessment_records' => itstudio_join_sanitize_records_value($input['result_assessment_records'] ?? ''), 'result_second_interview_records' => itstudio_join_sanitize_records_value($input['result_second_interview_records'] ?? ''), 'result_admission_records' => itstudio_join_sanitize_records_value($input['result_admission_records'] ?? ''), 'signup_form_shortcode' => itstudio_join_sanitize_shortcode_value($input['signup_form_shortcode'] ?? ''), ); foreach (itstudio_join_get_photo_field_map() as $field_key) { $sanitized[$field_key] = isset($input[$field_key]) ? absint($input[$field_key]) : 0; } return array_merge($defaults, $sanitized); } function itstudio_join_get_settings() { $defaults = itstudio_join_get_default_settings(); $stored = get_option('itstudio_join_settings', array()); if (!is_array($stored)) { return $defaults; } return array_merge($defaults, itstudio_join_sanitize_settings($stored)); } function itstudio_join_parse_datetime_local($value, $date_only_as_end_of_day = false) { $value = itstudio_join_sanitize_datetime_local_value($value); if ($value === '') { return null; } $timezone = wp_timezone(); $format = 'Y-m-d\TH:i'; if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) { $format = 'Y-m-d'; } $parsed = DateTimeImmutable::createFromFormat('!' . $format, $value, $timezone); $errors = DateTimeImmutable::getLastErrors(); if (!($parsed instanceof DateTimeImmutable)) { return null; } if (is_array($errors) && (($errors['warning_count'] ?? 0) > 0 || ($errors['error_count'] ?? 0) > 0)) { return null; } if ($format === 'Y-m-d') { return $date_only_as_end_of_day ? $parsed->setTime(23, 59, 59) : $parsed->setTime(0, 0, 0); } return $parsed; } function itstudio_join_parse_date($value) { $value = itstudio_join_sanitize_date_value($value); if ($value === '') { return null; } $timezone = wp_timezone(); $parsed = DateTimeImmutable::createFromFormat('!Y-m-d', $value, $timezone); $errors = DateTimeImmutable::getLastErrors(); if (!($parsed instanceof DateTimeImmutable)) { return null; } if (is_array($errors) && (($errors['warning_count'] ?? 0) > 0 || ($errors['error_count'] ?? 0) > 0)) { return null; } return $parsed; } function itstudio_join_to_datetime_local_input_value($value, $date_only_as_end_of_day = false) { $parsed = itstudio_join_parse_datetime_local($value, $date_only_as_end_of_day); return $parsed instanceof DateTimeImmutable ? $parsed->format('Y-m-d\TH:i') : ''; } function itstudio_join_to_date_input_value($value) { $parsed = itstudio_join_parse_date($value); if (!($parsed instanceof DateTimeImmutable)) { $parsed = itstudio_join_parse_datetime_local($value, false); } return $parsed instanceof DateTimeImmutable ? $parsed->format('Y-m-d') : ''; } function itstudio_join_get_result_field_map() { return array( 'registration' => 'result_registration_records', 'first_interview' => 'result_first_interview_records', 'assessment' => 'result_assessment_records', 'second_interview' => 'result_second_interview_records', 'public_notice' => 'result_admission_records', ); } function itstudio_join_has_uploaded_result_for_stage($settings, $stage_key) { $settings = is_array($settings) ? $settings : array(); $field_map = itstudio_join_get_result_field_map(); $field_key = isset($field_map[$stage_key]) ? $field_map[$stage_key] : ''; if ($field_key === '') { return false; } return trim((string) ($settings[$field_key] ?? '')) !== ''; } function itstudio_join_normalize_lookup_value($field, $value) { $field = trim((string) $field); $value = trim((string) $value); if ($value === '') { return ''; } if ($field === 'qq') { return preg_replace('/\D+/', '', $value); } if ($field === 'email') { return strtolower($value); } if ($field === 'student_id') { return strtoupper(preg_replace('/\s+/u', '', $value)); } $value = preg_replace('/\s+/u', '', $value); if (function_exists('mb_strtolower')) { return mb_strtolower($value, 'UTF-8'); } return strtolower($value); } function itstudio_join_parse_result_records($raw) { $raw = str_replace(array("\r\n", "\r"), "\n", (string) $raw); if ($raw === '') { return array(); } $records = array(); $lines = explode("\n", $raw); foreach ($lines as $line) { $line = trim((string) $line); if ($line === '' || strpos($line, '#') === 0) { continue; } $header_probe = preg_replace('/\s+/u', '', $line); $header_probe = function_exists('mb_strtolower') ? mb_strtolower($header_probe, 'UTF-8') : strtolower($header_probe); $is_cn_header = (strpos($header_probe, '姓名') !== false) && (strpos($header_probe, 'qq') !== false || strpos($header_probe, '邮箱') !== false || strpos($header_probe, '学号') !== false); $is_en_header = (strpos($header_probe, 'name') !== false) && (strpos($header_probe, 'qq') !== false || strpos($header_probe, 'email') !== false || strpos($header_probe, 'student') !== false); if ($is_cn_header || $is_en_header) { continue; } $parts = preg_split('/[,\|,\t]+/u', $line); $parts = is_array($parts) ? array_values(array_filter(array_map('trim', $parts), static function ($item) { return $item !== ''; })) : array(); if (empty($parts)) { continue; } $name = ''; $qq = ''; $email = ''; $student_id = ''; if (count($parts) === 1) { $single = (string) $parts[0]; if (strpos($single, '@') !== false) { $email = $single; } elseif (preg_match('/^\d{5,}$/', $single)) { $qq = $single; } else { $name = $single; } } else { $name = (string) ($parts[0] ?? ''); $qq = (string) ($parts[1] ?? ''); $email = (string) ($parts[2] ?? ''); $student_id = (string) ($parts[3] ?? ''); } $record = array( 'name' => itstudio_join_normalize_lookup_value('name', $name), 'qq' => itstudio_join_normalize_lookup_value('qq', $qq), 'email' => itstudio_join_normalize_lookup_value('email', $email), 'student_id' => itstudio_join_normalize_lookup_value('student_id', $student_id), ); if ($record['name'] === '' && $record['qq'] === '' && $record['email'] === '' && $record['student_id'] === '') { continue; } $records[] = $record; } return $records; } function itstudio_join_record_matches_query($record, $query) { $record = is_array($record) ? $record : array(); $query = is_array($query) ? $query : array(); $has_any_query = false; foreach (array('name', 'qq', 'email', 'student_id') as $field) { $q = trim((string) ($query[$field] ?? '')); if ($q === '') { continue; } $has_any_query = true; $r = trim((string) ($record[$field] ?? '')); if ($r === '' || $r !== $q) { return false; } } return $has_any_query; } function itstudio_join_find_record_in_stage_results($settings, $stage_key, $query) { $settings = is_array($settings) ? $settings : array(); $field_map = itstudio_join_get_result_field_map(); $field_key = isset($field_map[$stage_key]) ? $field_map[$stage_key] : ''; if ($field_key === '') { return false; } $raw = (string) ($settings[$field_key] ?? ''); if (trim($raw) === '') { return false; } $records = itstudio_join_parse_result_records($raw); if (empty($records)) { return false; } foreach ($records as $record) { if (itstudio_join_record_matches_query($record, $query)) { return true; } } return false; } function itstudio_join_get_stage_status_by_key($runtime, $stage_key) { $runtime = is_array($runtime) ? $runtime : array(); $stages = isset($runtime['stages']) && is_array($runtime['stages']) ? $runtime['stages'] : array(); foreach ($stages as $stage) { if (!is_array($stage)) { continue; } if ((string) ($stage['key'] ?? '') === (string) $stage_key) { return (string) ($stage['status'] ?? 'pending'); } } return 'pending'; } function itstudio_join_resolve_progress_lookup($runtime = array(), $request_source = null) { $runtime = is_array($runtime) ? $runtime : array(); $settings = isset($runtime['settings']) && is_array($runtime['settings']) ? $runtime['settings'] : itstudio_join_get_settings(); $request_source = is_array($request_source) ? $request_source : $_GET; $raw_name = isset($request_source['join_query_name']) ? sanitize_text_field(wp_unslash((string) $request_source['join_query_name'])) : ''; $raw_qq = isset($request_source['join_query_qq']) ? sanitize_text_field(wp_unslash((string) $request_source['join_query_qq'])) : ''; $raw_email = isset($request_source['join_query_email']) ? sanitize_text_field(wp_unslash((string) $request_source['join_query_email'])) : ''; $raw_student_id = isset($request_source['join_query_student_id']) ? sanitize_text_field(wp_unslash((string) $request_source['join_query_student_id'])) : ''; $query = array( 'name' => itstudio_join_normalize_lookup_value('name', $raw_name), 'qq' => itstudio_join_normalize_lookup_value('qq', $raw_qq), 'email' => itstudio_join_normalize_lookup_value('email', $raw_email), 'student_id' => itstudio_join_normalize_lookup_value('student_id', $raw_student_id), ); $has_query_value = false; foreach ($query as $value) { if ($value !== '') { $has_query_value = true; break; } } $submitted = isset($request_source['join_progress_lookup']) || $has_query_value; $response = array( 'submitted' => $submitted, 'has_query' => $has_query_value, 'name' => $raw_name, 'qq' => $raw_qq, 'email' => $raw_email, 'student_id' => $raw_student_id, 'message_cn' => '', 'message_en' => '', 'tone' => 'info', ); if (!$submitted) { return $response; } if (!$has_query_value) { $response['message_cn'] = '请至少填写姓名、QQ、邮箱、学号中的一项。'; $response['message_en'] = 'Please fill at least one item: name, QQ, email or student ID.'; $response['tone'] = 'warning'; return $response; } $status_registration = itstudio_join_get_stage_status_by_key($runtime, 'registration'); $status_first = itstudio_join_get_stage_status_by_key($runtime, 'first_interview'); $status_assessment = itstudio_join_get_stage_status_by_key($runtime, 'assessment'); $status_second = itstudio_join_get_stage_status_by_key($runtime, 'second_interview'); $status_notice = itstudio_join_get_stage_status_by_key($runtime, 'public_notice'); $uploaded_registration = itstudio_join_has_uploaded_result_for_stage($settings, 'registration'); $uploaded_first = itstudio_join_has_uploaded_result_for_stage($settings, 'first_interview'); $uploaded_assessment = itstudio_join_has_uploaded_result_for_stage($settings, 'assessment'); $uploaded_second = itstudio_join_has_uploaded_result_for_stage($settings, 'second_interview'); $uploaded_notice = itstudio_join_has_uploaded_result_for_stage($settings, 'public_notice'); if (!$uploaded_registration) { $response['message_cn'] = '报名数据尚未上传,暂无法查询。'; $response['message_en'] = 'Registration data has not been uploaded yet.'; $response['tone'] = 'warning'; return $response; } $is_registered = itstudio_join_find_record_in_stage_results($settings, 'registration', $query); if (!$is_registered) { $response['message_cn'] = '未报名。'; $response['message_en'] = 'Not registered.'; $response['tone'] = 'error'; return $response; } if ($status_registration === 'active' || $status_registration === 'upcoming' || $status_first === 'upcoming' || $status_first === 'pending') { $response['message_cn'] = '已报名。'; $response['message_en'] = 'Registered.'; $response['tone'] = 'success'; return $response; } if ($status_first === 'completed') { if (!$uploaded_first) { $response['message_cn'] = '第一次面试已结束,结果尚未上传。'; $response['message_en'] = 'Interview I has ended, but results are not uploaded yet.'; $response['tone'] = 'warning'; return $response; } $passed_first = itstudio_join_find_record_in_stage_results($settings, 'first_interview', $query); if (!$passed_first) { $response['message_cn'] = '未通过第一次面试,期待来年再次报名。'; $response['message_en'] = 'You did not pass Interview I. Welcome to apply again next year.'; $response['tone'] = 'error'; return $response; } if ($status_assessment === 'upcoming' || $status_assessment === 'pending' || $status_assessment === 'active') { $response['message_cn'] = '恭喜,您已通过第一次面试。'; $response['message_en'] = 'Congratulations! You have passed Interview I.'; $response['tone'] = 'success'; return $response; } } if ($status_assessment === 'completed') { if (!$uploaded_assessment) { $response['message_cn'] = '国庆能力摸底已结束,结果尚未上传。'; $response['message_en'] = 'Assessment stage has ended, but results are not uploaded yet.'; $response['tone'] = 'warning'; return $response; } $passed_assessment = itstudio_join_find_record_in_stage_results($settings, 'assessment', $query); if (!$passed_assessment) { $response['message_cn'] = '未通过国庆能力摸底,期待来年再次报名。'; $response['message_en'] = 'You did not pass the assessment stage. Welcome to apply again next year.'; $response['tone'] = 'error'; return $response; } if ($status_second === 'upcoming' || $status_second === 'pending' || $status_second === 'active') { $response['message_cn'] = '恭喜,您已通过国庆能力摸底。'; $response['message_en'] = 'Congratulations! You have passed the assessment stage.'; $response['tone'] = 'success'; return $response; } } if ($status_second === 'completed') { if (!$uploaded_second) { $response['message_cn'] = '第二次面试已结束,结果尚未上传。'; $response['message_en'] = 'Interview II has ended, but results are not uploaded yet.'; $response['tone'] = 'warning'; return $response; } $passed_second = itstudio_join_find_record_in_stage_results($settings, 'second_interview', $query); if (!$passed_second) { $response['message_cn'] = '未通过第二次面试,期待来年再次报名。'; $response['message_en'] = 'You did not pass Interview II. Welcome to apply again next year.'; $response['tone'] = 'error'; return $response; } if ($status_notice === 'pending' || $status_notice === 'upcoming' || $status_notice === 'active') { if ($status_notice === 'active' && $uploaded_notice) { $admitted = itstudio_join_find_record_in_stage_results($settings, 'public_notice', $query); if ($admitted) { $response['message_cn'] = '恭喜,您已被录取。'; $response['message_en'] = 'Congratulations! You have been admitted.'; $response['tone'] = 'success'; return $response; } $response['message_cn'] = '很遗憾,您未被录取,期待来年再次报名。'; $response['message_en'] = 'Sorry, you were not admitted. Welcome to apply again next year.'; $response['tone'] = 'error'; return $response; } $response['message_cn'] = '恭喜,您已通过第二次面试,请等待录取结果公布。'; $response['message_en'] = 'Congratulations! You have passed Interview II. Please wait for the final result.'; $response['tone'] = 'success'; return $response; } } if (($status_notice === 'active' || $status_notice === 'completed') && $uploaded_notice) { $admitted = itstudio_join_find_record_in_stage_results($settings, 'public_notice', $query); if ($admitted) { $response['message_cn'] = '恭喜,您已被录取。'; $response['message_en'] = 'Congratulations! You have been admitted.'; $response['tone'] = 'success'; } else { $response['message_cn'] = '很遗憾,您未被录取,期待来年再次报名。'; $response['message_en'] = 'Sorry, you were not admitted. Welcome to apply again next year.'; $response['tone'] = 'error'; } return $response; } $response['message_cn'] = '已报名。'; $response['message_en'] = 'Registered.'; $response['tone'] = 'success'; return $response; } function itstudio_join_datetime_to_ms($date) { if (!($date instanceof DateTimeImmutable)) { return null; } return (int) $date->format('U') * 1000; } function itstudio_join_resolve_stage_status($now, $start, $end) { if (!($now instanceof DateTimeImmutable)) { return 'pending'; } if (!($start instanceof DateTimeImmutable) && !($end instanceof DateTimeImmutable)) { return 'pending'; } if ($start instanceof DateTimeImmutable && $now < $start) { return 'upcoming'; } if ($start instanceof DateTimeImmutable && $end instanceof DateTimeImmutable) { if ($now >= $start && $now <= $end) { return 'active'; } if ($now > $end) { return 'completed'; } } if ($start instanceof DateTimeImmutable && !($end instanceof DateTimeImmutable)) { if ($now >= $start) { return 'active'; } } if (!($start instanceof DateTimeImmutable) && $end instanceof DateTimeImmutable) { return $now <= $end ? 'active' : 'completed'; } return 'upcoming'; } function itstudio_join_is_in_window($now, $start, $end) { if (!($now instanceof DateTimeImmutable) || !($start instanceof DateTimeImmutable)) { return false; } if ($now < $start) { return false; } if ($end instanceof DateTimeImmutable && $now > $end) { return false; } return true; } function itstudio_join_format_stage_range($start, $end, $all_day = false) { if (!($start instanceof DateTimeImmutable) && !($end instanceof DateTimeImmutable)) { return array( 'cn' => '时间待定', 'en' => 'Schedule TBA', ); } $format_cn_day = 'Y.m.d'; $format_cn_full = 'Y.m.d H:i'; $format_en_day = 'M j, Y'; $format_en_full = 'M j, Y H:i'; if ($start instanceof DateTimeImmutable && !($end instanceof DateTimeImmutable)) { return array( 'cn' => $all_day ? $start->format($format_cn_day) : $start->format($format_cn_full), 'en' => $all_day ? $start->format($format_en_day) : $start->format($format_en_full), ); } if (!($start instanceof DateTimeImmutable) && $end instanceof DateTimeImmutable) { return array( 'cn' => $all_day ? $end->format($format_cn_day) : $end->format($format_cn_full), 'en' => $all_day ? $end->format($format_en_day) : $end->format($format_en_full), ); } if (!($start instanceof DateTimeImmutable) || !($end instanceof DateTimeImmutable)) { return array( 'cn' => '时间待定', 'en' => 'Schedule TBA', ); } $start_cn = $all_day ? $start->format($format_cn_day) : $start->format($format_cn_full); $end_cn = $all_day ? $end->format($format_cn_day) : $end->format($format_cn_full); $start_en = $all_day ? $start->format($format_en_day) : $start->format($format_en_full); $end_en = $all_day ? $end->format($format_en_day) : $end->format($format_en_full); if ($start_cn === $end_cn) { return array( 'cn' => $start_cn, 'en' => $start_en, ); } return array( 'cn' => $start_cn . ' - ' . $end_cn, 'en' => $start_en . ' - ' . $end_en, ); } function itstudio_join_get_stage_photo_url($settings, $stage_key) { $settings = is_array($settings) ? $settings : array(); $field_map = itstudio_join_get_photo_field_map(); $field_key = isset($field_map[$stage_key]) ? $field_map[$stage_key] : ''; $attachment_id = 0; if ($field_key !== '' && isset($settings[$field_key])) { $attachment_id = absint($settings[$field_key]); } if ($attachment_id > 0) { $photo_url = wp_get_attachment_image_url($attachment_id, 'full'); if (is_string($photo_url) && $photo_url !== '') { return $photo_url; } } return ''; } function itstudio_join_get_runtime_data() { static $cached = null; if (is_array($cached)) { return $cached; } $settings = itstudio_join_get_settings(); $timezone = wp_timezone(); $now = new DateTimeImmutable('now', $timezone); $registration_start = itstudio_join_parse_datetime_local($settings['registration_start'], false); $registration_end = itstudio_join_parse_datetime_local($settings['registration_end'], true); if ($registration_start instanceof DateTimeImmutable) { $registration_start = $registration_start->setTime(0, 0, 0); } if ($registration_end instanceof DateTimeImmutable) { $registration_end = $registration_end->setTime(23, 59, 59); } if ($registration_start instanceof DateTimeImmutable && $registration_end instanceof DateTimeImmutable && $registration_end < $registration_start) { $registration_end = $registration_start; } $first_interview_start_raw = isset($settings['first_interview_date']) ? (string) $settings['first_interview_date'] : ''; $first_interview_end_raw = isset($settings['first_interview_end']) ? (string) $settings['first_interview_end'] : ''; $first_interview_start = itstudio_join_parse_datetime_local($first_interview_start_raw, false); $first_interview_end = itstudio_join_parse_datetime_local($first_interview_end_raw, true); if ($first_interview_start instanceof DateTimeImmutable && !($first_interview_end instanceof DateTimeImmutable)) { if (preg_match('/^\d{4}-\d{2}-\d{2}$/', trim($first_interview_start_raw))) { $first_interview_end = $first_interview_start->setTime(23, 59, 59); } else { $first_interview_end = $first_interview_start; } } elseif (!($first_interview_start instanceof DateTimeImmutable) && $first_interview_end instanceof DateTimeImmutable) { $first_interview_start = $first_interview_end; } elseif ($first_interview_start instanceof DateTimeImmutable && $first_interview_end instanceof DateTimeImmutable && $first_interview_end < $first_interview_start) { $first_interview_end = $first_interview_start; } $second_interview_start_raw = isset($settings['second_interview_date']) ? (string) $settings['second_interview_date'] : ''; $second_interview_end_raw = isset($settings['second_interview_end']) ? (string) $settings['second_interview_end'] : ''; $second_interview_start = itstudio_join_parse_datetime_local($second_interview_start_raw, false); $second_interview_end = itstudio_join_parse_datetime_local($second_interview_end_raw, true); if ($second_interview_start instanceof DateTimeImmutable && !($second_interview_end instanceof DateTimeImmutable)) { if (preg_match('/^\d{4}-\d{2}-\d{2}$/', trim($second_interview_start_raw))) { $second_interview_end = $second_interview_start->setTime(23, 59, 59); } else { $second_interview_end = $second_interview_start; } } elseif (!($second_interview_start instanceof DateTimeImmutable) && $second_interview_end instanceof DateTimeImmutable) { $second_interview_start = $second_interview_end; } elseif ($second_interview_start instanceof DateTimeImmutable && $second_interview_end instanceof DateTimeImmutable && $second_interview_end < $second_interview_start) { $second_interview_end = $second_interview_start; } $first_interview_location_cn = trim((string) ($settings['first_interview_location_cn'] ?? '')); $first_interview_location_en = trim((string) ($settings['first_interview_location_en'] ?? '')); if ($first_interview_location_en === '') { $first_interview_location_en = $first_interview_location_cn; } $second_interview_location_cn = trim((string) ($settings['second_interview_location_cn'] ?? '')); $second_interview_location_en = trim((string) ($settings['second_interview_location_en'] ?? '')); if ($second_interview_location_en === '') { $second_interview_location_en = $second_interview_location_cn; } $notice_start_day = itstudio_join_parse_date($settings['notice_start_date']); $notice_start = $notice_start_day instanceof DateTimeImmutable ? $notice_start_day->setTime(0, 0, 0) : null; $notice_end = $notice_start instanceof DateTimeImmutable ? $notice_start->modify('+6 days')->setTime(23, 59, 59) : null; $recruitment_year = null; if ($registration_start instanceof DateTimeImmutable) { $recruitment_year = (int) $registration_start->format('Y'); } elseif ($notice_start instanceof DateTimeImmutable) { $recruitment_year = (int) $notice_start->format('Y'); } else { $recruitment_year = (int) $now->format('Y'); } $default_assessment_start = DateTimeImmutable::createFromFormat('!Y-m-d', sprintf('%04d-10-01', $recruitment_year), $timezone); $default_assessment_end_base = DateTimeImmutable::createFromFormat('!Y-m-d', sprintf('%04d-10-07', $recruitment_year), $timezone); $default_assessment_end = $default_assessment_end_base instanceof DateTimeImmutable ? $default_assessment_end_base->setTime(23, 59, 59) : null; $assessment_start_day = itstudio_join_parse_date($settings['assessment_start_date']); $assessment_end_day = itstudio_join_parse_date($settings['assessment_end_date']); $assessment_start = $assessment_start_day instanceof DateTimeImmutable ? $assessment_start_day->setTime(0, 0, 0) : $default_assessment_start; $assessment_end = $assessment_end_day instanceof DateTimeImmutable ? $assessment_end_day->setTime(23, 59, 59) : $default_assessment_end; if ($assessment_start_day instanceof DateTimeImmutable && !($assessment_end_day instanceof DateTimeImmutable)) { $assessment_end = $assessment_start->setTime(23, 59, 59); } elseif (!($assessment_start_day instanceof DateTimeImmutable) && $assessment_end_day instanceof DateTimeImmutable) { $assessment_start = $assessment_end_day->setTime(0, 0, 0); } if ($assessment_start instanceof DateTimeImmutable && $assessment_end instanceof DateTimeImmutable && $assessment_end < $assessment_start) { $assessment_end = $assessment_start->setTime(23, 59, 59); } $stage_seed = array( array( 'key' => 'registration', 'label_cn' => '报名阶段', 'label_en' => 'Registration', 'short_cn' => '报名', 'short_en' => 'Reg', 'start' => $registration_start, 'end' => $registration_end, 'all_day' => true, 'location_cn' => '', 'location_en' => '', 'result_uploaded' => itstudio_join_has_uploaded_result_for_stage($settings, 'registration'), ), array( 'key' => 'first_interview', 'label_cn' => '第一次面试', 'label_en' => 'Interview I', 'short_cn' => '一面', 'short_en' => 'I-1', 'start' => $first_interview_start, 'end' => $first_interview_end, 'all_day' => false, 'location_cn' => $first_interview_location_cn, 'location_en' => $first_interview_location_en, 'result_uploaded' => itstudio_join_has_uploaded_result_for_stage($settings, 'first_interview'), ), array( 'key' => 'assessment', 'label_cn' => '国庆能力摸底', 'label_en' => 'Assessment', 'short_cn' => '摸底', 'short_en' => 'Assess', 'start' => $assessment_start, 'end' => $assessment_end, 'all_day' => true, 'location_cn' => '', 'location_en' => '', 'result_uploaded' => itstudio_join_has_uploaded_result_for_stage($settings, 'assessment'), ), array( 'key' => 'second_interview', 'label_cn' => '第二次面试', 'label_en' => 'Interview II', 'short_cn' => '二面', 'short_en' => 'I-2', 'start' => $second_interview_start, 'end' => $second_interview_end, 'all_day' => false, 'location_cn' => $second_interview_location_cn, 'location_en' => $second_interview_location_en, 'result_uploaded' => itstudio_join_has_uploaded_result_for_stage($settings, 'second_interview'), ), array( 'key' => 'public_notice', 'label_cn' => '录取结果公布', 'label_en' => 'Public Notice', 'short_cn' => '公布', 'short_en' => 'Notice', 'start' => $notice_start, 'end' => $notice_end, 'all_day' => true, 'location_cn' => '', 'location_en' => '', 'result_uploaded' => itstudio_join_has_uploaded_result_for_stage($settings, 'public_notice'), ), ); $stages = array(); foreach ($stage_seed as $stage) { $range = itstudio_join_format_stage_range($stage['start'], $stage['end'], !empty($stage['all_day'])); $status = itstudio_join_resolve_stage_status($now, $stage['start'], $stage['end']); $stages[] = array( 'key' => $stage['key'], 'label_cn' => $stage['label_cn'], 'label_en' => $stage['label_en'], 'short_cn' => $stage['short_cn'], 'short_en' => $stage['short_en'], 'status' => $status, 'range_cn' => $range['cn'], 'range_en' => $range['en'], 'location_cn' => isset($stage['location_cn']) ? (string) $stage['location_cn'] : '', 'location_en' => isset($stage['location_en']) ? (string) $stage['location_en'] : '', 'result_uploaded' => !empty($stage['result_uploaded']), 'start_ts' => itstudio_join_datetime_to_ms($stage['start']), 'end_ts' => itstudio_join_datetime_to_ms($stage['end']), ); } $current_stage_index = -1; foreach ($stages as $index => $stage) { if ($stage['status'] === 'active') { $current_stage_index = (int) $index; break; } } $next_stage_index = -1; if ($current_stage_index < 0) { foreach ($stages as $index => $stage) { if (($stage['status'] ?? '') === 'upcoming') { $next_stage_index = (int) $index; break; } } } $current_stage_mode = 'inactive'; $display_stage_index = -1; if ($current_stage_index >= 0 && isset($stages[$current_stage_index])) { $display_stage_index = $current_stage_index; $current_stage_mode = 'active'; } elseif ($next_stage_index >= 0 && isset($stages[$next_stage_index])) { $display_stage_index = $next_stage_index; $current_stage_mode = 'next'; } $current_stage = ($display_stage_index >= 0 && isset($stages[$display_stage_index])) ? $stages[$display_stage_index] : array( 'key' => 'inactive', 'label_cn' => '当前未在招新时段', 'label_en' => 'Recruitment is currently closed', 'short_cn' => '', 'short_en' => '', 'status' => 'inactive', 'range_cn' => '请关注后续通知', 'range_en' => 'Please check later updates', 'location_cn' => '', 'location_en' => '', 'result_uploaded' => false, 'start_ts' => null, 'end_ts' => null, ); $is_registration_open = itstudio_join_is_in_window($now, $registration_start, $registration_end); $is_query_open = false; if ($registration_start instanceof DateTimeImmutable) { if ($notice_end instanceof DateTimeImmutable) { $is_query_open = itstudio_join_is_in_window($now, $registration_start, $notice_end); } else { $is_query_open = ($now >= $registration_start); } } $is_notice_open = itstudio_join_is_in_window($now, $notice_start, $notice_end); $current_stage_photo_url = itstudio_join_get_stage_photo_url($settings, (string) ($current_stage['key'] ?? '')); if ($current_stage_photo_url === '') { $current_stage_photo_url = get_template_directory_uri() . '/resources/it_logo_2024.svg'; } $season_start = null; $season_end = null; foreach ($stage_seed as $stage_window) { $stage_start = $stage_window['start'] instanceof DateTimeImmutable ? $stage_window['start'] : null; $stage_end = $stage_window['end'] instanceof DateTimeImmutable ? $stage_window['end'] : null; if (!($stage_start instanceof DateTimeImmutable) && !($stage_end instanceof DateTimeImmutable)) { continue; } $window_start = $stage_start instanceof DateTimeImmutable ? $stage_start : $stage_end; $window_end = $stage_end instanceof DateTimeImmutable ? $stage_end : $stage_start; if (!($window_start instanceof DateTimeImmutable) || !($window_end instanceof DateTimeImmutable)) { continue; } if (!($season_start instanceof DateTimeImmutable) || $window_start < $season_start) { $season_start = $window_start; } if (!($season_end instanceof DateTimeImmutable) || $window_end > $season_end) { $season_end = $window_end; } } $show_progress_visual = false; if ($season_start instanceof DateTimeImmutable && $season_end instanceof DateTimeImmutable) { $show_progress_visual = ($now >= $season_start && $now <= $season_end); } elseif ($current_stage_index >= 0) { $show_progress_visual = true; } $cached = array( 'settings' => $settings, 'timezone' => $timezone->getName(), 'recruitment_year' => $recruitment_year, 'now_ts' => (int) $now->format('U') * 1000, 'stages' => $stages, 'current_stage_index' => $current_stage_index, 'current_stage_mode' => $current_stage_mode, 'current_stage' => $current_stage, 'is_registration_open' => $is_registration_open, 'is_query_open' => $is_query_open, 'is_notice_open' => $is_notice_open, 'show_progress_visual' => $show_progress_visual, 'current_stage_photo_url' => $current_stage_photo_url, 'query_deadline_cn' => $notice_end instanceof DateTimeImmutable ? $notice_end->format('Y-m-d H:i') : '', 'query_deadline_en' => $notice_end instanceof DateTimeImmutable ? $notice_end->format('M j, Y H:i') : '', ); return $cached; } function itstudio_join_get_frontend_payload() { $runtime = itstudio_join_get_runtime_data(); return array( 'nowTs' => (int) ($runtime['now_ts'] ?? 0), 'currentStageIndex' => (int) ($runtime['current_stage_index'] ?? -1), 'stages' => array_values(array_map(static function ($stage) { return array( 'key' => (string) ($stage['key'] ?? ''), 'labelCn' => (string) ($stage['label_cn'] ?? ''), 'labelEn' => (string) ($stage['label_en'] ?? ''), 'shortCn' => (string) ($stage['short_cn'] ?? ''), 'shortEn' => (string) ($stage['short_en'] ?? ''), 'status' => (string) ($stage['status'] ?? 'pending'), 'rangeCn' => (string) ($stage['range_cn'] ?? ''), 'rangeEn' => (string) ($stage['range_en'] ?? ''), 'locationCn' => (string) ($stage['location_cn'] ?? ''), 'locationEn' => (string) ($stage['location_en'] ?? ''), 'resultUploaded' => !empty($stage['result_uploaded']), 'startTs' => isset($stage['start_ts']) ? $stage['start_ts'] : null, 'endTs' => isset($stage['end_ts']) ? $stage['end_ts'] : null, ); }, (array) ($runtime['stages'] ?? array()))), ); } function itstudio_join_enqueue_assets() { if (!itstudio_is_join_page_context()) { return; } wp_enqueue_style( 'itstudio-join-page', get_template_directory_uri() . '/assets/css/join-page.css', array('itstudio-content'), '1.1.0' ); wp_enqueue_script( 'itstudio-animejs', get_template_directory_uri() . '/assets/js/vendor/anime.min.js', array(), '3.2.2', true ); wp_enqueue_script( 'itstudio-join-canvas', get_template_directory_uri() . '/assets/js/join-canvas.js', array('itstudio-animejs'), '1.1.0', true ); wp_localize_script('itstudio-join-canvas', 'itstudioJoinData', itstudio_join_get_frontend_payload()); } add_action('wp_enqueue_scripts', 'itstudio_join_enqueue_assets', 30); function itstudio_join_register_settings() { register_setting( 'itstudio_join_settings_group', 'itstudio_join_settings', array( 'type' => 'array', 'sanitize_callback' => 'itstudio_join_sanitize_settings', 'default' => itstudio_join_get_default_settings(), ) ); } add_action('admin_init', 'itstudio_join_register_settings'); function itstudio_join_register_settings_page() { add_options_page( '招新设置', '招新设置', 'manage_options', 'itstudio-join-settings', 'itstudio_join_render_settings_page' ); } add_action('admin_menu', 'itstudio_join_register_settings_page'); function itstudio_join_render_photo_field_row($field_key, $label, $settings) { $attachment_id = isset($settings[$field_key]) ? absint($settings[$field_key]) : 0; $preview_url = $attachment_id > 0 ? wp_get_attachment_image_url($attachment_id, 'medium_large') : ''; ?>

每行一条记录,格式:姓名,QQ,邮箱,学号。可用逗号、中文逗号、竖线或 Tab 分隔。

爱特工作室招新设置

用于配置「加入我们」页面的时间节点、阶段结果和表单。

提示:未检测到 Formidable Forms 插件。报名表单将无法在前台渲染。

提示:未检测到 WP Mail SMTP 插件。建议启用后再开放报名邮件通知。


阶段预览

国庆能力摸底默认固定为每年 10 月 1 日至 10 月 7 日;如填写上方“摸底开始/结束日期(调试)”则优先使用调试时间,留空则恢复默认固定窗口。

阶段 时间 状态
request) ? trim((string) $wp->request, '/') : ''; if ($request !== 'join') { return; } $template = locate_template('page-join.php'); if (!$template) { return; } global $wp_query; if ($wp_query) { $wp_query->is_404 = false; $wp_query->is_page = true; $wp_query->is_singular = true; $virtual_post = new WP_Post((object) array( 'ID' => 0, 'post_type' => 'page', 'post_parent' => 0, 'post_title' => '加入我们', 'post_status' => 'publish', 'post_name' => 'join', 'post_content' => '', )); $wp_query->post = $virtual_post; $wp_query->posts = array($virtual_post); $wp_query->queried_object = $virtual_post; $wp_query->queried_object_id = 0; $wp_query->post_count = 1; $wp_query->found_posts = 1; $wp_query->max_num_pages = 1; global $post; $post = $virtual_post; setup_postdata($post); } add_filter('document_title_parts', static function ($parts) { $parts['title'] = '加入我们'; return $parts; }); status_header(200); nocache_headers(); include $template; exit; } add_action('template_redirect', 'itstudio_join_fallback', 9);