Plugin pages render through generic content template#522
Merged
Conversation
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>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
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_contentsupport topage_content.htmltemplates (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.htmland generate the publications list as HTML. - Delete theme-specific
page_research.html(and the legacy defaultresearch.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.
plugins/scholar/scholar.go
Outdated
Comment on lines
+147
to
+149
| return "page_content.html", gin.H{ | ||
| "plugin_content": renderArticlesHTML(articles), | ||
| } |
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>` | ||
| } |
plugins/scholar/scholar.go
Outdated
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 != "" { |
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 { |
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> |
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> |
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> |
plugins/scholar/scholar.go
Outdated
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>`, | ||
| } |
plugins/scholar/scholar.go
Outdated
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>`, | ||
| } |
- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Plugins no longer need theme-specific templates. Page plugins now generate their HTML and pass it via
plugin_content, which the existingpage_content.htmltemplate renders as raw HTML. This means any theme works with any page plugin automatically.How it works
page_content.htmlchecks for{{ .plugin_content }}— if present, renders it as raw HTML{{ .page.Content }}What changed
RenderPagenow returnspage_content.html+plugin_contentHTML instead ofpage_research.html+articlesdataplugin_contentpage_research.htmlandresearch.htmlfrom all themes — no longer neededBenefits
Closes #513
Test plan
🤖 Generated with Claude Code