Skip to content
Permalink
Browse files

Merge branch 'master' into fix-wordpress-plugin-dep-writing

  • Loading branch information
felixfontein committed Nov 20, 2016
2 parents df8b296 + 56d7165 commit 0fb39e26213fbd68097ad873b60468104f58566c
@@ -39,6 +39,7 @@ def set_site(self, site):
directives.register_directive('book_figure', BookFigure)
return super(Plugin, self).set_site(site)


CODE_IMAGE = (u"""<div class="book-figure-media">
<a class="book-figure-image" href="{url}" target="_blank">
<img src="{image_url}" alt="{title}" />
@@ -255,6 +255,7 @@ def import_link(self, post):
'hidetitle': not post['title']
})


LINK_POST_TEMPLATE = '''
<div class="link"><a href="${url}">${title}</a></div>
<div class="description">
@@ -39,6 +39,7 @@ def set_site(self, site):
directives.register_directive('link_figure', LinkFigure)
return super(Plugin, self).set_site(site)


CODE_URL_BASIC = (u"""<a class="{classes}"
href="{url}"
title="{description}">
@@ -1,3 +1,50 @@
This very simple plugin throws all the stories to the navigation bar.

Proof-of-concept of a ConfigPlugin. By request on the mailing list.
Started as a Proof-of-concept of a ConfigPlugin. By request on the mailing list.

Changed to map the stories/pages to a hierachical structure in the menu
based on the permalink structure (defaults to the directory structure
of the posts).

This plugin generates menus and one level of submenus (further sub-levels are mapped to first level submenus).
WARNING: Support for submenus is theme-dependent.

The menu entries inserted by navstories are inserted after entries from `NAVIGATION_LINKS`.
Entries listed in `NAVIGATION_LINKS_POST_NAVSTORIES` are inserted after navstories entries.

Format of `NAVIGATION_LINKS_POST_NAVSTORIES` is identical to `NAVIGATION_LINKS`.

To include stories in the menu the permalink for the story must start with one of the strings listed in
`NAVSTORIES_PATHS`, other stories should be ignored by this pluging.

Sorting and display names in menu can be controlled for top-level entries via `NAVSTORIES_MAPPING`.

```python
# Paths (permalink) that should be processed by navstories plugin (path starting with /<variable>/, <variable> can contain /, e.g.: stories/b
NAVSTORIES_PATHS = {
DEFAULT_LANG: (
'pages',
'stories',
),
}
# Mapping "Toplevel in permalink" to "Visible text"
# The order is as listed here, entries not listed here are included in the end, with the top level of the permalink as text
NAVSTORIES_MAPPING = {
DEFAULT_LANG: (
# example (remove initial #):
#("b", "Boo"),
#("f", "Foo"),
),
}
# Indention for each level deeper in a submenu, than the highest level in that submenu, the submenu is flat, so it is only the menu text there are indented
NAVSTORIES_SUBMENU_INDENTION = '* '
# Static menu after dynamic navstories menu entries
# Format just as NAVIGATION_LINKS, but content included after navstories entries
NAVIGATION_LINKS_POST_NAVSTORIES = {
}
```

To exclude a single story from the navstories meny add the following
metadata `.. hidefromnav: yes` to the story.


@@ -7,7 +7,7 @@ plugincategory = ConfigPlugin
MinVersion = 7.1.0+

[Documentation]
Author = Chris Warrick
Version = 0.1.0
Author = Chris Warrick, Benny Simonsen
Version = 1.0.1
Website = http://plugins.getnikola.com/#navstories
Description = Add all stories to the navigation bar.
Description = Add all stories in specified locations to the navigation bar.
@@ -26,6 +26,10 @@

from __future__ import unicode_literals
from nikola.plugin_categories import ConfigPlugin
from nikola import utils
from nikola.utils import LOGGER

import re


class NavStories(ConfigPlugin):
@@ -34,16 +38,149 @@ class NavStories(ConfigPlugin):
name = 'navstories'
dates = {}

conf_vars = ['TRANSLATIONS', 'NAVSTORIES_PATHS', 'NAVSTORIES_MAPPING', 'NAVIGATION_LINKS_POST_NAVSTORIES']

# Indention for each level deeper in a submenu, than the highest level in that submenu
# overwriiten by site.config['NAVSTORIES_SUBMENU_INDENTION'] if defined
navstories_submenu_indention = '* '

class NavNode():
"""
Class containing parameters for a menu entry
"""

navpath = []
permalink = ''
title = ''

def __init__(self, navpath, permalink, title):
self.navpath = navpath
self.permalink = permalink
self.title = title

def map_to_menu(self, entries):
"""
Map form list of pages going into menu to tuple of format as NAVIGATION_LINKS and NAVIGATION_LINKS_POST_NAVSTORIES
List format:
- List of "top level entry"
- List
- Top level Menu text, or None if auto-mapped, i.e. not in NAVSTORIES_MAPPING
- List of pages in the top menu entry
- List of:
- navpath: List containing navigation hierarchy (permalink without langinfo (initial /en/) and without the NAVSTORIES_PATHS (e.g. /pages/))
- Permalink
- Page title
Example:
[ [ 'Menu text for nav',
[ NavNode instance, # E.g.: .navpath=['nav'], .permalink='/pages/nav/', .title='nav/'
NavNode instance, # E.g.: .navpath=['nav', 'p2', 'a'], .permalink='/pages/nav/p2/a/', .title='Page 2a'
NavNode instance, # E.g.: .navpath=['nav', 'P2', 'b'], .permalink='/pages/nav/P2/b/', .title='Page 2b'
NavNode instance, # E.g.: .navpath=['nav', 'P2', 'a'], .permalink='/pages/nav/P2/a/', .title='Page 2a'
NavNode instance, # E.g.: .navpath=['nav', 'p2'], .permalink='/pages/nav/p2/', .title='Page 2',
NavNode instance, # E.g.: .navpath=['nav', 'p1'], .permalink='/pages/nav/p1/', .title='Side 1'
],
[ 'Menu text for A',
[ NavNode instance, # E.g.: .navpath=['A', 'last'], .permalink='/pages/A/last/', .title='Page title for A/last'
],
[ None,
[ NavNode instance, # E.g.: .navpath=['B', 'cde'], .permalink='/pages/B/cde/', .title='Page title for B/cde'
],
]
"""
ret = []
for title, navnodes in entries:
# Determine toplevel menu name
# - If not None, use the name
# - If None, use title of page if page with navpath length=1 exist, else navpath[0]
if not title:
# Default is 1th level of navpath
title = navnodes[0].navpath[0]
# Search for toplevel page (navpath length = 1)
for n in navnodes:
if len(n.navpath) == 1:
title = n.title # Page Title
if len(navnodes) == 1 and len(navnodes[0].navpath) == 1:
# Only one menu item and it is not a subpage, let the item go direct to top level menu
ret.append(tuple([navnodes[0].permalink, title]))
else:
sub = []
# Find min depth in actual submenu
min_depth = min(len(n.navpath) for n in navnodes)
# Map pages to submenu
for n in sorted(navnodes, key=lambda n: n.permalink):
# Sort by permalink in page list
prefix = self.navstories_submenu_indention * (len(n.navpath) - min_depth)
sub.append(tuple([n.permalink, prefix + n.title]))
ret.append(tuple([tuple(sub), title]))
return tuple(ret)

def set_site(self, site):
"""
Map navstories config to nav_config[*] as TranslatableSettings
"""

# Read NAVSTORIES_SUBMENU_INDENTION and store in self.navstories_submenu_indention
if 'NAVSTORIES_SUBMENU_INDENTION' in site.config:
self.navstories_submenu_indention = site.config['NAVSTORIES_SUBMENU_INDENTION']

nav_config = {}
for i in self.conf_vars:
# Read config variables in a try...except in case a variable is missing
try:
nav_config[i] = utils.TranslatableSetting(i, site.config[i], site.config['TRANSLATIONS'])
except KeyError:
# Initialize to "empty" in case config variable i is missing
nav_config[i] = utils.TranslatableSetting(i, {}, site.config['TRANSLATIONS'])

site.scan_posts()
# NAVIGATION_LINKS is a TranslatableSetting, values is an actual dict
for lang in site.config['NAVIGATION_LINKS'].values:
# navstories config for lang
nav_conf_lang = {}
for i in self.conf_vars:
nav_conf_lang[i] = nav_config[i](lang)

# Which paths are navstories active for current lang? - Must start and end with /
paths = tuple(('/' + s.strip('/') + '/') for s in nav_conf_lang['NAVSTORIES_PATHS'])

# Unsorted (raw) new entries, deleted as mapped to new
new_raw = {}
# Sorted entries as a list of top-level menu entries, later
new = []
# Map site pages to new_raw structure
for p in site.pages:
# Generate navpath (menu) based on permalink without language prefix
# If TRANSLATION[DEFAULT_LANG] = '', then "permalink_nolang = p.permalink()" is ok
permalink_nolang = re.sub(r'^/' + nav_conf_lang['TRANSLATIONS'].lstrip('./') + '/?', '/', p.permalink(lang))
s_candidates = [s for s in paths if permalink_nolang.startswith(s)]
if not s_candidates:
continue
# get longest path
s = max(s_candidates, key=len)
# Strip off the longest path in paths
navpath = permalink_nolang[len(s):].strip('/').split('/')
if len(navpath) == 0:
# Should not happen that navpath is empty, but to prevent errors, and inform via a warning
LOGGER.warn("Page with permalink: '%s', title: '%s', not added to menu by navstories." % (p.permalink(lang), p.title(lang)))
continue
if lang in p.translated_to and not p.meta('hidefromnav'):
new.append(p)
new_entries = tuple(sorted([(p.permalink(lang), p.title(lang)) for p in new]))
# Add entry
if not navpath[0] in new_raw:
new_raw[navpath[0]] = []
new_raw[navpath[0]].append(self.NavNode(navpath, p.permalink(lang), p.title(lang)))

# Map from new_raw to new, sorting by NAVSTORIES_MAPPING
for map_key, map_txt in nav_conf_lang['NAVSTORIES_MAPPING']:
# Loop over all new_raw entries, checking if it matches map_key; if match: add it and delete from new_raw
if map_key in new_raw:
new.append([map_txt, new_raw[map_key]])
del(new_raw[map_key])
# Add remaing new_raw entries which didn't match any map_key
new.extend([[None, new_raw[_]] for _ in sorted(new_raw)])

old_entries = site.config['NAVIGATION_LINKS'].values[lang]
site.config['NAVIGATION_LINKS'].values[lang] = old_entries + new_entries
# Map to tuple
new_entries = self.map_to_menu(new)
old_entries = site.config['NAVIGATION_LINKS'](lang)
# Update NAVIGATION_LINKS with navstories dynamically generated entries and NAVIGATION_LINKS_POST_NAVSTORIES entries
site.config['NAVIGATION_LINKS'].values[lang] = old_entries + new_entries + nav_conf_lang['NAVIGATION_LINKS_POST_NAVSTORIES']
super(NavStories, self).set_site(site)
@@ -56,6 +56,7 @@ def set_site(self, site):
PyPlot.out_dir = os.path.join(site.config['OUTPUT_FOLDER'], 'pyplots')
return super(Plugin, self).set_site(site)


pyplot_spec = images.Image.option_spec
pyplot_spec['include-source'] = directives.flag

@@ -69,7 +69,6 @@ def gen_tasks(self):
"blog_title": self.site.config['BLOG_TITLE'],
"blog_description": self.site.config['BLOG_DESCRIPTION'],
"site_url": self.site.config['SITE_URL'],
"blog_description": self.site.config['BLOG_DESCRIPTION'],
"output_folder": self.site.config['OUTPUT_FOLDER'],
"cache_folder": self.site.config['CACHE_FOLDER'],
"feed_length": self.site.config['FEED_LENGTH'],
@@ -122,6 +122,7 @@ def pep_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
rn += sn
return [rn], []


explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)


@@ -180,6 +181,7 @@ def rfc_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


_litvar_re = re.compile('{([^}]+)}')


@@ -198,6 +200,7 @@ def emph_literal_role(typ, rawtext, text, lineno, inliner,
retnode += nodes.Text(text[pos:], text[pos:])
return [retnode], []


_amp_re = re.compile(r'(?<!&)&(?![&\s])')


@@ -253,6 +256,7 @@ def set_source_info(directive, node):
node.source, node.line = \
directive.state_machine.get_source_and_line(directive.lineno)


# FIXME: needs translations
versionlabels = {
'versionadded': 'New in version %s',
@@ -494,6 +498,7 @@ def unknown_visit(self, node):
pnode['classes'] = ['reference']
return [pnode], msg_list


_abbr_re = re.compile('\((.*)\)$', re.S)


0 comments on commit 0fb39e2

Please sign in to comment.
You can’t perform that action at this time.