<commit_before>---
layout: default
title: Obraz Plugins
---

{{ page.title }}
================

_The Obraz plugin system is still considered experimental and may be changed
in the future versions._

Plugins allow extending the site generation process via user-defined processing
functions.

Consider a photo hosting example. By writing a custom plugin you can:

* Use album data from your media library stored in a SQL database
* Add EXIF metadata from images to your photo pages
* Generate re-sized images and thumbnails

Plugins are Python files that should be put into the `_plugins` directory of
the site.

Obraz plugins are incompatible with Jekyll plugins, see
<a href="{{ site.baseurl }}/jekyll.html">compatiblity with Jekyll</a>.


Available Plugins
-----------------

* [Tags](https://bitbucket.org/vlasovskikh/obraz/src/public/doc/_plugins/tags.py)
  by [<NAME>](http://pirx.ru/): Generate per-tag pages

_Note:_ You can add your plugins to this list by sending a pull request to the
[Obraz repository][3] on Bitbucket.


Site Model
----------

Internally Obraz represents the site being generated as a Python dictionary.
This is the same dictionary as `site` in Jekyll [template data][2].

The generation process is split into three steps:

1. Loader functions populate the site dictionary by reading files from the
   source directory
2. Processor functions modify the site dictionary (e.g. sort data)
3. Generator functions create files in the destination directory based on the
   site dictionary

Functions for all these steps are registered via extension point decorators.
This applies to third-party plugin functions as well as to the processing
functions in Obraz itself.

There are also a couple of extension points that allow to define content and
template filters.


Extension Points
----------------

* **`@obraz.loader`**

    Register a site source content loader.

    Source content loaders transform a file path from the site source directory
    into a dictionary that will be merged by Obraz into the `site` dictionary.
    If the loader wants to skip a file and pass it to other loaders, then it
    should return `None`. Loaders are not allowed to modify their `site`
    parameter.

    Loaders are useful when you have source files in some format that you want
    to parse and make available as a part of the site dictionary instead of
    treating them like pages with YAML front matter or regular static files.

    A site content loader is a fuction of type `(path: str, site: dict) ->
    dict`.

    Example:

        import os
        import xml.etree.ElementTree as etree
        import obraz

        @obraz.loader
        def load_checkins(path, site):
            """Loading check-ins from a KML file."""
            if not obraz.is_file_visible(path, site):
                return None
            if not path.endswith('.kml'):
                return None
            root = etree.parse(os.path.join(site['source'], path))
            return {
                'checkins': root.findall('Folder/Placemark'),
            }

* **`@o<EMAIL>.processor`**

    Register a site content processor.

    Content processors are useful when you need to extend or rearrange already
    existing site data.

    A site content processor is a fuction of type `(site: dict) -> None`.

    Example:

        import os
        import obraz
        from PIL import Image
        from PIL.ExifTags import TAGS
<commit_msg>Renamed 'loader' extension point argument from 'site' to 'config'<commit_after>---
layout: default
title: Obraz Plugins
---

{{ page.title }}
================

_The Obraz plugin system is still considered experimental and may be changed
in the future versions._

Plugins allow extending the site generation process via user-defined processing
functions.

Consider a photo hosting example. By writing a custom plugin you can:

* Use album data from your media library stored in a SQL database
* Add EXIF metadata from images to your photo pages
* Generate re-sized images and thumbnails

Plugins are Python files that should be put into the `_plugins` directory of
the site.

Obraz plugins are incompatible with Jekyll plugins, see
<a href="{{ site.baseurl }}/jekyll.html">compatiblity with Jekyll</a>.


Available Plugins
-----------------

* [Tags](https://bitbucket.org/vlasovskikh/obraz/src/public/doc/_plugins/tags.py)
  by [<NAME>](http://pirx.ru/): Generate per-tag pages

_Note:_ You can add your plugins to this list by sending a pull request to the
[Obraz repository][3] on Bitbucket.


Site Model
----------

Internally Obraz represents the site being generated as a Python dictionary.
This is the same dictionary as `site` in Jekyll [template data][2].

The generation process is split into three steps:

1. Loader functions populate the site dictionary by reading files from the
   source directory
2. Processor functions modify the site dictionary (e.g. sort data)
3. Generator functions create files in the destination directory based on the
   site dictionary

Functions for all these steps are registered via extension point decorators.
This applies to third-party plugin functions as well as to the processing
functions in Obraz itself.

There are also a couple of extension points that allow to define content and
template filters.


Extension Points
----------------

* **`@obraz.loader`**

    Register a site source content loader.

    Source content loaders transform a file path from the site source directory
    into a dictionary that will be merged by Obraz into the `site` dictionary.
    If the loader wants to skip a file and pass it to other loaders, then it
    should return `None`. Loaders are not allowed to modify their `config`
    parameter.

    Loaders are useful when you have source files in some format that you want
    to parse and make available as a part of the site dictionary instead of
    treating them like pages with YAML front matter or regular static files.

    A site content loader is a fuction of type `(path: str, config: dict) ->
    dict`.

    Example:

        import os
        import xml.etree.ElementTree as etree
        import obraz

        @obraz.loader
        def load_checkins(path, config):
            """Loading check-ins from a KML file."""
            if not obraz.is_file_visible(path, config):
                return None
            if not path.endswith('.kml'):
                return None
            root = etree.parse(os.path.join(config['source'], path))
            return {
                'checkins': root.findall('Folder/Placemark'),
            }

* **`@obraz.processor`**

    Register a site content processor.

    Content processors are useful when you need to extend or rearrange already
    existing site data.

    A site content processor is a fuction of type `(site: dict) -> None`.

    Example:

        import os
        import obraz
        from PIL import Image
        from PIL.ExifTags import TAGS

{'fraction_non_alphanumeric': 0.07766431756076514, 'fraction_numerical': 0.0014382281029771322, 'mean_word_length': 2.9488926746166952, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 14, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '862297', 'n_tokens_mistral': 1801, 'n_tokens_neox': 1749, 'n_words': 888}