:gh:`m.dox <mosra/m.css$master/pelican-plugins/m/dox.py>`,
:gh:`m.gl <mosra/m.css$master/pelican-plugins/m/gl.py>`,
:gh:`m.abbr <mosra/m.css$master/pelican-plugins/m/abbr.py>`,
- :gh:`m.filesize <mosra/m.css$master/pelican-plugins/m/filesize.py>`
+ :gh:`m.filesize <mosra/m.css$master/pelican-plugins/m/filesize.py>`,
+ :gh:`m.alias <mosra/m.css$master/pelican-plugins/m/alias.py>`
- :gh:`m.metadata <mosra/m.css$master/pelican-plugins/m/metadata.py>`
Click on the headings below to get to know more. Note that particular plugins
that you can but don't have to use. With these, math and code snippets can be
entered directly in your :abbr:`reST <reStructuredText>` sources.
-`Links » <{filename}/plugins/links.rst>`_
-=========================================
+`Links and other » <{filename}/plugins/links.rst>`_
+===================================================
-The :py:`m.gh`, :py:`m.dox`, :py:`m.gl`, :py:`m.abbr` and :py:`m.fiilesize`
-plugins make it easy for you to link to GitHub projects, issues or PRs, to
-Doxygen documentation and do more useful things.
+The :py:`m.gh`, :py:`m.dox`, :py:`m.gl`, :py:`m.abbr`, :py:`m.fiilesize` and
+:py:`m.alias` plugins make it easy for you to link to GitHub projects, issues
+or PRs, to Doxygen documentation, query file sizes and provide URL aliases to
+preserve link compatibility.
`Metadata » <{filename}/plugins/metadata.rst>`_
===============================================
DEALINGS IN THE SOFTWARE.
..
-Links
-#####
+Links and other
+###############
:breadcrumb: {filename}/plugins.rst Pelican plugins
:footer:
.. note-dim::
:class: m-text-center
- `« Math and code <{filename}/plugins/math-and-code.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_
+ `« Math and code <{filename}/plugins/math-and-code.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_ | `Metadata » <{filename}/plugins/metadata.rst>`_
.. role:: py(code)
:language: py
:filesize:`{filename}/../css/m-dark.compiled.css` but only
:filesize-gz:`{filename}/../css/m-dark.compiled.css` when the server
sends it compressed.
+
+`Aliases`_
+==========
+
+Site content almost never stays on the same place for extended periods of time
+and preserving old links for backwards compatibility is a vital thing for user
+friendliness. This plugin allows you to create a redirect alias URLs for your
+pages and articles.
+
+Download the `m/alias.py <{filename}/plugins.rst>`_ file, put it
+including the ``m/`` directory into one of your :py:`PLUGIN_PATHS` and add
+:py:`m.alias` package to your :py:`PLUGINS` in ``pelicanconf.py``. This plugin
+assumes presence of `m.htmlsanity <{filename}/plugins/htmlsanity.rst>`_.
+
+.. code:: python
+
+ PLUGINS += ['m.htmlsanity', 'm.alias']
+
+.. note-success::
+
+ This plugin is loosely inspired by :gh:`Nitron/pelican-alias`, © 2013
+ Christopher Williams, licensed under
+ :gh:`MIT <Nitron/pelican-alias$master/LICENSE.txt>`.
+
+Use the :rst:`:alias:` field to specify one or more locations that should
+redirect to your article / page. Each line is treated as one alias, the
+locations have to begin with ``/`` and are relative to the Pelican output
+directory, each of them contains just a :html:`<meta http-equiv="refresh" />`
+that points to a fully-qualified URL of the article or page.
+
+If the alias ends with ``/``, the redirector file is saved into ``index.html``
+in given directory.
+
+.. code:: rst
+
+ My Article
+ ##########
+
+ :alias:
+ /2018/05/06/old-version-of-the-article/
+ /even-older-version-of-the-article.html
.. note-dim::
:class: m-text-center
- `« Images <{filename}/plugins/images.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_ | `Links » <{filename}/plugins/links.rst>`_
+ `« Images <{filename}/plugins/images.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_ | `Links and other » <{filename}/plugins/links.rst>`_
.. role:: css(code)
:language: css
.. note-dim::
:class: m-text-center
- `« Links <{filename}/plugins/links.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_
+ `« Links and other <{filename}/plugins/links.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_
.. role:: html(code)
:language: html
--- /dev/null
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018 Vladimír Vondruš <mosra@centrum.cz>
+#
+# Loosely based on https://github.com/Nitron/pelican-alias,
+# copyright © 2013 Christopher Williams
+#
+# 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.
+#
+
+import os
+import logging
+from pelican import signals
+
+from m.htmlsanity import format_siteurl
+
+logger = logging.getLogger(__name__)
+
+class AliasGenerator:
+ def __init__(self, context, settings, path, theme, output_path, *args):
+ self.output_path = output_path
+ self.context = context
+
+ def generate_output(self, writer):
+ for page in self.context['pages'] + self.context['articles']:
+ aliases = page.metadata.get('alias', '').strip()
+ if not aliases: continue
+ for alias in aliases.split('\n'):
+ alias = alias.strip()
+
+ # Currently only supporting paths starting with /
+ if not alias.startswith('/'): assert False
+ path = os.path.join(self.output_path, alias[1:])
+
+ # If path ends with /, write it into index.html
+ directory, filename = os.path.split(path)
+ if not filename: filename = 'index.html'
+
+ alias_file = os.path.join(directory, filename)
+ alias_target = format_siteurl(page.url)
+ logger.info('m.alias: creating alias {} -> {}'.format(alias_file[len(self.output_path):], alias_target))
+
+ # Write the redirect file
+ os.makedirs(directory, exist_ok=True)
+ with open(alias_file, 'w') as f:
+ f.write("""<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url={}" /></head></html>\n""".format(alias_target))
+
+def get_generators(generators): return AliasGenerator
+
+def register():
+ # TODO: why `lambda generators: AliasGenerator` doesn't work?
+ signals.get_generators.connect(get_generators)
--- /dev/null
+An Article
+##########
+
+:date: 2018-05-06
+:alias: /blog/old-article/
--- /dev/null
+<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=http://my.site/article.html" /></head></html>
+
--- /dev/null
+<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=http://my.site/page.html" /></head></html>
--- /dev/null
+<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0;url=http://my.site/page.html" /></head></html>
--- /dev/null
+A page
+######
+
+:alias:
+ /old-page/
+ /even-older-page.html
--- /dev/null
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018 Vladimír Vondruš <mosra@centrum.cz>
+#
+# 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.
+#
+
+from m.test import PluginTestCase
+
+class Alias(PluginTestCase):
+ def __init__(self, *args, **kwargs):
+ super().__init__(__file__, '', *args, **kwargs)
+
+ def test(self):
+ self.run_pelican({
+ 'PLUGINS': ['m.htmlsanity', 'm.alias'],
+ 'SITEURL': 'http://my.site'
+ })
+
+ self.assertEqual(*self.actual_expected_contents('old-page/index.html'))
+ self.assertEqual(*self.actual_expected_contents('even-older-page.html'))
+ self.assertEqual(*self.actual_expected_contents('blog/old-article/index.html'))
('Components', 'plugins/components/', 'plugins/components'),
('Images', 'plugins/images/', 'plugins/images'),
('Math and code', 'plugins/math-and-code/', 'plugins/math-and-code'),
- ('Links', 'plugins/links/', 'plugins/links'),
+ ('Links and other', 'plugins/links/', 'plugins/links'),
('Metadata', 'plugins/metadata/', 'plugins/metadata')]),
('Doxygen theme', 'doxygen/', 'doxygen', []),
('GitHub', 'https://github.com/mosra/m.css', '', [])]
('Components', 'plugins/components/'),
('Images', 'plugins/images/'),
('Math and code', 'plugins/math-and-code/'),
- ('Links', 'plugins/links/'),
+ ('Links and other', 'plugins/links/'),
('Metadata', 'plugins/metadata/')]
M_FINE_PRINT = """