summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2017-04-26 12:03:51 +0200
committerLars Knoll <lars.knoll@qt.io>2017-05-04 10:25:02 +0000
commit1f8f34167be8bede05f5442d9969caf055f3c031 (patch)
tree1d0c8465fd8b02785ca3e61e6f045418e9ec8f53
parent7fad692328ffa099e31d6d9a8d27d7aff796f655 (diff)
Add site generation scripts
Things left for future work: * Sort out a suitable host on which to publish this (QTQAINFRA-1211) * Set up a routine site-publishing cron job (QTQAINFRA-1211) * Make links of entries in some headers (QTQAINFRA-1212) Task-number: QTQAINFRA-1173 Started-by: Louai Al-Khanji <louai.al-khanji@qt.io> Change-Id: I7ee0c37e38cf30de52684ac80cb7848ec8712a0e Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io> Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
-rw-r--r--.gitignore8
-rw-r--r--.gitmodules4
-rw-r--r--ReadMe.rst149
-rw-r--r--docutils/ReadMe.txt13
-rw-r--r--docutils/docutils.conf6
-rw-r--r--docutils/quip.css344
m---------planetqt0
-rw-r--r--quips.pro38
-rwxr-xr-xscripts/gen-quip-0000.py73
-rwxr-xr-xscripts/quip2html.py265
-rw-r--r--scripts/setup.mk21
-rw-r--r--scripts/template.diff73
12 files changed, 994 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bc2d6bb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+# Transient file created by patch (see ReadMe.rst):
+/scripts/template.html
+/scripts/template.rej
+# Generated (if building in source):
+/html/
+/template.html
+/Makefile
+/.qmake.stash
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..f61321c
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "planetqt"]
+ path = planetqt
+ url = ../../www/planetqt.git
+ branch = master
diff --git a/ReadMe.rst b/ReadMe.rst
new file mode 100644
index 0000000..830b3cc
--- /dev/null
+++ b/ReadMe.rst
@@ -0,0 +1,149 @@
+QUIP module maintenance
+=======================
+
+This module contains
+* The reStructuredText sources of all QUIPs (``quip-*.txt``)
+* The scripts to process them (``quips.pro`` and ``scripts/``)
+* A copy of Planet Qt as a sub-module
+
+See ``html/``, once generated, for further details, or the preamble
+for ``html/quip-000.html`` defined in ``scripts/gen-quip-0000.py`` for
+a preview of what this is all about.
+
+Generating HTML from QUIPs
+--------------------------
+
+You'll need this module checked out, along with its sub-module: see
+`Planet Qt and the template`_ for how to get Planet Qt checked out and
+up to date.
+
+You can generate the HTML using ``qmake`` and ``make``, provided you
+have (them and) both ``python`` and its ``docutils`` package
+installed.
+I recommend a shadow build: in some directory outside this source
+tree, run ``qmake`` on ``quips.pro`` in this directory; then, in the
+same directory, run ``make`` and it should generate a sub-directory
+``html/`` containing the generated HTML and symlinks to the supporting
+material from Planet Qt.
+You can then point a web browser at the result and review it.
+
+You can, of course, do the same in the source tree; you'll clutter it
+with the generated content.
+While ``.gitignore`` should save you from committing that, you can
+clean up more robustly in an out-of-source build.
+
+Checking
+~~~~~~~~
+
+It may be constructive to use checklink_ on the resulting HTML.
+On Debian, this is available for local use from the package named
+``w3c-linkcheker``; other systems probably have something similar.
+It can be run (be patient; it takes several minutes) as::
+
+ checklink -qbr html/quip-*.html
+
+If that finds any broken links, check to see whether they derive from
+the template (report to the planetqt maintainers) or from the source
+of the QUIP (``quip-*.txt``, report to the relevant QUIP's author).
+Some template links may use URLs without scheme,
+e.g. ``href="//www.qt.io/terms-conditions/"``, which checklink shall
+show as broken (since it's accessing links using ``file://``,
+implicitly); these are not normally a problem.
+
+.. _checklink: http://validator.w3.org/docs/checklink.html
+
+Planet Qt and the template
+--------------------------
+
+When you update this module, you should ``git submodule update
+--init`` to keep its ``planetqt/`` submodule in sync.
+This shall normally be on a revision for which the scripts are known
+to work.
+
+Updating
+~~~~~~~~
+
+In the ``planetqt/`` submodule, you can ``git checkout master`` and
+``git pull --ff-only`` to get an up-to-date version.
+This might work fine with the existing scripts and templates, in which
+case it's fine to ``git add planetqt`` in the super-module and commit.
+
+However, if ``planetqt/template.qt`` has changed, it's possible the
+scripts shall fail to generate ``template.html`` from it (in the build
+directory, if different from the source directory).
+The script first attempts this by applying ``scripts/template.diff``
+to produce ``scripts/template.html``; if this had succeeded, the
+result would have been moved to the build directory.
+When it fails, a partially-patched ``scripts/template.html`` should
+result in a ``scripts/template.rej`` file, containing the unapplied
+hunks from the patch.
+You'll need to manually adapt the patch's modifications to produce a
+sensible ``template.html`` (see below) based on the new
+``template.qt`` from Planet Qt.
+
+Once you have a usable ``scripts/template.html``, run::
+
+ $ diff -bu planetqt/template.qt scripts/template.html >scripts/template.diff
+
+You might then want to move your working ``template.html`` to safety,
+while you check that building works; your test build shall, otherwise,
+stomp your ``template.html`` in the process of generating the one it
+uses.
+If it all works nicely, commit your updated ``template.html`` along
+with the updated ``planetqt/``.
+
+How the template works
+~~~~~~~~~~~~~~~~~~~~~~
+
+The template file, ``template.html``, is mostly used verbatim in the
+generated web page for each QUIP, with a few tokens replaced, each of
+which starts with a % character.
+In particular, this means that any URLs in ``planetqt/template.qt``
+that contain %-encoded tokens (as part of the usual URL-encoding) need
+the % in each such token doubled up, to a %% that the reStructuredText
+engine shall convert back to a single % in the generated web page.
+
+The other tokens replaced are ``%(name)`` for various choices of
+``name``; most of these are set up in ``scripts/quip2html.py`` by
+``QuipWriter::interpolation_dict()`` adding entries to its ``subs``
+mapping.
+An entry for ``%(stylesheet)`` is generated from
+``QuipWriter::settings_default_overrides['stylesheet_path']`` and
+there may be more entries automagically added by the ``docutils``
+infrastructure, 'though we don't presently exercise these.
+
+Complications
+~~~~~~~~~~~~~
+
+When updating ``template.html`` from a new ``template.qt``, it is best
+to change as little as possible: keep the ``diff`` small so that
+``patch`` has a better chance of applying it.
+Generally, that means only changing what you must, nothing more.
+If you think the web-design of planetqt can be improved on, submit
+changes to it, don't complicate your differences from it with changes
+the next person updating this directory might not understand.
+
+Under ``planetqt/website`` there are assorted files that shall be
+referenced from your ``template.html``, expecting them to be in the
+same directory.
+There's also a sub-directory ``images/``.
+All of these need to be visible in the generated ``html/`` directory;
+this is arranged by a setup target in ``quips.pro`` which (to save a
+lot of repetition) uses ``scripts/setup.mk`` to drive creation of
+symlinks from ``html/`` to ``planetqt/website/``.
+You'll need to keep the ``WEBFILES`` and ``WEBDIRS`` lists up to date;
+each is a list of names, of files or directories respectively, within
+``planetqt/website`` that are needed by the template.
+
+If the new planetqt template contains a copyright notice, consult with
+planetqt's owners about whether they're happy to have the adapted
+template, without that notice, and accompanying linked content,
+released under this module's CC0 license.
+If they are, remove their license as part of adapting the template;
+otherwise, work out some sensible way to satisfy their needs within
+the framework of this module; you may need to find some other source
+than planetqt for templates and styling.
+
+Resist the temptation to replace the contents of planetqt/ with links
+to files on planet.qt.io: doing so would make it necessary to keep
+permanently in sync, instead of only updating when convenient.
diff --git a/docutils/ReadMe.txt b/docutils/ReadMe.txt
new file mode 100644
index 0000000..859ef92
--- /dev/null
+++ b/docutils/ReadMe.txt
@@ -0,0 +1,13 @@
+== Materials specific to python's docutils ==
+
+The present content of this directory is just the style-sheet,
+quip.css, used for QUIPs; it is a copy of writers/pep_html/pep.css
+such as may be found under /usr/share/docutils/ in a Debian
+distribution. We may be better off eliminating this directory
+entirely by referencing docutils' existing copy of that pep.css,
+instead of this quip.css copy of it.
+
+The docutils.conf isn't presently used and I'm not even sure we *want*
+to use it - if we *could* make the template not embed quip.css, we'd
+need to arrange for quip.css to be in a suitable place in the
+generated source tree. It may well be easier to simply inline it ...
diff --git a/docutils/docutils.conf b/docutils/docutils.conf
new file mode 100644
index 0000000..47145e6
--- /dev/null
+++ b/docutils/docutils.conf
@@ -0,0 +1,6 @@
+# Configuration file for Docutils.
+# See http://docutils.sf.net/docs/tools.html
+
+[general]
+# link to the stylesheet; don't embed it
+embed-stylesheet: no
diff --git a/docutils/quip.css b/docutils/quip.css
new file mode 100644
index 0000000..c071a20
--- /dev/null
+++ b/docutils/quip.css
@@ -0,0 +1,344 @@
+/*
+:Author: David Goodger
+:Contact: goodger@python.org
+:date: $Date: 2006-05-21 22:44:42 +0200 (Son, 21. Mai 2006) $
+:version: $Revision: 4564 $
+:copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the PEP HTML output of Docutils.
+*/
+
+/* "! important" is used here to override other ``margin-top`` and
+ ``margin-bottom`` styles that are later in the stylesheet or
+ more specific. See http://www.w3.org/TR/CSS1#the-cascade */
+.first {
+ margin-top: 0 ! important }
+
+.last, .with-subtitle {
+ margin-bottom: 0 ! important }
+
+.hidden {
+ display: none }
+
+.navigation {
+ width: 100% ;
+ background: #99ccff ;
+ margin-top: 0px ;
+ margin-bottom: 0px }
+
+.navigation .navicon {
+ width: 150px ;
+ height: 35px }
+
+.navigation .textlinks {
+ padding-left: 1em ;
+ text-align: left }
+
+.navigation td, .navigation th {
+ padding-left: 0em ;
+ padding-right: 0em ;
+ vertical-align: middle }
+
+.rfc2822 {
+ margin-top: 0.5em ;
+ margin-left: 0.5em ;
+ margin-right: 0.5em ;
+ margin-bottom: 0em }
+
+.rfc2822 td {
+ text-align: left }
+
+.rfc2822 th.field-name {
+ text-align: right ;
+ font-family: sans-serif ;
+ padding-right: 0.5em ;
+ font-weight: bold ;
+ margin-bottom: 0em }
+
+a.toc-backref {
+ text-decoration: none ;
+ color: black }
+
+blockquote.epigraph {
+ margin: 2em 5em ; }
+
+body {
+ margin: 0px ;
+ margin-bottom: 1em ;
+ padding: 0px }
+
+dl.docutils dd {
+ margin-bottom: 0.5em }
+
+div.section {
+ margin-left: 1em ;
+ margin-right: 1em ;
+ margin-bottom: 1.5em }
+
+div.section div.section {
+ margin-left: 0em ;
+ margin-right: 0em ;
+ margin-top: 1.5em }
+
+div.abstract {
+ margin: 2em 5em }
+
+div.abstract p.topic-title {
+ font-weight: bold ;
+ text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+ margin: 2em ;
+ border: medium outset ;
+ padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+ font-weight: bold ;
+ font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+ color: red ;
+ font-weight: bold ;
+ font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+ compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+ margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+ margin-top: 0.5em }
+*/
+
+div.dedication {
+ margin: 2em 5em ;
+ text-align: center ;
+ font-style: italic }
+
+div.dedication p.topic-title {
+ font-weight: bold ;
+ font-style: normal }
+
+div.figure {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+div.footer, div.header {
+ clear: both;
+ font-size: smaller }
+
+div.footer {
+ margin-left: 1em ;
+ margin-right: 1em }
+
+div.line-block {
+ display: block ;
+ margin-top: 1em ;
+ margin-bottom: 1em }
+
+div.line-block div.line-block {
+ margin-top: 0 ;
+ margin-bottom: 0 ;
+ margin-left: 1.5em }
+
+div.sidebar {
+ margin-left: 1em ;
+ border: medium outset ;
+ padding: 1em ;
+ background-color: #ffffee ;
+ width: 40% ;
+ float: right ;
+ clear: right }
+
+div.sidebar p.rubric {
+ font-family: sans-serif ;
+ font-size: medium }
+
+div.system-messages {
+ margin: 5em }
+
+div.system-messages h1 {
+ color: red }
+
+div.system-message {
+ border: medium outset ;
+ padding: 1em }
+
+div.system-message p.system-message-title {
+ color: red ;
+ font-weight: bold }
+
+div.topic {
+ margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+ margin-top: 0.4em }
+
+h1 {
+ font-family: sans-serif ;
+ font-size: large }
+
+h2 {
+ font-family: sans-serif ;
+ font-size: medium }
+
+h3 {
+ font-family: sans-serif ;
+ font-size: small }
+
+h4 {
+ font-family: sans-serif ;
+ font-style: italic ;
+ font-size: small }
+
+h5 {
+ font-family: sans-serif;
+ font-size: x-small }
+
+h6 {
+ font-family: sans-serif;
+ font-style: italic ;
+ font-size: x-small }
+
+hr.docutils {
+ width: 75% }
+
+img.align-left {
+ clear: left }
+
+img.align-right {
+ clear: right }
+
+img.borderless {
+ border: 0 }
+
+ol.simple, ul.simple {
+ margin-bottom: 1em }
+
+ol.arabic {
+ list-style: decimal }
+
+ol.loweralpha {
+ list-style: lower-alpha }
+
+ol.upperalpha {
+ list-style: upper-alpha }
+
+ol.lowerroman {
+ list-style: lower-roman }
+
+ol.upperroman {
+ list-style: upper-roman }
+
+p.attribution {
+ text-align: right ;
+ margin-left: 50% }
+
+p.caption {
+ font-style: italic }
+
+p.credits {
+ font-style: italic ;
+ font-size: smaller }
+
+p.label {
+ white-space: nowrap }
+
+p.rubric {
+ font-weight: bold ;
+ font-size: larger ;
+ color: maroon ;
+ text-align: center }
+
+p.sidebar-title {
+ font-family: sans-serif ;
+ font-weight: bold ;
+ font-size: larger }
+
+p.sidebar-subtitle {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+p.topic-title {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+pre.address {
+ margin-bottom: 0 ;
+ margin-top: 0 ;
+ font-family: serif ;
+ font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+ margin-left: 2em ;
+ margin-right: 2em }
+
+span.classifier {
+ font-family: sans-serif ;
+ font-style: oblique }
+
+span.classifier-delimiter {
+ font-family: sans-serif ;
+ font-weight: bold }
+
+span.interpreted {
+ font-family: sans-serif }
+
+span.option {
+ white-space: nowrap }
+
+span.option-argument {
+ font-style: italic }
+
+span.pre {
+ white-space: pre }
+
+span.problematic {
+ color: red }
+
+span.section-subtitle {
+ /* font-size relative to parent (h1..h6 element) */
+ font-size: 80% }
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px }
+
+table.docinfo {
+ margin: 2em 4em }
+
+table.docutils {
+ margin-top: 0.5em ;
+ margin-bottom: 0.5em }
+
+table.footnote {
+ border-left: solid 1px black;
+ margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+ padding-left: 0.5em ;
+ padding-right: 0.5em ;
+ vertical-align: top }
+
+td.num {
+ text-align: right }
+
+th.field-name {
+ font-weight: bold ;
+ text-align: left ;
+ white-space: nowrap ;
+ padding-left: 0 }
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+ font-size: 100% }
+
+ul.auto-toc {
+ list-style-type: none }
diff --git a/planetqt b/planetqt
new file mode 160000
+Subproject a95fc537e3991c98f4dcb544c4f8ceb5b6b1005
diff --git a/quips.pro b/quips.pro
new file mode 100644
index 0000000..6462f59
--- /dev/null
+++ b/quips.pro
@@ -0,0 +1,38 @@
+TEMPLATE = aux
+
+QUIPS += $$files($$PWD/quip-*.txt)
+
+PYTHON = python
+GENQUIP0_PY = $$PWD/scripts/gen-quip-0000.py
+QUIP2HTML_PY = $$PWD/scripts/quip2html.py
+HTTP_PORT = 8000
+
+quip2html.input = QUIPS
+quip2html.output = html/${QMAKE_FILE_BASE}.html
+quip2html.commands = $$PYTHON $$QUIP2HTML_PY ${QMAKE_FILE_NAME} > ${QMAKE_FILE_OUT}
+quip2html.depends += $$QUIP2HTML_PY template.html
+quip2html.CONFIG += target_predeps
+
+quip_0000.target = html/quip-0000.html
+quip_0000.commands = $$PYTHON $$GENQUIP0_PY $$QUIPS | $$PYTHON $$QUIP2HTML_PY - > $$quip_0000.target
+quip_0000.depends += $$GENQUIP0_PY $$QUIP2HTML_PY $$QUIPS html/.ready
+
+html_index.target = html/index.html
+html_index.commands = ln -sf quip-0000.html html/index.html
+html_index.depends = html/.ready
+
+html_setup.target = html/.ready
+html_setup.commands = make -f $$PWD/scripts/setup.mk SRC=$$PWD setup
+
+template.depends = $$PWD/scripts/template.diff $$PWD/planetqt/template.qt
+template.target = template.html
+template.commands = patch -d $$PWD -l -p0 -i scripts/template.diff -o scripts/template.html && mv $$PWD/scripts/template.html ./
+
+html_serve.target = serve
+html_serve.commands = cd html && $$PYTHON -m SimpleHTTPServer $$HTTP_PORT
+
+POST_TARGETDEPS += html/quip-0000.html html/index.html html/.ready
+QMAKE_CLEAN += html/
+
+QMAKE_EXTRA_TARGETS += html_setup template quip_0000 html_index html_serve
+QMAKE_EXTRA_COMPILERS += quip2html
diff --git a/scripts/gen-quip-0000.py b/scripts/gen-quip-0000.py
new file mode 100755
index 0000000..0f2ded9
--- /dev/null
+++ b/scripts/gen-quip-0000.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2017 The Qt Company Ltd.
+# Contact: http://www.qt.io/licensing/
+#
+# You may use this file under the terms of the CC0 license.
+# See the file LICENSE.CC0 from this package for details.
+
+from email.parser import HeaderParser
+from glob import glob
+import os.path
+
+class RootQuip (dict):
+ def __init__(self, files):
+ parser = HeaderParser()
+ for quip in files:
+ with open(quip, 'r') as quipfp:
+ self[quip] = parser.parse(quipfp)
+
+ def last_name(names): # tool function for owners; not a method
+ while names and names[-1].startswith('<') and names[-1].endswith('>'):
+ names.pop()
+ return names[-1]
+
+ @staticmethod
+ def owners(authors, final=last_name):
+ return ', '.join(final(s) for s in (a.split() for a in authors.split(',')))
+ del last_name
+
+ def quip_entries(self):
+ fmt = ' <tr><td>%4s</td><td><a href="quip-%04d.html">%04d</a></td><td>%s</td><td>%s</td></tr>'
+ for filename in sorted(self):
+ q = self[filename]
+ num = int(q['QUIP'])
+ yield fmt % (q['Type'][:4], num, num, q['Title'], self.owners(q['Author']))
+
+ preamble = """\
+QUIP: 0
+Title: QUIP Index
+Author: Louai Al-Khanji, Edward Welbourne
+Status: Active
+Type: Informational
+Created: 2016-09-16
+Post-History: 2017-03-15
+
+QUIP Index
+==========
+
+This QUIP contains the index of all finalized Qt User-Submitted
+Improvement Proposals, known as QUIPs. A list of reviews of quip
+module changes, including any QUIPs under development, can be found in
+the code-review listing [0]_.
+
+The following QUIPs exist:
+
+.. raw:: html
+
+ <table>
+ <tr><th>type</th><th>number</th><th>title</th><th>owners</th></tr>
+"""
+
+ endpiece = """\
+ </table>
+
+.. [0] https://codereview.qt-project.org/#/q/project:meta/quips,n,z
+"""
+
+ def compose_body(self):
+ return self.preamble + '\n'.join(self.quip_entries()) + self.endpiece
+
+if __name__ == '__main__':
+ import sys
+ print RootQuip(sys.argv[1:]).compose_body()
diff --git a/scripts/quip2html.py b/scripts/quip2html.py
new file mode 100755
index 0000000..544b472
--- /dev/null
+++ b/scripts/quip2html.py
@@ -0,0 +1,265 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2017 The Qt Company Ltd.
+# Contact: http://www.qt.io/licensing/
+#
+# You may use this file under the terms of the CC0 license.
+# See the file LICENSE.CC0 from this package for details.
+"""Qt's Utilitarian Improvement Process (QUIP) processing
+
+Adapted from the python project's similar code for PEPs.
+"""
+
+from __future__ import absolute_import, with_statement
+from __future__ import print_function
+
+__docformat__ = 'reStructuredText'
+
+import os, sys, subprocess
+
+from docutils import core, nodes, utils, languages, DataError
+from docutils.readers import standalone
+from docutils.transforms import Transform, frontmatter, parts
+from docutils.parsers import rst
+from docutils.writers import html4css1
+
+class QuipHeaders(Transform):
+ """Refines the table displaying RFC 2822 headers
+
+ Checks all mandatory headers are present, inserts Version and
+ Last-Modified (in that order, after Title).
+
+ FIXME: Version should probably be called Commit, as it's a
+ commit-id, not any sort of sequential version number, which is
+ what the name "Version" is apt to conjure up in the reader's mind.
+ """
+ default_priority = 360
+
+ @staticmethod
+ def raw_node(text):
+ return nodes.raw('', text, format='html')
+
+ @staticmethod
+ def field_name(text):
+ text = '-'.join(x.capitalize() for x in text.split('-'))
+ return nodes.field_name('', text, format='html')
+
+ @staticmethod
+ def field_body(text):
+ return nodes.field_body('', nodes.paragraph('', text), format='html')
+
+ @classmethod
+ def mask_email(cls, ref):
+ """
+ Mask the email address in `ref` and return a replacement node.
+
+ `ref` is returned unchanged if it contains no email address.
+
+ For email addresses such as 'user@host', mask the address as 'user
+ at host' (text) to thwart simple email address harvesters.
+ """
+ if ref.hasattr('refuri') and ref['refuri'].startswith('mailto:'):
+ return cls.raw_node(ref.astext().replace('@', '&#32;&#97;t&#32;'))
+ return ref
+
+ # Populated by main():
+ git_data = {}
+
+ def apply(self):
+ # QUIP 1 says these should be in this order - not checked.
+ required = ['quip', 'title', 'author', 'status', 'type', 'created',
+ 'post-history',
+ ]
+ insertor = None # index after which to insert git_data entries
+
+ for index, field in enumerate(self.document[0]):
+ name = field[0].astext().lower()
+ body = field[1]
+ if name in required:
+ required.remove(name)
+ # else: optional fields - could check whether recognized
+ if name == 'title' and insertor is None:
+ insertor = index
+
+ if len(body) > 1:
+ raise DataError(
+ 'QUIP header field body contains several elements:\n%s'
+ % field.pformat(level=1))
+ elif name in self.git_data:
+ sys.stderr.write('Over-writing %s header content: %s\n'
+ % (field[0].astext(), body.astext()))
+ body.replace_self(self.field_body(self.git_data[name]))
+ del self.git_data[name]
+ if name == 'version': # appears before last-modified
+ insertor = index
+ elif len(body): # NB: body is a Node, true even if empty
+ para, = body
+ if not isinstance(para, nodes.paragraph):
+ raise DataError(
+ 'QUIP header field body may only contain a single paragraph:\n%s'
+ % field.pformat(level=1))
+ elif name == 'quip':
+ try:
+ int(body.astext()) # evaluate to check it *is* an int ...
+ except (ValueError, TypeError):
+ raise DataError(
+ 'QUIP header QUIP has non-numeric value: ' + body.astext())
+ elif name == 'author':
+ for node in para:
+ if isinstance(node, nodes.reference):
+ node.replace_self(self.mask_email(node))
+
+ # Check for missing mandatory field
+ if required:
+ sys.stderr.write('Missing mandatory header(s): ' + ', '.join(required) + '\n')
+ # raise DataError() instead, once post-history is fixed in all QUIPs
+
+ # Insert any git_data fields not replaced on the way:
+ for key in reversed(sorted(self.git_data)):
+ value = self.git_data[key]
+ field = self.document[0][insertor].deepcopy()
+ insertor += 1
+ field[0].replace_self(self.field_name(key))
+ field[1].replace_self(self.field_body(value))
+ self.document[0].insert(insertor, field)
+
+class QuipContents(Transform):
+
+ """
+ Insert an empty table of contents topic and a transform placeholder into
+ the document after the RFC 2822 header.
+ """
+
+ default_priority = 380
+
+ def apply(self):
+ language = languages.get_language(self.document.settings.language_code,
+ self.document.reporter)
+ name = language.labels['contents']
+ title = nodes.title('', name)
+ topic = nodes.topic('', title, classes=['contents'])
+ name = nodes.fully_normalize_name(name)
+ if not self.document.has_name(name):
+ topic['names'].append(name)
+ self.document.note_implicit_target(topic)
+ pending = nodes.pending(parts.Contents)
+ topic += pending
+ self.document.insert(1, topic)
+ self.document.note_pending(pending)
+
+class QuipReader(standalone.Reader):
+ # Based on PEP reader: docutils.readers.pep
+ inliner_class = rst.states.Inliner
+
+ def get_transforms(self):
+ transforms = standalone.Reader.get_transforms(self)
+ # We have QUIP-specific frontmatter handling.
+ transforms.remove(frontmatter.DocTitle)
+ transforms.remove(frontmatter.SectionSubTitle)
+ transforms.remove(frontmatter.DocInfo)
+ transforms.extend([QuipHeaders, QuipContents])
+ return transforms
+
+ def __init__(self, parser=None, parser_name=None):
+ """`parser` should be ``None``."""
+ if parser is None:
+ parser = rst.Parser(rfc2822=True, inliner=self.inliner_class())
+ standalone.Reader.__init__(self, parser, '')
+
+class QuipWriter(html4css1.Writer):
+ # Assumed to be run from the build directory
+ cwd_file = os.path.join(os.getcwd(), 'dummy')
+ script_dir = os.path.dirname(__file__)
+
+ default_template = 'template.html'
+ default_stylesheet = utils.relative_path(
+ cwd_file, os.path.join(script_dir, '../docutils/quip.css'))
+ # Can we arrange to just use /usr/share/docutils/writers/pep_html/pep.css instead ?
+ del cwd_file, script_dir
+
+ # Used by base-class somehow ?
+ settings_default_overrides = {
+ 'stylesheet_path': default_stylesheet, # => subs['stylesheet'], below
+ 'template': default_template,
+ }
+ del default_template, default_stylesheet
+
+ relative_path_settings = ('template',)
+
+ def interpolation_dict(self):
+ """Set up the substitions in our template
+
+ The template can contain tokens of form %(name)s which get
+ expanded to subs[name] expressed as a string, for various
+ names. The base-class's version of this provides 'stylesheet'
+ as one key of the resulting dictionary, populated from the
+ settings_default_overrides['stylesheet_path'] above.
+ """
+ subs = html4css1.Writer.interpolation_dict(self)
+ settings = self.document.settings
+ # Presently unused:
+ # subs['qthome'] = 'http://www.qt.io'
+ # subs['quiphome'] = 'http://quips.qt.io'
+ # subs['quipindex'] = 'http://quips.qt.io'
+ index = self.document.first_child_matching_class(nodes.field_list)
+ header = self.document[index]
+ self.quipnum = header[0][1].astext()
+ subs['quip'] = self.quipnum
+ try:
+ subs['quipnum'] = '%04i' % int(self.quipnum)
+ except ValueError:
+ subs['quipnum'] = self.quipnum
+ self.title = header[1][1].astext()
+ subs['title'] = 'QUIP %s | %s' % (self.quipnum, self.title)
+ subs['body'] = ''.join(self.body_pre_docinfo + self.docinfo + self.body)
+ return subs
+
+ def assemble_parts(self):
+ html4css1.Writer.assemble_parts(self)
+ self.parts['title'] = [self.title]
+ self.parts['quipnum'] = self.quipnum
+
+class GitRunner(object):
+ def __init__(self, filename, stderr):
+ self.path, self.base = os.path.split(filename)
+ self.stderr = stderr
+
+ def run(self, args):
+ proc = subprocess.Popen(['git'] + args,
+ stderr=self.stderr, stdout=subprocess.PIPE,
+ cwd=self.path, universal_newlines=True)
+ line = proc.stdout.readline()
+ proc.wait()
+ return line
+
+def main(filename, source, output, stderr):
+ git = GitRunner(filename, stderr)
+ data = QuipHeaders.git_data
+ data['version'] = git.run(['rev-list', 'HEAD', '-1', '--', git.base]).strip()
+ # Could use %aI for author (ISO) date, rather than commit date (%c):
+ data['last-modified'] = git.run(['show', '-s', '--pretty=format:%cI',
+ data['version']]).split('T')[0]
+
+ try:
+ output.write(core.publish_string(
+ source=source.read(),
+ reader=QuipReader(),
+ parser_name='restructuredtext',
+ writer_name='html4css1',
+ writer=QuipWriter(),
+ # Allow Docutils traceback if there's an exception:
+ settings_overrides={'traceback': True, 'halt_level': 2}))
+ except DataError as what:
+ stderr.write('\n'.join(what.args) + '\nin %s\n' % filename)
+ return 1
+
+ return 0
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2 or sys.argv[1] == '-':
+ here = os.path.split(sys.argv[0])[0]
+ sys.exit(main(os.path.join(here, 'gen-quip-0000.py'),
+ sys.stdin, sys.stdout, sys.stderr))
+ else:
+ with open(sys.argv[1]) as fd:
+ sys.exit(main(sys.argv[1], fd, sys.stdout, sys.stderr))
diff --git a/scripts/setup.mk b/scripts/setup.mk
new file mode 100644
index 0000000..74d088e
--- /dev/null
+++ b/scripts/setup.mk
@@ -0,0 +1,21 @@
+# Make-file for quips.pro to delegate to as:
+# make SRC=$$PWD setup
+default: setup
+WEB = $(SRC)/planetqt/website
+
+html:
+ mkdir -p html
+
+WEBDIRS = images
+$(WEBDIRS:%=html/%): html/%: html
+ [ -d $@ ] || ln -sf $(WEB)/images $(@D)
+
+WEBFILES = favicon.ico \
+ online.css style.css \
+ main.js extras.js modernizr.custom.84855.js \
+ cookie_small.png cookiebar-x.png list_arrow.png theqtcompany.png
+$(WEBFILES:%=html/%): html/%: html
+ [ -f $@ ] || ln -sf $(WEB)/$(@F) $(@D)
+
+setup: $(WEBFILES:%=html/%) $(WEBDIRS:%=html/%)
+ touch html/.ready
diff --git a/scripts/template.diff b/scripts/template.diff
new file mode 100644
index 0000000..0902e11
--- /dev/null
+++ b/scripts/template.diff
@@ -0,0 +1,73 @@
+--- planetqt/template.qt 2017-04-21 16:11:06.542439585 +0200
++++ scripts/template.html 2017-04-25 17:50:11.806838515 +0200
+@@ -9,8 +9,8 @@
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+
+- <title>Planet Qt</title>
+-
++ <title>%(title)s</title>
++ %(stylesheet)s
+ <link rel="shortcut icon" href="favicon.ico" />
+ <link href='http://fonts.googleapis.com/css?family=Open+Sans:700,300,400' rel='stylesheet' type='text/css'>
+ <link rel="stylesheet" type="text/css" href="online.css" />
+@@ -82,33 +82,14 @@
+ </div>
+ <div class="content">
+ <div class="wrapper">
+- <div class="content-column">
+- <div class="pages">
+- __paged_output_pages__
+- </div>
+- <div id="items">
+- __items__
+- </div>
+- <div class="pages">
+- __paged_output_pages__
+- </div>
+- </div>
+- <aside>
+- <img src="images/content-meteor.png" class="responsive-image" alt="Planet Qt" />
+- <a href="rss20.xml" class="rss-feed">RSS</a>
+- <h3>About Planet Qt</h3>
+- <p>The Qt Planet is an aggregator for Qt related blogs, regardless of who the author is. The opinions it contains are those of the respective author. This site is powered by <a href="http://offog.org/code/rawdog.html">Rawdog</a> and <a href="https://launchpad.net/rawdog-rss">Rawdog RSS</a>. Feed readers can read Planet Qt with <a href="rss20.xml">RSS</a>, <a href="foafroll.xml">FOAF</a> or <a href="opml.xml">OPML</a></p>
+- <h3>Join us</h3>
+- <ul class="icon-links">
+- <li class="qtproject-twitter"><a href="http://twitter.com/qtproject">@qtproject</a></li>
+- <li class="qtproject-gplus"><a href="https://plus.google.com/104580575722059274792">Qt Project</a></li>
+- <li class="qt-meetup"><a href="http://www.meetup.com/QtEverywhere/">Qt Meetups</a></li>
+- </ul>
+- <h3>Contributing</h3>
+- <p>All who blog in English about Qt can either add their complete RSS feed or a feed based on a specific category or tag for those blogging about multiple topics. All you need to do is clone the <a href="https://codereview.qt-project.org/#admin,project,www/planetqt,info">Planet Qt repository</a>, add your feed's address, and put your change up for <a href="http://wiki.qt.io/Code_Reviews">review</a>.</p>
+- <h3>Subscriptions</h3>
+- __feedlist__
+- </aside>
++ <h1>%(title)s</h1>
++ <ul>
++ <li><a href="./">QUIP Index</a></li>
++ <li><a href="quip-%(quipnum)s.html">QUIP %(quip)s</a></li>
++ </ul>
++<div class="context document">
++%(body)s
++</div>
+ </div>
+ </div>
+ <div id="footer">
+@@ -174,7 +155,7 @@
+ <a href="http://www.qt.io/about-us/" target="_blank" class="theqtcompany"></a>
+ <div class="footer-social clearfix">
+ <div class="facebook">
+- <iframe scrolling="no" frameborder="0" allowTransparency="true" src="//www.facebook.com/plugins/like.php?href=https%3A%2F%2Fwww.facebook.com%2Fqtbydigia&amp;width&amp;layout=button_count&amp;action=like&amp;show_faces=true&amp;share=false&amp;height=21" style="border:none;overflow:hidden;height:21px;"></iframe>
++ <iframe scrolling="no" frameborder="0" allowTransparency="true" src="//www.facebook.com/plugins/like.php?href=https%%3A%%2F%%2Fwww.facebook.com%%2Fqtbydigia&amp;width&amp;layout=button_count&amp;action=like&amp;show_faces=true&amp;share=false&amp;height=21" style="border:none;overflow:hidden;height:21px;"></iframe>
+ </div>
+ <div class="twitter">
+ <iframe id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" src="//platform.twitter.com/widgets/follow_button.33b190ea0cba008796487b65df7f6d8e.en.html#_=1414403615717&amp;id=twitter-widget-0&amp;lang=en&amp;screen_name=qtproject&amp;show_count=true&amp;show_screen_name=false&amp;size=m" class="twitter-follow-button twitter-follow-button" title="Twitter Follow Button" data-twttr-rendered="true" style="width: 160px; height: 20px;"></iframe>
+@@ -186,7 +167,7 @@
+ <div class="container clearfix no_discs">
+ <ul id="menu-footer-submenu" class="right clearfix"><li class="menu-item"><a href="http://www.qtworldsummit.com">Qt World Summit</a></li>
+ <li class="menu-item"><a title="Sign into your account." href="https://account.qt.io/login">Sign In</a></li>
+- <li class="menu-item"><a href="mailto:feedback@theqtcompany.com?Subject=Feedback%20about%20www.qt.io%20site">Feedback</a></li>
++ <li class="menu-item"><a href="mailto:feedback@theqtcompany.com?Subject=Feedback%%20about%%20www.qt.io%%20site">Feedback</a></li>
+ <li class="menu-item"><a href="http://www.qt.io/about-us">© 2015 The Qt Company</a></li>
+ </ul>
+ </div>