forked from Github-Mirrors/canaille

This allows blueprint to handle 404 errors. This is not supported directly in Flask. https://flask.palletsprojects.com/en/stable/errorhandling/#handling However, the blueprint cannot handle 404 routing errors because the 404 occurs at the routing level before the blueprint can be determined.
127 lines
3.7 KiB
Python
127 lines
3.7 KiB
Python
import logging
|
|
from functools import wraps
|
|
from urllib.parse import urlsplit
|
|
from urllib.parse import urlunsplit
|
|
|
|
from flask import abort
|
|
from flask import current_app
|
|
from flask import make_response
|
|
from flask import request
|
|
from werkzeug.exceptions import HTTPException
|
|
from werkzeug.routing import BaseConverter
|
|
|
|
from canaille.app.i18n import gettext as _
|
|
from canaille.app.session import current_user
|
|
from canaille.app.themes import render_template
|
|
|
|
|
|
def user_needed():
|
|
def wrapper(view_function):
|
|
@wraps(view_function)
|
|
def decorator(*args, **kwargs):
|
|
user = current_user()
|
|
if not user:
|
|
abort(403)
|
|
return view_function(*args, user=user, **kwargs)
|
|
|
|
return decorator
|
|
|
|
return wrapper
|
|
|
|
|
|
def permissions_needed(*args):
|
|
permissions = set(args)
|
|
|
|
def wrapper(view_function):
|
|
@wraps(view_function)
|
|
def decorator(*args, **kwargs):
|
|
user = current_user()
|
|
if not user or not user.can(*permissions):
|
|
abort(403)
|
|
return view_function(*args, user=user, **kwargs)
|
|
|
|
return decorator
|
|
|
|
return wrapper
|
|
|
|
|
|
def smtp_needed():
|
|
def wrapper(view_function):
|
|
@wraps(view_function)
|
|
def decorator(*args, **kwargs):
|
|
if current_app.features.has_smtp:
|
|
return view_function(*args, **kwargs)
|
|
|
|
message = _("No SMTP server has been configured")
|
|
logging.warning(message)
|
|
return (
|
|
render_template(
|
|
"error.html",
|
|
error_code=500,
|
|
icon="tools",
|
|
description=message,
|
|
),
|
|
500,
|
|
)
|
|
|
|
return decorator
|
|
|
|
return wrapper
|
|
|
|
|
|
def set_parameter_in_url_query(url, **kwargs):
|
|
split = list(urlsplit(url))
|
|
pairs = split[3].split("&")
|
|
parameters = {pair.split("=")[0]: pair.split("=")[1] for pair in pairs if pair}
|
|
parameters = {**parameters, **kwargs}
|
|
split[3] = "&".join(f"{key}={value}" for key, value in parameters.items())
|
|
return urlunsplit(split)
|
|
|
|
|
|
def request_is_htmx():
|
|
return request.headers.get("HX-Request", False) and not request.headers.get(
|
|
"HX-Boosted", False
|
|
)
|
|
|
|
|
|
def render_htmx_template(template, htmx_template=None, **kwargs):
|
|
template = (
|
|
(htmx_template or f"partial/{template}") if request_is_htmx() else template
|
|
)
|
|
return render_template(template, **kwargs)
|
|
|
|
|
|
def model_converter(model):
|
|
class ModelConverter(BaseConverter):
|
|
def __init__(self, *args, required=True, **kwargs):
|
|
self.required = required
|
|
super().__init__(self, *args, **kwargs)
|
|
|
|
def to_url(self, instance):
|
|
return instance.identifier or instance.id
|
|
|
|
def to_python(self, identifier):
|
|
current_app.backend.setup()
|
|
instance = current_app.backend.get(model, identifier)
|
|
if self.required and not instance:
|
|
abort(404)
|
|
|
|
return instance
|
|
|
|
return ModelConverter
|
|
|
|
|
|
def redirect_to_bp_handlers(app, error):
|
|
"""Find and execute blueprint handlers matching an error.
|
|
|
|
There is currently no way to make 404 handling generic:
|
|
https://flask.palletsprojects.com/en/stable/errorhandling/#handling
|
|
However, the blueprint cannot handle 404 routing errors because the
|
|
404 occurs at the routing level before the blueprint can be determined.
|
|
"""
|
|
for bp in app.blueprints.values():
|
|
if bp.url_prefix and request.path.startswith(bp.url_prefix):
|
|
for type_, handler in bp.error_handler_spec[None][None].items():
|
|
if type_ in (error.code, HTTPException): # pragma: no branch
|
|
return make_response(handler(error))
|
|
return None
|