mirror of
https://github.com/lucaspalomodevelop/eventcally.git
synced 2026-03-13 00:07:22 +00:00
Merge pull request #43 from DanielGrams/issue/42-settings
Move static legal information to editable settings #42
This commit is contained in:
commit
d437d41e5e
@ -1,4 +1,4 @@
|
||||
[ignore: env/**]
|
||||
[python: app/**.py]
|
||||
[jinja2: app/templates/**.html]
|
||||
[python: project/**.py]
|
||||
[jinja2: project/templates/**.html]
|
||||
extensions=jinja2.ext.autoescape,jinja2.ext.with_
|
||||
@ -42,17 +42,17 @@ flask db upgrade
|
||||
### Init
|
||||
|
||||
```sh
|
||||
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel init -i messages.pot -d app/translations -l de
|
||||
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel init -i messages.pot -d project/translations -l de
|
||||
```
|
||||
|
||||
### Extract new msgid's and merge into *.po files
|
||||
|
||||
```sh
|
||||
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel update -i messages.pot -d app/translations
|
||||
pybabel extract -F babel.cfg -o messages.pot . && pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . && pybabel update -i messages.pot -d project/translations
|
||||
```
|
||||
|
||||
#### Compile after translation is done
|
||||
|
||||
```sh
|
||||
pybabel compile -d app/translations
|
||||
pybabel compile -d project/translations
|
||||
```
|
||||
|
||||
43
migrations/versions/31b60d93351d_.py
Normal file
43
migrations/versions/31b60d93351d_.py
Normal file
@ -0,0 +1,43 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 31b60d93351d
|
||||
Revises: 3c5b34fd1156
|
||||
Create Date: 2020-12-12 13:48:34.244288
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
from project import dbtypes
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "31b60d93351d"
|
||||
down_revision = "3c5b34fd1156"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"settings",
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("tos", sa.UnicodeText(), nullable=True),
|
||||
sa.Column("legal_notice", sa.UnicodeText(), nullable=True),
|
||||
sa.Column("contact", sa.UnicodeText(), nullable=True),
|
||||
sa.Column("privacy", sa.UnicodeText(), nullable=True),
|
||||
sa.Column("created_by_id", sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["created_by_id"],
|
||||
["user.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table("settings")
|
||||
# ### end Alembic commands ###
|
||||
13
project/forms/admin.py
Normal file
13
project/forms/admin.py
Normal file
@ -0,0 +1,13 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_babelex import lazy_gettext
|
||||
from wtforms import TextAreaField, SubmitField
|
||||
from wtforms.validators import Optional
|
||||
|
||||
|
||||
class AdminSettingsForm(FlaskForm):
|
||||
tos = TextAreaField(lazy_gettext("Terms of service"), validators=[Optional()])
|
||||
legal_notice = TextAreaField(lazy_gettext("Legal notice"), validators=[Optional()])
|
||||
contact = TextAreaField(lazy_gettext("Contact"), validators=[Optional()])
|
||||
privacy = TextAreaField(lazy_gettext("Privacy"), validators=[Optional()])
|
||||
|
||||
submit = SubmitField(lazy_gettext("Save"))
|
||||
@ -40,6 +40,18 @@ class TrackableMixin(object):
|
||||
return relationship("User")
|
||||
|
||||
|
||||
# Global
|
||||
|
||||
|
||||
class Settings(db.Model, TrackableMixin):
|
||||
__tablename__ = "settings"
|
||||
id = Column(Integer(), primary_key=True)
|
||||
tos = Column(UnicodeText())
|
||||
legal_notice = Column(UnicodeText())
|
||||
contact = Column(UnicodeText())
|
||||
privacy = Column(UnicodeText())
|
||||
|
||||
|
||||
# Multi purpose
|
||||
|
||||
|
||||
|
||||
11
project/services/admin.py
Normal file
11
project/services/admin.py
Normal file
@ -0,0 +1,11 @@
|
||||
from project import db
|
||||
from project.models import Settings
|
||||
|
||||
|
||||
def upsert_settings():
|
||||
result = Settings.query.first()
|
||||
if result is None:
|
||||
result = Settings()
|
||||
db.session.add(result)
|
||||
|
||||
return result
|
||||
@ -11,6 +11,10 @@
|
||||
</nav>
|
||||
|
||||
<div class="list-group">
|
||||
<a href="{{ url_for('admin_settings') }}" class="list-group-item">
|
||||
{{ _('Settings') }}
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</a>
|
||||
<a href="{{ url_for('admin_admin_units') }}" class="list-group-item">
|
||||
{{ _('Admin Units') }}
|
||||
<i class="fa fa-caret-right"></i>
|
||||
|
||||
21
project/templates/admin/settings.html
Normal file
21
project/templates/admin/settings.html
Normal file
@ -0,0 +1,21 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "_macros.html" import render_field, render_field_with_errors %}
|
||||
{% block title %}
|
||||
{{ _('Settings') }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ _('Settings') }}</h1>
|
||||
|
||||
<form action="" method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{{ render_field_with_errors(form.tos) }}
|
||||
{{ render_field_with_errors(form.legal_notice) }}
|
||||
{{ render_field_with_errors(form.contact) }}
|
||||
{{ render_field_with_errors(form.privacy) }}
|
||||
|
||||
{{ render_field(form.submit) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
@ -1,19 +0,0 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}
|
||||
Impressum & Kontakt
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="p-3">
|
||||
<p class="h3">Impressum & Kontakt Entwickler</p>
|
||||
<p>
|
||||
Daniel Grams<br />
|
||||
Schreiberstrasse 2<br />
|
||||
38640 Goslar<br />
|
||||
<a href="mailto:moin@danielgrams.de">moin@danielgrams.de</a><br />
|
||||
<a href="https://www.danielgrams.de">https://www.danielgrams.de</a><br />
|
||||
Link zum Datenschutz: <a href="{{ url_for('datenschutz') }}">Datenschutz</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -62,7 +62,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<title>{% block title %}{{ title|default }}{% endblock title %}</title>
|
||||
<title>{% block title %}{{ title|default('oveda') }}{% endblock title %}</title>
|
||||
|
||||
{% block header %}
|
||||
{% endblock %}
|
||||
@ -136,15 +136,19 @@
|
||||
<div class="col h-100 text-center my-auto">
|
||||
<ul class="list-inline mb-2">
|
||||
<li class="list-inline-item">
|
||||
<a href="{{ url_for('impressum') }}" class="text-muted">Impressum</a>
|
||||
<a href="{{ url_for('tos') }}" class="text-muted">{{ _('Terms of service') }}</a>
|
||||
</li>
|
||||
<li class="list-inline-item">⋅</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="{{ url_for('impressum') }}" class="text-muted">Kontakt</a>
|
||||
<a href="{{ url_for('legal_notice') }}" class="text-muted">{{ _('Legal notice') }}</a>
|
||||
</li>
|
||||
<li class="list-inline-item">⋅</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="{{ url_for('datenschutz') }}" class="text-muted">Datenschutz</a>
|
||||
<a href="{{ url_for('contact') }}" class="text-muted">{{ _('Contact') }}</a>
|
||||
</li>
|
||||
<li class="list-inline-item">⋅</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="{{ url_for('privacy') }}" class="text-muted">{{ _('Privacy') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="text-muted small mb-4 mb-lg-0">Mit <i class="fa fa-heart"></i> in Goslar entwickelt.</p>
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}
|
||||
Datenschutz
|
||||
{{ title }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="p-3">
|
||||
|
||||
<h1>Datenschutzerklärung</h1>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{{ content }}
|
||||
{% endblock %}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,15 @@
|
||||
from project import app
|
||||
from project import app, db
|
||||
from project.models import AdminUnit
|
||||
from flask import render_template
|
||||
from flask import render_template, flash, url_for, redirect
|
||||
from flask_babelex import gettext
|
||||
from flask_security import roles_required
|
||||
from project.forms.admin import AdminSettingsForm
|
||||
from project.services.admin import upsert_settings
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
handleSqlError,
|
||||
)
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
|
||||
@app.route("/admin")
|
||||
@ -14,3 +22,25 @@ def admin():
|
||||
@roles_required("admin")
|
||||
def admin_admin_units():
|
||||
return render_template("admin/admin_units.html", admin_units=AdminUnit.query.all())
|
||||
|
||||
|
||||
@app.route("/admin/settings", methods=("GET", "POST"))
|
||||
@roles_required("admin")
|
||||
def admin_settings():
|
||||
settings = upsert_settings()
|
||||
form = AdminSettingsForm(obj=settings)
|
||||
|
||||
if form.validate_on_submit():
|
||||
form.populate_obj(settings)
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
flash(gettext("Settings successfully updated"), "success")
|
||||
return redirect(url_for("admin"))
|
||||
except SQLAlchemyError as e:
|
||||
db.session.rollback()
|
||||
flash(handleSqlError(e), "danger")
|
||||
else:
|
||||
flash_errors(form)
|
||||
|
||||
return render_template("admin/settings.html", form=form)
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
from project import app
|
||||
from project.services.admin import upsert_settings
|
||||
from project.views.utils import track_analytics
|
||||
from flask import url_for, render_template, request, redirect
|
||||
from flask_babelex import gettext
|
||||
from markupsafe import Markup
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@ -17,14 +20,36 @@ def example():
|
||||
return render_template("example.html")
|
||||
|
||||
|
||||
@app.route("/impressum")
|
||||
def impressum():
|
||||
return render_template("impressum.html")
|
||||
@app.route("/tos")
|
||||
def tos():
|
||||
title = gettext("Terms of service")
|
||||
settings = upsert_settings()
|
||||
content = Markup(settings.tos)
|
||||
return render_template("legal.html", title=title, content=content)
|
||||
|
||||
|
||||
@app.route("/datenschutz")
|
||||
def datenschutz():
|
||||
return render_template("datenschutz.html")
|
||||
@app.route("/legal_notice")
|
||||
def legal_notice():
|
||||
title = gettext("Legal notice")
|
||||
settings = upsert_settings()
|
||||
content = Markup(settings.legal_notice)
|
||||
return render_template("legal.html", title=title, content=content)
|
||||
|
||||
|
||||
@app.route("/contact")
|
||||
def contact():
|
||||
title = gettext("Contact")
|
||||
settings = upsert_settings()
|
||||
content = Markup(settings.contact)
|
||||
return render_template("legal.html", title=title, content=content)
|
||||
|
||||
|
||||
@app.route("/privacy")
|
||||
def privacy():
|
||||
title = gettext("Privacy")
|
||||
settings = upsert_settings()
|
||||
content = Markup(settings.privacy)
|
||||
return render_template("legal.html", title=title, content=content)
|
||||
|
||||
|
||||
@app.route("/developer")
|
||||
|
||||
@ -6,10 +6,10 @@ bcrypt==3.2.0
|
||||
beautifulsoup4==4.9.3
|
||||
black==20.8b1
|
||||
blinker==1.4
|
||||
certifi==2020.11.8
|
||||
certifi==2020.12.5
|
||||
cffi==1.14.4
|
||||
cfgv==3.2.0
|
||||
chardet==3.0.4
|
||||
chardet==4.0.0
|
||||
click==7.1.2
|
||||
colour==0.1.5
|
||||
coverage==5.3
|
||||
@ -38,7 +38,7 @@ GeoAlchemy2==0.8.4
|
||||
gunicorn==20.0.4
|
||||
identify==1.5.10
|
||||
idna==2.10
|
||||
importlib-metadata==2.1.1
|
||||
importlib-metadata==3.1.1
|
||||
iniconfig==1.1.1
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.2
|
||||
@ -48,14 +48,14 @@ mccabe==0.6.1
|
||||
mypy-extensions==0.4.3
|
||||
nodeenv==1.5.0
|
||||
oauthlib==3.1.0
|
||||
packaging==20.7
|
||||
packaging==20.8
|
||||
passlib==1.7.4
|
||||
pathspec==0.8.1
|
||||
Pillow==8.0.1
|
||||
pluggy==0.13.1
|
||||
pre-commit==2.9.2
|
||||
pre-commit==2.9.3
|
||||
psycopg2-binary==2.8.6
|
||||
py==1.9.0
|
||||
py==1.10.0
|
||||
pycodestyle==2.6.0
|
||||
pycparser==2.20
|
||||
pyflakes==2.2.0
|
||||
@ -74,7 +74,7 @@ requests==2.25.0
|
||||
requests-oauthlib==1.3.0
|
||||
rope==0.18.0
|
||||
six==1.15.0
|
||||
soupsieve==2.0.1
|
||||
soupsieve==2.1
|
||||
speaklater==1.3
|
||||
SQLAlchemy==1.3.20
|
||||
SQLAlchemy-Utils==0.36.8
|
||||
@ -83,7 +83,7 @@ typed-ast==1.4.1
|
||||
typing-extensions==3.7.4.3
|
||||
urllib3==1.26.2
|
||||
URLObject==2.4.3
|
||||
virtualenv==20.2.1
|
||||
virtualenv==20.2.2
|
||||
visitor==0.1.3
|
||||
Werkzeug==1.0.1
|
||||
WTForms==2.3.3
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def test_normal_user(client, seeder, utils):
|
||||
seeder.create_user()
|
||||
utils.login()
|
||||
@ -18,3 +21,40 @@ def test_admin_units(client, seeder, utils, app):
|
||||
seeder.create_admin_unit(user, "Meine Crew")
|
||||
response = client.get("/admin/admin_units")
|
||||
assert b"Meine Crew" in response.data
|
||||
|
||||
|
||||
@pytest.mark.parametrize("db_error", [True, False])
|
||||
def test_admin_settings(client, seeder, utils, app, mocker, db_error):
|
||||
user_id, admin_unit_id = seeder.setup_base(True)
|
||||
|
||||
url = utils.get_url("admin_settings")
|
||||
response = utils.get_ok(url)
|
||||
|
||||
if db_error:
|
||||
utils.mock_db_commit(mocker)
|
||||
|
||||
response = utils.post_form(
|
||||
url,
|
||||
response,
|
||||
{
|
||||
"tos": "Meine Nutzungsbedingungen",
|
||||
"legal_notice": "Mein Impressum",
|
||||
"contact": "Mein Kontakt",
|
||||
"privacy": "Mein Datenschutz",
|
||||
},
|
||||
)
|
||||
|
||||
if db_error:
|
||||
utils.assert_response_db_error(response)
|
||||
return
|
||||
|
||||
utils.assert_response_redirect(response, "admin")
|
||||
|
||||
with app.app_context():
|
||||
from project.services.admin import upsert_settings
|
||||
|
||||
settings = upsert_settings()
|
||||
assert settings.tos == "Meine Nutzungsbedingungen"
|
||||
assert settings.legal_notice == "Mein Impressum"
|
||||
assert settings.contact == "Mein Kontakt"
|
||||
assert settings.privacy == "Mein Datenschutz"
|
||||
|
||||
@ -12,14 +12,56 @@ def test_example(client, seeder, utils):
|
||||
utils.get_ok(url)
|
||||
|
||||
|
||||
def test_impressum(client, seeder, utils):
|
||||
url = utils.get_url("impressum")
|
||||
utils.get_ok(url)
|
||||
def test_tos(app, db, utils):
|
||||
with app.app_context():
|
||||
from project.services.admin import upsert_settings
|
||||
|
||||
settings = upsert_settings()
|
||||
settings.tos = "Meine Nutzungsbedingungen"
|
||||
db.session.commit()
|
||||
|
||||
url = utils.get_url("tos")
|
||||
response = utils.get_ok(url)
|
||||
assert b"Meine Nutzungsbedingungen" in response.data
|
||||
|
||||
|
||||
def test_datenschutz(client, seeder, utils):
|
||||
url = utils.get_url("datenschutz")
|
||||
utils.get_ok(url)
|
||||
def test_legal_notice(app, db, utils):
|
||||
with app.app_context():
|
||||
from project.services.admin import upsert_settings
|
||||
|
||||
settings = upsert_settings()
|
||||
settings.legal_notice = "Mein Impressum"
|
||||
db.session.commit()
|
||||
|
||||
url = utils.get_url("legal_notice")
|
||||
response = utils.get_ok(url)
|
||||
assert b"Mein Impressum" in response.data
|
||||
|
||||
|
||||
def test_contact(app, db, utils):
|
||||
with app.app_context():
|
||||
from project.services.admin import upsert_settings
|
||||
|
||||
settings = upsert_settings()
|
||||
settings.contact = "Mein Kontakt"
|
||||
db.session.commit()
|
||||
|
||||
url = utils.get_url("contact")
|
||||
response = utils.get_ok(url)
|
||||
assert b"Mein Kontakt" in response.data
|
||||
|
||||
|
||||
def test_privacy(app, db, utils):
|
||||
with app.app_context():
|
||||
from project.services.admin import upsert_settings
|
||||
|
||||
settings = upsert_settings()
|
||||
settings.privacy = "Mein Datenschutz"
|
||||
db.session.commit()
|
||||
|
||||
url = utils.get_url("privacy")
|
||||
response = utils.get_ok(url)
|
||||
assert b"Mein Datenschutz" in response.data
|
||||
|
||||
|
||||
def test_developer(client, seeder, utils):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user