PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.10.5
Subscriptions for WooCommerce with Stripe Recurring Payments v1.10.5
2.4.0 2.3.0 2.2.0 2.1.0 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 All 65 releases
subscription / includes / Admin / SettingsHelper.php

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

579 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 'class' => $args['class'] ?? '',
250 )
251 );
252 return ob_get_clean();
253 }
254
255 /**
256 * Render Field Heading.
257 *
258 * - Args:
259 * - title (string) - Field title.
260 * - description (string) - Field description (optional).
261 *
262 * @param array $args Field arguments.
263 * @param bool $should_print Whether to print the field or return as HTML string.
264 */
265 public static function render_heading( $args = [], $should_print = true ) {
266 $title = $args['title'] ?? '';
267 $description = $args['description'] ?? '';
268
269 ob_start();
270 ?>
271 <div class="wpsubs-settings-heading">
272 <h3 class="wpsubs-settings-heading__title"><?php echo esc_html( $title ); ?></h3>
273 <?php if ( ! empty( $description ) ) : ?>
274 <p class="wpsubs-settings-heading__desc"><?php echo wp_kses_post( $description ); ?></p>
275 <?php endif; ?>
276 </div>
277 <?php
278 $html_content = ob_get_clean();
279
280 // Output not escaped intentionally. Breaks the HTML structure when escaped.
281 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
282 return $should_print ? print( $html_content ) : $html_content;
283 }
284
285 /**
286 * Render Text field.
287 *
288 * - Args:
289 * - id (string) - Field ID.
290 * - title (string) - Field title.
291 * - description (string) - Field description (optional).
292 * - value (string) - Default value.
293 * - placeholder (string) - Default placeholder.
294 * - disabled (bool) - Disabled status.
295 * - type (string) - Input type [text, email, number, date, time, etc.].
296 *
297 * @param array $args Field arguments.
298 * @param bool $should_print Whether to print the field or return as HTML string.
299 */
300 public static function render_input_field( $args = [], $should_print = true ) {
301 $title = $args['title'] ?? '';
302 $description = $args['description'] ?? '';
303
304 // Return error if ID is not provided.
305 if ( empty( $args['id'] ?? '' ) ) {
306 $field_hint = empty( $title ) ? 'Error' : $title;
307 $no_id_msg = '<p><strong>' . $field_hint . ':</strong> ' . __( 'Field ID is required.', 'subscription' ) . '</p>';
308 return $should_print ? print wp_kses_post( $no_id_msg ) : $no_id_msg;
309 }
310
311 // Input HTML.
312 $text_el_html = self::inp_element( $args );
313
314 ob_start();
315 ?>
316 <div class="wpsubs-settings-field<?php echo ! empty( $args['pro_locked'] ) ? ' wpsubs-settings-field--locked' : ''; ?>">
317 <div class="wpsubs-settings-field__label">
318 <?php
319 if ( ! empty( $args['pro_locked'] ) ) {
320 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-escaped badge markup.
321 echo self::pro_badge_html();
322 }
323 ?>
324 <?php echo esc_html( $title ); ?>
325 </div>
326 <div class="wpsubs-settings-field__control">
327 <?php
328 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
329 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
330 echo $text_el_html;
331 ?>
332 <?php if ( ! empty( $description ) ) : ?>
333 <p class="wpsubs-settings-field__hint"><?php echo wp_kses_post( $description ); ?></p>
334 <?php endif; ?>
335 </div>
336 </div>
337 <?php
338 $html_content = ob_get_clean();
339
340 // Output not escaped intentionally. Breaks the HTML structure when escaped.
341 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
342 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
343 return $should_print ? print( $html_content ) : $html_content;
344 }
345
346 /**
347 * Render Switch field.
348 *
349 * - Args:
350 * - id (string) - Field ID.
351 * - title (string) - Field title.
352 * - label (string) - Checkbox label.
353 * - description (string) - Field description (optional).
354 * - value (string) - Checked value.
355 * - checked (bool) - Checked status.
356 * - disabled (bool) - Disabled status.
357 *
358 * @param array $args Field arguments.
359 * @param bool $should_print Whether to print the field or return as HTML string.
360 */
361 public static function render_switch_field( $args = [], $should_print = true ) {
362 $id = $args['id'];
363 $title = $args['title'] ?? '';
364 $label = $args['label'] ?? '';
365 $description = $args['description'] ?? '';
366 $value = $args['value'] ?? '0';
367
368 // Return error if ID is not provided.
369 if ( empty( $args['id'] ?? '' ) ) {
370 $field_hint = empty( $title ) ? 'Error' : $title;
371 $no_id_msg = '<p><strong>' . $field_hint . ':</strong> ' . __( 'Field ID is required.', 'subscription' ) . '</p>';
372 return $should_print ? print wp_kses_post( $no_id_msg ) : $no_id_msg;
373 }
374
375 $description_html = '';
376 if ( ! empty( $description ) ) {
377 $description_html = sprintf(
378 '<p class="wpsubs-settings-field__hint">%s</p>',
379 wp_kses_post( $description )
380 );
381 }
382
383 $style_attr = '';
384 if ( isset( $args['style'] ) ) {
385 $style_attr .= ' ' . $args['style'];
386 }
387
388 $other_attrs_html = '';
389 foreach ( ( $args['attributes'] ?? [] ) as $attr_key => $attr_value ) {
390 $other_attrs_html .= sprintf( ' %s="%s" ', esc_attr( $attr_key ), esc_attr( $attr_value ) );
391 }
392
393 $checked_attr = isset( $args['checked'] ) && (bool) $args['checked'] ? 'checked' : '';
394 $disabled_attr = isset( $args['disabled'] ) && (bool) $args['disabled'] ? 'disabled' : '';
395
396 ob_start();
397 ?>
398 <div class="wpsubs-settings-field<?php echo ! empty( $args['pro_locked'] ) ? ' wpsubs-settings-field--locked' : ''; ?>">
399 <div class="wpsubs-settings-field__label">
400 <?php
401 if ( ! empty( $args['pro_locked'] ) ) {
402 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-escaped badge markup.
403 echo self::pro_badge_html();
404 }
405 ?>
406 <?php echo esc_html( $title ); ?>
407 </div>
408 <div class="wpsubs-settings-field__control">
409 <label class="wpsubs-settings-toggle-label" for="<?php echo esc_attr( $id ); ?>">
410 <input
411 id="<?php echo esc_attr( $id ); ?>"
412 name="<?php echo esc_attr( $id ); ?>"
413 class="wpsubs-toggle"
414 type="checkbox"
415 value="<?php echo esc_attr( $value ); ?>"
416 <?php echo esc_attr( $checked_attr ); ?>
417 <?php echo esc_attr( $disabled_attr ); ?>
418 <?php
419 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
420 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
421 echo $other_attrs_html;
422 ?>
423 />
424 <span class="wpsubs-toggle-ui" aria-hidden="true"></span>
425 <?php if ( ! empty( $label ) ) : ?>
426 <span class="wpsubs-settings-toggle-label__text"><?php echo esc_html( $label ); ?></span>
427 <?php endif; ?>
428 </label>
429 <?php echo wp_kses_post( $description_html ); ?>
430 </div>
431 </div>
432 <?php
433 $html_content = ob_get_clean();
434
435 // Output not escaped intentionally. Breaks the HTML structure when escaped.
436 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
437 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
438 return $should_print ? print( $html_content ) : $html_content;
439 }
440
441 /**
442 * Render Select field.
443 *
444 * - Args:
445 * - id (string) - Field ID.
446 * - title (string) - Field title.
447 * - description (string) - Field description (optional).
448 * - options (array) - Field options [value => label].
449 * - selected (string) - Selected option value.
450 * - disabled (string|array) - Disabled option value(s).
451 *
452 * @param array $args Field arguments.
453 * @param bool $should_print Whether to print the field or return as HTML string.
454 */
455 public static function render_select_field( $args = [], $should_print = true ) {
456 $title = $args['title'] ?? '';
457 $description = $args['description'] ?? '';
458
459 // Return error if ID is not provided.
460 if ( empty( $args['id'] ?? '' ) ) {
461 $field_hint = empty( $title ) ? 'Error' : $title;
462 $no_id_msg = '<p><strong>' . $field_hint . ':</strong> ' . __( 'Field ID is required.', 'subscription' ) . '</p>';
463 return $should_print ? print wp_kses_post( $no_id_msg ) : $no_id_msg;
464 }
465
466 // Select HTML.
467 $select_el_html = self::select_element( $args );
468
469 ob_start();
470 ?>
471 <div class="wpsubs-settings-field<?php echo ! empty( $args['pro_locked'] ) ? ' wpsubs-settings-field--locked' : ''; ?>">
472 <div class="wpsubs-settings-field__label">
473 <?php
474 if ( ! empty( $args['pro_locked'] ) ) {
475 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-escaped badge markup.
476 echo self::pro_badge_html();
477 }
478 ?>
479 <?php echo esc_html( $title ); ?>
480 </div>
481 <div class="wpsubs-settings-field__control">
482 <?php
483 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
484 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
485 echo $select_el_html;
486 ?>
487 <?php if ( ! empty( $description ) ) : ?>
488 <p class="wpsubs-settings-field__hint"><?php echo wp_kses_post( $description ); ?></p>
489 <?php endif; ?>
490 </div>
491 </div>
492 <?php
493 $html_content = ob_get_clean();
494
495 // Output not escaped intentionally. Breaks the HTML structure when escaped.
496 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
497 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
498 return $should_print ? print( $html_content ) : $html_content;
499 }
500
501 /**
502 * Render Multiselect field.
503 * Just a wrapper over 'render_select_field' with multiple attribute.
504 *
505 * @param array $args Field arguments.
506 * @param bool $should_print Whether to print the field or return as HTML string.
507 */
508 public static function render_multiselect_field( $args = [], $should_print = true ) {
509 $default_multiselect_args = [
510 'attributes' => [
511 'multiple' => 'multiple',
512 ],
513 'enhanced' => true,
514 ];
515
516 $args = wp_parse_args( $args, $default_multiselect_args );
517
518 return self::render_select_field( $args, $should_print );
519 }
520
521 /**
522 * Render Joined field with multiple elements.
523 *
524 * - Args:
525 * - title (string) - Field title.
526 * - description (string) - Field description (optional).
527 * - vertical (bool) - Whether to show items vertically or not.
528 * - elements ([...string]) - Array of HTML elements to join.
529 *
530 * @param array $args Field arguments.
531 * @param bool $should_print Whether to print the field or return as HTML string.
532 */
533 public static function render_joined_field( $args = [], $should_print = true ) {
534 $title = $args['title'] ?? '';
535 $description = $args['description'] ?? '';
536
537 $vertical_style = ( $args['vertical'] ?? false ) ? 'flex-direction:column;' : '';
538
539 ob_start();
540 ?>
541 <div class="wpsubs-settings-field<?php echo ! empty( $args['pro_locked'] ) ? ' wpsubs-settings-field--locked' : ''; ?>">
542 <div class="wpsubs-settings-field__label">
543 <?php
544 if ( ! empty( $args['pro_locked'] ) ) {
545 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-escaped badge markup.
546 echo self::pro_badge_html();
547 }
548 ?>
549 <?php echo esc_html( $title ); ?>
550 </div>
551 <div class="wpsubs-settings-field__control">
552 <div class="wpsubs-input-group"
553 <?php
554 if ( $vertical_style ) :
555 ?>
556 style="<?php echo esc_attr( $vertical_style ); ?>"<?php endif; ?>>
557 <?php
558 foreach ( ( $args['elements'] ?? [] ) as $element_html ) {
559 // Output intentionally not escaped as element is already escaped during generation & re-escaping breaks the HTML structure.
560 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
561 echo $element_html;
562 }
563 ?>
564 </div>
565 <?php if ( ! empty( $description ) ) : ?>
566 <p class="wpsubs-settings-field__hint"><?php echo wp_kses_post( $description ); ?></p>
567 <?php endif; ?>
568 </div>
569 </div>
570 <?php
571 $html_content = ob_get_clean();
572
573 // Output not escaped intentionally. Breaks the HTML structure when escaped.
574 // All form elements inside $html_content are pre-escaped during generation (esc_attr, esc_html).
575 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
576 return $should_print ? print( $html_content ) : $html_content;
577 }
578 }
579