chore: WordPress.org compliance audit, SEO readme, and PCP fixes#4
chore: WordPress.org compliance audit, SEO readme, and PCP fixes#4
Conversation
Prepare the plugin for WordPress.org submission by addressing all 18 Plugin Directory Guidelines and fixing every error from the Plugin Check Plugin (PCP). Compliance & readme: - Rewrite readme.txt with SEO-optimized title, description, FAQ, screenshots section, and expanded External Services documentation listing every external domain individually with Terms/Privacy links - Update Plugin Name header in roi-insights.php to match readme title - Bump Tested up to from 6.7 to 6.9 (current WordPress release) - Remove all Ads Advisor references from readme.txt and README.md - Update call tracking rates to new pricing ($8/$6/$4 per number) - Add call recording credit rates to tier descriptions PCP fixes: - Replace raw GA4 <script src> tag with wp_enqueue_script() - Collapse multiline pixel echoes (TikTok, Pinterest, Nextdoor) to single lines so phpcs:ignore directives cover the escaped variables - Add phpcs:ignore annotation for $_POST JSON input with explanation Admin UX: - Fix notice dismissal: add AJAX handler to persist dismissed state in user_meta so is-dismissible notices stay hidden across page loads - Add code comment on Dashboard iframe explaining architectural choice (WordPress.org Guideline 8 — reviewer documentation)
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughUpdated documentation and plugin metadata; enqueued GA4 loader instead of inline script; added authenticated AJAX endpoint and admin JS to persist license-notice dismissal; expanded license cache clearing to remove related user-meta; tightened PHPCS annotation; added an admin iframe comment. Changes
Sequence DiagramsequenceDiagram
participant Admin as Admin User
participant JS as Admin JS (notice)
participant Ajax as admin-ajax.php
participant PHP as ROI_Insights::handle_dismiss_notice
participant Meta as User Meta
Admin->>JS: Click dismiss on license notice
JS->>JS: Intercept click, gather nonce/action
JS->>Ajax: POST action=roi_insights_dismiss_notice, nonce
Ajax->>PHP: Route request
PHP->>PHP: wp_verify_nonce()
PHP->>PHP: current_user_can('manage_options')
PHP->>Meta: update_user_meta(user_id, 'roi_insights_license_notice_dismissed', true)
Meta-->>PHP: Success
PHP-->>Ajax: JSON { "success": true }
Ajax-->>JS: Success response
JS->>JS: Remove/hide notice in DOM
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
includes/class-roi-insights.php (1)
221-230: Inline XHR lacks error handling and may silently fail.The inline script fires the dismiss request but doesn't handle network errors or non-2xx responses. If the request fails, the user won't know, and the notice will reappear on the next page load.
♻️ Suggested improvement with minimal error handling
- echo '<script>document.addEventListener("DOMContentLoaded",function(){var n=document.getElementById("roi-insights-license-notice");if(n){n.addEventListener("click",function(e){if(e.target.classList.contains("notice-dismiss")){var x=new XMLHttpRequest();x.open("POST","' . esc_url( admin_url( 'admin-ajax.php' ) ) . '");x.setRequestHeader("Content-Type","application/x-www-form-urlencoded");x.send("action=roi_insights_dismiss_notice&_wpnonce="+n.dataset.nonce)}})}});</script>' . "\n"; + echo '<script>document.addEventListener("DOMContentLoaded",function(){var n=document.getElementById("roi-insights-license-notice");if(n){n.addEventListener("click",function(e){if(e.target.classList.contains("notice-dismiss")){var x=new XMLHttpRequest();x.open("POST","' . esc_url( admin_url( 'admin-ajax.php' ) ) . '");x.setRequestHeader("Content-Type","application/x-www-form-urlencoded");x.onerror=function(){console.warn("ROI Insights: Failed to persist notice dismissal")};x.send("action=roi_insights_dismiss_notice&_wpnonce="+n.dataset.nonce)}})}});</script>' . "\n";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/class-roi-insights.php` around lines 221 - 230, The inline XHR attached to the roi-insights-license-notice (action roi_insights_dismiss_notice) lacks error handling and can silently fail; update the script that sends the POST to admin-ajax.php to add proper XHR handlers: set x.onerror to log/report network errors, set x.onload to check x.status for 2xx and handle non-2xx responses (log or retry), and optionally provide a visual fallback (e.g., keep the notice visible or show a brief message) so failures are surfaced; modify the inline script block that creates XMLHttpRequest in includes/class-roi-insights.php to wire those handlers and ensure n.dataset.nonce is escaped/used safely.includes/class-tracking.php (1)
91-92: Callingwp_enqueue_script()insidewp_headis unconventional.Scripts are typically enqueued via the
wp_enqueue_scriptshook. However, sinceinject_headexecutes at priority 1 andwp_print_head_scriptsruns at priority 9 withinwp_head, the GA4 script will be queued before it's printed. This should work correctly, but testing in production would confirm the script loads as expected in the final<head>output.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@includes/class-tracking.php` around lines 91 - 92, The GA4 script is being enqueued directly inside inject_head which is unconventional; move the wp_enqueue_script call for 'roi-insights-ga4-gtag' out of inject_head and into a proper enqueue hook (e.g., hook into wp_enqueue_scripts or admin_enqueue_scripts as appropriate) so scripts are registered/enqueued in the canonical phase, then leave inject_head to only output the inline gtag bootstrap (or use wp_add_inline_script tied to 'roi-insights-ga4-gtag'); update references to rawurlencode($s['ga4Id'])/ $ga4_id accordingly and ensure the inline script is escaped or attached via wp_add_inline_script rather than echoing directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@includes/class-roi-insights.php`:
- Around line 221-230: The inline XHR attached to the
roi-insights-license-notice (action roi_insights_dismiss_notice) lacks error
handling and can silently fail; update the script that sends the POST to
admin-ajax.php to add proper XHR handlers: set x.onerror to log/report network
errors, set x.onload to check x.status for 2xx and handle non-2xx responses (log
or retry), and optionally provide a visual fallback (e.g., keep the notice
visible or show a brief message) so failures are surfaced; modify the inline
script block that creates XMLHttpRequest in includes/class-roi-insights.php to
wire those handlers and ensure n.dataset.nonce is escaped/used safely.
In `@includes/class-tracking.php`:
- Around line 91-92: The GA4 script is being enqueued directly inside
inject_head which is unconventional; move the wp_enqueue_script call for
'roi-insights-ga4-gtag' out of inject_head and into a proper enqueue hook (e.g.,
hook into wp_enqueue_scripts or admin_enqueue_scripts as appropriate) so scripts
are registered/enqueued in the canonical phase, then leave inject_head to only
output the inline gtag bootstrap (or use wp_add_inline_script tied to
'roi-insights-ga4-gtag'); update references to rawurlencode($s['ga4Id'])/
$ga4_id accordingly and ensure the inline script is escaped or attached via
wp_add_inline_script rather than echoing directly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ed56f24e-a7b1-4ff4-988e-ac04957a6427
📒 Files selected for processing (7)
README.mdincludes/class-api.phpincludes/class-roi-insights.phpincludes/class-tracking.phpreadme.txtroi-insights.phpsrc/admin/Dashboard.tsx
- Clear roi_insights_license_notice_dismissed user meta in clear_cache() so license warnings reappear after key changes or expiration instead of staying permanently hidden. - Use WP 6.3+ script loading strategy array for GA4 gtag.js enqueue to preserve async loading behavior from the original raw script tag.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@includes/class-license.php`:
- Around line 269-270: clear_cache() currently deletes self::CACHE_KEY transient
and a user meta but doesn't remove the stale flag, so stale_or_invalid() may
return old data after transient backend failures; update clear_cache() to also
delete the 'roi_insights_license_cache_stale' transient (or corresponding
constant) so that delete_transient(self::CACHE_KEY) is followed by
delete_transient('roi_insights_license_cache_stale') (and keep
delete_user_meta(...) unchanged) to ensure stale_or_invalid() can't read stale
cache after a key update.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 46f86542-8fee-4034-900f-5e2bdbedf375
📒 Files selected for processing (2)
includes/class-license.phpincludes/class-tracking.php
🚧 Files skipped from review as they are similar to previous changes (1)
- includes/class-tracking.php
Delete the _stale transient in clear_cache() so stale_or_invalid() cannot return data from a previous license key if the backend is temporarily unreachable after a key update.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@includes/class-license.php`:
- Around line 265-271: The clear_cache() method currently deletes the
'roi_insights_license_notice_dismissed' meta only for get_current_user_id(),
which leaves other users' dismissals intact; either change the implementation to
clear the meta for all users (e.g., use delete_metadata('user', 0,
'roi_insights_license_notice_dismissed') or run a WP_User_Query and loop
delete_user_meta for each user) so that CACHE_KEY invalidation truly surfaces
notices to all admins, or update the clear_cache() docstring to explicitly state
it only resets the dismissal for the current user; adjust the code in
clear_cache() and mention the CACHE_KEY and
'roi_insights_license_notice_dismissed' keys accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a39bfee5-469d-448a-873c-9de3e0b8ae40
📒 Files selected for processing (1)
includes/class-license.php
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2f6c482. Configure here.
The wp_enqueue_script() array args format (strategy => async) was introduced in WP 6.3. Bump Requires at least from 6.2 to 6.3 so the GA4 async enqueue works correctly. WP 6.2 has been EOL since November 2024.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@readme.txt`:
- Line 91: Update the FAQ sentence that currently reads “All tracking scripts
load asynchronously and do not block page rendering” to a toned-down claim:
replace or rephrase that exact sentence to acknowledge exceptions (e.g., mention
that remote scripts are loaded async but small inline bootstrap snippets may run
on the main thread) and keep the note about md-roi.js size; ensure the revised
copy preserves the meaning of asynchronous loading while avoiding an absolute
guarantee.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 43002e3b-6a24-4071-affc-ef3db5af5c5c
📒 Files selected for processing (2)
readme.txtroi-insights.php
✅ Files skipped from review due to trivial changes (1)
- roi-insights.php
Acknowledge that inline pixel bootstrap snippets run on the main thread, while remote scripts are loaded async.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@readme.txt`:
- Line 128: The readme statement is misleading because the local attribution
script is enqueued unconditionally; update the wording to clarify that while no
external network requests are made until license activation, Google connect, or
tracking pixel enablement, the plugin still loads a local tracker
file—md-roi.js—on all frontend pages via the enqueue_tracker() method in
includes/class-tracking.php (hooked to wp_enqueue_scripts); change the sentence
to explicitly state that md-roi.js is loaded locally before activation or pixel
enablement, or alternatively adjust enqueue_tracker() to conditionally enqueue
the script only after activation if you prefer that behavior.
- Line 36: The README advertises a "Basic call log" feature that isn't
implemented; either remove that claim or implement it: to implement, add call
storage and schema (e.g., new DB table and model), add retrieval endpoints in
class-api.php (e.g., GET /calls and GET /calls/:id) that fetch from
api.roiknowledge.com if applicable, add admin settings toggles in
class-settings.php and settings UI for enabling call logging, and implement an
admin dashboard view to display calls (source, caller ID, duration) and
pagination; if opting to remove the claim, update the readme to delete the
"Basic call log" bullet and any references to dniScriptUrl/dniSwapNumber that
imply call logging. Ensure code touches reference dniScriptUrl and dniSwapNumber
only if you implement DNI-based call collection and document the backend
endpoint used for retrieval.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
PCP flags both .Missing and .Recommended variants for $_POST access. The nonce is verified in verify_request() which is called before get_body() in every handler.
npm run plugin-zip builds the frontend, assembles only the runtime files into a roi-insights/ directory, zips it, and cleans up. Excludes: .git, .gitignore, node_modules, src, tsconfig.json, package.json, package-lock.json, README.md (GitHub-only).
- Remove "Basic call log" as a standalone free feature bullet — the call log is part of the embedded dashboard service, not a plugin feature. Reword to make this clear. - Clarify that md-roi.js loads on all frontend pages as a local file (no external requests) before any license activation or pixel enablement.

Summary
Prepares the plugin for WordPress.org directory submission by addressing all 18 Plugin Directory Guidelines, merging the SEO-optimized readme.txt, and fixing every error reported by the Plugin Check Plugin (PCP).
wp_enqueue_script(), multiline pixel echoes collapsed sophpcs:ignorecovers escaped variables,$_POSTJSON input annotatedroi-insights.phpandreadme.txt, addedTested up tofieldFiles changed
readme.txtroi-insights.phpREADME.mdincludes/class-tracking.phpwp_enqueue_script(), collapse multiline pixel echoesincludes/class-api.php$_POSTinputincludes/class-roi-insights.phpsrc/admin/Dashboard.tsxPCP results addressed
wp_enqueue_script()Test plan
wp_enqueue_script(check page source for<script>tag in expected position)roi-insights/folderNote
Medium Risk
Moderate risk because it changes frontend script loading (GA4 via
wp_enqueue_script) and adds a new admin AJAX endpoint for persisting notice dismissal; both could affect tracking behavior or admin UX if misconfigured.Overview
Prepares the plugin for WordPress.org submission by rewriting
readme.txt(SEO/content refresh plus detailed External Services disclosures) and syncing plugin metadata inroi-insights.php(name/description, WP minimum and tested versions).Addresses Plugin Check/PHPCS issues by switching GA4 loading to
wp_enqueue_script, tightening$_POSTJSON handling annotations, collapsing multi-line pixel script echoes, and adding a packaging script (plugin-zip) plus ignoring the generatedroi-insights.zip.Improves license UX by persisting dismissal of the invalid-license admin notice via a new
wp_ajax_roi_insights_dismiss_noticehandler, and resetting dismissal state when the license cache is cleared; documentation/pricing tables are also updated (new call tracking rates and removal of Ads Advisor references).Reviewed by Cursor Bugbot for commit 7a1a451. Bugbot is set up for automated code reviews on this repo. Configure here.