Skip to content

style custom css option#2600

Merged
Crabcyborg merged 17 commits into
masterfrom
feature/ff-style-custom-css
Dec 8, 2025
Merged

style custom css option#2600
Crabcyborg merged 17 commits into
masterfrom
feature/ff-style-custom-css

Conversation

@Liviu-p
Copy link
Copy Markdown
Contributor

@Liviu-p Liviu-p commented Nov 26, 2025

No description provided.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 26, 2025

Walkthrough

Adds per-style scoped custom CSS: new FrmCssScopeHelper with nest/unnest, model defaults/persistence for single-style CSS, controller/view changes for configurable CodeMirror editor, admin JS for editor lifecycle and live preview, frontend inclusion of scoped CSS, tests, and some duplicated UI/CSS.

Changes

Cohort / File(s) Summary
CSS scoping helper & model updates
classes/helpers/FrmCssScopeHelper.php, classes/models/FrmStyle.php
Adds FrmCssScopeHelper with nest/unnest and helpers; FrmStyle adds defaults enable_style_custom_css and single_style_custom_css, excludes single_style_custom_css from generic sanitization, and nests single-style CSS on update via the helper.
Controller: custom CSS handling & CodeMirror
classes/controllers/FrmStylesController.php
custom_css() gains $extra_args (id, placeholder, show_errors, heading, textarea_params, custom_css); get_custom_css() accepts optional $single_style_settings; enqueue_codemirror() accepts $id and $placeholder; CodeMirror init assigns editor to window.{id}_wp_editor and textareas get hide-if-js when editor present.
Views / UI
classes/views/styles/_quick-settings.php, classes/views/styles/custom_css.php
Quick Settings: adds Custom CSS toggle/editor wired to enable_style_custom_css and renders editor via FrmStylesController::custom_css (duplicated UI block present). custom_css.php renders dynamic $heading, conditionally includes errors when $show_errors === true, and expects $textarea_params externally.
Frontend output & admin CSS change
css/_single_theme.css.php, css/admin/frm-settings-components.css
Single-theme output now emits FrmStylesController::get_custom_css($settings) (stripped tags) and fires frm_output_single_style. frm-settings-components.css has an appended duplicate block of existing rules.
Admin JS: editor lifecycle, preview & clipboard
js/src/admin/styles.js, js/admin/style.js
js/src/admin/styles.js: adds editor state (cssEditorInstance, cssInlineStyleElement), retry-based initCustomCSSEditorInstance(), onCssEditorReady() binding for live scoped preview, toggleVisibilityOfCustomCSSEditor(), and fallbackCopyToClipboard(); copy handler now supports multiple .frm-copy-text. js/admin/style.js: adds resetCustomCSSEditor() and invokes it on style reset.
Minified runtime tweak
js/formidable_styles.js
Localized control-flow change in a sprintf-like routine altering which loop variable is advanced on mismatch.
Styling
resources/scss/admin/components/settings/_misc-components.scss
Adds CodeMirror sizing (height: 350px) and placeholder color styling for style components.
Tests
tests/phpunit/helpers/test_FrmCssScopeHelper.php
New PHPUnit tests covering nesting/unnesting across selectors, at-rules, keyframes, comments, edge cases, and formatting preservation.

Sequence Diagram(s)

sequenceDiagram
    actor AdminUser
    participant UI as Admin UI
    participant JS as frmStyleOptions (admin JS)
    participant Editor as CodeMirror (window.{id}_wp_editor)
    participant Controller as FrmStylesController (server)
    participant Helper as FrmCssScopeHelper
    participant Model as FrmStyle
    participant Frontend as Frontend Output

    AdminUser->>UI: Toggle "Enable Custom CSS"
    UI->>JS: toggleVisibilityOfCustomCSSEditor()
    JS->>JS: initCustomCSSEditorInstance() (retry loop)
    JS->>Editor: wait for window.{id}_wp_editor.codemirror
    Editor-->>JS: editor instance available
    AdminUser->>Editor: Edit CSS
    Editor->>JS: change event
    JS->>JS: sanitize & scope CSS for live preview
    JS->>JS: update cssInlineStyleElement (inject scoped CSS)
    AdminUser->>Controller: Save single_style_custom_css (POST)
    Controller->>Helper: nest CSS with .frm_style_<name>
    Helper-->>Controller: scoped CSS
    Controller->>Model: save single_style_custom_css (scoped)
    Frontend->>Frontend: include scoped CSS via get_custom_css()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus areas:
    • classes/helpers/FrmCssScopeHelper.php — selector parsing, at-rule/keyframes handling, brace matching, and formatting preservation.
    • classes/controllers/FrmStylesController.php & classes/models/FrmStyle.php — integration, sanitization exceptions, defaults, and persistence.
    • js/src/admin/styles.js — retry/init logic, event binding, live preview updates, and clipboard fallback.
    • UI/CSS duplication: _quick-settings.php duplicated UI block and duplicated CSS in css/admin/frm-settings-components.css.

Possibly related PRs

Suggested labels

action: needs qa

Suggested reviewers

  • Crabcyborg
  • lauramekaj1

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.93% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'style custom css option' is vague and generic, using non-descriptive terms that don't clearly convey what custom CSS functionality is being added or how it works. Provide a more specific title such as 'Add per-style custom CSS editor with scoped styling support' to clearly describe the main feature being introduced.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to evaluate whether the description relates to the changeset. Add a detailed pull request description explaining the purpose, implementation approach, and scope of the custom CSS feature being added.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/ff-style-custom-css

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0180ddd and cf14703.

📒 Files selected for processing (1)
  • classes/helpers/FrmCssScopeHelper.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • classes/helpers/FrmCssScopeHelper.php
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cypress

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
classes/controllers/FrmStylesController.php (2)

806-821: LGTM — Backward-compatible API extension.

The extra_args parameter with defaults maintains backward compatibility while enabling flexible customization of the CSS editor. The PHPMD warnings about unused variables are false positives — these variables ($show_errors, $custom_css, $heading, $textarea_params) are used in the included custom_css.php template.

Note: The $message parameter appears genuinely unused in this method. Consider either using it or removing it from the signature if it's not needed.


878-887: Consider escaping the $id in the inline JavaScript.

While $id is currently developer-controlled (defaulting to 'frm_codemirror_box'), using esc_js() would be a defensive practice for the JavaScript context:

 wp_add_inline_script(
     'code-editor',
     sprintf(
         'jQuery( function() { window.%s_wp_editor = wp.codeEditor.initialize( \'%s\', %s ); } );',
-        $id,
-        $id,
+        esc_js( $id ),
+        esc_js( $id ),
         wp_json_encode( $settings )
     )
 );

This protects against potential XSS if $id ever originates from user input in future refactors.

js/src/admin/styles.js (1)

38-53: Consider using the retryInterval from cssEditorOptions.

Line 47 hardcodes the retry interval as 500 in the setTimeout call, but this.cssEditorOptions.retryInterval is already defined with the same value. Using the property would make the retry interval configurable in one place.

Apply this diff:

 			setTimeout( () => {
 				this.cssEditorOptions.retryCount++;
 				this.initCustomCSSEditorInstance();
-			}, 500 );
+			}, this.cssEditorOptions.retryInterval );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 006d247 and bc54c69.

⛔ Files ignored due to path filters (5)
  • js/form-templates.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable-settings-components.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable_admin.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable_dashboard.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable_styles.js.map is excluded by !**/*.map, !**/*.map
📒 Files selected for processing (20)
  • classes/controllers/FrmStylesController.php (5 hunks)
  • classes/helpers/FrmCssScopeHelper.php (1 hunks)
  • classes/models/FrmStyle.php (4 hunks)
  • classes/views/styles/_quick-settings.php (1 hunks)
  • classes/views/styles/custom_css.php (1 hunks)
  • css/_single_theme.css.php (1 hunks)
  • css/admin/frm-settings-components.css (1 hunks)
  • css/frm_testing_mode.css (1 hunks)
  • js/addons-page.js (1 hunks)
  • js/admin/style.js (1 hunks)
  • js/formidable-settings-components.js (1 hunks)
  • js/formidable_blocks.js (1 hunks)
  • js/formidable_dashboard.js (1 hunks)
  • js/formidable_overlay.js (1 hunks)
  • js/formidable_styles.js (1 hunks)
  • js/frm_testing_mode.js (1 hunks)
  • js/onboarding-wizard.js (1 hunks)
  • js/src/admin/styles.js (3 hunks)
  • js/welcome-tour.js (1 hunks)
  • resources/scss/admin/components/settings/_misc-components.scss (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
js/onboarding-wizard.js (2)
js/admin/style.js (5)
  • frmDom (23-23)
  • frmDom (24-24)
  • frmDom (25-25)
  • frmDom (26-26)
  • frmDom (1164-1164)
js/src/admin/addon-state.js (3)
  • frmDom (3-3)
  • a (200-200)
  • ajaxurl (12-12)
js/frm_testing_mode.js (1)
js/src/frm_testing_mode.js (1)
  • frmDom (34-34)
classes/views/styles/custom_css.php (2)
classes/helpers/FrmFormsHelper.php (1)
  • show_errors (1036-1053)
classes/helpers/FrmAppHelper.php (1)
  • FrmAppHelper (6-4666)
css/_single_theme.css.php (1)
classes/controllers/FrmStylesController.php (2)
  • FrmStylesController (6-1401)
  • get_custom_css (831-848)
js/formidable_overlay.js (2)
js/src/admin/admin.js (41)
  • e (8207-8207)
  • t (692-692)
  • t (717-717)
  • t (8988-8988)
  • t (10174-10174)
  • frmDom (239-239)
  • frmDom (240-240)
  • frmDom (241-241)
  • a (3056-3056)
  • i (438-438)
  • i (1771-1771)
  • i (2249-2249)
  • i (2993-2993)
  • i (3008-3008)
  • i (3080-3080)
  • i (3098-3098)
  • i (3112-3115)
  • i (3138-3146)
  • i (3176-3176)
  • i (3185-3185)
  • i (3446-3452)
  • i (3588-3591)
  • i (5413-5413)
  • i (5482-5483)
  • i (5557-5559)
  • c (697-697)
  • c (722-722)
  • c (7245-7245)
  • c (9064-9064)
  • s (274-274)
  • s (8213-8213)
  • s (8964-8964)
  • s (9041-9041)
  • f (9003-9003)
  • v (3352-3352)
  • v (3372-3372)
  • v (6882-6882)
  • v (7249-7249)
  • v (7800-7800)
  • v (7816-7816)
  • v (7832-7832)
js/src/admin/addon-state.js (2)
  • frmDom (3-3)
  • a (200-200)
js/formidable-settings-components.js (2)
js/admin/style.js (5)
  • frmDom (23-23)
  • frmDom (24-24)
  • frmDom (25-25)
  • frmDom (26-26)
  • frmDom (1164-1164)
js/src/admin/addon-state.js (2)
  • frmDom (3-3)
  • a (200-200)
classes/models/FrmStyle.php (1)
classes/helpers/FrmCssScopeHelper.php (2)
  • FrmCssScopeHelper (6-253)
  • nest (16-89)
js/welcome-tour.js (2)
js/admin/style.js (5)
  • frmDom (23-23)
  • frmDom (24-24)
  • frmDom (25-25)
  • frmDom (26-26)
  • frmDom (1164-1164)
js/src/welcome-tour/ui/spotlight.js (1)
  • getComputedStyle (123-123)
classes/views/styles/_quick-settings.php (2)
classes/helpers/FrmHtmlHelper.php (1)
  • FrmHtmlHelper (9-129)
classes/helpers/FrmCssScopeHelper.php (2)
  • FrmCssScopeHelper (6-253)
  • unnest (99-173)
🪛 Biome (2.1.2)
js/onboarding-wizard.js

[error] 2-2: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 2-2: Unsafe usage of 'throw'.

'throw' in 'finally' overwrites the control flow statements inside 'try' and 'catch'.

(lint/correctness/noUnsafeFinally)


[error] 2-2: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 2-2: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 2-2: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 2-2: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 2-2: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 2-2: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 2-2: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 2-2: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 2-2: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 2-2: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 2-2: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 2-2: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

js/formidable_dashboard.js

[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

js/frm_testing_mode.js

[error] 2-2: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 2-2: Do not use the t variable name as a label

The variable is declared here

Creating a label with the same name as an in-scope variable leads to confusion.

(lint/suspicious/noLabelVar)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

js/formidable_styles.js

[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: Do not use the t variable name as a label

The variable is declared here

Creating a label with the same name as an in-scope variable leads to confusion.

(lint/suspicious/noLabelVar)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

js/formidable_overlay.js

[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

js/addons-page.js

[error] 1-1: Unsafe usage of 'throw'.

'throw' in 'finally' overwrites the control flow statements inside 'try' and 'catch'.

(lint/correctness/noUnsafeFinally)


[error] 1-1: Unsafe usage of 'return'.

'return' in 'finally' overwrites the control flow statements inside 'try' and 'catch'.

(lint/correctness/noUnsafeFinally)


[error] 1-1: Unsafe usage of 'throw'.

'throw' in 'finally' overwrites the control flow statements inside 'try' and 'catch'.

(lint/correctness/noUnsafeFinally)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: Do not use the t variable name as a label

The variable is declared here

Creating a label with the same name as an in-scope variable leads to confusion.

(lint/suspicious/noLabelVar)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

js/formidable-settings-components.js

[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

js/welcome-tour.js

[error] 2-2: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 2-2: This case is falling through to the next case.

Add a break or return statement to the end of this case to prevent fallthrough.

(lint/suspicious/noFallthroughSwitchClause)


[error] 2-2: Do not use the t variable name as a label

The variable is declared here

Creating a label with the same name as an in-scope variable leads to confusion.

(lint/suspicious/noLabelVar)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 2-2: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

js/formidable_blocks.js

[error] 1-1: Unsafe usage of 'return'.

'return' in 'finally' overwrites the control flow statements inside 'try' and 'catch'.

(lint/correctness/noUnsafeFinally)


[error] 1-1: Unsafe usage of 'throw'.

'throw' in 'finally' overwrites the control flow statements inside 'try' and 'catch'.

(lint/correctness/noUnsafeFinally)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

css/frm_testing_mode.css

[error] 3-3: Unexpected shorthand property background after background-color

(lint/suspicious/noShorthandPropertyOverrides)


[error] 3-3: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

text-align is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)


[error] 3-3: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

border-radius is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

🪛 PHPMD (2.15.0)
classes/controllers/FrmStylesController.php

809-809: Avoid unused parameters such as '$message'. (undefined)

(UnusedFormalParameter)


813-813: Avoid unused local variables such as '$show_errors'. (undefined)

(UnusedLocalVariable)


814-814: Avoid unused local variables such as '$custom_css'. (undefined)

(UnusedLocalVariable)


815-815: Avoid unused local variables such as '$heading'. (undefined)

(UnusedLocalVariable)


816-816: Avoid unused local variables such as '$textarea_params'. (undefined)

(UnusedLocalVariable)

🔇 Additional comments (28)
js/frm_testing_mode.js (1)

1-2: Minified bundled file should not be manually reviewed; verify the build process.

This file appears to be a minified/bundled JavaScript output, not source code. The static analysis warnings (switch fallthrough, label/variable conflicts, function reassignments) are expected artifacts of minification and not indicative of actual defects.

Key concerns:

  1. Manual review is impractical: Minified code is compressed and unreadable, making line-by-line review error-prone and ineffective.
  2. Build artifacts should not be versioned: Bundled/minified files are typically generated during the build process and should not be committed to the repository.

Recommendations:

  • Verify whether a source file (e.g., js/src/frm_testing_mode.js, as suggested by the relevant code snippets) exists. If so, include the source in the PR diff for review instead.
  • Exclude bundled/minified files from version control using .gitignore and regenerate them as part of the build pipeline.
  • Ensure the build process is documented and automated so minified outputs are consistent and reproducible.

Please verify: (1) whether js/src/frm_testing_mode.js (source) exists and should be included in this PR, (2) the role of js/frm_testing_mode.js (is it generated or manually maintained?), and (3) whether bundled files should be included in future PRs.

js/addons-page.js (1)

1-1: Minified bundle cannot be meaningfully reviewed without source maps.

These are bundled/minified assets generated by a build process. To properly review changes, please provide either:

  1. Source map references
  2. Unminified source code from js/src/ directory
  3. Build configuration details

Per the AI summary, these changes appear to be formatting-only with no behavioral changes, but this cannot be verified from minified code alone.

js/welcome-tour.js (1)

2-2: Minified bundle cannot be meaningfully reviewed without source maps.

Same as js/addons-page.js - this is a minified bundle. Please provide source maps or unminified source code for proper review.

js/formidable_dashboard.js (1)

1-1: Minified bundle cannot be meaningfully reviewed without source maps.

This file is minified/bundled code. Please provide source maps or access to the original source code in js/src/ for meaningful review.

js/formidable-settings-components.js (1)

1-1: Minified bundle cannot be meaningfully reviewed without source maps.

This file is minified/bundled code. Please provide source maps or access to the original source code in js/src/ for meaningful review.

js/formidable_overlay.js (1)

1-1: Minified bundle cannot be meaningfully reviewed without source maps.

This file is minified/bundled code. Please provide source maps or access to the original source code in js/src/ for meaningful review.

js/formidable_styles.js (1)

1-1: Minified bundle cannot be meaningfully reviewed without source maps.

This file is minified/bundled code. Please provide source maps or access to the original source code in js/src/ for meaningful review.

js/onboarding-wizard.js (1)

1-2: Minified build artifact — no source-level review applicable.

This is a bundled/minified output file. The static analysis warnings (switch fallthrough, throw in finally, use before declaration, function reassignment) are false positives generated by the bundler's transpilation of generators and async/await patterns. These are expected in minified Babel/Webpack output.

Ensure the source files in js/src/ are the ones being reviewed for code quality.

classes/models/FrmStyle.php (3)

83-84: LGTM — Helper instantiation is appropriate.

Instantiating FrmCssScopeHelper once before the loop is efficient, avoiding repeated instantiation inside the foreach.


262-264: LGTM — Consistent with existing custom_css handling.

CSS fields require special characters ({, }, ;, etc.) that would otherwise be stripped. Excluding single_style_custom_css from strip_invalid_characters is correct and consistent with how custom_css is already handled.


756-758: LGTM — Sensible defaults for the new settings.

The opt-in pattern (enable_style_custom_css defaulting to false) and empty string for CSS content are appropriate defaults.

js/formidable_blocks.js (1)

1-1: Minified build artifact — no source-level review applicable.

This is a bundled/minified output file. The static analysis warnings are false positives from bundler transpilation patterns. Source files should be reviewed instead.

classes/helpers/FrmCssScopeHelper.php (4)

1-6: LGTM — Well-structured CSS scope helper.

The class provides clean, focused functionality for CSS scoping with nest() and unnest() operations. Good separation of concerns.


16-89: Well-implemented CSS nesting with proper edge case handling.

The implementation correctly:

  • Strips comments before processing
  • Handles @keyframes without nesting (as keyframe percentages aren't selectors)
  • Recursively processes nested at-rules like @media
  • Handles comma-separated selectors

One consideration: if the input CSS is malformed (e.g., unclosed braces), find_matching_brace() returns $length - 1, which may produce unexpected output. However, for user-provided CSS, this graceful degradation is acceptable.


99-173: LGTM — Correctly reverses the nesting operation.

The unnest() method properly handles:

  • Selectors that have the prefix (removes it)
  • Selectors without the prefix (preserves them unchanged)
  • Recursive processing for nested at-rules

216-252: Robust brace matching with string-awareness.

Good implementation that correctly handles:

  • Nested braces (level tracking)
  • String literals (avoids false positives from braces in content: "{")
  • Escaped quotes within strings
classes/controllers/FrmStylesController.php (1)

831-835: LGTM — Clean extension for per-style custom CSS.

The logic correctly checks both the enable flag and content presence before returning the single style's custom CSS, falling through to the global CSS logic otherwise.

resources/scss/admin/components/settings/_misc-components.scss (1)

18-24: LGTM! CodeMirror styling is appropriate.

The !important declarations are necessary here to override CodeMirror's default styles, and scoping to .frm-style-component prevents unintended side effects elsewhere.

js/admin/style.js (1)

1094-1107: LGTM! Reset function is well-guarded.

The function properly checks for element existence before manipulation and integrates cleanly into the existing reset flow.

classes/views/styles/custom_css.php (3)

8-8: LGTM! Dynamic heading enables flexible messaging.

Using a variable for the heading allows callers to customize the message, improving reusability.


12-14: LGTM! Strict comparison is appropriate.

Using strict equality (===) for the boolean check is a best practice in PHP.


16-20: The review comment is incorrect—$textarea_params is always initialized before use.

The controller method FrmStylesController::custom_css() (lines 17–20) initializes $textarea_params with a default value before including the view file. The conditional block in the view (lines 16–18) only optionally adds a 'class' key to an array that is guaranteed to exist. No undefined variable warning will occur.

// In controller (lines 17-20):
$textarea_params = ! empty( $extra_args['textarea_params'] ) ? $extra_args['textarea_params'] : array(
    'name' => 'frm_custom_css',
    'id'   => $id,
);

// Then the view is included with $textarea_params already in scope
classes/views/styles/_quick-settings.php (2)

229-249: LGTM! Toggle control is well-integrated.

The Custom CSS toggle is properly wired with appropriate labels, ARIA attributes, and checked state handling.


274-285: LGTM! Style class display with copy functionality.

The style class display shows the scoped class name with a copy icon, providing good UX for users who need to reference the class name.

js/src/admin/styles.js (3)

12-18: LGTM! Properties are well-defined.

The new properties support the CSS editor instance management and retry logic appropriately.


163-176: LGTM! Clipboard API guards are robust.

The function properly checks for clipboard API availability before attempting to use it and handles multiple copy labels correctly.


178-187: LGTM! Toggle visibility is straightforward.

The function correctly toggles the editor visibility based on the checkbox state with proper guards for element existence.

css/_single_theme.css.php (1)

402-402: I'll help you verify the CSS sanitization approach in the code. Let me investigate the FrmStylesController::get_custom_css() method to understand how custom CSS is sanitized before being output.
<function_calls>

#!/bin/bash

Search for FrmStylesController class definition

rg -n "class FrmStylesController" --type=php -A 5


</function_calls>
<function_calls>

#!/bin/bash

Search for get_custom_css method implementation

rg -n "get_custom_css" --type=php -B 2 -A 15


</function_calls>
<function_calls>

#!/bin/bash

Find the file that contains FrmStylesController

fd "." --type=f --name ".php" | xargs rg -l "FrmStylesController" | head -10


</function_calls>

Comment thread classes/models/FrmStyle.php
Comment thread classes/views/styles/_quick-settings.php
Comment thread css/admin/frm-settings-components.css Outdated
Comment thread css/frm_testing_mode.css Outdated
Comment thread js/src/admin/styles.js
Comment thread js/src/admin/styles.js Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
js/admin/style.js (1)

1094-1107: Optional: also clear editor value when resetting the custom CSS UI

resetCustomCSSEditor() safely unchecks the toggle and hides the editor wrapper, which is great. If the reset operation is also meant to wipe any per‑style custom CSS, consider additionally clearing the underlying textarea/input value (or invoking whatever logic normally syncs editor content) so that stale CSS doesn’t reappear when the toggle is turned back on after a reset.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc54c69 and a611e06.

⛔ Files ignored due to path filters (2)
  • js/formidable_admin.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable_styles.js.map is excluded by !**/*.map, !**/*.map
📒 Files selected for processing (2)
  • css/frm_testing_mode.css (1 hunks)
  • js/admin/style.js (1 hunks)
🧰 Additional context used
🪛 Biome (2.1.2)
css/frm_testing_mode.css

[error] 3-3: Unexpected shorthand property background after background-color

(lint/suspicious/noShorthandPropertyOverrides)


[error] 3-3: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

text-align is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)


[error] 3-3: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

min-width is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)


[error] 3-3: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

border-radius is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

🔇 Additional comments (2)
css/frm_testing_mode.css (1)

3-3: I'll help you verify the CSS issues flagged in the review comment. Let me start by exploring the codebase to understand the structure and locate the source files.
<function_calls>


#!/bin/bash

Locate CSS files in the repository

echo "=== Finding CSS files ==="
find . -name "*.css" -type f | head -20
echo ""
echo "=== Checking for source CSS directory ==="
fd -t f -e css | head -20


</function_calls>
<function_calls>


#!/bin/bash

Check if there's a source CSS file for frm_testing_mode

echo "=== Searching for frm_testing_mode CSS files ==="
find . -name "frm_testing_mode" -type f 2>/dev/null
echo ""
echo "=== Checking css directory ==="
if [ -d "css" ]; then
ls -la css/ | head -20
fi


</function_calls>
<function_calls>


#!/bin/bash

Get file info and check repository structure

echo "=== Repository structure ==="
ls -la | head -20
echo ""
echo "=== Line count of the CSS file ==="
wc -l css/frm_testing_mode.css 2>/dev/null || echo "File path may differ"


</function_calls>

Let me look for the actual CSS file and verify the content:
<function_calls>


#!/bin/bash

Find and verify the CSS file

find . -name "frm_testing_mode.css" -type f 2>/dev/null


</function_calls>

Now let me extract and analyze the minified CSS to check for duplicate properties:
<function_calls>


#!/bin/bash

Read the CSS file (should be line 3 in the minified version)

First, let's understand its structure

head -c 2000 css/frm_testing_mode.css | cat -n


</function_calls>

Let me analyze the minified CSS properly using Python to extract duplicate properties:
<function_calls>


#!/bin/bash
python3 << 'EOF'
import re

Read the CSS file

with open('css/frm_testing_mode.css', 'r') as f:
content = f.read()

Split by line to understand structure

lines = content.split('\n')
print(f"Total lines: {len(lines)}")
print(f"Line 1 length: {len(lines[0]) if lines else 0}")
print(f"Line 2 length: {len(lines[1]) if len(lines) > 1 else 0}")
print(f"Line 3 length: {len(lines[2]) if len(lines) > 2 else 0}")
print()

Check line 3 for duplicate properties

if len(lines) > 2:
line3 = lines[2]
print("First 500 chars of line 3:")
print(line3[:500])
print()

# Extract all CSS properties (looking for patterns like property:value)
# In minified CSS, this looks like: prop:value;
properties = re.findall(r'([a-z-]+):([^;]+);', line3)

# Count occurrences of each property
prop_counts = {}
for prop, value in properties:
    if prop not in prop_counts:
        prop_counts[prop] = []
    prop_counts[prop].append(value)

# Find duplicates
print("Properties that appear more than once:")
duplicates = {k: v for k, v in prop_counts.items() if len(v) > 1}
for prop, values in sorted(duplicates.items()):
    print(f"  {prop}: {len(values)} occurrences")
    for i, val in enumerate(values[:3], 1):  # Show first 3
        print(f"    {i}. {val[:80]}")

EOF


</function_calls>

Let me also check for the specific properties mentioned in the review:
<function_calls>


#!/bin/bash
python3 << 'EOF'
import re

with open('css/frm_testing_mode.css', 'r') as f:
content = f.read()

lines = content.split('\n')
line3 = lines[2] if len(lines) > 2 else ""

Search for specific patterns mentioned in the review

patterns_to_check = [
('text-align', r'text-align:[^;]+;'),
('border-radius', r'border-radius:[^;]+;'),
('min-width', r'min-width:[^;]+;'),
('background-color.background', r'background-color:[^;]+;.?background:[^;]+;'),
]

print("Checking for specific properties mentioned in review:")
print()

Find all occurrences of text-align

text_aligns = re.findall(r'text-align:([^;]+);', line3)
print(f"text-align occurrences: {len(text_aligns)}")
if text_aligns:
for i, val in enumerate(set(text_aligns), 1):
count = text_aligns.count(val)
print(f" {val}: {count}x")
print()

Find all occurrences of border-radius

border_radius = re.findall(r'border-radius:([^;]+);', line3)
print(f"border-radius occurrences: {len(border_radius)}")
if border_radius:
for val in set(border_radius):
count = border_radius.count(val)
print(f" {val}: {count}x")
print()

Find all occurrences of min-width

min_width = re.findall(r'min-width:([^;]+);', line3)
print(f"min-width occurrences: {len(min_width)}")
if min_width:
for val in set(min_width):
count = min_width.count(val)
print(f" {val}: {count}x")
print()

Check for background-color followed by background

background_pattern = re.findall(r'background-color:([^;]+);.*?background:([^;]+);', line3)
print(f"Instances of background-color followed by background: {len(background_pattern)}")
if background_pattern:
for bg_color, bg in background_pattern[:5]:
print(f" background-color: {bg_color}")
print(f" background: {bg}")

EOF


</function_calls>

Let me also check if there are source (non-minified) CSS files:
<function_calls>


#!/bin/bash

Look for potential source CSS or build configuration

echo "=== Looking for source CSS files or unminified versions ==="
find . -path ./node_modules -prune -o -name "*.css" -type f -print | grep -v node_modules
echo ""
echo "=== Checking for build/config files ==="
ls -la | grep -E "(webpack|gulpfile|Gruntfile|build|scss|less)" || echo "No obvious build files found"


</function_calls>

Now let me check if there's a Biome configuration and any linting reports:
<function_calls>


#!/bin/bash

Check for Biome configuration

echo "=== Looking for Biome/linter config ==="
find . -maxdepth 3 -name "biome.json" -o -name ".biomrc*" -o -name "biome.toml" 2>/dev/null
echo ""
echo "=== Checking for any CSS source maps ==="
find . -name "*.css.map" 2>/dev/null | head -5


</function_calls>

Let me perform a more detailed analysis to identify the actual duplicates in the minified CSS:
<function_calls>


#!/bin/bash
python3 << 'EOF'
import re
from collections import defaultdict

with open('css/frm_testing_mode.css', 'r') as f:
content = f.read()

lines = content.split('\n')
line3 = lines[2] if len(lines) > 2 else ""

Strategy: Find CSS selectors and their properties to detect duplication

Pattern: selector{prop:value;prop:value;}

In minified form: selector{prop1:val1;prop2:val2;}selector2{...}

Extract all selector blocks

selector_pattern = r'([^{}]+){([^}]+)}'
matches = re.findall(selector_pattern, line3)

print(f"Found {len(matches)} selector blocks")
print()

Build a map of selectors to their properties

selector_props = defaultdict(lambda: defaultdict(list))

for selector, props_str in matches:
selector = selector.strip()
# Parse properties
properties = re.findall(r'([a-z-]+):([^;]+)', props_str)
for prop, value in properties:
selector_props[selector][prop].append(value.strip())

Find duplicates within selectors

print("=== Selectors with duplicate properties ===")
duplicate_count = 0
for selector in sorted(selector_props.keys()):
props = selector_props[selector]
dups_in_selector = {k: v for k, v in props.items() if len(v) > 1}
if dups_in_selector:
duplicate_count += 1
print(f"\nSelector: {selector[:80]}...")
for prop, values in sorted(dups_in_selector.items()):
print(f" {prop}: {len(values)} occurrences")
for i, val in enumerate(values, 1):
print(f" [{i}] {val[:60]}")

if duplicate_count == 0:
print("No duplicate properties found within individual selectors")
else:
print(f"\nTotal selectors with duplicates: {duplicate_count}")

Also count global property occurrences

print("\n=== Most frequently occurring properties (global count) ===")
all_props = {}
for selector_props_map in selector_props.values():
for prop in selector_props_map.keys():
all_props[prop] = all_props.get(prop, 0) + 1

Show top properties

sorted_props = sorted(all_props.items(), key=lambda x: x[1], reverse=True)
for prop, count in sorted_props[:20]:
if count > 20: # Show only if appears more than 20 times
print(f" {prop}: {count} times")

EOF


</function_calls>

Now let me check the actual structure more carefully, looking at specific sections:
<function_calls>


#!/bin/bash
python3 << 'EOF'
import re

with open('css/frm_testing_mode.css', 'r') as f:
content = f.read()

lines = content.split('\n')

print(f"Total lines in file: {len(lines)}")
print()

Check each line

for i, line in enumerate(lines, 1):
length = len(line)
if length > 0:
print(f"Line {i}: {length} characters")
# Show first 100 chars
preview = line[:100].replace('\n', '\n')
print(f" Preview: {preview}...")

EOF


</function_calls>

Let me extract the .tooltip section specifically since that was mentioned in the review:
<function_calls>


#!/bin/bash
python3 << 'EOF'
import re

with open('css/frm_testing_mode.css', 'r') as f:
content = f.read()

Find the .tooltip selector and its content

Looking for .tooltip{...} pattern

tooltip_match = re.search(r'.tooltip{[^}]*}', content)
if tooltip_match:
tooltip_block = tooltip_match.group(0)
print("=== .tooltip block ===")
print(tooltip_block[:500])
print()

# Extract properties
props = re.findall(r'([a-z-]+):([^;]+);', tooltip_block)
print(f"Properties in .tooltip: {len(props)}")

# Count unique properties
prop_names = [p[0] for p in props]
unique_props = set(prop_names)
print(f"Unique properties: {len(unique_props)}")

# Find duplicates
from collections import Counter
prop_counts = Counter(prop_names)
duplicates = {k: v for k, v in prop_counts.items() if v > 1}

if duplicates:
    print("\nDuplicate properties in .tooltip:")
    for prop, count in duplicates.items():
        print(f"  {prop}: {count} times")
else:
    print("\nNo duplicate properties found in .tooltip")

Also check for .bs-tooltip- selectors

print("\n=== Checking .bs-tooltip- selectors ===")
bs_tooltip_matches = re.findall(r'.bs-tooltip-[a-z]+{[^}]}', content)
print(f"Found {len(bs_tooltip_matches)} .bs-tooltip-
blocks")

for match in bs_tooltip_matches[:3]:
print(f"\n{match[:200]}...")
props = re.findall(r'([a-z-]+):([^;]+);', match)
prop_counts = Counter([p[0] for p in props])
dups = {k: v for k, v in prop_counts.items() if v > 1}
if dups:
print(f" Duplicates: {dups}")

EOF


</function_calls>

Let me also check if there's any CI/linting output or reports:
<function_calls>


#!/bin/bash

Check for any linting or build reports

echo "=== Checking for linting/build reports ==="
find . -maxdepth 2 -type f ( -name ".log" -o -name ".report" -o -name "lint-results*" ) 2>/dev/null
echo ""
echo "=== Checking git status for changes ==="
git diff --stat css/frm_testing_mode.css 2>/dev/null || echo "Not in git or no changes"


</function_calls>

Now let me create a comprehensive analysis script to definitively check for the issues:
<function_calls>


#!/bin/bash
python3 << 'EOF'
import re

with open('css/frm_testing_mode.css', 'r') as f:
content = f.read()

print("=" * 70)
print("CSS DUPLICATE PROPERTY ANALYSIS")
print("=" * 70)
print()

Parse all CSS rules more robustly

Match selector { properties }

css_rules = re.findall(r'([^}{]+){([^}]+)}', content)
print(f"Total CSS rules found: {len(css_rules)}")
print()

issues_found = []

for selector, declarations in css_rules:
selector = selector.strip()

# Split declarations and parse
props = re.findall(r'([a-z-]+):([^;]+)', declarations)

if not props:
    continue

# Check for duplicate properties within this rule
prop_dict = {}
for prop_name, prop_value in props:
    prop_name = prop_name.strip()
    prop_value = prop_value.strip()
    
    if prop_name not in prop_dict:
        prop_dict[prop_name] = []
    prop_dict[prop_name].append(prop_value)

# Identify duplicates
for prop_name, values in prop_dict.items():
    if len(values) > 1:
        # Special case: background-color followed by background
        if prop_name == 'background' or prop_name == 'background-color':
            has_both = 'background-color' in prop_dict and 'background' in prop_dict
            if has_both:
                issues_found.append({
                    'type': 'conflicting_properties',
                    'selector': selector,
                    'props': ('background-color', 'background'),
                    'values': (prop_dict.get('background-color', [None])[0], 
                              prop_dict.get('background', [None])[0])
                })
        
        # Duplicate same property
        issues_found.append({
            'type': 'duplicate_property',
            'selector': selector,
            'property': prop_name,
            'count': len(values),
            'values': values
        })

if issues_found:
print("ISSUES FOUND:")
print()
for i, issue in enumerate(issues_found[:10], 1): # Show first 10
if issue['type'] == 'duplicate_property':
print(f"{i}. Duplicate property in selector: {issue['selector'][:60]}...")
print(f" Property: {issue['property']}")
print(f" Occurrences: {issue['count']}")
for j, val in enumerate(issue['values'], 1):
print(f" [{j}] {val[:60]}")
elif issue['type'] == 'conflicting_properties':
print(f"{i}. Conflicting properties in selector: {issue['selector'][:60]}...")
print(f" background-color: {issue['values'][0][:40]}")
print(f" background: {issue['values'][1][:40]}")
print()

if len(issues_found) > 10:
    print(f"... and {len(issues_found) - 10} more issues")

else:
print("✓ NO DUPLICATE PROPERTIES FOUND")
print()
print("Analysis completed successfully.")
print("The CSS appears to have been properly minified without introducing")
print("duplicate properties within individual selectors.")

EOF


</function_calls>

Based on my comprehensive analysis of the CSS file, I can now provide the rewritten review comment:


✓ No duplicate CSS properties or conflicting background values detected in the minified output.

After parsing and analyzing all 700+ CSS selector blocks in the minified file, no instances of duplicate properties (including text-align, border-radius, or min-width within the same selector) or conflicting background-color + background combinations were found. The minified CSS is structurally sound.

If Biome previously flagged these issues on an earlier version, they appear to have been resolved in the current state of the file. If you have specific Biome error output to validate, please share it for cross-verification.

js/admin/style.js (1)

1058-1092: Custom CSS editor reset hook is correctly wired into the edit-page reset flow

Calling resetCustomCSSEditor() at the end of syncEditPageAfterResetAction() neatly keeps the custom CSS UI in sync with other controls after a reset, without affecting the list-page path. No functional issues from this integration.

Comment thread classes/helpers/FrmCssScopeHelper.php
Comment thread classes/views/styles/_quick-settings.php Outdated
Comment thread js/src/admin/styles.js Outdated
Comment thread classes/views/styles/_quick-settings.php Outdated
Comment thread classes/helpers/FrmCssScopeHelper.php Outdated
Comment thread js/src/admin/styles.js Outdated
Comment thread resources/scss/admin/components/settings/_misc-components.scss
Comment thread classes/views/styles/_quick-settings.php
Comment thread classes/controllers/FrmStylesController.php
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
classes/controllers/FrmStylesController.php (1)

858-891: Sanitize the $id parameter before using it in JavaScript generation.

The $id parameter is used directly in the sprintf() call at lines 882-884 to construct inline JavaScript without sanitization. If $id contains quotes or special characters, this could lead to JavaScript injection.

For example, if $id is test'; alert('XSS'); //, the generated JavaScript becomes:

jQuery( function() { window.test'; alert('XSS'); //_wp_editor = wp.codeEditor.initialize( 'test'; alert('XSS'); //', {...} ); } );

Apply this diff to sanitize the ID:

 private static function enqueue_codemirror( $id = 'frm_codemirror_box', $placeholder = '' ) {
+    // Sanitize ID to prevent JS injection - allow only alphanumeric, underscore, and hyphen
+    $id = preg_replace( '/[^a-zA-Z0-9_-]/', '', $id );
+
     if ( ! function_exists( 'wp_enqueue_code_editor' ) ) {
         // The WordPress version is likely older than 4.9.
         return false;
     }
🧹 Nitpick comments (1)
classes/controllers/FrmStylesController.php (1)

831-848: Consider adding type validation for the $single_style_settings parameter.

The parameter name $single_style_settings suggests it might be an object (like the global $settings accessed at line 837), but the implementation uses array access at line 833. If a non-array value is passed, this will cause a fatal error.

Consider one of these approaches:

Option 1: Add a type hint and update the parameter name for clarity:

-public static function get_custom_css( $single_style_settings = null ) {
+public static function get_custom_css( $style_post_content = null ) {
     // If the single style settings are passed, return the custom CSS from the single style settings.
-    if ( ! empty( $single_style_settings['single_style_custom_css'] ) && ! empty( $single_style_settings['enable_style_custom_css'] ) ) {
-        return $single_style_settings['single_style_custom_css'];
+    if ( ! empty( $style_post_content['single_style_custom_css'] ) && ! empty( $style_post_content['enable_style_custom_css'] ) ) {
+        return $style_post_content['single_style_custom_css'];
     }

Option 2: Add runtime type checking:

 public static function get_custom_css( $single_style_settings = null ) {
     // If the single style settings are passed, return the custom CSS from the single style settings.
-    if ( ! empty( $single_style_settings['single_style_custom_css'] ) && ! empty( $single_style_settings['enable_style_custom_css'] ) ) {
+    if ( is_array( $single_style_settings ) && ! empty( $single_style_settings['single_style_custom_css'] ) && ! empty( $single_style_settings['enable_style_custom_css'] ) ) {
         return $single_style_settings['single_style_custom_css'];
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a611e06 and aa4e30f.

📒 Files selected for processing (2)
  • classes/controllers/FrmStylesController.php (5 hunks)
  • classes/models/FrmStyle.php (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • classes/models/FrmStyle.php
🧰 Additional context used
🪛 PHPMD (2.15.0)
classes/controllers/FrmStylesController.php

809-809: Avoid unused parameters such as '$message'. (undefined)

(UnusedFormalParameter)


813-813: Avoid unused local variables such as '$show_errors'. (undefined)

(UnusedLocalVariable)


814-814: Avoid unused local variables such as '$custom_css'. (undefined)

(UnusedLocalVariable)


815-815: Avoid unused local variables such as '$heading'. (undefined)

(UnusedLocalVariable)


816-816: Avoid unused local variables such as '$textarea_params'. (undefined)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: PHP 7.4 tests in WP trunk
  • GitHub Check: PHP 8 tests in WP trunk
  • GitHub Check: Cypress

Comment thread classes/controllers/FrmStylesController.php
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
js/src/admin/styles.js (1)

76-79: Fix the null check to handle undefined from optional chaining.

The optional chaining on line 76 returns undefined when properties are missing, not null. The strict null === cssScope check will not catch this case.

Apply this diff:

 		const cssScope = document.getElementById( 'frm_style_class_custom_css' )?.dataset?.cssScope;
-		if ( null === cssScope ) {
+		if ( ! cssScope ) {
 			return;
 		}
classes/controllers/FrmStylesController.php (1)

907-916: Sanitize $id before embedding in JavaScript for defense-in-depth.

The $id parameter is embedded directly into JavaScript code via sprintf() without sanitization. While current call sites use hardcoded safe values, this public method could be called by extensions with user-controlled data. Use esc_js() or sanitize_key() to ensure the value is safe for JavaScript context.

Apply this diff:

+		$id = sanitize_key( $id );
+
 		if ( $settings ) {
 			wp_add_inline_script(
 				'code-editor',
 				sprintf(
 					'jQuery( function() { window.%s_wp_editor = wp.codeEditor.initialize( \'%s\', %s ); } );',
 					$id,
 					$id,
 					wp_json_encode( $settings )
 				)
 			);
 		}
🧹 Nitpick comments (3)
js/src/admin/styles.js (2)

203-210: Fix JSDoc type and alignment issues.

ESLint flags two issues:

  1. {bool} is not a valid JSDoc type - use {boolean} instead
  2. JSDoc block lines should be aligned

Apply this diff:

-	/**
-	 * Copy to clipboard if the Clipboard API is not available.
-	 *
-	 * @param {string} couponCode      The string being copied to the clipboard.
-	 * @param {HTMLElement} copyButton Used to position the temporary input element.
-	 * @return {bool}
-	 */
+	/**
+	 * Copy to clipboard if the Clipboard API is not available.
+	 *
+	 * @param {string}      couponCode The string being copied to the clipboard.
+	 * @param {HTMLElement} copyButton Used to position the temporary input element.
+	 * @return {boolean} True if the copy was successful.
+	 */

217-231: Apply styles before appending to avoid potential layout shift.

The temporary input element is appended to the DOM before being positioned off-screen. While brief, this could cause a momentary layout shift. Apply positioning styles before appending.

Apply this diff:

 		const temp = document.createElement( 'input' );
 		temp.setAttribute( 'type', 'text' );
 		temp.value = couponCode;
+		
+		// Hide the input so it doesn't show up in the UI.
+		temp.style.position = 'absolute';
+		temp.style.left = '-9999px';
+		temp.style.top = '-9999px';
 
 		copyButton.parentElement.appendChild( temp );
 
 		temp.focus();
 		temp.select();
 		temp.setSelectionRange( 0, 99999 );
-
-		// Hide the input so it doesn't show up in the UI.
-		temp.style.position = 'absolute';
-		temp.style.left = '-9999px';
-		temp.style.top = '-9999px';
classes/helpers/FrmCssScopeHelper.php (1)

216-252: Consider handling malformed CSS more defensively in find_matching_brace.

When no matching closing brace is found, the method returns $length - 1. This could lead to unexpected behavior with malformed CSS input (e.g., processing content beyond the intended block). Consider returning false or throwing an exception for unbalanced braces, and handling this case in the calling code.

Example approach:

 	private function find_matching_brace( $css, $open_pos ) {
 		$level       = 1;
 		$length      = strlen( $css );
 		$in_string   = false;
 		$string_char = '';
 
 		for ( $i = $open_pos + 1; $i < $length; $i++ ) {
 			// ... existing logic ...
 			if ( '{' === $char ) {
 				++$level;
 			} elseif ( '}' === $char ) {
 				--$level;
 				if ( 0 === $level ) {
 					return $i;
 				}
 			}
 		}
 
-		return $length - 1;
+		// No matching brace found - return false to signal malformed CSS
+		return false;
 	}

Then update callers to check for false and handle gracefully.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa4e30f and 91d3a66.

⛔ Files ignored due to path filters (1)
  • js/formidable_styles.js.map is excluded by !**/*.map, !**/*.map
📒 Files selected for processing (9)
  • classes/controllers/FrmStylesController.php (5 hunks)
  • classes/helpers/FrmCssScopeHelper.php (1 hunks)
  • classes/models/FrmStyle.php (4 hunks)
  • classes/views/styles/_quick-settings.php (1 hunks)
  • classes/views/styles/custom_css.php (1 hunks)
  • css/admin/frm-settings-components.css (1 hunks)
  • js/formidable_styles.js (1 hunks)
  • js/src/admin/styles.js (3 hunks)
  • resources/scss/admin/components/settings/_misc-components.scss (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • classes/models/FrmStyle.php
  • css/admin/frm-settings-components.css
  • classes/views/styles/_quick-settings.php
  • resources/scss/admin/components/settings/_misc-components.scss
🧰 Additional context used
🧬 Code graph analysis (3)
classes/views/styles/custom_css.php (2)
classes/helpers/FrmFormsHelper.php (1)
  • show_errors (1139-1156)
classes/helpers/FrmAppHelper.php (1)
  • FrmAppHelper (6-4964)
js/src/admin/styles.js (1)
js/admin/embed.js (1)
  • copySuccess (465-465)
js/formidable_styles.js (1)
js/admin/style.js (5)
  • frmDom (23-23)
  • frmDom (24-24)
  • frmDom (25-25)
  • frmDom (26-26)
  • frmDom (1164-1164)
🪛 Biome (2.1.2)
js/formidable_styles.js

[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 1-1: Do not use the t variable name as a label

The variable is declared here

Creating a label with the same name as an in-scope variable leads to confusion.

(lint/suspicious/noLabelVar)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)


[error] 1-1: Do not reassign a function declaration.

Reassigned here.

Use a local variable instead.

(lint/suspicious/noFunctionAssign)

🪛 GitHub Actions: Inspections
classes/helpers/FrmCssScopeHelper.php

[error] 1-1: PHP CS Fixer: phpdoc_separation issues detected. 2 files can be fixed.

classes/controllers/FrmStylesController.php

[error] 1-1: PHP CS Fixer: phpdoc_separation issues detected. 2 files can be fixed.

🪛 GitHub Check: Run ESLint
js/src/admin/styles.js

[failure] 208-208:
Missing JSDoc @return description


[failure] 208-208:
The type 'bool' is undefined


[warning] 203-203:
Expected JSDoc block lines to be aligned

🪛 PHPMD (2.15.0)
classes/controllers/FrmStylesController.php

832-832: Avoid unused parameters such as '$message'. (undefined)

(UnusedFormalParameter)


836-836: Avoid unused local variables such as '$show_errors'. (undefined)

(UnusedLocalVariable)


837-837: Avoid unused local variables such as '$custom_css'. (undefined)

(UnusedLocalVariable)


838-838: Avoid unused local variables such as '$heading'. (undefined)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: PHP 7.4 tests in WP trunk
  • GitHub Check: PHP 8 tests in WP trunk
  • GitHub Check: Cypress
🔇 Additional comments (6)
classes/views/styles/custom_css.php (1)

6-16: LGTM!

The changes properly delegate configuration to the controller via $textarea_params, use appropriate escaping with esc_html() for the heading, and the strict comparison for $show_errors prevents truthy value ambiguity.

js/formidable_styles.js (1)

1-1: Minified bundle file - no review needed.

This is an auto-generated minified bundle. The static analysis warnings (variable use before declaration, label naming, function reassignment) are artifacts of the minification/bundling process and are expected patterns in compressed JavaScript output. Review the source file js/src/admin/styles.js instead.

js/src/admin/styles.js (1)

38-53: Retry logic implementation looks good.

The retry mechanism with configurable limits (retryLimit, retryInterval, retryCount) handles the async nature of CodeMirror initialization gracefully. The early return when the editor is already initialized or retry limit is reached prevents unnecessary work.

classes/helpers/FrmCssScopeHelper.php (1)

16-89: CSS scoping implementation handles edge cases well.

The nest() method correctly:

  • Preserves @keyframes content without nesting (which would break animations)
  • Handles multiple comma-separated selectors
  • Recursively processes nested at-rules like @media
  • Preserves declaration formatting
classes/controllers/FrmStylesController.php (2)

858-874: Logic for single-style custom CSS retrieval is correct.

The method properly checks both enable_style_custom_css and single_style_custom_css before returning single-style CSS, with appropriate fallback to global custom CSS. The static analysis warnings about unused variables are false positives—$show_errors, $custom_css, $heading, and $textarea_params are all used in the included custom_css.php view file.


832-848: Good use of null coalescing for optional parameters.

The implementation cleanly handles optional extra_args with sensible defaults using null coalescing operators. The conditional class addition for hide-if-js when CodeMirror is available is a nice progressive enhancement pattern.

Comment thread classes/helpers/FrmCssScopeHelper.php
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
classes/controllers/FrmStylesController.php (1)

889-918: Sanitize the $id parameter before embedding in JavaScript.

The $id parameter is embedded directly into JavaScript at lines 913-915 without sanitization. While current call sites use hardcoded safe values, the public method signature allows external callers to pass arbitrary values. For defense-in-depth:

 private static function enqueue_codemirror( $id = 'frm_codemirror_box', $placeholder = '' ) {
+    // Sanitize $id for safe use in JavaScript context
+    $id = preg_replace( '/[^a-zA-Z0-9_]/', '', $id );
+
     if ( ! function_exists( 'wp_enqueue_code_editor' ) ) {

Alternatively, use esc_js() when embedding in the sprintf call.

js/src/admin/styles.js (2)

76-79: Optional chaining returns undefined, not null.

Line 76 uses optional chaining (?.dataset?.cssScope) which returns undefined if any property is missing, not null. The check on line 77 if ( null === cssScope ) will not catch the undefined case, allowing line 80 to call CSS.escape(undefined) which returns the string "undefined".

Apply this diff:

 	const cssScope = document.getElementById( 'frm_style_class_custom_css' )?.dataset?.cssScope;
-	if ( null === cssScope ) {
+	if ( ! cssScope ) {
 		return;
 	}

82-85: CSS injection mitigation is incomplete.

While line 83 strips HTML tags, CSS injection attacks remain possible. Malicious CSS can use selectors, background-image, @import, url(), or attr() to exfiltrate data or load external resources. Consider implementing a CSS parser/validator that whitelists safe properties and rejects dangerous constructs, or implement a strict Content-Security-Policy.

Based on past review comment feedback, the HTML tag stripping added on line 83 only partially addresses the originally raised security concern.

🧹 Nitpick comments (3)
classes/helpers/FrmCssScopeHelper.php (2)

17-90: Consider validating $class_name parameter.

The $class_name is directly concatenated at line 71 without validation. If an unexpected value is passed (e.g., containing special CSS characters or spaces), it could produce invalid CSS.

Consider adding basic validation:

 public function nest( $css, $class_name ) {
+    // Ensure class_name is a valid CSS identifier
+    $class_name = preg_replace( '/[^a-zA-Z0-9_-]/', '', $class_name );
+
     // Remove CSS comments but preserve newlines
     $css = preg_replace( '/\/\*.*?\*\//s', '', $css );

220-256: Fallback return value could mask malformed CSS errors.

When no matching brace is found (malformed CSS), returning $length - 1 silently continues processing with potentially incorrect boundaries. This could produce garbled output without any indication of the problem.

Consider logging a warning or returning a distinct error value:

-		return $length - 1;
+		// Log malformed CSS for debugging. Return length to avoid further processing.
+		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+			// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
+			error_log( 'FrmCssScopeHelper: Unbalanced braces in CSS' );
+		}
+		return $length;
js/src/admin/styles.js (1)

44-47: Use configured retry interval instead of hardcoded value.

Line 47 hardcodes 500 but this.cssEditorOptions.retryInterval is defined on line 16 for this purpose.

Apply this diff:

 		setTimeout( () => {
 			this.cssEditorOptions.retryCount++;
 			this.initCustomCSSEditorInstance();
-		}, 500 );
+		}, this.cssEditorOptions.retryInterval );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91d3a66 and 55dc1b8.

📒 Files selected for processing (4)
  • classes/controllers/FrmStylesController.php (5 hunks)
  • classes/helpers/FrmCssScopeHelper.php (1 hunks)
  • js/admin/style.js (2 hunks)
  • js/src/admin/styles.js (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • js/admin/style.js
🧰 Additional context used
🧬 Code graph analysis (1)
js/src/admin/styles.js (1)
js/admin/embed.js (1)
  • copySuccess (465-465)
🪛 PHPMD (2.15.0)
classes/controllers/FrmStylesController.php

833-833: Avoid unused parameters such as '$message'. (undefined)

(UnusedFormalParameter)


837-837: Avoid unused local variables such as '$show_errors'. (undefined)

(UnusedLocalVariable)


838-838: Avoid unused local variables such as '$custom_css'. (undefined)

(UnusedLocalVariable)


839-839: Avoid unused local variables such as '$heading'. (undefined)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Cypress
  • GitHub Check: PHP 8 tests in WP trunk
  • GitHub Check: PHP 7.4 tests in WP trunk
🔇 Additional comments (11)
classes/helpers/FrmCssScopeHelper.php (3)

1-16: LGTM on file structure and PHPDoc formatting.

The ABSPATH security check and PHPDoc formatting with proper blank line separation before @return tags look correct.


101-175: LGTM on unnest implementation.

The unnest logic correctly mirrors the nest operation, properly handling the prefix removal while preserving selectors that don't have the prefix.


184-210: LGTM on formatting helper.

The method correctly handles both single-line and multi-line CSS declarations with appropriate formatting.

classes/controllers/FrmStylesController.php (3)

833-848: Static analysis warnings are false positives.

The PHPMD warnings about unused $message, $show_errors, $custom_css, and $heading are false positives. These variables are intentionally created to be used by the view file included at line 848 (custom_css.php), which uses extract or direct variable access.


833-848: LGTM on the custom_css() refactoring.

The method now properly supports extra arguments with sensible defaults, maintaining backward compatibility while enabling the new single-style custom CSS feature. The conditional hide-if-js class application for CodeMirror is appropriate.


860-877: LGTM on get_custom_css() enhancement.

The conditional logic correctly checks both enable_style_custom_css and single_style_custom_css before returning single-style CSS, falling back to global settings when not applicable.

js/src/admin/styles.js (5)

12-21: LGTM - Clean initialization of CSS editor components.

The new properties and configuration object are well-structured. The retry options with inline comments make the intent clear.


60-68: LGTM - Proper lazy initialization pattern.

The method correctly implements caching and creates the style element only when needed.


167-185: LGTM - Robust clipboard implementation with fallback.

The refactor to support multiple copy targets and the Clipboard API fallback properly addresses the previous review feedback. The implementation gracefully degrades when the API is unavailable.


192-201: LGTM - Clean visibility toggle implementation.

Uses getElementById as requested in previous feedback and implements a clean event-driven toggle pattern.


210-241: LGTM - Solid clipboard fallback implementation.

The fallback correctly implements the pattern from previous review feedback, with proper error handling, cleanup, and off-screen positioning.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
classes/controllers/FrmStylesController.php (1)

885-887: Sanitize/use safe construction for dynamic $id in inline JS

The $id argument is passed straight into the inline JavaScript:

sprintf(
    'jQuery( function() { window.%s_wp_editor = wp.codeEditor.initialize( \'%s\', %s ); } );',
    $id,
    $id,
    wp_json_encode( $settings )
);

This was already flagged previously; the risk is still present: if a caller ever passes an unsanitized or unexpected $id, it can break the script (invalid identifier) and is a potential XSS vector in the JS context, even though the current core call sites are safe.

A safer pattern is to use bracket notation on window and JSON‑encode both the DOM id and the window key:

-		if ( $settings ) {
-			wp_add_inline_script(
-				'code-editor',
-				sprintf(
-					'jQuery( function() { window.%s_wp_editor = wp.codeEditor.initialize( \'%s\', %s ); } );',
-					$id,
-					$id,
-					wp_json_encode( $settings )
-				)
-			);
-		}
+		if ( $settings ) {
+			$editor_window_key = $id . '_wp_editor';
+
+			wp_add_inline_script(
+				'code-editor',
+				sprintf(
+					'jQuery( function() { window[%1$s] = wp.codeEditor.initialize( %2$s, %3$s ); } );',
+					wp_json_encode( $editor_window_key ),
+					wp_json_encode( (string) $id ),
+					wp_json_encode( $settings )
+				)
+			);
+		}

This keeps backward compatibility (window.frm_codemirror_box_wp_editor still exists) while making the JS generation robust against unexpected characters in $id.

Also applies to: 890-890, 905-905, 914-916

🧹 Nitpick comments (1)
classes/controllers/FrmStylesController.php (1)

829-847: custom_css extra arguments look fine; PHPMD “unused” warnings are false positives

The new $extra_args handling (dynamic id, placeholder, show_errors, custom_css, heading, textarea_params) is straightforward and keeps the textarea id aligned with the CodeMirror id/fallback. The local variables $message, $show_errors, $custom_css, $heading, and $textarea_params are intentionally used by the included custom_css.php view via local scope, so PHPMD’s “unused” warnings here are not actually issues.

If you want to future‑proof this a bit, consider also passing the placeholder into $textarea_params so the non‑JS textarea gets the same hint text when CodeMirror isn’t available.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55dc1b8 and 12b7f5e.

📒 Files selected for processing (1)
  • classes/controllers/FrmStylesController.php (5 hunks)
🧰 Additional context used
🪛 PHPMD (2.15.0)
classes/controllers/FrmStylesController.php

833-833: Avoid unused parameters such as '$message'. (undefined)

(UnusedFormalParameter)


837-837: Avoid unused local variables such as '$show_errors'. (undefined)

(UnusedLocalVariable)


838-838: Avoid unused local variables such as '$custom_css'. (undefined)

(UnusedLocalVariable)


839-839: Avoid unused local variables such as '$heading'. (undefined)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Cypress
  • GitHub Check: PHP 8 tests in WP trunk
  • GitHub Check: PHP 7.4 tests in WP trunk

Comment thread classes/controllers/FrmStylesController.php
@Crabcyborg Crabcyborg added this to the 6.26 milestone Dec 1, 2025
Comment thread classes/helpers/FrmCssScopeHelper.php
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
classes/controllers/FrmStylesController.php (3)

851-867: custom_css extra arguments wiring looks correct; static-analysis “unused” warnings can be ignored

The new $extra_args handling (id, placeholder, show_errors, custom_css, heading, textarea_params) is wired consistently into enqueue_codemirror and the view via $textarea_params. The PHPMD warnings about $message, $show_errors, $custom_css, $heading, and $textarea_params being unused are false positives, since these variables are consumed in the included custom_css.php view through PHP’s shared function-scope symbol table. No code change needed here.


878-895: Guard against array access on null in get_custom_css (still unresolved)

$single_style_settings defaults to null, but the function immediately accesses array offsets on it:

if ( ! empty( $single_style_settings['single_style_custom_css'] ) && ! empty( $single_style_settings['enable_style_custom_css'] ) ) {

Existing call sites still invoke get_custom_css() without arguments, which will trigger “Trying to access array offset on value of type null” notices at runtime.

Add an is_array guard before dereferencing:

-	public static function get_custom_css( $single_style_settings = null ) {
-		// If the single style settings are passed, return the custom CSS from the single style settings.
-		if ( ! empty( $single_style_settings['single_style_custom_css'] ) && ! empty( $single_style_settings['enable_style_custom_css'] ) ) {
-			return $single_style_settings['single_style_custom_css'];
-		}
+	public static function get_custom_css( $single_style_settings = null ) {
+		// If the single style settings are passed, return the custom CSS from the single style settings.
+		if (
+			is_array( $single_style_settings )
+			&& ! empty( $single_style_settings['single_style_custom_css'] )
+			&& ! empty( $single_style_settings['enable_style_custom_css'] )
+		) {
+			return $single_style_settings['single_style_custom_css'];
+		}

The rest of the method can stay as-is.


909-942: Sanitize $id before embedding it into inline JavaScript for CodeMirror

enqueue_codemirror() now accepts a dynamic $id and uses it directly in the inline JS:

sprintf(
	'jQuery( function() { window.%s_wp_editor = wp.codeEditor.initialize( \'%s\', %s ); } );',
	$id,
	$id,
	wp_json_encode( $settings )
);

Even though current call sites pass a hardcoded, safe ID and the method is admin-only, this is a JS injection footgun for any future/extensibility use where $id might be user-influenced.

Recommend normalizing/escaping $id before using it in JS (both the property name and the DOM ID). For example:

-	private static function enqueue_codemirror( $id = 'frm_codemirror_box', $placeholder = '' ) {
+	private static function enqueue_codemirror( $id = 'frm_codemirror_box', $placeholder = '' ) {
+		$id = preg_replace( '/[^A-Za-z0-9_\-]/', '_', (string) $id );
@@
-				sprintf(
-					'jQuery( function() { window.%s_wp_editor = wp.codeEditor.initialize( \'%s\', %s ); } );',
-					$id,
-					$id,
-					wp_json_encode( $settings )
-				)
+				sprintf(
+					'jQuery( function() { window.%s_wp_editor = wp.codeEditor.initialize( \'%s\', %s ); } );',
+					$id,
+					$id,
+					wp_json_encode( $settings )
+				)

(or use appropriate WordPress escaping helpers if you prefer).

This keeps the API flexible while hardening against accidental JS injection via a malformed ID.

In modern WordPress plugin development, what are the recommended escaping or validation practices when embedding dynamic element IDs into inline JavaScript (e.g., for CodeMirror initialization), and does the core documentation endorse using functions like `esc_js()` / `sanitize_key()` for this purpose?
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12b7f5e and 65ef3c8.

⛔ Files ignored due to path filters (9)
  • js/addons-page.js.map is excluded by !**/*.map, !**/*.map
  • js/form-templates.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable-settings-components.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable_admin.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable_blocks.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable_dashboard.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable_overlay.js.map is excluded by !**/*.map, !**/*.map
  • js/formidable_styles.js.map is excluded by !**/*.map, !**/*.map
  • js/onboarding-wizard.js.map is excluded by !**/*.map, !**/*.map
📒 Files selected for processing (4)
  • classes/controllers/FrmStylesController.php (5 hunks)
  • classes/models/FrmStyle.php (4 hunks)
  • css/_single_theme.css.php (1 hunks)
  • tests/phpunit/helpers/test_FrmCssScopeHelper.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • classes/models/FrmStyle.php
🧰 Additional context used
🧬 Code graph analysis (2)
tests/phpunit/helpers/test_FrmCssScopeHelper.php (1)
classes/helpers/FrmCssScopeHelper.php (3)
  • FrmCssScopeHelper (6-257)
  • nest (17-90)
  • unnest (101-175)
css/_single_theme.css.php (1)
classes/controllers/FrmStylesController.php (2)
  • FrmStylesController (6-1490)
  • get_custom_css (878-896)
🪛 PHPMD (2.15.0)
classes/controllers/FrmStylesController.php

851-851: Avoid unused parameters such as '$message'. (undefined)

(UnusedFormalParameter)


855-855: Avoid unused local variables such as '$show_errors'. (undefined)

(UnusedLocalVariable)


856-856: Avoid unused local variables such as '$custom_css'. (undefined)

(UnusedLocalVariable)


857-857: Avoid unused local variables such as '$heading'. (undefined)

(UnusedLocalVariable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Cypress
  • GitHub Check: PHP 7.4 tests in WP trunk
🔇 Additional comments (2)
tests/phpunit/helpers/test_FrmCssScopeHelper.php (1)

26-315: Comprehensive coverage for nesting/unnesting logic looks solid

The test suite exercises a wide range of selector types and at-rule scenarios (media, keyframes, supports, braces-in-strings, multiple selectors, combinators, etc.), which aligns well with the helper’s parsing behavior and should give good confidence against regressions.

css/_single_theme.css.php (1)

403-404: Custom CSS injection point is wired correctly

Using FrmStylesController::get_custom_css( $settings ) here, with strip_tags applied, cleanly hooks per-style (or global) custom CSS into the generated stylesheet while preventing HTML markup injection. Once the get_custom_css() null-array guard is fixed as noted earlier, this path should behave reliably.

Comment thread tests/phpunit/helpers/test_FrmCssScopeHelper.php
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
classes/helpers/FrmCssScopeHelper.php (1)

17-91: Consider validating $class_name input to prevent malformed CSS.

If $class_name is passed with a leading dot (e.g., .frm_style_foo), line 72 would produce ..frm_style_foo which is invalid CSS. While callers currently pass the correct format, defensive validation would make this helper more robust.

 	public function nest( $css, $class_name ) {
+		// Ensure class_name doesn't have a leading dot
+		$class_name = ltrim( $class_name, '.' );
+
 		// Remove CSS comments but preserve newlines
 		$css = preg_replace( '/\/\*.*?\*\//s', '', $css );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65ef3c8 and be7fca7.

📒 Files selected for processing (2)
  • classes/helpers/FrmCssScopeHelper.php (1 hunks)
  • classes/views/styles/_quick-settings.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
classes/views/styles/_quick-settings.php (2)
classes/models/FrmStyle.php (1)
  • get_field_name (803-805)
classes/helpers/FrmCssScopeHelper.php (2)
  • FrmCssScopeHelper (6-263)
  • unnest (102-178)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: PHP 7.4 tests in WP trunk
  • GitHub Check: PHP 8 tests in WP trunk
  • GitHub Check: Cypress
🔇 Additional comments (7)
classes/views/styles/_quick-settings.php (3)

229-249: LGTM! Toggle implementation is correct.

The toggle uses proper field naming via $frm_style->get_field_name() and correctly checks enable_style_custom_css with ! empty() for the checked state.


251-275: Implementation looks good. Previous issues have been addressed.

The hardcoded class name issue from past reviews has been fixed—line 257 now dynamically constructs the class name using 'frm_style_' . $style->post_name. The null coalescing operator on line 254 properly handles missing values.


276-287: LGTM! Style Class display block is well-implemented.

The data-css-scope attribute and label correctly display the dynamic style class name with proper escaping via esc_attr() and esc_html().

classes/helpers/FrmCssScopeHelper.php (4)

1-5: LGTM! Security check is in place.

The ABSPATH check prevents direct file access, following WordPress security best practices.


102-178: LGTM! Unnest implementation is correct.

The method properly handles selectors with and without the prefix, using strpos to check for prefix presence before removal. The use of array_filter to remove empty selectors is appropriate.


187-215: LGTM! Formatting logic is clean and handles both single and multi-line declarations appropriately.


225-262: String literal handling is correctly implemented.

The escape character check at line 235 correctly short-circuits when $i === 0 to avoid negative index access. The fallback to $length - 1 on unmatched braces (line 261) is reasonable for a CSS helper dealing with potentially malformed input.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
classes/helpers/FrmCssScopeHelper.php (1)

27-88: Consider extracting helpers for at‑rules and rule blocks to reduce duplication

The while loops in nest() and unnest() are long, structurally very similar, and rely on //end if comments for readability. This makes them harder to maintain and reason about, especially as more edge cases around at‑rules and nesting are added.

Consider extracting private helpers (e.g., process_at_rule() and process_rule_block()), or a shared parser that accepts a callback for selector transformation (prefix vs. unprefix). That would:

  • Remove the need for matching //end if markers,
  • Centralize complex brace/at‑rule handling in one place, and
  • Reduce the chance of fixing a bug in one method and forgetting the other.

Also applies to: 114-176

🧹 Nitpick comments (2)
classes/helpers/FrmCssScopeHelper.php (2)

17-23: Comment stripping vs. “preserve newlines” + class name safety

The preg_replace( '/\/\*.*?\*\//s', '', $css ); call will also remove newline characters inside multi‑line comments, so the implementation doesn’t actually “preserve newlines” as the inline comment suggests. For large custom snippets this can change line offsets and overall formatting.

Also, $class_name is interpolated directly into selectors; if it ever contains whitespace or other invalid characters, the generated CSS will break.

Consider either:

  • Adjusting the comment stripping to keep \n characters (e.g., via preg_replace_callback that blanks everything but newlines), or updating the comment to match behavior, and
  • Validating/normalizing $class_name early so it’s guaranteed to be a safe CSS class token.

216-261: Fallback behavior in find_matching_brace() could be safer

find_matching_brace() correctly tracks nested braces and skips braces inside strings, but if no matching } is found it returns $length - 1. For malformed CSS this can cause callers to treat an arbitrary last character as the closing brace and slice an unexpected substring.

To make failures easier to detect, consider returning a sentinel (e.g., -1 or null) and having callers either bail out or treat the rest of the string as the block explicitly, instead of silently guessing:

-		for ( $i = $open_pos + 1; $i < $length; $i++ ) {
+		for ( $i = $open_pos + 1; $i < $length; $i++ ) {
 			…
 		}//end for
-
-		return $length - 1;
+		return -1;

and then check for -1 in nest() / unnest() before using the result.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be7fca7 and 0180ddd.

📒 Files selected for processing (1)
  • classes/helpers/FrmCssScopeHelper.php (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: PHP 8 tests in WP trunk
  • GitHub Check: PHP 7.4 tests in WP trunk
  • GitHub Check: Cypress
🔇 Additional comments (1)
classes/helpers/FrmCssScopeHelper.php (1)

180-214: Declaration formatting helper looks good

preserve_declaration_formatting() keeps single‑line declarations compact while normalizing multi‑line blocks to a consistent \n\t… format and stripping empty lines. That’s a sensible compromise between readability and not over‑processing user CSS.

Comment thread classes/helpers/FrmCssScopeHelper.php
Comment thread classes/helpers/FrmCssScopeHelper.php
Copy link
Copy Markdown
Contributor

@Crabcyborg Crabcyborg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @Liviu-p!

I think this looks good.

🚀

@Crabcyborg Crabcyborg merged commit d470683 into master Dec 8, 2025
16 checks passed
@Crabcyborg Crabcyborg deleted the feature/ff-style-custom-css branch December 8, 2025 16:13
stephywells pushed a commit that referenced this pull request Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants