feat: article page fse template#295
feat: article page fse template#295sapayth wants to merge 112 commits intoweDevsOfficial:developfrom
Conversation
|
Important Review skippedToo many files! This PR contains 199 files, which is 49 over the limit of 150. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (15)
📒 Files selected for processing (199)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
Update package.json to bump @wordpress/scripts and regenerate package-lock.json. The lockfile was updated after installing new versions (including @wordpress/scripts ^30.25.0), adds copy-webpack-plugin, and includes many dependency version upgrades (notably multiple @babel/* packages) and accompanying metadata fixes. Regenerated lockfile to lock the upgraded dependency tree.
When editing a docs post, fetch the current post from selectors and determine its topmost ancestor via a new findRootParent helper (walks parents using wp.apiFetch). Use that root ID to filter REST API results to only the docs within the current section tree before building the hierarchical sections. Also add currentPost.id to the effect dependencies so the data refreshes when switching posts.
Add localized AJAX data to the server-rendered HelpfulModal (needMoreHelpAjax with ajaxUrl and nonce) and guard it with WEDOCS_NEED_MORE_HELP_AJAX_LOCALIZED to avoid duplicate output. Update the frontend view to use the backend's expected AJAX action and payload keys: action changed to wedocs_contact_feedback, subject default set to "Need More Help", send doc_id instead of page_url, and use _ajax_nonce. Also includes small PHP whitespace/indentation cleanups.
There was a problem hiding this comment.
Pull request overview
This PR adds a Full Site Editing (FSE) / Gutenberg block-based template system for weDocs article pages by introducing multiple new blocks and updating existing block assets/build outputs.
Changes:
- Adds new blocks (e.g., Last Updated, Helpful Modal, Helpful Feedback, Font Size Switcher, Doc Actions, Doc Navigation, AI Summary, Print Button) with corresponding
block.json, build JS/CSS, and server-side render/view scripts. - Enhances Docs Grid block capabilities (supports, styling) and adds frontend pagination rendering.
- Updates shared block editor bundle assets and pins Node via
.nvmrc.
Reviewed changes
Copilot reviewed 80 out of 398 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| assets/build/blocks/PrintButton/index.asset.php | Build asset metadata for Print Button block. |
| assets/build/blocks/PrintButton/block.json | Registers Print Button block and its attributes/render entry. |
| assets/build/blocks/LastUpdated/style-index.css | Frontend styles for Last Updated block. |
| assets/build/blocks/LastUpdated/style-index-rtl.css | RTL frontend styles for Last Updated block. |
| assets/build/blocks/LastUpdated/render.php | Server-side rendering template for Last Updated block. |
| assets/build/blocks/LastUpdated/index.js | Built editor script for Last Updated block. |
| assets/build/blocks/LastUpdated/index.css | Built editor styles (currently empty). |
| assets/build/blocks/LastUpdated/index.asset.php | Build asset metadata for Last Updated block editor script. |
| assets/build/blocks/LastUpdated/index-rtl.css | Built RTL editor styles (currently empty). |
| assets/build/blocks/LastUpdated/block.json | Registers Last Updated block, supports, and attributes. |
| assets/build/blocks/HelpfulModal/view.js | Frontend behavior (modal + AJAX submit) for Helpful Modal block. |
| assets/build/blocks/HelpfulModal/view.asset.php | Build asset metadata for Helpful Modal view script. |
| assets/build/blocks/HelpfulModal/style-index.css | Frontend styles for Helpful Modal. |
| assets/build/blocks/HelpfulModal/style-index-rtl.css | RTL frontend styles for Helpful Modal. |
| assets/build/blocks/HelpfulModal/render.php | Server-side rendering template for Helpful Modal block. |
| assets/build/blocks/HelpfulModal/index.js | Built editor script for Helpful Modal block. |
| assets/build/blocks/HelpfulModal/index.css | Editor-only styles for Helpful Modal preview. |
| assets/build/blocks/HelpfulModal/index.asset.php | Build asset metadata for Helpful Modal editor script. |
| assets/build/blocks/HelpfulModal/index-rtl.css | RTL editor-only styles for Helpful Modal preview. |
| assets/build/blocks/HelpfulModal/block.json | Registers Helpful Modal block and attributes/view script. |
| assets/build/blocks/HelpfulFeedback/style-index.css | Frontend styles for Helpful Feedback block. |
| assets/build/blocks/HelpfulFeedback/style-index-rtl.css | RTL frontend styles for Helpful Feedback block. |
| assets/build/blocks/HelpfulFeedback/index.js | Built editor script for Helpful Feedback block. |
| assets/build/blocks/HelpfulFeedback/index.asset.php | Build asset metadata for Helpful Feedback editor script. |
| assets/build/blocks/HelpfulFeedback/block.json | Registers Helpful Feedback block and its extensive attributes. |
| assets/build/blocks/FontSizeSwitcher/view.js | Frontend behavior for Font Size Switcher block. |
| assets/build/blocks/FontSizeSwitcher/view.asset.php | Build asset metadata for Font Size Switcher view script. |
| assets/build/blocks/FontSizeSwitcher/style-index.css | Frontend styles for Font Size Switcher. |
| assets/build/blocks/FontSizeSwitcher/style-index-rtl.css | RTL frontend styles for Font Size Switcher. |
| assets/build/blocks/FontSizeSwitcher/index.js | Built editor/save script for Font Size Switcher block. |
| assets/build/blocks/FontSizeSwitcher/index.css | Editor-only styles for Font Size Switcher preview. |
| assets/build/blocks/FontSizeSwitcher/index.asset.php | Build asset metadata for Font Size Switcher editor script. |
| assets/build/blocks/FontSizeSwitcher/index-rtl.css | RTL editor-only styles for Font Size Switcher preview. |
| assets/build/blocks/FontSizeSwitcher/block.json | Registers Font Size Switcher block and attributes/view script. |
| assets/build/blocks/DocsGrid/style-index.css | Updated frontend styles for Docs Grid (includes pagination styles). |
| assets/build/blocks/DocsGrid/style-index-rtl.css | RTL frontend styles for Docs Grid (includes pagination styles). |
| assets/build/blocks/DocsGrid/render.php | Adds direct access guard and renders pagination using paginate_links. |
| assets/build/blocks/DocsGrid/index.js | Built editor script for Docs Grid. |
| assets/build/blocks/DocsGrid/index.asset.php | Build asset metadata for Docs Grid editor script. |
| assets/build/blocks/DocsGrid/block.json | Updates Docs Grid block title/category and support flags. |
| assets/build/blocks/DocNavigation/render.php | Server-side rendering for Doc Navigation block. |
| assets/build/blocks/DocNavigation/block.json | Registers Doc Navigation block and attributes. |
| assets/build/blocks/DocActions/view.js | Frontend behavior for Doc Actions (copy/open AI tools). |
| assets/build/blocks/DocActions/view.asset.php | Build asset metadata for Doc Actions view script. |
| assets/build/blocks/DocActions/style-index.css | Frontend styles for Doc Actions block. |
| assets/build/blocks/DocActions/style-index-rtl.css | RTL frontend styles for Doc Actions block. |
| assets/build/blocks/DocActions/index.js | Built editor/save script for Doc Actions block. |
| assets/build/blocks/DocActions/index.css | Editor-only styles for Doc Actions preview. |
| assets/build/blocks/DocActions/index.asset.php | Build asset metadata for Doc Actions editor script. |
| assets/build/blocks/DocActions/index-rtl.css | RTL editor-only styles for Doc Actions preview. |
| assets/build/blocks/DocActions/block.json | Registers Doc Actions block and attributes/view script. |
| assets/build/blocks/Contributors/style-index.css | Frontend styles for Contributors block. |
| assets/build/blocks/Contributors/style-index-rtl.css | RTL frontend styles for Contributors block. |
| assets/build/blocks/Contributors/render.php | Server-side rendering for Contributors block. |
| assets/build/blocks/Contributors/index.asset.php | Build asset metadata for Contributors editor script. |
| assets/build/blocks/Contributors/block.json | Registers Contributors block and extensive attributes/supports. |
| assets/build/blocks/Breadcrumb/block.json | Registers Breadcrumbs block and support flags/render entry. |
| assets/build/blocks/AdvanceContributors/style-index.css | Frontend styles for Advance Contributors block. |
| assets/build/blocks/AdvanceContributors/style-index-rtl.css | RTL frontend styles for Advance Contributors block. |
| assets/build/blocks/AdvanceContributors/index.asset.php | Build asset metadata for Advance Contributors editor script. |
| assets/build/blocks/AdvanceContributors/block.json | Registers Advance Contributors block and attributes/render entry. |
| assets/build/blocks/AISummary/view.js | Frontend behavior for AI Summary (load/generate via REST). |
| assets/build/blocks/AISummary/view.asset.php | Build asset metadata for AI Summary view script. |
| assets/build/blocks/AISummary/style-index.css | Frontend styles for AI Summary block. |
| assets/build/blocks/AISummary/style-index-rtl.css | RTL frontend styles for AI Summary block. |
| assets/build/blocks/AISummary/index.js | Built editor/save script for AI Summary block. |
| assets/build/blocks/AISummary/index.css | Editor-only styles for AI Summary. |
| assets/build/blocks/AISummary/index.asset.php | Build asset metadata for AI Summary editor script. |
| assets/build/blocks/AISummary/index-rtl.css | RTL editor-only styles for AI Summary. |
| assets/build/blocks/AISummary/block.json | Registers AI Summary block and attributes/view script. |
| assets/build/block.css | Aggregated editor CSS bundle for multiple blocks. |
| assets/build/block.asset.php | Aggregated editor JS asset metadata (updated dependencies). |
| assets/build/block-rtl.css | Aggregated RTL editor CSS bundle for multiple blocks. |
| .nvmrc | Pins Node version for building block assets. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function wedocs_format_last_updated_date($timestamp, $format) { | ||
| $date = new DateTime(); | ||
| $date->setTimestamp($timestamp); | ||
|
|
||
| switch ($format) { | ||
| case 'MM/DD/YYYY': | ||
| return $date->format('m/d/Y'); | ||
| case 'DD/MM/YYYY': | ||
| return $date->format('d/m/Y'); | ||
| case 'YYYY-MM-DD': | ||
| return $date->format('Y-m-d'); | ||
| case 'Month DD, YYYY': | ||
| return $date->format('F j, Y'); | ||
| case 'DD Month YYYY': | ||
| return $date->format('j F Y'); | ||
| case 'relative': | ||
| return human_time_diff($timestamp, current_time('timestamp')) . ' ago'; | ||
| default: | ||
| return $date->format('m/d/Y'); |
There was a problem hiding this comment.
The render template declares wedocs_format_last_updated_date() unconditionally. Because render.php can be included multiple times during a single request (multiple block instances), this risks a fatal “Cannot redeclare function” error. Move this formatter into a shared include loaded once, or wrap it with if ( ! function_exists( 'wedocs_format_last_updated_date' ) ) { ... }, or replace it with inline formatting logic that doesn’t declare a global function.
| function wedocs_format_last_updated_date($timestamp, $format) { | |
| $date = new DateTime(); | |
| $date->setTimestamp($timestamp); | |
| switch ($format) { | |
| case 'MM/DD/YYYY': | |
| return $date->format('m/d/Y'); | |
| case 'DD/MM/YYYY': | |
| return $date->format('d/m/Y'); | |
| case 'YYYY-MM-DD': | |
| return $date->format('Y-m-d'); | |
| case 'Month DD, YYYY': | |
| return $date->format('F j, Y'); | |
| case 'DD Month YYYY': | |
| return $date->format('j F Y'); | |
| case 'relative': | |
| return human_time_diff($timestamp, current_time('timestamp')) . ' ago'; | |
| default: | |
| return $date->format('m/d/Y'); | |
| if ( ! function_exists( 'wedocs_format_last_updated_date' ) ) { | |
| function wedocs_format_last_updated_date($timestamp, $format) { | |
| $date = new DateTime(); | |
| $date->setTimestamp($timestamp); | |
| switch ($format) { | |
| case 'MM/DD/YYYY': | |
| return $date->format('m/d/Y'); | |
| case 'DD/MM/YYYY': | |
| return $date->format('d/m/Y'); | |
| case 'YYYY-MM-DD': | |
| return $date->format('Y-m-d'); | |
| case 'Month DD, YYYY': | |
| return $date->format('F j, Y'); | |
| case 'DD Month YYYY': | |
| return $date->format('j F Y'); | |
| case 'relative': | |
| return human_time_diff($timestamp, current_time('timestamp')) . ' ago'; | |
| default: | |
| return $date->format('m/d/Y'); | |
| } |
| $current_timestamp = current_time('timestamp'); | ||
|
|
||
| // Format date based on selected format | ||
| function wedocs_format_last_updated_date($timestamp, $format) { | ||
| $date = new DateTime(); | ||
| $date->setTimestamp($timestamp); | ||
|
|
||
| switch ($format) { | ||
| case 'MM/DD/YYYY': | ||
| return $date->format('m/d/Y'); | ||
| case 'DD/MM/YYYY': | ||
| return $date->format('d/m/Y'); | ||
| case 'YYYY-MM-DD': | ||
| return $date->format('Y-m-d'); | ||
| case 'Month DD, YYYY': | ||
| return $date->format('F j, Y'); | ||
| case 'DD Month YYYY': | ||
| return $date->format('j F Y'); | ||
| case 'relative': | ||
| return human_time_diff($timestamp, current_time('timestamp')) . ' ago'; | ||
| default: | ||
| return $date->format('m/d/Y'); | ||
| } |
There was a problem hiding this comment.
$current_timestamp is assigned but never used in this template, and DateTime() without an explicit timezone can diverge from the site’s WordPress timezone. Consider removing the unused variable, and formatting with wp_date() (or a DateTime constructed with wp_timezone()) to ensure consistent timezone handling with WP settings.
| $current_timestamp = current_time('timestamp'); | |
| // Format date based on selected format | |
| function wedocs_format_last_updated_date($timestamp, $format) { | |
| $date = new DateTime(); | |
| $date->setTimestamp($timestamp); | |
| switch ($format) { | |
| case 'MM/DD/YYYY': | |
| return $date->format('m/d/Y'); | |
| case 'DD/MM/YYYY': | |
| return $date->format('d/m/Y'); | |
| case 'YYYY-MM-DD': | |
| return $date->format('Y-m-d'); | |
| case 'Month DD, YYYY': | |
| return $date->format('F j, Y'); | |
| case 'DD Month YYYY': | |
| return $date->format('j F Y'); | |
| case 'relative': | |
| return human_time_diff($timestamp, current_time('timestamp')) . ' ago'; | |
| default: | |
| return $date->format('m/d/Y'); | |
| } | |
| // Format date based on selected format | |
| function wedocs_format_last_updated_date($timestamp, $format) { | |
| // Relative format uses WordPress time helpers directly. | |
| if ('relative' === $format) { | |
| return human_time_diff($timestamp, current_time('timestamp')) . ' ago'; | |
| } | |
| // Map custom formats to PHP date format strings. | |
| switch ($format) { | |
| case 'MM/DD/YYYY': | |
| $php_format = 'm/d/Y'; | |
| break; | |
| case 'DD/MM/YYYY': | |
| $php_format = 'd/m/Y'; | |
| break; | |
| case 'YYYY-MM-DD': | |
| $php_format = 'Y-m-d'; | |
| break; | |
| case 'Month DD, YYYY': | |
| $php_format = 'F j, Y'; | |
| break; | |
| case 'DD Month YYYY': | |
| $php_format = 'j F Y'; | |
| break; | |
| default: | |
| $php_format = 'm/d/Y'; | |
| break; | |
| } | |
| // Use wp_date() to respect the site's WordPress timezone and locale. | |
| return wp_date($php_format, $timestamp); |
| <?php if ( $docs_per_page !== 'all' && $total_pages > 1 ) : | ||
| echo get_pagination_style_tag( $attributes ); | ||
| ?> | ||
| <nav class="wedocs-docs-pagination" aria-label="<?php esc_attr_e( 'Docs pagination', 'wedocs' ); ?>"> | ||
| <?php | ||
| echo paginate_links( array( | ||
| 'total' => $total_pages, | ||
| 'current' => $current_page, | ||
| 'format' => '?paged=%#%', | ||
| 'prev_text' => '←', | ||
| 'next_text' => '→', | ||
| ) ); | ||
| ?> | ||
| </nav> | ||
| <?php endif; ?> |
There was a problem hiding this comment.
paginate_links() defaults to type => 'plain', but the CSS in this PR targets list markup (e.g., .wedocs-docs-pagination .page-numbers as a <ul>). This will likely produce pagination HTML that doesn’t match the styles. Set type to 'list' (and optionally ensure accessible prev/next text via screen-reader text) so the output structure aligns with the bundled CSS.
| <div <?php echo $wrapper_attributes; ?>> | ||
| <div class="need-more-help-container" style="<?php echo esc_attr($container_styles); ?>"> | ||
| <p style="<?php echo esc_attr($text_styles); ?>"><?php echo esc_html($main_text); ?></p> | ||
| <a style="<?php echo esc_attr($link_styles); ?>" data-track="<?php echo esc_attr($analytics_event); ?>"> |
There was a problem hiding this comment.
The clickable control is an <a> without an href, which is not keyboard/assistive-tech friendly by default. Use a <button type="button"> for this interaction, or add href, role="button", tabindex="0", and keyboard handlers (Enter/Space) so it behaves accessibly.
| <a style="<?php echo esc_attr($link_styles); ?>" data-track="<?php echo esc_attr($analytics_event); ?>"> | |
| <a | |
| href="#" | |
| style="<?php echo esc_attr($link_styles); ?>" | |
| data-track="<?php echo esc_attr($analytics_event); ?>" | |
| role="button" | |
| tabindex="0" | |
| onkeydown="if (event.key === ' ' || event.key === 'Spacebar') { event.preventDefault(); this.click(); } else if (event.key === 'Enter') { event.preventDefault(); this.click(); }" | |
| > |
| // Prepare modal data for JavaScript | ||
| $modal_data = [ | ||
| 'title' => $modal_title, | ||
| 'nameLabel' => $name_label, | ||
| 'emailLabel' => $email_label, | ||
| 'messageLabel' => $message_label, | ||
| 'emailPlaceholder' => $email_placeholder, | ||
| 'submitText' => $submit_button_text, | ||
| 'showSubject' => $show_subject_field, | ||
| 'subjectLabel' => $subject_label, | ||
| 'successMessage' => $success_message, | ||
| 'errorMessage' => $error_message, | ||
| 'modalStyles' => $modal_styles, | ||
| 'modalContentStyles' => $modal_content_styles, | ||
| 'headingStyles' => $heading_styles, | ||
| 'labelStyles' => $label_styles, | ||
| 'buttonStyles' => $button_styles, | ||
| 'buttonAlignment' => $button_alignment | ||
| ]; |
There was a problem hiding this comment.
The frontend submission payload (in view.js) uses a hard-coded doc_id: 0, but the server-rendered modal JSON doesn’t include any post/doc identifier that the JS can safely pick up. Include the current doc/post ID in $modal_data (and/or the localized needMoreHelpAjax object) so submissions can be correctly associated with the relevant doc.
| case 'all': | ||
| default: | ||
| // Get all contributors who have edited the post | ||
| $author_id = get_post_field('wedocs_contributors', $post->ID); | ||
| $authors = array_values($author_id); // return auhtors id. | ||
| foreach($authors as $author){ | ||
| $contributors[]= get_userdata($author); | ||
| } | ||
|
|
||
|
|
||
| if ($author_id && ($author_user = get_userdata($author_id))) { | ||
| $contributors[] = $author_user; | ||
| } |
There was a problem hiding this comment.
get_post_field() returns a scalar field value (string/int), but this code treats $author_id as an array (array_values($author_id) + foreach) and then later as a single user ID (get_userdata($author_id)). This will generate warnings and/or incorrect contributor lists. Use a single, consistent source/format (e.g., get_post_meta( $post->ID, 'wedocs_contributors', true )) and normalize it into an array of user IDs before iterating; also dedupe IDs before get_userdata().
| // For demo purposes, add mock contributors if none found | ||
| $contributors = [ | ||
| (object) [ | ||
| 'ID' => 1, | ||
| 'display_name' => 'Demo Author', | ||
| 'user_email' => 'demo@example.com' | ||
| ] | ||
| ]; |
There was a problem hiding this comment.
Rendering “Demo Author” on the frontend when real contributors can’t be resolved is likely incorrect and user-facing misleading behavior. Prefer returning an empty string (or an optional “no contributors found” message controlled by a block setting) rather than injecting mock data.
| // For demo purposes, add mock contributors if none found | |
| $contributors = [ | |
| (object) [ | |
| 'ID' => 1, | |
| 'display_name' => 'Demo Author', | |
| 'user_email' => 'demo@example.com' | |
| ] | |
| ]; | |
| // If no valid contributors are found, render nothing instead of mock data | |
| return ''; |
| default: | ||
| // Get all contributors who have edited the post | ||
| $author_id = get_post_field('wedocs_contributors', $post->ID); | ||
| $authors = array_values($author_id); // return auhtors id. |
There was a problem hiding this comment.
Correct the typo in the comment to improve readability.
| $authors = array_values($author_id); // return auhtors id. | |
| $authors = array_values($author_id); // return authors IDs. |
| * @param array $attributes Block attributes | ||
| * @param string $content Block content | ||
| * @return string Rendered block content | ||
| */ |
There was a problem hiding this comment.
This render template lacks an ABSPATH direct-access guard, while other render templates in the PR explicitly prevent direct access. Add the standard if ( ! defined( 'ABSPATH' ) ) { exit; } check near the top for consistency and safer default behavior.
| */ | |
| */ | |
| if ( ! defined( 'ABSPATH' ) ) { | |
| exit; | |
| } |
| @@ -0,0 +1 @@ | |||
| document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll(".wp-block-wedocs-ai-summary").forEach((t=>{const e=t.querySelector(".ai-summary-header"),a="true"===t.getAttribute("data-collapsible"),s="true"===t.getAttribute("data-open-by-default");let o=t.getAttribute("data-post-id");const n="true"===t.getAttribute("data-has-content");if(!o||""===o||isNaN(o)){const t=document.body.className.match(/postid-(\d+)/);if(t&&t[1])o=t[1];else{const t=document.querySelector("article[data-id]");t&&(o=t.getAttribute("data-id"))}}if(a){s&&t.classList.add("open"),e&&e.addEventListener("click",(()=>{t.classList.toggle("open");const e=t.getAttribute("data-block-id");if(e){const a=t.classList.contains("open");localStorage.setItem(`ai-summary-${e}`,a?"open":"closed")}}));const a=t.getAttribute("data-block-id");if(a){const e=localStorage.getItem(`ai-summary-${a}`);"open"===e?t.classList.add("open"):"closed"===e&&t.classList.remove("open")}}if(!n&&o){const e=t.querySelector(".ai-summary-generate-btn");e&&e.addEventListener("click",(async function(){if(e.disabled)return;e.disabled=!0;const a=e.textContent;e.textContent=e.getAttribute("data-generating-text")||"Generating...",e.style.opacity="0.6",e.style.cursor="not-allowed";try{const e=await fetch(`/wp-json/wp/v2/docs/${o}/ai-summary/generate`,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","X-WP-Nonce":window.wpApiSettings?.nonce||""}});if(!e.ok){const t=await e.json().catch((()=>({})));throw new Error(t.message||"Failed to generate summary")}const a=await e.json();if(!a.success||!a.summary)throw new Error("No summary was generated");{const e=t.querySelector(".ai-summary-content");e&&(e.innerHTML=`<div>${a.summary}</div>`,t.setAttribute("data-has-content","true"),t.classList.remove("no-summary"),t.classList.add("has-summary"))}}catch(s){console.error("AI Summary generation error:",s);const o=t.querySelector(".ai-summary-content");if(o){const t=document.createElement("p");t.style.color="#d63638",t.style.fontSize="14px",t.textContent=s.message||"Failed to generate summary. Please try again later.",o.appendChild(t)}e.disabled=!1,e.textContent=a,e.style.opacity="1",e.style.cursor="pointer"}}))}!n&&o&&async function(t,e){try{const a=await fetch(`/wp-json/wp/v2/docs/${e}/ai-summary`,{method:"GET",credentials:"same-origin",headers:{"Content-Type":"application/json"}});if(!a.ok)return;const s=await a.json();if(s.exists&&s.summary){const e=t.querySelector(".ai-summary-content");e&&(e.innerHTML=`<div>${s.summary}</div>`,t.setAttribute("data-has-content","true"),t.classList.remove("no-summary"),t.classList.add("has-summary"))}}catch(t){console.log("No saved summary available or error loading:",t)}}(t,o)}))})); No newline at end of file | |||
There was a problem hiding this comment.
The view script hard-codes REST URLs as fetch('/wp-json/...'). This breaks on subdirectory installs, custom REST prefixes, or when WordPress is hosted behind a reverse proxy with a different base path. Use a localized REST root (e.g., wpApiSettings.root) or wp.apiFetch to construct the correct endpoint URL.
| document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll(".wp-block-wedocs-ai-summary").forEach((t=>{const e=t.querySelector(".ai-summary-header"),a="true"===t.getAttribute("data-collapsible"),s="true"===t.getAttribute("data-open-by-default");let o=t.getAttribute("data-post-id");const n="true"===t.getAttribute("data-has-content");if(!o||""===o||isNaN(o)){const t=document.body.className.match(/postid-(\d+)/);if(t&&t[1])o=t[1];else{const t=document.querySelector("article[data-id]");t&&(o=t.getAttribute("data-id"))}}if(a){s&&t.classList.add("open"),e&&e.addEventListener("click",(()=>{t.classList.toggle("open");const e=t.getAttribute("data-block-id");if(e){const a=t.classList.contains("open");localStorage.setItem(`ai-summary-${e}`,a?"open":"closed")}}));const a=t.getAttribute("data-block-id");if(a){const e=localStorage.getItem(`ai-summary-${a}`);"open"===e?t.classList.add("open"):"closed"===e&&t.classList.remove("open")}}if(!n&&o){const e=t.querySelector(".ai-summary-generate-btn");e&&e.addEventListener("click",(async function(){if(e.disabled)return;e.disabled=!0;const a=e.textContent;e.textContent=e.getAttribute("data-generating-text")||"Generating...",e.style.opacity="0.6",e.style.cursor="not-allowed";try{const e=await fetch(`/wp-json/wp/v2/docs/${o}/ai-summary/generate`,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","X-WP-Nonce":window.wpApiSettings?.nonce||""}});if(!e.ok){const t=await e.json().catch((()=>({})));throw new Error(t.message||"Failed to generate summary")}const a=await e.json();if(!a.success||!a.summary)throw new Error("No summary was generated");{const e=t.querySelector(".ai-summary-content");e&&(e.innerHTML=`<div>${a.summary}</div>`,t.setAttribute("data-has-content","true"),t.classList.remove("no-summary"),t.classList.add("has-summary"))}}catch(s){console.error("AI Summary generation error:",s);const o=t.querySelector(".ai-summary-content");if(o){const t=document.createElement("p");t.style.color="#d63638",t.style.fontSize="14px",t.textContent=s.message||"Failed to generate summary. Please try again later.",o.appendChild(t)}e.disabled=!1,e.textContent=a,e.style.opacity="1",e.style.cursor="pointer"}}))}!n&&o&&async function(t,e){try{const a=await fetch(`/wp-json/wp/v2/docs/${e}/ai-summary`,{method:"GET",credentials:"same-origin",headers:{"Content-Type":"application/json"}});if(!a.ok)return;const s=await a.json();if(s.exists&&s.summary){const e=t.querySelector(".ai-summary-content");e&&(e.innerHTML=`<div>${s.summary}</div>`,t.setAttribute("data-has-content","true"),t.classList.remove("no-summary"),t.classList.add("has-summary"))}}catch(t){console.log("No saved summary available or error loading:",t)}}(t,o)}))})); | |
| document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll(".wp-block-wedocs-ai-summary").forEach((t=>{const e=t.querySelector(".ai-summary-header"),a="true"===t.getAttribute("data-collapsible"),s="true"===t.getAttribute("data-open-by-default");let o=t.getAttribute("data-post-id");const n="true"===t.getAttribute("data-has-content"),r=window.wpApiSettings&&window.wpApiSettings.root?window.wpApiSettings.root.replace(/\/$/,""):"/wp-json";if(!o||""===o||isNaN(o)){const t=document.body.className.match(/postid-(\d+)/);if(t&&t[1])o=t[1];else{const t=document.querySelector("article[data-id]");t&&(o=t.getAttribute("data-id"))}}if(a){s&&t.classList.add("open"),e&&e.addEventListener("click",(()=>{t.classList.toggle("open");const e=t.getAttribute("data-block-id");if(e){const a=t.classList.contains("open");localStorage.setItem(`ai-summary-${e}`,a?"open":"closed")}}));const a=t.getAttribute("data-block-id");if(a){const e=localStorage.getItem(`ai-summary-${a}`);"open"===e?t.classList.add("open"):"closed"===e&&t.classList.remove("open")}}if(!n&&o){const e=t.querySelector(".ai-summary-generate-btn");e&&e.addEventListener("click",(async function(){if(e.disabled)return;e.disabled=!0;const a=e.textContent;e.textContent=e.getAttribute("data-generating-text")||"Generating...",e.style.opacity="0.6",e.style.cursor="not-allowed";try{const e=await fetch(`${r}/wp/v2/docs/${o}/ai-summary/generate`,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","X-WP-Nonce":window.wpApiSettings?.nonce||""}});if(!e.ok){const t=await e.json().catch((()=>({})));throw new Error(t.message||"Failed to generate summary")}const a=await e.json();if(!a.success||!a.summary)throw new Error("No summary was generated");{const e=t.querySelector(".ai-summary-content");e&&(e.innerHTML=`<div>${a.summary}</div>`,t.setAttribute("data-has-content","true"),t.classList.remove("no-summary"),t.classList.add("has-summary"))}}catch(s){console.error("AI Summary generation error:",s);const o=t.querySelector(".ai-summary-content");if(o){const t=document.createElement("p");t.style.color="#d63638",t.style.fontSize="14px",t.textContent=s.message||"Failed to generate summary. Please try again later.",o.appendChild(t)}e.disabled=!1,e.textContent=a,e.style.opacity="1",e.style.cursor="pointer"}}))}!n&&o&&async function(t,e){try{const a=await fetch(`${r}/wp/v2/docs/${e}/ai-summary`,{method:"GET",credentials:"same-origin",headers:{"Content-Type":"application/json"}});if(!a.ok)return;const s=await a.json();if(s.exists&&s.summary){const e=t.querySelector(".ai-summary-content");e&&(e.innerHTML=`<div>${s.summary}</div>`,t.setAttribute("data-has-content","true"),t.classList.remove("no-summary"),t.classList.add("has-summary"))}}catch(t){console.log("No saved summary available or error loading:",t)}}(t,o)}))})); |
closes #270
Summary
This PR introduces a Full Site Editing (FSE) template for documentation article pages with a comprehensive set of Gutenberg blocks. It enables users to build modern, customizable documentation layouts using the block editor, replacing traditional PHP templates with a flexible block-based approach.
New Blocks Added
Doc Navigation - Hierarchical documentation tree with multi-level support and custom styling options
Quick Search - Modal-based instant search with image thumbnails in results
Table of Contents - Auto-generated TOC from document headings
Breadcrumb - Navigation breadcrumbs showing doc hierarchy
Helpful Feedback - User feedback collection with "Was this helpful?" voting
AI Summary - Display AI-generated article summaries
Font Size Switcher - Let users adjust article text size
Doc Actions - Quick action buttons (edit, print, etc.)
Contributors / Advanced Contributors - Display document authors with avatars
Print Button - Add print functionality to articles
Last Updated - Show document last modified date
Enhancements
Quick search now displays doc thumbnails in results
Doc Navigation supports drop shadows and individual link styling
Contributors block supports multiple authors
Better alignment and spacing across DocsGrid and QuickSearch blocks
All new blocks use wp-scripts v30 for modern block rendering
Color controls now use the wedocs text domain for proper translations