Files
car-t-t/inc/cpt.php
T

499 lines
18 KiB
PHP

<?php
/**
* Custom post types: service (服务项目/膜品) and case (案例作品集).
* 主题激活时把 Customizer 中的 7 个产品迁移为 service 文章,并创建默认页面。
*
* @package Fragrance_Trade
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/** Register the service and case post types plus the case_type taxonomy. */
function fragrance_trade_register_post_types() {
register_post_type(
'service',
array(
'labels' => array(
'name' => __( '服务项目', 'fragrance-trade' ),
'singular_name' => __( '服务项目', 'fragrance-trade' ),
'add_new' => __( '添加服务项目', 'fragrance-trade' ),
'edit_item' => __( '编辑服务项目', 'fragrance-trade' ),
'all_items' => __( '全部服务项目', 'fragrance-trade' ),
),
'public' => true,
'has_archive' => 'services',
'rewrite' => array( 'slug' => 'services' ),
'menu_position' => 20,
'menu_icon' => 'dashicons-admin-customizer',
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
'show_in_rest' => true,
)
);
register_post_type(
'case',
array(
'labels' => array(
'name' => __( '案例作品', 'fragrance-trade' ),
'singular_name' => __( '案例', 'fragrance-trade' ),
'add_new' => __( '添加案例', 'fragrance-trade' ),
'edit_item' => __( '编辑案例', 'fragrance-trade' ),
'all_items' => __( '全部案例', 'fragrance-trade' ),
),
'public' => true,
'has_archive' => 'cases',
'rewrite' => array( 'slug' => 'cases' ),
'menu_position' => 21,
'menu_icon' => 'dashicons-format-gallery',
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
'show_in_rest' => true,
)
);
register_taxonomy(
'case_type',
'case',
array(
'labels' => array(
'name' => __( '案例分类', 'fragrance-trade' ),
'singular_name' => __( '案例分类', 'fragrance-trade' ),
),
'public' => true,
'hierarchical' => false,
'show_in_rest' => true,
'rewrite' => array( 'slug' => 'case-type' ),
)
);
}
add_action( 'init', 'fragrance_trade_register_post_types' );
/** Filter the case archive by the case_type query argument. */
function fragrance_trade_filter_cases_archive( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public archive filtering is read-only.
if ( $query->is_post_type_archive( 'case' ) && ! empty( $_GET['case_type'] ) ) {
$query->set( 'case_type', sanitize_text_field( wp_unslash( $_GET['case_type'] ) ) );
}
}
add_action( 'pre_get_posts', 'fragrance_trade_filter_cases_archive' );
/** Return the default service records created for a fresh installation. */
function fragrance_trade_service_seeds() {
return array(
'b2b_product_1' => array(
'title' => __( '隐形车衣(TPU 保护膜)', 'fragrance-trade' ),
'content' => __( '透明保护膜覆盖车漆表面,可帮助降低日常轻微划痕、飞石和污渍对漆面的影响。具体性能取决于所选产品系列。', 'fragrance-trade' ),
'excerpt' => __( '车漆保护 · 透明外观 · 日常养护', 'fragrance-trade' ),
),
'b2b_product_2' => array(
'title' => __( '彩色改色膜', 'fragrance-trade' ),
'content' => __( '通过不同颜色和表面质感改变车辆外观,可根据车型、原车颜色和日常使用场景选择亮面、哑光等效果。', 'fragrance-trade' ),
'excerpt' => __( '颜色选择 · 表面质感 · 外观焕新', 'fragrance-trade' ),
),
'b2b_product_3' => array(
'title' => __( '隔热防爆膜', 'fragrance-trade' ),
'content' => __( '用于车窗隔热与紫外线防护,可结合透光需求、隐私需求和法规要求选择适合的产品参数。', 'fragrance-trade' ),
'excerpt' => __( '隔热需求 · 紫外线防护 · 车窗适用', 'fragrance-trade' ),
),
'b2b_product_4' => array(
'title' => __( '陶瓷隔热膜', 'fragrance-trade' ),
'content' => __( '陶瓷类隔热膜适合关注隔热表现和车内电子信号使用的车主,实际参数与适用位置以具体产品说明为准。', 'fragrance-trade' ),
'excerpt' => __( '陶瓷膜料 · 信号兼容 · 隔热方案', 'fragrance-trade' ),
),
'b2b_product_5' => array(
'title' => __( '天窗冰甲', 'fragrance-trade' ),
'content' => __( '面向全景天窗的隔热保护项目,需要结合天窗结构、玻璃状况和所选膜料确认是否适合施工。', 'fragrance-trade' ),
'excerpt' => __( '全景天窗 · 隔热需求 · 安装评估', 'fragrance-trade' ),
),
'b2b_product_6' => array(
'title' => __( '前挡专用膜', 'fragrance-trade' ),
'content' => __( '前挡膜重点平衡透光视野、隔热需求和夜间驾驶体验,选购时应核对产品参数及当地法规要求。', 'fragrance-trade' ),
'excerpt' => __( '前挡适用 · 透光视野 · 隔热需求', 'fragrance-trade' ),
),
'b2b_product_7' => array(
'title' => __( '个性定制彩绘膜', 'fragrance-trade' ),
'content' => __( '可根据参考图和车辆尺寸沟通定制外观方案,制作前确认图案、颜色、覆盖范围与交付效果。', 'fragrance-trade' ),
'excerpt' => __( '图案沟通 · 方案确认 · 定制施工', 'fragrance-trade' ),
),
);
}
/** Hashes of the previous untouched seed copy, used to avoid overwriting edited services. */
function fragrance_trade_legacy_service_hashes() {
return array(
'b2b_product_1' => array( 'content' => '4ceefcf5b0d6835b2043eb974862641c4605408b249c47142a1384f8c6aba5a0', 'excerpt' => '474adc2b0a9cb84519f25b32f2aebaf66b7530c582d33a63fc428a70063982dd' ),
'b2b_product_2' => array( 'content' => 'c587d08672a5a73bd9d675a1aaa105b1dec46331a099649474a4669097c41707', 'excerpt' => 'edbbf896ccab06c5cbc79922f2130c190cb4dcd9720d427abd861c5d866d9de2' ),
'b2b_product_3' => array( 'content' => '49a236ff415f92de3c0a789ee42a7ea1083c60779f4dab3d80a162e9fbc534f7', 'excerpt' => '3be7e723aa8cfe1b5ce753e1ede8ceb5eb7122242a8bfde5dc6870e26291111e' ),
'b2b_product_4' => array( 'content' => '96db23ecaebbe16a7fd90254f098d9d652367ea200fe658792a7097082017d56', 'excerpt' => '3e59947da9a09130aeb65c0695c4b0ca1b78737e2cc320184a05ab0b4ca18424' ),
'b2b_product_5' => array( 'content' => '36d83bd3a439f92cdd89c4fb1e863e748d843733a9865b4740e9466c9ba0c916', 'excerpt' => '7e1985099340d070b75b2caa292cb877850bd7e7ed74a14d181dc98780d09666' ),
'b2b_product_6' => array( 'content' => 'caf3a595cc58b69861d66dbb92a04cfdddbe6ebfe7ef10a47e00e5cbbc112ea1', 'excerpt' => '1f1fb96587343da8d3fd938ec47f8f7abfb0a57c52a447c7b84997973ce57999' ),
'b2b_product_7' => array( 'content' => 'b1fda0e7a0c7b3445c61ac1ee1e53c7e27c0612c2e8706901f4a1c07d26959a0', 'excerpt' => '0815c3e0d55c9c491ecfa80d264f2c18e5112ab87e3366bc3bd7ca89c76a951c' ),
);
}
/** Find an existing seed by marker so a failed setup can be retried safely. */
function fragrance_trade_find_seeded_service( $seed_key ) {
$ids = get_posts(
array(
'post_type' => 'service',
'post_status' => array( 'publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash' ),
'posts_per_page' => 1,
'fields' => 'ids',
'meta_key' => '_fragrance_seed_key',
'meta_value' => $seed_key,
'no_found_rows' => true,
)
);
return $ids ? (int) $ids[0] : 0;
}
/** Locate an untouched service created by an older theme version and add its seed marker. */
function fragrance_trade_find_legacy_seed_service( $seed_key, $seed, $legacy_hashes ) {
$posts = get_posts(
array(
'post_type' => 'service',
'post_status' => array( 'publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash' ),
'title' => $seed['title'],
'posts_per_page' => 5,
'no_found_rows' => true,
)
);
foreach ( $posts as $post ) {
if (
hash_equals( $legacy_hashes['content'], hash( 'sha256', $post->post_content ) )
&& hash_equals( $legacy_hashes['excerpt'], hash( 'sha256', $post->post_excerpt ) )
) {
update_post_meta( $post->ID, '_fragrance_seed_key', $seed_key );
return (int) $post->ID;
}
}
return 0;
}
/** Update only untouched seed copy; preserve every service that an administrator edited. */
function fragrance_trade_migrate_seed_service_copy( &$errors ) {
$changed = false;
$legacy_hashes = fragrance_trade_legacy_service_hashes();
foreach ( fragrance_trade_service_seeds() as $seed_key => $seed ) {
$post_id = fragrance_trade_find_seeded_service( $seed_key );
if ( ! $post_id && isset( $legacy_hashes[ $seed_key ] ) ) {
$post_id = fragrance_trade_find_legacy_seed_service( $seed_key, $seed, $legacy_hashes[ $seed_key ] );
}
if ( ! $post_id || ! isset( $legacy_hashes[ $seed_key ] ) ) {
continue;
}
$post = get_post( $post_id );
$update = array( 'ID' => $post_id );
if ( hash_equals( $legacy_hashes[ $seed_key ]['content'], hash( 'sha256', $post->post_content ) ) ) {
$update['post_content'] = $seed['content'];
}
if ( hash_equals( $legacy_hashes[ $seed_key ]['excerpt'], hash( 'sha256', $post->post_excerpt ) ) ) {
$update['post_excerpt'] = $seed['excerpt'];
}
if ( 1 === count( $update ) ) {
continue;
}
$updated = wp_update_post( $update, true );
if ( is_wp_error( $updated ) ) {
$errors[] = sprintf(
/* translators: 1: service title, 2: error message. */
__( '无法更新服务“%1$s”的默认文案:%2$s', 'fragrance-trade' ),
$seed['title'],
$updated->get_error_message()
);
continue;
}
$changed = true;
}
return $changed;
}
/** Create seed services while preserving legacy Customizer product values. */
function fragrance_trade_create_seed_services( &$errors ) {
if ( get_option( 'fragrance_trade_setup_done' ) ) {
return false;
}
$changed = false;
foreach ( fragrance_trade_service_seeds() as $seed_key => $seed ) {
if ( fragrance_trade_find_seeded_service( $seed_key ) ) {
continue;
}
$title = get_theme_mod( $seed_key . '_title', $seed['title'] );
if ( ! $title ) {
continue;
}
$post_id = wp_insert_post(
array(
'post_type' => 'service',
'post_title' => $title,
'post_content' => get_theme_mod( $seed_key . '_text', $seed['content'] ),
'post_excerpt' => get_theme_mod( $seed_key . '_specs', $seed['excerpt'] ),
'post_status' => 'publish',
),
true
);
if ( is_wp_error( $post_id ) ) {
$errors[] = sprintf(
/* translators: 1: service title, 2: error message. */
__( '无法创建服务“%1$s”:%2$s', 'fragrance-trade' ),
$title,
$post_id->get_error_message()
);
continue;
}
update_post_meta( $post_id, '_fragrance_seed_key', $seed_key );
$image = get_theme_mod( $seed_key . '_image', '' );
if ( $image ) {
update_post_meta( $post_id, '_service_image', $image );
}
$changed = true;
}
return $changed;
}
/** Find a conventional page, including the renamed slug WordPress uses in Trash. */
function fragrance_trade_find_required_page( $slug ) {
$pages = get_posts(
array(
'post_type' => 'page',
'post_status' => array( 'publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash' ),
'post_name__in' => array( $slug, $slug . '__trashed' ),
'posts_per_page' => 1,
'orderby' => 'post_name__in',
'no_found_rows' => true,
)
);
return $pages ? $pages[0] : null;
}
/** Create or repair the required pages without changing existing content or status. */
function fragrance_trade_create_required_pages( &$errors ) {
$pages = array(
'home' => array( 'post_title' => __( '首页', 'fragrance-trade' ), 'template' => '' ),
'about' => array( 'post_title' => __( '关于门店', 'fragrance-trade' ), 'template' => 'page-about.php' ),
'faq' => array( 'post_title' => __( '常见问题', 'fragrance-trade' ), 'template' => 'page-faq.php' ),
'contact' => array( 'post_title' => __( '联系我们', 'fragrance-trade' ), 'template' => 'page-contact.php' ),
);
$result = array( 'changed' => false, 'pages' => array() );
foreach ( $pages as $slug => $data ) {
$page = fragrance_trade_find_required_page( $slug );
if ( $page ) {
if ( $data['template'] && get_page_template_slug( $page->ID ) !== $data['template'] ) {
$updated = wp_update_post(
array(
'ID' => $page->ID,
'page_template' => $data['template'],
),
true
);
if ( is_wp_error( $updated ) ) {
$errors[] = sprintf(
/* translators: 1: page title, 2: error message. */
__( '无法更新页面“%1$s”的模板:%2$s', 'fragrance-trade' ),
$page->post_title,
$updated->get_error_message()
);
} else {
$result['changed'] = true;
}
}
if ( 'publish' !== $page->post_status ) {
// Preserve user-owned draft and trash states; the admin notice below explains the impact.
} else {
$result['pages'][ $slug ] = $page->ID;
}
continue;
}
$page_id = wp_insert_post(
array(
'post_type' => 'page',
'post_name' => $slug,
'post_title' => $data['post_title'],
'post_status' => 'publish',
'page_template' => $data['template'],
),
true
);
if ( is_wp_error( $page_id ) ) {
$errors[] = sprintf(
/* translators: 1: page title, 2: error message. */
__( '无法创建页面“%1$s”:%2$s', 'fragrance-trade' ),
$data['post_title'],
$page_id->get_error_message()
);
continue;
}
$result['changed'] = true;
$result['pages'][ $slug ] = $page_id;
}
return $result;
}
/**
* Preserve an existing posts page or create the conventional news page.
*
* @param string[] $errors Setup errors collected by the caller.
* @return array{id:int,changed:bool}
*/
function fragrance_trade_ensure_news_page( &$errors ) {
$result = array( 'id' => 0, 'changed' => false );
$current_page_id = absint( get_option( 'page_for_posts', 0 ) );
$current_page = $current_page_id ? get_post( $current_page_id ) : null;
if ( $current_page && 'page' === $current_page->post_type ) {
$result['id'] = $current_page_id;
return $result;
}
$news_page = fragrance_trade_find_required_page( 'news' );
if ( ! $news_page ) {
$news_page_id = wp_insert_post(
array(
'post_type' => 'page',
'post_name' => 'news',
'post_title' => __( '新闻资讯', 'fragrance-trade' ),
'post_status' => 'publish',
),
true
);
if ( is_wp_error( $news_page_id ) ) {
$errors[] = sprintf(
/* translators: %s: error message. */
__( '无法创建新闻资讯页面:%s', 'fragrance-trade' ),
$news_page_id->get_error_message()
);
return $result;
}
$news_page = get_post( $news_page_id );
$result['changed'] = true;
}
$news_page_id = (int) $news_page->ID;
if ( $current_page_id !== $news_page_id ) {
update_option( 'page_for_posts', $news_page_id );
if ( $news_page_id !== absint( get_option( 'page_for_posts', 0 ) ) ) {
$errors[] = __( '无法将新闻资讯页面设置为 WordPress 文章页。', 'fragrance-trade' );
return $result;
}
$result['changed'] = true;
}
$result['id'] = $news_page_id;
return $result;
}
/** Versioned and retryable site setup. */
function fragrance_trade_maybe_setup_site() {
$target_version = 4;
if ( (int) get_option( 'fragrance_trade_schema_version', 0 ) >= $target_version ) {
return;
}
$errors = array();
$seed_changed = fragrance_trade_create_seed_services( $errors );
$copy_changed = fragrance_trade_migrate_seed_service_copy( $errors );
$page_result = fragrance_trade_create_required_pages( $errors );
$news_result = fragrance_trade_ensure_news_page( $errors );
if ( ! empty( $page_result['pages']['home'] ) ) {
update_option( 'page_on_front', $page_result['pages']['home'] );
update_option( 'show_on_front', 'page' );
}
if ( $seed_changed || $copy_changed || $page_result['changed'] || $news_result['changed'] ) {
flush_rewrite_rules();
}
if ( $errors ) {
update_option( 'fragrance_trade_setup_errors', array_values( array_unique( $errors ) ), false );
return;
}
delete_option( 'fragrance_trade_setup_errors' );
update_option( 'fragrance_trade_setup_done', 1 );
update_option( 'fragrance_trade_schema_version', $target_version );
}
add_action( 'after_switch_theme', 'fragrance_trade_maybe_setup_site' );
add_action( 'admin_init', 'fragrance_trade_maybe_setup_site' );
/** Show actionable setup failures until they are resolved. */
function fragrance_trade_setup_errors_notice() {
if ( ! current_user_can( 'switch_themes' ) ) {
return;
}
$errors = get_option( 'fragrance_trade_setup_errors', array() );
if ( ! $errors || ! is_array( $errors ) ) {
return;
}
?>
<div class="notice notice-error">
<p><strong><?php esc_html_e( 'B2B Trade 主题初始化未完成', 'fragrance-trade' ); ?></strong></p>
<ul>
<?php foreach ( $errors as $error ) : ?>
<li><?php echo esc_html( $error ); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php
}
add_action( 'admin_notices', 'fragrance_trade_setup_errors_notice' );
/** Warn when a conventional page exists but is not published. */
function fragrance_trade_page_status_notice() {
if ( ! current_user_can( 'edit_pages' ) ) {
return;
}
$unpublished = array();
foreach ( array( 'home', 'about', 'faq', 'contact' ) as $slug ) {
$page = fragrance_trade_find_required_page( $slug );
if ( $page && 'publish' !== $page->post_status ) {
$unpublished[] = $page->post_title;
}
}
$posts_page_id = absint( get_option( 'page_for_posts', 0 ) );
$posts_page = $posts_page_id ? get_post( $posts_page_id ) : null;
if (
$posts_page
&& 'page' === $posts_page->post_type
&& 'publish' !== $posts_page->post_status
&& ! in_array( $posts_page->post_title, $unpublished, true )
) {
$unpublished[] = $posts_page->post_title;
}
if ( ! $unpublished ) {
return;
}
?>
<div class="notice notice-warning">
<p>
<strong><?php esc_html_e( 'B2B Trade 约定页面尚未发布', 'fragrance-trade' ); ?></strong>
<?php
printf(
/* translators: %s: comma-separated page titles. */
esc_html__( '主题保留了这些页面的当前状态,不会自动发布或恢复它们:%s。请确认状态后再用于前台导航。', 'fragrance-trade' ),
esc_html( implode( '、', $unpublished ) )
);
?>
</p>
</div>
<?php
}
add_action( 'admin_notices', 'fragrance_trade_page_status_notice' );