chiark / gitweb /
New m.metadata plugin.
authorVladimír Vondruš <mosra@centrum.cz>
Tue, 19 Dec 2017 15:47:45 +0000 (16:47 +0100)
committerVladimír Vondruš <mosra@centrum.cz>
Tue, 19 Dec 2017 19:34:21 +0000 (20:34 +0100)
Provides per-category, per-author and per-tag metadata:

 * Author and category image and "badge" (short description)
 * Author, category and tag description on their filter page, filling
   social meta tags as well
 * Ability to specify author twitter handle for social meta tags

49 files changed:
doc/examples/authors/an-author.rst [new file with mode: 0644]
doc/examples/categories/examples.rst [new file with mode: 0644]
doc/examples/tags/jumbo.rst [new file with mode: 0644]
doc/pelican/theme.rst
doc/plugins.rst
doc/plugins/metadata.rst [new file with mode: 0644]
pelican-plugins/m/metadata.py [new file with mode: 0644]
pelican-plugins/m/test/metadata/article-jumbo.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/article-minimal.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/article-no-image.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/article.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/articles/article-jumbo.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/articles/article-minimal.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/articles/article-no-image.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/articles/article.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/author-an-author.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/author-author-with-no-image.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/author-minimal-author.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/authors/an-author.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/authors/author-with-no-image.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/authors/minimal-author.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/categories/a-category.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/categories/category-with-no-image.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/categories/minimal-category.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/category-a-category.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/category-category-with-no-image.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/category-minimal-category.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/category.jpg [new symlink]
pelican-plugins/m/test/metadata/mosra.jpg [new symlink]
pelican-plugins/m/test/metadata/tag-a-tag.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/tag-minimal-tag.html [new file with mode: 0644]
pelican-plugins/m/test/metadata/tags/a-tag.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata/tags/minimal-tag.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata_typography_html_escape/article.html [new file with mode: 0644]
pelican-plugins/m/test/metadata_typography_html_escape/articles/article.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata_typography_html_escape/author-an-author.html [new file with mode: 0644]
pelican-plugins/m/test/metadata_typography_html_escape/authors/an-author.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata_typography_html_escape/categories/a-category.rst [new file with mode: 0644]
pelican-plugins/m/test/metadata_typography_html_escape/category-a-category.html [new file with mode: 0644]
pelican-plugins/m/test/metadata_typography_html_escape/tag-a-tag.html [new file with mode: 0644]
pelican-plugins/m/test/metadata_typography_html_escape/tags/a-tag.rst [new file with mode: 0644]
pelican-plugins/m/test/test_metadata.py [new file with mode: 0644]
pelican-theme/templates/article.html
pelican-theme/templates/article_badges.html [new file with mode: 0644]
pelican-theme/templates/author.html
pelican-theme/templates/base_blog_section.html
pelican-theme/templates/category.html
pelican-theme/templates/tag.html
site/pelicanconf.py

diff --git a/doc/examples/authors/an-author.rst b/doc/examples/authors/an-author.rst
new file mode 100644 (file)
index 0000000..f4ca3ea
--- /dev/null
@@ -0,0 +1,6 @@
+:image: {filename}/static/mosra.jpg
+:badge: Info badge for `An Author <{author}an-author>`_ provided by the
+    `Metadata <{filename}/plugins/metadata.rst>`_ plugin.
+
+Detailed author info provided by the `Metadata <{filename}/plugins/metadata.rst>`_
+plugin.
diff --git a/doc/examples/categories/examples.rst b/doc/examples/categories/examples.rst
new file mode 100644 (file)
index 0000000..001503c
--- /dev/null
@@ -0,0 +1,6 @@
+:image: {filename}/static/site.jpg
+:badge: Info badge for the `Examples <{category}examples>`_ category provided
+    by the `Metadata <{filename}/plugins/metadata.rst>`_ plugin.
+
+Detailed category info provided by the `Metadata <{filename}/plugins/metadata.rst>`_
+plugin.
diff --git a/doc/examples/tags/jumbo.rst b/doc/examples/tags/jumbo.rst
new file mode 100644 (file)
index 0000000..1ae7908
--- /dev/null
@@ -0,0 +1,2 @@
+Detailed tag info provided by the `Metadata <{filename}/plugins/metadata.rst>`_
+plugin.
index 22f2641b5da831a86c274224f5d5c1ec569b1cd2..b6768e3e7a784de20353e982f3893ffdedf01b34 100644 (file)
@@ -563,6 +563,12 @@ option in the configuration:
 
     M_SHOW_AUTHOR_LIST = True
 
+.. note-success::
+
+    The theme is able to recognize additional description and images for
+    authors, categories and tags from the
+    `Metadata plugin <{filename}/plugins/metadata.rst>`_, if you enable it.
+
 `Jumbo articles`_
 -----------------
 
@@ -622,6 +628,11 @@ specialized for articles like this:
     present and to ``summary`` otherwise
 -   ``og:type`` is set to ``article``
 
+.. note-success::
+
+    Additional social meta tags (such as author or category info) are be
+    exposed by the `Metadata plugin <{filename}/plugins/metadata.rst>`_.
+
 .. note-info::
 
     You can see how article links will display by pasting
index 0ed4f5f393a6d15cc19ed98dfb915283748254e7..014aea8e1a8e77288a4b2b7e2e6c1c10a62468f7 100644 (file)
@@ -50,6 +50,7 @@ and restart Pelican. Download the plugins below or
     :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.metadata <mosra/m.css$master/pelican-plugins/m/metadata.py>`
 
 Click on the headings below to get to know more. Note that particular plugins
 can have additional dependencies besides just Pelican, see documentation of
@@ -90,3 +91,11 @@ entered directly in your :abbr:`reST <reStructuredText>` sources.
 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.
+
+`Metadata » <{filename}/plugins/metadata.rst>`_
+===============================================
+
+With the :py:`m.metadata` plugin it's possible to assign additional description
+and images to authors, categories and tags. The information can then appear on
+article listing page, as a badge under the article or be added to social meta
+tags.
diff --git a/doc/plugins/metadata.rst b/doc/plugins/metadata.rst
new file mode 100644 (file)
index 0000000..559b517
--- /dev/null
@@ -0,0 +1,260 @@
+..
+    This file is part of m.css.
+
+    Copyright © 2017 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.
+..
+
+Metadata
+########
+
+:breadcrumb: {filename}/plugins.rst Pelican plugins
+:summary: Assigns additional description and images to categories, authors and
+    tags.
+:footer:
+    .. note-dim::
+        :class: m-text-center
+
+        `« Links <{filename}/plugins/links.rst>`_ | `Pelican plugins <{filename}/plugins.rst>`_
+
+.. role:: html(code)
+    :language: html
+.. role:: py(code)
+    :language: py
+.. role:: rst(code)
+    :language: rst
+
+Assigns additional description and images to categories, authors and tags.
+
+`How to use`_
+=============
+
+Download the `m/metadata.py <{filename}/plugins.rst>`_ file, put it including
+the ``m/`` directory into one of your :py:`PLUGIN_PATHS` and add ``m.metadata``
+package to your :py:`PLUGINS` in ``pelicanconf.py``. In order to have the
+contents properly rendered, it's advised to add the new fields to
+:py:`FORMATTED_FIELDS`:
+
+.. code:: py
+
+    PLUGINS += ['m.metadata']
+    FORMATTED_FIELDS += ['description', 'badge']
+
+This plugin parses additional info from pages in subdirectories of the contents
+directory (i.e., where the :py:`PATH` setting points to). By default, the
+directories are as follows and can be configured with these settings:
+
+.. code:: py
+
+    M_METADATA_AUTHOR_PATH = 'authors'
+    M_METADATA_CATEGORY_PATH = 'categories'
+    M_METADATA_TAG_PATH = 'tags'
+
+The m.css Pelican theme recognizes presence of this plugin and renders the
+additional metadata both in the page and in the `Open Graph <http://ogp.me/>`_
+and `Twitter Card <https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary-card-with-large-image>`_ social :html:`<meta>` tags, in addition to tags rendered by
+`articles themselves <{filename}/pelican/theme.rst#social-meta-tags-for-articles>`_.
+See below for more information.
+
+`Author metadata`_
+==================
+
+Author pages are matched to authors based on the slug (so e.g. metadata for
+author named *John Doe* will be in a file named ``authors/john-doe.rst``). The
+following metadata are recognized:
+
+-   Page content is exposed to the theme in :py:`author.page.content` on the
+    author page and used to fill author details block on top of the author page
+    in the m.css Pelican theme. If not set, no author details block is
+    rendered.
+-   Page title is exposed to the theme in :py:`author.page.title` on the author
+    page and in :py:`article.author.badge_title` in all articles having given
+    author. In the m.css Pelican theme it is used as caption in author details
+    block and in ``og:title`` / ``twitter:title`` social :html:`<meta>` tag on
+    author page. Can be used to provide a longer version of author name on this
+    page. If not set, the m.css Pelican theme uses the global author name (the
+    one set by articles) instead.
+-   The :rst:`:description:` field is exposed to the theme in
+    :py:`author.page.description` on the author page. The m.css Pelican theme
+    uses it in :html:`<meta name="description">`. If not set, no tag is
+    rendered.
+-   The :rst:`:summary:` field is exposed to the theme in :py:`author.page.summary`
+    on the author page. The m.css Pelican theme uses it in ``og:description``
+    / ``twitter:description`` social :html:`<meta>` tag. If not set, no tag is
+    rendered.
+-   The :rst:`:badge:` field is exposed to the theme in :py:`article.author.badge`
+    in all articles having given author. The m.css Pelican theme uses it to
+    display an *About the author* badge at the end of article pages. Similarly
+    to page content, it's possible to use advanced :abbr:`reST <reStructuredText>`
+    formatting capabilities here. If not set, no badge at the end of author's
+    articles is rendered.
+-   The :rst:`:image:` field is exposed to the theme in :py:`author.page.image`
+    on the author page and in :py:`article.author.image` in all articles having
+    given author. The m.css Pelican theme uses it to add an image to author
+    badge on articles, to author details on author page and in ``og:image`` /
+    ``twitter:image`` social :html:`<meta>` tags on author page, overriding the
+    global :py:`M_SOCIAL_IMAGE`. It's expected to be smaller and square
+    similarly to the :py:`M_SOCIAL_IMAGE` `described in the theme documentation <{filename}/pelican/theme.rst#global-site-image>`_.
+    If not set, the details and badges are rendered without images and no
+    social tag is present. Does not affect ``twitter:card``, it's set to
+    ``summary`` regardless of whether the image is present or not.
+-   The :rst:`:twitter:` / :rst:`:twitter_id:` fields are exposed to the theme
+    in :py:`article.author.twitter` / :py:`article.author.twitter_id` and
+    :py:`author.page.twitter` / :py:`author.page.twitter_id`. The m.css
+    Pelican theme uses it to render ``twitter:creator`` / ``twitter::creator:id``
+    social :html:`<meta>` tags. If not set, no tags are rendered.
+
+Example of a completely filled author page, saved under ``authors/john-doe.rst``
+and matching an author named *John Doe*:
+
+.. code:: rst
+
+    John "Not That Serial Killer" Doe
+    #################################
+
+    :twitter: @se7en
+    :twitter_id: 7777777
+    :image: {filename}/authors/john-doe.jpg
+    :description: I'm not that serial killer.
+    :summary: I'm really not that serial killer.
+    :badge: No, really, don't confuse me with that guy.
+
+    What? No, I didn't kill anybody. Yet.
+
+.. note-info::
+
+    See how author info is rendered in the m.css Pelican theme
+    `on the author page <{author}an-author>`_ and
+    `on the article page <{filename}/examples/article.rst>`_.
+
+`Category metadata`_
+====================
+
+Category pages are matched to categories based on the slug (so e.g. metadata
+for category named *Guest posts* will be in a file named
+``categories/guest-posts.rst``). The following metadata are recognized:
+
+-   Page content is exposed to the theme in :py:`category.page.content` on the
+    category page and used to fill category details block on top of the
+    category page in the m.css Pelican theme. If not set, no category details
+    block is rendered.
+-   Page title is exposed to the theme in :py:`category.page.title` on the
+    category page and in :py:`article.category.badge_title` in all articles
+    being in given category. In the m.css Pelican theme it is used as caption
+    in category details block, in ``og:title`` / ``twitter:title`` social
+    :html:`<meta>` tag on category page and as badge title on article pages.
+    Can be used to provide a longer version of category name for article badge
+    and detailed category info. If not set, the m.css Pelican theme uses the
+    global category name (the one set by articles) instead.
+-   The :rst:`:description:` field is exposed to the theme in
+    :py:`category.page.description` on the category page. The m.css Pelican
+    theme uses it in :html:`<meta name="description">`. If not set, no tag is
+    rendered.
+-   The :rst:`:summary:` field is exposed to the theme in :py:`category.page.summary`
+    on the category page. The m.css Pelican theme uses it in ``og:description``
+    / ``twitter:description`` social :html:`<meta>` tag. If not set, no tag is
+    rendered.
+-   The :rst:`:badge:` field is exposed to the theme in :py:`article.category.badge`
+    in all articles being in given category. The m.css Pelican theme uses it to
+    display an informational badge at the end of article pages. Similarly to
+    page content, it's possible to use advanced :abbr:`reST <reStructuredText>`
+    formatting capabilities here. If not set, no badge at the end of articles
+    in given category is rendered.
+-   The :rst:`:image:` field is exposed to the theme in :py:`category.page.image`
+    on the category page and in :py:`article.category.image` in all articles
+    being in given category. The m.css Pelican theme uses it to add an image to
+    category badges on articles, to category details on category page and
+    in ``og:image`` / ``twitter:image`` social :html:`<meta>` tags on category
+    page. If `article cover image <{filename}/pelican/theme.rst#jumbo-articles>`_
+    is not specified, the image is used also for ``og:image`` / ``twitter:image``
+    on given article, overriding the global :py:`M_SOCIAL_IMAGE`. It's expected
+    to be smaller and square similarly to the
+    :py:`M_SOCIAL_IMAGE` `described in the theme documentation <{filename}/pelican/theme.rst#global-site-image>`_.
+    If not set, the details and badges are rendered without images and no
+    social tag is present. Does not affect ``twitter:card``, it's set to
+    ``summary`` or ``summary_large_image`` depending only on presence of
+    article cover image.
+
+Example of a completely filled category page, saved under ``categories/guest-posts.rst``
+and matching a category named *Guest posts*:
+
+.. code:: rst
+
+    Posts by our users
+    ##################
+
+    :image: {filename}/categories/guest-posts.jpg
+    :description: User stories and product reviews
+    :summary: Stories of our users and honest reviews of our product.
+    :badge: This article is a guest post. Want to share your story as well? Head
+        over to the `intro article <{filename}/blog/introducing-guest-posts.rst>`_
+        to get to know more. We'll happily publish it here.
+
+    This section contains guest posts, reviews and success stories. Want to share
+    your story as well? Head over to the
+    `intro article <{filename}/blog/introducing-guest-posts.rst>`_ to get to know
+    more. We'll happily publish it here.
+
+.. note-info::
+
+    See how category info is rendered in the m.css Pelican theme
+    `on the category page <{category}a-category>`_ and
+    `on the article page <{filename}/examples/article.rst>`_.
+
+`Tag metadata`_
+===============
+
+Tag pages are matched to authors based on the slug (so e.g. metadata for
+tag named *Pantomime* will be in a file named ``tags/pantomime.rst``). The
+following metadata are recognized:
+
+-   Page content is exposed to the theme in :py:`tag.page.content` on the tag
+    page and used to fill tag details block on top of the tag page in the m.css
+    Pelican theme. If not set, no tag details block is rendered.
+-   Page title is exposed to the theme in :py:`tag.page.title` on the tag page,
+    is used as caption in tag details block on tag page and in ``og:title`` /
+    ``twitter:title`` social :html:`<meta>` tag. Can be used to provide a
+    longer version of tag name on this page. If not set, the m.css Pelican
+    theme uses the global tag name (the one set by articles) instead.
+-   The :rst:`:description:` field is exposed to the theme in
+    :py:`tag.page.description` on the tag page. The m.css Pelican theme uses
+    it in :html:`<meta name="description">`. If not set, no :html:`<meta>` tag
+    is rendered.
+-   The :rst:`:summary:` field is exposed to the theme in :py:`tag.page.summary`
+    on the tag page. The m.css Pelican theme uses it in ``og:description``
+    / ``twitter:description`` social :html:`<meta>` tag. If not set, no
+    :html:`<meta>` tag is rendered.
+
+Example of a completely filled tag page, saved under ``tags/pantomime.rst``
+and matching a tag named *Pantomime*:
+
+.. code:: rst
+
+    ¯\_(ツ)_/¯
+    ##########
+
+    :description: ¯\_(ツ)_/¯
+    :summary: ¯\_(ツ)_/¯
+
+    ¯\_(ツ)_/¯
+
+.. note-info::
+
+    See how tag info is rendered `in the m.css Pelican theme <{tag}Jumbo>`_.
diff --git a/pelican-plugins/m/metadata.py b/pelican-plugins/m/metadata.py
new file mode 100644 (file)
index 0000000..09bc50f
--- /dev/null
@@ -0,0 +1,81 @@
+#
+#   This file is part of m.css.
+#
+#   Copyright © 2017 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.
+#
+
+import logging
+import os
+
+from pelican import signals
+
+logger = logging.getLogger(__name__)
+
+_names = {'authors': "Author",
+          'categories': "Category",
+          'tags': "Tag"}
+
+def _read_pages(article_generator, key):
+    path = article_generator.settings.get(*key)
+    fullpath = os.path.join(article_generator.settings['PATH'], path)
+    if not os.path.isdir(fullpath): return {}
+
+    pages = {}
+    for f in os.listdir(fullpath):
+        fullf = os.path.join(fullpath, f)
+        if not os.path.isfile(fullf): continue
+
+        logger.debug("Read file {} -> {}".format(os.path.join(path, f), {
+            'authors': "Author",
+            'categories': "Category",
+            'tags': "Tag"}[key[1]]))
+
+        base_path, filename = os.path.split(fullf)
+        pages[os.path.splitext(filename)[0]] = article_generator.readers.read_file(base_path, filename, context=article_generator.context)
+    return pages
+
+def populate_metadata(article_generator):
+    authors = _read_pages(article_generator, ('M_METADATA_AUTHOR_PATH', 'authors'))
+    categories = _read_pages(article_generator, ('M_METADATA_CATEGORY_PATH', 'categories'))
+    tags = _read_pages(article_generator, ('M_METADATA_TAG_PATH', 'tags'))
+
+    for author, _ in article_generator.authors:
+        author.page = authors.get(author.slug, {})
+    for category, _ in article_generator.categories:
+        category.page = categories.get(category.slug, {})
+    for tag in article_generator.tags:
+        tag.page = tags.get(tag.slug, {})
+
+    for article in article_generator.articles:
+        page = authors.get(article.author.slug, {})
+        for i in ['badge', 'image', 'twitter', 'twitter_id']:
+            if hasattr(page, i): setattr(article.author, i, getattr(page, i))
+        if hasattr(page, 'title'):
+            article.author.badge_title = page.title
+
+        page = categories.get(article.category.slug, {})
+        for i in ['badge', 'image']:
+            if hasattr(page, i): setattr(article.category, i, getattr(page, i))
+        if hasattr(page, 'title'):
+            article.category.badge_title = page.title
+
+def register():
+    signals.article_generator_finalized.connect(populate_metadata)
diff --git a/pelican-plugins/m/test/metadata/article-jumbo.html b/pelican-plugins/m/test/metadata/article-jumbo.html
new file mode 100644 (file)
index 0000000..ddd1c1f
--- /dev/null
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>A jumbo article | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <link rel="canonical" href="article-jumbo.html" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta name="twitter:creator" content="@czmosra" />
+  <meta name="twitter:creator:id" content="1537427036" />
+  <meta property="og:title" content="A jumbo article" />
+  <meta name="twitter:title" content="A jumbo article" />
+  <meta property="og:url" content="article-jumbo.html" />
+  <meta property="og:description" content="Article summary." />
+  <meta name="twitter:description" content="Article summary." />
+  <meta property="og:image" content="image.jpg" />
+  <meta name="twitter:image" content="image.jpg" />
+  <meta name="twitter:card" content="summary_large_image" />
+  <meta property="og:type" content="article" />
+</head>
+<body>
+<header><nav id="navigation" class="m-navbar-jumbo">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+  <article id="m-jumbo">
+    <header>
+      <div id="m-jumbo-image" style="background-image: url('image.jpg');">
+        <div id="m-jumbo-cover">
+          <div class="m-container">
+            <div class="m-row">
+              <div class="m-col-t-6 m-col-s-5 m-push-s-1 m-text-left">Dec 16, 2017</div>
+              <div class="m-col-t-6 m-col-s-5 m-push-s-1 m-text-right"><a href="author-an-author.html">An Author</a></div>
+            </div>
+            <div class="m-row">
+              <div class="m-col-t-12 m-col-s-10 m-push-s-1 m-col-m-8 m-push-m-2">
+                <h1><a href="article-jumbo.html" rel="bookmark" title="Permalink to A jumbo article">A jumbo article</a></h1>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="m-container">
+        <div class="m-row">
+          <div class="m-col-m-10 m-push-m-1 m-nopady">
+            <p>Article summary.</p>
+          </div>
+        </div>
+      </div>
+    </header>
+    <div class="m-container m-container-inflatable">
+      <div class="m-row">
+        <div class="m-col-m-10 m-push-m-1 m-nopady">
+<!-- content -->
+<p>An article that should have its own cover image in social meta tags, not the
+category-specific one.</p>
+<!-- /content -->
+          <div class="m-block m-success m-badge">
+            <img src="./mosra.jpg" alt="An Author" />
+            <h3>About the author</h3>
+            <p>Contents of the badge, displayed at article bottom.
+            <a href="./author-an-author.html">Link to the author.</a></p>
+          </div>
+          <div class="m-block m-warning m-badge">
+            <img src="./category.jpg" alt="A category" />
+            <h3>A category name, displayed on top of the category badge/details</h3>
+            <p>Contents of the badge, displayed at article bottom.
+            <a href="./category-a-category.html">Link to the category.</a></p>
+          </div>
+        </div>
+      </div>
+    </div>
+    <footer class="m-container">
+      <div class="m-row">
+        <div class="m-col-m-10 m-push-m-1 m-nopadb">
+          <p>Posted by <a href="author-an-author.html">An Author</a> on <time datetime="2017-12-16T00:00:00+00:00">Dec 16, 2017</time> in <a href="category-a-category.html">A category</a>.</p>
+        </div>
+      </div>
+    </footer>
+  </article>
+  <nav class="m-navpanel m-container">
+    <div class="m-row">
+      <div class="m-col-s-4 m-col-l-2 m-push-l-1">
+        <h3>Categories</h3>
+        <ol class="m-block-bar-s">
+          <li><a href="category-a-category.html">A category</a></li>
+          <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+          <li><a href="category-minimal-category.html">Minimal category</a></li>
+        </ol>
+      </div>
+      <div class="m-col-s-4 m-col-l-2 m-push-l-3">
+        <h3>Authors</h3>
+        <ol class="m-block-bar-s">
+          <li><a href="author-an-author.html">An Author</a></li>
+          <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+          <li><a href="author-minimal-author.html">Minimal author</a></li>
+        </ol>
+      </div>
+      <div class="m-col-s-4 m-col-l-2 m-push-l-5">
+        <h3>Tag cloud</h3>
+        <ul class="m-tagcloud">
+          <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+          <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+        </ul>
+      </div>
+    </div>
+  </nav>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/article-minimal.html b/pelican-plugins/m/test/metadata/article-minimal.html
new file mode 100644 (file)
index 0000000..d79b81c
--- /dev/null
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>An article | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <link rel="canonical" href="article-minimal.html" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="An article" />
+  <meta name="twitter:title" content="An article" />
+  <meta property="og:url" content="article-minimal.html" />
+  <meta property="og:description" content="Article summary." />
+  <meta name="twitter:description" content="Article summary." />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="article" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <article class="m-col-m-10 m-nopadb">
+      <header>
+        <h1><a href="article-minimal.html" rel="bookmark" title="Permalink to An article">
+          <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+            Dec <span class="m-date-day">17</span> 2017
+          </time>
+          An article
+        </a></h1>
+        <p>Article summary.</p>
+      </header>
+      <div class="m-clearfix-l"></div>
+<!-- content -->
+<p>There should be no category or author-related anything in the meta tags. Just
+badges with default titles.</p>
+<!-- /content -->
+      <div class="m-block m-success">
+        <h3>About the author</h3>
+        <p>Author badge.</p>
+      </div>
+      <div class="m-block m-warning">
+        <h3>Minimal category</h3>
+        <p>Category badge.</p>
+      </div>
+      <footer>
+        <p>Posted by <a href="author-minimal-author.html">Minimal author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-minimal-category.html">Minimal category</a>. Tags: <a href="tag-minimal-tag.html">Minimal tag</a>.</p>
+      </footer>
+    </article>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/article-no-image.html b/pelican-plugins/m/test/metadata/article-no-image.html
new file mode 100644 (file)
index 0000000..c355492
--- /dev/null
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>An article | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <link rel="canonical" href="article-no-image.html" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="An article" />
+  <meta name="twitter:title" content="An article" />
+  <meta property="og:url" content="article-no-image.html" />
+  <meta property="og:description" content="Article summary." />
+  <meta name="twitter:description" content="Article summary." />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="article" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <article class="m-col-m-10 m-nopadb">
+      <header>
+        <h1><a href="article-no-image.html" rel="bookmark" title="Permalink to An article">
+          <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+            Dec <span class="m-date-day">17</span> 2017
+          </time>
+          An article
+        </a></h1>
+        <p>Article summary.</p>
+      </header>
+      <div class="m-clearfix-l"></div>
+<!-- content -->
+<p>This article should have no images anywhere and everything still needs to look
+properly.</p>
+<!-- /content -->
+      <div class="m-block m-success">
+        <h3>About the author</h3>
+        <p>Contents of the badge w/o image.</p>
+      </div>
+      <div class="m-block m-warning">
+        <h3>Category with no image</h3>
+        <p>Contents of the badge w/o image, displayed at article bottom.</p>
+      </div>
+      <footer>
+        <p>Posted by <a href="author-author-with-no-image.html">Author with no image</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-category-with-no-image.html">Category with no image</a>.</p>
+      </footer>
+    </article>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/article.html b/pelican-plugins/m/test/metadata/article.html
new file mode 100644 (file)
index 0000000..d741a48
--- /dev/null
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>An article | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <link rel="canonical" href="article.html" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta name="twitter:creator" content="@czmosra" />
+  <meta name="twitter:creator:id" content="1537427036" />
+  <meta property="og:title" content="An article" />
+  <meta name="twitter:title" content="An article" />
+  <meta property="og:url" content="article.html" />
+  <meta property="og:description" content="Article summary." />
+  <meta name="twitter:description" content="Article summary." />
+  <meta property="og:image" content="./category.jpg" />
+  <meta name="twitter:image" content="./category.jpg" />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="article" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <article class="m-col-m-10 m-nopadb">
+      <header>
+        <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+          <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+            Dec <span class="m-date-day">17</span> 2017
+          </time>
+          An article
+        </a></h1>
+        <p>Article summary.</p>
+      </header>
+      <div class="m-clearfix-l"></div>
+<!-- content -->
+<p>There should be a category image + author's twitter in the social meta tag and
+two badges with images on the bottom.</p>
+<!-- /content -->
+      <div class="m-block m-success m-badge">
+        <img src="./mosra.jpg" alt="An Author" />
+        <h3>About the author</h3>
+        <p>Contents of the badge, displayed at article bottom.
+        <a href="./author-an-author.html">Link to the author.</a></p>
+      </div>
+      <div class="m-block m-warning m-badge">
+        <img src="./category.jpg" alt="A category" />
+        <h3>A category name, displayed on top of the category badge/details</h3>
+        <p>Contents of the badge, displayed at article bottom.
+        <a href="./category-a-category.html">Link to the category.</a></p>
+      </div>
+      <footer>
+        <p>Posted by <a href="author-an-author.html">An Author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-a-category.html">A category</a>. Tags: <a href="tag-a-tag.html">A tag</a>.</p>
+      </footer>
+    </article>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/articles/article-jumbo.rst b/pelican-plugins/m/test/metadata/articles/article-jumbo.rst
new file mode 100644 (file)
index 0000000..220bdeb
--- /dev/null
@@ -0,0 +1,11 @@
+A jumbo article
+###############
+
+:date: 2017-12-16
+:cover: image.jpg
+:author: An Author
+:category: A category
+:summary: Article summary.
+
+An article that should have its own cover image in social meta tags, not the
+category-specific one.
diff --git a/pelican-plugins/m/test/metadata/articles/article-minimal.rst b/pelican-plugins/m/test/metadata/articles/article-minimal.rst
new file mode 100644 (file)
index 0000000..8a212a2
--- /dev/null
@@ -0,0 +1,11 @@
+An article
+##########
+
+:date: 2017-12-17
+:author: Minimal author
+:category: Minimal category
+:tags: Minimal tag
+:summary: Article summary.
+
+There should be no category or author-related anything in the meta tags. Just
+badges with default titles.
diff --git a/pelican-plugins/m/test/metadata/articles/article-no-image.rst b/pelican-plugins/m/test/metadata/articles/article-no-image.rst
new file mode 100644 (file)
index 0000000..c072028
--- /dev/null
@@ -0,0 +1,10 @@
+An article
+##########
+
+:date: 2017-12-17
+:author: Author with no image
+:category: Category with no image
+:summary: Article summary.
+
+This article should have no images anywhere and everything still needs to look
+properly.
diff --git a/pelican-plugins/m/test/metadata/articles/article.rst b/pelican-plugins/m/test/metadata/articles/article.rst
new file mode 100644 (file)
index 0000000..f995b27
--- /dev/null
@@ -0,0 +1,11 @@
+An article
+##########
+
+:date: 2017-12-17
+:author: An Author
+:category: A category
+:tags: A tag
+:summary: Article summary.
+
+There should be a category image + author's twitter in the social meta tag and
+two badges with images on the bottom.
diff --git a/pelican-plugins/m/test/metadata/author-an-author.html b/pelican-plugins/m/test/metadata/author-an-author.html
new file mode 100644 (file)
index 0000000..46ddb13
--- /dev/null
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Posts by An Author | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="description" content="Author description, used for a &lt;meta name=&#34;description&#34;&gt; tag." />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta name="twitter:creator" content="@czmosra" />
+  <meta name="twitter:creator:id" content="1537427036" />
+  <meta property="og:title" content="Author name, displayed on top of the author page" />
+  <meta name="twitter:title" content="Author name, displayed on top of the author page" />
+  <meta property="og:url" content="author-an-author.html" />
+  <meta property="og:description" content="Author summary, used for the social &lt;meta&gt; tags." />
+  <meta name="twitter:description" content="Author summary, used for the social &lt;meta&gt; tags." />
+  <meta property="og:image" content="./mosra.jpg" />
+  <meta name="twitter:image" content="./mosra.jpg" />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts by <em>An Author</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-success m-badge">
+        <img src="./mosra.jpg" alt="An Author" />
+        <h3>Author name, displayed on top of the author page</h3>
+        <p>Contents of author details, displayed on top of the author page.
+        <a href="http://mcss.mosra.cz">Link.</a></p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-an-author.html">An Author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-a-category.html">A category</a>. Tags: <a href="tag-a-tag.html">A tag</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+      <article>
+        <header>
+          <h1><a href="article-jumbo.html" rel="bookmark" title="Permalink to A jumbo article">
+            <time class="m-date" datetime="2017-12-16T00:00:00+00:00">
+              Dec <span class="m-date-day">16</span> 2017
+            </time>
+            A jumbo article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-an-author.html">An Author</a> on <time datetime="2017-12-16T00:00:00+00:00">Dec 16, 2017</time> in <a href="category-a-category.html">A category</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/author-author-with-no-image.html b/pelican-plugins/m/test/metadata/author-author-with-no-image.html
new file mode 100644 (file)
index 0000000..03bf429
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Posts by Author with no image | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="Author with no image" />
+  <meta name="twitter:title" content="Author with no image" />
+  <meta property="og:url" content="author-author-with-no-image.html" />
+  <meta property="og:description" content="Contents of author details w/o image, displayed on top of the author page." />
+  <meta name="twitter:description" content="Contents of author details w/o image, displayed on top of the author page." />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts by <em>Author with no image</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-success">
+        <h3>Author with no image</h3>
+        <p>Contents of author details w/o image, displayed on top of the author page.</p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article-no-image.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-author-with-no-image.html">Author with no image</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-category-with-no-image.html">Category with no image</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/author-minimal-author.html b/pelican-plugins/m/test/metadata/author-minimal-author.html
new file mode 100644 (file)
index 0000000..37ff2d9
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Posts by Minimal author | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="Minimal author" />
+  <meta name="twitter:title" content="Minimal author" />
+  <meta property="og:url" content="author-minimal-author.html" />
+  <meta property="og:description" content="Author details." />
+  <meta name="twitter:description" content="Author details." />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts by <em>Minimal author</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-success">
+        <h3>Minimal author</h3>
+        <p>Author details.</p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article-minimal.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-minimal-author.html">Minimal author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-minimal-category.html">Minimal category</a>. Tags: <a href="tag-minimal-tag.html">Minimal tag</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/authors/an-author.rst b/pelican-plugins/m/test/metadata/authors/an-author.rst
new file mode 100644 (file)
index 0000000..2d21cc3
--- /dev/null
@@ -0,0 +1,13 @@
+Author name, displayed on top of the author page
+################################################
+
+:twitter: @czmosra
+:twitter_id: 1537427036
+:image: {filename}/mosra.jpg
+:description: Author description, used for a <meta name="description"> tag.
+:summary: Author summary, used for the social <meta> tags.
+:badge: Contents of the badge, displayed at article bottom.
+    `Link to the author. <{author}an-author>`_
+
+Contents of author details, displayed on top of the author page.
+`Link. <http://mcss.mosra.cz>`_
diff --git a/pelican-plugins/m/test/metadata/authors/author-with-no-image.rst b/pelican-plugins/m/test/metadata/authors/author-with-no-image.rst
new file mode 100644 (file)
index 0000000..d0c85c4
--- /dev/null
@@ -0,0 +1,6 @@
+Author with no image
+####################
+
+:badge: Contents of the badge w/o image.
+
+Contents of author details w/o image, displayed on top of the author page.
diff --git a/pelican-plugins/m/test/metadata/authors/minimal-author.rst b/pelican-plugins/m/test/metadata/authors/minimal-author.rst
new file mode 100644 (file)
index 0000000..52a9622
--- /dev/null
@@ -0,0 +1,3 @@
+:badge: Author badge.
+
+Author details.
diff --git a/pelican-plugins/m/test/metadata/categories/a-category.rst b/pelican-plugins/m/test/metadata/categories/a-category.rst
new file mode 100644 (file)
index 0000000..e178d4a
--- /dev/null
@@ -0,0 +1,11 @@
+A category name, displayed on top of the category badge/details
+###############################################################
+
+:image: {filename}/category.jpg
+:description: Category description, used for a <meta name="description"> tag.
+:summary: Category summary, used for the social <meta> tags.
+:badge: Contents of the badge, displayed at article bottom.
+    `Link to the category. <{category}a-category>`_
+
+Contents of category details, displayed on top of the category page.
+`Link. <http://mcss.mosra.cz>`_
diff --git a/pelican-plugins/m/test/metadata/categories/category-with-no-image.rst b/pelican-plugins/m/test/metadata/categories/category-with-no-image.rst
new file mode 100644 (file)
index 0000000..f18074c
--- /dev/null
@@ -0,0 +1,8 @@
+Category with no image
+######################
+
+:description: Category description, used for a <meta name="description"> tag.
+:summary: Category summary, used for the social <meta> tags.
+:badge: Contents of the badge w/o image, displayed at article bottom.
+
+Contents of category details w/o image, displayed on top of the category page.
diff --git a/pelican-plugins/m/test/metadata/categories/minimal-category.rst b/pelican-plugins/m/test/metadata/categories/minimal-category.rst
new file mode 100644 (file)
index 0000000..d7683ce
--- /dev/null
@@ -0,0 +1,3 @@
+:badge: Category badge.
+
+Category details.
diff --git a/pelican-plugins/m/test/metadata/category-a-category.html b/pelican-plugins/m/test/metadata/category-a-category.html
new file mode 100644 (file)
index 0000000..585ca49
--- /dev/null
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>A category | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="description" content="Category description, used for a &lt;meta name=&#34;description&#34;&gt; tag." />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="A category name, displayed on top of the category badge/details" />
+  <meta name="twitter:title" content="A category name, displayed on top of the category badge/details" />
+  <meta property="og:url" content="category-a-category.html" />
+  <meta property="og:description" content="Category summary, used for the social &lt;meta&gt; tags." />
+  <meta name="twitter:description" content="Category summary, used for the social &lt;meta&gt; tags." />
+  <meta property="og:image" content="./category.jpg" />
+  <meta name="twitter:image" content="./category.jpg" />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts in <em>A category</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-warning m-badge">
+        <img src="./category.jpg" alt="A category" />
+        <h3>A category name, displayed on top of the category badge/details</h3>
+        <p>Contents of category details, displayed on top of the category page.
+        <a href="http://mcss.mosra.cz">Link.</a></p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-an-author.html">An Author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-a-category.html">A category</a>. Tags: <a href="tag-a-tag.html">A tag</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+      <article>
+        <header>
+          <h1><a href="article-jumbo.html" rel="bookmark" title="Permalink to A jumbo article">
+            <time class="m-date" datetime="2017-12-16T00:00:00+00:00">
+              Dec <span class="m-date-day">16</span> 2017
+            </time>
+            A jumbo article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-an-author.html">An Author</a> on <time datetime="2017-12-16T00:00:00+00:00">Dec 16, 2017</time> in <a href="category-a-category.html">A category</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/category-category-with-no-image.html b/pelican-plugins/m/test/metadata/category-category-with-no-image.html
new file mode 100644 (file)
index 0000000..4115743
--- /dev/null
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Category with no image | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="description" content="Category description, used for a &lt;meta name=&#34;description&#34;&gt; tag." />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="Category with no image" />
+  <meta name="twitter:title" content="Category with no image" />
+  <meta property="og:url" content="category-category-with-no-image.html" />
+  <meta property="og:description" content="Category summary, used for the social &lt;meta&gt; tags." />
+  <meta name="twitter:description" content="Category summary, used for the social &lt;meta&gt; tags." />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts in <em>Category with no image</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-warning">
+        <h3>Category with no image</h3>
+        <p>Contents of category details w/o image, displayed on top of the category page.</p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article-no-image.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-author-with-no-image.html">Author with no image</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-category-with-no-image.html">Category with no image</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/category-minimal-category.html b/pelican-plugins/m/test/metadata/category-minimal-category.html
new file mode 100644 (file)
index 0000000..87e6880
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Minimal category | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="Minimal category" />
+  <meta name="twitter:title" content="Minimal category" />
+  <meta property="og:url" content="category-minimal-category.html" />
+  <meta property="og:description" content="Category details." />
+  <meta name="twitter:description" content="Category details." />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts in <em>Minimal category</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-warning">
+        <h3>Minimal category</h3>
+        <p>Category details.</p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article-minimal.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-minimal-author.html">Minimal author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-minimal-category.html">Minimal category</a>. Tags: <a href="tag-minimal-tag.html">Minimal tag</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/category.jpg b/pelican-plugins/m/test/metadata/category.jpg
new file mode 120000 (symlink)
index 0000000..158969f
--- /dev/null
@@ -0,0 +1 @@
+../../../../doc/static/site.jpg
\ No newline at end of file
diff --git a/pelican-plugins/m/test/metadata/mosra.jpg b/pelican-plugins/m/test/metadata/mosra.jpg
new file mode 120000 (symlink)
index 0000000..23fd096
--- /dev/null
@@ -0,0 +1 @@
+../../../../doc/static/mosra.jpg
\ No newline at end of file
diff --git a/pelican-plugins/m/test/metadata/tag-a-tag.html b/pelican-plugins/m/test/metadata/tag-a-tag.html
new file mode 100644 (file)
index 0000000..33192f0
--- /dev/null
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Posts tagged A tag | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="description" content="Tag description, used for a &lt;meta name=&#34;description&#34;&gt; tag." />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="Tag name, displayed on top of the tag page" />
+  <meta name="twitter:title" content="Tag name, displayed on top of the tag page" />
+  <meta property="og:url" content="tag-a-tag.html" />
+  <meta property="og:description" content="Tag summary, used for the social &lt;meta&gt; tags." />
+  <meta name="twitter:description" content="Tag summary, used for the social &lt;meta&gt; tags." />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts tagged <em>A tag</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-info">
+        <h3>Tag name, displayed on top of the tag page</h3>
+        <p>Contents of tag details, displayed on top of the tag page.
+        <a href="http://mcss.mosra.cz">Link.</a></p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-an-author.html">An Author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-a-category.html">A category</a>. Tags: <a href="tag-a-tag.html">A tag</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/tag-minimal-tag.html b/pelican-plugins/m/test/metadata/tag-minimal-tag.html
new file mode 100644 (file)
index 0000000..bdc288f
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Posts tagged Minimal tag | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="Minimal tag" />
+  <meta name="twitter:title" content="Minimal tag" />
+  <meta property="og:url" content="tag-minimal-tag.html" />
+  <meta property="og:description" content="Tag details." />
+  <meta name="twitter:description" content="Tag details." />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts tagged <em>Minimal tag</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-info">
+        <h3>Minimal tag</h3>
+        <p>Tag details.</p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article-minimal.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Article summary.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-minimal-author.html">Minimal author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-minimal-category.html">Minimal category</a>. Tags: <a href="tag-minimal-tag.html">Minimal tag</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A category</a></li>
+        <li><a href="category-category-with-no-image.html">Category with no image</a></li>
+        <li><a href="category-minimal-category.html">Minimal category</a></li>
+      </ol>
+      <h3>Authors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An Author</a></li>
+        <li><a href="author-author-with-no-image.html">Author with no image</a></li>
+        <li><a href="author-minimal-author.html">Minimal author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+        <li class="m-tag-5"><a href="tag-minimal-tag.html">Minimal tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata/tags/a-tag.rst b/pelican-plugins/m/test/metadata/tags/a-tag.rst
new file mode 100644 (file)
index 0000000..2642ea3
--- /dev/null
@@ -0,0 +1,8 @@
+Tag name, displayed on top of the tag page
+##########################################
+
+:description: Tag description, used for a <meta name="description"> tag.
+:summary: Tag summary, used for the social <meta> tags.
+
+Contents of tag details, displayed on top of the tag page.
+`Link. <http://mcss.mosra.cz>`_
diff --git a/pelican-plugins/m/test/metadata/tags/minimal-tag.rst b/pelican-plugins/m/test/metadata/tags/minimal-tag.rst
new file mode 100644 (file)
index 0000000..228c2c6
--- /dev/null
@@ -0,0 +1 @@
+Tag details.
diff --git a/pelican-plugins/m/test/metadata_typography_html_escape/article.html b/pelican-plugins/m/test/metadata_typography_html_escape/article.html
new file mode 100644 (file)
index 0000000..fe081ec
--- /dev/null
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>An article | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <link rel="canonical" href="article.html" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="An article" />
+  <meta name="twitter:title" content="An article" />
+  <meta property="og:url" content="article.html" />
+  <meta property="og:description" content="Article summary." />
+  <meta name="twitter:description" content="Article summary." />
+  <meta property="og:image" content="category.jpg?and&amp;in&amp;url=&#34;&#34;" />
+  <meta name="twitter:image" content="category.jpg?and&amp;in&amp;url=&#34;&#34;" />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="article" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <article class="m-col-m-10 m-nopadb">
+      <header>
+        <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+          <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+            Dec <span class="m-date-day">17</span> 2017
+          </time>
+          An article
+        </a></h1>
+        <p>Ar&shy;ti&shy;cle sum&shy;ma&shy;ry.</p>
+      </header>
+      <div class="m-clearfix-l"></div>
+<!-- content -->
+<p>Ar&shy;ti&shy;cle con&shy;tents.</p>
+<!-- /content -->
+      <div class="m-block m-success m-badge">
+        <img src="author.jpg?and&amp;in&amp;url=&#34;&#34;" alt="An &lt;&amp;&gt; Author" />
+        <h3>About the author</h3>
+        <p>Au&shy;thor &lt;&amp;&gt; badge, hy&shy;phen&shy;at&shy;ed and “quotes”.</p>
+      </div>
+      <div class="m-block m-warning m-badge">
+        <img src="category.jpg?and&amp;in&amp;url=&#34;&#34;" alt="A &lt;&amp;&gt; category" />
+        <h3>A &lt;&amp;&gt; “category”</h3>
+        <p>Cat&shy;e&shy;go&shy;ry &lt;&amp;&gt; badge, hy&shy;phen&shy;at&shy;ed and “quotes”.</p>
+      </div>
+      <footer>
+        <p>Posted by <a href="author-an-author.html">An &lt;&amp;&gt; Author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-a-category.html">A &lt;&amp;&gt; category</a>. Tags: <a href="tag-a-tag.html">A &lt;&amp;&gt; tag</a>.</p>
+      </footer>
+    </article>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Cat&shy;e&shy;gories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A &lt;&amp;&gt; cat&shy;e&shy;go&shy;ry</a></li>
+      </ol>
+      <h3>Au&shy;thors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An &lt;&amp;&gt; Author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A &lt;&amp;&gt; tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata_typography_html_escape/articles/article.rst b/pelican-plugins/m/test/metadata_typography_html_escape/articles/article.rst
new file mode 100644 (file)
index 0000000..1b1f878
--- /dev/null
@@ -0,0 +1,10 @@
+An article
+##########
+
+:date: 2017-12-17
+:author: An <&> Author
+:category: A <&> category
+:tags: A <&> tag
+:summary: Article summary.
+
+Article contents.
diff --git a/pelican-plugins/m/test/metadata_typography_html_escape/author-an-author.html b/pelican-plugins/m/test/metadata_typography_html_escape/author-an-author.html
new file mode 100644 (file)
index 0000000..19d755e
--- /dev/null
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Posts by An &lt;&amp;&gt; Author | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="description" content="Author &lt;&amp;&gt; description, not hyphenated but “quotes”" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="An &lt;&amp;&gt; “Author”" />
+  <meta name="twitter:title" content="An &lt;&amp;&gt; “Author”" />
+  <meta property="og:url" content="author-an-author.html" />
+  <meta property="og:description" content="Author &lt;&amp;&gt; summary, not hyphenated but “quotes”" />
+  <meta name="twitter:description" content="Author &lt;&amp;&gt; summary, not hyphenated but “quotes”" />
+  <meta property="og:image" content="author.jpg?and&amp;in&amp;url=&#34;&#34;" />
+  <meta name="twitter:image" content="author.jpg?and&amp;in&amp;url=&#34;&#34;" />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts by <em>An &lt;&amp;&gt; Author</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-success m-badge">
+        <img src="author.jpg?and&amp;in&amp;url=&#34;&#34;" alt="An &lt;&amp;&gt; Author" />
+        <h3>An &lt;&amp;&gt; “Author”</h3>
+        <p>Au&shy;thor &lt;&amp;&gt; de&shy;tails, hy&shy;phen&shy;at&shy;ed and “quotes”.</p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Ar&shy;ti&shy;cle sum&shy;ma&shy;ry.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-an-author.html">An &lt;&amp;&gt; Author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-a-category.html">A &lt;&amp;&gt; category</a>. Tags: <a href="tag-a-tag.html">A &lt;&amp;&gt; tag</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Cat&shy;e&shy;gories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A &lt;&amp;&gt; cat&shy;e&shy;go&shy;ry</a></li>
+      </ol>
+      <h3>Au&shy;thors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An &lt;&amp;&gt; Author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A &lt;&amp;&gt; tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata_typography_html_escape/authors/an-author.rst b/pelican-plugins/m/test/metadata_typography_html_escape/authors/an-author.rst
new file mode 100644 (file)
index 0000000..c8925ed
--- /dev/null
@@ -0,0 +1,9 @@
+An <&> "Author"
+###############
+
+:image: author.jpg?and&in&url=""
+:description: Author <&> description, not hyphenated but "quotes"
+:summary: Author <&> summary, not hyphenated but "quotes"
+:badge: Author <&> badge, hyphenated and "quotes".
+
+Author <&> details, hyphenated and "quotes".
diff --git a/pelican-plugins/m/test/metadata_typography_html_escape/categories/a-category.rst b/pelican-plugins/m/test/metadata_typography_html_escape/categories/a-category.rst
new file mode 100644 (file)
index 0000000..3fdef2d
--- /dev/null
@@ -0,0 +1,9 @@
+A <&> "category"
+################
+
+:image: category.jpg?and&in&url=""
+:description: Category <&> description, not hyphenated but "quotes"
+:summary: Category <&> summary, not hyphenated but "quotes"
+:badge: Category <&> badge, hyphenated and "quotes".
+
+Category <&> details, hyphenated and "quotes".
diff --git a/pelican-plugins/m/test/metadata_typography_html_escape/category-a-category.html b/pelican-plugins/m/test/metadata_typography_html_escape/category-a-category.html
new file mode 100644 (file)
index 0000000..3e2f0cd
--- /dev/null
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>A &lt;&amp;&gt; category | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="description" content="Category &lt;&amp;&gt; description, not hyphenated but “quotes”" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="A &lt;&amp;&gt; “category”" />
+  <meta name="twitter:title" content="A &lt;&amp;&gt; “category”" />
+  <meta property="og:url" content="category-a-category.html" />
+  <meta property="og:description" content="Category &lt;&amp;&gt; summary, not hyphenated but “quotes”" />
+  <meta name="twitter:description" content="Category &lt;&amp;&gt; summary, not hyphenated but “quotes”" />
+  <meta property="og:image" content="category.jpg?and&amp;in&amp;url=&#34;&#34;" />
+  <meta name="twitter:image" content="category.jpg?and&amp;in&amp;url=&#34;&#34;" />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts in <em>A &lt;&amp;&gt; category</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-warning m-badge">
+        <img src="category.jpg?and&amp;in&amp;url=&#34;&#34;" alt="A &lt;&amp;&gt; category" />
+        <h3>A &lt;&amp;&gt; “category”</h3>
+        <p>Cat&shy;e&shy;go&shy;ry &lt;&amp;&gt; de&shy;tails, hy&shy;phen&shy;at&shy;ed and “quotes”.</p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Ar&shy;ti&shy;cle sum&shy;ma&shy;ry.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-an-author.html">An &lt;&amp;&gt; Author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-a-category.html">A &lt;&amp;&gt; category</a>. Tags: <a href="tag-a-tag.html">A &lt;&amp;&gt; tag</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Cat&shy;e&shy;gories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A &lt;&amp;&gt; cat&shy;e&shy;go&shy;ry</a></li>
+      </ol>
+      <h3>Au&shy;thors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An &lt;&amp;&gt; Author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A &lt;&amp;&gt; tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata_typography_html_escape/tag-a-tag.html b/pelican-plugins/m/test/metadata_typography_html_escape/tag-a-tag.html
new file mode 100644 (file)
index 0000000..b884226
--- /dev/null
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Posts tagged A &lt;&amp;&gt; tag | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="description" content="Tag &lt;&amp;&gt; description, not hyphenated but “quotes”" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="A &lt;&amp;&gt; “tag”" />
+  <meta name="twitter:title" content="A &lt;&amp;&gt; “tag”" />
+  <meta property="og:url" content="tag-a-tag.html" />
+  <meta property="og:description" content="Tag &lt;&amp;&gt; summary, not hyphenated but “quotes”" />
+  <meta name="twitter:description" content="Tag &lt;&amp;&gt; summary, not hyphenated but “quotes”" />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="website" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+    </div>
+  </div>
+</nav></header>
+<main>
+<div class="m-container">
+  <div class="m-row">
+    <div class="m-col-m-10">
+      <div class="m-info m-note">
+        Showing only posts tagged <em>A &lt;&amp;&gt; tag</em>. <a href="./">Show all posts.</a>
+      </div>
+      <div class="m-block m-info">
+        <h3>A &lt;&amp;&gt; “tag”</h3>
+        <p>Tag &lt;&amp;&gt; de&shy;tails, hy&shy;phen&shy;at&shy;ed and “quotes”.</p>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-17T00:00:00+00:00">
+              Dec <span class="m-date-day">17</span> 2017
+            </time>
+            An article
+          </a></h1>
+          <p>Ar&shy;ti&shy;cle sum&shy;ma&shy;ry.</p>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-an-author.html">An &lt;&amp;&gt; Author</a> on <time datetime="2017-12-17T00:00:00+00:00">Dec 17, 2017</time> in <a href="category-a-category.html">A &lt;&amp;&gt; category</a>. Tags: <a href="tag-a-tag.html">A &lt;&amp;&gt; tag</a>.</p>
+        </footer>
+        <div class="m-clearfix-l"></div>
+      </article>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Cat&shy;e&shy;gories</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="category-a-category.html">A &lt;&amp;&gt; cat&shy;e&shy;go&shy;ry</a></li>
+      </ol>
+      <h3>Au&shy;thors</h3>
+      <ol class="m-block-bar-m">
+        <li><a href="author-an-author.html">An &lt;&amp;&gt; Author</a></li>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A &lt;&amp;&gt; tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-plugins/m/test/metadata_typography_html_escape/tags/a-tag.rst b/pelican-plugins/m/test/metadata_typography_html_escape/tags/a-tag.rst
new file mode 100644 (file)
index 0000000..f58a6ea
--- /dev/null
@@ -0,0 +1,8 @@
+A <&> "tag"
+###########
+
+:description: Tag <&> description, not hyphenated but "quotes"
+:summary: Tag <&> summary, not hyphenated but "quotes"
+:badge: Tag <&> badge, hyphenated and "quotes".
+
+Tag <&> details, hyphenated and "quotes".
diff --git a/pelican-plugins/m/test/test_metadata.py b/pelican-plugins/m/test/test_metadata.py
new file mode 100644 (file)
index 0000000..44c8289
--- /dev/null
@@ -0,0 +1,91 @@
+#
+#   This file is part of m.css.
+#
+#   Copyright © 2017 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 Metadata(PluginTestCase):
+    def __init__(self, *args, **kwargs):
+        super().__init__(__file__, '', *args, **kwargs)
+
+    def test(self):
+        self.run_pelican({
+            'PLUGINS': ['m.htmlsanity', 'm.metadata'],
+            'STATIC_PATHS': ['mosra.jpg', 'category.jpg'],
+            'FORMATTED_FIELDS': ['summary', 'description', 'badge'],
+            'DATE_FORMATS': {'en': ('en_US.UTF-8', '%b %d, %Y')},
+            'PAGE_PATHS': ['pages'], # doesn't exist
+            'ARTICLE_PATHS': ['articles'],
+            'AUTHOR_SAVE_AS': 'author-{slug}.html',
+            'AUTHOR_URL': 'author-{slug}.html',
+            'CATEGORY_SAVE_AS': 'category-{slug}.html',
+            'CATEGORY_URL': 'category-{slug}.html',
+            'TAG_SAVE_AS': 'tag-{slug}.html',
+            'TAG_URL': 'tag-{slug}.html',
+            'M_DISABLE_SOCIAL_META_TAGS': False,
+            'M_SHOW_AUTHOR_LIST': True
+        })
+
+        self.assertEqual(*self.actual_expected_contents('article.html'))
+        self.assertEqual(*self.actual_expected_contents('article-no-image.html'))
+        self.assertEqual(*self.actual_expected_contents('article-jumbo.html'))
+        self.assertEqual(*self.actual_expected_contents('article-minimal.html'))
+
+        self.assertEqual(*self.actual_expected_contents('author-an-author.html'))
+        self.assertEqual(*self.actual_expected_contents('author-author-with-no-image.html'))
+        self.assertEqual(*self.actual_expected_contents('author-minimal-author.html'))
+
+        self.assertEqual(*self.actual_expected_contents('category-a-category.html'))
+        self.assertEqual(*self.actual_expected_contents('category-category-with-no-image.html'))
+        self.assertEqual(*self.actual_expected_contents('category-minimal-category.html'))
+
+        self.assertEqual(*self.actual_expected_contents('tag-a-tag.html'))
+        self.assertEqual(*self.actual_expected_contents('tag-minimal-tag.html'))
+
+class TypographyHtmlEscape(PluginTestCase):
+    def __init__(self, *args, **kwargs):
+        super().__init__(__file__, 'typography_html_escape', *args, **kwargs)
+
+    def test(self):
+        self.run_pelican({
+            'PLUGINS': ['m.htmlsanity', 'm.metadata'],
+            'FORMATTED_FIELDS': ['summary', 'description', 'badge'],
+            'DATE_FORMATS': {'en': ('en_US.UTF-8', '%b %d, %Y')},
+            'PAGE_PATHS': ['pages'], # doesn't exist
+            'ARTICLE_PATHS': ['articles'],
+            'AUTHOR_SAVE_AS': 'author-{slug}.html',
+            'AUTHOR_URL': 'author-{slug}.html',
+            'CATEGORY_SAVE_AS': 'category-{slug}.html',
+            'CATEGORY_URL': 'category-{slug}.html',
+            'TAG_SAVE_AS': 'tag-{slug}.html',
+            'TAG_URL': 'tag-{slug}.html',
+            'M_DISABLE_SOCIAL_META_TAGS': False,
+            'M_SHOW_AUTHOR_LIST': True,
+            'M_HTMLSANITY_HYPHENATION': True,
+            'M_HTMLSANITY_SMART_QUOTES': True
+        })
+
+        self.assertEqual(*self.actual_expected_contents('article.html'))
+        self.assertEqual(*self.actual_expected_contents('author-an-author.html'))
+        self.assertEqual(*self.actual_expected_contents('category-a-category.html'))
+        self.assertEqual(*self.actual_expected_contents('tag-a-tag.html'))
index 801aa3f06114dd0c37b7b8bad3622d6e13f0ff01..92ee30fe87a805203b2a6cb64fc5bf37210ed038 100644 (file)
 
 {% block social %}
   {{- super() -}}
-  {# this has to be here otherwise the spacing is all wrong #}
+  {% if article.author and article.author.twitter %}
+  <meta name="twitter:creator" content="{{ article.author.twitter }}" />
+  {% endif %}
+  {% if article.author and article.author.twitter_id %}
+  <meta name="twitter:creator:id" content="{{ article.author.twitter_id }}" />
+  {% endif %}
   <meta property="og:title" content="{{ article.title }}" />
   <meta name="twitter:title" content="{{ article.title }}" />
   <meta property="og:url" content="{{ article.url|format_siteurl|e }}" />
@@ -26,6 +31,9 @@
   <meta property="og:image" content="{{ article.cover|expand_link(article)|e }}" />
   <meta name="twitter:image" content="{{ article.cover|expand_link(article)|e }}" />
   <meta name="twitter:card" content="summary_large_image" />
+  {% elif article.category.image %}
+  <meta property="og:image" content="{{ article.category.image|expand_link(article)|e }}" />
+  <meta name="twitter:image" content="{{ article.category.image|expand_link(article)|e }}" />
   {% elif M_SOCIAL_IMAGE %}
   <meta property="og:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
   <meta name="twitter:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
@@ -36,6 +44,8 @@
   <meta property="og:type" content="article" />
 {% endblock %}
 
+{% macro badges() %}{% include "article_badges.html" %}{% endmacro %}
+
 {% block content %}
   {% if article.cover %}
   <article id="m-jumbo"{% if article.class %} class="{{ article.class }}"{% endif %}>
@@ -80,6 +90,9 @@
 <!-- content -->
 {{ article.content|trim }}
 <!-- /content -->
+          {% if article.category.badge or (article.author and article.author.badge) %}
+          {{ badges()|indent(10) }}
+          {% endif %}
         </div>
       </div>
     </div>
 <!-- content -->
 {{ article.content|trim }}
 <!-- /content -->
+      {% endif %}
+      {% if article.category.badge or (article.author and article.author.badge) %}
+      {{ badges()|indent(6) }}
       {% endif %}
       <footer>
         {% macro footer() %}{% include "article_footer.html" %}{% endmacro %}
diff --git a/pelican-theme/templates/article_badges.html b/pelican-theme/templates/article_badges.html
new file mode 100644 (file)
index 0000000..e62c9d0
--- /dev/null
@@ -0,0 +1,18 @@
+{% if article.author and article.author.badge %}
+<div class="m-block m-success{% if article.author.image %} m-badge{% endif %}">
+  {% if article.author.image %}
+  <img src="{{ article.author.image|expand_link(article)|e }}" alt="{{ article.author|e }}" />
+  {% endif %}
+  <h3>About the author</h3>
+  {{ article.author.badge|expand_links(article)|indent(2) }}
+</div>
+{% endif %}
+{% if article.category.badge %}
+<div class="m-block m-warning{% if article.category.image %} m-badge{% endif %}">
+  {% if article.category.image %}
+  <img src="{{ article.category.image|expand_link(article)|e }}" alt="{{ article.category|e }}" />
+  {% endif %}
+  <h3>{% if article.category.badge_title %}{{ article.category.badge_title }}{% else %}{{ article.category|e }}{% endif %}</h3>
+  {{ article.category.badge|expand_links(article)|indent(2) }}
+</div>
+{% endif %}
index 6ea84ae96e9f138ef0a077f8fcd1659705c079c4..c919d7f59c9606584352b91aa11f189896fa0404 100644 (file)
@@ -2,14 +2,46 @@
 
 {% block title %}Posts by {{ author|e }} | {{ M_BLOG_NAME|e }}{% endblock %}
 
-{% block social_title_url %}
-  <meta property="og:title" content="{{ author|e }}" />
-  <meta name="twitter:title" content="{{ author|e }}" />
+{% block meta %}
+  {% if author.page and author.page.description %}
+  <meta name="description" content="{{ author.page.description|dehyphenate|striptags|e }}" />
+  {% endif %}
+{% endblock %}
+
+{% block social_title_url_image %}
+  {% if author.page and author.page.twitter %}
+  <meta name="twitter:creator" content="{{ author.page.twitter }}" />
+  {% endif %}
+  {% if author.page and author.page.twitter_id %}
+  <meta name="twitter:creator:id" content="{{ author.page.twitter_id }}" />
+  {% endif %}
+  <meta property="og:title" content="{% if author.page and author.page.title %}{{ author.page.title }}{% else %}{{ author|e }}{% endif %}" />
+  <meta name="twitter:title" content="{% if author.page and author.page.title %}{{ author.page.title }}{% else %}{{ author|e }}{% endif %}" />
   <meta property="og:url" content="{{ author.url|format_siteurl|e }}" />
+  {% if author.page and author.page.summary %}
+  <meta property="og:description" content="{{ author.page.summary|dehyphenate|striptags|e }}" />
+  <meta name="twitter:description" content="{{ author.page.summary|dehyphenate|striptags|e }}" />
+  {% endif %}
+  {% if author.page and author.page.image %}
+  <meta property="og:image" content="{{ author.page.image|expand_link(author.page)|e }}" />
+  <meta name="twitter:image" content="{{ author.page.image|expand_link(author.page)|e }}" />
+  {% elif M_SOCIAL_IMAGE %}
+  <meta property="og:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
+  <meta name="twitter:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
+  {% endif %}
 {% endblock %}
 
 {% block content_title %}
       <div class="m-info m-note">
         Showing only posts by <em>{{ author|e }}</em>. <a href="{{ M_BLOG_URL|format_siteurl|e }}">Show all posts.</a>
       </div>
+      {% if author.page and author.page.content and articles_page.number == 1 %}
+      <div class="m-block m-success{% if author.page.image %} m-badge{% endif %}">
+        {% if author.page.image %}
+        <img src="{{ author.page.image|expand_link(author.page)|e }}" alt="{{ author|e }}" />
+        {% endif %}
+        <h3>{% if author.page.title %}{{ author.page.title }}{% else %}{{ author|e }}{% endif %}</h3>
+        {{ author.page.content|indent(8) }}
+      </div>
+      {% endif %}
 {% endblock %}
index f25dbf24f3ba86ec5ba1d57fc1232e517c6ea4de..f442aa86f2e0a1a7b9372611d5d17f71c2b69f9a 100644 (file)
 
 {% block social %}
   {{- super() -}}
-  {% block social_title_url %}
-  {% endblock social_title_url %}
-  {% if M_SOCIAL_IMAGE %}
-  <meta property="og:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
-  <meta name="twitter:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
-  {% endif %}
+  {% block social_title_url_image %}
+  {% endblock social_title_url_image %}
   <meta name="twitter:card" content="summary" />
   <meta property="og:type" content="website" />
 {% endblock %}
index a92f43b9a163f16698394dbc650506402ceed4f0..c3bf5d74c673cdad99c9ea41234c8abf1863f44b 100644 (file)
@@ -2,14 +2,40 @@
 
 {% block title %}{{ category|e }} | {{ M_BLOG_NAME|e }}{% endblock %}
 
-{% block social_title_url %}
-  <meta property="og:title" content="{{ category|e }}" />
-  <meta name="twitter:title" content="{{ category|e }}" />
+{% block meta %}
+  {% if category.page and category.page.description %}
+  <meta name="description" content="{{ category.page.description|dehyphenate|striptags|e }}" />
+  {% endif %}
+{% endblock %}
+
+{% block social_title_url_image %}
+  <meta property="og:title" content="{% if category.page and category.page.title %}{{ category.page.title }}{% else %}{{ category|e }}{% endif %}" />
+  <meta name="twitter:title" content="{% if category.page and category.page.title %}{{ category.page.title }}{% else %}{{ category|e }}{% endif %}" />
   <meta property="og:url" content="{{ category.url|format_siteurl|e }}" />
+  {% if category.page and category.page.summary %}
+  <meta property="og:description" content="{{ category.page.summary|dehyphenate|striptags|e }}" />
+  <meta name="twitter:description" content="{{ category.page.summary|dehyphenate|striptags|e }}" />
+  {% endif %}
+  {% if category.page and category.page.image %}
+  <meta property="og:image" content="{{ category.page.image|expand_link(category.page)|e }}" />
+  <meta name="twitter:image" content="{{ category.page.image|expand_link(category.page)|e }}" />
+  {% elif M_SOCIAL_IMAGE %}
+  <meta property="og:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
+  <meta name="twitter:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
+  {% endif %}
 {% endblock %}
 
 {% block content_title %}
       <div class="m-info m-note">
         Showing only posts in <em>{{ category|e }}</em>. <a href="{{ M_BLOG_URL|format_siteurl|e }}">Show all posts.</a>
       </div>
+      {% if category.page and category.page.content and articles_page.number == 1 %}
+      <div class="m-block m-warning{% if category.page.image %} m-badge{% endif %}">
+        {% if category.page.image %}
+        <img src="{{ category.page.image|expand_link(category.page)|e }}" alt="{{ category|e }}" />
+        {% endif %}
+        <h3>{% if category.page.title %}{{ category.page.title }}{% else %}{{ category|e }}{% endif %}</h3>
+        {{ category.page.content|indent(8) }}
+      </div>
+      {% endif %}
 {% endblock %}
index 37b65c697fd0d987e306dc978b2101962c71cf60..b1cb113a25fdc7bb94d7506c050c2b777d2541bf 100644 (file)
@@ -2,14 +2,34 @@
 
 {% block title %}Posts tagged {{ tag|e }} | {{ M_BLOG_NAME|e }}{% endblock %}
 
-{% block social_title_url %}
-  <meta property="og:title" content="{{ tag|e }}" />
-  <meta name="twitter:title" content="{{ tag|e }}" />
+{% block meta %}
+  {% if tag.page and tag.page.description %}
+  <meta name="description" content="{{ tag.page.description|dehyphenate|striptags|e }}" />
+  {% endif %}
+{% endblock %}
+
+{% block social_title_url_image %}
+  <meta property="og:title" content="{% if tag.page and tag.page.title %}{{ tag.page.title }}{% else %}{{ tag|e }}{% endif %}" />
+  <meta name="twitter:title" content="{% if tag.page and tag.page.title %}{{ tag.page.title }}{% else %}{{ tag|e }}{% endif %}" />
   <meta property="og:url" content="{{ tag.url|format_siteurl|e }}" />
+  {% if tag.page and tag.page.summary %}
+  <meta property="og:description" content="{{ tag.page.summary|dehyphenate|striptags|e }}" />
+  <meta name="twitter:description" content="{{ tag.page.summary|dehyphenate|striptags|e }}" />
+  {% endif %}
+  {% if M_SOCIAL_IMAGE %}
+  <meta property="og:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
+  <meta name="twitter:image" content="{{ M_SOCIAL_IMAGE|format_siteurl|e }}" />
+  {% endif %}
 {% endblock %}
 
 {% block content_title %}
       <div class="m-info m-note">
         Showing only posts tagged <em>{{ tag|e }}</em>. <a href="{{ M_BLOG_URL|format_siteurl|e }}">Show all posts.</a>
       </div>
+      {% if tag.page and tag.page.content and articles_page.number == 1 %}
+      <div class="m-block m-info">
+        <h3>{% if tag.page.title %}{{ tag.page.title }}{% else %}{{ tag|e }}{% endif %}</h3>
+        {{ tag.page.content|indent(8) }}
+      </div>
+      {% endif %}
 {% endblock %}
index 9ab0938a8d7b05adae507e674687939ea8d03373..17052e01c44976a7fae2a02af3fc705d97ba3b2f 100644 (file)
@@ -33,6 +33,7 @@ STATIC_URL = '{path}'
 
 PATH = 'content'
 ARTICLE_PATHS = ['examples']
+ARTICLE_EXCLUDES = ['examples/authors', 'examples/categories', 'examples/tags']
 PAGE_PATHS = ['']
 
 TIMEZONE = 'Europe/Prague'
@@ -56,6 +57,10 @@ M_SOCIAL_TWITTER_SITE_ID = 1537427036
 M_SOCIAL_IMAGE = 'static/site.jpg'
 M_SOCIAL_BLOG_SUMMARY = 'Example articles for the m.css Pelican theme'
 
+M_METADATA_AUTHOR_PATH = 'examples/authors'
+M_METADATA_CATEGORY_PATH = 'examples/categories'
+M_METADATA_TAG_PATH = 'examples/tags'
+
 M_LINKS_NAVBAR1 = [('Why?', 'why/', 'why', []),
                    ('CSS', 'css/', 'css', [
                         ('Grid system', 'css/grid/', 'css/grid'),
@@ -72,7 +77,8 @@ M_LINKS_NAVBAR2 = [('Pelican plugins', 'plugins/', 'plugins', [
                         ('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', 'plugins/links/', 'plugins/links'),
+                        ('Metadata', 'plugins/metadata/', 'plugins/metadata')]),
                    ('Doxygen theme', 'doxygen/', 'doxygen', []),
                    ('GitHub', 'https://github.com/mosra/m.css', '', [])]
 
@@ -101,7 +107,8 @@ M_LINKS_FOOTER4 = [('Pelican plugins', 'plugins/'),
                    ('Components', 'plugins/components/'),
                    ('Images', 'plugins/images/'),
                    ('Math and code', 'plugins/math-and-code/'),
-                   ('Links', 'plugins/links/')]
+                   ('Links', 'plugins/links/'),
+                   ('Metadata', 'plugins/metadata/')]
 
 M_FINE_PRINT = """
 m.css. Copyright © Vladimír Vondruš 2017. Site powered by `Pelican <https://getpelican.com>`_
@@ -125,7 +132,8 @@ PLUGINS = ['m.abbr',
            'm.gh',
            'm.htmlsanity',
            'm.images',
-           'm.math']
+           'm.math',
+           'm.metadata']
 
 THEME = '../pelican-theme'
 THEME_STATIC_DIR = 'static'
@@ -137,7 +145,7 @@ M_CSS_FILES = ['https://fonts.googleapis.com/css?family=Source+Code+Pro:400,400i
 #M_CSS_FILES = ['https://fonts.googleapis.com/css?family=Libre+Baskerville:400,400i,700,700i%7CSource+Code+Pro:400,400i,600',
                #'/static/m-light.css']
 
-FORMATTED_FIELDS = ['summary', 'landing', 'header', 'footer']
+FORMATTED_FIELDS = ['summary', 'landing', 'header', 'footer', 'description', 'badge']
 
 M_HTMLSANITY_SMART_QUOTES = True
 M_HTMLSANITY_HYPHENATION = True
@@ -154,8 +162,8 @@ ARTICLE_URL = '{category}/{slug}/'
 ARTICLE_SAVE_AS = '{category}/{slug}/index.html'
 AUTHOR_URL = 'author/{slug}/'
 AUTHOR_SAVE_AS = 'author/{slug}/index.html'
-CATEGORY_URL = 'examples/' # Aliases with the archives page
-CATEGORY_SAVE_AS = '' # The archives page is used instead
+CATEGORY_URL = 'category/{slug}/'
+CATEGORY_SAVE_AS = 'category/{slug}/index.html'
 TAG_URL = 'tag/{slug}/'
 TAG_SAVE_AS = 'tag/{slug}/index.html'