funkwhale/api/funkwhale_api/plugins/__init__.py

182 lines
4.7 KiB
Python

import persisting_theory
import django.dispatch
from django import apps
import logging
from . import config
logger = logging.getLogger(__name__)
class Plugin(apps.AppConfig):
_is_funkwhale_plugin = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.hooks = HookRegistry()
self.settings = SettingRegistry()
self.user_settings = SettingRegistry()
def ready(self):
super().ready()
logging.info("Loading plugin %s", self.label)
self.load()
logging.info("Plugin %s loaded", self.label)
def load(self):
pass
class FuncRegistry(persisting_theory.Registry):
def connect(self, hook_name):
def inner(handler):
self[hook_name] = handler
return handler
return inner
class HookRegistry(FuncRegistry):
pass
class SettingRegistry(persisting_theory.Registry):
def prepare_name(self, data, name):
return data().identifier()
class PluginException(Exception):
pass
class PluginNotFound(PluginException):
pass
class Skip(PluginException):
pass
class PluginSignal(object):
def __init__(self, name, providing_args=[]):
self.name = name
self.providing_args = providing_args
class Hook(PluginSignal):
pass
class SignalsRegistry(persisting_theory.Registry):
def prepare_name(self, data, name):
return data.name
def dispatch(self, hook_name, plugins_conf, **kwargs):
"""
Call all handlers connected to hook_name in turn.
"""
if hook_name not in self:
raise LookupError(hook_name)
logger.debug("[Plugin:hook:%s] Dispatching hook", hook_name)
matching_hooks = []
for row in plugins_conf:
try:
matching_hooks.append((row, row["obj"].hooks[hook_name]))
except KeyError:
continue
if matching_hooks:
logger.debug(
"[Plugin:hook:%s] %s handlers found", hook_name, len(matching_hooks)
)
else:
logger.debug("[Plugin:hook:%s] No handler founds", hook_name)
return
for row, handler in matching_hooks:
logger.debug(
"[Plugin:hook:%s] Calling handler %s from plugin %s",
hook_name,
handler,
row["obj"].name,
)
try:
handler(plugin_conf=row, **kwargs)
except Skip:
logger.debug("[Plugin:hook:%s] handler skipped", hook_name)
except Exception:
logger.exception(
"[Plugin:hook:%s] unknown exception with handler %s",
hook_name,
handler,
)
else:
logger.debug("[Plugin:hook:%s] handler %s called successfully", handler)
logger.debug("[Plugin:hook:%s] Done", hook_name)
hooks = SignalsRegistry()
def get_plugin(name):
try:
plugin = apps.apps.get_app_config(name)
except LookupError:
raise PluginNotFound(name)
if not getattr(plugin, "_is_funkwhale_plugin", False):
raise PluginNotFound(name)
return plugin
def get_all_plugins():
return [
app
for app in apps.apps.get_app_configs()
if getattr(app, "_is_funkwhale_plugin", False)
]
def generate_plugins_conf(plugins, user=None):
from . import models
plugins_conf = []
qs = models.Plugin.objects.filter(is_enabled=True).values("name", "config")
by_plugin_name = {obj["name"]: obj["config"] for obj in qs}
for plugin in plugins:
if plugin.name not in by_plugin_name:
continue
conf = {
"obj": plugin,
"user": None,
"settings": by_plugin_name[plugin.name] or {},
}
plugins_conf.append(conf)
if plugins_conf and user and user.is_authenticated:
qs = models.UserPlugin.objects.filter(
user=user, plugin__is_enabled=True, is_enabled=True
).values("plugin__name", "config")
by_plugin_name = {obj["plugin__name"]: obj["config"] for obj in qs}
for row in plugins_conf:
if row["obj"].name in by_plugin_name:
row["user"] = {
"id": user.pk,
"settings": by_plugin_name[row["obj"].name],
}
return plugins_conf
def attach_plugins_conf(obj, user):
from funkwhale_api.common import preferences
plugins_enabled = preferences.get("plugins__enabled")
if plugins_enabled:
conf = generate_plugins_conf(plugins=get_all_plugins(), user=user)
else:
conf = None
setattr(obj, "plugins_conf", conf)