182 lines
4.7 KiB
Python
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)
|