PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.7.1
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.7.1
2.7.1 2.7.0 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.4.0 2.3.3 2.3.2 2.3.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 All 130 releases
wdesignkit / includes / admin / import / class-wdkit-import-taxonomy.php

class-wdkit-import-taxonomy.php in WDesignKit – AI Templates, Widget Builder & MCP Workflow 2.7.1, at includes/admin/import/class-wdkit-import-taxonomy.php

179 lines 5.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Category and tag creation for imported posts.
4 *
5 * ── How the browser does it ─────────────────────────────────────────────────
6 *
7 * import_loader.js#import_post_taxonomy() posts the kit's `categories` and `tags` name lists
8 * to the `import_taxonomy` action, gets back name => term_id pairs, and writes those ids back
9 * onto the post data before importing it. That handler is already idempotent — every term is
10 * looked up with term_exists() before an insert is attempted — so this class adds no
11 * de-duplication of its own; it only reaches the same code without `$_POST`.
12 *
13 * Blog posts themselves are NOT a separate subsystem: the browser imports them through the
14 * ordinary template path with `wp_post_type = 'post'`, which Wdkit_Page_Importer already
15 * handles and which the runner already makes resumable per template. The only piece missing
16 * for posts was their taxonomy, which is this file.
17 *
18 * @package Wdesignkit
19 * @since 2.6.5
20 */
21
22 if ( ! defined( 'ABSPATH' ) ) {
23 exit;
24 }
25
26 if ( ! class_exists( 'Wdkit_Import_Taxonomy' ) ) {
27
28 /**
29 * Taxonomy import.
30 */
31 class Wdkit_Import_Taxonomy {
32
33 /**
34 * Create the categories and tags a kit needs.
35 *
36 * @param array $categories Category names.
37 * @param array $tags Tag names.
38 * @return array{categories:array<string,int>,tags:array<string,int>,failed:array}
39 */
40 public static function ensure( $categories, $tags = array() ) {
41 $result = array(
42 'categories' => array(),
43 'tags' => array(),
44 'failed' => array(),
45 );
46
47 $categories = self::clean_names( $categories );
48 $tags = self::clean_names( $tags );
49
50 if ( empty( $categories ) && empty( $tags ) ) {
51 return $result;
52 }
53
54 if ( ! class_exists( 'Wdkit_Api_Call' ) || ! method_exists( 'Wdkit_Api_Call', 'wdkit_import_taxonomy_data' ) ) {
55 $result['failed'][] = 'api_unavailable';
56
57 return $result;
58 }
59
60 $response = Wdkit_Api_Call::get_instance()->wdkit_import_taxonomy_data( $categories, $tags );
61
62 /* isset() on a WP_Error is itself fatal in PHP 8, so this has to be checked
63 * before the subscripts below, not inside them. */
64 if ( ! is_array( $response ) ) {
65 $result['failed'][] = is_wp_error( $response ) ? $response->get_error_message() : 'api_unavailable';
66
67 return $result;
68 }
69
70 $result['categories'] = self::index_terms( isset( $response['categories'] ) ? $response['categories'] : array(), $result['failed'] );
71 $result['tags'] = self::index_terms( isset( $response['tags'] ) ? $response['tags'] : array(), $result['failed'] );
72
73 return $result;
74 }
75
76 /**
77 * Turn the handler's list into a name => term_id map, collecting failures.
78 *
79 * The handler reports a per-term `error` rather than failing the batch, so a term that
80 * could not be created is recorded and the rest still resolve.
81 *
82 * @param array $terms Handler output.
83 * @param array $failed Failure accumulator (by reference).
84 * @return array<string,int>
85 */
86 private static function index_terms( $terms, &$failed ) {
87 $map = array();
88
89 foreach ( (array) $terms as $term ) {
90 if ( ! is_array( $term ) || empty( $term['name'] ) ) {
91 continue;
92 }
93
94 if ( ! empty( $term['error'] ) ) {
95 $failed[] = array(
96 'name' => $term['name'],
97 'error' => $term['error'],
98 );
99
100 continue;
101 }
102
103 if ( ! empty( $term['term_id'] ) ) {
104 $map[ (string) $term['name'] ] = (int) $term['term_id'];
105 }
106 }
107
108 return $map;
109 }
110
111 /**
112 * Names are user-visible strings, so they are trimmed and capped but not slugged —
113 * the handler sanitises and term_exists() matches on the name as given.
114 *
115 * @param mixed $names Raw names.
116 * @return string[]
117 */
118 private static function clean_names( $names ) {
119 if ( ! is_array( $names ) ) {
120 return array();
121 }
122
123 $clean = array();
124
125 foreach ( $names as $name ) {
126 if ( ! is_string( $name ) ) {
127 continue;
128 }
129
130 $name = trim( $name );
131
132 if ( '' === $name ) {
133 continue;
134 }
135
136 $clean[ $name ] = mb_substr( $name, 0, 200 );
137
138 if ( count( $clean ) >= 100 ) {
139 break;
140 }
141 }
142
143 return array_values( $clean );
144 }
145
146 /**
147 * Collect the category and tag names a kit's decoded templates reference.
148 *
149 * @param array[] $decoded_templates Decoded template payloads.
150 * @return array{categories:string[],tags:string[]}
151 */
152 public static function collect( $decoded_templates ) {
153 $categories = array();
154 $tags = array();
155
156 foreach ( (array) $decoded_templates as $decoded ) {
157 if ( ! is_array( $decoded ) ) {
158 continue;
159 }
160
161 foreach ( array( 'categories', 'category' ) as $key ) {
162 if ( ! empty( $decoded[ $key ] ) && is_array( $decoded[ $key ] ) ) {
163 $categories = array_merge( $categories, $decoded[ $key ] );
164 }
165 }
166
167 if ( ! empty( $decoded['tags'] ) && is_array( $decoded['tags'] ) ) {
168 $tags = array_merge( $tags, $decoded['tags'] );
169 }
170 }
171
172 return array(
173 'categories' => self::clean_names( $categories ),
174 'tags' => self::clean_names( $tags ),
175 );
176 }
177 }
178 }
179