This is an automated email from the ASF dual-hosted git repository. janhoy pushed a commit to branch common-posts-page in repository https://gitbox.apache.org/repos/asf/solr-site.git
commit 0d1bf4be97b5bc7c5f8af2e132faacbca622425a Author: Jan Høydahl <[email protected]> AuthorDate: Tue Nov 18 08:54:27 2025 +0100 Common Posts page --- content/pages/posts.md | 4 ++ pelicanconf.py | 6 +- plugins/combined_posts/__init__.py | 110 ++++++++++++++++++++++++++++++++++++ themes/solr/static/css/base.css | 73 +++++++++++++++++++++++- themes/solr/templates/_header.html | 5 +- themes/solr/templates/bloghome.html | 9 +++ themes/solr/templates/news.html | 9 +++ themes/solr/templates/posts.html | 65 +++++++++++++++++++++ 8 files changed, 275 insertions(+), 6 deletions(-) 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 5c74eed45..37e3df7ee 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..9e995007f 100644 --- a/themes/solr/static/css/base.css +++ b/themes/solr/static/css/base.css @@ -1032,6 +1032,77 @@ 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; } + +/* + * Posts Page Styles + */ +.posts-filter { + margin-bottom: 30px; + padding-bottom: 20px; + border-bottom: 1px solid #e4e2dd; +} + +.posts-filter a { + margin-right: 20px; + text-transform: uppercase; + font-size: 0.9em; + font-weight: 500; +} + +.posts-filter a.active { + color: #2d7a3e; + font-weight: bold; +} + +.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/templates/_header.html b/themes/solr/templates/_header.html index 190dd5ffb..f04060144 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">Posts</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..4b4b96732 100644 --- a/themes/solr/templates/bloghome.html +++ b/themes/solr/templates/bloghome.html @@ -17,7 +17,16 @@ padding-top: 70px; margin-top: -70px; } + .breadcrumb { + margin-bottom: 20px; + font-size: 0.9em; + } </style> + + <div class="breadcrumb"> + <a href="{{ SITEURL }}/posts.html">← Back to All Posts</a> + </div> + <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..edc20c4ae 100644 --- a/themes/solr/templates/news.html +++ b/themes/solr/templates/news.html @@ -18,7 +18,16 @@ padding-top: 70px; margin-top: -70px; } + .breadcrumb { + margin-bottom: 20px; + font-size: 0.9em; + } </style> + + <div class="breadcrumb"> + <a href="{{ SITEURL }}/posts.html">← Back to All Posts</a> + </div> + <h1 id="solr-news">Solr<sup>™</sup> News<a class="headerlink" href="#solr-news" title="Permanent link">¶</a></h1> {{page.content}} diff --git a/themes/solr/templates/posts.html b/themes/solr/templates/posts.html new file mode 100644 index 000000000..2e5e44dd0 --- /dev/null +++ b/themes/solr/templates/posts.html @@ -0,0 +1,65 @@ +{% extends "page.html" %} + +{% 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"> + + + <h1 id="solr-posts">Solr<sup>™</sup> Posts<a class="headerlink" href="#solr-posts" title="Permanent link">¶</a></h1> + {{page.content}} + + <div class="posts-filter"> + <strong>View:</strong> + <a href="{{ SITEURL }}/posts.html" class="active">All Posts</a> + <a href="{{ SITEURL }}/blog.html">Blog</a> + <a href="{{ SITEURL }}/news.html">News</a> + </div> + + {% 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 %}
