PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.33.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.33.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmStylesCardHelper.php

FrmStylesCardHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.33.1, at classes/helpers/FrmStylesCardHelper.php

526 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 /**
7 * @since 6.0
8 */
9 class FrmStylesCardHelper {
10
11 const PAGE_SIZE = 3;
12
13 /**
14 * @var string
15 */
16 private $view_file_path;
17
18 /**
19 * @var stdClass|WP_Post
20 */
21 private $active_style;
22
23 /**
24 * @var WP_Post
25 */
26 private $default_style;
27
28 /**
29 * @var int
30 */
31 private $form_id;
32
33 /**
34 * @var bool
35 */
36 private $enabled;
37
38 /**
39 * @var bool If this is true, a lock will be rendered with the style name when self::echo_style_card is called.
40 */
41 private $locked;
42
43 /**
44 * @var bool If this is true, a "NEW" pill is included beside the style name.
45 */
46 private $is_new_template;
47
48 /**
49 * @param stdClass|WP_Post $active_style
50 * @param WP_Post $default_style
51 * @param int|string $form_id
52 * @param bool $enabled
53 */
54 public function __construct( $active_style, $default_style, $form_id, $enabled ) {
55 $this->view_file_path = FrmAppHelper::plugin_path() . '/classes/views/styles/_style-card.php';
56 $this->active_style = $active_style;
57 $this->default_style = $default_style;
58 $this->form_id = (int) $form_id;
59 $this->enabled = $enabled;
60 $this->locked = false;
61 $this->is_new_template = false;
62 }
63
64 /**
65 * Echo a style card for a specific target Style Post object.
66 *
67 * @since 6.0
68 *
69 * @param stdClass|WP_Post $style
70 * @param bool $hidden Used for pagination.
71 *
72 * @return void
73 */
74 private function echo_style_card( $style, $hidden = false ) {
75 $params = $this->get_params_for_style_card( $style, $hidden );
76 $is_locked = $this->locked;
77 $is_new_template = $this->is_new_template;
78 $is_active_style = $style->ID === $this->active_style->ID;
79
80 include $this->view_file_path;
81 }
82
83 /**
84 * Get params to use in the style card HTML element used in the style list.
85 *
86 * @since 6.0
87 *
88 * @param stdClass|WP_Post $style
89 * @param bool $hidden
90 *
91 * @return array
92 */
93 private function get_params_for_style_card( $style, $hidden = false ) {
94 if ( ! empty( $style->post_content['position'] ) ) {
95 $label_position = $style->post_content['position'];
96 } else {
97 $frm_style = new FrmStyle();
98 $label_position = $frm_style->get_defaults()['position'];
99 }
100
101 $class_name = 'frm_style_' . $style->post_name;
102 $params = array(
103 'class' => 'frm-style-card frm-transition-ease',
104 'style' => self::get_style_param_for_card( $style ),
105 'data-classname' => $class_name,
106 'data-style-id' => $style->ID,
107 'data-edit-url' => esc_url( FrmStylesHelper::get_edit_url( $style, $this->form_id ) ),
108 'data-label-position' => $label_position,
109 );
110
111 $is_active_style = $style->ID === $this->active_style->ID;
112
113 if ( $is_active_style ) {
114 $params['class'] .= ' frm-active-style-card frm-currently-set-style-card';
115 }
116
117 if ( $hidden ) {
118 $params['class'] .= ' frm_hidden';
119 }
120
121 if ( self::has_dark_background( $style ) ) {
122 $params['class'] .= ' frm-dark-style';
123 }
124
125 /**
126 * Filter params so Pro can add additional params, like data-delete-url.
127 *
128 * @since 6.0
129 *
130 * @param array $params
131 * @param array $args {
132 *
133 * @type WP_Post $style
134 * }
135 */
136 return apply_filters( 'frm_style_card_params', $params, compact( 'style' ) );
137 }
138
139 /**
140 * @param stdClass|WP_Post $style
141 *
142 * @return bool
143 */
144 private static function has_dark_background( $style ) {
145 $key = 'fieldset_bg_color';
146
147 if ( empty( $style->post_content[ $key ] ) ) {
148 return false;
149 }
150
151 $color = $style->post_content[ $key ];
152
153 if ( str_starts_with( $color, 'rgba' ) ) {
154 preg_match_all( '/([\\d.]+)/', $color, $matches );
155
156 if ( isset( $matches[1][3] ) && is_numeric( $matches[1][3] ) ) {
157 // Consider a faded out rgba value as light even when the color is dark.
158 $color_opacity = floatval( $matches[1][3] );
159
160 if ( $color_opacity < 0.5 ) {
161 return false;
162 }
163 }
164 }
165
166 $brightness = FrmStylesHelper::get_color_brightness( $color );
167 return $brightness < 155;
168 }
169
170 /**
171 * @since 6.0
172 *
173 * @param array $style {
174 * API style data.
175 *
176 * @type array $settings
177 * @type string $name
178 * @type string $slug
179 * }
180 *
181 * @param bool $hidden
182 *
183 * @return bool True if the template was valid and echoed.
184 */
185 private function echo_card_template( $style, $hidden = false ) {
186 if ( empty( $style['settings'] ) || ! is_array( $style['settings'] ) ) {
187 return false;
188 }
189
190 // Use a better name than my sample form.
191 $style_object = new stdClass();
192 $style_object->ID = 0;
193 $style_object->post_title = $style['name'];
194 $style_object->post_content = $style['settings'];
195
196 // An unlocked template uses a static "frm_style_template" in Pro.
197 // A locked template however uses a slug to match the sandbox CSS.
198 $style_object->post_name = isset( $style['url'] ) ? 'frm_style_template' : $style['slug'];
199
200 $this->locked = empty( $style['url'] );
201
202 if ( $this->locked ) {
203 /**
204 * Set up a locked style card for the upgrade modal.
205 *
206 * @param array $params
207 * @param array $args {
208 *
209 * @type stdClass|WP_Post $style
210 * }
211 *
212 * @param stdClass $style_object
213 * @param array $style
214 */
215 $param_filter = function ( $params, $args ) use ( $style_object, $style ) {
216 if ( $args['style'] !== $style_object ) {
217 return $params;
218 }
219
220 $params['class'] .= ' frm-locked-style';
221 $params['data-upgrade-url'] = FrmAppHelper::admin_upgrade_link(
222 array(
223 'content' => 'upgrade',
224 'medium' => 'styler-card',
225 ),
226 '/style-templates/' . $style['slug']
227 );
228 $params['data-requires'] = FrmFormsHelper::get_plan_required( $style );
229 return $params;
230 };
231 } else {
232 /**
233 * Include the template key for the preview in Pro.
234 *
235 * @param array $params
236 * @param array $style
237 *
238 * @return array
239 */
240 $param_filter = function ( $params ) use ( $style ) {
241 $params['data-template-key'] = $style['slug'];
242 return $params;
243 };
244 }//end if
245
246 $this->is_new_template = ! empty( $style['is_new'] );
247
248 add_filter( 'frm_style_card_params', $param_filter, 10, $this->locked ? 2 : 1 );
249
250 $this->echo_style_card( $style_object, $hidden );
251 return true;
252 }
253
254 /**
255 * Get the string to populate the style card's style attribute with.
256 * This is used to reset some style variables like font size, label padding, and field height, so the cards all look more similar in comparison.
257 * It's kept static as it only requires a $style as input and also gets called when resetting a style.
258 *
259 * @since 6.0
260 *
261 * @param stdClass|WP_Post $style A new style (including duplicated styles) is not a WP_Post object.
262 * Template cards also use an stdClss instead of a WP_Post object.
263 *
264 * @return string
265 */
266 public static function get_style_param_for_card( $style ) {
267 $styles = array();
268
269 // Add the background color setting for fieldsets to the card.
270 if ( empty( $style->post_content['fieldset_bg_color'] ) ) {
271 $background_color = '#fff';
272 } else {
273 $background_color = str_starts_with( $style->post_content['fieldset_bg_color'], 'rgb' ) ? $style->post_content['fieldset_bg_color'] : '#' . $style->post_content['fieldset_bg_color']; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
274 }
275
276 $styles[] = '--preview-background-color: ' . $background_color;
277
278 if ( empty( $style->post_content['submit_border_color'] ) ) {
279 $style->post_content['submit_border_color'] = 'transparent';
280 }
281
282 // Apply additional styles from the style.
283 $rules_to_apply = self::get_style_keys_for_card();
284 $frm_style = new FrmStyle();
285 $color_settings = $frm_style->get_color_settings();
286
287 foreach ( $rules_to_apply as $key ) {
288 if ( ! array_key_exists( $key, $style->post_content ) ) {
289 // A template from the API may not include every style key. If something is missing, skip it.
290 continue;
291 }
292
293 $value = $style->post_content[ $key ];
294 $is_hex = in_array( $key, $color_settings, true ) && $value && '#' !== $value[0] && ! str_contains( $value, 'rgb' ) && $value !== 'transparent';
295
296 if ( $is_hex ) {
297 $value = '#' . $value;
298 }
299
300 $styles[] = '--' . str_replace( '_', '-', $key ) . ':' . $value;
301 }
302
303 return implode( ';', $styles );
304 }
305
306 /**
307 * Get the keys we want to use from the style to use in the card.
308 * We don't use every style as we want the cards to look consistent, so size settings are left out.
309 * The card previews also only include a labelled text input and submit button so we don't need styles for other elements.
310 *
311 * @since 6.0
312 *
313 * @return array<string>
314 */
315 private static function get_style_keys_for_card() {
316 return array(
317 'field_border_width',
318 'field_border_style',
319 'border_color',
320 'border_radius',
321 'submit_bg_color',
322 'submit_border_color',
323 'submit_border_width',
324 'submit_border_radius',
325 'submit_text_color',
326 'label_color',
327 'text_color',
328 'bg_color',
329 );
330 }
331
332 /**
333 * Echo a card wrapper and its children style cards.
334 *
335 * @since 6.0
336 *
337 * @param string $id The ID of the card wrapper element.
338 * @param array $styles
339 *
340 * @return void
341 */
342 public function echo_card_wrapper( $id, $styles ) {
343 $wrapper_style = $this->get_style_attribute_value_for_wrapper();
344
345 // Begin card wrapper
346 $card_wrapper_params = array(
347 'id' => $id,
348 'class' => 'frm-style-card-wrapper with_frm_style',
349 'style' => $wrapper_style,
350 );
351
352 if ( $this->enabled ) {
353 $card_wrapper_params['class'] .= ' frm-styles-enabled';
354 }
355
356 $first_style = reset( $styles );
357 $is_template_wrapper = is_array( $first_style );
358 // phpcs:disable Generic.WhiteSpace.ScopeIndent
359 ?>
360 <div <?php FrmAppHelper::array_to_html_params( $card_wrapper_params, true ); ?>>
361 <?php
362 if ( $is_template_wrapper ) {
363 $this->echo_template_cards( $styles );
364 } else {
365 $this->echo_custom_cards( $styles );
366 }
367 ?>
368 </div>
369 <?php
370 // phpcs:enable Generic.WhiteSpace.ScopeIndent
371 }
372
373 /**
374 * Overwrite some styles. We want to make sure the sizes are normalized for the cards.
375 * The cards use some rules from the default style because of the with_frm_style class on the card wrapper.
376 * As these can be customized, apply some default values for the card previews instead.
377 * This is used in the card wrapper so it doesn't need to get added to each card.
378 *
379 * @since 6.0
380 *
381 * @return string
382 */
383 private function get_style_attribute_value_for_wrapper() {
384 $frm_style = new FrmStyle();
385 $defaults = $frm_style->get_defaults();
386
387 $styles = array();
388 $styles[] = '--field-font-size: ' . $defaults['field_font_size'];
389 $styles[] = '--field-height: ' . $defaults['field_height'];
390 $styles[] = '--field-pad: ' . $defaults['field_pad'];
391 $styles[] = '--font-size: ' . $defaults['font_size'];
392 $styles[] = '--label-padding: ' . $defaults['label_padding'];
393 $styles[] = '--form-align: ' . $defaults['form_align'];
394
395 return implode( ';', $styles );
396 }
397
398 /**
399 * @since 6.0
400 *
401 * @param array<array> $styles
402 *
403 * @return void
404 */
405 private function echo_template_cards( $styles ) {
406 array_walk(
407 $styles,
408 /**
409 * Echo a style card for a single template from API data.
410 *
411 * @param array|string $style
412 * @param string $key The key for the API data. It may be a numeric ID, or a key like "active_sub" or "expires".
413 * @param int $count Used for pagination.
414 *
415 * @return void
416 */
417 function ( $style, $key ) {
418 if ( ! is_numeric( $key ) ) {
419 // Skip active_sub/expires keys.
420 return;
421 }
422
423 $this->echo_card_template( $style );
424 }
425 );
426 }
427
428 /**
429 * @param array<WP_Post> $styles
430 *
431 * @return void
432 */
433 private function echo_custom_cards( $styles ) {
434 $count = 0;
435 $this->locked = false;
436 array_walk(
437 $styles,
438 /**
439 * Echo a style card for a single style in the $styles array.
440 *
441 * @param WP_Post $style
442 * @param int $count Used for pagination.
443 *
444 * @return void
445 */
446 function ( $style ) use ( &$count ) {
447 $hidden = $count > self::PAGE_SIZE - 1;
448 $this->echo_style_card( $style, $hidden );
449 ++$count;
450 }
451 );
452
453 $this->maybe_echo_card_pagination( $count );
454 }
455
456 /**
457 * @since 6.0
458 *
459 * @param int $count
460 *
461 * @return void
462 */
463 private function maybe_echo_card_pagination( $count ) {
464 if ( $count <= self::PAGE_SIZE ) {
465 // Not enough cards to require pagination.
466 return;
467 }
468 // phpcs:disable Generic.WhiteSpace.ScopeIndent
469 ?>
470 <div class="frm-style-card-pagination frm_wrap">
471 <a href="#" class="frm-show-all-styles">
472 <?php
473 printf(
474 /* translators: %d: The number of styles */
475 esc_html__( 'Show all (%d)', 'formidable' ),
476 esc_html( $count - self::PAGE_SIZE )
477 );
478 ?>
479 </a>
480 </div>
481 <?php
482 // phpcs:enable Generic.WhiteSpace.ScopeIndent
483 }
484
485 /**
486 * @since 6.0
487 *
488 * @return array
489 */
490 public function get_styles() {
491 if ( is_callable( 'FrmProStylesController::get_styles_for_styler' ) ) {
492 return FrmProStylesController::get_styles_for_styler( $this->active_style );
493 }
494 return array( $this->default_style );
495 }
496
497 /**
498 * Remove the default style from an array of styles.
499 *
500 * @param array $styles
501 *
502 * @return array
503 */
504 public function filter_custom_styles( $styles ) {
505 return array_filter(
506 $styles,
507 /**
508 * @param WP_Post $style
509 *
510 * @return bool
511 */
512 function ( $style ) {
513 return $this->default_style->ID !== $style->ID;
514 }
515 );
516 }
517
518 /**
519 * @return array
520 */
521 public function get_template_info() {
522 $style_api = new FrmStyleApi();
523 return $style_api->get_api_info();
524 }
525 }
526