PluginProbe ʕ •ᴥ•ʔ
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI / 3.6.0
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI v3.6.0
3.6.0 3.5.3 3.5.2 3.5.1 3.5.0 3.4.8 3.4.7 3.4.6 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5.1 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.10 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.6.1 1.6.7 1.7.0 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.2 1.7.2.1 1.7.2.2 1.7.3 1.7.4 1.7.5 1.7.5.1 1.7.5.2 1.7.6 1.7.7 1.7.7.1 1.7.7.2 1.7.8 1.7.9 1.8.0 1.8.0.1 1.8.1 1.8.2 1.8.2.1 1.8.2.2 1.8.2.3 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.0.1 1.9.1 1.9.2 1.9.3 1.9.4 1.9.4.1 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.0.1 2.0.1 2.0.2 2.0.3 2.0.3.1 2.0.4 2.0.4.1 2.0.5 2.0.6 2.0.7 2.0.8 2.0.8.1 2.0.9 3.0.0 3.0.0.1 3.0.1 3.0.2 3.0.3 3.0.3.1 3.0.4 3.0.4.1 3.0.4.2 3.0.5 3.0.5.1 3.0.5.2 3.0.6 3.0.6.1 3.0.7.1 3.0.8 3.0.8.1 3.0.9 3.0.9.1 3.0.9.2 3.0.9.3 3.0.9.4 3.0.9.5 3.1.0 3.1.1 3.1.2 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.0 3.4.0 3.4.1 3.4.2 3.4.2.1 3.4.3 3.4.4 3.4.5 trunk 1.0 1.0.1 1.0.2 1.0.3
everest-forms / addons / StyleCustomizer / V2 / FrontendEnqueue.php
everest-forms / addons / StyleCustomizer / V2 Last commit date
assets 1 week ago BuilderPanel.php 1 week ago Compiler.php 1 week ago Engine.php 1 week ago FrontendEnqueue.php 1 week ago Migrator.php 1 week ago Palettes.php 1 week ago PreviewDraft.php 1 week ago RestController.php 1 week ago Sanitizer.php 1 week ago Schema.php 1 week ago Templates.php 1 week ago
FrontendEnqueue.php
233 lines
1 <?php
2 /**
3 * Style Customizer v2 — engine-aware frontend enqueue.
4 *
5 * Decides, per form, whether the front end renders through the v2 engine or the legacy one
6 * (exactly one stylesheet per form). A v2 form gets the `evf-style-v2` marker class, the rule
7 * template ID-scoped to `#evf-{id}` (out-specifying Everest Forms' deeply-nested base CSS),
8 * the per-form CSS-variable block from {@see Compiler}, and its selected Google font.
9 *
10 * @package EverestForms\Addons\StyleCustomizer\V2
11 * @since x.x.x
12 */
13
14 namespace EverestForms\Addons\StyleCustomizer\V2;
15
16 defined( 'ABSPATH' ) || exit;
17
18 /**
19 * Frontend enqueue for v2 forms.
20 */
21 final class FrontendEnqueue {
22
23 /**
24 * Shared stylesheet handle.
25 */
26 const HANDLE = 'evf-style-v2';
27
28 /**
29 * Marker class added to a v2 form's wrapper. Shared with the builder panel so its live
30 * preview bridge can scope the same rule template inside the `?evf_preview` iframe.
31 */
32 const MARKER_CLASS = 'evf-style-v2';
33
34 /**
35 * Memoized rule-template text (read once per request from the shared CSS file).
36 *
37 * @var string|null
38 */
39 protected static $template = null;
40
41 /**
42 * Wire the frontend hooks. Called from {@see Engine::boot()} (only when enabled). Runs at
43 * priority 20, after the legacy Style Customizer's enqueue (10), so it can dequeue it.
44 */
45 public static function register() {
46 add_action( 'everest_forms_shortcode_scripts', array( __CLASS__, 'enqueue' ), 20 );
47 add_filter( 'everest_forms_frontend_container_class', array( __CLASS__, 'container_class' ), 10, 2 );
48 }
49
50 /**
51 * Enqueue the rule template + per-form variables for a v2 form. Mirrors the v1 enqueue
52 * hook signature (`$atts['id']`).
53 *
54 * @param array $atts Shortcode atts (`id`).
55 */
56 public static function enqueue( $atts ) {
57 $form_id = isset( $atts['id'] ) ? absint( $atts['id'] ) : 0;
58 if ( ! $form_id ) {
59 return;
60 }
61
62 $record = self::v2_record( $form_id );
63 if ( null === $record ) {
64 return; // Legacy form — the v1 enqueue handles it.
65 }
66
67 // A v2 form must not also load the legacy per-form compiled sheet.
68 self::suppress_legacy_styles( $form_id );
69
70 if ( ! wp_style_is( self::HANDLE, 'registered' ) ) {
71 wp_register_style( self::HANDLE, false, array(), (string) Schema::version() );
72 }
73 wp_enqueue_style( self::HANDLE );
74
75 // 1) The rule template (selectors reading the vars), ID-scoped to this form.
76 $css = self::scoped_rule_template( $form_id );
77
78 // 2) Per-form variable block.
79 $css .= Compiler::compile( $record, $form_id );
80
81 // Custom CSS is NOT emitted here — it's WP core's own site-wide Additional CSS
82 // (matches v1's actual behavior exactly, verified directly against v1), which WP core
83 // already prints globally on every page via its own wp_custom_css_cb() hook. Scoping it
84 // to this one form here would be wrong twice over: it'd only reach v2 forms, and it'd
85 // stop applying to every OTHER form the way it's supposed to.
86
87 if ( '' !== $css ) {
88 wp_add_inline_style( self::HANDLE, $css );
89 }
90
91 self::maybe_enqueue_font( $record, $form_id );
92 }
93
94 /**
95 * The shared rule template, ID-scoped to a form (`.evf-style-v2#evf-{id}`) — mirrors the
96 * live-preview bridge's transform so frontend and preview stay in lockstep.
97 *
98 * @param int $form_id Form id.
99 * @return string Rescoped CSS (empty if the template file is unreadable).
100 */
101 protected static function scoped_rule_template( $form_id ) {
102 $template = self::rule_template();
103 if ( '' === $template ) {
104 return '';
105 }
106 $scope = self::MARKER_CLASS . '#evf-' . (int) $form_id;
107 // Only replace the marker class when not followed by a name char (mirrors PreviewBridge's regex).
108 return (string) preg_replace(
109 '/\.' . preg_quote( self::MARKER_CLASS, '/' ) . '(?![\w-])/',
110 '.' . $scope,
111 $template
112 );
113 }
114
115 /**
116 * Read (and memoize) the shared rule-template file.
117 *
118 * @return string
119 */
120 protected static function rule_template() {
121 if ( null === self::$template ) {
122 $path = plugin_dir_path( __FILE__ ) . 'assets/css/frontend.css';
123 self::$template = is_readable( $path ) ? (string) file_get_contents( $path ) : ''; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- reading a bundled plugin asset, not a remote/user file.
124 }
125 return self::$template;
126 }
127
128 /**
129 * Enqueue the form's selected Google font, unless it's using the theme font.
130 *
131 * @param array $record V2 style record.
132 * @param int|string $form_id Form id.
133 */
134 protected static function maybe_enqueue_font( $record, $form_id ) {
135 $tokens = isset( $record['tokens'] ) && is_array( $record['tokens'] ) ? $record['tokens'] : array();
136 // The global "Apply Theme Style" toggle forces theme fonts too — see Compiler::compile().
137 $apply_theme_style = 'default' !== get_post_meta( $form_id, 'everest_forms_enable_theme_style', true );
138 $theme_font = $apply_theme_style || ! empty( $tokens['fonts.theme']['desktop'] );
139 $family = isset( $tokens['fonts.family']['desktop'] ) ? trim( (string) $tokens['fonts.family']['desktop'] ) : '';
140
141 if ( $theme_font || '' === $family ) {
142 return;
143 }
144 if ( function_exists( 'evfsc_enqueue_fonts' ) ) {
145 evfsc_enqueue_fonts( $family );
146 }
147 }
148
149 /**
150 * Add the `evf-style-v2` marker class to a v2 form's wrapper. Also adds
151 * `evf-choice-{variation}` when the Choices "Style variation" is `outline`/`filled`, and
152 * `evf-choice-align-{center|right}` when the Choices "Alignment" isn't the default `left`.
153 *
154 * @param array $classes Container classes.
155 * @param array $form_data Form data (`id`).
156 * @return array
157 */
158 public static function container_class( $classes, $form_data ) {
159 $form_id = isset( $form_data['id'] ) ? absint( $form_data['id'] ) : 0;
160 $record = $form_id ? self::v2_record( $form_id ) : null;
161 if ( null === $record ) {
162 return $classes;
163 }
164 $classes[] = 'evf-style-v2';
165 $variation = isset( $record['tokens']['choice.variation']['desktop'] ) ? (string) $record['tokens']['choice.variation']['desktop'] : '';
166 if ( in_array( $variation, array( 'outline', 'filled' ), true ) ) {
167 $classes[] = 'evf-choice-' . $variation;
168 }
169 // The Subscription Plan card's name/price row is a fixed `space-between` flex row (see
170 // evf-payment-subscription-plan-frontend.scss) that plain `text-align` can never reach —
171 // bridge the align token to a class too, the same way choice.variation already does, so
172 // only that field's row responds to it (every other choice type already works via
173 // `--evf-choice-align`'s inherited `text-align`).
174 $align = isset( $record['tokens']['choice.align']['desktop'] ) ? (string) $record['tokens']['choice.align']['desktop'] : '';
175 if ( in_array( $align, array( 'center', 'right' ), true ) ) {
176 $classes[] = 'evf-choice-align-' . $align;
177 }
178 // btn.widthMode is another "meta" token (no CSS var) — 'fill' adds a class, same pattern
179 // as choice.variation/choice.align above; 'fit' (the default) needs no class at all.
180 $width_mode = isset( $record['tokens']['btn.widthMode']['desktop'] ) ? (string) $record['tokens']['btn.widthMode']['desktop'] : '';
181 if ( 'fill' === $width_mode ) {
182 $classes[] = 'evf-btn-width-fill';
183 }
184 return $classes;
185 }
186
187 /**
188 * Dequeue + deregister the legacy per-form stylesheet handle for a v2 form.
189 *
190 * @param int $form_id Form id.
191 */
192 protected static function suppress_legacy_styles( $form_id ) {
193 $handle = 'everest-forms-style-' . $form_id;
194 if ( wp_style_is( $handle, 'enqueued' ) || wp_style_is( $handle, 'registered' ) ) {
195 wp_dequeue_style( $handle );
196 wp_deregister_style( $handle );
197 }
198 }
199
200 /**
201 * The stored style record for a form.
202 *
203 * @param int $form_id Form id.
204 * @return array
205 */
206 protected static function record( $form_id ) {
207 $all = get_option( 'everest_forms_styles', array() );
208 return isset( $all[ $form_id ] ) && is_array( $all[ $form_id ] ) ? $all[ $form_id ] : array();
209 }
210
211 /**
212 * The stored record, only if it's actually v2-shaped, with the SAME `evf_style_v2_record`
213 * filter {@see RestController::build_payload()} applies for the panel. Without this, an
214 * addon's backfill (e.g. Multi-Part's `EVFMP_Style_Customizer::backfill_record()`, which
215 * derives `pagination.*` tokens from the classic `settings.multi_part` values for a form that
216 * became v2-shaped — via ANY style save — without ever having its own Pagination section
217 * explicitly saved) only ever ran for the builder panel's display, never for what actually
218 * rendered on the frontend: `Compiler::compile()` would resolve those missing tokens to their
219 * schema DEFAULTS instead, silently dropping the form's real pagination colors/type on the
220 * live site while the panel kept showing them correctly.
221 *
222 * @param int $form_id Form id.
223 * @return array|null The record, or null if this form isn't v2-shaped (caller defers to v1).
224 */
225 protected static function v2_record( $form_id ) {
226 $record = self::record( $form_id );
227 if ( ! Engine::is_v2_record( $record ) ) {
228 return null;
229 }
230 return apply_filters( 'evf_style_v2_record', $record, $form_id );
231 }
232 }
233