PluginProbe ʕ •ᴥ•ʔ
SureForms – Drag & Drop Contact Form & Form Builder, Payment Form, Survey, Quiz & Calculator / 2.12.1
SureForms – Drag & Drop Contact Form & Form Builder, Payment Form, Survey, Quiz & Calculator v2.12.1
2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8 0.0.9 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.1.0 1.1.1 1.1.2 1.10.0 1.10.1 1.11.0 1.12.0 1.12.1 1.12.2 1.12.3 1.13.0 1.13.1 1.13.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.8.0 1.9.0 1.9.1 2.0.0 2.0.1 2.0.2 2.1.0 2.1.1 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.5.0 2.5.2 2.6.0
sureforms / inc / fields / multichoice-markup.php
sureforms / inc / fields Last commit date
address-markup.php 1 month ago base.php 2 months ago checkbox-markup.php 1 year ago countries.json 1 year ago dropdown-markup.php 1 month ago email-markup.php 2 months ago gdpr-markup.php 4 months ago inlinebutton-markup.php 3 weeks ago input-markup.php 4 months ago multichoice-markup.php 1 month ago number-markup.php 4 months ago payment-markup.php 2 months ago phone-markup.php 1 month ago textarea-markup.php 1 month ago url-markup.php 4 months ago
multichoice-markup.php
317 lines
1 <?php
2 /**
3 * Sureforms Multichoice Markup Class file.
4 *
5 * @package sureforms.
6 * @since 0.0.1
7 */
8
9 namespace SRFM\Inc\Fields;
10
11 use Spec_Gb_Helper;
12 use SRFM\Inc\Helper;
13 use SRFM\Inc\Smart_Tags;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * SureForms Multichoice Markup Class.
21 *
22 * @since 0.0.1
23 */
24 class Multichoice_Markup extends Base {
25 /**
26 * Flag indicating if only a single selection is allowed.
27 *
28 * @var bool
29 * @since 0.0.2
30 */
31 protected $single_selection;
32
33 /**
34 * Width of the choice input field.
35 *
36 * @var string
37 * @since 0.0.2
38 */
39 protected $choice_width;
40
41 /**
42 * HTML attribute string for the choice width.
43 *
44 * @var string
45 * @since 0.0.2
46 */
47 protected $choice_width_attr;
48
49 /**
50 * HTML attribute string for the input type (radio or checkbox).
51 *
52 * @var string
53 * @since 0.0.2
54 */
55 protected $type_attr;
56
57 /**
58 * SVG type for the input field.
59 *
60 * @var string
61 * @since 0.0.7
62 */
63 protected $svg_type;
64
65 /**
66 * HTML attribute string for the name attribute of the input field.
67 *
68 * @var string
69 * @since 0.0.2
70 */
71 protected $name_attr;
72
73 /**
74 * Flag indicating if the layout is vertical.
75 *
76 * @var bool
77 * @since 0.0.7
78 */
79 protected $vertical_layout;
80
81 /**
82 * Contains value if the option contains icon / image
83 *
84 * @var mixed
85 * @since 0.0.7
86 */
87 protected $option_type;
88
89 /**
90 * Flag indicating if the value should be shown.
91 *
92 * @var bool
93 * @since 1.5.0
94 */
95 protected $show_values;
96
97 /**
98 * Array of preselected option indices.
99 *
100 * @var array
101 * @since 2.3.0
102 */
103 protected $preselected_options;
104
105 /**
106 * Initialize the properties based on block attributes.
107 *
108 * @param array<mixed> $attributes Block attributes.
109 * @since 0.0.2
110 */
111 public function __construct( $attributes ) {
112 $this->slug = 'multi-choice';
113 $this->set_properties( $attributes );
114 $this->set_input_label( __( 'Multi Choice', 'sureforms' ) );
115 $this->set_error_msg( $attributes, 'srfm_multi_choice_block_required_text' );
116 $this->single_selection = $attributes['singleSelection'] ?? false;
117 $this->choice_width = $attributes['choiceWidth'] ?? '';
118 $this->vertical_layout = $attributes['verticalLayout'] ?? false;
119 $this->option_type = ! empty( $attributes['optionType'] ) && in_array( $attributes['optionType'], [ 'icon', 'image' ], true ) ? $attributes['optionType'] : 'icon';
120 $this->type_attr = $this->single_selection ? 'radio' : 'checkbox';
121 $this->svg_type = $this->single_selection ? 'circle' : 'square';
122 $this->name_attr = $this->single_selection ? 'name="srfm-input-' . esc_attr( $this->slug ) . '-' . esc_attr( $this->block_id ) . '"' : '';
123 $this->choice_width_attr = $this->choice_width ? 'srfm-choice-width-' . str_replace( '.', '-', $this->choice_width ) : '';
124 $this->show_values = apply_filters( 'srfm_show_options_values', false, $attributes['showValues'] ?? false );
125 $this->preselected_options = $attributes['preselectedOptions'] ?? [];
126 $this->resolve_dynamic_default( $attributes );
127 $this->set_markup_properties();
128 $this->set_aria_described_by();
129 }
130
131 /**
132 * Render the sureforms Multichoice classic styling
133 *
134 * @since 0.0.2
135 * @return string|bool
136 */
137 public function markup() {
138 $check_svg = Helper::fetch_svg( $this->svg_type . '-checked', 'srfm-' . $this->slug . '-icon', 'aria-hidden="true"' );
139 $unchecked_svg = Helper::fetch_svg( $this->svg_type . '-unchecked', 'srfm-' . $this->slug . '-icon-unchecked', 'aria-hidden="true"' );
140
141 $this->class_name = $this->get_field_classes( [ "srfm-{$this->type_attr}-mode" ] );
142
143 $allowed_tags_svg = Helper::$allowed_tags_svg;
144
145 ob_start();
146 ?>
147 <div data-block-id="<?php echo esc_attr( $this->block_id ); ?>" class="<?php echo esc_attr( $this->class_name ); ?>">
148 <fieldset>
149 <input class="srfm-input-<?php echo esc_attr( $this->slug ); ?>-hidden" data-required="<?php echo esc_attr( $this->data_require_attr ); ?>" aria-required="<?php echo esc_attr( $this->data_require_attr ); ?>" <?php echo wp_kses_post( $this->data_attribute_markup() ); ?> name="srfm-input-<?php echo esc_attr( $this->slug ); ?>-<?php echo esc_attr( $this->block_id ); ?><?php echo esc_attr( $this->field_name ); ?>" type="hidden" value=""/>
150 <legend class="srfm-block-legend">
151 <?php echo wp_kses_post( $this->label_markup ); ?>
152 </legend>
153 <?php echo wp_kses_post( $this->help_markup ); ?>
154 <?php if ( is_array( $this->options ) ) { ?>
155 <div class="srfm-block-wrap <?php echo esc_attr( $this->choice_width_attr ); ?> <?php echo $this->vertical_layout ? 'srfm-vertical-layout' : ''; ?>">
156 <?php foreach ( $this->options as $i => $option ) { ?>
157 <div class="srfm-<?php echo esc_attr( $this->slug ); ?>-single">
158 <input type="<?php echo esc_attr( $this->type_attr ); ?>"
159 id="srfm-<?php echo esc_attr( $this->slug ); ?>-<?php echo esc_attr( $this->block_id . '-' . $i ); ?>"
160 class="srfm-input-<?php echo esc_attr( $this->slug ); ?>-single" <?php echo wp_kses_post( $this->name_attr ); ?>
161 <?php echo 0 === $i ? 'aria-describedby="' . ( ! empty( $this->aria_described_by ) ? esc_attr( trim( $this->aria_described_by ) ) : '' ) . '"' : ''; ?>
162 <?php echo 0 === $i ? 'aria-required="' . ( ! empty( $this->data_require_attr ) ? esc_attr( $this->data_require_attr ) : '' ) . '"' : ''; ?>
163 <?php echo $this->show_values && isset( $option['value'] ) ? 'option-value="' . esc_attr( $option['value'] ) . '"' : ''; ?>
164 <?php echo is_array( $this->preselected_options ) && in_array( $i, $this->preselected_options, true ) ? 'checked' : ''; ?>
165 />
166 <div class="srfm-block-content-wrap">
167 <div class="srfm-option-container">
168 <?php if ( 'icon' === $this->option_type && ! empty( $option['icon'] ) ) { ?>
169 <span class="srfm-option-icon" aria-hidden="true">
170 <?php Spec_Gb_Helper::render_svg_html( $option['icon'] ); ?>
171 </span>
172 <?php } elseif ( 'image' === $this->option_type && ! empty( $option['image'] ) ) { ?>
173 <span class="srfm-option-image" aria-hidden="true">
174 <img src="<?php echo esc_url( $option['image'] ); ?>"/>
175 </span>
176 <?php } ?>
177 <!-- This label content is used in conditional logic and for storing values in a hidden input field as comma-separated values.
178 The markup should remain unchanged.
179 If you make any changes here, ensure the corresponding JavaScript value functionality and conditional logic are also updated. -->
180 <label for="srfm-<?php echo esc_attr( $this->slug ); ?>-<?php echo esc_attr( $this->block_id . '-' . $i ); ?>" data-option-text="<?php echo isset( $option['optionTitle'] ) ? esc_attr( $option['optionTitle'] ) : ''; ?>"><?php echo isset( $option['optionTitle'] ) ? esc_html( $option['optionTitle'] ) : ''; ?></label>
181 </div>
182 <div class="srfm-icon-container"><?php echo wp_kses( $check_svg, $allowed_tags_svg ) . wp_kses( $unchecked_svg, $allowed_tags_svg ); ?></div>
183 </div>
184 </div>
185 <?php } ?>
186 </div>
187 <?php } ?>
188 <div class="srfm-error-wrap"><?php echo wp_kses_post( $this->error_msg_markup ); ?></div>
189 </fieldset>
190 </div>
191 <?php
192 $markup = ob_get_clean();
193
194 return apply_filters(
195 'srfm_block_field_markup',
196 $markup,
197 [
198 'slug' => $this->slug,
199 'is_editing' => $this->is_editing,
200 'field_name' => $this->field_name,
201 'attributes' => $this->attributes,
202 ]
203 );
204 }
205
206 /**
207 * Resolve dynamic default value from smart tags and override preselected options.
208 *
209 * Radio (single-selection) mode matches the resolved value as a single
210 * trimmed string, so option titles that legitimately contain delimiter
211 * characters (commas, pipes, etc.) still match.
212 *
213 * Checkbox mode splits the resolved value on the pipe character (`|`) and
214 * matches every segment (for example "{get_input:a}|{get_input:b}" resolving
215 * to "Red|Blue", or a single `{get_input:colors}` with URL
216 * `?colors=Red|Blue`). The same pipe split applies to multi-character
217 * resolved values from `{get_input:...}` and `{get_cookie:...}`, so cookie
218 * payloads that contain pipes are treated as multi-value in checkbox mode.
219 *
220 * When the `Add Numeric Values to Options` (showValues) setting is on,
221 * each segment is also compared against the option's `value`.
222 *
223 * @param array<mixed> $attributes Block attributes.
224 * @since 2.8.1
225 * @since 2.10.0 Added checkbox (pipe-delimited) multi-value matching.
226 * @return void
227 */
228 protected function resolve_dynamic_default( $attributes ) {
229 // Coalesce first so blocks saved before this attribute existed (pre-2.10.0)
230 // don't trigger an "Undefined array key" warning on every frontend render.
231 $raw_default = $attributes['dynamicDefaultValue'] ?? '';
232 $dynamic_default = is_string( $raw_default ) ? $raw_default : '';
233 if ( empty( $dynamic_default ) ) {
234 return;
235 }
236
237 $smart_tags = new Smart_Tags();
238 $resolved = $smart_tags->process_smart_tags( $dynamic_default );
239
240 if ( empty( $resolved ) || ! is_string( $resolved ) || ! is_array( $this->options ) ) {
241 return;
242 }
243
244 // START: multi-value dynamic default resolution.
245 if ( ! $this->single_selection ) {
246 $segments = array_filter(
247 array_map( 'trim', explode( '|', $resolved ) ),
248 static function ( $segment ) {
249 return '' !== $segment;
250 }
251 );
252 // Cap segments to avoid pathological URLs (e.g. ?colors=|||||...) producing thousands of comparisons.
253 $segments = array_slice( $segments, 0, 50 );
254 } else {
255 $trimmed = trim( $resolved );
256 $segments = '' === $trimmed ? [] : [ $trimmed ];
257 }
258
259 if ( empty( $segments ) ) {
260 return;
261 }
262
263 $match_value_too = ! empty( $attributes['showValues'] );
264 $matching_indices = [];
265
266 foreach ( $segments as $segment ) {
267 foreach ( $this->options as $i => $option ) {
268 if ( ! is_array( $option ) || in_array( $i, $matching_indices, true ) ) {
269 continue;
270 }
271
272 $option_title = isset( $option['optionTitle'] ) && is_scalar( $option['optionTitle'] ) ? (string) $option['optionTitle'] : '';
273 $raw_value = $option['value'] ?? '';
274 $option_value = $match_value_too && is_scalar( $raw_value ) ? (string) $raw_value : '';
275
276 $is_match = ( '' !== $option_title && strcasecmp( $option_title, $segment ) === 0 )
277 || ( $match_value_too && '' !== $option_value && strcasecmp( $option_value, $segment ) === 0 );
278
279 if ( $is_match ) {
280 $matching_indices[] = $i;
281 if ( $this->single_selection ) {
282 break 2;
283 }
284 break;
285 }
286 }
287 }
288
289 if ( ! empty( $matching_indices ) ) {
290 $this->preselected_options = $this->single_selection ? [ $matching_indices[0] ] : $matching_indices;
291 }
292 // END: multi-value dynamic default resolution.
293 }
294
295 /**
296 * Data attribute markup for min and max value
297 *
298 * @since 0.0.13
299 * @return string
300 */
301 protected function data_attribute_markup() {
302 $data_attr = '';
303 if ( $this->single_selection ) {
304 return '';
305 }
306
307 if ( $this->min_selection ) {
308 $data_attr .= 'data-min-selection="' . esc_attr( $this->min_selection ) . '"';
309 }
310 if ( $this->max_selection ) {
311 $data_attr .= 'data-max-selection="' . esc_attr( $this->max_selection ) . '"';
312 }
313
314 return $data_attr;
315 }
316 }
317