aboutsummaryrefslogtreecommitdiffstats
path: root/docs/extending.txt
diff options
context:
space:
mode:
Diffstat (limited to 'docs/extending.txt')
-rw-r--r--docs/extending.txt174
1 files changed, 156 insertions, 18 deletions
diff --git a/docs/extending.txt b/docs/extending.txt
index 9e5dfbc..4fa935d 100644
--- a/docs/extending.txt
+++ b/docs/extending.txt
@@ -9,7 +9,7 @@
Extending Nikola
================
-:Version: 7.7.3
+:Version: 7.8.1
:Author: Roberto Alsina <ralsina@netmanagers.com.ar>
.. class:: alert alert-info pull-right
@@ -63,13 +63,13 @@ When you run ``nikola --help`` you will see something like this:
nikola import_feed import a RSS/Atom dump
nikola import_wordpress import a WordPress dump
nikola init create a Nikola site in the specified folder
- nikola install_theme install theme into current site
nikola list list tasks from dodo file
nikola mincss apply mincss to the generated site
nikola new_post create a new blog post or site page
nikola run run tasks
nikola serve start the test webserver
nikola strace use strace to list file_deps and targets
+ nikola theme manage themes
nikola version print the Nikola version number
nikola help show help / reference
@@ -172,8 +172,8 @@ TemplateSystem Plugins
----------------------
Nikola supports Mako and Jinja2. If you prefer some other templating
-system, then you will have to write a TemplateSystem plugin. Here's how they work.
-First, you have to create a .plugin file. Here's the one for the Mako plugin:
+system, then you will have to write a ``TemplateSystem`` plugin. Here's how they work.
+First, you have to create a ``.plugin`` file. Here's the one for the Mako plugin:
.. code-block:: ini
@@ -253,10 +253,10 @@ Task Plugins
------------
If you want to do something that depends on the data in your site, you
-probably want to do a Task plugin, which will make it be part of the
-``nikola build`` command. There are the currently available tasks, all
+probably want to do a ``Task`` plugin, which will make it be part of the
+``nikola build`` command. These are the currently available tasks, all
provided by plugins:
-T
+
.. sidebar:: Other Tasks
There are also ``LateTask`` plugins, which are executed later,
@@ -380,7 +380,7 @@ They must provide:
If the compiler produces something other than HTML files, it should also implement ``extension`` which
returns the preferred extension for the output file.
-These plugins can also be used to extract metadata from file. To do so, the
+These plugins can also be used to extract metadata from a file. To do so, the
plugin may implement ``read_metadata`` that will return a dict containing the
metadata contained in the file.
@@ -389,7 +389,8 @@ RestExtension Plugins
Implement directives for reStructuredText, see `media.py <https://github.com/getnikola/nikola/blob/master/nikola/plugins/compile/rest/media.py>`__ for a simple example.
-If your output depends on a config value, you need to make your post record a dependency on a pseudo-path, like this:
+If your output depends on a config value, you need to make your post record a
+dependency on a pseudo-path, like this:
.. code-block:: text
@@ -397,17 +398,18 @@ If your output depends on a config value, you need to make your post record a de
Then, whenever the ``OPTIONNAME`` option is changed in conf.py, the file will be rebuilt.
-If your directive depends or may depend on the whole timeline (like the post-list directive, where adding new posts
-to the site could make it stale), you should record a dependency on the
-pseudo-path ``####MAGIC####TIMELINE``.
+If your directive depends or may depend on the whole timeline (like the
+``post-list`` directive, where adding new posts to the site could make it
+stale), you should record a dependency on the pseudo-path
+``####MAGIC####TIMELINE``.
MarkdownExtension Plugins
-------------------------
Implement Markdown extensions, see `mdx_nikola.py <https://github.com/getnikola/nikola/blob/master/nikola/plugins/compile/markdown/mdx_nikola.py>`__ for a simple example.
-Note that python markdown extensions are often also available as separate packages. This is only meant to ship extensions
-along with Nikola.
+Note that Python markdown extensions are often also available as separate
+packages. This is only meant to ship extensions along with Nikola.
SignalHandler Plugins
---------------------
@@ -445,6 +447,16 @@ Currently Nikola emits the following signals:
'undeployed': # all files not deployed since they are either future posts/drafts
}
+``compiled``
+ When a post/page is compiled from its source to html, before anything else is done with it. The signal
+ data is in the form::
+
+ {
+ 'source': # the path to the source file
+ 'dest': # the path to the cache file for the post/page
+ 'post': # the Post object for the post/page
+ }
+
One example is the `deploy_hooks plugin. <https://github.com/getnikola/plugins/tree/master/v6/deploy_hooks>`__
ConfigPlugin Plugins
@@ -458,7 +470,7 @@ from ``super()``. Example plugin: `navstories <https://github.com/getnikola/plu
PostScanner Plugins
-------------------
-Get posts and stories from "somewhere" to be added to the timeline.
+Get posts and pages from "somewhere" to be added to the timeline.
The only currently existing plugin of this kind reads them from disk.
@@ -477,7 +489,7 @@ 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.
+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.
@@ -493,7 +505,7 @@ Here's the relevant code from the tag plugin.
# 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
+ self.site.config['TAG_PATH'], self.slugify_name(name, lang) + ".xml"] if
_f]
Template Hooks
@@ -529,7 +541,7 @@ HTML:
The second argument to ``append()`` is used to determine whether the function
-needs access to the current template context and the site. If it it set to
+needs access to the current template context and the site. If it is set to
``True``, the function will also receive ``site`` and ``context`` keyword
arguments. Example use:
@@ -549,3 +561,129 @@ arguments. Example use:
endswith=endswith) + t
site.template_hooks['page_header'].append(greeting, True, u'Nikola Tesla', endswith=u'!')
+
+Shortcodes
+==========
+
+Some (hopefully all) markup compilers support shortcodes in these forms::
+
+ {{% raw %}}{{% foo %}} # No arguments
+ {{% foo bar %}} # One argument, containing "bar"
+ {{% foo bar baz=bat %}} # Two arguments, one containing "bar", one called "baz" containing "bat"
+
+ {{% foo %}}Some text{{% /foo %}} # one argument called "data" containing "Some text"{{% /raw %}}
+
+So, if you are creating a plugin that generates markup, it may be a good idea
+to register it as a shortcode in addition of to restructured text directive or
+markdown extension, thus making it available to all markup formats.
+
+To implement your own shortcodes from a plugin, you can create a plugin inheriting ``ShortcodePlugin`` and
+from its ``set_site`` method, call
+
+``Nikola.register_shortcode(name, func)`` with the following arguments:
+
+``name``:
+ Name of the shortcode ("foo" in the examples above)
+``func``:
+ A function that will handle the shortcode
+
+The shortcode handler **must** return a two-element tuple, ``(output, dependencies)``
+
+``output``:
+ The text that will replace the shortcode in the document.
+
+``dependencies``:
+ A list of all the files on disk which will make the output be considered
+ out of date. For example, if the shortcode uses a template, it should be
+ the path to the template file.
+
+The shortcode handler **must** accept the following named arguments (or
+variable keyword arguments):
+
+``site``:
+ An instance of the Nikola class, to access site state
+
+``data``:
+ If the shortcut is used as opening/closing tags, it will be the text
+ between them, otherwise ``None``.
+
+``lang``:
+ The current language.
+
+If the shortcode tag has arguments of the form ``foo=bar`` they will be
+passed as named arguments. Everything else will be passed as positional
+arguments in the function call.
+
+So, for example::
+
+ {{% raw %}}{{% foo bar baz=bat beep %}}Some text{{% /foo %}}{{% /raw %}}
+
+Assuming you registered ``foo_handler`` as the handler function for the
+shortcode named ``foo``, this will result in the following call when the above
+shortcode is encountered::
+
+ foo_handler("bar", "beep", baz="bat", data="Some text", site=whatever)
+
+Template-based Shortcodes
+-------------------------
+
+Another way to define a new shortcode is to add a template file to the
+``shortcodes`` directory of your site. The template file must have the
+shortcode name as the basename and the extension ``.tmpl``. For example, if you
+want to add a new shortcode named ``foo``, create the template file as
+``shortcodes/foo.tmpl``.
+
+When the shortcode is encountered, the matching template will be rendered with
+its context provided by the arguments given in the shortcode. Keyword arguments
+are passed directly, i.e. the key becomes the variable name in the template
+namespace with a matching string value. Non-keyword arguments are passed as
+string values in a tuple named ``_args``. As for normal shortcodes with a
+handler function, ``site`` and ``data`` will be added to the keyword arguments.
+
+Example:
+
+The following shortcode:
+
+.. code:: text
+
+ {{% raw %}}{{% foo bar="baz" spam %}}{{% /raw %}}
+
+With a template in ``shortcodes/foo.tmpl`` with this content (using Jinja2
+syntax in this example)
+
+.. code:: jinja
+
+ <div class="{{ _args[0] if _args else 'ham' }}">{{ bar }}</div>
+
+Will result in this output
+
+.. code:: html
+
+ <div class="spam">baz</div>
+
+
+State and Cache
+===============
+
+Sometimes your plugins will need to cache things to speed up further actions. Here are the conventions for that:
+
+* If it's a file, put it somewhere in ``self.site.config['CACHE_FOLDER']`` (defaults to ``cache/``.
+* If it's a value, use ``self.site.cache.set(key, value)`` to set it and ``self.site.cache.get(key)`` to get it.
+ The key should be a string, the value should be json-encodable (so, be careful with datetime objects)
+
+The values and files you store there can **and will** be deleted sometimes by the user. They should always be
+things you can reconstruct without lossage. They are throwaways.
+
+On the other hand, sometimes you want to save something that is **not** a throwaway. These are things that may
+change the output, so the user should not delete them. We call that **state**. To save state:
+
+* If it's a file, put it somewhere in the working directory. Try not to do that please.
+* If it's a value, use ``self.site.state.set(key, value)`` to set it and ``self.state.cache.get(key)`` to get it.
+ The key should be a string, the value should be json-encodable (so, be careful with datetime objects)
+
+The ``cache`` and ``state`` objects are rather simplistic, and that's intentional. They have no default values: if
+the key is not there, you will get ``None`` and like it. They are meant to be both threadsafe, but hey, who can
+guarantee that sort of thing?
+
+There are no sections, and no access protection, so let's not use it to store passwords and such. Use responsibly.
+