weedops-theme/inc/custom-post-types.php
Alex 8eddfee34b Initial commit: Weedops WordPress theme
Includes front-page, strain archive/single templates, header/footer,
functions.php with custom post types, and base styles.
2026-03-23 22:54:38 +00:00

391 lines
20 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Weedops Custom Post Types, Taxonomies & Meta Fields
*
* Registers the 'strain' post type (and related CPTs) with full
* meta support: THC %, CBD %, strain type, effects, and more.
*
* @package Weedops
* @version 1.0.0
*/
defined( 'ABSPATH' ) || exit;
// ─────────────────────────────────────────────────────────────────────────────
// Post Types
// ─────────────────────────────────────────────────────────────────────────────
function weedops_register_post_types(): void {
// ── Strains ──────────────────────────────────────────────────────────────
register_post_type( 'wo_strain', [
'label' => __( 'Strains', 'weedops' ),
'labels' => [
'name' => __( 'Strains', 'weedops' ),
'singular_name' => __( 'Strain', 'weedops' ),
'add_new' => __( 'Add New', 'weedops' ),
'add_new_item' => __( 'Add New Strain', 'weedops' ),
'edit_item' => __( 'Edit Strain', 'weedops' ),
'new_item' => __( 'New Strain', 'weedops' ),
'view_item' => __( 'View Strain', 'weedops' ),
'search_items' => __( 'Search Strains', 'weedops' ),
'not_found' => __( 'No strains found', 'weedops' ),
'not_found_in_trash' => __( 'No strains in trash', 'weedops' ),
'menu_name' => __( 'Strains', 'weedops' ),
],
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'show_in_rest' => true,
'rest_base' => 'strains',
'menu_icon' => 'dashicons-palmtree',
'supports' => [ 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields', 'revisions' ],
'has_archive' => true,
'rewrite' => [ 'slug' => 'strains', 'with_front' => false ],
'capability_type' => 'post',
'map_meta_cap' => true,
] );
// ── Cannabis Products ─────────────────────────────────────────────────────
register_post_type( 'wo_product', [
'label' => __( 'Cannabis Products', 'weedops' ),
'labels' => [
'name' => __( 'Cannabis Products', 'weedops' ),
'singular_name' => __( 'Cannabis Product', 'weedops' ),
'add_new_item' => __( 'Add New Product', 'weedops' ),
'edit_item' => __( 'Edit Product', 'weedops' ),
'not_found' => __( 'No products found', 'weedops' ),
],
'public' => true,
'show_in_rest' => true,
'rest_base' => 'cannabis-products',
'supports' => [ 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields' ],
'menu_icon' => 'dashicons-archive',
'has_archive' => true,
'rewrite' => [ 'slug' => 'products', 'with_front' => false ],
'capability_type' => 'post',
] );
// ── Compliance Records ────────────────────────────────────────────────────
register_post_type( 'wo_compliance', [
'label' => __( 'Compliance Records', 'weedops' ),
'labels' => [
'name' => __( 'Compliance Records', 'weedops' ),
'singular_name' => __( 'Compliance Record', 'weedops' ),
'add_new_item' => __( 'Add Record', 'weedops' ),
],
'public' => false,
'show_ui' => true,
'show_in_menu' => true,
'show_in_rest' => true,
'rest_base' => 'compliance',
'supports' => [ 'title', 'custom-fields' ],
'menu_icon' => 'dashicons-shield',
'capability_type' => 'post',
] );
// ── Dispensaries / Locations ──────────────────────────────────────────────
register_post_type( 'wo_dispensary', [
'label' => __( 'Dispensaries', 'weedops' ),
'public' => true,
'show_in_rest' => true,
'rest_base' => 'dispensaries',
'supports' => [ 'title', 'editor', 'thumbnail', 'custom-fields' ],
'menu_icon' => 'dashicons-location-alt',
'has_archive' => true,
'rewrite' => [ 'slug' => 'dispensaries', 'with_front' => false ],
] );
}
add_action( 'init', 'weedops_register_post_types' );
// ─────────────────────────────────────────────────────────────────────────────
// Taxonomies
// ─────────────────────────────────────────────────────────────────────────────
function weedops_register_taxonomies(): void {
// ── Strain Type: indica / sativa / hybrid / cbd ───────────────────────────
register_taxonomy( 'wo_strain_type', [ 'wo_strain', 'wo_product' ], [
'label' => __( 'Strain Type', 'weedops' ),
'labels' => [
'name' => __( 'Strain Types', 'weedops' ),
'singular_name' => __( 'Strain Type', 'weedops' ),
'add_new_item' => __( 'Add Strain Type', 'weedops' ),
'search_items' => __( 'Search Types', 'weedops' ),
],
'hierarchical' => true,
'show_in_rest' => true,
'rest_base' => 'strain-types',
'rewrite' => [ 'slug' => 'strain-type' ],
'show_admin_column' => true,
] );
// ── Effects (relaxed, euphoric, creative, …) ──────────────────────────────
register_taxonomy( 'wo_effect', [ 'wo_strain', 'wo_product' ], [
'label' => __( 'Effects', 'weedops' ),
'labels' => [
'name' => __( 'Effects', 'weedops' ),
'singular_name' => __( 'Effect', 'weedops' ),
'add_new_item' => __( 'Add Effect', 'weedops' ),
],
'hierarchical' => false,
'show_in_rest' => true,
'rest_base' => 'effects',
'rewrite' => [ 'slug' => 'effect' ],
'show_admin_column' => true,
] );
// ── Terpenes ──────────────────────────────────────────────────────────────
register_taxonomy( 'wo_terpene', [ 'wo_strain', 'wo_product' ], [
'label' => __( 'Terpenes', 'weedops' ),
'hierarchical' => false,
'show_in_rest' => true,
'rest_base' => 'terpenes',
'rewrite' => [ 'slug' => 'terpene' ],
] );
// ── Product Category (flower / concentrate / edible / topical / …) ────────
register_taxonomy( 'wo_product_cat', [ 'wo_product' ], [
'label' => __( 'Product Category', 'weedops' ),
'hierarchical' => true,
'show_in_rest' => true,
'rest_base' => 'product-categories',
'rewrite' => [ 'slug' => 'product-category' ],
] );
// ── Compliance Status ─────────────────────────────────────────────────────
register_taxonomy( 'wo_compliance_status', [ 'wo_compliance', 'wo_product' ], [
'label' => __( 'Compliance Status', 'weedops' ),
'hierarchical' => true,
'show_in_rest' => true,
'public' => false,
'show_ui' => true,
] );
}
add_action( 'init', 'weedops_register_taxonomies' );
// ─────────────────────────────────────────────────────────────────────────────
// Meta Field Registration (REST API + Gutenberg)
// ─────────────────────────────────────────────────────────────────────────────
function weedops_register_meta_fields(): void {
// Strain meta
$strain_meta = [
'wo_thc_percent' => [ 'type' => 'number', 'label' => 'THC %', 'sanitize' => 'floatval' ],
'wo_cbd_percent' => [ 'type' => 'number', 'label' => 'CBD %', 'sanitize' => 'floatval' ],
'wo_genetics' => [ 'type' => 'string', 'label' => 'Genetics / Lineage', 'sanitize' => 'sanitize_text_field' ],
'wo_flowering_time' => [ 'type' => 'integer', 'label' => 'Flowering Time (days)', 'sanitize' => 'absint' ],
'wo_yield_indoor' => [ 'type' => 'string', 'label' => 'Indoor Yield', 'sanitize' => 'sanitize_text_field' ],
'wo_yield_outdoor' => [ 'type' => 'string', 'label' => 'Outdoor Yield', 'sanitize' => 'sanitize_text_field' ],
'wo_difficulty' => [ 'type' => 'string', 'label' => 'Grow Difficulty', 'sanitize' => 'sanitize_text_field' ],
];
foreach ( $strain_meta as $key => $config ) {
register_post_meta( 'wo_strain', $key, [
'type' => $config['type'],
'single' => true,
'show_in_rest' => true,
'description' => $config['label'],
'sanitize_callback' => $config['sanitize'],
'auth_callback' => fn() => current_user_can( 'edit_posts' ),
] );
}
// Product meta
$product_meta = [
'wo_sku' => [ 'type' => 'string', 'label' => 'SKU / License #', 'sanitize' => 'sanitize_text_field' ],
'wo_batch_id' => [ 'type' => 'string', 'label' => 'Batch ID', 'sanitize' => 'sanitize_text_field' ],
'wo_lab_tested' => [ 'type' => 'boolean', 'label' => 'Lab Tested', 'sanitize' => 'rest_sanitize_boolean' ],
'wo_coa_url' => [ 'type' => 'string', 'label' => 'COA (Certificate of Analysis) URL','sanitize' => 'esc_url_raw' ],
'wo_price_gram' => [ 'type' => 'number', 'label' => 'Price per Gram', 'sanitize' => 'floatval' ],
'wo_inventory_qty' => [ 'type' => 'integer', 'label' => 'Inventory Qty (g)', 'sanitize' => 'absint' ],
'wo_origin_state' => [ 'type' => 'string', 'label' => 'Origin State', 'sanitize' => 'sanitize_text_field' ],
'wo_license_no' => [ 'type' => 'string', 'label' => 'Producer License #', 'sanitize' => 'sanitize_text_field' ],
];
foreach ( $product_meta as $key => $config ) {
register_post_meta( 'wo_product', $key, [
'type' => $config['type'],
'single' => true,
'show_in_rest' => true,
'description' => $config['label'],
'sanitize_callback' => $config['sanitize'],
'auth_callback' => fn() => current_user_can( 'edit_posts' ),
] );
}
}
add_action( 'init', 'weedops_register_meta_fields' );
// ─────────────────────────────────────────────────────────────────────────────
// Admin Meta Box — Strain Details
// ─────────────────────────────────────────────────────────────────────────────
function weedops_add_strain_meta_box(): void {
add_meta_box(
'wo_strain_details',
__( 'Strain Details', 'weedops' ),
'weedops_render_strain_meta_box',
'wo_strain',
'normal',
'high'
);
}
add_action( 'add_meta_boxes', 'weedops_add_strain_meta_box' );
function weedops_render_strain_meta_box( WP_Post $post ): void {
wp_nonce_field( 'wo_save_strain_details', 'wo_strain_nonce' );
$thc = get_post_meta( $post->ID, 'wo_thc_percent', true );
$cbd = get_post_meta( $post->ID, 'wo_cbd_percent', true );
$genetics = get_post_meta( $post->ID, 'wo_genetics', true );
$difficulty = get_post_meta( $post->ID, 'wo_difficulty', true );
// Strain type from taxonomy
$strain_types = get_the_terms( $post->ID, 'wo_strain_type' );
$current_type = ( $strain_types && ! is_wp_error( $strain_types ) )
? $strain_types[0]->slug
: '';
// Effects from taxonomy
$effects_terms = get_terms( [ 'taxonomy' => 'wo_effect', 'hide_empty' => false ] );
$assigned_effects = wp_get_post_terms( $post->ID, 'wo_effect', [ 'fields' => 'ids' ] );
?>
<style>
.wo-metabox-grid { display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-bottom:16px; }
.wo-metabox-field label { display:block; font-weight:600; margin-bottom:4px; }
.wo-metabox-field input,
.wo-metabox-field select { width:100%; }
.wo-effects-list { columns:3; gap:12px; }
.wo-effects-list label { display:block; font-weight:normal; margin-bottom:4px; }
</style>
<div class="wo-metabox-grid">
<div class="wo-metabox-field">
<label for="wo_thc_percent"><?php esc_html_e( 'THC %', 'weedops' ); ?></label>
<input type="number" id="wo_thc_percent" name="wo_thc_percent"
value="<?php echo esc_attr( $thc ); ?>"
step="0.1" min="0" max="100" placeholder="e.g. 22.5" />
</div>
<div class="wo-metabox-field">
<label for="wo_cbd_percent"><?php esc_html_e( 'CBD %', 'weedops' ); ?></label>
<input type="number" id="wo_cbd_percent" name="wo_cbd_percent"
value="<?php echo esc_attr( $cbd ); ?>"
step="0.1" min="0" max="100" placeholder="e.g. 0.5" />
</div>
<div class="wo-metabox-field">
<label for="wo_strain_type_select"><?php esc_html_e( 'Strain Type', 'weedops' ); ?></label>
<select id="wo_strain_type_select" name="wo_strain_type_select">
<option value=""><?php esc_html_e( '— Select type —', 'weedops' ); ?></option>
<?php foreach ( [ 'indica' => 'Indica', 'sativa' => 'Sativa', 'hybrid' => 'Hybrid', 'cbd' => 'CBD / High-CBD' ] as $slug => $label ) : ?>
<option value="<?php echo esc_attr( $slug ); ?>"
<?php selected( $current_type, $slug ); ?>>
<?php echo esc_html( $label ); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="wo-metabox-field">
<label for="wo_difficulty"><?php esc_html_e( 'Grow Difficulty', 'weedops' ); ?></label>
<select id="wo_difficulty" name="wo_difficulty">
<option value=""><?php esc_html_e( '— Select —', 'weedops' ); ?></option>
<?php foreach ( [ 'beginner' => 'Beginner', 'intermediate' => 'Intermediate', 'advanced' => 'Advanced' ] as $val => $lbl ) : ?>
<option value="<?php echo esc_attr( $val ); ?>"
<?php selected( $difficulty, $val ); ?>>
<?php echo esc_html( $lbl ); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="wo-metabox-field" style="margin-bottom:16px;">
<label for="wo_genetics"><?php esc_html_e( 'Genetics / Lineage', 'weedops' ); ?></label>
<input type="text" id="wo_genetics" name="wo_genetics"
value="<?php echo esc_attr( $genetics ); ?>"
placeholder="e.g. OG Kush × Durban Poison" style="width:100%;" />
</div>
<?php if ( ! is_wp_error( $effects_terms ) && ! empty( $effects_terms ) ) : ?>
<div class="wo-metabox-field">
<label><?php esc_html_e( 'Effects', 'weedops' ); ?></label>
<div class="wo-effects-list">
<?php foreach ( $effects_terms as $effect ) : ?>
<label>
<input type="checkbox" name="wo_effect_terms[]"
value="<?php echo esc_attr( $effect->term_id ); ?>"
<?php checked( in_array( $effect->term_id, (array) $assigned_effects, true ) ); ?> />
<?php echo esc_html( $effect->name ); ?>
</label>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php
}
function weedops_save_strain_meta( int $post_id ): void {
// Bail on autosave / AJAX / wrong post type
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
if ( ! isset( $_POST['wo_strain_nonce'] ) ) return;
if ( ! wp_verify_nonce( sanitize_key( $_POST['wo_strain_nonce'] ), 'wo_save_strain_details' ) ) return;
if ( ! current_user_can( 'edit_post', $post_id ) ) return;
if ( get_post_type( $post_id ) !== 'wo_strain' ) return;
// THC %
if ( isset( $_POST['wo_thc_percent'] ) ) {
$thc = (float) $_POST['wo_thc_percent'];
update_post_meta( $post_id, 'wo_thc_percent', $thc );
}
// CBD %
if ( isset( $_POST['wo_cbd_percent'] ) ) {
$cbd = (float) $_POST['wo_cbd_percent'];
update_post_meta( $post_id, 'wo_cbd_percent', $cbd );
}
// Genetics
if ( isset( $_POST['wo_genetics'] ) ) {
update_post_meta( $post_id, 'wo_genetics', sanitize_text_field( wp_unslash( $_POST['wo_genetics'] ) ) );
}
// Difficulty
if ( isset( $_POST['wo_difficulty'] ) ) {
$allowed = [ 'beginner', 'intermediate', 'advanced', '' ];
$diff = in_array( $_POST['wo_difficulty'], $allowed, true ) ? $_POST['wo_difficulty'] : '';
update_post_meta( $post_id, 'wo_difficulty', $diff );
}
// Strain type — set taxonomy term by slug
if ( isset( $_POST['wo_strain_type_select'] ) ) {
$type_slug = sanitize_key( $_POST['wo_strain_type_select'] );
if ( ! empty( $type_slug ) ) {
// Make sure the term exists (create if needed)
$term = get_term_by( 'slug', $type_slug, 'wo_strain_type' );
if ( ! $term ) {
$inserted = wp_insert_term( ucfirst( $type_slug ), 'wo_strain_type', [ 'slug' => $type_slug ] );
$term_id = is_wp_error( $inserted ) ? 0 : $inserted['term_id'];
} else {
$term_id = $term->term_id;
}
if ( $term_id ) {
wp_set_post_terms( $post_id, [ $term_id ], 'wo_strain_type' );
}
} else {
wp_set_post_terms( $post_id, [], 'wo_strain_type' );
}
}
// Effects — array of term IDs
$effect_ids = isset( $_POST['wo_effect_terms'] )
? array_map( 'absint', (array) $_POST['wo_effect_terms'] )
: [];
wp_set_post_terms( $post_id, $effect_ids, 'wo_effect' );
}
add_action( 'save_post_wo_strain', 'weedops_save_strain_meta' );