Compare commits
2 Commits
3f9f2fe9ac
...
cd4631d0e6
Author | SHA1 | Date | |
---|---|---|---|
cd4631d0e6 | |||
cb0b7a3a7c |
@ -2,11 +2,29 @@
|
||||
import functools
|
||||
|
||||
# third-party
|
||||
import werkzeug.routing.map
|
||||
import flask
|
||||
|
||||
# internals
|
||||
import util
|
||||
|
||||
class URLMap(werkzeug.routing.map.Map):
|
||||
|
||||
def remove_endpoint(self, endpoint):
|
||||
|
||||
rules = []
|
||||
for rule in self._rules:
|
||||
if rule.endpoint == endpoint:
|
||||
rules.append(rule)
|
||||
|
||||
for rule in rules:
|
||||
self._rules.remove(rule)
|
||||
|
||||
del(self._rules_by_endpoint[endpoint])
|
||||
|
||||
self._remap = True
|
||||
self.update()
|
||||
|
||||
class MenuMixin:
|
||||
|
||||
def menu(self, name):
|
||||
@ -53,6 +71,8 @@ class Blueprint(flask.Blueprint, MenuMixin, BlockMixin):
|
||||
|
||||
class Application(flask.Flask, MenuMixin, BlockMixin):
|
||||
|
||||
url_map_class = URLMap
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -10,7 +10,8 @@ from application import app
|
||||
import util
|
||||
import rendering
|
||||
|
||||
app.db = playhouse.db_url.connect(app.config['DATABASE'])
|
||||
# NOTE: autorollback is deprecated in most current release of peewee, but development is done with 3.15.0
|
||||
app.db = playhouse.db_url.connect(app.config['DATABASE'], autorollback=True)
|
||||
|
||||
@app.before_request
|
||||
def db_connect():
|
||||
|
71
main.py
71
main.py
@ -3,6 +3,7 @@
|
||||
# builtins
|
||||
import math
|
||||
import datetime
|
||||
import functools
|
||||
|
||||
# third-party
|
||||
import markupsafe
|
||||
@ -580,6 +581,76 @@ class CuratedArt(commenting.Commentable):
|
||||
text = markdown.MarkdownField(null=False, verbose_name='Text')
|
||||
#link = peewee.ForeignKeyField(ScoredLink, null=True)
|
||||
|
||||
class PermaRedirect(admin.Administerable):
|
||||
|
||||
# WARNING: This entire class is hacky and might break with virtually any flask or werkzeug update
|
||||
|
||||
__cache__ = {} # collect simplified instances in memory, keyed by id
|
||||
match_path = peewee.CharField(null=False, unique=True, verbose_name='Match path', help_text='The path to match (i.e. as it was in URL on the old site')
|
||||
redirect_path = peewee.CharField(null=False, verbose_name='Redirect path', help_text='The path to redirect to')
|
||||
|
||||
@classmethod
|
||||
def view(cls, mode='full', **kwargs):
|
||||
|
||||
if mode != 'full':
|
||||
return super().view(mode=mode, **kwargs)
|
||||
|
||||
info = cls.__cache__[kwargs['id']]
|
||||
return flask.redirect(info['redirect_path'])
|
||||
|
||||
@classmethod
|
||||
def boot(cls):
|
||||
|
||||
try:
|
||||
|
||||
for instance in cls.select():
|
||||
cls.__cache__[instance.id] = {'match_path': instance.match_path, 'redirect_path': instance.redirect_path}
|
||||
|
||||
cls.update_routes()
|
||||
|
||||
except Exception as e:
|
||||
app.logger.warning('Could not initialize PermaRedirect cache.')
|
||||
|
||||
@classmethod
|
||||
def update_routes(cls):
|
||||
|
||||
for id, item in cls.__cache__.items():
|
||||
|
||||
# this is a simplified version of add_url_rule's logic
|
||||
endpoint = f'permaredirect-{id}'
|
||||
|
||||
if endpoint in app.url_map._rules_by_endpoint:
|
||||
app.url_map.remove_endpoint(endpoint)
|
||||
|
||||
#app.add_url_rule(item['match_path'], endpoint, functools.partial(cls.view, mode='full', id=id))
|
||||
rule = app.url_rule_class(item['match_path'], methods=('GET', 'OPTIONS'), endpoint=endpoint)
|
||||
rule.provide_automatic_options = True
|
||||
|
||||
app.url_map.add(rule)
|
||||
app.view_functions[endpoint] = functools.partial(cls.view, mode='full', id=id)
|
||||
|
||||
def save(self, **kwargs):
|
||||
|
||||
# NOTE/TODO: This is an ugly hack (but so is the rest of this class).
|
||||
# Should™ (also) be checked/handled via form validation
|
||||
for path in (self.match_path, self.redirect_path):
|
||||
if '<' in path or '>' in path:
|
||||
raise ValueError(f"PermaRedirect paths MUST NOT contain '<' or '>' but got: {path}")
|
||||
|
||||
super().save(**kwargs)
|
||||
|
||||
self.__class__.__cache__[self.id] = {'match_path': self.match_path, 'redirect_path': self.redirect_path}
|
||||
self.__class__.update_routes()
|
||||
|
||||
def delete_instance(self, **kwargs):
|
||||
|
||||
del(self.__class__.__cache__[self.id])
|
||||
self.__class__.update_routes()
|
||||
|
||||
super().delete_instance(**kwargs)
|
||||
|
||||
app.boot(PermaRedirect.boot)
|
||||
|
||||
app.boot_run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
10
themes/default/templates/renderable/permaredirect.html
Normal file
10
themes/default/templates/renderable/permaredirect.html
Normal file
@ -0,0 +1,10 @@
|
||||
{% extends 'default/templates/renderable/administerable.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<span>Match path:</span> <span class="match-path path">{{ content.match_path }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>Redirect path:</span> <span class="redirect-path path">{{ content.redirect_path }}</span>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user