This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-site.git
The following commit(s) were added to refs/heads/main by this push:
new 610bfafa3 Common Posts page (#161)
610bfafa3 is described below
commit 610bfafa366b8f75ac1cd8888b2298488c80ac47
Author: Jan Høydahl <[email protected]>
AuthorDate: Wed Mar 18 02:08:18 2026 +0100
Common Posts page (#161)
---
content/pages/news.md | 2 +-
content/pages/posts.md | 4 ++
pelicanconf.py | 6 +-
plugins/combined_posts/__init__.py | 110 ++++++++++++++++++++++++++++++++++
themes/solr/static/css/base.css | 58 +++++++++++++++++-
themes/solr/static/javascript/main.js | 8 ++-
themes/solr/templates/_header.html | 5 +-
themes/solr/templates/bloghome.html | 10 +++-
themes/solr/templates/news.html | 12 +++-
themes/solr/templates/posts.html | 81 +++++++++++++++++++++++++
themes/solr/templates/subnav.html | 2 +
11 files changed, 287 insertions(+), 11 deletions(-)
diff --git a/content/pages/news.md b/content/pages/news.md
index 2f9638a10..b540b6057 100644
--- a/content/pages/news.md
+++ b/content/pages/news.md
@@ -3,4 +3,4 @@ URL: news.html
save_as: news.html
template: news
-You may also read these news as an [ATOM feed](/feeds/solr/news.atom.xml).
+You may also read these news as ATOM feeds:
[Announcements](/feeds/solr/news.atom.xml), [Security
Announcements](/feeds/solr/security.atom.xml)..
diff --git a/content/pages/posts.md b/content/pages/posts.md
new file mode 100644
index 000000000..8c120fdd5
--- /dev/null
+++ b/content/pages/posts.md
@@ -0,0 +1,4 @@
+Title: Posts
+URL: posts.html
+save_as: posts.html
+template: posts
diff --git a/pelicanconf.py b/pelicanconf.py
index 440fbbd12..ee0c7925f 100755
--- a/pelicanconf.py
+++ b/pelicanconf.py
@@ -97,10 +97,14 @@ PLUGINS = [
'jinja2content',
'regex_replace',
'age_days_lt',
- 'vex'
+ 'vex',
+ 'combined_posts',
# 'md_inline_extension',
]
+# Configuration for combined posts pagination
+COMBINED_POSTS_PER_PAGE = 20
+
MARKDOWN = {
'extension_configs': {
'toc': {},
diff --git a/plugins/combined_posts/__init__.py
b/plugins/combined_posts/__init__.py
new file mode 100644
index 000000000..23268794a
--- /dev/null
+++ b/plugins/combined_posts/__init__.py
@@ -0,0 +1,110 @@
+"""
+Pelican plugin to generate a combined posts page with pagination.
+
+Combines articles (solr/news, solr/security) with pages (solr/blogposts),
+sorts them chronologically, and generates paginated output.
+"""
+
+from pelican import signals
+from pelican.generators import Generator
+from pelican.paginator import Paginator
+from pelican.writers import Writer
+import os
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class CombinedPostsGenerator(Generator):
+ """
+ Generator for creating a combined posts page with pagination.
+
+ Combines articles from 'solr/news' and 'solr/security' categories
+ with pages from 'solr/blogposts' category, sorts chronologically,
+ and generates paginated HTML pages.
+ """
+
+ def generate_output(self, writer):
+ """Generate paginated combined posts pages."""
+
+ # Get all articles and pages
+ articles = self.context.get('articles', [])
+ pages = self.context.get('pages', [])
+
+ # Filter and combine
+ # Include articles from news and security categories
+ combined_articles = [
+ a for a in articles
+ if hasattr(a, 'category') and a.category and
+ a.category.name in ['solr/news', 'solr/security']
+ ]
+
+ # Include pages from blogposts category
+ combined_pages = [
+ p for p in pages
+ if hasattr(p, 'category') and p.category and
+ p.category.name == 'solr/blogposts'
+ ]
+
+ # Combine all
+ combined_posts = combined_articles + combined_pages
+
+ # Sort by date, newest first
+ combined_posts.sort(key=lambda x: x.date, reverse=True)
+
+ # Get pagination settings
+ per_page = self.settings.get('COMBINED_POSTS_PER_PAGE', 20)
+
+ # Create paginator
+ paginator = Paginator('posts', 'posts{number}.html',
+ combined_posts, self.settings, per_page)
+
+ # Get the template
+ template = self.get_template('posts')
+
+ # Generate each page
+ for page_num in range(1, paginator.num_pages + 1):
+ posts_page = paginator.page(page_num)
+
+ # Build context
+ context = self.context.copy()
+ context.update({
+ 'posts': posts_page.object_list,
+ 'posts_page': posts_page,
+ 'posts_paginator': paginator,
+ 'page': posts_page, # For breadcrumb/metadata
+ 'title': 'Posts',
+ 'slug': 'posts',
+ })
+
+ # Determine output path
+ if page_num == 1:
+ output_path = os.path.join(self.output_path, 'posts.html')
+ url = 'posts.html'
+ else:
+ output_path = os.path.join(
+ self.output_path,
+ f'posts{page_num}.html'
+ )
+ url = f'posts{page_num}.html'
+
+ # Ensure output directory exists
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
+
+ # Render and write
+ output = template.render(context)
+ with open(output_path, 'w', encoding='utf-8') as f:
+ f.write(output)
+
+ # Log
+ logger.info(f'Writing {url} ({page_num}/{paginator.num_pages})')
+
+
+def get_generators(pelican):
+ """Register the CombinedPostsGenerator."""
+ return CombinedPostsGenerator
+
+
+def register():
+ """Plugin registration hook."""
+ signals.get_generators.connect(get_generators)
diff --git a/themes/solr/static/css/base.css b/themes/solr/static/css/base.css
index e33785f13..a11206b42 100644
--- a/themes/solr/static/css/base.css
+++ b/themes/solr/static/css/base.css
@@ -835,6 +835,11 @@ section.list ul li ul li {
z-index: 2000;
}
+.sub-nav dd a.selected {
+ color: #2d7a3e;
+ font-weight: bold;
+}
+
.codehilite {
margin: 10px 0;
background-color: #EEEEEE;
@@ -1032,6 +1037,57 @@ ul li div.box div.img.logo-container.orange-background {
background-color:#D9411E;
}
.full-width .gray .box.logo-box {
- position: relative;
+ position: relative;
border: 1px solid #CCC;
}
+
+
+.post-item-title {
+ color: #D9411E;
+}
+
+.post-item-title a {
+ color: #D9411E;
+ text-decoration: none;
+}
+
+.post-item-title a:hover {
+ text-decoration: underline;
+}
+
+.read-more {
+ margin-top: 10px;
+ font-size: 0.9em;
+}
+
+.read-more a {
+ color: #D9411E;
+ font-weight: 600;
+}
+
+/*
+ * Posts Page Pagination
+ */
+.pagination {
+ margin-top: 40px;
+ padding-top: 20px;
+ border-top: 1px solid #e4e2dd;
+ text-align: center;
+}
+
+.pagination a {
+ margin: 0 10px;
+ color: #D9411E;
+ font-weight: 600;
+ text-decoration: none;
+}
+
+.pagination a:hover {
+ text-decoration: underline;
+}
+
+.pagination-info {
+ margin: 10px 0;
+ font-size: 0.95em;
+ color: #555;
+}
diff --git a/themes/solr/static/javascript/main.js
b/themes/solr/static/javascript/main.js
index 4bd2bb84d..a45bcad22 100644
--- a/themes/solr/static/javascript/main.js
+++ b/themes/solr/static/javascript/main.js
@@ -101,7 +101,13 @@
scope.$watch(function() { return window.location.pathname },
function(n, o, s) {
scope.model.path = window.location.pathname
$(el).find('a').removeClass('selected')
- $(el).find('a[href="' + scope.model.path +
'"]').addClass('selected')
+ var path = scope.model.path
+ var newsPages = ['/news.html', '/blog.html']
+ if (newsPages.indexOf(path) !== -1) {
+ $(el).find('a[href$="/posts.html"]').addClass('selected')
+ } else {
+ $(el).find('a[href="' + path + '"]').addClass('selected')
+ }
})
}
diff --git a/themes/solr/templates/_header.html
b/themes/solr/templates/_header.html
index 190dd5ffb..b9ae4ae45 100644
--- a/themes/solr/templates/_header.html
+++ b/themes/solr/templates/_header.html
@@ -11,10 +11,7 @@
<div class="top-bar-section">
<ul class="navigation right">
<li>
- <a href="{{ SITEURL }}/blog.html">Blog</a>
- </li>
- <li>
- <a href="{{ SITEURL }}/news.html">News</a>
+ <a href="{{ SITEURL }}/posts.html">News</a>
</li>
<li>
<a href="{{ SITEURL }}/security.html">Security</a>
diff --git a/themes/solr/templates/bloghome.html
b/themes/solr/templates/bloghome.html
index 61782a6cb..a0ab9ff68 100644
--- a/themes/solr/templates/bloghome.html
+++ b/themes/solr/templates/bloghome.html
@@ -1,4 +1,11 @@
-{% extends "page.html" %}
+{% extends "subnav.html" %}
+
+{% block subnav_header %}<style>.container { padding-top: 0; }</style>{%
endblock %}
+{% block subnav_nav_items %}
+<dd><a href="{{ SITEURL }}/posts.html">All News</a></dd>
+<dd><a href="{{ SITEURL }}/blog.html" class="selected">Blog</a></dd>
+<dd><a href="{{ SITEURL }}/news.html">Announcements</a></dd>
+{% endblock %}
{% block ng_directives %}x-ng-app-root="/solr"{% endblock %}
@@ -18,6 +25,7 @@
margin-top: -70px;
}
</style>
+
<h1 id="solr-blogs">Solr<sup>™</sup> Blog Posts<a class="headerlink"
href="#solr-blog-posts" title="Permanent link">¶</a></h1>
{{page.content}}
diff --git a/themes/solr/templates/news.html b/themes/solr/templates/news.html
index 827aac9b8..f456c0b9e 100644
--- a/themes/solr/templates/news.html
+++ b/themes/solr/templates/news.html
@@ -1,4 +1,11 @@
-{% extends "page.html" %}
+{% extends "subnav.html" %}
+
+{% block subnav_header %}<style>.container { padding-top: 0; }</style>{%
endblock %}
+{% block subnav_nav_items %}
+<dd><a href="{{ SITEURL }}/posts.html">All News</a></dd>
+<dd><a href="{{ SITEURL }}/blog.html">Blog</a></dd>
+<dd><a href="{{ SITEURL }}/news.html" class="selected">Announcements</a></dd>
+{% endblock %}
{% block ng_directives %}x-ng-app-root="/solr"{% endblock %}
{% block rss %}<link rel="alternate" type="application/atom+xml" title="Solr
news except security news" href="/feeds/solr/news.atom.xml" />{% endblock %}
@@ -19,7 +26,8 @@
margin-top: -70px;
}
</style>
- <h1 id="solr-news">Solr<sup>™</sup> News<a class="headerlink"
href="#solr-news" title="Permanent link">¶</a></h1>
+
+ <h1 id="solr-news">Solr<sup>™</sup> Announcements<a class="headerlink"
href="#solr-news" title="Permanent link">¶</a></h1>
{{page.content}}
{% for article in (articles | selectattr("category.name", "in",
['solr/news', 'solr/security'])|list) %}
diff --git a/themes/solr/templates/posts.html b/themes/solr/templates/posts.html
new file mode 100644
index 000000000..5f3ff6139
--- /dev/null
+++ b/themes/solr/templates/posts.html
@@ -0,0 +1,81 @@
+{% extends "subnav.html" %}
+
+{% block subnav_title %}Solr™ News{% endblock %}
+{% block subnav_nav_items %}
+<dd><a href="{{ SITEURL }}/posts.html" class="selected">All News</a></dd>
+<dd><a href="{{ SITEURL }}/blog.html">Blog</a></dd>
+<dd><a href="{{ SITEURL }}/news.html">Announcements</a></dd>
+{% endblock %}
+
+{% block ng_directives %}x-ng-app-root="/solr"{% endblock %}
+{% block rss %}<link rel="alternate" type="application/atom+xml" title="Solr
news except security news" href="/feeds/solr/news.atom.xml" />{% endblock %}
+
+{% block content_inner %}
+<div class="small-12 columns">
+
+ <style type="text/css">
+ .headerlink, .elementid-permalink {
+ visibility: hidden;
+ }
+ h2:hover > .headerlink, h3:hover > .headerlink, h1:hover > .headerlink,
h6:hover > .headerlink, h4:hover > .headerlink, h5:hover > .headerlink,
dt:hover > .elementid-permalink {
+ visibility: visible;
+ }
+ h2 {
+ /* Avoid news title being hidden behind header when linked by anchor
link */
+ padding-top: 70px;
+ margin-top: -70px;
+ }
+ .breadcrumb {
+ margin-bottom: 20px;
+ font-size: 0.9em;
+ }
+ </style>
+
+ {{page.content}}
+
+ {% for article in posts %}
+ <h2 id="{{ article.slug }}" class="post-item-title">
+ {% if article.save_as %}
+ {# Blog post - has its own page #}
+ <a href="{{ article.url }}">{{ article.title }}</a>
+ {% else %}
+ {# News/Security article - link to news.html with anchor #}
+ <a href="{{ SITEURL }}/news.html#{{ article.slug }}">{{ article.title
}}</a>
+ {% endif %}
+ <a class="headerlink" href="#{{ article.slug }}" title="Permanent
link">¶</a>
+ </h2>
+ <h5>
+ {% if article.category and article.category.name in ['solr/news',
'solr/security'] %}
+ <strong>{{ article.category.name | replace('solr/', '') | title
}}:</strong>
+ {% endif %}
+ {{ article.locale_date }}
+ </h5>
+ {% if article.summary %}
+ <p>{{ article.summary | striptags | truncate(512) }}</p>
+ {% else %}
+ <p>{{ article.content | striptags | truncate(512) }}</p>
+ {% endif %}
+ {% if article.category and article.category.name in ['solr/news',
'solr/security'] %}
+ <div class="read-more">
+ <a href="{{ SITEURL }}/news.html#{{ article.slug }}">Read full article
on news page →</a>
+ </div>
+ {% endif %}
+ <hr/>
+ {% endfor %}
+
+ {% if posts_paginator and posts_paginator.num_pages > 1 %}
+ <div class="pagination">
+ {% if posts_page.has_previous() %}
+ <a href="{{ SITEURL }}/{% if posts_page.previous_page_number() == 1
%}posts.html{% else %}posts{{ posts_page.previous_page_number() }}.html{% endif
%}">← Previous</a>
+ {% endif %}
+ <span class="pagination-info">
+ Page {{ posts_page.number }} of {{ posts_paginator.num_pages }}
+ </span>
+ {% if posts_page.has_next() %}
+ <a href="{{ SITEURL }}/posts{{ posts_page.next_page_number()
}}.html">Next →</a>
+ {% endif %}
+ </div>
+ {% endif %}
+
+</div>
+{% endblock content_inner %}
diff --git a/themes/solr/templates/subnav.html
b/themes/solr/templates/subnav.html
index a54d91b48..b35b5a341 100644
--- a/themes/solr/templates/subnav.html
+++ b/themes/solr/templates/subnav.html
@@ -1,12 +1,14 @@
{% extends "page.html" %}
{% block subnav %}
+{% block subnav_header %}
<div class="row">
<div class="small-12 text-center columns">
<h1>{% block subnav_title %}{% endblock %}<br/>
<small>{% block subnav_subtitle %}{% endblock %}</small></h1>
</div>
</div>
+{% endblock %}
<div class="sub-nav-container">
<div class="row sub-nav-border anchor-top">
<div class="small-12 text-center columns">