Skip to content

Commit

Permalink
Merge branch 'master' into header-deduplication
Browse files Browse the repository at this point in the history
  • Loading branch information
ralsina committed May 17, 2017
2 parents 91204c8 + 9e8dd6d commit 83354b7
Show file tree
Hide file tree
Showing 30 changed files with 179 additions and 57 deletions.
6 changes: 6 additions & 0 deletions CHANGES.txt
Expand Up @@ -6,12 +6,18 @@ Features

* New ``deduplicate_ids``, for preventing duplication of HTML id
attributes (Issue #2570)
* Use ``.theme`` files to store theme metadata (Issue #2758)
* New ``add_header_permalinks`` filter, for Sphinx-style header links
(Issue #2636)
* Added alternate links for gallery translations (Issue #993)

Bugfixes
--------

* Ignore files ending wih "bak" (Issue #2740)
* Use page.tmpl by default, which is inherited from story.tmpl (Issue
#1891)

New in v7.8.5
=============

Expand Down
6 changes: 6 additions & 0 deletions docs/manual.txt
Expand Up @@ -1928,6 +1928,7 @@ add_header_permalinks
# Include *every* header (not recommended):
# HEADER_PERMALINKS_XPATH_LIST = ['*//{hx}']


deduplicate_ids
Prevent duplicated IDs in HTML output. An incrementing counter is added to
offending IDs. If used alongside ``add_header_permalinks``, it will fix
Expand All @@ -1946,6 +1947,11 @@ deduplicate_ids

DEDUPLICATE_IDS_TOP_CLASSES = ('postpage', 'storypage')

You can also use a file blacklist (``HEADER_PERMALINKS_FILE_BLACKLIST``),
useful for some index pages. Paths include the output directory (eg.
``output/index.html``)


You can apply filters to specific posts or pages by using the ``filters`` metadata field:

.. code:: restructuredtext
Expand Down
9 changes: 6 additions & 3 deletions nikola/conf.py.in
Expand Up @@ -123,8 +123,8 @@ THEME_COLOR = '#5670d4'
# Finally, note that destination can be translated, i.e. you can
# specify a different translation folder per language. Example:
# PAGES = (
# ("pages/*.rst", {"en": "pages", "de": "seiten"}, "story.tmpl"),
# ("pages/*.md", {"en": "pages", "de": "seiten"}, "story.tmpl"),
# ("pages/*.rst", {"en": "pages", "de": "seiten"}, "page.tmpl"),
# ("pages/*.md", {"en": "pages", "de": "seiten"}, "page.tmpl"),
# )

POSTS = ${POSTS}
Expand Down Expand Up @@ -590,11 +590,14 @@ GITHUB_COMMIT_SOURCE = True
# ({hx} is replaced by headers h1 through h6).
# You must change this if you use a custom theme that does not use
# "e-content entry-content" as a class for post and page contents.

# HEADER_PERMALINKS_XPATH_LIST = ['*//div[@class="e-content entry-content"]//{hx}']
# Include *every* header (not recommended):
# HEADER_PERMALINKS_XPATH_LIST = ['*//{hx}']

# File blacklist for header permalinks. Contains output path
# (eg. 'output/index.html')
# HEADER_PERMALINKS_FILE_BLACKLIST = []

# Expert setting! Create a gzipped copy of each generated file. Cheap server-
# side optimization for very high traffic sites or low memory servers.
# GZIP_FILES = False
Expand Down
10 changes: 10 additions & 0 deletions nikola/data/themes/base-jinja/base-jinja.theme
@@ -0,0 +1,10 @@
[Theme]
engine = jinja
parent = base
author = The Nikola Contributors
author_url = https://getnikola.com/
license = MIT

[Family]
family = base
mako_version = base
1 change: 0 additions & 1 deletion nikola/data/themes/base-jinja/engine

This file was deleted.

1 change: 0 additions & 1 deletion nikola/data/themes/base-jinja/parent

This file was deleted.

9 changes: 9 additions & 0 deletions nikola/data/themes/base/base.theme
@@ -0,0 +1,9 @@
[Theme]
engine = mako
author = The Nikola Contributors
author_url = https://getnikola.com/
license = MIT

[Family]
family = base
jinja_version = base-jinja
1 change: 0 additions & 1 deletion nikola/data/themes/base/engine

This file was deleted.

7 changes: 7 additions & 0 deletions nikola/data/themes/base/templates/gallery.tmpl
Expand Up @@ -37,5 +37,12 @@

<%block name="extra_head">
${parent.extra_head()}
%if len(translations) > 1:
%for langname in translations.keys():
%if langname != lang:
<link rel="alternate" hreflang="${langname}" href="${_link('gallery', gallery_path, langname)}">
%endif
%endfor
%endif
<link rel="alternate" type="application/rss+xml" title="RSS" href="rss.xml">
</%block>
1 change: 1 addition & 0 deletions nikola/data/themes/base/templates/page.tmpl
@@ -0,0 +1 @@
<%inherit file="story.tmpl"/>
13 changes: 13 additions & 0 deletions nikola/data/themes/bootstrap3-jinja/bootstrap3-jinja.theme
@@ -0,0 +1,13 @@
[Theme]
engine = jinja
parent = base-jinja
author = The Nikola Contributors
author_url = https://getnikola.com/
based_on = Bootstrap 3 <http://getbootstrap.com/>
license = MIT
tags = bootstrap

[Family]
family = bootstrap3
mako_version = bootstrap3
variants = bootstrap3-gradients, bootstrap3-gradients-jinja
1 change: 0 additions & 1 deletion nikola/data/themes/bootstrap3-jinja/engine

This file was deleted.

1 change: 0 additions & 1 deletion nikola/data/themes/bootstrap3-jinja/parent

This file was deleted.

13 changes: 13 additions & 0 deletions nikola/data/themes/bootstrap3/bootstrap3.theme
@@ -0,0 +1,13 @@
[Theme]
engine = mako
parent = base
author = The Nikola Contributors
author_url = https://getnikola.com/
based_on = Bootstrap 3 <http://getbootstrap.com/>
license = MIT
tags = bootstrap

[Family]
family = bootstrap3
jinja_version = bootstrap3-jinja
variants = bootstrap3-gradients, bootstrap3-gradients-jinja
1 change: 0 additions & 1 deletion nikola/data/themes/bootstrap3/engine

This file was deleted.

1 change: 0 additions & 1 deletion nikola/data/themes/bootstrap3/parent

This file was deleted.

7 changes: 7 additions & 0 deletions nikola/data/themes/bootstrap3/templates/gallery.tmpl
Expand Up @@ -49,6 +49,13 @@ ${parent.extra_head()}
width: 100%;
}
</style>
%if len(translations) > 1:
%for langname in translations.keys():
%if langname != lang:
<link rel="alternate" hreflang="${langname}" href="${_link('gallery', gallery_path, langname)}">
%endif
%endfor
%endif
</%block>


Expand Down
11 changes: 8 additions & 3 deletions nikola/filters.py
Expand Up @@ -400,10 +400,15 @@ def _normalize_html(data):
normalize_html = apply_to_text_file(_normalize_html)


@_ConfigurableFilter(xpath_list='HEADER_PERMALINKS_XPATH_LIST')
@apply_to_text_file
def add_header_permalinks(data, xpath_list=None):
@_ConfigurableFilter(xpath_list='HEADER_PERMALINKS_XPATH_LIST', file_blacklist='HEADER_PERMALINKS_FILE_BLACKLIST')
def add_header_permalinks(fname, xpath_list=None, file_blacklist=None):
"""Post-process HTML via lxml to add header permalinks Sphinx-style."""
# Blacklist requires custom file handling
file_blacklist = file_blacklist or []
if fname in file_blacklist:
return
with io.open(fname, 'r', encoding='utf-8') as inf:
data = inf.read()
doc = lxml.html.document_fromstring(data)
# Get language for slugify
try:
Expand Down
3 changes: 3 additions & 0 deletions nikola/nikola.py
Expand Up @@ -2267,6 +2267,9 @@ def generic_page_renderer(self, lang, post, filters, context=None):
deps = post.deps(lang)
uptodate_deps = post.deps_uptodate(lang)
deps.extend(utils.get_asset_path(x, self.THEMES) for x in ('bundles', 'parent', 'engine'))
_theme_ini = utils.get_asset_path(self.config['THEME'] + '.theme', self.THEMES)
if _theme_ini:
deps.append(_theme_ini)

context = copy(context) if context else {}
context['post'] = post
Expand Down
4 changes: 2 additions & 2 deletions nikola/plugins/command/auto/__init__.py
Expand Up @@ -270,8 +270,8 @@ def do_rebuild(self, event):
if (fname.endswith('~') or
fname.startswith('.') or
'__pycache__' in event_path or
event_path.endswith(('.pyc', '.pyo', '.pyd')) or
os.path.isdir(event_path)): # Skip on folders, these are usually duplicates
event_path.endswith(('.pyc', '.pyo', '.pyd', '_bak')) or
event.is_directory): # Skip on folders, these are usually duplicates
return
self.logger.info('REBUILDING SITE (from {0})'.format(event_path))
p = subprocess.Popen(self.cmd_arguments, stderr=subprocess.PIPE)
Expand Down
6 changes: 3 additions & 3 deletions nikola/plugins/command/console.py
Expand Up @@ -78,10 +78,10 @@ def ipython(self, willful=True):
"""Run an IPython shell."""
try:
import IPython
except ImportError as e:
except ImportError:
if willful:
req_missing(['IPython'], 'use the IPython console')
raise e # That’s how _execute knows whether to try something else.
raise # That’s how _execute knows whether to try something else.
else:
site = self.context['site'] # NOQA
nikola_site = self.context['site'] # NOQA
Expand All @@ -96,7 +96,7 @@ def bpython(self, willful=True):
except ImportError as e:
if willful:
req_missing(['bpython'], 'use the bpython console')
raise e # That’s how _execute knows whether to try something else.
raise # That’s how _execute knows whether to try something else.
else:
bpython.embed(banner=self.header.format('bpython'), locals_=self.context)

Expand Down
6 changes: 3 additions & 3 deletions nikola/plugins/command/init.py
Expand Up @@ -78,9 +78,9 @@
("posts/*.html", "posts", "post.tmpl"),
)""",
'PAGES': """(
("pages/*.rst", "pages", "story.tmpl"),
("pages/*.txt", "pages", "story.tmpl"),
("pages/*.html", "pages", "story.tmpl"),
("pages/*.rst", "pages", "page.tmpl"),
("pages/*.txt", "pages", "page.tmpl"),
("pages/*.html", "pages", "page.tmpl"),
)""",
'COMPILERS': """{
"rest": ('.rst', '.txt'),
Expand Down
4 changes: 2 additions & 2 deletions nikola/plugins/command/serve.py
Expand Up @@ -164,11 +164,11 @@ def _execute(self, options, args):
with open(self.serve_pidfile, 'w') as fh:
fh.write('{0}\n'.format(pid))
self.logger.info("Detached with PID {0}. Run `kill {0}` or `kill $(cat nikolaserve.pid)` to stop the server.".format(pid))
except AttributeError as e:
except AttributeError:
if os.name == 'nt':
self.logger.warning("Detaching is not available on Windows, server is running in the foreground.")
else:
raise e
raise
else:
self.detached = False
try:
Expand Down
41 changes: 30 additions & 11 deletions nikola/plugins/command/theme.py
Expand Up @@ -32,6 +32,7 @@
import shutil
import time
import requests
import configparser

import pygments
from pygments.lexers import PythonLexer
Expand Down Expand Up @@ -131,6 +132,13 @@ class CommandTheme(Command):
'default': 'base',
'help': 'Parent to use for new theme (default: base)',
},
{
'name': 'new_legacy_meta',
'long': 'legacy-meta',
'type': bool,
'default': False,
'help': 'Create legacy meta files for new theme',
},
]

def _execute(self, options, args):
Expand All @@ -147,6 +155,7 @@ def _execute(self, options, args):
new = options.get('new')
new_engine = options.get('new_engine')
new_parent = options.get('new_parent')
new_legacy_meta = options.get('new_legacy_meta')
command_count = [bool(x) for x in (
install,
uninstall,
Expand All @@ -172,7 +181,7 @@ def _execute(self, options, args):
elif copy_template:
return self.copy_template(copy_template)
elif new:
return self.new_theme(new, new_engine, new_parent)
return self.new_theme(new, new_engine, new_parent, new_legacy_meta)

def do_install_deps(self, url, name):
"""Install themes and their dependencies."""
Expand Down Expand Up @@ -316,7 +325,7 @@ def copy_template(self, template):
LOGGER.error("This file already exists in your templates directory ({0}).".format(base))
return 3

def new_theme(self, name, engine, parent):
def new_theme(self, name, engine, parent, create_legacy_meta=False):
"""Create a new theme."""
base = 'themes'
themedir = os.path.join(base, name)
Expand All @@ -326,9 +335,7 @@ def new_theme(self, name, engine, parent):
LOGGER.info("Created directory {0}".format(base))

# Check if engine and parent match
engine_file = utils.get_asset_path('engine', utils.get_theme_chain(parent, self.site.themes_dirs))
with io.open(engine_file, 'r', encoding='utf-8') as fh:
parent_engine = fh.read().strip()
parent_engine = utils.get_template_engine(utils.get_theme_chain(parent, self.site.themes_dirs))

if parent_engine != engine:
LOGGER.error("Cannot use engine {0} because parent theme '{1}' uses {2}".format(engine, parent, parent_engine))
Expand All @@ -342,12 +349,24 @@ def new_theme(self, name, engine, parent):
LOGGER.error("Theme already exists")
return 2

with io.open(os.path.join(themedir, 'parent'), 'w', encoding='utf-8') as fh:
fh.write(parent + '\n')
LOGGER.info("Created file {0}".format(os.path.join(themedir, 'parent')))
with io.open(os.path.join(themedir, 'engine'), 'w', encoding='utf-8') as fh:
fh.write(engine + '\n')
LOGGER.info("Created file {0}".format(os.path.join(themedir, 'engine')))
cp = configparser.ConfigParser()
cp['Theme'] = {
'engine': engine,
'parent': parent
}

theme_meta_path = os.path.join(themedir, name + '.theme')
with io.open(theme_meta_path, 'w', encoding='utf-8') as fh:
cp.write(fh)
LOGGER.info("Created file {0}".format(theme_meta_path))

if create_legacy_meta:
with io.open(os.path.join(themedir, 'parent'), 'w', encoding='utf-8') as fh:
fh.write(parent + '\n')
LOGGER.info("Created file {0}".format(os.path.join(themedir, 'parent')))
with io.open(os.path.join(themedir, 'engine'), 'w', encoding='utf-8') as fh:
fh.write(engine + '\n')
LOGGER.info("Created file {0}".format(os.path.join(themedir, 'engine')))

LOGGER.info("Theme {0} created successfully.".format(themedir))
LOGGER.notice('Remember to set THEME="{0}" in conf.py to use this theme.'.format(name))
Expand Down
4 changes: 2 additions & 2 deletions nikola/plugins/misc/scan_posts.py
Expand Up @@ -101,9 +101,9 @@ def scan(self):
destination_base=destination_translatable
)
timeline.append(post)
except Exception as err:
except Exception:
LOGGER.error('Error reading post {}'.format(base_path))
raise err
raise

return timeline

Expand Down
1 change: 1 addition & 0 deletions nikola/plugins/task/galleries.py
Expand Up @@ -258,6 +258,7 @@ def gen_tasks(self):
folder += '/'
folders.append((folder, ft))

context["gallery_path"] = gallery
context["folders"] = natsort.natsorted(
folders, alg=natsort.ns.F | natsort.ns.IC)
context["crumbs"] = utils.get_crumbs(gallery, index_folder=self, lang=lang)
Expand Down
2 changes: 1 addition & 1 deletion nikola/shortcodes.py
Expand Up @@ -382,7 +382,7 @@ def apply_shortcodes(data, registry, site=None, filename=None, raise_exceptions=
except ParsingError as e:
if raise_exceptions:
# Throw up
raise e
raise
if filename:
LOGGER.error("Shortcode error in file {0}: {1}".format(filename, e))
else:
Expand Down

0 comments on commit 83354b7

Please sign in to comment.