register_taxonomies(); self::instance()->register_post_types(); flush_rewrite_rules(); } public static function deactivate() { flush_rewrite_rules(); } public function get_cpts() { $cpts = get_option( self::CPT_OPTION, array() ); return is_array( $cpts ) ? array_map( array( $this, 'normalize_cpt_record' ), $cpts ) : array(); } public function get_taxonomies() { $taxonomies = get_option( self::TAX_OPTION, array() ); return is_array( $taxonomies ) ? array_map( array( $this, 'normalize_taxonomy_record' ), $taxonomies ) : array(); } public function get_cpt_overrides() { $cpts = get_option( self::CPT_OVERRIDE_OPTION, array() ); return is_array( $cpts ) ? array_map( array( $this, 'normalize_cpt_record' ), $cpts ) : array(); } public function get_taxonomy_overrides() { $tax = get_option( self::TAX_OVERRIDE_OPTION, array() ); return is_array( $tax ) ? array_map( array( $this, 'normalize_taxonomy_record' ), $tax ) : array(); } public function save_cpts( $cpts ) { update_option( self::CPT_OPTION, array_values( array_map( array( $this, 'normalize_cpt_record' ), $cpts ) ), false ); } public function save_taxonomies( $taxonomies ) { update_option( self::TAX_OPTION, array_values( array_map( array( $this, 'normalize_taxonomy_record' ), $taxonomies ) ), false ); } public function save_cpt_overrides( $cpts ) { update_option( self::CPT_OVERRIDE_OPTION, array_values( array_map( array( $this, 'normalize_cpt_record' ), $cpts ) ), false ); } public function save_taxonomy_overrides( $taxonomies ) { update_option( self::TAX_OVERRIDE_OPTION, array_values( array_map( array( $this, 'normalize_taxonomy_record' ), $taxonomies ) ), false ); } public function normalize_cpt_record( $record ) { $record = is_array( $record ) ? $record : array(); $custom_fields = array(); if ( ! empty( $record['custom_fields'] ) && is_array( $record['custom_fields'] ) ) { foreach ( $record['custom_fields'] as $field ) { if ( ! is_array( $field ) ) { continue; } $type = sanitize_key( $field['type'] ?? 'text' ); if ( ! in_array( $type, array( 'text', 'textarea', 'number', 'url', 'email', 'date', 'checkbox', 'select', 'radio' ), true ) ) { $type = 'text'; } $options = array(); if ( ! empty( $field['options'] ) && is_array( $field['options'] ) ) { foreach ( $field['options'] as $option ) { $option = sanitize_text_field( $option ); if ( '' !== $option ) { $options[] = $option; } } } $custom_fields[] = array( 'label' => sanitize_text_field( $field['label'] ?? '' ), 'key' => sanitize_key( $field['key'] ?? '' ), 'type' => $type, 'description' => sanitize_text_field( $field['description'] ?? '' ), 'options' => $options, ); } } $template_settings = $this->normalize_cpt_template_settings( $record['template_settings'] ?? array() ); return array( 'key' => sanitize_key( $record['key'] ?? '' ), 'singular_label' => sanitize_text_field( $record['singular_label'] ?? '' ), 'plural_label' => sanitize_text_field( $record['plural_label'] ?? '' ), 'slug' => sanitize_title( $record['slug'] ?? '' ), 'hierarchical' => ! empty( $record['hierarchical'] ) ? 1 : 0, 'object_type' => ! empty( $record['object_type'] ) && is_array( $record['object_type'] ) ? array_values( array_unique( array_map( 'sanitize_key', $record['object_type'] ) ) ) : array(), 'template_settings' => $this->normalize_taxonomy_template_settings( $record['template_settings'] ?? array() ), 'is_override' => ! empty( $record['is_override'] ) ? 1 : 0, ); } public function normalize_taxonomy_record( $record ) { $record = is_array( $record ) ? $record : array(); $template_settings = $this->normalize_cpt_template_settings( $record['template_settings'] ?? array() ); return array( 'key' => sanitize_key( $record['key'] ?? '' ), 'singular_label' => sanitize_text_field( $record['singular_label'] ?? '' ), 'plural_label' => sanitize_text_field( $record['plural_label'] ?? '' ), 'slug' => sanitize_title( $record['slug'] ?? '' ), 'hierarchical' => ! empty( $record['hierarchical'] ) ? 1 : 0, 'object_type' => ! empty( $record['object_type'] ) && is_array( $record['object_type'] ) ? array_values( array_unique( array_map( 'sanitize_key', $record['object_type'] ) ) ) : array(), 'template_settings' => $this->normalize_taxonomy_template_settings( $record['template_settings'] ?? array() ), 'is_override' => ! empty( $record['is_override'] ) ? 1 : 0, ); } public function normalize_cpt_template_settings( $settings ) { $settings = is_array( $settings ) ? $settings : array(); $single_mode = isset( $settings['single_mode'] ) && in_array( $settings['single_mode'], array( 'plugin', 'theme' ), true ) ? $settings['single_mode'] : 'plugin'; $archive_mode = isset( $settings['archive_mode'] ) && in_array( $settings['archive_mode'], array( 'plugin', 'theme' ), true ) ? $settings['archive_mode'] : 'plugin'; $archive_columns = absint( $settings['archive_columns'] ?? 3 ); if ( $archive_columns < 1 || $archive_columns > 4 ) { $archive_columns = 3; } return array( 'single_mode' => $single_mode, 'archive_mode' => $archive_mode, 'single_show_featured' => ! empty( $settings['single_show_featured'] ) ? 1 : 0, 'single_show_meta' => ! empty( $settings['single_show_meta'] ) ? 1 : 0, 'single_show_terms' => ! empty( $settings['single_show_terms'] ) ? 1 : 0, 'single_show_custom_fields' => ! empty( $settings['single_show_custom_fields'] ) ? 1 : 0, 'archive_show_featured' => ! empty( $settings['archive_show_featured'] ) ? 1 : 0, 'archive_show_excerpt' => ! empty( $settings['archive_show_excerpt'] ) ? 1 : 0, 'archive_show_meta' => ! empty( $settings['archive_show_meta'] ) ? 1 : 0, 'archive_columns' => $archive_columns, 'archive_intro' => sanitize_textarea_field( $settings['archive_intro'] ?? '' ), ); } public function normalize_taxonomy_template_settings( $settings ) { $settings = is_array( $settings ) ? $settings : array(); $archive_mode = isset( $settings['archive_mode'] ) && in_array( $settings['archive_mode'], array( 'plugin', 'theme' ), true ) ? $settings['archive_mode'] : 'plugin'; $archive_columns = absint( $settings['archive_columns'] ?? 3 ); if ( $archive_columns < 1 || $archive_columns > 4 ) { $archive_columns = 3; } return array( 'archive_mode' => $archive_mode, 'archive_show_featured' => ! empty( $settings['archive_show_featured'] ) ? 1 : 0, 'archive_show_excerpt' => ! empty( $settings['archive_show_excerpt'] ) ? 1 : 0, 'archive_show_meta' => ! empty( $settings['archive_show_meta'] ) ? 1 : 0, 'archive_columns' => $archive_columns, 'archive_intro' => sanitize_textarea_field( $settings['archive_intro'] ?? '' ), ); } public function get_cpt_template_settings( $post_type ) { $record = $this->get_any_managed_cpt_by_key( $post_type ); return $record['template_settings'] ?? $this->normalize_cpt_template_settings( array() ); } public function get_taxonomy_template_settings( $taxonomy ) { $record = $this->get_taxonomy_by_key( $taxonomy ) ?: $this->get_taxonomy_override_by_key( $taxonomy ); return $record['template_settings'] ?? $this->normalize_taxonomy_template_settings( array() ); } public function get_export_payload() { return array( 'plugin' => 'ansico-cpt-and-taxonomies', 'version' => ANSICO_CPTAX_VERSION, 'exported_at_gmt' => gmdate( 'c' ), 'cpts' => $this->get_cpts(), 'taxonomies' => $this->get_taxonomies(), 'cpt_overrides' => $this->get_cpt_overrides(), 'taxonomy_overrides' => $this->get_taxonomy_overrides(), ); } public function register_taxonomies() { foreach ( $this->get_taxonomies() as $taxonomy ) { if ( empty( $taxonomy['key'] ) ) { continue; } $attached_post_types = isset( $taxonomy['object_type'] ) && is_array( $taxonomy['object_type'] ) ? array_filter( $taxonomy['object_type'] ) : array( 'post' ); if ( empty( $attached_post_types ) ) { $attached_post_types = array( 'post' ); } register_taxonomy( $taxonomy['key'], $attached_post_types, $this->build_taxonomy_args( $taxonomy ) ); } } public function register_post_types() { foreach ( $this->get_cpts() as $cpt ) { if ( empty( $cpt['key'] ) ) { continue; } register_post_type( $cpt['key'], $this->build_post_type_args( $cpt ) ); } } public function build_taxonomy_args( $taxonomy, $existing_args = array() ) { $singular = $taxonomy['singular_label'] ?? ucfirst( $taxonomy['key'] ); $plural = $taxonomy['plural_label'] ?? $singular . 's'; $slug = $taxonomy['slug'] ?? $taxonomy['key']; $hierarchical = ! empty( $taxonomy['hierarchical'] ); $labels = array( 'name' => $plural, 'singular_name' => $singular, 'search_items' => sprintf( __( 'Search %s', 'ansico-cpt-and-taxonomies' ), $plural ), 'all_items' => sprintf( __( 'All %s', 'ansico-cpt-and-taxonomies' ), $plural ), 'parent_item' => sprintf( __( 'Parent %s', 'ansico-cpt-and-taxonomies' ), $singular ), 'parent_item_colon' => sprintf( __( 'Parent %s:', 'ansico-cpt-and-taxonomies' ), $singular ), 'edit_item' => sprintf( __( 'Edit %s', 'ansico-cpt-and-taxonomies' ), $singular ), 'update_item' => sprintf( __( 'Update %s', 'ansico-cpt-and-taxonomies' ), $singular ), 'add_new_item' => sprintf( __( 'Add New %s', 'ansico-cpt-and-taxonomies' ), $singular ), 'new_item_name' => sprintf( __( 'New %s Name', 'ansico-cpt-and-taxonomies' ), $singular ), 'menu_name' => $plural, ); return array_merge( $existing_args, array( 'labels' => $labels, 'public' => $existing_args['public'] ?? true, 'show_ui' => $existing_args['show_ui'] ?? true, 'show_admin_column' => $existing_args['show_admin_column'] ?? true, 'show_in_nav_menus' => $existing_args['show_in_nav_menus'] ?? true, 'show_tagcloud' => ! $hierarchical, 'show_in_rest' => $existing_args['show_in_rest'] ?? true, 'hierarchical' => $hierarchical, 'rewrite' => array( 'slug' => sanitize_title( $slug ) ), ) ); } public function build_post_type_args( $cpt, $existing_args = array() ) { $singular = $cpt['singular_label'] ?? ucfirst( $cpt['key'] ); $plural = $cpt['plural_label'] ?? $singular . 's'; $slug = $cpt['slug'] ?? $cpt['key']; $supports = isset( $cpt['supports'] ) && is_array( $cpt['supports'] ) ? array_values( array_filter( $cpt['supports'] ) ) : array( 'title', 'editor' ); $taxonomies = isset( $cpt['taxonomies'] ) && is_array( $cpt['taxonomies'] ) ? array_values( array_filter( $cpt['taxonomies'] ) ) : array(); if ( ! in_array( 'custom-fields', $supports, true ) && ! empty( $cpt['custom_fields'] ) ) { $supports[] = 'custom-fields'; } $labels = array( 'name' => $plural, 'singular_name' => $singular, 'menu_name' => $plural, 'name_admin_bar' => $singular, 'add_new' => __( 'Add New', 'ansico-cpt-and-taxonomies' ), 'add_new_item' => sprintf( __( 'Add New %s', 'ansico-cpt-and-taxonomies' ), $singular ), 'new_item' => sprintf( __( 'New %s', 'ansico-cpt-and-taxonomies' ), $singular ), 'edit_item' => sprintf( __( 'Edit %s', 'ansico-cpt-and-taxonomies' ), $singular ), 'view_item' => sprintf( __( 'View %s', 'ansico-cpt-and-taxonomies' ), $singular ), 'all_items' => sprintf( __( 'All %s', 'ansico-cpt-and-taxonomies' ), $plural ), 'search_items' => sprintf( __( 'Search %s', 'ansico-cpt-and-taxonomies' ), $plural ), 'parent_item_colon' => sprintf( __( 'Parent %s:', 'ansico-cpt-and-taxonomies' ), $singular ), 'not_found' => sprintf( __( 'No %s found.', 'ansico-cpt-and-taxonomies' ), strtolower( $plural ) ), 'not_found_in_trash' => sprintf( __( 'No %s found in Trash.', 'ansico-cpt-and-taxonomies' ), strtolower( $plural ) ), 'featured_image' => __( 'Featured image', 'ansico-cpt-and-taxonomies' ), 'set_featured_image' => __( 'Set featured image', 'ansico-cpt-and-taxonomies' ), 'remove_featured_image' => __( 'Remove featured image', 'ansico-cpt-and-taxonomies' ), 'use_featured_image' => __( 'Use as featured image', 'ansico-cpt-and-taxonomies' ), 'archives' => sprintf( __( '%s archives', 'ansico-cpt-and-taxonomies' ), $singular ), ); return array_merge( $existing_args, array( 'labels' => $labels, 'public' => $existing_args['public'] ?? true, 'show_ui' => $existing_args['show_ui'] ?? true, 'show_in_menu' => $existing_args['show_in_menu'] ?? true, 'show_in_rest' => $existing_args['show_in_rest'] ?? true, 'has_archive' => $existing_args['has_archive'] ?? true, 'rewrite' => array( 'slug' => sanitize_title( $slug ) ), 'exclude_from_search' => ! empty( $cpt['exclude_from_search'] ), 'can_export' => ! empty( $cpt['can_export'] ), 'supports' => $supports, 'taxonomies' => $taxonomies, 'menu_position' => $existing_args['menu_position'] ?? 20, 'menu_icon' => $existing_args['menu_icon'] ?? 'dashicons-admin-post', ) ); } public function filter_post_type_args( $args, $post_type ) { $override = $this->get_cpt_override_by_key( $post_type ); if ( ! $override ) { return $args; } return $this->build_post_type_args( $override, $args ); } public function filter_taxonomy_args( $args, $taxonomy ) { $override = $this->get_taxonomy_override_by_key( $taxonomy ); if ( ! $override ) { return $args; } return $this->build_taxonomy_args( $override, $args ); } public function attach_overridden_taxonomies_to_objects() { foreach ( $this->get_cpt_overrides() as $override ) { if ( empty( $override['key'] ) || empty( $override['taxonomies'] ) || ! is_array( $override['taxonomies'] ) ) { continue; } foreach ( $override['taxonomies'] as $taxonomy ) { if ( taxonomy_exists( $taxonomy ) ) { register_taxonomy_for_object_type( $taxonomy, $override['key'] ); } } } foreach ( $this->get_taxonomy_overrides() as $override ) { if ( empty( $override['key'] ) || empty( $override['object_type'] ) || ! is_array( $override['object_type'] ) ) { continue; } foreach ( $override['object_type'] as $object_type ) { if ( post_type_exists( $object_type ) ) { register_taxonomy_for_object_type( $override['key'], $object_type ); } } } } public function register_meta_boxes() { foreach ( $this->get_all_managed_cpts() as $cpt ) { if ( empty( $cpt['key'] ) || empty( $cpt['custom_fields'] ) || ! is_array( $cpt['custom_fields'] ) ) { continue; } add_meta_box( 'ansico_cptax_fields_' . $cpt['key'], __( 'Custom Fields', 'ansico-cpt-and-taxonomies' ), array( $this, 'render_meta_box' ), $cpt['key'], 'normal', 'default', array( 'fields' => $cpt['custom_fields'] ) ); } } public function render_meta_box( $post, $box ) { $fields = $box['args']['fields'] ?? array(); wp_nonce_field( 'ansico_cptax_save_fields', 'ansico_cptax_nonce' ); echo ''; foreach ( $fields as $field ) { $key = $field['key'] ?? ''; $label = $field['label'] ?? $key; $type = $field['type'] ?? 'text'; $description = $field['description'] ?? ''; $options = ! empty( $field['options'] ) && is_array( $field['options'] ) ? $field['options'] : array(); if ( ! $key ) { continue; } $meta_key = 'ansico_' . $key; $value = get_post_meta( $post->ID, $meta_key, true ); echo ''; echo ''; echo ''; echo ''; } echo '
'; switch ( $type ) { case 'textarea': echo ''; break; case 'checkbox': echo ''; break; case 'select': echo ''; break; case 'radio': foreach ( $options as $index => $option ) { echo ''; } break; default: echo ''; break; } if ( $description ) { echo '

' . esc_html( $description ) . '

'; } echo '
'; } public function save_custom_fields( $post_id ) { if ( ! isset( $_POST['ansico_cptax_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['ansico_cptax_nonce'] ) ), 'ansico_cptax_save_fields' ) ) { return; } if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } $post_type = get_post_type( $post_id ); $cpt = $this->get_any_managed_cpt_by_key( $post_type ); if ( ! $cpt || empty( $cpt['custom_fields'] ) ) { return; } $submitted = isset( $_POST['ansico_fields'] ) && is_array( $_POST['ansico_fields'] ) ? wp_unslash( $_POST['ansico_fields'] ) : array(); foreach ( $cpt['custom_fields'] as $field ) { $key = $field['key'] ?? ''; $type = $field['type'] ?? 'text'; $options = ! empty( $field['options'] ) && is_array( $field['options'] ) ? $field['options'] : array(); if ( ! $key ) { continue; } $meta_key = 'ansico_' . $key; if ( 'checkbox' === $type ) { $value = isset( $submitted[ $key ] ) ? '1' : '0'; } elseif ( 'textarea' === $type ) { $value = sanitize_textarea_field( $submitted[ $key ] ?? '' ); } elseif ( in_array( $type, array( 'select', 'radio' ), true ) ) { $candidate = sanitize_text_field( $submitted[ $key ] ?? '' ); $value = in_array( $candidate, $options, true ) ? $candidate : ''; } else { $value = sanitize_text_field( $submitted[ $key ] ?? '' ); } update_post_meta( $post_id, $meta_key, $value ); } } public function get_cpt_by_key( $key ) { foreach ( $this->get_cpts() as $cpt ) { if ( isset( $cpt['key'] ) && $cpt['key'] === $key ) { return $cpt; } } return null; } public function get_taxonomy_by_key( $key ) { foreach ( $this->get_taxonomies() as $taxonomy ) { if ( isset( $taxonomy['key'] ) && $taxonomy['key'] === $key ) { return $taxonomy; } } return null; } public function get_cpt_override_by_key( $key ) { foreach ( $this->get_cpt_overrides() as $cpt ) { if ( isset( $cpt['key'] ) && $cpt['key'] === $key ) { return $cpt; } } return null; } public function get_taxonomy_override_by_key( $key ) { foreach ( $this->get_taxonomy_overrides() as $tax ) { if ( isset( $tax['key'] ) && $tax['key'] === $key ) { return $tax; } } return null; } public function get_any_managed_cpt_by_key( $key ) { return $this->get_cpt_by_key( $key ) ?: $this->get_cpt_override_by_key( $key ); } public function get_all_managed_cpts() { return array_merge( $this->get_cpts(), $this->get_cpt_overrides() ); } public function get_editable_post_types() { $objects = get_post_types( array(), 'objects' ); $skip = array( 'attachment', 'revision', 'nav_menu_item', 'custom_css', 'customize_changeset', 'oembed_cache', 'user_request', 'wp_block', 'wp_template', 'wp_template_part', 'wp_global_styles', 'wp_navigation', 'wp_font_family', 'wp_font_face' ); foreach ( $skip as $post_type ) { unset( $objects[ $post_type ] ); } ksort( $objects ); return $objects; } public function get_editable_taxonomies() { $objects = get_taxonomies( array(), 'objects' ); unset( $objects['nav_menu'] ); ksort( $objects ); return $objects; } public function template_include( $template ) { if ( is_singular() ) { $post_type = get_post_type(); if ( $post_type && $this->get_any_managed_cpt_by_key( $post_type ) ) { $settings = $this->get_cpt_template_settings( $post_type ); if ( 'plugin' === ( $settings['single_mode'] ?? 'plugin' ) ) { $plugin_template = ANSICO_CPTAX_PATH . 'templates/single-cpt.php'; if ( file_exists( $plugin_template ) ) { return $plugin_template; } } } } if ( is_post_type_archive() ) { $post_type = get_query_var( 'post_type' ); if ( is_array( $post_type ) ) { $post_type = reset( $post_type ); } if ( $post_type && $this->get_any_managed_cpt_by_key( $post_type ) ) { $settings = $this->get_cpt_template_settings( $post_type ); if ( 'plugin' === ( $settings['archive_mode'] ?? 'plugin' ) ) { $plugin_template = ANSICO_CPTAX_PATH . 'templates/archive-cpt.php'; if ( file_exists( $plugin_template ) ) { return $plugin_template; } } } } if ( is_tax() || is_category() || is_tag() ) { $term = get_queried_object(); if ( ! empty( $term->taxonomy ) && ( $this->get_taxonomy_by_key( $term->taxonomy ) || $this->get_taxonomy_override_by_key( $term->taxonomy ) ) ) { $settings = $this->get_taxonomy_template_settings( $term->taxonomy ); if ( 'plugin' === ( $settings['archive_mode'] ?? 'plugin' ) ) { $plugin_template = ANSICO_CPTAX_PATH . 'templates/taxonomy-archive.php'; if ( file_exists( $plugin_template ) ) { return $plugin_template; } } } } return $template; } }