mirror of
https://github.com/lucaspalomodevelop/eventcally.git
synced 2026-03-13 00:07:22 +00:00
Merge pull request #308 from DanielGrams/issue/307
Organisationen einladen #307
This commit is contained in:
commit
8a26eb6ccc
@ -1,5 +1,5 @@
|
||||
describe("Admin Unit", () => {
|
||||
it("creates", () => {
|
||||
it.skip("creates", () => {
|
||||
cy.login();
|
||||
cy.visit("/admin_unit/create");
|
||||
cy.get("#name").type("Second Crew");
|
||||
@ -10,7 +10,30 @@ describe("Admin Unit", () => {
|
||||
cy.url().should("include", "/manage/admin_unit/");
|
||||
});
|
||||
|
||||
it("updates", () => {
|
||||
it("creates from invitation", () => {
|
||||
cy.login();
|
||||
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.createAdminUnitOrganizationInvitation(
|
||||
adminUnitId,
|
||||
"test@test.de"
|
||||
).then(function (invitationId) {
|
||||
cy.visit("admin_unit/create?invitation_id=" + invitationId);
|
||||
|
||||
cy.get("#name").should("have.value", "Invited Organization");
|
||||
cy.get("#short_name").should("have.value", "invitedorganization");
|
||||
cy.get("#short_name").should("have.class", "is-valid");
|
||||
|
||||
cy.get("#location-postalCode").type("38640");
|
||||
cy.get("#location-city").type("Goslar");
|
||||
cy.screenshot("create");
|
||||
cy.get("#submit").click();
|
||||
cy.url().should("include", "/manage/admin_unit/");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.skip("updates", () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.visit("/admin_unit/" + adminUnitId + "/update");
|
||||
@ -24,7 +47,7 @@ describe("Admin Unit", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("widgets", () => {
|
||||
it.skip("widgets", () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.visit("/manage/admin_unit/" + adminUnitId + "/widgets");
|
||||
|
||||
60
cypress/integration/admin_unit_organization_invitation.js
Normal file
60
cypress/integration/admin_unit_organization_invitation.js
Normal file
@ -0,0 +1,60 @@
|
||||
describe("Admin unit organization invitations", () => {
|
||||
it("list", () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.visit("/manage/admin_unit/" + adminUnitId + "/organization-invitations");
|
||||
cy.screenshot("list");
|
||||
});
|
||||
});
|
||||
|
||||
it("create", () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.visit("/manage/admin_unit/" + adminUnitId + "/organization-invitations");
|
||||
cy.visit("/manage/admin_unit/" + adminUnitId + "/organization-invitations/create");
|
||||
|
||||
cy.get('input[name=email]').type("invited@test.de");
|
||||
cy.get('input[name=organizationName]').type("Invited organization");
|
||||
cy.screenshot("create");
|
||||
cy.get("button[type=submit]").click();
|
||||
|
||||
cy.url().should(
|
||||
"not.include",
|
||||
"/create"
|
||||
);
|
||||
|
||||
cy.get('button:contains(invited@test.de)');
|
||||
cy.screenshot("list-filled");
|
||||
});
|
||||
});
|
||||
|
||||
it("updates", () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.createAdminUnitOrganizationInvitation(adminUnitId).then(function (invitationId) {
|
||||
cy.visit("/manage/admin_unit/" + adminUnitId + "/organization-invitations");
|
||||
cy.visit("/manage/admin_unit/" + adminUnitId + "/organization-invitations/" + invitationId + "/update");
|
||||
cy.screenshot("update");
|
||||
cy.get("button[type=submit]").click();
|
||||
cy.url().should(
|
||||
"not.include",
|
||||
"/update"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("deletes", () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.createAdminUnitOrganizationInvitation(adminUnitId).then(function (invitationId) {
|
||||
cy.visit("/manage/admin_unit/" + adminUnitId + "/organization-invitations");
|
||||
|
||||
cy.get('.dropdown-toggle.btn-link').click();
|
||||
cy.get('.b-dropdown.show li:last').click();
|
||||
|
||||
cy.get('.dropdown-toggle.btn-link').should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,5 +1,25 @@
|
||||
describe("Manage", () => {
|
||||
it("manage", () => {
|
||||
it("Organizations", () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.createAdminUnitOrganizationInvitation(
|
||||
adminUnitId,
|
||||
"test@test.de"
|
||||
).then(function (organizationInvitationId) {
|
||||
cy.createAdminUnitMemberInvitation(adminUnitId, "test@test.de").then(function (
|
||||
memberInvitationId
|
||||
) {
|
||||
cy.visit("/manage/admin_units");
|
||||
cy.get("h1:contains('Einladungen')");
|
||||
cy.get("h1:contains('Organisationseinladungen')");
|
||||
cy.get("h1:contains('Organisationen')");
|
||||
cy.screenshot("organizations");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.skip("Events", () => {
|
||||
cy.login();
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.createEvent(adminUnitId).then(function (eventId) {
|
||||
@ -8,7 +28,7 @@ describe("Manage", () => {
|
||||
"include",
|
||||
"/manage/admin_unit/" + adminUnitId + "/events"
|
||||
);
|
||||
cy.screenshot("events")
|
||||
cy.screenshot("events");
|
||||
|
||||
cy.get("#toggle-search-btn").click();
|
||||
cy.screenshot("search-form");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
describe("User", () => {
|
||||
it("registers user", () => {
|
||||
it("registers user", () => {
|
||||
cy.visit("/register");
|
||||
cy.screenshot("register");
|
||||
|
||||
@ -51,7 +51,7 @@ describe("User", () => {
|
||||
cy.get("div.alert").should("contain", "Bestätigungsanleitung");
|
||||
});
|
||||
|
||||
it("login", () => {
|
||||
it("login", () => {
|
||||
cy.visit("/login");
|
||||
cy.screenshot("login");
|
||||
|
||||
@ -85,4 +85,39 @@ describe("User", () => {
|
||||
cy.visit("/profile");
|
||||
cy.screenshot("profile");
|
||||
});
|
||||
|
||||
it("accepts organization invitation", () => {
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.createAdminUnitOrganizationInvitation(adminUnitId).then(function (invitationId) {
|
||||
cy.createUser("invited@test.de").then(function () {
|
||||
cy.login("invited@test.de", "password", "/user/organization-invitations/" + invitationId);
|
||||
|
||||
cy.get("button.btn-success");
|
||||
cy.get(".b-overlay").should('not.exist');
|
||||
cy.screenshot("choice-accept");
|
||||
|
||||
cy.get("button.btn-success").click();
|
||||
cy.url().should("include", "admin_unit/create?invitation_id=" + invitationId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("declines organization invitation", () => {
|
||||
cy.createAdminUnit().then(function (adminUnitId) {
|
||||
cy.createAdminUnitOrganizationInvitation(adminUnitId).then(function (invitationId) {
|
||||
cy.createUser("invited@test.de").then(function () {
|
||||
cy.login("invited@test.de", "password", "/user/organization-invitations/" + invitationId);
|
||||
|
||||
cy.get("button.btn-danger");
|
||||
cy.get(".b-overlay").should('not.exist');
|
||||
cy.screenshot("choice-decline");
|
||||
|
||||
cy.get("button.btn-danger").click();
|
||||
cy.url().should("include", "manage/admin_units");
|
||||
cy.get("h1:contains('Organisationseinladungen')").should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -141,6 +141,15 @@ Cypress.Commands.add("createAdminUnitRelation", (adminUnitId) => {
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("createAdminUnitOrganizationInvitation", (adminUnitId, email="invited@test.de") => {
|
||||
return cy
|
||||
.logexec("flask test admin-unit-organization-invitation-create " + adminUnitId + ' "' + email + '"')
|
||||
.then(function (result) {
|
||||
let json = JSON.parse(result.stdout);
|
||||
return json.invitation_id;
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("createSuggestion", (adminUnitId) => {
|
||||
return cy
|
||||
.logexec("flask test suggestion-create " + adminUnitId)
|
||||
@ -166,12 +175,12 @@ Cypress.Commands.add("assertRequired", (fieldId) => {
|
||||
|
||||
Cypress.Commands.add(
|
||||
"login",
|
||||
(email = "test@test.de", password = "password") => {
|
||||
(email = "test@test.de", password = "password", redirectUrl = "/manage") => {
|
||||
cy.visit("/login");
|
||||
cy.get("#email").type(email);
|
||||
cy.get("#password").type(password);
|
||||
cy.get("#submit").click();
|
||||
cy.url().should("include", "/manage");
|
||||
cy.url().should("include", redirectUrl);
|
||||
cy.getCookie("session").should("exist");
|
||||
}
|
||||
);
|
||||
|
||||
@ -1,8 +1,17 @@
|
||||
import "./commands";
|
||||
import failOnConsoleError from 'cypress-fail-on-console-error';
|
||||
import failOnConsoleError from "cypress-fail-on-console-error";
|
||||
|
||||
failOnConsoleError();
|
||||
|
||||
before(() => {
|
||||
if (Cypress.browser.family === "chromium") {
|
||||
Cypress.automation("remote:debugger:protocol", {
|
||||
command: "Network.setCacheDisabled",
|
||||
params: { cacheDisabled: true },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
135
messages.pot
135
messages.pot
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2021-09-29 23:02+0200\n"
|
||||
"POT-Creation-Date: 2021-10-14 10:41+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"
|
||||
@ -146,34 +146,42 @@ msgid "Scope_profile"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:45
|
||||
msgid "Scope_organizer:write"
|
||||
msgid "Scope_user:read"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:46
|
||||
msgid "Scope_place:write"
|
||||
msgid "Scope_user:write"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:47
|
||||
msgid "Scope_event:write"
|
||||
msgid "Scope_organizer:write"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:48
|
||||
msgid "Scope_organization:read"
|
||||
msgid "Scope_place:write"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:49
|
||||
msgid "Scope_organization:write"
|
||||
msgid "Scope_event:write"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:50
|
||||
msgid "Scope_organization:read"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:51
|
||||
msgid "Scope_organization:write"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:52
|
||||
msgid "There must be no self-reference."
|
||||
msgstr ""
|
||||
|
||||
#: project/utils.py:11
|
||||
#: project/utils.py:15
|
||||
msgid "Event_"
|
||||
msgstr ""
|
||||
|
||||
#: project/utils.py:15
|
||||
#: project/utils.py:19
|
||||
msgid "."
|
||||
msgstr ""
|
||||
|
||||
@ -181,24 +189,29 @@ msgstr ""
|
||||
msgid "message"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:10 project/templates/layout.html:305
|
||||
#: project/api/organization/resources.py:348
|
||||
#: project/views/admin_unit_member_invitation.py:92
|
||||
msgid "You have received an invitation"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:10 project/templates/layout.html:308
|
||||
#: project/views/root.py:42
|
||||
msgid "Terms of service"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:11 project/templates/layout.html:309
|
||||
#: project/forms/admin.py:11 project/templates/layout.html:312
|
||||
#: project/views/root.py:50
|
||||
msgid "Legal notice"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:12 project/templates/_macros.html:1409
|
||||
#: project/templates/layout.html:313
|
||||
#: project/templates/layout.html:316
|
||||
#: project/templates/widget/event_suggestion/create.html:204
|
||||
#: project/views/admin_unit.py:36 project/views/root.py:58
|
||||
#: project/views/admin_unit.py:50 project/views/root.py:58
|
||||
msgid "Contact"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:13 project/templates/layout.html:317
|
||||
#: project/forms/admin.py:13 project/templates/layout.html:320
|
||||
#: project/views/root.py:66
|
||||
msgid "Privacy"
|
||||
msgstr ""
|
||||
@ -208,7 +221,7 @@ msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:19 project/forms/admin_unit_member.py:12
|
||||
#: project/forms/admin_unit_member.py:32 project/templates/profile.html:66
|
||||
#: project/forms/admin_unit_member.py:32
|
||||
msgid "Roles"
|
||||
msgstr ""
|
||||
|
||||
@ -244,14 +257,22 @@ msgid "If set, members of the organization can create other organizations."
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:44
|
||||
msgid "Verify other organizations"
|
||||
msgid "Invite other organizations"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:45
|
||||
msgid "If set, members of the organization can invite other organizations."
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:51
|
||||
msgid "Verify other organizations"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:52
|
||||
msgid "If set, members of the organization can verify other organizations."
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:50 project/templates/admin/update_admin_unit.html:4
|
||||
#: project/forms/admin.py:57 project/templates/admin/update_admin_unit.html:4
|
||||
#: project/templates/admin/update_admin_unit.html:8
|
||||
msgid "Update organization"
|
||||
msgstr ""
|
||||
@ -295,7 +316,6 @@ msgstr ""
|
||||
#: project/templates/admin/admin_units.html:19
|
||||
#: project/templates/event_place/list.html:19
|
||||
#: project/templates/oauth2_client/list.html:25
|
||||
#: project/templates/profile.html:45 project/templates/profile.html:65
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
@ -342,7 +362,7 @@ msgstr ""
|
||||
|
||||
#: project/forms/admin_unit.py:63 project/templates/admin_unit/create.html:5
|
||||
#: project/templates/admin_unit/create.html:22
|
||||
#: project/templates/manage/admin_units.html:18
|
||||
#: project/templates/manage/admin_units.html:27
|
||||
msgid "Create organization"
|
||||
msgstr ""
|
||||
|
||||
@ -876,7 +896,7 @@ msgid "Distance"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/event_date.py:32 project/forms/planing.py:36
|
||||
#: project/templates/widget/event_date/list.html:60
|
||||
#: project/templates/widget/event_date/list.html:61
|
||||
msgid "Find"
|
||||
msgstr ""
|
||||
|
||||
@ -1324,8 +1344,7 @@ msgstr ""
|
||||
#: project/templates/admin/admin_units.html:11
|
||||
#: project/templates/layout.html:186
|
||||
#: project/templates/manage/admin_units.html:3
|
||||
#: project/templates/manage/admin_units.html:16
|
||||
#: project/templates/profile.html:60
|
||||
#: project/templates/manage/admin_units.html:25
|
||||
msgid "Organizations"
|
||||
msgstr ""
|
||||
|
||||
@ -1409,27 +1428,34 @@ msgstr ""
|
||||
msgid "Relations"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/layout.html:267
|
||||
#: project/templates/manage/admin_units.html:17
|
||||
#: project/templates/manage/organization_invitations.html:4
|
||||
#: project/templates/user/organization_invitations.html:4
|
||||
msgid "Organization invitations"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/admin/admin.html:15
|
||||
#: project/templates/admin/settings.html:4
|
||||
#: project/templates/admin/settings.html:8
|
||||
#: project/templates/admin_unit/update.html:6
|
||||
#: project/templates/admin_unit/update.html:23
|
||||
#: project/templates/layout.html:266 project/templates/manage/widgets.html:12
|
||||
#: project/templates/layout.html:269 project/templates/manage/widgets.html:12
|
||||
#: project/templates/profile.html:19
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/layout.html:267 project/templates/manage/reviews.html:10
|
||||
#: project/templates/layout.html:270 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:277
|
||||
#: project/templates/layout.html:280
|
||||
msgid "Switch organization"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/developer/read.html:4 project/templates/layout.html:327
|
||||
#: project/templates/developer/read.html:4 project/templates/layout.html:330
|
||||
#: project/templates/profile.html:29
|
||||
msgid "Developer"
|
||||
msgstr ""
|
||||
@ -1445,11 +1471,6 @@ msgstr ""
|
||||
msgid "OAuth2 clients"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/manage/admin_units.html:8
|
||||
#: project/templates/manage/members.html:9 project/templates/profile.html:40
|
||||
msgid "Invitations"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/admin/admin.html:23 project/templates/admin/users.html:4
|
||||
#: project/templates/admin/users.html:11
|
||||
msgid "Users"
|
||||
@ -1497,6 +1518,7 @@ msgid "You have been invited to join %(admin_unit_name)s."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/invitation_notice.html:5
|
||||
#: project/templates/email/organization_invitation_notice.html:5
|
||||
msgid "Click here to view the invitation"
|
||||
msgstr ""
|
||||
|
||||
@ -1508,6 +1530,22 @@ msgstr ""
|
||||
msgid "this is a message from Oveda - Die offene Veranstaltungsdatenbank."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_invitation_accepted_notice.html:4
|
||||
#, python-format
|
||||
msgid ""
|
||||
"%(email)s accepted your invitation to create the organisation "
|
||||
"%(admin_unit_name)s."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_invitation_accepted_notice.html:6
|
||||
msgid "Click here to view the relation"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_invitation_notice.html:4
|
||||
#, python-format
|
||||
msgid "%(admin_unit_name)s invited you to create an organization."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/reference_auto_verified_notice.html:4
|
||||
msgid "There is a new referenced event that was automatically verified."
|
||||
msgstr ""
|
||||
@ -1675,6 +1713,15 @@ msgstr ""
|
||||
msgid "Would you like to accept the invitation from %(name)s?"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/manage/admin_units.html:8
|
||||
#: project/templates/manage/members.html:9
|
||||
msgid "Invitations"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/manage/admin_units.html:29
|
||||
msgid "Invite organization"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/manage/delete_member.html:13
|
||||
msgid "Member"
|
||||
msgstr ""
|
||||
@ -1835,7 +1882,7 @@ msgstr ""
|
||||
msgid "Widget"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/widget/event_date/list.html:123
|
||||
#: project/templates/widget/event_date/list.html:124
|
||||
msgid "Print"
|
||||
msgstr ""
|
||||
|
||||
@ -1855,7 +1902,7 @@ msgstr ""
|
||||
msgid "Organization successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin.py:68 project/views/manage.py:274
|
||||
#: project/views/admin.py:68 project/views/manage.py:317
|
||||
msgid "Settings successfully updated"
|
||||
msgstr ""
|
||||
|
||||
@ -1863,20 +1910,24 @@ msgstr ""
|
||||
msgid "User successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:32
|
||||
#: project/views/admin_unit.py:46
|
||||
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:50
|
||||
#: project/views/admin_unit.py:79
|
||||
msgid "Organization successfully created"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:76
|
||||
#: project/views/admin_unit.py:105
|
||||
msgid "AdminUnit successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:127
|
||||
msgid "Organization invitation accepted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member.py:43
|
||||
msgid "Member successfully updated"
|
||||
msgstr ""
|
||||
@ -1889,27 +1940,23 @@ msgstr ""
|
||||
msgid "Member successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:44
|
||||
#: project/views/admin_unit_member_invitation.py:45
|
||||
msgid "Invitation successfully accepted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:51
|
||||
#: project/views/admin_unit_member_invitation.py:52
|
||||
msgid "Invitation successfully declined"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:91
|
||||
msgid "You have received an invitation"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:96
|
||||
#: project/views/admin_unit_member_invitation.py:97
|
||||
msgid "Invitation successfully sent"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:119
|
||||
#: project/views/admin_unit_member_invitation.py:120
|
||||
msgid "Entered email does not match invitation email"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:124
|
||||
#: project/views/admin_unit_member_invitation.py:125
|
||||
msgid "Invitation successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
|
||||
66
migrations/versions/920329927dc6_.py
Normal file
66
migrations/versions/920329927dc6_.py
Normal file
@ -0,0 +1,66 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 920329927dc6
|
||||
Revises: fca98f434287
|
||||
Create Date: 2021-10-03 22:32:34.070105
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
|
||||
from project import dbtypes
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "920329927dc6"
|
||||
down_revision = "fca98f434287"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
"adminunitinvitation",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("admin_unit_id", sa.Integer(), nullable=False),
|
||||
sa.Column("email", sa.String(length=255), nullable=False),
|
||||
sa.Column("admin_unit_name", sa.String(length=255), nullable=True),
|
||||
sa.Column(
|
||||
"relation_auto_verify_event_reference_requests",
|
||||
sa.Boolean(),
|
||||
server_default="0",
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("relation_verify", sa.Boolean(), server_default="0", nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("updated_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("created_by_id", sa.Integer(), nullable=True),
|
||||
sa.Column("updated_by_id", sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["admin_unit_id"],
|
||||
["adminunit.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["created_by_id"],
|
||||
["user.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["updated_by_id"],
|
||||
["user.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.add_column(
|
||||
"adminunit",
|
||||
sa.Column("can_invite_other", sa.Boolean(), server_default="0", nullable=False),
|
||||
)
|
||||
op.add_column(
|
||||
"adminunitrelation",
|
||||
sa.Column("invited", sa.Boolean(), server_default="0", nullable=False),
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column("adminunit", "can_invite_other")
|
||||
op.drop_column("adminunitrelation", "invited")
|
||||
op.drop_table("adminunitinvitation")
|
||||
@ -117,6 +117,8 @@ class RestApi(Api):
|
||||
scope_list = [
|
||||
"openid",
|
||||
"profile",
|
||||
"user:read",
|
||||
"user:write",
|
||||
"organizer:write",
|
||||
"place:write",
|
||||
"event:write",
|
||||
@ -191,6 +193,8 @@ import project.api.event_category.resources
|
||||
import project.api.event_date.resources
|
||||
import project.api.event_reference.resources
|
||||
import project.api.organization.resources
|
||||
import project.api.organization_invitation.resources
|
||||
import project.api.organization_relation.resources
|
||||
import project.api.organizer.resources
|
||||
import project.api.place.resources
|
||||
import project.api.user.resources
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
from flask_apispec import doc, marshal_with, use_kwargs
|
||||
from flask_babelex import gettext
|
||||
from sqlalchemy import and_
|
||||
|
||||
from project import db
|
||||
@ -30,6 +31,12 @@ from project.api.organization.schemas import (
|
||||
OrganizationListResponseSchema,
|
||||
OrganizationSchema,
|
||||
)
|
||||
from project.api.organization_invitation.schemas import (
|
||||
OrganizationInvitationCreateRequestSchema,
|
||||
OrganizationInvitationIdSchema,
|
||||
OrganizationInvitationListRequestSchema,
|
||||
OrganizationInvitationListResponseSchema,
|
||||
)
|
||||
from project.api.organization_relation.schemas import (
|
||||
OrganizationRelationCreateRequestSchema,
|
||||
OrganizationRelationIdSchema,
|
||||
@ -52,6 +59,7 @@ from project.api.resources import BaseResource, require_api_access
|
||||
from project.models import AdminUnit, Event, PublicStatus
|
||||
from project.oauth2 import require_oauth
|
||||
from project.services.admin_unit import (
|
||||
get_admin_unit_invitation_query,
|
||||
get_admin_unit_query,
|
||||
get_organizer_query,
|
||||
get_place_query,
|
||||
@ -63,6 +71,7 @@ from project.services.reference import (
|
||||
get_reference_outgoing_query,
|
||||
get_relation_outgoing_query,
|
||||
)
|
||||
from project.views.utils import send_mail
|
||||
|
||||
|
||||
class OrganizationResource(BaseResource):
|
||||
@ -298,6 +307,52 @@ class OrganizationOutgoingRelationListResource(BaseResource):
|
||||
return relation, 201
|
||||
|
||||
|
||||
class OrganizationOrganizationInvitationListResource(BaseResource):
|
||||
@doc(
|
||||
summary="List organization invitations of organization",
|
||||
tags=["Organizations", "Organization Invitations"],
|
||||
security=[{"oauth2": ["organization:read"]}],
|
||||
)
|
||||
@use_kwargs(OrganizationInvitationListRequestSchema, location=("query"))
|
||||
@marshal_with(OrganizationInvitationListResponseSchema)
|
||||
@require_api_access("organization:read")
|
||||
def get(self, id, **kwargs):
|
||||
login_api_user_or_401()
|
||||
admin_unit = get_admin_unit_for_manage_or_404(id)
|
||||
access_or_401(admin_unit, "admin_unit:update")
|
||||
|
||||
pagination = get_admin_unit_invitation_query(admin_unit).paginate()
|
||||
return pagination
|
||||
|
||||
@doc(
|
||||
summary="Add new organization invitation",
|
||||
tags=["Organizations", "Organization Invitations"],
|
||||
security=[{"oauth2": ["organization:write"]}],
|
||||
)
|
||||
@use_kwargs(OrganizationInvitationCreateRequestSchema, location="json", apply=False)
|
||||
@marshal_with(OrganizationInvitationIdSchema, 201)
|
||||
@require_api_access("organization:write")
|
||||
def post(self, id):
|
||||
login_api_user_or_401()
|
||||
admin_unit = get_admin_unit_for_manage_or_404(id)
|
||||
access_or_401(admin_unit, "admin_unit:update")
|
||||
|
||||
invitation = self.create_instance(
|
||||
OrganizationInvitationCreateRequestSchema, admin_unit_id=admin_unit.id
|
||||
)
|
||||
db.session.add(invitation)
|
||||
db.session.commit()
|
||||
|
||||
send_mail(
|
||||
invitation.email,
|
||||
gettext("You have received an invitation"),
|
||||
"organization_invitation_notice",
|
||||
invitation=invitation,
|
||||
)
|
||||
|
||||
return invitation, 201
|
||||
|
||||
|
||||
add_api_resource(OrganizationResource, "/organizations/<int:id>", "api_v1_organization")
|
||||
add_api_resource(
|
||||
OrganizationEventDateSearchResource,
|
||||
@ -340,3 +395,8 @@ add_api_resource(
|
||||
"/organizations/<int:id>/relations/outgoing",
|
||||
"api_v1_organization_outgoing_relation_list",
|
||||
)
|
||||
add_api_resource(
|
||||
OrganizationOrganizationInvitationListResource,
|
||||
"/organizations/<int:id>/organization-invitations",
|
||||
"api_v1_organization_organization_invitation_list",
|
||||
)
|
||||
|
||||
@ -43,12 +43,14 @@ class OrganizationSchema(OrganizationBaseSchema):
|
||||
location = fields.Nested(LocationSchema)
|
||||
logo = fields.Nested(ImageSchema)
|
||||
can_verify_other = marshmallow.auto_field()
|
||||
incoming_reference_requests_allowed = marshmallow.auto_field()
|
||||
|
||||
@post_dump(pass_original=True)
|
||||
def remove_private_fields(self, data, original_data, **kwargs):
|
||||
login_api_user()
|
||||
if not has_access(original_data, "admin_unit:update"):
|
||||
data.pop("can_verify_other", None)
|
||||
data.pop("incoming_reference_requests_allowed", None)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
0
project/api/organization_invitation/__init__.py
Normal file
0
project/api/organization_invitation/__init__.py
Normal file
94
project/api/organization_invitation/resources.py
Normal file
94
project/api/organization_invitation/resources.py
Normal file
@ -0,0 +1,94 @@
|
||||
from flask.helpers import make_response
|
||||
from flask_apispec import doc, marshal_with
|
||||
from flask_apispec.annotations import use_kwargs
|
||||
|
||||
from project import db
|
||||
from project.access import access_or_401, login_api_user_or_401
|
||||
from project.api import add_api_resource
|
||||
from project.api.organization_invitation.schemas import (
|
||||
OrganizationInvitationPatchRequestSchema,
|
||||
OrganizationInvitationSchema,
|
||||
OrganizationInvitationUpdateRequestSchema,
|
||||
)
|
||||
from project.api.resources import BaseResource, require_api_access
|
||||
from project.models import AdminUnitInvitation
|
||||
|
||||
|
||||
class OrganizationInvitationResource(BaseResource):
|
||||
@doc(
|
||||
summary="Get organization invitation",
|
||||
tags=["Organization Invitations"],
|
||||
security=[{"oauth2": ["organization:read"]}],
|
||||
)
|
||||
@marshal_with(OrganizationInvitationSchema)
|
||||
@require_api_access("organization:read")
|
||||
def get(self, id):
|
||||
login_api_user_or_401()
|
||||
invitation = AdminUnitInvitation.query.get_or_404(id)
|
||||
access_or_401(invitation.adminunit, "admin_unit:update")
|
||||
|
||||
return invitation
|
||||
|
||||
@doc(
|
||||
summary="Update organization invitation",
|
||||
tags=["Organization Invitations"],
|
||||
security=[{"oauth2": ["organization:write"]}],
|
||||
)
|
||||
@use_kwargs(OrganizationInvitationUpdateRequestSchema, location="json", apply=False)
|
||||
@marshal_with(None, 204)
|
||||
@require_api_access("organization:write")
|
||||
def put(self, id):
|
||||
login_api_user_or_401()
|
||||
invitation = AdminUnitInvitation.query.get_or_404(id)
|
||||
access_or_401(invitation.adminunit, "admin_unit:update")
|
||||
|
||||
invitation = self.update_instance(
|
||||
OrganizationInvitationUpdateRequestSchema, instance=invitation
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
return make_response("", 204)
|
||||
|
||||
@doc(
|
||||
summary="Patch organization invitation",
|
||||
tags=["Organization Invitations"],
|
||||
security=[{"oauth2": ["organization:write"]}],
|
||||
)
|
||||
@use_kwargs(OrganizationInvitationPatchRequestSchema, location="json", apply=False)
|
||||
@marshal_with(None, 204)
|
||||
@require_api_access("organization:write")
|
||||
def patch(self, id):
|
||||
login_api_user_or_401()
|
||||
invitation = AdminUnitInvitation.query.get_or_404(id)
|
||||
access_or_401(invitation.adminunit, "admin_unit:update")
|
||||
|
||||
invitation = self.update_instance(
|
||||
OrganizationInvitationPatchRequestSchema, instance=invitation
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
return make_response("", 204)
|
||||
|
||||
@doc(
|
||||
summary="Delete organization invitation",
|
||||
tags=["Organization Invitations"],
|
||||
security=[{"oauth2": ["organization:write"]}],
|
||||
)
|
||||
@marshal_with(None, 204)
|
||||
@require_api_access("organization:write")
|
||||
def delete(self, id):
|
||||
login_api_user_or_401()
|
||||
invitation = AdminUnitInvitation.query.get_or_404(id)
|
||||
access_or_401(invitation.adminunit, "admin_unit:update")
|
||||
|
||||
db.session.delete(invitation)
|
||||
db.session.commit()
|
||||
|
||||
return make_response("", 204)
|
||||
|
||||
|
||||
add_api_resource(
|
||||
OrganizationInvitationResource,
|
||||
"/organization-invitation/<int:id>",
|
||||
"api_v1_organization_invitation",
|
||||
)
|
||||
83
project/api/organization_invitation/schemas.py
Normal file
83
project/api/organization_invitation/schemas.py
Normal file
@ -0,0 +1,83 @@
|
||||
from marshmallow import fields
|
||||
|
||||
from project.api import marshmallow
|
||||
from project.api.organization.schemas import OrganizationRefSchema
|
||||
from project.api.schemas import (
|
||||
IdSchemaMixin,
|
||||
PaginationRequestSchema,
|
||||
PaginationResponseSchema,
|
||||
SQLAlchemyBaseSchema,
|
||||
TrackableSchemaMixin,
|
||||
)
|
||||
from project.models import AdminUnitInvitation
|
||||
|
||||
|
||||
class OrganizationInvitationModelSchema(SQLAlchemyBaseSchema):
|
||||
class Meta:
|
||||
model = AdminUnitInvitation
|
||||
load_instance = True
|
||||
|
||||
|
||||
class OrganizationInvitationIdSchema(OrganizationInvitationModelSchema, IdSchemaMixin):
|
||||
pass
|
||||
|
||||
|
||||
class OrganizationInvitationBaseSchemaMixin(TrackableSchemaMixin):
|
||||
organization_name = fields.Str(attribute="admin_unit_name")
|
||||
relation_auto_verify_event_reference_requests = marshmallow.auto_field()
|
||||
relation_verify = marshmallow.auto_field()
|
||||
|
||||
|
||||
class OrganizationInvitationSchema(
|
||||
OrganizationInvitationIdSchema, OrganizationInvitationBaseSchemaMixin
|
||||
):
|
||||
email = marshmallow.auto_field()
|
||||
organization = fields.Nested(OrganizationRefSchema, attribute="adminunit")
|
||||
|
||||
|
||||
class OrganizationInvitationRefSchema(OrganizationInvitationIdSchema):
|
||||
organization_name = fields.Str(attribute="admin_unit_name")
|
||||
email = marshmallow.auto_field()
|
||||
|
||||
|
||||
class OrganizationInvitationListRequestSchema(PaginationRequestSchema):
|
||||
pass
|
||||
|
||||
|
||||
class OrganizationInvitationListResponseSchema(PaginationResponseSchema):
|
||||
items = fields.List(
|
||||
fields.Nested(OrganizationInvitationRefSchema),
|
||||
metadata={"description": "Organization invitations"},
|
||||
)
|
||||
|
||||
|
||||
class OrganizationInvitationWriteSchemaMixin(object):
|
||||
email = marshmallow.auto_field()
|
||||
|
||||
|
||||
class OrganizationInvitationCreateRequestSchema(
|
||||
OrganizationInvitationModelSchema,
|
||||
OrganizationInvitationBaseSchemaMixin,
|
||||
OrganizationInvitationWriteSchemaMixin,
|
||||
):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.make_post_schema()
|
||||
|
||||
|
||||
class OrganizationInvitationUpdateRequestSchema(
|
||||
OrganizationInvitationModelSchema,
|
||||
OrganizationInvitationBaseSchemaMixin,
|
||||
):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.make_post_schema()
|
||||
|
||||
|
||||
class OrganizationInvitationPatchRequestSchema(
|
||||
OrganizationInvitationModelSchema,
|
||||
OrganizationInvitationBaseSchemaMixin,
|
||||
):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.make_patch_schema()
|
||||
87
project/api/user/resources.py
Normal file
87
project/api/user/resources.py
Normal file
@ -0,0 +1,87 @@
|
||||
from flask import abort
|
||||
from flask.helpers import make_response
|
||||
from flask_apispec import doc, marshal_with, use_kwargs
|
||||
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.organization_invitation.schemas import (
|
||||
OrganizationInvitationListRequestSchema,
|
||||
OrganizationInvitationListResponseSchema,
|
||||
OrganizationInvitationSchema,
|
||||
)
|
||||
from project.api.resources import BaseResource, require_api_access
|
||||
from project.models import AdminUnitInvitation
|
||||
from project.services.admin_unit import get_admin_unit_organization_invitations_query
|
||||
from project.utils import strings_are_equal_ignoring_case
|
||||
|
||||
|
||||
def invitation_receiver_or_401(invitation: AdminUnitInvitation):
|
||||
if not strings_are_equal_ignoring_case(invitation.email, current_user.email):
|
||||
abort(401)
|
||||
|
||||
|
||||
class UserOrganizationInvitationListResource(BaseResource):
|
||||
@doc(
|
||||
summary="List organization invitations of user",
|
||||
tags=["Users", "Organization Invitations"],
|
||||
security=[{"oauth2": ["user:read"]}],
|
||||
)
|
||||
@use_kwargs(OrganizationInvitationListRequestSchema, location=("query"))
|
||||
@marshal_with(OrganizationInvitationListResponseSchema)
|
||||
@require_api_access("user:read")
|
||||
def get(self, **kwargs):
|
||||
login_api_user_or_401()
|
||||
|
||||
pagination = get_admin_unit_organization_invitations_query(
|
||||
current_user.email
|
||||
).paginate()
|
||||
|
||||
return pagination
|
||||
|
||||
|
||||
class UserOrganizationInvitationResource(BaseResource):
|
||||
@doc(
|
||||
summary="Get organization invitation of user",
|
||||
tags=["Users", "Organization Invitations"],
|
||||
security=[{"oauth2": ["user:read"]}],
|
||||
)
|
||||
@marshal_with(OrganizationInvitationSchema)
|
||||
@require_api_access("user:read")
|
||||
def get(self, id):
|
||||
login_api_user_or_401()
|
||||
invitation = AdminUnitInvitation.query.get_or_404(id)
|
||||
invitation_receiver_or_401(invitation)
|
||||
|
||||
return invitation
|
||||
|
||||
@doc(
|
||||
summary="Delete organization invitation of user",
|
||||
tags=["Users", "Organization Invitations"],
|
||||
security=[{"oauth2": ["user:write"]}],
|
||||
)
|
||||
@marshal_with(None, 204)
|
||||
@require_api_access("user:write")
|
||||
def delete(self, id):
|
||||
login_api_user_or_401()
|
||||
invitation = AdminUnitInvitation.query.get_or_404(id)
|
||||
invitation_receiver_or_401(invitation)
|
||||
|
||||
db.session.delete(invitation)
|
||||
db.session.commit()
|
||||
|
||||
return make_response("", 204)
|
||||
|
||||
|
||||
add_api_resource(
|
||||
UserOrganizationInvitationListResource,
|
||||
"/user/organization-invitations",
|
||||
"api_v1_user_organization_invitation_list",
|
||||
)
|
||||
|
||||
add_api_resource(
|
||||
UserOrganizationInvitationResource,
|
||||
"/user/organization-invitation/<int:id>",
|
||||
"api_v1_user_organization_invitation",
|
||||
)
|
||||
@ -11,6 +11,7 @@ from project.api import scope_list
|
||||
from project.init_data import create_initial_data
|
||||
from project.models import (
|
||||
AdminUnit,
|
||||
AdminUnitInvitation,
|
||||
Event,
|
||||
EventAttendanceMode,
|
||||
EventReference,
|
||||
@ -137,6 +138,7 @@ def _create_admin_unit(user_id, name, verified=False):
|
||||
admin_unit.suggestions_enabled = True
|
||||
admin_unit.can_create_other = True
|
||||
admin_unit.can_verify_other = True
|
||||
admin_unit.can_invite_other = True
|
||||
admin_unit.location = Location()
|
||||
admin_unit.location.postalCode = "38640"
|
||||
admin_unit.location.city = "Goslar"
|
||||
@ -376,6 +378,31 @@ def create_admin_unit_relation(admin_unit_id):
|
||||
click.echo(json.dumps(result))
|
||||
|
||||
|
||||
def _create_admin_unit_invitation(
|
||||
admin_unit_id,
|
||||
email="invited@test.de",
|
||||
admin_unit_name="Invited Organization",
|
||||
):
|
||||
invitation = AdminUnitInvitation()
|
||||
invitation.admin_unit_id = admin_unit_id
|
||||
invitation.email = email
|
||||
invitation.admin_unit_name = admin_unit_name
|
||||
db.session.add(invitation)
|
||||
db.session.commit()
|
||||
return invitation.id
|
||||
|
||||
|
||||
@test_cli.command("admin-unit-organization-invitation-create")
|
||||
@click.argument("admin_unit_id")
|
||||
@click.argument("email")
|
||||
def create_admin_unit_organization_invitation(admin_unit_id, email):
|
||||
invitation_id = _create_admin_unit_invitation(admin_unit_id, email)
|
||||
result = {
|
||||
"invitation_id": invitation_id,
|
||||
}
|
||||
click.echo(json.dumps(result))
|
||||
|
||||
|
||||
def _create_event_suggestion(admin_unit_id, free_text=False):
|
||||
suggestion = EventSuggestion()
|
||||
suggestion.admin_unit_id = admin_unit_id
|
||||
|
||||
@ -40,6 +40,13 @@ class UpdateAdminUnitForm(FlaskForm):
|
||||
),
|
||||
validators=[Optional()],
|
||||
)
|
||||
can_invite_other = BooleanField(
|
||||
lazy_gettext("Invite other organizations"),
|
||||
description=lazy_gettext(
|
||||
"If set, members of the organization can invite other organizations."
|
||||
),
|
||||
validators=[Optional()],
|
||||
)
|
||||
can_verify_other = BooleanField(
|
||||
lazy_gettext("Verify other organizations"),
|
||||
description=lazy_gettext(
|
||||
|
||||
@ -42,6 +42,8 @@ def print_dynamic_texts():
|
||||
gettext("EventReviewStatus.rejected")
|
||||
gettext("Scope_openid")
|
||||
gettext("Scope_profile")
|
||||
gettext("Scope_user:read")
|
||||
gettext("Scope_user:write")
|
||||
gettext("Scope_organizer:write")
|
||||
gettext("Scope_place:write")
|
||||
gettext("Scope_event:write")
|
||||
|
||||
@ -296,6 +296,26 @@ class AdminUnitMemberInvitation(db.Model):
|
||||
roles = Column(UnicodeText())
|
||||
|
||||
|
||||
class AdminUnitInvitation(db.Model, TrackableMixin):
|
||||
__tablename__ = "adminunitinvitation"
|
||||
id = Column(Integer(), primary_key=True)
|
||||
admin_unit_id = db.Column(db.Integer, db.ForeignKey("adminunit.id"), nullable=False)
|
||||
email = Column(String(255), nullable=False)
|
||||
admin_unit_name = Column(String(255))
|
||||
relation_auto_verify_event_reference_requests = Column(
|
||||
Boolean(),
|
||||
nullable=False,
|
||||
default=False,
|
||||
server_default="0",
|
||||
)
|
||||
relation_verify = Column(
|
||||
Boolean(),
|
||||
nullable=False,
|
||||
default=False,
|
||||
server_default="0",
|
||||
)
|
||||
|
||||
|
||||
class AdminUnitRelation(db.Model, TrackableMixin):
|
||||
__tablename__ = "adminunitrelation"
|
||||
__table_args__ = (
|
||||
@ -325,6 +345,14 @@ class AdminUnitRelation(db.Model, TrackableMixin):
|
||||
server_default="0",
|
||||
)
|
||||
)
|
||||
invited = deferred(
|
||||
Column(
|
||||
Boolean(),
|
||||
nullable=False,
|
||||
default=False,
|
||||
server_default="0",
|
||||
)
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
source_id = (
|
||||
@ -362,6 +390,11 @@ class AdminUnit(db.Model, TrackableMixin):
|
||||
cascade="all, delete-orphan",
|
||||
backref=backref("adminunit", lazy=True),
|
||||
)
|
||||
admin_unit_invitations = relationship(
|
||||
"AdminUnitInvitation",
|
||||
cascade="all, delete-orphan",
|
||||
backref=backref("adminunit", lazy=True),
|
||||
)
|
||||
events = relationship(
|
||||
"Event", cascade="all, delete-orphan", backref=backref("admin_unit", lazy=True)
|
||||
)
|
||||
@ -431,6 +464,14 @@ class AdminUnit(db.Model, TrackableMixin):
|
||||
server_default="0",
|
||||
)
|
||||
)
|
||||
can_invite_other = deferred(
|
||||
Column(
|
||||
Boolean(),
|
||||
nullable=False,
|
||||
default=False,
|
||||
server_default="0",
|
||||
)
|
||||
)
|
||||
outgoing_relations = relationship(
|
||||
"AdminUnitRelation",
|
||||
primaryjoin=remote(AdminUnitRelation.source_admin_unit_id) == id,
|
||||
@ -496,6 +537,16 @@ def before_saving_admin_unit(mapper, connect, self):
|
||||
self.purge()
|
||||
|
||||
|
||||
@listens_for(AdminUnit.can_invite_other, "set")
|
||||
def set_admin_unit_can_invite_other(target, value, oldvalue, initiator):
|
||||
if (
|
||||
not value
|
||||
and target.admin_unit_invitations
|
||||
and len(target.admin_unit_invitations) > 0
|
||||
):
|
||||
target.admin_unit_invitations = []
|
||||
|
||||
|
||||
# Universal Types
|
||||
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ from sqlalchemy import and_, func, or_
|
||||
from project import db
|
||||
from project.models import (
|
||||
AdminUnit,
|
||||
AdminUnitInvitation,
|
||||
AdminUnitMember,
|
||||
AdminUnitMemberInvitation,
|
||||
AdminUnitMemberRole,
|
||||
@ -13,9 +14,10 @@ from project.models import (
|
||||
)
|
||||
from project.services.image import upsert_image_with_data
|
||||
from project.services.location import assign_location_values
|
||||
from project.utils import strings_are_equal_ignoring_case
|
||||
|
||||
|
||||
def insert_admin_unit_for_user(admin_unit, user):
|
||||
def insert_admin_unit_for_user(admin_unit, user, invitation=None):
|
||||
db.session.add(admin_unit)
|
||||
|
||||
# Nutzer als Admin hinzufügen
|
||||
@ -41,6 +43,7 @@ def insert_admin_unit_for_user(admin_unit, user):
|
||||
db.session.add(organizer)
|
||||
|
||||
# Place anlegen
|
||||
place = None
|
||||
if admin_unit.location:
|
||||
place = EventPlace()
|
||||
place.admin_unit_id = admin_unit.id
|
||||
@ -49,8 +52,31 @@ def insert_admin_unit_for_user(admin_unit, user):
|
||||
assign_location_values(place.location, admin_unit.location)
|
||||
db.session.add(place)
|
||||
|
||||
# Beziehung anlegen
|
||||
relation = None
|
||||
if invitation:
|
||||
inviting_admin_unit = get_admin_unit_by_id(invitation.admin_unit_id)
|
||||
relation = upsert_admin_unit_relation(invitation.admin_unit_id, admin_unit.id)
|
||||
relation.invited = True
|
||||
|
||||
name_equals_suggested_name = strings_are_equal_ignoring_case(
|
||||
admin_unit.name, invitation.admin_unit_name
|
||||
)
|
||||
relation.auto_verify_event_reference_requests = (
|
||||
inviting_admin_unit.incoming_reference_requests_allowed
|
||||
and invitation.relation_auto_verify_event_reference_requests
|
||||
and name_equals_suggested_name
|
||||
)
|
||||
relation.verify = (
|
||||
inviting_admin_unit.can_verify_other
|
||||
and invitation.relation_verify
|
||||
and name_equals_suggested_name
|
||||
)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return (organizer, place, relation)
|
||||
|
||||
|
||||
def get_admin_unit_by_id(id):
|
||||
return AdminUnit.query.filter_by(id=id).first()
|
||||
@ -204,3 +230,19 @@ def upsert_admin_unit_relation(source_admin_unit_id: int, target_admin_unit_id:
|
||||
result = insert_admin_unit_relation(source_admin_unit_id, target_admin_unit_id)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_admin_unit_invitation_query(admin_unit):
|
||||
return AdminUnitInvitation.query.filter(
|
||||
AdminUnitInvitation.admin_unit_id == admin_unit.id
|
||||
)
|
||||
|
||||
|
||||
def get_admin_unit_organization_invitations_query(email):
|
||||
return AdminUnitInvitation.query.filter(
|
||||
func.lower(AdminUnitInvitation.email) == func.lower(email)
|
||||
)
|
||||
|
||||
|
||||
def get_admin_unit_organization_invitations(email):
|
||||
return get_admin_unit_organization_invitations_query(email).all()
|
||||
|
||||
55
project/static/vue/common/validated-switch.vue.js
Normal file
55
project/static/vue/common/validated-switch.vue.js
Normal file
@ -0,0 +1,55 @@
|
||||
const ValidatedSwitch = {
|
||||
template: `
|
||||
<div>
|
||||
<ValidationProvider :vid="vid" :name="$attrs.label" :rules="rules" v-slot="validationContext">
|
||||
<b-form-group v-bind="$attrs" label="">
|
||||
<b-form-checkbox switch
|
||||
v-model="innerValue"
|
||||
v-bind="$attrs"
|
||||
:state="getValidationState(validationContext)"
|
||||
>
|
||||
{{ $attrs.label }}
|
||||
</b-form-checkbox>
|
||||
<b-form-invalid-feedback :state="getValidationState(validationContext)">
|
||||
{{ validationContext.errors[0] }}
|
||||
</b-form-invalid-feedback>
|
||||
</b-form-group>
|
||||
</ValidationProvider>
|
||||
</div>
|
||||
`,
|
||||
props: {
|
||||
vid: {
|
||||
type: String
|
||||
},
|
||||
rules: {
|
||||
type: [Object, String],
|
||||
default: ""
|
||||
},
|
||||
value: {
|
||||
type: null
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
innerValue: ""
|
||||
}),
|
||||
watch: {
|
||||
// Handles internal model changes.
|
||||
innerValue(newVal) {
|
||||
this.$emit("input", newVal);
|
||||
},
|
||||
// Handles external model changes.
|
||||
value(newVal) {
|
||||
this.innerValue = newVal;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.value) {
|
||||
this.innerValue = this.value;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getValidationState({ dirty, validated, valid = null }) {
|
||||
return (this.rules != "" && (dirty || validated)) ? valid : null;
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,127 @@
|
||||
const OrganizationOrganizationInvitationCreate = {
|
||||
template: `
|
||||
<div>
|
||||
<h1>{{ $t("comp.title") }}</h1>
|
||||
<b-overlay :show="isLoadingAdminUnit">
|
||||
<div v-if="adminUnit">
|
||||
<ValidationObserver v-slot="{ handleSubmit }">
|
||||
<b-form @submit.stop.prevent="handleSubmit(submitForm)">
|
||||
<validated-input
|
||||
:label="$t('shared.models.adminUnitInvitation.email')"
|
||||
:description="$t('shared.models.adminUnitInvitation.emailDescription')"
|
||||
name="email"
|
||||
v-model="form.email"
|
||||
rules="required|email" />
|
||||
<validated-input
|
||||
:label="$t('shared.models.adminUnitInvitation.organizationName')"
|
||||
name="organizationName"
|
||||
v-model="form.organization_name"
|
||||
rules="required" />
|
||||
<validated-switch
|
||||
v-if="adminUnit.can_verify_other"
|
||||
:label="$t('shared.models.adminUnitInvitation.relationVerify')"
|
||||
:description="$t('shared.models.adminUnitInvitation.relationVerifyDescription')"
|
||||
name="relationVerify"
|
||||
v-model="form.relation_verify" />
|
||||
<validated-switch
|
||||
v-if="adminUnit.incoming_reference_requests_allowed"
|
||||
:label="$t('shared.models.adminUnitInvitation.relationAutoVerifyEventReferenceRequests')"
|
||||
:description="$t('shared.models.adminUnitInvitation.relationAutoVerifyEventReferenceRequestsDescription')"
|
||||
name="relationAutoVerifyEventReferenceRequests"
|
||||
v-model="form.relation_auto_verify_event_reference_requests" />
|
||||
<b-button variant="secondary" @click="goBack" v-bind:disabled="isSubmitting">{{ $t("shared.cancel") }}</b-button>
|
||||
<b-button variant="primary" type="submit" v-bind:disabled="isSubmitting">
|
||||
<b-spinner small v-if="isSubmitting"></b-spinner>
|
||||
{{ $t("shared.submit") }}
|
||||
</b-button>
|
||||
</b-form>
|
||||
</ValidationObserver>
|
||||
</div>
|
||||
</b-overlay>
|
||||
</div>
|
||||
`,
|
||||
i18n: {
|
||||
messages: {
|
||||
en: {
|
||||
comp: {
|
||||
title: "Add invitation",
|
||||
successMessage: "Invitation successfully created",
|
||||
},
|
||||
},
|
||||
de: {
|
||||
comp: {
|
||||
title: "Einladung hinzufügen",
|
||||
successMessage: "Einladung erfolgreich erstellt",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
isLoadingAdminUnit: false,
|
||||
isSubmitting: false,
|
||||
adminUnit: null,
|
||||
form: {
|
||||
email: null,
|
||||
organization_name: null,
|
||||
relation_auto_verify_event_reference_requests: false,
|
||||
relation_verify: true,
|
||||
},
|
||||
}),
|
||||
computed: {
|
||||
adminUnitId() {
|
||||
return this.$route.params.admin_unit_id
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isLoadingAdminUnit = false;
|
||||
this.adminUnit = null;
|
||||
this.form = {
|
||||
email: null,
|
||||
organization_name: null,
|
||||
relation_auto_verify_event_reference_requests: false,
|
||||
relation_verify: true,
|
||||
}
|
||||
this.loadAdminUnit();
|
||||
},
|
||||
methods: {
|
||||
loadAdminUnit() {
|
||||
axios
|
||||
.get(`/api/v1/organizations/${this.adminUnitId}`, {
|
||||
withCredentials: true,
|
||||
handleLoading: this.handleLoadingAdminUnit,
|
||||
})
|
||||
.then((response) => {
|
||||
this.adminUnit = response.data;
|
||||
});
|
||||
},
|
||||
handleLoadingAdminUnit(isLoading) {
|
||||
this.isLoadingAdminUnit = isLoading;
|
||||
},
|
||||
submitForm() {
|
||||
let data = {
|
||||
'email': this.form.email,
|
||||
'organization_name': this.form.organization_name,
|
||||
'relation_auto_verify_event_reference_requests': this.form.relation_auto_verify_event_reference_requests,
|
||||
'relation_verify': this.form.relation_verify,
|
||||
};
|
||||
|
||||
axios
|
||||
.post(`/api/v1/organizations/${this.adminUnitId}/organization-invitations`,
|
||||
data,
|
||||
{
|
||||
withCredentials: true,
|
||||
handleLoading: this.handleSubmitting,
|
||||
})
|
||||
.then(() => {
|
||||
this.$root.makeSuccessToast(this.$t("comp.successMessage"))
|
||||
this.goBack()
|
||||
})
|
||||
},
|
||||
handleSubmitting(isLoading) {
|
||||
this.isSubmitting = isLoading;
|
||||
},
|
||||
goBack() {
|
||||
this.$root.goBack(`/manage/admin_unit/${this.adminUnitId}/organization-invitations`)
|
||||
},
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,137 @@
|
||||
const OrganizationOrganizationInvitationList = {
|
||||
template: `
|
||||
<div>
|
||||
<h1>{{ $t("comp.title") }}</h1>
|
||||
|
||||
<div class="my-4">
|
||||
<b-button variant="outline-secondary" :to="{ path: 'create'}" append><i class="fa fa-plus"></i> {{ $t("comp.addTitle") }}</b-button>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger" role="alert" v-if="errorMessage">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
|
||||
<b-table
|
||||
ref="table"
|
||||
id="main-table"
|
||||
:fields="fields"
|
||||
:items="loadTableData"
|
||||
:current-page="currentPage"
|
||||
:per-page="perPage"
|
||||
primary-key="id"
|
||||
thead-class="d-none"
|
||||
outlined
|
||||
hover
|
||||
responsive
|
||||
show-empty
|
||||
:empty-text="$t('shared.emptyData')"
|
||||
style="min-height:100px"
|
||||
>
|
||||
<template #cell(email)="data">
|
||||
<b-dropdown :id="'item-dropdown-' + data.item.id" :text="data.value" variant="link" toggle-class="m-0 p-0">
|
||||
<b-dropdown-item @click.prevent="editItem(data.item.id)">{{ $t("shared.edit") }}…</b-dropdown-item>
|
||||
<b-dropdown-item @click.prevent="deleteItem(data.item.id)">{{ $t("shared.delete") }}…</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
</template>
|
||||
</b-table>
|
||||
<b-pagination v-if="totalRows > 0"
|
||||
v-model="currentPage"
|
||||
:total-rows="totalRows"
|
||||
:per-page="perPage"
|
||||
aria-controls="main-table"
|
||||
></b-pagination>
|
||||
</div>
|
||||
`,
|
||||
i18n: {
|
||||
messages: {
|
||||
en: {
|
||||
comp: {
|
||||
title: "Invitations",
|
||||
addTitle: "Add invitation",
|
||||
deletedMessage: "Invitation successfully deleted",
|
||||
deleteConfirmation: "Do you really want to delete the invitation?",
|
||||
},
|
||||
},
|
||||
de: {
|
||||
comp: {
|
||||
title: "Einladungen",
|
||||
addTitle: "Einladung hinzufügen",
|
||||
deletedMessage: "Einladung erfolgreich gelöscht",
|
||||
deleteConfirmation: "Möchtest du die Einladung wirklich löschen?",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
errorMessage: null,
|
||||
fields: [
|
||||
{
|
||||
key: "email",
|
||||
label: i18n.t("shared.models.adminUnitInvitation.email"),
|
||||
},
|
||||
{
|
||||
key: "organization_name",
|
||||
label: i18n.t("shared.models.adminUnitInvitation.organizationName"),
|
||||
},
|
||||
],
|
||||
totalRows: 0,
|
||||
currentPage: 1,
|
||||
perPage: 10,
|
||||
searchResult: {
|
||||
items: [],
|
||||
},
|
||||
}),
|
||||
computed: {
|
||||
adminUnitId() {
|
||||
return this.$route.params.admin_unit_id;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
loadTableData(ctx, callback) {
|
||||
const vm = this;
|
||||
axios
|
||||
.get(`/api/v1/organizations/${this.adminUnitId}/organization-invitations`, {
|
||||
params: {
|
||||
page: ctx.currentPage,
|
||||
per_page: ctx.perPage,
|
||||
},
|
||||
withCredentials: true,
|
||||
handler: this,
|
||||
})
|
||||
.then((response) => {
|
||||
vm.totalRows = response.data.total;
|
||||
callback(response.data.items);
|
||||
})
|
||||
.catch(() => {
|
||||
callback([]);
|
||||
});
|
||||
return null;
|
||||
},
|
||||
refreshTableData() {
|
||||
this.$refs.table.refresh();
|
||||
},
|
||||
handleRequestStart() {
|
||||
this.errorMessage = null;
|
||||
},
|
||||
handleRequestError(error, message) {
|
||||
this.errorMessage = message;
|
||||
},
|
||||
editItem(id) {
|
||||
this.$router.push({
|
||||
path: `/manage/admin_unit/${this.adminUnitId}/organization-invitations/${id}/update`,
|
||||
});
|
||||
},
|
||||
deleteItem(id) {
|
||||
if (confirm(this.$t("comp.deleteConfirmation"))) {
|
||||
axios
|
||||
.delete(`/api/v1/organization-invitation/${id}`, {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then(() => {
|
||||
this.$root.makeSuccessToast(this.$t("comp.deletedMessage"));
|
||||
this.refreshTableData();
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,142 @@
|
||||
const OrganizationOrganizationInvitationUpdate = {
|
||||
template: `
|
||||
<div>
|
||||
<h1>{{ $t("comp.title") }}</h1>
|
||||
<b-overlay :show="isLoading || isLoadingAdminUnit">
|
||||
<div v-if="adminUnit && invitation">
|
||||
<h2 v-if="invitation">{{ invitation.email }}</h2>
|
||||
<ValidationObserver v-slot="{ handleSubmit }">
|
||||
<b-form @submit.stop.prevent="handleSubmit(submitForm)">
|
||||
<validated-input
|
||||
:label="$t('shared.models.adminUnitInvitation.organizationName')"
|
||||
name="organizationName"
|
||||
v-model="form.organization_name"
|
||||
rules="required" />
|
||||
<validated-switch
|
||||
v-if="adminUnit.can_verify_other"
|
||||
:label="$t('shared.models.adminUnitInvitation.relationVerify')"
|
||||
:description="$t('shared.models.adminUnitInvitation.relationVerifyDescription')"
|
||||
name="relationVerify"
|
||||
v-model="form.relation_verify" />
|
||||
<validated-switch
|
||||
v-if="adminUnit.incoming_reference_requests_allowed"
|
||||
:label="$t('shared.models.adminUnitInvitation.relationAutoVerifyEventReferenceRequests')"
|
||||
:description="$t('shared.models.adminUnitInvitation.relationAutoVerifyEventReferenceRequestsDescription')"
|
||||
name="relationAutoVerifyEventReferenceRequests"
|
||||
v-model="form.relation_auto_verify_event_reference_requests" />
|
||||
<b-button variant="secondary" @click="goBack" v-bind:disabled="isSubmitting">{{ $t("shared.cancel") }}</b-button>
|
||||
<b-button variant="primary" type="submit" v-bind:disabled="isSubmitting">
|
||||
<b-spinner small v-if="isSubmitting"></b-spinner>
|
||||
{{ $t("shared.submit") }}
|
||||
</b-button>
|
||||
</b-form>
|
||||
</ValidationObserver>
|
||||
</div>
|
||||
</b-overlay>
|
||||
</div>
|
||||
`,
|
||||
i18n: {
|
||||
messages: {
|
||||
en: {
|
||||
comp: {
|
||||
title: "Update invitation",
|
||||
successMessage: "Relation successfully updated",
|
||||
},
|
||||
},
|
||||
de: {
|
||||
comp: {
|
||||
title: "Einladung aktualisieren",
|
||||
successMessage: "Einladung erfolgreich aktualisiert",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
isLoading: false,
|
||||
isLoadingAdminUnit: false,
|
||||
isSubmitting: false,
|
||||
invitation: null,
|
||||
adminUnit: null,
|
||||
form: {
|
||||
organization_name: null,
|
||||
relation_auto_verify_event_reference_requests: false,
|
||||
relation_verify: true,
|
||||
},
|
||||
}),
|
||||
computed: {
|
||||
adminUnitId() {
|
||||
return this.$route.params.admin_unit_id;
|
||||
},
|
||||
invitationId() {
|
||||
return this.$route.params.organization_invitation_id;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isLoading = false;
|
||||
this.invitation = null;
|
||||
this.adminUnit = null;
|
||||
this.form = {
|
||||
organization_name: null,
|
||||
relation_auto_verify_event_reference_requests: false,
|
||||
relation_verify: true,
|
||||
};
|
||||
this.loadFormData();
|
||||
this.loadAdminUnit();
|
||||
},
|
||||
methods: {
|
||||
loadFormData() {
|
||||
axios
|
||||
.get(`/api/v1/organization-invitation/${this.invitationId}`, {
|
||||
withCredentials: true,
|
||||
handleLoading: this.handleLoading,
|
||||
})
|
||||
.then((response) => {
|
||||
this.invitation = response.data;
|
||||
this.form = {
|
||||
organization_name: response.data.organization_name,
|
||||
relation_auto_verify_event_reference_requests: response.data.relation_auto_verify_event_reference_requests,
|
||||
relation_verify: response.data.relation_verify,
|
||||
};
|
||||
});
|
||||
},
|
||||
handleLoading(isLoading) {
|
||||
this.isLoading = isLoading;
|
||||
},
|
||||
loadAdminUnit() {
|
||||
axios
|
||||
.get(`/api/v1/organizations/${this.adminUnitId}`, {
|
||||
withCredentials: true,
|
||||
handleLoading: this.handleLoadingAdminUnit,
|
||||
})
|
||||
.then((response) => {
|
||||
this.adminUnit = response.data;
|
||||
});
|
||||
},
|
||||
handleLoadingAdminUnit(isLoading) {
|
||||
this.isLoadingAdminUnit = isLoading;
|
||||
},
|
||||
submitForm() {
|
||||
let data = {
|
||||
'organization_name': this.form.organization_name,
|
||||
'relation_auto_verify_event_reference_requests': this.form.relation_auto_verify_event_reference_requests,
|
||||
'relation_verify': this.form.relation_verify,
|
||||
};
|
||||
|
||||
axios
|
||||
.put(`/api/v1/organization-invitation/${this.invitationId}`, data, {
|
||||
withCredentials: true,
|
||||
handleLoading: this.handleSubmitting,
|
||||
})
|
||||
.then(() => {
|
||||
this.$root.makeSuccessToast(this.$t("comp.successMessage"));
|
||||
this.goBack();
|
||||
});
|
||||
},
|
||||
handleSubmitting(isLoading) {
|
||||
this.isSubmitting = isLoading;
|
||||
},
|
||||
goBack() {
|
||||
this.$root.goBack(`/manage/admin_unit/${this.adminUnitId}/organization-invitations`);
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -13,16 +13,18 @@ const OrganizationRelationCreate = {
|
||||
labelKey="shared.models.adminUnitRelation.targetOrganization"
|
||||
:serializer="i => i.name"
|
||||
/>
|
||||
<b-form-group>
|
||||
<b-form-checkbox switch id="auto_verify_event_reference_requests" v-model="form.auto_verify_event_reference_requests">
|
||||
{{ $t("shared.models.adminUnitRelation.autoVerifyEventReferenceRequests") }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
<b-form-group v-if="adminUnit.can_verify_other">
|
||||
<b-form-checkbox switch id="verify" v-model="form.verify">
|
||||
{{ $t("shared.models.adminUnitRelation.verify") }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
<validated-switch
|
||||
v-if="adminUnit.can_verify_other"
|
||||
:label="$t('shared.models.adminUnitRelation.verify')"
|
||||
:description="$t('shared.models.adminUnitRelation.verifyDescription')"
|
||||
name="verify"
|
||||
v-model="form.verify" />
|
||||
<validated-switch
|
||||
v-if="adminUnit.incoming_reference_requests_allowed"
|
||||
:label="$t('shared.models.adminUnitRelation.autoVerifyEventReferenceRequests')"
|
||||
:description="$t('shared.models.adminUnitRelation.autoVerifyEventReferenceRequestsDescription')"
|
||||
name="auto_verify_event_reference_requests"
|
||||
v-model="form.auto_verify_event_reference_requests" />
|
||||
<b-button variant="secondary" @click="goBack" v-bind:disabled="isSubmitting">{{ $t("shared.cancel") }}</b-button>
|
||||
<b-button variant="primary" type="submit" v-bind:disabled="isSubmitting">
|
||||
<b-spinner small v-if="isSubmitting"></b-spinner>
|
||||
|
||||
@ -3,20 +3,22 @@ const OrganizationRelationUpdate = {
|
||||
<div>
|
||||
<h1>{{ $t("comp.title") }}</h1>
|
||||
<b-overlay :show="isLoading || isLoadingAdminUnit">
|
||||
<div v-if="adminUnit">
|
||||
<h2 v-if="relation">{{ relation.source_organization.name }}</h2>
|
||||
<div v-if="adminUnit && relation">
|
||||
<h2 v-if="relation">{{ relation.target_organization.name }}</h2>
|
||||
<ValidationObserver v-slot="{ handleSubmit }">
|
||||
<b-form @submit.stop.prevent="handleSubmit(submitForm)">
|
||||
<b-form-group>
|
||||
<b-form-checkbox switch id="auto_verify_event_reference_requests" v-model="form.auto_verify_event_reference_requests">
|
||||
{{ $t("shared.models.adminUnitRelation.autoVerifyEventReferenceRequests") }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
<b-form-group v-if="adminUnit.can_verify_other">
|
||||
<b-form-checkbox switch id="verify" v-model="form.verify">
|
||||
{{ $t("shared.models.adminUnitRelation.verify") }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
<validated-switch
|
||||
v-if="adminUnit.can_verify_other"
|
||||
:label="$t('shared.models.adminUnitRelation.verify')"
|
||||
:description="$t('shared.models.adminUnitRelation.verifyDescription')"
|
||||
name="verify"
|
||||
v-model="form.verify" />
|
||||
<validated-switch
|
||||
v-if="adminUnit.incoming_reference_requests_allowed"
|
||||
:label="$t('shared.models.adminUnitRelation.autoVerifyEventReferenceRequests')"
|
||||
:description="$t('shared.models.adminUnitRelation.autoVerifyEventReferenceRequestsDescription')"
|
||||
name="auto_verify_event_reference_requests"
|
||||
v-model="form.auto_verify_event_reference_requests" />
|
||||
<b-button variant="secondary" @click="goBack" v-bind:disabled="isSubmitting">{{ $t("shared.cancel") }}</b-button>
|
||||
<b-button variant="primary" type="submit" v-bind:disabled="isSubmitting">
|
||||
<b-spinner small v-if="isSubmitting"></b-spinner>
|
||||
|
||||
84
project/static/vue/user-organization-invitations/read.vue.js
Normal file
84
project/static/vue/user-organization-invitations/read.vue.js
Normal file
@ -0,0 +1,84 @@
|
||||
const UserOrganizationInvitationRead = {
|
||||
template: `
|
||||
<div>
|
||||
<h1>{{ $t("comp.title") }}</h1>
|
||||
<div class="mt-3 w-normal">
|
||||
<b-overlay :show="isLoading">
|
||||
<div v-if="invitation">
|
||||
<p>{{ $t("comp.instruction", { name: invitation.organization.name }) }}</p>
|
||||
<div class="d-flex justify-content-between my-4 decision-container">
|
||||
<b-button variant="success" class="m-1" @click="accept()"><i class="fa fa-check"></i> {{ $t("comp.accept") }}…</b-button>
|
||||
<b-button variant="danger" class="m-1" @click="decline()"><i class="fa fa-ban"></i> {{ $t("shared.decline") }}</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</b-overlay>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
i18n: {
|
||||
messages: {
|
||||
en: {
|
||||
comp: {
|
||||
title: "Invitation",
|
||||
instruction: "{name} invited you to create an organization.",
|
||||
accept: "Create organization",
|
||||
declinedMessage: "Invitation successfully declined",
|
||||
declineConfirmation: "Do you really want to decline the invitation?",
|
||||
},
|
||||
},
|
||||
de: {
|
||||
comp: {
|
||||
title: "Einladung",
|
||||
instruction: "{name} hat dich eingeladen, eine Organisation zu erstellen.",
|
||||
accept: "Organisation erstellen",
|
||||
declinedMessage: "Einladung erfolgreich abgelehnt",
|
||||
declineConfirmation: "Möchtest du die Einladung wirklich ablehnen?",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
isLoading: false,
|
||||
invitation: null,
|
||||
}),
|
||||
computed: {
|
||||
invitationId() {
|
||||
return this.$route.params.organization_invitation_id;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.isLoading = false;
|
||||
this.invitation = null;
|
||||
this.loadData();
|
||||
},
|
||||
methods: {
|
||||
loadData() {
|
||||
axios
|
||||
.get(`/api/v1/user/organization-invitation/${this.invitationId}`, {
|
||||
withCredentials: true,
|
||||
handleLoading: this.handleLoading,
|
||||
})
|
||||
.then((response) => {
|
||||
this.invitation = response.data;
|
||||
});
|
||||
},
|
||||
handleLoading(isLoading) {
|
||||
this.isLoading = isLoading;
|
||||
},
|
||||
accept() {
|
||||
window.location.href = `/admin_unit/create?invitation_id=${this.invitationId}`;
|
||||
},
|
||||
decline() {
|
||||
if (confirm(this.$t("comp.declineConfirmation"))) {
|
||||
axios
|
||||
.delete(`/api/v1/user/organization-invitation/${this.invitationId}`, {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then(() => {
|
||||
this.$root.makeSuccessToast(this.$t("comp.declinedMessage"));
|
||||
this.$root.goBack(`/manage/admin_units`);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -1570,9 +1570,8 @@ $('#allday').on('change', function() {
|
||||
}
|
||||
});
|
||||
|
||||
$("#name").blur(function() {
|
||||
function suggest_short_name() {
|
||||
if ($("#short_name").val().length == 0) {
|
||||
|
||||
var name = $("#name").val().toLowerCase().replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue').replace(/ß/g, 'ss');
|
||||
var re = /\w/g;
|
||||
var suggestion = (name.match(re) || []).join('');
|
||||
@ -1580,8 +1579,16 @@ $('#allday').on('change', function() {
|
||||
$("#short_name").val(suggestion);
|
||||
$("#short_name").valid();
|
||||
}
|
||||
}
|
||||
|
||||
$("#name").blur(function() {
|
||||
suggest_short_name();
|
||||
});
|
||||
|
||||
if ($("#name").val().length > 0) {
|
||||
suggest_short_name();
|
||||
}
|
||||
|
||||
$('#location_search').on('place_changed', function() {
|
||||
$('#location_search').closest('.card-body').find(':input').valid();
|
||||
$('#location_search').removeClass('is-valid');
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
{{ render_field_with_errors(form.incoming_reference_requests_allowed, ri="switch") }}
|
||||
{{ render_field_with_errors(form.suggestions_enabled, ri="switch") }}
|
||||
{{ render_field_with_errors(form.can_create_other, ri="switch") }}
|
||||
{{ render_field_with_errors(form.can_invite_other, ri="switch") }}
|
||||
{{ render_field_with_errors(form.can_verify_other, ri="switch") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ _('Create organization') }}</h1>
|
||||
<form id="main-form" action="{{ url_for('admin_unit_create') }}" method="POST" enctype="multipart/form-data">
|
||||
<form id="main-form" action="" method="POST" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="card mb-4">
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
{% extends "email/layout.html" %}
|
||||
{% from "_macros.html" import render_email_button %}
|
||||
{% block content %}
|
||||
<p>{{ _('%(email)s accepted your invitation to create the organisation %(admin_unit_name)s.', email=invitation.email, admin_unit_name=admin_unit.name) }}</p>
|
||||
{% set path = relation.id ~ '/update' %}
|
||||
{{ render_email_button(url_for('manage_admin_unit_relations', id=invitation.admin_unit_id, path=path, _external=True), _('Click here to view the relation')) }}
|
||||
{% endblock %}
|
||||
@ -0,0 +1,4 @@
|
||||
{{ _('%(email)s accepted your invitation to create the organisation %(admin_unit_name)s.', email=invitation.email, admin_unit_name=admin_unit.name) }}
|
||||
{{ _('Click the link below to view the relation') }}
|
||||
{% set path = relation.id ~ '/update' %}
|
||||
{{ url_for('manage_admin_unit_relations', id=invitation.admin_unit_id, path=path, _external=True) }}
|
||||
@ -0,0 +1,6 @@
|
||||
{% extends "email/layout.html" %}
|
||||
{% from "_macros.html" import render_email_button %}
|
||||
{% block content %}
|
||||
<p>{{ _('%(admin_unit_name)s invited you to create an organization.', admin_unit_name=invitation.adminunit.name) }}</p>
|
||||
{{ render_email_button(url_for('user_organization_invitation', id=invitation.id, _external=True), _('Click here to view the invitation')) }}
|
||||
{% endblock %}
|
||||
@ -0,0 +1,3 @@
|
||||
{{ _('%(admin_unit_name)s invited you to create an organization.', admin_unit_name=invitation.adminunit.name) }}
|
||||
{{ _('Click the link below to view the invitation') }}
|
||||
{{ url_for('user_organization_invitation', id=invitation.id, _external=True) }}
|
||||
@ -263,6 +263,9 @@
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="{{ url_for('manage_admin_unit_members', id=current_admin_unit.id) }}">{{ _('Members') }}</a>
|
||||
<a class="dropdown-item" href="{{ url_for('manage_admin_unit_relations', id=current_admin_unit.id) }}">{{ _('Relations') }}</a>
|
||||
{% if current_admin_unit.can_invite_other %}
|
||||
<a class="dropdown-item" href="{{ url_for('manage_admin_unit_organization_invitations', id=current_admin_unit.id) }}">{{ _('Organization invitations') }}</a>
|
||||
{% endif %}
|
||||
<a class="dropdown-item" href="{{ url_for('admin_unit_update', id=current_admin_unit.id) }}">{{ _('Settings') }}</a>
|
||||
<a class="dropdown-item" href="{{ url_for('manage_admin_unit_widgets', id=current_admin_unit.id) }}">{{ _('Widgets') }}</a>
|
||||
</div>
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
<script src="https://unpkg.com/vee-validate@3.4.11/dist/vee-validate.full.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='vue/common/typeahead.vue.js')}}"></script>
|
||||
<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>
|
||||
|
||||
{% block component_scripts %}
|
||||
@ -41,6 +42,7 @@
|
||||
|
||||
Vue.component("custom-typeahead", CustomTypeahead);
|
||||
Vue.component("validated-input", ValidatedInput);
|
||||
Vue.component("validated-switch", ValidatedSwitch);
|
||||
Vue.component("validated-textarea", ValidatedTextarea);
|
||||
|
||||
{% block component_definitions %}
|
||||
@ -53,7 +55,18 @@
|
||||
adminUnitRelation: {
|
||||
targetOrganization: "Other organization",
|
||||
autoVerifyEventReferenceRequests: "Verify reference requests automatically",
|
||||
autoVerifyEventReferenceRequestsDescription: "If set, all upcoming reference requests of the other organization are verified automatically.",
|
||||
verify: "Verify other organization",
|
||||
verifyDescription: "If set, events of the other organization are publicly visible.",
|
||||
},
|
||||
adminUnitInvitation: {
|
||||
email: "Email",
|
||||
emailDescription: "The invitation will be sent to this email address.",
|
||||
organizationName: "New organization's name",
|
||||
relationAutoVerifyEventReferenceRequests: "Verify reference requests automatically",
|
||||
relationAutoVerifyEventReferenceRequestsDescription: "If set, all upcoming reference requests of the new organization are verified automatically.",
|
||||
relationVerify: "Verify new organization",
|
||||
relationVerifyDescription: "If set, events of the new organization are publicly visible.",
|
||||
},
|
||||
eventReport: {
|
||||
contactName: "Name",
|
||||
@ -64,6 +77,7 @@
|
||||
},
|
||||
},
|
||||
cancel: "Cancel",
|
||||
decline: "Decline",
|
||||
submit: "Submit",
|
||||
edit: "Edit",
|
||||
delete: "Delete",
|
||||
@ -121,7 +135,18 @@
|
||||
targetOrganization: "Andere Organisation",
|
||||
autoVerifyEventReferenceRequests:
|
||||
"Empfehlungsanfragen automatisch verifizieren",
|
||||
autoVerifyEventReferenceRequestsDescription: "Wenn gesetzt, werden alle zukünftigen Empfehlungsanfragen der anderen Organisation automatisch verifiziert.",
|
||||
verify: "Andere Organisation verifizieren",
|
||||
verifyDescription: "Wenn gesetzt, sind Veranstaltungen der anderen Organisation öffentlich sichtbar.",
|
||||
},
|
||||
adminUnitInvitation: {
|
||||
email: "Email",
|
||||
emailDescription: "An diese Email-Adresse wird die Einladung gesendet.",
|
||||
organizationName: "Name der neuen Organisation",
|
||||
relationAutoVerifyEventReferenceRequests: "Empfehlungsanfragen automatisch verifizieren",
|
||||
relationAutoVerifyEventReferenceRequestsDescription: "Wenn gesetzt, werden alle zukünftigen Empfehlungsanfragen der neuen Organisation automatisch verifiziert.",
|
||||
relationVerify: "Neue Organisation verifizieren",
|
||||
relationVerifyDescription: "Wenn gesetzt, sind Veranstaltungen der neuen Organisation öffentlich sichtbar.",
|
||||
},
|
||||
eventReport: {
|
||||
contactName: "Name",
|
||||
@ -132,6 +157,7 @@
|
||||
},
|
||||
},
|
||||
cancel: "Abbrechen",
|
||||
decline: "Ablehnen",
|
||||
submit: "Senden",
|
||||
edit: "Bearbeiten",
|
||||
delete: "Löschen",
|
||||
|
||||
@ -6,16 +6,28 @@
|
||||
|
||||
{% if invitations %}
|
||||
<h1>{{ _('Invitations') }}</h1>
|
||||
<div class="list-group">
|
||||
<div class="list-group mb-4">
|
||||
{% for invitation in invitations %}
|
||||
<a href="{{ url_for('admin_unit_member_invitation', id=invitation.id) }}" class="list-group-item">{{ invitation.adminunit.name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if organization_invitations %}
|
||||
<h1>{{ _('Organization invitations') }}</h1>
|
||||
<div class="list-group mb-4">
|
||||
{% for invitation in organization_invitations %}
|
||||
<a href="{{ url_for('user_organization_invitation', id=invitation.id) }}" class="list-group-item">{{ invitation.admin_unit_name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h1>{{ _('Organizations') }}</h1>
|
||||
<div class="my-4">
|
||||
<a class="btn btn-outline-secondary my-1" href="{{ url_for('admin_unit_create') }}" role="button"><i class="fa fa-plus"></i> {{ _('Create organization') }}</a>
|
||||
<a class="btn btn-outline-secondary m-1" href="{{ url_for('admin_unit_create') }}" role="button"><i class="fa fa-plus"></i> {{ _('Create organization') }}</a>
|
||||
{% if current_admin_unit and current_admin_unit.can_invite_other %}
|
||||
<a class="btn btn-outline-secondary m-1" href="{{ url_for('manage_admin_unit_organization_invitations', id=current_admin_unit.id, path='create') }}" role="button"><i class="fa fa-plus"></i> {{ _('Invite organization') }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="list-group">
|
||||
|
||||
34
project/templates/manage/organization_invitations.html
Normal file
34
project/templates/manage/organization_invitations.html
Normal file
@ -0,0 +1,34 @@
|
||||
{% extends "layout_vue.html" %}
|
||||
|
||||
{%- block title -%}
|
||||
{{ _('Organization invitations') }}
|
||||
{%- endblock -%}
|
||||
|
||||
{% block component_scripts %}
|
||||
<script src="{{ url_for('static', filename='vue/organization-organization-invitations/list.vue.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='vue/organization-organization-invitations/create.vue.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='vue/organization-organization-invitations/update.vue.js')}}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block component_definitions %}
|
||||
Vue.component("OrganizationOrganizationInvitationList", OrganizationOrganizationInvitationList);
|
||||
Vue.component("OrganizationOrganizationInvitationCreate", OrganizationOrganizationInvitationCreate);
|
||||
Vue.component("OrganizationOrganizationInvitationUpdate", OrganizationOrganizationInvitationUpdate);
|
||||
{% endblock %}
|
||||
|
||||
{% block vue_routes %}
|
||||
const routes = [
|
||||
{
|
||||
path: "/manage/admin_unit/:admin_unit_id/organization-invitations",
|
||||
component: OrganizationOrganizationInvitationList,
|
||||
},
|
||||
{
|
||||
path: "/manage/admin_unit/:admin_unit_id/organization-invitations/create",
|
||||
component: OrganizationOrganizationInvitationCreate,
|
||||
},
|
||||
{
|
||||
path: "/manage/admin_unit/:admin_unit_id/organization-invitations/:organization_invitation_id/update",
|
||||
component: OrganizationOrganizationInvitationUpdate,
|
||||
},
|
||||
];
|
||||
{% endblock %}
|
||||
@ -36,46 +36,4 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if invitations %}
|
||||
<h2>{{ _('Invitations') }}</h2>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for invitation in invitations %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('admin_unit_member_invitation', id=invitation.id) }}">{{ invitation.adminunit.name }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if admin_unit_members %}
|
||||
<h2>{{ _('Organizations') }}</h2>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Roles') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in admin_unit_members %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('manage_admin_unit', id=member.adminunit.id) }}">{{ member.adminunit.name }}</a></td>
|
||||
<td>{{ render_roles(member.roles)}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
22
project/templates/user/organization_invitations.html
Normal file
22
project/templates/user/organization_invitations.html
Normal file
@ -0,0 +1,22 @@
|
||||
{% extends "layout_vue.html" %}
|
||||
|
||||
{%- block title -%}
|
||||
{{ _('Organization invitations') }}
|
||||
{%- endblock -%}
|
||||
|
||||
{% block component_scripts %}
|
||||
<script src="{{ url_for('static', filename='vue/user-organization-invitations/read.vue.js')}}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block component_definitions %}
|
||||
Vue.component("UserOrganizationInvitationRead", UserOrganizationInvitationRead);
|
||||
{% endblock %}
|
||||
|
||||
{% block vue_routes %}
|
||||
const routes = [
|
||||
{
|
||||
path: "/user/organization-invitations/:organization_invitation_id",
|
||||
component: UserOrganizationInvitationRead,
|
||||
},
|
||||
];
|
||||
{% 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: 2021-09-29 23:02+0200\n"
|
||||
"POT-Creation-Date: 2021-10-14 10:41+0200\n"
|
||||
"PO-Revision-Date: 2020-06-07 18:51+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: de\n"
|
||||
@ -147,34 +147,42 @@ msgid "Scope_profile"
|
||||
msgstr "Profil-Informationen"
|
||||
|
||||
#: project/i10n.py:45
|
||||
msgid "Scope_user:read"
|
||||
msgstr "Nutzer-Einstellungen lesen"
|
||||
|
||||
#: project/i10n.py:46
|
||||
msgid "Scope_user:write"
|
||||
msgstr "Nutzer-Einstellungen anlegen, ändern und löschen"
|
||||
|
||||
#: project/i10n.py:47
|
||||
msgid "Scope_organizer:write"
|
||||
msgstr "Veranstalter anlegen, ändern und löschen"
|
||||
|
||||
#: project/i10n.py:46
|
||||
#: project/i10n.py:48
|
||||
msgid "Scope_place:write"
|
||||
msgstr "Orte anlegen, ändern und löschen"
|
||||
|
||||
#: project/i10n.py:47
|
||||
#: project/i10n.py:49
|
||||
msgid "Scope_event:write"
|
||||
msgstr "Veranstaltungen anlegen, ändern und löschen"
|
||||
|
||||
#: project/i10n.py:48
|
||||
#: project/i10n.py:50
|
||||
msgid "Scope_organization:read"
|
||||
msgstr "Organisationen lesen"
|
||||
|
||||
#: project/i10n.py:49
|
||||
#: project/i10n.py:51
|
||||
msgid "Scope_organization:write"
|
||||
msgstr "Organisationen anlegen, ändern und löschen"
|
||||
|
||||
#: project/i10n.py:50
|
||||
#: project/i10n.py:52
|
||||
msgid "There must be no self-reference."
|
||||
msgstr "Es darf keine Selbstreferenz geben."
|
||||
|
||||
#: project/utils.py:11
|
||||
#: project/utils.py:15
|
||||
msgid "Event_"
|
||||
msgstr "Event_"
|
||||
|
||||
#: project/utils.py:15
|
||||
#: project/utils.py:19
|
||||
msgid "."
|
||||
msgstr "."
|
||||
|
||||
@ -182,24 +190,29 @@ msgstr "."
|
||||
msgid "message"
|
||||
msgstr "message"
|
||||
|
||||
#: project/forms/admin.py:10 project/templates/layout.html:305
|
||||
#: project/api/organization/resources.py:348
|
||||
#: project/views/admin_unit_member_invitation.py:92
|
||||
msgid "You have received an invitation"
|
||||
msgstr "Du hast eine Einladung erhalten"
|
||||
|
||||
#: project/forms/admin.py:10 project/templates/layout.html:308
|
||||
#: project/views/root.py:42
|
||||
msgid "Terms of service"
|
||||
msgstr "Nutzungsbedingungen"
|
||||
|
||||
#: project/forms/admin.py:11 project/templates/layout.html:309
|
||||
#: project/forms/admin.py:11 project/templates/layout.html:312
|
||||
#: project/views/root.py:50
|
||||
msgid "Legal notice"
|
||||
msgstr "Impressum"
|
||||
|
||||
#: project/forms/admin.py:12 project/templates/_macros.html:1409
|
||||
#: project/templates/layout.html:313
|
||||
#: project/templates/layout.html:316
|
||||
#: project/templates/widget/event_suggestion/create.html:204
|
||||
#: project/views/admin_unit.py:36 project/views/root.py:58
|
||||
#: project/views/admin_unit.py:50 project/views/root.py:58
|
||||
msgid "Contact"
|
||||
msgstr "Kontakt"
|
||||
|
||||
#: project/forms/admin.py:13 project/templates/layout.html:317
|
||||
#: project/forms/admin.py:13 project/templates/layout.html:320
|
||||
#: project/views/root.py:66
|
||||
msgid "Privacy"
|
||||
msgstr "Datenschutz"
|
||||
@ -209,7 +222,7 @@ msgid "Save"
|
||||
msgstr "Speichern"
|
||||
|
||||
#: project/forms/admin.py:19 project/forms/admin_unit_member.py:12
|
||||
#: project/forms/admin_unit_member.py:32 project/templates/profile.html:66
|
||||
#: project/forms/admin_unit_member.py:32
|
||||
msgid "Roles"
|
||||
msgstr "Rollen"
|
||||
|
||||
@ -249,16 +262,26 @@ msgstr ""
|
||||
"erstellen."
|
||||
|
||||
#: project/forms/admin.py:44
|
||||
msgid "Invite other organizations"
|
||||
msgstr "Andere Organisationen einladen"
|
||||
|
||||
#: project/forms/admin.py:45
|
||||
msgid "If set, members of the organization can invite other organizations."
|
||||
msgstr ""
|
||||
"Wenn gesetzt, können Mitglieder der Organisation andere Organisationen "
|
||||
"einladen."
|
||||
|
||||
#: project/forms/admin.py:51
|
||||
msgid "Verify other organizations"
|
||||
msgstr "Andere Organisationen verifizieren"
|
||||
|
||||
#: project/forms/admin.py:45
|
||||
#: project/forms/admin.py:52
|
||||
msgid "If set, members of the organization can verify other organizations."
|
||||
msgstr ""
|
||||
"Wenn gesetzt, können Mitglieder der Organisation andere Organisationen "
|
||||
"verifizieren."
|
||||
|
||||
#: project/forms/admin.py:50 project/templates/admin/update_admin_unit.html:4
|
||||
#: project/forms/admin.py:57 project/templates/admin/update_admin_unit.html:4
|
||||
#: project/templates/admin/update_admin_unit.html:8
|
||||
msgid "Update organization"
|
||||
msgstr "Organisation aktualisieren"
|
||||
@ -302,7 +325,6 @@ msgstr "Längengrad"
|
||||
#: project/templates/admin/admin_units.html:19
|
||||
#: project/templates/event_place/list.html:19
|
||||
#: project/templates/oauth2_client/list.html:25
|
||||
#: project/templates/profile.html:45 project/templates/profile.html:65
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
@ -352,7 +374,7 @@ msgstr "Logo"
|
||||
|
||||
#: project/forms/admin_unit.py:63 project/templates/admin_unit/create.html:5
|
||||
#: project/templates/admin_unit/create.html:22
|
||||
#: project/templates/manage/admin_units.html:18
|
||||
#: project/templates/manage/admin_units.html:27
|
||||
msgid "Create organization"
|
||||
msgstr "Organisation erstellen"
|
||||
|
||||
@ -912,7 +934,7 @@ msgid "Distance"
|
||||
msgstr "Distanz"
|
||||
|
||||
#: project/forms/event_date.py:32 project/forms/planing.py:36
|
||||
#: project/templates/widget/event_date/list.html:60
|
||||
#: project/templates/widget/event_date/list.html:61
|
||||
msgid "Find"
|
||||
msgstr "Finden"
|
||||
|
||||
@ -1366,8 +1388,7 @@ msgstr "Beispiel"
|
||||
#: project/templates/admin/admin_units.html:11
|
||||
#: project/templates/layout.html:186
|
||||
#: project/templates/manage/admin_units.html:3
|
||||
#: project/templates/manage/admin_units.html:16
|
||||
#: project/templates/profile.html:60
|
||||
#: project/templates/manage/admin_units.html:25
|
||||
msgid "Organizations"
|
||||
msgstr "Organisationen"
|
||||
|
||||
@ -1451,27 +1472,34 @@ msgstr "Mitglieder"
|
||||
msgid "Relations"
|
||||
msgstr "Beziehungen"
|
||||
|
||||
#: project/templates/layout.html:267
|
||||
#: project/templates/manage/admin_units.html:17
|
||||
#: project/templates/manage/organization_invitations.html:4
|
||||
#: project/templates/user/organization_invitations.html:4
|
||||
msgid "Organization invitations"
|
||||
msgstr "Organisationseinladungen"
|
||||
|
||||
#: project/templates/admin/admin.html:15
|
||||
#: project/templates/admin/settings.html:4
|
||||
#: project/templates/admin/settings.html:8
|
||||
#: project/templates/admin_unit/update.html:6
|
||||
#: project/templates/admin_unit/update.html:23
|
||||
#: project/templates/layout.html:266 project/templates/manage/widgets.html:12
|
||||
#: project/templates/layout.html:269 project/templates/manage/widgets.html:12
|
||||
#: project/templates/profile.html:19
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: project/templates/layout.html:267 project/templates/manage/reviews.html:10
|
||||
#: project/templates/layout.html:270 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:277
|
||||
#: project/templates/layout.html:280
|
||||
msgid "Switch organization"
|
||||
msgstr "Organisation wechseln"
|
||||
|
||||
#: project/templates/developer/read.html:4 project/templates/layout.html:327
|
||||
#: project/templates/developer/read.html:4 project/templates/layout.html:330
|
||||
#: project/templates/profile.html:29
|
||||
msgid "Developer"
|
||||
msgstr "Entwickler"
|
||||
@ -1487,11 +1515,6 @@ msgstr "Apps"
|
||||
msgid "OAuth2 clients"
|
||||
msgstr "OAuth2 Clients"
|
||||
|
||||
#: project/templates/manage/admin_units.html:8
|
||||
#: project/templates/manage/members.html:9 project/templates/profile.html:40
|
||||
msgid "Invitations"
|
||||
msgstr "Einladungen"
|
||||
|
||||
#: project/templates/admin/admin.html:23 project/templates/admin/users.html:4
|
||||
#: project/templates/admin/users.html:11
|
||||
msgid "Users"
|
||||
@ -1539,6 +1562,7 @@ 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_invitation_notice.html:5
|
||||
msgid "Click here to view the invitation"
|
||||
msgstr "Klicke hier, um die Einladung anzunehmen."
|
||||
|
||||
@ -1550,6 +1574,24 @@ msgstr "Moin"
|
||||
msgid "this is a message from Oveda - Die offene Veranstaltungsdatenbank."
|
||||
msgstr "das ist eine Nachricht von Oveda - Die offene Veranstaltungsdatenbank."
|
||||
|
||||
#: project/templates/email/organization_invitation_accepted_notice.html:4
|
||||
#, python-format
|
||||
msgid ""
|
||||
"%(email)s accepted your invitation to create the organisation "
|
||||
"%(admin_unit_name)s."
|
||||
msgstr ""
|
||||
"%(email)s hat deine Einladung akzeptiert, um die Organisation "
|
||||
"%(admin_unit_name)s zu erstellen."
|
||||
|
||||
#: project/templates/email/organization_invitation_accepted_notice.html:6
|
||||
msgid "Click here to view the relation"
|
||||
msgstr "Klicke hier, um die Beziehung anzuzeigen."
|
||||
|
||||
#: project/templates/email/organization_invitation_notice.html:4
|
||||
#, python-format
|
||||
msgid "%(admin_unit_name)s invited you to create an organization."
|
||||
msgstr "%(admin_unit_name)s hat dich eingeladen, eine Organisation zu erstellen."
|
||||
|
||||
#: project/templates/email/reference_auto_verified_notice.html:4
|
||||
msgid "There is a new referenced event that was automatically verified."
|
||||
msgstr "Es gibt eine neue Empfehlung, die automatisch verifiziert wurde."
|
||||
@ -1719,6 +1761,15 @@ msgstr "Einladung"
|
||||
msgid "Would you like to accept the invitation from %(name)s?"
|
||||
msgstr "Möchtest du die Einladung von %(name)s akzeptieren?"
|
||||
|
||||
#: project/templates/manage/admin_units.html:8
|
||||
#: project/templates/manage/members.html:9
|
||||
msgid "Invitations"
|
||||
msgstr "Einladungen"
|
||||
|
||||
#: project/templates/manage/admin_units.html:29
|
||||
msgid "Invite organization"
|
||||
msgstr "Organisation einladen"
|
||||
|
||||
#: project/templates/manage/delete_member.html:13
|
||||
msgid "Member"
|
||||
msgstr "Mitglied"
|
||||
@ -1881,7 +1932,7 @@ msgstr "Du hast noch keinen Account? Kein Problem!"
|
||||
msgid "Widget"
|
||||
msgstr "Widget"
|
||||
|
||||
#: project/templates/widget/event_date/list.html:123
|
||||
#: project/templates/widget/event_date/list.html:124
|
||||
msgid "Print"
|
||||
msgstr "Drucken"
|
||||
|
||||
@ -1901,7 +1952,7 @@ msgstr "Vorschau"
|
||||
msgid "Organization successfully updated"
|
||||
msgstr "Organisation erfolgreich aktualisiert"
|
||||
|
||||
#: project/views/admin.py:68 project/views/manage.py:274
|
||||
#: project/views/admin.py:68 project/views/manage.py:317
|
||||
msgid "Settings successfully updated"
|
||||
msgstr "Einstellungen erfolgreich aktualisiert"
|
||||
|
||||
@ -1909,7 +1960,7 @@ msgstr "Einstellungen erfolgreich aktualisiert"
|
||||
msgid "User successfully updated"
|
||||
msgstr "Nutzer erfolgreich aktualisiert"
|
||||
|
||||
#: project/views/admin_unit.py:32
|
||||
#: project/views/admin_unit.py:46
|
||||
msgid ""
|
||||
"Organizations cannot currently be created. The project is in a closed "
|
||||
"test phase. If you are interested, you can contact us."
|
||||
@ -1918,14 +1969,18 @@ msgstr ""
|
||||
" sich in einer geschlossenen Test-Phase. Bei Interesse kannst du uns "
|
||||
"kontaktieren."
|
||||
|
||||
#: project/views/admin_unit.py:50
|
||||
#: project/views/admin_unit.py:79
|
||||
msgid "Organization successfully created"
|
||||
msgstr "Organisation erfolgreich erstellt"
|
||||
|
||||
#: project/views/admin_unit.py:76
|
||||
#: project/views/admin_unit.py:105
|
||||
msgid "AdminUnit successfully updated"
|
||||
msgstr "Organisation erfolgreich aktualisiert"
|
||||
|
||||
#: project/views/admin_unit.py:127
|
||||
msgid "Organization invitation accepted"
|
||||
msgstr "Organisationseinladung akzeptiert"
|
||||
|
||||
#: project/views/admin_unit_member.py:43
|
||||
msgid "Member successfully updated"
|
||||
msgstr "Mitglied erfolgreich aktualisiert"
|
||||
@ -1938,27 +1993,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:44
|
||||
#: project/views/admin_unit_member_invitation.py:45
|
||||
msgid "Invitation successfully accepted"
|
||||
msgstr "Einladung erfolgreich akzeptiert"
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:51
|
||||
#: project/views/admin_unit_member_invitation.py:52
|
||||
msgid "Invitation successfully declined"
|
||||
msgstr "Einladung erfolgreich abgelehnt"
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:91
|
||||
msgid "You have received an invitation"
|
||||
msgstr "Du hast eine Einladung erhalten"
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:96
|
||||
#: project/views/admin_unit_member_invitation.py:97
|
||||
msgid "Invitation successfully sent"
|
||||
msgstr "Einladung erfolgreich gesendet"
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:119
|
||||
#: project/views/admin_unit_member_invitation.py:120
|
||||
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:124
|
||||
#: project/views/admin_unit_member_invitation.py:125
|
||||
msgid "Invitation successfully deleted"
|
||||
msgstr "Einladung erfolgreich gelöscht"
|
||||
|
||||
|
||||
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: 2021-09-29 23:02+0200\n"
|
||||
"POT-Creation-Date: 2021-10-14 10:41+0200\n"
|
||||
"PO-Revision-Date: 2021-04-30 15:04+0200\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: en\n"
|
||||
@ -147,34 +147,42 @@ msgid "Scope_profile"
|
||||
msgstr "Profile information"
|
||||
|
||||
#: project/i10n.py:45
|
||||
msgid "Scope_user:read"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:46
|
||||
msgid "Scope_user:write"
|
||||
msgstr ""
|
||||
|
||||
#: project/i10n.py:47
|
||||
msgid "Scope_organizer:write"
|
||||
msgstr "Create, update and delete organizers"
|
||||
|
||||
#: project/i10n.py:46
|
||||
#: project/i10n.py:48
|
||||
msgid "Scope_place:write"
|
||||
msgstr "Create, update and delete places"
|
||||
|
||||
#: project/i10n.py:47
|
||||
#: project/i10n.py:49
|
||||
msgid "Scope_event:write"
|
||||
msgstr "Create, update and delete events"
|
||||
|
||||
#: project/i10n.py:48
|
||||
#: project/i10n.py:50
|
||||
msgid "Scope_organization:read"
|
||||
msgstr "Read organizations"
|
||||
|
||||
#: project/i10n.py:49
|
||||
#: project/i10n.py:51
|
||||
msgid "Scope_organization:write"
|
||||
msgstr "Create, update and delete organizations"
|
||||
|
||||
#: project/i10n.py:50
|
||||
#: project/i10n.py:52
|
||||
msgid "There must be no self-reference."
|
||||
msgstr ""
|
||||
|
||||
#: project/utils.py:11
|
||||
#: project/utils.py:15
|
||||
msgid "Event_"
|
||||
msgstr ""
|
||||
|
||||
#: project/utils.py:15
|
||||
#: project/utils.py:19
|
||||
msgid "."
|
||||
msgstr ""
|
||||
|
||||
@ -182,24 +190,29 @@ msgstr ""
|
||||
msgid "message"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:10 project/templates/layout.html:305
|
||||
#: project/api/organization/resources.py:348
|
||||
#: project/views/admin_unit_member_invitation.py:92
|
||||
msgid "You have received an invitation"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:10 project/templates/layout.html:308
|
||||
#: project/views/root.py:42
|
||||
msgid "Terms of service"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:11 project/templates/layout.html:309
|
||||
#: project/forms/admin.py:11 project/templates/layout.html:312
|
||||
#: project/views/root.py:50
|
||||
msgid "Legal notice"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:12 project/templates/_macros.html:1409
|
||||
#: project/templates/layout.html:313
|
||||
#: project/templates/layout.html:316
|
||||
#: project/templates/widget/event_suggestion/create.html:204
|
||||
#: project/views/admin_unit.py:36 project/views/root.py:58
|
||||
#: project/views/admin_unit.py:50 project/views/root.py:58
|
||||
msgid "Contact"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:13 project/templates/layout.html:317
|
||||
#: project/forms/admin.py:13 project/templates/layout.html:320
|
||||
#: project/views/root.py:66
|
||||
msgid "Privacy"
|
||||
msgstr ""
|
||||
@ -209,7 +222,7 @@ msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:19 project/forms/admin_unit_member.py:12
|
||||
#: project/forms/admin_unit_member.py:32 project/templates/profile.html:66
|
||||
#: project/forms/admin_unit_member.py:32
|
||||
msgid "Roles"
|
||||
msgstr ""
|
||||
|
||||
@ -245,14 +258,22 @@ msgid "If set, members of the organization can create other organizations."
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:44
|
||||
msgid "Verify other organizations"
|
||||
msgid "Invite other organizations"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:45
|
||||
msgid "If set, members of the organization can invite other organizations."
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:51
|
||||
msgid "Verify other organizations"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:52
|
||||
msgid "If set, members of the organization can verify other organizations."
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/admin.py:50 project/templates/admin/update_admin_unit.html:4
|
||||
#: project/forms/admin.py:57 project/templates/admin/update_admin_unit.html:4
|
||||
#: project/templates/admin/update_admin_unit.html:8
|
||||
msgid "Update organization"
|
||||
msgstr ""
|
||||
@ -296,7 +317,6 @@ msgstr ""
|
||||
#: project/templates/admin/admin_units.html:19
|
||||
#: project/templates/event_place/list.html:19
|
||||
#: project/templates/oauth2_client/list.html:25
|
||||
#: project/templates/profile.html:45 project/templates/profile.html:65
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
@ -343,7 +363,7 @@ msgstr ""
|
||||
|
||||
#: project/forms/admin_unit.py:63 project/templates/admin_unit/create.html:5
|
||||
#: project/templates/admin_unit/create.html:22
|
||||
#: project/templates/manage/admin_units.html:18
|
||||
#: project/templates/manage/admin_units.html:27
|
||||
msgid "Create organization"
|
||||
msgstr ""
|
||||
|
||||
@ -877,7 +897,7 @@ msgid "Distance"
|
||||
msgstr ""
|
||||
|
||||
#: project/forms/event_date.py:32 project/forms/planing.py:36
|
||||
#: project/templates/widget/event_date/list.html:60
|
||||
#: project/templates/widget/event_date/list.html:61
|
||||
msgid "Find"
|
||||
msgstr ""
|
||||
|
||||
@ -1325,8 +1345,7 @@ msgstr ""
|
||||
#: project/templates/admin/admin_units.html:11
|
||||
#: project/templates/layout.html:186
|
||||
#: project/templates/manage/admin_units.html:3
|
||||
#: project/templates/manage/admin_units.html:16
|
||||
#: project/templates/profile.html:60
|
||||
#: project/templates/manage/admin_units.html:25
|
||||
msgid "Organizations"
|
||||
msgstr ""
|
||||
|
||||
@ -1410,27 +1429,34 @@ msgstr ""
|
||||
msgid "Relations"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/layout.html:267
|
||||
#: project/templates/manage/admin_units.html:17
|
||||
#: project/templates/manage/organization_invitations.html:4
|
||||
#: project/templates/user/organization_invitations.html:4
|
||||
msgid "Organization invitations"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/admin/admin.html:15
|
||||
#: project/templates/admin/settings.html:4
|
||||
#: project/templates/admin/settings.html:8
|
||||
#: project/templates/admin_unit/update.html:6
|
||||
#: project/templates/admin_unit/update.html:23
|
||||
#: project/templates/layout.html:266 project/templates/manage/widgets.html:12
|
||||
#: project/templates/layout.html:269 project/templates/manage/widgets.html:12
|
||||
#: project/templates/profile.html:19
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/layout.html:267 project/templates/manage/reviews.html:10
|
||||
#: project/templates/layout.html:270 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:277
|
||||
#: project/templates/layout.html:280
|
||||
msgid "Switch organization"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/developer/read.html:4 project/templates/layout.html:327
|
||||
#: project/templates/developer/read.html:4 project/templates/layout.html:330
|
||||
#: project/templates/profile.html:29
|
||||
msgid "Developer"
|
||||
msgstr ""
|
||||
@ -1446,11 +1472,6 @@ msgstr ""
|
||||
msgid "OAuth2 clients"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/manage/admin_units.html:8
|
||||
#: project/templates/manage/members.html:9 project/templates/profile.html:40
|
||||
msgid "Invitations"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/admin/admin.html:23 project/templates/admin/users.html:4
|
||||
#: project/templates/admin/users.html:11
|
||||
msgid "Users"
|
||||
@ -1498,6 +1519,7 @@ msgid "You have been invited to join %(admin_unit_name)s."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/invitation_notice.html:5
|
||||
#: project/templates/email/organization_invitation_notice.html:5
|
||||
msgid "Click here to view the invitation"
|
||||
msgstr ""
|
||||
|
||||
@ -1509,6 +1531,22 @@ msgstr ""
|
||||
msgid "this is a message from Oveda - Die offene Veranstaltungsdatenbank."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_invitation_accepted_notice.html:4
|
||||
#, python-format
|
||||
msgid ""
|
||||
"%(email)s accepted your invitation to create the organisation "
|
||||
"%(admin_unit_name)s."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_invitation_accepted_notice.html:6
|
||||
msgid "Click here to view the relation"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/organization_invitation_notice.html:4
|
||||
#, python-format
|
||||
msgid "%(admin_unit_name)s invited you to create an organization."
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/email/reference_auto_verified_notice.html:4
|
||||
msgid "There is a new referenced event that was automatically verified."
|
||||
msgstr ""
|
||||
@ -1676,6 +1714,15 @@ msgstr ""
|
||||
msgid "Would you like to accept the invitation from %(name)s?"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/manage/admin_units.html:8
|
||||
#: project/templates/manage/members.html:9
|
||||
msgid "Invitations"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/manage/admin_units.html:29
|
||||
msgid "Invite organization"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/manage/delete_member.html:13
|
||||
msgid "Member"
|
||||
msgstr ""
|
||||
@ -1836,7 +1883,7 @@ msgstr ""
|
||||
msgid "Widget"
|
||||
msgstr ""
|
||||
|
||||
#: project/templates/widget/event_date/list.html:123
|
||||
#: project/templates/widget/event_date/list.html:124
|
||||
msgid "Print"
|
||||
msgstr ""
|
||||
|
||||
@ -1856,7 +1903,7 @@ msgstr ""
|
||||
msgid "Organization successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin.py:68 project/views/manage.py:274
|
||||
#: project/views/admin.py:68 project/views/manage.py:317
|
||||
msgid "Settings successfully updated"
|
||||
msgstr ""
|
||||
|
||||
@ -1864,20 +1911,24 @@ msgstr ""
|
||||
msgid "User successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:32
|
||||
#: project/views/admin_unit.py:46
|
||||
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:50
|
||||
#: project/views/admin_unit.py:79
|
||||
msgid "Organization successfully created"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:76
|
||||
#: project/views/admin_unit.py:105
|
||||
msgid "AdminUnit successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit.py:127
|
||||
msgid "Organization invitation accepted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member.py:43
|
||||
msgid "Member successfully updated"
|
||||
msgstr ""
|
||||
@ -1890,27 +1941,23 @@ msgstr ""
|
||||
msgid "Member successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:44
|
||||
#: project/views/admin_unit_member_invitation.py:45
|
||||
msgid "Invitation successfully accepted"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:51
|
||||
#: project/views/admin_unit_member_invitation.py:52
|
||||
msgid "Invitation successfully declined"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:91
|
||||
msgid "You have received an invitation"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:96
|
||||
#: project/views/admin_unit_member_invitation.py:97
|
||||
msgid "Invitation successfully sent"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:119
|
||||
#: project/views/admin_unit_member_invitation.py:120
|
||||
msgid "Entered email does not match invitation email"
|
||||
msgstr ""
|
||||
|
||||
#: project/views/admin_unit_member_invitation.py:124
|
||||
#: project/views/admin_unit_member_invitation.py:125
|
||||
msgid "Invitation successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@ -7,6 +7,10 @@ from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm.base import NO_CHANGE, object_state
|
||||
|
||||
|
||||
def strings_are_equal_ignoring_case(string1: str, string2: str):
|
||||
return string1 and string2 and string1.casefold() == string2.casefold()
|
||||
|
||||
|
||||
def get_event_category_name(category):
|
||||
return lazy_gettext("Event_" + category.name)
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from flask import flash, redirect, render_template, url_for
|
||||
from flask import flash, redirect, render_template, request, url_for
|
||||
from flask_babelex import gettext
|
||||
from flask_security import auth_required, current_user
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
@ -7,16 +7,19 @@ from project import app, db
|
||||
from project.access import (
|
||||
can_create_admin_unit,
|
||||
get_admin_unit_for_manage_or_404,
|
||||
get_admin_unit_members_with_permission,
|
||||
has_access,
|
||||
)
|
||||
from project.forms.admin_unit import CreateAdminUnitForm, UpdateAdminUnitForm
|
||||
from project.models import AdminUnit, Location
|
||||
from project.models import AdminUnit, AdminUnitInvitation, AdminUnitRelation, Location
|
||||
from project.services.admin_unit import insert_admin_unit_for_user
|
||||
from project.utils import strings_are_equal_ignoring_case
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
flash_message,
|
||||
handleSqlError,
|
||||
permission_missing,
|
||||
send_mails,
|
||||
)
|
||||
|
||||
|
||||
@ -27,7 +30,18 @@ def update_admin_unit_with_form(admin_unit, form):
|
||||
@app.route("/admin_unit/create", methods=("GET", "POST"))
|
||||
@auth_required()
|
||||
def admin_unit_create():
|
||||
if not can_create_admin_unit():
|
||||
invitation = None
|
||||
|
||||
invitation_id = (
|
||||
int(request.args.get("invitation_id")) if "invitation_id" in request.args else 0
|
||||
)
|
||||
if invitation_id > 0:
|
||||
invitation = AdminUnitInvitation.query.get_or_404(invitation_id)
|
||||
|
||||
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():
|
||||
flash_message(
|
||||
gettext(
|
||||
"Organizations cannot currently be created. The project is in a closed test phase. If you are interested, you can contact us."
|
||||
@ -40,13 +54,28 @@ def admin_unit_create():
|
||||
|
||||
form = CreateAdminUnitForm()
|
||||
|
||||
if invitation and not form.is_submitted():
|
||||
form.name.data = invitation.admin_unit_name
|
||||
|
||||
if form.validate_on_submit():
|
||||
admin_unit = AdminUnit()
|
||||
admin_unit.location = Location()
|
||||
update_admin_unit_with_form(admin_unit, form)
|
||||
|
||||
try:
|
||||
insert_admin_unit_for_user(admin_unit, current_user)
|
||||
_, _, relation = insert_admin_unit_for_user(
|
||||
admin_unit, current_user, invitation
|
||||
)
|
||||
|
||||
if relation:
|
||||
send_admin_unit_invitation_accepted_mails(
|
||||
invitation, relation, admin_unit
|
||||
)
|
||||
|
||||
if invitation:
|
||||
db.session.delete(invitation)
|
||||
db.session.commit()
|
||||
|
||||
flash(gettext("Organization successfully created"), "success")
|
||||
return redirect(url_for("manage_admin_unit", id=admin_unit.id))
|
||||
except SQLAlchemyError as e:
|
||||
@ -82,3 +111,22 @@ def admin_unit_update(id):
|
||||
flash_errors(form)
|
||||
|
||||
return render_template("admin_unit/update.html", form=form, admin_unit=admin_unit)
|
||||
|
||||
|
||||
def send_admin_unit_invitation_accepted_mails(
|
||||
invitation: AdminUnitInvitation, relation: AdminUnitRelation, admin_unit: AdminUnit
|
||||
):
|
||||
# Benachrichtige alle Mitglieder der AdminUnit, die diese Einladung erstellt hatte
|
||||
members = get_admin_unit_members_with_permission(
|
||||
invitation.admin_unit_id, "admin_unit:update"
|
||||
)
|
||||
emails = list(map(lambda member: member.user.email, members))
|
||||
|
||||
send_mails(
|
||||
emails,
|
||||
gettext("Organization invitation accepted"),
|
||||
"organization_invitation_accepted_notice",
|
||||
invitation=invitation,
|
||||
relation=relation,
|
||||
admin_unit=admin_unit,
|
||||
)
|
||||
|
||||
@ -13,6 +13,7 @@ from project.forms.admin_unit_member import (
|
||||
from project.models import AdminUnitMemberInvitation, AdminUnitMemberRole
|
||||
from project.services.admin_unit import add_user_to_admin_unit_with_roles
|
||||
from project.services.user import find_user_by_email
|
||||
from project.utils import strings_are_equal_ignoring_case
|
||||
from project.views.utils import (
|
||||
flash_errors,
|
||||
handleSqlError,
|
||||
@ -33,7 +34,7 @@ def admin_unit_member_invitation(id):
|
||||
if not current_user.is_authenticated:
|
||||
return app.login_manager.unauthorized()
|
||||
|
||||
if invitation.email != current_user.email:
|
||||
if not strings_are_equal_ignoring_case(invitation.email, current_user.email):
|
||||
return permission_missing(url_for("profile"))
|
||||
|
||||
form = NegotiateAdminUnitMemberInvitationForm()
|
||||
|
||||
@ -22,7 +22,10 @@ from project.models import (
|
||||
EventSuggestion,
|
||||
User,
|
||||
)
|
||||
from project.services.admin_unit import get_admin_unit_member_invitations
|
||||
from project.services.admin_unit import (
|
||||
get_admin_unit_member_invitations,
|
||||
get_admin_unit_organization_invitations,
|
||||
)
|
||||
from project.services.event import get_events_query
|
||||
from project.services.event_search import EventSearchParams
|
||||
from project.services.event_suggestion import get_event_reviews_query
|
||||
@ -53,15 +56,37 @@ def manage():
|
||||
if "from_login" in request.args:
|
||||
admin_units = get_admin_units_for_manage()
|
||||
invitations = get_admin_unit_member_invitations(current_user.email)
|
||||
organization_invitations = get_admin_unit_organization_invitations(
|
||||
current_user.email
|
||||
)
|
||||
|
||||
if len(admin_units) == 1 and len(invitations) == 0:
|
||||
if (
|
||||
len(admin_units) == 1
|
||||
and len(invitations) == 0
|
||||
and len(organization_invitations) == 0
|
||||
):
|
||||
return redirect(url_for("manage_admin_unit", id=admin_units[0].id))
|
||||
|
||||
if len(admin_units) == 0 and len(invitations) == 1:
|
||||
if (
|
||||
len(admin_units) == 0
|
||||
and len(invitations) == 1
|
||||
and len(organization_invitations) == 0
|
||||
):
|
||||
return redirect(
|
||||
url_for("admin_unit_member_invitation", id=invitations[0].id)
|
||||
)
|
||||
|
||||
if (
|
||||
len(admin_units) == 0
|
||||
and len(invitations) == 0
|
||||
and len(organization_invitations) == 1
|
||||
):
|
||||
return redirect(
|
||||
url_for(
|
||||
"user_organization_invitation", id=organization_invitations[0].id
|
||||
)
|
||||
)
|
||||
|
||||
return redirect(url_for("manage_admin_units"))
|
||||
|
||||
|
||||
@ -70,13 +95,18 @@ def manage():
|
||||
def manage_admin_units():
|
||||
admin_units = get_admin_units_for_manage()
|
||||
invitations = get_admin_unit_member_invitations(current_user.email)
|
||||
organization_invitations = get_admin_unit_organization_invitations(
|
||||
current_user.email
|
||||
)
|
||||
|
||||
admin_units.sort(key=lambda x: x.name)
|
||||
invitations.sort(key=lambda x: x.adminunit.name)
|
||||
organization_invitations.sort(key=lambda x: x.adminunit.name)
|
||||
|
||||
return render_template(
|
||||
"manage/admin_units.html",
|
||||
invitations=invitations,
|
||||
organization_invitations=organization_invitations,
|
||||
admin_units=admin_units,
|
||||
)
|
||||
|
||||
@ -234,6 +264,19 @@ def manage_admin_unit_relations(id, path=None):
|
||||
)
|
||||
|
||||
|
||||
@app.route("/manage/admin_unit/<int:id>/organization-invitations")
|
||||
@app.route("/manage/admin_unit/<int:id>/organization-invitations/<path:path>")
|
||||
@auth_required()
|
||||
def manage_admin_unit_organization_invitations(id, path=None):
|
||||
admin_unit = get_admin_unit_for_manage_or_404(id)
|
||||
g.manage_admin_unit_id = id
|
||||
|
||||
return render_template(
|
||||
"manage/organization_invitations.html",
|
||||
admin_unit=admin_unit,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/manage/admin_unit/<int:id>/widgets", methods=("GET", "POST"))
|
||||
@auth_required()
|
||||
def manage_admin_unit_widgets(id):
|
||||
|
||||
@ -1,17 +1,33 @@
|
||||
from flask import render_template
|
||||
from flask import redirect, render_template, url_for
|
||||
from flask_security import auth_required, current_user
|
||||
|
||||
from project import app
|
||||
from project.models import AdminUnitMember
|
||||
from project.services.admin_unit import get_admin_unit_member_invitations
|
||||
from project.models import AdminUnitInvitation
|
||||
from project.services.user import find_user_by_email
|
||||
|
||||
|
||||
@app.route("/profile")
|
||||
@auth_required()
|
||||
def profile():
|
||||
admin_unit_members = AdminUnitMember.query.filter_by(user_id=current_user.id).all()
|
||||
invitations = get_admin_unit_member_invitations(current_user.email)
|
||||
return render_template("profile.html")
|
||||
|
||||
return render_template(
|
||||
"profile.html", admin_unit_members=admin_unit_members, invitations=invitations
|
||||
)
|
||||
|
||||
@app.route("/user/organization-invitations/<int:id>")
|
||||
def user_organization_invitation(id):
|
||||
invitation = AdminUnitInvitation.query.get_or_404(id)
|
||||
|
||||
# Wenn Email nicht als Nutzer vorhanden, dann direkt zu Registrierung
|
||||
if not find_user_by_email(invitation.email):
|
||||
return redirect(url_for("security.register"))
|
||||
|
||||
if not current_user.is_authenticated:
|
||||
return app.login_manager.unauthorized()
|
||||
|
||||
return render_template("user/organization_invitations.html")
|
||||
|
||||
|
||||
@app.route("/user/organization-invitations")
|
||||
@app.route("/user/organization-invitations/<path:path>")
|
||||
@auth_required()
|
||||
def user_organization_invitations(path=None):
|
||||
return render_template("user/organization_invitations.html")
|
||||
|
||||
@ -372,3 +372,57 @@ def test_outgoing_relation_post_selfReference(client, app, seeder, utils):
|
||||
|
||||
response = utils.post_json(url, data)
|
||||
utils.assert_response_bad_request(response)
|
||||
|
||||
|
||||
def test_organization_invitation_list(client, seeder, utils):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_organization_invitation_list",
|
||||
id=admin_unit_id,
|
||||
)
|
||||
response = utils.get_json(url)
|
||||
utils.assert_response_ok(response)
|
||||
assert len(response.json["items"]) == 1
|
||||
assert response.json["items"][0]["id"] == invitation_id
|
||||
assert response.json["items"][0]["email"] == "invited@test.de"
|
||||
assert response.json["items"][0]["organization_name"] == "Invited Organization"
|
||||
|
||||
|
||||
def test_organization_invitation_list_post(client, app, seeder, utils, mocker):
|
||||
mail_mock = utils.mock_send_mails(mocker)
|
||||
_, admin_unit_id = seeder.setup_api_access()
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_organization_invitation_list",
|
||||
id=admin_unit_id,
|
||||
)
|
||||
data = {
|
||||
"email": "invited@test.de",
|
||||
"organization_name": "Invited Organization",
|
||||
"relation_auto_verify_event_reference_requests": True,
|
||||
"relation_verify": True,
|
||||
}
|
||||
|
||||
response = utils.post_json(url, data)
|
||||
utils.assert_response_created(response)
|
||||
assert "id" in response.json
|
||||
invitation_id = int(response.json["id"])
|
||||
|
||||
with app.app_context():
|
||||
from project.models import AdminUnitInvitation
|
||||
|
||||
invitation = AdminUnitInvitation.query.get(invitation_id)
|
||||
assert invitation is not None
|
||||
assert invitation.admin_unit_id == admin_unit_id
|
||||
assert invitation.email == "invited@test.de"
|
||||
assert invitation.admin_unit_name == "Invited Organization"
|
||||
assert invitation.relation_auto_verify_event_reference_requests
|
||||
assert invitation.relation_verify
|
||||
|
||||
invitation_url = utils.get_url(
|
||||
"user_organization_invitation",
|
||||
id=invitation_id,
|
||||
)
|
||||
utils.assert_send_mail_called(mail_mock, "invited@test.de", invitation_url)
|
||||
|
||||
87
tests/api/test_organization_invitation.py
Normal file
87
tests/api/test_organization_invitation.py
Normal file
@ -0,0 +1,87 @@
|
||||
def test_read(client, seeder, utils):
|
||||
user_id, admin_unit_id = seeder.setup_api_access()
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_invitation",
|
||||
id=invitation_id,
|
||||
)
|
||||
response = utils.get_json(url)
|
||||
utils.assert_response_ok(response)
|
||||
assert response.json["id"] == invitation_id
|
||||
assert response.json["organization"]["id"] == admin_unit_id
|
||||
assert response.json["email"] == "invited@test.de"
|
||||
assert response.json["organization_name"] == "Invited Organization"
|
||||
assert response.json["relation_auto_verify_event_reference_requests"] is False
|
||||
assert response.json["relation_verify"] is False
|
||||
|
||||
|
||||
def test_put(client, app, seeder, utils):
|
||||
_, admin_unit_id = seeder.setup_api_access()
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
data = {
|
||||
"organization_name": "Invited Organization1",
|
||||
"relation_auto_verify_event_reference_requests": True,
|
||||
"relation_verify": True,
|
||||
}
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_invitation",
|
||||
id=invitation_id,
|
||||
)
|
||||
response = utils.put_json(url, data)
|
||||
utils.assert_response_no_content(response)
|
||||
|
||||
with app.app_context():
|
||||
from project.models import AdminUnitInvitation
|
||||
|
||||
invitation = AdminUnitInvitation.query.get(invitation_id)
|
||||
assert invitation is not None
|
||||
assert invitation.admin_unit_id == admin_unit_id
|
||||
assert invitation.email == "invited@test.de"
|
||||
assert invitation.admin_unit_name == "Invited Organization1"
|
||||
assert invitation.relation_auto_verify_event_reference_requests
|
||||
assert invitation.relation_verify
|
||||
|
||||
|
||||
def test_patch(client, app, seeder, utils):
|
||||
_, admin_unit_id = seeder.setup_api_access()
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
data = {
|
||||
"relation_auto_verify_event_reference_requests": True,
|
||||
}
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_invitation",
|
||||
id=invitation_id,
|
||||
)
|
||||
response = utils.patch_json(url, data)
|
||||
utils.assert_response_no_content(response)
|
||||
|
||||
with app.app_context():
|
||||
from project.models import AdminUnitInvitation
|
||||
|
||||
invitation = AdminUnitInvitation.query.get(invitation_id)
|
||||
assert invitation is not None
|
||||
assert invitation.admin_unit_id == admin_unit_id
|
||||
assert invitation.relation_auto_verify_event_reference_requests
|
||||
|
||||
|
||||
def test_delete(client, app, seeder, utils):
|
||||
_, admin_unit_id = seeder.setup_api_access()
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_organization_invitation",
|
||||
id=invitation_id,
|
||||
)
|
||||
response = utils.delete(url)
|
||||
utils.assert_response_no_content(response)
|
||||
|
||||
with app.app_context():
|
||||
from project.models import AdminUnitInvitation
|
||||
|
||||
invitation = AdminUnitInvitation.query.get(invitation_id)
|
||||
assert invitation is None
|
||||
65
tests/api/test_user.py
Normal file
65
tests/api/test_user.py
Normal file
@ -0,0 +1,65 @@
|
||||
def test_organization_invitation_list(client, seeder, utils):
|
||||
_, admin_unit_id = seeder.setup_base(log_in=False)
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
seeder.create_user("invited@test.de")
|
||||
utils.login("invited@test.de")
|
||||
|
||||
url = utils.get_url("api_v1_user_organization_invitation_list")
|
||||
response = utils.get_json(url)
|
||||
utils.assert_response_ok(response)
|
||||
assert len(response.json["items"]) == 1
|
||||
assert response.json["items"][0]["id"] == invitation_id
|
||||
assert response.json["items"][0]["email"] == "invited@test.de"
|
||||
assert response.json["items"][0]["organization_name"] == "Invited Organization"
|
||||
|
||||
|
||||
def test_organization_invitation_read(client, seeder, utils):
|
||||
_, admin_unit_id = seeder.setup_base(log_in=False)
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
seeder.create_user("invited@test.de")
|
||||
utils.login("invited@test.de")
|
||||
|
||||
url = utils.get_url("api_v1_user_organization_invitation", id=invitation_id)
|
||||
response = utils.get_json(url)
|
||||
utils.assert_response_ok(response)
|
||||
assert response.json["id"] == invitation_id
|
||||
assert response.json["organization"]["id"] == admin_unit_id
|
||||
assert response.json["email"] == "invited@test.de"
|
||||
assert response.json["organization_name"] == "Invited Organization"
|
||||
assert response.json["relation_auto_verify_event_reference_requests"] is False
|
||||
assert response.json["relation_verify"] is False
|
||||
|
||||
|
||||
def test_organization_invitation_read_wrongEmail(client, seeder, utils):
|
||||
_, admin_unit_id = seeder.setup_base(log_in=False)
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
seeder.create_user("other@test.de")
|
||||
utils.login("other@test.de")
|
||||
|
||||
url = utils.get_url("api_v1_user_organization_invitation", id=invitation_id)
|
||||
response = utils.get_json(url)
|
||||
utils.assert_response_unauthorized(response)
|
||||
|
||||
|
||||
def test_organization_invitation_delete(client, app, seeder, utils):
|
||||
_, admin_unit_id = seeder.setup_base(log_in=False)
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
seeder.create_user("invited@test.de")
|
||||
utils.login("invited@test.de")
|
||||
|
||||
url = utils.get_url(
|
||||
"api_v1_user_organization_invitation",
|
||||
id=invitation_id,
|
||||
)
|
||||
response = utils.delete(url)
|
||||
utils.assert_response_no_content(response)
|
||||
|
||||
with app.app_context():
|
||||
from project.models import AdminUnitInvitation
|
||||
|
||||
invitation = AdminUnitInvitation.query.get(invitation_id)
|
||||
assert invitation is None
|
||||
@ -52,6 +52,7 @@ class Seeder(object):
|
||||
can_create_other=False,
|
||||
can_verify_other=False,
|
||||
verified=False,
|
||||
can_invite_other=True,
|
||||
):
|
||||
from project.models import AdminUnit
|
||||
from project.services.admin_unit import insert_admin_unit_for_user
|
||||
@ -66,6 +67,7 @@ class Seeder(object):
|
||||
admin_unit.suggestions_enabled = suggestions_enabled
|
||||
admin_unit.can_create_other = can_create_other
|
||||
admin_unit.can_verify_other = can_verify_other
|
||||
admin_unit.can_invite_other = can_invite_other
|
||||
insert_admin_unit_for_user(admin_unit, user)
|
||||
self._db.session.commit()
|
||||
admin_unit_id = admin_unit.id
|
||||
@ -123,6 +125,31 @@ class Seeder(object):
|
||||
|
||||
return invitation_id
|
||||
|
||||
def create_admin_unit_invitation(
|
||||
self,
|
||||
admin_unit_id,
|
||||
email="invited@test.de",
|
||||
admin_unit_name="Invited Organization",
|
||||
relation_auto_verify_event_reference_requests=False,
|
||||
relation_verify=False,
|
||||
):
|
||||
from project.models import AdminUnitInvitation
|
||||
|
||||
with self._app.app_context():
|
||||
invitation = AdminUnitInvitation()
|
||||
invitation.admin_unit_id = admin_unit_id
|
||||
invitation.email = email
|
||||
invitation.admin_unit_name = admin_unit_name
|
||||
invitation.relation_auto_verify_event_reference_requests = (
|
||||
relation_auto_verify_event_reference_requests
|
||||
)
|
||||
invitation.relation_verify = relation_verify
|
||||
self._db.session.add(invitation)
|
||||
self._db.session.commit()
|
||||
invitation_id = invitation.id
|
||||
|
||||
return invitation_id
|
||||
|
||||
def create_admin_unit_member_event_verifier(self, admin_unit_id):
|
||||
return self.create_admin_unit_member(admin_unit_id, ["event_verifier"])
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from project.models import AdminUnitRelation
|
||||
from project.models import AdminUnitInvitation, AdminUnitRelation
|
||||
|
||||
|
||||
def test_location_update_coordinate(client, app, db):
|
||||
@ -227,3 +227,21 @@ def test_admin_unit_verification(client, app, db, seeder):
|
||||
|
||||
all_verified = AdminUnit.query.filter(AdminUnit.is_verified).all()
|
||||
assert len(all_verified) == 0
|
||||
|
||||
|
||||
def test_admin_unit_invitations(client, app, db, seeder):
|
||||
_, admin_unit_id = seeder.setup_base(log_in=False)
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
with app.app_context():
|
||||
from project.services.admin_unit import get_admin_unit_by_id
|
||||
|
||||
admin_unit = get_admin_unit_by_id(admin_unit_id)
|
||||
assert len(admin_unit.admin_unit_invitations) == 1
|
||||
|
||||
admin_unit.can_invite_other = False
|
||||
db.session.commit()
|
||||
|
||||
assert len(admin_unit.admin_unit_invitations) == 0
|
||||
invitation = AdminUnitInvitation.query.get(invitation_id)
|
||||
assert invitation is None
|
||||
|
||||
@ -120,6 +120,7 @@ def test_admin_admin_unit_update(client, seeder, utils, app, mocker, db, db_erro
|
||||
admin_unit.incoming_reference_requests_allowed = False
|
||||
admin_unit.suggestions_enabled = False
|
||||
admin_unit.can_create_other = False
|
||||
admin_unit.can_invite_other = False
|
||||
admin_unit.can_verify_other = False
|
||||
db.session.commit()
|
||||
|
||||
@ -136,6 +137,7 @@ def test_admin_admin_unit_update(client, seeder, utils, app, mocker, db, db_erro
|
||||
"incoming_reference_requests_allowed": "y",
|
||||
"suggestions_enabled": "y",
|
||||
"can_create_other": "y",
|
||||
"can_invite_other": "y",
|
||||
"can_verify_other": "y",
|
||||
},
|
||||
)
|
||||
@ -153,4 +155,5 @@ def test_admin_admin_unit_update(client, seeder, utils, app, mocker, db, db_erro
|
||||
assert admin_unit.incoming_reference_requests_allowed
|
||||
assert admin_unit.suggestions_enabled
|
||||
assert admin_unit.can_create_other
|
||||
assert admin_unit.can_invite_other
|
||||
assert admin_unit.can_verify_other
|
||||
|
||||
@ -134,6 +134,77 @@ def test_create_requiresAdmin_memberOfOrgWithFlag(client, app, utils, seeder):
|
||||
assert response.status_code == 302
|
||||
|
||||
|
||||
def test_create_from_invitation(client, app, utils, seeder, mocker):
|
||||
mail_mock = utils.mock_send_mails(mocker)
|
||||
user_id = seeder.create_user()
|
||||
admin_unit_id = seeder.create_admin_unit(
|
||||
user_id, can_invite_other=True, can_verify_other=True
|
||||
)
|
||||
invitation_id = seeder.create_admin_unit_invitation(
|
||||
admin_unit_id,
|
||||
relation_auto_verify_event_reference_requests=True,
|
||||
relation_verify=True,
|
||||
)
|
||||
|
||||
seeder.create_user("invited@test.de")
|
||||
utils.login("invited@test.de")
|
||||
url = utils.get_url("admin_unit_create", invitation_id=invitation_id)
|
||||
response = utils.get_ok(url)
|
||||
|
||||
response = utils.post_form(
|
||||
url,
|
||||
response,
|
||||
{
|
||||
"short_name": "invitedorganization",
|
||||
"location-postalCode": "38640",
|
||||
"location-city": "Goslar",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 302
|
||||
|
||||
with app.app_context():
|
||||
from project.models import AdminUnitInvitation
|
||||
from project.services.admin_unit import get_admin_unit_by_name
|
||||
|
||||
admin_unit = get_admin_unit_by_name("Invited Organization")
|
||||
assert admin_unit is not None
|
||||
|
||||
relation = admin_unit.incoming_relations[0]
|
||||
assert relation.source_admin_unit_id == admin_unit_id
|
||||
assert relation.auto_verify_event_reference_requests
|
||||
assert relation.verify
|
||||
assert relation.invited
|
||||
relation_id = relation.id
|
||||
|
||||
invitation = AdminUnitInvitation.query.get(invitation_id)
|
||||
assert invitation is None
|
||||
|
||||
invitation_url = utils.get_url(
|
||||
"manage_admin_unit_relations", id=admin_unit_id, path=f"{relation_id}/update"
|
||||
)
|
||||
utils.assert_send_mail_called(mail_mock, "test@test.de", invitation_url)
|
||||
|
||||
|
||||
def test_create_from_invitation_currentUserDoesNotMatchInvitationEmail(
|
||||
client, app, utils, seeder
|
||||
):
|
||||
user_id = seeder.create_user()
|
||||
admin_unit_id = seeder.create_admin_unit(
|
||||
user_id, can_invite_other=True, can_verify_other=True
|
||||
)
|
||||
invitation_id = seeder.create_admin_unit_invitation(
|
||||
admin_unit_id,
|
||||
relation_auto_verify_event_reference_requests=True,
|
||||
relation_verify=True,
|
||||
)
|
||||
|
||||
seeder.create_user("other@test.de")
|
||||
utils.login("other@test.de")
|
||||
url = utils.get_url("admin_unit_create", invitation_id=invitation_id)
|
||||
response = utils.get(url)
|
||||
utils.assert_response_redirect(response, "manage_admin_units")
|
||||
|
||||
|
||||
def test_update(client, app, utils, seeder):
|
||||
seeder.create_user()
|
||||
user_id = utils.login()
|
||||
|
||||
@ -48,6 +48,25 @@ def test_index_after_login(client, app, db, utils, seeder):
|
||||
)
|
||||
|
||||
|
||||
def test_index_after_login_organization_invitation(client, app, db, utils, seeder):
|
||||
user_id = seeder.create_user()
|
||||
admin_unit_id = seeder.create_admin_unit(user_id, "Meine Crew")
|
||||
|
||||
email = "invited@test.de"
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id, email)
|
||||
|
||||
seeder.create_user(email)
|
||||
utils.login(email)
|
||||
|
||||
response = utils.get_endpoint("manage_after_login")
|
||||
utils.assert_response_redirect(response, "manage", from_login=1)
|
||||
|
||||
response = utils.get_endpoint("manage", from_login=1)
|
||||
utils.assert_response_redirect(
|
||||
response, "user_organization_invitation", id=invitation_id
|
||||
)
|
||||
|
||||
|
||||
def test_admin_unit(client, seeder, utils):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
|
||||
@ -169,3 +188,10 @@ def test_admin_unit_relations(client, seeder, utils):
|
||||
|
||||
url = utils.get_url("manage_admin_unit_relations", id=admin_unit_id)
|
||||
utils.get_ok(url)
|
||||
|
||||
|
||||
def test_admin_unit_organization_invitations(client, seeder, utils):
|
||||
user_id, admin_unit_id = seeder.setup_base()
|
||||
|
||||
url = utils.get_url("manage_admin_unit_organization_invitations", id=admin_unit_id)
|
||||
utils.get_ok(url)
|
||||
|
||||
@ -4,3 +4,34 @@ def test_profile(client, seeder, utils):
|
||||
|
||||
url = utils.get_url("profile", id=1)
|
||||
utils.get_ok(url)
|
||||
|
||||
|
||||
def test_organization_invitation_not_registered(client, app, utils, seeder):
|
||||
_, admin_unit_id = seeder.setup_base(log_in=False)
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
url = utils.get_url("user_organization_invitation", id=invitation_id)
|
||||
response = client.get(url)
|
||||
utils.assert_response_redirect(response, "security.register")
|
||||
|
||||
|
||||
def test_organization_invitation_not_authenticated(client, app, utils, seeder):
|
||||
_, admin_unit_id = seeder.setup_base(log_in=False)
|
||||
invitation_id = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
seeder.create_user("invited@test.de")
|
||||
url = utils.get_url("user_organization_invitation", id=invitation_id)
|
||||
response = client.get(url)
|
||||
assert response.status_code == 302
|
||||
assert response.headers["Location"].startswith("http://localhost/login")
|
||||
|
||||
|
||||
def test_organization_invitation_list(client, seeder, utils):
|
||||
_, admin_unit_id = seeder.setup_base(log_in=False)
|
||||
_ = seeder.create_admin_unit_invitation(admin_unit_id)
|
||||
|
||||
seeder.create_user("invited@test.de")
|
||||
utils.login("invited@test.de")
|
||||
|
||||
url = utils.get_url("user_organization_invitations")
|
||||
utils.get_ok(url)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user