-
Notifications
You must be signed in to change notification settings - Fork 93
Feat/helpful feedback block #259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
|
Warning Rate limit exceeded@iftakharul-islam has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 28 minutes and 14 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⛔ Files ignored due to path filters (8)
📒 Files selected for processing (20)
WalkthroughThis PR introduces a new "Helpful Feedback" Gutenberg block that enables users to vote on content helpfulness via Yes/No buttons. The feature includes a server-side renderer, AJAX vote handler, frontend voting UI with state management, comprehensive styling across editor and frontend, and vote tracking via cookies, user metadata, and IP address. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (19)
src/blocks/HelpfulFeedback/save.js (1)
1-1: Remove unused importThe
useBlockPropsimport is not used in this component since it returnsnullfor server-side rendering. Remove the unused import to keep the code clean.Apply this diff:
-import { useBlockProps } from '@wordpress/block-editor'; - const Save = () => { return null; };assets/build/blocks/HelpfulFeedback/block.json (1)
1-234: Avoid committing both source and build manifests to prevent drift.This block.json duplicates src/blocks/HelpfulFeedback/block.json. Prefer a single source of truth (src) and generate build artifacts during build/release. If keeping both, add a CI check to ensure they stay in sync.
includes/Ajax.php (4)
43-46: Hooks look good; add capability/rate‑limit considerations if abuse becomes an issue.Registration for both priv and nopriv is correct. Consider lightweight rate limiting (per IP) to reduce abuse.
332-336: Harden privacy: use a salted hash for IPs.Replace md5($user_ip) with wp_hash($user_ip) to avoid reversible hashes and tie to site salts.
- update_post_meta( $post_id, "wedocs_helpful_vote_ip_" . md5( $user_ip ), $vote ); + update_post_meta( $post_id, "wedocs_helpful_vote_ip_" . wp_hash( $user_ip ), $vote );
339-343: Set secure cookie attributes (Secure, HttpOnly, SameSite).Current setcookie lacks security attributes. Prefer the array options form (PHP ≥7.3):
- setcookie( 'wedocs_response', $cookie_val, time() + WEEK_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN ); + setcookie( 'wedocs_response', $cookie_val, [ + 'expires' => time() + WEEK_IN_SECONDS, + 'path' => COOKIEPATH, + 'domain' => COOKIE_DOMAIN, + 'secure' => is_ssl(), + 'httponly' => true, + 'samesite' => 'Lax', + ] );Note: If older PHP versions must be supported, we can fall back to the legacy signature without SameSite; confirm target PHP/WP matrix.
320-330: Non‑atomic vote check/update allows rare double counts under race.Two rapid requests can both pass the "already voted" checks and increment. Mitigate with a short‑lived per‑user/IP transient lock or move the "mark as voted" write before increment with a unique check (e.g., add_post_meta + unique key) and rollback on failure.
src/blocks/HelpfulFeedback/edit.js (2)
1-16: Drop unused imports to reduce bundle size.RichText, RangeControl, and BorderControl aren’t used.
-import { useBlockProps, InspectorControls, RichText } from '@wordpress/block-editor'; +import { useBlockProps, InspectorControls } from '@wordpress/block-editor'; @@ - RangeControl, - __experimentalBorderControl as BorderControl, + // RangeControl, + // __experimentalBorderControl as BorderControl,If these are planned soon, ignore.
104-113: Guard against undefined BoxControl values.If containerPadding/Margin are ever undefined, template strings will output "undefined". Optionally default them when destructuring:
- containerPadding, - containerMargin, + containerPadding = { top:'0', right:'0', bottom:'0', left:'0' }, + containerMargin = { top:'0', right:'0', bottom:'0', left:'0' },assets/js/helpful-feedback.js (1)
109-116: Optional: update hidden counts only when present.Current logic is fine. If you want to suppress layout shift, also hide parentheses when counts are 0 in preview; not required.
assets/build/block.js (2)
1-1: Remove leftover console.log in production bundleDrop the debug log to avoid console noise in users’ editors.
- console.log("editor panel:",r); + // debug log removed
1-1: Exclude built artifacts from linters to prevent false positivesThe
assets/build/directory contains bundled artifacts that trigger unnecessary linter warnings (e.g., Biome's "used before declaration"). Create ignore files at the repository root:+.eslintignore +assets/build/ + +.biomeignore +assets/build/Both ESLint and Biome will automatically discover and respect these files.
src/blocks/HelpfulFeedback/view.js (4)
32-41: Block repeat votes on already‑voted containersAvoid unnecessary requests when the UI is already finalized.
- // Check if already voting - if ($container.hasClass('loading')) { + // Prevent duplicate or post-vote clicks + if ($container.hasClass('loading') || $container.hasClass('has-voted')) { return; }
141-155: Encode thank-you message before injecting HTMLEven though PHP escapes the data attribute, encode again before HTML interpolation.
- const thankYouContent = ` + const safeThankYou = $('<div>').text(thankYouMessage).html(); + const thankYouContent = ` <div class="wedocs-feedback-thank-you"> <div class="wedocs-feedback-title" style="color: #28a745; margin-bottom: 16px;"> - ${thankYouMessage} + ${safeThankYou} </div> <div class="wedocs-feedback-result"> ... `;
55-70: Add an AJAX timeout and surface transport errorsPrevent long‑hung requests on flaky networks.
$.ajax({ url: wedocsHelpfulFeedback.ajax_url, type: 'POST', data: ajaxData, dataType: 'json', + timeout: 10000,
95-107: Internationalize user‑visible stringsReplace hardcoded messages with localized strings (wp.i18n or wp_localize_script). Example using localization:
- let errorMessage = 'An error occurred while voting'; + let errorMessage = wedocsHelpfulFeedback.i18n_error_generic || 'An error occurred while voting'; ... - errorMessage = 'You are not authorized to vote'; + errorMessage = wedocsHelpfulFeedback.i18n_error_unauthorized || 'You are not authorized to vote'; ... - errorMessage = 'Too many requests. Please try again later'; + errorMessage = wedocsHelpfulFeedback.i18n_error_rate_limited || 'Too many requests. Please try again later';And in PHP where the script is localized:
wp_localize_script( 'wedocs-helpful-feedback-frontend', 'wedocsHelpfulFeedback', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wedocs_helpful_feedback_nonce'), 'i18n_error_generic' => __('An error occurred while voting','wedocs'), 'i18n_error_unauthorized' => __('You are not authorized to vote','wedocs'), 'i18n_error_rate_limited' => __('Too many requests. Please try again later','wedocs'), ] );src/blocks/HelpfulFeedback/render.php (3)
44-60: Harden cookie handling to numeric IDsCast cookie values to integers before comparison.
- $previous = isset($_COOKIE['wedocs_response']) ? explode(',', $_COOKIE['wedocs_response']) : []; - if (in_array($post_id, $previous)) { + $previous = isset($_COOKIE['wedocs_response']) ? explode(',', $_COOKIE['wedocs_response']) : []; + $previous_ids = array_map('intval', $previous); + if (in_array((int) $post_id, $previous_ids, true)) { $has_voted = true; ... }
213-218: Normalize script URL joinAvoid double slashes by removing the leading slash after
plugin_dir_url(...).-wp_enqueue_script('wedocs-helpful-feedback-frontend', plugin_dir_url(WEDOCS_FILE) . '/assets/js/helpful-feedback.js', ['jquery'], WEDOCS_VERSION, true); +wp_enqueue_script('wedocs-helpful-feedback-frontend', plugin_dir_url(WEDOCS_FILE) . 'assets/js/helpful-feedback.js', ['jquery'], WEDOCS_VERSION, true);
115-123: Border widths: honor per-side valuesCurrent inline border uses only the top width for all sides. If you intend per-side widths, emit separate width/style/color.
- $container_styles[] = sprintf( - 'border: %s %s %s', - esc_attr($border_width['top'] ?? '1px'), - esc_attr($attributes['containerBorderStyle']), - esc_attr($attributes['containerBorderColor'] ?? '#e0e0e0') - ); + $container_styles[] = sprintf( + 'border-width: %s %s %s %s; border-style: %s; border-color: %s', + esc_attr($border_width['top'] ?? '1px'), + esc_attr($border_width['right'] ?? '1px'), + esc_attr($border_width['bottom'] ?? '1px'), + esc_attr($border_width['left'] ?? '1px'), + esc_attr($attributes['containerBorderStyle']), + esc_attr($attributes['containerBorderColor'] ?? '#e0e0e0') + );assets/build/blocks/HelpfulFeedback/render.php (1)
44-47: Cast cookie IDs to integersSame robustness improvement as the src file.
- $previous = isset($_COOKIE['wedocs_response']) ? explode(',', $_COOKIE['wedocs_response']) : []; - if (in_array($post_id, $previous)) { + $previous = isset($_COOKIE['wedocs_response']) ? explode(',', $_COOKIE['wedocs_response']) : []; + $previous_ids = array_map('intval', $previous); + if (in_array((int) $post_id, $previous_ids, true)) { $has_voted = true;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
assets/build/block.asset.php(1 hunks)assets/build/block.js(1 hunks)assets/build/blocks/HelpfulFeedback/block.json(1 hunks)assets/build/blocks/HelpfulFeedback/render.php(1 hunks)assets/build/index.css(2 hunks)assets/build/style-block.css(1 hunks)assets/js/helpful-feedback.js(1 hunks)includes/Ajax.php(2 hunks)src/blocks/HelpfulFeedback/block.json(1 hunks)src/blocks/HelpfulFeedback/edit.js(1 hunks)src/blocks/HelpfulFeedback/index.js(1 hunks)src/blocks/HelpfulFeedback/render.php(1 hunks)src/blocks/HelpfulFeedback/save.js(1 hunks)src/blocks/HelpfulFeedback/style.scss(1 hunks)src/blocks/HelpfulFeedback/view.js(1 hunks)src/blocks/index.js(1 hunks)wedocs.php(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
src/blocks/HelpfulFeedback/view.js (2)
src/utils/menuFix.js (1)
$(2-2)assets/js/frontend.js (1)
data(163-166)
src/blocks/HelpfulFeedback/edit.js (1)
src/blocks/CustomControls/RadioImageControl.js (1)
RadioImageControl(3-26)
assets/build/blocks/HelpfulFeedback/render.php (2)
src/blocks/HelpfulFeedback/render.php (1)
render_wedocs_helpful_feedback(20-407)src/blocks/HelpfulFeedback/edit.js (1)
attributes(22-69)
src/blocks/HelpfulFeedback/render.php (2)
assets/build/blocks/HelpfulFeedback/render.php (1)
render_wedocs_helpful_feedback(20-407)src/blocks/HelpfulFeedback/edit.js (1)
attributes(22-69)
src/blocks/HelpfulFeedback/index.js (2)
src/blocks/HelpfulFeedback/edit.js (1)
Edit(19-511)src/blocks/HelpfulFeedback/save.js (1)
Save(3-5)
assets/build/block.js (1)
src/blocks/DocsGrid/edit.js (1)
wp(16-16)
🪛 Biome (2.1.2)
assets/build/block.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)
🔇 Additional comments (13)
assets/build/index.css (1)
3190-3230: Build artifact - auto-generated CSSThis file contains compiled CSS output. The changes include:
- Minor selector formatting adjustment (line 3190-3192)
- New responsive artboard utility classes for horizontal phone layouts (lines 3201-3230)
These are expected build artifacts and don't require detailed review.
src/blocks/index.js (1)
3-3: LGTM - Block import follows established patternThe HelpfulFeedback block import is correctly added alongside existing block imports, maintaining consistency with the codebase structure.
wedocs.php (2)
50-50: LGTM - Render file inclusion follows established patternThe render file is properly included using the same pattern as the DocsGrid block (line 49), ensuring consistency across block implementations.
187-193: LGTM - Block registration is properly structuredThe HelpfulFeedback block registration:
- Follows the same pattern as DocsGrid (lines 180-185)
- Uses the correct path to the block directory
- Specifies the appropriate render callback function
- Maintains consistency with existing block registration conventions
assets/build/block.asset.php (1)
1-1: Expected build artifact updateThe version hash change is an expected result of the build process after adding the new HelpfulFeedback block. Dependencies remain unchanged.
src/blocks/HelpfulFeedback/index.js (1)
1-17: LGTM - Standard Gutenberg block registrationThe block registration follows WordPress best practices:
- Properly imports
registerBlockTypefrom@wordpress/blocks- Loads Edit and Save components
- References metadata from block.json
- Includes stylesheet for proper styling
- Clean, maintainable structure
assets/build/style-block.css (1)
3-3: Build artifact - compiled CSSThis is the compiled/minified output from
src/blocks/HelpfulFeedback/style.scss. The CSS includes comprehensive styling for the Helpful Feedback block with accessibility, responsive design, and print styles. As a build artifact, it doesn't require detailed review.src/blocks/HelpfulFeedback/style.scss (1)
1-294: Excellent styling implementation with strong accessibility focusThis SCSS file demonstrates best practices:
Accessibility Features:
- Minimum touch target sizes (44px × 44px) on line 38-39
- Clear focus indicators with 2px outline (lines 41-44)
- Proper disabled state handling (lines 46-49)
- High contrast mode support (lines 253-259)
- Reduced motion support (lines 262-271)
Responsive Design:
- Mobile-first approach with appropriate breakpoints
- Layout adapts at 480px for different screen sizes (lines 165-172, 175-194)
- Flexible button arrangements
Additional Strengths:
- Well-organized with clear section comments
- Two distinct layout variants (layout1: rectangular, layout2: circular)
- Loading and voted state management
- Print-optimized styles (lines 274-294)
- Smooth animations with accessibility fallbacks
src/blocks/HelpfulFeedback/block.json (1)
18-38: Ensure the server derives allowAnonymous from this attribute, not the request.The AJAX handler should read allowAnonymous from the block config for the given post, otherwise clients can bypass login by posting allow_anonymous=true. See my comment in includes/Ajax.php for a concrete fix using parse_blocks.
assets/js/helpful-feedback.js (1)
47-53: Client may send allow_anonymous, but server must ignore it.Keep sending it if you need UI parity, but ensure the server derives the setting from the block, not from this value (see Ajax.php fix).
includes/Ajax.php (1)
260-266: Nonce setup verified as correct.The nonce is properly created with
wp_create_nonce('wedocs_helpful_feedback_nonce')and localized towedocsHelpfulFeedback.nonceat./src/blocks/HelpfulFeedback/render.php:215-217. The action name matches what's verified in Ajax.php, following standard WordPress security practices.src/blocks/HelpfulFeedback/view.js (1)
195-201: I need to verify ifsrc/blocks/HelpfulFeedback/view.jsis loaded separately or is just a build source file.The review comment is incorrect—no double initialization risk exists.
The codebase uses a standard WordPress build process where
src/blocks/HelpfulFeedback/view.jsis a source file that gets compiled toassets/js/helpful-feedback.js. Only one script is enqueued viawp_enqueue_script()with the single handlewedocs-helpful-feedback-frontend, pointing toassets/js/helpful-feedback.js. Since only the built file loads on the frontend, there is no scenario where both files execute or double-bind event handlers.The identical code in both files confirms the src-to-build relationship—not separate parallel loading.
Likely an incorrect or invalid review comment.
src/blocks/HelpfulFeedback/render.php (1)
20-20: No collision risk exists; the review comment is incorrect.Only
assets/build/blocks/HelpfulFeedback/render.phpis loaded at runtime via the registeredregister_block_type()call in wedocs.php:188. Thesrc/blocks/HelpfulFeedback/render.phpfile is never executed—it is not independently required anywhere and is not registered as a block. Therefore, both function definitions never coexist in memory, and no fatal redeclaration error can occur. The suggested guard is unnecessary.Likely an incorrect or invalid review comment.
| <?php | ||
| /** | ||
| * Server-side rendering for the Helpful Feedback block | ||
| * | ||
| * @package weDocs | ||
| */ | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Avoid duplicate sources of truth
Having both this built PHP and src/blocks/HelpfulFeedback/render.php risks drift and redeclare fatals. Prefer a single canonical render file (ideally the one co-located with block.json used by register_block_type_from_metadata) and remove or exclude the other from loading.
🏁 Script executed:
#!/bin/bash
rg -n "render.php" assets/build/blocks/HelpfulFeedback src/blocks/HelpfulFeedback -SLength of output: 155
---
🏁 Script executed:
```shell
#!/bin/bash
# Check if both render.php files exist and compare their contents
echo "=== Checking render.php files ==="
for file in assets/build/blocks/HelpfulFeedback/render.php src/blocks/HelpfulFeedback/render.php; do
if [ -f "$file" ]; then
echo "✓ Found: $file"
else
echo "✗ Not found: $file"
fi
done
echo -e "\n=== Content comparison ==="
if [ -f "assets/build/blocks/HelpfulFeedback/render.php" ] && [ -f "src/blocks/HelpfulFeedback/render.php" ]; then
echo "File sizes:"
wc -c assets/build/blocks/HelpfulFeedback/render.php src/blocks/HelpfulFeedback/render.php
echo -e "\nDifference check:"
diff assets/build/blocks/HelpfulFeedback/render.php src/blocks/HelpfulFeedback/render.php && echo "Files are identical" || echo "Files differ"
fi
# Look for build config files
echo -e "\n=== Build configuration files ==="
fd -n "webpack|rollup|gulp|tsconfig|package\.json" --max-depth 2
Length of output: 906
Remove or regenerate the built render.php file to maintain a single source of truth
The two render.php files differ on line 214—assets/build has hardcoded './assets/js/helpful-feedback.js' and version '1.0.0', while src has /assets/js/helpful-feedback.js and WEDOCS_VERSION. This confirms the built version is stale. Delete assets/build/blocks/HelpfulFeedback/render.php or ensure it's properly generated from the source during your build process to prevent maintenance burden and potential runtime issues.
🤖 Prompt for AI Agents
In assets/build/blocks/HelpfulFeedback/render.php lines 1-7, the built file is
stale (differs at line 214: it uses a hardcoded
'./assets/js/helpful-feedback.js' and version '1.0.0' vs src which uses
'/assets/js/helpful-feedback.js' and WEDOCS_VERSION); remove
assets/build/blocks/HelpfulFeedback/render.php from the repo or regenerate it
from src so the built copy matches the single source of truth, and if
regenerating, run the build task and update the build script to emit the correct
path and WEDOCS_VERSION constant so the generated file stays in sync.
| // Enqueue frontend script and styles | ||
| wp_enqueue_script('wedocs-helpful-feedback-frontend', plugin_dir_url(WEDOCS_FILE) .'./assets/js/helpful-feedback.js', ['jquery'], '1.0.0', true); | ||
| wp_localize_script('wedocs-helpful-feedback-frontend', 'wedocsHelpfulFeedback', [ | ||
| 'ajax_url' => admin_url('admin-ajax.php'), | ||
| 'nonce' => wp_create_nonce('wedocs_helpful_feedback_nonce') | ||
| ]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align script enqueue with plugin constants and clean URL
Use the version constant and drop ./ for predictable caching and correct URLs.
-wp_enqueue_script('wedocs-helpful-feedback-frontend', plugin_dir_url(WEDOCS_FILE) .'./assets/js/helpful-feedback.js', ['jquery'], '1.0.0', true);
+wp_enqueue_script('wedocs-helpful-feedback-frontend', plugin_dir_url(WEDOCS_FILE) . 'assets/js/helpful-feedback.js', ['jquery'], WEDOCS_VERSION, true);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Enqueue frontend script and styles | |
| wp_enqueue_script('wedocs-helpful-feedback-frontend', plugin_dir_url(WEDOCS_FILE) .'./assets/js/helpful-feedback.js', ['jquery'], '1.0.0', true); | |
| wp_localize_script('wedocs-helpful-feedback-frontend', 'wedocsHelpfulFeedback', [ | |
| 'ajax_url' => admin_url('admin-ajax.php'), | |
| 'nonce' => wp_create_nonce('wedocs_helpful_feedback_nonce') | |
| ]); | |
| // Enqueue frontend script and styles | |
| wp_enqueue_script('wedocs-helpful-feedback-frontend', plugin_dir_url(WEDOCS_FILE) . 'assets/js/helpful-feedback.js', ['jquery'], WEDOCS_VERSION, true); | |
| wp_localize_script('wedocs-helpful-feedback-frontend', 'wedocsHelpfulFeedback', [ | |
| 'ajax_url' => admin_url('admin-ajax.php'), | |
| 'nonce' => wp_create_nonce('wedocs_helpful_feedback_nonce') | |
| ]); |
🤖 Prompt for AI Agents
In assets/build/blocks/HelpfulFeedback/render.php around lines 213 to 218, the
script enqueue uses a hardcoded version and a malformed path with "./"; change
the path to concatenate plugin_dir_url(WEDOCS_FILE) with
'assets/js/helpful-feedback.js' (remove the "./") and replace the hardcoded
'1.0.0' with the plugin version constant (e.g., WEDOCS_VERSION); if there are
matching style enqueues nearby, apply the same path and version constant changes
for consistency and predictable caching.
| #<?php echo esc_attr($block_id); ?> .wedocs-feedback-voted-option { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 8px; | ||
| justify-content: <?php echo esc_attr($alignment); ?>; | ||
| margin-bottom: 10px; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix flex alignment mapping here as well
Mirror the mapping fix from the src version so “right” aligns correctly.
- justify-content: <?php echo esc_attr($alignment); ?>;
+ justify-content: <?php
+ $justify = 'center';
+ if ( 'left' === $alignment ) { $justify = 'flex-start'; }
+ if ( 'right' === $alignment ) { $justify = 'flex-end'; }
+ echo esc_attr( $justify );
+ ?>;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #<?php echo esc_attr($block_id); ?> .wedocs-feedback-voted-option { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| justify-content: <?php echo esc_attr($alignment); ?>; | |
| margin-bottom: 10px; | |
| } | |
| #<?php echo esc_attr($block_id); ?> .wedocs-feedback-voted-option { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| justify-content: <?php | |
| $justify = 'center'; | |
| if ( 'left' === $alignment ) { $justify = 'flex-start'; } | |
| if ( 'right' === $alignment ) { $justify = 'flex-end'; } | |
| echo esc_attr( $justify ); | |
| ?>; | |
| margin-bottom: 10px; | |
| } |
🤖 Prompt for AI Agents
In assets/build/blocks/HelpfulFeedback/render.php around lines 388–394, the CSS
is using the raw $alignment value for justify-content which causes "right" to
not align correctly; replace the direct echo of $alignment with a mapped value
(e.g., map 'left' => 'flex-start', 'center' => 'center', 'right' => 'flex-end')
and echo that mapped value with a sensible default fallback (such as
'flex-start') so the built file mirrors the src alignment fix.
| const thankYouContent = ` | ||
| <div class="wedocs-feedback-thank-you"> | ||
| <div class="wedocs-feedback-title" style="color: #28a745; margin-bottom: 16px;"> | ||
| ${thankYouMessage} | ||
| </div> | ||
| <div class="wedocs-feedback-result"> | ||
| <div class="wedocs-feedback-voted-option voted-${vote}" style="display: flex; align-items: center; gap: 8px; justify-content: ${$container.hasClass('left') ? 'flex-start' : $container.hasClass('right') ? 'flex-end' : 'center'}; margin-bottom: 10px;"> | ||
| <span class="wedocs-feedback-icon">${voteIcon}</span> | ||
| ${voteText} | ||
| </div> | ||
| ${voteSummary} | ||
| </div> | ||
| </div> | ||
| `; | ||
|
|
||
| // Fade out buttons and fade in thank you message | ||
| $buttons.fadeOut(300, function() { | ||
| $title.fadeOut(200, function() { | ||
| $container.append(thankYouContent); | ||
| $container.find('.wedocs-feedback-thank-you').hide().fadeIn(400); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Escape thankYouMessage to prevent XSS.
thankYouMessage comes from markup attributes; avoid inserting it as HTML. Build DOM with text nodes instead of string HTML.
Apply this change:
- const thankYouContent = `
- <div class="wedocs-feedback-thank-you">
- <div class="wedocs-feedback-title" style="color: #28a745; margin-bottom: 16px;">
- ${thankYouMessage}
- </div>
- <div class="wedocs-feedback-result">
- <div class="wedocs-feedback-voted-option voted-${vote}" style="display: flex; align-items: center; gap: 8px; justify-content: ${$container.hasClass('left') ? 'flex-start' : $container.hasClass('right') ? 'flex-end' : 'center'}; margin-bottom: 10px;">
- <span class="wedocs-feedback-icon">${voteIcon}</span>
- ${voteText}
- </div>
- ${voteSummary}
- </div>
- </div>
- `;
+ const $thankYou = $('<div/>', { class: 'wedocs-feedback-thank-you' });
+ const $titleDiv = $('<div/>', {
+ class: 'wedocs-feedback-title',
+ style: 'color:#28a745;margin-bottom:16px;'
+ }).text(thankYouMessage);
+ const $result = $('<div/>', { class: 'wedocs-feedback-result' });
+ const $voted = $('<div/>', {
+ class: `wedocs-feedback-voted-option voted-${vote}`,
+ style: `display:flex;align-items:center;gap:8px;justify-content:${$container.hasClass('left') ? 'flex-start' : $container.hasClass('right') ? 'flex-end' : 'center'};margin-bottom:10px;`
+ }).append($('<span/>', { class: 'wedocs-feedback-icon', html: voteIcon })).append(document.createTextNode(' ' + voteText));
+ $result.append($voted);
+ if (voteSummary) {
+ $result.append($(voteSummary));
+ }
+ $thankYou.append($titleDiv).append($result);And below:
- $container.append(thankYouContent);
- $container.find('.wedocs-feedback-thank-you').hide().fadeIn(400);
+ $container.append($thankYou.hide());
+ $thankYou.fadeIn(400);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const thankYouContent = ` | |
| <div class="wedocs-feedback-thank-you"> | |
| <div class="wedocs-feedback-title" style="color: #28a745; margin-bottom: 16px;"> | |
| ${thankYouMessage} | |
| </div> | |
| <div class="wedocs-feedback-result"> | |
| <div class="wedocs-feedback-voted-option voted-${vote}" style="display: flex; align-items: center; gap: 8px; justify-content: ${$container.hasClass('left') ? 'flex-start' : $container.hasClass('right') ? 'flex-end' : 'center'}; margin-bottom: 10px;"> | |
| <span class="wedocs-feedback-icon">${voteIcon}</span> | |
| ${voteText} | |
| </div> | |
| ${voteSummary} | |
| </div> | |
| </div> | |
| `; | |
| // Fade out buttons and fade in thank you message | |
| $buttons.fadeOut(300, function() { | |
| $title.fadeOut(200, function() { | |
| $container.append(thankYouContent); | |
| $container.find('.wedocs-feedback-thank-you').hide().fadeIn(400); | |
| }); | |
| const $thankYou = $('<div/>', { class: 'wedocs-feedback-thank-you' }); | |
| const $titleDiv = $('<div/>', { | |
| class: 'wedocs-feedback-title', | |
| style: 'color:#28a745;margin-bottom:16px;' | |
| }).text(thankYouMessage); | |
| const $result = $('<div/>', { class: 'wedocs-feedback-result' }); | |
| const $voted = $('<div/>', { | |
| class: `wedocs-feedback-voted-option voted-${vote}`, | |
| style: `display:flex;align-items:center;gap:8px;justify-content:${$container.hasClass('left') ? 'flex-start' : $container.hasClass('right') ? 'flex-end' : 'center'};margin-bottom:10px;` | |
| }).append($('<span/>', { class: 'wedocs-feedback-icon', html: voteIcon })).append(document.createTextNode(' ' + voteText)); | |
| $result.append($voted); | |
| if (voteSummary) { | |
| $result.append($(voteSummary)); | |
| } | |
| $thankYou.append($titleDiv).append($result); | |
| // Fade out buttons and fade in thank you message | |
| $buttons.fadeOut(300, function() { | |
| $title.fadeOut(200, function() { | |
| $container.append($thankYou.hide()); | |
| $thankYou.fadeIn(400); | |
| }); |
🤖 Prompt for AI Agents
In assets/js/helpful-feedback.js around lines 141 to 161, the template string
injects thankYouMessage directly into HTML which risks XSS; replace the
string-concatenation approach with DOM construction so the thankYouMessage is
inserted as a text node (e.g., create the .wedocs-feedback-thank-you and child
elements with jQuery/DOM APIs and use .text() or textContent for
thankYouMessage) instead of embedding it in the HTML string, then append and
fade the constructed nodes as before; ensure any other user-derived strings used
there are likewise inserted via text methods rather than raw HTML.
| $post_id = intval( $_POST['post_id'] ?? 0 ); | ||
| $vote = sanitize_text_field( $_POST['vote'] ?? '' ); | ||
| $allow_anonymous = filter_var( $_POST['allow_anonymous'] ?? true, FILTER_VALIDATE_BOOLEAN ); | ||
|
|
||
| if ( ! $post_id || ! in_array( $vote, [ 'yes', 'no' ] ) ) { | ||
| wp_send_json_error( [ | ||
| 'message' => __( 'Invalid voting data.', 'wedocs' ) | ||
| ] ); | ||
| } | ||
|
|
||
| // Verify this is a docs post | ||
| if ( get_post_type( $post_id ) !== 'docs' ) { | ||
| wp_send_json_error( [ | ||
| 'message' => __( 'Invalid post type.', 'wedocs' ) | ||
| ] ); | ||
| } | ||
|
|
||
| // Get current user ID and IP | ||
| $user_id = get_current_user_id(); | ||
| $user_ip = $_SERVER['REMOTE_ADDR'] ?? ''; | ||
|
|
||
| // Check if user can vote | ||
| if ( ! $user_id && ! $allow_anonymous ) { | ||
| wp_send_json_error( [ | ||
| 'message' => __( 'You must be logged in to vote.', 'wedocs' ) | ||
| ] ); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Auth bypass: do not trust client‑provided allow_anonymous.
Clients can set allow_anonymous=true to bypass the login requirement. Derive this from the block configuration for the target post instead of $_POST.
Apply this change and helper:
- $vote = sanitize_text_field( $_POST['vote'] ?? '' );
- $allow_anonymous = filter_var( $_POST['allow_anonymous'] ?? true, FILTER_VALIDATE_BOOLEAN );
+ $vote = sanitize_text_field( $_POST['vote'] ?? '' );
+ // Derive from block attributes in post content; default to true if not found.
+ $allow_anonymous = (bool) $this->get_helpful_feedback_setting( $post_id, 'allowAnonymous', true );Add this helper inside the class (outside any method):
/**
* Read Helpful Feedback block attribute from post content.
*
* @param int $post_id
* @param string $key
* @param mixed $default
* @return mixed
*/
private function get_helpful_feedback_setting( $post_id, $key, $default = null ) {
$post = get_post( $post_id );
if ( ! $post ) {
return $default;
}
$blocks = parse_blocks( $post->post_content );
foreach ( $blocks as $block ) {
if ( isset( $block['blockName'] ) && 'wedocs/helpful-feedback' === $block['blockName'] ) {
if ( isset( $block['attrs'][ $key ] ) ) {
return $block['attrs'][ $key ];
}
}
}
return $default;
}This prevents clients from toggling auth in transit and honors the editor setting.
🤖 Prompt for AI Agents
In includes/Ajax.php around lines 268 to 295, the current code trusts a
client-supplied $_POST['allow_anonymous'], allowing an auth bypass; replace that
by reading the post's Helpful Feedback block attribute and use it to determine
whether anonymous voting is allowed. Add the provided private helper
get_helpful_feedback_setting($post_id, $key, $default = null) inside the class
(outside any method), then call it for the key that controls anonymous voting
(e.g. 'allowAnonymous' or the actual attribute name used) to set
$allow_anonymous instead of filter_var($_POST['allow_anonymous'] ...), and keep
the existing login check logic unchanged.
| #<?php echo esc_attr($block_id); ?> .wedocs-feedback-voted-option { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 8px; | ||
| justify-content: <?php echo esc_attr($alignment); ?>; | ||
| margin-bottom: 10px; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix invalid flex alignment (justify-content)
justify-content doesn’t accept “left/right”. Map to flex values to render correctly for “right”.
Apply:
- #<?php echo esc_attr($block_id); ?> .wedocs-feedback-voted-option {
+ #<?php echo esc_attr($block_id); ?> .wedocs-feedback-voted-option {
display: flex;
align-items: center;
gap: 8px;
- justify-content: <?php echo esc_attr($alignment); ?>;
+ justify-content: <?php
+ $justify = 'center';
+ if ( 'left' === $alignment ) { $justify = 'flex-start'; }
+ if ( 'right' === $alignment ) { $justify = 'flex-end'; }
+ echo esc_attr( $justify );
+ ?>;
margin-bottom: 10px;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #<?php echo esc_attr($block_id); ?> .wedocs-feedback-voted-option { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| justify-content: <?php echo esc_attr($alignment); ?>; | |
| margin-bottom: 10px; | |
| } | |
| #<?php echo esc_attr($block_id); ?> .wedocs-feedback-voted-option { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| justify-content: <?php | |
| $justify = 'center'; | |
| if ( 'left' === $alignment ) { $justify = 'flex-start'; } | |
| if ( 'right' === $alignment ) { $justify = 'flex-end'; } | |
| echo esc_attr( $justify ); | |
| ?>; | |
| margin-bottom: 10px; | |
| } |
🤖 Prompt for AI Agents
In src/blocks/HelpfulFeedback/render.php around lines 388 to 394, the CSS uses
justify-content: <?php echo esc_attr($alignment); ?> but justify-content does
not accept "left"/"right"; map those values to valid flex values (use
"flex-start" for "left", "flex-end" for "right", keep "center" as-is) and output
the mapped value instead of the raw $alignment (e.g., compute a safe $justify =
$alignment === 'left' ? 'flex-start' : ($alignment === 'right' ? 'flex-end' :
$alignment) and echo esc_attr($justify)).
There was a problem hiding this 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)
includes/Ajax.php (1)
262-298: Critical auth bypass: client-controlled allow_anonymousLine 273 reads
allow_anonymousfrom$_POST, allowing attackers to bypass authentication by simply settingallow_anonymous=truein the request. This security flaw was flagged in previous reviews but remains unaddressed.Fix: Derive
allow_anonymousfrom the block's saved attributes in the post content rather than trusting client input. The block configuration is stored server-side and cannot be manipulated by clients.🔎 Recommended fix
Add a helper method to read the block attribute from post content:
/** * Read Helpful Feedback block attribute from post content. * * @param int $post_id * @param string $key * @param mixed $default * @return mixed */ private function get_helpful_feedback_setting( $post_id, $key, $default = null ) { $post = get_post( $post_id ); if ( ! $post ) { return $default; } $blocks = parse_blocks( $post->post_content ); foreach ( $blocks as $block ) { if ( isset( $block['blockName'] ) && 'wedocs/helpful-feedback' === $block['blockName'] ) { if ( isset( $block['attrs'][ $key ] ) ) { return $block['attrs'][ $key ]; } } } return $default; }Then replace line 273:
- $allow_anonymous = filter_var( $_POST['allow_anonymous'] ?? true, FILTER_VALIDATE_BOOLEAN ); + $allow_anonymous = (bool) $this->get_helpful_feedback_setting( $post_id, 'allowAnonymous', true );Based on learnings from past review comments.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
assets/build/block.jsassets/build/index.cssincludes/Ajax.phpwedocs.php
🚧 Files skipped from review as they are similar to previous changes (1)
- wedocs.php
🧰 Additional context used
🧬 Code graph analysis (1)
assets/build/block.js (2)
assets/build/editor.js (2)
l(1-1)a(1-1)src/blocks/DocsGrid/edit.js (1)
wp(16-16)
🪛 Biome (2.1.2)
assets/build/block.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)
🔇 Additional comments (3)
assets/build/index.css (1)
2996-2996: LGTM: CSS formatting and responsive artboard additionsThe selector formatting fix and new horizontal phone artboard sizes are well-structured and follow consistent patterns. These additions support multi-device responsive layouts for the UI components.
Also applies to: 3007-3036
includes/Ajax.php (1)
264-268: Well-implemented vote handling logicThe nonce verification, input validation, post type checking, duplicate vote prevention, and vote recording logic are well-structured. The extensibility hook at line 352 is a nice touch for future enhancements.
The combination of cookie-based, user meta, and IP-based tracking provides reasonable protection against duplicate votes (acknowledging the IP limitations noted separately).
Also applies to: 275-286, 299-361
assets/build/block.js (1)
1-1: Built file - source review recommendedThis is a minified/built JavaScript file. Meaningful code review should be performed on the source files (e.g.,
src/blocks/HelpfulFeedback/) rather than the compiled output.The static analysis "used before declaration" errors are false positives common in bundled code due to JavaScript hoisting and the way webpack structures modules.
To properly review the block logic, please examine the source files:
src/blocks/HelpfulFeedback/edit.jssrc/blocks/HelpfulFeedback/save.jssrc/blocks/HelpfulFeedback/view.jssrc/blocks/DocsGrid/edit.jsThese would provide better context for the actual changes and logic being introduced.
| // Get current user ID and IP | ||
| $user_id = get_current_user_id(); | ||
| $user_ip = $_SERVER['REMOTE_ADDR'] ?? ''; | ||
|
|
||
| // Check if user can vote | ||
| if ( ! $user_id && ! $allow_anonymous ) { | ||
| wp_send_json_error( [ | ||
| 'message' => __( 'You must be logged in to vote.', 'wedocs' ) | ||
| ] ); | ||
| } | ||
|
|
||
| // Check if user has already voted | ||
| $has_voted = false; | ||
|
|
||
| // Check cookie-based tracking (for compatibility with existing system) | ||
| $previous = isset( $_COOKIE['wedocs_response'] ) ? explode( ',', $_COOKIE['wedocs_response'] ) : []; | ||
| if ( in_array( $post_id, $previous ) ) { | ||
| $has_voted = true; | ||
| } | ||
|
|
||
| // Check user-specific voting records | ||
| if ( ! $has_voted && $user_id ) { | ||
| // Check by user ID | ||
| $user_vote = get_post_meta( $post_id, "wedocs_helpful_vote_user_{$user_id}", true ); | ||
| if ( $user_vote ) { | ||
| $has_voted = true; | ||
| } | ||
| } elseif ( ! $has_voted && $allow_anonymous && $user_ip ) { | ||
| // Check by IP for anonymous users | ||
| $ip_vote = get_post_meta( $post_id, "wedocs_helpful_vote_ip_" . md5( $user_ip ), true ); | ||
| if ( $ip_vote ) { | ||
| $has_voted = true; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the Ajax.php file
fd "Ajax.php" --type fRepository: weDevsOfficial/wedocs-plugin
Length of output: 89
🏁 Script executed:
#!/bin/bash
# Read the specific lines mentioned in the review (lines 288-338 to see all mentioned usages)
if [ -f "includes/Ajax.php" ]; then
wc -l includes/Ajax.php
echo "--- Lines 288-340 of includes/Ajax.php ---"
sed -n '288,340p' includes/Ajax.php
fiRepository: weDevsOfficial/wedocs-plugin
Length of output: 2217
🏁 Script executed:
#!/bin/bash
# Search for all occurrences of REMOTE_ADDR and X-Forwarded-For in the file
echo "=== REMOTE_ADDR occurrences ==="
rg "REMOTE_ADDR" includes/ -n
echo ""
echo "=== X-Forwarded-For occurrences ==="
rg "X_FORWARDED_FOR|X-Forwarded-For" includes/ -nRepository: weDevsOfficial/wedocs-plugin
Length of output: 689
🏁 Script executed:
#!/bin/bash
# Let me check the functions.php file to see if there's already an IP utility function
echo "--- Lines 360-380 of includes/functions.php (context around X-Forwarded-For) ---"
sed -n '360,380p' includes/functions.phpRepository: weDevsOfficial/wedocs-plugin
Length of output: 922
🏁 Script executed:
#!/bin/bash
# Get a fuller view of the IP handling function in functions.php
echo "--- Search for function definition near the IP handling code ---"
sed -n '350,390p' includes/functions.phpRepository: weDevsOfficial/wedocs-plugin
Length of output: 1179
🏁 Script executed:
#!/bin/bash
# Check if wedocs_get_ip_address() is being used elsewhere in the codebase
echo "=== Usage of wedocs_get_ip_address() function ==="
rg "wedocs_get_ip_address" --type php -nRepository: weDevsOfficial/wedocs-plugin
Length of output: 326
🏁 Script executed:
#!/bin/bash
# Check if there are other places in Ajax.php that might be using this pattern
echo "=== All IP-related code in Ajax.php ==="
rg "\\\$_SERVER\['REMOTE_ADDR" includes/Ajax.php -B 2 -A 2 -nRepository: weDevsOfficial/wedocs-plugin
Length of output: 296
🏁 Script executed:
#!/bin/bash
# Check for any IP validation in the existing wedocs_get_ip_address function or surrounding code
echo "=== Full wedocs_get_ip_address function and context ==="
sed -n '355,380p' includes/functions.phpRepository: weDevsOfficial/wedocs-plugin
Length of output: 955
🏁 Script executed:
#!/bin/bash
# Check if there are any other security/validation measures around IP in the codebase
echo "=== Search for filter_var or IP validation patterns ==="
rg "filter_var.*FILTER_VALIDATE_IP" includes/ -n
echo ""
echo "=== Search for IP sanitization ==="
rg "sanitize.*ip|validate.*ip" includes/ -i -nRepository: weDevsOfficial/wedocs-plugin
Length of output: 337
Use the existing wedocs_get_ip_address() function instead of direct $_SERVER access
Line 290 directly accesses $_SERVER['REMOTE_ADDR'] for IP-based duplicate-vote prevention, but the codebase already has a wedocs_get_ip_address() function (in includes/functions.php) that properly handles multiple proxy headers (HTTP_CLIENT_IP, X-Forwarded-For, etc.).
Replace line 290 with:
$user_ip = wedocs_get_ip_address();This reuses existing infrastructure instead of duplicating IP detection logic.
Note: The wedocs_get_ip_address() function itself lacks IP validation; consider adding filter_var($ipaddress, FILTER_VALIDATE_IP) checks in that function to reject malformed IPs.
🤖 Prompt for AI Agents
In includes/Ajax.php around lines 288 to 321, replace the direct
$_SERVER['REMOTE_ADDR'] access with a call to the project helper
wedocs_get_ip_address() so IP-based duplicate-vote checks use the centralized
proxy-aware resolver; specifically change the assignment of $user_ip to use
wedocs_get_ip_address() and keep the subsequent md5/post_meta IP check logic
intact, and (optionally) ensure the helper performs FILTER_VALIDATE_IP
validation so malformed values are rejected before storing/checking.
Fixes 199
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.