from pelican import signals, contents import os.path from copy import copy from itertools import chain ''' This plugin creates a URL hierarchy for articles that matches the directory hierarchy of their sources. ''' class UnexpectedException(Exception): pass def get_path(article, settings): ''' Return the dirname relative to ARTICLE_PATHS prefix. ''' path = os.path.split(article.get_relative_source_path())[0] + '/' path = path.replace( os.path.sep, '/' ) # Try to lstrip the longest prefix first for prefix in sorted(settings['ARTICLE_PATHS'], key=len, reverse=True): if not prefix.endswith('/'): prefix += '/' if path.startswith(prefix): return path[len(prefix):-1] raise UnexpectedException('Article outside of ARTICLE_PATHS ?!?') def in_default_lang(article): # article.in_default_lang property is undocumented (=unstable) interface return article.lang == article.settings['DEFAULT_LANG'] def override_metadata(content_object): if type(content_object) is not contents.Article: return article = content_object path = get_path(article, article.settings) def _override_value(article, key): metadata = copy(article.metadata) # We override the slug to include the path up to the filename #metadata['slug'] = os.path.join(path, article.slug) metadata['slug'] = path # We have to account for non-default language and format either, # e.g., ARTICLE_SAVE_AS or ARTICLE_LANG_SAVE_AS #infix = '' if in_default_lang(article) else 'LANG_' infix = '' return article.settings['ARTICLE_' + infix + key.upper()].format(**metadata) for key in ('save_as', 'url'): if not hasattr(article, 'override_' + key): setattr(article, 'override_' + key, _override_value(article, key)) def set_relationships(generator): def _all_articles(): return chain(generator.articles, generator.translations) # initialize parents and children lists for article in _all_articles(): article.parent = None article.parents = [] article.children = [] # set immediate parents and children for article in _all_articles(): # Parent of /a/b/ is /a/, parent of /a/b.html is /a/ parent_url = os.path.dirname(article.url[:-1]) if parent_url: parent_url += '/' for article2 in _all_articles(): if article2.url == parent_url and article2 != article: article.parent = article2 article2.children.append(article) # If no parent found, try the parent of the default language article #if not article.parent and not in_default_lang(article): if not article.parent: for article2 in generator.articles: if (article.slug == article2.slug and os.path.dirname(article.source_path) == os.path.dirname(article2.source_path)): # Only set the parent but not the children, obviously article.parent = article2.parent # set all parents (ancestors) for article in _all_articles(): p = article while p.parent: article.parents.insert(0, p.parent) p = p.parent def register(): signals.content_object_init.connect(override_metadata) signals.article_generator_finalized.connect(set_relationships)