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">

Reply via email to