123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764 |
- .. title: Extending Nikola
- .. slug: extending
- .. date: 2012-03-30 23:00:00 UTC-03:00
- .. tags:
- .. link:
- .. description:
- .. author: The Nikola Team
-
- :Version: 8.2.3
- :Author: Roberto Alsina <ralsina@netmanagers.com.ar>
-
- .. class:: alert alert-primary float-md-right
-
- .. contents::
-
-
- .. class:: lead
-
- Nikola is extensible. Almost all its functionality is based on plugins,
- and you can add your own or replace the provided ones.
-
- Plugins consist of a metadata file (with ``.plugin`` extension) and
- a Python module (a ``.py`` file) or package (a folder containing
- a ``__init__.py`` file.
-
- To use a plugin in your site, you just have to put it in a ``plugins``
- folder in your site.
-
- Plugins come in various flavours, aimed at extending different aspects
- of Nikola.
-
- Available Plugin Categories
- ===========================
-
- Command Plugins
- ---------------
-
- When you run ``nikola --help`` you will see something like this:
-
- .. code-block:: console
-
- $ nikola help
- Nikola is a tool to create static websites and blogs. For full documentation and more
- information, please visit https://getnikola.com/
-
-
- Available commands:
- nikola auto automatically detect site changes, rebuild
- and optionally refresh a browser
- nikola bootswatch_theme given a swatch name from bootswatch.com and a
- parent theme, creates a custom theme
- nikola build run tasks
- nikola check check links and files in the generated site
- nikola clean clean action / remove targets
- nikola console start an interactive python console with access to
- your site and configuration
- nikola deploy deploy the site
- nikola dumpdb dump dependency DB
- nikola forget clear successful run status from internal DB
- nikola help show help
- nikola ignore ignore task (skip) on subsequent runs
- nikola import_blogger import a blogger dump
- 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 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
- nikola help <command> show command usage
- nikola help <task-name> show task usage
-
- That will give you a list of all available commands in your version of Nikola.
- Each and every one of those is a plugin. Let's look at a typical example:
-
- First, the ``serve.plugin`` file:
-
- .. code-block:: ini
-
- [Core]
- Name = serve
- Module = serve
-
- [Documentation]
- Author = Roberto Alsina
- Version = 0.1
- Website = https://getnikola.com
- Description = Start test server.
-
- .. note:: If you want to publish your plugin on the Plugin Index, `read
- the docs for the Index
- <https://github.com/getnikola/plugins/blob/master/README.md>`__
- (and the .plugin file examples and explanations).
-
- For your own plugin, just change the values in a sensible way. The
- ``Module`` will be used to find the matching Python module, in this case
- ``serve.py``, from which this is the interesting bit:
-
- .. code-block:: python
-
- from nikola.plugin_categories import Command
-
- # You have to inherit Command for this to be a
- # command plugin:
-
- class CommandServe(Command):
- """Start test server."""
-
- name = "serve"
- doc_usage = "[options]"
- doc_purpose = "start the test webserver"
-
- cmd_options = (
- {
- 'name': 'port',
- 'short': 'p',
- 'long': 'port',
- 'default': 8000,
- 'type': int,
- 'help': 'Port number',
- },
- {
- 'name': 'address',
- 'short': 'a',
- 'long': '--address',
- 'type': str,
- 'default': '127.0.0.1',
- 'help': 'Address to bind',
- },
- )
-
- def _execute(self, options, args):
- """Start test server."""
- out_dir = self.site.config['OUTPUT_FOLDER']
- if not os.path.isdir(out_dir):
- print("Error: Missing '{0}' folder?".format(out_dir))
- return 1 # Exit code on failure. (return 0 not necessary)
- else:
- os.chdir(out_dir)
- httpd = HTTPServer((options['address'], options['port']),
- OurHTTPRequestHandler)
- sa = httpd.socket.getsockname()
- print("Serving HTTP on", sa[0], "port", sa[1], "...")
- httpd.serve_forever()
-
- As mentioned above, a plugin can have options, which the user can see by doing
- ``nikola help command`` and can later use, for example:
-
- .. code-block:: console
-
- $ nikola help serve
- nikola serve [options]
- start the test webserver
-
- Options:
- -p ARG, --port=ARG
- Port number [default: 8000]
- -a ARG, --address=ARG
- Address to bind [default: 127.0.0.1]
-
- $ nikola serve -p 9000
- Serving HTTP on 127.0.0.1 port 9000 ...
-
- So, what can you do with commands? Well, anything you want, really. I have implemented
- a sort of planet using it. So, be creative, and if you do something interesting,
- let me know ;-)
-
- 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:
-
- .. code-block:: ini
-
- [Core]
- Name = mako
- Module = mako
-
- [Documentation]
- Author = Roberto Alsina
- Version = 0.1
- Website = https://getnikola.com
- Description = Support for Mako templates.
-
- .. note:: If you want to publish your plugin on the Plugin Index, `read
- the docs for the Index
- <https://github.com/getnikola/plugins/blob/master/README.md>`__
- (and the .plugin file examples and explanations).
-
- You will have to replace "mako" with your template system's name, and other data
- in the obvious ways.
-
- The "Module" option is the name of the module, which has to look something like this,
- a stub for a hypothetical system called "Templater":
-
- .. code-block:: python
-
- from nikola.plugin_categories import TemplateSystem
-
- # You have to inherit TemplateSystem
-
- class TemplaterTemplates(TemplateSystem):
- """Wrapper for Templater templates."""
-
- # name has to match Name in the .plugin file
- name = "templater"
-
- # A list of directories where the templates will be
- # located. Most template systems have some sort of
- # template loading tool that can use this.
- def set_directories(self, directories, cache_folder):
- """Sets the list of folders where templates are located and cache."""
- pass
-
- # You *must* implement this, even if to return []
- # It should return a list of all the files that,
- # when changed, may affect the template's output.
- # usually this involves template inheritance and
- # inclusion.
- def template_deps(self, template_name):
- """Returns filenames which are dependencies for a template."""
- return []
-
- def render_template(self, template_name, output_name, context):
- """Renders template to a file using context.
-
- This must save the data to output_name *and* return it
- so that the caller may do additional processing.
- """
- pass
-
- # The method that does the actual rendering.
- # template_name is the name of the template file,
- # context is a dictionary containing the data the template
- # uses for rendering.
- def render_template_to_string(self, template, context):
- """Renders template to a string using context. """
- pass
-
- def inject_directory(self, directory):
- """Injects the directory with the lowest priority in the
- template search mechanism."""
- pass
-
- You can see a real example in `the Jinja plugin <https://github.com/getnikola/nikola/blob/master/nikola/plugins/template/jinja.py>`__
-
- 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. These are the currently available tasks, all
- provided by plugins:
-
- .. sidebar:: Other Tasks
-
- There are also ``LateTask`` plugins, which are executed later,
- and ``TaskMultiplier`` plugins that take a task and create
- more tasks out of it.
-
- .. code-block:: console
-
- $ nikola list
- Scanning posts....done!
- copy_assets
- copy_files
- create_bundles
- post_render
- redirect
- render_galleries
- render_listings
- render_pages
- render_posts
- render_site
- render_sources
- render_taxonomies
- robots_file
- scale_images
- sitemap
-
- These have access to the ``site`` object which contains your timeline and
- your configuration.
-
- The critical bit of Task plugins is their ``gen_tasks`` method, which ``yields``
- `doit tasks <https://pydoit.org/tasks.html>`_.
-
- The details of how to handle dependencies, etc., are a bit too much for this
- document, so I'll just leave you with an example, the ``copy_assets`` task.
- First the ``task_copy_assets.plugin`` file, which you should copy and edit
- in the logical ways:
-
- .. code-block:: ini
-
- [Core]
- Name = copy_assets
- Module = task_copy_assets
-
- [Documentation]
- Author = Roberto Alsina
- Version = 0.1
- Website = https://getnikola.com
- Description = Copy theme assets into output.
-
-
- .. note:: If you want to publish your plugin on the Plugin Index, `read
- the docs for the Index
- <https://github.com/getnikola/plugins/blob/master/README.md>`_
- (and the .plugin file examples and explanations).
-
- And the ``task_copy_assets.py`` file, in its entirety:
-
- .. code-block:: python
-
- import os
-
- from nikola.plugin_categories import Task
- from nikola import utils
-
- # Have to inherit Task to be a task plugin
- class CopyAssets(Task):
- """Copy theme assets into output."""
-
- name = "copy_assets"
-
- # This yields the tasks
- def gen_tasks(self):
- """Create tasks to copy the assets of the whole theme chain.
-
- If a file is present on two themes, use the version
- from the "youngest" theme.
- """
-
- # I put all the configurations and data the plugin uses
- # in a dictionary because utils.config_changed will
- # make it so that if these change, this task will be
- # marked out of date, and run again.
-
- kw = {
- "themes": self.site.THEMES,
- "output_folder": self.site.config['OUTPUT_FOLDER'],
- "filters": self.site.config['FILTERS'],
- }
-
- tasks = {}
- for theme_name in kw['themes']:
- src = os.path.join(utils.get_theme_path(theme_name), 'assets')
- dst = os.path.join(kw['output_folder'], 'assets')
- for task in utils.copy_tree(src, dst):
- if task['name'] in tasks:
- continue
- tasks[task['name']] = task
- task['uptodate'] = task.get('uptodate', []) + \
- [utils.config_changed(kw)]
- task['basename'] = self.name
- # If your task generates files, please do this.
- yield utils.apply_filters(task, kw['filters'])
-
- PageCompiler Plugins
- --------------------
-
- These plugins implement markup languages, they take sources for posts or pages and
- create HTML or other output files. A good example is `the misaka plugin
- <https://github.com/getnikola/plugins/tree/master/v8/misaka>`__ or the built-in
- compiler plugins.
-
- They must provide:
-
- ``compile``
- Function that builds a file.
-
- ``create_post``
- Function that creates an empty file with some metadata in it.
-
- 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 a file. To do so, the
- plugin must set ``supports_metadata`` to ``True`` and implement ``read_metadata`` that will return a dict containing the
- metadata contained in the file. Optionally, it may list ``metadata_conditions`` (see `MetadataExtractor Plugins`_ below)
-
- MetadataExtractor Plugins
- -------------------------
-
- Plugins that extract metadata from posts. If they are based on post content,
- they must implement ``_extract_metadata_from_text`` (takes source of a post
- returns a dict of metadata). They may also implement
- ``split_metadata_from_text``, ``extract_text``. If they are based on filenames,
- they only need ``extract_filename``. If ``support_write`` is set to True,
- ``write_metadata`` must be implemented.
-
- Every extractor must be configured properly. The ``name``, ``source`` (from the
- ``MetaSource`` enum in ``metadata_extractors``) and ``priority``
- (``MetaPriority``) fields are mandatory. There might also be a list of
- ``conditions`` (tuples of ``MetaCondition, arg``), used to check if an
- extractor can provide metadata, a compiled regular expression used to split
- metadata (``split_metadata_re``, may be ``None``, used by default
- ``split_metadata_from_text``), a list of ``requirements`` (3-tuples: import
- name, pip name, friendly name), ``map_from`` (name of ``METADATA_MAPPING`` to
- use, if any) and ``supports_write`` (whether the extractor supports writing
- metadata in the desired format).
-
- For more details, see the definition in ``plugin_categories.py`` and default extractors in ``metadata_extractors.py``.
-
- 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:
-
- .. code-block:: text
-
- ####MAGIC####CONFIG:OPTIONNAME
-
- 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``.
-
- 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.
-
- SignalHandler Plugins
- ---------------------
-
- These plugins extend the ``SignalHandler`` class and connect to one or more
- signals via `blinker <https://pythonhosted.org/blinker/>`_.
-
- The easiest way to do this is to reimplement ``set_site()`` and just connect to
- whatever signals you want there.
-
- Currently Nikola emits the following signals:
-
- ``sighandlers_loaded``
- Right after SignalHandler plugin activation.
- ``initialized``
- When all tasks are loaded.
- ``configured``
- When all the configuration file is processed. Note that plugins are activated before this is emitted.
- ``scanned``
- After posts are scanned.
- ``new_post`` / ``new_page``
- When a new post is created, using the ``nikola new_post``/``nikola new_page`` commands. The signal
- data contains the path of the file, and the metadata file (if there is one).
- ``existing_post`` / ``existing_page``
- When a new post fails to be created due to a title conflict. Contains the same data as ``new_post``.
- ``deployed``
- When the ``nikola deploy`` command is run, and there is at least one new
- entry/post since ``last_deploy``. The signal data is of the form::
-
- {
- 'last_deploy: # datetime object for the last deployed time,
- 'new_deploy': # datetime object for the current deployed time,
- 'clean': # whether there was a record of a last deployment,
- 'deployed': # all files deployed after the last deploy,
- '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/v7/deploy_hooks>`__
-
- ConfigPlugin Plugins
- --------------------
-
- Does nothing specific, can be used to modify the site object (and thus the config).
-
- Put all the magic you want in ``set_site()``, and don’t forget to run the one
- from ``super()``. Example plugin: `navstories <https://github.com/getnikola/plugins/tree/master/v7/navstories>`__
-
-
- CommentSystem Plugins
- ---------------------
-
- Can be used to add a new comment system. (It doesn’t do anything by itself.) It’s expected to provide templates named ``comment_helper_foo.tmpl``.
-
- Example plugin: `cactuscomments <https://github.com/getnikola/plugins/tree/master/v8/cactuscomments>`__
-
- Shortcode Plugins
- -----------------
-
- Shortcode Plugins are a simple way to create a custom shortcode handler.
- By default, the ``set_site`` method will register the ``handler`` method as a
- shortcode with the plugin’s ``name`` as the shortcode name.
-
- See the Shortcodes_ section for more details on shortcodes.
-
- PostScanner Plugins
- -------------------
-
- Get posts and pages from "somewhere" to be added to the timeline.
- There are currently two plugins for this: the built-in ``scan_posts``, and
- ``pkgindex_scan`` (in the Plugin Index), which is used to treat .plugin/.theme
- + README.md as posts to generate the Plugin and Theme Indexes.
-
- Plugin Index
- ============
-
- There is a `plugin index <https://plugins.getnikola.com/>`__, which stores all
- 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 return 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, lang) + ".xml"] if
- _f]
-
- Template Hooks
- ==============
-
- Plugins can use a hook system for adding stuff into templates. In order to use
- it, a plugin must register itself. The following hooks currently exist:
-
- * ``extra_head`` (not equal to the config option!)
- * ``body_end`` (not equal to the config option!)
- * ``page_header``
- * ``menu``
- * ``menu_alt`` (right-side menu in bootstrap, after ``menu`` in base)
- * ``page_footer``
-
- For example, in order to register a script into ``extra_head``:
-
- .. code-block:: python
-
- # In set_site
- site.template_hooks['extra_head'].append('<script src="/assets/js/fancyplugin.js">')
-
- There is also another API available. It allows use of dynamically generated
- HTML:
-
- .. code-block:: python
-
- # In set_site
- def generate_html_bit(name, ftype='js'):
- """Generate HTML for an asset."""
- return '<script src="/assets/{t}/{n}.{t}">'.format(n=name, t=ftype)
-
- site.template_hooks['extra_head'].append(generate_html_bit, False, 'fancyplugin', ftype='js')
-
-
- The second argument to ``append()`` is used to determine whether the function
- 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:
-
- .. code-block:: python
-
- # In set_site
- def greeting(addr, endswith='', site=None, context=None):
- """Greet someone."""
- if context['lang'] == 'en':
- greet = u'Hello'
- elif context['lang'] == 'es':
- greet = u'¡Hola'
-
- t = u' BLOG_TITLE = {0}'.format(site.config['BLOG_TITLE'](context['lang']))
-
- return u'<h3>{greet} {addr}{endswith}</h3>'.format(greet=greet, addr=addr,
- endswith=endswith) + t
-
- site.template_hooks['page_header'].append(greeting, True, u'Nikola Tesla', endswith=u'!')
-
- Dependencies for template hooks:
-
- * if the input is a string, the string value, alongside arguments to ``append``, is used for calculating dependencies
- * if the input is a callable, it attempts ``input.template_registry_identifier``, then ``input.__doc__``, and if neither is available, it uses a static string.
-
- Make sure to provide at least a docstring, or a identifier, to ensure rebuilds work properly.
-
- Shortcodes
- ==========
-
- Some (hopefully all) markup compilers support shortcodes in these forms:
-
- .. code:: text
-
- {{% raw %}}{{% foo %}}{{% /raw %}} # No arguments
- {{% raw %}}{{% foo bar %}}{{% /raw %}} # One argument, containing "bar"
- {{% raw %}}{{% foo bar baz=bat %}}{{% /raw %}} # Two arguments, one containing "bar", one called "baz" containing "bat"
-
- {{% raw %}}{{% foo %}}Some text{{% /foo %}}{{% /raw %}} # one argument called "data" containing "Some text"
-
- 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``.
- By default, the ``set_site`` method will register the ``handler`` method as a
- shortcode with the plugin’s ``name`` as the shortcode name. To have other
- shortcode names, you can 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.
-
- Logging
- =======
-
- Plugins often need to produce messages to the screen. All plugins get a logger object (``self.logger``) by default,
- configured to work with Nikola (logging level, colorful output, plugin name as the logger name). If you need, you can
- also use the global (``nikola.utils.LOGGER``) logger, or you can instantiate custom loggers with
- ``nikola.utils.get_logger`` or the ``nikola.log`` module.
-
- Template and Dependency Injection
- =================================
-
- Plugins have access to two injection facilities.
-
- If your plugin needs custom templates for its features (adding pages, displaying stuff, etc.), you can put them in the
- ``templates/mako`` and ``templates/jinja`` subfolders in your plugin’s folder. Note that those templates have a very low
- priority, so that users can override your plugin’s templates with their own.
-
- If your plugin needs to inject dependencies, the ``inject_dependency(target, dependency)`` function can be used to add a
- ``dependency`` for tasks which basename == ``target``. This facility should be limited to cases which really need it,
- consider other facilities first (eg. adding post dependencies).
|