diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..e36af018 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,91 @@ +name: CI + +on: + push: + branches: + - 'main' + - '*.*' + - '!*backport*' + tags: + - 'v*' + - '!*dev*' + - '!*pre*' + - '!*post*' + pull_request: + workflow_dispatch: + schedule: + # ┌───────── minute (0 - 59) + # │ ┌───────── hour (0 - 23) + # │ │ ┌───────── day of the month (1 - 31) + # │ │ │ ┌───────── month (1 - 12 or JAN-DEC) + # │ │ │ │ ┌───────── day of the week (0 - 6 or SUN-SAT) + - cron: '0 7 * * *' # Every day at 07:00 UTC + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + core: + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main + with: + submodules: false + coverage: codecov + libraries: | + apt: + - pandoc + - graphviz + envs: | + - linux: py312-sphinx7 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + test: + needs: [core] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main + with: + submodules: false + coverage: codecov + libraries: | + brew: + - pandoc + - graphviz + choco: + - pandoc + - graphviz + apt: + - pandoc + - graphviz + envs: | + - linux: py311-sphinx6 + - macos: py310-sphinx5 + - windows: py39-sphinx5 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + docs: + needs: [test] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main + with: + default_python: '3.9' + submodules: false + pytest: false + envs: | + - linux: py312-docs + + extras: + needs: [test] + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main + with: + default_python: '3.9' + submodules: false + coverage: codecov + libraries: | + apt: + - pandoc + - graphviz + envs: | + - linux: py312-sphinxdev + - linux: py312-conda + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 5b6c905b..0bcd7e3b 100644 --- a/.gitignore +++ b/.gitignore @@ -227,3 +227,4 @@ test/ pydata-sphinx-theme/ _build _version.py +demo/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 28043656..5abd21da 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,6 @@ ci: autofix_prs: false + autoupdate_schedule: "quarterly" repos: - repo: https://github.com/myint/docformatter rev: v1.7.5 diff --git a/Makefile b/Makefile index 522b9ba5..fc86bab1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: demo rebuild tests demo: - rm -rf demo + rm -rf demo && mkdir demo printf "demo\nABlog\nABlog Team\nhttps://ablog.readthedocs.io/" | ablog start rebuild: diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 6508b2cb..00000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: $(BuildDefinitionName)_$(Date:yyyyMMdd)$(Rev:.rr) - -schedules: - - cron: "0 0 * * *" - displayName: Daily midnight build - branches: - include: - - main - always: true - -resources: - repositories: - - repository: OpenAstronomy - type: github - endpoint: sunpy - name: OpenAstronomy/azure-pipelines-templates - ref: master - -trigger: - branches: - include: - - '*' - exclude: - - '*backport*' - tags: - include: - - 'v*' - exclude: - - '*dev*' - - '*pre*' - - '*post*' - -pr: - autoCancel: true - -stages: - - stage: FirstPhaseTests - displayName: Core Tests - jobs: - - template: run-tox-env.yml@OpenAstronomy - parameters: - submodules: false - coverage: codecov - libraries: - apt: - - pandoc - - graphviz - envs: - - linux: py311-sphinx6 - - - stage: SecondPhaseTests - displayName: Stage 2 Tests - dependsOn: FirstPhaseTests - jobs: - - template: run-tox-env.yml@OpenAstronomy - parameters: - submodules: false - coverage: codecov - libraries: - brew: - - pandoc - - graphviz - choco: - - pandoc - - graphviz - apt: - - pandoc - - graphviz - envs: - - linux: py310-sphinx5 - - macos: py39-sphinx5 - - windows: py39-sphinx5 - - - stage: ThirdPhaseTests - displayName: Stage 3 Tests - dependsOn: SecondPhaseTests - jobs: - - template: run-tox-env.yml@OpenAstronomy - parameters: - submodules: false - coverage: codecov - libraries: - apt: - - pandoc - - graphviz - envs: - - linux: py310-sphinxdev - - linux: py310-docs - - linux: py310-conda diff --git a/conftest.py b/conftest.py index 96deb252..fa383537 100644 --- a/conftest.py +++ b/conftest.py @@ -1,7 +1,8 @@ +from pathlib import Path + import docutils import pytest import sphinx -from sphinx.testing.path import path # Load app, status and warning fixtures. pytest_plugins = ["sphinx.testing.fixtures"] @@ -18,7 +19,7 @@ def pytest_report_header(config): @pytest.fixture(scope="session") def rootdir(): - return path(__file__).parent.abspath() / "roots" + return Path(__file__).parent.absolute() / "roots" @pytest.fixture(autouse=True) diff --git a/docs/conf.py b/docs/conf.py index bcb299f5..5001e64b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,7 @@ import re from pathlib import Path -from pkg_resources import get_distribution +from packaging.version import parse as _parse from sphinx import addnodes import ablog @@ -22,11 +22,7 @@ "myst_parser", ] -versionmod = get_distribution("ablog") -version = ".".join(versionmod.version.split(".")[:3]) -release = versionmod.version.split("+")[0] -is_development = ".dev" in release - +version = str(_parse(ablog.__version__)) project = "ABlog" copyright = "2014-2022, ABlog Team" master_doc = "index" @@ -35,13 +31,11 @@ ".md": "markdown", } exclude_patterns = ["_build", "docs/manual/.ipynb_checkpoints"] - html_title = "ABlog" html_use_index = True html_domain_indices = False html_show_sourcelink = True html_favicon = "_static/ablog.ico" - blog_title = "ABlog" blog_baseurl = "https://ablog.readthedocs.io/" blog_locations = { @@ -74,7 +68,6 @@ disqus_shortname = "https-ablog-readthedocs-io" disqus_pages = True fontawesome_link_cdn = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" - html_style = "alabaster.css" html_theme = "alabaster" html_sidebars = { @@ -98,7 +91,6 @@ "description": "ABlog for blogging with Sphinx", "logo": "ablog.png", } - intersphinx_mapping = { "python": ("https://docs.python.org/", None), "sphinx": ("https://www.sphinx-doc.org/en/master/", None), diff --git a/docs/manual/ablog-configuration-options.rst b/docs/manual/ablog-configuration-options.rst index ec997bc2..f24e50e6 100644 --- a/docs/manual/ablog-configuration-options.rst +++ b/docs/manual/ablog-configuration-options.rst @@ -91,6 +91,10 @@ Post related Date display format (default is ``'%b %d, %Y'``) for published posts that goes as input to :meth:`datetime.date.strftime`. +.. confval:: post_date_format_short + + Date display format in recent posts (default is ``'%d %B'``) for published posts that goes as input to :meth:`datetime.date.strftime`. + .. confval:: post_auto_excerpt Number of paragraphs (default is ``1``) that will be displayed as an excerpt from the post. diff --git a/docs/manual/posting-and-listing.rst b/docs/manual/posting-and-listing.rst index afe5cce4..1ae9a0f9 100644 --- a/docs/manual/posting-and-listing.rst +++ b/docs/manual/posting-and-listing.rst @@ -67,6 +67,10 @@ Any page in a Sphinx_ project can be converted to a post using the following dir You can change this behavior and also add an image to the excerpt. To find out how, see :ref:`post-excerpts-and-images`. + **Canonical links** + + If you re-publish content already existing on another URL (e.g., if you re-publish content from an employer's blog your personal one), use the ``canonical_link`` parameter to create a [canonical link relation](https://datatracker.ietf.org/doc/html/rfc6596) to the original version. + **External links** If you'd like a post to point to an external website (e.g., if you host your posts on a blogging platform like Medium but wish to maintain a list of posts on your ``Ablog`` site), use the ``external_link`` parameter and this will be used instead. diff --git a/docs/release/ablog-v0.11-released.rst b/docs/release/ablog-v0.11-released.rst index 0d8615fe..7858f09c 100644 --- a/docs/release/ablog-v0.11-released.rst +++ b/docs/release/ablog-v0.11-released.rst @@ -9,18 +9,18 @@ ABlog v0.11 released ABlog v0.11 is released with the main focus being to update and tweak the HTML templates allow themes to override the default templates. In addition, all ablog elements in the templates wrapped in ``ablog__*`` divs to allow custom CSS rules. -We also adopt `NEP29 ` and drop support for older versions of Python and package versions that are 24 months old or older at time of release. +We also adopt `NEP29 `__ and drop support for older versions of Python and package versions that are 24 months old or older at time of release. Added support for external links to be posts. There are several breaking changes: -- 1. The template files are now in the `templates/ablog` folder. +- 1. The template files are now in the ``templates/ablog`` folder. Older templates are still in the old location but will raise a warning. These will be removed in a future version, please do not use them anymore. You will need to update any paths to them to add "ablog/" to the path. - 2. ``ablog`` has support for not injecting its own templates into the Sphinx build. - This is supported by add `skip_injecting_base_ablog_templates = True` to your configuration file. + This is supported by add ``skip_injecting_base_ablog_templates = True`` to your configuration file. - 3. Minimum version of Python is >=3.9 and Sphinx is >=5.0. Pull Requests merged in: @@ -43,3 +43,45 @@ Pull Requests merged in: `append posts to atom feed to keep post order from new to old `__ from `lexming `__. `avoid spurious warning about posts with front-matter and post directive `__ from `lexming `__. + +ABlog v0.11.3 released +---------------------- + +Pull Requests merged in: + +`use fully qualified URLs for images in atom feed `__ from `lexming `__. + +ABlog v0.11.4 released +---------------------- + +Pull Requests merged in: + +`Use paragraph instead of container for blog post excerpts `__ from `dstansby `__. + +ABlog v0.11.5 released +---------------------- + +Pull Requests merged in: + +`Fix incorrect /div when using discuss `__ from `Cadair `__. + +ABlog v0.11.6 released +---------------------- + +Pull Requests merged in: + +`Adds IT locale `__ from `Stefano David `__. + +`Enables configuring a canonical_link for individual posts `__ from `Hendrik Makait `__. + +ABlog v0.11.7 released +---------------------- + +Pull Requests merged in: + +`Add stylesheet for tagcloud `__ from `Shengyu Zhang `__. + +`Create demo/ before running ablog start `__ from `Shengyu Zhang `__. + + +`Add span to more items in templates `__ from `Nabil Freij `__. diff --git a/roots/test-canonical/canonical.rst b/roots/test-canonical/canonical.rst new file mode 100644 index 00000000..c5e706bc --- /dev/null +++ b/roots/test-canonical/canonical.rst @@ -0,0 +1,9 @@ +.. post:: 2021-12-01 + :tags: Canonical + :canonical_link: https://canonical.example.org/foo.html + +Canonical post +============= + +This post will get generated, but its [canonical link](https://datatracker.ietf.org/doc/html/rfc6596) +in the header will point to ``canonical_link``. diff --git a/roots/test-canonical/conf.py b/roots/test-canonical/conf.py new file mode 100644 index 00000000..781e7747 --- /dev/null +++ b/roots/test-canonical/conf.py @@ -0,0 +1,20 @@ +extensions = ["ablog"] + +# Enable Atom feed generation +blog_baseurl = "https://blog.example.com/" +# Include full post in feeds +blog_feed_fulltext = True +# Add a social media Atom feed +blog_feed_templates = { + # Use defaults, no templates + "atom": {}, + # Create content text suitable posting to micro-bogging + "social": { + # Format tags as hashtags and append to the content + "content": "{{ title }}{% for tag in post.tags %}" + " #{{ tag.name|trim()|replace(' ', '') }}" + "{% endfor %}", + }, +} +# Sphinx creates canonical links pointing to this base URL by default +html_baseurl = blog_baseurl diff --git a/roots/test-canonical/index.rst b/roots/test-canonical/index.rst new file mode 100644 index 00000000..4a0bb51b --- /dev/null +++ b/roots/test-canonical/index.rst @@ -0,0 +1,2 @@ +test-external +============= diff --git a/roots/test-canonical/post.rst b/roots/test-canonical/post.rst new file mode 100644 index 00000000..46ac2929 --- /dev/null +++ b/roots/test-canonical/post.rst @@ -0,0 +1,11 @@ +.. post:: 2022-12-01 + :tags: Foo Tag, BarTag + +Foo Post Title +============== + + Foo post description `with link`_. + +Foo post content. + +.. _`with link`: https://example.com diff --git a/roots/test-canonical/postlist.rst b/roots/test-canonical/postlist.rst new file mode 100644 index 00000000..d944b649 --- /dev/null +++ b/roots/test-canonical/postlist.rst @@ -0,0 +1,4 @@ +postlist +======== + +.. postlist:: diff --git a/setup.cfg b/setup.cfg index 7e595fb5..e54595f8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -58,6 +58,8 @@ filterwarnings = always::pytest.PytestConfigWarning # Sphinx and other packages raise these ignore:'imghdr' is deprecated and slated for removal in Python 3.13:DeprecationWarning + # python-datetuil + ignore:datetime.datetime.utcfromtimestamp:DeprecationWarning [pycodestyle] max_line_length = 120 diff --git a/src/ablog/__init__.py b/src/ablog/__init__.py index 778f1163..91118f56 100755 --- a/src/ablog/__init__.py +++ b/src/ablog/__init__.py @@ -70,6 +70,8 @@ def html_page_context(app, pagename, templatename, context, doctree): if builder_support(app): context["ablog"] = blog = Blog(app) context["anchor"] = anchor + if pagename in blog and blog[pagename].canonical_link: + context["pageurl"] = blog[pagename].canonical_link # following is already available for archive pages if blog.blog_baseurl and "feed_path" not in context: context["feed_path"] = blog.blog_path @@ -89,6 +91,10 @@ def config_inited(app, config): ) app.config.matched_blog_posts = matched_patterns + # Add ablog stylesheets to static_path. + static_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "stylesheets")) + app.config.html_static_path.append(static_path) + def builder_inited(app): if not isinstance(app.builder, StandaloneHTMLBuilder) or app.config.skip_injecting_base_ablog_templates: diff --git a/src/ablog/blog.py b/src/ablog/blog.py index 904da76f..daa61868 100644 --- a/src/ablog/blog.py +++ b/src/ablog/blog.py @@ -358,6 +358,7 @@ def __init__(self, blog, docname, info): self.nocomments = info["nocomments"] self.published = date and date < TOMORROW self.draft = not self.published + self.canonical_link = info["canonical_link"] self.external_link = info["external_link"] self._title = info["title"] self.excerpt = info["excerpt"] @@ -413,7 +414,7 @@ def to_html(self, pagename, fulltext=False, drop_h1=True, img_url=False): else: doctree.append(deepcopy) else: - excerpt_container = nodes.container() + excerpt_container = nodes.paragraph() excerpt_container.attributes["classes"].append("ablog-post-excerpt") for node in self.excerpt: excerpt_container.append(node.deepcopy()) diff --git a/src/ablog/locales/it/LC_MESSAGES/sphinx.mo b/src/ablog/locales/it/LC_MESSAGES/sphinx.mo new file mode 100644 index 00000000..2c2495df Binary files /dev/null and b/src/ablog/locales/it/LC_MESSAGES/sphinx.mo differ diff --git a/src/ablog/locales/it/LC_MESSAGES/sphinx.po b/src/ablog/locales/it/LC_MESSAGES/sphinx.po new file mode 100644 index 00000000..8c977602 --- /dev/null +++ b/src/ablog/locales/it/LC_MESSAGES/sphinx.po @@ -0,0 +1,130 @@ +# Translations template for ablog. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the ablog project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: ablog 0.10.30.dev19+gb9b1a31\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-11-14 16:46-0800\n" +"PO-Revision-Date: 2023-09-15 11:32+0200\n" +"Last-Translator: Stefano David \n" +"Language-Team: \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Generated-By: Babel 2.11.0\n" +"X-Generator: Poedit 3.3.2\n" + +#: ablog/post.py:272 +msgid "Updated on " +msgstr "Aggiornato il " + +#: ablog/post.py:564 ablog/templates/ablog/authors.html:3 +#: ablog/templates/authors.html:4 +msgid "Authors" +msgstr "Autori" + +#: ablog/post.py:564 ablog/post.py:631 +msgid "Posts by" +msgstr "Articoli di" + +#: ablog/post.py:565 ablog/templates/ablog/locations.html:4 +#: ablog/templates/locations.html:5 +msgid "Locations" +msgstr "Località" + +#: ablog/post.py:565 ablog/post.py:632 +msgid "Posts from" +msgstr "Articolo da" + +#: ablog/post.py:566 ablog/templates/ablog/languages.html:4 +#: ablog/templates/languages.html:5 +msgid "Languages" +msgstr "Lingue" + +#: ablog/post.py:566 ablog/post.py:567 ablog/post.py:633 ablog/post.py:634 +msgid "Posts in" +msgstr "Articoli in" + +#: ablog/post.py:567 ablog/templates/ablog/categories.html:4 +#: ablog/templates/categories.html:5 +msgid "Categories" +msgstr "Categorie" + +#: ablog/post.py:568 +msgid "All posts" +msgstr "Tutti gli articoli" + +#: ablog/post.py:568 ablog/post.py:635 +msgid "Posted in" +msgstr "Pubblicato in" + +#: ablog/post.py:569 ablog/templates/ablog/postcard2.html:117 +#: ablog/templates/ablog/tagcloud.html:3 ablog/templates/postcard2.html:118 +#: ablog/templates/tagcloud.html:4 +msgid "Tags" +msgstr "Tag" + +#: ablog/post.py:569 ablog/post.py:636 +msgid "Posts tagged" +msgstr "Articoli con tag" + +#: ablog/post.py:594 +msgid "All Posts" +msgstr "Tutti gli articoli" + +#: ablog/post.py:595 +msgid "All" +msgstr "Tutti" + +#: ablog/post.py:603 +msgid "Drafts" +msgstr "Bozze" + +#: ablog/templates/ablog/archives.html:4 ablog/templates/archives.html:5 +msgid "Archives" +msgstr "Archivi" + +#: ablog/templates/ablog/collection.html:56 ablog/templates/collection.html:57 +msgid "Read more ..." +msgstr "Leggi di più..." + +#: ablog/templates/ablog/postcard2.html:8 ablog/templates/postcard2.html:9 +msgid "Update" +msgstr "Aggiornamento" + +#: ablog/templates/ablog/postcard2.html:20 ablog/templates/postcard2.html:21 +msgid "Author" +msgstr "Autore" + +#: ablog/templates/ablog/postcard2.html:44 ablog/templates/postcard2.html:45 +msgid "Location" +msgstr "Località" + +#: ablog/templates/ablog/postcard2.html:68 ablog/templates/postcard2.html:69 +msgid "Language" +msgstr "Lingua" + +#: ablog/templates/ablog/postcard2.html:92 ablog/templates/postcard2.html:93 +msgid "Category" +msgstr "Categoria" + +#: ablog/templates/ablog/postcard2.html:123 ablog/templates/postcard2.html:124 +msgid "Tag" +msgstr "Tag" + +#: ablog/templates/ablog/postnavy.html:8 ablog/templates/postnavy.html:9 +msgid "Previous" +msgstr "Precedente" + +#: ablog/templates/ablog/postnavy.html:22 ablog/templates/postnavy.html:23 +msgid "Next" +msgstr "Successivo" + +#: ablog/templates/ablog/recentposts.html:4 ablog/templates/recentposts.html:5 +msgid "Recent Posts" +msgstr "Articoli recenti" diff --git a/src/ablog/post.py b/src/ablog/post.py index 635cd3c3..a51f8ac1 100644 --- a/src/ablog/post.py +++ b/src/ablog/post.py @@ -80,6 +80,7 @@ class PostDirective(Directive): "excerpt": int, "exclude": directives.flag, "nocomments": directives.flag, + "canonical_link": str, "external_link": str, } @@ -234,6 +235,7 @@ def _update_post_node(node, options, arguments): node["excerpt"] = options.get("excerpt", None) node["exclude"] = "exclude" in options node["nocomments"] = "nocomments" in options + node["canonical_link"] = options.get("canonical_link", []) node["external_link"] = options.get("external_link", []) return node @@ -406,6 +408,7 @@ def process_posts(app, doctree): "nocomments": node["nocomments"], "image": node["image"], "exclude": node["exclude"], + "canonical_link": node["canonical_link"], "external_link": node["external_link"], "doctree": section_copy, } diff --git a/src/ablog/stylesheets/ablog/tagcloud.css b/src/ablog/stylesheets/ablog/tagcloud.css new file mode 100644 index 00000000..7a6d07eb --- /dev/null +++ b/src/ablog/stylesheets/ablog/tagcloud.css @@ -0,0 +1,36 @@ +ul.ablog-cloud { + list-style: none; + overflow: auto; +} + +ul.ablog-cloud li { + float: left; + height: 20pt; + line-height: 18pt; + margin-right: 5px; +} + +ul.ablog-cloud a { + text-decoration: none; + vertical-align: middle; +} + +li.ablog-cloud-1 { + font-size: 80%; +} + +li.ablog-cloud-2 { + font-size: 95%; +} + +li.ablog-cloud-3 { + font-size: 110%; +} + +li.ablog-cloud-4 { + font-size: 125%; +} + +li.ablog-cloud-5 { + font-size: 140%; +} diff --git a/src/ablog/templates/ablog/catalog.html b/src/ablog/templates/ablog/catalog.html index c55a56b5..7aa0c703 100644 --- a/src/ablog/templates/ablog/catalog.html +++ b/src/ablog/templates/ablog/catalog.html @@ -11,16 +11,17 @@ {% if collection %}

- {{ header }} + {{ header }} {{ collection }} +

{% for post in collection %}

{% if post.published %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} - Draft + Draft {% endif %} -

{% endif %} {% endfor %} -{% endblock %} +{% endblock body %} diff --git a/src/ablog/templates/ablog/collection.html b/src/ablog/templates/ablog/collection.html index 666b92af..f089604a 100644 --- a/src/ablog/templates/ablog/collection.html +++ b/src/ablog/templates/ablog/collection.html @@ -1,4 +1,5 @@ -{%- extends "page.html" %} {% block body %} +{%- extends "page.html" %} +{% block body %} {% macro postlink(post) -%} {% if post.external_link -%} {{- post.external_link -}} @@ -12,21 +13,22 @@

{% endif %} - {{ header }} + {{ header }} {% if collection.href %} {{ collection }} {% else %} {{ collection }} {% endif %} +

{% if ablog.blog_archive_titles %} {% for post in collection %}

{% if post.published %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} - Draft + Draft {% endif %} - {{ post.title }} @@ -45,15 +47,15 @@

{% if fa %} {% endif %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} {% if fa %} {% endif %} {% if post.date %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} - Draft + Draft {% endif %} {% endif %} @@ -66,4 +68,4 @@

{% endfor %} {% endif %}

-{% endblock %} +{% endblock body %} diff --git a/src/ablog/templates/ablog/postcard.html b/src/ablog/templates/ablog/postcard.html index 76192886..56d5650e 100644 --- a/src/ablog/templates/ablog/postcard.html +++ b/src/ablog/templates/ablog/postcard.html @@ -7,15 +7,15 @@

{% if fa %} {% endif %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} {% if fa %} {% endif %} {% if post.date %} - {{ post.date.strftime(ablog.post_date_format)}} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} - Draft + Draft {% endif %} {% endif %}

diff --git a/src/ablog/templates/ablog/postcard2.html b/src/ablog/templates/ablog/postcard2.html index 162f6456..9c35461d 100644 --- a/src/ablog/templates/ablog/postcard2.html +++ b/src/ablog/templates/ablog/postcard2.html @@ -1,9 +1,9 @@ -{% if post.published and post.date != post.update %}
+{% if post.published and post.date != post.update %}
  • {% if fa %} - + {% else %} {{ gettext('Update') }}: {% endif %} @@ -17,7 +17,7 @@ {% if fa %} {% else %} - {{ gettext('Author')}}: + {{ gettext('Author') }}: {% endif %} {% for coll in post.author %} @@ -161,5 +161,5 @@ {% endif %}
  • -
    {% endif %} +
    diff --git a/src/ablog/templates/ablog/redirect.html b/src/ablog/templates/ablog/redirect.html index baa3af6a..79f7468e 100644 --- a/src/ablog/templates/ablog/redirect.html +++ b/src/ablog/templates/ablog/redirect.html @@ -2,7 +2,7 @@ {%- block extrahead %} {{ super() }} -{% endblock %} +{% endblock extrahead %} {% block body %} You are being redirected to {{ post.title }} in {{ ablog.post_redirect_refresh }} seconds; -{% endblock %} +{% endblock body %} diff --git a/src/ablog/templates/ablog/tagcloud.html b/src/ablog/templates/ablog/tagcloud.html index e3ca32f5..3a9eb07c 100644 --- a/src/ablog/templates/ablog/tagcloud.html +++ b/src/ablog/templates/ablog/tagcloud.html @@ -1,5 +1,6 @@ {% if ablog.tags %}
    +

    {{ gettext('Tags') }}

      {% for coll in ablog.tags %} diff --git a/src/ablog/templates/catalog.html b/src/ablog/templates/catalog.html index 0d2e488c..dfb5ba16 100644 --- a/src/ablog/templates/catalog.html +++ b/src/ablog/templates/catalog.html @@ -12,16 +12,16 @@ {% if collection %}

      - {{ header }} - {{ collection }} + {{ header }} + {{ collection }}

      {% for post in collection %}

      {% if post.published %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} - Draft + Draft {% endif %} -

      {% endif %} {% endfor %} -{% endblock %} +{% endblock body %} diff --git a/src/ablog/templates/collection.html b/src/ablog/templates/collection.html index 6cd2a207..a7cf5e0e 100644 --- a/src/ablog/templates/collection.html +++ b/src/ablog/templates/collection.html @@ -1,5 +1,6 @@ {{ warning("collection.html is an old template path, that is no longer used by ablog. Please use ablog/collection.html instead.") }} -{%- extends "page.html" %} {% block body %} +{%- extends "page.html" %} +{% block body %} {% macro postlink(post) -%} {% if post.external_link -%} {{- post.external_link -}} @@ -14,21 +15,22 @@

      {% endif %} - {{ header }} + {{ header }} {% if collection.href %} {{ collection }} {% else %} {{ collection }} {% endif %} +

      {% if ablog.blog_archive_titles %} {% for post in collection %}

      {% if post.published %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} - Draft + Draft {% endif %} - {{ post.title }} @@ -47,15 +49,15 @@

      {% if fa %} {% endif %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} {% if fa %} {% endif %} {% if post.date %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} - Draft + Draft {% endif %} {% endif %} @@ -68,4 +70,4 @@

      {% endfor %} {% endif %}

      -{% endblock %} +{% endblock body %} diff --git a/src/ablog/templates/page.html b/src/ablog/templates/page.html index 875f6a86..f360992c 100644 --- a/src/ablog/templates/page.html +++ b/src/ablog/templates/page.html @@ -19,7 +19,7 @@ type="text/css" /> {% endif %} -{% endblock %} +{% endblock extrahead %} {% block body %} {{ body }}
      @@ -57,4 +57,4 @@

      Comments

      {% endif %}
      -{% endblock %} +{% endblock body %} diff --git a/src/ablog/templates/postcard.html b/src/ablog/templates/postcard.html index ccfa498b..137ae98e 100644 --- a/src/ablog/templates/postcard.html +++ b/src/ablog/templates/postcard.html @@ -8,15 +8,15 @@

      {% if fa %} {% endif %} - {{ post.date.strftime(ablog.post_date_format) }} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} {% if fa %} {% endif %} {% if post.date %} - {{ post.date.strftime(ablog.post_date_format)}} + {{ post.date.strftime(ablog.post_date_format) }} {% else %} - Draft + Draft {% endif %} {% endif %}

      diff --git a/src/ablog/templates/postcard2.html b/src/ablog/templates/postcard2.html index 44762c41..e43f9180 100644 --- a/src/ablog/templates/postcard2.html +++ b/src/ablog/templates/postcard2.html @@ -1,10 +1,10 @@ {{ warning("postcard2.html is an old template path, that is no longer used by ablog. Please use ablog/postcard2.html instead.") }} -{% if post.published and post.date != post.update %}
      +{% if post.published and post.date != post.update %}
    • {% if fa %} - + {% else %} {{ gettext('Update') }}: {% endif %} @@ -18,7 +18,7 @@ {% if fa %} {% else %} - {{ gettext('Author')}}: + {{ gettext('Author') }}: {% endif %} {% for coll in post.author %} @@ -162,5 +162,5 @@ {% endif %}
    • -
      {% endif %} +
    diff --git a/src/ablog/templates/redirect.html b/src/ablog/templates/redirect.html index 21109bb1..83ab0032 100644 --- a/src/ablog/templates/redirect.html +++ b/src/ablog/templates/redirect.html @@ -3,7 +3,7 @@ {%- block extrahead %} {{ super() }} -{% endblock %} +{% endblock extrahead %} {% block body %} You are being redirected to {{ post.title }} in {{ ablog.post_redirect_refresh }} seconds; -{% endblock %} +{% endblock body %} diff --git a/src/ablog/templates/tagcloud.html b/src/ablog/templates/tagcloud.html index 67592664..c6b3d87f 100644 --- a/src/ablog/templates/tagcloud.html +++ b/src/ablog/templates/tagcloud.html @@ -1,6 +1,7 @@ {{ warning("tagcloud.html is an old template path, that is no longer used by ablog. Please use ablog/tagcloud.html instead.") }} {% if ablog.tags %}
    +

    {{ gettext('Tags') }}

      {% for coll in ablog.tags %} diff --git a/src/ablog/tests/test_canonical.py b/src/ablog/tests/test_canonical.py new file mode 100644 index 00000000..12346452 --- /dev/null +++ b/src/ablog/tests/test_canonical.py @@ -0,0 +1,25 @@ +import pytest + + +def read_text(path): + """ + Support function to give backward compatibility with older sphinx (v2). + """ + if hasattr(path, "read_text"): + return path.read_text() + return path.text() + + +@pytest.mark.sphinx("html", testroot="canonical") # using roots/test-canonical +def test_canonical(app, status, warning): + app.build() + + assert app.statuscode == 0 + assert (app.outdir / "post.html").exists() + assert (app.outdir / "canonical.html").exists() + + html = read_text(app.outdir / "post.html") + assert '' in html + + html = read_text(app.outdir / "canonical.html") + assert '' in html diff --git a/tox.ini b/tox.ini index 144f5aa9..8c4c5326 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [tox] envlist = - py{39,310,311}{-sphinx5,-sphinx6,-sphinx7,-sphinxdev,-docs,-linkcheck} -isolated_build = true + py{39,310,311,312}{-sphinx5,-sphinx6,-sphinx7,-sphinx8,-sphinxdev,-docs,-linkcheck} [testenv] allowlist_externals = @@ -17,8 +16,8 @@ commands = sphinx5: pip install -U "sphinx>=5.0,<6.0" sphinx6: pip install -U "sphinx>=6.0,<7.0" sphinx7: pip install -U "sphinx>=7.0,<8.0" - # TODO: Figure this out on azure - # sphinxdev: pip install -U "git+https://repo.or.cz/docutils.git#egg=docutils&subdirectory=docutils" + sphinx8: pip install -U "sphinx>=8.0,<9.0" + sphinxdev: pip install -U "git+https://repo.or.cz/docutils.git#egg=docutils&subdirectory=docutils" sphinxdev: pip install -U "git+https://github.com/sphinx-doc/sphinx" pip freeze --all --no-input pytest -vvv -r a --pyargs ablog