PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.27
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.27
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.27, at classes/helpers/FrmStylesCardHelper.php

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