Widget settings

This commit is contained in:
Daniel Grams 2020-11-08 17:46:14 +01:00
parent bfaa3c6841
commit d897ccc336
17 changed files with 373 additions and 172 deletions

View File

@ -4,6 +4,7 @@ from flask_wtf.file import FileField, FileAllowed
from wtforms import StringField, SubmitField, DecimalField, TextAreaField, FormField, SelectField
from wtforms.fields.html5 import EmailField, TelField, URLField
from wtforms.validators import DataRequired, Optional, Regexp
from wtforms.widgets.html5 import ColorInput
import decimal
from models import Location, Image
from .common import FileImageForm
@ -38,4 +39,11 @@ class CreateAdminUnitForm(BaseAdminUnitForm):
submit = SubmitField(lazy_gettext("Create admin unit"))
class UpdateAdminUnitForm(BaseAdminUnitForm):
submit = SubmitField(lazy_gettext("Update settings"))
class UpdateAdminUnitWidgetForm(FlaskForm):
widget_font = StringField(lazy_gettext('Font'), validators=[Optional()])
widget_background_color = StringField(lazy_gettext('Background Color'), default='#ffffff', widget=ColorInput(), validators=[Optional()])
widget_primary_color = StringField(lazy_gettext('Primary Color'), default='#007bff', widget=ColorInput(), validators=[Optional()])
widget_link_color = StringField(lazy_gettext('Link Color'), default='#007bff', widget=ColorInput(), validators=[Optional()])
submit = SubmitField(lazy_gettext("Update settings"))

View File

@ -0,0 +1,46 @@
"""empty message
Revision ID: 27da3ceea723
Revises: 00daa8c472ba
Create Date: 2020-11-08 16:14:01.866196
"""
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utils
import db
# revision identifiers, used by Alembic.
revision = '27da3ceea723'
down_revision = '00daa8c472ba'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
#op.drop_table('spatial_ref_sys')
op.add_column('adminunit', sa.Column('widget_background_color', sqlalchemy_utils.types.color.ColorType(length=20), nullable=True))
op.add_column('adminunit', sa.Column('widget_font', sa.Unicode(length=255), nullable=True))
op.add_column('adminunit', sa.Column('widget_link_color', sqlalchemy_utils.types.color.ColorType(length=20), nullable=True))
op.add_column('adminunit', sa.Column('widget_primary_color', sqlalchemy_utils.types.color.ColorType(length=20), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('adminunit', 'widget_primary_color')
op.drop_column('adminunit', 'widget_link_color')
op.drop_column('adminunit', 'widget_font')
op.drop_column('adminunit', 'widget_background_color')
op.create_table('spatial_ref_sys',
sa.Column('srid', sa.INTEGER(), autoincrement=False, nullable=False),
sa.Column('auth_name', sa.VARCHAR(length=256), autoincrement=False, nullable=True),
sa.Column('auth_srid', sa.INTEGER(), autoincrement=False, nullable=True),
sa.Column('srtext', sa.VARCHAR(length=2048), autoincrement=False, nullable=True),
sa.Column('proj4text', sa.VARCHAR(length=2048), autoincrement=False, nullable=True),
sa.CheckConstraint('(srid > 0) AND (srid <= 998999)', name='spatial_ref_sys_srid_check'),
sa.PrimaryKeyConstraint('srid', name='spatial_ref_sys_pkey')
)
# ### end Alembic commands ###

View File

@ -6,6 +6,7 @@ from sqlalchemy.schema import CheckConstraint
from sqlalchemy.types import TypeDecorator
from sqlalchemy.event import listens_for
from sqlalchemy import UniqueConstraint, Boolean, DateTime, Column, Integer, String, ForeignKey, Unicode, UnicodeText, Numeric, LargeBinary
from sqlalchemy_utils import ColorType
from flask_security import UserMixin, RoleMixin
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin
from enum import IntEnum
@ -130,6 +131,10 @@ class AdminUnit(db.Model, TrackableMixin):
email = Column(Unicode(255))
phone = Column(Unicode(255))
fax = Column(Unicode(255))
widget_font = Column(Unicode(255))
widget_background_color = Column(ColorType)
widget_primary_color = Column(ColorType)
widget_link_color = Column(ColorType)
@listens_for(AdminUnit, 'before_insert')
@listens_for(AdminUnit, 'before_update')
@ -426,4 +431,4 @@ class FeaturedEventRejectionReason(IntEnum):
untrustworthy = 2
illegal = 3
irrelevant = 4
# Deprecated end
# Deprecated end

View File

@ -7,6 +7,7 @@ certifi==2020.6.20
cffi==1.14.0
chardet==3.0.4
click==7.1.2
colour==0.1.5
dnspython==1.16.0
dominate==2.5.1
email-validator==1.1.1

View File

@ -1,34 +1,12 @@
body {
background-color:#eceef0;
font-family: Verdana;
color: #3c444b;
}
.btn-primary {
background-color: #B09641;
color: black;
border-color: #B09641;
}
.btn-primary:hover {
background-color: #B09641;
color: black;
border-color: #B09641;
}
.card {
background-color: transparent;
}
.card-title, .page-link, .page-link:hover, a, a:hover {
color: #7B2424;
}
.wizard > .steps .disabled a,
.wizard > .steps .disabled a:hover,
.wizard > .steps .disabled a:active
{
background: #aaa;
background-color: #aaa;
color: #aaa;
cursor: default;
}
@ -37,7 +15,7 @@ body {
.wizard > .steps .current a:hover,
.wizard > .steps .current a:active
{
background: #B09641;
background-color: #17a2b8;
color: #fff;
cursor: default;
}
@ -46,7 +24,7 @@ body {
.wizard > .steps .done a:hover,
.wizard > .steps .done a:active
{
background: #B09641;
background-color: #17a2b8;
color: #fff;
}
@ -54,7 +32,7 @@ body {
.wizard > .actions a:hover,
.wizard > .actions a:active
{
background: #B09641;
background-color: #17a2b8;
color: #fff;
display: block;
padding: 0.5em 1em;

View File

@ -992,3 +992,51 @@ if (URL) {
</div>
{% endmacro %}
{% macro render_widget_styles(styles) %}
<style>
body {
{% if 'background' in styles %}
background-color: {{ styles['background'] }};
{% endif %}
{% if 'font' in styles %}
font-family: {{ styles['font'] }};
{% endif %}
}
{% if 'primary' in styles %}
.btn-primary,
.btn-primary:hover,
.btn-primary:active,
.btn-primary:not(:disabled):not(.disabled):active,
.btn-primary:focus {
background-color: {{ styles['primary'] }};
border-color: {{ styles['primary'] }};
}
{% endif %}
{% if 'link' in styles %}
.card-title, .page-link, .page-link:hover, a, a:hover {
color: {{ styles['link'] }};
}
{% endif %}
{% if 'primary' in styles %}
.wizard > .steps .current a,
.wizard > .steps .current a:hover,
.wizard > .steps .current a:active,
.wizard > .steps .current a,
.wizard > .steps .current a:hover,
.wizard > .steps .current a:active,
.wizard > .steps .done a,
.wizard > .steps .done a:hover,
.wizard > .steps .done a:active,
.wizard > .actions a,
.wizard > .actions a:hover,
.wizard > .actions a:active
{
background-color: {{ styles['primary'] }};
}
{% endif %}
</style>
{% endmacro %}

View File

@ -7,8 +7,8 @@ oveda - Terminkalender für Goslar und Hahnenklee
<h1>Terminkalender für Goslar und Hahnenklee</h1>
<div class="my-4">
<a class="btn btn-secondary my-1" href="{{ url_for('event_suggestion_create_for_admin_unit', au_short_name='goslar') }}" role="button"><i class="fa fa-plus"></i> {{ _('Create event suggestion') }}</a>
</div>
<a class="btn btn-secondary my-1" href="{{ url_for('event_suggestion_create_for_admin_unit', au_short_name='goslar') }}" role="button" target="_blank"><i class="fa fa-plus"></i> {{ _('Create event suggestion') }}</a>
</div>
<div class="input-group mb-2 mr-sm-2">
<div class="input-group-prepend">

View File

@ -1,18 +1,32 @@
{% extends "layout_manage.html" %}
{% set active_id = "widgets" %}
{% from "_macros.html" import render_pagination, render_event_date, render_field_with_errors, render_event_organizer %}
{% from "_macros.html" import render_pagination, render_event_date, render_field, render_field_with_errors, render_event_organizer %}
{% block title %}
{{ _('Widgets') }}
{% endblock %}
{% block content %}
<h1>{{ _('Widgets') }}</h1>
<h1>{{ _('Widgets') }} <a class="btn btn-light" role="button" data-toggle="collapse" href="#settingsContainer" aria-expanded="false" aria-controls="settingsContainer"><i class="fa fa-cog"></i></a></h1>
<div class="collapse" id="settingsContainer">
<h2>{{ _('Settings') }}</h2>
<form action="" method="POST">
{{ form.hidden_tag() }}
{{ render_field_with_errors(form.widget_font) }}
{{ render_field_with_errors(form.widget_background_color) }}
{{ render_field_with_errors(form.widget_primary_color) }}
{{ render_field_with_errors(form.widget_link_color) }}
{{ render_field(form.submit) }}
</form>
</div>
<h2>{{ _('Veranstaltungen als iFrame einbetten') }}</h2>
<textarea class="form-control"><iframe src="{{ url_for('widget_event_dates', au_short_name=admin_unit.short_name, _external=True) }}"></iframe></textarea>
<textarea class="form-control"><iframe src="{{ url_for('widget_event_dates', au_short_name=admin_unit.short_name, _external=True) }}" allowtransparency="true"></iframe></textarea>
<p><a class="btn btn-outline-info my-2" data-toggle="collapse" href="#iFrameContainer" aria-expanded="false" aria-controls="iFrameContainer">Vorschau</a></p>
<div class="collapse" id="iFrameContainer">
<iframe src="{{ url_for('widget_event_dates', au_short_name=admin_unit.short_name) }}" style="display: block; width: 90vw; height: 40vh; max-width: 100%;"></iframe>
<iframe src="{{ url_for('widget_event_dates', au_short_name=admin_unit.short_name) }}" allowtransparency="true" style="display: block; width: 90vw; height: 40vh; max-width: 100%;"></iframe>
</div>
<h2>{{ _('Link, um Veranstaltungen vorzuschlagen') }}</h2>

View File

@ -1,16 +1,17 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_pagination, render_event_status_pill, render_event_status_pill, render_place, render_events_sub_menu %}
{% from "_macros.html" import render_widget_styles, render_pagination, render_event_status_pill, render_event_status_pill, render_place, render_events_sub_menu %}
{% block title %}
{{ _('Widget') }}
{% endblock %}
{% block styles %}
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='widget.css')}}" />
{{ render_widget_styles(styles) }}
{% endblock %}
{% block navbar %}
{% endblock %}
{% block content %}
<form action="" class="form-inline mb-4" method="GET" autocomplete="off">
<form action="{{ request.url }}" class="form-inline mb-4" method="GET" autocomplete="off">
{{ form.hidden_tag() }}
<div class="input-group mb-2 mr-sm-2">
@ -51,7 +52,7 @@
<!-- Desktop -->
<div class="row mb-3 d-none d-sm-block">
<div class="col-sm">
<div class="card shadow">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-sm-8">
@ -62,7 +63,7 @@
<small class="text-muted mr-2"><i class="fa fa-database"></i> {{ date.event.admin_unit.name }}</small>
{% endif %}
<small class="text-muted"><i class="fa fa-map-marker"></i> {{ date.event.event_place.name }}</small>
<a href="{{ url_for('widget_event_date', id=date.id) }}" target="_blank" class="stretched-link"></a>
<a href="{{ url_for('widget_event_date', au_short_name=admin_unit.short_name, id=date.id) }}" target="_blank" class="stretched-link"></a>
</div>
<div class="col-sm-4 text-right">
{% if date.event.photo_id %}
@ -78,7 +79,7 @@
<!-- Mobile -->
<div class="row mb-3 d-sm-none">
<div class="col-sm">
<div class="card shadow">
<div class="card">
<div>
{% if date.event.photo_id %}
<img src="{{ url_for('image', id=date.event.photo_id) }}" class="card-img-top" style="object-fit: cover; height: 20vh;" />
@ -94,7 +95,7 @@
<small class="text-muted mr-2"><i class="fa fa-database"></i> {{ date.event.admin_unit.name }}</small>
{% endif %}
<small class="text-muted"><i class="fa fa-map-marker"></i> {{ date.event.event_place.name }}</small>
<a href="{{ url_for('widget_event_date', id=date.id) }}" target="_blank" class="stretched-link"></a>
<a href="{{ url_for('widget_event_date', au_short_name=admin_unit.short_name, id=date.id) }}" target="_blank" class="stretched-link"></a>
</div>
</div>
</div>
@ -106,4 +107,6 @@
{{ render_pagination(pagination) }}
{% endblock %}
{% block footer %}
{% endblock %}

View File

@ -1,11 +1,12 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_event_props, render_image_with_link, render_place, render_link_prop %}
{% from "_macros.html" import render_widget_styles, render_event_props, render_image_with_link, render_place, render_link_prop %}
{% set event = event_date.event %}
{% block title %}
{{ event.name }}
{% endblock %}
{% block styles %}
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='widget.css')}}" />
{{ render_widget_styles(styles) }}
{% endblock %}
{% block header %}
<script type="application/ld+json">

View File

@ -1,11 +1,12 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_logo, render_cropper_code, render_crop_image_form, render_jquery_steps_header, render_cropper_header, render_base_image_form, render_radio_buttons, render_datepicker_js, render_field_with_errors, render_field %}
{% from "_macros.html" import render_widget_styles, render_logo, render_cropper_code, render_crop_image_form, render_jquery_steps_header, render_cropper_header, render_base_image_form, render_radio_buttons, render_datepicker_js, render_field_with_errors, render_field %}
{% block title %}
{{ _('Create event suggestion') }}
{% endblock %}
{% block styles %}
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='jquery-steps.css')}}" />
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='widget.css')}}" />
{{ render_widget_styles(styles) }}
{% endblock %}
{% block navbar %}
{% endblock %}

View File

@ -1,10 +1,11 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_location, render_location_prop, render_logo, render_pagination, render_event_status_pill, render_place, render_events_sub_menu %}
{% from "_macros.html" import render_widget_styles, render_location, render_location_prop, render_logo, render_pagination, render_event_status_pill, render_place, render_events_sub_menu %}
{% block title %}
{{ admin_unit.name }} Infoscreen
{% endblock %}
{% block styles %}
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='infoscreen.css')}}" />
{{ render_widget_styles(styles) }}
{% endblock %}
{% block navbar %}
{% endblock %}

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2020-11-05 09:07+0100\n"
"POT-Creation-Date: 2020-11-08 17:10+0100\n"
"PO-Revision-Date: 2020-06-07 18:51+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
@ -126,34 +126,34 @@ msgstr "Event_"
msgid "."
msgstr "."
#: forms/admin_unit.py:12 forms/event.py:14 forms/event_place.py:13
#: forms/admin_unit.py:13 forms/event.py:14 forms/event_place.py:13
#: forms/organizer.py:12
msgid "Street"
msgstr "Straße"
#: forms/admin_unit.py:13 forms/event.py:15 forms/event_place.py:14
#: forms/admin_unit.py:14 forms/event.py:15 forms/event_place.py:14
#: forms/organizer.py:13
msgid "Postal code"
msgstr "Postleitzahl"
#: forms/admin_unit.py:14 forms/event.py:16 forms/event_place.py:15
#: forms/admin_unit.py:15 forms/event.py:16 forms/event_place.py:15
#: forms/organizer.py:14
msgid "City"
msgstr "Stadt/Ort"
#: forms/admin_unit.py:15 forms/event_place.py:16 forms/organizer.py:15
#: forms/admin_unit.py:16 forms/event_place.py:16 forms/organizer.py:15
msgid "State"
msgstr "Bundesland"
#: forms/admin_unit.py:16 forms/event_place.py:17 forms/organizer.py:16
#: forms/admin_unit.py:17 forms/event_place.py:17 forms/organizer.py:16
msgid "Latitude"
msgstr "Breitengrad"
#: forms/admin_unit.py:17 forms/event_place.py:18 forms/organizer.py:17
#: forms/admin_unit.py:18 forms/event_place.py:18 forms/organizer.py:17
msgid "Longitude"
msgstr "Längengrad"
#: forms/admin_unit.py:20 forms/event.py:19 forms/event.py:39
#: forms/admin_unit.py:21 forms/event.py:19 forms/event.py:39
#: forms/event.py:138 forms/event_place.py:21 forms/event_place.py:43
#: forms/event_suggestion.py:15 forms/event_suggestion.py:20
#: forms/organizer.py:20 forms/organizer.py:44 forms/reference.py:18
@ -163,56 +163,72 @@ msgstr "Längengrad"
msgid "Name"
msgstr "Name"
#: forms/admin_unit.py:21
#: forms/admin_unit.py:22
msgid "Short name"
msgstr "Kurzname"
#: forms/admin_unit.py:21
#: forms/admin_unit.py:22
msgid "The short name is used to create a unique identifier for your events"
msgstr ""
"Der Kurzname wird verwendet, um Ihre Veranstaltungen eindeutig zu "
"identifizieren. Der Kurzname darf nur Buchstaben, Nummern und "
"Unterstriche enthalten."
#: forms/admin_unit.py:21
#: forms/admin_unit.py:22
msgid "Short name must contain only letters numbers or underscore"
msgstr "Der Kurzname darf nur Buchstaben, Nummern und Unterstriche enthalten"
#: forms/admin_unit.py:22 forms/event.py:33 forms/event.py:40
#: forms/admin_unit.py:23 forms/event.py:33 forms/event.py:40
#: forms/event_place.py:22 forms/event_suggestion.py:18 forms/organizer.py:21
msgid "Link URL"
msgstr "Link URL"
#: forms/admin_unit.py:23 forms/admin_unit_member.py:12
#: forms/admin_unit.py:24 forms/admin_unit_member.py:12
#: forms/admin_unit_member.py:22 forms/admin_unit_member.py:26
#: forms/event.py:34 forms/event_suggestion.py:22 forms/organizer.py:22
#: templates/_macros.html:229
msgid "Email"
msgstr "Email"
#: forms/admin_unit.py:24 forms/event.py:35 forms/event_suggestion.py:21
#: forms/admin_unit.py:25 forms/event.py:35 forms/event_suggestion.py:21
#: forms/organizer.py:23 templates/_macros.html:256
msgid "Phone"
msgstr "Telefon"
#: forms/admin_unit.py:25 forms/event.py:36 forms/organizer.py:24
#: forms/admin_unit.py:26 forms/event.py:36 forms/organizer.py:24
#: templates/_macros.html:264
msgid "Fax"
msgstr "Fax"
#: forms/admin_unit.py:26 forms/organizer.py:25
#: forms/admin_unit.py:27 forms/organizer.py:25
msgid "Logo"
msgstr "Logo"
#: forms/admin_unit.py:38 templates/admin_unit/create.html:10
#: forms/admin_unit.py:39 templates/admin_unit/create.html:10
#: templates/manage/admin_units.html:18
msgid "Create admin unit"
msgstr "Verwaltungseinheit erstellen"
#: forms/admin_unit.py:41
#: forms/admin_unit.py:42 forms/admin_unit.py:49
msgid "Update settings"
msgstr "Einstellungen speichern"
#: forms/admin_unit.py:45
msgid "Font"
msgstr "Schriftart"
#: forms/admin_unit.py:46
msgid "Background Color"
msgstr "Hintergrundfarbe"
#: forms/admin_unit.py:47
msgid "Primary Color"
msgstr "Hauptfarbe"
#: forms/admin_unit.py:48
msgid "Link Color"
msgstr "Linkfarbe"
#: forms/admin_unit_member.py:13 forms/admin_unit_member.py:29
#: templates/profile.html:40
msgid "Roles"
@ -420,7 +436,7 @@ msgid "EventAttendanceMode.mixed"
msgstr "Online und offline"
#: forms/event.py:70 forms/event_place.py:23 forms/event_suggestion.py:27
#: templates/event_suggestion/create.html:215
#: templates/widget/event_suggestion/create.html:216
msgid "Photo"
msgstr "Foto"
@ -536,7 +552,7 @@ msgid "Distance"
msgstr "Distanz"
#: forms/event_date.py:25 forms/planing.py:25
#: templates/widget/event_date/list.html:46
#: templates/widget/event_date/list.html:47
msgid "Find"
msgstr "Finden"
@ -624,8 +640,9 @@ msgstr ""
"das System hochlade, hinsichtlich ihrer Nutzungsrechte abgeklärt habe und"
" erkläre, dass diese weitergegeben werden dürfen."
#: forms/event_suggestion.py:30 templates/event_suggestion/create.html:4
#: templates/event_suggestion/create.html:123 templates/example.html:10
#: forms/event_suggestion.py:30 templates/example.html:10
#: templates/widget/event_suggestion/create.html:4
#: templates/widget/event_suggestion/create.html:124
msgid "Create event suggestion"
msgstr "Veranstaltung vorschlagen"
@ -757,8 +774,8 @@ msgstr "Link"
#: templates/_macros.html:319 templates/event/create.html:59
#: templates/event/delete.html:13 templates/event/update.html:16
#: templates/event_suggestion/create.html:195
#: templates/reference/delete.html:13
#: templates/widget/event_suggestion/create.html:196
msgid "Event"
msgstr "Veranstaltung"
@ -781,22 +798,22 @@ msgstr "Ort bei Google suchen"
#: templates/_macros.html:569 templates/_macros.html:571
#: templates/event_date/list.html:270
#: templates/event_suggestion/create.html:159
#: templates/event_suggestion/create.html:184
#: templates/event_suggestion/create.html:204
#: templates/event_suggestion/create.html:227
#: templates/event_suggestion/create.html:245
#: templates/event_suggestion/create.html:266
#: templates/widget/event_suggestion/create.html:160
#: templates/widget/event_suggestion/create.html:185
#: templates/widget/event_suggestion/create.html:205
#: templates/widget/event_suggestion/create.html:228
#: templates/widget/event_suggestion/create.html:246
#: templates/widget/event_suggestion/create.html:267
msgid "Previous"
msgstr "Zurück"
#: templates/_macros.html:574 templates/_macros.html:576
#: templates/event_date/list.html:271
#: templates/event_suggestion/create.html:160
#: templates/event_suggestion/create.html:185
#: templates/event_suggestion/create.html:205
#: templates/event_suggestion/create.html:228
#: templates/event_suggestion/create.html:246
#: templates/widget/event_suggestion/create.html:161
#: templates/widget/event_suggestion/create.html:186
#: templates/widget/event_suggestion/create.html:206
#: templates/widget/event_suggestion/create.html:229
#: templates/widget/event_suggestion/create.html:247
msgid "Next"
msgstr "Weiter"
@ -846,7 +863,7 @@ msgstr "Empfehlung anfragen"
msgid "Event suggestion"
msgstr "Veranstaltungsvorschlag"
#: templates/_macros.html:976 templates/event_suggestion/create.html:170
#: templates/_macros.html:976 templates/widget/event_suggestion/create.html:171
msgid "Contact"
msgstr "Kontakt"
@ -855,7 +872,7 @@ msgid "Widget als iFrame einbetten"
msgstr "Widget als iFrame einbetten"
#: templates/home.html:26 templates/home.html:142
#: templates/security/login_user.html:25 views/event_suggestion.py:44
#: templates/security/login_user.html:25 views/widget.py:99
msgid "Register for free"
msgstr "Kostenlos registrieren"
@ -944,6 +961,7 @@ msgid "Widgets"
msgstr "Widgets"
#: templates/admin_unit/update.html:11 templates/layout_manage.html:48
#: templates/manage/widgets.html:12
msgid "Settings"
msgstr "Einstellungen"
@ -1043,18 +1061,6 @@ msgstr "Empfehlung anfragen für Veranstaltung \"%(name)s\""
msgid "Info"
msgstr "Info"
#: templates/event_suggestion/create.html:141
msgid "Continue as guest"
msgstr "Weiter als Gast"
#: templates/event_suggestion/create.html:238
msgid "Optional details"
msgstr "Optionale Details"
#: templates/event_suggestion/create.html:256
msgid "Preview"
msgstr "Vorschau"
#: templates/event_suggestion/review.html:9 templates/manage/reviews.html:19
msgid "Review event suggestion"
msgstr "Veranstaltungsvorschlag prüfen"
@ -1125,15 +1131,15 @@ msgstr "Anfrage prüfen"
msgid "Show review status"
msgstr "Prüfungsstatus anzeigen"
#: templates/manage/widgets.html:11
#: templates/manage/widgets.html:25
msgid "Veranstaltungen als iFrame einbetten"
msgstr "Veranstaltungen als iFrame einbetten"
#: templates/manage/widgets.html:18
#: templates/manage/widgets.html:32
msgid "Link, um Veranstaltungen vorzuschlagen"
msgstr "Link, um Veranstaltungen vorzuschlagen"
#: templates/manage/widgets.html:22
#: templates/manage/widgets.html:36
msgid "URL für Infoscreen"
msgstr "URL für Infoscreen"
@ -1158,6 +1164,18 @@ msgstr "Du hast noch keinen Account? Kein Problem!"
msgid "Widget"
msgstr "Widget"
#: templates/widget/event_suggestion/create.html:142
msgid "Continue as guest"
msgstr "Weiter als Gast"
#: templates/widget/event_suggestion/create.html:239
msgid "Optional details"
msgstr "Optionale Details"
#: templates/widget/event_suggestion/create.html:257
msgid "Preview"
msgstr "Vorschau"
#: views/admin_unit.py:49
msgid "Admin unit successfully created"
msgstr "Verwaltungseinheit erfolgreich erstellt"
@ -1234,30 +1252,18 @@ msgstr "Der eingegebene Name entspricht nicht dem Namen des Ortes"
msgid "Place successfully deleted"
msgstr "Ort erfolgreich gelöscht"
#: views/event_suggestion.py:41
msgid "Thank you so much! The event is being verified."
msgstr "Vielen Dank! Die Veranstaltung wird geprüft."
#: views/event_suggestion.py:44
msgid ""
"For more options and your own calendar of events, you can register for "
"free."
msgstr ""
"Für mehr Optionen und einen eigenen Veranstaltungskalender, kannst du "
"dich kostenlos registrieren."
#: views/event_suggestion.py:83
#: views/event_suggestion.py:45
msgid "Event suggestion successfully rejected"
msgstr "Veranstaltungsvorschlag erfolgreich abgelehnt"
#: views/event_suggestion.py:109
msgid "New event review"
msgstr "Neue Veranstaltung zu prüfen"
#: views/event_suggestion.py:116 views/reference_request_review.py:76
#: views/event_suggestion.py:68 views/reference_request_review.py:76
msgid "Event review status updated"
msgstr "Prüfungsstatus aktualisiert"
#: views/manage.py:173
msgid "Settings successfully updated"
msgstr "Einstellungen erfolgreich aktualisiert"
#: views/organizer.py:29
msgid "Organizer successfully created"
msgstr "Veranstalter erfolgreich erstellt"
@ -1315,6 +1321,22 @@ msgstr "Fehler im Feld %s: %s"
msgid "Show"
msgstr "Anzeigen"
#: views/widget.py:96
msgid "Thank you so much! The event is being verified."
msgstr "Vielen Dank! Die Veranstaltung wird geprüft."
#: views/widget.py:99
msgid ""
"For more options and your own calendar of events, you can register for "
"free."
msgstr ""
"Für mehr Optionen und einen eigenen Veranstaltungskalender, kannst du "
"dich kostenlos registrieren."
#: views/widget.py:135
msgid "New event review"
msgstr "Neue Veranstaltung zu prüfen"
#~ msgid "You"
#~ msgstr "Du"

View File

@ -2,55 +2,17 @@ from app import app, db
from models import EventSuggestion, User, Event, EventDate, EventReviewStatus, AdminUnit, AdminUnitMember, EventOrganizer, EventCategory
from flask import render_template, flash, url_for, redirect, request, jsonify, abort
from flask_babelex import gettext
from flask_security import auth_required, current_user
from flask_security import current_user
from access import has_access, access_or_401, can_reference_event, has_admin_unit_member_permission
from dateutils import today
from datetime import datetime
from forms.event_suggestion import CreateEventSuggestionForm, RejectEventSuggestionForm
from .utils import flash_errors, upsert_image_with_data, send_mail, handleSqlError, flash_message
from forms.event_suggestion import RejectEventSuggestionForm
from .utils import flash_errors, send_mail, handleSqlError, flash_message
from utils import get_event_category_name
from services.event import upsert_event_category, update_event_dates_with_recurrence_rule
from services.place import get_event_places
from sqlalchemy.sql import asc, func
from sqlalchemy.exc import SQLAlchemyError
@app.route("/<string:au_short_name>/event_suggestions/create", methods=('GET', 'POST'))
def event_suggestion_create_for_admin_unit(au_short_name):
admin_unit = AdminUnit.query.filter(AdminUnit.short_name == au_short_name).first_or_404()
form = CreateEventSuggestionForm()
form.organizer_id.choices = [(o.id, o.name) for o in EventOrganizer.query.filter(EventOrganizer.admin_unit_id == admin_unit.id).order_by(func.lower(EventOrganizer.name))]
places = get_event_places(admin_unit.id)
form.event_place_id.choices = [(p.id, p.name) for p in places]
form.organizer_id.choices.insert(0, ('', ''))
form.event_place_id.choices.insert(0, ('', ''))
if form.validate_on_submit():
event_suggestion = EventSuggestion()
form.populate_obj(event_suggestion)
event_suggestion.admin_unit_id = admin_unit.id
event_suggestion.review_status = EventReviewStatus.inbox
try:
db.session.add(event_suggestion)
db.session.commit()
send_event_inbox_mails(admin_unit, event_suggestion)
flash(gettext('Thank you so much! The event is being verified.'), 'success')
if not current_user.is_authenticated:
flash_message(gettext('For more options and your own calendar of events, you can register for free.'), url_for('security.register'), gettext('Register for free'), 'info')
return redirect(url_for('event_suggestion_review_status', event_suggestion_id=event_suggestion.id))
except SQLAlchemyError as e:
db.session.rollback()
flash(handleSqlError(e), 'danger')
else:
flash_errors(form)
return render_template('event_suggestion/create.html', form=form, admin_unit=admin_unit)
@app.route('/event_suggestion/<int:event_suggestion_id>/review')
def event_suggestion_review(event_suggestion_id):
event_suggestion = EventSuggestion.query.get_or_404(event_suggestion_id)
@ -100,16 +62,6 @@ def event_suggestion_review_status(event_suggestion_id):
return render_template('event_suggestion/review_status.html',
event_suggestion=event_suggestion)
def send_event_inbox_mails(admin_unit, event_suggestion):
members = AdminUnitMember.query.join(User).filter(AdminUnitMember.admin_unit_id == admin_unit.id).all()
for member in members:
if has_admin_unit_member_permission(member, 'event:verify'):
send_mail(member.user.email,
gettext('New event review'),
'review_notice',
event_suggestion=event_suggestion)
def send_event_suggestion_review_status_mail(event_suggestion):
if event_suggestion.contact_email and event_suggestion.contact_email_notice:
send_mail(event_suggestion.contact_email,

View File

@ -6,9 +6,10 @@ from flask_security import auth_required, roles_required, current_user
from access import has_access, access_or_401, get_admin_unit_for_manage, get_admin_units_for_manage, get_admin_unit_for_manage_or_404
from sqlalchemy.sql import asc, desc, func
from sqlalchemy import and_, or_, not_
from .utils import get_pagination_urls, permission_missing
from .utils import get_pagination_urls, permission_missing, handleSqlError, flash_errors
from forms.event_place import FindEventPlaceForm
from forms.event import FindEventForm
from forms.admin_unit import UpdateAdminUnitWidgetForm
from services.event_search import EventSearchParams
from services.event import get_events_query
from services.event_suggestion import get_event_reviews_query
@ -134,8 +135,49 @@ def manage_admin_unit_members(id):
invitations=invitations,
pagination=get_pagination_urls(members, id=id))
@app.route('/manage/admin_unit/<int:id>/widgets')
@app.route('/manage/admin_unit/<int:id>/widgets', methods=('GET', 'POST'))
@auth_required()
def manage_admin_unit_widgets(id):
admin_unit = get_admin_unit_for_manage_or_404(id)
return render_template('manage/widgets.html', admin_unit=admin_unit)
default_background_color = '#ffffff'
default_primary_color = '#007bff'
form = UpdateAdminUnitWidgetForm(obj=admin_unit)
if not form.widget_background_color.data:
form.widget_background_color.data = default_background_color
if not form.widget_primary_color.data:
form.widget_primary_color.data = default_primary_color
if not form.widget_link_color.data:
form.widget_link_color.data = default_primary_color
if form.validate_on_submit():
if not has_access(admin_unit, 'admin_unit:update'):
return permission_missing(url_for('manage_admin_unit', id=admin_unit.id))
form.populate_obj(admin_unit)
if form.widget_background_color.data == default_background_color:
admin_unit.widget_background_color = None
if form.widget_primary_color.data == default_primary_color:
admin_unit.widget_primary_color = None
if form.widget_link_color.data == default_primary_color:
admin_unit.widget_link_color = None
try:
db.session.commit()
flash(gettext('Settings successfully updated'), 'success')
return redirect(url_for('manage_admin_unit_widgets', id=admin_unit.id))
except SQLAlchemyError as e:
db.session.rollback()
flash(handleSqlError(e), 'danger')
else:
flash_errors(form)
return render_template('manage/widgets.html',
form=form,
admin_unit=admin_unit)

View File

@ -1,16 +1,23 @@
from app import app
from models import EventDate, Event, AdminUnit
from app import app, db
from models import User, EventDate, Event, AdminUnit, EventOrganizer, EventSuggestion, EventReviewStatus, AdminUnitMember
from dateutils import today, date_set_end_of_day, form_input_from_date, form_input_to_date
from dateutil.relativedelta import relativedelta
from flask import render_template, request
from flask import render_template, request, flash, redirect, url_for
from flask_babelex import gettext
from flask_security import auth_required, current_user
from sqlalchemy import and_, or_, not_
from sqlalchemy.sql import asc, func
from sqlalchemy.exc import SQLAlchemyError
from services.event import get_event_dates_query
from services.event_search import EventSearchParams
from .utils import get_pagination_urls
from services.place import get_event_places
from .utils import get_pagination_urls, flash_errors, flash_message, send_mail, handleSqlError
import json
from jsonld import DateTimeEncoder, get_sd_for_event_date
from forms.event_date import FindEventDateForm
from forms.event_suggestion import CreateEventSuggestionForm
from .event_date import prepare_event_date_form
from access import has_admin_unit_member_permission
@app.route("/<string:au_short_name>/widget/eventdates")
def widget_event_dates(au_short_name):
@ -30,16 +37,20 @@ def widget_event_dates(au_short_name):
return render_template('widget/event_date/list.html',
form=form,
styles=get_styles(admin_unit),
admin_unit=admin_unit,
params=params,
dates=dates.items,
pagination=get_pagination_urls(dates, au_short_name=au_short_name))
@app.route('/widget/eventdate/<int:id>')
def widget_event_date(id):
@app.route('/<string:au_short_name>/widget/eventdate/<int:id>')
def widget_event_date(au_short_name, id):
admin_unit = AdminUnit.query.filter(AdminUnit.short_name == au_short_name).first_or_404()
event_date = EventDate.query.get_or_404(id)
structured_data = json.dumps(get_sd_for_event_date(event_date), indent=2, cls=DateTimeEncoder)
return render_template('widget/event_date/read.html',
event_date=event_date,
styles=get_styles(admin_unit),
structured_data=structured_data)
@app.route("/<string:au_short_name>/widget/infoscreen")
@ -55,4 +66,72 @@ def widget_infoscreen(au_short_name):
return render_template('widget/infoscreen/read.html',
admin_unit=admin_unit,
params=params,
dates=dates.items)
styles=get_styles(admin_unit),
dates=dates.items)
@app.route("/<string:au_short_name>/widget/event_suggestions/create", methods=('GET', 'POST'))
def event_suggestion_create_for_admin_unit(au_short_name):
admin_unit = AdminUnit.query.filter(AdminUnit.short_name == au_short_name).first_or_404()
form = CreateEventSuggestionForm()
form.organizer_id.choices = [(o.id, o.name) for o in EventOrganizer.query.filter(EventOrganizer.admin_unit_id == admin_unit.id).order_by(func.lower(EventOrganizer.name))]
places = get_event_places(admin_unit.id)
form.event_place_id.choices = [(p.id, p.name) for p in places]
form.organizer_id.choices.insert(0, ('', ''))
form.event_place_id.choices.insert(0, ('', ''))
if form.validate_on_submit():
event_suggestion = EventSuggestion()
form.populate_obj(event_suggestion)
event_suggestion.admin_unit_id = admin_unit.id
event_suggestion.review_status = EventReviewStatus.inbox
try:
db.session.add(event_suggestion)
db.session.commit()
send_event_inbox_mails(admin_unit, event_suggestion)
flash(gettext('Thank you so much! The event is being verified.'), 'success')
if not current_user.is_authenticated:
flash_message(gettext('For more options and your own calendar of events, you can register for free.'), url_for('security.register'), gettext('Register for free'), 'info')
return redirect(url_for('event_suggestion_review_status', event_suggestion_id=event_suggestion.id))
except SQLAlchemyError as e:
db.session.rollback()
flash(handleSqlError(e), 'danger')
else:
flash_errors(form)
return render_template('widget/event_suggestion/create.html',
form=form,
admin_unit=admin_unit,
styles=get_styles(admin_unit))
def get_styles(admin_unit):
styles = dict()
if admin_unit.widget_font:
styles["font"] = admin_unit.widget_font
if admin_unit.widget_background_color:
styles["background"] = admin_unit.widget_background_color.hex
if admin_unit.widget_primary_color:
styles["primary"] = admin_unit.widget_primary_color.hex
if admin_unit.widget_link_color:
styles["link"] = admin_unit.widget_link_color.hex
return styles
def send_event_inbox_mails(admin_unit, event_suggestion):
members = AdminUnitMember.query.join(User).filter(AdminUnitMember.admin_unit_id == admin_unit.id).all()
for member in members:
if has_admin_unit_member_permission(member, 'event:verify'):
send_mail(member.user.email,
gettext('New event review'),
'review_notice',
event_suggestion=event_suggestion)