PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.9.6
Subscriptions for WooCommerce with Stripe Recurring Payments v1.9.6
2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 All 61 releases
subscription / includes / Admin / SettingsHelper.php

SettingsHelper.php in Subscriptions for WooCommerce with Stripe Recurring Payments 1.9.6, at includes/Admin/SettingsHelper.php

583 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Settings Helper File
4 *
5 * @package SpringDevs\Subscription\Admin
6 */
7
8 namespace SpringDevs\Subscription\Admin;
9
10 /**
11 * Settings Helper Class
12 *
13 * @package SpringDevs\Subscription\Admin
14 */
15 class SettingsHelper {
16 /**
17 * Singleton instance
18 *
19 * @var SettingsHelper|null
20 */
21 private static $instance = null;
22
23 /**
24 * Get singleton instance
25 *
26 * @return SettingsHelper
27 */
28 public static function get_instance() {
29 if ( null === self::$instance ) {
30 self::$instance = new self();
31 }
32 return self::$instance;
33 }
34
35 /**
36 * Initialize the class.
37 */
38 private function __construct() {
39 add_filter( 'process_subscrpt_settings_fields', [ $this, 'process_settings_fields' ], 100, 1 );
40 }
41
42 /**
43 * Process settings fields.
44 *
45 * @param array $fields Settings fields.
46 * @return array Processed settings fields.
47 */
48 public function process_settings_fields( $fields ) {
49 // Group settings fields.
50 $fields = $this->group_settings_fields( $fields );
51
52 // Sort fields by priority (groups & fields).
53 $fields = $this->sort_settings_fields( $fields );
54
55 return $fields;
56 }
57
58 /**
59 * Group settings fields.
60 *
61 * @param array $fields Settings fields.
62 * @return array Processed settings fields.
63 */
64 public function group_settings_fields( $fields ) {
65 $tmp_fields = [];
66 foreach ( $fields as $field ) {
67 $field_group = $field['group'] ?? 'main';
68
69 if ( $field['type'] === 'heading' ) {
70 $group_priority = $field['priority'] ?? 0;
71 $tmp_fields[ $field_group ]['priority'] = $group_priority;
72 $field['priority'] = -1;
73 }
74
75 $tmp_fields[ $field_group ]['fields'][] = $field;
76 }
77 return $tmp_fields;
78 }
79
80 /**
81 * Sort settings fields.
82 *
83 * @param array $fields Settings fields.
84 * @return array Processed settings fields.
85 */
86 public function sort_settings_fields( $fields ) {
87 // Sort groups by priority.
88 uasort(
89 $fields,
90 function ( $a, $b ) {
91 $priority_a = $a['priority'] ?? 0;
92 $priority_b = $b['priority'] ?? 0;
93 return $priority_a <=> $priority_b;
94 }
95 );
96
97 // Sort fields within each group by priority.
98 foreach ( $fields as $group_key => $group_data ) {
99 uasort(
100 $group_data['fields'],
101 function ( $a, $b ) {
102 $priority_a = $a['priority'] ?? 0;
103 $priority_b = $b['priority'] ?? 0;
104 return $priority_a <=> $priority_b;
105 }
106 );
107 $fields[ $group_key ]['fields'] = $group_data['fields'];
108 }
109 return $fields;
110 }
111
112 /**
113 * Render specified settings field.
114 *
115 * @param string $field Field type.
116 * @param array $args Field arguments.
117 * @param bool $should_print Whether to print the field or return as HTML string.
118 */
119 public static function render_settings_field( $field, $args, $should_print = true ) {
120 if ( empty( $field ) ) {
121 $field = 'input'; // Default field type.
122 subscrpt_write_debug_log( "[SettingsHelper] Field type not specified. Defaulting to 'input'." );
123 }
124
125 switch ( $field ) {
126 case 'heading':
127 return self::render_heading( $args, $should_print );
128 case 'switch':
129 case 'toggle':
130 return self::render_switch_field( $args, $should_print );
131 case 'select':
132 return self::render_select_field( $args, $should_print );
133 case 'multi_select':
134 return self::render_multiselect_field( $args, $should_print );
135 case 'join':
136 return self::render_joined_field( $args, $should_print );
137 case 'input':
138 default:
139 return self::render_input_field( $args, $should_print );
140 }
141 }
142
143
144 /**
145 * Text Element HTML.
146 *
147 * @param array $args Same as 'render_text_field'.
148 * @param bool $join_item Whether to return element for 'join' container or not.
149 */
150 public static function inp_element( $args = [], $join_item = false ) {
151 $id = $args['id'];
152 $value = $args['value'] ?? '';
153 $placeholder = $args['placeholder'] ?? '';
154 $type = $args['type'] ?? 'text';
155
156 $join_class = $join_item ? 'join-item mx-0!' : '';
157
158 $disabled_attr = isset( $args['disabled'] ) && $args['disabled'] ? 'disabled' : '';
159
160 $style_attr = 'outline-offset: 0.5px !important; outline-color: #e5e7eb !important;';
161 if ( isset( $args['style'] ) ) {
162 $style_attr .= ' ' . $args['style'];
163 }
164
165 $other_attrs_html = '';
166 foreach ( ( $args['attributes'] ?? [] ) as $attr_key => $attr_value ) {
167 $other_attrs_html .= sprintf( ' %s="%s" ', esc_attr( $attr_key ), esc_attr( $attr_value ) );
168 }
169
170 ob_start();
171 ?>
172 <input
173 id="<?php echo esc_attr( $id ); ?>"
174 name="<?php echo esc_attr( $id ); ?>"
175 class="input! min-w-80! max-w-full! <?php echo esc_attr( $join_class ); ?>"
176 style="<?php echo esc_attr( $style_attr ); ?>"
177 type="<?php echo esc_attr( $type ); ?>"
178 placeholder="<?php echo esc_attr( $placeholder ); ?>"
179 value="<?php echo esc_attr( $value ); ?>"
180 <?php echo esc_attr( $disabled_attr ); ?>
181 <?php echo wp_kses_post( $other_attrs_html ); ?>
182 />
183 <?php
184 return ob_get_clean();
185 }
186
187 /**
188 * Select Element HTML.
189 *
190 * @param array $args Same as 'render_select_field'.
191 * @param bool $join_item Whether to return element for 'join' container or not.
192 */
193 public static function select_element( $args = [], $join_item = false ) {
194 $id = $args['id'];
195 $value = $args['value'] ?? '';
196
197 $join_class = $join_item ? 'join-item mx-0!' : '';
198 $wc_enhanced_class = isset( $args['enhanced'] ) && $args['enhanced'];
199
200 $basic_classes = 'select! min-w-80! max-w-full!';
201
202 // WC Select2 style (multiselect).
203 if ( $wc_enhanced_class ) {
204 // Enqueue WooCommerce enhanced select script & styles.
205 wp_enqueue_style( 'woocommerce_admin_styles' );
206 wp_enqueue_script( 'wc-enhanced-select' );
207
208 // Update basic classes for wc-enhanced-select.
209 $basic_classes = 'min-w-80! max-w-full! wc-enhanced-select';
210 }
211
212 // Need to prefix name with [] for multiple select.
213 $name_prefix = '';
214 if ( isset( $args['attributes']['multiple'] ) && $args['attributes']['multiple'] ) {
215 $name_prefix = '[]';
216 }
217
218 $style_attr = 'outline-offset: 0.5px !important; outline-color: #e5e7eb !important;';
219 if ( isset( $args['style'] ) ) {
220 $style_attr .= ' ' . $args['style'];
221 }
222
223 $other_attrs_html = '';
224 foreach ( ( $args['attributes'] ?? [] ) as $attr_key => $attr_value ) {
225 $other_attrs_html .= sprintf( ' %s="%s" ', esc_attr( $attr_key ), esc_attr( $attr_value ) );
226 }
227
228 $options_html = '';
229 foreach ( ( $args['options'] ?? [] ) as $value => $label ) {
230 $selected = false;
231 if ( isset( $args['selected'] ) ) {
232 if ( is_array( $args['selected'] ) ) {
233 $selected = in_array( $value, $args['selected'], true );
234 } else {
235 $selected = $args['selected'] === $value;
236 }
237 }
238
239 $disabled = false;
240 if ( isset( $args['disabled'] ) ) {
241 if ( is_array( $args['disabled'] ) ) {
242 $disabled = in_array( $value, $args['disabled'], true );
243 } else {
244 $disabled = $args['disabled'] === $value;
245 }
246 }
247
248 $options_tmp_html = sprintf(
249 '<option value="%s" %s %s>%s</option>',
250 esc_attr( $value ),
251 $selected ? 'selected' : '',
252 $disabled ? 'disabled' : '',
253 esc_html( $label ),
254 );
255 $options_html .= $options_tmp_html;
256 }
257
258 ob_start();
259 ?>
260 <select
261 id="<?php echo esc_attr( $id ); ?>"
262 name="<?php echo esc_attr( $id . $name_prefix ); ?>"
263 class="<?php echo esc_attr( $basic_classes . ' ' . $join_class ); ?>"
264 style="<?php echo esc_attr( $style_attr ); ?>"
265 <?php echo wp_kses_post( $other_attrs_html ); ?>
266 >
267 <?php
268 // Output intentionally not escaped as options are already escaped during generation & re-escaping breaks the HTML structure.
269 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
270 echo $options_html;
271 ?>
272 </select>
273 <?php
274 return ob_get_clean();
275 }
276
277 /**
278 * Render Field Heading.
279 *
280 * - Args:
281 * - title (string) - Field title.
282 * - description (string) - Field description (optional).
283 *
284 * @param array $args Field arguments.
285 * @param bool $should_print Whether to print the field or return as HTML string.
286 */
287 public static function render_heading( $args = [], $should_print = true ) {
288 $title = $args['title'] ?? '';
289 $description = $args['description'] ?? '';
290
291 ob_start();
292 ?>
293 <div class="my-4 first-of-type:mt-0">
294 <h2 class="m-0!"><?php echo esc_html( $title ); ?></h2>
295
296 <?php if ( ! empty( $description ) ) : ?>
297 <p class="mb-0! mt-2! ml-0.5! text-[13px]! text-gray-500!">
298 <?php echo wp_kses_post( $description ); ?>
299 </p>
300 <?php endif; ?>
301 </div>
302 <?php
303 $html_content = ob_get_clean();
304
305 // Output not escaped intentionally. Breaks the HTML structure when escaped.
306 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
307 return $should_print ? print( $html_content ) : $html_content;
308 }
309
310 /**
311 * Render Text field.
312 *
313 * - Args:
314 * - id (string) - Field ID.
315 * - title (string) - Field title.
316 * - description (string) - Field description (optional).
317 * - value (string) - Default value.
318 * - placeholder (string) - Default placeholder.
319 * - disabled (bool) - Disabled status.
320 * - type (string) - Input type [text, email, number, date, time, etc.].
321 *
322 * @param array $args Field arguments.
323 * @param bool $should_print Whether to print the field or return as HTML string.
324 */
325 public static function render_input_field( $args = [], $should_print = true ) {
326 $title = $args['title'] ?? '';
327 $description = $args['description'] ?? '';
328
329 // Return error if ID is not provided.
330 if ( empty( $args['id'] ?? '' ) ) {
331 $field_hint = empty( $title ) ? 'Error' : $title;
332 $no_id_msg = '<p><strong>' . $field_hint . ':</strong> ' . __( 'Field ID is required.', 'subscription' ) . '</p>';
333 return $should_print ? print wp_kses_post( $no_id_msg ) : $no_id_msg;
334 }
335
336 // Input HTML.
337 $text_el_html = self::inp_element( $args );
338
339 ob_start();
340 ?>
341 <div class="grid grid-cols-6 gap-4">
342 <span class="font-semibold text-sm mt-0.5"><?php echo esc_html( $title ); ?></span>
343
344 <div class="col-span-5">
345 <?php
346 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
347 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
348 echo $text_el_html;
349 ?>
350 <br/>
351 <?php if ( ! empty( $description ) ) : ?>
352 <p class="mb-0! mt-2! ml-0.5! text-[13px]! text-gray-500!">
353 <?php echo wp_kses_post( $description ); ?>
354 </p>
355 <?php endif; ?>
356 </div>
357 </div>
358 <?php
359 $html_content = ob_get_clean();
360
361 // Output not escaped intentionally. Breaks the HTML structure when escaped.
362 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
363 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
364 return $should_print ? print( $html_content ) : $html_content;
365 }
366
367 /**
368 * Render Switch field.
369 *
370 * - Args:
371 * - id (string) - Field ID.
372 * - title (string) - Field title.
373 * - label (string) - Checkbox label.
374 * - description (string) - Field description (optional).
375 * - value (string) - Checked value.
376 * - checked (bool) - Checked status.
377 * - disabled (bool) - Disabled status.
378 *
379 * @param array $args Field arguments.
380 * @param bool $should_print Whether to print the field or return as HTML string.
381 */
382 public static function render_switch_field( $args = [], $should_print = true ) {
383 $id = $args['id'];
384 $title = $args['title'] ?? '';
385 $label = $args['label'] ?? '';
386 $description = $args['description'] ?? '';
387 $value = $args['value'] ?? '0';
388
389 // Return error if ID is not provided.
390 if ( empty( $args['id'] ?? '' ) ) {
391 $field_hint = empty( $title ) ? 'Error' : $title;
392 $no_id_msg = '<p><strong>' . $field_hint . ':</strong> ' . __( 'Field ID is required.', 'subscription' ) . '</p>';
393 return $should_print ? print wp_kses_post( $no_id_msg ) : $no_id_msg;
394 }
395
396 $description_html = '';
397 if ( ! empty( $description ) ) {
398 $description_html = sprintf(
399 '<p class="mb-0! mt-2! ml-0.5! text-[13px]! text-gray-500!">%s</p>',
400 wp_kses_post( $description )
401 );
402 }
403
404 $style_attr = '';
405 if ( isset( $args['style'] ) ) {
406 $style_attr .= ' ' . $args['style'];
407 }
408
409 $other_attrs_html = '';
410 foreach ( ( $args['attributes'] ?? [] ) as $attr_key => $attr_value ) {
411 $other_attrs_html .= sprintf( ' %s="%s" ', esc_attr( $attr_key ), esc_attr( $attr_value ) );
412 }
413
414 $checked_attr = isset( $args['checked'] ) && (bool) $args['checked'] ? 'checked' : '';
415 $disabled_attr = isset( $args['disabled'] ) && (bool) $args['disabled'] ? 'disabled' : '';
416
417 ob_start();
418 ?>
419 <div class="grid grid-cols-6 gap-4">
420 <span class="font-semibold text-sm mt-0.5"><?php echo esc_html( $title ); ?></span>
421
422 <div class="col-span-5">
423 <label for="<?php echo esc_attr( $id ); ?>">
424 <input
425 id="<?php echo esc_attr( $id ); ?>"
426 name="<?php echo esc_attr( $id ); ?>"
427 class="wp-subscription-toggle"
428 style="<?php echo esc_attr( $style_attr ); ?>"
429 type="checkbox"
430 value="<?php echo esc_attr( $value ); ?>"
431 <?php echo esc_attr( $checked_attr ); ?>
432 <?php echo esc_attr( $disabled_attr ); ?>
433 <?php
434 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
435 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
436 echo $other_attrs_html;
437 ?>
438 />
439 <span class="wp-subscription-toggle-ui" aria-hidden="true"></span>
440
441 <span class="ml-2 text-sm align-middle"><?php echo esc_html( $label ); ?></span>
442 </label>
443
444 <br/>
445 <?php echo wp_kses_post( $description_html ); ?>
446 </div>
447 </div>
448 <?php
449 $html_content = ob_get_clean();
450
451 // Output not escaped intentionally. Breaks the HTML structure when escaped.
452 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
453 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
454 return $should_print ? print( $html_content ) : $html_content;
455 }
456
457 /**
458 * Render Select field.
459 *
460 * - Args:
461 * - id (string) - Field ID.
462 * - title (string) - Field title.
463 * - description (string) - Field description (optional).
464 * - options (array) - Field options [value => label].
465 * - selected (string) - Selected option value.
466 * - disabled (string|array) - Disabled option value(s).
467 *
468 * @param array $args Field arguments.
469 * @param bool $should_print Whether to print the field or return as HTML string.
470 */
471 public static function render_select_field( $args = [], $should_print = true ) {
472 $title = $args['title'] ?? '';
473 $description = $args['description'] ?? '';
474
475 // Return error if ID is not provided.
476 if ( empty( $args['id'] ?? '' ) ) {
477 $field_hint = empty( $title ) ? 'Error' : $title;
478 $no_id_msg = '<p><strong>' . $field_hint . ':</strong> ' . __( 'Field ID is required.', 'subscription' ) . '</p>';
479 return $should_print ? print wp_kses_post( $no_id_msg ) : $no_id_msg;
480 }
481
482 // Select HTML.
483 $select_el_html = self::select_element( $args );
484
485 ob_start();
486 ?>
487 <div class="grid grid-cols-6 gap-4">
488 <span class="font-semibold text-sm mt-0.5"><?php echo esc_html( $title ); ?></span>
489
490 <div class="col-span-5">
491 <?php
492 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
493 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
494 echo $select_el_html;
495 ?>
496 <br/>
497 <?php if ( ! empty( $description ) ) : ?>
498 <p class="mb-0! mt-2! ml-0.5! text-[13px]! text-gray-500!">
499 <?php echo wp_kses_post( $description ); ?>
500 </p>
501 <?php endif; ?>
502 </div>
503 </div>
504 <?php
505 $html_content = ob_get_clean();
506
507 // Output not escaped intentionally. Breaks the HTML structure when escaped.
508 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
509 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
510 return $should_print ? print( $html_content ) : $html_content;
511 }
512
513 /**
514 * Render Multiselect field.
515 * Just a wrapper over 'render_select_field' with multiple attribute.
516 *
517 * @param array $args Field arguments.
518 * @param bool $should_print Whether to print the field or return as HTML string.
519 */
520 public static function render_multiselect_field( $args = [], $should_print = true ) {
521 $default_multiselect_args = [
522 'attributes' => [
523 'multiple' => 'multiple',
524 ],
525 'enhanced' => true,
526 ];
527
528 $args = wp_parse_args( $args, $default_multiselect_args );
529
530 return self::render_select_field( $args, $should_print );
531 }
532
533 /**
534 * Render Joined field with multiple elements.
535 *
536 * - Args:
537 * - title (string) - Field title.
538 * - description (string) - Field description (optional).
539 * - vertical (bool) - Whether to show items vertically or not.
540 * - elements ([...string]) - Array of HTML elements to join.
541 *
542 * @param array $args Field arguments.
543 * @param bool $should_print Whether to print the field or return as HTML string.
544 */
545 public static function render_joined_field( $args = [], $should_print = true ) {
546 $title = $args['title'] ?? '';
547 $description = $args['description'] ?? '';
548
549 $vertical_class = ( $args['vertical'] ?? false ) ? 'join-vertical' : '';
550
551 ob_start();
552 ?>
553 <div class="grid grid-cols-6 gap-4">
554 <span class="font-semibold text-sm mt-0.5"><?php echo esc_html( $title ); ?></span>
555
556 <div class="col-span-5">
557 <div class="join <?php echo esc_attr( $vertical_class ); ?>">
558 <?php
559 foreach ( ( $args['elements'] ?? [] ) as $element_html ) {
560 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
561 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
562 echo $element_html;
563 }
564 ?>
565 </div>
566 <br/>
567 <?php if ( ! empty( $description ) ) : ?>
568 <p class="mb-0! mt-2! ml-0.5! text-[13px]! text-gray-500!">
569 <?php echo wp_kses_post( $description ); ?>
570 </p>
571 <?php endif; ?>
572 </div>
573 </div>
574 <?php
575 $html_content = ob_get_clean();
576
577 // Output not escaped intentionally. Breaks the HTML structure when escaped.
578 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
579 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
580 return $should_print ? print( $html_content ) : $html_content;
581 }
582 }
583