Feat: Add personnal plugins

This commit is contained in:
Bertrand Benjamin 2019-08-23 09:22:45 +02:00
parent a8a6b862a6
commit b188406fcc
6 changed files with 292 additions and 0 deletions

View File

@ -0,0 +1,75 @@
Page Hierarchy
==============
*Author: Ahmad Khayyat (<akhayyat@gmail.com>)*
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
<ul class="breadcrumb">
<li><a href="{{ SITEURL }}/" title="{{ SITENAME }}">
<i class="fa fa-home fa-lg"></i>
</a></li>
{% for parent in page.parents %}
<li><a href="{{ SITEURL }}/{{ parent.url }}">{{ parent.title }}</a></li>
{% endfor %}
<li class="active">{{ page.title }}</li>
</ul>
```
[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

View File

@ -0,0 +1,2 @@
#from .page_hierarchy import *
from .article_hierarchy import *

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1 @@
from .list_files import *

View File

@ -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)