"I Tell Myself": shoveling text between files, joins to the main thread
[Ultimately_Untrue_Thought.git] / plugins / page_hierarchy.py
1 from pelican import signals, contents
2 import os.path
3 from copy import copy
4 from itertools import chain
5
6 # akhayyat/pelican-page-hierarchy@ad7c987914
7
8 '''
9 This plugin creates a URL hierarchy for pages that matches the
10 directory hierarchy of their sources.
11 '''
12
13 class UnexpectedException(Exception): pass
14
15 def get_path(page, settings):
16     ''' Return the dirname relative to PAGE_PATHS prefix. '''
17     path = os.path.split(page.get_relative_source_path())[0] + '/'
18     path = path.replace( os.path.sep, '/' )
19     # Try to lstrip the longest prefix first
20     for prefix in sorted(settings['PAGE_PATHS'], key=len, reverse=True):
21         if not prefix.endswith('/'): prefix += '/'
22         if path.startswith(prefix):
23             return path[len(prefix):-1]
24     raise UnexpectedException('Page outside of PAGE_PATHS ?!?')
25
26 def in_default_lang(page):
27     # page.in_default_lang property is undocumented (=unstable) interface
28     return page.lang == page.settings['DEFAULT_LANG']
29
30 def override_metadata(content_object):
31     if type(content_object) is not contents.Page:
32         return
33     page = content_object
34     path = get_path(page, page.settings)
35
36     def _override_value(page, key):
37         metadata = copy(page.metadata)
38         # We override the slug to include the path up to the filename
39         metadata['slug'] = os.path.join(path, page.slug)
40         # We have to account for non-default language and format either,
41         # e.g., PAGE_SAVE_AS or PAGE_LANG_SAVE_AS
42         infix = '' if in_default_lang(page) else 'LANG_'
43         return page.settings['PAGE_' + infix + key.upper()].format(**metadata)
44
45     for key in ('save_as', 'url'):
46         if not hasattr(page, 'override_' + key):
47             setattr(page, 'override_' + key, _override_value(page, key))
48
49 def set_relationships(generator):
50     def _all_pages():
51         return chain(generator.pages, generator.translations)
52
53     # initialize parents and children lists
54     for page in _all_pages():
55         page.parent = None
56         page.parents = []
57         page.children = []
58
59     # set immediate parents and children
60     for page in _all_pages():
61         # Parent of /a/b/ is /a/, parent of /a/b.html is /a/
62         parent_url = os.path.dirname(page.url[:-1])
63         if parent_url: parent_url += '/'
64         for page2 in _all_pages():
65             if page2.url == parent_url and page2 != page:
66                 page.parent = page2
67                 page2.children.append(page)
68         # If no parent found, try the parent of the default language page
69         if not page.parent and not in_default_lang(page):
70             for page2 in generator.pages:
71                 if (page.slug == page2.slug and
72                     os.path.dirname(page.source_path) ==
73                     os.path.dirname(page2.source_path)):
74                     # Only set the parent but not the children, obviously
75                     page.parent = page2.parent
76
77     # set all parents (ancestors)
78     for page in _all_pages():
79         p = page
80         while p.parent:
81             page.parents.insert(0, p.parent)
82             p = p.parent
83
84
85 def register():
86     signals.content_object_init.connect(override_metadata)
87     signals.page_generator_finalized.connect(set_relationships)