Skip to content
Snippets Groups Projects
plugins.py 3.25 KiB
Newer Older
from django.apps import AppConfig

from pluggy import PluginManager, HookimplMarker, HookspecMarker

plugins_manager = PluginManager("funkwhale")
plugin_hook = HookimplMarker("funkwhale")
plugin_spec = HookspecMarker("funkwhale")


class ConfigError(ValueError):
    pass


class Plugin(AppConfig):
Agate's avatar
Agate committed
    conf_serializer = None
Agate's avatar
Agate committed
        return self.instance.conf

    def set_conf(self, data):
        if self.conf_serializer:
            s = self.conf_serializer(data=data)
            s.is_valid(raise_exception=True)
            data = s.validated_data
        instance = self.instance()
        instance.conf = data
        instance.save(update_fields=["conf"])

    def instance(self):
        """Return the DB object that match the plugin"""
        from funkwhale_api.common import models

        return models.PodPlugin.objects.get_or_create(code=self.name)[0]

    def plugin_settings(self):
        """
        Return plugin specific settings from django.conf.settings
        """
        from django.conf import settings

        d = {}
        for key in dir(settings):
            k = key.lower()
            if not k.startswith("plugin_{}_".format(self.name.lower())):
                continue

            value = getattr(settings, key)
            s_key = k.replace("plugin_{}_".format(self.name.lower()), "")
            d[s_key] = value
            return clean(d, self.conf, self.name)


def clean(d, conf, plugin_name):
    cleaned = {}
    for key, c in conf.items():
        if key in d:
            try:
                cleaned[key] = c["validator"](d[key])
            except (ValueError, TypeError, AttributeError):
                raise ConfigError(
                    "Invalid value {} for setting {} in plugin {}".format(
                        d[key], key, plugin_name
                    )
                )

        else:
            cleaned[key] = c["default"]

    return cleaned


class HookSpec:
    @plugin_spec
    def database_engine(self):
        """
        Customize the database engine with a new class
        """

    @plugin_spec
    def register_apps(self):
        """
        Register additional apps in INSTALLED_APPS.

        :rvalue: list"""

    def middlewares_before(self):
        """
        Register additional middlewares at the outer level.

        :rvalue: list"""

    def middlewares_after(self):
        """
        Register additional middlewares at the inner level.

        :rvalue: list"""

    def urls(self):
        """
        Register additional urls.

        :rvalue: list"""


plugins_manager.add_hookspecs(HookSpec())
Agate's avatar
Agate committed
    return plugins_manager.register(plugin_class(plugin_class.name, "noop"))


def save(plugin_class):
    from funkwhale_api.common.models import PodPlugin

    return PodPlugin.objects.get_or_create(code=plugin_class.name)[0]


def trigger_hook(name, *args, **kwargs):
    handler = getattr(plugins_manager.hook, name)
    return handler(*args, **kwargs)


@register
class DefaultPlugin(Plugin):
Agate's avatar
Agate committed
    name = "default"
    verbose_name = "Default plugin"

    @plugin_hook
    def database_engine(self):
        return "django.db.backends.postgresql"
Agate's avatar
Agate committed

    @plugin_hook
    def urls(self):
        return []