Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor path #820

Merged
merged 5 commits into from
Oct 12, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
New in 6.2.0
============

Features
--------

* Path/Link resolution mechanism for plugins (Issue #790)

Bugfixes
--------

Expand Down
25 changes: 25 additions & 0 deletions docs/extending.txt
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,28 @@ of the plugins for Nikola people wanted to share with the world.
You may want to read the `README for the Index
<https://github.com/getnikola/plugins/blob/master/README.md>`_ if you want to
publish your package there.

Path/Link Resolution Mechanism
==============================

Any plugin can register a function using ``Nikola.register_path_handler`` to
allow resolution of paths and links. These are useful for templates, which
can access them via _link.

For example, you can always get a link to the path for the feed of the "foo" tag
by using ``_link('tag_rss', 'foo')`` or the ``link://tag_rss/foo`` URL.

Here's the relevant code from the tag plugin.

.. code-block:: python

# In set_site
site.register_path_handler('tag_rss', self.tag_rss_path)

# And these always take name and lang as arguments and returl a list of
# path elements.
def tag_rss_path(self, name, lang):
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['TAG_PATH'], self.slugify_name(name) + ".xml"] if
_f]

128 changes: 34 additions & 94 deletions nikola/nikola.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ class Nikola(object):
def __init__(self, **config):
"""Setup proper environment for running tasks."""

# Register our own path handlers
self.path_handlers = {
'slug': self.slug_path,
'post_path': self.post_path,
}

self.strict = False
self.global_data = {}
self.posts_per_year = defaultdict(list)
Expand Down Expand Up @@ -640,7 +646,11 @@ def current_lang(self): # FIXME: this is duplicated, turn into a mixin
def path(self, kind, name, lang=None, is_link=False):
"""Build the path to a certain kind of page.

kind is one of:
These are mostly defined by plugins by registering via
the register_path_handler method, except for slug and
post_path which are defined in this class' init method.

Here's some of the others, for historical reasons:

* tag_index (name is ignored)
* tag (and name is the tag name)
Expand Down Expand Up @@ -668,99 +678,7 @@ def path(self, kind, name, lang=None, is_link=False):
if lang is None:
lang = self.current_lang()

path = []

def slugify_name(name):
if self.config['SLUG_TAG_PATH']:
name = utils.slugify(name)
return name

def tag_index():
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['TAG_PATH'],
self.config['INDEX_FILE']] if _f]

def tag():
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['TAG_PATH'], slugify_name(name) + ".html"] if
_f]

def category():
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['TAG_PATH'], "cat_" + slugify_name(name) + ".html"] if
_f]

def tag_rss():
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['TAG_PATH'], slugify_name(name) + ".xml"] if
_f]

def category_rss():
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['TAG_PATH'], "cat_" + slugify_name(name) + ".xml"] if
_f]

def index():
if name not in [None, 0]:
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['INDEX_PATH'],
'index-{0}.html'.format(name)] if _f]
else:
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['INDEX_PATH'],
self.config['INDEX_FILE']]
if _f]

def post_path():
return [_f for _f in [self.config['TRANSLATIONS'][lang],
os.path.dirname(name),
self.config['INDEX_FILE']] if _f]

def rss():
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['RSS_PATH'], 'rss.xml'] if _f]

def archive():
if name:
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['ARCHIVE_PATH'], name,
self.config['INDEX_FILE']] if _f]
else:
return [_f for _f in [self.config['TRANSLATIONS'][lang],
self.config['ARCHIVE_PATH'],
self.config['ARCHIVE_FILENAME']] if _f]

def gallery():
return [_f for _f in [self.config['GALLERY_PATH'], name,
self.config['INDEX_FILE']] if _f]

def listing():
return [_f for _f in [self.config['LISTINGS_FOLDER'], name +
'.html'] if _f]

def slug():
results = [p for p in self.timeline if p.meta('slug') == name]
if not results:
utils.LOGGER.warning("Can't resolve path request for slug: {0}".format(name))
else:
if len(results) > 1:
utils.LOGGER.warning('Ambiguous path request for slug: {0}'.format(name))
return [_f for _f in results[0].permalink(lang).split('/') if _f]

path = {
"tag_index": tag_index,
"tag": tag,
"category": category,
"tag_rss": tag_rss,
"category_rss": category_rss,
"index": index,
"post_path": post_path,
"rss": rss,
"archive": archive,
"gallery": gallery,
"listing": listing,
"slug": slug,
}[kind]()
path = self.path_handlers[kind](name, lang)

if is_link:
link = '/' + ('/'.join(path))
Expand All @@ -773,6 +691,28 @@ def slug():
else:
return os.path.join(*path)

def post_path(self, name, lang):
"""post_path path handler"""
return [_f for _f in [self.config['TRANSLATIONS'][lang],
os.path.dirname(name),
self.config['INDEX_FILE']] if _f]

def slug_path(self, name, lang):
"""slug path handler"""
results = [p for p in self.timeline if p.meta('slug') == name]
if not results:
utils.LOGGER.warning("Can't resolve path request for slug: {0}".format(name))
else:
if len(results) > 1:
utils.LOGGER.warning('Ambiguous path request for slug: {0}'.format(name))
return [_f for _f in results[0].permalink(lang).split('/') if _f]

def register_path_handler(self, kind, f):
if kind in self.path_handlers:
utils.LOGGER.warning('Conflicting path handlers for kind: {0}'.format(kind))
else:
self.path_handlers[kind] = f

def link(self, *args):
return self.path(*args, is_link=True)

Expand Down
14 changes: 14 additions & 0 deletions nikola/plugins/task/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class Archive(Task):

name = "render_archive"

def set_site(self, site):
site.register_path_handler('archive', self.archive_path)
return super(Archive, self).set_site(site)

def gen_tasks(self):
kw = {
"messages": self.site.MESSAGES,
Expand Down Expand Up @@ -140,6 +144,16 @@ def gen_tasks(self):
task['basename'] = self.name
yield task

def archive_path(self, name, lang):
if name:
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['ARCHIVE_PATH'], name,
self.site.config['INDEX_FILE']] if _f]
else:
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['ARCHIVE_PATH'],
self.site.config['ARCHIVE_FILENAME']] if _f]


def get_month_name(month_no, locale):
if sys.version_info[0] == 3: # Python 3
Expand Down
8 changes: 8 additions & 0 deletions nikola/plugins/task/galleries.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class Galleries(Task):
name = str("render_galleries")
dates = {}

def set_site(self, site):
site.register_path_handler('gallery', self.gallery_path)
return super(Galleries, self).set_site(site)

def gen_tasks(self):
"""Render image galleries."""

Expand Down Expand Up @@ -391,3 +395,7 @@ def image_date(self, src):
self.dates[src] = datetime.datetime.fromtimestamp(
os.stat(src).st_mtime)
return self.dates[src]

def gallery_path(self, name, lang):
return [_f for _f in [self.site.config['GALLERY_PATH'], name,
self.site.config['INDEX_FILE']] if _f]
15 changes: 15 additions & 0 deletions nikola/plugins/task/indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class Indexes(Task):

name = "render_indexes"

def set_site(self, site):
site.register_path_handler('index', self.index_path)
return super(Indexes, self).set_site(site)

def gen_tasks(self):
self.site.scan_posts()
yield self.group_task()
Expand Down Expand Up @@ -150,3 +154,14 @@ def gen_tasks(self):
task['uptodate'] = [config_changed(task_cfg)]
task['basename'] = self.name
yield task

def index_path(self, name, lang):
if name not in [None, 0]:
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['INDEX_PATH'],
'index-{0}.html'.format(name)] if _f]
else:
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['INDEX_PATH'],
self.site.config['INDEX_FILE']]
if _f]
8 changes: 8 additions & 0 deletions nikola/plugins/task/listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class Listings(Task):

name = "render_listings"

def set_site(self, site):
site.register_path_handler('listing', self.listing_path)
return super(Listings, self).set_site(site)

def gen_tasks(self):
"""Render pretty code listings."""
kw = {
Expand Down Expand Up @@ -126,3 +130,7 @@ def render_listing(in_name, out_name, folders=[], files=[]):
self.site.GLOBAL_CONTEXT)],
'clean': True,
}

def listing_path(self, name, lang):
return [_f for _f in [self.site.config['LISTINGS_FOLDER'], name +
'.html'] if _f]
8 changes: 8 additions & 0 deletions nikola/plugins/task/rss.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class RenderRSS(Task):

name = "render_rss"

def set_site(self, site):
site.register_path_handler('rss', self.rss_path)
return super(RenderRSS, self).set_site(site)

def gen_tasks(self):
"""Generate RSS feeds."""
kw = {
Expand Down Expand Up @@ -81,3 +85,7 @@ def gen_tasks(self):
'clean': True,
'uptodate': [utils.config_changed(kw)],
}

def rss_path(self, name, lang):
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['RSS_PATH'], 'rss.xml'] if _f]
38 changes: 38 additions & 0 deletions nikola/plugins/task/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ class RenderTags(Task):

name = "render_tags"

def set_site(self, site):
site.register_path_handler('tag_index', self.tag_index_path)
site.register_path_handler('tag', self.tag_path)
site.register_path_handler('tag_rss', self.tag_rss_path)
site.register_path_handler('category', self.category_path)
site.register_path_handler('category_rss', self.category_rss_path)
return super(RenderTags, self).set_site(site)

def gen_tasks(self):
"""Render the tag pages and feeds."""

Expand Down Expand Up @@ -281,3 +289,33 @@ def tag_rss(self, tag, lang, posts, kw, is_category):
'uptodate': [utils.config_changed(kw)],
'task_dep': ['render_posts'],
}

def slugify_name(self, name):
if self.site.config['SLUG_TAG_PATH']:
name = utils.slugify(name)
return name

def tag_index_path(self, name, lang):
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['TAG_PATH'],
self.site.config['INDEX_FILE']] if _f]

def tag_path(self, name, lang):
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['TAG_PATH'], self.slugify_name(name) + ".html"] if
_f]

def tag_rss_path(self, name, lang):
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['TAG_PATH'], self.slugify_name(name) + ".xml"] if
_f]

def category_path(self, name, lang):
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['TAG_PATH'], "cat_" + self.slugify_name(name) + ".html"] if
_f]

def category_rss_path(self, name, lang):
return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
self.site.config['TAG_PATH'], "cat_" + self.slugify_name(name) + ".xml"] if
_f]