plugin = $plugin; add_action( 'admin_menu', array( $this, 'admin_menu' ) ); add_action( 'admin_init', array( $this, 'handle_actions' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); } public function admin_menu() { add_menu_page( __( 'Ansico CPT & Taxonomies', 'ansico-cpt-and-taxonomies' ), __( 'Ansico CPT', 'ansico-cpt-and-taxonomies' ), 'manage_options', 'ansico-cptax', array( $this, 'render_cpts_page' ), 'dashicons-database-add', 58 ); add_submenu_page( 'ansico-cptax', __( 'Custom Post Types', 'ansico-cpt-and-taxonomies' ), __( 'Custom Post Types', 'ansico-cpt-and-taxonomies' ), 'manage_options', 'ansico-cptax', array( $this, 'render_cpts_page' ) ); add_submenu_page( 'ansico-cptax', __( 'Taxonomies', 'ansico-cpt-and-taxonomies' ), __( 'Taxonomies', 'ansico-cpt-and-taxonomies' ), 'manage_options', 'ansico-cptax-taxonomies', array( $this, 'render_taxonomies_page' ) ); add_submenu_page( 'ansico-cptax', __( 'Tools', 'ansico-cpt-and-taxonomies' ), __( 'Tools', 'ansico-cpt-and-taxonomies' ), 'manage_options', 'ansico-cptax-tools', array( $this, 'render_tools_page' ) ); } public function enqueue_assets( $hook_suffix ) { if ( false === strpos( $hook_suffix, 'ansico-cptax' ) ) { return; } wp_enqueue_style( 'ansico-cptax-admin', ANSICO_CPTAX_URL . 'assets/admin.css', array(), ANSICO_CPTAX_VERSION ); wp_enqueue_script( 'ansico-cptax-admin', ANSICO_CPTAX_URL . 'assets/admin.js', array(), ANSICO_CPTAX_VERSION, true ); } public function handle_actions() { if ( ! current_user_can( 'manage_options' ) ) { return; } if ( empty( $_POST['ansico_cptax_action'] ) ) { return; } $action = sanitize_text_field( wp_unslash( $_POST['ansico_cptax_action'] ) ); if ( 'export_json' === $action ) { check_admin_referer( 'ansico_cptax_export' ); $this->download_export(); } check_admin_referer( 'ansico_cptax_save' ); if ( 'save_cpt' === $action ) { $record = $this->sanitize_cpt_request(); if ( ! empty( $record['is_override'] ) ) { $records = $this->plugin->get_cpt_overrides(); $records = $this->upsert_by_key( $records, sanitize_key( wp_unslash( $_POST['current_key'] ?? '' ) ), $record, 'key' ); $this->plugin->save_cpt_overrides( $records ); } else { $records = $this->plugin->get_cpts(); $records = $this->upsert_by_key( $records, sanitize_key( wp_unslash( $_POST['current_key'] ?? '' ) ), $record, 'key' ); $this->plugin->save_cpts( $records ); } flush_rewrite_rules(); wp_safe_redirect( admin_url( 'admin.php?page=ansico-cptax&updated=1' ) ); exit; } if ( 'delete_cpt' === $action ) { $key = sanitize_key( wp_unslash( $_POST['key'] ?? '' ) ); $is_override = ! empty( $_POST['is_override'] ); $records = $is_override ? $this->plugin->get_cpt_overrides() : $this->plugin->get_cpts(); $records = array_values( array_filter( $records, function( $cpt ) use ( $key ) { return ( $cpt['key'] ?? '' ) !== $key; } ) ); if ( $is_override ) { $this->plugin->save_cpt_overrides( $records ); } else { $this->plugin->save_cpts( $records ); } flush_rewrite_rules(); wp_safe_redirect( admin_url( 'admin.php?page=ansico-cptax&deleted=1' ) ); exit; } if ( 'save_taxonomy' === $action ) { $record = $this->sanitize_taxonomy_request(); if ( ! empty( $record['is_override'] ) ) { $records = $this->plugin->get_taxonomy_overrides(); $records = $this->upsert_by_key( $records, sanitize_key( wp_unslash( $_POST['current_key'] ?? '' ) ), $record, 'key' ); $this->plugin->save_taxonomy_overrides( $records ); } else { $records = $this->plugin->get_taxonomies(); $records = $this->upsert_by_key( $records, sanitize_key( wp_unslash( $_POST['current_key'] ?? '' ) ), $record, 'key' ); $this->plugin->save_taxonomies( $records ); } flush_rewrite_rules(); wp_safe_redirect( admin_url( 'admin.php?page=ansico-cptax-taxonomies&updated=1' ) ); exit; } if ( 'delete_taxonomy' === $action ) { $key = sanitize_key( wp_unslash( $_POST['key'] ?? '' ) ); $is_override = ! empty( $_POST['is_override'] ); $records = $is_override ? $this->plugin->get_taxonomy_overrides() : $this->plugin->get_taxonomies(); $records = array_values( array_filter( $records, function( $taxonomy ) use ( $key ) { return ( $taxonomy['key'] ?? '' ) !== $key; } ) ); if ( $is_override ) { $this->plugin->save_taxonomy_overrides( $records ); } else { $this->plugin->save_taxonomies( $records ); } flush_rewrite_rules(); wp_safe_redirect( admin_url( 'admin.php?page=ansico-cptax-taxonomies&deleted=1' ) ); exit; } if ( 'import_json' === $action ) { $replace_existing = ! empty( $_POST['replace_existing'] ); $result = $this->handle_import_json( $replace_existing ); $status = ! empty( $result['error'] ) ? 'import_error=1' : 'imported=1'; $message = ! empty( $result['message'] ) ? rawurlencode( $result['message'] ) : ''; wp_safe_redirect( admin_url( 'admin.php?page=ansico-cptax-tools&' . $status . '&message=' . $message ) ); exit; } } private function download_export() { $filename = 'ansico-cptax-export-' . gmdate( 'Y-m-d-His' ) . '.json'; $payload = $this->plugin->get_export_payload(); nocache_headers(); header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) ); header( 'Content-Disposition: attachment; filename=' . $filename ); echo wp_json_encode( $payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ); exit; } private function handle_import_json( $replace_existing ) { if ( empty( $_FILES['import_file']['tmp_name'] ) ) { return array( 'error' => true, 'message' => __( 'No JSON file was uploaded.', 'ansico-cpt-and-taxonomies' ), ); } $raw = file_get_contents( $_FILES['import_file']['tmp_name'] ); // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown if ( false === $raw || '' === $raw ) { return array( 'error' => true, 'message' => __( 'The uploaded file could not be read.', 'ansico-cpt-and-taxonomies' ), ); } $payload = json_decode( $raw, true ); if ( ! is_array( $payload ) ) { return array( 'error' => true, 'message' => __( 'The uploaded file is not valid JSON.', 'ansico-cpt-and-taxonomies' ), ); } $cpts = $this->normalize_import_records( $payload['cpts'] ?? array(), 'cpt' ); $taxonomies = $this->normalize_import_records( $payload['taxonomies'] ?? array(), 'taxonomy' ); $cpt_overrides = $this->normalize_import_records( $payload['cpt_overrides'] ?? array(), 'cpt' ); $taxonomy_overrides = $this->normalize_import_records( $payload['taxonomy_overrides'] ?? array(), 'taxonomy' ); if ( $replace_existing ) { $this->plugin->save_cpts( $cpts ); $this->plugin->save_taxonomies( $taxonomies ); $this->plugin->save_cpt_overrides( $cpt_overrides ); $this->plugin->save_taxonomy_overrides( $taxonomy_overrides ); } else { $this->plugin->save_cpts( $this->merge_by_key( $this->plugin->get_cpts(), $cpts ) ); $this->plugin->save_taxonomies( $this->merge_by_key( $this->plugin->get_taxonomies(), $taxonomies ) ); $this->plugin->save_cpt_overrides( $this->merge_by_key( $this->plugin->get_cpt_overrides(), $cpt_overrides ) ); $this->plugin->save_taxonomy_overrides( $this->merge_by_key( $this->plugin->get_taxonomy_overrides(), $taxonomy_overrides ) ); } flush_rewrite_rules(); return array( 'error' => false, 'message' => __( 'JSON import completed.', 'ansico-cpt-and-taxonomies' ), ); } private function normalize_import_records( $records, $type ) { $normalized = array(); if ( ! is_array( $records ) ) { return $normalized; } foreach ( $records as $record ) { if ( 'taxonomy' === $type ) { $record = $this->plugin->normalize_taxonomy_record( $record ); } else { $record = $this->plugin->normalize_cpt_record( $record ); } if ( empty( $record['key'] ) ) { continue; } $normalized[] = $record; } return $normalized; } private function merge_by_key( $existing, $incoming ) { foreach ( $incoming as $record ) { $existing = $this->upsert_by_key( $existing, $record['key'] ?? '', $record, 'key' ); } return array_values( $existing ); } private function upsert_by_key( $records, $current_key, $record, $key_field ) { $updated = false; foreach ( $records as $index => $existing ) { if ( ( $existing[ $key_field ] ?? '' ) === $current_key ) { $records[ $index ] = $record; $updated = true; break; } } if ( ! $updated ) { $records[] = $record; } return array_values( $records ); } private function sanitize_cpt_request() { $supports = isset( $_POST['supports'] ) && is_array( $_POST['supports'] ) ? array_map( 'sanitize_key', wp_unslash( $_POST['supports'] ) ) : array(); $taxonomies = isset( $_POST['taxonomies'] ) && is_array( $_POST['taxonomies'] ) ? array_map( 'sanitize_key', wp_unslash( $_POST['taxonomies'] ) ) : array(); $custom_fields = $this->sanitize_custom_fields(); $template_settings = array( 'single_mode' => sanitize_key( wp_unslash( $_POST['single_mode'] ?? 'plugin' ) ), 'archive_mode' => sanitize_key( wp_unslash( $_POST['archive_mode'] ?? 'plugin' ) ), 'single_show_featured' => ! empty( $_POST['single_show_featured'] ) ? 1 : 0, 'single_show_meta' => ! empty( $_POST['single_show_meta'] ) ? 1 : 0, 'single_show_terms' => ! empty( $_POST['single_show_terms'] ) ? 1 : 0, 'single_show_custom_fields' => ! empty( $_POST['single_show_custom_fields'] ) ? 1 : 0, 'archive_show_featured' => ! empty( $_POST['archive_show_featured'] ) ? 1 : 0, 'archive_show_excerpt' => ! empty( $_POST['archive_show_excerpt'] ) ? 1 : 0, 'archive_show_meta' => ! empty( $_POST['archive_show_meta'] ) ? 1 : 0, 'archive_columns' => absint( wp_unslash( $_POST['archive_columns'] ?? 3 ) ), 'archive_intro' => sanitize_textarea_field( wp_unslash( $_POST['archive_intro'] ?? '' ) ), ); return $this->plugin->normalize_cpt_record( array( 'key' => sanitize_key( wp_unslash( $_POST['key'] ?? '' ) ), 'singular_label' => sanitize_text_field( wp_unslash( $_POST['singular_label'] ?? '' ) ), 'plural_label' => sanitize_text_field( wp_unslash( $_POST['plural_label'] ?? '' ) ), 'slug' => sanitize_title( wp_unslash( $_POST['slug'] ?? '' ) ), 'exclude_from_search' => ! empty( $_POST['exclude_from_search'] ) ? 1 : 0, 'can_export' => ! empty( $_POST['can_export'] ) ? 1 : 0, 'supports' => $supports, 'taxonomies' => $taxonomies, 'custom_fields' => $custom_fields, 'template_settings' => $template_settings, 'is_override' => ! empty( $_POST['is_override'] ) ? 1 : 0, ) ); } private function sanitize_taxonomy_request() { $object_type = isset( $_POST['object_type'] ) && is_array( $_POST['object_type'] ) ? array_map( 'sanitize_key', wp_unslash( $_POST['object_type'] ) ) : array(); $template_settings = array( 'archive_mode' => sanitize_key( wp_unslash( $_POST['archive_mode'] ?? 'plugin' ) ), 'archive_show_featured' => ! empty( $_POST['archive_show_featured'] ) ? 1 : 0, 'archive_show_excerpt' => ! empty( $_POST['archive_show_excerpt'] ) ? 1 : 0, 'archive_show_meta' => ! empty( $_POST['archive_show_meta'] ) ? 1 : 0, 'archive_columns' => absint( wp_unslash( $_POST['archive_columns'] ?? 3 ) ), 'archive_intro' => sanitize_textarea_field( wp_unslash( $_POST['archive_intro'] ?? '' ) ), ); return $this->plugin->normalize_taxonomy_record( array( 'key' => sanitize_key( wp_unslash( $_POST['key'] ?? '' ) ), 'singular_label' => sanitize_text_field( wp_unslash( $_POST['singular_label'] ?? '' ) ), 'plural_label' => sanitize_text_field( wp_unslash( $_POST['plural_label'] ?? '' ) ), 'slug' => sanitize_title( wp_unslash( $_POST['slug'] ?? '' ) ), 'hierarchical' => ! empty( $_POST['hierarchical'] ) ? 1 : 0, 'object_type' => $object_type, 'template_settings' => $template_settings, 'is_override' => ! empty( $_POST['is_override'] ) ? 1 : 0, ) ); } private function sanitize_custom_fields() { $labels = isset( $_POST['field_label'] ) && is_array( $_POST['field_label'] ) ? wp_unslash( $_POST['field_label'] ) : array(); $keys = isset( $_POST['field_key'] ) && is_array( $_POST['field_key'] ) ? wp_unslash( $_POST['field_key'] ) : array(); $types = isset( $_POST['field_type'] ) && is_array( $_POST['field_type'] ) ? wp_unslash( $_POST['field_type'] ) : array(); $descriptions = isset( $_POST['field_description'] ) && is_array( $_POST['field_description'] ) ? wp_unslash( $_POST['field_description'] ) : array(); $options = isset( $_POST['field_options'] ) && is_array( $_POST['field_options'] ) ? wp_unslash( $_POST['field_options'] ) : array(); $custom_fields = array(); foreach ( $keys as $index => $key ) { $key = sanitize_key( $key ); if ( ! $key ) { continue; } $raw_options = preg_split( '/[ ,]+/', (string) ( $options[ $index ] ?? '' ) ); $normalized_options = array(); foreach ( $raw_options as $option ) { $option = sanitize_text_field( $option ); if ( '' !== $option ) { $normalized_options[] = $option; } } $custom_fields[] = array( 'label' => sanitize_text_field( $labels[ $index ] ?? '' ), 'key' => $key, 'type' => sanitize_key( $types[ $index ] ?? 'text' ), 'description' => sanitize_text_field( $descriptions[ $index ] ?? '' ), 'options' => $normalized_options, ); } return $custom_fields; } private function render_page_header( $title, $description, $active_tab ) { ?>
' . esc_html__( 'Saved successfully.', 'ansico-cpt-and-taxonomies' ) . '
' . esc_html__( 'Deleted successfully.', 'ansico-cpt-and-taxonomies' ) . '
' . esc_html( $message ) . '
' . esc_html( $message ) . '
' . esc_html( $cpt['key'] ?? '' ) . '| ' . esc_html__( 'Name', 'ansico-cpt-and-taxonomies' ) . ' | ' . esc_html__( 'Key', 'ansico-cpt-and-taxonomies' ) . ' | ' . esc_html__( 'Slug', 'ansico-cpt-and-taxonomies' ) . ' | ' . esc_html__( 'Actions', 'ansico-cpt-and-taxonomies' ) . ' |
|---|---|---|---|
| ' . esc_html__( 'No items found.', 'ansico-cpt-and-taxonomies' ) . ' | |||
| ' . esc_html( $taxonomy['plural_label'] ?? $taxonomy['key'] ) . ' | '; echo '' . esc_html( $taxonomy['key'] ?? '' ) . ' | ';
echo '' . esc_html( $taxonomy['slug'] ?? '' ) . ' | '; echo '' . esc_html__( 'Edit', 'ansico-cpt-and-taxonomies' ) . ' '; echo ' | '; echo '