Skip to content

Plugin pages render through generic content template#522

Merged
compscidr merged 2 commits intomainfrom
fix/plugin-pages-use-content-template
Mar 17, 2026
Merged

Plugin pages render through generic content template#522
compscidr merged 2 commits intomainfrom
fix/plugin-pages-use-content-template

Conversation

@compscidr
Copy link
Collaborator

Summary

Plugins no longer need theme-specific templates. Page plugins now generate their HTML and pass it via plugin_content, which the existing page_content.html template renders as raw HTML. This means any theme works with any page plugin automatically.

How it works

  • page_content.html checks for {{ .plugin_content }} — if present, renders it as raw HTML
  • If not present, falls back to the normal Showdown markdown rendering of {{ .page.Content }}
  • The scholar plugin generates its article list HTML in Go with proper escaping, including title, authors, year, journal, and citation count

What changed

  • Scholar plugin: RenderPage now returns page_content.html + plugin_content HTML instead of page_research.html + articles data
  • page_content.html: All three themes updated to check for plugin_content
  • Removed: page_research.html and research.html from all themes — no longer needed

Benefits

  • Themes don't need to know about plugins
  • Any future page plugin works with any theme out of the box
  • Plugin controls its own HTML layout
  • Citation count (research: show citation count #513) included in the output

Closes #513

Test plan

  • All tests pass
  • Research page shows articles with year, journal, and citation count
  • Regular content pages still render markdown normally
  • Works across all three themes

🤖 Generated with Claude Code

Plugins no longer need theme-specific templates. The scholar plugin
now generates its article list as HTML and passes it via plugin_content,
which page_content.html renders as raw HTML. Regular content pages
continue to use Showdown markdown rendering.

This means:
- Any theme works with any page plugin automatically
- No page_research.html needed in themes
- Plugin controls its own HTML layout
- Citation count and journal info now shown inline
- All output properly HTML-escaped

Removed: page_research.html and research.html from all themes.
Updated: page_content.html in all themes to check for plugin_content.

Closes #513

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 17, 2026 04:41
@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

❌ Patch coverage is 0% with 26 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
plugins/scholar/scholar.go 0.00% 26 Missing ⚠️

📢 Thoughts on this report? Let us know!

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 consolidates rendering of the “research” page into the Scholar plugin by injecting server-rendered HTML into the standard page_content.html templates, and removes the now-redundant research-specific theme templates.

Changes:

  • Add .plugin_content support to page_content.html templates (default/forest/minimal) to render plugin-provided HTML and skip the markdown/Showdown path when present.
  • Update the Scholar plugin to render the research page via page_content.html and generate the publications list as HTML.
  • Delete theme-specific page_research.html (and the legacy default research.html) templates.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
themes/minimal/templates/page_research.html Removed redundant research-specific page template.
themes/minimal/templates/page_content.html Render .plugin_content when provided; otherwise render markdown via Showdown + DOMPurify.
themes/forest/templates/page_research.html Removed redundant research-specific page template.
themes/forest/templates/page_content.html Render .plugin_content when provided; otherwise render markdown via Showdown + DOMPurify.
themes/default/templates/research.html Removed legacy research template.
themes/default/templates/page_research.html Removed redundant research-specific page template.
themes/default/templates/page_content.html Render .plugin_content when provided; otherwise render markdown via Showdown + DOMPurify.
plugins/scholar/scholar.go Switch plugin page rendering to page_content.html and generate article list HTML in Go.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +147 to +149
return "page_content.html", gin.H{
"plugin_content": renderArticlesHTML(articles),
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c81c85c.

Comment on lines +153 to +160
func renderArticlesHTML(articles []*scholarlib.Article) string {
out := ""
for _, a := range articles {
out += `<div style="margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #eee;">`
out += `<div><a href="` + html.EscapeString(a.ScholarURL) + `">` + html.EscapeString(a.Title) + `</a></div>`
if a.Authors != "" {
out += `<div style="color: #666; font-size: 13px;">` + html.EscapeString(a.Authors) + `</div>`
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c81c85c.

Comment on lines +156 to +158
out += `<div style="margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #eee;">`
out += `<div><a href="` + html.EscapeString(a.ScholarURL) + `">` + html.EscapeString(a.Title) + `</a></div>`
if a.Authors != "" {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c81c85c.

Comment on lines +152 to +155
// renderArticlesHTML generates the HTML for the articles list.
func renderArticlesHTML(articles []*scholarlib.Article) string {
out := ""
for _, a := range articles {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c81c85c.

Comment on lines +24 to +28
{{ if .plugin_content }}
<div id="page-content">{{ .plugin_content | rawHTML }}</div>
{{ else }}
<div id="page-content"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js" integrity="sha512-LhccdVNGe2QMEfI3x4DVV3ckMRe36TfydKss6mJpdHjNFiV07dFpS2xzeZedptKZrwxfICJpez09iNioiSZ3hA==" crossorigin="anonymous"></script>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c81c85c.

Comment on lines +8 to +12
{{ if .plugin_content }}
<div id="page-content">{{ .plugin_content | rawHTML }}</div>
{{ else }}
<div id="page-content"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js" integrity="sha512-LhccdVNGe2QMEfI3x4DVV3ckMRe36TfydKss6mJpdHjNFiV07dFpS2xzeZedptKZrwxfICJpez09iNioiSZ3hA==" crossorigin="anonymous"></script>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c81c85c.

Comment on lines +8 to +12
{{ if .plugin_content }}
<div id="page-content">{{ .plugin_content | rawHTML }}</div>
{{ else }}
<div id="page-content"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js" integrity="sha512-LhccdVNGe2QMEfI3x4DVV3ckMRe36TfydKss6mJpdHjNFiV07dFpS2xzeZedptKZrwxfICJpez09iNioiSZ3hA==" crossorigin="anonymous"></script>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c81c85c.

Comment on lines 123 to 125
return "page_content.html", gin.H{
"plugin_content": `<div class="alert alert-warning">Google Scholar ID not configured. Set it in the Scholar plugin settings.</div>`,
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c81c85c.

Comment on lines +138 to +141
log.Printf("Scholar query failed: %v", err)
data["articles"] = make([]*scholarlib.Article, 0)
data["errors"] = err.Error()
return "page_content.html", gin.H{
"plugin_content": `<div class="alert alert-danger">` + html.EscapeString(err.Error()) + `</div>`,
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in c81c85c.

- Use strings.Builder instead of string concatenation for O(n) HTML generation
- Join meta fields (year, journal, citations) with separator to avoid leading middot
- Validate URLs with safeHref — only allow http/https schemes
- Return "No publications found" for empty article list
- Use has_plugin_content boolean flag in templates instead of truthy string check
- Add role="alert" to warning and error alert divs for accessibility
- Add tests: empty articles, article rendering with all fields, XSS escaping, safeHref validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@compscidr compscidr merged commit 1fa7e76 into main Mar 17, 2026
1 check passed
@compscidr compscidr deleted the fix/plugin-pages-use-content-template branch March 17, 2026 04:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

research: show citation count

2 participants