2015-12-26 15:11:45 +01:00
|
|
|
title: How To
|
2015-12-19 14:52:17 +01:00
|
|
|
---
|
2015-12-22 00:28:10 +01:00
|
|
|
summary: Covers the most common things plugins would do and how to implement it.
|
2015-12-19 14:52:17 +01:00
|
|
|
---
|
|
|
|
sort_key: 30
|
|
|
|
---
|
|
|
|
body:
|
|
|
|
|
|
|
|
So you want to build a plugin but it's not quite clear how you would structure
|
|
|
|
a plugin to accomplish this? This part of the documentation should help you
|
|
|
|
find answers to those questions.
|
|
|
|
|
|
|
|
## Expose Functionality
|
|
|
|
|
|
|
|
Most plugins will provide functionality of sorts. There are two places where
|
|
|
|
functionality is typically needed: in templates or for other plugins to use.
|
|
|
|
Plugins can import from each other just like this, but functionality exposed
|
|
|
|
into templates should follow some basic rules:
|
|
|
|
|
|
|
|
* modify `jinja_env.globals` or `jinja_env.filters` and do not use
|
|
|
|
`process-template-context` unless you absolutely know what you are doing.
|
|
|
|
* expose as few global variables as possible. If you have a lot you want to
|
|
|
|
provide then consider registering an object with the templates and to
|
|
|
|
attach multiple attributes on it.
|
|
|
|
* use clear names for template globals. This is a shared space that all
|
|
|
|
plugins and Lektor itself modify so do not provide a function called
|
|
|
|
`get_script` but for instance call it `get_my_plugin_script`.
|
|
|
|
|
|
|
|
A simple example of a plugin that implements [Gravatar
|
|
|
|
:ext](https://en.gravatar.com/) support:
|
|
|
|
|
|
|
|
```python
|
|
|
|
from hashlib import md5
|
|
|
|
from werkzeug.urls import url_encode
|
|
|
|
from lektor.pluginsystem import Plugin
|
|
|
|
|
|
|
|
BASE_URL = 'https://secure.gravatar.com/avatar/'
|
|
|
|
|
|
|
|
def get_gravatar(email, **options):
|
|
|
|
fn = md5(email.lower().strip().encode('utf-8')).hexdigest()
|
|
|
|
return '%s/%s?%s' % (BASE_URL, fn, url_encode(options))
|
|
|
|
|
|
|
|
class GravatarPlugin(Plugin):
|
|
|
|
name = 'Gravatar'
|
|
|
|
def on_setup_env(self, **extra):
|
|
|
|
env.jinja_env.filters['gravatar'] = get_gravatar
|
|
|
|
```
|
|
|
|
|
|
|
|
## Configure Plugins
|
|
|
|
|
|
|
|
Plugins can come with their own config files and it's encouraged that plugins
|
|
|
|
take advantage of this. Each plugin has exactly one INI file called
|
|
|
|
`<plugin-id>.ini` inside the `configs` folder.
|
|
|
|
|
|
|
|
To get access to the plugin the `get_config` function can be used which
|
|
|
|
returns a dict like object to access the ini file.
|
|
|
|
|
|
|
|
```python
|
|
|
|
config = self.get_config()
|
|
|
|
value = config.get('section.key', 'default_value')
|
|
|
|
```
|
|
|
|
|
|
|
|
This would correspond to this config in `configs/my-plugin.ini`:
|
|
|
|
|
|
|
|
```ini
|
|
|
|
[section]
|
|
|
|
key = the value
|
|
|
|
```
|
|
|
|
|
|
|
|
## Dependency Tracking
|
|
|
|
|
|
|
|
While a lot of dependencies are tracked automatically, when you develop a
|
|
|
|
plugin you probably will discover that sometimes you need to track your own
|
|
|
|
ones. There are different ways in which dependency tracking can work and
|
|
|
|
depending on the situation you might have to use two different mechanisms.
|
|
|
|
|
|
|
|
1. **Dependency tracking by association**: while a build of a source object
|
|
|
|
into an artifact is active more dependencies for that artifact can be
|
|
|
|
registered with the [record_dependency :ref](../../api/build/context/record-dependency/)
|
|
|
|
method of the context. It takes a filename that should be recorded as
|
|
|
|
additional dependency for the current artifact
|
|
|
|
2. **Dependency tracking as artifact source**: if you build your own artifact
|
|
|
|
you need to define the source files that make up the artifact (if you have
|
|
|
|
such a thing). For instance if you build a thumbnail you will need to
|
|
|
|
track those source files that are the source images. This can be done
|
|
|
|
through the [sub_artifact :ref](../../api/build/context/sub-artifact/)
|
|
|
|
method which declares a new artifact.
|
|
|
|
|
|
|
|
Here examples for both of those in one:
|
|
|
|
|
|
|
|
```python
|
|
|
|
import os
|
|
|
|
from flask import json
|
|
|
|
from lektor.pluginsystem import Plugin
|
|
|
|
|
|
|
|
def dump_exif(image):
|
|
|
|
ctx = get_ctx()
|
|
|
|
path = posixpath.join(image.path, '-exif.json')
|
|
|
|
@ctx.sub_artifact(path, sources=[image.source_filename])
|
|
|
|
def include_file(artifact):
|
|
|
|
ctx.record_dependency(__file__)
|
|
|
|
with artifact.open('wb') as f:
|
|
|
|
json.dump(image.exif.to_dict(), f)
|
|
|
|
return path
|
|
|
|
|
|
|
|
class ExifDumpPlugin(Plugin):
|
|
|
|
def setup_env(self, **extra):
|
|
|
|
self.env.jinja_env.globals['dump_exif'] = dump_exif
|
|
|
|
```
|
|
|
|
|
|
|
|
This dumps out the EXIF data into a JSON file and returns the artifact name.
|
|
|
|
The source image is tracked as direct source for the artifact and within the
|
|
|
|
function we also track the plugin's filename to rebuild if the plugin changes.
|