This commit is contained in:
Daniel Grams 2022-01-17 11:27:50 +01:00
parent 0d58305ea1
commit d6bfd4d2d6
31 changed files with 880 additions and 243 deletions

View File

@ -8,14 +8,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-01-11 23:44+0100\n"
"POT-Creation-Date: 2022-01-17 09:16+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.0\n"
"Generated-By: Babel 2.9.1\n"
#: project/i10n.py:13
msgid "Event_Art"
@ -193,29 +193,29 @@ msgstr ""
msgid "message"
msgstr ""
#: project/api/organization/resources.py:399
#: project/api/organization/resources.py:398
#: project/views/admin_unit_member_invitation.py:85
msgid "You have received an invitation"
msgstr ""
#: project/forms/admin.py:10 project/templates/layout.html:313
#: project/forms/admin.py:10 project/templates/layout.html:314
#: project/views/root.py:37
msgid "Terms of service"
msgstr ""
#: project/forms/admin.py:11 project/templates/layout.html:317
#: project/forms/admin.py:11 project/templates/layout.html:318
#: project/views/root.py:45
msgid "Legal notice"
msgstr ""
#: project/forms/admin.py:12 project/templates/_macros.html:1392
#: project/templates/layout.html:321
#: project/forms/admin.py:12 project/templates/_macros.html:1395
#: project/templates/layout.html:322
#: project/templates/widget/event_suggestion/create.html:204
#: project/views/admin_unit.py:73 project/views/root.py:53
msgid "Contact"
msgstr ""
#: project/forms/admin.py:13 project/templates/layout.html:325
#: project/forms/admin.py:13 project/templates/layout.html:326
#: project/views/root.py:61
msgid "Privacy"
msgstr ""
@ -330,7 +330,7 @@ msgstr ""
msgid "The short name is used to create a unique identifier for your events"
msgstr ""
#: project/forms/admin_unit.py:41 project/templates/_macros.html:1528
#: project/forms/admin_unit.py:41 project/templates/_macros.html:1531
msgid "Short name must contain only letters numbers or underscore"
msgstr ""
@ -344,7 +344,7 @@ msgstr ""
#: project/forms/admin_unit_member.py:23 project/forms/admin_unit_member.py:28
#: project/forms/event.py:107 project/forms/event_suggestion.py:38
#: project/forms/organizer.py:27 project/templates/_macros.html:235
#: project/templates/_macros.html:1488 project/templates/admin/users.html:19
#: project/templates/_macros.html:1491 project/templates/admin/users.html:19
msgid "Email"
msgstr ""
@ -535,16 +535,16 @@ msgstr ""
msgid "All-day"
msgstr ""
#: project/forms/event.py:54 project/templates/_macros.html:1708
#: project/forms/event.py:54 project/templates/_macros.html:1711
#: project/templates/widget/event_suggestion/create.html:240
msgid "Recurring event"
msgstr ""
#: project/forms/event.py:61 project/templates/_macros.html:1249
#: project/forms/event.py:61 project/templates/_macros.html:1252
msgid "The start must be before the end."
msgstr ""
#: project/forms/event.py:67 project/templates/_macros.html:1266
#: project/forms/event.py:67 project/templates/_macros.html:1269
msgid "An event can last a maximum of 14 days."
msgstr ""
@ -755,7 +755,7 @@ msgstr ""
#: project/forms/event.py:286 project/forms/event.py:295
#: project/forms/event.py:368 project/forms/event_suggestion.py:50
#: project/templates/_macros.html:436 project/templates/_macros.html:596
#: project/templates/_macros.html:436 project/templates/_macros.html:599
#: project/templates/event/create.html:284
#: project/templates/event/update.html:166
#: project/templates/event_place/create.html:31
@ -775,7 +775,7 @@ msgstr ""
#: project/forms/event.py:302 project/forms/event.py:311
#: project/forms/event.py:376 project/forms/event.py:439
#: project/forms/event_suggestion.py:60 project/templates/_macros.html:473
#: project/templates/_macros.html:633 project/templates/event/create.html:253
#: project/templates/_macros.html:636 project/templates/event/create.html:253
#: project/templates/event/update.html:156
#: project/templates/organizer/create.html:27
#: project/templates/organizer/delete.html:13
@ -873,7 +873,7 @@ msgstr ""
msgid "Update event"
msgstr ""
#: project/forms/event.py:423 project/templates/_macros.html:1221
#: project/forms/event.py:423 project/templates/_macros.html:1224
#: project/templates/event/actions.html:66
#: project/templates/event/delete.html:6
msgid "Delete event"
@ -1046,10 +1046,10 @@ msgid "Weekdays"
msgstr ""
#: project/forms/reference.py:11 project/forms/reference_request.py:16
#: project/templates/_macros.html:489 project/templates/_macros.html:649
#: project/templates/_macros.html:489 project/templates/_macros.html:652
#: project/templates/admin_unit/create.html:28
#: project/templates/admin_unit/update.html:29
#: project/templates/layout.html:261
#: project/templates/layout.html:262
msgid "Organization"
msgstr ""
@ -1074,7 +1074,7 @@ msgstr ""
msgid "Delete request"
msgstr ""
#: project/forms/reference_request.py:28 project/templates/_macros.html:1404
#: project/forms/reference_request.py:28 project/templates/_macros.html:1407
#: project/templates/event_suggestion/review_status.html:18
#: project/templates/reference_request/review_status.html:12
msgid "Review status"
@ -1176,58 +1176,63 @@ msgstr ""
msgid "Event"
msgstr ""
#: project/templates/_macros.html:391 project/templates/_macros.html:917
#: project/templates/_macros.html:391 project/templates/_macros.html:920
msgid "Date"
msgstr ""
#: project/templates/_macros.html:418 project/templates/_macros.html:577
#: project/templates/_macros.html:1473 project/templates/event/actions.html:51
#: project/templates/_macros.html:1476 project/templates/event/actions.html:51
msgid "Share"
msgstr ""
#: project/templates/_macros.html:422 project/templates/_macros.html:581
#: project/templates/_macros.html:1503
#: project/templates/_macros.html:1506
msgid "Add to calendar"
msgstr ""
#: project/templates/_macros.html:430 project/templates/_macros.html:589
#: project/templates/_macros.html:430 project/templates/_macros.html:592
#: project/templates/event/report.html:4
msgid "Report event"
msgstr ""
#: project/templates/_macros.html:457 project/templates/_macros.html:615
#: project/templates/_macros.html:457 project/templates/_macros.html:618
msgid "Show directions"
msgstr ""
#: project/templates/_macros.html:462 project/templates/_macros.html:620
#: project/templates/_macros.html:462 project/templates/_macros.html:623
msgid "The event takes place online."
msgstr ""
#: project/templates/_macros.html:464 project/templates/_macros.html:622
#: project/templates/_macros.html:464 project/templates/_macros.html:625
msgid "The event takes place both offline and online."
msgstr ""
#: project/templates/_macros.html:676 project/templates/event_date/list.html:5
#: project/templates/_macros.html:585 project/templates/layout.html:188
#: project/templates/user/favorite_events.html:4
msgid "Favorite events"
msgstr ""
#: project/templates/_macros.html:679 project/templates/event_date/list.html:5
#: project/templates/event_date/list.html:299
#: project/templates/reference_request/review.html:32
msgid "Event Dates"
msgstr ""
#: project/templates/_macros.html:768
#: project/templates/_macros.html:771
msgid "Search location on Google"
msgstr ""
#: project/templates/_macros.html:834
#: project/templates/_macros.html:837
#, python-format
msgid "%(count)d event dates"
msgstr ""
#: project/templates/_macros.html:857 project/templates/_macros.html:859
#: project/templates/_macros.html:860 project/templates/_macros.html:862
#: project/templates/event_date/list.html:321
msgid "First"
msgstr ""
#: project/templates/_macros.html:862 project/templates/_macros.html:864
#: project/templates/_macros.html:865 project/templates/_macros.html:867
#: project/templates/event_date/list.html:322
#: project/templates/widget/event_suggestion/create.html:193
#: project/templates/widget/event_suggestion/create.html:218
@ -1238,12 +1243,12 @@ msgstr ""
msgid "Previous"
msgstr ""
#: project/templates/_macros.html:866
#: project/templates/_macros.html:869
#, python-format
msgid "Page %(page)d of %(pages)d (%(total)d total)"
msgstr ""
#: project/templates/_macros.html:868 project/templates/_macros.html:870
#: project/templates/_macros.html:871 project/templates/_macros.html:873
#: project/templates/event_date/list.html:324
#: project/templates/widget/event_suggestion/create.html:194
#: project/templates/widget/event_suggestion/create.html:219
@ -1253,88 +1258,88 @@ msgstr ""
msgid "Next"
msgstr ""
#: project/templates/_macros.html:873 project/templates/_macros.html:875
#: project/templates/_macros.html:876 project/templates/_macros.html:878
#: project/templates/event_date/list.html:325
msgid "Last"
msgstr ""
#: project/templates/_macros.html:940
#: project/templates/_macros.html:943
msgid "Radius"
msgstr ""
#: project/templates/_macros.html:1150
#: project/templates/_macros.html:1153
msgid "Edit image"
msgstr ""
#: project/templates/_macros.html:1171
#: project/templates/_macros.html:1174
msgid "Close"
msgstr ""
#: project/templates/_macros.html:1172
#: project/templates/_macros.html:1175
msgid "Okay"
msgstr ""
#: project/templates/_macros.html:1184
#: project/templates/_macros.html:1187
msgid "Choose image file"
msgstr ""
#: project/templates/_macros.html:1220 project/templates/event/actions.html:65
#: project/templates/_macros.html:1223 project/templates/event/actions.html:65
#: project/templates/event/delete.html:12
msgid "Edit event"
msgstr ""
#: project/templates/_macros.html:1223 project/templates/manage/events.html:65
#: project/templates/_macros.html:1226 project/templates/manage/events.html:65
msgid "More"
msgstr ""
#: project/templates/_macros.html:1270
#: project/templates/_macros.html:1273
msgid "Please enter a valid time, between 00:00 and 23:59."
msgstr ""
#: project/templates/_macros.html:1298
#: project/templates/_macros.html:1301
#, python-format
msgid "Just use %(term)s"
msgstr ""
#: project/templates/_macros.html:1364
#: project/templates/_macros.html:1367
msgid "Event suggestion"
msgstr ""
#: project/templates/_macros.html:1482
#: project/templates/_macros.html:1485
msgid "Link copied"
msgstr ""
#: project/templates/_macros.html:1482
#: project/templates/_macros.html:1485
msgid "Copy link"
msgstr ""
#: project/templates/_macros.html:1511
#: project/templates/_macros.html:1514
msgid "Google calendar"
msgstr ""
#: project/templates/_macros.html:1512
#: project/templates/_macros.html:1515
msgid "Apple calendar"
msgstr ""
#: project/templates/_macros.html:1513
#: project/templates/_macros.html:1516
msgid "Yahoo calendar"
msgstr ""
#: project/templates/_macros.html:1514
#: project/templates/_macros.html:1517
msgid "Other calendar"
msgstr ""
#: project/templates/_macros.html:1709
#: project/templates/_macros.html:1712
msgid "Remove event date"
msgstr ""
#: project/templates/_macros.html:1738 project/templates/event/create.html:176
#: project/templates/_macros.html:1741 project/templates/event/create.html:176
#: project/templates/event/update.html:99
#: project/templates/widget/event_suggestion/create.html:129
msgid "Enter organizer"
msgstr ""
#: project/templates/_macros.html:1762
#: project/templates/_macros.html:1765
msgid "Enter list name"
msgstr ""
@ -1347,7 +1352,7 @@ msgstr ""
msgid "Register for free"
msgstr ""
#: project/templates/layout.html:175 project/templates/layout.html:219
#: project/templates/layout.html:175 project/templates/layout.html:220
#: project/templates/manage/events.html:6
#: project/templates/manage/events.html:41
#: project/templates/manage/events_vue.html:4
@ -1368,7 +1373,7 @@ msgstr ""
msgid "Planing"
msgstr ""
#: project/templates/layout.html:187 project/templates/layout.html:275
#: project/templates/layout.html:187 project/templates/layout.html:276
#: project/templates/oauth2_client/list.html:10
#: project/templates/oauth2_client/read.html:10
#: project/templates/oauth2_token/list.html:10 project/templates/profile.html:4
@ -1378,86 +1383,86 @@ msgstr ""
#: project/templates/admin/admin.html:3 project/templates/admin/admin.html:9
#: project/templates/admin/admin_units.html:10
#: project/templates/admin/users.html:10 project/templates/layout.html:190
#: project/templates/admin/users.html:10 project/templates/layout.html:191
msgid "Admin"
msgstr ""
#: project/templates/layout.html:194
#: project/templates/layout.html:195
msgid "Logout"
msgstr ""
#: project/templates/layout.html:225
#: project/templates/layout.html:226
msgid "Show events"
msgstr ""
#: project/templates/event/create.html:5
#: project/templates/event/create.html:221 project/templates/layout.html:226
#: project/templates/event/create.html:221 project/templates/layout.html:227
#: project/templates/manage/events.html:44
#: project/templates/manage/organizers.html:21
msgid "Create event"
msgstr ""
#: project/templates/layout.html:227
#: project/templates/layout.html:228
msgid "Import event"
msgstr ""
#: project/templates/layout.html:229
#: project/templates/layout.html:230
#: project/templates/manage/event_lists.html:4
msgid "Event lists"
msgstr ""
#: project/templates/layout.html:232
#: project/templates/layout.html:233
msgid "Review suggestions"
msgstr ""
#: project/templates/layout.html:242
#: project/templates/layout.html:243
#: project/templates/manage/references_incoming.html:5
#: project/templates/manage/references_outgoing.html:5
msgid "References"
msgstr ""
#: project/templates/layout.html:248
#: project/templates/layout.html:249
#: project/templates/manage/references_incoming.html:9
msgid "Incoming references"
msgstr ""
#: project/templates/layout.html:249
#: project/templates/layout.html:250
#: project/templates/manage/references_outgoing.html:9
msgid "Outgoing references"
msgstr ""
#: project/templates/layout.html:251
#: project/templates/layout.html:252
#: project/templates/manage/reference_requests_incoming.html:9
msgid "Incoming reference requests"
msgstr ""
#: project/templates/layout.html:256
#: project/templates/layout.html:257
#: project/templates/manage/reference_requests_outgoing.html:9
msgid "Outgoing reference requests"
msgstr ""
#: project/templates/layout.html:264 project/templates/manage/organizers.html:5
#: project/templates/layout.html:265 project/templates/manage/organizers.html:5
#: project/templates/manage/organizers.html:9
msgid "Organizers"
msgstr ""
#: project/templates/event_place/list.html:3
#: project/templates/event_place/list.html:7 project/templates/layout.html:265
#: project/templates/event_place/list.html:7 project/templates/layout.html:266
#: project/templates/manage/places.html:5
#: project/templates/manage/places.html:9
msgid "Places"
msgstr ""
#: project/templates/layout.html:267 project/templates/manage/members.html:5
#: project/templates/layout.html:268 project/templates/manage/members.html:5
#: project/templates/manage/members.html:28
msgid "Members"
msgstr ""
#: project/templates/layout.html:268 project/templates/manage/relations.html:4
#: project/templates/layout.html:269 project/templates/manage/relations.html:4
msgid "Relations"
msgstr ""
#: project/templates/layout.html:270
#: project/templates/layout.html:271
#: project/templates/manage/admin_units.html:17
#: project/templates/manage/organization_invitations.html:4
#: project/templates/user/organization_invitations.html:4
@ -1469,27 +1474,27 @@ msgstr ""
#: project/templates/admin/settings.html:8
#: project/templates/admin_unit/update.html:6
#: project/templates/admin_unit/update.html:23
#: project/templates/layout.html:272 project/templates/manage/widgets.html:11
#: project/templates/layout.html:273 project/templates/manage/widgets.html:11
#: project/templates/manage/widgets.html:15 project/templates/profile.html:19
msgid "Settings"
msgstr ""
#: project/templates/layout.html:273
#: project/templates/layout.html:274
#: project/templates/manage/custom_widgets.html:13
msgid "Custom widgets"
msgstr ""
#: project/templates/layout.html:274 project/templates/manage/reviews.html:10
#: project/templates/layout.html:275 project/templates/manage/reviews.html:10
#: project/templates/manage/widgets.html:5
#: project/templates/manage/widgets.html:9
msgid "Widgets"
msgstr ""
#: project/templates/layout.html:285
#: project/templates/layout.html:286
msgid "Switch organization"
msgstr ""
#: project/templates/developer/read.html:4 project/templates/layout.html:335
#: project/templates/developer/read.html:4 project/templates/layout.html:336
#: project/templates/profile.html:29
msgid "Developer"
msgstr ""
@ -1942,7 +1947,7 @@ msgstr ""
msgid "Widget"
msgstr ""
#: project/templates/widget/event_date/list.html:149
#: project/templates/widget/event_date/list.html:153
msgid "Print"
msgstr ""

View File

@ -0,0 +1,41 @@
"""empty message
Revision ID: 40873357f372
Revises: c5fbefbe9881
Create Date: 2022-01-13 23:08:14.098369
"""
import sqlalchemy as sa
import sqlalchemy_utils
from alembic import op
from project import dbtypes
# revision identifiers, used by Alembic.
revision = "40873357f372"
down_revision = "c5fbefbe9881"
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"user_favoriteevents",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=False),
sa.Column("event_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["event_id"],
["event.id"],
),
sa.ForeignKeyConstraint(
["user_id"],
["user.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("user_id", "event_id"),
)
def downgrade():
op.drop_table("user_favoriteevents")

View File

@ -80,6 +80,7 @@ class EventResource(BaseResource):
@marshal_with(EventSchema)
@require_oauth(optional=True)
def get(self, id):
login_api_user()
event = get_event_with_details_or_404(id)
api_can_read_event_or_401(event)
return event
@ -163,7 +164,9 @@ class EventSearchResource(BaseResource):
@doc(summary="Search for events", tags=["Events"])
@use_kwargs(EventSearchRequestSchema, location=("query"))
@marshal_with(EventSearchResponseSchema)
@require_oauth(optional=True)
def get(self, **kwargs):
login_api_user()
params = EventSearchParams()
params.load_from_request()
pagination = get_events_query(params).paginate()

View File

@ -1,3 +1,4 @@
from flask_security import current_user
from marshmallow import fields, validate
from marshmallow.decorators import pre_load
from marshmallow_enum import EnumField
@ -147,7 +148,22 @@ class EventBaseSchemaMixin(TrackableSchemaMixin):
)
class EventSchema(EventIdSchema, EventBaseSchemaMixin):
class EventCurrentUserMixin(object):
is_favored = fields.Method(
"get_is_favored",
metadata={"description": "True, if event is favored by current user"},
)
def get_is_favored(self, event):
if not current_user or not current_user.is_authenticated:
return False
from project.services.user import has_favorite_event
return has_favorite_event(current_user.id, event.id)
class EventSchema(EventIdSchema, EventBaseSchemaMixin, EventCurrentUserMixin):
organization = fields.Nested(OrganizationRefSchema, attribute="admin_unit")
organizer = fields.Nested(OrganizerRefSchema)
place = fields.Nested(PlaceRefSchema, attribute="event_place")
@ -175,7 +191,7 @@ class EventRefSchema(EventIdSchema):
name = marshmallow.auto_field()
class EventSearchItemSchema(EventRefSchema):
class EventSearchItemSchema(EventRefSchema, EventCurrentUserMixin):
description = marshmallow.auto_field()
date_definitions = fields.List(fields.Nested(EventDateDefinitionSchema))
photo = fields.Nested(ImageSchema)
@ -199,6 +215,16 @@ class EventListResponseSchema(PaginationResponseSchema):
)
class UserFavoriteEventListRequestSchema(PaginationRequestSchema):
pass
class UserFavoriteEventListResponseSchema(PaginationResponseSchema):
items = fields.List(
fields.Nested(EventRefSchema), metadata={"description": "Events"}
)
class EventSearchRequestSchema(PaginationRequestSchema):
keyword = fields.Str(
metadata={"description": "Looks for keyword in name, description and tags."},

View File

@ -2,6 +2,7 @@ from flask_apispec import doc, marshal_with, use_kwargs
from sqlalchemy import and_
from sqlalchemy.orm import defaultload, lazyload
from project.access import login_api_user
from project.api import add_api_resource
from project.api.event.resources import api_can_read_event_or_401
from project.api.event_date.schemas import (
@ -56,7 +57,9 @@ class EventDateSearchResource(BaseResource):
@doc(summary="Search for event dates", tags=["Event Dates"])
@use_kwargs(EventDateSearchRequestSchema, location=("query"))
@marshal_with(EventDateSearchResponseSchema)
@require_oauth(optional=True)
def get(self, **kwargs):
login_api_user()
params = EventSearchParams()
params.load_from_request()
pagination = get_event_dates_query(params).paginate()

View File

@ -6,14 +6,22 @@ from flask_security import current_user
from project import db
from project.access import login_api_user_or_401
from project.api import add_api_resource
from project.api.event.schemas import (
EventSearchRequestSchema,
EventSearchResponseSchema,
UserFavoriteEventListRequestSchema,
UserFavoriteEventListResponseSchema,
)
from project.api.organization_invitation.schemas import (
OrganizationInvitationListRequestSchema,
OrganizationInvitationListResponseSchema,
OrganizationInvitationSchema,
)
from project.api.resources import BaseResource, require_api_access
from project.models import AdminUnitInvitation
from project.models import AdminUnitInvitation, Event
from project.services.admin_unit import get_admin_unit_organization_invitations_query
from project.services.event import get_events_query
from project.services.event_search import EventSearchParams
from project.utils import strings_are_equal_ignoring_case
@ -74,6 +82,82 @@ class UserOrganizationInvitationResource(BaseResource):
return make_response("", 204)
class UserFavoriteEventListResource(BaseResource):
@doc(
summary="List favorite events of user",
tags=["Users", "Events"],
security=[{"oauth2": ["user:read"]}],
)
@use_kwargs(UserFavoriteEventListRequestSchema, location=("query"))
@marshal_with(UserFavoriteEventListResponseSchema)
@require_api_access("user:read")
def get(self, **kwargs):
from project.services.user import get_favorite_events_query
login_api_user_or_401()
pagination = get_favorite_events_query(current_user.id).paginate()
return pagination
class UserFavoriteEventSearchResource(BaseResource):
@doc(
summary="Search for favorite events of user",
tags=["Users", "Events"],
security=[{"oauth2": ["user:read"]}],
)
@use_kwargs(EventSearchRequestSchema, location=("query"))
@marshal_with(EventSearchResponseSchema)
@require_api_access("user:read")
def get(self, **kwargs):
login_api_user_or_401()
params = EventSearchParams()
params.load_from_request()
params.favored_by_user_id = current_user.id
pagination = get_events_query(params).paginate()
return pagination
class UserFavoriteEventListWriteResource(BaseResource):
@doc(
summary="Add event to users favorites",
tags=["Users", "Events"],
security=[{"oauth2": ["user:write"]}],
)
@marshal_with(None, 204)
@require_api_access("user:write")
def put(self, event_id):
from project.services.user import add_favorite_event
login_api_user_or_401()
event = Event.query.get_or_404(event_id)
if add_favorite_event(current_user.id, event.id):
db.session.commit()
return make_response("", 204)
@doc(
summary="Remove event from users favorites",
tags=["Users", "Events"],
security=[{"oauth2": ["user:write"]}],
)
@marshal_with(None, 204)
@require_api_access("user:write")
def delete(self, event_id):
from project.services.user import remove_favorite_event
login_api_user_or_401()
event = Event.query.get_or_404(event_id)
if remove_favorite_event(current_user.id, event.id):
db.session.commit()
return make_response("", 204)
add_api_resource(
UserOrganizationInvitationListResource,
"/user/organization-invitations",
@ -85,3 +169,21 @@ add_api_resource(
"/user/organization-invitation/<int:id>",
"api_v1_user_organization_invitation",
)
add_api_resource(
UserFavoriteEventListResource,
"/user/favorite-events",
"api_v1_user_favorite_event_list",
)
add_api_resource(
UserFavoriteEventSearchResource,
"/user/favorite-events/search",
"api_v1_user_favorite_event_search",
)
add_api_resource(
UserFavoriteEventListWriteResource,
"/user/favorite-events/<int:event_id>",
"api_v1_user_favorite_event_list_write",
)

View File

@ -193,6 +193,11 @@ class User(db.Model, UserMixin):
roles = relationship(
"Role", secondary="roles_users", backref=backref("users", lazy="dynamic")
)
favorite_events = relationship(
"Event",
secondary="user_favoriteevents",
backref=backref("favored_by_users", lazy=True),
)
def get_user_id(self):
return self.id
@ -1014,6 +1019,14 @@ class Event(db.Model, TrackableMixin, EventMixin):
def has_multiple_dates(self) -> bool:
return self.is_recurring or len(self.date_definitions) > 1
def is_favored_by_current_user(self) -> bool:
if not current_user or not current_user.is_authenticated:
return False
from project.services.user import has_favorite_event
return has_favorite_event(current_user.id, self.id)
def validate(self):
if self.organizer and self.organizer.admin_unit_id != self.admin_unit_id:
raise make_check_violation("Invalid organizer.")
@ -1143,6 +1156,14 @@ class EventEventLists(db.Model):
list_id = db.Column(db.Integer, db.ForeignKey("eventlist.id"), nullable=False)
class UserFavoriteEvents(db.Model):
__tablename__ = "user_favoriteevents"
__table_args__ = (UniqueConstraint("user_id", "event_id"),)
id = Column(Integer(), primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
event_id = db.Column(db.Integer, db.ForeignKey("event.id"), nullable=False)
class Analytics(db.Model):
__tablename__ = "analytics"
id = Column(Integer(), primary_key=True)

View File

@ -32,6 +32,7 @@ from project.models import (
Image,
Location,
PublicStatus,
UserFavoriteEvents,
sanitize_allday_instance,
)
from project.utils import get_pending_changes, get_place_str
@ -94,6 +95,16 @@ def fill_event_filter(event_filter, params):
func.ST_DistanceSphere(Location.coordinate, point) <= params.distance,
)
if params.favored_by_user_id:
user_favorite_exists = UserFavoriteEvents.query.filter(
UserFavoriteEvents.event_id == Event.id,
UserFavoriteEvents.user_id == params.favored_by_user_id,
).exists()
event_filter = and_(
event_filter,
user_favorite_exists,
)
return event_filter

View File

@ -28,6 +28,7 @@ class EventSearchParams(object):
self.weekday = None
self.sort = None
self.status = None
self.favored_by_user_id = None
@property
def date_from(self):

View File

@ -1,7 +1,7 @@
from flask_security import hash_password
from project import user_datastore
from project.models import Role, User
from project.models import Event, Role, User, UserFavoriteEvents
def create_user(email, password):
@ -57,3 +57,46 @@ def get_user(id):
def find_all_users_with_role(role_name: str) -> list:
return User.query.filter(User.roles.any(Role.name == role_name)).all()
def get_favorite_events_query(user_id: int):
return Event.query.join(
UserFavoriteEvents, UserFavoriteEvents.event_id == Event.id
).filter(UserFavoriteEvents.user_id == user_id)
def get_favorite_event(user_id: int, event_id: int) -> UserFavoriteEvents:
return UserFavoriteEvents.query.filter(
UserFavoriteEvents.event_id == event_id,
UserFavoriteEvents.user_id == user_id,
).first()
def has_favorite_event(user_id: int, event_id: int) -> bool:
if get_favorite_event(user_id, event_id):
return True
return False
def add_favorite_event(user_id: int, event_id: int) -> bool:
from project import db
if has_favorite_event(user_id, event_id):
return False
favorite = UserFavoriteEvents(user_id=user_id, event_id=event_id)
db.session.add(favorite)
return True
def remove_favorite_event(user_id: int, event_id: int):
from project import db
favorite = get_favorite_event(user_id, event_id)
if not favorite:
return False
db.session.delete(favorite)
return True

26
project/static/user.js Normal file
View File

@ -0,0 +1,26 @@
$(function () {
$('.toggle-user-event-favorite').click(function (e) {
var button = $(this);
var event_id = button.data("event-id");
var icon = button.find("i");
var is_favored = icon.hasClass("fa");
if (is_favored) {
$.ajax({
url: "/api/v1/user/favorite-events/" + event_id,
type: "delete",
success: function () {
icon.removeClass("fa").addClass("far");
}
});
} else {
$.ajax({
url: "/api/v1/user/favorite-events/" + event_id,
type: "put",
success: function () {
icon.removeClass("far").addClass("fa");
}
});
}
});
});

View File

@ -0,0 +1,22 @@
const EventWarningPills = {
template: `
<span>
<span v-if="event.status" class="badge badge-pill badge-warning">
<template v-if="event.status == 'cancelled'">Abgesagt</template>
<template v-else-if="event.status == 'movedOnline'">Online verschoben</template>
<template v-else-if="event.status == 'postponed'">Verschoben</template>
<template v-else-if="event.status == 'rescheduled'">Neu angesetzt</template>
</span>
<span v-if="event.booked_up" class="badge badge-pill badge-warning">Ausgebucht</span>
<span v-if="event.attendance_mode" class="badge badge-pill badge-info">
<template v-if="event.attendance_mode == 'online'">Online</template>
<template v-else-if="event.attendance_mode == 'mixed'">Präsenzveranstaltung und online</template>
</span>
</span>
`,
props: {
event: {
type: null
},
},
};

View File

@ -0,0 +1,127 @@
const UserFavoriteEventList = {
template: `
<div>
<h1>{{ $t("comp.title") }}</h1>
<b-overlay :show="isLoading" variant="transparent">
<div v-if="errorMessage" class="mb-4">
<b-alert show variant="danger">{{ errorMessage }}</b-alert>
<button type="button" class="btn btn-outline-secondary" @click="loadData()"><i class="fa fa-sync-alt"></i></button>
</div>
<div v-for="event in items">
<div class="card mb-3">
<div>
<img v-if="event.photo" :src="url_for_image(event.photo, 500)" class="card-img-top" style="object-fit: cover; height: 40vw;" />
</div>
<div class="card-body">
<h5 class="card-title"><a href="#" @click.stop.prevent="viewItem(event.id)">{{ event.name }}</a> <event-warning-pills :event="event"></event-warning-pills></h5>
<h6 class="card-subtitle mb-2 text-body"><i class="fa fa-calendar"></i> {{ $root.render_event_date_instance(event.date_definitions[0].start, event.date_definitions[0].allday) }}</h6>
<p class="card-text" v-if="event.description" v-html="event.description.truncate(100, true)"></p>
<small class="text-muted mr-2"><i class="fa fa-database"></i> {{ event.organization.name }}</small>
<small v-if="event.organizer.name != event.organization.name" class="text-muted mr-2"><i class="fa fa-server"></i> {{ event.organizer.name }}</small>
<small class="text-muted"><i class="fa fa-map-marker"></i> {{ event.place.name }}</small>
</div>
<div class="card-footer">
<a href="#" @click.stop.prevent="removeItem(event.id)" class="card-link">{{ $t("shared.delete") }}&hellip;</a>
</div>
</div>
</div>
<b-pagination v-if="totalRows > perPage"
v-model="currentPage"
:total-rows="totalRows"
:per-page="perPage"
></b-pagination>
</b-overlay>
</div>
`,
i18n: {
messages: {
en: {
comp: {
title: "Favorite events",
removedMessage: "Event successfully removed",
removeConfirmation: "Do you really want to remove the event?",
},
},
de: {
comp: {
title: "Merkzettel",
removedMessage: "Veranstaltung erfolgreich entfernt",
removeConfirmation:
"Möchtest du die Veranstaltung wirklich entfernen?",
},
},
},
},
data: () => ({
errorMessage: null,
isLoading: false,
fields: [
{
key: "name",
label: i18n.t("shared.models.event.name"),
},
],
totalRows: 0,
currentPage: 1,
perPage: 10,
items: [],
}),
mounted() {
this.isLoading = false;
this.loadData();
},
methods: {
loadData() {
const vm = this;
axios
.get(`/api/v1/user/favorite-events/search`, {
params: {
page: vm.currentPage,
per_page: vm.perPage,
},
withCredentials: true,
handler: this,
handleLoading: this.handleLoading,
})
.then((response) => {
vm.totalRows = response.data.total;
vm.items = response.data.items;
})
.catch(() => {
callback([]);
});
return null;
},
refreshTableData() {
this.$refs.table.refresh();
},
handleLoading(isLoading) {
this.isLoading = isLoading;
},
handleRequestStart() {
this.errorMessage = null;
},
handleRequestError(error, message) {
this.errorMessage = message;
},
viewItem(id) {
window.location.href = `/event/${id}`;
},
removeItem(id) {
if (confirm(this.$t("comp.removeConfirmation"))) {
axios
.delete(`/api/v1/user/favorite-events/${id}`, {
withCredentials: true,
})
.then(() => {
this.$root.makeSuccessToast(this.$t("comp.removedMessage"));
this.loadData();
});
}
},
},
};

View File

@ -523,7 +523,7 @@
{{ render_fax_prop(organizer.fax) }}
{% endmacro %}
{% macro render_event_props_seo(event, start, end, allday, dates = None, show_rating = False, show_admin_unit = True, user_rights=None, share_links=None, calendar_links=None) %}
{% macro render_event_props_seo(event, start, end, allday, dates = None, show_rating = False, show_admin_unit = True, user_rights=None, share_links=None, calendar_links=None, current_user=None) %}
<div class="w-normal mx-auto">
{% if event.photo_id %}
@ -571,7 +571,7 @@
<div class="mt-4" style="white-space:pre-wrap;">{{ event.description|urlize(nofollow=True, target='_blank', rel="nofollow") }}</div>
{% endif %}
{% if share_links or calendar_links %}
{% if share_links or calendar_links or (current_user and current_user.is_authenticated) %}
<div class="mt-4">
{% if share_links %}
<button type="button" class="btn btn-outline-secondary mr-1 mb-1" data-toggle="modal" data-target="#shareModal"><i class="fa fa-share-alt"></i> {{ _('Share') }}</button>
@ -581,6 +581,9 @@
<button type="button" class="btn btn-outline-secondary mb-1" data-toggle="modal" data-target="#calendarExportModal"><i class="fa fa-calendar"></i> {{ _('Add to calendar') }}</button>
{{ render_calendar_export_modal(calendar_links) }}
{% endif %}
{% if current_user and current_user.is_authenticated %}
<button type="button" class="btn btn-outline-secondary mb-1 toggle-user-event-favorite" data-event-id="{{ event.id }}"><i class="{%if event.is_favored_by_current_user() %}fa{% else %}far{% endif %} fa-bookmark"></i> {{ _('Favorite events') }}</button>
{% endif %}
</div>
{% endif %}
@ -1761,4 +1764,4 @@ $('#event_list_ids').select2({
},
placeholder: "{{ _('Enter list name') }}"
});
{% endmacro %}
{% endmacro %}

View File

@ -3,9 +3,12 @@
{%- block title -%}
{{ event.name }}
{%- endblock -%}
{% block header_before_site_js %}
<script src="{{ url_for('static', filename='user.js')}}"></script>
{%- endblock -%}
{% block content_container_attribs %}{% endblock %}
{% block content %}
{{ render_event_props_seo(event, event.min_start_definition.start, event.min_start_definition.end, event.min_start_definition.allday, dates, user_rights['can_update_event'], user_rights=user_rights, share_links=share_links) }}
{{ render_event_props_seo(event, event.min_start_definition.start, event.min_start_definition.end, event.min_start_definition.allday, dates, user_rights['can_update_event'], user_rights=user_rights, share_links=share_links, current_user=current_user) }}
{% endblock %}

View File

@ -4,9 +4,12 @@
{%- block title -%}
{{ meta['title'] }}
{%- endblock -%}
{% block header_before_site_js %}
<script src="{{ url_for('static', filename='user.js')}}"></script>
{%- endblock -%}
{% block content_container_attribs %}{% endblock %}
{% block content %}
{{ render_event_props_seo(event, event_date.start, event_date.end, event_date.allday, dates, user_rights=user_rights, share_links=share_links, calendar_links=calendar_links) }}
{{ render_event_props_seo(event, event_date.start, event_date.end, event_date.allday, dates, user_rights=user_rights, share_links=share_links, calendar_links=calendar_links, current_user=current_user) }}
{% endblock %}

View File

@ -185,6 +185,7 @@
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarUserDropdown">
<a class="dropdown-item" href="{{ url_for('manage_admin_units') }}">{{ _('Organizations') }}</a>
<a class="dropdown-item" href="{{ url_for('profile') }}">{{ _('Profile') }}</a>
<a class="dropdown-item" href="{{ url_for('user_favorite_events') }}">{{ _('Favorite events') }}</a>
{% if current_user.has_role('admin') %}
<a class="dropdown-item" href="{{ url_for('admin') }}">{{ _('Admin') }}</a>

View File

@ -28,6 +28,7 @@
<script src="{{ url_for('static', filename='vue/common/validated-input.vue.js')}}"></script>
<script src="{{ url_for('static', filename='vue/common/validated-switch.vue.js')}}"></script>
<script src="{{ url_for('static', filename='vue/common/validated-textarea.vue.js')}}"></script>
<script src="{{ url_for('static', filename='vue/common/event-warning-pills.vue.js')}}"></script>
{% block component_scripts %}
{% endblock %}
@ -46,6 +47,7 @@
Vue.component("validated-input", ValidatedInput);
Vue.component("validated-switch", ValidatedSwitch);
Vue.component("validated-textarea", ValidatedTextarea);
Vue.component("event-warning-pills", EventWarningPills);
{% block component_definitions %}
{% endblock %}
@ -453,6 +455,18 @@
goBack(fallbackPath) {
window.history.length > 1 ? this.$router.go(-1) : this.$router.push({ path: fallbackPath })
},
render_event_date_instance(value, allday, format = "dd. DD.MM.YYYY LT", alldayFormat = "dd. DD.MM.YYYY") {
const instance = moment(value);
if (allday) {
return instance.format(alldayFormat);
}
return instance.format(format);
},
url_for_image(image, size) {
return `${axios.defaults.baseURL}${image.image_url}?s=${size}`
},
},
});
</script>

View File

@ -0,0 +1,22 @@
{% extends "layout_vue.html" %}
{%- block title -%}
{{ _('Favorite events') }}
{%- endblock -%}
{% block component_scripts %}
<script src="{{ url_for('static', filename='vue/user-favorite-events/list.vue.js')}}"></script>
{% endblock %}
{% block component_definitions %}
Vue.component("UserFavoriteEventList", UserFavoriteEventList);
{% endblock %}
{% block vue_routes %}
const routes = [
{
path: "/user/favorite-events",
component: UserFavoriteEventList,
},
];
{% 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: 2022-01-11 23:44+0100\n"
"POT-Creation-Date: 2022-01-17 09:16+0100\n"
"PO-Revision-Date: 2020-06-07 18:51+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.0\n"
"Generated-By: Babel 2.9.1\n"
#: project/i10n.py:13
msgid "Event_Art"
@ -194,29 +194,29 @@ msgstr "."
msgid "message"
msgstr "message"
#: project/api/organization/resources.py:399
#: project/api/organization/resources.py:398
#: project/views/admin_unit_member_invitation.py:85
msgid "You have received an invitation"
msgstr "Du hast eine Einladung erhalten"
#: project/forms/admin.py:10 project/templates/layout.html:313
#: project/forms/admin.py:10 project/templates/layout.html:314
#: project/views/root.py:37
msgid "Terms of service"
msgstr "Nutzungsbedingungen"
#: project/forms/admin.py:11 project/templates/layout.html:317
#: project/forms/admin.py:11 project/templates/layout.html:318
#: project/views/root.py:45
msgid "Legal notice"
msgstr "Impressum"
#: project/forms/admin.py:12 project/templates/_macros.html:1392
#: project/templates/layout.html:321
#: project/forms/admin.py:12 project/templates/_macros.html:1395
#: project/templates/layout.html:322
#: project/templates/widget/event_suggestion/create.html:204
#: project/views/admin_unit.py:73 project/views/root.py:53
msgid "Contact"
msgstr "Kontakt"
#: project/forms/admin.py:13 project/templates/layout.html:325
#: project/forms/admin.py:13 project/templates/layout.html:326
#: project/views/root.py:61
msgid "Privacy"
msgstr "Datenschutz"
@ -342,7 +342,7 @@ msgstr ""
"eindeutig zu identifizieren. Der Kurzname darf nur Buchstaben, Nummern "
"und Unterstriche enthalten."
#: project/forms/admin_unit.py:41 project/templates/_macros.html:1528
#: project/forms/admin_unit.py:41 project/templates/_macros.html:1531
msgid "Short name must contain only letters numbers or underscore"
msgstr "Der Kurzname darf nur Buchstaben, Nummern und Unterstriche enthalten"
@ -356,7 +356,7 @@ msgstr "Link URL"
#: project/forms/admin_unit_member.py:23 project/forms/admin_unit_member.py:28
#: project/forms/event.py:107 project/forms/event_suggestion.py:38
#: project/forms/organizer.py:27 project/templates/_macros.html:235
#: project/templates/_macros.html:1488 project/templates/admin/users.html:19
#: project/templates/_macros.html:1491 project/templates/admin/users.html:19
msgid "Email"
msgstr "Email"
@ -554,16 +554,16 @@ msgstr "Gib an, wann der Termin endet. Ein Termin darf maximal 14 Tage dauern."
msgid "All-day"
msgstr "Ganztägig"
#: project/forms/event.py:54 project/templates/_macros.html:1708
#: project/forms/event.py:54 project/templates/_macros.html:1711
#: project/templates/widget/event_suggestion/create.html:240
msgid "Recurring event"
msgstr "Serientermin"
#: project/forms/event.py:61 project/templates/_macros.html:1249
#: project/forms/event.py:61 project/templates/_macros.html:1252
msgid "The start must be before the end."
msgstr "Der Start muss vor dem Ende sein."
#: project/forms/event.py:67 project/templates/_macros.html:1266
#: project/forms/event.py:67 project/templates/_macros.html:1269
msgid "An event can last a maximum of 14 days."
msgstr "Eine Veranstaltung darf maximal 14 Tage dauern."
@ -791,7 +791,7 @@ msgstr "Ungültiger Mitveranstalter."
#: project/forms/event.py:286 project/forms/event.py:295
#: project/forms/event.py:368 project/forms/event_suggestion.py:50
#: project/templates/_macros.html:436 project/templates/_macros.html:596
#: project/templates/_macros.html:436 project/templates/_macros.html:599
#: project/templates/event/create.html:284
#: project/templates/event/update.html:166
#: project/templates/event_place/create.html:31
@ -811,7 +811,7 @@ msgstr "Neuen Ort eingeben"
#: project/forms/event.py:302 project/forms/event.py:311
#: project/forms/event.py:376 project/forms/event.py:439
#: project/forms/event_suggestion.py:60 project/templates/_macros.html:473
#: project/templates/_macros.html:633 project/templates/event/create.html:253
#: project/templates/_macros.html:636 project/templates/event/create.html:253
#: project/templates/event/update.html:156
#: project/templates/organizer/create.html:27
#: project/templates/organizer/delete.html:13
@ -913,7 +913,7 @@ msgstr "Wähle den öffentlichen Status der Veranstaltung."
msgid "Update event"
msgstr "Veranstaltung aktualisieren"
#: project/forms/event.py:423 project/templates/_macros.html:1221
#: project/forms/event.py:423 project/templates/_macros.html:1224
#: project/templates/event/actions.html:66
#: project/templates/event/delete.html:6
msgid "Delete event"
@ -1090,10 +1090,10 @@ msgid "Weekdays"
msgstr "Wochentage"
#: project/forms/reference.py:11 project/forms/reference_request.py:16
#: project/templates/_macros.html:489 project/templates/_macros.html:649
#: project/templates/_macros.html:489 project/templates/_macros.html:652
#: project/templates/admin_unit/create.html:28
#: project/templates/admin_unit/update.html:29
#: project/templates/layout.html:261
#: project/templates/layout.html:262
msgid "Organization"
msgstr "Organisation"
@ -1118,7 +1118,7 @@ msgstr "Anfrage speichern"
msgid "Delete request"
msgstr "Anfrage löschen"
#: project/forms/reference_request.py:28 project/templates/_macros.html:1404
#: project/forms/reference_request.py:28 project/templates/_macros.html:1407
#: project/templates/event_suggestion/review_status.html:18
#: project/templates/reference_request/review_status.html:12
msgid "Review status"
@ -1220,60 +1220,65 @@ msgstr "Zuletzt aktualisiert am %(updated_at)s."
msgid "Event"
msgstr "Veranstaltung"
#: project/templates/_macros.html:391 project/templates/_macros.html:917
#: project/templates/_macros.html:391 project/templates/_macros.html:920
msgid "Date"
msgstr "Datum"
#: project/templates/_macros.html:418 project/templates/_macros.html:577
#: project/templates/_macros.html:1473 project/templates/event/actions.html:51
#: project/templates/_macros.html:1476 project/templates/event/actions.html:51
msgid "Share"
msgstr "Teilen"
#: project/templates/_macros.html:422 project/templates/_macros.html:581
#: project/templates/_macros.html:1503
#: project/templates/_macros.html:1506
msgid "Add to calendar"
msgstr "Zum Kalender"
#: project/templates/_macros.html:430 project/templates/_macros.html:589
#: project/templates/_macros.html:430 project/templates/_macros.html:592
#: project/templates/event/report.html:4
msgid "Report event"
msgstr "Veranstaltung melden"
#: project/templates/_macros.html:457 project/templates/_macros.html:615
#: project/templates/_macros.html:457 project/templates/_macros.html:618
msgid "Show directions"
msgstr "Anreise planen"
#: project/templates/_macros.html:462 project/templates/_macros.html:620
#: project/templates/_macros.html:462 project/templates/_macros.html:623
msgid "The event takes place online."
msgstr "Die Veranstaltung findet online statt."
#: project/templates/_macros.html:464 project/templates/_macros.html:622
#: project/templates/_macros.html:464 project/templates/_macros.html:625
msgid "The event takes place both offline and online."
msgstr ""
"Die Veranstaltung findet sowohl als Präsenzveranstaltung als auch online "
"statt."
#: project/templates/_macros.html:676 project/templates/event_date/list.html:5
#: project/templates/_macros.html:585 project/templates/layout.html:188
#: project/templates/user/favorite_events.html:4
msgid "Favorite events"
msgstr "Merkzettel"
#: project/templates/_macros.html:679 project/templates/event_date/list.html:5
#: project/templates/event_date/list.html:299
#: project/templates/reference_request/review.html:32
msgid "Event Dates"
msgstr "Termine"
#: project/templates/_macros.html:768
#: project/templates/_macros.html:771
msgid "Search location on Google"
msgstr "Ort bei Google suchen"
#: project/templates/_macros.html:834
#: project/templates/_macros.html:837
#, python-format
msgid "%(count)d event dates"
msgstr "%(count)d Termine"
#: project/templates/_macros.html:857 project/templates/_macros.html:859
#: project/templates/_macros.html:860 project/templates/_macros.html:862
#: project/templates/event_date/list.html:321
msgid "First"
msgstr "Letzte"
#: project/templates/_macros.html:862 project/templates/_macros.html:864
#: project/templates/_macros.html:865 project/templates/_macros.html:867
#: project/templates/event_date/list.html:322
#: project/templates/widget/event_suggestion/create.html:193
#: project/templates/widget/event_suggestion/create.html:218
@ -1284,12 +1289,12 @@ msgstr "Letzte"
msgid "Previous"
msgstr "Zurück"
#: project/templates/_macros.html:866
#: project/templates/_macros.html:869
#, python-format
msgid "Page %(page)d of %(pages)d (%(total)d total)"
msgstr "Seite %(page)d von %(pages)d (%(total)d insgesamt)"
#: project/templates/_macros.html:868 project/templates/_macros.html:870
#: project/templates/_macros.html:871 project/templates/_macros.html:873
#: project/templates/event_date/list.html:324
#: project/templates/widget/event_suggestion/create.html:194
#: project/templates/widget/event_suggestion/create.html:219
@ -1299,88 +1304,88 @@ msgstr "Seite %(page)d von %(pages)d (%(total)d insgesamt)"
msgid "Next"
msgstr "Weiter"
#: project/templates/_macros.html:873 project/templates/_macros.html:875
#: project/templates/_macros.html:876 project/templates/_macros.html:878
#: project/templates/event_date/list.html:325
msgid "Last"
msgstr "Erste"
#: project/templates/_macros.html:940
#: project/templates/_macros.html:943
msgid "Radius"
msgstr "Umkreis"
#: project/templates/_macros.html:1150
#: project/templates/_macros.html:1153
msgid "Edit image"
msgstr "Bild bearbeiten"
#: project/templates/_macros.html:1171
#: project/templates/_macros.html:1174
msgid "Close"
msgstr "Schließen"
#: project/templates/_macros.html:1172
#: project/templates/_macros.html:1175
msgid "Okay"
msgstr "OK"
#: project/templates/_macros.html:1184
#: project/templates/_macros.html:1187
msgid "Choose image file"
msgstr "Bild-Datei auswählen"
#: project/templates/_macros.html:1220 project/templates/event/actions.html:65
#: project/templates/_macros.html:1223 project/templates/event/actions.html:65
#: project/templates/event/delete.html:12
msgid "Edit event"
msgstr "Veranstaltung bearbeiten"
#: project/templates/_macros.html:1223 project/templates/manage/events.html:65
#: project/templates/_macros.html:1226 project/templates/manage/events.html:65
msgid "More"
msgstr "Mehr"
#: project/templates/_macros.html:1270
#: project/templates/_macros.html:1273
msgid "Please enter a valid time, between 00:00 and 23:59."
msgstr "Bitte gib eine gültige Uhrzeit zwischen 00:00 und 23:59 ein."
#: project/templates/_macros.html:1298
#: project/templates/_macros.html:1301
#, python-format
msgid "Just use %(term)s"
msgstr "Verwende einfach %(term)s"
#: project/templates/_macros.html:1364
#: project/templates/_macros.html:1367
msgid "Event suggestion"
msgstr "Veranstaltungsvorschlag"
#: project/templates/_macros.html:1482
#: project/templates/_macros.html:1485
msgid "Link copied"
msgstr "Link kopiert"
#: project/templates/_macros.html:1482
#: project/templates/_macros.html:1485
msgid "Copy link"
msgstr "Link kopieren"
#: project/templates/_macros.html:1511
#: project/templates/_macros.html:1514
msgid "Google calendar"
msgstr "Google Kalender"
#: project/templates/_macros.html:1512
#: project/templates/_macros.html:1515
msgid "Apple calendar"
msgstr "Apple Kalender"
#: project/templates/_macros.html:1513
#: project/templates/_macros.html:1516
msgid "Yahoo calendar"
msgstr "Yahoo Kalender"
#: project/templates/_macros.html:1514
#: project/templates/_macros.html:1517
msgid "Other calendar"
msgstr "Anderer Kalender"
#: project/templates/_macros.html:1709
#: project/templates/_macros.html:1712
msgid "Remove event date"
msgstr "Termin entfernen"
#: project/templates/_macros.html:1738 project/templates/event/create.html:176
#: project/templates/_macros.html:1741 project/templates/event/create.html:176
#: project/templates/event/update.html:99
#: project/templates/widget/event_suggestion/create.html:129
msgid "Enter organizer"
msgstr "Veranstalter eingeben"
#: project/templates/_macros.html:1762
#: project/templates/_macros.html:1765
msgid "Enter list name"
msgstr "Listenname eingeben"
@ -1393,7 +1398,7 @@ msgstr "Verwaltung"
msgid "Register for free"
msgstr "Kostenlos registrieren"
#: project/templates/layout.html:175 project/templates/layout.html:219
#: project/templates/layout.html:175 project/templates/layout.html:220
#: project/templates/manage/events.html:6
#: project/templates/manage/events.html:41
#: project/templates/manage/events_vue.html:4
@ -1414,7 +1419,7 @@ msgstr "Organisationen"
msgid "Planing"
msgstr "Planung"
#: project/templates/layout.html:187 project/templates/layout.html:275
#: project/templates/layout.html:187 project/templates/layout.html:276
#: project/templates/oauth2_client/list.html:10
#: project/templates/oauth2_client/read.html:10
#: project/templates/oauth2_token/list.html:10 project/templates/profile.html:4
@ -1424,86 +1429,86 @@ msgstr "Profil"
#: project/templates/admin/admin.html:3 project/templates/admin/admin.html:9
#: project/templates/admin/admin_units.html:10
#: project/templates/admin/users.html:10 project/templates/layout.html:190
#: project/templates/admin/users.html:10 project/templates/layout.html:191
msgid "Admin"
msgstr "Administration"
#: project/templates/layout.html:194
#: project/templates/layout.html:195
msgid "Logout"
msgstr "Ausloggen"
#: project/templates/layout.html:225
#: project/templates/layout.html:226
msgid "Show events"
msgstr "Veranstaltungen anzeigen"
#: project/templates/event/create.html:5
#: project/templates/event/create.html:221 project/templates/layout.html:226
#: project/templates/event/create.html:221 project/templates/layout.html:227
#: project/templates/manage/events.html:44
#: project/templates/manage/organizers.html:21
msgid "Create event"
msgstr "Veranstaltung erstellen"
#: project/templates/layout.html:227
#: project/templates/layout.html:228
msgid "Import event"
msgstr "Veranstaltung imporierten"
#: project/templates/layout.html:229
#: project/templates/layout.html:230
#: project/templates/manage/event_lists.html:4
msgid "Event lists"
msgstr "Veranstaltungslisten"
#: project/templates/layout.html:232
#: project/templates/layout.html:233
msgid "Review suggestions"
msgstr "Vorschläge prüfen"
#: project/templates/layout.html:242
#: project/templates/layout.html:243
#: project/templates/manage/references_incoming.html:5
#: project/templates/manage/references_outgoing.html:5
msgid "References"
msgstr "Empfehlungen"
#: project/templates/layout.html:248
#: project/templates/layout.html:249
#: project/templates/manage/references_incoming.html:9
msgid "Incoming references"
msgstr "Eingehende Empfehlungen"
#: project/templates/layout.html:249
#: project/templates/layout.html:250
#: project/templates/manage/references_outgoing.html:9
msgid "Outgoing references"
msgstr "Ausgehende Empfehlungen"
#: project/templates/layout.html:251
#: project/templates/layout.html:252
#: project/templates/manage/reference_requests_incoming.html:9
msgid "Incoming reference requests"
msgstr "Eingehende Empfehlungsanfragen"
#: project/templates/layout.html:256
#: project/templates/layout.html:257
#: project/templates/manage/reference_requests_outgoing.html:9
msgid "Outgoing reference requests"
msgstr "Ausgehende Empfehlungsanfragen"
#: project/templates/layout.html:264 project/templates/manage/organizers.html:5
#: project/templates/layout.html:265 project/templates/manage/organizers.html:5
#: project/templates/manage/organizers.html:9
msgid "Organizers"
msgstr "Veranstalter"
#: project/templates/event_place/list.html:3
#: project/templates/event_place/list.html:7 project/templates/layout.html:265
#: project/templates/event_place/list.html:7 project/templates/layout.html:266
#: project/templates/manage/places.html:5
#: project/templates/manage/places.html:9
msgid "Places"
msgstr "Orte"
#: project/templates/layout.html:267 project/templates/manage/members.html:5
#: project/templates/layout.html:268 project/templates/manage/members.html:5
#: project/templates/manage/members.html:28
msgid "Members"
msgstr "Mitglieder"
#: project/templates/layout.html:268 project/templates/manage/relations.html:4
#: project/templates/layout.html:269 project/templates/manage/relations.html:4
msgid "Relations"
msgstr "Beziehungen"
#: project/templates/layout.html:270
#: project/templates/layout.html:271
#: project/templates/manage/admin_units.html:17
#: project/templates/manage/organization_invitations.html:4
#: project/templates/user/organization_invitations.html:4
@ -1515,27 +1520,27 @@ msgstr "Organisationseinladungen"
#: project/templates/admin/settings.html:8
#: project/templates/admin_unit/update.html:6
#: project/templates/admin_unit/update.html:23
#: project/templates/layout.html:272 project/templates/manage/widgets.html:11
#: project/templates/layout.html:273 project/templates/manage/widgets.html:11
#: project/templates/manage/widgets.html:15 project/templates/profile.html:19
msgid "Settings"
msgstr "Einstellungen"
#: project/templates/layout.html:273
#: project/templates/layout.html:274
#: project/templates/manage/custom_widgets.html:13
msgid "Custom widgets"
msgstr "Custom widgets"
#: project/templates/layout.html:274 project/templates/manage/reviews.html:10
#: project/templates/layout.html:275 project/templates/manage/reviews.html:10
#: project/templates/manage/widgets.html:5
#: project/templates/manage/widgets.html:9
msgid "Widgets"
msgstr "Widgets"
#: project/templates/layout.html:285
#: project/templates/layout.html:286
msgid "Switch organization"
msgstr "Organisation wechseln"
#: project/templates/developer/read.html:4 project/templates/layout.html:335
#: project/templates/developer/read.html:4 project/templates/layout.html:336
#: project/templates/profile.html:29
msgid "Developer"
msgstr "Entwickler"
@ -1997,7 +2002,7 @@ msgstr "Du hast noch keinen Account? Kein Problem!"
msgid "Widget"
msgstr "Widget"
#: project/templates/widget/event_date/list.html:149
#: project/templates/widget/event_date/list.html:153
msgid "Print"
msgstr "Drucken"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-01-11 23:44+0100\n"
"POT-Creation-Date: 2022-01-17 09:16+0100\n"
"PO-Revision-Date: 2021-04-30 15:04+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.0\n"
"Generated-By: Babel 2.9.1\n"
#: project/i10n.py:13
msgid "Event_Art"
@ -194,29 +194,29 @@ msgstr ""
msgid "message"
msgstr ""
#: project/api/organization/resources.py:399
#: project/api/organization/resources.py:398
#: project/views/admin_unit_member_invitation.py:85
msgid "You have received an invitation"
msgstr ""
#: project/forms/admin.py:10 project/templates/layout.html:313
#: project/forms/admin.py:10 project/templates/layout.html:314
#: project/views/root.py:37
msgid "Terms of service"
msgstr ""
#: project/forms/admin.py:11 project/templates/layout.html:317
#: project/forms/admin.py:11 project/templates/layout.html:318
#: project/views/root.py:45
msgid "Legal notice"
msgstr ""
#: project/forms/admin.py:12 project/templates/_macros.html:1392
#: project/templates/layout.html:321
#: project/forms/admin.py:12 project/templates/_macros.html:1395
#: project/templates/layout.html:322
#: project/templates/widget/event_suggestion/create.html:204
#: project/views/admin_unit.py:73 project/views/root.py:53
msgid "Contact"
msgstr ""
#: project/forms/admin.py:13 project/templates/layout.html:325
#: project/forms/admin.py:13 project/templates/layout.html:326
#: project/views/root.py:61
msgid "Privacy"
msgstr ""
@ -331,7 +331,7 @@ msgstr ""
msgid "The short name is used to create a unique identifier for your events"
msgstr ""
#: project/forms/admin_unit.py:41 project/templates/_macros.html:1528
#: project/forms/admin_unit.py:41 project/templates/_macros.html:1531
msgid "Short name must contain only letters numbers or underscore"
msgstr ""
@ -345,7 +345,7 @@ msgstr ""
#: project/forms/admin_unit_member.py:23 project/forms/admin_unit_member.py:28
#: project/forms/event.py:107 project/forms/event_suggestion.py:38
#: project/forms/organizer.py:27 project/templates/_macros.html:235
#: project/templates/_macros.html:1488 project/templates/admin/users.html:19
#: project/templates/_macros.html:1491 project/templates/admin/users.html:19
msgid "Email"
msgstr ""
@ -536,16 +536,16 @@ msgstr ""
msgid "All-day"
msgstr ""
#: project/forms/event.py:54 project/templates/_macros.html:1708
#: project/forms/event.py:54 project/templates/_macros.html:1711
#: project/templates/widget/event_suggestion/create.html:240
msgid "Recurring event"
msgstr ""
#: project/forms/event.py:61 project/templates/_macros.html:1249
#: project/forms/event.py:61 project/templates/_macros.html:1252
msgid "The start must be before the end."
msgstr ""
#: project/forms/event.py:67 project/templates/_macros.html:1266
#: project/forms/event.py:67 project/templates/_macros.html:1269
msgid "An event can last a maximum of 14 days."
msgstr ""
@ -756,7 +756,7 @@ msgstr ""
#: project/forms/event.py:286 project/forms/event.py:295
#: project/forms/event.py:368 project/forms/event_suggestion.py:50
#: project/templates/_macros.html:436 project/templates/_macros.html:596
#: project/templates/_macros.html:436 project/templates/_macros.html:599
#: project/templates/event/create.html:284
#: project/templates/event/update.html:166
#: project/templates/event_place/create.html:31
@ -776,7 +776,7 @@ msgstr ""
#: project/forms/event.py:302 project/forms/event.py:311
#: project/forms/event.py:376 project/forms/event.py:439
#: project/forms/event_suggestion.py:60 project/templates/_macros.html:473
#: project/templates/_macros.html:633 project/templates/event/create.html:253
#: project/templates/_macros.html:636 project/templates/event/create.html:253
#: project/templates/event/update.html:156
#: project/templates/organizer/create.html:27
#: project/templates/organizer/delete.html:13
@ -874,7 +874,7 @@ msgstr ""
msgid "Update event"
msgstr ""
#: project/forms/event.py:423 project/templates/_macros.html:1221
#: project/forms/event.py:423 project/templates/_macros.html:1224
#: project/templates/event/actions.html:66
#: project/templates/event/delete.html:6
msgid "Delete event"
@ -1047,10 +1047,10 @@ msgid "Weekdays"
msgstr ""
#: project/forms/reference.py:11 project/forms/reference_request.py:16
#: project/templates/_macros.html:489 project/templates/_macros.html:649
#: project/templates/_macros.html:489 project/templates/_macros.html:652
#: project/templates/admin_unit/create.html:28
#: project/templates/admin_unit/update.html:29
#: project/templates/layout.html:261
#: project/templates/layout.html:262
msgid "Organization"
msgstr ""
@ -1075,7 +1075,7 @@ msgstr ""
msgid "Delete request"
msgstr ""
#: project/forms/reference_request.py:28 project/templates/_macros.html:1404
#: project/forms/reference_request.py:28 project/templates/_macros.html:1407
#: project/templates/event_suggestion/review_status.html:18
#: project/templates/reference_request/review_status.html:12
msgid "Review status"
@ -1177,58 +1177,63 @@ msgstr ""
msgid "Event"
msgstr ""
#: project/templates/_macros.html:391 project/templates/_macros.html:917
#: project/templates/_macros.html:391 project/templates/_macros.html:920
msgid "Date"
msgstr ""
#: project/templates/_macros.html:418 project/templates/_macros.html:577
#: project/templates/_macros.html:1473 project/templates/event/actions.html:51
#: project/templates/_macros.html:1476 project/templates/event/actions.html:51
msgid "Share"
msgstr ""
#: project/templates/_macros.html:422 project/templates/_macros.html:581
#: project/templates/_macros.html:1503
#: project/templates/_macros.html:1506
msgid "Add to calendar"
msgstr ""
#: project/templates/_macros.html:430 project/templates/_macros.html:589
#: project/templates/_macros.html:430 project/templates/_macros.html:592
#: project/templates/event/report.html:4
msgid "Report event"
msgstr ""
#: project/templates/_macros.html:457 project/templates/_macros.html:615
#: project/templates/_macros.html:457 project/templates/_macros.html:618
msgid "Show directions"
msgstr ""
#: project/templates/_macros.html:462 project/templates/_macros.html:620
#: project/templates/_macros.html:462 project/templates/_macros.html:623
msgid "The event takes place online."
msgstr ""
#: project/templates/_macros.html:464 project/templates/_macros.html:622
#: project/templates/_macros.html:464 project/templates/_macros.html:625
msgid "The event takes place both offline and online."
msgstr ""
#: project/templates/_macros.html:676 project/templates/event_date/list.html:5
#: project/templates/_macros.html:585 project/templates/layout.html:188
#: project/templates/user/favorite_events.html:4
msgid "Favorite events"
msgstr ""
#: project/templates/_macros.html:679 project/templates/event_date/list.html:5
#: project/templates/event_date/list.html:299
#: project/templates/reference_request/review.html:32
msgid "Event Dates"
msgstr ""
#: project/templates/_macros.html:768
#: project/templates/_macros.html:771
msgid "Search location on Google"
msgstr ""
#: project/templates/_macros.html:834
#: project/templates/_macros.html:837
#, python-format
msgid "%(count)d event dates"
msgstr ""
#: project/templates/_macros.html:857 project/templates/_macros.html:859
#: project/templates/_macros.html:860 project/templates/_macros.html:862
#: project/templates/event_date/list.html:321
msgid "First"
msgstr ""
#: project/templates/_macros.html:862 project/templates/_macros.html:864
#: project/templates/_macros.html:865 project/templates/_macros.html:867
#: project/templates/event_date/list.html:322
#: project/templates/widget/event_suggestion/create.html:193
#: project/templates/widget/event_suggestion/create.html:218
@ -1239,12 +1244,12 @@ msgstr ""
msgid "Previous"
msgstr ""
#: project/templates/_macros.html:866
#: project/templates/_macros.html:869
#, python-format
msgid "Page %(page)d of %(pages)d (%(total)d total)"
msgstr ""
#: project/templates/_macros.html:868 project/templates/_macros.html:870
#: project/templates/_macros.html:871 project/templates/_macros.html:873
#: project/templates/event_date/list.html:324
#: project/templates/widget/event_suggestion/create.html:194
#: project/templates/widget/event_suggestion/create.html:219
@ -1254,88 +1259,88 @@ msgstr ""
msgid "Next"
msgstr ""
#: project/templates/_macros.html:873 project/templates/_macros.html:875
#: project/templates/_macros.html:876 project/templates/_macros.html:878
#: project/templates/event_date/list.html:325
msgid "Last"
msgstr ""
#: project/templates/_macros.html:940
#: project/templates/_macros.html:943
msgid "Radius"
msgstr ""
#: project/templates/_macros.html:1150
#: project/templates/_macros.html:1153
msgid "Edit image"
msgstr ""
#: project/templates/_macros.html:1171
#: project/templates/_macros.html:1174
msgid "Close"
msgstr ""
#: project/templates/_macros.html:1172
#: project/templates/_macros.html:1175
msgid "Okay"
msgstr ""
#: project/templates/_macros.html:1184
#: project/templates/_macros.html:1187
msgid "Choose image file"
msgstr ""
#: project/templates/_macros.html:1220 project/templates/event/actions.html:65
#: project/templates/_macros.html:1223 project/templates/event/actions.html:65
#: project/templates/event/delete.html:12
msgid "Edit event"
msgstr ""
#: project/templates/_macros.html:1223 project/templates/manage/events.html:65
#: project/templates/_macros.html:1226 project/templates/manage/events.html:65
msgid "More"
msgstr ""
#: project/templates/_macros.html:1270
#: project/templates/_macros.html:1273
msgid "Please enter a valid time, between 00:00 and 23:59."
msgstr ""
#: project/templates/_macros.html:1298
#: project/templates/_macros.html:1301
#, python-format
msgid "Just use %(term)s"
msgstr ""
#: project/templates/_macros.html:1364
#: project/templates/_macros.html:1367
msgid "Event suggestion"
msgstr ""
#: project/templates/_macros.html:1482
#: project/templates/_macros.html:1485
msgid "Link copied"
msgstr ""
#: project/templates/_macros.html:1482
#: project/templates/_macros.html:1485
msgid "Copy link"
msgstr ""
#: project/templates/_macros.html:1511
#: project/templates/_macros.html:1514
msgid "Google calendar"
msgstr ""
#: project/templates/_macros.html:1512
#: project/templates/_macros.html:1515
msgid "Apple calendar"
msgstr ""
#: project/templates/_macros.html:1513
#: project/templates/_macros.html:1516
msgid "Yahoo calendar"
msgstr ""
#: project/templates/_macros.html:1514
#: project/templates/_macros.html:1517
msgid "Other calendar"
msgstr ""
#: project/templates/_macros.html:1709
#: project/templates/_macros.html:1712
msgid "Remove event date"
msgstr ""
#: project/templates/_macros.html:1738 project/templates/event/create.html:176
#: project/templates/_macros.html:1741 project/templates/event/create.html:176
#: project/templates/event/update.html:99
#: project/templates/widget/event_suggestion/create.html:129
msgid "Enter organizer"
msgstr ""
#: project/templates/_macros.html:1762
#: project/templates/_macros.html:1765
msgid "Enter list name"
msgstr ""
@ -1348,7 +1353,7 @@ msgstr ""
msgid "Register for free"
msgstr ""
#: project/templates/layout.html:175 project/templates/layout.html:219
#: project/templates/layout.html:175 project/templates/layout.html:220
#: project/templates/manage/events.html:6
#: project/templates/manage/events.html:41
#: project/templates/manage/events_vue.html:4
@ -1369,7 +1374,7 @@ msgstr ""
msgid "Planing"
msgstr ""
#: project/templates/layout.html:187 project/templates/layout.html:275
#: project/templates/layout.html:187 project/templates/layout.html:276
#: project/templates/oauth2_client/list.html:10
#: project/templates/oauth2_client/read.html:10
#: project/templates/oauth2_token/list.html:10 project/templates/profile.html:4
@ -1379,86 +1384,86 @@ msgstr ""
#: project/templates/admin/admin.html:3 project/templates/admin/admin.html:9
#: project/templates/admin/admin_units.html:10
#: project/templates/admin/users.html:10 project/templates/layout.html:190
#: project/templates/admin/users.html:10 project/templates/layout.html:191
msgid "Admin"
msgstr ""
#: project/templates/layout.html:194
#: project/templates/layout.html:195
msgid "Logout"
msgstr ""
#: project/templates/layout.html:225
#: project/templates/layout.html:226
msgid "Show events"
msgstr ""
#: project/templates/event/create.html:5
#: project/templates/event/create.html:221 project/templates/layout.html:226
#: project/templates/event/create.html:221 project/templates/layout.html:227
#: project/templates/manage/events.html:44
#: project/templates/manage/organizers.html:21
msgid "Create event"
msgstr ""
#: project/templates/layout.html:227
#: project/templates/layout.html:228
msgid "Import event"
msgstr ""
#: project/templates/layout.html:229
#: project/templates/layout.html:230
#: project/templates/manage/event_lists.html:4
msgid "Event lists"
msgstr ""
#: project/templates/layout.html:232
#: project/templates/layout.html:233
msgid "Review suggestions"
msgstr ""
#: project/templates/layout.html:242
#: project/templates/layout.html:243
#: project/templates/manage/references_incoming.html:5
#: project/templates/manage/references_outgoing.html:5
msgid "References"
msgstr ""
#: project/templates/layout.html:248
#: project/templates/layout.html:249
#: project/templates/manage/references_incoming.html:9
msgid "Incoming references"
msgstr ""
#: project/templates/layout.html:249
#: project/templates/layout.html:250
#: project/templates/manage/references_outgoing.html:9
msgid "Outgoing references"
msgstr ""
#: project/templates/layout.html:251
#: project/templates/layout.html:252
#: project/templates/manage/reference_requests_incoming.html:9
msgid "Incoming reference requests"
msgstr ""
#: project/templates/layout.html:256
#: project/templates/layout.html:257
#: project/templates/manage/reference_requests_outgoing.html:9
msgid "Outgoing reference requests"
msgstr ""
#: project/templates/layout.html:264 project/templates/manage/organizers.html:5
#: project/templates/layout.html:265 project/templates/manage/organizers.html:5
#: project/templates/manage/organizers.html:9
msgid "Organizers"
msgstr ""
#: project/templates/event_place/list.html:3
#: project/templates/event_place/list.html:7 project/templates/layout.html:265
#: project/templates/event_place/list.html:7 project/templates/layout.html:266
#: project/templates/manage/places.html:5
#: project/templates/manage/places.html:9
msgid "Places"
msgstr ""
#: project/templates/layout.html:267 project/templates/manage/members.html:5
#: project/templates/layout.html:268 project/templates/manage/members.html:5
#: project/templates/manage/members.html:28
msgid "Members"
msgstr ""
#: project/templates/layout.html:268 project/templates/manage/relations.html:4
#: project/templates/layout.html:269 project/templates/manage/relations.html:4
msgid "Relations"
msgstr ""
#: project/templates/layout.html:270
#: project/templates/layout.html:271
#: project/templates/manage/admin_units.html:17
#: project/templates/manage/organization_invitations.html:4
#: project/templates/user/organization_invitations.html:4
@ -1470,27 +1475,27 @@ msgstr ""
#: project/templates/admin/settings.html:8
#: project/templates/admin_unit/update.html:6
#: project/templates/admin_unit/update.html:23
#: project/templates/layout.html:272 project/templates/manage/widgets.html:11
#: project/templates/layout.html:273 project/templates/manage/widgets.html:11
#: project/templates/manage/widgets.html:15 project/templates/profile.html:19
msgid "Settings"
msgstr ""
#: project/templates/layout.html:273
#: project/templates/layout.html:274
#: project/templates/manage/custom_widgets.html:13
msgid "Custom widgets"
msgstr ""
#: project/templates/layout.html:274 project/templates/manage/reviews.html:10
#: project/templates/layout.html:275 project/templates/manage/reviews.html:10
#: project/templates/manage/widgets.html:5
#: project/templates/manage/widgets.html:9
msgid "Widgets"
msgstr ""
#: project/templates/layout.html:285
#: project/templates/layout.html:286
msgid "Switch organization"
msgstr ""
#: project/templates/developer/read.html:4 project/templates/layout.html:335
#: project/templates/developer/read.html:4 project/templates/layout.html:336
#: project/templates/profile.html:29
msgid "Developer"
msgstr ""
@ -1943,7 +1948,7 @@ msgstr ""
msgid "Widget"
msgstr ""
#: project/templates/widget/event_date/list.html:149
#: project/templates/widget/event_date/list.html:153
msgid "Print"
msgstr ""

View File

@ -28,3 +28,9 @@ def user_organization_invitation(id):
@auth_required()
def user_organization_invitations(path=None):
return render_template("user/organization_invitations.html")
@app.route("/user/favorite-events")
@auth_required()
def user_favorite_events():
return render_template("user/favorite_events.html")

View File

@ -100,6 +100,18 @@ def test_search(client, seeder, utils):
assert response.json["items"][0]["id"] == event_id
def test_search_is_favored(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
event_id = seeder.create_event(admin_unit_id)
seeder.add_favorite_event(user_id, event_id)
url = utils.get_url("api_v1_event_search")
response = utils.get_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_id
assert response.json["items"][0]["is_favored"]
def test_dates(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base(log_in=False)
event_id = seeder.create_event(admin_unit_id)

View File

@ -127,3 +127,15 @@ def test_search_oneDay(client, seeder, utils):
response = utils.get_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["event"]["id"] == event_id
def test_search_is_favored(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_base()
event_id = seeder.create_event(admin_unit_id)
seeder.add_favorite_event(user_id, event_id)
url = utils.get_url("api_v1_event_date_search")
response = utils.get_ok(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["event"]["id"] == event_id
assert response.json["items"][0]["event"]["is_favored"]

View File

@ -63,3 +63,64 @@ def test_organization_invitation_delete(client, app, seeder, utils):
invitation = AdminUnitInvitation.query.get(invitation_id)
assert invitation is None
def test_favorite_event_list(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
seeder.create_event(admin_unit_id)
seeder.add_favorite_event(user_id, event_id)
url = utils.get_url("api_v1_user_favorite_event_list")
response = utils.get_json(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_id
seeder.remove_favorite_event(user_id, event_id)
url = utils.get_url("api_v1_user_favorite_event_list")
response = utils.get_json(url)
assert len(response.json["items"]) == 0
def test_favorite_event_search(client, seeder, utils):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
seeder.create_event(admin_unit_id)
seeder.add_favorite_event(user_id, event_id)
url = utils.get_url("api_v1_user_favorite_event_search")
response = utils.get_json(url)
assert len(response.json["items"]) == 1
assert response.json["items"][0]["id"] == event_id
def test_favorite_event_list_put(client, seeder, utils, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
url = utils.get_url("api_v1_user_favorite_event_list_write", event_id=event_id)
response = utils.put_json(url)
utils.assert_response_no_content(response)
with app.app_context():
from project.services.user import get_favorite_event
favorite = get_favorite_event(user_id, event_id)
assert favorite is not None
def test_favorite_event_list_delete(client, seeder, utils, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
seeder.add_favorite_event(user_id, event_id)
url = utils.get_url("api_v1_user_favorite_event_list_write", event_id=event_id)
response = utils.delete(url)
utils.assert_response_no_content(response)
with app.app_context():
from project.services.user import get_favorite_event
favorite = get_favorite_event(user_id, event_id)
assert favorite is None

View File

@ -159,6 +159,20 @@ class Seeder(object):
return invitation_id
def add_favorite_event(self, user_id, event_id):
from project.services.user import add_favorite_event
with self._app.app_context():
if add_favorite_event(user_id, event_id):
self._db.session.commit()
def remove_favorite_event(self, user_id, event_id):
from project.services.user import remove_favorite_event
with self._app.app_context():
if remove_favorite_event(user_id, event_id):
self._db.session.commit()
def create_admin_unit_member_event_verifier(self, admin_unit_id):
return self.create_admin_unit_member(admin_unit_id, ["event_verifier"])

View File

@ -13,3 +13,30 @@ def test_update_event_dates_with_recurrence_rule(client, seeder, utils, app):
admin = admins[0]
assert admin.id == admin_id
assert admin.email == "admin@test.de"
def test_add_favorite_event(client, seeder, utils, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
with app.app_context():
from project.services.user import add_favorite_event, get_favorite_event
assert add_favorite_event(user_id, event_id)
assert add_favorite_event(user_id, event_id) is False
favorite = get_favorite_event(user_id, event_id)
assert favorite is not None
def test_remove_favorite_event(client, seeder, utils, app):
user_id, admin_unit_id = seeder.setup_api_access()
event_id = seeder.create_event(admin_unit_id)
seeder.add_favorite_event(user_id, event_id)
with app.app_context():
from project.services.user import has_favorite_event, remove_favorite_event
assert remove_favorite_event(user_id, event_id)
assert remove_favorite_event(user_id, event_id) is False
assert has_favorite_event(user_id, event_id) is False

View File

@ -374,3 +374,14 @@ def test_event_list_deletion(client, app, db, seeder):
event_list_b = EventList.query.get(event_list_b_id)
assert len(event_list_b.events) == 0
def test_event_is_favored_by_current_user(client, app, db, seeder):
_, admin_unit_id = seeder.setup_base(log_in=False)
event_id = seeder.create_event(admin_unit_id)
with app.app_context():
from project.models import Event
event = Event.query.get(event_id)
assert event.is_favored_by_current_user() is False

View File

@ -58,3 +58,10 @@ def test_organization_invitation_list(client, seeder, utils):
url = utils.get_url("user_organization_invitations")
utils.get_ok(url)
def test_user_favorite_events(client, seeder, utils):
_, admin_unit_id = seeder.setup_base()
url = utils.get_url("user_favorite_events")
utils.get_ok(url)