1
0
Fork 0

Adding upstream version 0.11.12.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-10 17:15:56 +01:00
parent f89f073565
commit 2f2c7f3767
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
146 changed files with 9553 additions and 0 deletions

8
.codecov.yaml Normal file
View file

@ -0,0 +1,8 @@
codecov:
token: a0dfd87f-8eb9-4a41-9e4e-a06919f216cd
comment: off
coverage:
status:
project:
default:
threshold: 0.2%

10
.djlintrc Normal file
View file

@ -0,0 +1,10 @@
{
"profile": "jinja",
"extension": "html",
"indent": "2",
"max_line_length": "120",
"use_gitignore": "True",
"format_js": "True",
"format_css": "True",
"ignore": "H006,J018,T003,H025"
}

6
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"

145
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,145 @@
name: CI
on:
push:
branches:
- "main"
- "*.*"
- "!*backport*"
tags:
- "v*"
- "!*dev*"
- "!*pre*"
- "!*post*"
pull_request:
# Allow manual runs through the web UI
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: py313-sphinx8
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
test:
needs: [core]
uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main
with:
submodules: false
libraries: |
brew:
- pandoc
- graphviz
choco:
- pandoc
- graphviz
apt:
- pandoc
- graphviz
envs: |
- macos: py312-sphinx8
- windows: py311-sphinx8
- linux: py310-sphinx8
- linux: py312-pydata-sphinx-theme
- linux: py313-devdeps
extra_tests:
needs: [test]
uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main
with:
submodules: false
libraries: |
apt:
- pandoc
- graphviz
envs: |
- linux: py312-pydata-sphinx-theme
- linux: py313-devdeps
docs:
needs: [core]
uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@main
with:
submodules: false
pytest: false
libraries: |
apt:
- pandoc
- graphviz
envs: |
- linux: py313-docs
- linux: py313-linkcheck
sdist_verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- run: python -m pip install -U --user build
- run: python -m build . --sdist
- run: python -m pip install -U --user twine
- run: python -m twine check dist/*
conda:
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
- uses: conda-incubator/setup-miniconda@v3
with:
activate-environment: ablog-test
environment-file: ablog-conda-test-env.yml
python-version: "3.13"
- name: Install ablog
shell: bash -el {0}
run: |
pip install --no-deps --no-build-isolation .
- name: Run test
shell: bash -el {0}
run: |
conda list
cd /tmp
pytest -vvv -r a --pyargs ablog
make tests
publish:
# Build wheels on PRs only when labelled. Releases will only be published if tagged ^v.*
# see https://github-actions-workflows.openastronomy.org/en/latest/publish.html#upload-to-pypi
if: |
github.event_name != 'pull_request' ||
(
github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'Run publish')
)
needs: [test, docs, sdist_verify]
uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish_pure_python.yml@main
with:
python-version: "3.13"
submodules: false
secrets:
pypi_token: ${{ secrets.PYPI_TOKEN }}

198
.gitignore vendored Normal file
View file

@ -0,0 +1,198 @@
### Python: https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
pip-wheel-metadata/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
junit/
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
### https://raw.github.com/github/gitignore/master/Global/OSX.gitignore
.DS_Store
.AppleDouble
.LSOverride
# Icon must ends with two \r.
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
### Linux: https://raw.githubusercontent.com/github/gitignore/master/Global/Linux.gitignore
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### MacOS: https://raw.githubusercontent.com/github/gitignore/master/Global/macOS.gitignore
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.TemporaryItems
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Windows: https://raw.githubusercontent.com/github/gitignore/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
### VScode: https://raw.githubusercontent.com/github/gitignore/master/Global/VisualStudioCode.gitignore
.vscode/*
### Pycharm(?)
.idea
# Ablog
.github_cache
src/ablog/version.py
docs/_build/
docs/api/
docs/.doctrees/
docs/_website/
docs/_latex/
test/
*.orig
.history/
pydata-sphinx-theme/
_build
demo/
src/ablog/_version.py

54
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,54 @@
ci:
autofix_prs: false
autoupdate_schedule: "quarterly"
repos:
- repo: https://github.com/PyCQA/docformatter
rev: eb1df347edd128b30cd3368dddc3aa65edcfac38
hooks:
- id: docformatter
args: ["--in-place", "--pre-summary-newline", "--make-summary-multi"]
- repo: https://github.com/PyCQA/autoflake
rev: v2.3.1
hooks:
- id: autoflake
args:
[
"--in-place",
"--remove-all-unused-imports",
"--remove-unused-variable",
]
exclude: ".*(.fits|.fts|.fit|.txt|tca.*|extern.*|.rst|.md|docs/conf.py)$"
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.7.2"
hooks:
- id: ruff
args: ["--fix", "--unsafe-fixes"]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-ast
- id: check-case-conflict
- id: trailing-whitespace
exclude: ".*(.fits|.fts|.fit|.txt|.csv)$"
- id: mixed-line-ending
exclude: ".*(.fits|.fts|.fit|.txt|.csv)$"
- id: end-of-file-fixer
exclude: ".*(.fits|.fts|.fit|.txt|.csv)$"
- id: check-yaml
- id: debug-statements
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
additional_dependencies:
- tomli
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.3.3
hooks:
- id: prettier
- repo: https://github.com/Riverside-Healthcare/djLint
rev: v1.35.4
hooks:
- id: djlint-jinja
types_or: ["html"]

21
.readthedocs.yml Normal file
View file

@ -0,0 +1,21 @@
version: 2
build:
os: ubuntu-lts-latest
tools:
python: "mambaforge-latest"
jobs:
pre_install:
- git update-index --assume-unchanged .rtd-environment.yml docs/conf.py
conda:
environment: .rtd-environment.yml
sphinx:
builder: html
configuration: docs/conf.py
fail_on_warning: false
python:
install:
- method: pip
extra_requirements:
- all
- docs
path: .

10
.rtd-environment.yml Normal file
View file

@ -0,0 +1,10 @@
name: rtd_ablog
channels:
- conda-forge
dependencies:
- python=3.12
- pip
- graphviz
- make
- nbsphinx
- pandoc

3
.stylelintrc.json Normal file
View file

@ -0,0 +1,3 @@
{
"extends": "stylelint-config-standard"
}

21
LICENSE.rst Normal file
View file

@ -0,0 +1,21 @@
ABlog for blogging with Sphinx
Copyright (C) 2014-2022 Ahmet Bakan and The SunPy Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

28
Makefile Normal file
View file

@ -0,0 +1,28 @@
.PHONY: demo rebuild tests
demo:
rm -rf demo && mkdir demo
printf "demo\nABlog\nABlog Team\nhttps://ablog.readthedocs.io/" | ablog start
rebuild:
cd docs; watchmedo shell-command --patterns='*.rst' --command='ablog build' --recursive
test:
set -e; cd docs; git clean -xfd; ablog build -T -W; git clean -xfd; cd ..
test1:
set -e; cd docs; git clean -xfd; ablog build -T -W -b json; git clean -xfd; cd ..
test2:
set -e; cd docs; git clean -xfd; ablog build -T -W -b pickle; git clean -xfd; cd ..
test3:
set -e; mkdir -p test; cd test; git clean -xfd; printf "\nABlog\nABlog Team\nhttps://ablog.readthedocs.io/" | ablog start; ablog build -W; cd ..; rm -rf test
test4:
set -e; mkdir -p testablog; cd testablog; git clean -xfd; printf "\nABlog\nABlog Team\nhttps://ablog.readthedocs.io/" | ablog start; ablog build -W; cd ..; rm -rf testablog
test5:
set -e; cd docs; git clean -xfd; ablog build -W -b latex -T -d .doctrees -w _latex; git clean -xfd; cd ..
tests: test test1 test2 test3 test4 test5

26
README.rst Normal file
View file

@ -0,0 +1,26 @@
ABlog for Sphinx
================
|CI| |Upload Python Package|
.. |CI| image:: https://github.com/sunpy/ablog/actions/workflows/ci.yml/badge.svg
:target: https://github.com/sunpy/ablog/actions/workflows/ci.yml
.. |Upload Python Package| image:: https://github.com/sunpy/ablog/actions/workflows/pythonpublish.yml/badge.svg
:target: https://github.com/sunpy/ablog/actions/workflows/pythonpublish.yml
ABlog is a Sphinx extension that converts any documentation or personal website project into a full-fledged blog.
`Please check our documentation for information on how to get started. <https://ablog.readthedocs.io/>`__
Note
----
This is the new home of `Ahmet Bakan's Ablog Sphinx extension <https://github.com/abakan-zz/ablog/>`__.
The original project is no longer maintained and the `SunPy Project <https://www.sunpy.org>`__ has taken over maintenance.
Warning
-------
**This version is maintained with the aim to keep it working for SunPy Project website and thus new features or bugfixes are highly unlikely unless they directly impact the SunPy Project**
**We strongly encourage users and interested in parties in submitting patches to ablog**

23
ablog-conda-test-env.yml Normal file
View file

@ -0,0 +1,23 @@
name: ablog-conda-test-env
channels:
- conda-forge
dependencies:
- docutils
- feedgen
- graphviz
- invoke
- make
- myst-parser
- nbsphinx
- packaging
- pandoc
- pip
- pytest
- python-dateutil
- setuptools
- setuptools-scm
- sphinx
- sphinx-automodapi
- watchdog
- pip:
- sunpy-sphinx-theme

30
conftest.py Normal file
View file

@ -0,0 +1,30 @@
from pathlib import Path
import docutils
import pytest
import sphinx
# Load app, status and warning fixtures.
pytest_plugins = ["sphinx.testing.fixtures"]
# inspired from sphinx's conftest.py
def pytest_report_header(config):
header = f"libraries: Sphinx-{sphinx.__display_version__}, docutils-{docutils.__version__}"
if hasattr(config, "_tmp_path_factory"):
header += f"\nbase tempdir: {config._tmp_path_factory.getbasetemp()}"
return header
@pytest.fixture(scope="session")
def rootdir():
return Path(__file__).parent.absolute() / "roots"
@pytest.fixture(scope="function", autouse=True)
def reset_blog_config():
# Reset cached configurations to enable confoverrides
from ablog.blog import Blog
Blog._dict = {}

19
docs/Makefile Normal file
View file

@ -0,0 +1,19 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS = -v
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

BIN
docs/_static/ablog.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/_static/ablog.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

949
docs/_static/ablog.svg vendored Normal file
View file

@ -0,0 +1,949 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="https://purl.org/dc/elements/1.1/"
xmlns:cc="https://creativecommons.org/ns#"
xmlns:rdf="https://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="https://www.w3.org/2000/svg"
xmlns="https://www.w3.org/2000/svg"
xmlns:sodipodi="https://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="https://www.inkscape.org/namespaces/inkscape"
height="1200"
width="3600"
id="svg4026"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="ablog.svg"
inkscape:export-filename="/Users/abakan/Documents/GitHub/ablog/docs/_static/ablog.png"
inkscape:export-xdpi="5.0250001"
inkscape:export-ydpi="5.0250001">
<metadata
id="metadata4034">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="https://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs4032">
<filter
id="filter3248"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3250"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3252"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3254"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3256"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3258"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3260"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3262"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3264"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3266"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3268"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3270"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3272"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3274"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3385"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3387"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3389"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3391"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3393"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3395"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3397"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3399"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3401"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3403"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3405"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3407"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3409"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3411"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3413"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3415"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3417"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3419"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3421"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3423"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3425"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3427"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3429"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3431"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3433"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3435"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3437"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3439"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3441"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3443"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3445"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3447"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3449"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3451"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3453"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3455"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3457"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3459"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3461"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3463"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3465"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3467"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3950"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3952"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3954"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3956"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3958"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3960"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3962"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3964"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3966"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3968"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3970"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3972"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3974"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3976"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3978"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3980"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3982"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3984"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap3986"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology3988"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend3990"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter3992"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur3994"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend3996"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence3998"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap4000"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology4002"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend4004"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter4006"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4008"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend4010"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence4012"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap4014"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology4016"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend4018"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter4020"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4022"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend4024"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence4026"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap4028"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology4030"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend4032"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter4397"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4399"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend4401"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence4403"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap4405"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology4407"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend4409"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter4411"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4413"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend4415"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence4417"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap4419"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology4421"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend4423"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter4425"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4427"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend4429"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence4431"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap4433"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology4435"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend4437"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter4439"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4441"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend4443"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence4445"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap4447"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology4449"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend4451"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter4453"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4455"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend4457"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence4459"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap4461"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology4463"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend4465"
in2="result2"
mode="screen" />
</filter>
<filter
id="filter4467"
inkscape:label="Air spray"
inkscape:menu="Scatter"
inkscape:menu-tooltip="Convert to small scattered particles with some thickness"
width="2"
height="2"
y="-0.5"
x="-0.5"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4469"
stdDeviation="0.01"
result="result1" />
<feBlend
id="feBlend4471"
in2="result1"
result="fbSourceGraphic"
mode="multiply" />
<feTurbulence
id="feTurbulence4473"
baseFrequency="0.8"
type="fractalNoise"
seed="0"
numOctaves="3"
result="result3" />
<feDisplacementMap
id="feDisplacementMap4475"
in2="result3"
in="fbSourceGraphic"
xChannelSelector="R"
yChannelSelector="G"
scale="50"
result="result2" />
<feMorphology
id="feMorphology4477"
radius="1"
operator="dilate"
result="result4" />
<feBlend
id="feBlend4479"
in2="result2"
mode="screen" />
</filter>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1286"
inkscape:window-height="1009"
id="namedview4030"
showgrid="false"
inkscape:zoom="0.236"
inkscape:cx="2155.7813"
inkscape:cy="500"
inkscape:window-x="401"
inkscape:window-y="175"
inkscape:window-maximized="0"
inkscape:current-layer="svg4026" />
<text
xml:space="preserve"
style="font-size:1160px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial Rounded MT Bold;-inkscape-font-specification:'Arial Rounded MT Bold,'"
x="944.91528"
y="907.62714"
id="text3784"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3786"
x="944.91528"
y="907.62714"
style="font-size:1160px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;font-family:Arial Rounded MT Bold;-inkscape-font-specification:'Arial Rounded MT Bold,'">Blog</tspan></text>
<path
d="M 833.65625 83.84375 C 701.98958 83.84375 580.11458 116.55208 468.03125 181.96875 C 355.94792 247.38542 267.40625 336.13542 202.40625 448.21875 C 137.40625 560.30208 104.90625 682.17708 104.90625 813.84375 C 104.90625 843.01042 115.11458 867.59375 135.53125 887.59375 C 155.94792 907.59375 180.32292 917.59375 208.65625 917.59375 C 236.98958 917.59375 261.36458 907.59375 281.78125 887.59375 C 302.19792 867.59375 312.40625 843.01042 312.40625 813.84375 C 312.40625 669.67708 363.44792 546.76042 465.53125 445.09375 C 567.61458 343.42708 690.32292 292.59375 833.65625 292.59375 C 861.98958 292.59375 886.36458 282.59375 906.78125 262.59375 C 927.19792 242.59375 937.40625 218.01042 937.40625 188.84375 C 937.40625 159.67708 927.19792 134.88542 906.78125 114.46875 C 886.36458 94.052083 861.98958 83.84375 833.65625 83.84375 z M 833.65625 396.375 C 753.11275 396.375 699.43885 415.49755 674.84375 425.96875 C 652.66285 435.41205 632.55455 445.84635 604.28125 465.34375 C 577.68735 483.68305 558.5651 513.6848 559.625 547.0625 C 560.5463 576.0772 562.6609 593.9731 583.6875 617.625 C 611.3285 648.7173 626.15755 648.26095 641.28125 652.46875 C 667.09175 659.64985 699.51095 650.3316 716.21875 640.5625 C 727.78705 633.7985 737.68565 628.10535 746.46875 623.84375 L 746.46875 808.4375 C 746.39575 810.2263 746.375 812.0521 746.375 813.875 C 746.375 843.0417 755.7673 867.625 774.5625 887.625 C 793.3577 907.625 815.7919 917.625 841.875 917.625 C 867.9582 917.625 890.3923 907.625 909.1875 887.625 C 927.9827 867.625 937.40625 843.0417 937.40625 813.875 C 937.40625 813.3721 937.38 812.8754 937.375 812.375 L 937.46875 812.375 L 937.46875 509.75 L 937.09375 509.75 C 937.28755 506.9953 937.40625 504.2168 937.40625 501.375 C 937.40625 472.2084 927.19785 447.4167 906.78125 427 C 886.36455 406.5834 861.98955 396.375 833.65625 396.375 z M 521 709.09375 C 492.66667 709.09375 468.29167 719.30208 447.875 739.71875 C 427.45833 760.13542 417.25 784.92708 417.25 814.09375 C 417.25 843.26042 427.45833 867.84375 447.875 887.84375 C 468.29167 907.84375 492.66667 917.84375 521 917.84375 C 549.33333 917.84375 573.70833 907.84375 594.125 887.84375 C 614.54167 867.84375 624.75 843.26042 624.75 814.09375 C 624.75 784.92708 614.54167 760.13542 594.125 739.71875 C 573.70833 719.30208 549.33333 709.09375 521 709.09375 z "
id="path3094-3" />
</svg>

After

Width:  |  Height:  |  Size: 28 KiB

151
docs/conf.py Normal file
View file

@ -0,0 +1,151 @@
import re
from pathlib import Path
from packaging.version import parse as _parse
from sphinx import addnodes
import ablog
ablog_builder = "dirhtml"
ablog_website = "_website"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.todo",
"sphinx.ext.ifconfig",
"sphinx.ext.extlinks",
"sphinx_automodapi.automodapi",
"ablog",
"alabaster",
"nbsphinx",
"myst_parser",
]
version = str(_parse(ablog.__version__))
project = "ABlog"
copyright = "2014-2022, ABlog Team"
master_doc = "index"
source_suffix = {
".rst": "restructuredtext",
".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 = {
"Pittsburgh": ("Pittsburgh, PA", "https://en.wikipedia.org/wiki/Pittsburgh"),
"San Fran": ("San Francisco, CA", "https://en.wikipedia.org/wiki/San_Francisco"),
"Denizli": ("Denizli, Turkey", "https://en.wikipedia.org/wiki/Denizli"),
}
blog_languages = {
"en": ("English", None),
"nl": ("Nederlands", None),
"zh_CN": ("Chinese", None),
}
blog_default_language = "en"
language = "en"
blog_authors = {
"Ahmet": ("Ahmet Bakan", "https://ahmetbakan.com"),
"Luc": ("Luc Saffre", "https://saffre-rumma.net/luc/"),
"Mehmet": ("Mehmet Gerçeker", "https://github.com/mehmetg"),
"Libor": ("Libor Jelínek", "https://liborjelinek.github.io/"),
}
blog_feed_archives = True
blog_feed_fulltext = True
blog_feed_templates = {
"atom": {
"content": "{{ title }}{% for tag in post.tags %}" " #{{ tag.name|trim()|replace(' ', '') }}" "{% endfor %}",
},
"social": {
"content": "{{ title }}{% for tag in post.tags %}" " #{{ tag.name|trim()|replace(' ', '') }}" "{% endfor %}",
},
}
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_theme = "alabaster"
html_sidebars = {
"**": [
"about.html", # Comes from alabaster
"searchfield.html", # Comes from alabaster
"ablog/postcard.html",
"ablog/recentposts.html",
"ablog/tagcloud.html",
"ablog/categories.html",
"ablog/archives.html",
"ablog/authors.html",
"ablog/languages.html",
"ablog/locations.html",
]
}
html_theme_options = {
"travis_button": False,
"github_user": "sunpy",
"github_repo": "ablog",
"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),
}
extlinks = {
"wiki": ("https://en.wikipedia.org/wiki/%s", "%s"),
"issue": ("https://github.com/sunpy/ablog/issues/%s", "issue %s"),
"pull": ("https://github.com/sunpy/ablog/pull/%s", "pull request %s"),
}
rst_epilog = """
.. _Sphinx: http://sphinx-doc.org/
.. _Python: https://python.org
.. _Disqus: https://disqus.com/
.. _GitHub: https://github.com/sunpy/ablog
.. _PyPI: https://pypi.python.org/pypi/ablog
.. _Read The Docs: https://readthedocs.org/
.. _Alabaster: https://github.com/bitprophet/alabaster
"""
locale_dirs = [str(Path(ablog.__file__).parent / Path("locales"))]
nitpicky = True
nitpick_ignore = []
for line in open("nitpick-exceptions"):
if line.strip() == "" or line.startswith("#"):
continue
dtype, target = line.split(None, 1)
target = target.strip()
nitpick_ignore.append((dtype, target))
def parse_event(env, sig, signode):
event_sig_re = re.compile(r"([a-zA-Z-]+)\s*\((.*)\)")
m = event_sig_re.match(sig)
if not m:
signode += addnodes.desc_name(sig, sig)
return sig
name, args = m.groups()
signode += addnodes.desc_name(name, name)
plist = addnodes.desc_parameterlist()
for arg in args.split(","):
arg = arg.strip()
plist += addnodes.desc_parameter(arg, arg)
signode += plist
return name
def setup(app):
from sphinx.ext.autodoc import cut_lines
from sphinx.util.docfields import GroupedField
app.connect("autodoc-process-docstring", cut_lines(4, what=["module"]))
app.add_object_type(
"confval",
"confval",
objname="configuration value",
indextemplate="pair: %s; configuration value",
)
fdesc = GroupedField("parameter", label="Parameters", names=["param"], can_collapse=True)
app.add_object_type("event", "event", "pair: %s; event", parse_event, doc_field_types=[fdesc])

104
docs/index.rst Normal file
View file

@ -0,0 +1,104 @@
ABlog for Sphinx
================
ABlog is a Sphinx extension that converts any documentation or personal website project into a full-fledged blog with:
* :ref:`Atom feeds <blog-feed>`
* :ref:`Archive pages <blog-archives>`
* :ref:`sidebars`
* :ref:`disqus-integration`
* :ref:`Font-Awesome integration <font-awesome>`
* :doc:`manual/markdown`
Ablog is part of the `SunPy Project <https://www.sunpy.org>`__.
.. _installation:
Installation
------------
You can install ABlog using `pip <https://pip.pypa.io/en/stable/>`__::
pip install -U ablog
or `miniforge <https://github.com/conda-forge/miniforge>`__::
conda install ablog
This will also install `Sphinx <http://sphinx-doc.org/>`__, `feedgen <https://github.com/lkiesow/python-feedgen>`__, and `Invoke <https://www.pyinvoke.org/>`__ respectively required for building your website, making it look good, generating feeds, and running deploy commands.
Getting Started
---------------
If you are starting a new project, see the :ref:`quick-start` guide.
If you already have a project, enable blogging by making following changes in ``conf.py``:
.. code-block:: python
# 1. Add 'ablog' and 'sphinx.ext.intersphinx' to the list of extensions
extensions = [
'...',
'ablog',
'sphinx.ext.intersphinx',
]
How it works
------------
If you are new to Sphinx_ and reStructuredText markup language, you might find `reStructuredText Primer`_ useful.
Once you have content (in ``.rst`` files), you can post *any page* using the :rst:dir:`post` directive as follows:
.. _reStructuredText Primer: https://www.sphinx-doc.org/en/master/
.. code-block:: rst
.. post:: Apr 15, 2014
:tags: earth, love, peace
:category: python
:author: me
:location: SF
:language: en
An alternative method is:
.. code-block:: rst
:blogpost: true
:date: Oct 10, 2020
:author: Nabil Freij
:location: World
:category: Manual
:language: English
at the top of the file.
ABlog will index all files posted as above and list them in archives and feeds specified in ``:tag:``, ``:category:``, etc. options.
You can also include a list of posts using :rst:dir:`postlist` directive:
.. code-block:: rst
.. postlist::
:list-style: circle
:category: Manual
:format: {title}
:sort:
For ABlog documentation, this converts to the following where you can find more about configuring and using ABlog:
.. postlist::
:category: Manual
:list-style: circle
:format: {title}
:sort:
.. only:: html
.. image:: https://readthedocs.org/projects/ablog/badge/?version=latest
:target: https://ablog.readthedocs.io
.. toctree::
:hidden:
:glob:
*/*

35
docs/make.bat Normal file
View file

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd

View file

@ -0,0 +1,178 @@
.. _commands:
ABlog Commands
==============
.. post:: Mar 1, 2015
:tags: config, commands
:author: Ahmet, Mehmet
:category: Manual
:location: SF
``ablog`` commands are for streamlining blog operations, i.e. building, serving, and viewing blog pages, as well as starting a new blog::
$ ablog
usage: ablog [-h] [-v] {start,build,clean,serve,post,deploy} ...
ABlog for blogging with Sphinx
optional arguments:
-h, --help show this help message and exit
-v, --version print ABlog version and exit
subcommands:
{start,build,clean,serve,post,deploy}
start start a new blog project
build build your blog project
clean clean your blog build files
serve serve and view your project
post create a blank post
deploy deploy your website build files
See 'ablog <command> -h' for more information on a specific command.
.. contents:: Here are all the things you can do:
:local:
:backlinks: top
.. _start:
Start a New Project
-------------------
``ablog start`` command is for quickly setting up a blog project.
See :ref:`quick-start` for how it works and what it prepares for you::
$ ablog start -h
usage: ablog start [-h]
Start a new blog project by answering a few questions. You will end up with a
configuration file and sample pages.
optional arguments:
-h, --help show this help message and exit
.. _build:
Build your Website
------------------
Running ``ablog build`` in your project folder builds your website HTML pages::
$ ablog build -h
usage: ablog build [-h] [-a] [-b BUILDER] [-s SOURCEDIR] [-w WEBSITE]
[-d DOCTREES] [-T] [-P]
Path options can be set in conf.py. Default values of paths are relative to
conf.py.
optional arguments:
-h, --help show this help message and exit
-a write all files; default is to only write new and changed
files
-b BUILDER builder to use, default `ablog_builder` or dirhtml
-s SOURCEDIR root path for source files, default is path to the folder that
contains conf.py
-w WEBSITE path for website, default is _website when `ablog_website` is
not set in conf.py
-d DOCTREES path for the cached environment and doctree files, default
.doctrees when `ablog_doctrees` is not set in conf.py
-T show full traceback on exception
-P run pdb on exception
Serve and View Locally
----------------------
Running ``ablog serve``, after building your website, will start a Python server and open up browser tab to view your website::
$ ablog serve -h
usage: ablog serve [-h] [-w WEBSITE] [-p PORT] [-n] [-r] [--patterns]
Serve options can be set in conf.py. Default values of paths are relative to
conf.py.
optional arguments:
-h, --help show this help message and exit
-w WEBSITE path for website, default is _website when `ablog_website` is
not set in conf.py
-p PORT port number for HTTP server; default is 8000
-n do not open website in a new browser tab
-r rebuild when a file matching patterns change or get added
--patterns patterns for triggering rebuilds
.. _deploy:
Deploy to GitHub Pages
----------------------
Running ``ablog deploy`` will push your website to GitHub::
$ ablog deploy -h
usage: ablog deploy [-h] [-w WEBSITE] [-p REPODIR] [-g GITHUB_PAGES]
[-m MESSAGE] [-f] [--push-quietly]
[--github-branch GITHUB_BRANCH] [--github-ssh]
[--github-token GITHUB_TOKEN] [--github-url GITHUB_URL]
Path options can be set in conf.py. Default values of paths are relative to
conf.py.
options:
-h, --help show this help message and exit
-w WEBSITE path for website, default is _website when
`ablog_website` is not set in conf.py
-p REPODIR path to the location of repository to be deployed, e.g.
`../username.github.io`, default is folder containing
`conf.py`
-g GITHUB_PAGES GitHub username for deploying to GitHub pages
-m MESSAGE commit message
-f overwrite last commit, i.e. `commit --amend; push -f`
--push-quietly be more quiet when pushing changes
--github-branch GITHUB_BRANCH
Branch to use. Default is 'master'.
--github-ssh use ssh when cloning website
--github-token GITHUB_TOKEN
environment variable name storing GitHub access token
--github-url GITHUB_URL
Custom GitHub URL. Useful when multiple accounts are
configured on the same machine. Default is:
git@github.com
Create a Post
-------------
Finally, ``ablog post`` will make a new post template file::
$ ablog post -h
usage: ablog post [-h] [-t TITLE] filename
positional arguments:
filename filename, e.g. my-nth-post (.rst appended)
optional arguments:
-h, --help show this help message and exit
-t TITLE post title; default is formed from filename
Clean Build Files
-----------------
In case you needed, running ``ablog clean`` will remove build files and do a deep clean with ``-D`` option::
$ ablog clean -h
usage: ablog clean [-h] [-d DOCTREES] [-w WEBSITE] [-D]
Path options can be set in conf.py. Default values of paths are relative to
conf.py.
optional arguments:
-h, --help show this help message and exit
-d DOCTREES path for the cached environment and doctree files, default
.doctrees when `ablog_doctrees` is not set in conf.py
-w WEBSITE path for website, default is _website when `ablog_website` is
not set in conf.py
-D deep clean, remove cached environment and doctree files
.. update:: Apr 7, 2015
Added ``ablog clean`` and ``ablog deploy`` commands.

View file

@ -0,0 +1,291 @@
.. _config:
ABlog Configuration Options
===========================
.. post:: May 10, 2014
:tags: config
:author: Ahmet
:category: Manual
:location: Pittsburgh
This post describes ABlog configuration options that go in :ref:`Sphinx build configuration file <sphinx:build-config>`.
General options
---------------
.. confval:: blog_path
A path relative to the configuration directory for blog archive pages.
Default is ``'blog'``.
Authors, languages, & locations
-------------------------------
.. confval:: blog_authors
A dictionary of author names mapping to author full display names and links.
Dictionary keys are what should be used in ``post`` directive to refer to the author.
Default is ``{}``.
Example::
blog_authors = {
'Ahmet': ('Ahmet Bakan', 'http://ahmetbakan.com'),
'Durden': ('Tyler Durden',
'https://en.wikipedia.org/wiki/Tyler_Durden'),
}
.. confval:: blog_default_author
Name of the default author defined in :confval:`blog_authors`.
Default is ``None``.
.. confval:: blog_languages
A dictionary of language code names mapping to full display names and links of these languages.
Similar to :confval:`blog_authors`, dictionary keys should be used in ``post`` directive to refer to the locations.
Default is ``{}``.
Example::
blog_languages = {
'en': ('English', None),
}
.. confval:: blog_default_language
Code name of the default language defined in :confval:`blog_languages`.
Default is ``None``.
.. confval:: blog_locations
A dictionary of location names mapping to full display names and links of these locations.
Similar to :confval:`blog_authors`, dictionary keys should be used in ``post`` directive to refer to the locations.
Default is ``{}``.
.. confval:: blog_default_location
Name of the default location defined in :confval:`blog_locations`.
Default is ``None``.
.. update:: Sep 15, 2014
Added :confval:`blog_languages` and :confval:`blog_default_language` configuration variables.
Post related
------------
.. confval:: post_date_format
Date display format (default is ``'%b %d, %Y'``, e.g., ``12 August 2024``) for published posts that goes as input to :meth:`datetime.date.strftime`.
.. confval:: post_date_format_short
Date display format in recent posts sidebar (default is ``'%d %B'``, e.g., ``12 October``) 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.
Setting this ``0`` will result in displaying no post excerpt in archive pages.
This option can be set on a per post basis using :rst:dir:`post` directive option ``excerpt``.
See :ref:`post-excerpts-and-images` for a more detailed discussion.
.. confval:: post_auto_image
Index of the image that will be displayed in the excerpt of the post.
Default is ``0``, meaning no image.
Setting this to ``1`` will include the first image, when available, to the excerpt.
This option can be set on a per post basis using :rst:dir:`post` directive option ``image``.
.. confval:: post_redirect_refresh
Number of seconds (default is ``5``) that a redirect page waits before refreshing the page to redirect to the post.
.. confval:: post_always_section
When ``True``, post title and excerpt is always taken from the section that contains the :rst:dir:`post` directive, instead of the document.
This is the behavior when :rst:dir:`post` is used multiple times in a document.
Default is ``False``.
.. confval:: post_show_prev_next
When ``True``, links to the previous and next posts will be rendered at the bottom of the page.
Default is ``True``
Blog feeds
----------
Turn feeds on by setting :confval:`blog_baseurl` configuration variable.
.. confval:: blog_baseurl
Base URL for the website, turns on generating feeds. E.g., ``https://ablog.readthedocs.io``.
Then optionally set the following:
.. confval:: blog_title
The “title” for the blog, used in feeds title (not archive web pages title). Default is ``'Blog'``.
.. confval:: blog_archive_titles
Choose to archive only post titles in collection pages, default is ``False``.
.. confval:: blog_feed_archives
Choose to create feeds per author, location, tag, category, and year, default is ``False``.
.. confval:: blog_feed_fulltext
Choose to display full text in blog feeds, default is ``False``.
.. confval:: blog_feed_subtitle
Blog feed subtitle, default is ``None``.
.. confval:: blog_feed_titles
Choose to feed only post titles, default is ``False``.
.. confval:: blog_feed_templates
A dictionary of feed filename roots mapping to nested dictionaries of feed entry
elements, ``title``, ``summary``, and/or ``content``, and a `Jinja2`_ template which will be
used to render the value used for that element in that feed. Templates are rendered
with the the following context:
- ``feed_length``
- ``feed_fulltext``
- ``feed_posts``
- ``pagename``
- ``feed_title``
- ``feed_url``
- ``feed``
- ``post``
- ``post_url``
- ``content``
- ``feed_entry``
- ``title``
- ``summary``
- ``blog``
- ``url``
- ``app``
Default is: ``{"atom": {}}``
Example to add an additional feed for posting to social media::
blog_feed_templates = {
# Use defaults, no templates
"atom": {},
# Create content text suitable posting to social media
"social": {
# Format tags as hashtags and append to the content
"content": "{{ title }}{% for tag in post.tags %}"
" #{{ tag.name|trim()|replace(' ', '') }}"
"{% endfor %}",
},
}
.. confval:: blog_feed_length
Specify number of recent posts to include in feeds, default is ``None`` for all posts.
.. update:: Aug 24, 2014
Added :confval:`blog_feed_archives`, :confval:`blog_feed_fulltext`, :confval:`blog_feed_subtitle`, and :confval:`post_always_section` options.
.. update:: Nov 27, 2014
Added :confval:`blog_feed_titles`, :confval:`blog_feed_length`, and :confval:`blog_archive_titles` options.
.. update:: Mar 20, 2021
Added :confval:`blog_feed_templates` option.
.. _fa:
.. _Jinja2: https://jinja.palletsprojects.com/
.. _font-awesome:
Font awesome
------------
ABlog templates will use of `Font Awesome`_ icons if one of the following is set:
.. _Font Awesome: https://fontawesome.com/
.. confval:: fontawesome_link_cdn
URL to `Font Awesome`_ :file:`.css` hosted at `Bootstrap CDN`_ or anywhere else.
Default: ``None``
.. _Bootstrap CDN: https://www.bootstrapcdn.com/fontawesome/
.. update:: Jul 29, 2015
:confval:`fontawesome_link_cdn` was a *boolean* option, and now became a *string* to enable using desired version of `Font Awesome`_.
To get the old behavior, use ``https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css'``.
.. confval:: fontawesome_included
Sphinx_ theme already links to `Font Awesome`_.
Default: ``False``
Alternatively, you can provide the path to `Font Awesome`_ :file:`.css` with the following configuration option:
.. confval:: fontawesome_css_file
Path to `Font Awesome`_ :file:`.css` (default is ``None``) that will be linked to in HTML output by ABlog.
.. _disqus-integration:
Disqus integration
------------------
Of course one cannot think of a blog that doesn't allow for visitors to comment.
You can enable Disqus_ by setting :confval:`disqus_shortname` and :confval:`blog_baseurl` variables.
The reason for requiring :confval:`blog_baseurl` to be specified as of v0.7.2 is to ensure that Disqus associates correct URLs with threads when you serve new posts locally for the first time.
.. confval:: disqus_shortname
Disqus_ short name for the website.
.. confval:: disqus_pages
Choose to disqus pages that are not posts, default is ``False``.
.. confval:: disqus_drafts
Choose to disqus posts that are drafts (without a published date), default is ``False``.
Isso integration
----------------
An alternative to Disqus, is `Isso <https://isso-comments.de/>`__.
Integration is provided by `sphinxnotes-isso`_ and the instructions there.
.. _sphinxnotes-isso: https://sphinx-notes.github.io/isso/
Command Options
---------------
.. update:: Apr 7, 2015
Added :ref:`commands` options.
.. confval:: ablog_website
Directory name for build output files. Default is ``_website``.
.. confval:: ablog_doctrees
Directory name for build cache files. Default is ``.doctrees``.
.. confval:: ablog_builder
HTML builder, default is ``dirhtml``. Build HTML pages, but with a single directory per document.
Makes for prettier URLs (no .html) if served from a webserver. Alternative is ``html`` to build one HTML file per document.
.. confval:: github_pages
GitHub user name used by ``ablog deploy`` command.
See :ref:`deploy` and :ref:`deploy-to-github-pages` for more information.

View file

@ -0,0 +1,56 @@
ABlog Internationalization
==========================
.. post:: Aug 30, 2014
:tags: i18n
:category: Manual
:author: Luc, Ahmet
:language: Chinese
ABlog automatically generates certain labels like :ref:`blog-posts` and :ref:`blog-categories`.
If these labels appear in English on your blog although you specified another language, then this page is for you.
ABlog needs your help for translation of these labels.
Translation process involves the following steps:
* Update translatable messages:
Execute extract_messages_ each time a translatable message text is changed or added::
$ python setup.py extract_messages -o ablog/locales/sphinx.pot
...
This will create or update :file:`ablog/locales/sphinx.pot` file, the central messages catalog used by the different translations.
Either:
* Create new translation catalog:
Execute init_catalog_ once for each *new* language, e.g.::
$ python setup.py init_catalog -l de -i ablog/locales/sphinx.pot -o ablog/locales/de/LC_MESSAGES/sphinx.po
This will create a file :file:`ablog/locales/de/LC_MESSAGES/sphinx.po` in which translations needs to be placed.
* Update translation catalog:
Execute update_catalog_ for each *existing* language, e.g.::
$ python setup.py update_catalog -l de -i ablog/locales/sphinx.pot -o ablog/locales/de/LC_MESSAGES/sphinx.po
This will update file :file:`ablog/locales/de/LC_MESSAGES/sphinx.po` where translations of new text needs to be placed.
Finally:
* Compile catalogs:
Execute compile_catalog_ for each existing language, e.g::
$ python setup.py compile_catalog --directory ablog/locales/ --domain sphinx --locale de
If you remove ``--locale de`` then all catalogs will be compiled.
.. _extract_messages: https://babel.pocoo.org/en/latest/setup.html#extract-messages
.. _init_catalog: https://babel.pocoo.org/en/latest/setup.html#init-catalog
.. _update_catalog: https://babel.pocoo.org/en/latest/setup.html#update-catalog
.. _compile_catalog: https://babel.pocoo.org/en/latest/setup.html#compile-catalog

View file

@ -0,0 +1,126 @@
.. _quick-start:
ABlog Quick Start
=================
.. post:: Mar 1, 2015
:tags: config, tips
:author: Mehmet, Ahmet
:category: Manual
:location: SF
This short walk through of blogging work flow assumes that you have already installed ABlog. If not, see :ref:`installation` guide.
*Note that this post is a working draft. Feel free to revise it on GitHub.*
Start a Project
---------------
To start a new project, run ``ablog start`` command in a directory where you want to keep your project source files.
This command will ask you a few questions and create the following files:
* :file:`conf.py` that contains project configuration for building HTML pages.
* :file:`first-post.rst`, a blog post example.
* :file:`index.rst` that contains content for the *landing* page of your website.
* :file:`about.rst`, another non-post page example.
Build and View
--------------
With no further delay, let's see what your project will look like.
First run ``ablog build``, in your project folder, to have HTML pages built in :file:`_website` folder.
Then, call ``ablog serve`` to view them in your default web browser.
See :ref:`commands` for more information about these commands.
Your landing page is built from :file:`index.rst` and contains links to your first post and about page.
Take a look at :file:`index.rst` for some tips on navigation links within the project.
Write Content
-------------
If you are new to Sphinx_ and reStructuredText markup language, you might find `reStructuredText Primer <https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`__ useful.
Pages
^^^^^
Pages in your project are :file:`.rst` files that are only a :rst:dir:`post` directive short of becoming blog posts.
To make regular pages accessible from the navigation bar, you need to list them in a :rst:dir:`toctree`.
This is shown for *about* page into :file:`index.rst`.
Posts
^^^^^
You can convert any page to a post with a :rst:dir:`post` directive.
ABlog will take care of listing posts in specified archives and sidebars.
Blog posts
^^^^^^^^^^
You can start new blog posts with either a front-matter or a directive using ABlog.
Simply use something based on the following template as the front-matter::
:blogpost: true
:date: January 1, 2020
:author: A. Author
:location: World
:category: Blog
:language: English
:tags: blog
Simply use something based on the following template as the directive for ABlog::
.. post:: January 1, 2020
:author: A. Author
:location: World
:category: Blog
:language: English
:tags: blog
For more information, see :ref:`posting-directive` and :ref:`posting-front-matter`.
Comments
--------
You can enable comments in your website by creating a Disqus_ account and obtaining a unique identifier, i.e. :confval:`disqus_shortname`.
See :ref:`disqus-integration` for configuration options.
Analytics
---------
ABlog uses Alabaster_ theme by default. You can use theme options to set your `Google Analytics`__ identifier to enable tracking.
__ https://www.google.com/analytics/
Configuration
-------------
There are four major groups of configuration options that can help you customize how your website looks:
* :ref:`config` - add blog authors, post locations and languages to your blog, adjust archive and feed content, etc.
* `General configuration <https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration>`__ and `project information <https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information>`__
* :ref:`html-options` - configure appearance of your website.
* Alabaster_ theme options - link to your GitHub account and project, set up tracking, etc.
Other Folders
-------------
You might have noticed that your project contains three folders that we have not mention yet.
Here they are:
* :file:`_static` is for keeping image, :file:`.js`, and :file:`.css` files.
:confval:`html_static_path` Sphinx option for more information.
* :file:`_templates` is for custom HTML templates.
See :confval:`templates_path` for more information.
* :file:`.doctree` folder, created after build command is called, is where Sphinx_ stores the state of your project.
Files in this folder saves time when you rebuild your project.

20
docs/manual/api.rst Normal file
View file

@ -0,0 +1,20 @@
.. api:
ABlog API
=========
.. post:: Feb 17, 2018
:tags: api
:author: Nabil Freij
:category: Manual
:location: World
.. automodapi:: ablog
.. automodapi:: ablog.blog
.. automodapi:: ablog.commands
.. automodapi:: ablog.post
.. automodapi:: ablog.start

View file

@ -0,0 +1,47 @@
Cross-referencing Blog Pages
============================
.. post:: May 11, 2014
:tags: tips, Sphinx
:category: Manual
:location: Pittsburgh
:author: Ahmet
ABlog creates references to all post and archive pages.
Posts can be cross-referenced using the name of the file, or when the file is named :file:`index`, the name of the folder that contains the file.
This page, :ref:`cross-referencing-blog-pages`, for example is referenced as ``:ref:`cross-referencing-blog-pages``` using :rst:role:`ref` role.
When posts have long file names, it may be inconvenient to use them repeatedly for cross-referencing.
An alternative that Sphinx_ offers is creating your own short and unique labels for cross-referencing to posts. See :ref:`xref-syntax` for details.
.. _archives:
Archive pages
-------------
Archive pages, on the other hand, can be cross-referenced by combining archive type and archive name as follows:
============== ========================== ===============================
Archive Example reStructured Text
============== ========================== ===============================
Posts :ref:`blog-posts` ``:ref:`blog-posts```
Drafts :ref:`blog-drafts` ``:ref:`blog-drafts```
Blog Feed :ref:`blog-feed` ``:ref:`blog-feed```
Author :ref:`author-ahmet` ``:ref:`author-ahmet```
Language :ref:`language-en` ``:ref:`language-en```
Location :ref:`location-pittsburgh` ``:ref:`location-pittsburgh```
============== ========================== ===============================
Following archive pages list all posts by grouping them:
============== ========================== ===============================
Archive Example reStructured Text
============== ========================== ===============================
By tag :ref:`blog-tags` ``:ref:`blog-tags```
By author :ref:`blog-authors` ``:ref:`blog-authors```
By language :ref:`blog-languages` ``:ref:`blog-languages```
By location :ref:`blog-locations` ``:ref:`blog-locations```
By category :ref:`blog-categories` ``:ref:`blog-categories```
By archive :ref:`blog-archives` ``:ref:`blog-archives```
============== ========================== ===============================

View file

@ -0,0 +1,41 @@
Deploy to GitHub Pages
======================
.. post:: Apr 07, 2015
:tags: deploy
:author: Ahmet
:category: Manual
:location: SF
If you are looking for a place to publish your blog, `GitHub Pages`__ might be the place for you.
__ https://pages.github.com/
Assuming that you have a GitHub account, here are what you need to do to get published:
1. Head over to GitHub_ and create a new repository named ``username.github.io``, where username is your username (or organization name) on GitHub.
2. (optional) If you followed the link, you might as well give a star to ABlog ;)
3. Set :confval:`github_pages` configuration variable in :file:`conf.py` file.
4. Run ``ablog build`` in your project folder.
5. Run ``ablog deploy``. This command will
i. clone your GitHub pages repository to project folder,
ii. copy all files from build folder (:file:`_website`) to :file:`username.github.io`,
iii. add and commit copied files,
iv. add `.nojekyll <https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#troubleshooting-publishing-from-a-branch>`_
file, since this ain't no Jekyll_
v. and finally push the changes to publish.
Let us know how this works for you!
.. _Jekyll: https://jekyllrb.com/

View file

@ -0,0 +1,10 @@
:blogpost: true
:date: September 01, 2021
:author: Chris
:category: Manual
:external_link: https://www.sphinx-doc.org/en/master/
External post
=============
This text will be in auto-generated post previews, but links to the post will direct to ``external_link``.

View file

@ -0,0 +1,33 @@
Draft Example
=============
.. post::
:tags: draft
:category: Manual
As the title suggests, this is a draft example and shall remain so until the end of time or internet.
How do you draft a post?
------------------------
Just indicate that the page is a post using :rst:dir:`post` directive, but do not provide give a published date:
.. code-block:: rst
.. post::
:tags: draft
:category: Manual
You can still label a post you are drafting with tags and categories, but the post will not be listed in corresponding archive pages until it is published.
How can you see a list of drafts?
---------------------------------
See :ref:`blog-drafts` archive page, which can be referred to as ``:ref:`blog-drafts```.
Why would you make a post draft?
--------------------------------
Let's say you are using Disqus_ on your website, and allowing non-post pages to be discussed as well, but you don't want a draft to be discussed before it is published.
By adding :rst:dir:`post` directive without published date and keeping configuration variable :confval:`disqus_drafts` as ``False``, you can achieve that.

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

56
docs/manual/markdown.md Normal file
View file

@ -0,0 +1,56 @@
---
blogpost: true
date: Oct 10, 2020
author: Nabil Freij
location: World
category: Manual
language: English
---
# Markdown Support
ABlog can support markdown pages using [myst-parser](https://pypi.org/project/myst-parser/).
This page is a markdown file underneath.
You will need to do a few things to get setup.
1. Install [myst-parser](https://pypi.org/project/myst-parser/)
2. Add these options to your config, `conf.py`
```python
extensions = [
...
"myst_parser",
...
]
myst_update_mathjax = False
```
Then use the new blogpost metadata format (with a slight twist):
```
---
blogpost: true
date: Oct 10, 2020
author: Nabil Freij
location: World
category: Manual
language: English
---
```
Notice here we do not have a ":" at the start since the markdown metadata format is different from rst.
Please be aware that adding "myst-parser" will mean it will read all markdown files and try to parse them.
You will need to use the following in your `conf.py` to prevent this:
```python
exclude_patterns = [
"posts/*/.ipynb_checkpoints/*",
".github/*",
".history",
"github_submodule/*",
"LICENSE.md",
"README.md",
]
```

View file

@ -0,0 +1,81 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Jupyter Notebook Posting"
]
},
{
"cell_type": "raw",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
".. post:: 27 Oct 2018\n",
" :author: Nabil Freij, Second Author\n",
" :tags: posting\n",
" :category: Manual"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To add support for Notebooks to your Ablog instance, you need to configure your `docs/conf.py` (or wherever your `conf.py` is located.\n",
"\n",
"You will need to add\n",
"\n",
"```\n",
" extensions = [..., 'nbsphinx', ...]\n",
" exclude_patterns = ['docs/manual/.ipynb_checkpoints/*'] (To exclude the notebook autosaves)\n",
"```\n",
"\n",
"You will need to install [nbsphinx](https://nbsphinx.readthedocs.io/) either from `Anaconda` or `pip`. You might need to install [ipython](https://ipython.org/) to make sure the notebook can be run."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Within the notebook you need to make sure the cells are in this order: Titlte cell, post cell. So for this notebook, it looks like this: ![posting](images/notebook_cells.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So the information is similar to how you create a normal RST post."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Another working example is SunPy's website which runs [Ablog](https://sunpy.org/blog.html). The Pull Request that added support can be found [here](https://github.com/sunpy/sunpy.org/pull/131) and how to link them to a [Binder](https://mybinder.org/) instance [here](https://github.com/sunpy/sunpy.org/pull/134)."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.6"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View file

@ -0,0 +1,44 @@
Post Excerpts and Images
========================
.. post:: May 12, 2014
:tags: directive
:category: Manual
:location: Pittsburgh
:author: Ahmet
:exclude:
:image: 2
This post describes how to choose an excerpt and an image image for a post to be displayed in archive pages.
Excerpts
--------
ABlog, by default, uses first paragraph of the document as post excerpt.
Default number of paragraphs to use in excerpts is controlled via :confval:`post_auto_excerpt` configuration variable.
This option can be overwritten using ``:excerpt:`` option in :rst:dir:`post` directive.
Alternatively, you can provide some content in a post directive as follows::
.. post:: Apr 15, 2014
This is all of the excerpt for this post.
This content is going to be used as excerpt in archive pages.
Furthermore, if you do not want the excerpt to be included in the post, you can use ``:exclude:`` option as follows::
.. post:: Apr 15, 2014
:exclude:
This is all of the excerpt for this post.
It will be displayed in archive pages and excluded from the post page.
Images
------
Let's first include a local and a non-local image in this post.
.. image:: /_static/ablog.png
.. image:: https://www.python.org/static/community_logos/python-logo.png
To link the second one of these, we add ``:image: 2`` option in :rst:dir:`post` directive.

View file

@ -0,0 +1,212 @@
Posting and Listing
===================
.. post:: May 9, 2014
:tags: directive
:category: Manual
:location: Pittsburgh
:author: Ahmet
This post describes :rst:dir:`post`, :rst:dir:`update`, and :rst:dir:`postlist` directives.
.. _posting-directive:
Posting with a Directive
------------------------
Any page in a Sphinx_ project can be converted to a post using the following directive:
.. rst:directive:: post
All possible directive options are shown below::
.. post:: 15 Apr, 2013
:tags: tips, ablog, directive
:category: Example, How To
:author: Ahmet, Durden
:location: Pittsburgh, SF
:redirect: blog/old-page-name-for-the-post
:excerpt: 2
:image: 1
:external_link: https://anexternalwebsite.org
:nocomments:
**Drafts & Posts**
Posts without dates or with future dates are considered as drafts and are published only in :ref:`blog-drafts` archive page.
Posts with dates that are older than the day Sphinx project is built are published in :ref:`blog-posts` page.
Post date format must follow the format specified with confval:`post_date_format` configuration option.
**Tags & Categories**
You can specify multiple tags and categories by separating them with commas.
Posts will be listed in archive pages generated for each unique tag and category.
**Authors, Languages, & Locations**
Likewise, you can specify authors, languages, and locations of a post using ``:author:``, ``:language:``, and ``:location:`` options.
All of these option names are in their singular form, but multiple values separated by commas are accepted.
Using :confval:`blog_authors`, :confval:`blog_languages`, and :confval:`blog_locations` configuration variables, you can also provide home pages and/or full display names of authors, languages, and locations, which will be displayed in archive pages generated for all unique authors, languages, and locations.
**Redirections**
You can make ABlog create pages that will redirect to current post using ``:redirect:`` option. It takes a comma separated list of paths, relative to the root folder.
The redirect page waits for :confval:`post_redirect_refresh` seconds before redirection occurs.
**Disable comments**
You can disable comments for the current post using the ``:nocomments:`` option.
Currently there is no way to disable comments in a specific page.
**Excerpts & Images**
By default, ABlog uses the first paragraph of a page as post excerpt.
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.
**Update Notes**
.. rst:directive:: update
Update in a post can be noted anywhere in the post as follows::
.. update:: 20 Apr, 2014
Added :rst:dir:`update` directive and :ref:`posting-sections` section.
Also revised the text here and there.
Update date format must follow the format specified with :confval:`post_date_format` configuration option.
Update directive renders like the updates that are at the end of this post.
.. _posting-front-matter:
Posting with page front-matter
------------------------------
If you'd prefer to use `page front matter <https://www.sphinx-doc.org/en/1.7/markup/misc.html>`__ instead of using a directive, you may mark a page as a "blog post" by adding the following front-matter at the top:
.. code-block:: rst
:blogpost: true
``ABlog`` will treat any pages with this front-matter as a blog post.
All fields that are available to the :ref:`posting directive <posting-directive>` can be given as page-level front-matter as well.
.. admonition:: Automatically detect blog posts with a ``glob`` pattern
:class: tip
Instead of adding ``blogpost: true`` to each page, you may also provide a pattern (or list of patterns) in your ``conf.py`` file using the ``blog_post_pattern`` option.
Any filenames that match this pattern will be treated as blog posts (and page front-matter will be used to classify the blog post).
For example, the following configuration would match all ``rst`` files in the ``posts/`` folder:
.. code-block:: python
blog_post_pattern = "posts/*.rst"
and this configuration will match all blog posts that match either ``rst`` or ``md``:
.. code-block:: python
blog_post_pattern = ["posts/*.rst", "posts/*.md"]
.. _posting-sections:
Posting Sections
----------------
.. post:: Aug 20, 2014
:tags: directive
:category: Manual
:location: SF
:author: Ahmet
:rst:dir:`post` directive can be used multiple times in a single page to create multiple posts of different sections of the document.
When :rst:dir:`post` is used more than once, post titles and excerpts are extracted from the sections that contain the directives.
This behavior can also be set as the default behavior using :confval:`post_always_section` configuration options.
Some caveats and differences from posting a document once are:
* Next and previous links at the bottom will only regard the first post in the document.
* Information displayed on the sidebar will belong to the first post.
* References for section posts is not automatically created. Labels for cross-referencing needs to be created manually, e.g., ``.. _posting-sections``. See :ref:`xref-syntax` for details.
Multiple use of :rst:dir:`post` may be suitable for major additions to a previous post. For minor changes, :rst:dir:`update` directive may be preferred.
Listing
-------
A list of posts can be displayed in any page using the following directive:
.. rst:directive:: postlist
Following example display all the options the directive takes::
.. postlist:: 5
:author: Ahmet
:category: Manual
:location: Pittsburgh
:language: en
:tags: tips
:date: %A, %B %d, %Y
:format: {title} by {author} on {date}
:list-style: circle
:excerpts:
:sort:
:expand: Read more ...
This will result in a bullet list of up to 5 posts (default is all) authored by `:ref:`author-ahmet`` in `:ref:`language-en`` when he was in `:ref:`location-pittsburgh`` and posted in `:ref:`category-manual`` with tags `:ref:`tag-tips``.
Posts will be in ``:sort:``\ed to appear in chronological order and listed with their ``:excerpts:``.
Here are those posts:
.. postlist:: 5
:author: Ahmet
:category: Manual
:location: Pittsburgh
:language: en
:tags: tips
:date: %A, %B %d, %Y
:format: {title} by {author} on {date}
:list-style: circle
:excerpts:
:sort:
:expand: Read more ...
When no options are given all posts will be considered and they will be ordered by recency.
Also, note that if the current post is one of the most recent posts, it will be omitted.
.. update:: Aug 21, 2014
Added :rst:dir:`update` directive and
:ref:`posting-sections` section.
Also revised the text here and there.
.. update:: Sep 15, 2014
* :rst:dir:`post` directive has ``:language:`` option.
* :rst:dir:`postlist` directive takes arguments to filter posts.
.. update:: Mar 28, 2015
Added ``:excerpts:`` option to :rst:dir:`postlist` to list posts with their excerpts.
.. update:: Apr 14, 2015
Added ``:list-style:`` option to :rst:dir:`postlist` to control bullet list style.
*circle*, *disc*, and *none* (default) are recognized.
.. update:: May 25, 2021
Added ``:expand:`` option to :rst:dir:`postlist` to add a call to action to continue reading the post.

View file

@ -0,0 +1,101 @@
Templating and Themes Support
=============================
.. post:: Oct 26, 2024
:tags: themes
:category: Manual
:author: Libor
Ablog, being a Sphinx extension, has highly customizable HTML output. The generated HTML files are based on `Sphinx templates`_. You, or Sphinx themes, can partially or completely override these templates to customize the resulting HTML.
.. _Sphinx templates: https://www.sphinx-doc.org/en/master/development/html_themes/templating.html
.. versionchanged:: 0.11
The :doc:`Ablog 0.11 </release/ablog-v0.11-released>` has changed and improved the way you can customize templates and themes. Please note that this document describes the new way of customizing templates and themes support.
.. _sidebars:
Blog sidebars
-------------
Sidebars are a common way to provide additional information to the reader. There are seven Ablog sidebars you can include in your HTML output using the Sphinx_ :confval:`html_sidebars` configuration option (in addition to your theme sidebars).
- ``ablog/postcard.html`` provides information regarding the current post (when on a post page).
- ``ablog/recentposts.html`` lists the most recent five posts.
- ``ablog/tagcloud.html`` provides links to archive pages generated for each tag.
- ``ablog/category.html``, ``ablog/authors.html``, ``ablog/languages.html``, and ``ablog/locations.html`` sidebars generate lists of links to respective archive pages with the number of matching posts (e.g., "Manual (14)", "2023 (8)", "English (22)").
For example, the sidebars that you see on this website on the left are:
.. code-block:: python
html_sidebars = {
"**": [
# Comes from Alabaster theme
"about.html",
"searchfield.html",
# Ablog sidebars
"ablog/postcard.html",
"ablog/recentposts.html",
"ablog/tagcloud.html",
"ablog/categories.html",
"ablog/archives.html",
"ablog/authors.html",
"ablog/languages.html",
"ablog/locations.html",
]
}
Styling default Ablog sidebars
------------------------------
Ablog standard sidebars are wrapped in ``<div>`` with CSS classes like :samp:`ablog-sidebar-item ablog__{<template_name>}`, making them easier to style.
For example, the ``recentposts.html`` template is wrapped in ``<div class="ablog-sidebar-item ablog__recentposts">``.
.. seealso::
Built-in sidebars can be found in the ``ablog/`` folder in the `Ablog source code <https://github.com/sunpy/ablog/tree/main/src/ablog/templates/ablog>`_.
If styling is not enough, you can override the Ablog templates in your Sphinx project or in the Sphinx theme.
Partial or complete override of Ablog templates
-----------------------------------------------
To control whether Ablog injects its own templates into the Sphinx build, you can use the following ``conf.py`` configuration option:
.. confval:: skip_injecting_base_ablog_templates
If set to ``True``, Ablog will not inject its own templates into the Sphinx build. This is useful if you want to completely override Ablog templates in your Sphinx project or in the Sphinx theme. The default is ``False``.
Customizing templates in the project
------------------------------------
All Ablog templates are under the ``ablog/`` folder space. For example, ``ablog/postcard.html``. You can override these templates by placing them in the ``ablog/`` folder in your project templates folder.
#. Add the :confval:`templates_path` option in your ``conf.py`` file:
.. code-block:: python
templates_path = ["_templates"]
#. Create a folder ``_templates/`` next to your ``conf.py`` file. It will hold your custom templates.
#. Create a folder ``ablog/`` inside the ``_templates/`` folder.
#. Create a file here with the same name as the template you want to override. For example, ``postcard.html``. This file will be used as a custom template for the sidebar. You can copy the content of the original template from the Ablog source code and modify it as you need.
#. Optionally: if you want to completely override all Ablog templates, set the :confval:`skip_injecting_base_ablog_templates` option to ``True``, copy all Ablog templates here, and customize them as you need.
Customizing templates in the theme
----------------------------------
If you are a Sphinx theme author, you can ship customized Ablog templates in your theme. You can override Ablog templates by placing them in the ``ablog/`` folder in your theme templates, e.g., ``ablog/postcard.html``.
#. In the theme root (where the ``theme.toml`` (or ``theme.ini`` in older Sphinx themes) file is), create a folder ``ablog/``.
#. Create a file here with the same name as the template you want to override. For example, ``postcard.html``.
#. This file will be used as a custom template for the sidebar. You can copy the content of the original template from the Ablog source code and modify it as you need.
#. In your ``theme.toml`` file, add the following (under the ``[options]`` section):
.. code-block:: toml
ablog_inject_templates_after_theme = true
This will ensure that Ablog templates are injected *after* the theme templates, so you can override them while still using the Ablog templates as a fallback.

View file

@ -0,0 +1,21 @@
Watch Yourself Blogging
=======================
.. post:: Apr 19, 2015
:tags: commands, tips
:category: Manual
:author: Ahmet
:location: SF
:language: en
Wouldn't you like your blog being rebuilt and served to you automatically as you are blogging on a sunny Sunday afternoon?
It's now possible with the improved ``ablog serve`` command.
First, you need to install Watchdog_ Python package, e.g. `pip install watchdog`.
Then, you need to run ``ablog serve -r``.
Regardless of the weather being sunny or the day of the week, your project will be rebuilt when you change a page or add a new one.
This won't refresh your browser page though.
Unless you want to hit refresh once in a while, you can easily find an auto refresher extension for you browser.
.. _Watchdog: https://github.com/gorakhargosh/watchdog

8
docs/nitpick-exceptions Normal file
View file

@ -0,0 +1,8 @@
py:class docutils.nodes.Node
py:class docutils.nodes.General
py:class docutils.nodes.Body
py:class docutils.nodes.admonition
py:class docutils.parsers.rst.directives.admonitions.BaseAdmonition
py:class docutils.transforms.Transform
py:class docutils.nodes.Element
py:class docutils.nodes.Admonition

View file

@ -0,0 +1,20 @@
:blogpost: true
:tags: tips
:author: Ahmet
:category: Release
:location: SF
:date: May 14, 2014
ABlog v0.1 released
===================
ABlog v0.1 is released.
This is the very first release, so there are no release notes in this post.
Yes, this page is also a post and it resides in a folder different from
most other posts in this blogumentation.
The idea is to enable making any page in a Sphinx_ project a post so that
users of a software package can subscribe to feeds and follow new releases
as well as code examples added to the documentation.

View file

@ -0,0 +1,282 @@
ABlog v0.10 released
====================
.. post:: Nov 17, 2019
:author: Nabil Freij
:category: Release
:location: World
ABlog v0.10 is released with the main focus being to support the latest version of Sphinx as well as Python 3 only support.
Ablog V0.9.X will no longer be supported as Python 2 comes to an end in a few months and it is time people upgraded.
Pull Requests merged in:
`Overhaul of package underneath for python3 only <https://github.com/sunpy/ablog/pull/41>`__ from `nabobalis <https://github.com/nabobalis>`__.
`Add validation for conf.py entries <https://github.com/sunpy/ablog/pull/38>`__ from `rayalan <https://github.com/rayalan>`__.
`Deploy improve <https://github.com/sunpy/ablog/pull/42>`__ from `rayalan <https://github.com/rayalan>`__.
`Get ablog ready for 0.10 <https://github.com/sunpy/ablog/pull/46>`__ from `nabobalis <https://github.com/nabobalis>`__.
ABlog v0.10.1 released
----------------------
Pull Requests merged in:
`Change StopIteration to return <https://github.com/sunpy/ablog/pull/48>`__ from `remyabel2 <https://github.com/remyabel2>`__.
ABlog v0.10.2 released
----------------------
Pull Requests merged in:
`Fix unclosed span tag <https://github.com/sunpy/ablog/pull/41>`__ from `ykrods <https://github.com/ykrods>`__.
ABlog v0.10.3 released
----------------------
Pull Requests merged in:
`Pin werkzeug to < 1 <https://github.com/sunpy/ablog/pull/53>`__ from `dstansby <https://github.com/dstansby>`__.
`MNT: Fix Giles URL <https://github.com/sunpy/ablog/pull/50>`__ from `pllim <https://github.com/pllim>`__.
ABlog v0.10.4 released
----------------------
Pull Requests merged in:
`Add zh_CN locale <https://github.com/sunpy/ablog/pull/61>`__ from `daimon99 <https://github.com/daimon99>`__.
`Add intersphinx to the extension list <https://github.com/sunpy/ablog/pull/60>`__ from `plaindocs <https://github.com/plaindocs>`__.
`Fix "test5" <https://github.com/sunpy/ablog/pull/58>`__ and `Use "dirhtml" builder on Read The Docs <https://github.com/sunpy/ablog/pull/57>`__ from `blueyed <https://github.com/blueyed>`__.
ABlog v0.10.5 released
----------------------
Pull Requests merged in:
`Add custom GitHub URL support <https://github.com/sunpy/ablog/pull/63>`__ from `tg-m <https://github.com/tg-m>`__.
ABlog v0.10.6 released
----------------------
Pull Requests merged in:
`Add french locale <https://github.com/sunpy/ablog/pull/65>`__ from `kujiu <https://github.com/kujiu>`__.
ABlog v0.10.7 released
----------------------
Pull Requests merged in:
`Automatically add templates path to documentation <https://github.com/sunpy/ablog/pull/63>`__ from `choldgraf <https://github.com/choldgraf>`__.
ABlog v0.10.8 released
----------------------
Removed the hard dependencies on alabaster and sphinx-automodapi.
Replaced `werkzeug <https://pypi.org/project/Werkzeug/>`__ with `feedgen <https://pypi.org/project/feedgen/>`__ due to the former removing ATOM support.
Version pin of nbsphinx has been removed.
ABlog v0.10.9 released
----------------------
Pull Requests merged in:
`frontmatter and blog post matching <https://github.com/sunpy/ablog/pull/63>`__ from `choldgraf <https://github.com/choldgraf>`__.
ABlog v0.10.10 released
-----------------------
Pull Requests merged in:
`Various Issues <https://github.com/sunpy/ablog/pull/77>`__.
`Fix missing reference caused by ref with title <https://github.com/sunpy/ablog/pull/73>`__ from `ykrods <https://github.com/ykrods>`__.
`Add instructions for starting new blog posts with front-matter <https://github.com/sunpy/ablog/pull/71>`__ from `kakirastern <https://github.com/kakirastern>`__.
ABlog v0.10.11 released
-----------------------
Pull Requests merged in:
`improving glob matching and documenting it <https://github.com/sunpy/ablog/pull/79>`__ from `choldgraf <https://github.com/choldgraf>`__.
ABlog v0.10.12 released
-----------------------
Pull Requests merged in:
`id of feed is now blog.blog_baseurl <https://github.com/sunpy/ablog/pull/83>`__.
ABlog v0.10.13 released
-----------------------
Pull Requests merged in:
`updated CI and py39 tests <https://github.com/sunpy/ablog/pull/86>`__.
`Add test #87 <https://github.com/sunpy/ablog/pull/87>`__.
`Some minor fixes <https://github.com/sunpy/ablog/pull/88>`__.
`Ensure blog_post_pattern are relative to srcdir <https://github.com/sunpy/ablog/pull/89>`__.
ABlog v0.10.14 released
-----------------------
Pull Requests merged in:
`feat(feeds): Add missing Atom entry metadata <https://github.com/sunpy/ablog/pull/92>`__.
`feat(feeds): Add entry element template support <https://github.com/sunpy/ablog/pull/93>`__.
`misc update <https://github.com/sunpy/ablog/pull/94>`__.
ABlog v0.10.15 released
-----------------------
Fixed `Index Out of Range with Atom Feeds <https://github.com/sunpy/ablog/issues/96>`__.
ABlog v0.10.16 released
-----------------------
Pull Requests merged in:
`fix(feeds): Feed validation, templates regression <https://github.com/sunpy/ablog/pull/97>`__.
ABlog v0.10.17 released
-----------------------
Pull Requests merged in:
`Correct draft URL <https://github.com/sunpy/ablog/pull/98>`__.
ABlog v0.10.18 released
-----------------------
Pull Requests merged in:
`Correct posts URL <https://github.com/sunpy/ablog/pull/99>`__.
`Add isso integration <https://github.com/sunpy/ablog/pull/100>`__.
ABlog v0.10.19 released
-----------------------
Pull Requests merged in:
`Add expand option <https://github.com/sunpy/ablog/pull/104>`__.
ABlog v0.10.20 released
-----------------------
Pull Requests merged in:
`fix documentation typo in blog-drafts <https://github.com/sunpy/ablog/pull/105>`__.
`Fix typo <https://github.com/sunpy/ablog/pull/109>`__.
`Catalan translation <https://github.com/sunpy/ablog/pull/113>`__.
`Fix ablog post <https://github.com/sunpy/ablog/pull/114>`__.
ABlog v0.10.21 released
-----------------------
Pull Requests merged in:
`Fix/multilang feed links <https://github.com/sunpy/ablog/pull/116>`__.
BREAKING CHANGE - DROPPED PYTHON 3.6 SUPPORT
ABlog v0.10.22 released
-----------------------
Pull Requests merged in:
`Fix tags field for myst_parser <https://github.com/sunpy/ablog/pull/119>`__.
ABlog v0.10.23 released
-----------------------
Pull Requests merged in:
`optionally show previous / next links on post page <https://github.com/sunpy/ablog/pull/120>`__.
`add classes to post elements <https://github.com/sunpy/ablog/pull/121>`__.
ABlog v0.10.24 released
-----------------------
Breaking Changes:
Minimum versions of packages increased:
.. code-block:: bash
feedgen>=0.9.0
invoke>=1.6.0
python-dateutil>=2.8.0
sphinx>=4.0.0
watchdog>=2.0.0
myst-parser>=0.17.0
pytest>=6.0.0
Pull Requests merged in:
`Get rid of eval and fix #128 <https://github.com/sunpy/ablog/pull/131>`__.
`CI Tweak <https://github.com/sunpy/ablog/pull/132>`__.
ABlog v0.10.25 released
-----------------------
Pull Requests merged in:
`Normalise path to posix as sphinx expects <https://github.com/sunpy/ablog/pull/134>`__.
ABlog v0.10.26 released
-----------------------
Pull Requests merged in:
`docs: Fix format of sphinx.ext.extlink for Sphinx 5.x <https://github.com/sunpy/ablog/pull/141>`__.
`docs: Use ref link rather than hardcode link <https://github.com/sunpy/ablog/pull/140>`__.
`Ci and Warnings Fix <https://github.com/sunpy/ablog/pull/142>`__.
ABlog v0.10.27 released
-----------------------
Pull Requests merged in:
`Improve conditional check for author metadata <https://github.com/sunpy/ablog/pull/146>`__.
ABlog v0.10.28 released
-----------------------
Pull Requests merged in:
`findall -> traverse for older versions of docutils <https://github.com/sunpy/ablog/pull/152>`__.
ABlog v0.10.29 released
-----------------------
Pull Requests merged in:
`Fix the error on some empty option value of post directive <https://github.com/sunpy/ablog/pull/155>`__.
ABlog v0.10.30 released
-----------------------
Pull Requests merged in:
`Sort Feed posts by date <https://github.com/sunpy/ablog/pull/172>`__.
`Fix sidebars ablog translations <https://github.com/sunpy/ablog/pull/161>`__.
ABlog v0.10.31 released
-----------------------
Pull Requests merged in:
`Add external links for posts <https://github.com/sunpy/ablog/pull/112>`__.

View file

@ -0,0 +1,128 @@
ABlog v0.11 released
====================
.. post:: March 23, 2023
:author: Nabil Freij
:category: Release
:location: World
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 <https://numpy.org/neps/nep-0029-deprecation_policy.html>`__ 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.
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.
- 3. Minimum version of Python is >=3.9 and Sphinx is >=5.0.
Pull Requests merged in:
`Template rework <https://github.com/sunpy/ablog/pull/144>`__.
`Add external links for posts <https://github.com/sunpy/ablog/pull/112>`__ from `Chris Holdgraf <https://github.com/choldgraf>`__.
Unreleased
----------
Pull Requests merged in:
`Fix theme support for Ablog <https://github.com/sunpy/ablog/pull/295>`__ from `Libor Jelínek <https://github.com/liborjelinek/>`__.
ABlog v0.11.1 released
----------------------
Pull Requests merged in:
`Update version handling to remove use of pkg_resources <https://github.com/sunpy/ablog/pull/211>`__
ABlog v0.11.2 released
----------------------
Pull Requests merged in:
`append posts to atom feed to keep post order from new to old <https://github.com/sunpy/ablog/pull/216>`__ from `lexming <https://github.com/lexming>`__.
`avoid spurious warning about posts with front-matter and post directive <https://github.com/sunpy/ablog/pull/214>`__ from `lexming <https://github.com/lexming>`__.
ABlog v0.11.3 released
----------------------
Pull Requests merged in:
`use fully qualified URLs for images in atom feed <https://github.com/sunpy/ablog/pull/218>`__ from `lexming <https://github.com/lexming>`__.
ABlog v0.11.4 released
----------------------
Pull Requests merged in:
`Use paragraph instead of container for blog post excerpts <https://github.com/sunpy/ablog/pull/226>`__ from `dstansby <https://github.com/dstansby>`__.
ABlog v0.11.5 released
----------------------
Pull Requests merged in:
`Fix incorrect /div when using discuss <https://github.com/sunpy/ablog/pull/251>`__ from `Cadair <https://github.com/Cadair>`__.
ABlog v0.11.6 released
----------------------
Pull Requests merged in:
`Adds IT locale <https://github.com/sunpy/ablog/pull/253>`__ from `Stefano David <https://github.com/stefanodavid>`__.
`Enables configuring a canonical_link for individual posts <https://github.com/sunpy/ablog/pull/258>`__ from `Hendrik Makait <https://github.com/hendrikmakait>`__.
ABlog v0.11.7 released
----------------------
Pull Requests merged in:
`Add stylesheet for tagcloud <https://github.com/sunpy/ablog/pull/268>`__ from `Shengyu Zhang <https://github.com/SilverRainZ>`__.
`Create demo/ before running ablog start <https://github.com/sunpy/ablog/pull/269>`__ from `Shengyu Zhang <https://github.com/SilverRainZ>`__.
`Add span to more items in templates <https://github.com/sunpy/ablog/pull/270>`__ from `Nabil Freij <https://github.com/nabobalis>`__.
ABlog v0.11.8 released
----------------------
Added support for ``sphinx`` >=7.3.0
ABlog v0.11.9 released
----------------------
`Make '_strip' function return as list not set. <https://github.com/sunpy/ablog/pull/280>`__ from `Joe Ziminski <https://github.com/JoeZiminski>`__.
ABlog v0.11.10 released
-----------------------
Fixed wrong branch in the release process.
ABlog v0.11.11 released
-----------------------
Mark Ablog parallel safe.
Dropped support for Python 3.9, Sphinx less than 6.2.
This mirrors the requirements for alabaster 1.0.0.
ABlog v0.11.12 released
-----------------------
`Improve ablog-configuration-options.rst. <https://github.com/sunpy/ablog/pull/292>`__ from `Libor Jelínek <https://github.com/liborjelinek>`__.
`Fix sidebars CSS naming. <https://github.com/sunpy/ablog/pull/298>`__ from `Libor Jelínek <https://github.com/liborjelinek>`__.
`Ablog is NOT safe for parallel read. <https://github.com/sunpy/ablog/pull/299>`__ from `Libor Jelínek <https://github.com/liborjelinek>`__.
`Fix theme support for ablog. <https://github.com/sunpy/ablog/pull/295>`__ from `Libor Jelínek <https://github.com/liborjelinek>`__.

View file

@ -0,0 +1,42 @@
ABlog v0.2 released
===================
.. post:: Aug 31, 2014
:author: Ahmet
:category: Release
:location: SF
ABlog v0.2 is released. This version comes with several new features:
* You can post a document multiple times, see :ref:`posting-sections`
for details.
* You can make note of updates in a post using :rst:dir:`update`
directive.
* Blog feeds for authors, locations, categories, tags, and years
can be enabled using :confval:`blog_feed_archives` configuration
variable.
* Blog Feeds can be made full text using :confval:`blog_feed_fulltext`
configuration variable.
* Recent posts side bar includes month and day of the posts.
ABlog v0.2.1 released
---------------------
ABlog v0.2.1 is a bug fix release that solves duplicated content
problem in full text atom feeds.
ABlog v0.2.2 released
---------------------
ABlog v0.2.2 is a bug fix release that solves broken links problem
in post lists (:issue:`12`).
ABlog v0.2.3 released
---------------------
ABlog v0.2.3 is a bug fix release that solves broken links (:issue:`13`)
and non-unique post IDs problems atom feeds.

View file

@ -0,0 +1,28 @@
ABlog v0.3 released
===================
ABlog v0.3 is released. This version comes with the following core
improvements:
* You can now specify language of a post with ``:language:`` option,
and an archive page will be created for each language.
See :confval:`blog_languages` and :confval:`blog_default_language`
if you are posting in multiple languages.
* You can list language archives on your website by adding
``languages.html`` to :confval:`html_sidebars` configuration option.
* :rst:dir:`postlist` directive takes options to filter posts.
ABlog v0.3.1 released
---------------------
ABlog v0.3.1 is a minor release to fix two issues in templates:
* Links to collection (archive) feeds is displayed only on collection page
(e.g. `:ref:`category-manual``), not on a catalog page that lists posts
for multiple collections (e.g. `:ref:`blog-categories``).
* Links to collection feeds is displayed only when they are generated
(see :confval:`blog_feed_archives`). Previously, links would be generated
to feeds that did not exist.

View file

@ -0,0 +1,33 @@
ABlog v0.4 released
===================
.. post:: Dec 20, 2014
:author: Ahmet
:category: Release
:location: SF
ABlog v0.4 is released. This version comes with the following improvements
and bug fixes:
* Added :confval:`blog_feed_titles`, :confval:`blog_feed_length`, and
:confval:`blog_archive_titles` configuration options (see :issue:`24`).
* Set the default for :confval:`blog_feed_archives` to ``False``, which
was set to ``True`` although documented to be otherwise.
* Fixed issues with :confval:`post_auto_excerpt` and
:confval:`post_auto_image` configuration options.
* Fixed :issue:`2`, relative size of tags being the minimum size when
all tags have the same number of posts. Now, mean size is
used, and max/min size can be controlled from template.
* Fixed :issue:`19`. Yearly archives are ordered by recency.
* Fixed duplicated post title in feeds, :issue:`21`.
* Fixed :issue:`22`, :rst:dir:`postlist` directive listing more than
specified number of posts.
* :rst:dir:`postlist` directive accepts arguments to format list items
(:issue:`20`).

View file

@ -0,0 +1,15 @@
ABlog v0.5 released
===================
.. post:: Mar 25, 2015
:author: Ahmet, Mehmet
:category: Release
:location: SF
ABlog v0.5 is released. This version comes with :ref:`ablog-commands` and
a :ref:`quick-start` guide.
ABlog v0.5.1 released
---------------------
Added ``:excerpts:`` option to :rst:dir:`postlist` directive.

View file

@ -0,0 +1,46 @@
ABlog v0.6 released
===================
.. post:: Apr 8, 2015
:author: Ahmet
:category: Release
:location: SF
ABlog v0.6 is released with new :ref:`ablog-commands`. You can use
``ablog deploy`` to :ref:`deploy-to-github-pages`, and also ``ablog clean``
to do spring cleaning every once in a while.
ABlog v0.6.1 released
---------------------
ABlog v0.6.1 is released with improvements to ``ablog deploy`` command.
It will add ``.nojekyll`` file when needed to deployments to GitHub pages.
ABlog v0.6.2 released
---------------------
ABlog v0.6.2 is released to fix an issue with loading of Disqus comments
(:issue:`33`) and interpreting non-ascii characters (:issue:`34`).
ABlog v0.6.3 released
---------------------
ABlog v0.6.3 comes with Russian localisation and following enhancements:
* Added ``:list-style:`` option to :rst:dir:`postlist` to enable
controlling bullet list style.
* ``ablog post`` command de-slugifies filename to make the title
when it's not given.
ABlog v0.6.4 released
---------------------
ABlog v0.6.4 comes with improved ``ablog serve`` command that helps you
:ref:`watch-yourself-blogging`.
ABlog v0.6.5 released
---------------------
ABlog v0.6.5 is a bug fix release to resolve :issue:`38`, an exception raised
when using :rst:dir:`postlist` without specifying number of posts.

View file

@ -0,0 +1,92 @@
ABlog v0.7 released
===================
.. post:: May 3, 2015
:author: Ahmet
:category: Release
:location: Denizli
ABlog v0.7.0 is released to fix the long standing :issue:`1` related to
pickling of Sphinx build environment on Read The Docs. Improvements
also resolved issues with using LaTeX builder, improved cross-referencing
for non-html builders.
ABlog v0.7.1 released
---------------------
ABlog v0.7.1 is released to fix Python 3 import issues in :command:`ablog serve`
command.
ABlog v0.7.2 released
---------------------
ABlog v0.7.2 is released to prevent potential issues with Disqus thread URLs
by requiring :confval:`disqus_shortname` and :confval:`blog_baseurl`
to be specified together for Disqus integration.
ABlog v0.7.3 released
---------------------
ABlog v0.7.3 makes use of `python-dateutil`__ for parsing post dates, so now you
can be flexible with the format you use in posts. Thanks to `Andy Maloney`__
for this improvement.
__ https://pypi.python.org/pypi/python-dateutil
__ https://github.com/amaloney
ABlog v0.7.5 released
---------------------
ABlog v0.7.5 is released to fix Windows specific path resolving issue with
archive pages. Thanks to Peter Mills for reporting this issue.
ABlog v0.7.6 released
---------------------
ABlog v0.7.6 is released to fix path resolving issue that arose when
``:excerpts:`` is used in :rst:dir:`postlist` directive. Once again, thanks
to Peter Mills for reporting this issue. Other minor changes are:
* ``-P`` argument is added to :ref:`ablog build <build>` command to enable running pdb
on exceptions.
* ``conf.py`` file created by :ref:`ablog start <start>` updated to include
``about.html`` sidebar that comes with Alabaster_ theme.
ABlog v0.7.7 released
---------------------
ABlog v0.7.7 is released to fix path resolving :issue:`41` that arose when
cross-references were used in post excerpts, and also post redirect
issue in templates.
ABlog v0.7.8 released
---------------------
ABlog v0.7.8 is released to fix a Python 2 issue that appears when creating
collection pages that contain non-ascii characters in their names (:issue:`45`)
and filename escaping issue when committing changes using
:ref:`ablog deploy <deploy>` command (:pull:`44`).
Thanks to `uralbash`_ for these contributions.
.. _uralbash: https://github.com/uralbash
ABlog v0.7.9 released
---------------------
ABlog v0.7.9 is released to fix Windows specific file renaming issue in
:ref:`ablog deploy <deploy>` command (:issue:`46`). Thanks to `Velimir`_
for the fix.
.. _Velimir: https://github.com/montyvesselinov
ABlog v0.7.10 released
----------------------
ABlog v0.7.10 is released to resolve Sphinx JSON/Pickle builder issues
related to serialization.
ABlog v0.7.12 released
----------------------
ABlog v0.7.12 (and also v0.7.11) maintenance release are available.

View file

@ -0,0 +1,58 @@
ABlog v0.8 released
===================
.. post:: Oct 12, 2015
:author: Ahmet
:category: Release
:location: SF
ABlog v0.8.0 is released with additions and changes:
* Added ``-a`` argument to :ref:`ablog build <build>` command, with which
you can force rewriting all pages when rebuilding your project. Default is
writing only pages that have changed.
* Added ``-f`` argument to :ref:`ablog deploy <deploy>` command, with which
you can amend to latest commit to keep GitHub pages repository small.
Thanks to `uralbash`_ for this contribution.
* Added ``-p`` argument to :ref:`ablog deploy <deploy>` command, with which
you can specify the path to your GitHub pages repository, i.e.
``username.github.io``.
* Changed :confval:`fontawesome_link_cdn` to be a string argument to enable
linking to desired version of `Font Awesome`_. Thanks to `Albert Mietus`_
for this contribution.
* Post lists font style is now controlled through CSS. Thanks to
`Albert Mietus`_ for this contribution as well.
* Fixed internal link resolution issue that affected atom feeds of
collections, i.e. feeds of posts under a category, tag, or author.
.. _Font Awesome: https://fontawesome.com/
.. _Albert Mietus: https://github.com/AlbertMietus
.. _uralbash: https://github.com/uralbash
ABlog v0.8.1 released
---------------------
ABlog v0.8.1 is released to fix atom feed linking in HTML header (:issue:`54`).
ABlog v0.8.2 released
---------------------
ABlog v0.8.2 is released to fix date parsing (:issue:`58`) and Python 2.6
installation (:issue:`59`) issues.
ABlog v0.8.3 released
---------------------
ABlog v0.8.3 is released to bring you recent enhancements:
* `ninmesara`_ added ``:nocomments:`` argument to :rst:dir:`post` directive
to disable comments per post.
* `José Carlos García`_ added Spanish translations.
.. _ninmesara: https://github.com/ninmesara
.. _José Carlos García: https://github.com/quobit

View file

@ -0,0 +1,61 @@
ABlog v0.9 released
===================
.. post:: Feb 17, 2018
:author: Nabil Freij
:category: Release
:location: World
ABlog v0.9.0 is released with the main focus being to support the latest version of Sphinx.
This also moves the main development from `Abakan`_ to `SunPy`_.
This has merged in all current (at time of writing, 6) open PRs to the original repository.
These are:
`fix(commands): Update command arguments so patterns works correctly <https://github.com/abakan-zz/ablog/pull/96>`__ from `rayalan <https://github.com/rayalan>`__.
`Fix couple of bugs with latest stable Sphinx <https://github.com/abakan-zz/ablog/pull/93>`__ from `tadeboro <https://github.com/tadeboro>`__.
`don't use fancy quotes in the conf.py template <https://github.com/abakan-zz/ablog/pull/87>`__ from `tiwo <https://github.com/tiwo>`__.
`Pass through additional Sphinx options and fix a typo <https://github.com/abakan-zz/ablog/pull/84>`__ from `ahrbel <https://github.com/ahrbe1>`__.
`fix #78 (ImportError: cannot import name make_admonition in Sphinx 1.6) <https://github.com/abakan-zz/ablog/pull/79>`_ from `lsaffre <https://github.com/lsaffre>`__.
`Raise exception when title is missing <https://github.com/abakan-zz/ablog/pull/76>`__ from `rgrinberg <https://github.com/rgrinberg>`__.
.. _Abakan: https://github.com/abakan/ablog
.. _SunPy: https://github.com/sunpy/ablog
ABlog v0.9.1 released
---------------------
Minor update to remove Ablog{}.format(python_number) exes
ABlog v0.9.2 released
---------------------
Fixed Windows String issue.
ABlog v0.9.3 released
---------------------
Added example on how to use writing blog posts in Jupyter notebooks.
`show </span> when if fa <https://github.com/sunpy/ablog/pull/22>`__, `Add create file encoding <https://github.com/sunpy/ablog/pull/23>`__ and `fix serve command path <https://github.com/sunpy/ablog/pull/31>`__ from `anzawatta <https://github.com/anzawatta>`__.
Sorry I was late to release these!
ABlog v0.9.4 released
---------------------
Fixes for gettext break and some pathing issues.
ABlog v0.9.5 released
---------------------
v0.9.5 is the that supports Python 2 and Sphinx <2.
v0.10.0 on main now, will not.
`Define an auto-orphan option <https://github.com/sunpy/ablog/pull/39>`__, `Repair update directive <https://github.com/sunpy/ablog/pull/37>`__ and `Fix sidebar and blog_baseurl misconfig during quick start <https://github.com/sunpy/ablog/pull/36>`__ from `rayalan <https://github.com/rayalan>`__.

64
pyproject.toml Normal file
View file

@ -0,0 +1,64 @@
[build-system]
requires = ["setuptools", "setuptools_scm", "wheel"]
build-backend = 'setuptools.build_meta'
[tool.black]
line-length = 120
include = '\.pyi?$'
exclude = '''
(
/(
\.eggs
| \.git
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
| astropy_helpers
| docs
| .history
)/
| ah_bootstrap.py
)
'''
target-version = ['py310']
[tool.ruff]
# Enable Pyflakes `E` and `F` codes by default.
select = ["E", "F"]
ignore = ["E501", "E741"]
# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = ["A", "B", "C", "D", "E", "F"]
exclude = [
".eggs",
".git",
".mypy_cache",
".ruff_cache",
".tox",
".venv",
"__pypackages__",
"_build",
"build",
"dist",
"node_modules",
"venv",
]
# Same as Black.
line-length = 120
target-version = "py310"
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
[tool.codespell]
skip = "*cache*,*egg*,*extern*,.git,.idea,.tox,*.svg,.history,*sphinx.po"
ignore-words-list = "THIRDPARTY,"
[tool.djlint]
files=["*.html"]

16
roots/test-build/conf.py Normal file
View file

@ -0,0 +1,16 @@
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 %}",
},
}

View file

@ -0,0 +1,5 @@
.. post:: 2021-03-23
##############
Foo Empty Post
##############

View file

@ -0,0 +1,2 @@
test-build
============

11
roots/test-build/post.rst Normal file
View file

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

View file

@ -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``.

View file

@ -0,0 +1,18 @@
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

View file

@ -0,0 +1,2 @@
test-external
=============

View file

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

View file

@ -0,0 +1,4 @@
postlist
========
.. postlist::

View file

@ -0,0 +1,16 @@
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 %}",
},
}

View file

@ -0,0 +1,8 @@
.. post:: 2021-12-01
:tags: External
:external_link: https://www.sphinx-doc.org/en/master/
External post
=============
This text will be in auto-generated post previews, but links to the post will direct to ``external_link``.

View file

@ -0,0 +1,2 @@
test-external
=============

View file

@ -0,0 +1,4 @@
postlist
========
.. postlist::

View file

@ -0,0 +1 @@
extensions = ["ablog"]

View file

@ -0,0 +1,7 @@
test-postlist
===============
.. toctree::
:maxdepth: 1
postlist

View file

@ -0,0 +1,4 @@
.. post:: 2020-12-01
post 1
=======

View file

@ -0,0 +1,4 @@
.. post:: 2020-12-01
post 2
=======

View file

@ -0,0 +1,4 @@
.. post:: 2020-12-01
post 3
=======

View file

@ -0,0 +1,4 @@
.. post:: 2020-12-01
post 4
=======

View file

@ -0,0 +1,4 @@
postlist
==========
.. postlist::

View file

@ -0,0 +1 @@
extensions = ["ablog"]

View file

@ -0,0 +1,7 @@
test-postlist
===============
.. toctree::
:maxdepth: 1
postlist

View file

@ -0,0 +1,4 @@
.. post:: 2020-12-01
post
=======

View file

@ -0,0 +1,4 @@
postlist
==========
.. postlist::

View file

@ -0,0 +1 @@
custom postcard.html

View file

@ -0,0 +1 @@
custom postcard.html from theme

View file

@ -0,0 +1,5 @@
[theme]
inherit = "basic"
[options]
ablog_inject_templates_after_theme = true

View file

@ -0,0 +1 @@
extensions = ["ablog"]

View file

@ -0,0 +1,7 @@
test-postlist
===============
.. toctree::
:maxdepth: 1
postlist

View file

@ -0,0 +1,5 @@
.. post:: 2020-12-01
:author: Durden
post
=======

View file

@ -0,0 +1,4 @@
postlist
==========
.. postlist::

112
setup.cfg Normal file
View file

@ -0,0 +1,112 @@
[metadata]
name = ablog
author = The SunPy Community
author_email = sunpy@googlegroups.com
description = A Sphinx extension that converts any documentation or personal website project into a full-fledged blog.
long_description = file: README.rst
long_description_content_type = text/x-rst
license = MIT
url = https://ablog.readthedocs.io/
edit_on_github = True
github_project = sunpy/ablog
[options]
python_requires = >=3.10
package_dir=
=src
packages=find:
include_package_data = True
setup_requires =
setuptools_scm
install_requires =
docutils>=0.18
feedgen>=0.9.0
invoke>=1.6.0
packaging>=19.0
python-dateutil>=2.8.2
sphinx>=6.2.0
watchdog>=2.1.0
[options.packages.find]
where=src
[options.extras_require]
notebook =
ipython>=7.30.0
nbsphinx>=0.8.0
markdown =
myst-parser>=0.17.0
docs =
alabaster>=1.0.0
sphinx-automodapi
tests =
pytest
defusedxml>=0.8.0rc2
[options.entry_points]
console_scripts =
ablog = ablog.commands:ablog_main
[tool:pytest]
testpaths = "tests"
norecursedirs = ".tox" "build" "docs[\/]_build" "docs[\/]generated" "*.egg-info" ".history"
markers =
sphinx
addopts = -p no:unraisableexception -p no:threadexception
filterwarnings =
error
# Do not fail on pytest config issues (i.e. missing plugins) but do show them
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
[flake8]
max-line-length = 120
[isort]
default_section = THIRDPARTY
force_grid_wrap = 0
include_trailing_comma = true
known_first_party = ablog
length_sort = False
length_sort_sections = stdlib
line_length = 120
multi_line_output = 3
skip = .history
sections = FUTURE, STDLIB, THIRDPARTY, FIRSTPARTY, LOCALFOLDER
[coverage:run]
omit =
*/ablog/__init__*
*/ablog/*/tests/*
*/ablog/*setup*
*/ablog/conftest.py
*/ablog/cython_version*
*/ablog/extern/*
*/ablog/version*
ablog/__init__*
ablog/*/tests/*
ablog/*setup*
ablog/conftest.py
ablog/cython_version*
ablog/extern/*
ablog/version*
[coverage:report]
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain about packages we have installed
except ImportError
# Don't complain if tests don't hit assertions
raise AssertionError
raise NotImplementedError
# Don't complain about script hooks
def main\(.*\):
# Ignore branches that don't pertain to this version of Python
pragma: py{ignore_python_version}

30
setup.py Normal file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env python
from setuptools import setup # isort:skip
import os
from itertools import chain
try:
# Recommended for setuptools 61.0.0+
# (though may disappear in the future)
from setuptools.config.setupcfg import read_configuration
except ImportError:
from setuptools.config import read_configuration
################################################################################
# Programmatically generate some extras combos.
################################################################################
extras = read_configuration("setup.cfg")["options"]["extras_require"]
# Dev is everything
extras["dev"] = list(chain(*extras.values()))
# All is everything but tests and docs
exclude_keys = ("tests", "docs", "dev")
ex_extras = dict(filter(lambda i: i[0] not in exclude_keys, extras.items()))
# Concatenate all the values together for 'all'
extras["all"] = list(chain.from_iterable(ex_extras.values()))
setup(
extras_require=extras,
use_scm_version={"write_to": os.path.join("src", "ablog", "_version.py")},
)

172
src/ablog/__init__.py Executable file
View file

@ -0,0 +1,172 @@
"""
ABlog for Sphinx.
"""
import os
from glob import glob
from pathlib import PurePath
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.errors import ThemeError
from sphinx.jinja2glue import BuiltinTemplateLoader, SphinxFileSystemLoader
from sphinx.locale import get_translation
from .blog import CONFIG, Blog
from .post import (
CheckFrontMatter,
PostDirective,
PostListDirective,
UpdateDirective,
UpdateNode,
generate_archive_pages,
generate_atom_feeds,
missing_reference,
process_postlist,
process_posts,
purge_posts,
)
from .version import version as __version__
__all__ = ["setup", "__version__"]
PKGDIR = os.path.abspath(os.path.dirname(__file__))
# Name used for the *.pot, *.po and *.mo files
MESSAGE_CATALOG_NAME = "sphinx"
_ = get_translation(MESSAGE_CATALOG_NAME) # NOQA
def get_html_templates_path():
"""
Return path to ABlog templates folder.
"""
pkgdir = os.path.abspath(os.path.dirname(__file__))
return os.path.join(pkgdir, "templates")
def anchor(post):
"""
Return anchor string for posts that are page sections.
"""
if post.section:
return "#" + post.section
else:
return ""
def builder_support(builder):
"""
Return True when builder is supported.
Supported builders output in html format, but exclude
`PickleHTMLBuilder` and `JSONHTMLBuilder`, which run into issues
when serializing blog objects.
"""
if hasattr(builder, "builder"):
builder = builder.builder
not_supported = {"json", "pickle"}
return builder.format == "html" and builder.name not in not_supported
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
context["feed_title"] = blog.blog_title
def config_inited(app, config):
# Automatically identify any blog posts if a pattern is specified in the config
if isinstance(config.blog_post_pattern, str):
config.blog_post_pattern = [config.blog_post_pattern]
matched_patterns = []
for pattern in config.blog_post_pattern:
pattern = os.path.join(app.srcdir, pattern)
# make sure that blog post paths have forward slashes even on windows
matched_patterns.extend(
PurePath(ii).relative_to(app.srcdir).with_suffix("").as_posix() for ii in glob(pattern, recursive=True)
)
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:
return
if not isinstance(app.builder.templates, BuiltinTemplateLoader):
raise Exception(
"Ablog does not know how to inject templates into with custom "
"template bridges. You can use `ablog.get_html_templates_path()` to "
"get the path to add in your custom template bridge and set "
"`skip_injecting_base_ablog_templates = False` in your "
"`conf.py` file."
)
if get_html_templates_path() in app.config.templates_path:
raise Exception(
"Found the path from `ablog.get_html_templates_path()` in the "
"`templates_path` variable from `conf.py`. Doing so interferes "
"with Ablog's ability to stay compatible with Sphinx themes that "
"support it out of the box. Please remove `get_html_templates_path` "
"from `templates_path` in your `conf.py` to resolve this."
)
theme = app.builder.theme
loaders = app.builder.templates.loaders
templatepathlen = app.builder.templates.templatepathlen
try:
# Modern Sphinx now errors instead of returning the default if there is not a value
# in any of the config files.
after_theme = theme.get_config("options", "ablog_inject_templates_after_theme", False)
except ThemeError:
after_theme = False
if after_theme:
# Inject *after* the user templates and the theme templates,
# allowing themes to override the templates provided by this
# extension while those templates still serve as a fallback.
loaders.append(SphinxFileSystemLoader(get_html_templates_path()))
else:
# Inject *after* the user templates and *before* the theme
# templates. This enables ablog to provide support for themes
# that don't support it out-of-the-box, like alabaster.
loaders.insert(templatepathlen, SphinxFileSystemLoader(get_html_templates_path()))
def setup(app):
"""
Setup ABlog extension.
"""
app.require_sphinx("6.2")
for args in CONFIG:
app.add_config_value(*args[:3])
app.add_directive("post", PostDirective)
app.add_directive("postlist", PostListDirective)
app.connect("config-inited", config_inited)
app.connect("builder-inited", builder_inited)
app.connect("doctree-read", process_posts)
app.connect("env-purge-doc", purge_posts)
app.connect("doctree-resolved", process_postlist)
app.connect("missing-reference", missing_reference)
app.connect("html-collect-pages", generate_archive_pages)
app.connect("html-collect-pages", generate_atom_feeds)
app.connect("html-page-context", html_page_context)
app.add_transform(CheckFrontMatter)
app.add_directive("update", UpdateDirective)
app.add_node(
UpdateNode,
html=(lambda s, n: s.visit_admonition(n), lambda s, n: s.depart_admonition(n)),
latex=(lambda s, n: s.visit_admonition(n), lambda s, n: s.depart_admonition(n)),
)
pkgdir = os.path.abspath(os.path.dirname(__file__))
locale_dir = os.path.join(pkgdir, "locales")
app.add_message_catalog(MESSAGE_CATALOG_NAME, locale_dir)
return {
"version": __version__,
"parallel_read_safe": False,
"parallel_write_safe": True,
}

615
src/ablog/blog.py Normal file
View file

@ -0,0 +1,615 @@
"""
Classes for handling posts and archives.
"""
import os
import re
import datetime as dtmod
from datetime import datetime
from operator import attrgetter
from unicodedata import normalize
from urllib.parse import urljoin
from collections.abc import Container
from docutils import nodes
from docutils.io import StringOutput
from docutils.utils import new_document
from sphinx import addnodes
from sphinx.util.osutil import relative_uri
__all__ = ["Blog", "Post", "Collection", "BlogPageMixin", "Catalog"]
def slugify(string):
"""
Slugify *s*.
"""
string = normalize("NFKD", str(string))
string = re.sub(r"[^\w\s-]", "", string).strip().lower()
return re.sub(r"[-\s]+", "-", string)
def os_path_join(path, *paths):
return os.path.join(path, *paths).replace(os.path.sep, "/")
def require_config_type(type_, is_optional=True):
def verify_fn(key, value, config):
if isinstance(value, type_) or (is_optional and value is None):
return value
# Historically, we're pretty sloppy on whether None or False is the default for omission
# so accept them both.
if value is False and is_optional:
return None
raise KeyError(key + " must be a " + type_.__name__ + (" or omitted" if is_optional else ""))
return verify_fn
def require_config_str_or_list_lookup(lookup_config_key, is_optional=True):
"""
The default values can be a string or list of strings that match entries in
a comprehensive list -- for example, the default authors are one or more of
all the authors.
"""
def verify_fn(key, value, config):
if is_optional and value is None:
return value
if isinstance(value, str):
value = [value]
if not isinstance(value, list):
raise KeyError(key + " must be a str or list")
allowed_values = config[lookup_config_key]
for v in value:
if v not in allowed_values:
raise KeyError(str(v) + "must be a key of " + lookup_config_key)
return value
return verify_fn
def require_config_full_name_link_dict(is_link_optional=True):
"""
The definition for authors and similar entries is to map a short name to a
(full name, link) tuple.
"""
def verify_fn(key, value, config):
for full_name, link in value.values():
if not isinstance(full_name, str):
raise KeyError(key + " must have full name entries that are strings")
is_link_valid = isinstance(link, str) or (is_link_optional and link is None)
if not is_link_valid:
raise KeyError(key + " links must be a string" + (" or omitted" if is_link_optional else ""))
return value
return verify_fn
DEBUG = True
CONFIG = [
# name, default, rebuild, verify_fn
# where verify_fn is (key, value, app.config) --> value, throwing a KeyError if the value isn't right
("blog_archive_titles", None, False, require_config_type(bool)),
("blog_authors", {}, True, require_config_full_name_link_dict()),
("blog_baseurl", "", True, require_config_type(str)),
("blog_default_author", None, True, require_config_str_or_list_lookup("blog_authors")),
("blog_default_language", None, True, require_config_str_or_list_lookup("blog_languages")),
("blog_default_location", None, True, require_config_str_or_list_lookup("blog_locations")),
("blog_feed_archives", False, True),
("blog_feed_fulltext", False, True),
("blog_feed_length", None, None),
("blog_feed_subtitle", None, True),
("blog_feed_templates", {"atom": {}}, True),
("blog_feed_titles", None, False),
("blog_languages", {}, True, require_config_full_name_link_dict()),
("blog_locations", {}, True, require_config_full_name_link_dict()),
("blog_path", "blog", True, require_config_type(str)),
("blog_post_pattern", [], True, require_config_type((str, list))),
("blog_title", "Blog", True, require_config_type(str)),
("disqus_drafts", False, True),
("disqus_pages", False, True),
("disqus_shortname", None, True),
("fontawesome_css_file", "", True, require_config_type(str)),
("fontawesome_included", False, True, require_config_type(bool)),
("fontawesome_link_cdn", None, True),
("post_always_section", False, True),
("post_auto_excerpt", 1, True),
("post_auto_image", 0, True),
("post_auto_orphan", True, True, require_config_type(bool)),
("post_date_format_short", "%d %B", True, require_config_type(str)),
("post_date_format", "%d %B %Y", True, require_config_type(str)),
("post_redirect_refresh", 5, True),
("post_show_prev_next", True, True),
("skip_injecting_base_ablog_templates", False, True),
]
TOMORROW = datetime.today() + dtmod.timedelta(1)
TOMORROW = TOMORROW.replace(hour=0, minute=0, second=0, microsecond=0)
FUTURE = datetime(9999, 12, 31)
def revise_pending_xrefs(doctree, docname):
for node in doctree.findall(addnodes.pending_xref):
node["refdoc"] = docname
def link_posts(posts):
"""
Link posts after sorting them post by published date.
"""
posts = filter(attrgetter("order"), posts)
posts = sorted(posts)
posts[0].prev = posts[-1].next = None
for i in range(1, len(posts)):
post = posts[i]
posts[i - 1].next = post
post.prev = posts[i - 1]
class Blog(Container):
"""
Handle blog operations.
"""
# using a shared state
_dict = {}
def __init__(self, app):
self.__dict__ = self._dict
if not self._dict:
self._init(app)
def _init(self, app):
"""
Instantiate Blog.
"""
self.app = app
self.config = {}
self.references = refs = {}
# get configuration from Sphinx app
for opt in CONFIG:
try:
key, _, _, verify_fn = opt
except ValueError:
key, _, _ = opt
verify_fn = None
value = verify_fn(key, getattr(app.config, key), app.config) if verify_fn else getattr(app.config, opt[0])
self.config[opt[0]] = value
# blog catalog contains all posts
self.blog = Catalog(self, "blog", "blog", None)
# contains post collections by year
self.archive = Catalog(self, "archive", "archive", None, reverse=True)
self.archive.docname += "/archive"
refs["blog-archives"] = (self.archive.docname, "Archives")
self.catalogs = cat = {} # catalogs of user set labels
self.tags = cat["tags"] = Catalog(self, "tags", "tag", "tag")
refs["blog-tags"] = (self.tags.docname, "Tags")
self.author = cat["author"] = Catalog(self, "author", "author", "author")
refs["blog-authors"] = (self.author.docname, "Authors")
self.location = cat["location"] = Catalog(self, "location", "location", "location")
refs["blog-locations"] = (self.location.docname, "Locations")
self.language = cat["language"] = Catalog(self, "language", "language", "language")
refs["blog-languages"] = (self.language.docname, "Languages")
self.category = cat["category"] = Catalog(self, "category", "category", "category")
refs["blog-categories"] = (self.category.docname, "Categories")
for catname in ["author", "location", "language"]:
catalog = self.catalogs[catname]
items = self.config["blog_" + catname + "s"].items()
for label, (name, link) in items:
catalog[label] = Collection(catalog, label, name, link)
self.posts = self.blog["post"] = Collection(self.blog, "post", "Posts", path=self.blog_path)
self.drafts = self.blog["draft"] = Collection(
self.blog, "draft", "Drafts", path=os_path_join(self.blog_path, "drafts")
)
# add references to posts and drafts
# e.g. :ref:`blog-posts`
refs["blog-posts"] = (self.config["blog_path"], "Posts")
refs["blog-drafts"] = (os_path_join(self.config["blog_path"], "drafts"), "Drafts")
refs["blog-feed"] = (os_path_join(self.config["blog_path"], "atom.xml"), self.blog_title + " Feed")
# set some internal configuration options
self.config["fontawesome"] = (
self.config["fontawesome_included"]
or self.config["fontawesome_link_cdn"]
or self.config["fontawesome_css_file"]
)
def __getattr__(self, name):
try:
attr = self.config[name]
except KeyError:
raise AttributeError(f"ABlog has no configuration option {repr(name)}")
return attr
def __getitem__(self, key):
return self.posts[key] or self.drafts[key]
def __contains__(self, item):
return item in self.posts or item in self.drafts
def __len__(self):
return len(self.posts)
def __nonzero__(self):
return len(self) > 0
@property
def feed_path(self):
"""
RSS feed page name.
"""
return os_path_join(self.blog_path, "atom.xml")
def register(self, docname, info):
"""
Register post *docname*.
"""
post = Post(self, docname, info)
if post.date and post.date < TOMORROW:
self.posts.add(post)
else:
self.drafts.add(post)
for catalog in self.catalogs.values():
catalog.add(post)
def recent(self, num, docname=None, **labels):
"""
Yield *num* recent posts, excluding the one with `docname`.
"""
if num is None:
num = len(self)
for i, post in enumerate(self.posts):
if post.docname == docname:
num += 1
continue
if i == num:
return
yield post
def page_id(self, pagename):
"""
Return pagename, trimming :file:`index` from end when found.
Return value is used as disqus page identifier.
"""
if self.config["blog_baseurl"]:
if pagename.endswith("index"):
pagename = pagename[:-5]
pagename = pagename.strip("/")
return "/" + pagename + ("/" if pagename else "")
def page_url(self, pagename):
"""
Return page URL when :confval:`blog_baseurl` is set, otherwise
``None``.
When found, :file:`index.html` is trimmed from the end of the
URL.
"""
if self.config["blog_baseurl"]:
url = urljoin(self.config["blog_baseurl"], pagename)
if url.endswith("index"):
url = url[:-5]
return url
def html_builder_write_doc(self, docname, doctree, img_url=False):
"""
Part of :meth:`sphinx.builders.html.StandaloneHTMLBuilder.write_doc` method
used to convert *doctree* to HTML.
Extra argument `img_url` enables conversion of `<img>` source paths to
fully qualified URLs based on `blog_baseurl`.
"""
# Source of images
img_folder = "_images"
if img_url and self.config["blog_baseurl"]:
img_src_path = urljoin(self.config["blog_baseurl"], img_folder)
else:
img_src_path = relative_uri(self.get_target_uri(docname), img_folder)
destination = StringOutput(encoding="utf-8")
doctree.settings = self.docsettings
self.secnumbers = {}
self.imgpath = img_src_path
self.dlpath = relative_uri(self.get_target_uri(docname), "_downloads")
self.current_docname = docname
self.docwriter.write(doctree, destination)
self.docwriter.assemble_parts()
return self.docwriter.parts["fragment"]
class BlogPageMixin:
def __str__(self):
return self.title
def __repr__(self):
return str(self) + " <" + str(self.docname) + ">"
@property
def blog(self):
"""
Reference to :class:`~ablog.blog.Blog` object.
"""
return self._blog
@property
def title(self):
return getattr(self, "name", getattr(self, "_title"))
class Post(BlogPageMixin):
"""
Handle post metadata.
"""
def __init__(self, blog, docname, info):
self._blog = blog
self.docname = docname
self.section = info["section"]
self.order = info["order"]
self.date = date = info["date"]
self.update = info["update"]
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"]
self.doctree = info["doctree"]
self._next = self._prev = -1
self._computed_date = date or FUTURE
# archives
if self.published:
self.tags = info.get("tags")
self.author = info.get("author")
self.category = info.get("category")
self.location = info.get("location")
self.language = info.get("language")
if not self.author and blog.blog_default_author:
self.author = blog.blog_default_author
if not self.location and blog.blog_default_location:
self.location = blog.blog_default_location
if not self.language and blog.blog_default_language:
self.language = blog.blog_default_language
self.archive = self._blog.archive[self.date.year]
self.archive.add(self)
else:
self.tags = info.get("tags")
self.author = info.get("author")
self.category = info.get("category")
self.location = info.get("location")
self.language = info.get("language")
self.archive = []
self.redirect = info.get("redirect")
self.options = info
def __lt__(self, other):
return (self._computed_date, self.title) < (other._computed_date, other.title)
def to_html(self, pagename, fulltext=False, drop_h1=True, img_url=False):
"""
Return excerpt or *fulltext* as HTML after resolving references with
respect to *pagename*.
By default, first `<h1>` tag is dropped from the output. More
than one can be dropped by setting *drop_h1* to the desired
number of tags to be dropped.
`img_url` enables conversion of `<img>` source paths to fully
qualified URLs based on `blog_baseurl`.
"""
doctree = new_document("")
if fulltext:
deepcopy = self.doctree.deepcopy()
if isinstance(deepcopy, nodes.document):
doctree.extend(deepcopy.children)
else:
doctree.append(deepcopy)
else:
excerpt_container = nodes.paragraph()
excerpt_container.attributes["classes"].append("ablog-post-excerpt")
for node in self.excerpt:
excerpt_container.append(node.deepcopy())
doctree.append(excerpt_container)
app = self._blog.app
revise_pending_xrefs(doctree, pagename)
app.env.resolve_references(doctree, pagename, app.builder)
add_permalinks, app.builder.add_permalinks = (app.builder.add_permalinks, False)
html = html_builder_write_doc(app.builder, pagename, doctree, img_url=img_url)
app.builder.add_permalinks = add_permalinks
if drop_h1:
html = re.sub("<h1>(.*?)</h1>", "", html, count=abs(int(drop_h1)))
return html
@property
def next(self):
"""
Next published post in chronological order.
"""
if self._next == -1:
link_posts(self._blog.posts)
return self._next
@next.setter
def next(self, post):
"""
Set next published post in chronological order.
"""
self._next = post
@property
def prev(self):
"""
Previous published post in chronological order.
"""
if self._prev == -1:
link_posts(self._blog.posts)
return self._prev
@prev.setter
def prev(self, post):
"""
Set previous published post in chronological order.
"""
self._prev = post
class Catalog(BlogPageMixin):
"""
Handles collections of posts.
"""
def __init__(self, blog, name, xref, path, reverse=False):
self._blog = blog
self.name = name
self.xref = xref # for creating labels, e.g. `tag-python`
self.collections = {}
if path:
self.path = self.docname = os_path_join(blog.blog_path, path)
else:
self.path = self.docname = blog.blog_path
self._coll_lens = None
self._min_max = None
self._reverse = reverse
def __str__(self):
return str(self.name)
def __getitem__(self, name):
try:
return self.collections[name]
except KeyError:
return self.collections.setdefault(name, Collection(self, name))
def __setitem__(self, name, item):
self.collections[name] = item
def __len__(self):
return sum(len(coll) for coll in self)
def __nonzero__(self):
return len(self) > 0
def __iter__(self):
keys = list(self.collections)
keys.sort(reverse=self._reverse)
for key in keys:
yield self.collections[key]
def add(self, post):
"""
Add post to appropriate collection(s) and replace collections labels
with collection objects.
"""
colls = []
for label in getattr(post, self.name, []):
coll = self[label]
if post.published:
coll.add(post)
colls.append(coll)
setattr(post, self.name, colls)
def _minmax(self):
"""
Return minimum and maximum sizes of collections.
"""
if self._coll_lens is None or len(self._coll_lens) != len(self.collections):
self._coll_lens = [len(coll) for coll in self.collections.values() if len(coll)]
self._min_max = min(self._coll_lens), max(self._coll_lens)
return self._min_max
class Collection(BlogPageMixin):
"""
Posts sharing a label, i.e. tag, category, author, or location.
"""
def __init__(self, catalog, label, name=None, href=None, path=None, page=0):
self._catalog = catalog
self._blog = catalog.blog
self.label = label
self.name = name or self.label
self.href = href
self.page = page
self._posts = {}
self._posts_iter = None
self._path = path
self.xref = self.catalog.xref + "-" + slugify(label)
self._slug = None
self._html = None
self._catalog.blog.references[self.xref] = (self.docname, self.name)
def __str__(self):
return str(self.name)
def __len__(self):
return len(self._posts)
def __nonzero__(self):
return len(self) > 0
def __unicode__(self):
return str(self.name)
def __iter__(self):
if self._posts_iter is None:
posts = list(self._posts.values())
posts.sort(reverse=True)
self._posts_iter = posts
yield from self._posts_iter
def __getitem__(self, key):
return self._posts.get(key)
def __contains__(self, item):
return item in self._posts
def __eq__(self, other):
return self.name == other.name
def __lt__(self, other):
return self.name < other.name
def __gt__(self, other):
return self.name > other.name
@property
def catalog(self):
"""
:class:`~ablog.blog.Catalog` that the collection belongs to.
"""
return self._catalog
def add(self, post):
"""
Add post to the collection.
"""
post_name = post.docname
if post.section:
post_name += "#" + post.section
self._posts[post_name] = post
def relsize(self, maxsize=5, minsize=1):
"""
Relative size used in tag clouds.
"""
min_, max_ = self.catalog._minmax()
diff = maxsize - minsize
if len(self.catalog) == 1 or min_ == max_:
return int(round(diff / 2.0 + minsize))
size = int(1.0 * (len(self) - min_) / (max_ - min_) * diff + minsize)
return size
@property
def docname(self):
"""
Collection page document name.
"""
if self._path is None:
self._path = os_path_join(self.catalog.path, slugify(self.name))
return self._path
path = docname

469
src/ablog/commands.py Normal file
View file

@ -0,0 +1,469 @@
import os
import sys
import glob
import shutil
import argparse
import webbrowser
import socketserver
from os import path
from http import server
from os.path import join, isfile, abspath
from datetime import date
from invoke import run
from watchdog.observers import Observer
from watchdog.tricks import ShellCommandTrick
import ablog
from ablog.start import ablog_start
__all__ = ["ablog_build", "ablog_clean", "ablog_serve", "ablog_deploy", "ablog_main"]
BUILDDIR = "_website"
DOCTREES = ".doctrees"
POST_TEMPLATE = """
{title}
{equal}
.. post:: {date}
:tags:
:category:
"""
def find_confdir(sourcedir=None):
"""
Return path to current directory or its parent that contains conf.py.
"""
confdir = sourcedir or os.getcwd()
def parent(d):
return abspath(join(d, ".."))
while not isfile(join(confdir, "conf.py")) and confdir != parent(confdir):
confdir = parent(confdir)
conf = join(confdir, "conf.py")
if isfile(conf) and "ablog" in open(conf).read():
return confdir
else:
sys.exit("Current directory and its parents doesn't " "contain configuration file (conf.py).")
def read_conf(confdir):
"""
Return conf.py file as a module.
"""
sys.path.insert(0, confdir)
conf = __import__("conf")
sys.path.pop(0)
return conf
parser = argparse.ArgumentParser(
description="ABlog for blogging with Sphinx",
epilog="See 'ablog <command> -h' for more information on a specific " "command.",
)
parser.add_argument("-v", "--version", help="print ABlog version and exit", action="version", version=ablog.__version__)
commands = ablog_commands = parser.add_subparsers(title="commands")
def cmd(func=None, **kwargs):
if func is None:
def cmd_inner(func):
return cmd(func, **kwargs)
return cmd_inner
else:
command = commands.add_parser(**kwargs)
command.set_defaults(func=func)
command.set_defaults(subparser=command)
func.command = command
return func
def arg(*args, **kwargs):
if args and callable(args[0]):
func = args[0]
args = args[1:]
else:
func = None
if func is None:
def arg_inner(func):
return arg(func, *args, **kwargs)
return arg_inner
else:
func.command.add_argument(*args, **kwargs)
return func
def arg_website(func):
arg(
func,
"-w",
dest="website",
type=str,
help=f"path for website, default is {BUILDDIR} when `ablog_website` is not set in conf.py",
)
return func
def arg_doctrees(func):
arg(
func,
"-d",
dest="doctrees",
type=str,
help="path for the cached environment and doctree files, "
f"default {DOCTREES} when `ablog_doctrees` is not set in conf.py",
)
return func
cmd(
ablog_start,
name="start",
help="start a new blog project",
description="Start a new blog project by answering a few questions. "
"You will end up with a configuration file and sample pages.",
)
@arg("-P", dest="runpdb", action="store_true", default=False, help="run pdb on exception")
@arg("-T", dest="traceback", action="store_true", default=False, help="show full traceback on exception")
@arg("-W", dest="werror", action="store_true", default=False, help="turn warnings into errors")
@arg("-N", dest="no_colors", action="store_true", default=False, help="do not emit colored output")
@arg("-Q", dest="extra_quiet", action="store_true", default=False, help="no output at all, not even warnings")
@arg(
"-q",
dest="quiet",
action="store_true",
default=False,
help="no output on stdout, just warnings on stderr",
)
@arg("-v", dest="verbosity", action="count", default=0, help="increase verbosity (can be repeated)")
@arg_doctrees
@arg_website
@arg(
"-s",
dest="sourcedir",
type=str,
help="root path for source files, " "default is path to the folder that contains conf.py",
)
@arg("-b", dest="builder", type=str, help="builder to use, default `ablog_builder` or dirhtml")
@arg(
"-a",
dest="allfiles",
action="store_true",
default=False,
help="write all files; default is to only write new and changed files",
)
@cmd(
name="build",
help="build your blog project",
description="Path options can be set in conf.py. " "Default values of paths are relative to conf.py.",
)
def ablog_build(
builder=None,
sourcedir=None,
website=None,
doctrees=None,
traceback=False,
runpdb=False,
allfiles=False,
werror=False,
verbosity=0,
quiet=False,
extra_quiet=False,
no_colors=False,
**kwargs,
):
confdir = find_confdir(sourcedir)
conf = read_conf(confdir)
website = website or os.path.join(confdir, getattr(conf, "ablog_website", BUILDDIR))
doctrees = doctrees or os.path.join(confdir, getattr(conf, "ablog_doctrees", DOCTREES))
sourcedir = sourcedir or confdir
argv = sys.argv[:1]
argv.extend(["-b", builder or getattr(conf, "ablog_builder", "dirhtml")])
argv.extend(["-d", doctrees])
if traceback:
argv.extend(["-T"])
if runpdb:
argv.extend(["-P"])
if allfiles:
argv.extend(["-a"])
if werror:
argv.extend(["-W"])
if verbosity > 0:
argv.extend(["-v"] * verbosity)
if quiet:
argv.extend(["-q"])
if extra_quiet:
argv.extend(["-Q"])
if no_colors:
argv.extend(["-N"])
argv.extend([sourcedir, website])
from sphinx.cmd.build import main
sys.exit(main(argv[1:]))
@arg(
"-D",
dest="deep",
action="store_true",
default=False,
help="deep clean, remove cached environment and doctree files",
)
@arg_doctrees
@arg_website
@cmd(
name="clean",
help="clean your blog build files",
description="Path options can be set in conf.py. " "Default values of paths are relative to conf.py.",
)
def ablog_clean(website=None, doctrees=None, deep=False, **kwargs):
confdir = find_confdir()
conf = read_conf(confdir)
website = website or os.path.join(confdir, getattr(conf, "ablog_website", BUILDDIR))
doctrees = doctrees or os.path.join(confdir, getattr(conf, "ablog_doctrees", DOCTREES))
nothing = True
if glob.glob(os.path.join(website, "*")):
shutil.rmtree(website)
print(f"Removed {os.path.relpath(website)}.")
nothing = False
if deep and glob.glob(os.path.join(doctrees, "*")):
shutil.rmtree(doctrees)
print(f"Removed {os.path.relpath(doctrees)}.")
nothing = False
if nothing:
print("Nothing to clean.")
@arg("--patterns", dest="patterns", default="*.rst;*.txt", help="patterns for triggering rebuilds")
@arg(
"-r",
dest="rebuild",
action="store_true",
default=False,
help="rebuild when a file matching patterns change or get added",
)
@arg("-n", dest="view", action="store_false", default=True, help="do not open website in a new browser tab")
@arg("-p", dest="port", type=int, default=8000, help="port number for HTTP server; default is 8000")
@arg_website
@cmd(
name="serve",
help="serve and view your project",
description="Serve options can be set in conf.py. " "Default values of paths are relative to conf.py.",
)
def ablog_serve(website=None, port=8000, view=True, rebuild=False, patterns="*.rst;*.txt", **kwargs):
confdir = find_confdir()
conf = read_conf(confdir)
# to allow restarting the server in short succession
socketserver.TCPServer.allow_reuse_address = True
Handler = server.SimpleHTTPRequestHandler
httpd = socketserver.TCPServer(("", port), Handler)
ip, port = httpd.socket.getsockname()
print(f"Serving HTTP on {ip}:{port}.")
print("Quit the server with Control-C.")
website = website or os.path.join(confdir, getattr(conf, "ablog_website", "_website"))
os.chdir(website)
if rebuild:
patterns = patterns.split(";")
ignore_patterns = [os.path.join(website, "*")]
handler = ShellCommandTrick(
shell_command="ablog build -s " + confdir,
patterns=patterns,
ignore_patterns=ignore_patterns,
ignore_directories=False,
wait_for_process=True,
drop_during_process=False,
)
observer = Observer(timeout=1)
observer.schedule(handler, confdir, recursive=True)
observer.start()
try:
if view:
webbrowser.open_new_tab(f"http://127.0.0.1:{port}") and httpd.serve_forever()
else:
httpd.serve_forever()
except KeyboardInterrupt:
observer.stop()
observer.join()
else:
if view:
webbrowser.open_new_tab(f"http://127.0.0.1:{port}") and httpd.serve_forever()
else:
httpd.serve_forever()
@arg("-t", dest="title", type=str, help="post title; default is formed from filename")
@arg(dest="filename", type=str, help="filename, e.g. my-nth-post (.rst appended)")
@cmd(name="post", help="create a blank post")
def ablog_post(filename, title=None, **kwargs):
# Generate basic post params.
today = date.today()
if not filename.lower().endswith(".rst"):
filename += ".rst"
today = today.strftime("%b %d, %Y")
if not title:
title = filename[:-4].replace("-", " ").title()
pars = {"date": today, "title": title, "equal": "=" * len(title)}
if path.isfile(filename):
pass
else:
# read the file, and add post directive
# and save it
with open(filename, "w", encoding="utf-8") as out:
post_text = POST_TEMPLATE.format(**pars)
out.write(post_text)
print(f"Blog post created: {filename}")
@arg(
"--github-url",
dest="github_url",
type=str,
default="git@github.com",
help="Custom GitHub URL. Useful when multiple accounts are configured "
"on the same machine. Default is: git@github.com",
)
@arg(
"--github-token",
dest="github_token",
type=str,
help="environment variable name storing GitHub access token",
)
@arg("--github-ssh", dest="github_is_http", action="store_true", help="use ssh when cloning website")
@arg(
"--github-branch",
dest="github_branch",
type=str,
help="Branch to use. Default is: master.",
default="master",
)
@arg(
"--push-quietly",
dest="push_quietly",
action="store_true",
default=False,
help="be more quiet when pushing changes",
)
@arg(
"-f",
dest="push_force",
action="store_true",
default=False,
help="overwrite last commit, i.e. `commit --amend; push -f`",
)
@arg("-m", dest="message", type=str, help="commit message")
@arg("-g", dest="github_pages", type=str, help="GitHub username for deploying to GitHub pages")
@arg(
"-p",
dest="repodir",
type=str,
help="path to the location of repository to be deployed, e.g. "
"`../username.github.io`, default is folder containing `conf.py`",
)
@arg_website
@cmd(
name="deploy",
help="deploy your website build files",
description="Path options can be set in conf.py. " "Default values of paths are relative to conf.py.",
)
def ablog_deploy(
website,
message=None,
github_pages=None,
push_quietly=False,
push_force=False,
github_token=None,
github_is_http=True,
github_url=None,
github_branch=None,
repodir=None,
**kwargs,
):
confdir = find_confdir()
conf = read_conf(confdir)
github_pages = github_pages or getattr(conf, "github_pages", None)
github_url = github_url or getattr(conf, "github_url", None)
github_url += ":"
github_branch = github_branch or getattr(conf, "github_branch", "master")
website = website or os.path.join(confdir, getattr(conf, "ablog_builddir", "_website"))
tomove = glob.glob(os.path.join(website, "*"))
if not tomove:
print("Nothing to deploy, build first.")
return
if github_pages:
if repodir is None:
repodir = os.path.join(confdir, f"{github_pages}.github.io")
if os.path.isdir(repodir):
os.chdir(repodir)
run("git pull", echo=True)
else:
run(
"git clone "
+ ("https://github.com/" if github_is_http else github_url)
+ "{0}/{0}.github.io.git {1}".format(github_pages, repodir),
echo=True,
)
git_add = []
for tm in tomove:
for root, dirnames, filenames in os.walk(website):
for filename in filenames:
fn = os.path.join(root, filename)
fnnew = fn.replace(website, repodir)
try:
os.renames(fn, fnnew)
except OSError:
if os.path.isdir(fnnew):
shutil.rmtree(fnnew)
else:
os.remove(fnnew)
os.renames(fn, fnnew)
git_add.append(fnnew)
print(f"Moved {len(git_add)} files to {github_pages}.github.io")
os.chdir(repodir)
run("git add -f " + " ".join(['"{}"'.format(os.path.relpath(p)) for p in git_add]), echo=True)
if not os.path.isfile(".nojekyll"):
open(".nojekyll", "w")
run("git add -f .nojekyll")
# Check to see if anything has actually been committed
result = run("git diff --cached --name-status HEAD")
if not result.stdout:
print("Nothing changed from last deployment")
return
commit = f"git commit -m \"{message or 'Updates.'}\""
if push_force:
commit += " --amend"
run(commit, echo=True)
if github_token:
with open(os.path.join(repodir, ".git/credentials"), "w") as out:
out.write(f"https://{os.environ[github_token]}:@github.com")
run('git config credential.helper "store --file=.git/credentials"')
push = "git push"
if push_quietly:
push += " -q"
if push_force:
push += " -f"
push += f" origin {github_branch}"
run(push, echo=True)
else:
print("No place to deploy.")
def ablog_main():
"""
Ablog Main.
"""
if len(sys.argv) == 1:
parser.print_help()
else:
namespace = parser.parse_args()
namespace.func(**namespace.__dict__)

Binary file not shown.

View file

@ -0,0 +1,129 @@
# Catalan translations for ablog.
# Copyright (C) 2021 ORGANIZATION
# This file is distributed under the same license as the ablog project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: ablog 0.10.12\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-11-14 16:46-0800\n"
"PO-Revision-Date: 2021-09-18 18:57+0200\n"
"Last-Translator: Francesc Vilardell Sallés <vilardellsalles@gmail.com>\n"
"Language: ca\n"
"Language-Team: ca <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.11.0\n"
#: ablog/post.py:272
msgid "Updated on "
msgstr "Actualitzat el "
#: ablog/post.py:564 ablog/templates/ablog/authors.html:3
#: ablog/templates/authors.html:4
msgid "Authors"
msgstr "Autors"
#: ablog/post.py:564 ablog/post.py:631
msgid "Posts by"
msgstr "Publicacions de"
#: ablog/post.py:565 ablog/templates/ablog/locations.html:4
#: ablog/templates/locations.html:5
msgid "Locations"
msgstr "Ubicacions"
#: ablog/post.py:565 ablog/post.py:632
msgid "Posts from"
msgstr "Publicacions des de"
#: ablog/post.py:566 ablog/templates/ablog/languages.html:4
#: ablog/templates/languages.html:5
msgid "Languages"
msgstr "Idiomes"
#: ablog/post.py:566 ablog/post.py:567 ablog/post.py:633 ablog/post.py:634
msgid "Posts in"
msgstr "Publicacions en"
#: ablog/post.py:567 ablog/templates/ablog/categories.html:4
#: ablog/templates/categories.html:5
msgid "Categories"
msgstr "Categories"
#: ablog/post.py:568
msgid "All posts"
msgstr "Totes les publicacions"
#: ablog/post.py:568 ablog/post.py:635
msgid "Posted in"
msgstr "Publicat al"
#: 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 "Etiquetes"
#: ablog/post.py:569 ablog/post.py:636
msgid "Posts tagged"
msgstr "Publicacions amb etiqueta"
#: ablog/post.py:594
msgid "All Posts"
msgstr "Totes les publicacions"
#: ablog/post.py:595
msgid "All"
msgstr "Totes les"
#: ablog/post.py:603
msgid "Drafts"
msgstr "Borradors"
#: ablog/templates/ablog/archives.html:4 ablog/templates/archives.html:5
msgid "Archives"
msgstr "Arxius"
#: ablog/templates/ablog/collection.html:56 ablog/templates/collection.html:57
msgid "Read more ..."
msgstr "Llegir-ne més ..."
#: ablog/templates/ablog/postcard2.html:8 ablog/templates/postcard2.html:9
msgid "Update"
msgstr "Actualització"
#: ablog/templates/ablog/postcard2.html:20 ablog/templates/postcard2.html:21
msgid "Author"
msgstr "Autor"
#: ablog/templates/ablog/postcard2.html:44 ablog/templates/postcard2.html:45
msgid "Location"
msgstr "Ubicació"
#: ablog/templates/ablog/postcard2.html:68 ablog/templates/postcard2.html:69
msgid "Language"
msgstr "Idioma"
#: 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 "Etiqueta"
#: ablog/templates/ablog/postnavy.html:8 ablog/templates/postnavy.html:9
msgid "Previous"
msgstr "Anterior"
#: ablog/templates/ablog/postnavy.html:22 ablog/templates/postnavy.html:23
msgid "Next"
msgstr "Següent"
#: ablog/templates/ablog/recentposts.html:4 ablog/templates/recentposts.html:5
msgid "Recent Posts"
msgstr "Publicacions recents"

Binary file not shown.

View file

@ -0,0 +1,131 @@
# German translations for ablog.
# Copyright (C) 2014 ORGANIZATION
# This file is distributed under the same license as the ablog project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: ablog 0.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-11-14 16:46-0800\n"
"PO-Revision-Date: 2014-07-25 20:46+0300\n"
"Last-Translator: Luc Saffre <luc.saffre@gmx.net>\n"
"Language: de\n"
"Language-Team: de <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.11.0\n"
#: ablog/post.py:272
#, fuzzy
msgid "Updated on "
msgstr "Aktualisiert am"
#: ablog/post.py:564 ablog/templates/ablog/authors.html:3
#: ablog/templates/authors.html:4
msgid "Authors"
msgstr "Autoren"
#: ablog/post.py:564 ablog/post.py:631
msgid "Posts by"
msgstr "Einträge von"
#: ablog/post.py:565 ablog/templates/ablog/locations.html:4
#: ablog/templates/locations.html:5
msgid "Locations"
msgstr "Orte"
#: ablog/post.py:565 ablog/post.py:632
msgid "Posts from"
msgstr "Einträge aus"
#: ablog/post.py:566 ablog/templates/ablog/languages.html:4
#: ablog/templates/languages.html:5
msgid "Languages"
msgstr "Sprachen"
#: ablog/post.py:566 ablog/post.py:567 ablog/post.py:633 ablog/post.py:634
msgid "Posts in"
msgstr "Einträge in"
#: ablog/post.py:567 ablog/templates/ablog/categories.html:4
#: ablog/templates/categories.html:5
msgid "Categories"
msgstr "Kategorien"
#: ablog/post.py:568
msgid "All posts"
msgstr "Alle Einträge"
#: ablog/post.py:568 ablog/post.py:635
msgid "Posted in"
msgstr "Einträge 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 "Schlagworte"
#: ablog/post.py:569 ablog/post.py:636
msgid "Posts tagged"
msgstr "Einträge mit Schlagwort"
#: ablog/post.py:594
#, fuzzy
msgid "All Posts"
msgstr "Alle Einträge"
#: ablog/post.py:595
msgid "All"
msgstr ""
#: ablog/post.py:603
msgid "Drafts"
msgstr "Entwurf"
#: ablog/templates/ablog/archives.html:4 ablog/templates/archives.html:5
msgid "Archives"
msgstr "Archive"
#: ablog/templates/ablog/collection.html:56 ablog/templates/collection.html:57
msgid "Read more ..."
msgstr "Weiter..."
#: ablog/templates/ablog/postcard2.html:8 ablog/templates/postcard2.html:9
msgid "Update"
msgstr "Aktualisierung"
#: ablog/templates/ablog/postcard2.html:20 ablog/templates/postcard2.html:21
msgid "Author"
msgstr "Autor"
#: ablog/templates/ablog/postcard2.html:44 ablog/templates/postcard2.html:45
msgid "Location"
msgstr "Ort"
#: ablog/templates/ablog/postcard2.html:68 ablog/templates/postcard2.html:69
msgid "Language"
msgstr "Sprache"
#: ablog/templates/ablog/postcard2.html:92 ablog/templates/postcard2.html:93
msgid "Category"
msgstr "Kategorie"
#: ablog/templates/ablog/postcard2.html:123 ablog/templates/postcard2.html:124
msgid "Tag"
msgstr "Schlagwort"
#: ablog/templates/ablog/postnavy.html:8 ablog/templates/postnavy.html:9
msgid "Previous"
msgstr "Vorige"
#: ablog/templates/ablog/postnavy.html:22 ablog/templates/postnavy.html:23
msgid "Next"
msgstr "Nächste"
#: ablog/templates/ablog/recentposts.html:4 ablog/templates/recentposts.html:5
msgid "Recent Posts"
msgstr "Neue Einträge"

Binary file not shown.

View file

@ -0,0 +1,131 @@
# Spanish translations for ablog.
# Copyright (C) 2014 ORGANIZATION
# This file is distributed under the same license as the ablog project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: ablog 0.2.3\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-11-14 16:46-0800\n"
"PO-Revision-Date: 2016-04-21 14:26+0200\n"
"Last-Translator: José Carlos García <jcg@quobit.net>\n"
"Language: es\n"
"Language-Team: es <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.11.0\n"
#: ablog/post.py:272
#, fuzzy
msgid "Updated on "
msgstr "Actualizado el"
#: ablog/post.py:564 ablog/templates/ablog/authors.html:3
#: ablog/templates/authors.html:4
msgid "Authors"
msgstr "Autores"
#: ablog/post.py:564 ablog/post.py:631
msgid "Posts by"
msgstr "Entradas por"
#: ablog/post.py:565 ablog/templates/ablog/locations.html:4
#: ablog/templates/locations.html:5
msgid "Locations"
msgstr "Lugares"
#: ablog/post.py:565 ablog/post.py:632
msgid "Posts from"
msgstr "Entradas desde"
#: ablog/post.py:566 ablog/templates/ablog/languages.html:4
#: ablog/templates/languages.html:5
msgid "Languages"
msgstr "Idiomas"
#: ablog/post.py:566 ablog/post.py:567 ablog/post.py:633 ablog/post.py:634
msgid "Posts in"
msgstr "Entradas en"
#: ablog/post.py:567 ablog/templates/ablog/categories.html:4
#: ablog/templates/categories.html:5
msgid "Categories"
msgstr "Categorías"
#: ablog/post.py:568
msgid "All posts"
msgstr "Todas las entradas"
#: ablog/post.py:568 ablog/post.py:635
msgid "Posted in"
msgstr "Publicado en"
#: 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 "Etiquetas"
#: ablog/post.py:569 ablog/post.py:636
msgid "Posts tagged"
msgstr "Entradas etiquetadas"
#: ablog/post.py:594
#, fuzzy
msgid "All Posts"
msgstr "Todas las entradas"
#: ablog/post.py:595
msgid "All"
msgstr ""
#: ablog/post.py:603
msgid "Drafts"
msgstr "Borradores"
#: ablog/templates/ablog/archives.html:4 ablog/templates/archives.html:5
msgid "Archives"
msgstr "Archivos"
#: ablog/templates/ablog/collection.html:56 ablog/templates/collection.html:57
msgid "Read more ..."
msgstr "Leer más ..."
#: ablog/templates/ablog/postcard2.html:8 ablog/templates/postcard2.html:9
msgid "Update"
msgstr "Actualizado"
#: ablog/templates/ablog/postcard2.html:20 ablog/templates/postcard2.html:21
msgid "Author"
msgstr "Autor"
#: ablog/templates/ablog/postcard2.html:44 ablog/templates/postcard2.html:45
msgid "Location"
msgstr "Lugar"
#: ablog/templates/ablog/postcard2.html:68 ablog/templates/postcard2.html:69
msgid "Language"
msgstr "Idioma"
#: ablog/templates/ablog/postcard2.html:92 ablog/templates/postcard2.html:93
msgid "Category"
msgstr "Categoría"
#: ablog/templates/ablog/postcard2.html:123 ablog/templates/postcard2.html:124
msgid "Tag"
msgstr "Etiqueta"
#: ablog/templates/ablog/postnavy.html:8 ablog/templates/postnavy.html:9
msgid "Previous"
msgstr "Anterior"
#: ablog/templates/ablog/postnavy.html:22 ablog/templates/postnavy.html:23
msgid "Next"
msgstr "Siguiente"
#: ablog/templates/ablog/recentposts.html:4 ablog/templates/recentposts.html:5
msgid "Recent Posts"
msgstr "Entradas recientes"

Binary file not shown.

View file

@ -0,0 +1,131 @@
# Estonian translations for ablog.
# Copyright (C) 2014 ORGANIZATION
# This file is distributed under the same license as the ablog project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: ablog 0.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-11-14 16:46-0800\n"
"PO-Revision-Date: 2014-07-31 09:13+0300\n"
"Last-Translator: Luc Saffre <luc.saffre@gmx.net>\n"
"Language: et\n"
"Language-Team: et <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.11.0\n"
#: ablog/post.py:272
#, fuzzy
msgid "Updated on "
msgstr "Uuendus"
#: ablog/post.py:564 ablog/templates/ablog/authors.html:3
#: ablog/templates/authors.html:4
msgid "Authors"
msgstr "Autorid"
#: ablog/post.py:564 ablog/post.py:631
msgid "Posts by"
msgstr "Postitused autorilt"
#: ablog/post.py:565 ablog/templates/ablog/locations.html:4
#: ablog/templates/locations.html:5
msgid "Locations"
msgstr "Kohad"
#: ablog/post.py:565 ablog/post.py:632
msgid "Posts from"
msgstr ""
#: ablog/post.py:566 ablog/templates/ablog/languages.html:4
#: ablog/templates/languages.html:5
msgid "Languages"
msgstr "Keeltes"
#: ablog/post.py:566 ablog/post.py:567 ablog/post.py:633 ablog/post.py:634
msgid "Posts in"
msgstr ""
#: ablog/post.py:567 ablog/templates/ablog/categories.html:4
#: ablog/templates/categories.html:5
msgid "Categories"
msgstr "Kategooriad"
#: ablog/post.py:568
msgid "All posts"
msgstr "Kõik postitused"
#: ablog/post.py:568 ablog/post.py:635
msgid "Posted in"
msgstr "Postitused kategoorias"
#: 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 "Märksõnad"
#: ablog/post.py:569 ablog/post.py:636
msgid "Posts tagged"
msgstr "Postitused märksõnaga"
#: ablog/post.py:594
#, fuzzy
msgid "All Posts"
msgstr "Kõik postitused"
#: ablog/post.py:595
msgid "All"
msgstr ""
#: ablog/post.py:603
msgid "Drafts"
msgstr "Eelnõu"
#: ablog/templates/ablog/archives.html:4 ablog/templates/archives.html:5
msgid "Archives"
msgstr "Arhiiv"
#: ablog/templates/ablog/collection.html:56 ablog/templates/collection.html:57
msgid "Read more ..."
msgstr "Edasi..."
#: ablog/templates/ablog/postcard2.html:8 ablog/templates/postcard2.html:9
msgid "Update"
msgstr "Ajakohastama"
#: ablog/templates/ablog/postcard2.html:20 ablog/templates/postcard2.html:21
msgid "Author"
msgstr "Autor"
#: ablog/templates/ablog/postcard2.html:44 ablog/templates/postcard2.html:45
msgid "Location"
msgstr "Koht"
#: ablog/templates/ablog/postcard2.html:68 ablog/templates/postcard2.html:69
msgid "Language"
msgstr "Keel"
#: ablog/templates/ablog/postcard2.html:92 ablog/templates/postcard2.html:93
msgid "Category"
msgstr "Kategooria"
#: ablog/templates/ablog/postcard2.html:123 ablog/templates/postcard2.html:124
msgid "Tag"
msgstr "Märksõna"
#: ablog/templates/ablog/postnavy.html:8 ablog/templates/postnavy.html:9
msgid "Previous"
msgstr "Eelmine"
#: ablog/templates/ablog/postnavy.html:22 ablog/templates/postnavy.html:23
msgid "Next"
msgstr "Järgmine"
#: ablog/templates/ablog/recentposts.html:4 ablog/templates/recentposts.html:5
msgid "Recent Posts"
msgstr "Viimased postitused"

Binary file not shown.

View file

@ -0,0 +1,130 @@
# French translations for ablog.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the ablog project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
#
msgid ""
msgstr ""
"Project-Id-Version: ablog 0.10.5\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-11-14 16:46-0800\n"
"PO-Revision-Date: 2020-05-22 20:04+0200\n"
"Last-Translator: \n"
"Language: fr\n"
"Language-Team: fr <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.11.0\n"
#: ablog/post.py:272
#, fuzzy
msgid "Updated on "
msgstr "Mis à jour"
#: ablog/post.py:564 ablog/templates/ablog/authors.html:3
#: ablog/templates/authors.html:4
msgid "Authors"
msgstr "Auteurs"
#: ablog/post.py:564 ablog/post.py:631
msgid "Posts by"
msgstr "Billets par"
#: ablog/post.py:565 ablog/templates/ablog/locations.html:4
#: ablog/templates/locations.html:5
msgid "Locations"
msgstr "Lieux"
#: ablog/post.py:565 ablog/post.py:632
msgid "Posts from"
msgstr "Billets depuis"
#: ablog/post.py:566 ablog/templates/ablog/languages.html:4
#: ablog/templates/languages.html:5
msgid "Languages"
msgstr "Langues"
#: ablog/post.py:566 ablog/post.py:567 ablog/post.py:633 ablog/post.py:634
msgid "Posts in"
msgstr "Billets dans"
#: ablog/post.py:567 ablog/templates/ablog/categories.html:4
#: ablog/templates/categories.html:5
msgid "Categories"
msgstr "Catégories"
#: ablog/post.py:568
msgid "All posts"
msgstr "Tous les billets"
#: ablog/post.py:568 ablog/post.py:635
msgid "Posted in"
msgstr "Publié dans"
#: 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 "Tags"
#: ablog/post.py:569 ablog/post.py:636
msgid "Posts tagged"
msgstr "Billets tagués"
#: ablog/post.py:594
msgid "All Posts"
msgstr "Tous les billets"
#: ablog/post.py:595
msgid "All"
msgstr "Tous les billets"
#: ablog/post.py:603
msgid "Drafts"
msgstr "Brouillons"
#: ablog/templates/ablog/archives.html:4 ablog/templates/archives.html:5
msgid "Archives"
msgstr "Archives"
#: ablog/templates/ablog/collection.html:56 ablog/templates/collection.html:57
msgid "Read more ..."
msgstr "Lire plus…"
#: ablog/templates/ablog/postcard2.html:8 ablog/templates/postcard2.html:9
msgid "Update"
msgstr "Mis à jour"
#: ablog/templates/ablog/postcard2.html:20 ablog/templates/postcard2.html:21
msgid "Author"
msgstr "Auteur"
#: ablog/templates/ablog/postcard2.html:44 ablog/templates/postcard2.html:45
msgid "Location"
msgstr "Lieu"
#: ablog/templates/ablog/postcard2.html:68 ablog/templates/postcard2.html:69
msgid "Language"
msgstr "Langue"
#: ablog/templates/ablog/postcard2.html:92 ablog/templates/postcard2.html:93
msgid "Category"
msgstr "Catégorie"
#: 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 "Précédent"
#: ablog/templates/ablog/postnavy.html:22 ablog/templates/postnavy.html:23
msgid "Next"
msgstr "Suivant"
#: ablog/templates/ablog/recentposts.html:4 ablog/templates/recentposts.html:5
msgid "Recent Posts"
msgstr "Billets récents"

Binary file not shown.

View file

@ -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 <EMAIL@ADDRESS>, 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 <code@stefanodavid.eu>\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"

Binary file not shown.

View file

@ -0,0 +1,129 @@
# Portuguese translations for ablog.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the ablog project.
# Luís Henriques <henrix@camandro.org>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: ablog 0.10.30.dev7+g9a43710e3a7d\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-11-14 16:46-0800\n"
"PO-Revision-Date: 2022-10-02 20:41+0100\n"
"Last-Translator: Luís Henriques <henrix@camandro.org>\n"
"Language: pt\n"
"Language-Team: pt <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.11.0\n"
#: ablog/post.py:272
msgid "Updated on "
msgstr "Actualizado em "
#: ablog/post.py:564 ablog/templates/ablog/authors.html:3
#: ablog/templates/authors.html:4
msgid "Authors"
msgstr "Autores"
#: ablog/post.py:564 ablog/post.py:631
msgid "Posts by"
msgstr "Entradas por"
#: ablog/post.py:565 ablog/templates/ablog/locations.html:4
#: ablog/templates/locations.html:5
msgid "Locations"
msgstr "Localizações"
#: ablog/post.py:565 ablog/post.py:632
msgid "Posts from"
msgstr "Entradas de"
#: ablog/post.py:566 ablog/templates/ablog/languages.html:4
#: ablog/templates/languages.html:5
msgid "Languages"
msgstr "Linguagens"
#: ablog/post.py:566 ablog/post.py:567 ablog/post.py:633 ablog/post.py:634
msgid "Posts in"
msgstr "Entradas em"
#: ablog/post.py:567 ablog/templates/ablog/categories.html:4
#: ablog/templates/categories.html:5
msgid "Categories"
msgstr "Categorias"
#: ablog/post.py:568
msgid "All posts"
msgstr "Todas as entradas"
#: ablog/post.py:568 ablog/post.py:635
msgid "Posted in"
msgstr "Publicado em"
#: 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 "Etiquetas"
#: ablog/post.py:569 ablog/post.py:636
msgid "Posts tagged"
msgstr "Entradas com etiqueta"
#: ablog/post.py:594
msgid "All Posts"
msgstr "Todas as Entradas"
#: ablog/post.py:595
msgid "All"
msgstr "Todas"
#: ablog/post.py:603
msgid "Drafts"
msgstr "Rascunhos"
#: ablog/templates/ablog/archives.html:4 ablog/templates/archives.html:5
msgid "Archives"
msgstr "Arquivos"
#: ablog/templates/ablog/collection.html:56 ablog/templates/collection.html:57
msgid "Read more ..."
msgstr "Ler mais ..."
#: ablog/templates/ablog/postcard2.html:8 ablog/templates/postcard2.html:9
msgid "Update"
msgstr "Actualizado"
#: ablog/templates/ablog/postcard2.html:20 ablog/templates/postcard2.html:21
msgid "Author"
msgstr "Autor"
#: ablog/templates/ablog/postcard2.html:44 ablog/templates/postcard2.html:45
msgid "Location"
msgstr "Localização"
#: ablog/templates/ablog/postcard2.html:68 ablog/templates/postcard2.html:69
msgid "Language"
msgstr "Idioma"
#: 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 "Etiqueta"
#: ablog/templates/ablog/postnavy.html:8 ablog/templates/postnavy.html:9
msgid "Previous"
msgstr "Anterior"
#: ablog/templates/ablog/postnavy.html:22 ablog/templates/postnavy.html:23
msgid "Next"
msgstr "Próximo"
#: ablog/templates/ablog/recentposts.html:4 ablog/templates/recentposts.html:5
msgid "Recent Posts"
msgstr "Entradas Recentes"

Some files were not shown because too many files have changed in this diff Show more