# HG changeset patch # User Oben Sonne # Date 1573130557 -3600 # Thu Nov 07 13:42:37 2019 +0100 # Node ID e067410da3c30491fa7ab3f21fdd2278aafb0f5c # Parent 9821e494d31b73e0cd8ae628d5f5dbb3b5c4ce9c BitBucket wiki pages are now in the in-repo docs folder diff --git a/README.md b/README.md --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Check the list of [sites built with Poole][examples] (and feel free to add yours). -[examples]: https://bitbucket.org/obensonne/poole/wiki/Home +[examples]: ./docs/sites.md [markdown]: http://daringfireball.net/projects/markdown/ [pymd]: https://pypi.python.org/pypi/Markdown @@ -49,7 +49,7 @@ Run `poole --build` whenever you've made some changes in the *input* folder. [tgz]: https://hg.sr.ht/~obensonne/poole/archive/tip.tar.gz -[themes]: https://bitbucket.org/obensonne/poole/wiki/Themes +[themes]: ./docs/themes.md ## How It Works @@ -349,7 +349,7 @@ the macros module, for instance generate a list of blog posts or create an RSS file. Check out the [example recipes][recipes]. -[recipes]: https://bitbucket.org/obensonne/poole/wiki/Recipes +[recipes]: ./docs/recipes.md ## Feedback diff --git a/docs/macros.md b/docs/macros.md new file mode 100644 --- /dev/null +++ b/docs/macros.md @@ -0,0 +1,152 @@ +Macros +------ + +Though poole is made for simple websites you can add a thin layer of +smartness to a site using macros. + +**Note:** If you end up using macros a lot and everywhere, you should +have a look at more powerful site generator\'s like Jekyll or Hyde which +realize the idea of processing logic much better and with a more clear +distinction between logic, content and layout. + +### Macros as variables + +You can use macros to avoid writing some repetitive content again and +again. Consider the following as the content of a page, let\'s say +*some-page.md*, in your project\'s *input* folder: + +**some-page.md**: + + #!text + # book_title: Fool + # book_author: Moore + + Books + ----- + + My current favorite book is {{ book_title }} by {{ book_author }}. + ... + That is why I love *{{book_title}}*. + +At the beginning it defines 2 macros which are used later using *{{ +macro-name }}*. Macros defined within a page are only valid in the scope +of that page. If you want to reference your currently favored book on +other pages, you should define it as a *global* macro. + +To define global macros, create a file *macros.py* in the same folder +where *page.html* is located and set your macros there: + +**macros.py**: + + #!python + book_title = Fool + book_author = Moore + +Now you can reference these macro in every page. + +What about a *today* macro, specifying the site build day: + +**macros.py:** + + #!python + import datetime + today = datetime.datetime.now().strftime("%Y-%m-%d") + +### Overriding global macros in pages + +A good use case for a global macro defined in *macors.py* is to set a +description or some keywords for your site which can then be referenced +in the *page.html* file, e.g.: + +**macros.py:** + + #!python + # ... + description = "a site about boo" + keywords = "foo, bar" + # ... + +**page.html:** + + #!html + + + + + +For individual pages you can override these settings, for instance: + +**some-page.md:** + + #!text + # keywords: foo, baz + ... + +Page macro definitions override global macro definitions in *macros.py*! + +### Dynamically generated content + +In *macros.py* you can define functions which then can be referenced as +macros. Here\'s a simple and useless example: + +**macros.py**: + + #!python + def asum(pages, page, a="0", b="1"): + return int(a) + int(b) + +**some-page.md:** + + #!text + ... + The sum of 1 and 5 is {{ asum a=1 b=5 }}. + ... + +This will be replaced by, suprise, *6*. + +Macro function in must have at least 2 parameters: + +1. *pages*: a list of all pages in the site processed by *poole* +2. *page*: the current page where this macro is used + +Additional parameters must be declared as keword arguments. + +The objects in *pages* as well as *page* itself are Page objects which +have the following public fields: + +- *name*: name of the page (either the file name without extension or + the value of the `name` macro, if defined within the page\'s source + file +- *macros*: a dictionary of all macros defined for the page (including + global macros defined in *macro.py*) +- *url*: URL to link to that page + +Here is a more complex example, the built-in *menu* macro used in the +default *page.html/ file to display a navigation menu:* + + #!python + def menu(pages, page, tag="span", current="current"): + """Compile an HTML list of pages to appear as a navigation menu. + + Any page which has a macro {{{menu_pos}}} defined is included. Menu + positions are sorted by the integer values of {{{menu_pos}}} (smallest + first). + + The current page's //tag// element is assigned the CSS class //current//. + + """ + menu_pages = [p for p in self.pages if "menu_pos" in p.macros] + menu_pages.sort(key=lambda p: int(p.macros["menu_pos"])) + + html = '' + for p in menu_pages: + style = p == self.__page and (' class="%s"' % current) or '' + html += '<%s%s>%s' % (tag, style, p.url, p.name, tag) + return html + +You can write your own *menu* macro in *macros.py*, if you don\'t like +the built-in one. + +### Limitations + +Macros are not nestable. diff --git a/docs/recipes.md b/docs/recipes.md new file mode 100644 --- /dev/null +++ b/docs/recipes.md @@ -0,0 +1,368 @@ +Recipes +======= + +\* Fancy things you can do with Poole. * + +[Navigation Menu](Recipes#!navigation-menu) · +[Breadcrumb Navigation](Recipes#!breadcrumb-navigation) · +[List of Blog Posts](Recipes#!list-of-blog-posts) · +[Google Sitemap File](Recipes#!google-sitemap-file) · +[RSS Feed for Blog Posts](Recipes#!rss-feed-for-blog-posts) · +[Multiple Languages Support](Recipes#!multiple-languages-support) · +[Link File Size](Recipes#!link-file-size) + +Feel free to add yours! + +------------------------------------------------------------------------ + +Navigation Menu +--------------- + +Have a look into the `page.html` file in a freshly initialized Poole +project. + +------------------------------------------------------------------------ + +Breadcrumb Navigation +--------------------- + +To add breadcrumb navigation, put this into the project\'s `macros.py` +file: + + #!python + def breadcrumb(): + parents = {p.title: (p.url, p.get('parent')) for p in pages} + title = page.title + output = hx(title) + while parents[title][1] is not None: + title = parents[title][1] + url = parents[title][0] + output = '%s > %s' % (url, hx(title), output) + return output + +For each page that has a parent, set the page attribute `parent` to the +`title` of the parent page. The breadcrumb trail can then be included by +specifying `{{ breadcrumb() }}` in your `page.html` (or elsewhere). + +------------------------------------------------------------------------ + +List of Blog Posts +------------------ + +If you want to write some blog posts, you probably would like to have a +page listing all or the latest blog posts. This is easy if you set +certain page attributes in every blog post page: + +`input/brain-on-mongs.md`: + + title: blog + post: This is your brain on mongs + date: 2010-03-01 + --- + + # {{ page.post }} + + Posted on {{ page.date }} + + My hero is full of keyboards. Get nonsense at + +`input/blog.md`: + + This is my blog. + + # My posts + + {% + from datetime import datetime + posts = [p for p in pages if "post" in p] # get all blog post pages + posts.sort(key=lambda p: p.get("date"), reverse=True) # sort post pages by date + for p in posts: + date = datetime.strptime(p["date"], "%Y-%m-%d").strftime("%B %d, %Y") + print " * **[%s](%s)** - %s" % (p.post, p.url, date) # markdown list item + %} + +Feel free to adjust this to your needs. + +**TIP:** Instead of setting the post title and date as page attributes, +you can encode them in the page\'s file name using a structure like +`page-title.YYYY-MM-DD.post-title.md`. For instance for the file name +`blog.2010-03-01.This_is_your_brain_on_mongs.md` Poole would +automatically set the page attributes which has been set manually in the +example above. + +To see this example in action, have a look into the example pages in a +freshly initialized Poole project. + +------------------------------------------------------------------------ + +Google Sitemap File +------------------- + +To generate a Google `sitemap.xml` file, put this into the project\'s +`macros.py` file: + + #!python + from datetime import datetime + import os.path + + _SITEMAP = """ + + %s + + """ + + _SITEMAP_URL = """ + + %s/%s + %s + %s + %s + + """ + + def hook_preconvert_sitemap(): + """Generate Google sitemap.xml file.""" + date = datetime.strftime(datetime.now(), "%Y-%m-%d") + urls = [] + for p in pages: + urls.append(_SITEMAP_URL % (options.base_url.rstrip('/'), p.url, date, + p.get("changefreq", "monthly"), p.get("priority", "0.8"))) + fname = os.path.join(options.project, "output", "sitemap.xml") + fp = open(fname, 'w') + fp.write(_SITEMAP % "".join(urls)) + fp.close() + +You probably want to adjust the default values for *changefreq* and +*priority*. + +**Info:** Every function in `macros.py` whose name starts with +`hook_preconvert_` or `hook_postconvert_` is executed exactly once per +project build \-- either before or after converting pages from markdown +to HTML. In post-convert hooks the HTML content of a page (yet without +header and footer) can be accessed with `page.html`. This is useful to +generate full-content RSS feeds. + +------------------------------------------------------------------------ + +RSS Feed for Blog Posts +----------------------- + +To generate an RSS feed for blog posts put this into the project\'s +`macros.py` file and adjust for your site: + + #!python + import email.utils + import os.path + import time + + _RSS = """ + + + %s + %s + %s + en-us + %s + %s + http://blogs.law.harvard.edu/tech/rss + Poole + %s + + + """ + + _RSS_ITEM = """ + + %s + %s + %s + %s + %s + + """ + + def hook_postconvert_rss(): + items = [] + posts = [p for p in pages if "post" in p] # get all blog post pages + posts.sort(key=lambda p: p.date, reverse=True) + for p in posts: + title = p.post + link = "%s/%s" % (options.base_url.rstrip("/"), p.url) + desc = p.get("description", "") + date = time.mktime(time.strptime("%s 12" % p.date, "%Y-%m-%d %H")) + date = email.utils.formatdate(date) + items.append(_RSS_ITEM % (title, link, desc, date, link)) + + items = "".join(items) + + # --- CHANGE THIS --- # + title = "Maximum volume yields maximum moustaches" + link = "%s/blog.html" % options.base_url.rstrip("/") + desc = "My name is dragonforce. You killed my dragons. Prepare to scream." + date = email.utils.formatdate() + + rss = _RSS % (title, link, desc, date, date, items) + + fp = open(os.path.join(output, "rss.xml"), 'w') + fp.write(rss) + fp.close() + +------------------------------------------------------------------------ + +Multiple languages support +-------------------------- + +To make your website available in several languages, put this into the +project\'s `macros.py` file: + + #!python + + import re + import itertools + + + def hook_preconvert_multilang(): + MKD_PATT = r'\.(?:md|mkd|mdown|markdown)$' + _re_lang = re.compile(r'^[\s+]?lang[\s+]?[:=]((?:.|\n )*)', re.MULTILINE) + vpages = [] # Set of all virtual pages + for p in pages: + current_lang = "en" # Default language + langs = [] # List of languages for the current page + page_vpages = {} # Set of virtual pages for the current page + text_lang = re.split(_re_lang, p.source) + text_grouped = dict(zip([current_lang,] + \ + [lang.strip() for lang in text_lang[1::2]], \ + text_lang[::2])) + + for lang, text in text_grouped.iteritems(): + spath = p.fname.split(os.path.sep) + langs.append(lang) + filename = re.sub(MKD_PATT, ".%s\g<0>" % lang, p.fname).split(os.path.sep)[-1] + vp = Page(filename, virtual=text) + # Copy real page attributes to the virtual page + for attr in p: + if not vp.has_key(attr): + vp[attr] = p[attr] + # Define a title in the proper language + vp["title"] = p["title_%s" % lang] \ + if p.has_key("title_%s" % lang) \ + else p["title"] + # Keep track of the current lang of the virtual page + vp["lang"] = lang + # Fix post name if exists + if vp.has_key("post"): + vp["post"] = vp["post"][:-len(lang) - 1] + page_vpages[lang] = vp + + # Each virtual page has to know about its sister vpages + for lang, vpage in page_vpages.iteritems(): + vpage["lang_links"] = dict([(l, v["url"]) for l, v in page_vpages.iteritems()]) + vpage["other_lang"] = langs # set other langs and link + + vpages += page_vpages.values() + + pages[:] = vpages + +Then make the following modifications in `page.html`: + + #!python + + mpages = [p for p in pages if "menu-position" in p] + +becomes + + #!python + + mpages = [p for p in pages if "menu-position" in p and p.has_key("lang") and p["lang"] == page["lang"]] + +Add the language list by adding this code in `page.html`, for example at +the end of the div menu: + + #!html +
+ +
+ +Adjust the `poole.css` file by adding something like: + + #!css + div#lang { + float:right; + text-align:right; + color: white; + } + +Finally, if you want to show blog pages of the current language only, +replace: + + #!python + + posts = [p for p in pages if "post" in p] # get all blog post pages + +with + + #!python + + posts = [p for p in pages if "post" in p if p.lang == page.lang] # get all blog post pages + +in `blog.md` (or whatever your blog file is). + +### Usage + +The directive `lang: lang_name` (where `lang_name` can be any language +code, typically according to +[ISO 639-1](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)) +separate different languages of the same page. The attribute +`title_lang_name` can be used to translate the title page (which may be +displayed in the menu). Example: + +`input/stuff/news.md:` + + title: Hot News + title_fr: Nouvelles + foobar: King Kong + --- + Here are some news about {{ page.foobar }}. + Did I say {% print(page.foobar) %}? + + lang: fr + + Voici quelques nouvelles a propos de {{ page.foobar }}. + Ai-je bien dit {% print(page.foobar) %} ? + +The first block will always take the default language (which can be +changed in the hook above). + +------------------------------------------------------------------------ + +Link File Size +-------------- + +For people with slow internet access, or simply to inform the visitor +about the size of a downloadable file on your poole web site, you can +use the following postconvert hook: + + #!python + def hook_postconvert_size(): + file_ext = '|'.join(['pdf', 'eps', 'ps']) + def matched_link(matchobj): + try: + # We assume a relative link to a document in the output directory of poole. + size = os.path.getsize(os.path.join("output", matchobj.group(1))) + return "%s (%d KiB)" % (matchobj.group(1), \ + matchobj.group(3), \ + size // 1024) + except: + print "Unable to estimate file size for %s" % matchobj.group(1) + return '%s' % (matchobj.group(1), \ + matchobj.group(3)) + + _re_url = '(.*?)<\/a>' % file_ext + for p in pages: + p.html = re.sub(_re_url, matched_link, p.html) + +It will add the file size in KiB, right after the link, for the file +extensions specified in the second line. diff --git a/docs/sites.md b/docs/sites.md new file mode 100644 --- /dev/null +++ b/docs/sites.md @@ -0,0 +1,29 @@ +Websites built with Poole +------------------------- + +- [twentyweeks.com](https://twentyweeks.com) +- [bertjwregeer.com](http://bertjwregeer.com/index.html) +- [chistoe-nebo.info](http://www.chistoe-nebo.info/) +- [evanchen.cc](http://web.evanchen.cc/) +- [hpi.uni-potsdam.de/giese/events/2011/seams2011](http://www.hpi.uni-potsdam.de/giese/events/2011/seams2011/index.html) +- [i-for-change.co.uk](http://www.i-for-change.co.uk) +- [jcby.com](http://jcby.com/index.html) +- [land.umonkey.net](http://land.umonkey.net/) +- +- [ mechatronics3d.com](http://www.mechatronics3d.com) +- [monitoring-plugins.org](http://www.monitoring-plugins.org/) +- [obensonne.bitbucket.org](http://obensonne.bitbucket.org/) +- [paulbarker.me.uk](http://www.paulbarker.me.uk) +- [profgra.org/lycee](http://profgra.org/lycee/) +- [rpedroso.github.com/sharme](http://rpedroso.github.com/sharme) +- [serge.liyun.free.fr/serge](http://serge.liyun.free.fr/serge/index.html) +- [taecilla.github.io](http://taecilla.github.io/) +- [thpani.at](http://thpani.at/) +- [translation.baham.co](https://translation.baham.co) +- [ultimatehurl.com](http://ultimatehurl.com/index.html) +- [ece.mcgill.ca/\~\~tnorth](http://www.ece.mcgill.ca/~tnorth/) +- [xythobuz.de](http://xythobuz.de) + +*This list is in alphabetical order. Feel free to add your site (or +remove it if the addition by someone else is not your will).* + diff --git a/docs/themes.md b/docs/themes.md new file mode 100644