Includes front-page, strain archive/single templates, header/footer, functions.php with custom post types, and base styles.
391 lines
20 KiB
PHP
391 lines
20 KiB
PHP
<?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' );
|