Login system (not fully complete yet)
This commit is contained in:
parent
ec7a433864
commit
5989f0d97e
233
messages.pot
233
messages.pot
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Starfall 0.1.0-alpha.3\n"
|
||||
"Report-Msgid-Bugs-To: flare@theflare.at\n"
|
||||
"POT-Creation-Date: 2026-03-10 10:46+0100\n"
|
||||
"POT-Creation-Date: 2026-03-18 10:46+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -17,6 +17,59 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.18.0\n"
|
||||
|
||||
#: starfall/web/controllers/secure/login.py:12
|
||||
#: starfall/web/controllers/secure/login.py:16
|
||||
msgid "page.login.form.username"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/login.py:19
|
||||
msgid "page.login.error.username"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/login.py:23
|
||||
#: starfall/web/controllers/secure/login.py:27
|
||||
msgid "page.login.form.password"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/login.py:30
|
||||
msgid "page.login.error.password"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:15
|
||||
#: starfall/web/controllers/secure/register.py:20
|
||||
msgid "page.register.form.username"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:23
|
||||
msgid "page.register.error.username"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:27
|
||||
#: starfall/web/controllers/secure/register.py:31
|
||||
msgid "page.register.form.password"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:34
|
||||
msgid "page.register.error.password"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:38
|
||||
#: starfall/web/controllers/secure/register.py:42
|
||||
msgid "page.register.form.email"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:45
|
||||
msgid "page.register.error.email"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:71
|
||||
msgid "page.register.status.email_already_exists"
|
||||
msgstr ""
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:72
|
||||
msgid "page.register.status.success"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/base.jinja:6
|
||||
msgid "base.meta.title.empty"
|
||||
msgstr ""
|
||||
|
|
@ -41,96 +94,36 @@ msgstr ""
|
|||
msgid "base.label.toggle_nav"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/base.jinja:47
|
||||
#: web/templates/base.jinja:46
|
||||
msgid "base.label.login"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/base.jinja:53
|
||||
msgid "base.label.theme.btn"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/base.jinja:52
|
||||
#: web/templates/base.jinja:58
|
||||
msgid "base.label.theme.light"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/base.jinja:55
|
||||
#: web/templates/base.jinja:61
|
||||
msgid "base.label.theme.dark"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/base.jinja:58
|
||||
#: web/templates/base.jinja:64
|
||||
msgid "base.label.theme.auto"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/base.jinja:76
|
||||
msgid "page.empty.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/base.jinja:77
|
||||
msgid "page.empty.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/base.jinja:89
|
||||
#: web/templates/base.jinja:90
|
||||
msgid "base.label.copy"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/home.jinja:3
|
||||
msgid "page.home.title"
|
||||
#: web/templates/components/content_empty.jinja:3
|
||||
msgid "page.empty.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/home.jinja:8
|
||||
msgid "page.home.card.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/home.jinja:9
|
||||
msgid "page.home.card.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:3 web/templates/imprint.jinja:8
|
||||
msgid "page.imprint.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:10
|
||||
msgid "page.imprint.card.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:12
|
||||
msgid "page.imprint.card.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:13
|
||||
msgid "page.imprint.card.lastmodified"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:23
|
||||
msgid "page.imprint.legal"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:31
|
||||
msgid "page.imprint.sec1.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:32
|
||||
msgid "page.imprint.sec2.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:33
|
||||
msgid "page.imprint.sec3.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:34
|
||||
msgid "page.imprint.sec4.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:35
|
||||
msgid "page.imprint.sec1.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:36
|
||||
msgid "page.imprint.sec2.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:37
|
||||
msgid "page.imprint.sec3.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/imprint.jinja:38
|
||||
msgid "page.imprint.sec4.body"
|
||||
#: web/templates/components/content_empty.jinja:6
|
||||
msgid "page.empty.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/errors/not_found.jinja:8
|
||||
|
|
@ -141,3 +134,95 @@ msgstr ""
|
|||
msgid "error.404.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/home.jinja:3
|
||||
msgid "page.home.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/home.jinja:8
|
||||
msgid "page.home.card.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/home.jinja:11
|
||||
msgid "page.home.card.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:3 web/templates/public/imprint.jinja:8
|
||||
msgid "page.imprint.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:11
|
||||
msgid "page.imprint.card.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:13
|
||||
msgid "page.imprint.card.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:14
|
||||
msgid "page.imprint.card.lastmodified"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:24
|
||||
msgid "page.imprint.legal"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:32
|
||||
msgid "page.imprint.sec1.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:33
|
||||
msgid "page.imprint.sec2.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:34
|
||||
msgid "page.imprint.sec3.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:35
|
||||
msgid "page.imprint.sec4.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:36
|
||||
msgid "page.imprint.sec1.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:37
|
||||
msgid "page.imprint.sec2.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:38
|
||||
msgid "page.imprint.sec3.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/public/imprint.jinja:39
|
||||
msgid "page.imprint.sec4.body"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/secure/login.jinja:3 web/templates/secure/login.jinja:8
|
||||
msgid "page.login.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/secure/login.jinja:20
|
||||
msgid "page.login.form.submit"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/secure/login.jinja:24
|
||||
msgid "page.login.label.register"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/secure/login.jinja:26
|
||||
msgid "page.login.label.lostpassword"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/secure/register.jinja:5 web/templates/secure/register.jinja:10
|
||||
msgid "page.register.title"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/secure/register.jinja:20
|
||||
msgid "page.register.form.submit"
|
||||
msgstr ""
|
||||
|
||||
#: web/templates/secure/register.jinja:24
|
||||
msgid "page.register.label.login"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ discord-py-interactions
|
|||
flask
|
||||
flask_assets
|
||||
flask-babel
|
||||
flask-login
|
||||
flask-sqlalchemy
|
||||
flask-wtf
|
||||
libsass
|
||||
livereload
|
||||
rich
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@ from sqlalchemy.orm import Mapped, mapped_column
|
|||
from starfall.db import db
|
||||
|
||||
|
||||
class Users(db.Table):
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
class User(db.Model):
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
email: Mapped[str] = mapped_column(unique=True)
|
||||
username: Mapped[str] = mapped_column()
|
||||
password: Mapped[str] = mapped_column()
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ from typing import final
|
|||
from flask import Blueprint, Flask, request
|
||||
from flask_assets import Bundle, Environment
|
||||
from flask_babel import Babel
|
||||
from flask_wtf import CSRFProtect
|
||||
from livereload import Server
|
||||
|
||||
from starfall.config import Config
|
||||
from starfall.db import db, load_schema
|
||||
from starfall.types import SnapshotQueue
|
||||
from starfall.web.blueprints.base import BaseBlueprint
|
||||
from starfall.web.controllers.user import login_manager
|
||||
|
||||
|
||||
@final
|
||||
|
|
@ -26,6 +28,7 @@ class WebUI:
|
|||
self.config: Config | None = None
|
||||
self.queue: SnapshotQueue | None = None
|
||||
self.server: Server | None = None
|
||||
self.csrf: CSRFProtect | None = None
|
||||
|
||||
def select_locale(self):
|
||||
# user = getattr(g, "user", None)
|
||||
|
|
@ -58,12 +61,21 @@ class WebUI:
|
|||
TEMPLATES_AUTO_RELOAD=True,
|
||||
)
|
||||
self.app.jinja_env.auto_reload = True
|
||||
logging.getLogger("web").debug("flask initialized")
|
||||
|
||||
db.init_app(self.app)
|
||||
load_schema()
|
||||
|
||||
with self.app.app_context():
|
||||
db.create_all()
|
||||
logging.getLogger("web").debug("db initialized")
|
||||
|
||||
self.babel = Babel(
|
||||
self.app,
|
||||
locale_selector=self.select_locale,
|
||||
timezone_selector=self.select_timezone,
|
||||
)
|
||||
logging.getLogger("web").debug("babel initialized")
|
||||
|
||||
self.assets = Environment(self.app)
|
||||
self.assets.load_path = ["web/static/scss", "web/static/css"]
|
||||
|
|
@ -79,37 +91,43 @@ class WebUI:
|
|||
depends=["**/*.scss"],
|
||||
)
|
||||
_ = self.assets.register("scss", scss)
|
||||
logging.getLogger("web").debug("assets initialized")
|
||||
|
||||
db.init_app(self.app)
|
||||
load_schema()
|
||||
self.csrf = CSRFProtect(self.app)
|
||||
logging.getLogger("web").debug("csrf connected")
|
||||
|
||||
with self.app.app_context():
|
||||
db.create_all()
|
||||
login_manager.init_app(self.app)
|
||||
|
||||
self.import_blueprints("secure", "secure")
|
||||
self.import_blueprints("public", "public")
|
||||
self.import_blueprints()
|
||||
self.app.register_blueprint(
|
||||
self.blueprint,
|
||||
options={"queue": self.queue},
|
||||
)
|
||||
logging.getLogger("web").debug("blueprints initialized")
|
||||
|
||||
self.server = Server(self.app.wsgi_app)
|
||||
self.server.watch(
|
||||
filepath=os.path.join(str(self.app.template_folder), "**/*.jinja"),
|
||||
)
|
||||
self.server.watch(
|
||||
filepath=os.path.join(str(self.app.static_folder), "**/*.js"),
|
||||
)
|
||||
self.server.watch(
|
||||
filepath=os.path.join(str(self.app.static_folder), "**/*.scss"),
|
||||
)
|
||||
self.server.watch(os.path.join(str(self.app.template_folder), "**/*.jinja"))
|
||||
self.server.watch(os.path.join(str(self.app.static_folder), "**/*.js"))
|
||||
self.server.watch(os.path.join(str(self.app.static_folder), "**/*.scss"))
|
||||
logging.getLogger("web").debug("livereload initialized")
|
||||
|
||||
self.server.serve(
|
||||
host=self.config.get("web.host"),
|
||||
port=self.config.get("web.port"),
|
||||
)
|
||||
|
||||
def import_blueprints(self):
|
||||
def import_blueprints(self, path_suffix: str = "", module_suffix: str = ""):
|
||||
path = os.path.realpath(os.path.dirname(__file__) + os.sep + "blueprints")
|
||||
for _, module_name, _ in iter_modules([path], f"{__name__}.blueprints."):
|
||||
if len(path_suffix):
|
||||
path += os.sep + path_suffix
|
||||
|
||||
prefix = f"{__name__}.blueprints."
|
||||
if len(module_suffix):
|
||||
prefix += module_suffix + "."
|
||||
|
||||
for _, module_name, _ in iter_modules([path], prefix):
|
||||
if module_name.endswith("base"):
|
||||
continue
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
from flask import Blueprint, Flask, render_template
|
||||
from flask_assets import Environment
|
||||
|
||||
from starfall.web.blueprints.base import BaseBlueprint
|
||||
|
||||
|
||||
class AdminBlueprint(BaseBlueprint):
|
||||
def __init__(self, blueprint: Blueprint, assets: Environment, app: Flask):
|
||||
super().__init__(blueprint, assets, app)
|
||||
blueprint.add_url_rule("/login", methods=["GET", "POST"], view_func=self.login)
|
||||
|
||||
def login(self):
|
||||
return render_template("admin/login.jinja", bp=self)
|
||||
|
|
@ -3,19 +3,19 @@ from flask_assets import Environment
|
|||
from flask_babel import get_locale
|
||||
|
||||
from starfall.web.blueprints.base import BaseBlueprint
|
||||
from starfall.web.controllers.imprint import ImprintController
|
||||
from starfall.web.controllers.public.imprint import ImprintController
|
||||
|
||||
|
||||
class ImprintBlueprint(BaseBlueprint):
|
||||
def __init__(self, blueprint: Blueprint, assets: Environment, app: Flask) -> None:
|
||||
super().__init__(blueprint, assets, app)
|
||||
blueprint.add_url_rule("/imprint", view_func=self.imprint)
|
||||
blueprint.add_url_rule("/imprint/", view_func=self.imprint)
|
||||
|
||||
def imprint(self):
|
||||
self._log_access()
|
||||
ImprintController.apply(self)
|
||||
return render_template(
|
||||
"imprint.jinja",
|
||||
"public/imprint.jinja",
|
||||
bp=self,
|
||||
lang=get_locale(),
|
||||
)
|
||||
|
|
@ -16,7 +16,7 @@ class MainBlueprint(BaseBlueprint):
|
|||
def index(self):
|
||||
self._log_access()
|
||||
return render_template(
|
||||
"home.jinja",
|
||||
"public/home.jinja",
|
||||
bp=self,
|
||||
lang=get_locale(),
|
||||
)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
from flask import Blueprint, Flask, redirect, render_template, url_for
|
||||
from flask_assets import Environment
|
||||
|
||||
from starfall.web.blueprints.base import BaseBlueprint
|
||||
from starfall.web.controllers.secure.login import LoginController
|
||||
from starfall.web.controllers.secure.register import RegisterController
|
||||
|
||||
|
||||
class AdminBlueprint(BaseBlueprint):
|
||||
def __init__(self, blueprint: Blueprint, assets: Environment, app: Flask):
|
||||
super().__init__(blueprint, assets, app)
|
||||
blueprint.add_url_rule("/secure/", view_func=self.secure)
|
||||
blueprint.add_url_rule("/secure/login/", methods=["GET", "POST"], view_func=self.login)
|
||||
blueprint.add_url_rule("/secure/register/", methods=["GET", "POST"], view_func=self.register)
|
||||
|
||||
def secure(self):
|
||||
return redirect(url_for("starfall.login"))
|
||||
|
||||
def login(self):
|
||||
LoginController.apply(self)
|
||||
return render_template("secure/login.jinja", bp=self)
|
||||
|
||||
def register(self):
|
||||
RegisterController.apply(self)
|
||||
return render_template("secure/register.jinja", bp=self)
|
||||
|
|
@ -1,2 +1,11 @@
|
|||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
|
||||
class BaseController:
|
||||
pass
|
||||
@classmethod
|
||||
def encrypt_password(cls, password: str):
|
||||
return generate_password_hash(password)
|
||||
|
||||
@classmethod
|
||||
def validate_password(cls, pwhash: str, password: str):
|
||||
return check_password_hash(pwhash, password)
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
from datetime import datetime
|
||||
|
||||
from starfall.web.blueprints.base import BaseBlueprint
|
||||
from starfall.web.controllers.base import BaseController
|
||||
|
||||
|
||||
class ImprintController:
|
||||
class ImprintController(BaseController):
|
||||
@classmethod
|
||||
def apply(cls, bp: BaseBlueprint):
|
||||
bp.data["lastmod"] = cls.last_modified()
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
from flask_babel import _, lazy_gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import PasswordField, StringField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
from starfall.web.blueprints.base import BaseBlueprint
|
||||
from starfall.web.controllers.base import BaseController
|
||||
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
username: StringField = StringField(
|
||||
label=lazy_gettext("page.login.form.username"), # pyright: ignore[reportArgumentType]
|
||||
render_kw={
|
||||
"autocomplete": "username",
|
||||
"class": "form-control",
|
||||
"placeholder": lazy_gettext("page.login.form.username"),
|
||||
},
|
||||
validators=[
|
||||
DataRequired(message=lazy_gettext("page.login.error.username")), # pyright: ignore[reportArgumentType]
|
||||
],
|
||||
)
|
||||
password: PasswordField = PasswordField(
|
||||
label=lazy_gettext("page.login.form.password"), # pyright: ignore[reportArgumentType]
|
||||
render_kw={
|
||||
"autocomplete": "password",
|
||||
"class": "form-control",
|
||||
"placeholder": lazy_gettext("page.login.form.password"),
|
||||
},
|
||||
validators=[
|
||||
DataRequired(message=lazy_gettext("page.login.error.password")), # pyright: ignore[reportArgumentType]
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class LoginController(BaseController):
|
||||
@classmethod
|
||||
def apply(cls, bp: BaseBlueprint):
|
||||
bp.data["form"] = LoginForm()
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
from flask import request
|
||||
from flask_babel import LazyString, lazy_gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import EmailField, PasswordField, StringField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
from starfall.db import db
|
||||
from starfall.db.schema.users import User
|
||||
from starfall.web.blueprints.base import BaseBlueprint
|
||||
from starfall.web.controllers.base import BaseController
|
||||
|
||||
|
||||
class RegisterForm(FlaskForm):
|
||||
username: StringField = StringField(
|
||||
label=lazy_gettext("page.register.form.username"), # pyright: ignore[reportArgumentType]
|
||||
render_kw={
|
||||
"autocomplete": "username",
|
||||
"class": "form-control",
|
||||
"aria-describedby": "username-addon",
|
||||
"placeholder": lazy_gettext("page.register.form.username"),
|
||||
},
|
||||
validators=[
|
||||
DataRequired(message=lazy_gettext("page.register.error.username")), # pyright: ignore[reportArgumentType]
|
||||
],
|
||||
)
|
||||
password: PasswordField = PasswordField(
|
||||
label=lazy_gettext("page.register.form.password"), # pyright: ignore[reportArgumentType]
|
||||
render_kw={
|
||||
"autocomplete": "password",
|
||||
"class": "form-control",
|
||||
"placeholder": lazy_gettext("page.register.form.password"),
|
||||
},
|
||||
validators=[
|
||||
DataRequired(message=lazy_gettext("page.register.error.password")), # pyright: ignore[reportArgumentType]
|
||||
],
|
||||
)
|
||||
email: EmailField = EmailField(
|
||||
label=lazy_gettext("page.register.form.email"), # pyright: ignore[reportArgumentType]
|
||||
render_kw={
|
||||
"autocomplete": "email",
|
||||
"class": "form-control",
|
||||
"placeholder": lazy_gettext("page.register.form.email"),
|
||||
},
|
||||
validators=[
|
||||
DataRequired(message=lazy_gettext("page.register.error.email")), # pyright: ignore[reportArgumentType]
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class RegisterController(BaseController):
|
||||
@classmethod
|
||||
def apply(cls, bp: BaseBlueprint):
|
||||
if "POST" == request.method:
|
||||
bp.data["status_class"], bp.data["status"] = cls.handle_form()
|
||||
bp.data["form"] = RegisterForm()
|
||||
|
||||
@classmethod
|
||||
def handle_form(cls) -> tuple[str | None, LazyString | None]:
|
||||
form = RegisterForm()
|
||||
if not form.validate_on_submit():
|
||||
return None, None
|
||||
user = User(
|
||||
username=str(form.username.data),
|
||||
password=cls.encrypt_password(str(form.password.data)),
|
||||
email=str(form.email.data),
|
||||
)
|
||||
try:
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
except Exception:
|
||||
return "error", lazy_gettext("page.register.status.email_already_exists")
|
||||
return "success", lazy_gettext("page.register.status.success")
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
from starfall.web.controllers.base import BaseController
|
||||
from flask_login import LoginManager
|
||||
|
||||
login_manager = LoginManager()
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def user(user_id):
|
||||
return UserController.get(user_id)
|
||||
|
||||
|
||||
class UserController(BaseController):
|
||||
@classmethod
|
||||
def get(cls, user_id):
|
||||
return None
|
||||
Binary file not shown.
|
|
@ -7,16 +7,70 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Starfall 0.1.0-alpha.3\n"
|
||||
"Report-Msgid-Bugs-To: flare@theflare.at\n"
|
||||
"POT-Creation-Date: 2026-03-10 10:46+0100\n"
|
||||
"PO-Revision-Date: 2026-03-10 01:17+0100\n"
|
||||
"POT-Creation-Date: 2026-03-18 10:46+0100\n"
|
||||
"PO-Revision-Date: 2026-03-18 10:47+0100\n"
|
||||
"Last-Translator: Flare Starfall <flare@theflare.at>\n"
|
||||
"Language: en\n"
|
||||
"Language-Team: en <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Generated-By: Babel 2.18.0\n"
|
||||
"X-Generator: Poedit 3.9\n"
|
||||
|
||||
#: starfall/web/controllers/secure/login.py:12
|
||||
#: starfall/web/controllers/secure/login.py:16
|
||||
msgid "page.login.form.username"
|
||||
msgstr "Username / Email"
|
||||
|
||||
#: starfall/web/controllers/secure/login.py:19
|
||||
msgid "page.login.error.username"
|
||||
msgstr "A username or email is required."
|
||||
|
||||
#: starfall/web/controllers/secure/login.py:23
|
||||
#: starfall/web/controllers/secure/login.py:27
|
||||
msgid "page.login.form.password"
|
||||
msgstr "Password"
|
||||
|
||||
#: starfall/web/controllers/secure/login.py:30
|
||||
msgid "page.login.error.password"
|
||||
msgstr "A password is required."
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:15
|
||||
#: starfall/web/controllers/secure/register.py:20
|
||||
msgid "page.register.form.username"
|
||||
msgstr "Username"
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:23
|
||||
msgid "page.register.error.username"
|
||||
msgstr "A username is required."
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:27
|
||||
#: starfall/web/controllers/secure/register.py:31
|
||||
msgid "page.register.form.password"
|
||||
msgstr "Password"
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:34
|
||||
msgid "page.register.error.password"
|
||||
msgstr "A password is required."
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:38
|
||||
#: starfall/web/controllers/secure/register.py:42
|
||||
msgid "page.register.form.email"
|
||||
msgstr "Email"
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:45
|
||||
msgid "page.register.error.email"
|
||||
msgstr "A valid email is required."
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:71
|
||||
msgid "page.register.status.email_already_exists"
|
||||
msgstr "A user with this email already exists."
|
||||
|
||||
#: starfall/web/controllers/secure/register.py:72
|
||||
msgid "page.register.status.success"
|
||||
msgstr "Registration successful. You may now log in."
|
||||
|
||||
#: web/templates/base.jinja:6
|
||||
msgid "base.meta.title.empty"
|
||||
|
|
@ -42,79 +96,92 @@ msgstr "Starfall - Logo"
|
|||
msgid "base.label.toggle_nav"
|
||||
msgstr "Open menu"
|
||||
|
||||
#: web/templates/base.jinja:47
|
||||
#: web/templates/base.jinja:46
|
||||
msgid "base.label.login"
|
||||
msgstr "Login"
|
||||
|
||||
#: web/templates/base.jinja:53
|
||||
msgid "base.label.theme.btn"
|
||||
msgstr "Theme"
|
||||
|
||||
#: web/templates/base.jinja:52
|
||||
#: web/templates/base.jinja:58
|
||||
msgid "base.label.theme.light"
|
||||
msgstr "Light"
|
||||
|
||||
#: web/templates/base.jinja:55
|
||||
#: web/templates/base.jinja:61
|
||||
msgid "base.label.theme.dark"
|
||||
msgstr "Dark"
|
||||
|
||||
#: web/templates/base.jinja:58
|
||||
#: web/templates/base.jinja:64
|
||||
msgid "base.label.theme.auto"
|
||||
msgstr "Auto"
|
||||
|
||||
#: web/templates/base.jinja:76
|
||||
msgid "page.empty.title"
|
||||
msgstr "This page has no content"
|
||||
|
||||
#: web/templates/base.jinja:77
|
||||
msgid "page.empty.body"
|
||||
msgstr ""
|
||||
"<p>This page has not been assigned any content.</p><p>This is unlikely to"
|
||||
" be intentional - <span class=\"text-warning\">please notify the site "
|
||||
"administrator</span> about this problem.</p>"
|
||||
|
||||
#: web/templates/base.jinja:89
|
||||
#: web/templates/base.jinja:90
|
||||
msgid "base.label.copy"
|
||||
msgstr "© Team Starfall"
|
||||
|
||||
#: web/templates/home.jinja:3
|
||||
#: web/templates/components/content_empty.jinja:3
|
||||
msgid "page.empty.title"
|
||||
msgstr "This page has no content"
|
||||
|
||||
#: web/templates/components/content_empty.jinja:6
|
||||
msgid "page.empty.body"
|
||||
msgstr ""
|
||||
"<p>This page has not been assigned any content.</p><p>This is unlikely to be "
|
||||
"intentional - <span class=\"text-warning\">please notify the site "
|
||||
"administrator</span> about this problem.</p>"
|
||||
|
||||
#: web/templates/errors/not_found.jinja:8
|
||||
msgid "error.404.title"
|
||||
msgstr "404 Not Found"
|
||||
|
||||
#: web/templates/errors/not_found.jinja:9
|
||||
msgid "error.404.body"
|
||||
msgstr ""
|
||||
"<p>Whatever you were looking for cannot be found here.<br>You may want to <a "
|
||||
"href=\"/\">start over</a>.</p>"
|
||||
|
||||
#: web/templates/public/home.jinja:3
|
||||
msgid "page.home.title"
|
||||
msgstr "Home of Team Starfall"
|
||||
|
||||
#: web/templates/home.jinja:8
|
||||
#: web/templates/public/home.jinja:8
|
||||
msgid "page.home.card.title"
|
||||
msgstr "Shoot for the stars."
|
||||
|
||||
#: web/templates/home.jinja:9
|
||||
#: web/templates/public/home.jinja:11
|
||||
msgid "page.home.card.body"
|
||||
msgstr ""
|
||||
"<p>Home of Team Starfall, a small group of friends dedicated to messing "
|
||||
"with video games in ways the developers are unlikely to expect.</p><p>And"
|
||||
" hey - occasionally we get fun(ny) results out of it.</p><p>On the side, "
|
||||
"some of us get involved in all kinds of shenanigans, ranging from "
|
||||
"character creation in various kinds of games and software, over creating "
|
||||
"music, to web design and programming.</p>"
|
||||
"<p>Home of Team Starfall, a small group of friends dedicated to messing with "
|
||||
"video games in ways the developers are unlikely to expect.</p><p>And hey - "
|
||||
"occasionally we get fun(ny) results out of it.</p><p>On the side, some of us "
|
||||
"get involved in all kinds of shenanigans, ranging from character creation in "
|
||||
"various kinds of games and software, over creating music, to web design and "
|
||||
"programming.</p>"
|
||||
|
||||
#: web/templates/imprint.jinja:3 web/templates/imprint.jinja:8
|
||||
#: web/templates/public/imprint.jinja:3 web/templates/public/imprint.jinja:8
|
||||
msgid "page.imprint.title"
|
||||
msgstr "Imprint & Privacy"
|
||||
|
||||
#: web/templates/imprint.jinja:10
|
||||
#: web/templates/public/imprint.jinja:11
|
||||
msgid "page.imprint.card.title"
|
||||
msgstr "In short"
|
||||
|
||||
#: web/templates/imprint.jinja:12
|
||||
#: web/templates/public/imprint.jinja:13
|
||||
msgid "page.imprint.card.body"
|
||||
msgstr ""
|
||||
"The only thing this site processes is your site visit, stored for no "
|
||||
"longer than 7 days unless absolutely necessary. This data is only ever "
|
||||
"accessed by one person (this website's creator), and only in cases where "
|
||||
"reading the server logs is necessary (say, a website outage, or reading "
|
||||
"the logs while developing a new part of the website).<br />This "
|
||||
"disclaimer is not by itself legally binding. The full privacy statement "
|
||||
"is found below."
|
||||
"The only thing this site processes is your site visit, stored for no longer "
|
||||
"than 7 days unless absolutely necessary. This data is only ever accessed by "
|
||||
"one person (this website's creator), and only in cases where reading the "
|
||||
"server logs is necessary (say, a website outage, or reading the logs while "
|
||||
"developing a new part of the website).<br />This disclaimer is not by itself "
|
||||
"legally binding. The full privacy statement is found below."
|
||||
|
||||
#: web/templates/imprint.jinja:13
|
||||
#: web/templates/public/imprint.jinja:14
|
||||
msgid "page.imprint.card.lastmodified"
|
||||
msgstr "Last modified:"
|
||||
|
||||
#: web/templates/imprint.jinja:23
|
||||
#: web/templates/public/imprint.jinja:24
|
||||
msgid "page.imprint.legal"
|
||||
msgstr ""
|
||||
"Based on the <a href=\"https://www.generator-datenschutzerklärung.de\" "
|
||||
|
|
@ -124,123 +191,135 @@ msgstr ""
|
|||
"Partner</a>.<br>Edited to provide details of the data controller, and to "
|
||||
"detail exactly what server data is stored."
|
||||
|
||||
#: web/templates/imprint.jinja:31
|
||||
#: web/templates/public/imprint.jinja:32
|
||||
msgid "page.imprint.sec1.title"
|
||||
msgstr "Privacy Policy"
|
||||
|
||||
#: web/templates/imprint.jinja:32
|
||||
#: web/templates/public/imprint.jinja:33
|
||||
msgid "page.imprint.sec2.title"
|
||||
msgstr "I. Information about us as controllers of your data"
|
||||
|
||||
#: web/templates/imprint.jinja:33
|
||||
#: web/templates/public/imprint.jinja:34
|
||||
msgid "page.imprint.sec3.title"
|
||||
msgstr "II. The rights of users and data subjects"
|
||||
|
||||
#: web/templates/imprint.jinja:34
|
||||
#: web/templates/public/imprint.jinja:35
|
||||
msgid "page.imprint.sec4.title"
|
||||
msgstr "III. Information about the data processing"
|
||||
|
||||
#: web/templates/imprint.jinja:35
|
||||
#: web/templates/public/imprint.jinja:36
|
||||
msgid "page.imprint.sec1.body"
|
||||
msgstr ""
|
||||
"<p>Personal data (usually referred to just as „data“ below) will only be "
|
||||
"processed by us to the extent necessary and for the purpose of providing "
|
||||
"a functional and user-friendly website, including its contents, and the "
|
||||
"processed by us to the extent necessary and for the purpose of providing a "
|
||||
"functional and user-friendly website, including its contents, and the "
|
||||
"services offered there.</p>\n"
|
||||
"<p>Per Art. 4 No. 1 of Regulation (EU) 2016/679, i.e. the General Data "
|
||||
"Protection Regulation (hereinafter referred to as the „GDPR“), "
|
||||
"„processing“ refers to any operation or set of operations such as "
|
||||
"collection, recording, organization, structuring, storage, adaptation, "
|
||||
"alteration, retrieval, consultation, use, disclosure by transmission, "
|
||||
"dissemination, or otherwise making available, alignment, or combination, "
|
||||
"restriction, erasure, or destruction performed on personal data, whether "
|
||||
"by automated means or not.</p>\n"
|
||||
"Protection Regulation (hereinafter referred to as the „GDPR“), „processing“ "
|
||||
"refers to any operation or set of operations such as collection, recording, "
|
||||
"organization, structuring, storage, adaptation, alteration, retrieval, "
|
||||
"consultation, use, disclosure by transmission, dissemination, or otherwise "
|
||||
"making available, alignment, or combination, restriction, erasure, or "
|
||||
"destruction performed on personal data, whether by automated means or not.</"
|
||||
"p>\n"
|
||||
"<p>The following privacy policy is intended to inform you in particular "
|
||||
"about the type, scope, purpose, duration, and legal basis for the "
|
||||
"processing of such data either under our own control or in conjunction "
|
||||
"with others. We also inform you below about the third-party components we"
|
||||
" use to optimize our website and improve the user experience which may "
|
||||
"result in said third parties also processing data they collect and "
|
||||
"control.</p>\n"
|
||||
"about the type, scope, purpose, duration, and legal basis for the processing "
|
||||
"of such data either under our own control or in conjunction with others. We "
|
||||
"also inform you below about the third-party components we use to optimize "
|
||||
"our website and improve the user experience which may result in said third "
|
||||
"parties also processing data they collect and control.</p>\n"
|
||||
"<p>Our privacy policy is structured as follows:</p>\n"
|
||||
"<p>I. Information about us as controllers of your data<br>II. The rights "
|
||||
"of users and data subjects<br>III. Information about the data "
|
||||
"processing</p>"
|
||||
"<p>I. Information about us as controllers of your data<br>II. The rights of "
|
||||
"users and data subjects<br>III. Information about the data processing</p>"
|
||||
|
||||
#: web/templates/imprint.jinja:36
|
||||
#: web/templates/public/imprint.jinja:37
|
||||
msgid "page.imprint.sec2.body"
|
||||
msgstr ""
|
||||
"<p>The party responsible for this website (the „controller“) for purposes"
|
||||
" of data protection law is:<br>Robert Bäs-Fischlmair a.k.a. Flare "
|
||||
"<p>The party responsible for this website (the „controller“) for purposes of "
|
||||
"data protection law is:<br>Robert Bäs-Fischlmair a.k.a. Flare "
|
||||
"Starfall<br>Phone: +43 (0) 677 62890651<br>Email: <a "
|
||||
"href=\"mailto:flare@theflare.at\">flare@theflare.at</a></p>"
|
||||
|
||||
#: web/templates/imprint.jinja:37
|
||||
#: web/templates/public/imprint.jinja:38
|
||||
msgid "page.imprint.sec3.body"
|
||||
msgstr ""
|
||||
"<p>With regard to the data processing to be described in more detail "
|
||||
"below, users and data subjects have the right</p>\n"
|
||||
"<p>With regard to the data processing to be described in more detail below, "
|
||||
"users and data subjects have the right</p>\n"
|
||||
"<ul>\n"
|
||||
"<li>to confirmation of whether data concerning them is being processed, "
|
||||
"information about the data being processed, further information about the"
|
||||
" nature of the data processing, and copies of the data (cf. also Art. 15 "
|
||||
"information about the data being processed, further information about the "
|
||||
"nature of the data processing, and copies of the data (cf. also Art. 15 "
|
||||
"GDPR);</li>\n"
|
||||
"<li>to correct or complete incorrect or incomplete data (cf. also Art. 16 "
|
||||
"GDPR);</li>\n"
|
||||
"<li>to correct or complete incorrect or incomplete data (cf. also Art. 16"
|
||||
" GDPR);</li>\n"
|
||||
"<li>to the immediate deletion of data concerning them (cf. also Art. 17 "
|
||||
"DSGVO), or, alternatively, if further processing is necessary as "
|
||||
"stipulated in Art. 17 Para. 3 GDPR, to restrict said processing per Art. "
|
||||
"18 GDPR; </li> <li>to receive copies of the data concerning them and/or "
|
||||
"provided by them and to have the same transmitted to other "
|
||||
"providers/controllers (cf. also Art. 20 GDPR);</li> <li>to file "
|
||||
"complaints with the supervisory authority if they believe that data "
|
||||
"concerning them is being processed by the controller in breach of data "
|
||||
"protection provisions (see also Art. 77 GDPR).</li>\n"
|
||||
"DSGVO), or, alternatively, if further processing is necessary as stipulated "
|
||||
"in Art. 17 Para. 3 GDPR, to restrict said processing per Art. 18 GDPR; </li> "
|
||||
"<li>to receive copies of the data concerning them and/or provided by them "
|
||||
"and to have the same transmitted to other providers/controllers (cf. also "
|
||||
"Art. 20 GDPR);</li> <li>to file complaints with the supervisory authority if "
|
||||
"they believe that data concerning them is being processed by the controller "
|
||||
"in breach of data protection provisions (see also Art. 77 GDPR).</li>\n"
|
||||
"</ul>\n"
|
||||
"<p>In addition, the controller is obliged to inform all recipients to "
|
||||
"whom it discloses data of any such corrections, deletions, or "
|
||||
"restrictions placed on processing the same per Art. 16, 17 Para. 1, 18 "
|
||||
"GDPR. However, this obligation does not apply if such notification is "
|
||||
"impossible or involves a disproportionate effort. Nevertheless, users "
|
||||
"have a right to information about these recipients.</p>\n"
|
||||
"<p><strong>Likewise, under Art. 21 GDPR, users and data subjects have the"
|
||||
" right to object to the controller's future processing of their data"
|
||||
" pursuant to Art. 6 Para. 1 lit. f) GDPR. In particular, an objection to "
|
||||
"data processing for the purpose of direct advertising is "
|
||||
"permissible.</strong></p>"
|
||||
"<p>In addition, the controller is obliged to inform all recipients to whom "
|
||||
"it discloses data of any such corrections, deletions, or restrictions placed "
|
||||
"on processing the same per Art. 16, 17 Para. 1, 18 GDPR. However, this "
|
||||
"obligation does not apply if such notification is impossible or involves a "
|
||||
"disproportionate effort. Nevertheless, users have a right to information "
|
||||
"about these recipients.</p>\n"
|
||||
"<p><strong>Likewise, under Art. 21 GDPR, users and data subjects have the "
|
||||
"right to object to the controller's future processing of their data "
|
||||
"pursuant to Art. 6 Para. 1 lit. f) GDPR. In particular, an objection to data "
|
||||
"processing for the purpose of direct advertising is permissible.</strong></p>"
|
||||
|
||||
#: web/templates/imprint.jinja:38
|
||||
#: web/templates/public/imprint.jinja:39
|
||||
msgid "page.imprint.sec4.body"
|
||||
msgstr ""
|
||||
"<p>Your data processed when using our website will be deleted or blocked "
|
||||
"as soon as the purpose for its storage ceases to apply, provided the "
|
||||
"deletion of the same is not in breach of any statutory storage "
|
||||
"obligations or unless otherwise stipulated below.</p>\n"
|
||||
"<p>Your data processed when using our website will be deleted or blocked as "
|
||||
"soon as the purpose for its storage ceases to apply, provided the deletion "
|
||||
"of the same is not in breach of any statutory storage obligations or unless "
|
||||
"otherwise stipulated below.</p>\n"
|
||||
"<h3>Server data</h3>\n"
|
||||
"<p>For technical reasons, the following data sent by your internet "
|
||||
"browser to us or to our server provider will be collected, especially to "
|
||||
"ensure a secure and stable website: These server log files record the "
|
||||
"type and version of your browser, operating system, the webpages on our "
|
||||
"site visited, and the date and time of your visit. The IP address from "
|
||||
"which you visited our site is additionally recorded if an error "
|
||||
"occurs.</p>\n"
|
||||
"<p>For technical reasons, the following data sent by your internet browser "
|
||||
"to us or to our server provider will be collected, especially to ensure a "
|
||||
"secure and stable website: These server log files record the type and "
|
||||
"version of your browser, operating system, the webpages on our site visited, "
|
||||
"and the date and time of your visit. The IP address from which you visited "
|
||||
"our site is additionally recorded if an error occurs.</p>\n"
|
||||
"<p>The data thus collected will be temporarily stored, but not in "
|
||||
"association with any other of your data.</p>\n"
|
||||
"<p>The basis for this storage is Art. 6 Para. 1 lit. f) GDPR. Our "
|
||||
"legitimate interest lies in the improvement, stability, functionality, "
|
||||
"and security of our website.</p>\n"
|
||||
"<p>The data will be deleted within no more than seven days, unless "
|
||||
"continued storage is required for evidentiary purposes. In which case, "
|
||||
"all or part of the data will be excluded from deletion until the "
|
||||
"investigation of the relevant incident is finally resolved.</p>"
|
||||
"<p>The basis for this storage is Art. 6 Para. 1 lit. f) GDPR. Our legitimate "
|
||||
"interest lies in the improvement, stability, functionality, and security of "
|
||||
"our website.</p>\n"
|
||||
"<p>The data will be deleted within no more than seven days, unless continued "
|
||||
"storage is required for evidentiary purposes. In which case, all or part of "
|
||||
"the data will be excluded from deletion until the investigation of the "
|
||||
"relevant incident is finally resolved.</p>"
|
||||
|
||||
#: web/templates/errors/not_found.jinja:8
|
||||
msgid "error.404.title"
|
||||
msgstr "404 Not Found"
|
||||
#: web/templates/secure/login.jinja:3 web/templates/secure/login.jinja:8
|
||||
msgid "page.login.title"
|
||||
msgstr "Login"
|
||||
|
||||
#: web/templates/errors/not_found.jinja:9
|
||||
msgid "error.404.body"
|
||||
msgstr ""
|
||||
"<p>Whatever you were looking for cannot be found here.<br>You may want to"
|
||||
" <a href=\"/\">start over</a>.</p>"
|
||||
#: web/templates/secure/login.jinja:20
|
||||
msgid "page.login.form.submit"
|
||||
msgstr "Login"
|
||||
|
||||
#: web/templates/secure/login.jinja:24
|
||||
msgid "page.login.label.register"
|
||||
msgstr "Register"
|
||||
|
||||
#: web/templates/secure/login.jinja:26
|
||||
msgid "page.login.label.lostpassword"
|
||||
msgstr "Forgot Password?"
|
||||
|
||||
#: web/templates/secure/register.jinja:5 web/templates/secure/register.jinja:10
|
||||
msgid "page.register.title"
|
||||
msgstr "Register"
|
||||
|
||||
#: web/templates/secure/register.jinja:20
|
||||
msgid "page.register.form.submit"
|
||||
msgstr "Register"
|
||||
|
||||
#: web/templates/secure/register.jinja:24
|
||||
msgid "page.register.label.login"
|
||||
msgstr "Login"
|
||||
|
|
|
|||
|
|
@ -40,6 +40,12 @@
|
|||
{%endblock%}
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/secure/login">
|
||||
<i class="bi bi-person-circle"></i>
|
||||
<span class="visually-hidden">{{_('base.label.login')}}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-menu dropdown">
|
||||
<button id="theme" class="btn btn-link nav-link dropdown-toggle" type="button"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
|
|
@ -71,12 +77,7 @@
|
|||
{%block beforeMainContainer%}{%endblock%}
|
||||
|
||||
{%block body%}
|
||||
<div class="container" id="content">
|
||||
<div class="card">
|
||||
<h1 class="card-header">{{_('page.empty.title')}}</h1>
|
||||
<div class="card-body">{{_('page.empty.body')|safe}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{%include 'components/content_empty.jinja'%}
|
||||
{%endblock%}
|
||||
|
||||
{%block afterMainContainer%}{%endblock%}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<div class="container" id="content">
|
||||
<section>
|
||||
<h1 class="text-center text-primary-emphasis">{{_('page.empty.title')}}</h1>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">{{_('page.empty.body')|safe}}</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{%extends 'base.jinja'%}
|
||||
|
||||
{%block title%}{{_('page.home.title')}}{%endblock%}
|
||||
|
||||
{%block body%}
|
||||
<div class="container" id="content">
|
||||
<div class="card">
|
||||
<h1 class="card-header text-center text-primary-emphasis">{{_('page.home.card.title')}}</h1>
|
||||
<div class="card-body">{{_('page.home.card.body')|safe}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{%endblock%}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{% macro field(input, id='', prefix='', suffix='') -%}
|
||||
<div class="form-group my-1">
|
||||
{{input.label}}
|
||||
|
||||
{%if id and (prefix or suffix)%}
|
||||
<div class="input-group">
|
||||
{%if prefix%}
|
||||
<div class="input-group-prepend">
|
||||
<div class="input-group-text" id="{{id}}-addon">{{prefix}}</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
|
||||
{{input()}}
|
||||
|
||||
{%if suffix%}
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text" id="{{id}}-addon">{{suffix}}</div>
|
||||
</div>
|
||||
{%endif%}
|
||||
</div>
|
||||
{%else%}
|
||||
{{input()}}
|
||||
{%endif%}
|
||||
|
||||
{%if input.errors%}
|
||||
<p class="text-error">
|
||||
{%for error in input.errors%}
|
||||
<span>{{error}}</span>
|
||||
{%endfor%}
|
||||
</p>
|
||||
{%endif%}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{%extends 'base.jinja'%}
|
||||
|
||||
{%block title%}{{_('page.home.title')}}{%endblock%}
|
||||
|
||||
{%block body%}
|
||||
<div class="container" id="content">
|
||||
<section>
|
||||
<h1 class="text-center text-primary-emphasis">{{_('page.home.card.title')}}</h1>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">{{_('page.home.card.body')|safe}}</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{%endblock%}
|
||||
|
|
@ -4,25 +4,26 @@
|
|||
|
||||
{%block body%}
|
||||
<div class="container" id="content">
|
||||
<div class="d-flex flex-column">
|
||||
<h1 class="mb-4">{{_('page.imprint.title')}}</h1>
|
||||
<section class="card mb-2">
|
||||
<section class="d-flex flex-column">
|
||||
<h1 class="mb-4 text-center text-primary-emphasis">{{_('page.imprint.title')}}</h1>
|
||||
|
||||
<div class="card mb-2">
|
||||
<h2 class="card-header">{{_('page.imprint.card.title')}}</h2>
|
||||
<div class="card-body">
|
||||
<p>{{_('page.imprint.card.body')|safe}}</p>
|
||||
<p><strong>{{_('page.imprint.card.lastmodified')}} {{bp.data["lastmod"]|dateformat}}</strong></p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{%for i in range(1,4)%}
|
||||
<section class="border rounded p-2 mb-2">
|
||||
<div class="border rounded p-2 mb-2">
|
||||
<h2>{{_('page.imprint.sec' ~ i ~ '.title')}}</h2>
|
||||
{{_('page.imprint.sec' ~ i ~ '.body')|safe}}
|
||||
</section>
|
||||
</div>
|
||||
{%endfor%}
|
||||
<section>
|
||||
<div>
|
||||
<p><small>{{_('page.imprint.legal')|safe}}</small></p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{%endblock%}
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{%extends "base.jinja"%}
|
||||
{%import "macros/form.jinja" as form%}
|
||||
|
||||
{%block title%}{{_('page.login.title')}}{% endblock %}
|
||||
|
||||
{%block body%}
|
||||
<div class="container" id="content">
|
||||
<section class="d-flex flex-column align-items-center">
|
||||
<h1 class="text-center text-primary-emphasis">{{_('page.login.title')}}</h1>
|
||||
|
||||
<form class="border border-secondary bg-secondary-subtle rounded p-3" method="POST">
|
||||
{%if bp.data["status"]%}
|
||||
<div class="alert alert-{{bp.data['status_class']}}" role="alert">{{bp.data["status"]}}</div>
|
||||
{%endif%}
|
||||
{{bp.data["form"].hidden_tag() }}
|
||||
{{form.field(bp.data["form"].username)}}
|
||||
{{form.field(bp.data["form"].password)}}
|
||||
<button type="submit" class="my-3 btn btn-primary text-center">{{_('page.login.form.submit')}}</button>
|
||||
</form>
|
||||
|
||||
<div class="d-flex flex-row justify-content-center">
|
||||
<a class="text-muted p-2" href="/secure/register"><small>{{_('page.login.label.register')}}</small></a>
|
||||
<a class="text-muted p-2"
|
||||
href="/secure/lostpassword"><small>{{_('page.login.label.lostpassword')}}</small></a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{%endblock%}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{%extends "base.jinja"%}
|
||||
{%import "macros/form.jinja" as form%}
|
||||
|
||||
{%block title%}{{_('page.register.title')}}{% endblock %}
|
||||
|
||||
{%block body%}
|
||||
<div class="container" id="content">
|
||||
<section class="d-flex flex-column align-items-center">
|
||||
<h1 class="text-center text-primary-emphasis">{{_('page.register.title')}}</h1>
|
||||
|
||||
<form class="border border-secondary bg-secondary-subtle rounded p-3" method="POST">
|
||||
{%if bp.data["status"]%}
|
||||
<div class="alert alert-{{bp.data['status_class']}}" role="alert">{{bp.data["status"]}}</div>
|
||||
{%endif%}
|
||||
{{bp.data["form"].hidden_tag() }}
|
||||
{{form.field(input=bp.data["form"].username, id="username", prefix="@")}}
|
||||
{{form.field(input=bp.data["form"].password)}}
|
||||
{{form.field(input=bp.data["form"].email)}}
|
||||
<button type="submit" class="my-3 btn btn-primary text-center">{{_('page.register.form.submit')}}</button>
|
||||
</form>
|
||||
|
||||
<div class="d-flex flex-row justify-content-center">
|
||||
<a class="text-muted p-2" href="/secure/login"><small>{{_('page.register.label.login')}}</small></a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{%endblock%}
|
||||
Loading…
Reference in New Issue