mirror of
https://github.com/lucaspalomodevelop/eventcally.git
synced 2026-03-13 00:07:22 +00:00
Delete account #456
This commit is contained in:
parent
df99e1d281
commit
235c41531e
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -2,16 +2,11 @@
|
||||
"editor.formatOnSave": true,
|
||||
"python.pythonPath": "./env/bin/python3",
|
||||
"python.formatting.provider": "black",
|
||||
"python.sortImports.args": [
|
||||
"-sp .isort.cfg"
|
||||
],
|
||||
"isort.args": ["-sp", ".isort.cfg"],
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": false,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.testing.pytestArgs": [
|
||||
"tests",
|
||||
"--capture=sys"
|
||||
],
|
||||
"python.testing.pytestArgs": ["tests", "--capture=sys"],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.nosetestsEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
|
||||
98
messages.pot
98
messages.pot
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2023-04-27 15:10+0200\n"
|
||||
"POT-Creation-Date: 2023-05-01 18:11+0200\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"
|
||||
@ -194,7 +194,7 @@ msgid "message"
|
||||
msgstr ""
|
||||
|
||||
#: project/api/organization/resources.py:401
|
||||
#: project/views/admin_unit_member_invitation.py:85
|
||||
#: project/views/admin_unit_member_invitation.py:89
|
||||
msgid "You have received an invitation"
|
||||
msgstr ""
|
||||
|
||||
@ -211,7 +211,7 @@ msgstr ""
|
||||
#: project/forms/admin.py:13 project/templates/_macros.html:1473
|
||||
#: project/templates/layout.html:311
|
||||
#: project/templates/widget/event_suggestion/create.html:204
|
||||
#: project/views/admin_unit.py:82 project/views/root.py:71
|
||||
#: project/views/admin_unit.py:83 project/views/root.py:71
|
||||
msgid "Contact"
|
||||
msgstr ""
|
||||
|
||||
@ -247,6 +247,7 @@ msgstr ""
|
||||
#: project/forms/admin_unit_member.py:11 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/forms/user.py:18 project/forms/user.py:23
|
||||
#: project/templates/_macros.html:237 project/templates/_macros.html:1569
|
||||
#: project/templates/admin/admin.html:27 project/templates/admin/email.html:4
|
||||
#: project/templates/admin/email.html:66 project/templates/admin/users.html:19
|
||||
@ -474,14 +475,15 @@ msgstr ""
|
||||
msgid "Link Color"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin_unit.py:133
|
||||
#: project/forms/admin_unit.py:133 project/forms/user.py:17
|
||||
msgid "Request deletion"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin_unit.py:138
|
||||
#: project/forms/admin_unit.py:138 project/forms/user.py:22
|
||||
#: project/templates/admin_unit/cancel_deletion.html:6
|
||||
#: project/templates/admin_unit/update.html:26
|
||||
#: project/templates/manage/events.html:49
|
||||
#: project/templates/manage/events.html:49 project/templates/profile.html:13
|
||||
#: project/templates/user/cancel_deletion.html:6
|
||||
msgid "Cancel deletion"
|
||||
msgstr ""
|
||||
|
||||
@ -1324,7 +1326,7 @@ msgstr ""
|
||||
#: project/templates/_macros.html:590 project/templates/_macros.html:633
|
||||
#: project/templates/_macros.html:765
|
||||
#: project/templates/admin/admin_units.html:36
|
||||
#: project/templates/admin/users.html:32
|
||||
#: project/templates/admin/users.html:34
|
||||
#: project/templates/manage/events.html:116
|
||||
#: project/templates/manage/members.html:35
|
||||
#: project/templates/manage/organizers.html:33
|
||||
@ -1539,7 +1541,7 @@ msgstr ""
|
||||
#: 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
|
||||
#: project/templates/profile.html:10
|
||||
#: project/templates/profile.html:17
|
||||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
@ -1641,7 +1643,7 @@ msgstr ""
|
||||
#: project/templates/admin_unit/update.html:6
|
||||
#: project/templates/admin_unit/update.html:30
|
||||
#: project/templates/layout.html:259 project/templates/manage/widgets.html:11
|
||||
#: project/templates/manage/widgets.html:15 project/templates/profile.html:19
|
||||
#: project/templates/manage/widgets.html:15 project/templates/profile.html:32
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
@ -1667,24 +1669,34 @@ msgstr ""
|
||||
|
||||
#: project/templates/developer/read.html:4
|
||||
#: project/templates/developer/read.html:8 project/templates/layout.html:319
|
||||
#: project/templates/profile.html:33
|
||||
#: project/templates/profile.html:46
|
||||
msgid "Developer"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/profile.html:23
|
||||
#: project/templates/profile.html:12 project/views/admin_unit.py:89
|
||||
#: project/views/admin_unit_member_invitation.py:39
|
||||
msgid "Your account is scheduled for deletion."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/profile.html:26
|
||||
#: project/templates/user/request_deletion.html:6
|
||||
msgid "Delete account"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/profile.html:36
|
||||
#: project/templates/user/notifications.html:4
|
||||
#: project/templates/user/notifications.html:8
|
||||
msgid "Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/profile.html:27
|
||||
#: project/templates/profile.html:40
|
||||
msgid "Applications"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/oauth2_client/list.html:4
|
||||
#: project/templates/oauth2_client/list.html:11
|
||||
#: project/templates/oauth2_client/read.html:11
|
||||
#: project/templates/profile.html:37
|
||||
#: project/templates/profile.html:50
|
||||
msgid "OAuth2 clients"
|
||||
msgstr ""
|
||||
|
||||
@ -1702,7 +1714,7 @@ msgid "View"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/admin/admin_units.html:37
|
||||
#: project/templates/admin/users.html:33
|
||||
#: project/templates/admin/users.html:35
|
||||
#: project/templates/manage/events.html:117
|
||||
#: project/templates/manage/members.html:21
|
||||
#: project/templates/manage/members.html:36
|
||||
@ -1714,6 +1726,8 @@ msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/admin/delete_user.html:13
|
||||
#: project/templates/user/cancel_deletion.html:13
|
||||
#: project/templates/user/request_deletion.html:15
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
@ -1800,7 +1814,6 @@ msgid "You have been invited to join %(admin_unit_name)s."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/invitation_notice.html:5
|
||||
#: project/templates/email/organization_deletion_requested_notice.html:5
|
||||
#: project/templates/email/organization_invitation_notice.html:5
|
||||
msgid "Click here to view the invitation"
|
||||
msgstr ""
|
||||
@ -1823,6 +1836,10 @@ msgstr ""
|
||||
msgid "%(admin_unit_name)s is scheduled for deletion."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_deletion_requested_notice.html:5
|
||||
msgid "Click here below to cancel the deletion"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_invitation_accepted_notice.html:4
|
||||
#, python-format
|
||||
msgid ""
|
||||
@ -1889,6 +1906,15 @@ msgstr ""
|
||||
msgid "Click here to open the site"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/user_deletion_requested_notice.html:4
|
||||
#, python-format
|
||||
msgid "%(user_email)s is scheduled for deletion."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/user_deletion_requested_notice.html:5
|
||||
msgid "Click the link below to cancel the deletion"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/event/actions.html:5
|
||||
#: project/templates/event/actions.html:22
|
||||
msgid "Actions for event"
|
||||
@ -2205,6 +2231,12 @@ msgstr ""
|
||||
msgid "Register for free"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/user/request_deletion.html:8
|
||||
msgid ""
|
||||
"The account is not deleted immediately. After a period of time, the "
|
||||
"account will be deleted. Until then, the deletion can be canceled."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/widget/event_date/list.html:5
|
||||
msgid "Widget"
|
||||
msgstr ""
|
||||
@ -2229,8 +2261,8 @@ msgstr ""
|
||||
msgid "Organization successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin.py:85 project/views/admin_unit.py:182
|
||||
#: project/views/admin_unit.py:215
|
||||
#: project/views/admin.py:85 project/views/admin_unit.py:187
|
||||
#: project/views/admin_unit.py:220
|
||||
msgid "Entered name does not match organization name"
|
||||
msgstr ""
|
||||
|
||||
@ -2239,7 +2271,7 @@ msgid "Organization successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin.py:113 project/views/manage.py:432
|
||||
#: project/views/user.py:28
|
||||
#: project/views/user.py:40
|
||||
msgid "Settings successfully updated"
|
||||
msgstr ""
|
||||
|
||||
@ -2261,29 +2293,29 @@ msgstr ""
|
||||
msgid "Entered email does not match user email"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin.py:237
|
||||
#: project/views/admin.py:236
|
||||
msgid "User successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:78
|
||||
#: project/views/admin_unit.py:79
|
||||
msgid ""
|
||||
"Organizations cannot currently be created. The project is in a closed "
|
||||
"test phase. If you are interested, you can contact us."
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:128
|
||||
#: project/views/admin_unit.py:133
|
||||
msgid "Organization successfully created"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:158
|
||||
#: project/views/admin_unit.py:163
|
||||
msgid "AdminUnit successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:245
|
||||
#: project/views/admin_unit.py:250
|
||||
msgid "Organization invitation accepted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:259
|
||||
#: project/views/admin_unit.py:264
|
||||
msgid "Organization deletion requested"
|
||||
msgstr ""
|
||||
|
||||
@ -2299,23 +2331,23 @@ msgstr ""
|
||||
msgid "Member successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:38
|
||||
#: project/views/admin_unit_member_invitation.py:42
|
||||
msgid "Invitation successfully accepted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:45
|
||||
#: project/views/admin_unit_member_invitation.py:49
|
||||
msgid "Invitation successfully declined"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:90
|
||||
#: project/views/admin_unit_member_invitation.py:94
|
||||
msgid "Invitation successfully sent"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:113
|
||||
#: project/views/admin_unit_member_invitation.py:117
|
||||
msgid "Entered email does not match invitation email"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:118
|
||||
#: project/views/admin_unit_member_invitation.py:122
|
||||
msgid "Invitation successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
@ -2470,6 +2502,14 @@ msgid ""
|
||||
"verified automatically."
|
||||
msgstr ""
|
||||
|
||||
#: project/views/user.py:84 project/views/user.py:111
|
||||
msgid "Entered email does not match your email"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/user.py:130
|
||||
msgid "User deletion requested"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/utils.py:71
|
||||
msgid ""
|
||||
"An entry with the entered values already exists. Duplicate entries are "
|
||||
|
||||
@ -50,4 +50,5 @@ format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
|
||||
[alembic:exclude]
|
||||
tables = spatial_ref_sys
|
||||
tables = spatial_ref_sys,layer,topology
|
||||
indexes = idx_event_description,idx_event_fulltext,idx_event_name,idx_event_tags,idx_location_coordinate
|
||||
|
||||
@ -44,19 +44,23 @@ def get_metadata():
|
||||
return target_db.metadata
|
||||
|
||||
|
||||
def exclude_tables_from_config(config_):
|
||||
tables_ = config_.get("tables", None)
|
||||
if tables_ is not None:
|
||||
tables = tables_.split(",")
|
||||
return tables
|
||||
def exclude_items_from_config(section, key):
|
||||
items_ = section.get(key, None)
|
||||
if items_ is not None:
|
||||
items = items_.split(",")
|
||||
return items
|
||||
|
||||
|
||||
exclude_tables = exclude_tables_from_config(config.get_section("alembic:exclude"))
|
||||
config_exclude_section = config.get_section("alembic:exclude")
|
||||
exclude_tables = exclude_items_from_config(config_exclude_section, "tables")
|
||||
exclude_indexes = exclude_items_from_config(config_exclude_section, "indexes")
|
||||
|
||||
|
||||
def include_object(object, name, type_, reflected, compare_to):
|
||||
if type_ == "table" and name in exclude_tables:
|
||||
return False
|
||||
elif type_ == "index" and name in exclude_indexes:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
65
migrations/versions/58d8aae621e6_.py
Normal file
65
migrations/versions/58d8aae621e6_.py
Normal file
@ -0,0 +1,65 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 58d8aae621e6
|
||||
Revises: cbac4166f9c0
|
||||
Create Date: 2023-04-28 11:26:33.237832
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
|
||||
from project import dbtypes
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "58d8aae621e6"
|
||||
down_revision = "cbac4166f9c0"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_unique_constraint(
|
||||
op.f("uq_eventlist_name"), "eventlist", ["name", "admin_unit_id"]
|
||||
)
|
||||
op.add_column(
|
||||
"oauth2_token",
|
||||
sa.Column(
|
||||
"access_token_revoked_at", sa.Integer(), nullable=False, server_default="0"
|
||||
),
|
||||
)
|
||||
op.add_column(
|
||||
"oauth2_token",
|
||||
sa.Column(
|
||||
"refresh_token_revoked_at", sa.Integer(), nullable=False, server_default="0"
|
||||
),
|
||||
)
|
||||
op.drop_column("oauth2_token", "revoked")
|
||||
op.alter_column(
|
||||
"user",
|
||||
"newsletter_enabled",
|
||||
existing_type=sa.BOOLEAN(),
|
||||
nullable=True,
|
||||
existing_server_default=sa.text("true"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column(
|
||||
"user",
|
||||
"newsletter_enabled",
|
||||
existing_type=sa.BOOLEAN(),
|
||||
nullable=False,
|
||||
existing_server_default=sa.text("true"),
|
||||
)
|
||||
op.add_column(
|
||||
"oauth2_token",
|
||||
sa.Column("revoked", sa.BOOLEAN(), autoincrement=False, nullable=True),
|
||||
)
|
||||
op.drop_column("oauth2_token", "refresh_token_revoked_at")
|
||||
op.drop_column("oauth2_token", "access_token_revoked_at")
|
||||
op.drop_constraint(op.f("uq_eventlist_name"), "eventlist", type_="unique")
|
||||
# ### end Alembic commands ###
|
||||
56
migrations/versions/5fde26e1904d_.py
Normal file
56
migrations/versions/5fde26e1904d_.py
Normal file
@ -0,0 +1,56 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 5fde26e1904d
|
||||
Revises: 58d8aae621e6
|
||||
Create Date: 2023-04-28 13:04:22.142011
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.sql.elements import conv
|
||||
from sqlalchemy.sql.naming import ConventionDict, _get_convention
|
||||
|
||||
from project import dbtypes
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "5fde26e1904d"
|
||||
down_revision = "58d8aae621e6"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
conn = op.get_bind()
|
||||
ctx = op.get_context()
|
||||
existing_metadata = sa.schema.MetaData()
|
||||
existing_metadata.reflect(bind=conn)
|
||||
target_metadata = ctx.opts["target_metadata"]
|
||||
|
||||
for table_name, table in existing_metadata.tables.items():
|
||||
if table_name not in target_metadata.tables:
|
||||
continue
|
||||
|
||||
for c in table.constraints:
|
||||
existing_name = c.name
|
||||
if not existing_name:
|
||||
continue
|
||||
|
||||
convention = _get_convention(target_metadata.naming_convention, type(c))
|
||||
|
||||
if not convention:
|
||||
continue
|
||||
|
||||
target_c_name = conv(
|
||||
convention % ConventionDict(c, table, target_metadata.naming_convention)
|
||||
)
|
||||
if not target_c_name:
|
||||
continue
|
||||
|
||||
if existing_name != target_c_name:
|
||||
op.execute(
|
||||
f"ALTER TABLE public.{table_name} RENAME CONSTRAINT {existing_name} to {target_c_name};"
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
596
migrations/versions/cceaf9b28134_.py
Normal file
596
migrations/versions/cceaf9b28134_.py
Normal file
@ -0,0 +1,596 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: cceaf9b28134
|
||||
Revises: d952cd5df596
|
||||
Create Date: 2023-05-01 18:02:53.515904
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
|
||||
from project import dbtypes
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "cceaf9b28134"
|
||||
down_revision = "d952cd5df596"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(
|
||||
"fk_adminunit_created_by_id_user", "adminunit", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_adminunit_updated_by_id_user", "adminunit", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_adminunit_updated_by_id_user"),
|
||||
"adminunit",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_adminunit_created_by_id_user"),
|
||||
"adminunit",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_adminunitinvitation_updated_by_id_user",
|
||||
"adminunitinvitation",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_adminunitinvitation_created_by_id_user",
|
||||
"adminunitinvitation",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_adminunitinvitation_updated_by_id_user"),
|
||||
"adminunitinvitation",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_adminunitinvitation_created_by_id_user"),
|
||||
"adminunitinvitation",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_adminunitrelation_updated_by_id_user",
|
||||
"adminunitrelation",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_adminunitrelation_created_by_id_user",
|
||||
"adminunitrelation",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_adminunitrelation_updated_by_id_user"),
|
||||
"adminunitrelation",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_adminunitrelation_created_by_id_user"),
|
||||
"adminunitrelation",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_customwidget_updated_by_id_user", "customwidget", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_customwidget_created_by_id_user", "customwidget", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_customwidget_updated_by_id_user"),
|
||||
"customwidget",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_customwidget_created_by_id_user"),
|
||||
"customwidget",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint("fk_event_updated_by_id_user", "event", type_="foreignkey")
|
||||
op.drop_constraint("fk_event_created_by_id_user", "event", type_="foreignkey")
|
||||
op.create_foreign_key(
|
||||
op.f("fk_event_created_by_id_user"),
|
||||
"event",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_event_updated_by_id_user"),
|
||||
"event",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventlist_created_by_id_user", "eventlist", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventlist_updated_by_id_user", "eventlist", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventlist_updated_by_id_user"),
|
||||
"eventlist",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventlist_created_by_id_user"),
|
||||
"eventlist",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventorganizer_updated_by_id_user", "eventorganizer", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventorganizer_created_by_id_user", "eventorganizer", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventorganizer_updated_by_id_user"),
|
||||
"eventorganizer",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventorganizer_created_by_id_user"),
|
||||
"eventorganizer",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventplace_created_by_id_user", "eventplace", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventplace_updated_by_id_user", "eventplace", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventplace_created_by_id_user"),
|
||||
"eventplace",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventplace_updated_by_id_user"),
|
||||
"eventplace",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventreference_created_by_id_user", "eventreference", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventreference_updated_by_id_user", "eventreference", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventreference_updated_by_id_user"),
|
||||
"eventreference",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventreference_created_by_id_user"),
|
||||
"eventreference",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventreferencerequest_updated_by_id_user",
|
||||
"eventreferencerequest",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventreferencerequest_created_by_id_user",
|
||||
"eventreferencerequest",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventreferencerequest_created_by_id_user"),
|
||||
"eventreferencerequest",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventreferencerequest_updated_by_id_user"),
|
||||
"eventreferencerequest",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventsuggestion_created_by_id_user", "eventsuggestion", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_eventsuggestion_updated_by_id_user", "eventsuggestion", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventsuggestion_created_by_id_user"),
|
||||
"eventsuggestion",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_eventsuggestion_updated_by_id_user"),
|
||||
"eventsuggestion",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint("fk_image_updated_by_id_user", "image", type_="foreignkey")
|
||||
op.drop_constraint("fk_image_created_by_id_user", "image", type_="foreignkey")
|
||||
op.create_foreign_key(
|
||||
op.f("fk_image_updated_by_id_user"),
|
||||
"image",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_image_created_by_id_user"),
|
||||
"image",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint("fk_location_created_by_id_user", "location", type_="foreignkey")
|
||||
op.drop_constraint("fk_location_updated_by_id_user", "location", type_="foreignkey")
|
||||
op.create_foreign_key(
|
||||
op.f("fk_location_created_by_id_user"),
|
||||
"location",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_location_updated_by_id_user"),
|
||||
"location",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.drop_constraint("fk_settings_updated_by_id_user", "settings", type_="foreignkey")
|
||||
op.drop_constraint("fk_settings_created_by_id_user", "settings", type_="foreignkey")
|
||||
op.create_foreign_key(
|
||||
op.f("fk_settings_created_by_id_user"),
|
||||
"settings",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_settings_updated_by_id_user"),
|
||||
"settings",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(
|
||||
op.f("fk_settings_updated_by_id_user"), "settings", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_settings_created_by_id_user"), "settings", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_settings_created_by_id_user", "settings", "user", ["created_by_id"], ["id"]
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_settings_updated_by_id_user", "settings", "user", ["updated_by_id"], ["id"]
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_location_updated_by_id_user"), "location", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_location_created_by_id_user"), "location", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_location_updated_by_id_user", "location", "user", ["updated_by_id"], ["id"]
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_location_created_by_id_user", "location", "user", ["created_by_id"], ["id"]
|
||||
)
|
||||
op.drop_constraint(op.f("fk_image_created_by_id_user"), "image", type_="foreignkey")
|
||||
op.drop_constraint(op.f("fk_image_updated_by_id_user"), "image", type_="foreignkey")
|
||||
op.create_foreign_key(
|
||||
"fk_image_created_by_id_user", "image", "user", ["created_by_id"], ["id"]
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_image_updated_by_id_user", "image", "user", ["updated_by_id"], ["id"]
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventsuggestion_updated_by_id_user"),
|
||||
"eventsuggestion",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventsuggestion_created_by_id_user"),
|
||||
"eventsuggestion",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventsuggestion_updated_by_id_user",
|
||||
"eventsuggestion",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventsuggestion_created_by_id_user",
|
||||
"eventsuggestion",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventreferencerequest_updated_by_id_user"),
|
||||
"eventreferencerequest",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventreferencerequest_created_by_id_user"),
|
||||
"eventreferencerequest",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventreferencerequest_created_by_id_user",
|
||||
"eventreferencerequest",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventreferencerequest_updated_by_id_user",
|
||||
"eventreferencerequest",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventreference_created_by_id_user"),
|
||||
"eventreference",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventreference_updated_by_id_user"),
|
||||
"eventreference",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventreference_updated_by_id_user",
|
||||
"eventreference",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventreference_created_by_id_user",
|
||||
"eventreference",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventplace_updated_by_id_user"), "eventplace", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventplace_created_by_id_user"), "eventplace", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventplace_updated_by_id_user",
|
||||
"eventplace",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventplace_created_by_id_user",
|
||||
"eventplace",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventorganizer_created_by_id_user"),
|
||||
"eventorganizer",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventorganizer_updated_by_id_user"),
|
||||
"eventorganizer",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventorganizer_created_by_id_user",
|
||||
"eventorganizer",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventorganizer_updated_by_id_user",
|
||||
"eventorganizer",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventlist_created_by_id_user"), "eventlist", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_eventlist_updated_by_id_user"), "eventlist", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventlist_updated_by_id_user",
|
||||
"eventlist",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_eventlist_created_by_id_user",
|
||||
"eventlist",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.drop_constraint(op.f("fk_event_updated_by_id_user"), "event", type_="foreignkey")
|
||||
op.drop_constraint(op.f("fk_event_created_by_id_user"), "event", type_="foreignkey")
|
||||
op.create_foreign_key(
|
||||
"fk_event_created_by_id_user", "event", "user", ["created_by_id"], ["id"]
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_event_updated_by_id_user", "event", "user", ["updated_by_id"], ["id"]
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_customwidget_created_by_id_user"), "customwidget", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_customwidget_updated_by_id_user"), "customwidget", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_customwidget_created_by_id_user",
|
||||
"customwidget",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_customwidget_updated_by_id_user",
|
||||
"customwidget",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_adminunitrelation_created_by_id_user"),
|
||||
"adminunitrelation",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_adminunitrelation_updated_by_id_user"),
|
||||
"adminunitrelation",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_adminunitrelation_created_by_id_user",
|
||||
"adminunitrelation",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_adminunitrelation_updated_by_id_user",
|
||||
"adminunitrelation",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_adminunitinvitation_created_by_id_user"),
|
||||
"adminunitinvitation",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_adminunitinvitation_updated_by_id_user"),
|
||||
"adminunitinvitation",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_adminunitinvitation_created_by_id_user",
|
||||
"adminunitinvitation",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_adminunitinvitation_updated_by_id_user",
|
||||
"adminunitinvitation",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_adminunit_created_by_id_user"), "adminunit", type_="foreignkey"
|
||||
)
|
||||
op.drop_constraint(
|
||||
op.f("fk_adminunit_updated_by_id_user"), "adminunit", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_adminunit_updated_by_id_user",
|
||||
"adminunit",
|
||||
"user",
|
||||
["updated_by_id"],
|
||||
["id"],
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_adminunit_created_by_id_user",
|
||||
"adminunit",
|
||||
"user",
|
||||
["created_by_id"],
|
||||
["id"],
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
53
migrations/versions/d952cd5df596_.py
Normal file
53
migrations/versions/d952cd5df596_.py
Normal file
@ -0,0 +1,53 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: d952cd5df596
|
||||
Revises: 5fde26e1904d
|
||||
Create Date: 2023-05-01 17:27:48.768633
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
|
||||
from project import dbtypes
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "d952cd5df596"
|
||||
down_revision = "5fde26e1904d"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(
|
||||
"fk_adminunitmember_user_id_user", "adminunitmember", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
op.f("fk_adminunitmember_user_id_user"),
|
||||
"adminunitmember",
|
||||
"user",
|
||||
["user_id"],
|
||||
["id"],
|
||||
ondelete="CASCADE",
|
||||
)
|
||||
op.add_column(
|
||||
"user", sa.Column("deletion_requested_at", sa.DateTime(), nullable=True)
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("user", "deletion_requested_at")
|
||||
op.drop_constraint(
|
||||
op.f("fk_adminunitmember_user_id_user"), "adminunitmember", type_="foreignkey"
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_adminunitmember_user_id_user",
|
||||
"adminunitmember",
|
||||
"user",
|
||||
["user_id"],
|
||||
["id"],
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
@ -193,7 +193,7 @@ convention = {
|
||||
}
|
||||
metadata = MetaData(naming_convention=convention)
|
||||
db = SQLAlchemy(app, metadata=metadata)
|
||||
migrate = Migrate(app, db)
|
||||
migrate = Migrate(app, db, render_as_batch=False)
|
||||
|
||||
# Celery tasks
|
||||
from project import celery_tasks
|
||||
|
||||
@ -11,6 +11,9 @@ def setup_periodic_tasks(sender, **kwargs):
|
||||
sender.add_periodic_task(
|
||||
crontab(hour=0, minute=30), delete_admin_units_with_due_request_task
|
||||
)
|
||||
sender.add_periodic_task(
|
||||
crontab(hour=0, minute=40), delete_user_with_due_request_task
|
||||
)
|
||||
sender.add_periodic_task(crontab(hour=1, minute=0), update_recurring_dates_task)
|
||||
sender.add_periodic_task(crontab(hour=2, minute=0), dump_all_task)
|
||||
sender.add_periodic_task(crontab(hour=3, minute=0), seo_generate_sitemap_task)
|
||||
@ -97,6 +100,36 @@ def delete_admin_unit_task(admin_unit_id):
|
||||
delete_admin_unit(admin_unit)
|
||||
|
||||
|
||||
@celery.task(
|
||||
acks_late=True,
|
||||
reject_on_worker_lost=True,
|
||||
)
|
||||
def delete_user_with_due_request_task():
|
||||
from project.services.user import get_users_with_due_delete_request
|
||||
|
||||
users = get_users_with_due_delete_request()
|
||||
|
||||
if not users:
|
||||
return
|
||||
|
||||
group(delete_user_task.s(user.id) for user in users).delay()
|
||||
|
||||
|
||||
@celery.task(
|
||||
acks_late=True,
|
||||
reject_on_worker_lost=True,
|
||||
)
|
||||
def delete_user_task(user_id):
|
||||
from project.services.user import delete_user, get_user
|
||||
|
||||
user = get_user(user_id)
|
||||
|
||||
if not user:
|
||||
return
|
||||
|
||||
delete_user(user)
|
||||
|
||||
|
||||
@celery.task(
|
||||
acks_late=True,
|
||||
reject_on_worker_lost=True,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from flask_babel import lazy_gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import BooleanField, SubmitField
|
||||
from wtforms.validators import Optional
|
||||
from wtforms import BooleanField, EmailField, SubmitField
|
||||
from wtforms.validators import DataRequired, Optional
|
||||
|
||||
|
||||
class NotificationForm(FlaskForm):
|
||||
@ -11,3 +11,13 @@ class NotificationForm(FlaskForm):
|
||||
validators=[Optional()],
|
||||
)
|
||||
submit = SubmitField(lazy_gettext("Save"))
|
||||
|
||||
|
||||
class RequestUserDeletionForm(FlaskForm):
|
||||
submit = SubmitField(lazy_gettext("Request deletion"))
|
||||
email = EmailField(lazy_gettext("Email"), validators=[DataRequired()])
|
||||
|
||||
|
||||
class CancelUserDeletionForm(FlaskForm):
|
||||
submit = SubmitField(lazy_gettext("Cancel deletion"))
|
||||
email = EmailField(lazy_gettext("Email"), validators=[DataRequired()])
|
||||
|
||||
@ -48,6 +48,13 @@ class AdminUnitMember(db.Model):
|
||||
admin_unit_id = db.Column(db.Integer, db.ForeignKey("adminunit.id"), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
|
||||
user = db.relationship("User", backref=db.backref("adminunitmembers", lazy=True))
|
||||
user_id = db.Column(
|
||||
db.Integer, db.ForeignKey("user.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
user = db.relationship(
|
||||
"User",
|
||||
backref=db.backref("adminunitmembers", cascade="all, delete-orphan", lazy=True),
|
||||
)
|
||||
roles = relationship(
|
||||
"AdminUnitMemberRole",
|
||||
secondary="adminunitmemberroles_members",
|
||||
|
||||
@ -6,11 +6,7 @@ from project.models.trackable_mixin import TrackableMixin
|
||||
|
||||
class EventList(db.Model, TrackableMixin):
|
||||
__tablename__ = "eventlist"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"name", "admin_unit_id", name="eventreference_name_admin_unit_id"
|
||||
),
|
||||
)
|
||||
__table_args__ = (UniqueConstraint("name", "admin_unit_id"),)
|
||||
id = Column(Integer(), primary_key=True)
|
||||
name = Column(Unicode(255))
|
||||
admin_unit_id = db.Column(db.Integer, db.ForeignKey("adminunit.id"), nullable=False)
|
||||
|
||||
@ -6,11 +6,7 @@ from project.models.trackable_mixin import TrackableMixin
|
||||
|
||||
class EventReference(db.Model, TrackableMixin):
|
||||
__tablename__ = "eventreference"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"event_id", "admin_unit_id", name="eventreference_event_id_admin_unit_id"
|
||||
),
|
||||
)
|
||||
__table_args__ = (UniqueConstraint("event_id", "admin_unit_id"),)
|
||||
id = Column(Integer(), primary_key=True)
|
||||
event_id = db.Column(db.Integer, db.ForeignKey("event.id"), nullable=False)
|
||||
admin_unit_id = db.Column(db.Integer, db.ForeignKey("adminunit.id"), nullable=False)
|
||||
|
||||
@ -27,7 +27,6 @@ class EventReferenceRequest(db.Model, TrackableMixin):
|
||||
UniqueConstraint(
|
||||
"event_id",
|
||||
"admin_unit_id",
|
||||
name="eventreferencerequest_event_id_admin_unit_id",
|
||||
),
|
||||
)
|
||||
id = Column(Integer(), primary_key=True)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import datetime
|
||||
|
||||
from sqlalchemy import Column, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import declared_attr, deferred, relationship
|
||||
from sqlalchemy.orm import backref, declared_attr, deferred, relationship
|
||||
|
||||
from project.models.functions import _current_user_id_or_none
|
||||
|
||||
@ -29,7 +29,7 @@ class TrackableMixin(object):
|
||||
return deferred(
|
||||
Column(
|
||||
"created_by_id",
|
||||
ForeignKey("user.id"),
|
||||
ForeignKey("user.id", ondelete="SET NULL"),
|
||||
default=_current_user_id_or_none,
|
||||
),
|
||||
group="trackable",
|
||||
@ -41,6 +41,7 @@ class TrackableMixin(object):
|
||||
"User",
|
||||
primaryjoin="User.id == %s.created_by_id" % cls.__name__,
|
||||
remote_side="User.id",
|
||||
backref=backref("created_%s" % cls.__tablename__, lazy=True),
|
||||
)
|
||||
|
||||
@declared_attr
|
||||
@ -48,7 +49,7 @@ class TrackableMixin(object):
|
||||
return deferred(
|
||||
Column(
|
||||
"updated_by_id",
|
||||
ForeignKey("user.id"),
|
||||
ForeignKey("user.id", ondelete="SET NULL"),
|
||||
default=_current_user_id_or_none,
|
||||
onupdate=_current_user_id_or_none,
|
||||
),
|
||||
@ -61,4 +62,5 @@ class TrackableMixin(object):
|
||||
"User",
|
||||
primaryjoin="User.id == %s.updated_by_id" % cls.__name__,
|
||||
remote_side="User.id",
|
||||
backref=backref("updated_%s" % cls.__tablename__, lazy=True),
|
||||
)
|
||||
|
||||
@ -60,6 +60,8 @@ class User(db.Model, UserMixin):
|
||||
)
|
||||
)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
created_at = deferred(Column(DateTime, default=datetime.datetime.utcnow))
|
||||
deletion_requested_at = deferred(Column(DateTime, nullable=True))
|
||||
|
||||
def get_user_id(self):
|
||||
return self.id
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import datetime
|
||||
|
||||
from flask_security import hash_password
|
||||
|
||||
from project import user_datastore
|
||||
from project import db, user_datastore
|
||||
from project.models import Event, Role, User, UserFavoriteEvents
|
||||
|
||||
|
||||
@ -99,3 +101,13 @@ def remove_favorite_event(user_id: int, event_id: int):
|
||||
|
||||
db.session.delete(favorite)
|
||||
return True
|
||||
|
||||
|
||||
def get_users_with_due_delete_request():
|
||||
due = datetime.datetime.utcnow() - datetime.timedelta(days=3)
|
||||
return User.query.filter(User.deletion_requested_at < due).all()
|
||||
|
||||
|
||||
def delete_user(user):
|
||||
user_datastore.delete_user(user)
|
||||
db.session.commit()
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
<th>{{ _('Email') }}</th>
|
||||
<th>created_at</th>
|
||||
<th>confirmed_at</th>
|
||||
<th>deletion_requested_at</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -28,6 +29,7 @@
|
||||
<td>{{ user.email }}</td>
|
||||
<td>{% if user.created_at %}{{ user.created_at | dateformat }}{% endif %}</td>
|
||||
<td>{% if user.confirmed_at %}{{ user.confirmed_at | dateformat }}{% endif %}</td>
|
||||
<td>{% if user.deletion_requested_at %}{{ user.deletion_requested_at | dateformat }}{% endif %}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('admin_user_update', id=user.id) }}">{{ _('Edit') }}</a>
|
||||
<a href="{{ url_for('admin_user_delete', id=user.id) }}">{{ _('Delete') }}</a>
|
||||
|
||||
@ -2,5 +2,5 @@
|
||||
{% from "_macros.html" import render_email_button %}
|
||||
{% block content %}
|
||||
<p>{{ _('%(admin_unit_name)s is scheduled for deletion.', admin_unit_name=admin_unit.name) }}</p>
|
||||
{{ render_email_button(url_for('admin_unit_cancel_deletion', id=admin_unit.id, _external=True), _('Click here to view the invitation')) }}
|
||||
{{ render_email_button(url_for('admin_unit_cancel_deletion', id=admin_unit.id, _external=True), _('Click here below to cancel the deletion')) }}
|
||||
{% endblock %}
|
||||
@ -0,0 +1,6 @@
|
||||
{% extends "email/layout.html" %}
|
||||
{% from "_macros.html" import render_email_button %}
|
||||
{% block content %}
|
||||
<p>{{ _('%(user_email)s is scheduled for deletion.', user_email=user.email) }}</p>
|
||||
{{ render_email_button(url_for('user_cancel_deletion', _external=True), _('Click the link below to cancel the deletion')) }}
|
||||
{% endblock %}
|
||||
@ -0,0 +1,3 @@
|
||||
{{ _('%(user_email)s is scheduled for deletion.', user_email=user.email) }}
|
||||
{{ _('Click the link below to cancel the deletion') }}
|
||||
{{ url_for('user_cancel_deletion', _external=True) }}
|
||||
@ -7,6 +7,13 @@
|
||||
|
||||
<h1>{{ current_user.email }}</h1>
|
||||
|
||||
{% if current_user.deletion_requested_at %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{ _('Your account is scheduled for deletion.') }}
|
||||
<a class="alert-link" href="{{ url_for('user_cancel_deletion') }}" role="button">{{ _('Cancel deletion') }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h2>{{ _('Profile') }}</h2>
|
||||
|
||||
<div class="list-group">
|
||||
@ -14,6 +21,12 @@
|
||||
{{ _fsdomain('Change password') }}
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</a>
|
||||
{% if not current_user.deletion_requested_at %}
|
||||
<a href="{{ url_for('user_request_deletion') }}" class="list-group-item text-danger">
|
||||
{{ _('Delete account') }}
|
||||
<i class="fa fa-caret-right"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h2>{{ _('Settings') }}</h2>
|
||||
|
||||
24
project/templates/user/cancel_deletion.html
Normal file
24
project/templates/user/cancel_deletion.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "_macros.html" import render_field_with_errors, render_field %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ _('Cancel deletion') }} "{{ current_user.email }}"</h1>
|
||||
|
||||
<form action="" method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
{{ _('User') }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{ render_field_with_errors(form.email) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ render_field(form.submit) }}
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
26
project/templates/user/request_deletion.html
Normal file
26
project/templates/user/request_deletion.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends "layout.html" %}
|
||||
{% from "_macros.html" import render_field_with_errors, render_field %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ _('Delete account') }} "{{ current_user.email }}"</h1>
|
||||
|
||||
<p>{{ _('The account is not deleted immediately. After a period of time, the account will be deleted. Until then, the deletion can be canceled.') }}</p>
|
||||
|
||||
<form action="" method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
{{ _('User') }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{ render_field_with_errors(form.email) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ render_field(form.submit) }}
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2023-04-27 15:10+0200\n"
|
||||
"POT-Creation-Date: 2023-05-01 18:11+0200\n"
|
||||
"PO-Revision-Date: 2020-06-07 18:51+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: de\n"
|
||||
@ -195,7 +195,7 @@ msgid "message"
|
||||
msgstr "message"
|
||||
|
||||
#: project/api/organization/resources.py:401
|
||||
#: project/views/admin_unit_member_invitation.py:85
|
||||
#: project/views/admin_unit_member_invitation.py:89
|
||||
msgid "You have received an invitation"
|
||||
msgstr "Du hast eine Einladung erhalten"
|
||||
|
||||
@ -212,7 +212,7 @@ msgstr "Impressum"
|
||||
#: project/forms/admin.py:13 project/templates/_macros.html:1473
|
||||
#: project/templates/layout.html:311
|
||||
#: project/templates/widget/event_suggestion/create.html:204
|
||||
#: project/views/admin_unit.py:82 project/views/root.py:71
|
||||
#: project/views/admin_unit.py:83 project/views/root.py:71
|
||||
msgid "Contact"
|
||||
msgstr "Kontakt"
|
||||
|
||||
@ -248,6 +248,7 @@ msgstr "Nutzer löschen"
|
||||
#: project/forms/admin_unit_member.py:11 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/forms/user.py:18 project/forms/user.py:23
|
||||
#: project/templates/_macros.html:237 project/templates/_macros.html:1569
|
||||
#: project/templates/admin/admin.html:27 project/templates/admin/email.html:4
|
||||
#: project/templates/admin/email.html:66 project/templates/admin/users.html:19
|
||||
@ -494,14 +495,15 @@ msgstr "Hauptfarbe"
|
||||
msgid "Link Color"
|
||||
msgstr "Linkfarbe"
|
||||
|
||||
#: project/forms/admin_unit.py:133
|
||||
#: project/forms/admin_unit.py:133 project/forms/user.py:17
|
||||
msgid "Request deletion"
|
||||
msgstr "Löschung beantragen"
|
||||
|
||||
#: project/forms/admin_unit.py:138
|
||||
#: project/forms/admin_unit.py:138 project/forms/user.py:22
|
||||
#: project/templates/admin_unit/cancel_deletion.html:6
|
||||
#: project/templates/admin_unit/update.html:26
|
||||
#: project/templates/manage/events.html:49
|
||||
#: project/templates/manage/events.html:49 project/templates/profile.html:13
|
||||
#: project/templates/user/cancel_deletion.html:6
|
||||
msgid "Cancel deletion"
|
||||
msgstr "Löschen abbrechen"
|
||||
|
||||
@ -1381,7 +1383,7 @@ msgstr "Merkzettel"
|
||||
#: project/templates/_macros.html:590 project/templates/_macros.html:633
|
||||
#: project/templates/_macros.html:765
|
||||
#: project/templates/admin/admin_units.html:36
|
||||
#: project/templates/admin/users.html:32
|
||||
#: project/templates/admin/users.html:34
|
||||
#: project/templates/manage/events.html:116
|
||||
#: project/templates/manage/members.html:35
|
||||
#: project/templates/manage/organizers.html:33
|
||||
@ -1596,7 +1598,7 @@ msgstr "Planung"
|
||||
#: 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
|
||||
#: project/templates/profile.html:10
|
||||
#: project/templates/profile.html:17
|
||||
msgid "Profile"
|
||||
msgstr "Profil"
|
||||
|
||||
@ -1698,7 +1700,7 @@ msgstr "Organisationseinladungen"
|
||||
#: project/templates/admin_unit/update.html:6
|
||||
#: project/templates/admin_unit/update.html:30
|
||||
#: project/templates/layout.html:259 project/templates/manage/widgets.html:11
|
||||
#: project/templates/manage/widgets.html:15 project/templates/profile.html:19
|
||||
#: project/templates/manage/widgets.html:15 project/templates/profile.html:32
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
@ -1724,24 +1726,34 @@ msgstr "Organisation wechseln"
|
||||
|
||||
#: project/templates/developer/read.html:4
|
||||
#: project/templates/developer/read.html:8 project/templates/layout.html:319
|
||||
#: project/templates/profile.html:33
|
||||
#: project/templates/profile.html:46
|
||||
msgid "Developer"
|
||||
msgstr "Entwickler"
|
||||
|
||||
#: project/templates/profile.html:23
|
||||
#: project/templates/profile.html:12 project/views/admin_unit.py:89
|
||||
#: project/views/admin_unit_member_invitation.py:39
|
||||
msgid "Your account is scheduled for deletion."
|
||||
msgstr "Dein Account ist zur Löschung vorgesehen."
|
||||
|
||||
#: project/templates/profile.html:26
|
||||
#: project/templates/user/request_deletion.html:6
|
||||
msgid "Delete account"
|
||||
msgstr "Account löschen"
|
||||
|
||||
#: project/templates/profile.html:36
|
||||
#: project/templates/user/notifications.html:4
|
||||
#: project/templates/user/notifications.html:8
|
||||
msgid "Notifications"
|
||||
msgstr "Benachrichtigungen"
|
||||
|
||||
#: project/templates/profile.html:27
|
||||
#: project/templates/profile.html:40
|
||||
msgid "Applications"
|
||||
msgstr "Apps"
|
||||
|
||||
#: project/templates/oauth2_client/list.html:4
|
||||
#: project/templates/oauth2_client/list.html:11
|
||||
#: project/templates/oauth2_client/read.html:11
|
||||
#: project/templates/profile.html:37
|
||||
#: project/templates/profile.html:50
|
||||
msgid "OAuth2 clients"
|
||||
msgstr "OAuth2 Clients"
|
||||
|
||||
@ -1759,7 +1771,7 @@ msgid "View"
|
||||
msgstr "Anzeigen"
|
||||
|
||||
#: project/templates/admin/admin_units.html:37
|
||||
#: project/templates/admin/users.html:33
|
||||
#: project/templates/admin/users.html:35
|
||||
#: project/templates/manage/events.html:117
|
||||
#: project/templates/manage/members.html:21
|
||||
#: project/templates/manage/members.html:36
|
||||
@ -1771,6 +1783,8 @@ msgid "Delete"
|
||||
msgstr "Löschen"
|
||||
|
||||
#: project/templates/admin/delete_user.html:13
|
||||
#: project/templates/user/cancel_deletion.html:13
|
||||
#: project/templates/user/request_deletion.html:15
|
||||
msgid "User"
|
||||
msgstr "Nutzer"
|
||||
|
||||
@ -1815,7 +1829,9 @@ msgstr "Nutzer:in einladen"
|
||||
msgid ""
|
||||
"The organization is not deleted immediately. After a period of time, the "
|
||||
"organization will be deleted. Until then, the deletion can be canceled."
|
||||
msgstr "Die Organisation wird nicht sofort gelöscht. Nach einer Frist wird die Organisation gelöscht. Bis dahin kann die Löschung abgebrochen werden."
|
||||
msgstr ""
|
||||
"Die Organisation wird nicht sofort gelöscht. Nach einer Frist wird die "
|
||||
"Organisation gelöscht. Bis dahin kann die Löschung abgebrochen werden."
|
||||
|
||||
#: project/templates/admin_unit/update.html:25
|
||||
#: project/templates/manage/events.html:48
|
||||
@ -1857,7 +1873,6 @@ msgid "You have been invited to join %(admin_unit_name)s."
|
||||
msgstr "Du wurdest eingeladen, %(admin_unit_name)s beizutreten."
|
||||
|
||||
#: project/templates/email/invitation_notice.html:5
|
||||
#: project/templates/email/organization_deletion_requested_notice.html:5
|
||||
#: project/templates/email/organization_invitation_notice.html:5
|
||||
msgid "Click here to view the invitation"
|
||||
msgstr "Klicke hier, um die Einladung anzunehmen."
|
||||
@ -1880,6 +1895,10 @@ msgstr "Benachrichtigungseinstellungen"
|
||||
msgid "%(admin_unit_name)s is scheduled for deletion."
|
||||
msgstr "%(admin_unit_name)s ist zur Löschung vorgesehen."
|
||||
|
||||
#: project/templates/email/organization_deletion_requested_notice.html:5
|
||||
msgid "Click here below to cancel the deletion"
|
||||
msgstr "Klicke hier, um das Löschen abzubrechen"
|
||||
|
||||
#: project/templates/email/organization_invitation_accepted_notice.html:4
|
||||
#, python-format
|
||||
msgid ""
|
||||
@ -1948,6 +1967,15 @@ msgstr "Das ist eine Test-Mail"
|
||||
msgid "Click here to open the site"
|
||||
msgstr "Klicke hier, um die Seite zu öffnen"
|
||||
|
||||
#: project/templates/email/user_deletion_requested_notice.html:4
|
||||
#, python-format
|
||||
msgid "%(user_email)s is scheduled for deletion."
|
||||
msgstr "%(user_email)s ist zur Löschung vorgesehen."
|
||||
|
||||
#: project/templates/email/user_deletion_requested_notice.html:5
|
||||
msgid "Click the link below to cancel the deletion"
|
||||
msgstr "Klicke den Link, um das Löschen abzubrechen"
|
||||
|
||||
#: project/templates/event/actions.html:5
|
||||
#: project/templates/event/actions.html:22
|
||||
msgid "Actions for event"
|
||||
@ -2273,6 +2301,14 @@ msgstr "Du hast noch keinen Account? Kein Problem!"
|
||||
msgid "Register for free"
|
||||
msgstr "Kostenlos registrieren"
|
||||
|
||||
#: project/templates/user/request_deletion.html:8
|
||||
msgid ""
|
||||
"The account is not deleted immediately. After a period of time, the "
|
||||
"account will be deleted. Until then, the deletion can be canceled."
|
||||
msgstr ""
|
||||
"Dein Account wird nicht sofort gelöscht. Nach einer Frist wird dein "
|
||||
"Account gelöscht. Bis dahin kann die Löschung abgebrochen werden."
|
||||
|
||||
#: project/templates/widget/event_date/list.html:5
|
||||
msgid "Widget"
|
||||
msgstr "Widget"
|
||||
@ -2297,8 +2333,8 @@ msgstr "Vorschau"
|
||||
msgid "Organization successfully updated"
|
||||
msgstr "Organisation erfolgreich aktualisiert"
|
||||
|
||||
#: project/views/admin.py:85 project/views/admin_unit.py:182
|
||||
#: project/views/admin_unit.py:215
|
||||
#: project/views/admin.py:85 project/views/admin_unit.py:187
|
||||
#: project/views/admin_unit.py:220
|
||||
msgid "Entered name does not match organization name"
|
||||
msgstr "Der eingegebene Name entspricht nicht dem Namen der Organisation"
|
||||
|
||||
@ -2307,7 +2343,7 @@ msgid "Organization successfully deleted"
|
||||
msgstr "Organisation erfolgreich gelöscht"
|
||||
|
||||
#: project/views/admin.py:113 project/views/manage.py:432
|
||||
#: project/views/user.py:28
|
||||
#: project/views/user.py:40
|
||||
msgid "Settings successfully updated"
|
||||
msgstr "Einstellungen erfolgreich aktualisiert"
|
||||
|
||||
@ -2329,11 +2365,11 @@ msgstr "Nutzer erfolgreich aktualisiert"
|
||||
msgid "Entered email does not match user email"
|
||||
msgstr "Die eingegebene Email passt nicht zur Email des Nutzers"
|
||||
|
||||
#: project/views/admin.py:237
|
||||
#: project/views/admin.py:236
|
||||
msgid "User successfully deleted"
|
||||
msgstr "Nutzer erfolgreich gelöscht"
|
||||
|
||||
#: project/views/admin_unit.py:78
|
||||
#: project/views/admin_unit.py:79
|
||||
msgid ""
|
||||
"Organizations cannot currently be created. The project is in a closed "
|
||||
"test phase. If you are interested, you can contact us."
|
||||
@ -2342,19 +2378,19 @@ msgstr ""
|
||||
" sich in einer geschlossenen Test-Phase. Bei Interesse kannst du uns "
|
||||
"kontaktieren."
|
||||
|
||||
#: project/views/admin_unit.py:128
|
||||
#: project/views/admin_unit.py:133
|
||||
msgid "Organization successfully created"
|
||||
msgstr "Organisation erfolgreich erstellt"
|
||||
|
||||
#: project/views/admin_unit.py:158
|
||||
#: project/views/admin_unit.py:163
|
||||
msgid "AdminUnit successfully updated"
|
||||
msgstr "Organisation erfolgreich aktualisiert"
|
||||
|
||||
#: project/views/admin_unit.py:245
|
||||
#: project/views/admin_unit.py:250
|
||||
msgid "Organization invitation accepted"
|
||||
msgstr "Organisationseinladung akzeptiert"
|
||||
|
||||
#: project/views/admin_unit.py:259
|
||||
#: project/views/admin_unit.py:264
|
||||
msgid "Organization deletion requested"
|
||||
msgstr "Löschung der Organisation beantragt"
|
||||
|
||||
@ -2370,23 +2406,23 @@ msgstr "Die eingegebene Email passt nicht zur Email des Mitglieds"
|
||||
msgid "Member successfully deleted"
|
||||
msgstr "Mitglied erfolgreich gelöscht"
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:38
|
||||
#: project/views/admin_unit_member_invitation.py:42
|
||||
msgid "Invitation successfully accepted"
|
||||
msgstr "Einladung erfolgreich akzeptiert"
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:45
|
||||
#: project/views/admin_unit_member_invitation.py:49
|
||||
msgid "Invitation successfully declined"
|
||||
msgstr "Einladung erfolgreich abgelehnt"
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:90
|
||||
#: project/views/admin_unit_member_invitation.py:94
|
||||
msgid "Invitation successfully sent"
|
||||
msgstr "Einladung erfolgreich gesendet"
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:113
|
||||
#: project/views/admin_unit_member_invitation.py:117
|
||||
msgid "Entered email does not match invitation email"
|
||||
msgstr "Die eingegebene Email passt nicht zur Email der Einladung"
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:118
|
||||
#: project/views/admin_unit_member_invitation.py:122
|
||||
msgid "Invitation successfully deleted"
|
||||
msgstr "Einladung erfolgreich gelöscht"
|
||||
|
||||
@ -2545,6 +2581,14 @@ msgstr ""
|
||||
"Ob alle zukünftigen Empfehlungsanfragen von %(admin_unit_name)s "
|
||||
"automatisch verifiziert werden sollen."
|
||||
|
||||
#: project/views/user.py:84 project/views/user.py:111
|
||||
msgid "Entered email does not match your email"
|
||||
msgstr "Die eingegebene Email entspricht nicht deiner Email"
|
||||
|
||||
#: project/views/user.py:130
|
||||
msgid "User deletion requested"
|
||||
msgstr "Löschung des Nutzers beantragt"
|
||||
|
||||
#: project/views/utils.py:71
|
||||
msgid ""
|
||||
"An entry with the entered values already exists. Duplicate entries are "
|
||||
|
||||
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2023-04-27 15:10+0200\n"
|
||||
"POT-Creation-Date: 2023-05-01 18:11+0200\n"
|
||||
"PO-Revision-Date: 2021-04-30 15:04+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: en\n"
|
||||
@ -195,7 +195,7 @@ msgid "message"
|
||||
msgstr ""
|
||||
|
||||
#: project/api/organization/resources.py:401
|
||||
#: project/views/admin_unit_member_invitation.py:85
|
||||
#: project/views/admin_unit_member_invitation.py:89
|
||||
msgid "You have received an invitation"
|
||||
msgstr ""
|
||||
|
||||
@ -212,7 +212,7 @@ msgstr ""
|
||||
#: project/forms/admin.py:13 project/templates/_macros.html:1473
|
||||
#: project/templates/layout.html:311
|
||||
#: project/templates/widget/event_suggestion/create.html:204
|
||||
#: project/views/admin_unit.py:82 project/views/root.py:71
|
||||
#: project/views/admin_unit.py:83 project/views/root.py:71
|
||||
msgid "Contact"
|
||||
msgstr ""
|
||||
|
||||
@ -248,6 +248,7 @@ msgstr ""
|
||||
#: project/forms/admin_unit_member.py:11 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/forms/user.py:18 project/forms/user.py:23
|
||||
#: project/templates/_macros.html:237 project/templates/_macros.html:1569
|
||||
#: project/templates/admin/admin.html:27 project/templates/admin/email.html:4
|
||||
#: project/templates/admin/email.html:66 project/templates/admin/users.html:19
|
||||
@ -475,14 +476,15 @@ msgstr ""
|
||||
msgid "Link Color"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin_unit.py:133
|
||||
#: project/forms/admin_unit.py:133 project/forms/user.py:17
|
||||
msgid "Request deletion"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin_unit.py:138
|
||||
#: project/forms/admin_unit.py:138 project/forms/user.py:22
|
||||
#: project/templates/admin_unit/cancel_deletion.html:6
|
||||
#: project/templates/admin_unit/update.html:26
|
||||
#: project/templates/manage/events.html:49
|
||||
#: project/templates/manage/events.html:49 project/templates/profile.html:13
|
||||
#: project/templates/user/cancel_deletion.html:6
|
||||
msgid "Cancel deletion"
|
||||
msgstr ""
|
||||
|
||||
@ -1332,7 +1334,7 @@ msgstr ""
|
||||
#: project/templates/_macros.html:590 project/templates/_macros.html:633
|
||||
#: project/templates/_macros.html:765
|
||||
#: project/templates/admin/admin_units.html:36
|
||||
#: project/templates/admin/users.html:32
|
||||
#: project/templates/admin/users.html:34
|
||||
#: project/templates/manage/events.html:116
|
||||
#: project/templates/manage/members.html:35
|
||||
#: project/templates/manage/organizers.html:33
|
||||
@ -1547,7 +1549,7 @@ msgstr ""
|
||||
#: 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
|
||||
#: project/templates/profile.html:10
|
||||
#: project/templates/profile.html:17
|
||||
msgid "Profile"
|
||||
msgstr ""
|
||||
|
||||
@ -1649,7 +1651,7 @@ msgstr ""
|
||||
#: project/templates/admin_unit/update.html:6
|
||||
#: project/templates/admin_unit/update.html:30
|
||||
#: project/templates/layout.html:259 project/templates/manage/widgets.html:11
|
||||
#: project/templates/manage/widgets.html:15 project/templates/profile.html:19
|
||||
#: project/templates/manage/widgets.html:15 project/templates/profile.html:32
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
@ -1675,24 +1677,34 @@ msgstr ""
|
||||
|
||||
#: project/templates/developer/read.html:4
|
||||
#: project/templates/developer/read.html:8 project/templates/layout.html:319
|
||||
#: project/templates/profile.html:33
|
||||
#: project/templates/profile.html:46
|
||||
msgid "Developer"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/profile.html:23
|
||||
#: project/templates/profile.html:12 project/views/admin_unit.py:89
|
||||
#: project/views/admin_unit_member_invitation.py:39
|
||||
msgid "Your account is scheduled for deletion."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/profile.html:26
|
||||
#: project/templates/user/request_deletion.html:6
|
||||
msgid "Delete account"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/profile.html:36
|
||||
#: project/templates/user/notifications.html:4
|
||||
#: project/templates/user/notifications.html:8
|
||||
msgid "Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/profile.html:27
|
||||
#: project/templates/profile.html:40
|
||||
msgid "Applications"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/oauth2_client/list.html:4
|
||||
#: project/templates/oauth2_client/list.html:11
|
||||
#: project/templates/oauth2_client/read.html:11
|
||||
#: project/templates/profile.html:37
|
||||
#: project/templates/profile.html:50
|
||||
msgid "OAuth2 clients"
|
||||
msgstr ""
|
||||
|
||||
@ -1710,7 +1722,7 @@ msgid "View"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/admin/admin_units.html:37
|
||||
#: project/templates/admin/users.html:33
|
||||
#: project/templates/admin/users.html:35
|
||||
#: project/templates/manage/events.html:117
|
||||
#: project/templates/manage/members.html:21
|
||||
#: project/templates/manage/members.html:36
|
||||
@ -1722,6 +1734,8 @@ msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/admin/delete_user.html:13
|
||||
#: project/templates/user/cancel_deletion.html:13
|
||||
#: project/templates/user/request_deletion.html:15
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
@ -1808,7 +1822,6 @@ msgid "You have been invited to join %(admin_unit_name)s."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/invitation_notice.html:5
|
||||
#: project/templates/email/organization_deletion_requested_notice.html:5
|
||||
#: project/templates/email/organization_invitation_notice.html:5
|
||||
msgid "Click here to view the invitation"
|
||||
msgstr ""
|
||||
@ -1831,6 +1844,10 @@ msgstr ""
|
||||
msgid "%(admin_unit_name)s is scheduled for deletion."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_deletion_requested_notice.html:5
|
||||
msgid "Click here below to cancel the deletion"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_invitation_accepted_notice.html:4
|
||||
#, python-format
|
||||
msgid ""
|
||||
@ -1897,6 +1914,15 @@ msgstr ""
|
||||
msgid "Click here to open the site"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/user_deletion_requested_notice.html:4
|
||||
#, python-format
|
||||
msgid "%(user_email)s is scheduled for deletion."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/user_deletion_requested_notice.html:5
|
||||
msgid "Click the link below to cancel the deletion"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/event/actions.html:5
|
||||
#: project/templates/event/actions.html:22
|
||||
msgid "Actions for event"
|
||||
@ -2213,6 +2239,12 @@ msgstr ""
|
||||
msgid "Register for free"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/user/request_deletion.html:8
|
||||
msgid ""
|
||||
"The account is not deleted immediately. After a period of time, the "
|
||||
"account will be deleted. Until then, the deletion can be canceled."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/widget/event_date/list.html:5
|
||||
msgid "Widget"
|
||||
msgstr ""
|
||||
@ -2237,8 +2269,8 @@ msgstr ""
|
||||
msgid "Organization successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin.py:85 project/views/admin_unit.py:182
|
||||
#: project/views/admin_unit.py:215
|
||||
#: project/views/admin.py:85 project/views/admin_unit.py:187
|
||||
#: project/views/admin_unit.py:220
|
||||
msgid "Entered name does not match organization name"
|
||||
msgstr ""
|
||||
|
||||
@ -2247,7 +2279,7 @@ msgid "Organization successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin.py:113 project/views/manage.py:432
|
||||
#: project/views/user.py:28
|
||||
#: project/views/user.py:40
|
||||
msgid "Settings successfully updated"
|
||||
msgstr ""
|
||||
|
||||
@ -2269,29 +2301,29 @@ msgstr ""
|
||||
msgid "Entered email does not match user email"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin.py:237
|
||||
#: project/views/admin.py:236
|
||||
msgid "User successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:78
|
||||
#: project/views/admin_unit.py:79
|
||||
msgid ""
|
||||
"Organizations cannot currently be created. The project is in a closed "
|
||||
"test phase. If you are interested, you can contact us."
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:128
|
||||
#: project/views/admin_unit.py:133
|
||||
msgid "Organization successfully created"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:158
|
||||
#: project/views/admin_unit.py:163
|
||||
msgid "AdminUnit successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:245
|
||||
#: project/views/admin_unit.py:250
|
||||
msgid "Organization invitation accepted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:259
|
||||
#: project/views/admin_unit.py:264
|
||||
msgid "Organization deletion requested"
|
||||
msgstr ""
|
||||
|
||||
@ -2307,23 +2339,23 @@ msgstr ""
|
||||
msgid "Member successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:38
|
||||
#: project/views/admin_unit_member_invitation.py:42
|
||||
msgid "Invitation successfully accepted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:45
|
||||
#: project/views/admin_unit_member_invitation.py:49
|
||||
msgid "Invitation successfully declined"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:90
|
||||
#: project/views/admin_unit_member_invitation.py:94
|
||||
msgid "Invitation successfully sent"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:113
|
||||
#: project/views/admin_unit_member_invitation.py:117
|
||||
msgid "Entered email does not match invitation email"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:118
|
||||
#: project/views/admin_unit_member_invitation.py:122
|
||||
msgid "Invitation successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
@ -2478,6 +2510,14 @@ msgid ""
|
||||
"verified automatically."
|
||||
msgstr ""
|
||||
|
||||
#: project/views/user.py:84 project/views/user.py:111
|
||||
msgid "Entered email does not match your email"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/user.py:130
|
||||
msgid "User deletion requested"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/utils.py:71
|
||||
msgid ""
|
||||
"An entry with the entered values already exists. Duplicate entries are "
|
||||
|
||||
@ -5,7 +5,7 @@ from flask_security import roles_required
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from project import app, db, user_datastore
|
||||
from project import app, db
|
||||
from project.base_tasks import send_mail_task
|
||||
from project.forms.admin import (
|
||||
AdminNewsletterForm,
|
||||
@ -19,7 +19,7 @@ from project.forms.admin import (
|
||||
from project.models import AdminUnit, Role, User
|
||||
from project.services.admin import upsert_settings
|
||||
from project.services.admin_unit import delete_admin_unit
|
||||
from project.services.user import set_roles_for_user
|
||||
from project.services.user import delete_user, set_roles_for_user
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
get_celery_poll_group_result,
|
||||
@ -232,8 +232,7 @@ def admin_user_delete(id):
|
||||
flash(gettext("Entered email does not match user email"), "danger")
|
||||
else:
|
||||
try:
|
||||
user_datastore.delete_user(user)
|
||||
db.session.commit()
|
||||
delete_user(user)
|
||||
flash(gettext("User successfully deleted"), "success")
|
||||
return redirect(url_for("admin_users"))
|
||||
except SQLAlchemyError as e:
|
||||
|
||||
@ -73,7 +73,8 @@ def admin_unit_create():
|
||||
if not strings_are_equal_ignoring_case(invitation.email, current_user.email):
|
||||
return permission_missing(url_for("manage_admin_units"))
|
||||
|
||||
if not invitation and not can_create_admin_unit():
|
||||
if not invitation:
|
||||
if not can_create_admin_unit():
|
||||
flash_message(
|
||||
gettext(
|
||||
"Organizations cannot currently be created. The project is in a closed test phase. If you are interested, you can contact us."
|
||||
@ -84,6 +85,10 @@ def admin_unit_create():
|
||||
)
|
||||
return redirect(url_for("manage_admin_units"))
|
||||
|
||||
if current_user.deletion_requested_at: # pragma: no cover
|
||||
flash(gettext("Your account is scheduled for deletion."), "danger")
|
||||
return redirect(url_for("profile"))
|
||||
|
||||
form = CreateAdminUnitForm()
|
||||
|
||||
if invitation and not form.is_submitted():
|
||||
|
||||
@ -35,6 +35,10 @@ def admin_unit_member_invitation(id):
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
if form.accept.data:
|
||||
if current_user.deletion_requested_at: # pragma: no cover
|
||||
flash(gettext("Your account is scheduled for deletion."), "danger")
|
||||
return redirect(url_for("profile"))
|
||||
|
||||
message = gettext("Invitation successfully accepted")
|
||||
roles = invitation.roles.split(",")
|
||||
add_user_to_admin_unit_with_roles(
|
||||
|
||||
@ -1,12 +1,24 @@
|
||||
import datetime
|
||||
|
||||
from flask import flash, redirect, render_template, url_for
|
||||
from flask_babel import gettext
|
||||
from flask_security import auth_required, current_user
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from project import app, db
|
||||
from project.forms.user import NotificationForm
|
||||
from project.forms.user import (
|
||||
CancelUserDeletionForm,
|
||||
NotificationForm,
|
||||
RequestUserDeletionForm,
|
||||
)
|
||||
from project.models import AdminUnitInvitation, User
|
||||
from project.views.utils import get_invitation_access_result, handleSqlError
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
get_invitation_access_result,
|
||||
handleSqlError,
|
||||
non_match_for_deletion,
|
||||
send_mail,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/profile")
|
||||
@ -56,3 +68,66 @@ def user_organization_invitations(path=None):
|
||||
@auth_required()
|
||||
def user_favorite_events():
|
||||
return render_template("user/favorite_events.html")
|
||||
|
||||
|
||||
@app.route("/user/request-deletion", methods=("GET", "POST"))
|
||||
@auth_required()
|
||||
def user_request_deletion():
|
||||
if current_user.deletion_requested_at: # pragma: no cover
|
||||
return redirect(url_for("user_cancel_deletion"))
|
||||
|
||||
form = None
|
||||
form = RequestUserDeletionForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
if non_match_for_deletion(form.email.data, current_user.email):
|
||||
flash(gettext("Entered email does not match your email"), "danger")
|
||||
else:
|
||||
current_user.deletion_requested_at = datetime.datetime.utcnow()
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
send_user_deletion_requested_mail(current_user)
|
||||
return redirect(url_for("profile"))
|
||||
except SQLAlchemyError as e:
|
||||
db.session.rollback()
|
||||
flash(handleSqlError(e), "danger")
|
||||
else:
|
||||
flash_errors(form)
|
||||
|
||||
return render_template("user/request_deletion.html", form=form)
|
||||
|
||||
|
||||
@app.route("/user/cancel-deletion", methods=("GET", "POST"))
|
||||
@auth_required()
|
||||
def user_cancel_deletion():
|
||||
if not current_user.deletion_requested_at: # pragma: no cover
|
||||
return redirect(url_for("user_request_deletion"))
|
||||
|
||||
form = CancelUserDeletionForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
if non_match_for_deletion(form.email.data, current_user.email):
|
||||
flash(gettext("Entered email does not match your email"), "danger")
|
||||
else:
|
||||
current_user.deletion_requested_at = None
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
return redirect(url_for("profile"))
|
||||
except SQLAlchemyError as e:
|
||||
db.session.rollback()
|
||||
flash(handleSqlError(e), "danger")
|
||||
else:
|
||||
flash_errors(form)
|
||||
|
||||
return render_template("user/cancel_deletion.html", form=form)
|
||||
|
||||
|
||||
def send_user_deletion_requested_mail(user):
|
||||
send_mail(
|
||||
user.email,
|
||||
gettext("User deletion requested"),
|
||||
"user_deletion_requested_notice",
|
||||
user=user,
|
||||
)
|
||||
|
||||
@ -170,7 +170,7 @@ def send_mail_message(msg):
|
||||
app.logger.info(msg.body)
|
||||
return
|
||||
|
||||
mail.send(msg)
|
||||
mail.send(msg) # pragma: no cover
|
||||
|
||||
|
||||
def non_match_for_deletion(str1: str, str2: str) -> bool:
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import pytest
|
||||
|
||||
from project.api import RestApi
|
||||
|
||||
|
||||
def test_handle_error_unique(app):
|
||||
from project.api import RestApi
|
||||
from project.utils import make_unique_violation
|
||||
|
||||
error = make_unique_violation()
|
||||
@ -17,6 +16,7 @@ def test_handle_error_unique(app):
|
||||
|
||||
|
||||
def test_handle_error_checkViolation(app):
|
||||
from project.api import RestApi
|
||||
from project.utils import make_check_violation
|
||||
|
||||
error = make_check_violation()
|
||||
@ -30,6 +30,7 @@ def test_handle_error_checkViolation(app):
|
||||
|
||||
|
||||
def test_handle_error_integrity(app):
|
||||
from project.api import RestApi
|
||||
from project.utils import make_integrity_error
|
||||
|
||||
error = make_integrity_error("custom")
|
||||
@ -45,6 +46,8 @@ def test_handle_error_integrity(app):
|
||||
def test_handle_error_httpException(app):
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
from project.api import RestApi
|
||||
|
||||
error = InternalServerError()
|
||||
|
||||
with app.app_context():
|
||||
@ -58,6 +61,8 @@ def test_handle_error_unprocessableEntity(app):
|
||||
from marshmallow import ValidationError
|
||||
from werkzeug.exceptions import UnprocessableEntity
|
||||
|
||||
from project.api import RestApi
|
||||
|
||||
args = {"name": ["Required"]}
|
||||
validation_error = ValidationError(args)
|
||||
|
||||
@ -76,6 +81,8 @@ def test_handle_error_unprocessableEntity(app):
|
||||
def test_handle_error_validationError(app):
|
||||
from marshmallow import ValidationError
|
||||
|
||||
from project.api import RestApi
|
||||
|
||||
args = {"name": ["Required"]}
|
||||
validation_error = ValidationError(args)
|
||||
|
||||
@ -90,6 +97,7 @@ def test_handle_error_validationError(app):
|
||||
|
||||
def test_handle_error_unspecificRaises(app):
|
||||
error = Exception()
|
||||
from project.api import RestApi
|
||||
|
||||
with app.app_context():
|
||||
app.config["PROPAGATE_EXCEPTIONS"] = False
|
||||
|
||||
@ -2,8 +2,6 @@ import base64
|
||||
|
||||
import pytest
|
||||
|
||||
from project.models import PublicStatus
|
||||
|
||||
|
||||
def test_read(client, app, db, seeder, utils):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
@ -225,6 +223,7 @@ def test_put(client, seeder, utils, app, db, mocker, variant):
|
||||
EventAttendanceMode,
|
||||
EventStatus,
|
||||
EventTargetGroupOrigin,
|
||||
PublicStatus,
|
||||
)
|
||||
|
||||
event = db.session.get(Event, event_id)
|
||||
|
||||
@ -333,6 +333,7 @@ class Seeder(object):
|
||||
description="Beschreibung",
|
||||
tags="",
|
||||
place_id=None,
|
||||
**kwargs
|
||||
):
|
||||
from project.models import (
|
||||
Event,
|
||||
@ -344,6 +345,7 @@ class Seeder(object):
|
||||
|
||||
with self._app.app_context():
|
||||
event = Event()
|
||||
event.__dict__.update(kwargs)
|
||||
event.admin_unit_id = admin_unit_id
|
||||
event.categories = [upsert_event_category("Other")]
|
||||
event.name = name
|
||||
|
||||
@ -40,3 +40,22 @@ def test_remove_favorite_event(client, seeder, utils, app):
|
||||
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
|
||||
|
||||
|
||||
def test_get_users_with_due_delete_request(client, seeder, db, utils, app):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
|
||||
with app.app_context():
|
||||
import datetime
|
||||
|
||||
from project.models import User
|
||||
from project.services.user import get_users_with_due_delete_request
|
||||
|
||||
user = db.session.get(User, user_id)
|
||||
user.deletion_requested_at = datetime.datetime.utcnow() - datetime.timedelta(
|
||||
days=4
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
due_users = get_users_with_due_delete_request()
|
||||
assert len(due_users) == 1
|
||||
|
||||
@ -40,6 +40,25 @@ END $$;
|
||||
conn.execute(sqlalchemy.text(sql).execution_options(autocommit=True))
|
||||
|
||||
|
||||
def migrate_with_result(app):
|
||||
from alembic import command
|
||||
|
||||
config = app.extensions["migrate"].migrate.get_config(
|
||||
None, opts=["autogenerate"], x_arg=None
|
||||
)
|
||||
return command.revision(
|
||||
config,
|
||||
None,
|
||||
autogenerate=True,
|
||||
sql=False,
|
||||
head="head",
|
||||
splice=False,
|
||||
branch_label=None,
|
||||
version_path=None,
|
||||
rev_id=None,
|
||||
)
|
||||
|
||||
|
||||
def test_migrations(app, seeder):
|
||||
from flask_migrate import downgrade, upgrade
|
||||
|
||||
@ -49,6 +68,8 @@ def test_migrations(app, seeder):
|
||||
with app.app_context():
|
||||
drop_db(db)
|
||||
upgrade()
|
||||
migrate_result = migrate_with_result(app)
|
||||
assert not migrate_result
|
||||
create_initial_data()
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
seeder.upsert_default_event_place(admin_unit_id)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from project.models import EventDateDefinition
|
||||
from tests.seeder import Seeder
|
||||
|
||||
|
||||
def test_location_update_coordinate(client, app, db):
|
||||
@ -150,7 +150,7 @@ def test_event_date_defintion_deletion(client, app, db, seeder):
|
||||
event_id = seeder.create_event(admin_unit_id)
|
||||
|
||||
with app.app_context():
|
||||
from project.models import Event
|
||||
from project.models import Event, EventDateDefinition
|
||||
|
||||
# Initial eine Definition
|
||||
event = db.session.get(Event, event_id)
|
||||
@ -568,3 +568,26 @@ def test_delete_admin_unit(client, app, db, seeder):
|
||||
|
||||
location = db.session.get(Location, location_id)
|
||||
assert admin_unit is location
|
||||
|
||||
|
||||
def test_delete_user(client, app, db, seeder: Seeder):
|
||||
user_id, admin_unit_id = seeder.setup_base(log_in=False)
|
||||
event_id = seeder.create_event(admin_unit_id, created_by_id=user_id)
|
||||
|
||||
with app.app_context():
|
||||
from project.models import AdminUnit, AdminUnitMember, Event, User
|
||||
from project.services.user import delete_user
|
||||
|
||||
user = db.session.get(User, user_id)
|
||||
member_id = user.adminunitmembers[0].id
|
||||
delete_user(user)
|
||||
|
||||
# User and membership should be gone
|
||||
assert db.session.get(User, user_id) is None
|
||||
assert db.session.get(AdminUnitMember, member_id) is None
|
||||
|
||||
# Admin unit and event should still be there
|
||||
assert db.session.get(AdminUnit, admin_unit_id) is not None
|
||||
event = db.session.get(Event, event_id)
|
||||
event is not None
|
||||
event.created_by_id is None
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
def test_register(client, app, utils):
|
||||
from project.services.user import find_user_by_email
|
||||
|
||||
|
||||
def test_register(client, app, utils):
|
||||
utils.register("test@test.de", "MeinPasswortIstDasBeste")
|
||||
|
||||
with app.app_context():
|
||||
|
||||
@ -2,11 +2,11 @@ import shutil
|
||||
|
||||
import pytest
|
||||
|
||||
from project import img_path
|
||||
|
||||
|
||||
@pytest.mark.parametrize("size", [None, 100])
|
||||
def test_read(app, seeder, utils, size):
|
||||
from project import img_path
|
||||
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
image_id = seeder.upsert_default_image()
|
||||
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import os
|
||||
|
||||
from project import dump_path
|
||||
|
||||
|
||||
def test_home(client, seeder, utils):
|
||||
url = utils.get_url("home")
|
||||
@ -69,6 +67,8 @@ def test_privacy(app, db, utils):
|
||||
|
||||
|
||||
def test_developer(client, seeder, utils):
|
||||
from project import dump_path
|
||||
|
||||
file_name = "all.zip"
|
||||
all_path = os.path.join(dump_path, file_name)
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from tests.seeder import Seeder
|
||||
|
||||
|
||||
def test_profile(client, seeder, utils):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
@ -110,3 +112,102 @@ def test_login_flash(client, seeder, utils):
|
||||
utils.assert_response_error_message(
|
||||
response, "Beachte, dass du deine E-Mail-Adresse bestätigen muss."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("db_error", [True, False])
|
||||
@pytest.mark.parametrize("non_match", [True, False])
|
||||
def test_user_request_deletion(
|
||||
client, seeder: Seeder, utils, app, db, mocker, db_error, non_match
|
||||
):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
|
||||
url = utils.get_url("user_request_deletion", id=user_id)
|
||||
response = utils.get_ok(url)
|
||||
|
||||
if db_error:
|
||||
utils.mock_db_commit(mocker)
|
||||
|
||||
form_email = "test@test.de"
|
||||
|
||||
if non_match:
|
||||
form_email = "wrong"
|
||||
|
||||
response = utils.post_form(
|
||||
url,
|
||||
response,
|
||||
{
|
||||
"email": form_email,
|
||||
},
|
||||
)
|
||||
|
||||
if non_match:
|
||||
utils.assert_response_error_message(
|
||||
response, "Die eingegebene Email entspricht nicht deiner Email"
|
||||
)
|
||||
return
|
||||
|
||||
if db_error:
|
||||
utils.assert_response_db_error(response)
|
||||
return
|
||||
|
||||
utils.assert_response_redirect(response, "profile")
|
||||
|
||||
with app.app_context():
|
||||
from project.models import User
|
||||
|
||||
user = db.session.get(User, user_id)
|
||||
assert user.deletion_requested_at is not None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("db_error", [True, False])
|
||||
@pytest.mark.parametrize("non_match", [True, False])
|
||||
def test_user_cancel_deletion(
|
||||
client, seeder, utils, app, db, mocker, db_error, non_match
|
||||
):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
|
||||
with app.app_context():
|
||||
import datetime
|
||||
|
||||
from project.models import User
|
||||
|
||||
user = db.session.get(User, user_id)
|
||||
user.deletion_requested_at = datetime.datetime.utcnow()
|
||||
db.session.commit()
|
||||
|
||||
url = utils.get_url("user_cancel_deletion", id=user_id)
|
||||
response = utils.get_ok(url)
|
||||
|
||||
if db_error:
|
||||
utils.mock_db_commit(mocker)
|
||||
|
||||
form_email = "test@test.de"
|
||||
|
||||
if non_match:
|
||||
form_email = "wrong"
|
||||
|
||||
response = utils.post_form(
|
||||
url,
|
||||
response,
|
||||
{
|
||||
"email": form_email,
|
||||
},
|
||||
)
|
||||
|
||||
if non_match:
|
||||
utils.assert_response_error_message(
|
||||
response, "Die eingegebene Email entspricht nicht deiner Email"
|
||||
)
|
||||
return
|
||||
|
||||
if db_error:
|
||||
utils.assert_response_db_error(response)
|
||||
return
|
||||
|
||||
utils.assert_response_redirect(response, "profile")
|
||||
|
||||
with app.app_context():
|
||||
from project.models import User
|
||||
|
||||
user = db.session.get(User, user_id)
|
||||
assert user.deletion_requested_at is None
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user