feat(hooks): Some more work on the hooks functions
This commit is contained in:
parent
cfac55efe0
commit
38ebaf0e79
|
@ -12,6 +12,7 @@ from . import logger
|
|||
from .plugins import AuthPlugin
|
||||
from flaschengeist.config import config, configure_app
|
||||
from flaschengeist.controller import roleController
|
||||
from flaschengeist.utils.hook import Hook
|
||||
|
||||
|
||||
class CustomJSONEncoder(JSONEncoder):
|
||||
|
@ -35,6 +36,7 @@ class CustomJSONEncoder(JSONEncoder):
|
|||
return JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
@Hook("plugins.loaded")
|
||||
def __load_plugins(app):
|
||||
logger.debug("Search for plugins")
|
||||
|
||||
|
@ -77,23 +79,20 @@ def __load_plugins(app):
|
|||
raise RuntimeError("No authentication plugin configured or authentication plugin not found")
|
||||
|
||||
|
||||
@Hook("plugins.installed")
|
||||
def install_all():
|
||||
from flaschengeist.database import db
|
||||
|
||||
db.create_all()
|
||||
db.session.commit()
|
||||
installed = []
|
||||
for name, plugin in current_app.config["FG_PLUGINS"].items():
|
||||
if not plugin:
|
||||
logger.debug(f"Skip disabled plugin: {name}")
|
||||
continue
|
||||
logger.info(f"Install plugin {name}")
|
||||
plugin.install()
|
||||
installed.append(plugin)
|
||||
if plugin.permissions:
|
||||
roleController.create_permissions(plugin.permissions)
|
||||
for plugin in installed:
|
||||
plugin.post_install()
|
||||
|
||||
|
||||
def create_app(test_config=None):
|
||||
|
|
|
@ -10,6 +10,21 @@ from flaschengeist.models.user import _Avatar, User
|
|||
from flaschengeist.models.setting import _PluginSetting
|
||||
from flaschengeist.utils.hook import HookBefore, HookAfter
|
||||
|
||||
plugins_installed = HookAfter("plugins.installed")
|
||||
"""Hook decorator for when all plugins are installed
|
||||
Possible use case would be to populate the database with some presets.
|
||||
|
||||
Args:
|
||||
hook_result: void (kwargs)
|
||||
"""
|
||||
plugins_loaded = HookAfter("plugins.loaded")
|
||||
"""Hook decorator for when all plugins are loaded
|
||||
Possible use case would be to check if a specific other plugin is loaded and change own behavior
|
||||
|
||||
Args:
|
||||
app: Current flask app instance (args)
|
||||
hook_result: void (kwargs)
|
||||
"""
|
||||
before_role_updated = HookBefore("update_role")
|
||||
"""Hook decorator for when roles are modified
|
||||
Args:
|
||||
|
@ -57,12 +72,6 @@ class Plugin:
|
|||
"""
|
||||
pass
|
||||
|
||||
def post_install(self):
|
||||
"""Fill database or do other stuff
|
||||
Called after all plugins are installed
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_setting(self, name: str, **kwargs):
|
||||
"""Get plugin setting from database
|
||||
Args:
|
||||
|
|
|
@ -7,13 +7,16 @@ import os
|
|||
import hashlib
|
||||
import binascii
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from flaschengeist.plugins import AuthPlugin
|
||||
from flaschengeist.plugins import AuthPlugin, plugins_installed
|
||||
from flaschengeist.models.user import User, Role, Permission
|
||||
from flaschengeist.database import db
|
||||
from flaschengeist import logger
|
||||
|
||||
|
||||
class AuthPlain(AuthPlugin):
|
||||
def install(self):
|
||||
plugins_installed(self.post_install)
|
||||
|
||||
def post_install(self):
|
||||
if User.query.filter(User.deleted == False).count() == 0:
|
||||
logger.info("Installing admin user")
|
||||
|
|
|
@ -1,43 +1,69 @@
|
|||
_hook_dict = ({}, {})
|
||||
from functools import wraps
|
||||
|
||||
|
||||
class Hook(object):
|
||||
"""Decorator for Hooks
|
||||
Use to decorate system hooks where plugins should be able to hook in
|
||||
_hooks_before = {}
|
||||
_hooks_after = {}
|
||||
|
||||
|
||||
def Hook(function=None, id=None):
|
||||
"""Hook decorator
|
||||
Use to decorate functions as hooks, so plugins can hook up their custom functions.
|
||||
"""
|
||||
# `id` passed as `arg` not `kwarg`
|
||||
if isinstance(function, str):
|
||||
return Hook(id=function)
|
||||
|
||||
def decorator(function):
|
||||
@wraps(function)
|
||||
def wrapped(*args, **kwargs):
|
||||
_id = id if id is not None else function.__qualname__
|
||||
# Hooks before
|
||||
for f in _hooks_before.get(_id, []):
|
||||
f(*args, **kwargs)
|
||||
# Main function
|
||||
result = function(*args, **kwargs)
|
||||
# Hooks after
|
||||
for f in _hooks_after.get(_id, []):
|
||||
f(*args, hook_result=result, **kwargs)
|
||||
return result
|
||||
|
||||
return wrapped
|
||||
|
||||
# Called @Hook or we are in the second step
|
||||
if callable(function):
|
||||
return decorator(function)
|
||||
else:
|
||||
return decorator
|
||||
|
||||
|
||||
def HookBefore(id: str):
|
||||
"""Decorator for functions to be called before a Hook-Function is called
|
||||
The hooked up function must accept the same arguments as the function hooked onto,
|
||||
as the functions are called with the same arguments.
|
||||
Hint: This enables you to modify the arguments!
|
||||
"""
|
||||
if not id or not isinstance(id, str):
|
||||
raise TypeError("HookBefore requires the ID of the function to hook up")
|
||||
|
||||
def wrapped(function):
|
||||
_hooks_before.setdefault(id, []).append(function)
|
||||
return function
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def HookAfter(id: str):
|
||||
"""Decorator for functions to be called after a Hook-Function is called
|
||||
As with the HookBefore, the hooked up function must accept the same
|
||||
arguments as the function hooked onto, but also receives a
|
||||
`hook_result` kwarg containing the result of the function.
|
||||
"""
|
||||
|
||||
def __init__(self, function):
|
||||
self.function = function
|
||||
if not id or not isinstance(id, str):
|
||||
raise TypeError("HookAfter requires the ID of the function to hook up")
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
# Hooks before
|
||||
for function in _hook_dict[0].get(self.function.__name__, []):
|
||||
function(*args, **kwargs)
|
||||
# Main function
|
||||
ret = self.function(*args, **kwargs)
|
||||
# Hooks after
|
||||
for function in _hook_dict[1].get(self.function.__name__, []):
|
||||
function(*args, **kwargs)
|
||||
return ret
|
||||
|
||||
|
||||
class HookBefore(object):
|
||||
"""Decorator for functions to be called before a Hook-Function is called"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __call__(self, function):
|
||||
_hook_dict[0].setdefault(self.name, []).append(function)
|
||||
def wrapped(function):
|
||||
_hooks_after.setdefault(id, []).append(function)
|
||||
return function
|
||||
|
||||
|
||||
class HookAfter(object):
|
||||
"""Decorator for functions to be called after a Hook-Function is called"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __call__(self, function):
|
||||
_hook_dict[1].setdefault(self.name, []).append(function)
|
||||
return function
|
||||
return wrapped
|
||||
|
|
Loading…
Reference in New Issue