summaryrefslogtreecommitdiff
path: root/kosten/app/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'kosten/app/utils.py')
-rw-r--r--kosten/app/utils.py117
1 files changed, 117 insertions, 0 deletions
diff --git a/kosten/app/utils.py b/kosten/app/utils.py
new file mode 100644
index 0000000..73f2b51
--- /dev/null
+++ b/kosten/app/utils.py
@@ -0,0 +1,117 @@
+from functools import wraps
+from flask import flash, request, render_template, url_for
+from flask import redirect as _redirect
+
+from .login import current_user
+
+import datetime
+today = datetime.date.today
+
+def _gen_tpl(endpoint):
+ return endpoint.replace('.', '/') + '.jinja'
+
+def templated(template=None):
+ """Marks a view as being rendered by a template. The view then shall
+ return a dictionary holding the parameters for the template. Ig this
+ is not the case, the response is returned unchanged. This is needed
+ to support `redirect` and similar.
+
+ The correct template is deducted as:
+ - when passed nothing: the name of the view
+ - when passed a string '.bla', the endpoint 'bla' in the current
+ blueprint
+ - when passed any other string: this string (VERBATIM!)
+
+ Except for the last case, the hierarchy of blueprint and view is taken
+ as directories in the template directory. And '.jinja' is appended.
+
+ If the first argument is a function, this is taken as 'None' to allow:
+ >>> @templated
+ ... def foo():
+ ... ...
+
+ (else it would have to be ``@templated()``).
+ """
+
+ fun = None
+ if template is not None and callable(template):
+ # a function was passed in
+ fun = template
+ template = None
+
+ def decorator(f):
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ if template is None:
+ template_name = _gen_tpl(request.endpoint)
+ elif template[0] == '.' and request.blueprint is not None:
+ template_name = _gen_tpl(request.blueprint + template)
+ else:
+ template_name = template
+
+ ctx = f(*args, **kwargs)
+ if ctx is None:
+ ctx = {}
+ elif not isinstance(ctx, dict):
+ return ctx
+ return render_template(template_name, **ctx)
+ return decorated_function
+
+ if fun is None:
+ return decorator
+ else:
+ return decorator(fun)
+
+def redirect (target, **kwargs):
+ """Convenience wrapper for `flask.redirect`. It applies `url_for`
+ on the target, which also gets passed all arguments.
+
+ Special argument '_code' to set the HTTP-Code.
+ """
+ code = kwargs.pop('_code', None)
+ url = url_for(target, **kwargs)
+
+ if code is None:
+ return _redirect(url)
+ else:
+ return _redirect(url, code)
+
+def assert_authorisation(constructor, param):
+ """Asserts that the current user has the right to load some specific data.
+
+ This is done by using the argument with keyword `param` and pass it
+ to `constructor`. If the resulting object has an attribute `user_id`,
+ this is checked to be equal to `current_user.id`.
+
+ Usage example::
+
+ @route('/job/<int:id>')
+ @assert_authorisation(Job, 'id')
+ def show_job(id):
+ # this is only executed if Job(id).user_id == current_user.id
+
+ """
+ def decorator(f):
+ @wraps(f)
+ def decorated_function(*args, **kwargs):
+ p = kwargs.get(param, None)
+
+ if p is None:
+ raise TypeError("Keyword %s expected but not received." % param)
+
+ obj = constructor(p)
+ if obj is None:
+ flash("Eintrag existiert nicht!", 'error')
+ return redirect('index')
+
+ if not hasattr(obj, 'user_id'):
+ return f(*args, **kwargs)
+
+ # explicitly use user_id to avoid having to load the user object
+ if obj.user_id != current_user.id:
+ flash("Nicht erlaubte Operation!", 'error')
+ return redirect('index')
+ else:
+ return f(*args, **kwargs)
+ return decorated_function
+ return decorator