PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.10.3
Subscriptions for WooCommerce with Stripe Recurring Payments v1.10.3
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.10.3, at includes/Admin/SettingsHelper.php

578 lines 18.4 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 * Pro badge markup, shown beside settings that require WPSubscription Pro.
145 *
146 * @return string Pre-escaped badge HTML.
147 */
148 public static function pro_badge_html() {
149 return '<span class="subscrpt-pro-badge" title="' . esc_attr__( 'WPSubscription Pro required', 'subscription' ) . '">' . esc_html__( 'Pro', 'subscription' ) . '</span>';
150 }
151
152
153 /**
154 * Text Element HTML.
155 *
156 * @param array $args Same as 'render_text_field'.
157 * @param bool $join_item Whether to return element for 'join' container or not.
158 */
159 public static function inp_element( $args = [], $join_item = false ) {
160 $id = $args['id'];
161 $value = $args['value'] ?? '';
162 $placeholder = $args['placeholder'] ?? '';
163 $type = $args['type'] ?? 'text';
164
165 $disabled_attr = isset( $args['disabled'] ) && $args['disabled'] ? 'disabled' : '';
166
167 $style_attr = '';
168 if ( isset( $args['style'] ) ) {
169 $style_attr = $args['style'];
170 }
171
172 $other_attrs_html = '';
173 foreach ( ( $args['attributes'] ?? [] ) as $attr_key => $attr_value ) {
174 $other_attrs_html .= sprintf( ' %s="%s" ', esc_attr( $attr_key ), esc_attr( $attr_value ) );
175 }
176
177 ob_start();
178 ?>
179 <input
180 id="<?php echo esc_attr( $id ); ?>"
181 name="<?php echo esc_attr( $id ); ?>"
182 class="wpsubs-input"
183 <?php
184 if ( $style_attr ) :
185 ?>
186 style="<?php echo esc_attr( $style_attr ); ?>"<?php endif; ?>
187 type="<?php echo esc_attr( $type ); ?>"
188 placeholder="<?php echo esc_attr( $placeholder ); ?>"
189 value="<?php echo esc_attr( $value ); ?>"
190 <?php echo esc_attr( $disabled_attr ); ?>
191 <?php echo wp_kses_post( $other_attrs_html ); ?>
192 />
193 <?php
194 return ob_get_clean();
195 }
196
197 /**
198 * Select Element HTML.
199 *
200 * @param array $args Same as 'render_select_field'.
201 * @param bool $join_item Whether to return element for 'join' container or not.
202 */
203 public static function select_element( $args = [], $join_item = false ) {
204 $id = $args['id'];
205
206 // Enhanced / multiselect → wpsubs-tag-select (pill input with filter).
207 if ( isset( $args['enhanced'] ) && $args['enhanced'] ) {
208 $multiple = isset( $args['attributes']['multiple'] ) && $args['attributes']['multiple'];
209 $adv_options = array();
210 foreach ( ( $args['options'] ?? [] ) as $opt_value => $opt_label ) {
211 $adv_options[] = array(
212 'value' => (string) $opt_value,
213 'label' => $opt_label,
214 );
215 }
216
217 ob_start();
218 wpsubs_render_tag_select(
219 array(
220 'name' => $id,
221 'value' => $args['selected'] ?? ( $multiple ? array() : '' ),
222 'options' => $adv_options,
223 'multiple' => $multiple,
224 )
225 );
226 return ob_get_clean();
227 }
228
229 // Regular select → wpsubs-adv-select (button-based custom dropdown).
230 $selected = (string) ( $args['selected'] ?? '' );
231 $adv_options = array();
232 foreach ( ( $args['options'] ?? [] ) as $opt_value => $opt_label ) {
233 $adv_options[] = array(
234 'value' => (string) $opt_value,
235 'label' => $opt_label,
236 'disabled' => isset( $args['disabled'] ) && ( is_array( $args['disabled'] )
237 ? in_array( $opt_value, $args['disabled'], true )
238 : $args['disabled'] === $opt_value ),
239 );
240 }
241
242 ob_start();
243 wpsubs_render_adv_select(
244 array(
245 'name' => $id,
246 'value' => $selected,
247 'options' => $adv_options,
248 'align' => 'left',
249 )
250 );
251 return ob_get_clean();
252 }
253
254 /**
255 * Render Field Heading.
256 *
257 * - Args:
258 * - title (string) - Field title.
259 * - description (string) - Field description (optional).
260 *
261 * @param array $args Field arguments.
262 * @param bool $should_print Whether to print the field or return as HTML string.
263 */
264 public static function render_heading( $args = [], $should_print = true ) {
265 $title = $args['title'] ?? '';
266 $description = $args['description'] ?? '';
267
268 ob_start();
269 ?>
270 <div class="wpsubs-settings-heading">
271 <h3 class="wpsubs-settings-heading__title"><?php echo esc_html( $title ); ?></h3>
272 <?php if ( ! empty( $description ) ) : ?>
273 <p class="wpsubs-settings-heading__desc"><?php echo wp_kses_post( $description ); ?></p>
274 <?php endif; ?>
275 </div>
276 <?php
277 $html_content = ob_get_clean();
278
279 // Output not escaped intentionally. Breaks the HTML structure when escaped.
280 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
281 return $should_print ? print( $html_content ) : $html_content;
282 }
283
284 /**
285 * Render Text field.
286 *
287 * - Args:
288 * - id (string) - Field ID.
289 * - title (string) - Field title.
290 * - description (string) - Field description (optional).
291 * - value (string) - Default value.
292 * - placeholder (string) - Default placeholder.
293 * - disabled (bool) - Disabled status.
294 * - type (string) - Input type [text, email, number, date, time, etc.].
295 *
296 * @param array $args Field arguments.
297 * @param bool $should_print Whether to print the field or return as HTML string.
298 */
299 public static function render_input_field( $args = [], $should_print = true ) {
300 $title = $args['title'] ?? '';
301 $description = $args['description'] ?? '';
302
303 // Return error if ID is not provided.
304 if ( empty( $args['id'] ?? '' ) ) {
305 $field_hint = empty( $title ) ? 'Error' : $title;
306 $no_id_msg = '<p><strong>' . $field_hint . ':</strong> ' . __( 'Field ID is required.', 'subscription' ) . '</p>';
307 return $should_print ? print wp_kses_post( $no_id_msg ) : $no_id_msg;
308 }
309
310 // Input HTML.
311 $text_el_html = self::inp_element( $args );
312
313 ob_start();
314 ?>
315 <div class="wpsubs-settings-field<?php echo ! empty( $args['pro_locked'] ) ? ' wpsubs-settings-field--locked' : ''; ?>">
316 <div class="wpsubs-settings-field__label">
317 <?php
318 if ( ! empty( $args['pro_locked'] ) ) {
319 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-escaped badge markup.
320 echo self::pro_badge_html();
321 }
322 ?>
323 <?php echo esc_html( $title ); ?>
324 </div>
325 <div class="wpsubs-settings-field__control">
326 <?php
327 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
328 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
329 echo $text_el_html;
330 ?>
331 <?php if ( ! empty( $description ) ) : ?>
332 <p class="wpsubs-settings-field__hint"><?php echo wp_kses_post( $description ); ?></p>
333 <?php endif; ?>
334 </div>
335 </div>
336 <?php
337 $html_content = ob_get_clean();
338
339 // Output not escaped intentionally. Breaks the HTML structure when escaped.
340 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
341 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
342 return $should_print ? print( $html_content ) : $html_content;
343 }
344
345 /**
346 * Render Switch field.
347 *
348 * - Args:
349 * - id (string) - Field ID.
350 * - title (string) - Field title.
351 * - label (string) - Checkbox label.
352 * - description (string) - Field description (optional).
353 * - value (string) - Checked value.
354 * - checked (bool) - Checked status.
355 * - disabled (bool) - Disabled status.
356 *
357 * @param array $args Field arguments.
358 * @param bool $should_print Whether to print the field or return as HTML string.
359 */
360 public static function render_switch_field( $args = [], $should_print = true ) {
361 $id = $args['id'];
362 $title = $args['title'] ?? '';
363 $label = $args['label'] ?? '';
364 $description = $args['description'] ?? '';
365 $value = $args['value'] ?? '0';
366
367 // Return error if ID is not provided.
368 if ( empty( $args['id'] ?? '' ) ) {
369 $field_hint = empty( $title ) ? 'Error' : $title;
370 $no_id_msg = '<p><strong>' . $field_hint . ':</strong> ' . __( 'Field ID is required.', 'subscription' ) . '</p>';
371 return $should_print ? print wp_kses_post( $no_id_msg ) : $no_id_msg;
372 }
373
374 $description_html = '';
375 if ( ! empty( $description ) ) {
376 $description_html = sprintf(
377 '<p class="wpsubs-settings-field__hint">%s</p>',
378 wp_kses_post( $description )
379 );
380 }
381
382 $style_attr = '';
383 if ( isset( $args['style'] ) ) {
384 $style_attr .= ' ' . $args['style'];
385 }
386
387 $other_attrs_html = '';
388 foreach ( ( $args['attributes'] ?? [] ) as $attr_key => $attr_value ) {
389 $other_attrs_html .= sprintf( ' %s="%s" ', esc_attr( $attr_key ), esc_attr( $attr_value ) );
390 }
391
392 $checked_attr = isset( $args['checked'] ) && (bool) $args['checked'] ? 'checked' : '';
393 $disabled_attr = isset( $args['disabled'] ) && (bool) $args['disabled'] ? 'disabled' : '';
394
395 ob_start();
396 ?>
397 <div class="wpsubs-settings-field<?php echo ! empty( $args['pro_locked'] ) ? ' wpsubs-settings-field--locked' : ''; ?>">
398 <div class="wpsubs-settings-field__label">
399 <?php
400 if ( ! empty( $args['pro_locked'] ) ) {
401 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-escaped badge markup.
402 echo self::pro_badge_html();
403 }
404 ?>
405 <?php echo esc_html( $title ); ?>
406 </div>
407 <div class="wpsubs-settings-field__control">
408 <label class="wpsubs-settings-toggle-label" for="<?php echo esc_attr( $id ); ?>">
409 <input
410 id="<?php echo esc_attr( $id ); ?>"
411 name="<?php echo esc_attr( $id ); ?>"
412 class="wpsubs-toggle"
413 type="checkbox"
414 value="<?php echo esc_attr( $value ); ?>"
415 <?php echo esc_attr( $checked_attr ); ?>
416 <?php echo esc_attr( $disabled_attr ); ?>
417 <?php
418 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
419 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
420 echo $other_attrs_html;
421 ?>
422 />
423 <span class="wpsubs-toggle-ui" aria-hidden="true"></span>
424 <?php if ( ! empty( $label ) ) : ?>
425 <span class="wpsubs-settings-toggle-label__text"><?php echo esc_html( $label ); ?></span>
426 <?php endif; ?>
427 </label>
428 <?php echo wp_kses_post( $description_html ); ?>
429 </div>
430 </div>
431 <?php
432 $html_content = ob_get_clean();
433
434 // Output not escaped intentionally. Breaks the HTML structure when escaped.
435 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
436 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
437 return $should_print ? print( $html_content ) : $html_content;
438 }
439
440 /**
441 * Render Select field.
442 *
443 * - Args:
444 * - id (string) - Field ID.
445 * - title (string) - Field title.
446 * - description (string) - Field description (optional).
447 * - options (array) - Field options [value => label].
448 * - selected (string) - Selected option value.
449 * - disabled (string|array) - Disabled option value(s).
450 *
451 * @param array $args Field arguments.
452 * @param bool $should_print Whether to print the field or return as HTML string.
453 */
454 public static function render_select_field( $args = [], $should_print = true ) {
455 $title = $args['title'] ?? '';
456 $description = $args['description'] ?? '';
457
458 // Return error if ID is not provided.
459 if ( empty( $args['id'] ?? '' ) ) {
460 $field_hint = empty( $title ) ? 'Error' : $title;
461 $no_id_msg = '<p><strong>' . $field_hint . ':</strong> ' . __( 'Field ID is required.', 'subscription' ) . '</p>';
462 return $should_print ? print wp_kses_post( $no_id_msg ) : $no_id_msg;
463 }
464
465 // Select HTML.
466 $select_el_html = self::select_element( $args );
467
468 ob_start();
469 ?>
470 <div class="wpsubs-settings-field<?php echo ! empty( $args['pro_locked'] ) ? ' wpsubs-settings-field--locked' : ''; ?>">
471 <div class="wpsubs-settings-field__label">
472 <?php
473 if ( ! empty( $args['pro_locked'] ) ) {
474 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-escaped badge markup.
475 echo self::pro_badge_html();
476 }
477 ?>
478 <?php echo esc_html( $title ); ?>
479 </div>
480 <div class="wpsubs-settings-field__control">
481 <?php
482 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
483 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
484 echo $select_el_html;
485 ?>
486 <?php if ( ! empty( $description ) ) : ?>
487 <p class="wpsubs-settings-field__hint"><?php echo wp_kses_post( $description ); ?></p>
488 <?php endif; ?>
489 </div>
490 </div>
491 <?php
492 $html_content = ob_get_clean();
493
494 // Output not escaped intentionally. Breaks the HTML structure when escaped.
495 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
496 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
497 return $should_print ? print( $html_content ) : $html_content;
498 }
499
500 /**
501 * Render Multiselect field.
502 * Just a wrapper over 'render_select_field' with multiple attribute.
503 *
504 * @param array $args Field arguments.
505 * @param bool $should_print Whether to print the field or return as HTML string.
506 */
507 public static function render_multiselect_field( $args = [], $should_print = true ) {
508 $default_multiselect_args = [
509 'attributes' => [
510 'multiple' => 'multiple',
511 ],
512 'enhanced' => true,
513 ];
514
515 $args = wp_parse_args( $args, $default_multiselect_args );
516
517 return self::render_select_field( $args, $should_print );
518 }
519
520 /**
521 * Render Joined field with multiple elements.
522 *
523 * - Args:
524 * - title (string) - Field title.
525 * - description (string) - Field description (optional).
526 * - vertical (bool) - Whether to show items vertically or not.
527 * - elements ([...string]) - Array of HTML elements to join.
528 *
529 * @param array $args Field arguments.
530 * @param bool $should_print Whether to print the field or return as HTML string.
531 */
532 public static function render_joined_field( $args = [], $should_print = true ) {
533 $title = $args['title'] ?? '';
534 $description = $args['description'] ?? '';
535
536 $vertical_style = ( $args['vertical'] ?? false ) ? 'flex-direction:column;' : '';
537
538 ob_start();
539 ?>
540 <div class="wpsubs-settings-field<?php echo ! empty( $args['pro_locked'] ) ? ' wpsubs-settings-field--locked' : ''; ?>">
541 <div class="wpsubs-settings-field__label">
542 <?php
543 if ( ! empty( $args['pro_locked'] ) ) {
544 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-escaped badge markup.
545 echo self::pro_badge_html();
546 }
547 ?>
548 <?php echo esc_html( $title ); ?>
549 </div>
550 <div class="wpsubs-settings-field__control">
551 <div class="wpsubs-input-group"
552 <?php
553 if ( $vertical_style ) :
554 ?>
555 style="<?php echo esc_attr( $vertical_style ); ?>"<?php endif; ?>>
556 <?php
557 foreach ( ( $args['elements'] ?? [] ) as $element_html ) {
558 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
559 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
560 echo $element_html;
561 }
562 ?>
563 </div>
564 <?php if ( ! empty( $description ) ) : ?>
565 <p class="wpsubs-settings-field__hint"><?php echo wp_kses_post( $description ); ?></p>
566 <?php endif; ?>
567 </div>
568 </div>
569 <?php
570 $html_content = ob_get_clean();
571
572 // Output not escaped intentionally. Breaks the HTML structure when escaped.
573 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
574 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
575 return $should_print ? print( $html_content ) : $html_content;
576 }
577 }
578