Skip to content

feat: article page fse template#295

Open
sapayth wants to merge 112 commits intoweDevsOfficial:developfrom
sapayth:feat/article_page_fse_template_v3
Open

feat: article page fse template#295
sapayth wants to merge 112 commits intoweDevsOfficial:developfrom
sapayth:feat/article_page_fse_template_v3

Conversation

@sapayth
Copy link
Member

@sapayth sapayth commented Feb 18, 2026

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

Important

Review skipped

Too many files!

This PR contains 199 files, which is 49 over the limit of 150.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c9784c38-7fe7-4a7d-871a-68bf6ac4a6fc

📥 Commits

Reviewing files that changed from the base of the PR and between 255f1bb and e91bc65.

⛔ Files ignored due to path filters (15)
  • assets/build/5f4138884ca3828fa2ff.svg is excluded by !**/*.svg
  • assets/build/fonts/wedocs.0e4fd4b5.ttf is excluded by !**/*.ttf
  • assets/build/fonts/wedocs.29ec52e7.woff is excluded by !**/*.woff
  • assets/build/images/slider-1.b05d3c91.jpg is excluded by !**/*.jpg
  • assets/build/images/slider-2.f7822c31.jpg is excluded by !**/*.jpg
  • assets/build/images/slider-3.ab3b7f94.jpg is excluded by !**/*.jpg
  • assets/build/images/slider-4.501d8d95.jpg is excluded by !**/*.jpg
  • assets/img/demo-avatar/avatar_1.png is excluded by !**/*.png
  • assets/img/demo-avatar/avatar_2.png is excluded by !**/*.png
  • package-lock.json is excluded by !**/package-lock.json
  • src/assets/img/avatar_1.png is excluded by !**/*.png
  • src/assets/img/avatar_2.png is excluded by !**/*.png
  • src/assets/img/pro-badge.png is excluded by !**/*.png
  • src/blocks/AdvanceContributors/demo-avtar/avatar_1.png is excluded by !**/*.png
  • src/blocks/AdvanceContributors/demo-avtar/avatar_2.png is excluded by !**/*.png
📒 Files selected for processing (199)
  • .gitignore
  • .nvmrc
  • assets/build/block.asset.php
  • assets/build/block.js
  • assets/build/blocks/DocsGrid/block.json
  • assets/build/blocks/DocsGrid/render.php
  • assets/build/editor.asset.php
  • assets/build/editor.js
  • assets/build/frontend.asset.php
  • assets/build/frontend.css
  • assets/build/frontend.js
  • assets/build/icons.asset.php
  • assets/build/icons.js
  • assets/build/index.asset.php
  • assets/build/index.css
  • assets/build/index.js
  • assets/build/print.asset.php
  • assets/build/print.css
  • assets/build/print.js
  • assets/build/store.asset.php
  • assets/build/store.js
  • assets/build/style-block.css
  • assets/js/frontend.js
  • assets/js/helpful-feedback.js
  • includes/API/API.php
  • includes/Ajax.php
  • includes/Assets.php
  • includes/Frontend.php
  • includes/Templates/AbstractPageTemplate.php
  • includes/Templates/AbstractTemplate.php
  • includes/Templates/SingleDocTemplate.php
  • includes/Templates/TemplateManager.php
  • includes/functions.php
  • languages/wedocs.pot
  • package.json
  • readme.md
  • src/blocks/AISummary/block.json
  • src/blocks/AISummary/edit.js
  • src/blocks/AISummary/editor.scss
  • src/blocks/AISummary/index.js
  • src/blocks/AISummary/save.js
  • src/blocks/AISummary/style.scss
  • src/blocks/AISummary/view.js
  • src/blocks/AdvanceContributors/block.json
  • src/blocks/AdvanceContributors/edit-old.js
  • src/blocks/AdvanceContributors/edit.js
  • src/blocks/AdvanceContributors/index.js
  • src/blocks/AdvanceContributors/render.php
  • src/blocks/AdvanceContributors/save.js
  • src/blocks/AdvanceContributors/style.scss
  • src/blocks/Breadcrumb/Inspector.js
  • src/blocks/Breadcrumb/block.json
  • src/blocks/Breadcrumb/edit.js
  • src/blocks/Breadcrumb/index.js
  • src/blocks/Breadcrumb/render.php
  • src/blocks/Breadcrumb/style.css
  • src/blocks/Breadcrumb/style.scss
  • src/blocks/Contributors/block.json
  • src/blocks/Contributors/edit-old.js
  • src/blocks/Contributors/edit.js
  • src/blocks/Contributors/index.js
  • src/blocks/Contributors/render.php
  • src/blocks/Contributors/save.js
  • src/blocks/Contributors/style.scss
  • src/blocks/DocActions/block.json
  • src/blocks/DocActions/edit.js
  • src/blocks/DocActions/editor.scss
  • src/blocks/DocActions/index.js
  • src/blocks/DocActions/save.js
  • src/blocks/DocActions/style.scss
  • src/blocks/DocActions/view.js
  • src/blocks/DocNavigation/block.json
  • src/blocks/DocNavigation/edit.js
  • src/blocks/DocNavigation/index.js
  • src/blocks/DocNavigation/render.php
  • src/blocks/DocNavigation/save.js
  • src/blocks/DocNavigation/style.scss
  • src/blocks/DocsGrid/StyleControls.js
  • src/blocks/DocsGrid/block.json
  • src/blocks/DocsGrid/edit.js
  • src/blocks/DocsGrid/render.php
  • src/blocks/FontSizeSwitcher/block.json
  • src/blocks/FontSizeSwitcher/edit.js
  • src/blocks/FontSizeSwitcher/editor.scss
  • src/blocks/FontSizeSwitcher/index.js
  • src/blocks/FontSizeSwitcher/save.js
  • src/blocks/FontSizeSwitcher/style.scss
  • src/blocks/FontSizeSwitcher/view.js
  • src/blocks/HelpfulFeedback/block.json
  • src/blocks/HelpfulFeedback/edit.js
  • src/blocks/HelpfulFeedback/edit.js.backup
  • src/blocks/HelpfulFeedback/index.js
  • src/blocks/HelpfulFeedback/render.php
  • src/blocks/HelpfulFeedback/save.js
  • src/blocks/HelpfulFeedback/style.scss
  • src/blocks/HelpfulFeedback/view.js
  • src/blocks/HelpfulModal/block.json
  • src/blocks/HelpfulModal/edit.js
  • src/blocks/HelpfulModal/editor.scss
  • src/blocks/HelpfulModal/index.js
  • src/blocks/HelpfulModal/render.php
  • src/blocks/HelpfulModal/save.js
  • src/blocks/HelpfulModal/style.scss
  • src/blocks/HelpfulModal/view.js
  • src/blocks/LastUpdated/block.json
  • src/blocks/LastUpdated/edit.js
  • src/blocks/LastUpdated/editor.scss
  • src/blocks/LastUpdated/index.js
  • src/blocks/LastUpdated/render.php
  • src/blocks/LastUpdated/style.scss
  • src/blocks/PrintButton/Inspector.js
  • src/blocks/PrintButton/block.json
  • src/blocks/PrintButton/edit.js
  • src/blocks/PrintButton/index.js
  • src/blocks/PrintButton/render.php
  • src/blocks/PrintButton/style.scss
  • src/blocks/QuickSearch/attributes.js
  • src/blocks/QuickSearch/block.json
  • src/blocks/QuickSearch/edit.js
  • src/blocks/QuickSearch/index.js
  • src/blocks/QuickSearch/render.php
  • src/blocks/QuickSearch/save.js
  • src/blocks/QuickSearch/style.scss
  • src/blocks/QuickSearch/templates/empty-state.php
  • src/blocks/QuickSearch/templates/search-item.php
  • src/blocks/QuickSearch/templates/search-results.php
  • src/blocks/ReadingProgress/block.json
  • src/blocks/ReadingProgress/edit.js
  • src/blocks/ReadingProgress/editor.scss
  • src/blocks/ReadingProgress/index.js
  • src/blocks/ReadingProgress/save.js
  • src/blocks/ReadingProgress/style.scss
  • src/blocks/ReadingProgress/view.js
  • src/blocks/Search/block.json
  • src/blocks/Search/edit.js
  • src/blocks/Search/index.js
  • src/blocks/Search/save.js
  • src/blocks/Sidebar/block.json
  • src/blocks/Sidebar/components/ArticleItem.js
  • src/blocks/Sidebar/components/CountBadge.js
  • src/blocks/Sidebar/components/SectionItem.js
  • src/blocks/Sidebar/components/SidebarContainer.js
  • src/blocks/Sidebar/components/index.js
  • src/blocks/Sidebar/edit.js
  • src/blocks/Sidebar/index.js
  • src/blocks/Sidebar/render.php
  • src/blocks/Sidebar/save.js
  • src/blocks/Sidebar/style.css
  • src/blocks/Sidebar/view.js
  • src/blocks/SocialShare/block.json
  • src/blocks/SocialShare/edit.js
  • src/blocks/SocialShare/index.js
  • src/blocks/SocialShare/render.php
  • src/blocks/SocialShare/save.js
  • src/blocks/SocialShare/style.scss
  • src/blocks/SocialShare/view.js
  • src/blocks/TableOfContents/ColorsControls.js
  • src/blocks/TableOfContents/Inspector.js
  • src/blocks/TableOfContents/block.json
  • src/blocks/TableOfContents/controls/ListItems.js
  • src/blocks/TableOfContents/edit.js
  • src/blocks/TableOfContents/editor.scss
  • src/blocks/TableOfContents/index.js
  • src/blocks/TableOfContents/render.php
  • src/blocks/TableOfContents/save.js
  • src/blocks/TableOfContents/style.scss
  • src/blocks/TableOfContents/view.js
  • src/blocks/commonControls/AlignmentControls.js
  • src/blocks/commonControls/BackgroundImageControls.js
  • src/blocks/commonControls/BorderAndShadowControls.js
  • src/blocks/commonControls/ColorsControls.js
  • src/blocks/commonControls/CommonControls.js
  • src/blocks/commonControls/DimensionsControls.js
  • src/blocks/commonControls/DisplayControls.js
  • src/blocks/commonControls/TypographyControls.js
  • src/blocks/components/BackgroundControl.js
  • src/blocks/components/BorderControl.js
  • src/blocks/components/DimensionControl.js
  • src/blocks/components/ShadowControl.js
  • src/blocks/components/SpacingControl.js
  • src/blocks/components/TabSystem.js
  • src/blocks/components/TypographyControl.js
  • src/blocks/helpers/README.md
  • src/blocks/helpers/block-helpers.js
  • src/blocks/helpers/block-styles.php
  • src/blocks/helpers/index.js
  • src/blocks/index.js
  • src/components/Blocks/AlignmentControls.js
  • src/components/Blocks/BackgroundImageControls.js
  • src/components/Blocks/BorderAndShadowControls.js
  • src/components/Blocks/ColorsControls.js
  • src/components/Blocks/DimensionsControls.js
  • src/components/Blocks/DisplayControls.js
  • src/components/Blocks/TypographyControls.js
  • src/editor/AiDocWriterPanel.js
  • templates/block-templates/single-docs.html
  • templates/single-docs.php
  • webpack.config.js
  • wedocs.php

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

sapayth and others added 13 commits February 18, 2026 12:53
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.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines +30 to +48
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');
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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');
}

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +49
$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');
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

$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.

Suggested change
$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);

Copilot uses AI. Check for mistakes.
Comment on lines +325 to +339
<?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' => '&larr;',
'next_text' => '&rarr;',
) );
?>
</nav>
<?php endif; ?>
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
<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); ?>">
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
<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(); }"
>

Copilot uses AI. Check for mistakes.
Comment on lines +158 to +176
// 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
];
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +124 to +136
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;
}
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

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().

Copilot uses AI. Check for mistakes.
Comment on lines +161 to +168
// For demo purposes, add mock contributors if none found
$contributors = [
(object) [
'ID' => 1,
'display_name' => 'Demo Author',
'user_email' => 'demo@example.com'
]
];
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
// 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 '';

Copilot uses AI. Check for mistakes.
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.
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

Correct the typo in the comment to improve readability.

Suggested change
$authors = array_values($author_id); // return auhtors id.
$authors = array_values($author_id); // return authors IDs.

Copilot uses AI. Check for mistakes.
* @param array $attributes Block attributes
* @param string $content Block content
* @return string Rendered block content
*/
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
*/
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

Copilot uses AI. Check for mistakes.
@@ -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
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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)}))}));

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants