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/') @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