Elementor widgets#324
Conversation
Register and include several new Elementor widgets and add AJAX endpoints/handlers. - Added new Elementor widgets: DocsSidebar, NeedHelp, SearchModal, TableOfContents, WasThisHelpful (full widget implementations added under includes/Elementor/Widgets). - Updated includes/Elementor.php to require and register the new widgets with Elementor. - Extended includes/Ajax.php to register AJAX actions and implemented handlers for: - wedocs_helpful_vote: records yes/no votes using post meta (_wedocs_helpful_yes/_wedocs_helpful_no) with nonce verification. - wedocs_helpful_feedback: saves textual negative feedback into post meta (_wedocs_helpful_feedback) with nonce verification. - wedocs_need_help_submit: processes the "Need More Help" contact form (nonce check per widget), sends email to recipient (falls back to admin email), and optionally saves submission to Elementor Pro submissions if available. - Added private helper save_to_elementor_submissions() to persist form entries to Elementor Pro Submissions DB (checks for Elementor Pro class first). Security: nonces and input sanitization are applied; email/recipient fallbacks handled. Widgets include styles, scripts and content templates for Elementor editor preview.
Register and include three new Elementor widgets (DocNavigation, DocsBreadcrumb, DocsHamburgerMenu) by updating includes/Elementor.php. Add full widget implementations for next/previous doc navigation, docs breadcrumb, and an off-canvas/hamburger docs menu including controls, rendering, editor preview templates and inline styles. Also adjust responsive styles in src/assets/less/responsive.less to support the new widgets.
Switch feedback storage to unified 'positive'/'negative' meta keys (Ajax, admin metabox, list table) and update code to read/write those keys. Add editor-aware context and preview rendering for Elementor widgets (DocNavigation, DocsBreadcrumb, DocsSidebar), plus new responsive controls (mobile stacking/toggle/hamburger) and related CSS for better editor UX and mobile behavior. Include a new three_column Elementor template and multiple formatting/whitespace cleanups across API and admin code. These changes improve consistency of feedback data and provide accurate previews in Elementor editor.
Register template import flow and editor scripts for weDocs. Adds init_templates hook, admin-only enqueue for Elementor editor, and functions to import a three_column template from JSON, fix/normalize existing template meta formats, set template conditions, reset imports for development, and get template version. Marks imported templates with _wedocs_template and uses an option (wedocs_elementor_templates_imported) to avoid re-importing. Also includes minor code style tweaks and updates three_column.json to a validated, pretty-printed template format.
WalkthroughAdds seven new Elementor widgets (DocsSidebar, DocsHamburgerMenu, DocNavigation, DocsBreadcrumb, TableOfContents, SearchModal, WasThisHelpful, NeedHelp), three new AJAX handlers for voting/feedback/contact, a full Elementor template-import lifecycle with a bundled three-column JSON template, responsive CSS for the search modal, and minor formatting/type-cast fixes in existing admin and API files. ChangesElementor Widgets, AJAX Handlers & Template Lifecycle
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ 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)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 PHPStan (2.2.2)PHP Fatal error: Uncaught Error: Undefined constant "ABSPATH" in /includes/functions.php:423 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.
Actionable comments posted: 12
🧹 Nitpick comments (2)
includes/Elementor.php (1)
117-120: ⚡ Quick winAvoid normalizing template meta on every request.
fix_existing_template_conditions()runs before the import flag check, so everyinitperforms an unboundedelementor_libraryquery while Elementor is active. Gate this behind a versioned “normalized” option or run it only from an admin/upgrade path.Also applies to: 158-164
🤖 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 `@includes/Elementor.php` around lines 117 - 120, The `fix_existing_template_conditions()` method is being called on every request during init, which causes unnecessary unbounded queries to the elementor_library table. Add a versioned option check before calling this method in `import_default_templates()` to track whether the template conditions have already been normalized. Only execute `fix_existing_template_conditions()` if the stored version is outdated or the option doesn't exist yet, and update the option after successful execution. This prevents the expensive operation from running on every init and improves performance.includes/Elementor/Widgets/NeedHelp.php (1)
821-821: 💤 Low valueConsider handling the case when
get_the_ID()returns false.If this widget is used outside a post context (e.g., on a custom Elementor template not tied to a post),
get_the_ID()returnsfalse, which becomes the string'false'in JavaScript. This is sent to the server aspost_id: 'false'.🛡️ Suggested defensive fix
- post_id: <?php echo get_the_ID(); ?>, + post_id: <?php echo (int) get_the_ID(); ?>,🤖 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 `@includes/Elementor/Widgets/NeedHelp.php` at line 821, The post_id field in the JavaScript object assignment is vulnerable to receiving false when get_the_ID() returns false outside a post context, which converts to the string 'false' in JavaScript. Add a defensive check to handle the case when get_the_ID() returns false, either by conditionally including the post_id field only when a valid ID exists, or by using a ternary operator to provide a fallback value (such as null or 0) when no post context is available. This ensures that invalid post_id values are not sent to the server.
🤖 Prompt for all review comments with 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.
Inline comments:
In `@includes/Ajax.php`:
- Around line 593-611: The handle_helpful_vote() method lacks duplicate vote
prevention mechanisms that exist in the handle_helpful_feedback_vote() method.
Add validation to check if the post is of type 'docs' using get_post_type()
similar to line 417. Then implement duplicate vote prevention by checking for
existing votes using cookie-based tracking (via isset($_COOKIE)), user meta
checks (if user is logged in), and IP-based meta checks to prevent the same user
or IP from voting multiple times on the same post. Store the vote tracking
information using update_post_meta() or setcookie() as appropriate after a
successful vote, and return an error response if a duplicate vote is detected
before incrementing the vote counts.
- Around line 616-639: The handle_helpful_feedback method lacks post type
validation, allowing feedback to be submitted for any post ID rather than only
docs post type. After retrieving the post_id and before processing the feedback,
add validation to check that the post exists and is of type docs using get_post
and verify its post_type property equals docs, returning an appropriate error if
validation fails, similar to the pattern used in handle_helpful_vote.
In `@includes/Elementor.php`:
- Around line 139-151: The template import sequence has a race condition where
concurrent requests can both pass the initial
`get_option('wedocs_elementor_templates_imported')` check and proceed to call
`import_template_from_json()`. Replace the initial `get_option()` check with an
atomic `add_option()` call that attempts to claim a lock for the import
process—if `add_option()` returns false (indicating the option already exists),
return early. This ensures only one request can proceed past the lock and
execute the `import_template_from_json()` and `set_template_conditions()` calls,
eliminating the race window.
- Around line 144-151: The `set_template_conditions()` call for 'single_docs' is
being applied immediately after importing the template without checking for
existing conflicting Theme Builder templates, which can cause condition
conflicts on Elementor Pro sites. Before calling
`set_template_conditions($template_id, 'single_docs')`, implement a check to
verify that no existing Elementor templates already target the same
'single_docs' conditions. If conflicting templates are detected, either skip the
auto-assignment of conditions or defer it to an explicit admin action during
onboarding rather than automatic import. Apply this same logic to the additional
location mentioned (lines 265-275) where similar template condition assignments
occur.
- Around line 203-207: The `_elementor_data` JSON being stored via
`update_post_meta()` and `meta_input` in `wp_insert_post()` needs to be wrapped
with `wp_slash()` to prevent WordPress from stripping escaped quotes and
backslashes during storage. Modify the `update_post_meta()` call to wrap
`wp_json_encode($elementor_data)` with `wp_slash()`, and apply the same fix to
the `meta_input` array entry for `_elementor_data` around line 270 in the same
file.
In `@includes/Elementor/Templates/three_column.json`:
- Around line 122-123: Remove or replace the hardcoded developer email address
in the recipient_email field within the three_column.json template. The field
currently contains "dev-email@wpengine.local" which will cause contact form
submissions from imported sites to be sent to the wrong destination. Either set
the recipient_email value to an empty string, a placeholder value, or remove the
field entirely so users must configure their own email address during site
import.
In `@includes/Elementor/Widgets/DocNavigation.php`:
- Around line 266-286: The selectors in the title_color and title_hover_color
controls are targeting a non-existent anchor element structure. The selectors
currently use {{WRAPPER}} .wedocs-el-nav__title a and {{WRAPPER}}
.wedocs-el-nav__title a:hover, but the actual rendered output places plain text
directly inside .wedocs-el-nav__title without an anchor wrapper. Update both
selector strings to target .wedocs-el-nav__title directly (removing the a tag
reference and changing a:hover to just :hover) to match the actual rendered HTML
structure so the color controls will apply properly.
- Around line 400-413: The prev and next post lookup queries in the
DocNavigation widget use only menu_order for comparison, which fails when
sibling documents share the same menu_order value. Fix the $next_query and
$prev_query strings by adding a deterministic ID-based tie-breaker: append AND
ID > {$current_post->ID} to the $next_query condition and AND ID <
{$current_post->ID} to the $prev_query condition. Additionally, convert both
queries from direct string interpolation to use $wpdb->prepare() for safe
parameterized query execution instead of the current concatenation approach.
In `@includes/Elementor/Widgets/DocsBreadcrumb.php`:
- Around line 377-384: The while loop in the breadcrumb traversal does not check
if get_post($parent_id) returns null before accessing $page properties on lines
380 and 383. Add a null check immediately after the get_post() call to verify
that $page exists, and if it does not (indicating an orphaned post_parent),
break out of the loop to prevent null reference errors when accessing $page->ID
and dereferencing it for get_the_title() and get_permalink().
- Around line 390-393: In the DocsBreadcrumb widget where the current breadcrumb
item is being added to the items array, replace the `get_the_title()` call
(which relies on global post state) with a call to get the title from the
resolved docs context. This ensures the correct current label is displayed in
editor and fallback contexts where the global post state may be unreliable.
Identify what the resolved docs context variable or method is in the class and
use that instead of the global post-dependent function.
In `@includes/Elementor/Widgets/DocsHamburgerMenu.php`:
- Around line 404-416: The code in the DocsHamburgerMenu widget directly
accesses properties on the global $post variable without first verifying that
$post is a valid WP_Post object. Add a guard check at the beginning of this
method to verify that $post exists and is an object before proceeding with the
logic that accesses $post->post_parent and $post->ID. If $post is not valid, the
method should handle this gracefully by returning early or using a safe default
value instead of attempting to access the parent document logic.
- Around line 458-460: In the panel header link section where the parent post is
rendered, apply proper output escaping to both the URL and text content. Wrap
the get_permalink($parent) function call with esc_url() to safely escape the
href attribute value, and wrap the get_post_field('post_title', $parent,
'display') call with esc_html() to safely escape the displayed title text within
the anchor tag.
---
Nitpick comments:
In `@includes/Elementor.php`:
- Around line 117-120: The `fix_existing_template_conditions()` method is being
called on every request during init, which causes unnecessary unbounded queries
to the elementor_library table. Add a versioned option check before calling this
method in `import_default_templates()` to track whether the template conditions
have already been normalized. Only execute `fix_existing_template_conditions()`
if the stored version is outdated or the option doesn't exist yet, and update
the option after successful execution. This prevents the expensive operation
from running on every init and improves performance.
In `@includes/Elementor/Widgets/NeedHelp.php`:
- Line 821: The post_id field in the JavaScript object assignment is vulnerable
to receiving false when get_the_ID() returns false outside a post context, which
converts to the string 'false' in JavaScript. Add a defensive check to handle
the case when get_the_ID() returns false, either by conditionally including the
post_id field only when a valid ID exists, or by using a ternary operator to
provide a fallback value (such as null or 0) when no post context is available.
This ensures that invalid post_id values are not sent to the server.
🪄 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: 0806c1e3-d6e6-4ea5-9024-1a17ca1b9e69
📒 Files selected for processing (14)
includes/API/API.phpincludes/Admin/Docs_List_Table.phpincludes/Ajax.phpincludes/Elementor.phpincludes/Elementor/Templates/three_column.jsonincludes/Elementor/Widgets/DocNavigation.phpincludes/Elementor/Widgets/DocsBreadcrumb.phpincludes/Elementor/Widgets/DocsHamburgerMenu.phpincludes/Elementor/Widgets/DocsSidebar.phpincludes/Elementor/Widgets/NeedHelp.phpincludes/Elementor/Widgets/SearchModal.phpincludes/Elementor/Widgets/TableOfContents.phpincludes/Elementor/Widgets/WasThisHelpful.phpsrc/assets/less/responsive.less
| public function handle_helpful_vote() { | ||
| check_ajax_referer('wedocs_helpful_vote', 'nonce'); | ||
|
|
||
| $post_id = intval($_POST['post_id'] ?? 0); | ||
| $vote = sanitize_text_field($_POST['vote'] ?? ''); | ||
|
|
||
| if (!$post_id || !in_array($vote, ['yes', 'no'], true)) { | ||
| wp_send_json_error(['message' => __('Invalid vote.', 'wedocs')]); | ||
| } | ||
|
|
||
| $meta_key = $vote === 'yes' ? 'positive' : 'negative'; | ||
| $current = (int) get_post_meta($post_id, $meta_key, true); | ||
| update_post_meta($post_id, $meta_key, $current + 1); | ||
|
|
||
| wp_send_json_success([ | ||
| 'yes' => (int) get_post_meta($post_id, 'positive', true), | ||
| 'no' => (int) get_post_meta($post_id, 'negative', true), | ||
| ]); | ||
| } |
There was a problem hiding this comment.
Missing duplicate vote prevention allows unlimited vote manipulation.
Unlike the existing handle_helpful_feedback_vote() method (lines 434-481) which implements cookie-based tracking, user meta, and IP meta checks to prevent duplicate votes, this handler has no such protection. Any user can call this endpoint repeatedly to artificially inflate positive or negative counts.
Additionally, there's no validation that $post_id refers to a docs post type (compare to line 417 in the existing handler).
🔒 Proposed fix: Add duplicate vote prevention
public function handle_helpful_vote() {
check_ajax_referer('wedocs_helpful_vote', 'nonce');
$post_id = intval($_POST['post_id'] ?? 0);
$vote = sanitize_text_field($_POST['vote'] ?? '');
if (!$post_id || !in_array($vote, ['yes', 'no'], true)) {
wp_send_json_error(['message' => __('Invalid vote.', 'wedocs')]);
}
+ // Verify this is a docs post
+ if (get_post_type($post_id) !== 'docs') {
+ wp_send_json_error(['message' => __('Invalid post type.', 'wedocs')]);
+ }
+
+ // Check for duplicate vote via cookie
+ $previous = isset($_COOKIE['wedocs_response']) ? explode(',', $_COOKIE['wedocs_response']) : [];
+ if (in_array($post_id, $previous, false)) {
+ wp_send_json_error([
+ 'already_voted' => true,
+ 'message' => __('You have already voted on this article.', 'wedocs'),
+ ]);
+ }
+
$meta_key = $vote === 'yes' ? 'positive' : 'negative';
$current = (int) get_post_meta($post_id, $meta_key, true);
update_post_meta($post_id, $meta_key, $current + 1);
+ // Set cookie to prevent duplicate votes
+ $previous[] = $post_id;
+ setcookie('wedocs_response', implode(',', $previous), time() + WEEK_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN);
+
wp_send_json_success([
'yes' => (int) get_post_meta($post_id, 'positive', true),
'no' => (int) get_post_meta($post_id, 'negative', true),
]);
}🤖 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 `@includes/Ajax.php` around lines 593 - 611, The handle_helpful_vote() method
lacks duplicate vote prevention mechanisms that exist in the
handle_helpful_feedback_vote() method. Add validation to check if the post is of
type 'docs' using get_post_type() similar to line 417. Then implement duplicate
vote prevention by checking for existing votes using cookie-based tracking (via
isset($_COOKIE)), user meta checks (if user is logged in), and IP-based meta
checks to prevent the same user or IP from voting multiple times on the same
post. Store the vote tracking information using update_post_meta() or
setcookie() as appropriate after a successful vote, and return an error response
if a duplicate vote is detected before incrementing the vote counts.
| public function handle_helpful_feedback() { | ||
| check_ajax_referer('wedocs_helpful_vote', 'nonce'); | ||
|
|
||
| $post_id = intval($_POST['post_id'] ?? 0); | ||
| $feedback = sanitize_textarea_field($_POST['feedback'] ?? ''); | ||
|
|
||
| if (!$post_id || empty($feedback)) { | ||
| wp_send_json_error(['message' => __('Invalid feedback.', 'wedocs')]); | ||
| } | ||
|
|
||
| $existing = get_post_meta($post_id, '_wedocs_helpful_feedback', true); | ||
| if (!is_array($existing)) { | ||
| $existing = []; | ||
| } | ||
|
|
||
| $existing[] = [ | ||
| 'feedback' => $feedback, | ||
| 'date' => current_time('mysql'), | ||
| 'ip' => sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? ''), | ||
| ]; | ||
|
|
||
| update_post_meta($post_id, '_wedocs_helpful_feedback', $existing); | ||
| wp_send_json_success(); | ||
| } |
There was a problem hiding this comment.
Add post type validation to prevent feedback on non-docs posts.
Similar to handle_helpful_vote(), this handler accepts any valid post ID without verifying it's a docs post type.
🛡️ Proposed fix
$post_id = intval($_POST['post_id'] ?? 0);
$feedback = sanitize_textarea_field($_POST['feedback'] ?? '');
if (!$post_id || empty($feedback)) {
wp_send_json_error(['message' => __('Invalid feedback.', 'wedocs')]);
}
+ if (get_post_type($post_id) !== 'docs') {
+ wp_send_json_error(['message' => __('Invalid post type.', 'wedocs')]);
+ }
+
$existing = get_post_meta($post_id, '_wedocs_helpful_feedback', true);🤖 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 `@includes/Ajax.php` around lines 616 - 639, The handle_helpful_feedback method
lacks post type validation, allowing feedback to be submitted for any post ID
rather than only docs post type. After retrieving the post_id and before
processing the feedback, add validation to check that the post exists and is of
type docs using get_post and verify its post_type property equals docs,
returning an appropriate error if validation fails, similar to the pattern used
in handle_helpful_vote.
| // Check if template import is needed | ||
| if (get_option('wedocs_elementor_templates_imported')) { | ||
| return; | ||
| } | ||
|
|
||
| $template_id = $this->import_template_from_json('three_column', 'weDocs - Single Doc Page'); | ||
|
|
||
| if ($template_id) { | ||
| // Set template conditions for single docs | ||
| $this->set_template_conditions($template_id, 'single_docs'); | ||
|
|
||
| // Mark as imported | ||
| update_option('wedocs_elementor_templates_imported', true); |
There was a problem hiding this comment.
Make the one-time import atomic.
The get_option() → wp_insert_post() → update_option() sequence has a race window: concurrent first requests can both insert the default template and attach the same conditions. Use an add_option() lock/claim or transient lock, then re-check before inserting.
Suggested locking shape
// Check if template import is needed
if (get_option('wedocs_elementor_templates_imported')) {
return;
}
- $template_id = $this->import_template_from_json('three_column', 'weDocs - Single Doc Page');
+ if (! add_option('wedocs_elementor_templates_import_lock', time(), '', 'no')) {
+ return;
+ }
+
+ try {
+ $template_id = $this->import_template_from_json('three_column', 'weDocs - Single Doc Page');
- if ($template_id) {
- // Set template conditions for single docs
- $this->set_template_conditions($template_id, 'single_docs');
+ if ($template_id) {
+ // Set template conditions for single docs
+ $this->set_template_conditions($template_id, 'single_docs');
- // Mark as imported
- update_option('wedocs_elementor_templates_imported', true);
+ // Mark as imported
+ update_option('wedocs_elementor_templates_imported', true);
+ }
+ } finally {
+ delete_option('wedocs_elementor_templates_import_lock');
}📝 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.
| // Check if template import is needed | |
| if (get_option('wedocs_elementor_templates_imported')) { | |
| return; | |
| } | |
| $template_id = $this->import_template_from_json('three_column', 'weDocs - Single Doc Page'); | |
| if ($template_id) { | |
| // Set template conditions for single docs | |
| $this->set_template_conditions($template_id, 'single_docs'); | |
| // Mark as imported | |
| update_option('wedocs_elementor_templates_imported', true); | |
| // Check if template import is needed | |
| if (get_option('wedocs_elementor_templates_imported')) { | |
| return; | |
| } | |
| if (! add_option('wedocs_elementor_templates_import_lock', time(), '', 'no')) { | |
| return; | |
| } | |
| try { | |
| $template_id = $this->import_template_from_json('three_column', 'weDocs - Single Doc Page'); | |
| if ($template_id) { | |
| // Set template conditions for single docs | |
| $this->set_template_conditions($template_id, 'single_docs'); | |
| // Mark as imported | |
| update_option('wedocs_elementor_templates_imported', true); | |
| } | |
| } finally { | |
| delete_option('wedocs_elementor_templates_import_lock'); | |
| } |
🤖 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 `@includes/Elementor.php` around lines 139 - 151, The template import sequence
has a race condition where concurrent requests can both pass the initial
`get_option('wedocs_elementor_templates_imported')` check and proceed to call
`import_template_from_json()`. Replace the initial `get_option()` check with an
atomic `add_option()` call that attempts to claim a lock for the import
process—if `add_option()` returns false (indicating the option already exists),
return early. This ensures only one request can proceed past the lock and
execute the `import_template_from_json()` and `set_template_conditions()` calls,
eliminating the race window.
| $template_id = $this->import_template_from_json('three_column', 'weDocs - Single Doc Page'); | ||
|
|
||
| if ($template_id) { | ||
| // Set template conditions for single docs | ||
| $this->set_template_conditions($template_id, 'single_docs'); | ||
|
|
||
| // Mark as imported | ||
| update_option('wedocs_elementor_templates_imported', true); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "Elementor.php" -type fRepository: weDevsOfficial/wedocs-plugin
Length of output: 97
🏁 Script executed:
wc -l includes/Elementor.phpRepository: weDevsOfficial/wedocs-plugin
Length of output: 99
🏁 Script executed:
sed -n '140,160p' includes/Elementor.phpRepository: weDevsOfficial/wedocs-plugin
Length of output: 762
🏁 Script executed:
sed -n '260,280p' includes/Elementor.phpRepository: weDevsOfficial/wedocs-plugin
Length of output: 1037
🏁 Script executed:
sed -n '1,335p' includes/Elementor.php | grep -n "set_template_conditions\|import_template_from_json" -A 20 | head -80Repository: weDevsOfficial/wedocs-plugin
Length of output: 2566
🏁 Script executed:
grep -n "function set_template_conditions\|function import_template_from_json" includes/Elementor.php -A 30Repository: weDevsOfficial/wedocs-plugin
Length of output: 2092
🌐 Web query:
Elementor Pro theme builder template precedence multiple templates single doc page
💡 Result:
In Elementor Pro, when multiple Theme Builder templates are eligible to be applied to a single page (i.e., they meet the display conditions), Elementor does not use a user-configurable priority order to resolve them [1][2]. Instead, it uses a deterministic hierarchy system based on the specificity of the conditions applied [1][3]. Key mechanisms governing template precedence include: Specificity Over Generality: Elementor follows a principle where more specific conditions take precedence over broader ones [1][3]. For example, a template with a condition set to a specific page or category will typically override a template with a broader "Entire Site" condition [4]. Condition Tree and Hierarchy: Elementor’s condition system is modeled after the WordPress Template Hierarchy [1][3]. When resolving which template to render, Elementor evaluates conditions based on their registered priority and group type [3]. While developers can register custom conditions and assign them a numerical priority (ranging from 0 to 100), the default behavior for built-in templates is to resolve based on the specificity of the match [3]. Exclusions: If a template is explicitly excluded from a specific page, that exclusion will override any inclusion condition, even if the inclusion condition is more specific [1]. Conflict Resolution: If two templates have identical specificity (e.g., both are set to "Entire Site"), Elementor's behavior is typically consistent but not user-configurable [2]. In such scenarios, if you encounter conflicts, the recommended best practice is to: 1. Audit your display conditions: Ensure that templates are not targeting overlapping sets of pages unintentionally [4]. 2. Use Exclusions: Explicitly exclude a template from pages where it should not appear to prevent it from conflicting with a more preferred template [1]. 3. Consolidate Templates: Where possible, merge conflicting templates into a single template that uses conditional logic or dynamic content to handle different page layouts, rather than maintaining multiple competing templates [5]. Note: Some third-party themes or Elementor Addon plugins may implement their own template priority managers, which can override Elementor’s default behavior [2]. If you are using such a plugin, you must check that specific plugin's settings to control template precedence [2].
Citations:
- 1: https://developers.elementor.com/docs/theme-conditions/
- 2: https://docs.cmsmasters.net/how-to-manage-template-priority-between-cmsmasters-elementor-addon-and-elementor-pro/
- 3: https://developers.elementor.com/docs/theme-conditions/condition-group-type/
- 4: https://elementor.com/help/the-elementor-theme-builder/
- 5: https://elementor.com/help/single-page-site-part/
🌐 Web query:
Elementor Pro elementor_library post status draft vs publish best practices bundled templates
💡 Result:
In Elementor, templates are stored in the WordPress database as custom posts with the post type elementor_library [1]. Understanding the difference between draft and publish statuses for these items is critical for both site management and functional reliability. Best Practices for Post Status 1. Published Status (Recommended for Active Templates): If a template is intended to be used as a global part of your site—such as a header, footer, single post layout, or maintenance mode page—it should be set to Publish [2]. Elementor features that rely on dynamic assignment (like Theme Builder locations) often require the template to be published to function correctly or to be recognized in site-wide settings [2]. 2. Draft Status (Recommended for Work-in-Progress): Keeping templates in Draft status is best practice while you are actively designing or testing new layouts. This prevents them from being accidentally selected or interfering with live site settings. However, be aware that some users have reported that Elementor’s interface may still flag or warn about template conflicts even if the conflicting template is in Draft status [3]. If you encounter persistent issues with template assignments, ensure the unnecessary draft is completely removed (trashed) rather than just saved as a draft [3]. Key Functional Differences - Active Integration: Elements like Maintenance Mode, 404 pages, or Header/Footer builders generally require the template to have a Published status to successfully bind to the site's front-end display [2]. - Reusability: When saving a page as a template for manual insertion (using the Library icon in the editor), the status of the template is less critical for the insertion process itself, but keeping your library organized by publishing only "ready" versions is a recommended workflow habit to avoid confusion [4][5]. - Database Handling: Because Elementor templates are standard WordPress posts, they follow standard WordPress post status behavior [1]. You can manage them via the standard WordPress dashboard or the Elementor Templates menu [1]. General Workflow Advice - Organize and Cleanup: Regularly audit your Elementor Library. If a draft is no longer needed, move it to the trash to avoid potential configuration conflicts or warnings in the Theme Builder [3]. - Testing: Before publishing a new site-wide template, use the Preview functionality within the Elementor editor to ensure it displays correctly [5]. Once verified, update the status to Publish to make it active for your site's visitors [5]. If you are using programmatic methods (such as the WP-CLI or REST API) to manage templates, always verify the status field, as automated processes often expect a Publish status to successfully apply theme templates or site-wide overrides [2][6].
Citations:
- 1: https://themewaves.com/how-to-find-your-template-drafts-in-elementor/
- 2: https://github.com/mwender/elementor-mcp-skill/blob/main/references/maintenance-mode.md
- 3: Bug report: Theme builder does not ignore draft templates elementor/elementor#8229
- 4: https://elementor.com/help/create-templates-for-faster-website-building/
- 5: https://www.likacloud.com/en/docs/wordpress/wp-themes/elementor-save-publish-tips/
- 6: Internal: REST API for Elementor post meta, user meta and settings [ED-17682] elementor/elementor#29693
🏁 Script executed:
grep -r "elementor_conditions\|theme.*builder\|precedence" includes/ --include="*.php" | head -20Repository: weDevsOfficial/wedocs-plugin
Length of output: 405
Auto-apply template conditions only after checking for existing Theme Builder templates.
On first init, this template is published and immediately assigned to include/single/docs conditions, which can conflict with existing Theme Builder templates targeting the same doc pages on Elementor Pro sites. Elementor's template precedence is based on condition specificity—when two templates have identical specificity (both targeting all single docs), conflict resolution is not user-configurable.
Instead of relying on draft status (which breaks Theme Builder integration), verify no conflicting templates exist before auto-assigning conditions, or defer condition assignment to explicit admin action during onboarding.
Safer approach: Check for existing templates before applying conditions
if ($template_id) {
- // Set template conditions for single docs
- $this->set_template_conditions($template_id, 'single_docs');
+ // Check for existing templates targeting single docs before auto-applying
+ $existing = get_posts([
+ 'post_type' => 'elementor_library',
+ 'meta_key' => '_elementor_conditions',
+ 'meta_value' => 'include/single/docs',
+ ]);
+
+ if (empty($existing)) {
+ // Only auto-apply if no conflicting templates exist
+ $this->set_template_conditions($template_id, 'single_docs');
+ }
// Mark as imported
update_option('wedocs_elementor_templates_imported', true);
}Also applies to: 265-275
🤖 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 `@includes/Elementor.php` around lines 144 - 151, The
`set_template_conditions()` call for 'single_docs' is being applied immediately
after importing the template without checking for existing conflicting Theme
Builder templates, which can cause condition conflicts on Elementor Pro sites.
Before calling `set_template_conditions($template_id, 'single_docs')`, implement
a check to verify that no existing Elementor templates already target the same
'single_docs' conditions. If conflicting templates are detected, either skip the
auto-assignment of conditions or defer it to an explicit admin action during
onboarding rather than automatic import. Apply this same logic to the additional
location mentioned (lines 265-275) where similar template condition assignments
occur.
| // Check if elementor data is stored as array instead of JSON string | ||
| if (is_array($elementor_data) && !empty($elementor_data)) { | ||
| // Convert from array to JSON string | ||
| update_post_meta($template->ID, '_elementor_data', wp_json_encode($elementor_data)); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Expectation: _elementor_data writes that persist JSON use wp_slash(wp_json_encode(...)).
rg -n -C2 "_elementor_data|wp_json_encode" --type=phpRepository: weDevsOfficial/wedocs-plugin
Length of output: 7667
🏁 Script executed:
# Check if wp_slash is used elsewhere in the codebase, particularly with post-meta
rg -n "wp_slash" --type=php -A2 -B2Repository: weDevsOfficial/wedocs-plugin
Length of output: 54
🏁 Script executed:
# Search for other post-meta updates with JSON data in the codebase
rg -n "update_post_meta.*json|update_post_meta.*encode" --type=php -iRepository: weDevsOfficial/wedocs-plugin
Length of output: 200
🏁 Script executed:
# Check how _elementor_data is being retrieved/used after being stored
rg -n "get_post_meta.*_elementor_data" --type=php -A3Repository: weDevsOfficial/wedocs-plugin
Length of output: 792
🌐 Web query:
WordPress wp_slash post-meta unslashing behavior sanitization
💡 Result:
In WordPress, post metadata functions such as update_post_meta and add_post_meta internally apply stripslashes to the input data before storing it in the database [1][2]. This historical behavior means that if your data contains escaped characters (such as JSON strings with escaped quotes), those backslashes will be removed, often corrupting the intended format [1][2][3]. To prevent this data corruption, you must use wp_slash on your data before passing it to these metadata functions [4][1]. The wp_slash function adds an extra layer of escaping (backslashes) that compensates for the subsequent stripping performed by the metadata API [1][2]. Key points regarding this behavior: 1. Requirement: If you are providing raw or already-escaped data (like a JSON-encoded string) to functions like update_post_meta, you must wrap that data in wp_slash [1][2][3]. 2. Purpose: wp_slash is specifically intended for preparing data for WordPress core APIs that expect "slashed" input [5]. Conversely, wp_unslash should be used when you need to remove slashes, for example when retrieving data from sources like the $_POST superglobal, which WordPress automatically escapes upon request arrival [6]. 3. Caveats: While wp_slash and wp_unslash are intended to be inverses, they are not perfectly symmetrical in all cases, particularly regarding the handling of objects and nested structures [7]. Specifically, wp_unslash utilizes map_deep, which recursively traverses objects, whereas wp_slash does not [7]. 4. Best Practice: Always check the documentation for the specific API you are using, as some newer APIs or alternative functions may not follow this "slashed" input expectation [8]. However, for the core metadata functions, wp_slash is the standard workaround to ensure data integrity [1][2].
Citations:
- 1: https://developer.wordpress.org/reference/functions/update_post_meta/
- 2: https://developer.wordpress.org/plugins/metadata/managing-post-metadata/
- 3: https://wordpress.stackexchange.com/questions/53336/wordpress-is-stripping-escape-backslashes-from-json-strings-in-post-meta
- 4: https://core.trac.wordpress.org/ticket/56832
- 5: https://developer.wordpress.org/reference/functions/wp_slash/?output_format=md
- 6: https://wp-kama.com/function/wp_unslash
- 7: https://core.trac.wordpress.org/ticket/56119
- 8: https://core.trac.wordpress.org/ticket/21767
🏁 Script executed:
# Check the context around line 270 to see the full wp_insert_post call
sed -n '260,280p' includes/Elementor.phpRepository: weDevsOfficial/wedocs-plugin
Length of output: 1037
🌐 Web query:
WordPress wp_insert_post meta_input stripslashes behavior
💡 Result:
The wp_insert_post function (and by extension its meta_input array) is designed to handle slashed data for historical compatibility reasons [1][2]. Because WordPress historically relied on magic quotes, the core API functions like wp_insert_post and update_post_meta expect input to be slashed [1][3][2]. When you pass data through the meta_input array, it is passed to update_post_meta [4][5], which in turn processes the data using internal functions that expect it to be escaped [1]. Consequently, if you provide unslashed data (containing backslashes) to these functions, WordPress will effectively "strip" those slashes during its internal processing [3]. To ensure your data is stored exactly as intended, you should use wp_slash on your data array before passing it to wp_insert_post [3][2]. Example usage: wp_insert_post( wp_slash( array( 'post_title' => 'My Title', 'meta_input' => array( 'my_meta_key' => 'Value with \ backslashes',),))); In this example, wp_slash ensures that the backslashes are properly escaped before the internal logic removes them [3][2]. If you do not apply wp_slash, the backslashes in your meta value may be removed or corrupted [3].
Citations:
- 1: https://core.trac.wordpress.org/ticket/21767
- 2: https://make.xwp.co/2015/11/11/use-meta_input-and-tax_input-with-wp_insert_post/
- 3: https://core.trac.wordpress.org/ticket/54601
- 4: https://developer.wordpress.org/reference/functions/wp_insert_post/
- 5: https://wordpress.stackexchange.com/questions/216178/what-is-meta-input-parameter-in-wp-insert-post-used-for
Wrap _elementor_data JSON with wp_slash() before saving to post meta.
WordPress's post-meta functions (update_post_meta, add_post_meta, and wp_insert_post with meta_input) internally apply stripslashes() before storing data in the database. Without wp_slash(), JSON-encoded data containing escaped quotes or backslashes will be corrupted during storage.
This affects two locations:
- Line 206:
update_post_meta()call - Line 270:
meta_inputarray inwp_insert_post()
Suggested fixes
- update_post_meta($template->ID, '_elementor_data', wp_json_encode($elementor_data));
+ update_post_meta($template->ID, '_elementor_data', wp_slash(wp_json_encode($elementor_data)));- '_elementor_data' => wp_json_encode($template_data['content']),
+ '_elementor_data' => wp_slash(wp_json_encode($template_data['content'])),📝 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.
| // Check if elementor data is stored as array instead of JSON string | |
| if (is_array($elementor_data) && !empty($elementor_data)) { | |
| // Convert from array to JSON string | |
| update_post_meta($template->ID, '_elementor_data', wp_json_encode($elementor_data)); | |
| } | |
| // Check if elementor data is stored as array instead of JSON string | |
| if (is_array($elementor_data) && !empty($elementor_data)) { | |
| // Convert from array to JSON string | |
| update_post_meta($template->ID, '_elementor_data', wp_slash(wp_json_encode($elementor_data))); | |
| } |
🤖 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 `@includes/Elementor.php` around lines 203 - 207, The `_elementor_data` JSON
being stored via `update_post_meta()` and `meta_input` in `wp_insert_post()`
needs to be wrapped with `wp_slash()` to prevent WordPress from stripping
escaped quotes and backslashes during storage. Modify the `update_post_meta()`
call to wrap `wp_json_encode($elementor_data)` with `wp_slash()`, and apply the
same fix to the `meta_input` array entry for `_elementor_data` around line 270
in the same file.
| if ($show_next) { | ||
| $next_query = "SELECT ID FROM {$wpdb->posts} | ||
| WHERE post_parent = {$current_post->post_parent} AND post_type = 'docs' AND post_status = 'publish' AND menu_order > {$current_post->menu_order} | ||
| ORDER BY menu_order ASC | ||
| LIMIT 0, 1"; | ||
| $next_post_id = (int) $wpdb->get_var($next_query); | ||
| } | ||
|
|
||
| if ($show_prev) { | ||
| $prev_query = "SELECT ID FROM {$wpdb->posts} | ||
| WHERE post_parent = {$current_post->post_parent} AND post_type = 'docs' AND post_status = 'publish' AND menu_order < {$current_post->menu_order} | ||
| ORDER BY menu_order DESC | ||
| LIMIT 0, 1"; | ||
| $prev_post_id = (int) $wpdb->get_var($prev_query); |
There was a problem hiding this comment.
Prev/next lookup fails when sibling docs share menu_order.
Line 402 and Line 410 compare only menu_order (>/<). If siblings have equal menu_order (common default), no adjacent doc is found. Add a deterministic tie-break using ID and prepare the query parameters.
Suggested fix
- $next_query = "SELECT ID FROM {$wpdb->posts}
- WHERE post_parent = {$current_post->post_parent} AND post_type = 'docs' AND post_status = 'publish' AND menu_order > {$current_post->menu_order}
- ORDER BY menu_order ASC
- LIMIT 0, 1";
- $next_post_id = (int) $wpdb->get_var($next_query);
+ $next_query = $wpdb->prepare(
+ "SELECT ID FROM {$wpdb->posts}
+ WHERE post_parent = %d
+ AND post_type = 'docs'
+ AND post_status = 'publish'
+ AND (menu_order > %d OR (menu_order = %d AND ID > %d))
+ ORDER BY menu_order ASC, ID ASC
+ LIMIT 1",
+ (int) $current_post->post_parent,
+ (int) $current_post->menu_order,
+ (int) $current_post->menu_order,
+ (int) $current_post->ID
+ );
+ $next_post_id = (int) $wpdb->get_var( $next_query );🤖 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 `@includes/Elementor/Widgets/DocNavigation.php` around lines 400 - 413, The
prev and next post lookup queries in the DocNavigation widget use only
menu_order for comparison, which fails when sibling documents share the same
menu_order value. Fix the $next_query and $prev_query strings by adding a
deterministic ID-based tie-breaker: append AND ID > {$current_post->ID} to the
$next_query condition and AND ID < {$current_post->ID} to the $prev_query
condition. Additionally, convert both queries from direct string interpolation
to use $wpdb->prepare() for safe parameterized query execution instead of the
current concatenation approach.
| while ($parent_id) { | ||
| $page = get_post($parent_id); | ||
| $parents[] = [ | ||
| 'label' => get_the_title($page->ID), | ||
| 'url' => get_permalink($page->ID), | ||
| ]; | ||
| $parent_id = $page->post_parent; | ||
| } |
There was a problem hiding this comment.
Handle missing parent posts in breadcrumb traversal.
Line 378 can return null from get_post($parent_id), but Line 380/383 dereference $page unguarded. An orphaned post_parent will break this render path.
Suggested fix
while ($parent_id) {
$page = get_post($parent_id);
+ if ( ! $page ) {
+ break;
+ }
$parents[] = [
'label' => get_the_title($page->ID),
'url' => get_permalink($page->ID),
];
$parent_id = $page->post_parent;
}📝 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.
| while ($parent_id) { | |
| $page = get_post($parent_id); | |
| $parents[] = [ | |
| 'label' => get_the_title($page->ID), | |
| 'url' => get_permalink($page->ID), | |
| ]; | |
| $parent_id = $page->post_parent; | |
| } | |
| while ($parent_id) { | |
| $page = get_post($parent_id); | |
| if ( ! $page ) { | |
| break; | |
| } | |
| $parents[] = [ | |
| 'label' => get_the_title($page->ID), | |
| 'url' => get_permalink($page->ID), | |
| ]; | |
| $parent_id = $page->post_parent; | |
| } |
🤖 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 `@includes/Elementor/Widgets/DocsBreadcrumb.php` around lines 377 - 384, The
while loop in the breadcrumb traversal does not check if get_post($parent_id)
returns null before accessing $page properties on lines 380 and 383. Add a null
check immediately after the get_post() call to verify that $page exists, and if
it does not (indicating an orphaned post_parent), break out of the loop to
prevent null reference errors when accessing $page->ID and dereferencing it for
get_the_title() and get_permalink().
| $items[] = [ | ||
| 'label' => get_the_title(), | ||
| 'current' => true, | ||
| ]; |
There was a problem hiding this comment.
Use the resolved docs context for the current breadcrumb label.
Line 391 calls get_the_title() with no ID, which relies on global post state. In editor/fallback contexts this can produce the wrong current label.
Suggested fix
- $items[] = [
- 'label' => get_the_title(),
+ $items[] = [
+ 'label' => get_the_title( $current_post->ID ),
'current' => 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.
| $items[] = [ | |
| 'label' => get_the_title(), | |
| 'current' => true, | |
| ]; | |
| $items[] = [ | |
| 'label' => get_the_title( $current_post->ID ), | |
| 'current' => true, | |
| ]; |
🤖 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 `@includes/Elementor/Widgets/DocsBreadcrumb.php` around lines 390 - 393, In the
DocsBreadcrumb widget where the current breadcrumb item is being added to the
items array, replace the `get_the_title()` call (which relies on global post
state) with a call to get the title from the resolved docs context. This ensures
the correct current label is displayed in editor and fallback contexts where the
global post state may be unreliable. Identify what the resolved docs context
variable or method is in the class and use that instead of the global
post-dependent function.
| global $post; | ||
|
|
||
| // Determine the parent doc | ||
| $ancestors = []; | ||
| $parent = false; | ||
|
|
||
| if (!empty($post->post_parent)) { | ||
| $ancestors = get_post_ancestors($post->ID); | ||
| $root = count($ancestors) - 1; | ||
| $parent = $ancestors[$root]; | ||
| } else { | ||
| $parent = !empty($post->ID) ? $post->ID : ''; | ||
| } |
There was a problem hiding this comment.
Guard $post before dereferencing doc fields.
Line 410 and Line 415 access $post properties without confirming $post is a valid WP_Post. This widget can render outside single-doc contexts, so this path can emit runtime warnings and break rendering.
Suggested fix
- global $post;
+ global $post;
+ if ( ! ( $post instanceof \WP_Post ) || $post->post_type !== 'docs' ) {
+ if ( \Elementor\Plugin::$instance->editor->is_edit_mode() ) {
+ echo '<p style="color: `#999`; font-style: italic; padding: 20px; text-align: center;">' . esc_html__( 'Hamburger Menu: Preview it on a single doc page.', 'wedocs' ) . '</p>';
+ }
+ return;
+ }📝 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.
| global $post; | |
| // Determine the parent doc | |
| $ancestors = []; | |
| $parent = false; | |
| if (!empty($post->post_parent)) { | |
| $ancestors = get_post_ancestors($post->ID); | |
| $root = count($ancestors) - 1; | |
| $parent = $ancestors[$root]; | |
| } else { | |
| $parent = !empty($post->ID) ? $post->ID : ''; | |
| } | |
| global $post; | |
| if ( ! ( $post instanceof \WP_Post ) || $post->post_type !== 'docs' ) { | |
| if ( \Elementor\Plugin::$instance->editor->is_edit_mode() ) { | |
| echo '<p style="color: `#999`; font-style: italic; padding: 20px; text-align: center;">' . esc_html__( 'Hamburger Menu: Preview it on a single doc page.', 'wedocs' ) . '</p>'; | |
| } | |
| return; | |
| } | |
| // Determine the parent doc | |
| $ancestors = []; | |
| $parent = false; | |
| if (!empty($post->post_parent)) { | |
| $ancestors = get_post_ancestors($post->ID); | |
| $root = count($ancestors) - 1; | |
| $parent = $ancestors[$root]; | |
| } else { | |
| $parent = !empty($post->ID) ? $post->ID : ''; | |
| } |
🤖 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 `@includes/Elementor/Widgets/DocsHamburgerMenu.php` around lines 404 - 416, The
code in the DocsHamburgerMenu widget directly accesses properties on the global
$post variable without first verifying that $post is a valid WP_Post object. Add
a guard check at the beginning of this method to verify that $post exists and is
an object before proceeding with the logic that accesses $post->post_parent and
$post->ID. If $post is not valid, the method should handle this gracefully by
returning early or using a safe default value instead of attempting to access
the parent document logic.
| <a href="<?php echo get_permalink($parent); ?>" style="text-decoration: none; color: inherit;"> | ||
| <?php echo get_post_field('post_title', $parent, 'display'); ?> | ||
| </a> |
There was a problem hiding this comment.
Escape permalink and title in the panel header link.
Line 458 outputs get_permalink() raw and Line 459 outputs post_title raw. These should be escaped for URL/text output contexts.
Suggested fix
- <a href="<?php echo get_permalink($parent); ?>" style="text-decoration: none; color: inherit;">
- <?php echo get_post_field('post_title', $parent, 'display'); ?>
+ <a href="<?php echo esc_url( get_permalink( $parent ) ); ?>" style="text-decoration: none; color: inherit;">
+ <?php echo esc_html( get_post_field( 'post_title', $parent, 'display' ) ); ?>
</a>📝 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.
| <a href="<?php echo get_permalink($parent); ?>" style="text-decoration: none; color: inherit;"> | |
| <?php echo get_post_field('post_title', $parent, 'display'); ?> | |
| </a> | |
| <a href="<?php echo esc_url( get_permalink( $parent ) ); ?>" style="text-decoration: none; color: inherit;"> | |
| <?php echo esc_html( get_post_field( 'post_title', $parent, 'display' ) ); ?> | |
| </a> |
🤖 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 `@includes/Elementor/Widgets/DocsHamburgerMenu.php` around lines 458 - 460, In
the panel header link section where the parent post is rendered, apply proper
output escaping to both the URL and text content. Wrap the
get_permalink($parent) function call with esc_url() to safely escape the href
attribute value, and wrap the get_post_field('post_title', $parent, 'display')
call with esc_html() to safely escape the displayed title text within the anchor
tag.
Summary by CodeRabbit
Release Notes
New Features
Style