chiark / gitweb /
theme: ability to set global social meta tags.
authorVladimír Vondruš <mosra@centrum.cz>
Sat, 16 Dec 2017 16:31:21 +0000 (17:31 +0100)
committerVladimír Vondruš <mosra@centrum.cz>
Sat, 16 Dec 2017 16:50:51 +0000 (17:50 +0100)
18 files changed:
doc/pelican/theme.rst
pelican-theme/templates/archives.html
pelican-theme/templates/article.html
pelican-theme/templates/base.html
pelican-theme/templates/base_blog_section.html
pelican-theme/templates/page.html
pelican-theme/test/blog_global_social_meta/article.html [new file with mode: 0644]
pelican-theme/test/blog_global_social_meta/article.rst [new file with mode: 0644]
pelican-theme/test/blog_global_social_meta/author-the-author.html [new file with mode: 0644]
pelican-theme/test/blog_global_social_meta/category-a-category.html [new file with mode: 0644]
pelican-theme/test/blog_global_social_meta/tag-a-tag.html [new file with mode: 0644]
pelican-theme/test/layout_global_social_meta/archives.html [new file with mode: 0644]
pelican-theme/test/layout_global_social_meta/index.html [new file with mode: 0644]
pelican-theme/test/page_global_social_meta/page.html [new file with mode: 0644]
pelican-theme/test/page_global_social_meta/page.rst [new file with mode: 0644]
pelican-theme/test/test_blog.py
pelican-theme/test/test_layout.py
pelican-theme/test/test_page.py

index 6257a1f5f5b7360451024652ffcd0d3867078f46..ddcc699c320a5ef8404b7ec29c85188c626d1c96 100644 (file)
@@ -34,6 +34,7 @@ Theme
 
 .. role:: rst(code)
     :language: rst
+.. |x| unicode:: U+2715 .. nicer multiply sign
 
 The second largest offering of m.css is a full-featured theme for the
 `Pelican static site generator <https://getpelican.com/>`_. The theme is
@@ -223,6 +224,85 @@ documentation, populating the last column implicitly:
     reserved.
     """
 
+`(Social) meta tags`_
+---------------------
+
+The :rst:`M_BLOG_DESCRIPTION` setting, if available, is used to populate
+:html:`<meta name="description">` on the index / archive page, which can be
+then shown in search engine results. For sharing pages on Twitter, Facebook and
+elsewhere, it's possible to configure site-wide `Open Graph <http://ogp.me/>`_
+and `Twitter Card <https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary-card-with-large-image>`_
+:html:`<meta>` tags:
+
+-   ``og:site_name`` is set to :py:`M_SOCIAL_SITE_NAME`, if available
+-   ``twitter:site`` / ``twitter:site:id`` is set to :py:`M_SOCIAL_TWITTER_SITE`
+    / :py:`M_SOCIAL_TWITTER_SITE_ID``, if available
+-   Global ``og:title`` / ``twitter:title`` is set to :py:`M_BLOG_NAME` on
+    index and archive pages and to category/author/tag name on particular
+    filtering pages. This is overriden by particular pages and articles.
+-   Global ``og:url`` is set to :py:`M_BLOG_URL` on index and archive pages and
+    to category/author/tag URL on particular filtering pages. Pagination is
+    *not* included in the URL. This is overriden by particular pages and
+    articles.
+-   Global ``og:image`` / ``twitter:image`` is set to the
+    :py:`M_SOCIAL_IMAGE` setting, if available. The image is expected to be
+    smaller and square; Pelican internal linking capabilities are *not*
+    supported in this setting. This can be overriden by particular pages and
+    articles.
+-   Global ``twitter:card`` is set to ``summary``. This is further affected by
+    metadata of particular pages and articles.
+-   Global ``og:description`` / ``twitter:description`` is set to
+    :py:`M_SOCIAL_BLOG_SUMMARY` on index and archive pages.
+-   Global ``og:type`` is set to ``website``. This is overriden by particular
+    pages and articles.
+
+See `(Social) meta tags for pages`_ and `(Social) meta tags for articles`_
+sections below for page- and article-specific :html:`<meta>` tags.
+
+.. note-danger::
+
+    The :html:`<meta name="keywords">` tag is not supported, as it doesn't
+    have any effect on search engine results at all.
+
+Example configuration to give sane defaults to all social meta tags:
+
+.. code:: py
+
+    M_BLOG_NAME = "Your Brand Blog"
+    M_BLOG_URL = 'http://blog.your.brand/'
+    M_BLOG_DESCRIPTION = "Your Brand is the brand that provides all that\'s needed."
+
+    M_SOCIAL_TWITTER_SITE = '@your.brand'
+    M_SOCIAL_TWITTER_SITE_ID = 1234567890
+    M_SOCIAL_IMAGE = 'http://your.brand/static/site.png'
+    M_SOCIAL_BLOG_SUMMARY = "This is the brand you need"
+
+.. block-success:: Recommended sizes for global site image
+
+    The theme assumes that the global site image is smaller and square in order
+    to appear just as a small thumbnail next to a link, not as large cover
+    image above it --- the reasoning beind is that there's no point in annoying
+    the users by decorating the global site links with the exact same large
+    image.
+
+    For Twitter, this is controlled explicitly by setting ``twitter:card``
+    to ``summary`` instead of ``summary_large_image``, but in case of Facebook,
+    it's needed to rely on their autodetection.
+    `Their documentation <https://developers.facebook.com/docs/sharing/best-practices/#images>`_
+    says that images smaller than 600\ |x|\ 315 px are displayed as small
+    thumbnails. Square image of size 256\ |x|\ 256 px is known to work well.
+
+    Note that the assumptions are different for pages and articles with
+    explicit cover images, see `(Social) meta tags for pages`_ below for
+    details.
+
+.. note-info::
+
+    You can see how links for default pages will display by pasting
+    URL of the `article listing page <{category}examples>`_ into either
+    `Facebook Debugger <https://developers.facebook.com/tools/debug/>`_ or
+    `Twitter Card Validator <https://cards-dev.twitter.com/validator>`_.
+
 `Pages`_
 ========
 
@@ -386,10 +466,13 @@ above:
 `(Social) meta tags for pages`_
 -------------------------------
 
-You can use :rst:`:description:` field to populate :html:`<meta name="description">`,
-which can be then shown in search engine results. Other than that, the field
-does not appear anywhere on the rendered page. It's recommended to add it to
-:py:`FORMATTED_FIELDS` so you can make use of the
+Every page has :html:`<link rel="canonical">` pointing to its URL to avoid
+duplicates in search engines when using GET parameters. In addition to the
+global meta tags described in `(Social) meta tags`_ above, you can use the
+:rst:`:description:` field to populate :html:`<meta name="description">`. Other
+than that, the field does not appear anywhere on the rendered page. If such
+field is not set, the description :html:`<meta>` tag is not rendered at all.
+It's recommended to add it to :py:`FORMATTED_FIELDS` so you can make use of the
 `advanced typography features <{filename}/plugins/htmlsanity.rst#typography>`_
 like smart quotes etc. in it:
 
@@ -397,9 +480,8 @@ like smart quotes etc. in it:
 
     FORMATTED_FIELDS += ['description']
 
-For sharing pages on Twitter, Facebook and elsewhere, both `Open Graph <http://ogp.me/>`_
-and `Twitter Card <https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary-card-with-large-image>`_
-:html:`<meta>` tags are supported:
+The global `Open Graph`_ and `Twitter Card`_ :html:`<meta>` tags are
+specialized for pages like this:
 
 -   Page title is mapped to ``og:title`` / ``twitter:title``
 -   Page URL is mapped to ``og:url``
@@ -409,10 +491,10 @@ and `Twitter Card <https://developer.twitter.com/en/docs/tweets/optimize-with-ca
     that may not be what you want. This is also different from the
     :rst:`:description:` field mentioned above and, unlike with articles,
     :rst:`:summary:` doesn't appear anywhere on the rendered page.
--   The :rst:`:cover:` field (e.g. the one used on `landing pages <#landing-pages>`_),
-    if present, is mapped to ``og:image`` / ``twitter:image``. The exact same
-    file is used without any resizing or cropping and is assumed to be in
-    landscape.
+-   The :rst:`:cover:` field (e.g. the one used on `landing pages`_), if
+    present, is mapped to ``og:image`` / ``twitter:image``, overriding the
+    global :py:`M_SOCIAL_IMAGE` setting. The exact same file is used without
+    any resizing or cropping and is assumed to be in landscape.
 -   ``twitter:card`` is set to ``summary_large_image`` if :rst:`:cover:` is
     present and to ``summary`` otherwise
 -   ``og:type`` is set to ``page``
@@ -430,12 +512,26 @@ social links:
     :cover: {filename}/static/cover.jpg
     :summary: This is the brand you need.
 
-.. note-success::
+.. block-success:: Recommended sizes for cover images
 
-    You can see how page links will display by pasting
-    URL of the `index page <{filename}/index.rst>`_ into either
-    `Facebook Debugger <https://developers.facebook.com/tools/debug/>`_ or
-    `Twitter Card Validator <https://cards-dev.twitter.com/validator>`_.
+    Unlike the global site image described in `(Social) meta tags`_,
+    page-specific cover images are assumed to be larger and in landscape to
+    display large on top of the link, as they should act to promote the
+    particular content instead of being just a decoration.
+
+    `Facebook recommendations for the cover image <https://developers.facebook.com/docs/sharing/best-practices/#images>`_
+    say that the image should have 1.91:1 aspect ratio and be ideally at least
+    1200\ |x|\ 630 px large, while `Twitter recommends <https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary-card-with-large-image>`_ 2:1 aspect ratio and at
+    most 4096\ |x|\ 4096 px. In case of Twitter, the large image display is
+    controlled explicitly by having ``twitter:card`` set to ``summary_large_image``,
+    but for Facebook one needs to rely on their autodetection. Make sure the
+    image is at least 600\ |x|\ 315 px to avoid fallback to a small thumbnail.
+
+.. note-info::
+
+    You can see how page links will display by pasting URL of the
+    `index page <{filename}/index.rst>`_ into either `Facebook Debugger`_ or
+    `Twitter Card Validator`_.
 
 `Articles`_
 ===========
@@ -494,11 +590,16 @@ invert text color on cover, add a :rst:`:class:` field containing the
 `(Social) meta tags for articles`_
 ----------------------------------
 
-Like with pages, you can use :rst:`:description:` field to populate
-:html:`<meta name="description">`, which can be then shown in search engine
-results. Other than that, the field doesn't appear anywhere in the rendered
-article. `Open Graph`_ and `Twitter Card`_ :html:`<meta>` tags are also
-supported in a similar way:
+Every article has :html:`<link rel="canonical">` pointing to its URL to avoid
+duplicates in search engines when using GET parameters. In addition to the
+global meta tags described in `(Social) meta tags`_ above, you can use the
+:rst:`:description:` field to populate :html:`<meta name="description">`. Other
+than that, the field doesn't appear anywhere in the rendered article.  If such
+field is not set, the description :html:`<meta>` tag is not rendered at all.
+Again, it's recommended to add it to :py:`FORMATTED_FIELDS`.
+
+The global `Open Graph`_ and `Twitter Card`_ :html:`<meta>` tags are
+specialized for articles like this:
 
 -   Article title is mapped to ``og:title`` / ``twitter:title``
 -   Article URL is mapped to ``og:url``
@@ -507,15 +608,16 @@ supported in a similar way:
     summary, Pelican takes it from the first few sentences of the content and
     that may not be what you want. This is also different from the
     :rst:`:description:` field mentioned above.
--   The :rst:`:cover:` field from `jumbo articles <#jumbo-articles>`_, if
-    present, is mapped to ``og:image`` / ``twitter:image``. The exact same
-    file is used without any resizing or cropping and is assumed to be in
-    landscape.
+-   The :rst:`:cover:` field from `jumbo articles`_, if present, is mapped to
+    ``og:image`` / ``twitter:image``, overriding the global :py:`M_SOCIAL_IMAGE`
+    setting. The exact same file is used without any resizing or cropping and
+    is assumed to be in landscape. See `(Social) meta tags for pages`_ above
+    for image size recommendations.
 -   ``twitter:card`` is set to ``summary_large_image`` if :rst:`:cover:` is
     present and to ``summary`` otherwise
 -   ``og:type`` is set to ``article``
 
-.. note-success::
+.. note-info::
 
     You can see how article links will display by pasting
     URL of e.g. the `jumbo article`_ into either `Facebook Debugger`_ or
index 08fe337ac481335991588a9cd6f1877eac8e8af2..7de533a5248ab07e0d4e9b7f565621f02adcf33a 100644 (file)
   {% endif %}
 {% endblock %}
 
+{% block meta %}
+  {% if M_BLOG_DESCRIPTION %}
+  <meta name="description" content="{{ M_BLOG_DESCRIPTION|e }}" />
+  {% endif %}
+{% endblock %}
+
 {% block social %}
   {{- super() -}}
   {# this has to be here otherwise the spacing is all wrong. fuck. #}
   {% block social_url %}
   <meta property="og:url" content="{{ M_BLOG_URL|format_siteurl|e }}" />
   {% endblock %}
+  {% if M_SOCIAL_BLOG_SUMMARY %}
+  <meta property="og:description" content="{{ M_SOCIAL_BLOG_SUMMARY|e }}" />
+  <meta name="twitter:description" content="{{ M_SOCIAL_BLOG_SUMMARY|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 %}
   <meta name="twitter:card" content="summary" />
   <meta property="og:type" content="website" />
 {% endblock %}
index fe65e9047a103ffcaf5c0c1287b09defda68816a..801aa3f06114dd0c37b7b8bad3622d6e13f0ff01 100644 (file)
   <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" />
-  {% else %}
+  {% 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 %}
+  {% if not article.cover %}
   <meta name="twitter:card" content="summary" />
   {% endif %}
   <meta property="og:type" content="article" />
index 90fa8a409d19b64bd77e9634dad19eae8c9538c1..146c9db75dcbe5a02c83048768b52ca789eb5667 100644 (file)
   {% endif %}
   {% block meta %}
   {% endblock meta %}
+  {% if M_SOCIAL_TWITTER_SITE %}
+  <meta name="twitter:site" content="{{ M_SOCIAL_TWITTER_SITE }}" />
+  {% endif %}
+  {% if M_SOCIAL_TWITTER_SITE_ID %}
+  <meta name="twitter:site:id" content="{{ M_SOCIAL_TWITTER_SITE_ID }}" />
+  {% endif %}
   {% block social %}
   {% endblock social %}
 </head>
index fb02ccdbd76a4a6e5d593995755e43009beb81d4..f25dbf24f3ba86ec5ba1d57fc1232e517c6ea4de 100644 (file)
   {{- 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 %}
   <meta name="twitter:card" content="summary" />
   <meta property="og:type" content="website" />
 {% endblock %}
index cf5c6233a8baa08074ac47f9580b33751e36c98f..737119c2f5bf90a01ee4e82aa78df35e4cf0a952 100644 (file)
   <meta property="og:image" content="{{ page.cover|expand_link(page)|e }}" />
   <meta name="twitter:image" content="{{ page.cover|expand_link(page)|e }}" />
   <meta name="twitter:card" content="summary_large_image" />
-  {% else %}
+  {% elif M_SOCIAL_IMAGE %}
+  <meta property="og:image" content="{{ M_SOCIAL_IMAGE|format_siteurl }}" />
+  <meta name="twitter:image" content="{{ M_SOCIAL_IMAGE|format_siteurl }}" />
+  {% endif %}
+  {% if not page.cover %}
   <meta name="twitter:card" content="summary" />
   {% endif %}
   <meta property="og:type" content="page" />
diff --git a/pelican-theme/test/blog_global_social_meta/article.html b/pelican-theme/test/blog_global_social_meta/article.html
new file mode 100644 (file)
index 0000000..ffaad89
--- /dev/null
@@ -0,0 +1,60 @@
+<!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+Code+Pro:400,400i,600%7CSource+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 name="twitter:site" content="@czmosra" />
+  <meta name="twitter:site:id" content="1537427036" />
+  <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:image" content="http://your.brand/static/site.png" />
+  <meta name="twitter:image" content="http://your.brand/static/site.png" />
+  <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-16T00:00:00+00:00">
+            Dec <span class="m-date-day">16</span> 2017
+          </time>
+          An article
+        </a></h1>
+      </header>
+      <div class="m-clearfix-l"></div>
+      <footer>
+        <p>Posted by <a href="author-the-author.html">The 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>. 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>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-theme/test/blog_global_social_meta/article.rst b/pelican-theme/test/blog_global_social_meta/article.rst
new file mode 100644 (file)
index 0000000..516096a
--- /dev/null
@@ -0,0 +1,7 @@
+An article
+##########
+
+:date: 2017-12-16
+:category: A category
+:tags: A tag
+:author: The Author
diff --git a/pelican-theme/test/blog_global_social_meta/author-the-author.html b/pelican-theme/test/blog_global_social_meta/author-the-author.html
new file mode 100644 (file)
index 0000000..093066a
--- /dev/null
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Posts by The Author | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,400i,600%7CSource+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="twitter:site" content="@czmosra" />
+  <meta name="twitter:site:id" content="1537427036" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="The Author" />
+  <meta name="twitter:title" content="The Author" />
+  <meta property="og:url" content="author-the-author.html" />
+  <meta property="og:image" content="http://your.brand/static/site.png" />
+  <meta name="twitter:image" content="http://your.brand/static/site.png" />
+  <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>The Author</em>. <a href="./">Show all posts.</a>
+      </div>
+      <article>
+        <header>
+          <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-16T00:00:00+00:00">
+              Dec <span class="m-date-day">16</span> 2017
+            </time>
+            An article
+          </a></h1>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-the-author.html">The 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>. 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>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-theme/test/blog_global_social_meta/category-a-category.html b/pelican-theme/test/blog_global_social_meta/category-a-category.html
new file mode 100644 (file)
index 0000000..d63db06
--- /dev/null
@@ -0,0 +1,64 @@
+<!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+Code+Pro:400,400i,600%7CSource+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="twitter:site" content="@czmosra" />
+  <meta name="twitter:site:id" content="1537427036" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="A category" />
+  <meta name="twitter:title" content="A category" />
+  <meta property="og:url" content="category-a-category.html" />
+  <meta property="og:image" content="http://your.brand/static/site.png" />
+  <meta name="twitter:image" content="http://your.brand/static/site.png" />
+  <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>
+      <article>
+        <header>
+          <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-16T00:00:00+00:00">
+              Dec <span class="m-date-day">16</span> 2017
+            </time>
+            An article
+          </a></h1>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-the-author.html">The 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>. 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>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-theme/test/blog_global_social_meta/tag-a-tag.html b/pelican-theme/test/blog_global_social_meta/tag-a-tag.html
new file mode 100644 (file)
index 0000000..332a594
--- /dev/null
@@ -0,0 +1,64 @@
+<!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+Code+Pro:400,400i,600%7CSource+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="twitter:site" content="@czmosra" />
+  <meta name="twitter:site:id" content="1537427036" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="A tag" />
+  <meta name="twitter:title" content="A tag" />
+  <meta property="og:url" content="tag-a-tag.html" />
+  <meta property="og:image" content="http://your.brand/static/site.png" />
+  <meta name="twitter:image" content="http://your.brand/static/site.png" />
+  <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>
+      <article>
+        <header>
+          <h1><a href="article.html" rel="bookmark" title="Permalink to An article">
+            <time class="m-date" datetime="2017-12-16T00:00:00+00:00">
+              Dec <span class="m-date-day">16</span> 2017
+            </time>
+            An article
+          </a></h1>
+        </header>
+        <footer>
+          <p>Posted by <a href="author-the-author.html">The 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>. 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>
+      </ol>
+      <h3>Tag cloud</h3>
+      <ul class="m-tagcloud">
+        <li class="m-tag-5"><a href="tag-a-tag.html">A tag</a></li>
+      </ul>
+    </nav>
+  </div>
+</div>
+</main>
+</body>
+</html>
diff --git a/pelican-theme/test/layout_global_social_meta/archives.html b/pelican-theme/test/layout_global_social_meta/archives.html
new file mode 100644 (file)
index 0000000..0b57a3a
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Your Brand Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,400i,600%7CSource+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="http://your.brand/static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="description" content="Your Brand provides everything you&#39;ll ever need." />
+  <meta name="twitter:site" content="@czmosra" />
+  <meta name="twitter:site:id" content="1537427036" />
+  <meta property="og:site_name" content="Your Brand Blog" />
+  <meta property="og:title" content="Your Brand Blog" />
+  <meta name="twitter:title" content="Your Brand Blog" />
+  <meta property="og:url" content="http://blog.your.brand/" />
+  <meta property="og:description" content="This is The Brand." />
+  <meta name="twitter:description" content="This is The Brand." />
+  <meta property="og:image" content="http://your.brand/static/site.png" />
+  <meta name="twitter:image" content="http://your.brand/static/site.png" />
+  <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-note m-success">
+        <h3>Congratulations!</h3>
+        The m.css theme is alive and kicking! Now, feed it some articles so it doesn't feel so empty :)
+      </div>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><em class="m-text m-dim">(none yet)</em></li>
+      </ol>
+    </nav>
+  </div>
+</div>
+</main>
+<footer><nav>
+  <div class="m-container">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <p>A Pelican Blog. Powered by <a href="https://getpelican.com">Pelican</a> and <a href="http://mcss.mosra.cz">m.css</a>.</p>
+      </div>
+    </div>
+  </div>
+</nav></footer>
+</body>
+</html>
diff --git a/pelican-theme/test/layout_global_social_meta/index.html b/pelican-theme/test/layout_global_social_meta/index.html
new file mode 100644 (file)
index 0000000..ec23f20
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>Your Brand Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,400i,600%7CSource+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="http://your.brand/static/m-dark.css" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="description" content="Your Brand provides everything you&#39;ll ever need." />
+  <meta name="twitter:site" content="@czmosra" />
+  <meta name="twitter:site:id" content="1537427036" />
+  <meta property="og:site_name" content="Your Brand Blog" />
+  <meta property="og:title" content="Your Brand Blog" />
+  <meta name="twitter:title" content="Your Brand Blog" />
+  <meta property="og:url" content="http://your.brand/" />
+  <meta property="og:description" content="This is The Brand." />
+  <meta name="twitter:description" content="This is The Brand." />
+  <meta property="og:image" content="http://your.brand/static/site.png" />
+  <meta name="twitter:image" content="http://your.brand/static/site.png" />
+  <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-note m-success">
+        <h3>Congratulations!</h3>
+        The m.css theme is alive and kicking! Now, feed it some articles so it doesn't feel so empty :)
+      </div>
+    </div>
+    <nav class="m-navpanel m-col-m-2">
+      <h3>Categories</h3>
+      <ol class="m-block-bar-m">
+        <li><em class="m-text m-dim">(none yet)</em></li>
+      </ol>
+    </nav>
+  </div>
+</div>
+</main>
+<footer><nav>
+  <div class="m-container">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <p>A Pelican Blog. Powered by <a href="https://getpelican.com">Pelican</a> and <a href="http://mcss.mosra.cz">m.css</a>.</p>
+      </div>
+    </div>
+  </div>
+</nav></footer>
+</body>
+</html>
diff --git a/pelican-theme/test/page_global_social_meta/page.html b/pelican-theme/test/page_global_social_meta/page.html
new file mode 100644 (file)
index 0000000..5607fa5
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+  <meta charset="UTF-8" />
+  <title>A page | A Pelican Blog</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,400i,600%7CSource+Sans+Pro:400,400i,600,600i" />
+  <link rel="stylesheet" href="static/m-dark.css" />
+  <link rel="canonical" href="page.html" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="twitter:site" content="@czmosra" />
+  <meta name="twitter:site:id" content="1537427036" />
+  <meta property="og:site_name" content="A Pelican Blog" />
+  <meta property="og:title" content="A page" />
+  <meta name="twitter:title" content="A page" />
+  <meta property="og:url" content="page.html" />
+  <meta property="og:image" content="http://your.brand/static/site.png" />
+  <meta name="twitter:image" content="http://your.brand/static/site.png" />
+  <meta name="twitter:card" content="summary" />
+  <meta property="og:type" content="page" />
+</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>
+<article>
+  <div class="m-container m-container-inflatable">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <h1>A page</h1>
+      </div>
+    </div>
+  </div>
+</article>
+</main>
+</body>
+</html>
diff --git a/pelican-theme/test/page_global_social_meta/page.rst b/pelican-theme/test/page_global_social_meta/page.rst
new file mode 100644 (file)
index 0000000..58c8b8c
--- /dev/null
@@ -0,0 +1,2 @@
+A page
+######
index f1870c556412aac09f20c221c5493739d8b65546..2f01cc25688ae925b1853f1a224d1e92f3e421c4 100644 (file)
@@ -463,3 +463,22 @@ class HtmlEscape(BlogTestCase):
 
         self.assertEqual(*self.actual_expected_contents('article.html'))
         self.assertEqual(*self.actual_expected_contents('article-jumbo.html'))
+
+class GlobalSocialMeta(BlogTestCase):
+    def __init__(self, *args, **kwargs):
+        super().__init__(__file__, 'global_social_meta', *args, **kwargs)
+
+    def test(self):
+        self.run_pelican({
+            'M_BLOG_DESCRIPTION': 'This is not displayed anywhere.',
+            'M_SOCIAL_TWITTER_SITE': '@czmosra',
+            'M_SOCIAL_TWITTER_SITE_ID': '1537427036',
+            'M_SOCIAL_IMAGE': 'http://your.brand/static/site.png',
+            'M_SOCIAL_BLOG_SUMMARY': 'This is also not displayed anywhere.'
+        })
+
+        # Verify that the social meta tags are present in all pages
+        self.assertEqual(*self.actual_expected_contents('article.html'))
+        self.assertEqual(*self.actual_expected_contents('category-a-category.html'))
+        self.assertEqual(*self.actual_expected_contents('author-the-author.html'))
+        self.assertEqual(*self.actual_expected_contents('tag-a-tag.html'))
index fdd8e48f54dceeb519708e2162fce63f59b4a161..108f5644db426e0f1867d562f09c07d1076b5c4c 100644 (file)
@@ -213,3 +213,24 @@ class HtmlEscape(BaseTestCase):
         # Verify that everything is properly escaped everywhere
         self.assertEqual(*self.actual_expected_contents('index.html'))
         self.assertEqual(*self.actual_expected_contents('archives.html', 'index.html'))
+
+class GlobalSocialMeta(BaseTestCase):
+    def __init__(self, *args, **kwargs):
+        super().__init__(__file__, 'global_social_meta', *args, **kwargs)
+
+    def test(self):
+        self.run_pelican({
+            'SITEURL': 'http://your.brand',
+            'M_BLOG_NAME': 'Your Brand Blog',
+            'M_BLOG_URL': 'http://blog.your.brand/',
+            'M_BLOG_DESCRIPTION': 'Your Brand provides everything you\'ll ever need.',
+            'M_SOCIAL_TWITTER_SITE': '@czmosra',
+            'M_SOCIAL_TWITTER_SITE_ID': '1537427036',
+            'M_SOCIAL_IMAGE': 'http://your.brand/static/site.png',
+            'M_SOCIAL_BLOG_SUMMARY': 'This is The Brand.'
+        })
+
+        # Verify that the social meta tags are present. Archives should have a
+        # different og:url but nothing else.
+        self.assertEqual(*self.actual_expected_contents('index.html'))
+        self.assertEqual(*self.actual_expected_contents('archives.html'))
index d11ba89424c82d5d9125fb05b2992e936a892a4f..160ebe0c2d0c3e8409f1680e14c8946224d47b0c 100644 (file)
@@ -144,3 +144,19 @@ class HtmlEscape(PageTestCase):
         # Verify that also the Pelican-produced content has correctly escaped
         # everything.
         self.assertEqual(*self.actual_expected_contents('content.html'))
+
+class GlobalSocialMeta(PageTestCase):
+    def __init__(self, *args, **kwargs):
+        super().__init__(__file__, 'global_social_meta', *args, **kwargs)
+
+    def test(self):
+        self.run_pelican({
+            'M_BLOG_DESCRIPTION': 'This is not displayed anywhere.',
+            'M_SOCIAL_TWITTER_SITE': '@czmosra',
+            'M_SOCIAL_TWITTER_SITE_ID': '1537427036',
+            'M_SOCIAL_IMAGE': 'http://your.brand/static/site.png',
+            'M_SOCIAL_BLOG_SUMMARY': 'This is also not displayed anywhere.'
+        })
+
+        # Verify that the social meta tags are present
+        self.assertEqual(*self.actual_expected_contents('page.html'))