A new Riff-radio.org site with a static approach.

extending.rst 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. .. title: Extending Nikola
  2. .. slug: extending
  3. .. date: 2012-03-30 23:00:00 UTC-03:00
  4. .. tags:
  5. .. link:
  6. .. description:
  7. .. author: The Nikola Team
  8. :Version: 8.2.3
  9. :Author: Roberto Alsina <ralsina@netmanagers.com.ar>
  10. .. class:: alert alert-primary float-md-right
  11. .. contents::
  12. .. class:: lead
  13. Nikola is extensible. Almost all its functionality is based on plugins,
  14. and you can add your own or replace the provided ones.
  15. Plugins consist of a metadata file (with ``.plugin`` extension) and
  16. a Python module (a ``.py`` file) or package (a folder containing
  17. a ``__init__.py`` file.
  18. To use a plugin in your site, you just have to put it in a ``plugins``
  19. folder in your site.
  20. Plugins come in various flavours, aimed at extending different aspects
  21. of Nikola.
  22. Available Plugin Categories
  23. ===========================
  24. Command Plugins
  25. ---------------
  26. When you run ``nikola --help`` you will see something like this:
  27. .. code-block:: console
  28. $ nikola help
  29. Nikola is a tool to create static websites and blogs. For full documentation and more
  30. information, please visit https://getnikola.com/
  31. Available commands:
  32. nikola auto automatically detect site changes, rebuild
  33. and optionally refresh a browser
  34. nikola bootswatch_theme given a swatch name from bootswatch.com and a
  35. parent theme, creates a custom theme
  36. nikola build run tasks
  37. nikola check check links and files in the generated site
  38. nikola clean clean action / remove targets
  39. nikola console start an interactive python console with access to
  40. your site and configuration
  41. nikola deploy deploy the site
  42. nikola dumpdb dump dependency DB
  43. nikola forget clear successful run status from internal DB
  44. nikola help show help
  45. nikola ignore ignore task (skip) on subsequent runs
  46. nikola import_blogger import a blogger dump
  47. nikola import_feed import a RSS/Atom dump
  48. nikola import_wordpress import a WordPress dump
  49. nikola init create a Nikola site in the specified folder
  50. nikola list list tasks from dodo file
  51. nikola mincss apply mincss to the generated site
  52. nikola new_post create a new blog post or site page
  53. nikola run run tasks
  54. nikola serve start the test webserver
  55. nikola strace use strace to list file_deps and targets
  56. nikola theme manage themes
  57. nikola version print the Nikola version number
  58. nikola help show help / reference
  59. nikola help <command> show command usage
  60. nikola help <task-name> show task usage
  61. That will give you a list of all available commands in your version of Nikola.
  62. Each and every one of those is a plugin. Let's look at a typical example:
  63. First, the ``serve.plugin`` file:
  64. .. code-block:: ini
  65. [Core]
  66. Name = serve
  67. Module = serve
  68. [Documentation]
  69. Author = Roberto Alsina
  70. Version = 0.1
  71. Website = https://getnikola.com
  72. Description = Start test server.
  73. .. note:: If you want to publish your plugin on the Plugin Index, `read
  74. the docs for the Index
  75. <https://github.com/getnikola/plugins/blob/master/README.md>`__
  76. (and the .plugin file examples and explanations).
  77. For your own plugin, just change the values in a sensible way. The
  78. ``Module`` will be used to find the matching Python module, in this case
  79. ``serve.py``, from which this is the interesting bit:
  80. .. code-block:: python
  81. from nikola.plugin_categories import Command
  82. # You have to inherit Command for this to be a
  83. # command plugin:
  84. class CommandServe(Command):
  85. """Start test server."""
  86. name = "serve"
  87. doc_usage = "[options]"
  88. doc_purpose = "start the test webserver"
  89. cmd_options = (
  90. {
  91. 'name': 'port',
  92. 'short': 'p',
  93. 'long': 'port',
  94. 'default': 8000,
  95. 'type': int,
  96. 'help': 'Port number',
  97. },
  98. {
  99. 'name': 'address',
  100. 'short': 'a',
  101. 'long': '--address',
  102. 'type': str,
  103. 'default': '127.0.0.1',
  104. 'help': 'Address to bind',
  105. },
  106. )
  107. def _execute(self, options, args):
  108. """Start test server."""
  109. out_dir = self.site.config['OUTPUT_FOLDER']
  110. if not os.path.isdir(out_dir):
  111. print("Error: Missing '{0}' folder?".format(out_dir))
  112. return 1 # Exit code on failure. (return 0 not necessary)
  113. else:
  114. os.chdir(out_dir)
  115. httpd = HTTPServer((options['address'], options['port']),
  116. OurHTTPRequestHandler)
  117. sa = httpd.socket.getsockname()
  118. print("Serving HTTP on", sa[0], "port", sa[1], "...")
  119. httpd.serve_forever()
  120. As mentioned above, a plugin can have options, which the user can see by doing
  121. ``nikola help command`` and can later use, for example:
  122. .. code-block:: console
  123. $ nikola help serve
  124. nikola serve [options]
  125. start the test webserver
  126. Options:
  127. -p ARG, --port=ARG
  128. Port number [default: 8000]
  129. -a ARG, --address=ARG
  130. Address to bind [default: 127.0.0.1]
  131. $ nikola serve -p 9000
  132. Serving HTTP on 127.0.0.1 port 9000 ...
  133. So, what can you do with commands? Well, anything you want, really. I have implemented
  134. a sort of planet using it. So, be creative, and if you do something interesting,
  135. let me know ;-)
  136. TemplateSystem Plugins
  137. ----------------------
  138. Nikola supports Mako and Jinja2. If you prefer some other templating
  139. system, then you will have to write a ``TemplateSystem`` plugin. Here's how they work.
  140. First, you have to create a ``.plugin`` file. Here's the one for the Mako plugin:
  141. .. code-block:: ini
  142. [Core]
  143. Name = mako
  144. Module = mako
  145. [Documentation]
  146. Author = Roberto Alsina
  147. Version = 0.1
  148. Website = https://getnikola.com
  149. Description = Support for Mako templates.
  150. .. note:: If you want to publish your plugin on the Plugin Index, `read
  151. the docs for the Index
  152. <https://github.com/getnikola/plugins/blob/master/README.md>`__
  153. (and the .plugin file examples and explanations).
  154. You will have to replace "mako" with your template system's name, and other data
  155. in the obvious ways.
  156. The "Module" option is the name of the module, which has to look something like this,
  157. a stub for a hypothetical system called "Templater":
  158. .. code-block:: python
  159. from nikola.plugin_categories import TemplateSystem
  160. # You have to inherit TemplateSystem
  161. class TemplaterTemplates(TemplateSystem):
  162. """Wrapper for Templater templates."""
  163. # name has to match Name in the .plugin file
  164. name = "templater"
  165. # A list of directories where the templates will be
  166. # located. Most template systems have some sort of
  167. # template loading tool that can use this.
  168. def set_directories(self, directories, cache_folder):
  169. """Sets the list of folders where templates are located and cache."""
  170. pass
  171. # You *must* implement this, even if to return []
  172. # It should return a list of all the files that,
  173. # when changed, may affect the template's output.
  174. # usually this involves template inheritance and
  175. # inclusion.
  176. def template_deps(self, template_name):
  177. """Returns filenames which are dependencies for a template."""
  178. return []
  179. def render_template(self, template_name, output_name, context):
  180. """Renders template to a file using context.
  181. This must save the data to output_name *and* return it
  182. so that the caller may do additional processing.
  183. """
  184. pass
  185. # The method that does the actual rendering.
  186. # template_name is the name of the template file,
  187. # context is a dictionary containing the data the template
  188. # uses for rendering.
  189. def render_template_to_string(self, template, context):
  190. """Renders template to a string using context. """
  191. pass
  192. def inject_directory(self, directory):
  193. """Injects the directory with the lowest priority in the
  194. template search mechanism."""
  195. pass
  196. You can see a real example in `the Jinja plugin <https://github.com/getnikola/nikola/blob/master/nikola/plugins/template/jinja.py>`__
  197. Task Plugins
  198. ------------
  199. If you want to do something that depends on the data in your site, you
  200. probably want to do a ``Task`` plugin, which will make it be part of the
  201. ``nikola build`` command. These are the currently available tasks, all
  202. provided by plugins:
  203. .. sidebar:: Other Tasks
  204. There are also ``LateTask`` plugins, which are executed later,
  205. and ``TaskMultiplier`` plugins that take a task and create
  206. more tasks out of it.
  207. .. code-block:: console
  208. $ nikola list
  209. Scanning posts....done!
  210. copy_assets
  211. copy_files
  212. create_bundles
  213. post_render
  214. redirect
  215. render_galleries
  216. render_listings
  217. render_pages
  218. render_posts
  219. render_site
  220. render_sources
  221. render_taxonomies
  222. robots_file
  223. scale_images
  224. sitemap
  225. These have access to the ``site`` object which contains your timeline and
  226. your configuration.
  227. The critical bit of Task plugins is their ``gen_tasks`` method, which ``yields``
  228. `doit tasks <https://pydoit.org/tasks.html>`_.
  229. The details of how to handle dependencies, etc., are a bit too much for this
  230. document, so I'll just leave you with an example, the ``copy_assets`` task.
  231. First the ``task_copy_assets.plugin`` file, which you should copy and edit
  232. in the logical ways:
  233. .. code-block:: ini
  234. [Core]
  235. Name = copy_assets
  236. Module = task_copy_assets
  237. [Documentation]
  238. Author = Roberto Alsina
  239. Version = 0.1
  240. Website = https://getnikola.com
  241. Description = Copy theme assets into output.
  242. .. note:: If you want to publish your plugin on the Plugin Index, `read
  243. the docs for the Index
  244. <https://github.com/getnikola/plugins/blob/master/README.md>`_
  245. (and the .plugin file examples and explanations).
  246. And the ``task_copy_assets.py`` file, in its entirety:
  247. .. code-block:: python
  248. import os
  249. from nikola.plugin_categories import Task
  250. from nikola import utils
  251. # Have to inherit Task to be a task plugin
  252. class CopyAssets(Task):
  253. """Copy theme assets into output."""
  254. name = "copy_assets"
  255. # This yields the tasks
  256. def gen_tasks(self):
  257. """Create tasks to copy the assets of the whole theme chain.
  258. If a file is present on two themes, use the version
  259. from the "youngest" theme.
  260. """
  261. # I put all the configurations and data the plugin uses
  262. # in a dictionary because utils.config_changed will
  263. # make it so that if these change, this task will be
  264. # marked out of date, and run again.
  265. kw = {
  266. "themes": self.site.THEMES,
  267. "output_folder": self.site.config['OUTPUT_FOLDER'],
  268. "filters": self.site.config['FILTERS'],
  269. }
  270. tasks = {}
  271. for theme_name in kw['themes']:
  272. src = os.path.join(utils.get_theme_path(theme_name), 'assets')
  273. dst = os.path.join(kw['output_folder'], 'assets')
  274. for task in utils.copy_tree(src, dst):
  275. if task['name'] in tasks:
  276. continue
  277. tasks[task['name']] = task
  278. task['uptodate'] = task.get('uptodate', []) + \
  279. [utils.config_changed(kw)]
  280. task['basename'] = self.name
  281. # If your task generates files, please do this.
  282. yield utils.apply_filters(task, kw['filters'])
  283. PageCompiler Plugins
  284. --------------------
  285. These plugins implement markup languages, they take sources for posts or pages and
  286. create HTML or other output files. A good example is `the misaka plugin
  287. <https://github.com/getnikola/plugins/tree/master/v8/misaka>`__ or the built-in
  288. compiler plugins.
  289. They must provide:
  290. ``compile``
  291. Function that builds a file.
  292. ``create_post``
  293. Function that creates an empty file with some metadata in it.
  294. If the compiler produces something other than HTML files, it should also implement ``extension`` which
  295. returns the preferred extension for the output file.
  296. These plugins can also be used to extract metadata from a file. To do so, the
  297. plugin must set ``supports_metadata`` to ``True`` and implement ``read_metadata`` that will return a dict containing the
  298. metadata contained in the file. Optionally, it may list ``metadata_conditions`` (see `MetadataExtractor Plugins`_ below)
  299. MetadataExtractor Plugins
  300. -------------------------
  301. Plugins that extract metadata from posts. If they are based on post content,
  302. they must implement ``_extract_metadata_from_text`` (takes source of a post
  303. returns a dict of metadata). They may also implement
  304. ``split_metadata_from_text``, ``extract_text``. If they are based on filenames,
  305. they only need ``extract_filename``. If ``support_write`` is set to True,
  306. ``write_metadata`` must be implemented.
  307. Every extractor must be configured properly. The ``name``, ``source`` (from the
  308. ``MetaSource`` enum in ``metadata_extractors``) and ``priority``
  309. (``MetaPriority``) fields are mandatory. There might also be a list of
  310. ``conditions`` (tuples of ``MetaCondition, arg``), used to check if an
  311. extractor can provide metadata, a compiled regular expression used to split
  312. metadata (``split_metadata_re``, may be ``None``, used by default
  313. ``split_metadata_from_text``), a list of ``requirements`` (3-tuples: import
  314. name, pip name, friendly name), ``map_from`` (name of ``METADATA_MAPPING`` to
  315. use, if any) and ``supports_write`` (whether the extractor supports writing
  316. metadata in the desired format).
  317. For more details, see the definition in ``plugin_categories.py`` and default extractors in ``metadata_extractors.py``.
  318. RestExtension Plugins
  319. ---------------------
  320. Implement directives for reStructuredText, see `media.py <https://github.com/getnikola/nikola/blob/master/nikola/plugins/compile/rest/media.py>`__ for a simple example.
  321. If your output depends on a config value, you need to make your post record a
  322. dependency on a pseudo-path, like this:
  323. .. code-block:: text
  324. ####MAGIC####CONFIG:OPTIONNAME
  325. Then, whenever the ``OPTIONNAME`` option is changed in conf.py, the file will be rebuilt.
  326. If your directive depends or may depend on the whole timeline (like the
  327. ``post-list`` directive, where adding new posts to the site could make it
  328. stale), you should record a dependency on the pseudo-path
  329. ``####MAGIC####TIMELINE``.
  330. MarkdownExtension Plugins
  331. -------------------------
  332. 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.
  333. Note that Python markdown extensions are often also available as separate
  334. packages. This is only meant to ship extensions along with Nikola.
  335. SignalHandler Plugins
  336. ---------------------
  337. These plugins extend the ``SignalHandler`` class and connect to one or more
  338. signals via `blinker <https://pythonhosted.org/blinker/>`_.
  339. The easiest way to do this is to reimplement ``set_site()`` and just connect to
  340. whatever signals you want there.
  341. Currently Nikola emits the following signals:
  342. ``sighandlers_loaded``
  343. Right after SignalHandler plugin activation.
  344. ``initialized``
  345. When all tasks are loaded.
  346. ``configured``
  347. When all the configuration file is processed. Note that plugins are activated before this is emitted.
  348. ``scanned``
  349. After posts are scanned.
  350. ``new_post`` / ``new_page``
  351. When a new post is created, using the ``nikola new_post``/``nikola new_page`` commands. The signal
  352. data contains the path of the file, and the metadata file (if there is one).
  353. ``existing_post`` / ``existing_page``
  354. When a new post fails to be created due to a title conflict. Contains the same data as ``new_post``.
  355. ``deployed``
  356. When the ``nikola deploy`` command is run, and there is at least one new
  357. entry/post since ``last_deploy``. The signal data is of the form::
  358. {
  359. 'last_deploy: # datetime object for the last deployed time,
  360. 'new_deploy': # datetime object for the current deployed time,
  361. 'clean': # whether there was a record of a last deployment,
  362. 'deployed': # all files deployed after the last deploy,
  363. 'undeployed': # all files not deployed since they are either future posts/drafts
  364. }
  365. ``compiled``
  366. When a post/page is compiled from its source to html, before anything else is done with it. The signal
  367. data is in the form::
  368. {
  369. 'source': # the path to the source file
  370. 'dest': # the path to the cache file for the post/page
  371. 'post': # the Post object for the post/page
  372. }
  373. One example is the `deploy_hooks plugin. <https://github.com/getnikola/plugins/tree/master/v7/deploy_hooks>`__
  374. ConfigPlugin Plugins
  375. --------------------
  376. Does nothing specific, can be used to modify the site object (and thus the config).
  377. Put all the magic you want in ``set_site()``, and don’t forget to run the one
  378. from ``super()``. Example plugin: `navstories <https://github.com/getnikola/plugins/tree/master/v7/navstories>`__
  379. CommentSystem Plugins
  380. ---------------------
  381. 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``.
  382. Example plugin: `cactuscomments <https://github.com/getnikola/plugins/tree/master/v8/cactuscomments>`__
  383. Shortcode Plugins
  384. -----------------
  385. Shortcode Plugins are a simple way to create a custom shortcode handler.
  386. By default, the ``set_site`` method will register the ``handler`` method as a
  387. shortcode with the plugin’s ``name`` as the shortcode name.
  388. See the Shortcodes_ section for more details on shortcodes.
  389. PostScanner Plugins
  390. -------------------
  391. Get posts and pages from "somewhere" to be added to the timeline.
  392. There are currently two plugins for this: the built-in ``scan_posts``, and
  393. ``pkgindex_scan`` (in the Plugin Index), which is used to treat .plugin/.theme
  394. + README.md as posts to generate the Plugin and Theme Indexes.
  395. Plugin Index
  396. ============
  397. There is a `plugin index <https://plugins.getnikola.com/>`__, which stores all
  398. of the plugins for Nikola people wanted to share with the world.
  399. You may want to read the `README for the Index
  400. <https://github.com/getnikola/plugins/blob/master/README.md>`_ if you want to
  401. publish your package there.
  402. Path/Link Resolution Mechanism
  403. ==============================
  404. Any plugin can register a function using ``Nikola.register_path_handler`` to
  405. allow resolution of paths and links. These are useful for templates, which
  406. can access them via ``_link``.
  407. For example, you can always get a link to the path for the feed of the "foo" tag
  408. by using ``_link('tag_rss', 'foo')`` or the ``link://tag_rss/foo`` URL.
  409. Here's the relevant code from the tag plugin.
  410. .. code-block:: python
  411. # In set_site
  412. site.register_path_handler('tag_rss', self.tag_rss_path)
  413. # And these always take name and lang as arguments and return a list of
  414. # path elements.
  415. def tag_rss_path(self, name, lang):
  416. return [_f for _f in [self.site.config['TRANSLATIONS'][lang],
  417. self.site.config['TAG_PATH'], self.slugify_name(name, lang) + ".xml"] if
  418. _f]
  419. Template Hooks
  420. ==============
  421. Plugins can use a hook system for adding stuff into templates. In order to use
  422. it, a plugin must register itself. The following hooks currently exist:
  423. * ``extra_head`` (not equal to the config option!)
  424. * ``body_end`` (not equal to the config option!)
  425. * ``page_header``
  426. * ``menu``
  427. * ``menu_alt`` (right-side menu in bootstrap, after ``menu`` in base)
  428. * ``page_footer``
  429. For example, in order to register a script into ``extra_head``:
  430. .. code-block:: python
  431. # In set_site
  432. site.template_hooks['extra_head'].append('<script src="/assets/js/fancyplugin.js">')
  433. There is also another API available. It allows use of dynamically generated
  434. HTML:
  435. .. code-block:: python
  436. # In set_site
  437. def generate_html_bit(name, ftype='js'):
  438. """Generate HTML for an asset."""
  439. return '<script src="/assets/{t}/{n}.{t}">'.format(n=name, t=ftype)
  440. site.template_hooks['extra_head'].append(generate_html_bit, False, 'fancyplugin', ftype='js')
  441. The second argument to ``append()`` is used to determine whether the function
  442. needs access to the current template context and the site. If it is set to
  443. ``True``, the function will also receive ``site`` and ``context`` keyword
  444. arguments. Example use:
  445. .. code-block:: python
  446. # In set_site
  447. def greeting(addr, endswith='', site=None, context=None):
  448. """Greet someone."""
  449. if context['lang'] == 'en':
  450. greet = u'Hello'
  451. elif context['lang'] == 'es':
  452. greet = u'¡Hola'
  453. t = u' BLOG_TITLE = {0}'.format(site.config['BLOG_TITLE'](context['lang']))
  454. return u'<h3>{greet} {addr}{endswith}</h3>'.format(greet=greet, addr=addr,
  455. endswith=endswith) + t
  456. site.template_hooks['page_header'].append(greeting, True, u'Nikola Tesla', endswith=u'!')
  457. Dependencies for template hooks:
  458. * if the input is a string, the string value, alongside arguments to ``append``, is used for calculating dependencies
  459. * 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.
  460. Make sure to provide at least a docstring, or a identifier, to ensure rebuilds work properly.
  461. Shortcodes
  462. ==========
  463. Some (hopefully all) markup compilers support shortcodes in these forms:
  464. .. code:: text
  465. {{% raw %}}{{% foo %}}{{% /raw %}} # No arguments
  466. {{% raw %}}{{% foo bar %}}{{% /raw %}} # One argument, containing "bar"
  467. {{% raw %}}{{% foo bar baz=bat %}}{{% /raw %}} # Two arguments, one containing "bar", one called "baz" containing "bat"
  468. {{% raw %}}{{% foo %}}Some text{{% /foo %}}{{% /raw %}} # one argument called "data" containing "Some text"
  469. So, if you are creating a plugin that generates markup, it may be a good idea
  470. to register it as a shortcode in addition of to restructured text directive or
  471. markdown extension, thus making it available to all markup formats.
  472. To implement your own shortcodes from a plugin, you can create a plugin inheriting ``ShortcodePlugin``.
  473. By default, the ``set_site`` method will register the ``handler`` method as a
  474. shortcode with the plugin’s ``name`` as the shortcode name. To have other
  475. shortcode names, you can call
  476. ``Nikola.register_shortcode(name, func)`` with the following arguments:
  477. ``name``:
  478. Name of the shortcode ("foo" in the examples above)
  479. ``func``:
  480. A function that will handle the shortcode
  481. The shortcode handler **must** return a two-element tuple, ``(output, dependencies)``
  482. ``output``:
  483. The text that will replace the shortcode in the document.
  484. ``dependencies``:
  485. A list of all the files on disk which will make the output be considered
  486. out of date. For example, if the shortcode uses a template, it should be
  487. the path to the template file.
  488. The shortcode handler **must** accept the following named arguments (or
  489. variable keyword arguments):
  490. ``site``:
  491. An instance of the Nikola class, to access site state
  492. ``data``:
  493. If the shortcut is used as opening/closing tags, it will be the text
  494. between them, otherwise ``None``.
  495. ``lang``:
  496. The current language.
  497. If the shortcode tag has arguments of the form ``foo=bar`` they will be
  498. passed as named arguments. Everything else will be passed as positional
  499. arguments in the function call.
  500. So, for example::
  501. {{% raw %}}{{% foo bar baz=bat beep %}}Some text{{% /foo %}}{{% /raw %}}
  502. Assuming you registered ``foo_handler`` as the handler function for the
  503. shortcode named ``foo``, this will result in the following call when the above
  504. shortcode is encountered::
  505. foo_handler("bar", "beep", baz="bat", data="Some text", site=whatever)
  506. Template-based Shortcodes
  507. -------------------------
  508. Another way to define a new shortcode is to add a template file to the
  509. ``shortcodes`` directory of your site. The template file must have the
  510. shortcode name as the basename and the extension ``.tmpl``. For example, if you
  511. want to add a new shortcode named ``foo``, create the template file as
  512. ``shortcodes/foo.tmpl``.
  513. When the shortcode is encountered, the matching template will be rendered with
  514. its context provided by the arguments given in the shortcode. Keyword arguments
  515. are passed directly, i.e. the key becomes the variable name in the template
  516. namespace with a matching string value. Non-keyword arguments are passed as
  517. string values in a tuple named ``_args``. As for normal shortcodes with a
  518. handler function, ``site`` and ``data`` will be added to the keyword arguments.
  519. Example:
  520. The following shortcode:
  521. .. code:: text
  522. {{% raw %}}{{% foo bar="baz" spam %}}{{% /raw %}}
  523. With a template in ``shortcodes/foo.tmpl`` with this content (using Jinja2
  524. syntax in this example)
  525. .. code:: jinja
  526. <div class="{{ _args[0] if _args else 'ham' }}">{{ bar }}</div>
  527. Will result in this output
  528. .. code:: html
  529. <div class="spam">baz</div>
  530. State and Cache
  531. ===============
  532. Sometimes your plugins will need to cache things to speed up further actions. Here are the conventions for that:
  533. * If it's a file, put it somewhere in ``self.site.config['CACHE_FOLDER']`` (defaults to ``cache/``.
  534. * If it's a value, use ``self.site.cache.set(key, value)`` to set it and ``self.site.cache.get(key)`` to get it.
  535. The key should be a string, the value should be json-encodable (so, be careful with datetime objects)
  536. The values and files you store there can **and will** be deleted sometimes by the user. They should always be
  537. things you can reconstruct without lossage. They are throwaways.
  538. On the other hand, sometimes you want to save something that is **not** a throwaway. These are things that may
  539. change the output, so the user should not delete them. We call that **state**. To save state:
  540. * If it's a file, put it somewhere in the working directory. Try not to do that please.
  541. * If it's a value, use ``self.site.state.set(key, value)`` to set it and ``self.state.cache.get(key)`` to get it.
  542. The key should be a string, the value should be json-encodable (so, be careful with datetime objects)
  543. The ``cache`` and ``state`` objects are rather simplistic, and that's intentional. They have no default values: if
  544. the key is not there, you will get ``None`` and like it. They are meant to be both threadsafe, but hey, who can
  545. guarantee that sort of thing?
  546. There are no sections, and no access protection, so let's not use it to store passwords and such. Use responsibly.
  547. Logging
  548. =======
  549. Plugins often need to produce messages to the screen. All plugins get a logger object (``self.logger``) by default,
  550. configured to work with Nikola (logging level, colorful output, plugin name as the logger name). If you need, you can
  551. also use the global (``nikola.utils.LOGGER``) logger, or you can instantiate custom loggers with
  552. ``nikola.utils.get_logger`` or the ``nikola.log`` module.
  553. Template and Dependency Injection
  554. =================================
  555. Plugins have access to two injection facilities.
  556. If your plugin needs custom templates for its features (adding pages, displaying stuff, etc.), you can put them in the
  557. ``templates/mako`` and ``templates/jinja`` subfolders in your plugin’s folder. Note that those templates have a very low
  558. priority, so that users can override your plugin’s templates with their own.
  559. If your plugin needs to inject dependencies, the ``inject_dependency(target, dependency)`` function can be used to add a
  560. ``dependency`` for tasks which basename == ``target``. This facility should be limited to cases which really need it,
  561. consider other facilities first (eg. adding post dependencies).