PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 3.2.5
ShopBuilder – WooCommerce Builder For Elementor v3.2.5
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
shopbuilder / app / Modules / VariationSwatches / TermMeta.php

TermMeta.php in ShopBuilder – WooCommerce Builder For Elementor 3.2.5, at app/Modules/VariationSwatches/TermMeta.php

445 lines 14.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace RadiusTheme\SB\Modules\VariationSwatches;
4
5 use RadiusTheme\SB\Helpers\Fns;
6
7 /**
8 * Handles custom term meta fields for product attributes (color, image, etc.)
9 * in WooCommerce taxonomies like `pa_color`, `pa_size`, etc.
10 */
11 class TermMeta {
12
13 /**
14 * The taxonomy to which the term meta fields apply.
15 *
16 * @var string
17 */
18 private $taxonomy;
19
20 /**
21 * Post type context (usually 'product').
22 *
23 * @var string
24 */
25 private $post_type = 'product';
26
27 /**
28 * Meta field definitions.
29 *
30 * @var array
31 */
32 private $fields = [];
33
34 /**
35 * TermMeta constructor.
36 *
37 * @param string $taxonomy Taxonomy name.
38 * @param array $fields Array of meta field definitions.
39 */
40 public function __construct( $taxonomy, $fields = [] ) {
41
42 $this->taxonomy = $taxonomy;
43 $this->fields = $fields;
44
45 add_action( 'delete_term', [ $this, 'delete_term' ], 5, 3 );
46
47 // Add form.
48 add_action( "{$this->taxonomy}_add_form_fields", [ $this, 'add' ] );
49 add_action( "{$this->taxonomy}_edit_form_fields", [ $this, 'edit' ], 10 );
50 add_action( 'created_term', [ $this, 'save' ], 10, 3 );
51 add_action( 'edit_term', [ $this, 'save' ], 10, 3 );
52 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
53
54 // Add columns.
55 add_filter( "manage_edit-{$this->taxonomy}_columns", [ $this, 'taxonomy_columns' ] );
56 add_filter( "manage_{$this->taxonomy}_custom_column", [ $this, 'taxonomy_column' ], 10, 3 );
57 }
58 /**
59 * Adds a column for previewing meta fields.
60 *
61 * @param array $columns Original taxonomy columns.
62 * @return array Modified columns.
63 */
64 public function taxonomy_columns( $columns ) {
65 $new_columns = [];
66 if ( isset( $columns['cb'] ) ) {
67 $new_columns['cb'] = $columns['cb'];
68 unset( $columns['cb'] );
69 }
70 $new_columns['rtsb-meta-preview'] = '';
71
72 return array_merge( $new_columns, $columns );
73 }
74 /**
75 * Renders the content for the custom meta preview column.
76 *
77 * @param string $columns Current output (ignored).
78 * @param string $column Column name.
79 * @param int $term_id Term ID.
80 * @return void
81 */
82 public function taxonomy_column( $columns, $column, $term_id ) {
83 $attribute = SwatchesFns::get_wc_attribute_taxonomy( $this->taxonomy );
84 $fields = SwatchesFns::get_taxonomy_meta_fields( $attribute->attribute_type );
85 ?>
86 <style>
87 #rtsb-meta-preview {
88 width: 30px;
89 }
90 .rtsb-preview{
91 border: 1px solid #000;
92 height: 30px;
93 width: 30px;
94 }
95 </style>
96 <?php
97 switch ( $attribute->attribute_type ) {
98 case 'color':
99 $key = $fields[0]['id'];
100 $is_dual_key = isset( $fields[1]['id'] ) ? $fields[1]['id'] : '';
101 $dual_color_key = isset( $fields[2]['id'] ) ? $fields[2]['id'] : '';
102
103 $value = sanitize_hex_color( get_term_meta( $term_id, $key, true ) );
104
105 $is_dual = (bool) ( get_term_meta( $term_id, $is_dual_key, true ) === 'yes' );
106 if ( $is_dual ) {
107 $secondary_color = sanitize_hex_color( get_term_meta( $term_id, $dual_color_key, true ) );
108 printf( '<div class="rtsb-preview rtsb-color-preview rtsb-dual-color-preview" style="background: linear-gradient(-45deg, %1$s 0%%, %1$s 50%%, %2$s 50%%, %2$s 100%%);"></div>', esc_attr( $secondary_color ), esc_attr( $value ) );
109 } else {
110 printf( '<div class="rtsb-preview rtsb-color-preview" style="background-color:%s;"></div>', esc_attr( $value ) );
111 }
112 break;
113 case 'image':
114 $key = $fields[0]['id'];
115 $attachment_id = absint( get_term_meta( $term_id, $key, true ) );
116 $image = wp_get_attachment_image_url( $attachment_id );
117
118 printf( '<img src="%s" class="rtsb-preview rtsb-image-preview" />', esc_url( $image ) );
119 break;
120 case 'button':
121 default:
122 do_action( 'rtsb/variation/attribute/preview', $term_id, $attribute, $fields );
123 break;
124 }
125 }
126 /**
127 * Delete term meta when a term is deleted.
128 *
129 * @param int $term_id The term ID.
130 * @param int $tt_id Term taxonomy ID.
131 * @param string $taxonomy Taxonomy slug.
132 */
133 public function delete_term( $term_id, $tt_id, $taxonomy ) {
134 global $wpdb;
135
136 $term_id = absint( $term_id );
137
138 if ( $term_id && $taxonomy === $this->taxonomy ) {
139 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
140 $wpdb->delete( $wpdb->termmeta, [ 'term_id' => $term_id ], [ '%d' ] );
141 wp_cache_delete( $term_id, 'term_meta' ); // Optional, clears any remaining cache.
142 }
143 }
144 /**
145 * Enqueues scripts and styles needed for color pickers and media uploads.
146 *
147 * @return void
148 */
149 public function enqueue_scripts() {
150 wp_enqueue_media();
151 wp_enqueue_style( 'wp-color-picker' );
152 wp_enqueue_script( 'wp-color-picker' );
153 }
154 /**
155 * Saves custom term meta on term creation or update.
156 *
157 * @param int $term_id Term ID.
158 * @param int $tt_id Term taxonomy ID (optional).
159 * @param string $taxonomy Taxonomy name (optional).
160 * @return void
161 */
162 public function save( $term_id, $tt_id = '', $taxonomy = '' ) {
163 if ( $taxonomy !== $this->taxonomy ) {
164 return;
165 }
166 if ( ! wp_verify_nonce( Fns::get_nonce(), rtsb()->nonceText ) ) {
167 return; // Invalid nonce.
168 }
169 foreach ( $this->fields as $field ) {
170 $field_id = $field['id'];
171 $field_type = $field['type'];
172 // If field is not submitted and is a checkbox, remove its meta.
173 if ( ! isset( $_POST[ $field_id ] ) ) {
174 if ( 'checkbox' === $field_type ) {
175 delete_term_meta( $term_id, $field_id );
176 }
177 continue;
178 }
179
180 switch ( $field_type ) {
181 case 'text':
182 case 'color':
183 $post_value = sanitize_text_field( wp_unslash( $_POST[ $field_id ] ) );
184 break;
185 case 'url':
186 $post_value = esc_url_raw( wp_unslash( $_POST[ $field_id ] ) );
187 break;
188 case 'image':
189 $post_value = absint( wp_unslash( $_POST[ $field_id ] ) );
190 break;
191 case 'textarea':
192 $post_value = sanitize_textarea_field( wp_unslash( $_POST[ $field_id ] ) );
193 break;
194 case 'checkbox':
195 $post_value = sanitize_text_field( wp_unslash( $_POST[ $field_id ] ) );
196 $post_value = 'yes' === $post_value ? 'yes' : '';
197 break;
198 case 'editor':
199 $post_value = wp_kses_post( wp_unslash( $_POST[ $field_id ] ) );
200 break;
201 case 'select':
202 case 'select2':
203 $post_value = sanitize_key( wp_unslash( $_POST[ $field_id ] ) );
204 break;
205 default:
206 $post_value = sanitize_text_field( wp_unslash( $_POST[ $field_id ] ) );
207 do_action( 'rtsb/save/term/meta', $term_id, $field, $post_value, $taxonomy );
208 break;
209 }
210
211 update_term_meta( $term_id, $field_id, $post_value );
212 }
213
214 do_action( 'rtsb/after/term/meta/saved', $term_id, $taxonomy );
215 }
216
217 /**
218 * Outputs meta fields on the "Add New Term" screen.
219 *
220 * @return void
221 */
222 public function add() {
223 $this->generate_fields();
224 }
225 /**
226 * Outputs meta fields on the "Edit Term" screen.
227 *
228 * @param object $term Term object.
229 * @return void
230 */
231 public function edit( $term ) {
232 $this->generate_fields( $term );
233 }
234 /**
235 * Generates form fields for add/edit screens.
236 *
237 * @param object|false $term Optional term object for edit context.
238 * @return void
239 */
240 private function generate_fields( $term = false ) {
241 $screen = get_current_screen();
242 if ( ( $screen->post_type == $this->post_type ) && ( $screen->taxonomy == $this->taxonomy ) ) {
243 self::generate_form_fields( $this->fields, $term );
244 }
245 }
246 /**
247 * Outputs each field's HTML markup.
248 *
249 * @param array $fields Field definitions.
250 * @param object|false $term Term object, or false for "Add New".
251 * @return void
252 */
253 public static function generate_form_fields( $fields, $term ) {
254 wp_enqueue_script( 'rtsb-variation-swatch-admin' );
255 $fields = apply_filters( 'rtsb/term/meta/fields', $fields, $term );
256
257 if ( empty( $fields ) ) {
258 return;
259 }
260 wp_nonce_field( rtsb()->nonceText, rtsb()->nonceId );
261 foreach ( $fields as $field ) {
262
263 $field = apply_filters( 'rtsb/term/meta/field', $field, $term );
264
265 $field['id'] = esc_html( $field['id'] );
266
267 if ( ! $term ) {
268 $field['value'] = isset( $field['default'] ) ? $field['default'] : '';
269 } else {
270 $field['value'] = get_term_meta( $term->term_id, $field['id'], true );
271 }
272
273 $field['size'] = isset( $field['size'] ) ? $field['size'] : '40';
274 $field['required'] = ( isset( $field['required'] ) && true === $field['required'] ) ? ' aria-required="true"' : '';
275 $field['placeholder'] = ( isset( $field['placeholder'] ) ) ? ' placeholder="' . $field['placeholder'] . '" data-placeholder="' . $field['placeholder'] . '"' : '';
276 $field['desc'] = ( isset( $field['desc'] ) ) ? $field['desc'] : '';
277
278 $field['dependency'] = ( isset( $field['dependency'] ) ) ? $field['dependency'] : [];
279
280 self::field_start( $field, $term );
281 switch ( $field['type'] ) {
282 case 'text':
283 case 'url':
284 ?>
285 <input name="<?php echo esc_attr( $field['id'] ); ?>" id="<?php echo esc_attr( $field['id'] ); ?>"
286 type="<?php echo esc_attr( $field['type'] ); ?>"
287 value="<?php echo esc_attr( $field['value'] ); ?>"
288 size="<?php echo esc_attr( $field['size'] ); ?>" <?php echo esc_html( $field['required'] ) . esc_html( $field['placeholder'] ); ?>>
289 <?php
290 break;
291 case 'color':
292 ?>
293 <input name="<?php echo esc_attr( $field['id'] ); ?>" id="<?php echo esc_attr( $field['id'] ); ?>" type="text"
294 class="rtsb-vs-color-picker" value="<?php echo esc_attr( $field['value'] ); ?>"
295 data-default-color="<?php echo esc_attr( $field['value'] ); ?>"
296 size="<?php echo esc_attr( $field['size'] ); ?>" <?php echo esc_html( $field['required'] ) . esc_html( $field['placeholder'] ); ?>>
297 <?php
298 break;
299 case 'textarea':
300 ?>
301 <textarea name="<?php echo esc_attr( $field['id'] ); ?>" id="<?php echo esc_attr( $field['id'] ); ?>" rows="5"
302 cols="<?php echo esc_attr( $field['size'] ); ?>" <?php echo esc_attr( $field['required'] ) . esc_attr( $field['placeholder'] ); ?>><?php echo esc_textarea( $field['value'] ); ?></textarea>
303 <?php
304 break;
305 case 'editor':
306 $field['settings'] = $field['settings'] ?? [
307 'textarea_rows' => 8,
308 'quicktags' => false,
309 'media_buttons' => false,
310 ];
311 wp_editor( $field['value'], $field['id'], $field['settings'] );
312 break;
313 case 'select':
314 case 'select2':
315 $field['options'] = isset( $field['options'] ) ? $field['options'] : [];
316 $field['multiple'] = isset( $field['multiple'] ) ? ' multiple="multiple"' : '';
317 $css_class = ( 'select2' === $field['type'] ) ? 'rtsb-selectwoo' : '';
318
319 ?>
320 <select name="<?php echo esc_attr( $field['id'] ); ?>" id="<?php echo esc_attr( $field['id'] ); ?>"
321 class="<?php echo esc_attr( $css_class ); ?>" <?php echo esc_html( $field['multiple'] ); ?>>
322 <?php
323 foreach ( $field['options'] as $key => $option ) {
324 echo '<option' . selected( $field['value'], $key, false ) . ' value="' . esc_attr( $key ) . '">' . esc_html( $option ) . '</option>';
325 }
326 ?>
327 </select>
328 <?php
329 break;
330 case 'image':
331 ?>
332 <div class="rtsb-image-wrapper">
333 <div class="image-preview">
334 <img data-placeholder="<?php echo esc_url( self::placeholder_img_src() ); ?>"
335 src="<?php echo esc_url( self::get_img_src( $field['value'] ) ); ?>" width="60px"
336 height="60px"/>
337 </div>
338 <div class="button-wrapper">
339 <input type="hidden" id="<?php echo esc_attr( $field['id'] ); ?>" name="<?php echo esc_attr( $field['id'] ); ?>"
340 value="<?php echo esc_attr( $field['value'] ); ?>"/>
341 <button type="button"
342 class="rtsb-vs-upload-image button button-primary button-small"><?php esc_html_e( 'Upload / Add image', 'shopbuilder' ); ?></button>
343 <button type="button"
344 style="<?php echo( empty( $field['value'] ) ? 'display:none' : '' ); ?>"
345 class="rtsb-vs-remove-image button button-danger button-small"><?php esc_html_e( 'Remove image', 'shopbuilder' ); ?></button>
346 </div>
347 </div>
348 <?php
349 break;
350 case 'checkbox':
351 $label = isset( $field['trigger_label'] ) ? $field['trigger_label'] : $field['label'];
352 ?>
353 <label for="<?php echo esc_attr( $field['id'] ); ?>">
354
355 <input name="<?php echo esc_attr( $field['id'] ); ?>" id="<?php echo esc_attr( $field['id'] ); ?>"
356 <?php checked( $field['value'], 'yes' ); ?>
357 type="<?php echo esc_attr( $field['type'] ); ?>"
358 value="yes" <?php echo esc_html( $field['required'] ) . esc_html( $field['placeholder'] ); ?>>
359
360 <?php echo esc_html( $label ); ?></label>
361 <?php
362 break;
363 default:
364 do_action( 'rtsb/term/meta/field', $field, $term );
365 break;
366
367 }
368 self::field_end( $field, $term );
369
370 }
371 }
372 /**
373 * Outputs field start wrapper for add/edit screens.
374 *
375 * @param array $field Field config.
376 * @param object|false $term Term object.
377 * @return void
378 */
379 private static function field_start( $field, $term ) {
380 $depends = empty( $field['dependency'] ) ? '' : "data-rt-depends='" . wp_json_encode( $field['dependency'] ) . "'";
381 $classes = [ $field['id'] ];
382 if ( isset( $field['class'] ) ) {
383 $classes[] = $field['class'];
384 }
385 if ( ! $term ) {
386 ?>
387 <div <?php echo esc_attr( $depends ); ?> class="form-field <?php echo esc_attr( implode( ' ', $classes ) ); ?> <?php echo empty( $field['required'] ) ? '' : 'form-required'; ?>">
388 <label for="<?php echo esc_attr( $field['id'] ); ?>"><?php echo esc_html( $field['label'] ); ?></label>
389 <?php
390 } else {
391 ?>
392 <tr <?php echo esc_attr( $depends ); ?> class="form-field <?php echo esc_attr( $field['id'] ); ?> <?php echo empty( $field['required'] ) ? '' : 'form-required'; ?>">
393 <th scope="row">
394 <label for="<?php echo esc_attr( $field['id'] ); ?>"><?php echo esc_html( $field['label'] ); ?></label>
395 </th>
396 <td>
397 <?php
398 }
399 }
400 /**
401 * Outputs the image source from attachment ID or placeholder.
402 *
403 * @param int|false $thumbnail_id Attachment ID.
404 * @return string Image URL.
405 */
406 private static function get_img_src( $thumbnail_id = false ) {
407 if ( ! empty( $thumbnail_id ) ) {
408 $image = wp_get_attachment_thumb_url( $thumbnail_id );
409 } else {
410 $image = self::placeholder_img_src();
411 }
412
413 return $image;
414 }
415 /**
416 * Returns a fallback placeholder image URL.
417 *
418 * @return string
419 */
420 private static function placeholder_img_src() {
421 return 'https://placehold.co/400';
422 }
423 /**
424 * Outputs closing tags and descriptions for form fields.
425 *
426 * @param array $field Field config.
427 * @param object|false $term Term object.
428 * @return void
429 */
430 private static function field_end( $field, $term ) {
431 if ( ! $term ) {
432 ?>
433 <p><?php echo wp_kses_post( $field['desc'] ); ?></p>
434 </div>
435 <?php
436 } else {
437 ?>
438 <p class="description"><?php echo wp_kses_post( $field['desc'] ); ?></p></td>
439 </tr>
440 <?php
441 }
442 }
443 }
444
445