From b188406fccd7da8184f1bbefff91f8cc3aa18181 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 23 Aug 2019 09:22:45 +0200 Subject: [PATCH] Feat: Add personnal plugins --- plugins/hierarchy/README.md | 75 ++++++++++++++++++++++ plugins/hierarchy/__init__.py | 2 + plugins/hierarchy/article_hierarchy.py | 88 ++++++++++++++++++++++++++ plugins/hierarchy/page_hierarchy.py | 86 +++++++++++++++++++++++++ plugins/list_files/__init__.py | 1 + plugins/list_files/list_files.py | 40 ++++++++++++ 6 files changed, 292 insertions(+) create mode 100644 plugins/hierarchy/README.md create mode 100644 plugins/hierarchy/__init__.py create mode 100644 plugins/hierarchy/article_hierarchy.py create mode 100644 plugins/hierarchy/page_hierarchy.py create mode 100644 plugins/list_files/__init__.py create mode 100644 plugins/list_files/list_files.py diff --git a/plugins/hierarchy/README.md b/plugins/hierarchy/README.md new file mode 100644 index 0000000..e87b71a --- /dev/null +++ b/plugins/hierarchy/README.md @@ -0,0 +1,75 @@ +Page Hierarchy +============== +*Author: Ahmad Khayyat ()* + +A [Pelican][1] plugin that creates a URL hierarchy for pages that +matches the filesystem hierarchy of their sources. + +For example, to have the following filesystem structure of page +sources result in the URLs listed next to each file, + +```text +└── content/pages/ # PAGE_DIR + ├── about.md # URL: pages/about/ + ├── projects.md # URL: pages/projects/ + ├── projects/ # (directory) + │ ├── p1.md # URL: pages/projects/p1/ + │ ├── p2.md # URL: pages/projects/p2/ + │ └── p2/ # (directory) + │ └── features.md # URL: pages/projects/p2/features/ + └── contact.md # URL: pages/contact/ +``` + +you can use this plugin with the following Pelican settings: + +```python +# pelicanconf.py +PAGE_URL = '{slug}/' +PAGE_SAVE_AS = '{slug}/index.html' +SLUGIFY_SOURCE = 'basename' +``` + +When generating the `url` and `save_as` attributes, the plugin +prefixes the page's `slug` by its relative path. Although the initial +`slug` is generated from the page's `title` by default, it can be +generated from the source file basename by setting the +`SLUGIFY_SOURCE` setting to `'basename'`, as shown in the settings +snippet above. The `slug` can also be set using [`PATH_METADATA`][2]. + +This plugin is compatible with [Pelican translations][3]. + +Parent and Children Pages +------------------------- +This plugin also adds three attributes to each page object: + +- `parent`: the immediate parent page. `None` if the page is + top-level. If a translated page has no parent, the default-language + parent is used. + +- `parents`: a list of all ancestor pages, starting from the top-level + ancestor. + +- `children`: a list of all immediate child pages, in no specific + order. + +These attributes can be used to generate breadcrumbs or nested +navigation menus. For example, this is a template excerpt for +breadcrumbs: + +```html + + +``` + + +[1]: http://getpelican.com/ +[2]: http://docs.getpelican.com/en/latest/settings.html#path-metadata +[3]: http://docs.getpelican.com/en/latest/settings.html#translations diff --git a/plugins/hierarchy/__init__.py b/plugins/hierarchy/__init__.py new file mode 100644 index 0000000..0b7f56e --- /dev/null +++ b/plugins/hierarchy/__init__.py @@ -0,0 +1,2 @@ +#from .page_hierarchy import * +from .article_hierarchy import * diff --git a/plugins/hierarchy/article_hierarchy.py b/plugins/hierarchy/article_hierarchy.py new file mode 100644 index 0000000..eb99344 --- /dev/null +++ b/plugins/hierarchy/article_hierarchy.py @@ -0,0 +1,88 @@ +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) diff --git a/plugins/hierarchy/page_hierarchy.py b/plugins/hierarchy/page_hierarchy.py new file mode 100644 index 0000000..2192f28 --- /dev/null +++ b/plugins/hierarchy/page_hierarchy.py @@ -0,0 +1,86 @@ +from pelican import signals, contents +import os.path +from copy import copy +from itertools import chain + +''' +This plugin creates a URL hierarchy for pages that matches the +directory hierarchy of their sources. +''' + +class UnexpectedException(Exception): pass + +def get_path(page, settings): + ''' Return the dirname relative to PAGE_PATHS prefix. ''' + path = os.path.split(page.get_relative_source_path())[0] + '/' + path = path.replace( os.path.sep, '/' ) + # Try to lstrip the longest prefix first + for prefix in sorted(settings['PAGE_PATHS'], key=len, reverse=True): + if not prefix.endswith('/'): prefix += '/' + if path.startswith(prefix): + return path[len(prefix):-1] + raise UnexpectedException('Page outside of PAGE_PATHS ?!?') + +def in_default_lang(page): + # page.in_default_lang property is undocumented (=unstable) interface + return page.lang == page.settings['DEFAULT_LANG'] + +def override_metadata(content_object): + if type(content_object) is not contents.Page: + return + page = content_object + path = get_path(page, page.settings) + + def _override_value(page, key): + metadata = copy(page.metadata) + # We override the slug to include the path up to the filename + #metadata['slug'] = os.path.join(path, page.slug) + metadata['slug'] = path + # We have to account for non-default language and format either, + # e.g., PAGE_SAVE_AS or PAGE_LANG_SAVE_AS + infix = '' if in_default_lang(page) else 'LANG_' + return page.settings['PAGE_' + infix + key.upper()].format(**metadata) + + for key in ('save_as', 'url'): + if not hasattr(page, 'override_' + key): + setattr(page, 'override_' + key, _override_value(page, key)) + +def set_relationships(generator): + def _all_pages(): + return chain(generator.pages, generator.translations) + + # initialize parents and children lists + for page in _all_pages(): + page.parent = None + page.parents = [] + page.children = [] + + # set immediate parents and children + for page in _all_pages(): + # Parent of /a/b/ is /a/, parent of /a/b.html is /a/ + parent_url = os.path.dirname(page.url[:-1]) + if parent_url: parent_url += '/' + for page2 in _all_pages(): + if page2.url == parent_url and page2 != page: + page.parent = page2 + page2.children.append(page) + # If no parent found, try the parent of the default language page + if not page.parent and not in_default_lang(page): + for page2 in generator.pages: + if (page.slug == page2.slug and + os.path.dirname(page.source_path) == + os.path.dirname(page2.source_path)): + # Only set the parent but not the children, obviously + page.parent = page2.parent + + # set all parents (ancestors) + for page in _all_pages(): + p = page + while p.parent: + page.parents.insert(0, p.parent) + p = p.parent + + +def register(): + signals.content_object_init.connect(override_metadata) + signals.page_generator_finalized.connect(set_relationships) diff --git a/plugins/list_files/__init__.py b/plugins/list_files/__init__.py new file mode 100644 index 0000000..a39f5d2 --- /dev/null +++ b/plugins/list_files/__init__.py @@ -0,0 +1 @@ +from .list_files import * diff --git a/plugins/list_files/list_files.py b/plugins/list_files/list_files.py new file mode 100644 index 0000000..1b4f18d --- /dev/null +++ b/plugins/list_files/list_files.py @@ -0,0 +1,40 @@ +from pelican import signals, contents +from os import listdir, getcwd +from os.path import isfile, join, sep, split +from copy import copy +from itertools import chain + +''' +This plugin creates a URL hierarchy for articles that matches the +directory hierarchy of their sources. +''' + +def lsfiles(path): + return [f for f in listdir(path) if isfile(join(path, f))] + +def save_files(content_object): + if type(content_object) is not contents.Article: + return + article = content_object + path = split(article.get_relative_source_path())[0] + '/' + path = path.replace(sep, '/' ) + abs_path = getcwd() + "/content/" + path + + files = {i:(path+i) for i in lsfiles(abs_path)} + try: + fig_files = lsfiles(abs_path+"/fig") + except FileNotFoundError: + pass + else: + fig_files = {i:(path+"fig/"+i) for i in fig_files} + files.update(fig_files) + + article.link_files = [] + + for i in files.items(): + article.link_files.append(i) + + article.link_files.sort() + +def register(): + signals.content_object_init.connect(save_files)