diff options
| author | 2021-02-03 19:17:00 -0500 | |
|---|---|---|
| committer | 2021-02-03 19:17:00 -0500 | |
| commit | 3a0d66f07b112b6d2bdc2b57bbf717a89a351ce6 (patch) | |
| tree | a7cf56282e54f05785243bc1e903d6594f2c06ba /docs/extending.txt | |
| parent | 787b97a4cb24330b36f11297c6d3a7a473a907d0 (diff) | |
New upstream version 8.1.2.upstream/8.1.2
Diffstat (limited to 'docs/extending.txt')
| -rw-r--r-- | docs/extending.txt | 550 |
1 files changed, 0 insertions, 550 deletions
diff --git a/docs/extending.txt b/docs/extending.txt deleted file mode 100644 index bb94003..0000000 --- a/docs/extending.txt +++ /dev/null @@ -1,550 +0,0 @@ -.. title: Extending Nikola -.. slug: extending -.. date: 2012-03-30 23:00:00 UTC-03:00 -.. tags: -.. link: -.. description: - -Extending Nikola -================ - -:Version: 7.6.4 -:Author: Roberto Alsina <ralsina@netmanagers.com.ar> - -.. class:: alert alert-info pull-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. - -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 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 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 nummber (default: 8000)', - }, - { - 'name': 'address', - 'short': 'a', - 'long': '--address', - 'type': str, - 'default': '127.0.0.1', - 'help': 'Address to bind (default: 127.0.0.1)', - }, - ) - - 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)) - 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 - Purpose: start the test webserver - Usage: nikola serve [options] - - Options: - -p ARG, --port=ARG Port nummber (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. There are the currently available tasks, all -provided by plugins: -T -.. 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! - build_bundles - build_less - copy_assets - copy_files - post_render - redirect - render_archive - render_galleries - render_galleries_clean - render_indexes - render_listings - render_pages - render_posts - render_rss - render_site - render_sources - render_tags - 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 <http://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/v7/misaka>`__ - -They must provide: - -``compile_html`` - 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 file. To do so, the -plugin may implement ``read_metadata`` that will return a dict containing the -metadata contained in the file. - -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 <http://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 - } - -One example is the `deploy_hooks plugin. <https://github.com/getnikola/plugins/tree/master/v6/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>`__ - -PostScanner Plugins -------------------- - -Get posts and stories from "somewhere" to be added to the timeline. -The only currently existing plugin of this kind reads them from disk. - - -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 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] - -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'): - return '<script src="/assets/{t}/{n}.{t}">'.format(n=name, t=ftype) - - site.template_hooks['extra_head'].append(generate_html_bit, False, 'fancyplugin', type='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 it 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): - 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'!') |
