Compare commits

..

4 Commits

5 changed files with 105 additions and 43 deletions

View File

@ -17,5 +17,3 @@ matrix:
PYTHON: PYTHON:
- 3.10 - 3.10
- 3.9 - 3.9
- 3.8
- 3.7

View File

@ -7,20 +7,29 @@ This is the backend of the Flaschengeist.
### Requirements ### Requirements
- `mysql` or `mariadb` - `mysql` or `mariadb`
- maybe `libmariadb` development files[1] - maybe `libmariadb` development files[1]
- python 3.7+ - python 3.9+
- pip 21.0+
[1] By default Flaschengeist uses mysql as database backend, if you are on Windows Flaschengeist uses `PyMySQL`, but on *[1] By default Flaschengeist uses mysql as database backend, if you are on Windows Flaschengeist uses `PyMySQL`, but on
Linux / Mac the faster `mysqlclient` is used, if it is not already installed installing from pypi requires the Linux / Mac the faster `mysqlclient` is used, if it is not already installed installing from pypi requires the
development files for `libmariadb` to be present on your system. development files for `libmariadb` to be present on your system.*
### Install python files ### Install python files
pip3 install --user . It is recommended to upgrade pip to the latest version before installing:
python -m pip install --upgrade pip
Default installation with *mariadb*/*mysql* support:
pip3 install --user ".[mysql]"
or with ldap support or with ldap support
pip3 install --user ".[ldap]" pip3 install --user ".[ldap]"
or if you want to also run the tests: or if you want to also run the tests:
pip3 install --user ".[ldap,test]" pip3 install --user ".[ldap,tests]"
You will also need a MySQL driver, recommended drivers are You will also need a MySQL driver, recommended drivers are
- `mysqlclient` - `mysqlclient`

View File

@ -0,0 +1,76 @@
"""Controller for Plugin logic
Used by plugins for setting and notification functionality.
"""
import sqlalchemy
from ..database import db
from ..models.setting import _PluginSetting
from ..models.notification import Notification
def get_setting(plugin_id: str, name: str, **kwargs):
"""Get plugin setting from database
Args:
plugin_id: ID of the plugin
name: string identifying the setting
default: Default value
Returns:
Value stored in database (native python)
Raises:
`KeyError` if no such setting exists in the database
"""
try:
setting = (
_PluginSetting.query.filter(_PluginSetting.plugin == plugin_id).filter(_PluginSetting.name == name).one()
)
return setting.value
except sqlalchemy.orm.exc.NoResultFound:
if "default" in kwargs:
return kwargs["default"]
else:
raise KeyError
def set_setting(plugin_id: str, name: str, value):
"""Save setting in database
Args:
plugin_id: ID of the plugin
name: String identifying the setting
value: Value to be stored
"""
setting = (
_PluginSetting.query.filter(_PluginSetting.plugin == plugin_id)
.filter(_PluginSetting.name == name)
.one_or_none()
)
if setting is not None:
if value is None:
db.session.delete(setting)
else:
setting.value = value
else:
db.session.add(_PluginSetting(plugin=plugin_id, name=name, value=value))
db.session.commit()
def notify(plugin_id: str, user, text: str, data=None):
"""Create a new notification for an user
Args:
plugin_id: ID of the plugin
user: `flaschengeist.models.user.User` to notify
text: Visibile notification text
data: Optional data passed to the notificaton
Returns:
ID of the created `flaschengeist.models.notification.Notification`
Hint: use the data for frontend actions.
"""
if not user.deleted:
n = Notification(text=text, data=data, plugin=plugin_id, user_=user)
db.session.add(n)
db.session.commit()
return n.id

View File

@ -1,13 +1,9 @@
from dataclasses import asdict, dataclass, field
from flask import Blueprint from flask import Blueprint
from dataclasses import asdict, dataclass, field
from werkzeug.datastructures import FileStorage from werkzeug.datastructures import FileStorage
from werkzeug.exceptions import MethodNotAllowed, NotFound from werkzeug.exceptions import MethodNotAllowed, NotFound
from flaschengeist.database import db
from flaschengeist.controller import imageController
from flaschengeist.models.notification import Notification
from flaschengeist.models.user import _Avatar, User from flaschengeist.models.user import _Avatar, User
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") plugins_installed = HookAfter("plugins.installed")
@ -133,18 +129,9 @@ class Plugin(PluginMetadata):
Raises: Raises:
`KeyError` if no such setting exists in the database `KeyError` if no such setting exists in the database
""" """
try: from ..controller import pluginController
setting = (
_PluginSetting.query.filter(_PluginSetting.plugin == self.name) return pluginController.get_setting(self.id)
.filter(_PluginSetting.name == name)
.one()
)
return setting.value
except sqlalchemy.orm.exc.NoResultFound:
if "default" in kwargs:
return kwargs["default"]
else:
raise KeyError
def set_setting(self, name: str, value): def set_setting(self, name: str, value):
"""Save setting in database """Save setting in database
@ -153,19 +140,9 @@ class Plugin(PluginMetadata):
name: String identifying the setting name: String identifying the setting
value: Value to be stored value: Value to be stored
""" """
setting = ( from ..controller import pluginController
_PluginSetting.query.filter(_PluginSetting.plugin == self.name)
.filter(_PluginSetting.name == name) return pluginController.set_setting(self.id, name, value)
.one_or_none()
)
if setting is not None:
if value is None:
db.session.delete(setting)
else:
setting.value = value
else:
db.session.add(_PluginSetting(plugin=self.name, name=name, value=value))
db.session.commit()
def notify(self, user, text: str, data=None): def notify(self, user, text: str, data=None):
"""Create a new notification for an user """Create a new notification for an user
@ -179,11 +156,9 @@ class Plugin(PluginMetadata):
Hint: use the data for frontend actions. Hint: use the data for frontend actions.
""" """
if not user.deleted: from ..controller import pluginController
n = Notification(text=text, data=data, plugin=self.id, user_=user)
db.session.add(n) return pluginController.notify(self.id, user, text, data)
db.session.commit()
return n.id
def serialize(self): def serialize(self):
"""Serialize a plugin into a dict """Serialize a plugin into a dict
@ -283,6 +258,10 @@ class AuthPlugin(Plugin):
MethodNotAllowed: If not supported by Backend MethodNotAllowed: If not supported by Backend
Any valid HTTP exception Any valid HTTP exception
""" """
# By default save the image to the avatar,
# deleting would happen by unsetting it
from ..controller import imageController
user.avatar_ = imageController.upload_image(file) user.avatar_ = imageController.upload_image(file)
def delete_avatar(self, user: User): def delete_avatar(self, user: User):

View File

@ -19,7 +19,7 @@ classifiers =
[options] [options]
include_package_data = True include_package_data = True
python_requires = >=3.7 python_requires = >=3.9
packages = find: packages = find:
install_requires = install_requires =
Flask >= 2.0 Flask >= 2.0