| @@ -148,8 +148,148 @@ | ||
| 148 | 148 | return 'inherit' !== ( $block_attrs['formTheme'] ?? 'inherit' ); |
| 149 | 149 | } |
| 150 | 150 | |
| 151 | 151 | /** |
| 152 | + * Check if the form has default SureForms styling disabled. | |
| 153 | + * | |
| 154 | + * When enabled (via the `_srfm_forms_styling` meta set through REST/MCP, or | |
| 155 | + * the `srfm_disable_default_styles` filter), the form is rendered without the | |
| 156 | + * SureForms frontend stylesheets and inline CSS variables so the site's own | |
| 157 | + * CSS fully controls the form's appearance. | |
| 158 | + * | |
| 159 | + * @param int|string $form_id Form post ID. | |
| 160 | + * @return bool True when default styling is disabled for the form. | |
| 161 | + * @since 2.12.2 | |
| 162 | + */ | |
| 163 | + public static function is_default_styling_disabled( $form_id ) { | |
| 164 | + $form_id = absint( $form_id ); | |
| 165 | + if ( ! $form_id ) { | |
| 166 | + return false; | |
| 167 | + } | |
| 168 | + | |
| 169 | + $form_styling = get_post_meta( $form_id, '_srfm_forms_styling', true ); | |
| 170 | + $disabled = is_array( $form_styling ) && ! empty( $form_styling['disable_default_styles'] ); | |
| 171 | + | |
| 172 | + /** | |
| 173 | + * Filters whether SureForms' default frontend styling is disabled for a form. | |
| 174 | + * | |
| 175 | + * Lets themes/plugins toggle the unstyled mode programmatically, overriding the | |
| 176 | + * stored per-form meta. Return true to render the form without SureForms' default | |
| 177 | + * stylesheets and inline CSS variables. | |
| 178 | + * | |
| 179 | + * @param bool $disabled Whether default styling is disabled (from meta). | |
| 180 | + * @param int $form_id Form post ID. | |
| 181 | + * @since 2.12.2 | |
| 182 | + */ | |
| 183 | + return (bool) apply_filters( 'srfm_disable_default_styles', $disabled, $form_id ); | |
| 184 | + } | |
| 185 | + | |
| 186 | + /** | |
| 187 | + * Check whether the SureForms frontend stylesheets can be skipped for a post. | |
| 188 | + * | |
| 189 | + * Returns true only when every SureForms form found on the post has default | |
| 190 | + * styling disabled. When the forms on the post cannot be determined, the | |
| 191 | + * stylesheets are kept as the safe default. | |
| 192 | + * | |
| 193 | + * @param \WP_Post $post Post being rendered. | |
| 194 | + * @return bool True when the frontend stylesheets can be skipped. | |
| 195 | + * @since 2.12.2 | |
| 196 | + */ | |
| 197 | + public static function should_skip_frontend_styles( $post ) { | |
| 198 | + if ( SRFM_FORMS_POST_TYPE === $post->post_type ) { | |
| 199 | + $form_ids = [ $post->ID ]; | |
| 200 | + } else { | |
| 201 | + $form_ids = self::get_form_ids_from_content( $post->post_content ); | |
| 202 | + } | |
| 203 | + | |
| 204 | + if ( empty( $form_ids ) ) { | |
| 205 | + return false; | |
| 206 | + } | |
| 207 | + | |
| 208 | + foreach ( $form_ids as $form_id ) { | |
| 209 | + if ( ! self::is_default_styling_disabled( $form_id ) ) { | |
| 210 | + return false; | |
| 211 | + } | |
| 212 | + } | |
| 213 | + | |
| 214 | + return true; | |
| 215 | + } | |
| 216 | + | |
| 217 | + /** | |
| 218 | + * Extract SureForms form IDs from post content. | |
| 219 | + * | |
| 220 | + * Looks for srfm/form blocks (including nested ones) and [sureforms] | |
| 221 | + * shortcodes. | |
| 222 | + * | |
| 223 | + * @param string $content Post content. | |
| 224 | + * @return array<int> Unique form IDs found in the content. | |
| 225 | + * @since 2.12.2 | |
| 226 | + */ | |
| 227 | + public static function get_form_ids_from_content( $content ) { | |
| 228 | + $form_ids = []; | |
| 229 | + | |
| 230 | + // Also parse when the content embeds reusable/synced patterns (core/block | |
| 231 | + // refs) — a form inside a pattern appears as wp:block {"ref":N}, which | |
| 232 | + // has_block( 'srfm/form' ) alone would never see. | |
| 233 | + if ( has_block( 'srfm/form', $content ) || has_block( 'core/block', $content ) ) { | |
| 234 | + $visited_refs = []; | |
| 235 | + $form_ids = self::collect_form_block_ids( parse_blocks( $content ), $visited_refs ); | |
| 236 | + } | |
| 237 | + | |
| 238 | + if ( has_shortcode( $content, 'sureforms' ) && preg_match_all( '/' . get_shortcode_regex( [ 'sureforms' ] ) . '/', $content, $matches ) ) { | |
| 239 | + foreach ( $matches[3] as $atts_string ) { | |
| 240 | + $atts = shortcode_parse_atts( $atts_string ); | |
| 241 | + if ( is_array( $atts ) && ! empty( $atts['id'] ) ) { | |
| 242 | + $form_ids[] = absint( $atts['id'] ); | |
| 243 | + } | |
| 244 | + } | |
| 245 | + } | |
| 246 | + | |
| 247 | + return array_values( array_unique( array_filter( $form_ids ) ) ); | |
| 248 | + } | |
| 249 | + | |
| 250 | + /** | |
| 251 | + * Recursively collect form IDs from parsed srfm/form blocks, following | |
| 252 | + * reusable/synced pattern references (core/block) into their wp_block posts. | |
| 253 | + * | |
| 254 | + * @param array<mixed> $blocks Parsed blocks from parse_blocks(). | |
| 255 | + * @param array<int, true> $visited_refs Reusable-block post IDs already expanded, | |
| 256 | + * keyed by ID — guards against reference cycles. | |
| 257 | + * @return array<int> Form IDs found in srfm/form blocks. | |
| 258 | + * @since 2.12.2 | |
| 259 | + */ | |
| 260 | + private static function collect_form_block_ids( $blocks, &$visited_refs = [] ) { | |
| 261 | + $form_ids = []; | |
| 262 | + | |
| 263 | + foreach ( $blocks as $block ) { | |
| 264 | + if ( ! is_array( $block ) ) { | |
| 265 | + continue; | |
| 266 | + } | |
| 267 | + $attrs = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : []; | |
| 268 | + if ( isset( $block['blockName'] ) && 'srfm/form' === $block['blockName'] && ! empty( $attrs['id'] ) && is_scalar( $attrs['id'] ) ) { | |
| 269 | + $form_ids[] = absint( $attrs['id'] ); | |
| 270 | + } | |
| 271 | + // Reusable/synced pattern: expand the referenced wp_block post so a | |
| 272 | + // form living inside a pattern is detected like an inline block. | |
| 273 | + if ( isset( $block['blockName'] ) && 'core/block' === $block['blockName'] && ! empty( $attrs['ref'] ) && is_scalar( $attrs['ref'] ) ) { | |
| 274 | + $ref = absint( $attrs['ref'] ); | |
| 275 | + if ( $ref && ! isset( $visited_refs[ $ref ] ) ) { | |
| 276 | + $visited_refs[ $ref ] = true; | |
| 277 | + $ref_post = get_post( $ref ); | |
| 278 | + if ( $ref_post instanceof \WP_Post && 'wp_block' === $ref_post->post_type && 'publish' === $ref_post->post_status && '' !== $ref_post->post_content ) { | |
| 279 | + $form_ids = array_merge( $form_ids, self::collect_form_block_ids( parse_blocks( $ref_post->post_content ), $visited_refs ) ); | |
| 280 | + } | |
| 281 | + } | |
| 282 | + } | |
| 283 | + if ( ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) { | |
| 284 | + $form_ids = array_merge( $form_ids, self::collect_form_block_ids( $block['innerBlocks'], $visited_refs ) ); | |
| 285 | + } | |
| 286 | + } | |
| 287 | + | |
| 288 | + return $form_ids; | |
| 289 | + } | |
| 290 | + | |
| 291 | + /** | |
| 152 | 292 | * Map dimension block attributes (Top/Right/Bottom/Left/Unit) to form styling. |
| 153 | 293 | * |
| 154 | 294 | * @param array<string,mixed> $form_styling Form styling array. |
| 155 | 295 | * @param array<string,mixed> $block_attrs Block attributes. |