Rewrite plugins docs
This commit is contained in:
parent
5d4c10165f
commit
a16343a531
|
@ -0,0 +1 @@
|
||||||
|
Rewrote the plugins documentation (#1910)
|
|
@ -0,0 +1,200 @@
|
||||||
|
# Write a plugin
|
||||||
|
|
||||||
|
You can write plugins to extend the features of your Funkwhale pod. Follow the instructions in this guide to get started with your first plugin.
|
||||||
|
|
||||||
|
```{contents}
|
||||||
|
:local:
|
||||||
|
:depth: 2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Before you begin
|
||||||
|
|
||||||
|
Before you start writing your plugin, you need to understand the following core concepts:
|
||||||
|
|
||||||
|
```{contents}
|
||||||
|
:local:
|
||||||
|
:depth: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
We'll explain each of these concepts in the next few sections
|
||||||
|
|
||||||
|
### Scopes
|
||||||
|
|
||||||
|
Plugins fall into two different **scopes**:
|
||||||
|
|
||||||
|
1. User-level plugins that are configured by end-users for their own use
|
||||||
|
2. Pod-level plugins that are configured by pod admins and are not connected to a particular user
|
||||||
|
|
||||||
|
User-level plugins can also be used to import files from a third-party service, such as cloud storage or FTP.
|
||||||
|
|
||||||
|
### Hooks
|
||||||
|
|
||||||
|
**Hooks** are entrypoints that allow your plugin to listen to changes. You can create hooks to react to different events that occur in the Funkwhale application.
|
||||||
|
|
||||||
|
An example of this can be seen in our Scrobbler plugin. We register a `LISTENING_CREATED` hook to notify any registered callback function when a listening is recorded. When a user listens to a track, the `notfy_lastfm` function fires.
|
||||||
|
|
||||||
|
```{code-block} python
|
||||||
|
from config import plugins
|
||||||
|
from .funkwhale_startup import PLUGIN
|
||||||
|
|
||||||
|
@plugins.register_hook(plugins.LISTENING_CREATED, PLUGIN)
|
||||||
|
def notify_lastfm(listening, conf, **kwargs):
|
||||||
|
# do something
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Available hooks
|
||||||
|
|
||||||
|
```{eval-rst}
|
||||||
|
.. autodata:: config.plugins.LISTENING_CREATED
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filters
|
||||||
|
|
||||||
|
**Filters** are entrypoints that allow you to modify or add information. When you use the `register_filter` decorator, your function should return a value to be used by the server.
|
||||||
|
|
||||||
|
In this example, the `PLUGINS_DEPENDENCIES` filter is used to install additional dependencies required by your plugin. The `dependencies` function returns the additional dependency `django_prometheus` to request the dependency be installed by the server.
|
||||||
|
|
||||||
|
```{code-block} python
|
||||||
|
# funkwhale_startup.py
|
||||||
|
# ...
|
||||||
|
from config import plugins
|
||||||
|
|
||||||
|
@plugins.register_filter(plugins.PLUGINS_DEPENDENCIES, PLUGIN)
|
||||||
|
def dependencies(dependencies, **kwargs):
|
||||||
|
return dependencies + ["django_prometheus"]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Available filters
|
||||||
|
|
||||||
|
```{eval-rst}
|
||||||
|
.. autodata:: config.plugins.PLUGINS_DEPENDENCIES
|
||||||
|
.. autodata:: config.plugins.PLUGINS_APPS
|
||||||
|
.. autodata:: config.plugins.MIDDLEWARES_BEFORE
|
||||||
|
.. autodata:: config.plugins.MIDDLEWARES_AFTER
|
||||||
|
.. autodata:: config.plugins.URLS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Write your plugin
|
||||||
|
|
||||||
|
Once you know what type of plugin you want to write and what entrypoint you want to use, you can start writing your plugin.
|
||||||
|
|
||||||
|
Plugins are made up of the following 3 files:
|
||||||
|
|
||||||
|
- `__init__.py` - indicates that the directory is a Python package
|
||||||
|
- `funkwhale_startup.py` - the file that loads during Funkwhale initialization
|
||||||
|
- `funkwhale_ready.py` - the file that loads when Funkwhale is configured and ready
|
||||||
|
|
||||||
|
### Declare your plugin
|
||||||
|
|
||||||
|
You need to declare your plugin and its configuration options so that Funkwhale knows how to load the plugin. To do this, you must declare a new `plugins` instance in your `funkwhale_startup.py` file.
|
||||||
|
|
||||||
|
Your `plugins` should include the following information:
|
||||||
|
|
||||||
|
```{list-table}
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Parameter
|
||||||
|
- Data type
|
||||||
|
- Description
|
||||||
|
* - `name`
|
||||||
|
- String
|
||||||
|
- The name of your plugin, used in the `.env` file
|
||||||
|
* - `label`
|
||||||
|
- String
|
||||||
|
- The readable label that appears in the Funkwhale frontend
|
||||||
|
* - `description`
|
||||||
|
- String
|
||||||
|
- A meaningful description of your plugin and what it does
|
||||||
|
* - `version`
|
||||||
|
- String
|
||||||
|
- The version number of your plugin
|
||||||
|
* - `user`
|
||||||
|
- Boolean
|
||||||
|
- Whether the plugin is a **user-level** plugin or a **pod-level** plugin. See [scopes](#scopes) for more information
|
||||||
|
* - `conf`
|
||||||
|
- Array of Objects
|
||||||
|
- A list of configuration options
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we declare a new **user-level** plugin called "My Plugin". The user can configure a `greeting` in the plugin configuration.
|
||||||
|
|
||||||
|
```{code-block} python
|
||||||
|
# funkwhale_startup.py
|
||||||
|
from config import plugins
|
||||||
|
|
||||||
|
PLUGIN = plugins.get_plugin_config(
|
||||||
|
name="myplugin",
|
||||||
|
label="My Plugin",
|
||||||
|
description="An example plugin that greets you",
|
||||||
|
version="0.1",
|
||||||
|
user=True,
|
||||||
|
conf=[
|
||||||
|
# This configuration option is editable by each user
|
||||||
|
{"name": "greeting", "type": "text", "label": "Greeting", "default": "Hello"},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Write your plugin logic
|
||||||
|
|
||||||
|
Once you've declared your plugin, you can write the plugin code in your `funkwhale_ready.py` file.
|
||||||
|
|
||||||
|
```{note}
|
||||||
|
You must import your plugin declaration from your `funkwhale_startup.py` file.
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we create a simple API endpoint that returns a greeting to the user. To do this:
|
||||||
|
|
||||||
|
1. We create a new APIView class that accepts a `GET` request
|
||||||
|
2. We read the greeting value from the plugin `conf`
|
||||||
|
3. We return the greeting value with the user's username
|
||||||
|
4. We register this view at the endpoint `/greeting`
|
||||||
|
|
||||||
|
```{code-block} python
|
||||||
|
# funkwhale_ready.py
|
||||||
|
from django.urls import path
|
||||||
|
from rest_framework import response
|
||||||
|
from rest_framework import views
|
||||||
|
|
||||||
|
from config import plugins
|
||||||
|
|
||||||
|
# Import the plugin declaration from funkwhale_startup
|
||||||
|
from .funkwhale_startup import PLUGIN
|
||||||
|
|
||||||
|
# Create a new APIView class
|
||||||
|
class GreetingView(views.APIView):
|
||||||
|
permission_classes = []
|
||||||
|
# Register a GET response
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
# Check the conf value of the plugin for the user
|
||||||
|
conf = plugins.get_conf(PLUGIN["name"], request.user)
|
||||||
|
if not conf["enabled"]:
|
||||||
|
# Return an error code if the user hasn't enabled the plugin
|
||||||
|
return response.Response(status=405)
|
||||||
|
# Set the greeting value to the user's configured greeting
|
||||||
|
greeting = conf["conf"]["greeting"]
|
||||||
|
data = {
|
||||||
|
# Append the user's username to the greeting
|
||||||
|
"greeting": "{} {}!".format(greeting, request.user.username)
|
||||||
|
}
|
||||||
|
# Return the greeting
|
||||||
|
return response.Response(data)
|
||||||
|
|
||||||
|
# Register the new APIView at the /greeting endpoint
|
||||||
|
@plugins.register_filter(plugins.URLS, PLUGIN)
|
||||||
|
def register_view(urls, **kwargs):
|
||||||
|
return urls + [
|
||||||
|
path('greeting', GreetingView.as_view())
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Result
|
||||||
|
|
||||||
|
Here is an example of how the above plugin works:
|
||||||
|
|
||||||
|
1. User "Harry" enables the plugin
|
||||||
|
2. "Harry" changes the greeting to "You're a wizard"
|
||||||
|
3. "Harry" visits the `/greeting` endpoint in their browser
|
||||||
|
4. The browser returns the message "You're a wizard Harry"
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Funkwhale plugins
|
||||||
|
|
||||||
|
Plugins can be used to extend Funkwhale's featureset without needing to touch the underlying code. Plugins can extend existing features, add support for third-party services, or introduce cosmetic changes to the Funkwhale webapp.
|
||||||
|
|
||||||
|
Plugins have been supported since Funkwhale 1.0. Some core plugins, such as the standard Scrobbler plugin, are maintained by the Funkwhale team.
|
||||||
|
|
||||||
|
```{toctree}
|
||||||
|
---
|
||||||
|
caption: Resources
|
||||||
|
maxdepth: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
create
|
||||||
|
install
|
||||||
|
|
||||||
|
```
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Install a plugin
|
||||||
|
|
||||||
|
Once you have [created your plugin](create.md), you can install it on your Funkwhale pod.
|
||||||
|
|
||||||
|
## Install a local plugin
|
||||||
|
|
||||||
|
To install a plugin located on your server:
|
||||||
|
|
||||||
|
1. Add the plugin directory to the `FUNKWHALE_PLUGINS_PATH` variable in your `.env` file
|
||||||
|
2. Add the plugin name to the `FUNKWHALE_PLUGINS` variable in your `.env` file
|
||||||
|
|
||||||
|
```{code-block} text
|
||||||
|
FUNKWHALE_PLUGINS=myplugin,anotherplugin
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Restart Funkwhale to pick up the changes
|
||||||
|
|
||||||
|
## Install a third-party plugin
|
||||||
|
|
||||||
|
You can install third-party plugins using the `manage.py` script. To do this:
|
||||||
|
|
||||||
|
1. Add the plugin name to the `FUNKWHALE_PLUGINS` variable in your `.env` file
|
||||||
|
|
||||||
|
```{code-block} text
|
||||||
|
FUNKWHALE_PLUGINS=myplugin,anotherplugin
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Call the `manage.py` script with the location of the plugin archive
|
||||||
|
|
||||||
|
:::: {tab-set}
|
||||||
|
|
||||||
|
:::{tab-item} Debian
|
||||||
|
|
||||||
|
```{code-block} shell
|
||||||
|
python manage.py fw plugins install https://plugin_url.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{tab-item} Docker
|
||||||
|
|
||||||
|
```{code-block} shell
|
||||||
|
docker-compose run --rm api python manage.py fw plugins install https://plugin_url.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
::::
|
||||||
|
|
||||||
|
3. Restart Funkwhale to pick up the changes
|
|
@ -1,95 +0,0 @@
|
||||||
API Authentication
|
|
||||||
==================
|
|
||||||
|
|
||||||
Each Funkwhale API endpoint supports access from:
|
|
||||||
|
|
||||||
- Anonymous users (if the endpoint is configured to do so, for exemple via the ``API Authentication Required`` setting)
|
|
||||||
- Logged-in users
|
|
||||||
- Third-party apps (via OAuth2)
|
|
||||||
|
|
||||||
To seamlessly support this range of access modes, we internally use oauth scopes
|
|
||||||
to describes what permissions are required to perform any given operation.
|
|
||||||
|
|
||||||
OAuth
|
|
||||||
-----
|
|
||||||
|
|
||||||
Create an app
|
|
||||||
:::::::::::::
|
|
||||||
|
|
||||||
To connect to Funkwhale API via OAuth, you need to create an application. There are
|
|
||||||
two ways to do that:
|
|
||||||
|
|
||||||
1. By visiting ``/settings/applications/new`` when logged in on your Funkwhale instance.
|
|
||||||
2. By sending a ``POST`` request to ``/api/v1/oauth/apps/``, as described in `our API documentation <https://docs.funkwhale.audio/swagger/>`_.
|
|
||||||
|
|
||||||
Both method will give you a client ID and secret.
|
|
||||||
|
|
||||||
Getting an access token
|
|
||||||
:::::::::::::::::::::::
|
|
||||||
|
|
||||||
Once you have a client ID and secret, you can request access tokens
|
|
||||||
using the `authorization code grant flow <https://tools.ietf.org/html/rfc6749#section-4.1>`_.
|
|
||||||
|
|
||||||
We support the ``urn:ietf:wg:oauth:2.0:oob`` redirect URI for non-web applications, as well
|
|
||||||
as traditionnal redirection-based flow.
|
|
||||||
|
|
||||||
Our authorization endpoint is located at ``/authorize``, and our token endpoint at ``/api/v1/oauth/token/``.
|
|
||||||
|
|
||||||
Refreshing tokens
|
|
||||||
:::::::::::::::::
|
|
||||||
|
|
||||||
When your access token is expired, you can `request a new one as described in the OAuth specification <https://tools.ietf.org/html/rfc6749#section-6>`_.
|
|
||||||
|
|
||||||
Security considerations
|
|
||||||
:::::::::::::::::::::::
|
|
||||||
|
|
||||||
- Grant codes are valid for a 5 minutes after authorization request is approved by the end user.
|
|
||||||
- Access codes are valid for 10 hours. When expired, you will need to request a new one using your refresh token.
|
|
||||||
- We return a new refresh token everytime an access token is requested, and invalidate the old one. Ensure you store the new refresh token in your app.
|
|
||||||
|
|
||||||
|
|
||||||
Scopes
|
|
||||||
::::::
|
|
||||||
|
|
||||||
Scopes are defined in :file:`funkwhale_api/users/oauth/scopes.py:BASE_SCOPES`, and generally are mapped to a business-logic resources (follows, favorites, etc.). All those base scopes come in two flawours:
|
|
||||||
|
|
||||||
- `read:<base_scope>`: get read-only access to the resource
|
|
||||||
- `write:<base_scope>`: get write-only access to the ressource
|
|
||||||
|
|
||||||
For example, ``playlists`` is a base scope, and ``write:playlists`` is the actual scope needed to perform write
|
|
||||||
operations on playlists (via a ``POST``, ``PATCH``, ``PUT`` or ``DELETE``. ``read:playlists`` is used
|
|
||||||
to perform read operations on playlists such as fetching a given playlist via ``GET``.
|
|
||||||
|
|
||||||
Having the generic ``read`` or ``write`` scope give you the corresponding access on *all* resources.
|
|
||||||
|
|
||||||
This is the list of OAuth scopes that third-party applications can request:
|
|
||||||
|
|
||||||
.. list-table:: Oauth scopes
|
|
||||||
:header-rows: 1
|
|
||||||
|
|
||||||
* - Scope
|
|
||||||
- Description
|
|
||||||
* - ``read``
|
|
||||||
- Read-only access to all data (equivalent to all ``read:*`` scopes).
|
|
||||||
* - ``write``
|
|
||||||
- Read-only access to all data (equivalent to all ``write:*`` scopes).
|
|
||||||
* - ``<read/write>:profile``
|
|
||||||
- Access to profile data (e-mail address, username, etc.)
|
|
||||||
* - ``<read/write>:libraries``
|
|
||||||
- Access to library data (uploads, libraries, tracks, albums, artists…)
|
|
||||||
* - ``<read/write>:favorites``
|
|
||||||
- Access to favorites
|
|
||||||
* - ``<read/write>:listenings``
|
|
||||||
- Access to history
|
|
||||||
* - ``<read/write>:follows``
|
|
||||||
- Access to followers
|
|
||||||
* - ``<read/write>:playlists``
|
|
||||||
- Access to playlists
|
|
||||||
* - ``<read/write>:radios``
|
|
||||||
- Access to radios
|
|
||||||
* - ``<read/write>:filters``
|
|
||||||
- Access to content filters
|
|
||||||
* - ``<read/write>:notifications``
|
|
||||||
- Access to notifications
|
|
||||||
* - ``<read/write>:edits``
|
|
||||||
- Access to metadata edits
|
|
|
@ -1,14 +0,0 @@
|
||||||
Developer documentation
|
|
||||||
=========================
|
|
||||||
|
|
||||||
This documentation is targeted primarily at developers who want to understand
|
|
||||||
how Funkwhale works and how to build apps that integrate with Funkwhale's ecosystem.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
../api
|
|
||||||
./authentication
|
|
||||||
./plugins
|
|
||||||
../federation/index
|
|
||||||
subsonic
|
|
|
@ -1,165 +0,0 @@
|
||||||
Funkwhale plugins
|
|
||||||
=================
|
|
||||||
|
|
||||||
Starting with Funkwhale 1.0, it is now possible to implement new features
|
|
||||||
via plugins.
|
|
||||||
|
|
||||||
Some plugins are maintained by the Funkwhale team (e.g. this is the case of the ``scrobbler`` plugin),
|
|
||||||
or by third-parties.
|
|
||||||
|
|
||||||
Installing a plugin
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
To install a plugin, ensure its directory is present in the ``FUNKWHALE_PLUGINS_PATH`` directory.
|
|
||||||
|
|
||||||
Then, add its name to the ``FUNKWHALE_PLUGINS`` environment variable, like this::
|
|
||||||
|
|
||||||
FUNKWHALE_PLUGINS=myplugin,anotherplugin
|
|
||||||
|
|
||||||
We provide a command to make it easy to install third-party plugins::
|
|
||||||
|
|
||||||
python manage.py fw plugins install https://pluginurl.zip
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
If you use the command, you will still need to append the plugin name to ``FUNKWHALE_PLUGINS``
|
|
||||||
|
|
||||||
|
|
||||||
Types of plugins
|
|
||||||
----------------
|
|
||||||
|
|
||||||
There are two types of plugins:
|
|
||||||
|
|
||||||
1. Plugins that are accessible to end-users, a.k.a. user-level plugins. This is the case of our Scrobbler plugin
|
|
||||||
2. Pod-level plugins that are configured by pod admins and are not tied to a particular user
|
|
||||||
|
|
||||||
Additionally, user-level plugins can be regular plugins or source plugins. A source plugin provides
|
|
||||||
a way to import files from a third-party service, e.g via webdav, FTP or something similar.
|
|
||||||
|
|
||||||
Hooks and filters
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Funkwhale includes two kind of entrypoints for plugins to use: hooks and filters. B
|
|
||||||
|
|
||||||
Hooks should be used when you want to react to some change. For instance, the ``LISTENING_CREATED`` hook
|
|
||||||
notify each registered callback that a listening was created. Our ``scrobbler`` plugin has a callback
|
|
||||||
registered to this hook, so that it can notify Last.fm properly:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from config import plugins
|
|
||||||
from .funkwhale_startup import PLUGIN
|
|
||||||
|
|
||||||
@plugins.register_hook(plugins.LISTENING_CREATED, PLUGIN)
|
|
||||||
def notify_lastfm(listening, conf, **kwargs):
|
|
||||||
# do something
|
|
||||||
|
|
||||||
Filters work slightly differently, and expect callbacks to return a value that will be used by Funkwhale.
|
|
||||||
|
|
||||||
For instance, the ``PLUGINS_DEPENDENCIES`` filter can be used as a way to install additional dependencies needed by your plugin:
|
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# funkwhale_startup.py
|
|
||||||
# ...
|
|
||||||
from config import plugins
|
|
||||||
|
|
||||||
@plugins.register_filter(plugins.PLUGINS_DEPENDENCIES, PLUGIN)
|
|
||||||
def dependencies(dependencies, **kwargs):
|
|
||||||
return dependencies + ["django_prometheus"]
|
|
||||||
|
|
||||||
To sum it up, hooks are used when you need to react to something, and filters when you need to alter something.
|
|
||||||
|
|
||||||
Writing a plugin
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Regardless of the type of plugin you want to write, lots of concepts are similar.
|
|
||||||
|
|
||||||
First, a plugin need three files:
|
|
||||||
|
|
||||||
- a ``__init__.py`` file, since it's a Python package
|
|
||||||
- a ``funkwhale_startup.py`` file, that is loaded during Funkwhale initialization
|
|
||||||
- a ``funkwhale_ready.py`` file, that is loaded when Funkwhale is configured and ready
|
|
||||||
|
|
||||||
So your plugin directory should look like this::
|
|
||||||
|
|
||||||
myplugin
|
|
||||||
├── funkwhale_ready.py
|
|
||||||
├── funkwhale_startup.py
|
|
||||||
└── __init__.py
|
|
||||||
|
|
||||||
Now, let's write our plugin!
|
|
||||||
|
|
||||||
``funkwhale_startup.py`` is where you declare your plugin and it's configuration options:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# funkwhale_startup.py
|
|
||||||
from config import plugins
|
|
||||||
|
|
||||||
PLUGIN = plugins.get_plugin_config(
|
|
||||||
name="myplugin",
|
|
||||||
label="My Plugin",
|
|
||||||
description="An example plugin that greets you",
|
|
||||||
version="0.1",
|
|
||||||
# here, we write a user-level plugin
|
|
||||||
user=True,
|
|
||||||
conf=[
|
|
||||||
# this configuration options are editable by each user
|
|
||||||
{"name": "greeting", "type": "text", "label": "Greeting", "default": "Hello"},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
Now that our plugin is declared and configured, let's implement actual functionality in ``funkwhale_ready.py``:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
# funkwhale_ready.py
|
|
||||||
from django.urls import path
|
|
||||||
from rest_framework import response
|
|
||||||
from rest_framework import views
|
|
||||||
|
|
||||||
from config import plugins
|
|
||||||
|
|
||||||
from .funkwhale_startup import PLUGIN
|
|
||||||
|
|
||||||
# Our greeting view, where the magic happens
|
|
||||||
class GreetingView(views.APIView):
|
|
||||||
permission_classes = []
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
# retrieve plugin configuration for the current user
|
|
||||||
conf = plugins.get_conf(PLUGIN["name"], request.user)
|
|
||||||
if not conf["enabled"]:
|
|
||||||
# plugin is disabled for this user
|
|
||||||
return response.Response(status=405)
|
|
||||||
greeting = conf["conf"]["greeting"]
|
|
||||||
data = {
|
|
||||||
"greeting": "{} {}!".format(greeting, request.user.username)
|
|
||||||
}
|
|
||||||
return response.Response(data)
|
|
||||||
|
|
||||||
# Ensure our view is known by Django and available at /greeting
|
|
||||||
@plugins.register_filter(plugins.URLS, PLUGIN)
|
|
||||||
def register_view(urls, **kwargs):
|
|
||||||
return urls + [
|
|
||||||
path('greeting', GreetingView.as_view())
|
|
||||||
]
|
|
||||||
|
|
||||||
And that's pretty much it. Now, login, visit https://yourpod.domain/settings/plugins, set a value in the ``greeting`` field and enable the plugin.
|
|
||||||
|
|
||||||
After that, you should be greeted properly if you go to https://yourpod.domain/greeting.
|
|
||||||
|
|
||||||
Hooks reference
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. autodata:: config.plugins.LISTENING_CREATED
|
|
||||||
|
|
||||||
Filters reference
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. autodata:: config.plugins.PLUGINS_DEPENDENCIES
|
|
||||||
.. autodata:: config.plugins.PLUGINS_APPS
|
|
||||||
.. autodata:: config.plugins.MIDDLEWARES_BEFORE
|
|
||||||
.. autodata:: config.plugins.MIDDLEWARES_AFTER
|
|
||||||
.. autodata:: config.plugins.URLS
|
|
|
@ -76,6 +76,7 @@ developer_documentation/setup/index
|
||||||
developer_documentation/contribute/index
|
developer_documentation/contribute/index
|
||||||
developer_documentation/workflows/index
|
developer_documentation/workflows/index
|
||||||
developer_documentation/api/index
|
developer_documentation/api/index
|
||||||
|
developer_documentation/plugins/index
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue