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 .plugins import AuthPlugin
|
||||||
from flaschengeist.config import config, configure_app
|
from flaschengeist.config import config, configure_app
|
||||||
from flaschengeist.controller import roleController
|
from flaschengeist.controller import roleController
|
||||||
|
from flaschengeist.utils.hook import Hook
|
||||||
|
|
||||||
|
|
||||||
class CustomJSONEncoder(JSONEncoder):
|
class CustomJSONEncoder(JSONEncoder):
|
||||||
|
@ -35,6 +36,7 @@ class CustomJSONEncoder(JSONEncoder):
|
||||||
return JSONEncoder.default(self, o)
|
return JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
|
||||||
|
@Hook("plugins.loaded")
|
||||||
def __load_plugins(app):
|
def __load_plugins(app):
|
||||||
logger.debug("Search for plugins")
|
logger.debug("Search for plugins")
|
||||||
|
|
||||||
|
@ -77,23 +79,20 @@ def __load_plugins(app):
|
||||||
raise RuntimeError("No authentication plugin configured or authentication plugin not found")
|
raise RuntimeError("No authentication plugin configured or authentication plugin not found")
|
||||||
|
|
||||||
|
|
||||||
|
@Hook("plugins.installed")
|
||||||
def install_all():
|
def install_all():
|
||||||
from flaschengeist.database import db
|
from flaschengeist.database import db
|
||||||
|
|
||||||
db.create_all()
|
db.create_all()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
installed = []
|
|
||||||
for name, plugin in current_app.config["FG_PLUGINS"].items():
|
for name, plugin in current_app.config["FG_PLUGINS"].items():
|
||||||
if not plugin:
|
if not plugin:
|
||||||
logger.debug(f"Skip disabled plugin: {name}")
|
logger.debug(f"Skip disabled plugin: {name}")
|
||||||
continue
|
continue
|
||||||
logger.info(f"Install plugin {name}")
|
logger.info(f"Install plugin {name}")
|
||||||
plugin.install()
|
plugin.install()
|
||||||
installed.append(plugin)
|
|
||||||
if plugin.permissions:
|
if plugin.permissions:
|
||||||
roleController.create_permissions(plugin.permissions)
|
roleController.create_permissions(plugin.permissions)
|
||||||
for plugin in installed:
|
|
||||||
plugin.post_install()
|
|
||||||
|
|
||||||
|
|
||||||
def create_app(test_config=None):
|
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.models.setting import _PluginSetting
|
||||||
from flaschengeist.utils.hook import HookBefore, HookAfter
|
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")
|
before_role_updated = HookBefore("update_role")
|
||||||
"""Hook decorator for when roles are modified
|
"""Hook decorator for when roles are modified
|
||||||
Args:
|
Args:
|
||||||
|
@ -57,12 +72,6 @@ class Plugin:
|
||||||
"""
|
"""
|
||||||
pass
|
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):
|
def get_setting(self, name: str, **kwargs):
|
||||||
"""Get plugin setting from database
|
"""Get plugin setting from database
|
||||||
Args:
|
Args:
|
||||||
|
|
|
@ -7,13 +7,16 @@ import os
|
||||||
import hashlib
|
import hashlib
|
||||||
import binascii
|
import binascii
|
||||||
from werkzeug.exceptions import BadRequest
|
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.models.user import User, Role, Permission
|
||||||
from flaschengeist.database import db
|
from flaschengeist.database import db
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
|
|
||||||
|
|
||||||
class AuthPlain(AuthPlugin):
|
class AuthPlain(AuthPlugin):
|
||||||
|
def install(self):
|
||||||
|
plugins_installed(self.post_install)
|
||||||
|
|
||||||
def post_install(self):
|
def post_install(self):
|
||||||
if User.query.filter(User.deleted == False).count() == 0:
|
if User.query.filter(User.deleted == False).count() == 0:
|
||||||
logger.info("Installing admin user")
|
logger.info("Installing admin user")
|
||||||
|
|
|
@ -1,43 +1,69 @@
|
||||||
_hook_dict = ({}, {})
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
class Hook(object):
|
_hooks_before = {}
|
||||||
"""Decorator for Hooks
|
_hooks_after = {}
|
||||||
Use to decorate system hooks where plugins should be able to hook in
|
|
||||||
|
|
||||||
|
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):
|
if not id or not isinstance(id, str):
|
||||||
self.function = function
|
raise TypeError("HookAfter requires the ID of the function to hook up")
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def wrapped(function):
|
||||||
# Hooks before
|
_hooks_after.setdefault(id, []).append(function)
|
||||||
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)
|
|
||||||
return function
|
return function
|
||||||
|
|
||||||
|
return wrapped
|
||||||
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
|
|
||||||
|
|
Loading…
Reference in New Issue