mirror of
https://github.com/lucaspalomodevelop/eventcally.git
synced 2026-03-13 08:09:37 +00:00
meilenstein
This commit is contained in:
parent
fc64b55dfd
commit
a151f3a22a
11
README.md
11
README.md
@ -4,6 +4,17 @@ python manage.py db init
|
||||
python manage.py db migrate
|
||||
python manage.py db upgrade
|
||||
|
||||
## Developemt !!!
|
||||
python manage.py db history
|
||||
python manage.py db downgrade
|
||||
// reset git: migrations/versions
|
||||
python manage.py db migrate
|
||||
python manage.py db upgrade
|
||||
|
||||
## Kill local detached server
|
||||
lsof -i :5000
|
||||
kill -9 PIDNUMBER
|
||||
|
||||
# i18n
|
||||
|
||||
https://pythonhosted.org/Flask-BabelEx/
|
||||
|
||||
369
app.py
369
app.py
@ -1,8 +1,13 @@
|
||||
import os
|
||||
from flask import Flask, render_template, request
|
||||
from flask import Flask, render_template, request, url_for, redirect
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from sqlalchemy.orm import joinedload
|
||||
from flask_security import Security, current_user, auth_required, roles_required, hash_password, SQLAlchemySessionUserDatastore
|
||||
from flask_babelex import Babel, gettext, lazy_gettext
|
||||
from flask_security.utils import FsPermNeed
|
||||
from flask_babelex import Babel, gettext, lazy_gettext, format_datetime
|
||||
from flask_principal import Permission
|
||||
from datetime import datetime
|
||||
import pytz
|
||||
|
||||
# Create app
|
||||
app = Flask(__name__)
|
||||
@ -30,7 +35,7 @@ db = SQLAlchemy(app)
|
||||
|
||||
# Setup Flask-Security
|
||||
# Define models
|
||||
from models import User, Role
|
||||
from models import User, Role, AdminUnit, AdminUnitMember, AdminUnitMemberRole, OrgMember, OrgMemberRole, Organization, AdminUnitOrg, AdminUnitOrgRole, Event, EventDate
|
||||
user_datastore = SQLAlchemySessionUserDatastore(db.session, User, Role)
|
||||
security = Security(app, user_datastore)
|
||||
|
||||
@ -39,32 +44,369 @@ def get_locale():
|
||||
return request.accept_languages.best_match(app.config['LANGUAGES'])
|
||||
|
||||
# Create a user to test with
|
||||
def upsert_user(email, password="password"):
|
||||
result = user_datastore.find_user(email=email)
|
||||
if result is None:
|
||||
result = user_datastore.create_user(email=email, password=hash_password(password))
|
||||
return result
|
||||
|
||||
def upsert_admin_unit(unit_name):
|
||||
admin_unit = AdminUnit.query.filter_by(name = unit_name).first()
|
||||
if admin_unit is None:
|
||||
admin_unit = AdminUnit(name = unit_name)
|
||||
db.session.add(admin_unit)
|
||||
return admin_unit
|
||||
|
||||
def get_admin_unit(unit_name):
|
||||
return AdminUnit.query.filter_by(name = unit_name).first()
|
||||
|
||||
def upsert_org_member_role(role_name, permissions):
|
||||
result = OrgMemberRole.query.filter_by(name = role_name).first()
|
||||
if result is None:
|
||||
result = OrgMemberRole(name = role_name)
|
||||
result.remove_permissions(result.get_permissions())
|
||||
result.add_permissions(permissions)
|
||||
db.session.add(result)
|
||||
return result
|
||||
|
||||
def upsert_admin_unit_member_role(role_name, permissions):
|
||||
result = AdminUnitMemberRole.query.filter_by(name = role_name).first()
|
||||
if result is None:
|
||||
result = AdminUnitMemberRole(name = role_name)
|
||||
db.session.add(result)
|
||||
|
||||
result.remove_permissions(result.get_permissions())
|
||||
result.add_permissions(permissions)
|
||||
return result
|
||||
|
||||
def upsert_admin_unit_org_role(role_name, permissions):
|
||||
result = AdminUnitOrgRole.query.filter_by(name = role_name).first()
|
||||
if result is None:
|
||||
result = AdminUnitOrgRole(name = role_name)
|
||||
db.session.add(result)
|
||||
|
||||
result.remove_permissions(result.get_permissions())
|
||||
result.add_permissions(permissions)
|
||||
return result
|
||||
|
||||
def add_user_to_organization(user, organization):
|
||||
result = OrgMember.query.with_parent(organization).filter_by(user_id = user.id).first()
|
||||
if result is None:
|
||||
result = OrgMember(user = user, organization_id = organization.id)
|
||||
organization.members.append(result)
|
||||
db.session.add(result)
|
||||
return result
|
||||
|
||||
def add_user_to_admin_unit(user, admin_unit):
|
||||
result = OrgMember.query.with_parent(admin_unit).filter_by(user_id = user.id).first()
|
||||
if result is None:
|
||||
result = OrgMember(user = user)
|
||||
admin_unit.members.append(result)
|
||||
db.session.add(result)
|
||||
return result
|
||||
|
||||
def add_user_to_admin_unit(user, admin_unit):
|
||||
result = AdminUnitMember.query.with_parent(admin_unit).filter_by(user_id = user.id).first()
|
||||
if result is None:
|
||||
result = AdminUnitMember(user = user, admin_unit_id=admin_unit.id)
|
||||
admin_unit.members.append(result)
|
||||
db.session.add(result)
|
||||
return result
|
||||
|
||||
def add_organization_to_admin_unit(organization, admin_unit):
|
||||
result = AdminUnitOrg.query.with_parent(admin_unit).filter_by(organization_id = organization.id).first()
|
||||
if result is None:
|
||||
result = AdminUnitOrg(organization = organization, admin_unit_id=admin_unit.id)
|
||||
admin_unit.organizations.append(result)
|
||||
db.session.add(result)
|
||||
return result
|
||||
|
||||
def add_role_to_admin_unit_org(admin_unit_org, role):
|
||||
if AdminUnitOrgRole.query.with_parent(admin_unit_org).filter_by(name = role.name).first() is None:
|
||||
admin_unit_org.roles.append(role)
|
||||
|
||||
def add_role_to_admin_unit_member(admin_unit_member, role):
|
||||
if AdminUnitMemberRole.query.with_parent(admin_unit_member).filter_by(name = role.name).first() is None:
|
||||
admin_unit_member.roles.append(role)
|
||||
|
||||
def add_role_to_org_member(org_member, role):
|
||||
if OrgMemberRole.query.with_parent(org_member).filter_by(name = role.name).first() is None:
|
||||
org_member.roles.append(role)
|
||||
|
||||
def upsert_organization(org_name):
|
||||
result = Organization.query.filter_by(name = org_name).first()
|
||||
if result is None:
|
||||
result = Organization(name = org_name)
|
||||
db.session.add(result)
|
||||
return result
|
||||
|
||||
def create_berlin_date(year, month, day, hour, minute = 0):
|
||||
return pytz.timezone('Europe/Berlin').localize(datetime(year, month, day, hour=hour, minute=minute))
|
||||
|
||||
def upsert_event(event_name, host, location, start, description, link = None, verified = False):
|
||||
result = Event().query.filter_by(name = event_name).first()
|
||||
if result is None:
|
||||
result = Event()
|
||||
result.name = event_name
|
||||
result.host = host
|
||||
result.location = location
|
||||
result.description = description
|
||||
result.external_link = link
|
||||
result.admin_unit = get_admin_unit('Stadt Goslar')
|
||||
result.verified = verified
|
||||
eventDate = EventDate(event_id = result.id, start=start)
|
||||
result.dates.append(eventDate)
|
||||
db.session.add(result)
|
||||
return result
|
||||
|
||||
def has_admin_unit_member_permission(admin_unit_id, permission):
|
||||
admin_unit_member = AdminUnitMember.query.filter_by(admin_unit_id=admin_unit_id, user_id=current_user.id).first()
|
||||
if admin_unit_member is not None:
|
||||
for role in admin_unit_member.roles:
|
||||
if permission in role.get_permissions():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def can_list_admin_unit_members(admin_unit):
|
||||
if not current_user.is_authenticated:
|
||||
return False
|
||||
|
||||
# User permission, e.g. user is global admin
|
||||
user_perm = Permission(FsPermNeed('admin_unit.members:read'))
|
||||
if user_perm.can():
|
||||
return True
|
||||
|
||||
if has_admin_unit_member_permission(admin_unit.id, 'admin_unit.members:read'):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def can_list_org_members(organization):
|
||||
if not current_user.is_authenticated:
|
||||
return False
|
||||
|
||||
# User permission, e.g. user is global admin
|
||||
user_perm = Permission(FsPermNeed('organization.members:read'))
|
||||
if user_perm.can():
|
||||
return True
|
||||
|
||||
return True # todo
|
||||
|
||||
def can_verify_event(event):
|
||||
if not current_user.is_authenticated:
|
||||
return False
|
||||
|
||||
# User permission, e.g. user is global admin
|
||||
user_perm = Permission(FsPermNeed('event:verify'))
|
||||
if user_perm.can():
|
||||
return True
|
||||
|
||||
# Admin unit member permissions (Holger, Artur)
|
||||
if has_admin_unit_member_permission(event.admin_unit_id, 'event:verify'):
|
||||
return True
|
||||
|
||||
# Event has Admin Unit
|
||||
# Admin Unit has organization members with roles with permission 'event:verify'
|
||||
# This organization has members with roles with permission 'event:verify'
|
||||
# Der aktuelle nutzer muss unter diesen nutzern sein
|
||||
admin_unit_orgs = AdminUnitOrg.query.filter_by(admin_unit_id=event.admin_unit_id).all()
|
||||
for admin_unit_org in admin_unit_orgs:
|
||||
for admin_unit_org_role in admin_unit_org.roles:
|
||||
if 'event:verify' in admin_unit_org_role.get_permissions():
|
||||
org_member = OrgMember.query.filter_by(organization_id=admin_unit_org.organization_id, user_id=current_user.id).first()
|
||||
if org_member is not None:
|
||||
for org_member_role in org_member.roles:
|
||||
if 'event:verify' in org_member_role.get_permissions():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@app.before_first_request
|
||||
def create_user():
|
||||
admin_role = user_datastore.find_or_create_role("admin", permissions=["user_create"])
|
||||
# Admin units
|
||||
goslar = upsert_admin_unit('Stadt Goslar')
|
||||
upsert_admin_unit('Bad Harzburg')
|
||||
upsert_admin_unit('Clausthal')
|
||||
upsert_admin_unit('Walkenried')
|
||||
upsert_admin_unit('Bad Lauterberg')
|
||||
upsert_admin_unit('Harzgerode')
|
||||
upsert_admin_unit('Ilsenburg')
|
||||
upsert_admin_unit('Osterode')
|
||||
upsert_admin_unit('Quedlinburg')
|
||||
upsert_admin_unit('Wernigerode')
|
||||
upsert_admin_unit('Halberstadt')
|
||||
upsert_admin_unit('Wennigsen')
|
||||
upsert_admin_unit('Hildesheim')
|
||||
|
||||
admin_user = user_datastore.find_user(email="grams.daniel@gmail.com")
|
||||
if admin_user is None:
|
||||
admin_user = user_datastore.create_user(email="grams.daniel@gmail.com", password=hash_password("password"))
|
||||
# Organizations
|
||||
admin_unit_org_event_verifier_role = upsert_admin_unit_org_role('event_verifier', ['event:verify'])
|
||||
gmg = upsert_organization("GOSLAR marketing gmbh")
|
||||
gz = upsert_organization("Goslarsche Zeitung")
|
||||
|
||||
user_datastore.add_role_to_user(admin_user, admin_role)
|
||||
gmg_admin_unit_org = add_organization_to_admin_unit(gmg, goslar)
|
||||
add_role_to_admin_unit_org(gmg_admin_unit_org, admin_unit_org_event_verifier_role)
|
||||
|
||||
normal_user = user_datastore.find_user(email="normal.user@gmail.com")
|
||||
if normal_user is None:
|
||||
normal_user = user_datastore.create_user(email="normal.user@gmail.com", password=hash_password("password"))
|
||||
gz_admin_unit_org = add_organization_to_admin_unit(gz, goslar)
|
||||
add_role_to_admin_unit_org(gz_admin_unit_org, admin_unit_org_event_verifier_role)
|
||||
|
||||
# Users
|
||||
admin_role = user_datastore.find_or_create_role("admin")
|
||||
admin_role.add_permissions(["user:create", "event:verify", "admin_unit.members:read", "organization.members:read"])
|
||||
|
||||
admin_unit_admin_role = upsert_admin_unit_member_role('admin', ["admin_unit.members:read"])
|
||||
admin_unit_event_verifier_role = upsert_admin_unit_member_role('event_verifier', ["event:verify"])
|
||||
org_member_event_verifier_role = upsert_org_member_role('event_verifier', ["event:verify"])
|
||||
|
||||
daniel = upsert_user("grams.daniel@gmail.com")
|
||||
user_datastore.add_role_to_user(daniel, admin_role)
|
||||
|
||||
holger = upsert_user("holger@test.de")
|
||||
holger_goslar_member = add_user_to_admin_unit(holger, goslar)
|
||||
add_role_to_admin_unit_member(holger_goslar_member, admin_unit_admin_role)
|
||||
add_role_to_admin_unit_member(holger_goslar_member, admin_unit_event_verifier_role)
|
||||
|
||||
artur = upsert_user("artur@test.de")
|
||||
artur_goslar_member = add_user_to_admin_unit(artur, goslar)
|
||||
add_role_to_admin_unit_member(artur_goslar_member, admin_unit_event_verifier_role)
|
||||
|
||||
mia = upsert_user("mia@test.de")
|
||||
mia_gmg_member = add_user_to_organization(mia, gmg)
|
||||
add_role_to_org_member(mia_gmg_member, org_member_event_verifier_role)
|
||||
|
||||
tom = upsert_user("tom@test.de")
|
||||
tom_gz_member = add_user_to_organization(tom, gz)
|
||||
add_role_to_org_member(tom_gz_member, org_member_event_verifier_role)
|
||||
|
||||
# Events
|
||||
berlin = pytz.timezone('Europe/Berlin')
|
||||
upsert_event("Vienenburger Seefest",
|
||||
"Stadt Goslar",
|
||||
"Vienenburger See",
|
||||
create_berlin_date(2020, 8, 14, 17, 0),
|
||||
'Vom 14. bis 16. August 2020 findet im ausgewiesenen Naherholungsgebiet am Fuße des Harzes, dem Goslarer Ortsteil Vienenburg, das Seefest unter dem Motto „Feuer & Wasser“ statt.',
|
||||
'https://www.goslar.de/kultur-freizeit/veranstaltungen/156-vienenburger-seefest?layout=*',
|
||||
True)
|
||||
|
||||
upsert_event("Tausend Schritte durch die Altstadt",
|
||||
"Stadt Goslar",
|
||||
"Tourist-Information Goslar",
|
||||
create_berlin_date(2020, 9, 1, 10, 0),
|
||||
'Tausend Schritte durch die Altstadt Erleben Sie einen geführten Stadtrundgang durch den historischen Stadtkern. Lassen Sie sich von Fachwerkromantik und kaiserlichen Bauten inmitten der UNESCO-Welterbestätte verzaubern. ganzjährig (außer 01.01.) täglich 10:00 Uhr Treffpunkt: Tourist-Information am Marktplatz (Dauer ca. 2 Std.) Erwachsene 8,00 Euro Inhaber Gastkarte Goslar/Kurkarte Hahnenklee 7,00 Euro Schüler/Studenten 6,00 Euro')
|
||||
|
||||
upsert_event("Spaziergang am Nachmittag",
|
||||
"Stadt Goslar",
|
||||
"Tourist-Information Goslar",
|
||||
create_berlin_date(2020, 9, 1, 13, 30),
|
||||
'Spaziergang am Nachmittag Begeben Sie sich auf einen geführten Rundgang durch die historische Altstadt. Entdecken Sie malerische Fachwerkgassen und imposante Bauwerke bei einem Streifzug durch das UNESCO-Weltkulturerbe. April – Oktober und 25.11. – 30.12. Montag – Samstag 13:30 Uhr Treffpunkt: Tourist-Information am Marktplatz (Dauer ca. 1,5 Std.) Erwachsene 7,00 Euro Inhaber Gastkarte Goslar/Kurkarte Hahnenklee 6,00 Euro Schüler/Studenten 5,00 Euro')
|
||||
|
||||
upsert_event("Ein Blick hinter die Kulissen - Rathausbaustelle",
|
||||
"Stadt Goslar",
|
||||
"Nagelkopf am Rathaus",
|
||||
create_berlin_date(2020, 9, 3, 17, 0),
|
||||
'Allen interessierten Bürgern wird die Möglichkeit geboten, unter fachkundiger Führung des Goslarer Gebäudemanagement (GGM) einen Blick hinter die Kulissen durch das derzeit gesperrte historische Rathaus und die Baustelle Kulturmarktplatz zu werfen. Da bei beiden Führungen die Anzahl der Teilnehmer auf 16 Personen begrenzt ist, ist eine Anmeldung unbedingt notwendig sowie festes Schuhhwerk. Bitte melden Sie sich bei Interesse in der Tourist-Information (Tel. 05321-78060) an. Kinder unter 18 Jahren sind aus Sicherheitsgründen auf der Baustelle nicht zugelassen.')
|
||||
|
||||
upsert_event("Wöltingerode unter Dampf",
|
||||
"Kloster Wöltingerode",
|
||||
"Kloster Wöltingerode",
|
||||
create_berlin_date(2020, 9, 5, 12, 0),
|
||||
'Mit einem ländlichen Programm rund um historische Trecker und Landmaschinen, köstliche Produkte aus Brennerei und Region, einem Kunsthandwerkermarkt und besonderen Führungen findet das Hoffest auf dem Klostergut statt.',
|
||||
'https://www.woelti-unter-dampf.de')
|
||||
|
||||
upsert_event("Altstadtfest",
|
||||
"GOSLAR marketing gmbh",
|
||||
"Goslar",
|
||||
create_berlin_date(2020, 9, 11, 15, 0),
|
||||
'Drei Tage lang dürfen sich die Besucher des Goslarer Altstadtfestes auf ein unterhaltsames und abwechslungsreiches Veranstaltungsprogramm freuen. Der Flohmarkt auf der Kaiserpfalzwiese lädt zum Stöbern vor historischer Kulisse ein.',
|
||||
'https://www.goslar.de/kultur-freizeit/veranstaltungen/altstadtfest')
|
||||
|
||||
upsert_event("Adventsmarkt auf der Vienenburg",
|
||||
"Vienenburg",
|
||||
"Vienenburg",
|
||||
create_berlin_date(2020, 12, 13, 12, 0),
|
||||
'Inmitten der mittelalterlichen Burg mit dem restaurierten Burgfried findet der „Advent auf der Burg“ statt. Der Adventsmarkt wird von gemeinnützigen und sozialen Vereinen sowie den Kirchen ausgerichtet.')
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Views
|
||||
@app.route("/")
|
||||
def home():
|
||||
return render_template('home.html')
|
||||
admin_unit_members = AdminUnitMember.query.filter_by(user_id = current_user.id).all() if current_user.is_authenticated else None
|
||||
organization_members = OrgMember.query.filter_by(user_id = current_user.id).all() if current_user.is_authenticated else None
|
||||
return render_template('home.html',
|
||||
admin_unit_members=admin_unit_members,
|
||||
organization_members=organization_members)
|
||||
|
||||
@app.route("/admin_units")
|
||||
def admin_units():
|
||||
return render_template('admin_units.html',
|
||||
admin_units=AdminUnit.query.all())
|
||||
|
||||
@app.route('/admin_unit/<int:admin_unit_id>')
|
||||
def admin_unit(admin_unit_id):
|
||||
admin_unit = AdminUnit.query.filter_by(id = admin_unit_id).first()
|
||||
current_user_member = AdminUnitMember.query.with_parent(admin_unit).filter_by(user_id = current_user.id).first() if current_user.is_authenticated else None
|
||||
|
||||
return render_template('admin_unit.html',
|
||||
admin_unit=admin_unit,
|
||||
current_user_member=current_user_member,
|
||||
can_list_admin_unit_members=can_list_admin_unit_members(admin_unit))
|
||||
|
||||
@app.route("/organizations")
|
||||
def organizations():
|
||||
return render_template('organizations.html',
|
||||
organizations=Organization.query.all())
|
||||
|
||||
@app.route('/organization/<int:organization_id>')
|
||||
def organization(organization_id):
|
||||
organization = Organization.query.filter_by(id = organization_id).first()
|
||||
current_user_member = OrgMember.query.with_parent(organization).filter_by(user_id = current_user.id).first() if current_user.is_authenticated else None
|
||||
|
||||
return render_template('organization.html',
|
||||
organization=organization,
|
||||
current_user_member=current_user_member,
|
||||
can_list_members=can_list_org_members(organization))
|
||||
|
||||
@app.route("/profile")
|
||||
@auth_required()
|
||||
def profile():
|
||||
return render_template('profile.html')
|
||||
|
||||
@app.route("/events")
|
||||
def events():
|
||||
events = Event.query.all()
|
||||
return render_template('events.html', events=events)
|
||||
|
||||
@app.route('/event/<int:event_id>', methods=('GET', 'POST'))
|
||||
def event(event_id):
|
||||
event = Event.query.filter_by(id = event_id).first()
|
||||
user_can_verify_event = can_verify_event(event)
|
||||
|
||||
if user_can_verify_event and request.method == 'POST':
|
||||
action = request.form['action']
|
||||
if action == 'verify':
|
||||
event.verified = True
|
||||
elif action == 'unverify':
|
||||
event.verified = False
|
||||
db.session.commit()
|
||||
|
||||
return render_template('event.html', event=event, user_can_verify_event=user_can_verify_event)
|
||||
|
||||
from forms.create_event import CreateEventForm
|
||||
|
||||
@app.route("/events/create", methods=('GET', 'POST'))
|
||||
def create_event():
|
||||
form = CreateEventForm()
|
||||
if form.validate_on_submit():
|
||||
event = Event()
|
||||
form.populate_obj(event)
|
||||
event.admin_unit = get_admin_unit('Stadt Goslar')
|
||||
eventDate = EventDate(event_id = event.id, start=form.start.data)
|
||||
event.dates.append(eventDate)
|
||||
db.session.commit()
|
||||
return redirect(url_for('events'))
|
||||
return render_template('create_event.html', form=form)
|
||||
|
||||
@app.route("/admin")
|
||||
@roles_required("admin")
|
||||
def admin():
|
||||
@ -73,7 +415,8 @@ def admin():
|
||||
@app.route("/admin/admin_units")
|
||||
@roles_required("admin")
|
||||
def admin_admin_units():
|
||||
return render_template('admin/admin_units.html')
|
||||
return render_template('admin/admin_units.html',
|
||||
admin_units=AdminUnit.query.all())
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
0
forms/__init__.py
Normal file
0
forms/__init__.py
Normal file
13
forms/create_event.py
Normal file
13
forms/create_event.py
Normal file
@ -0,0 +1,13 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, SubmitField, TextAreaField
|
||||
from wtforms.fields.html5 import DateTimeLocalField
|
||||
from wtforms.validators import DataRequired, Optional
|
||||
|
||||
class CreateEventForm(FlaskForm):
|
||||
submit = SubmitField("Create event")
|
||||
host = StringField('Host', validators=[DataRequired()])
|
||||
name = StringField('Name', validators=[DataRequired()])
|
||||
description = TextAreaField('Description', validators=[DataRequired()])
|
||||
start = DateTimeLocalField('Start', format='%Y-%m-%dT%H:%M', validators=[DataRequired()])
|
||||
location = StringField('Location', validators=[DataRequired()])
|
||||
external_link = StringField('Link URL', validators=[Optional()])
|
||||
@ -1,4 +1,4 @@
|
||||
from flask_script import Manager
|
||||
from flask_script import Manager, Command
|
||||
from flask_migrate import Migrate, MigrateCommand
|
||||
from app import app, db
|
||||
|
||||
|
||||
142
migrations/versions/a2ba246b23d0_.py
Normal file
142
migrations/versions/a2ba246b23d0_.py
Normal file
@ -0,0 +1,142 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: a2ba246b23d0
|
||||
Revises: 36945eb387b4
|
||||
Create Date: 2020-06-14 19:39:43.809236
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a2ba246b23d0'
|
||||
down_revision = '36945eb387b4'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('adminunit',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('adminunitmemberrole',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=80), nullable=True),
|
||||
sa.Column('description', sa.String(length=255), nullable=True),
|
||||
sa.Column('permissions', sa.UnicodeText(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('adminunitorgrole',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=80), nullable=True),
|
||||
sa.Column('description', sa.String(length=255), nullable=True),
|
||||
sa.Column('permissions', sa.UnicodeText(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('organization',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('orgmemberrole',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=80), nullable=True),
|
||||
sa.Column('description', sa.String(length=255), nullable=True),
|
||||
sa.Column('permissions', sa.UnicodeText(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('adminunitmember',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('admin_unit_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('adminunitorg',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('admin_unit_id', sa.Integer(), nullable=False),
|
||||
sa.Column('organization_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
|
||||
sa.ForeignKeyConstraint(['organization_id'], ['organization.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('event',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('admin_unit_id', sa.Integer(), nullable=False),
|
||||
sa.Column('host', sa.Unicode(length=255), nullable=False),
|
||||
sa.Column('external_link', sa.String(length=255), nullable=True),
|
||||
sa.Column('location', sa.Unicode(length=255), nullable=False),
|
||||
sa.Column('name', sa.Unicode(length=255), nullable=False),
|
||||
sa.Column('description', sa.UnicodeText(), nullable=False),
|
||||
sa.Column('verified', sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('orgmember',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('organization_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['organization_id'], ['organization.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('adminunitmemberroles_members',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('member_id', sa.Integer(), nullable=True),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['member_id'], ['adminunitmember.id'], ),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['adminunitmemberrole.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('adminunitorgroles_organizations',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('admin_unit_org_id', sa.Integer(), nullable=True),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['admin_unit_org_id'], ['adminunitorg.id'], ),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['adminunitorgrole.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('eventdate',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('event_id', sa.Integer(), nullable=False),
|
||||
sa.Column('start', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['event_id'], ['event.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('orgmemberroles_members',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('member_id', sa.Integer(), nullable=True),
|
||||
sa.Column('role_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['member_id'], ['orgmember.id'], ),
|
||||
sa.ForeignKeyConstraint(['role_id'], ['orgmemberrole.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('orgmemberroles_members')
|
||||
op.drop_table('eventdate')
|
||||
op.drop_table('adminunitorgroles_organizations')
|
||||
op.drop_table('adminunitmemberroles_members')
|
||||
op.drop_table('orgmember')
|
||||
op.drop_table('event')
|
||||
op.drop_table('adminunitorg')
|
||||
op.drop_table('adminunitmember')
|
||||
op.drop_table('orgmemberrole')
|
||||
op.drop_table('organization')
|
||||
op.drop_table('adminunitorgrole')
|
||||
op.drop_table('adminunitmemberrole')
|
||||
op.drop_table('adminunit')
|
||||
# ### end Alembic commands ###
|
||||
119
models.py
119
models.py
@ -1,8 +1,10 @@
|
||||
from app import db
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
from sqlalchemy import Boolean, DateTime, Column, Integer, String, ForeignKey, UnicodeText
|
||||
from sqlalchemy import Boolean, DateTime, Column, Integer, String, ForeignKey, Unicode, UnicodeText
|
||||
from flask_security import UserMixin, RoleMixin
|
||||
|
||||
### User
|
||||
|
||||
class RolesUsers(db.Model):
|
||||
__tablename__ = 'roles_users'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
@ -31,4 +33,117 @@ class User(db.Model, UserMixin):
|
||||
fs_uniquifier = Column(String(255))
|
||||
confirmed_at = Column(DateTime())
|
||||
roles = relationship('Role', secondary='roles_users',
|
||||
backref=backref('users', lazy='dynamic'))
|
||||
backref=backref('users', lazy='dynamic'))
|
||||
|
||||
### Organization
|
||||
|
||||
class OrgMemberRolesMembers(db.Model):
|
||||
__tablename__ = 'orgmemberroles_members'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
member_id = Column('member_id', Integer(), ForeignKey('orgmember.id'))
|
||||
role_id = Column('role_id', Integer(), ForeignKey('orgmemberrole.id'))
|
||||
|
||||
class OrgMemberRole(db.Model, RoleMixin):
|
||||
__tablename__ = 'orgmemberrole'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
name = Column(String(80), unique=True)
|
||||
description = Column(String(255))
|
||||
permissions = Column(UnicodeText())
|
||||
|
||||
class OrgMember(db.Model):
|
||||
__tablename__ = 'orgmember'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
organization_id = db.Column(db.Integer, db.ForeignKey('organization.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
user = db.relationship('User', backref=db.backref('orgmembers', lazy=True))
|
||||
|
||||
roles = relationship('OrgMemberRole', secondary='orgmemberroles_members',
|
||||
backref=backref('members', lazy='dynamic'))
|
||||
|
||||
class Organization(db.Model):
|
||||
__tablename__ = 'organization'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
name = Column(String(255), unique=True)
|
||||
members = relationship('OrgMember', backref=backref('organization', lazy=True))
|
||||
|
||||
### Admin Unit
|
||||
|
||||
class AdminUnitMemberRolesMembers(db.Model):
|
||||
__tablename__ = 'adminunitmemberroles_members'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
member_id = Column('member_id', Integer(), ForeignKey('adminunitmember.id'))
|
||||
role_id = Column('role_id', Integer(), ForeignKey('adminunitmemberrole.id'))
|
||||
|
||||
class AdminUnitMemberRole(db.Model, RoleMixin):
|
||||
__tablename__ = 'adminunitmemberrole'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
name = Column(String(80), unique=True)
|
||||
description = Column(String(255))
|
||||
permissions = Column(UnicodeText())
|
||||
|
||||
class AdminUnitMember(db.Model):
|
||||
__tablename__ = 'adminunitmember'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
admin_unit_id = db.Column(db.Integer, db.ForeignKey('adminunit.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
user = db.relationship('User', backref=db.backref('adminunitmembers', lazy=True))
|
||||
roles = relationship('AdminUnitMemberRole', secondary='adminunitmemberroles_members',
|
||||
backref=backref('members', lazy='dynamic'))
|
||||
|
||||
class AdminUnitOrgRoleOrganizations(db.Model):
|
||||
__tablename__ = 'adminunitorgroles_organizations'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
admin_unit_org_id = Column('admin_unit_org_id', Integer(), ForeignKey('adminunitorg.id'))
|
||||
role_id = Column('role_id', Integer(), ForeignKey('adminunitorgrole.id'))
|
||||
|
||||
class AdminUnitOrgRole(db.Model, RoleMixin):
|
||||
__tablename__ = 'adminunitorgrole'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
name = Column(String(80), unique=True)
|
||||
description = Column(String(255))
|
||||
permissions = Column(UnicodeText())
|
||||
|
||||
class AdminUnitOrg(db.Model):
|
||||
__tablename__ = 'adminunitorg'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
admin_unit_id = db.Column(db.Integer, db.ForeignKey('adminunit.id'), nullable=False)
|
||||
organization_id = db.Column(db.Integer, db.ForeignKey('organization.id'), nullable=False)
|
||||
organization = db.relationship('Organization', backref=db.backref('adminunitorgs', lazy=True))
|
||||
roles = relationship('AdminUnitOrgRole', secondary='adminunitorgroles_organizations',
|
||||
backref=backref('organizations', lazy='dynamic'))
|
||||
|
||||
class AdminUnit(db.Model):
|
||||
__tablename__ = 'adminunit'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
name = Column(String(255), unique=True)
|
||||
members = relationship('AdminUnitMember', backref=backref('adminunit', lazy=True))
|
||||
organizations = relationship('AdminUnitOrg', backref=backref('adminunit', lazy=True))
|
||||
|
||||
# Events
|
||||
class Event(db.Model):
|
||||
__tablename__ = 'event'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
admin_unit_id = db.Column(db.Integer, db.ForeignKey('adminunit.id'), nullable=False)
|
||||
admin_unit = db.relationship('AdminUnit', backref=db.backref('events', lazy=True))
|
||||
host = Column(Unicode(255), nullable=False) # org|adminunit|string
|
||||
#co_hosts: -"-
|
||||
#format: real|online
|
||||
external_link = Column(String(255))
|
||||
#ticket_link = Column(String(255))
|
||||
location = Column(Unicode(255), nullable=False) # place|address
|
||||
dates = relationship('EventDate', backref=backref('event', lazy=False))
|
||||
name = Column(Unicode(255), nullable=False)
|
||||
description = Column(UnicodeText(), nullable=False)
|
||||
#photo: image(1200x628)
|
||||
#category: relationship, nullable=False
|
||||
#keywords = Column(String(255)) oder liste?
|
||||
#kid_friendly: bool
|
||||
verified = Column(Boolean())
|
||||
|
||||
# (Multiple Events möglich, wiederholend oder frei, dann aber mit endzeit)
|
||||
class EventDate(db.Model):
|
||||
__tablename__ = 'eventdate'
|
||||
id = Column(Integer(), primary_key=True)
|
||||
event_id = db.Column(db.Integer, db.ForeignKey('event.id'), nullable=False)
|
||||
start = db.Column(db.DateTime(timezone=True), nullable=False)
|
||||
#end: date_time
|
||||
@ -1,7 +1,7 @@
|
||||
|
||||
h1 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 1.8rem;
|
||||
font-size: 1.6rem;
|
||||
margin: 1rem 0 2rem;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
@ -40,4 +40,40 @@ footer {
|
||||
/* background-color: #009688;
|
||||
color: white; */
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
tr.collapse.in {
|
||||
display:table-row;
|
||||
}
|
||||
|
||||
tr.table-borderless td {
|
||||
border:0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
tr.table-line-through td {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.text-toogle[aria-expanded=false] .text-expanded {
|
||||
display: none;
|
||||
}
|
||||
.text-toogle[aria-expanded=true] .text-collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table td.fit,
|
||||
.table th.fit {
|
||||
white-space: nowrap;
|
||||
width: 1%;
|
||||
}
|
||||
|
||||
.ui-autocomplete {
|
||||
z-index: 1025 !important;
|
||||
}
|
||||
|
||||
@media (max-width: 320px) {
|
||||
td, th {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,10 @@
|
||||
</nav>
|
||||
|
||||
<div class="list-group">
|
||||
<a href="{{ url_for('admin_admin_units') }}" class="list-group-item list-group-item-action">{{ _('Admin Units') }}</a>
|
||||
<a href="{{ url_for('admin_admin_units') }}" class="list-group-item list-group-item-action">
|
||||
{{ _('Admin Units') }}
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -11,5 +11,21 @@
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for admin_unit in admin_units %}
|
||||
<tr>
|
||||
<td>{{ admin_unit.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
58
templates/admin_unit.html
Normal file
58
templates/admin_unit.html
Normal file
@ -0,0 +1,58 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}
|
||||
{{ admin_unit.name }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ admin_unit.name }}</h1>
|
||||
|
||||
{% if current_user_member %}
|
||||
<div class="my-4">
|
||||
{{ _('You are a member of this admin unit.') }}
|
||||
({% for role in current_user_member.roles %}{{ role.name }}{%if not loop.last %}, {% endif %}{% endfor %})
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if can_list_admin_unit_members %}
|
||||
<p class="h5 mt-5">{{ _('Members') }}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Roles') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in admin_unit.members %}
|
||||
<tr>
|
||||
<td>{{ member.user.email }}</td>
|
||||
<td>{% for role in member.roles %}{{ role.name }}{%if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<p class="h5 mt-5">{{ _('Organizations') }}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Roles') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for admin_unit_org in admin_unit.organizations %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('organization', organization_id=admin_unit_org.organization_id) }}">{{ admin_unit_org.organization.name }}</a></td>
|
||||
<td>{% for role in admin_unit_org.roles %}{{ role.name }}{%if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
26
templates/admin_units.html
Normal file
26
templates/admin_units.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}
|
||||
{{ _('Admin Units') }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ _('Admin Units') }}</h1>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for admin_unit in admin_units %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('admin_unit', admin_unit_id=admin_unit.id) }}">{{ admin_unit.name }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
18
templates/create_event.html
Normal file
18
templates/create_event.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "_macros.html" import render_field_with_errors, render_field %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ _('Create event') }}</h1>
|
||||
<form action="{{ url_for('create_event') }}" method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ render_field_with_errors(form.host) }}
|
||||
{{ render_field_with_errors(form.name) }}
|
||||
{{ render_field_with_errors(form.description) }}
|
||||
{{ render_field_with_errors(form.start) }}
|
||||
{{ render_field_with_errors(form.location) }}
|
||||
{{ render_field_with_errors(form.external_link) }}
|
||||
{{ render_field(form.submit) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
44
templates/event.html
Normal file
44
templates/event.html
Normal file
@ -0,0 +1,44 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}
|
||||
{{ event.name }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ event.name }}</h1>
|
||||
|
||||
{% if user_can_verify_event %}
|
||||
<div class="my-4">
|
||||
<form action="{{ url_for('event', event_id=event.id) }}" method="POST">
|
||||
{% if event.verified %}
|
||||
<input name="action" type="hidden" value="unverify">
|
||||
<p><input class="btn btn-danger" name="submit" type="submit" value="{{ _('Mark event as unverified') }}"></p>
|
||||
{% else %}
|
||||
<input name="action" type="hidden" value="verify">
|
||||
<p><input class="btn btn-success" name="submit" type="submit" value="{{ _('Mark event as verified') }}"></p>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="my-4">
|
||||
<div><i class="fa fa-fw fa-calendar" data-toggle="tooltip" title="{{ _('Date') }}"></i> {{ event.dates[0].start | datetimeformat }}</div>
|
||||
<div><i class="fa fa-fw fa-map-marker" data-toggle="tooltip" title="{{ _('Location') }}"></i> {{ event.location }}</div>
|
||||
{% if event.verified %}
|
||||
<div><i class="fa fa-fw fa-check-circle text-success" data-toggle="tooltip" title="{{ _('Verified') }}"></i> {{ _('Verified') }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="my-4">{{ event.description }}</div>
|
||||
|
||||
{% if event.external_link %}
|
||||
<div class="my-4">
|
||||
<i class="fa fa-fw fa-external-link" data-toggle="tooltip" title="{{ _('Link') }}"></i>
|
||||
<a href="{{ event.external_link }}">{{ event.external_link }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="my-4">
|
||||
<div><i class="fa fa-fw fa-users" data-toggle="tooltip" title="{{ _('Host') }}"></i> {{ event.host }}</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
41
templates/events.html
Normal file
41
templates/events.html
Normal file
@ -0,0 +1,41 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}
|
||||
{{ _('Events') }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ _('Events') }}</h1>
|
||||
|
||||
<div class="my-4">
|
||||
<a class="btn btn-primary" href="{{ url_for('create_event') }}" role="button"><i class="fa fa-plus"></i> {{ _('Create event') }}</a>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Date') }}</th>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Host') }}</th>
|
||||
<th>{{ _('Location') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for event in events %}
|
||||
<tr>
|
||||
<td>{{ event.dates[0].start | datetimeformat }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('event', event_id=event.id) }}">{{ event.name }}</a>
|
||||
{% if event.verified %}
|
||||
<i class="fa fa-check-circle text-success" data-toggle="tooltip" title="{{ _('Verified') }}"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ event.host }}</td>
|
||||
<td>{{ event.location }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -4,6 +4,58 @@ Prototyp
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{{ _('Hi there!') }}
|
||||
<h1>{{ _('Hi there!') }}</h1>
|
||||
|
||||
<!--
|
||||
<div class="list-group">
|
||||
<a href="{{ url_for('events') }}" class="list-group-item list-group-item-action">
|
||||
{{ _('Events') }}
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</a>
|
||||
</div>-->
|
||||
|
||||
{% if admin_unit_members %}
|
||||
<p class="h5 mt-5">{{ _('Your Admin Units') }}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Roles') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in admin_unit_members %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('admin_unit', admin_unit_id=member.adminunit.id) }}">{{ member.adminunit.name }}</a></td>
|
||||
<td>{% for role in member.roles %}{{ role.name }}{%if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if organization_members %}
|
||||
<p class="h5 mt-5">{{ _('Your Organizations') }}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Roles') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in organization_members %}
|
||||
<tr>
|
||||
<td>{{ member.organization.name }}</td>
|
||||
<td>{% for role in member.roles %}{{ role.name }}{%if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@ -27,6 +27,10 @@
|
||||
|
||||
<script>
|
||||
moment.locale('de')
|
||||
|
||||
$( function() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
})
|
||||
</script>
|
||||
|
||||
{% block header %}
|
||||
@ -36,7 +40,7 @@
|
||||
<body{% block body_attribs %}{% endblock body_attribs %}>
|
||||
{% block body -%}
|
||||
{% block navbar %}
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<a class="navbar-brand" href="{{ url_for('home') }}">
|
||||
Prototyp
|
||||
</a>
|
||||
@ -44,15 +48,18 @@
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||
<div class="navbar-nav">
|
||||
|
||||
<div class="navbar-nav mr-auto">
|
||||
<a class="nav-item nav-link" href="{{ url_for('admin_units') }}">{{ _('Admin Units') }}</a>
|
||||
<a class="nav-item nav-link" href="{{ url_for('organizations') }}">{{ _('Organizations') }}</a>
|
||||
<a class="nav-item nav-link" href="{{ url_for('events') }}">{{ _('Events') }}</a>
|
||||
</div>
|
||||
<div class="navbar-nav navbar-right">
|
||||
{% if current_user.is_authenticated %}
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{ _('You') }}
|
||||
{{ current_user.email }}
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<h6 class="dropdown-header">{{ current_user.email }}</h6>
|
||||
<a class="dropdown-item" href="{{ url_for('profile') }}">{{ _('Profile') }}</a>
|
||||
|
||||
{% if current_user.has_role('admin') %}
|
||||
|
||||
38
templates/organization.html
Normal file
38
templates/organization.html
Normal file
@ -0,0 +1,38 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}
|
||||
{{ organization.name }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ organization.name }}</h1>
|
||||
|
||||
{% if current_user_member %}
|
||||
<div class="my-4">
|
||||
{{ _('You are a member of this organization.') }}
|
||||
({% for role in current_user_member.roles %}{{ role.name }}{%if not loop.last %}, {% endif %}{% endfor %})
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if can_list_members %}
|
||||
<p class="h5 mt-5">{{ _('Members') }}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Roles') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in organization.members %}
|
||||
<tr>
|
||||
<td>{{ member.user.email }}</td>
|
||||
<td>{% for role in member.roles %}{{ role.name }}{%if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
26
templates/organizations.html
Normal file
26
templates/organizations.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}
|
||||
{{ _('Organizations') }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ _('Organizations') }}</h1>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for organization in organizations %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('organization', organization_id=organization.id) }}">{{ organization.name }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -4,6 +4,50 @@
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{{ current_user.email }}
|
||||
<h1>{{ current_user.email }}</h1>
|
||||
|
||||
{% if admin_unit_members %}
|
||||
<p class="h5 mt-5">{{ _('Admin Units') }}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Roles') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in admin_unit_members %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('admin_unit', admin_unit_id=member.adminunit.id) }}">{{ member.adminunit.name }}</a></td>
|
||||
<td>{% for role in member.roles %}{{ role.name }}{%if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if organization_members %}
|
||||
<p class="h5 mt-5">{{ _('Organizations') }}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Roles') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in organization_members %}
|
||||
<tr>
|
||||
<td>{{ member.organization.name }}</td>
|
||||
<td>{% for role in member.roles %}{{ role.name }}{%if not loop.last %}, {% endif %}{% endfor %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
Loading…
x
Reference in New Issue
Block a user