Photos und recurring events

This commit is contained in:
Daniel Grams 2020-06-25 08:20:02 +02:00
parent 37859688cc
commit 7317453576
13 changed files with 2033 additions and 36 deletions

62
app.py
View File

@ -11,6 +11,7 @@ from flask_principal import Permission
from datetime import datetime
import pytz
from urllib.parse import quote_plus
from dateutil.rrule import rrulestr, rruleset, rrule
# Create app
app = Flask(__name__)
@ -44,6 +45,10 @@ from models import EventCategory, Image, EventSuggestion, EventSuggestionDate, O
user_datastore = SQLAlchemySessionUserDatastore(db.session, User, Role)
security = Security(app, user_datastore)
berlin_tz = pytz.timezone('Europe/Berlin')
now = datetime.now(tz=berlin_tz)
today = datetime(now.year, now.month, now.day, tzinfo=now.tzinfo)
@babel.localeselector
def get_locale():
return request.accept_languages.best_match(app.config['LANGUAGES'])
@ -207,7 +212,7 @@ def upsert_organization(org_name, street = None, postalCode = None, city = None,
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))
return berlin_tz.localize(datetime(year, month, day, hour=hour, minute=minute))
def upsert_actor_for_admin_unit(admin_unit_id):
result = Actor.query.filter_by(admin_unit_id = admin_unit_id).first()
@ -296,11 +301,16 @@ def upsert_event_category(category_name):
return result
def upsert_event(event_name, host, location_name, start, description, link = None, verified = False, admin_unit = None, ticket_link=None, photo_res=None, category=None):
def upsert_event(event_name, host, location_name, start, description, link = None, verified = False, admin_unit = None, ticket_link=None, photo_res=None, category=None, recurrence_rule=None):
if admin_unit is None:
admin_unit = get_admin_unit('Stadt Goslar')
place = upsert_place(location_name)
if category is not None:
category_object = upsert_event_category(category)
else:
category_object = upsert_event_category('Other')
result = Event.query.filter_by(name = event_name).first()
if result is None:
result = Event()
@ -314,19 +324,25 @@ def upsert_event(event_name, host, location_name, start, description, link = Non
result.host = host
result.place = place
result.ticket_link = ticket_link
result.category = category_object
eventDate = EventDate(event_id = result.id, start=start)
result.dates = []
result.dates.append(eventDate)
if recurrence_rule is not None:
result.recurrence_rule = recurrence_rule
start_wo_tz = start.replace(tzinfo=None)
rule_set = rrulestr(recurrence_rule, forceset=True, dtstart=start_wo_tz)
for rule_date in list(rule_set):
rule_data_w_tz = berlin_tz.localize(rule_date)
eventDate = EventDate(event_id = result.id, start=rule_data_w_tz)
result.dates.append(eventDate)
else:
eventDate = EventDate(event_id = result.id, start=start)
result.dates.append(eventDate)
if photo_res is not None:
result.photo = upsert_image_with_res(result.photo, photo_res)
if category is not None:
result.category = upsert_event_category(category)
else:
result.category = upsert_event_category('Other')
return result
def get_event_hosts():
@ -625,6 +641,7 @@ def create_user():
upsert_event_category('Fitness')
upsert_event_category('Sports')
upsert_event_category('Other')
db.session.commit()
# Admin units
goslar = upsert_admin_unit('Stadt Goslar')
@ -643,7 +660,7 @@ def create_user():
# Organizations
admin_unit_org_event_verifier_role = upsert_admin_unit_org_role('event_verifier', ['event:verify', "event:create"])
gmg = upsert_organization("GOSLAR marketing gmbh")
gmg = upsert_organization("GOSLAR marketing gmbh", "Markt 7", "38640", "Goslar", url='https://www.goslar.de/kontakt', logo_res="gmg.jpeg")
gz = upsert_organization("Goslarsche Zeitung")
celtic_inn = upsert_organization("Celtic Inn")
kloster_woelteringerode = upsert_organization("Kloster Wöltingerode")
@ -748,13 +765,13 @@ def create_user():
upsert_place('Mehrzweckhalle Hahndorf')
upsert_place('Sportplatz Hahndorf')
upsert_place('St. Kilian Hahndorf')
upsert_place("Tourist-Information Goslar", 'Markt 7', '38644', 'Goslar', 51.906172, 10.429346)
upsert_place("Tourist-Information Goslar", 'Markt 7', '38644', 'Goslar', 51.906172, 10.429346, 'http://www.goslar.de/kontakt', "Zentral am Marktplatz gelegen, ist die Tourist-Information die erste Anlaufstelle für Goslar-Besucher. 14 motivierte und serviceorientierte Mitarbeiter sorgen dafür, dass sich der Gast rundherum wohl fühlt. Die ständige Qualitätsverbesserung und der flexible Umgang mit den Kundenwünschen sind Teil unserer Leitsätze.", 'touristinfo.jpeg')
upsert_place("Nagelkopf am Rathaus Goslar", 'Marktkirchhof 3', '38640', 'Goslar', 51.9055939, 10.4263286)
upsert_place("Kloster Wöltingerode", 'Wöltingerode 3', '38690', 'Goslar', 51.9591156, 10.5371815)
upsert_place("Marktplatz Goslar", 'Markt 6', '38640', 'Goslar', 51.9063601, 10.4249433)
upsert_place("Burg Vienenburg", 'Burgweg 2', '38690', 'Goslar', 51.9476558, 10.5617368)
upsert_place("Kurhaus Bad Harzburg", 'Kurhausstraße 11', '38667', 'Bad Harzburg', 51.8758165, 10.5593392)
upsert_place("Goslarsche Höfe", 'Okerstraße 32', '38640', 'Goslar', 51.911571, 10.4391331, 'https://www.goslarsche-hoefe.de/', 'Dir Rosserei', photo_res="schlosserei.jpeg")
upsert_place("Goslarsche Höfe", 'Okerstraße 32', '38640', 'Goslar', 51.911571, 10.4391331, 'https://www.goslarsche-hoefe.de/')
upsert_place("Schlosserei im Rammelsberg", 'Bergtal 19', '38640', 'Goslar', 51.890527, 10.418880, 'http://www.rammelsberg.de/', 'Die "Schlosserei" ist erprobter Veranstaltungsort und bietet Platz für ca. 700 Besucher. Das Ambiente ist technisch gut ausgestattet und flexibel genug, für jeden Künstler individuell wandelbar zu sein. Dabei lebt nicht nur der Veranstaltungsraum, es wirkt der gesamte Komplex des Rammelsberges und macht den Besuch zu einem unvergeßlichen Erlebnis.', photo_res="schlosserei.jpeg")
# Org or admins
@ -804,7 +821,6 @@ def create_user():
add_role_to_org_member(grzno_miners_rock_member, org_member_event_creator_role)
# Events
berlin = pytz.timezone('Europe/Berlin')
upsert_event("Vienenburger Seefest",
goslar_ooa,
"Vienenburger See",
@ -814,16 +830,20 @@ def create_user():
True)
upsert_event("Tausend Schritte durch die Altstadt",
goslar_ooa,
gmg_ooa,
"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')
create_berlin_date(2020, 1, 2, 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',
photo_res="tausend.jpeg",
recurrence_rule="FREQ=DAILY;UNTIL=20201231T235959")
upsert_event("Spaziergang am Nachmittag",
goslar_ooa,
gmg_ooa,
"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')
create_berlin_date(2020, 4, 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',
photo_res="nachmittag.jpeg",
recurrence_rule="""RRULE:FREQ=WEEKLY;UNTIL=20201031T235959;BYDAY=MO,TU,WE,TH,FR,SA""")
upsert_event("Ein Blick hinter die Kulissen - Rathausbaustelle",
goslar_ooa,
@ -946,9 +966,9 @@ def place(place_id):
@app.route("/events")
def events():
events = Event.query.all()
dates = EventDate.query.filter(EventDate.start >= today).order_by(EventDate.start).all()
return render_template('events.html',
events=events,
dates=dates,
user_can_create_event=can_create_event(),
user_can_list_event_suggestion=can_list_event_suggestion())

View File

@ -1,8 +1,8 @@
"""empty message
Revision ID: 4c52ae230b29
Revision ID: bbad7e33a780
Revises:
Create Date: 2020-06-23 14:55:52.652970
Create Date: 2020-06-24 21:17:25.548159
"""
from alembic import op
@ -10,7 +10,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '4c52ae230b29'
revision = 'bbad7e33a780'
down_revision = None
branch_labels = None
depends_on = None
@ -241,7 +241,8 @@ def upgrade():
sa.Column('ticket_link', sa.String(length=255), nullable=True),
sa.Column('verified', sa.Boolean(), nullable=True),
sa.Column('photo_id', sa.Integer(), nullable=True),
sa.Column('category_id', sa.Integer(), nullable=True),
sa.Column('category_id', sa.Integer(), nullable=False),
sa.Column('recurrence_rule', sa.UnicodeText(), nullable=True),
sa.Column('created_by_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['admin_unit_id'], ['adminunit.id'], ),
sa.ForeignKeyConstraint(['category_id'], ['eventcategory.id'], ),

View File

@ -244,6 +244,7 @@ class Event(db.Model, TrackableMixin):
category_id = db.Column(db.Integer, db.ForeignKey('eventcategory.id'), nullable=False)
category = relationship('EventCategory', uselist=False)
recurrence_rule = Column(UnicodeText())
dates = relationship('EventDate', backref=backref('event', lazy=False), cascade="all, delete-orphan")
# wiederkehrende Dates sind zeitlich eingeschränkt
# beim event müsste man dann auch nochmal start_time (nullable=False) und end_time machen.

BIN
static/img/gmg.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
static/img/nachmittag.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
static/img/tausend.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
static/img/touristinfo.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

View File

@ -0,0 +1,226 @@
div.riform {
padding: 1em;
background-color: white;
box-shadow: 0 0 3em 0.5em #666;
line-height: 2;
-moz-box-shadow: 0 0 3em 0.5em #666;
-webkit-box-shadow: 0 0 3em #666;
}
div.riform h1 {
color: #888888;
border-bottom: 1px solid #DDDDDD;
font-size: 20px;
line-height: 1;
margin: 0;
padding-bottom: 5px;
padding-left: 5px;
}
div.riform form {
margin-bottom: 0;
}
div.riform .rifield {
clear: both;
}
div.riform .rifield .field {
float:left;
clear: none;
}
div.riform .label {
display: block;
float: left;
font-weight: bold;
margin-right: 10px;
text-align: right;
width: 130px;
}
div.riform #rirtemplate {
margin-top: 6px;
}
div#riformfields {
min-height: 11em;
min-width: 25em;
}
div.riform #rirangeoptions input,
div.riform #rimonthlyoptions input,
div.riform #riyearlyoptions input {
margin: 0;
}
div.riform #riweeklyweekdays .riweeklyweekday input {
display:block;
margin: 8px auto 0;
}
div.riform #riweeklyweekdays .riweeklyweekday label {
display:block;
}
div.riform #riweeklyweekdays .riweeklyweekday {
margin-right: 15px;
float: left;
}
div.riform input.ricancelbutton {
// pb_close.png
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAABHVBMVEUAAAAAAAADAwMEBAQFBQUGBgYJCQkKCgoPDw8RERETExMWFhYbGxscHBwfHx8iIiIlJSUoKCgsLCwyMjI1NTU4ODg7OztDQ0NGRkZKSkpLS0tNTU1XV1ddXV1gYGBjY2NkZGRmZmZoaGhsbGxvb291dXV7e3t+fn6BgYGCgoKFhYWLi4uMjIyNjY2Ojo6QkJCTk5OVlZWWlpaXl5eZmZmdnZ2fn5+qqqqurq6vr6+ysrKzs7O5ubnCwsLFxcXHx8fJycnLy8vMzMzR0dHV1dXY2Nja2trc3Nzd3d3f39/g4ODh4eHk5OTl5eXo6Ojr6+vs7Ozt7e3u7u7x8fHy8vLz8/P19fX29vb39/f4+Pj6+vr7+/v8/Pz9/f3////kwcJJAAAAAXRSTlMAQObYZgAAAThJREFUKBXNwWdb2gAYhtH3CVbQFisVlOLWVlHcdeBedXSQqoiR4f3/f4YJF8Gg9psfPMfezL8avvqNvaaCz/2Nr2wvPFBbz8qXXrynZs/AXp9aUidgHRpsOmpz5sEibthzFLVGxZ5wl5Q0MCjfx/G49KGEtf1iRdIXyEufS6xLmuTKQh4ZSX0e5PtLUJDU28BC3CqQ8+AWio58p1iIczXlqkDRUWCHH9bCTzWlroA5NW2zai1cK5ByoQF5BY74bi01kpISLhSHPZiW5JSx0CXfJGWg6CjnsSUpi2uhJdy4pMlpR1I6n5S0z661lSiowxgVezIBY4oYrLNqEWswq7bRKgcW1bMCZyNxBbKHcCDrkJj6C9XTjY3jMlwvyJ6Ja/mCpj+bXbKXupUYmpn5+kmy/4jFFLP34hGuw0GxTwkuWgAAAABJRU5ErkJggg==);
background-color: transparent;
font-size: 0; /* For IE8 */
color: transparent;
border: none;
position: absolute;
left: -14px;
top: -14px;
cursor: pointer;
height: 30px;
width: 30px;
}
div.rioccurrencesactions .riaddoccurrence #adddate {
width: 75%;
}
div.rioccurrencesactions .rioccurancesheader {
border-bottom: 1px solid #DDDDDD;
line-height: 1.5;
clear: both;
margin-top: 30px;
}
div.rioccurrencesactions .rioccurancesheader h2 {
color: #888888;
display: inline;
font-size: 18px;
font-weight: bold;
margin: 0px 0px 5px 5px;
}
div.rioccurrences div.batching {
font-size: 70%;
text-align: center;
}
div.rioccurrences span.current {
font-weight: bold;
}
div.riform span.action a {
height: 19px;
width: 19px;
overflow: hidden;
float: right;
text-indent: 9999px;
}
div.rioccurrences .occurrence {
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
}
div.rioccurrences .occurrence:hover {
border-top: 1px solid #DDDDDD;
border-bottom: 1px solid #DDDDDD;
}
div.rioccurrences .occurrence.start span.rlabel,
div.rioccurrences .occurrence.rdate span.rlabel {
color: #9CBA9B;
margin: 0 5px;
font-size: 70%;
font-weight: bold;
}
div.rioccurrences .occurrence.exdate {
opacity:0.4;
filter:alpha(opacity=40);
}
div.ridisplay .occurrence.exdate {
display: none;
}
div.ridisplay label.ridisplay {
font-weight: 300;
}
div.ridisplay .rimain a {
margin-right: 0.5em;
}
div.rioccurrences .occurrence.rdate {
background: #FFFFE0;
}
div.rioccurrences div.occurrence {
margin-left: 5px;
}
div.rioccurrences a.rrule,
div.rioccurrences a.rdate,
div.rioccurrences a.exdate {
color: transparent;
margin-top: 6px;
margin-right: 5px;
}
div.rioccurrences a.rrule,
div.rioccurrences a.rdate {
// delete.png
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAMAAABFjsb+AAAAdVBMVEUAAAAAAAACAgIHBwcICAgODg4PDw8QEBASEhIXFxcbGxsdHR0fHx8gICAnJyc2NjY3Nzc4ODg5OTk+Pj5ERERISEhJSUlSUlJYWFhqampvb2/Hx8fPz8/R0dHn5+fs7Ozv7+/x8fHy8vL39/f9/f3+/v7///8jaUCMAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAOkAAADpAVSSFEsAAAB9SURBVBjTY2BQQwcMDGqyYuxsCMAuJqvGoCbCiApEgGIsECaTEBOEwQIUYwbSbAKCUmqSggKsQDYzVEwGarw0kpiwKlhIhR9JjEVNXFlMWUKNEUWMU4FZgYfGYrxKHEp8KGJM8opqQCgHEwP7l1sUBLhg/sUWLljCD0s4AwBmjBYPljOv7QAAAABJRU5ErkJggg==);
}
div.rioccurrences a.exdate {
// undelete.png
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAMAAABFjsb+AAAAnFBMVEUCodIAAAABAQEVAAAHBwcICAgKCgoPDw8QEBBTAABVAABZAABaAAAXFxdcBwdhCgodHR2TAAAhISFlDw9nDw8vLy8wMDAxMTH4AAD5AQH6AgL+Bgb/BweDKipBQUFERERJSUlQUFBXV1daWlpmZmb0nZ31nZ36o6P/pqb/qanNzc3R0dHp6ens7Ozt7e3v7+/y8vL39/f8/Pz////TudgfAAAAAXRSTlMAQObYZgAAAKFJREFUGFdt0FsXQkAUhuHZIYfIITTVFJVEiNr//781xoUJ79Ws52KvNR8hOI0QrFJDHzPSCgkmoIBcwk0D3+LP1X41mMZNBZNuozjDLI7W3FRhYJ/K4fxrNDgzgd+LZFAcn7fPHUE2DJnbOhNzcprPrHVZODP9cShm1u7Y5s+8zum8ktqS1Q2+saktagrTeguufQFYvvhvAnKK2GVhv4WdfzaPHhGyo11gAAAAAElFTkSuQmCC);
}
div.rioccurrencesactions a.rirefreshbutton {
// refresh.png
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAMAAABFjsb+AAAA1VBMVEWbLGwAAAABAQECAgIFBQUGBgYHBwcICAgJCQkKCgoWFhYZGRkaGhoeHh4fHx8gICAkJCQmJiYoKCgrKyssLCwxMTE3Nzc4ODg5OTk+Pj5DQ0NHR0dISEhRUVFUVFRWVlZXV1ddXV1fX19gYGBkZGRoaGhwcHBxcXF6enp/f3+BgYGDg4OEhISJiYmNjY2VlZWampqnp6eoqKirq6uurq62trbCwsLDw8PFxcXIyMjNzc3V1dXW1tbZ2dnb29vv7+/19fX39/f4+Pj6+vr7+/v8/Pz///9okOBsAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAOkAAADpAVSSFEsAAADYSURBVBjTZdBrk8EwFAbghEprFaulxN2y2HVZ6n5tWX3//0+ScIwZ3g/JmWcmOTlhDK9hDBtpCiEqqYTQMeUGDJLrtBcxfo9UJnSRnKCfbyV1KchKRyDq4Fh6mhchxF8hQOQ9zDpg1V0a3F7hYJE1EdjlD3XMDtAkG8Gnnj5GZDMMyYaYkQ0wJ5tjQFbDyVFgec4JNTJji73LY/7vHlvj8T43xLn4gwtC9zlHbo2vf2CdozlMfXlcfvYwjuvSVFalnnz6fduqynaNbEYn7dTVmm3s9Ke+/fMVeXMr/BRXPaEAAAAASUVORK5CYII=);
color: transparent;
margin-top: 4px;
margin-right: 5px;
}
div#messagearea,
div.errorarea {
display: none;
background-color: red;
color: white;
font-weight: bold;
padding: 2px 10px;
}
div#calroot {
z-index:10000;
}
div.ributtons .risavebutton {
display: block;
margin: 30px auto 0;
}
// jQueryTools overrides
#calnext,
#calprev {
background-image: url(data:image/gif;base64,R0lGODlhDgAOAOZsAPb29vv7+/f39/Ly8vr6+vn5+QCFzfz8/PDw8PPz8wCEzQODzACHzwCFzgCBzCKb1fL7/fHx8aXZ7+r4/F295LXg8pTR6wKCzA2L0J3V7ZnW7T234gaS05bV7v3+/ofN65Tb8JfX7/n+/pff8uv6/YXN60C748/s9wCIz9ry+gKHzgOIzs7r9gF+yx+Z1QKEzSKg2AGP0mG54uf2++b4/AyP0QGCzGG844bN62m95Gm/5Fm95AeQ0vf8/bzl9ACAyxma1rbi9DKm2////weGzpfW7Mvp9QyO0J/Y7qrc8AiJz9jy+mK949Tv+QCDzHLA5geIzgCDzcfr9gqM0Mzv+ACQ0g2Q0sLn9XbM6y+p3L/s96LY706w3sbo9PX19ZXT7PD7/Tis3gyQ0g+R0v7+/gCGzgCHzvj4+ACCzPT09O/v7wCJz+7u7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAGwALAAAAAAOAA4AAAe0gGyCamlnBAQCCYKLbAMHTVgbJh00BWqLA2QlMQpoCxdAQQGXagc4CmZlRxIWC1ZXZ2xpVBwGZTVIZBlKDllgCAUfaAxTGkNfUAxraydpATloK0VkW0Rmyw1JAAFPDio6EEYuUcsGFQBnIQZmNkwiLA9lZlUpCQMkME4oLzceXRg/KPS4VMDHGDRl0Mjg0kLIDC+DCEgJw2NNAzE7JghgpEYAGS0gRiw5kIbRogjozqRBwCgQADs=);
}
#calnext {
background-image: url(data:image/gif;base64,R0lGODlhDgAOAOZsAPb29vv7+/f39/Ly8vr6+vn5+QCFzfz8/PDw8PPz8wCEzQODzACHzwCFzgCBzCKb1fL7/fHx8aXZ7+r4/F295LXg8pTR6wKCzA2L0J3V7ZnW7T234gaS05bV7v3+/ofN65Tb8JfX7/n+/pff8uv6/YXN60C748/s9wCIz9ry+gKHzgOIzs7r9gF+yx+Z1QKEzSKg2AGP0mG54uf2++b4/AyP0QGCzGG844bN62m95Gm/5Fm95AeQ0vf8/bzl9ACAyxma1rbi9DKm2////weGzpfW7Mvp9QyO0J/Y7qrc8AiJz9jy+mK949Tv+QCDzHLA5geIzgCDzcfr9gqM0Mzv+ACQ0g2Q0sLn9XbM6y+p3L/s96LY706w3sbo9PX19ZXT7PD7/Tis3gyQ0g+R0v7+/gCGzgCHzvj4+ACCzPT09O/v7wCJz+7u7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAGwALAAAAAAOAA4AAAe2gGyCbAhpZ2cAEYOLaQdLIyBaZAJqiwITO2INazxhUgSVbF4zQi1cMmhlaGM+BWxqPRQ/GF0eNy8oTjAkAwkpVWZlDywiTDZmBiGIFQZra1EuRhA6Kg5PAQBJm2tmRFtkRStoOQFpJ84MUF9DGlMMaB8FCGBZDkoZZEg1ZQYcVGlszlyxssCChCNlzCjAcaCSmgBBgFxYgEZBjBJkBgxSU4BGBxMbsDQ5oHERmwQCCBA4kyYUm0AAOw==);
}
div.overlaybg div.close,
div.overlay div.close {
// pb_close.png
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAABHVBMVEUAAAAAAAADAwMEBAQFBQUGBgYJCQkKCgoPDw8RERETExMWFhYbGxscHBwfHx8iIiIlJSUoKCgsLCwyMjI1NTU4ODg7OztDQ0NGRkZKSkpLS0tNTU1XV1ddXV1gYGBjY2NkZGRmZmZoaGhsbGxvb291dXV7e3t+fn6BgYGCgoKFhYWLi4uMjIyNjY2Ojo6QkJCTk5OVlZWWlpaXl5eZmZmdnZ2fn5+qqqqurq6vr6+ysrKzs7O5ubnCwsLFxcXHx8fJycnLy8vMzMzR0dHV1dXY2Nja2trc3Nzd3d3f39/g4ODh4eHk5OTl5eXo6Ojr6+vs7Ozt7e3u7u7x8fHy8vLz8/P19fX29vb39/f4+Pj6+vr7+/v8/Pz9/f3////kwcJJAAAAAXRSTlMAQObYZgAAAThJREFUKBXNwWdb2gAYhtH3CVbQFisVlOLWVlHcdeBedXSQqoiR4f3/f4YJF8Gg9psfPMfezL8avvqNvaaCz/2Nr2wvPFBbz8qXXrynZs/AXp9aUidgHRpsOmpz5sEibthzFLVGxZ5wl5Q0MCjfx/G49KGEtf1iRdIXyEufS6xLmuTKQh4ZSX0e5PtLUJDU28BC3CqQ8+AWio58p1iIczXlqkDRUWCHH9bCTzWlroA5NW2zai1cK5ByoQF5BY74bi01kpISLhSHPZiW5JSx0CXfJGWg6CjnsSUpi2uhJdy4pMlpR1I6n5S0z661lSiowxgVezIBY4oYrLNqEWswq7bRKgcW1bMCZyNxBbKHcCDrkJj6C9XTjY3jMlwvyJ6Ja/mCpj+bXbKXupUYmpn5+kmy/4jFFLP34hGuw0GxTwkuWgAAAABJRU5ErkJggg==);
}
#calroot {
width: auto;
}

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
{% block content %}
<h1>{{ _('Create event') }}</h1>
<form action="{{ url_for('event_create') }}" method="POST" enctype="multipart/form-data">
{{ form.hidden_tag() }}
@ -15,6 +16,11 @@
{{ render_field_with_errors(form.name) }}
{{ render_field_with_errors(form.description) }}
{{ render_field_with_errors(form.start) }}
<!-- <textarea name="repeat"></textarea>
<script type="text/javascript">
$("textarea[name=repeat]").recurrenceinput({startField: "start"});
</script> -->
</div>
</div>

View File

@ -20,17 +20,17 @@
</tr>
</thead>
<tbody>
{% for event in events %}
{% for date in dates %}
<tr>
<td>{{ event.dates[0].start | datetimeformat }}</td>
<td>{{ date.start | datetimeformat }}</td>
<td>
<a href="{{ url_for('event', event_id=event.id) }}">{{ event.name }}</a>
{% if event.verified %}
<a href="{{ url_for('event', event_id=date.event.id) }}">{{ date.event.name }}</a>
{% if date.event.verified %}
<i class="fa fa-check-circle text-success" data-toggle="tooltip" title="{{ _('Verified') }}"></i>
{% endif %}
</td>
<td>{{ render_ooa(event.host) }}</td>
<td>{{ render_place(event.place) }}</td>
<td>{{ render_ooa(date.event.host) }}</td>
<td>{{ render_place(date.event.place) }}</td>
</tr>
{% endfor %}
</tbody>

View File

@ -6,13 +6,12 @@ Prototyp
<h1>{{ _('Hi there!') }}</h1>
<!--
<div class="list-group">
<div class="list-group my-4">
<a href="{{ url_for('events') }}" class="list-group-item list-group-item-action">
{{ _('Events') }}
<i class="fa fa-caret-right"></i>
</a>
</div>-->
</div>
{% if admin_unit_members %}
<h2>{{ _('Your Admin Units') }}</h2>

View File

@ -16,6 +16,7 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='site.css')}}" />
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-beta.1/dist/css/select2.min.css" rel="stylesheet" />
<!-- <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='jquery.recurrenceinput.css')}}" /> -->
{%- block styles %}
{%- endblock styles %}
@ -24,6 +25,9 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/i18n/jquery-ui-i18n.js" integrity="sha256-HwXB0HPJCXhungCtZoScc1a53ngQOXua64nb0cI6PHY=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment-with-locales.min.js" integrity="sha256-AdQN98MVZs44Eq2yTwtoKufhnU+uZ7v2kXnD5vqzZVo=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-beta.1/dist/js/select2.min.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-tools/1.2.7/jquery.tools.min.js" integrity="sha256-MxBycAbJaZYkVUCna8pQ6wfU77HziLeBohh5jnr1ttI=" crossorigin="anonymous"></script>
<script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
<script src="{{ url_for('static', filename='jquery.recurrenceinput.js')}}"></script> -->
<title>{% block title %}{{ title|default }}{% endblock title %}</title>
@ -53,10 +57,10 @@
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<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>
<a class="nav-item nav-link" href="{{ url_for('places') }}">{{ _('Places') }}</a>
<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>
</div>
<div class="navbar-nav navbar-right">
{% if current_user.is_authenticated %}