Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions classes/controllers/FrmTestModeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ private static function render_testing_mode_container() {
}

if ( ! empty( $form->options['chat'] ) ) {
// Track view test mode with chat form.
FrmUsageController::update_flows_data( 'view_test_mode', 'chat_form' );
Comment on lines +99 to +100

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Make usage-counter updates concurrency-safe.

At Lines [99-100] and [148-151], both new events use FrmUsageController::update_flows_data(). That method reads the flows option, increments the value, and writes it back in separate operations. Concurrent test-mode views can overwrite each other and undercount view_test_mode.

Update the counter atomically, or use storage that supports concurrent increments, before relying on these new call sites.

Also applies to: 148-151

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 100-100: Avoid side effects in a file that defines symbols
Context: echo '

' . esc_html__( 'Test Mode is currently not supported for conversational forms.', 'formidable' ) . '
';
Note: [CWE-710] Improper Adherence to Coding Standards.

(no-side-effect)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@classes/controllers/FrmTestModeController.php` around lines 99 - 100, Make
the usage-counter update invoked by FrmUsageController::update_flows_data
concurrency-safe by replacing its separate read/increment/write sequence with an
atomic increment or concurrency-safe storage operation. Preserve both
view_test_mode call sites in FrmTestModeController while ensuring concurrent
events cannot overwrite each other or undercount the counter.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 5 \
  -e "frm_test_mode" \
  -e "maybe_add_test_mode_container" \
  -e "render_testing_mode_container" \
  --glob '*.php' .

Repository: Strategy11/formidable-forms

Length of output: 10698


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== FrmFormsController load hook section =="
sed -n '490,550p' classes/controllers/FrmFormsController.php

echo
echo "== FrmTestModeController relevant method sections =="
sed -n '1,180p' classes/controllers/FrmTestModeController.php

echo
echo "== Ajax references to frm_filter_final_form and test mode =="
rg -n -C 3 \
  -e "frm_filter_final_form" \
  -e "testmode|test_mode" \
  -e "do_action\\(\\s*['\"]wp_ajax_" \
  --glob '*.php' classes || true

echo
echo "== Action/filter registration and AJAX callback candidates =="
rg -n -C 4 \
  -e "add_action\\(\\s*['\"]wp_ajax|add_filter\\(\\s*['\"]wp_ajax" \
  -e "FrmFormsController::" \
  --glob '*.php' classes/controllers classes || true

Repository: Strategy11/formidable-forms

Length of output: 50384


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== Formidable testmode URL/query references in JS =="
rg -n -C 3 \
  -e "testmode|/testmode|frm_test_mode|frm_forms_preview|iframe|src|new URL|URL\\(" \
  --glob '*.js' --glob '*.php' --glob '*.jsx' --glob '*.tsx' .

echo
echo "== frm_filter_final_form usages =="
rg -n -C 2 'frm_filter_final_form' --glob '*.php' .

echo
echo "== FrmFormsController render show_form outline/sections =="
ast-grep outline classes/controllers/FrmFormsController.php --match show_form --view expanded || true
sed -n '2460,2555p' classes/controllers/FrmFormsController.php

Repository: Strategy11/formidable-forms

Length of output: 50384


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== frm_filter_final_form usages (limited) =="
rg -n \
  -e "frm_filter_final_form" \
  --glob '*.php' classes .

echo
echo "== Targeted testmode query-string references in PHP source =="
rg -n \
  -e "testmode|frm_forms_preview|frm_forms_trash|wp_ajax_nopriv|wp_ajax_.*preview" \
  --glob '*.php' classes/controllers/FrmFormsController.php classes/controllers/FrmHooksController.php

echo
echo "== FrmEntriesAJAXSubmitController ajax hook =="
sed -n '1,120p' classes/controllers/FrmEntriesAJAXSubmitController.php || true

Repository: Strategy11/formidable-forms

Length of output: 5969


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== Show form and route relevant sections =="
rg -n \
  -e "public static function show_form|public static function route|function get_form_shortcode" \
  classes/controllers/FrmFormsController.php

sed -n '1980,2100p' classes/controllers/FrmFormsController.php
sed -n '2480,2545p' classes/controllers/FrmFormsController.php

echo
echo "== Testmode JS references across tracked JS files only =="
python3 - <<'PY'
import os, pathlib, re
patterns = [re.compile(r'\btestmode\b'), re.compile(r'/testmode\b'), re.compile(r'frm_test_mode\b'), re.compile(r'frm_forms_preview\b')]
for f in pathlib.Path('.').rglob('*'):
    if f.is_file() and f.suffix.lower() in {'.js','.jsx','.ts','.tsx'} and '.git' not in f.parts:
        try:
            text = f.read_text(encoding='utf-8')
        except Exception:
            continue
        if any(p.search(text) for p in patterns):
            print(f)
PY

echo
echo "== Minimal deterministic filter call probe =="
python3 - <<'PY'
# Reads the controller source and reports hook context around each apply_filter/add_filter call for frm_filter_final_form.
from pathlib import Path
p = Path('classes/controllers/FrmFormsController.php')
lines = p.read_text().splitlines()
for i,l in enumerate(lines, 1):
    if 'frm_filter_final_form' in l:
        start=max(1,i-4); end=min(len(lines),i+4)
        print(f'--- lines {start}-{end} ---')
        for n in range(start,end+1):
            print(f'{n}: {lines[n-1]}')
PY

Repository: Strategy11/formidable-forms

Length of output: 7116


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== Testmode JS references with line numbers =="
python3 - <<'PY'
import os, pathlib, re
source_paths = [p for p in pathlib.Path('.').rglob('*') if p.is_file() and p.suffix.lower() in {'.js','.jsx','.ts','.tsx'} and '.git' not in p.parts]
for f in source_paths:
    try:
        lines = f.read_text(encoding='utf-8').splitlines()
    except Exception:
        continue
    hits = [i for i,l in enumerate(lines, 1) if any(x in l for x in ('testmode','/testmode','frm_test_mode','frm_forms_preview'))]
    if hits:
        print(f'FILE: {f}')
        for i in hits:
            print(f'{i}: {lines[i-1]}')
PY

echo
echo "== Full route/show_form call path around preview AJAX =="
sed -n '2159,2260p' classes/controllers/FrmFormsController.php
sed -n '2495,2555p' classes/controllers/FrmFormsController.php

Repository: Strategy11/formidable-forms

Length of output: 50384


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== Ajax form action and testmode URL generation =="
sed -n '1005,1075p' js/formidable.js
sed -n '1020,1065p' js/formidable.min.js

echo
echo "== Ajax preview form generation in PHP controllers/helpers =="
rg -n -C 4 \
  -e "do_action\\(\\s*['\"]wp_ajax_|register_ajax|ajax_hooks|frm_after_form" \
  --glob '*.php' classes/js classes/controllers classes || true

echo
echo "== Formidable core register_ajax occurrences =="
rg -n -C 3 \
  -e "register_ajax|add_action\\(\\s*['\"]wp_ajax_|add_filter\\(\\s*['\"]wp_ajax_" \
  --glob '*.php' classes/controllers/FrmFormsController.php classes/helpers/FrmFormsHelper.php 2>/dev/null || true

Repository: Strategy11/formidable-forms

Length of output: 5403


Gate test-mode usage tracking to preview-only requests.

The frm_filter_final_form listener runs from show_form(), which is used by ajax preview, error redraws, and AJAX success returns. Those repeated AJAX renders also hit render_testing_mode_container() when testmode is present, so each render can increment view_test_mode. Track only the initial preview render, or gate the update by request context.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 100-100: Avoid side effects in a file that defines symbols
Context: echo '

' . esc_html__( 'Test Mode is currently not supported for conversational forms.', 'formidable' ) . '
';
Note: [CWE-710] Improper Adherence to Coding Standards.

(no-side-effect)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@classes/controllers/FrmTestModeController.php` around lines 99 - 100, Update
the test-mode tracking in FrmTestModeController around
render_testing_mode_container() so FrmUsageController::update_flows_data() runs
only for the initial preview request, not AJAX error redraws or success renders.
Use the existing request context or preview-specific condition to gate the
'view_test_mode'/'chat_form' update while preserving the container rendering
behavior.

echo '<div class="frm_note_style">' . esc_html__( 'Test Mode is currently not supported for conversational forms.', 'formidable' ) . '</div>';
return;
}
Expand Down Expand Up @@ -143,6 +145,10 @@ private static function render_testing_mode_container() {

self::include_svg();

if ( ! $enabled ) {
FrmUsageController::update_flows_data( 'view_test_mode', 'no_addon' );
}

include FrmAppHelper::plugin_path() . '/classes/views/test-mode/container.php';
}

Expand Down
2 changes: 1 addition & 1 deletion classes/controllers/FrmUsageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public static function ajax_track_flows() {
}

/**
* Updates flows data.
* Updates flows data. This increases the count of flow_data[ $key ][ $value ].
*
* @since 6.16.1
*
Expand Down
11 changes: 11 additions & 0 deletions classes/views/test-mode/container.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@
}
?>
</select>

<?php

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@truongwp This is for the add-on tracking?

Can we add a new hook here instead so we can move this logic into the add-on?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Can we use $enabled to check, like we did with other test mode add-on features?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@truongwp Like the hook is only called if $enabled is true?

That would be fine.

Otherwise I'm not sure if I follow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@Crabcyborg No. I meant we won't need to add a hook there, just check if ( $enabled ) { foreach ... }. Because that file contains many test mode add-ons code.

@Crabcyborg Crabcyborg Jun 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@truongwp

I just think there's a difference.

This file is intended to use the whole UI, as it's shown in Lite as disabled placeholders.

And these inputs are only used when the add-on is actually active, and have no visual benefit for being in Lite.

Since they only impact the add-on, it would be nice if we only needed to modify the add-on when we needed to make changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok. I added the hook and moved the code to the add-on.

/**
* Fires inside the enabled form actions container.
*
* @since x.x
*
* @param array $form_actions
*/
do_action( 'frm_testmode_form_actions', $form_actions );

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Variable $form_actions might not be defined


A variable has been used but not defined, which may result in warnings during program execution. This can also cause bugs since the intended usage scope of the variable is not known.

?>
</div>
</label>
</div>
Expand Down
Loading