Merge pull request 'develop -> main (v0.1)' (#16) from develop into master
Reviewed-on: ERRMS/ERRMS#16
This commit is contained in:
commit
ae86b585eb
|
@ -3,10 +3,6 @@
|
|||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
23
Cargo.toml
23
Cargo.toml
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "ERRMS"
|
||||
name = "errms"
|
||||
version = "0.1.0"
|
||||
authors = ["Keanu Doelle <ares@anghenfil.de>"]
|
||||
edition = "2018"
|
||||
|
@ -7,3 +7,24 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
config = "0.10.1"
|
||||
serde_derive = "1.0.115"
|
||||
serde = { version = "1.0.115", features = ["derive"] }
|
||||
serde_json = "1.0.57"
|
||||
log = "0.4.11"
|
||||
env_logger = "0.7.1"
|
||||
rocket = "0.4.5"
|
||||
diesel = { version = "1.4.5", features = ["postgres", "uuidv07", "chrono"] } #uuidv07 vs uuid to use uuid >= 0.7
|
||||
diesel_geometry = "1.4.0"
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
rust-argon2 = "0.8.2"
|
||||
chrono = { version = "0.4.15", features = ["serde"] }
|
||||
rand = "0.7.3"
|
||||
iban_validate = "4"
|
||||
lettre = "0.9"
|
||||
lettre_email = "0.9"
|
||||
|
||||
[dependencies.rocket_contrib]
|
||||
version = "0.4.5"
|
||||
default-features = false
|
||||
features = ["handlebars_templates", "serve", "json"]
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -520,7 +520,7 @@ governed by version 3 of the GNU General Public License.
|
|||
|
||||
The Free Software Foundation may publish revised and/or new versions of the
|
||||
GNU Affero General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to address
|
||||
be similar in spirit to the present version, but may differ in detail to Address
|
||||
new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies
|
||||
|
|
38
README.md
38
README.md
|
@ -1,3 +1,39 @@
|
|||
# ERRMS
|
||||
|
||||
Build status (develop branch):
|
||||
[![builds.sr.ht status](https://builds.sr.ht/~anghenfil.svg)](https://builds.sr.ht/~anghenfil?search=)
|
||||
![project status](https://www.repostatus.org/badges/latest/wip.svg)
|
||||
|
||||
## About ERRMS
|
||||
* ERRMS stands for **E**mergency **R**esponse and
|
||||
**R**escue **M**anagement **S**ystem
|
||||
* application for fire departments, humanitarian aid organisations and similar
|
||||
* organise member data, events/operations and other resources
|
||||
|
||||
Checkout our presentation: https://md.kabi.tk/p/H1nWPbueL
|
||||
|
||||
## characteristics
|
||||
* fully open source (APGLv3)
|
||||
* modular software
|
||||
* strong focus on data privacy & data security
|
||||
* provides feature-rich API for integration
|
||||
* multilingual, easy to translate software
|
||||
* strong, precise permission system
|
||||
|
||||
## How to contribute
|
||||
* You can contribute by signing pull requests and issues or programming!
|
||||
* Get in contact (Matrix: #errms@matrix.anghenfil.de) and check our website errms.dev and our project management tool: pm.errms.dev
|
||||
|
||||
|
||||
## Zeitplan/Timetable
|
||||
**Version 0.1:**
|
||||
* core system
|
||||
* Mitgliedsverwaltung/member management
|
||||
|
||||
**Version 0.2:**
|
||||
* Fahrzeugverwaltung (nur Basisfunktionen) / vehicle management (limited functionality)
|
||||
* Einsatzverwaltung / event management
|
||||
* Veranstalterverwaltung / manage event organizer
|
||||
|
||||
**Version 0.3:**
|
||||
* Abrechnung / billing module
|
||||
* Fahrzeugverwaltung (erweitert) / expended vehicle management
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
[development]
|
||||
address = "localhost"
|
||||
port = 8000
|
||||
keep_alive = 5
|
||||
log = "normal"
|
||||
limits = { forms = 32768 }
|
||||
template_dir = "resources/templates"
|
||||
|
||||
[staging]
|
||||
address = "0.0.0.0"
|
||||
port = 8000
|
||||
keep_alive = 5
|
||||
log = "normal"
|
||||
limits = { forms = 32768 }
|
||||
template_dir = "resources/templates"
|
||||
|
||||
[production]
|
||||
address = "0.0.0.0"
|
||||
port = 8000
|
||||
keep_alive = 5
|
||||
log = "critical"
|
||||
limits = { forms = 32768 }
|
||||
template_dir = "resources/templates"
|
|
@ -0,0 +1,22 @@
|
|||
[database]
|
||||
connection_string = "postgresql://postgres:qwertz@localhost:5432/postgres"
|
||||
|
||||
[application]
|
||||
url = "http://localhost:8000/"
|
||||
name = "einsatz.online"
|
||||
default_language = "de-DE"
|
||||
fallback_language = "en-US"
|
||||
loglevel = "debug"
|
||||
#Session timeout in seconds. Default is 15 minutes
|
||||
session_timeout = 900
|
||||
upload_path = "uploads/"
|
||||
#Maximum login attempts until email is locked for login
|
||||
max_login_attempts = 6
|
||||
#Duration of email lock after max_login_attempts in seconds. Default is 30 minutes
|
||||
login_lock_duration = 1800
|
||||
#How long does it take until tokens expire?
|
||||
reset_password_token_valid_duration = 3600
|
||||
|
||||
[mail]
|
||||
from = "No Reply <noreply@localhost>"
|
||||
reply_to = "support@localhost"
|
|
@ -0,0 +1,7 @@
|
|||
# For documentation on how to configure this file,
|
||||
# see diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/schema.rs"
|
||||
# Add types from `diesel_full_text_search` like `tsvector`
|
||||
import_types = ["diesel::sql_types::*", "diesel_geometry::sql_types::*"]
|
|
@ -0,0 +1,6 @@
|
|||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
||||
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
|
@ -0,0 +1,36 @@
|
|||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
|
||||
|
||||
|
||||
-- Sets up a trigger for the given table to automatically set a column called
|
||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
||||
-- in the modified columns)
|
||||
--
|
||||
-- # Example
|
||||
--
|
||||
-- ```sql
|
||||
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
||||
--
|
||||
-- SELECT diesel_manage_updated_at('users');
|
||||
-- ```
|
||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
||||
BEGIN
|
||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
IF (
|
||||
NEW IS DISTINCT FROM OLD AND
|
||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
||||
) THEN
|
||||
NEW.updated_at := current_timestamp;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table entities;
|
|
@ -0,0 +1,9 @@
|
|||
-- Your SQL goes here
|
||||
create table entities
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null,
|
||||
constraint entities_pk
|
||||
primary key (entity_id)
|
||||
);
|
||||
|
||||
INSERT INTO entities (entity_id) VALUES ('00000000-0000-0000-0000-000000000000');
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table users;
|
|
@ -0,0 +1,13 @@
|
|||
-- Your SQL goes here
|
||||
create table users
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint pk___users___id
|
||||
primary key,
|
||||
password text,
|
||||
email text not null
|
||||
);
|
||||
|
||||
create unique index users_email_uindex
|
||||
on users (email);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table members;
|
|
@ -0,0 +1,31 @@
|
|||
create table members
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null
|
||||
constraint pk___members___id
|
||||
primary key
|
||||
constraint members_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
users_id uuid
|
||||
constraint fk___members___users_id___users
|
||||
references users
|
||||
on update cascade on delete set null,
|
||||
firstname text not null,
|
||||
lastname text not null,
|
||||
date_of_birth date,
|
||||
sex smallint,
|
||||
salutation text,
|
||||
place_of_birth text,
|
||||
academic_titles text,
|
||||
personnel_number integer,
|
||||
ui_language text,
|
||||
nationality text,
|
||||
entrance_date date,
|
||||
birth_name text,
|
||||
iban text,
|
||||
bic text
|
||||
);
|
||||
|
||||
create unique index members_personnel_number_uindex
|
||||
on members (personnel_number);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE addresses;
|
|
@ -0,0 +1,13 @@
|
|||
-- Your SQL goes here
|
||||
create table addresses
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint addresses_pk
|
||||
primary key,
|
||||
title text,
|
||||
street text,
|
||||
number text,
|
||||
zipcode text,
|
||||
city text,
|
||||
country text
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table addresses_entities;
|
|
@ -0,0 +1,14 @@
|
|||
-- Your SQL goes here
|
||||
create table addresses_entities
|
||||
(
|
||||
address_id uuid not null,
|
||||
entitiy_id uuid not null,
|
||||
constraint addresses_entities_pk
|
||||
primary key (address_id, entitiy_id),
|
||||
constraint addresses_entities_addresses_id_fk
|
||||
foreign key (address_id) references addresses
|
||||
on update cascade on delete cascade,
|
||||
constraint addresses_entities_entities_entity_id_fk
|
||||
foreign key (entitiy_id) references entities
|
||||
on update cascade on delete cascade
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table buildings;
|
|
@ -0,0 +1,12 @@
|
|||
-- Your SQL goes here
|
||||
create table buildings
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null,
|
||||
name text not null,
|
||||
description text,
|
||||
constraint buildings_pk
|
||||
primary key (entity_id),
|
||||
constraint buildings_entities_entity_id_fk
|
||||
foreign key (entity_id) references entities
|
||||
on update cascade on delete cascade
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table vehicles;
|
|
@ -0,0 +1,17 @@
|
|||
-- Your SQL goes here
|
||||
create table vehicles
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null,
|
||||
identifier text not null,
|
||||
numberplate text,
|
||||
description text,
|
||||
next_inspection date,
|
||||
is_operational boolean default true not null,
|
||||
admissible_total_weight real,
|
||||
required_license text,
|
||||
constraint vehicles_pk
|
||||
primary key (entity_id),
|
||||
constraint vehicles_entities_entity_id_fk
|
||||
foreign key (entity_id) references entities
|
||||
on update cascade on delete cascade
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table communication_types;
|
|
@ -0,0 +1,8 @@
|
|||
-- Your SQL goes here
|
||||
create table communication_types
|
||||
(
|
||||
type_id uuid default uuid_generate_v1() not null
|
||||
constraint pk___communication_types___id
|
||||
primary key,
|
||||
type_name text not null
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table communication_targets;
|
|
@ -0,0 +1,16 @@
|
|||
-- Your SQL goes here
|
||||
create table communication_targets
|
||||
(
|
||||
target_id uuid default uuid_generate_v1() not null
|
||||
constraint pk___communication_targets___id
|
||||
primary key,
|
||||
entity_id uuid not null
|
||||
constraint communication_targets_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
com_type uuid not null
|
||||
constraint fk___communication_target___type___communication_types
|
||||
references communication_types,
|
||||
value text not null,
|
||||
description text
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table permissions;
|
|
@ -0,0 +1,50 @@
|
|||
-- Your SQL goes here
|
||||
create table permissions
|
||||
(
|
||||
permission text not null
|
||||
constraint permissions_pk
|
||||
primary key,
|
||||
description text
|
||||
);
|
||||
|
||||
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.configuration.fields.communication_types.view', 'Permission to see all communication types');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.dashboard.view', 'Permission to see Dashboard');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.event_management.view', 'Permission to see Event Management');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.groups.core.edit', 'Permission to edit group name + description');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.groups.create', 'Permission to create new group');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.groups.delete', 'Permission to delete entire group');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.groups.edit', 'Permission to see edit group mode');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.groups.members.edit', 'Permission to edit group members (adding/removing)');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.groups.members.view', 'Permission to see all members in group');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.groups.permissions.edit', 'Permission to edit group permissions');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.groups.permissions.view', 'Permission to see group permissions');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.groups.view', 'Permission to see group data');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.list.view', 'Permission to access member list. Note: Member will only see members selected in context. Exception: members with modules.member_management.list.view.all will see any members, ignoring context!');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.communication.edit', null);
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.communication.view', null);
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.core.edit', null);
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.create', 'Permission to create new member');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.delete', 'Permission to delete specified member profile');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.drive_permissions_licenses.edit', null);
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.drive_permissions_licenses.view', null);
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.edit', 'Permission to edit specified member profile');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.groups.edit', null);
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.groups.view', 'Permission to see all groups of member in profile view');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.iban_bic.edit', 'Permission to edit member''s IBAN + BIC');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.iban_bic.view', 'Permission to see member''s IBAN + BIC');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.image.edit', null);
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.image.view', 'Permission to see members profile image');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.qualification.view', null);
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.qualifications.edit', null);
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.view', 'Permission to see specified member profile');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.search', 'Permission to search for members');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.view', 'Permission to see Member Management');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.resource_management.view', 'Permission to see Resource Management');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.units.edit', 'Permission to edit existing unit');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.units.delete', 'Permission to delete existing unit');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.units.create', 'Permission to create new unit');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.units.members.view', 'Permission to see all unit members');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.units.members.edit', 'Permission to change unit members');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.units.view', 'Permission to see members units');
|
||||
INSERT INTO permissions (permission, description) VALUES ('modules.member_management.profile.units.edit', 'Permission to edit members units');
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table roles;
|
|
@ -0,0 +1,11 @@
|
|||
-- Your SQL goes here
|
||||
create table roles
|
||||
(
|
||||
id text not null
|
||||
constraint roles_pk
|
||||
primary key,
|
||||
description text
|
||||
);
|
||||
|
||||
INSERT INTO roles (id, description) VALUES ('member', 'Default Member role');
|
||||
INSERT INTO roles (id, description) VALUES ('admin', 'Default Administrator role');
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table roles_permissions;
|
|
@ -0,0 +1,51 @@
|
|||
create table roles_permissions
|
||||
(
|
||||
role_id text not null
|
||||
constraint roles_permissions_roles_id_fk
|
||||
references roles
|
||||
on update cascade on delete cascade,
|
||||
permission_id text not null
|
||||
constraint roles_permissions_permissions_permission_fk
|
||||
references permissions
|
||||
on update cascade on delete cascade,
|
||||
role_permission_id uuid default uuid_generate_v1() not null
|
||||
constraint roles_permissions_pk_2
|
||||
primary key
|
||||
);
|
||||
|
||||
create unique index roles_permissions_role_permission_id_uindex
|
||||
on roles_permissions (role_permission_id);
|
||||
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.event_management.view', 'a2ac9294-e4b3-11ea-ab3e-e86a6432fc61');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.view', 'a2ad8f1e-e4b3-11ea-ab3e-e86a6432fc61');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.resource_management.view', 'a2ae30fe-e4b3-11ea-ab3e-e86a6432fc61');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.dashboard.view', 'a2aeb7c2-e4b3-11ea-ab3e-e86a6432fc61');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('member', 'modules.dashboard.view', 'a2af63fc-e4b3-11ea-ab3e-e86a6432fc61');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.list.view', 'ff5a63e4-e71b-11ea-9387-a4c3f0e2b1e4');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.view', '45bf2b6e-193d-11eb-aab5-e86a645407c8');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.edit', '62312536-1a32-11eb-9574-e86a642ef0c5');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('member', 'modules.configuration.fields.communication_types.view', '66f5052e-1a3c-11eb-9574-e86a642ef0c5');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.configuration.fields.communication_types.view', '66f62fe4-1a3c-11eb-9574-e86a642ef0c5');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.delete', '57221870-1ae7-11eb-98ca-a4c3f0e8cdb7');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.search', '8fa74146-1c6a-11eb-ac8d-e86a64e97ce1');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.qualifications.edit', '5d03484c-2170-11eb-9d03-e86a6481f7b8');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.communication.view', 'adb72014-2171-11eb-9d03-e86a6481f7b8');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.groups.view', '6813a728-241c-11eb-b21a-e86a64d15afc');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.groups.view', '5bbc2b16-25ee-11eb-bb25-e86a644092b4');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.groups.members.view', '5bbd2a02-25ee-11eb-bb25-e86a644092b4');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.groups.members.edit', '5bbdf900-25ee-11eb-bb25-e86a644092b4');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.groups.edit', '5bbee55e-25ee-11eb-bb25-e86a644092b4');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.groups.delete', '5bbfc42e-25ee-11eb-bb25-e86a644092b4');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.groups.core.edit', '5bc085f8-25ee-11eb-bb25-e86a644092b4');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.groups.create', '7da2f954-278c-11eb-a7ba-e86a644092b4');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.groups.permissions.view', '02668fca-3282-11eb-8766-e86a64433ce6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.groups.permissions.edit', '0268544a-3282-11eb-8766-e86a64433ce6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.iban_bic.view', '7a29dc26-3726-11eb-8f7d-e86a6413f6e6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.iban_bic.edit', 'abcecbba-3726-11eb-8f7d-e86a6413f6e6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.units.edit', '990c4fd2-373c-11eb-92e1-e86a6413f6e6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.units.delete', '990d09fe-373c-11eb-92e1-e86a6413f6e6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.units.create', '990db9e4-373c-11eb-92e1-e86a6413f6e6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.units.members.view', '990e9eae-373c-11eb-92e1-e86a6413f6e6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.units.members.edit', '990f68ac-373c-11eb-92e1-e86a6413f6e6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.units.view', '991035c0-373c-11eb-92e1-e86a6413f6e6');
|
||||
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id) VALUES ('admin', 'modules.member_management.profile.units.edit', '991129b2-373c-11eb-92e1-e86a6413f6e6');
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table members_roles;
|
|
@ -0,0 +1,14 @@
|
|||
-- Your SQL goes here
|
||||
create table members_roles
|
||||
(
|
||||
member_id uuid not null
|
||||
constraint members_roles_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
role_id text not null
|
||||
constraint members_roles_roles_id_fk
|
||||
references roles
|
||||
on update cascade on delete cascade,
|
||||
constraint members_roles_pk
|
||||
primary key (member_id, role_id)
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table groups;
|
|
@ -0,0 +1,17 @@
|
|||
-- Your SQL goes here
|
||||
create table groups
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null
|
||||
constraint groups_pk
|
||||
primary key
|
||||
constraint groups_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
group_name text not null,
|
||||
group_description text
|
||||
);
|
||||
|
||||
create unique index groups_group_name_uindex
|
||||
on groups (group_name);
|
||||
|
||||
INSERT INTO groups (entity_id, group_name, group_description) VALUES ('00000000-0000-0000-0000-000000000000', 'default', '!autogenerated group!');
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table groups_entities;
|
|
@ -0,0 +1,14 @@
|
|||
-- Your SQL goes here
|
||||
create table groups_entities
|
||||
(
|
||||
group_id uuid not null
|
||||
constraint groups_entities_groups_group_id_fk
|
||||
references groups
|
||||
on update cascade on delete cascade,
|
||||
entity_id uuid not null
|
||||
constraint groups_entities_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
constraint groups_entities_pk
|
||||
primary key (group_id, entity_id)
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table roles_permissions_context;
|
|
@ -0,0 +1,30 @@
|
|||
-- Your SQL goes here
|
||||
create table roles_permissions_context
|
||||
(
|
||||
role_permission_id uuid
|
||||
constraint roles_permissions_contexts_roles_permissions_role_permission_id
|
||||
references roles_permissions
|
||||
on update cascade on delete cascade,
|
||||
entity uuid
|
||||
constraint roles_permissions_contexts_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
constraint roles_permissions_context_pk
|
||||
primary key (role_permission_id, entity)
|
||||
);
|
||||
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('7a29dc26-3726-11eb-8f7d-e86a6413f6e6', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('990e9eae-373c-11eb-92e1-e86a6413f6e6', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('990db9e4-373c-11eb-92e1-e86a6413f6e6', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('990d09fe-373c-11eb-92e1-e86a6413f6e6', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('990c4fd2-373c-11eb-92e1-e86a6413f6e6', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('abcecbba-3726-11eb-8f7d-e86a6413f6e6', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('ff5a63e4-e71b-11ea-9387-a4c3f0e2b1e4', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('45bf2b6e-193d-11eb-aab5-e86a645407c8', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('62312536-1a32-11eb-9574-e86a642ef0c5', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('57221870-1ae7-11eb-98ca-a4c3f0e8cdb7', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('5d03484c-2170-11eb-9d03-e86a6481f7b8', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('adb72014-2171-11eb-9d03-e86a6481f7b8', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('991129b2-373c-11eb-92e1-e86a6413f6e6', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('991035c0-373c-11eb-92e1-e86a6413f6e6', '00000000-0000-0000-0000-000000000000');
|
||||
INSERT INTO roles_permissions_context (role_permission_id, entity) VALUES ('990f68ac-373c-11eb-92e1-e86a6413f6e6', '00000000-0000-0000-0000-000000000000');
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table license_categories;
|
|
@ -0,0 +1,26 @@
|
|||
-- Your SQL goes here
|
||||
create table license_categories
|
||||
(
|
||||
name text not null
|
||||
constraint license_categories_pk
|
||||
primary key,
|
||||
description text
|
||||
);
|
||||
|
||||
INSERT INTO license_categories (name, description) VALUES ('AM', 'zwei- oder dreirädrige Kraftfahrzeuge mit einer bauartbedingten Höchstgeschwindigkeit von bis zu 45 km/h, bis 4 kW, 50 cm³, bis 270 kg leer. Vierrädrige Leichtkraftfahrzeuge bis 45 km/h, bis 6 kW, bis 50 cm³, bis 425 kg leer.');
|
||||
INSERT INTO license_categories (name, description) VALUES ('A1', 'Krafträder mit einem Hubraum von bis zu 125 cm³ mit einer Motorleistung von bis zu 11 kW (Leichtkrafträder) und einem Leistungsgewicht bis zu 0,1 kW/kg sowie dreirädrige Kraftfahrzeuge mit einer Leistung von bis zu 15 kW');
|
||||
INSERT INTO license_categories (name, description) VALUES ('A2', 'Krafträder mit einer Motorleistung von bis zu 35 kW und einem Leistungsgewicht bis zu 0,2 kW/kg, die nicht von einem Fahrzeug mit mehr als der doppelten Motorleistung abgeleitet sind');
|
||||
INSERT INTO license_categories (name, description) VALUES ('A', 'Krafträder über 50 cm³ oder über 45 km/h, auch mit Beiwagen, sowie dreirädrige Kraftfahrzeuge[4] mit einer Leistung von mehr als 15 kW');
|
||||
INSERT INTO license_categories (name, description) VALUES ('B1', 'Mehrspurige Kraftfahrzeuge bis 550 kg Leermasse');
|
||||
INSERT INTO license_categories (name, description) VALUES ('B', 'Mehrspurige Kraftfahrzeuge bis 3,5 t zulässiger Gesamtmasse und maximal 9 Sitzplätzen (einschließlich Fahrer).');
|
||||
INSERT INTO license_categories (name, description) VALUES ('C1', 'Mehrspuriges Kraftfahrzeug bis 7,5 t zulässiger Gesamtmasse, maximal 9 Sitzplätze (einschließlich Fahrer)');
|
||||
INSERT INTO license_categories (name, description) VALUES ('C', 'Mehrspurige Kraftfahrzeuge über 7,5 t zulässiger Gesamtmasse, maximal 9 Sitzplätze (einschließlich Fahrer)');
|
||||
INSERT INTO license_categories (name, description) VALUES ('D1', 'Omnibusse mit bis 16 Sitzplätzen einschließlich Fahrer und höchstens 8 m Länge');
|
||||
INSERT INTO license_categories (name, description) VALUES ('D', 'Omnibusse mit mehr als 9 Sitzplätzen (einschließlich Fahrer)');
|
||||
INSERT INTO license_categories (name, description) VALUES ('BE', 'Züge aus B-Zugfahrzeug und Anhänger über 0,75 t zulässiger Gesamtmasse (sofern der Zug nicht unter Klasse B fällt)');
|
||||
INSERT INTO license_categories (name, description) VALUES ('C1E', 'Züge aus C1-Zugfahrzeug und Anhänger über 0,75 t zulässiger Gesamtmasse, sowie Züge aus B-Zugfahrzeug und Anhänger über 3,5 t zulässiger Gesamtmasse');
|
||||
INSERT INTO license_categories (name, description) VALUES ('CE', 'Lastzüge und Sattelkraftfahrzeuge');
|
||||
INSERT INTO license_categories (name, description) VALUES ('D1E', 'Züge aus D1-Zugfahrzeug und Anhänger mit mehr als 0,75 t zulässiger Gesamtmasse');
|
||||
INSERT INTO license_categories (name, description) VALUES ('DE', 'Züge aus D-Zugfahrzeug und Anhänger mit mehr als 0,75 t zulässiger Gesamtmasse');
|
||||
INSERT INTO license_categories (name, description) VALUES ('L', '');
|
||||
INSERT INTO license_categories (name, description) VALUES ('T', '');
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table licenses_members;
|
|
@ -0,0 +1,15 @@
|
|||
-- Your SQL goes here
|
||||
create table licenses_members
|
||||
(
|
||||
member_id uuid not null
|
||||
constraint members_licenses_members_entity_id_fk
|
||||
references members
|
||||
on update cascade on delete cascade,
|
||||
license_name text not null
|
||||
constraint members_licenses_license_categories_name_fk
|
||||
references license_categories
|
||||
on update cascade on delete cascade,
|
||||
drive_permission boolean default false not null,
|
||||
constraint members_licenses_pk
|
||||
primary key (member_id, license_name)
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table qualification_categories;
|
|
@ -0,0 +1,12 @@
|
|||
-- Your SQL goes here
|
||||
create table qualification_categories
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint qualification_categories_pk
|
||||
primary key,
|
||||
name text not null,
|
||||
description text
|
||||
);
|
||||
|
||||
INSERT INTO public.qualification_categories (id, name, description) VALUES ('168faee2-f159-11ea-8d76-e86a6444789b', 'Medizinisch', null);
|
||||
INSERT INTO public.qualification_categories (id, name, description) VALUES ('1690690e-f159-11ea-8d76-e86a6444789b', 'Führung', 'Führungsqualifikationen nach DV 100');
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table qualifications;
|
|
@ -0,0 +1,24 @@
|
|||
-- Your SQL goes here
|
||||
create table qualifications
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint qualifications_pk
|
||||
primary key,
|
||||
name text not null,
|
||||
description text,
|
||||
category uuid not null
|
||||
constraint qualifications_qualification_categories_id_fk
|
||||
references qualification_categories
|
||||
on update cascade on delete cascade
|
||||
);
|
||||
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('59214062-f164-11ea-8e01-e86a6444789b', 'Rettungssanitäter', null, '168faee2-f159-11ea-8d76-e86a6444789b');
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('6567de9e-f164-11ea-8e01-e86a6444789b', 'Notfallsanitäter', null, '168faee2-f159-11ea-8d76-e86a6444789b');
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('7045e310-f164-11ea-8e01-e86a6444789b', 'Zugführer', null, '1690690e-f159-11ea-8d76-e86a6444789b');
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('7047574a-f164-11ea-8e01-e86a6444789b', 'Gruppenführer', null, '1690690e-f159-11ea-8d76-e86a6444789b');
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('70491904-f164-11ea-8e01-e86a6444789b', 'Verbandsführer', null, '1690690e-f159-11ea-8d76-e86a6444789b');
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('d3b907ba-f164-11ea-8e01-e86a6444789b', 'Arzt', null, '168faee2-f159-11ea-8d76-e86a6444789b');
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('d3b97b5a-f164-11ea-8e01-e86a6444789b', 'Rettungsassistent', null, '168faee2-f159-11ea-8d76-e86a6444789b');
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('d3b9e28e-f164-11ea-8e01-e86a6444789b', 'Erste-Hilfe-Lehrgang', null, '168faee2-f159-11ea-8d76-e86a6444789b');
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('d3ba426a-f164-11ea-8e01-e86a6444789b', 'Notarzt', null, '168faee2-f159-11ea-8d76-e86a6444789b');
|
||||
INSERT INTO public.qualifications (id, name, description, category) VALUES ('d3baa624-f164-11ea-8e01-e86a6444789b', 'Rettungshelfer', null, '168faee2-f159-11ea-8d76-e86a6444789b');
|
|
@ -0,0 +1 @@
|
|||
-- This file should undo anything in `up.sql`
|
|
@ -0,0 +1,14 @@
|
|||
-- Your SQL goes here
|
||||
create table qualifications_members
|
||||
(
|
||||
member_id uuid not null
|
||||
constraint qualifications_members_members_entity_id_fk
|
||||
references members
|
||||
on update cascade on delete cascade,
|
||||
qualification_id uuid not null
|
||||
constraint qualifications_members_qualifications_id_fk
|
||||
references qualifications
|
||||
on update cascade on delete cascade,
|
||||
constraint qualifications_members_pk
|
||||
primary key (member_id, qualification_id)
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table units;
|
|
@ -0,0 +1,11 @@
|
|||
-- Your SQL goes here
|
||||
create table units
|
||||
(
|
||||
unit_id uuid default uuid_generate_v1() not null
|
||||
constraint untis_pk
|
||||
primary key
|
||||
constraint units_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
name text not null
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table units_members;
|
|
@ -0,0 +1,14 @@
|
|||
create table units_members
|
||||
(
|
||||
unit_id uuid
|
||||
constraint units_members_units_unit_id_fk
|
||||
references units
|
||||
on update cascade on delete cascade,
|
||||
member_id uuid
|
||||
constraint units_members_members_entity_id_fk
|
||||
references members
|
||||
on update cascade on delete cascade,
|
||||
crew smallint not null,
|
||||
PRIMARY KEY (unit_id, member_id)
|
||||
);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE login_attempts;
|
|
@ -0,0 +1,7 @@
|
|||
-- Your SQL goes here
|
||||
create table login_attempts
|
||||
(
|
||||
id uuid default uuid_generate_v1() primary key,
|
||||
email text not null,
|
||||
timestamp timestamp default now() not null
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table password_resets;
|
|
@ -0,0 +1,17 @@
|
|||
-- Your SQL goes here
|
||||
create table password_resets
|
||||
(
|
||||
token text not null
|
||||
constraint password_resets_pk
|
||||
primary key,
|
||||
user_id uuid not null
|
||||
constraint password_resets_users_id_fk
|
||||
references users
|
||||
on update cascade on delete cascade,
|
||||
issued timestamp default CURRENT_TIMESTAMP not null
|
||||
);
|
||||
|
||||
create unique index password_resets_token_uindex
|
||||
on password_resets (token);
|
||||
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
#sidebar {
|
||||
min-width: 200px;
|
||||
max-width: 200px;
|
||||
min-height: 100vh;
|
||||
background: #2D4360;
|
||||
color: #fff;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 10px -5px 8px -7px #AAAAAA;
|
||||
}
|
||||
|
||||
.sidebar-header button{
|
||||
color: white;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a[data-toggle="collapse"] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-toggle::after {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 20px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
a, a:hover, a:focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
#sidebar .sidebar-header {
|
||||
padding: 20px;
|
||||
background: #355072;
|
||||
}
|
||||
|
||||
#sidebar ul.components {
|
||||
padding: 0 0;
|
||||
}
|
||||
|
||||
#sidebar ul p {
|
||||
color: #fff;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#sidebar ul li a {
|
||||
padding: 10px;
|
||||
font-size: 1.1em;
|
||||
display: block;
|
||||
border-bottom: 1px solid rgba(71,95,126,.30);
|
||||
}
|
||||
#sidebar ul li a:hover {
|
||||
color: #7386D5;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#sidebar ul li.active > a, a[aria-expanded="true"] {
|
||||
color: #fff;
|
||||
background: #475F7E;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#sidebar li{
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul ul a {
|
||||
font-size: 0.9em !important;
|
||||
padding-left: 30px !important;
|
||||
background: #355072;
|
||||
}
|
||||
|
||||
.components h4{
|
||||
padding-left: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
/* Welcome module */
|
||||
.welcome_logo{
|
||||
width: 200px;
|
||||
float: right;
|
||||
}
|
||||
.login{
|
||||
margin: 40px;
|
||||
}
|
||||
|
||||
.login form{
|
||||
margin: 50px;
|
||||
}
|
||||
|
||||
.login_submit{
|
||||
float: right;
|
||||
}
|
||||
|
||||
.sidebar_entry_active{
|
||||
text-decoration: underline;
|
||||
}
|
||||
.group_selection_group{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.filter{
|
||||
border: 1px solid #dee2e6;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.filter_select{
|
||||
float: unset !important;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#content{
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.profile_image{
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.col-form-label{
|
||||
}
|
||||
|
||||
[readonly]{
|
||||
border: none;
|
||||
background-color: unset !important;
|
||||
color: black;
|
||||
}
|
||||
|
||||
/*[readonly]:focus{
|
||||
outline: none !important;
|
||||
-webkit-appearance:none;
|
||||
box-shadow: none !important;
|
||||
}*/
|
||||
|
||||
label{
|
||||
color: dimgrey;
|
||||
}
|
||||
|
||||
.iconbutton{
|
||||
color: unset !important;
|
||||
background-color: unset !important;
|
||||
border: none !important;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.iconbar{
|
||||
|
||||
}
|
||||
#qualifications_edit{
|
||||
float: right;
|
||||
}
|
||||
|
||||
.search-result-overlay{
|
||||
position:absolute;
|
||||
z-index: 1;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
th.rotate {
|
||||
height: 160px;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.table-header-rotated {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th.rotate > div {
|
||||
transform:
|
||||
translate(-9px, 0px)
|
||||
rotate(-45deg);
|
||||
width: 30px;
|
||||
}
|
||||
th.rotate > div > span {
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 5px 10px;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
|
@ -0,0 +1,18 @@
|
|||
function show_error(api_error){
|
||||
alert("Fehler "+api_error.error.code+" aufgetreten: "+api_error.error.description);
|
||||
}
|
||||
|
||||
function is_ok(data){
|
||||
if(data == null){
|
||||
return true;
|
||||
}else if(typeof data !== 'object') {
|
||||
return true;
|
||||
}else{
|
||||
if('error' in data){
|
||||
show_error(data);
|
||||
return false;
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,54 @@
|
|||
$( document ).ready(function() {
|
||||
$(".delete_member_button").on("click", MemberDeleteModule.delete_button_listener);
|
||||
//Searchbar typing events:
|
||||
$("#member_delete_modal_name_confirmation").on("keyup", MemberDeleteModule.name_confirmation_typing);
|
||||
$("#member_delete_modal_name_confirmation").bind("paste", MemberDeleteModule.name_confirmation_typing);
|
||||
$("#member_delete_modal_name_confirmation").bind("cut", MemberDeleteModule.name_confirmation_typing);
|
||||
$("#member_delete_modal_delete_button").on("click", MemberDeleteModule.delete_member);
|
||||
});
|
||||
|
||||
var name = "";
|
||||
var uuid = "";
|
||||
|
||||
MemberDeleteModule = ( function() {
|
||||
var delete_button_listener = function(){
|
||||
$("#member_delete_modal_delete_button").prop("disabled", true);
|
||||
$("#member_delete_modal_member_name").html("");
|
||||
$("#member_delete_modal_name_confirmation").val("");
|
||||
$("#member_delete_modal_name_confirmation").attr("placeholder", "");
|
||||
|
||||
name = $(this).data("member-firstname")+" "+$(this).data("member-lastname");
|
||||
uuid = $(this).data("member-id");
|
||||
|
||||
$("#member_delete_modal_member_name").html(name);
|
||||
$("#member_delete_modal_name_confirmation").attr("placeholder", name);
|
||||
$("#member_delete_modal").modal();
|
||||
};
|
||||
var delete_member = function(){
|
||||
$.ajax({
|
||||
url: '/api/members/'+uuid,
|
||||
type: 'DELETE',
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
};
|
||||
var name_confirmation_typing = function(){
|
||||
if($("#member_delete_modal_name_confirmation").val() === name){
|
||||
$('#member_delete_modal_delete_button').removeClass('disabled');
|
||||
$('#member_delete_modal_delete_button').prop("disabled", false);
|
||||
}
|
||||
};
|
||||
return{
|
||||
delete_button_listener: delete_button_listener,
|
||||
name_confirmation_typing: name_confirmation_typing,
|
||||
delete_member: delete_member,
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,61 @@
|
|||
$( document ).ready(function() {
|
||||
$('body').click(function(evt){
|
||||
if(evt.target.class === "group-detailed-view-member-search-result-overlay")
|
||||
return;
|
||||
if($(evt.target).closest('.group-detailed-view-member-search-result-overlay').length)
|
||||
return;
|
||||
$(".group-detailed-view-member-search-result-overlay").hide();
|
||||
});
|
||||
|
||||
//Remove current searchbar value on click
|
||||
$("#group-detailed-view-member-searchbar").on("click", GroupMemberSearch.searchbar_onclick);
|
||||
$("#group-detailed-view-member-searchbar").on("keyup", GroupMemberSearch.searchbar_typing);
|
||||
$("#group-detailed-view-member-searchbar").bind("paste", GroupMemberSearch.searchbar_typing);
|
||||
$("#group-detailed-view-member-searchbar").bind("cut", GroupMemberSearch.searchbar_typing);
|
||||
$("#group-detailed-view-member-searchbar").on("mouseenter", function (){
|
||||
$(".group-detailed-view-member-search-result-overlay").show();
|
||||
});
|
||||
$(".group-detailed-view-member-search-result-overlay").on("focusout", function (){
|
||||
$(".group-detailed-view-member-search-result-overlay").hide();
|
||||
})
|
||||
$(".group-detailed-view-member-search-result-overlay").on("mouseleave", function (){
|
||||
$(".group-detailed-view-member-search-result-overlay").hide();
|
||||
})
|
||||
});
|
||||
|
||||
GroupMemberSearch = ( function() {
|
||||
var searchbar_onclick = function(){
|
||||
$("#group-detailed-view-member-searchbar").val("");
|
||||
$(".group-detailed-view-member-search-result-overlay").show()
|
||||
};
|
||||
var searchbar_typing = function (){
|
||||
$("#group-detailed-view-member-searchbar-type").html("");
|
||||
$(".group-detailed-view-member-search-result-overlay-list").html("");
|
||||
$(".group-detailed-view-member-search-result-overlay").show();
|
||||
|
||||
$.ajax({
|
||||
url: '/api/members/',
|
||||
type: 'GET',
|
||||
data: {
|
||||
"name": $("#group-detailed-view-member-searchbar").val()
|
||||
},
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
$.each(data.members, function(index, value){
|
||||
$(".group-detailed-view-member-search-result-overlay-list").append("<span onclick=\"GroupModule.add_member_click_listener(this)\" style=\"cursor: pointer\" data-entity-id=\""+value.entity_id+"\" data-firstname=\""+value.firstname+"\" data-lastname=\""+value.lastname+"\"><li class='list-group-item'><span class=\"badge badge-secondary\">Hinzufügen:</span> "+value.firstname+" "+value.lastname+"</li></span>")
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
};
|
||||
return {
|
||||
searchbar_onclick : searchbar_onclick,
|
||||
searchbar_typing : searchbar_typing,
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,281 @@
|
|||
var all_checked = false;
|
||||
var all_members_checked = false;
|
||||
var delete_list = [];
|
||||
var active_group_id = "";
|
||||
|
||||
$( document ).ready(function() {
|
||||
$(".new_group_button").on("click", GroupModule.create_group_button_listener);
|
||||
$(".groups_delete_button").on("click", GroupModule.delete_group_button_listener);
|
||||
$(".check_all_groups").on("click",GroupModule.check_all_button_listener);
|
||||
|
||||
$("#groups_delete_modal_confirmation").on("keyup", GroupModule.delete_group_confirmation_check);
|
||||
$("#groups_delete_modal_confirmation").bind("paste", GroupModule.delete_group_confirmation_check);
|
||||
$("#groups_delete_modal_confirmation").bind("cut", GroupModule.delete_group_confirmation_check);
|
||||
|
||||
$("#groups_delete_modal_delete_button").on("click", GroupModule.delete_groups_request);
|
||||
$(".group_list_row").on("click", GroupModule.load_group_detailed_view);
|
||||
|
||||
$(".group-detailed-view-member-search-member-to-add").on("click", GroupModule.add_member_click_listener);
|
||||
$(".group_detailed_view_check_all_members").on("click", GroupModule.check_all_members_listener);
|
||||
$(".group-detailed-view-remove-member-button").on("click", GroupModule.remove_member_from_group_listener);
|
||||
$(".group_detailed_view_submit_core_data").on("click", GroupModule.save_core_data_button_listener);
|
||||
});
|
||||
//TODO: Remove add member to group in UI if user missing permission
|
||||
GroupModule = ( function() {
|
||||
var load_group_detailed_view = function(){
|
||||
active_group_id = $(this).data("group-id");
|
||||
$(".group_detailed_view").prop("hidden", false);
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/groups/"+active_group_id,
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
clear_detailed_view();
|
||||
set_group_detailed_view_core_data(data.group.name, data.group.description, !data.caller_permissions.permission_groups_core_edit);
|
||||
if(data.caller_permissions.permission_groups_members_view){
|
||||
set_group_detailed_view_member_list(data.members)
|
||||
}else{
|
||||
$(".group_detailed_view_member_list").hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
var clear_detailed_view = function(){
|
||||
$(".group_detailed_view_member_tr").remove();
|
||||
$("#group_detailed_view_name_input").val("");
|
||||
$("#group_detailed_view_description_input").val("");
|
||||
$("#group_detailed_view_name_input").prop('readonly', false);
|
||||
$("#group_detailed_view_description_input").prop('readonly', false);
|
||||
$(".group_detailed_view_member_list").show();
|
||||
$(".group_detailed_view_submit_core_data").show();
|
||||
};
|
||||
var set_group_detailed_view_core_data = function(name, description, readonly){
|
||||
$("#group_detailed_view_name_input").val(name);
|
||||
$("#group_detailed_view_description_input").val(description);
|
||||
if(readonly){
|
||||
$("#group_detailed_view_name_input").prop('readonly', true);
|
||||
$("#group_detailed_view_description_input").prop('readonly', true);
|
||||
$(".group_detailed_view_submit_core_data").hide();
|
||||
}
|
||||
};
|
||||
var set_group_detailed_view_member_list = function(members){
|
||||
if(!members){
|
||||
return;
|
||||
}
|
||||
$(members).each(function(index, value){
|
||||
$(".group_detailed_view_tbody").append("<tr class=\"group_detailed_view_member_tr\" data-member-id=\""+value.entity_id+"\">\n" +
|
||||
" <td><input type=\"checkbox\" class=\"group_detailed_view_member_selected\" data-member-id=\""+value.entity_id+"\"></td>\n" +
|
||||
" <td>"+value.firstname+"</td>\n" +
|
||||
" <td>"+value.lastname+"</td>\n" +
|
||||
" </tr>");
|
||||
});
|
||||
};
|
||||
var create_group_button_listener = function(){
|
||||
if($.trim($('#new_group_name').val()) == ''){
|
||||
alert("Fehler: Es wurde kein Gruppenname angegeben!");
|
||||
return
|
||||
}
|
||||
|
||||
var create_group_data = {};
|
||||
var group_data = {};
|
||||
var role_permissions = [];
|
||||
|
||||
group_data.group_name = $("#new_group_name").val();
|
||||
if(!$.trim($("#new_group_description").val()) == ''){
|
||||
group_data.group_description = $("#new_group_description").val();
|
||||
}
|
||||
|
||||
$(".new_group_role_row").each(function(index, row){
|
||||
var group_role_perm = {};
|
||||
group_role_perm.role_id = $(row).data("role-id");
|
||||
group_role_perm.permission_groups_core_edit = $(row).find("input.permission_groups_core_edit").prop("checked");
|
||||
group_role_perm.permission_groups_delete = $(row).find("input.permission_groups_delete").prop("checked");
|
||||
group_role_perm.permission_groups_members_view = $(row).find("input.permission_groups_members_view").prop("checked");
|
||||
group_role_perm.permission_groups_members_edit = $(row).find("input.permission_groups_members_edit").prop("checked");
|
||||
group_role_perm.permission_groups_permissions_edit = $(row).find("input.permission_groups_permissions_edit").prop("checked");
|
||||
group_role_perm.permission_groups_permissions_view = $(row).find("input.permission_groups_permissions_view").prop("checked");
|
||||
|
||||
role_permissions.push(group_role_perm);
|
||||
});
|
||||
|
||||
create_group_data.role_permissions = role_permissions;
|
||||
create_group_data.group_data = group_data;
|
||||
|
||||
create_group_request(create_group_data);
|
||||
};
|
||||
var create_group_request = function(data){
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/groups",
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(data),
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
location.reload();//TODO: update data directly instead of reloading page
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
var delete_group_confirmation_check = function(){
|
||||
if($("#groups_delete_modal_confirmation").val() === "GRUPPEN LÖSCHEN"){
|
||||
$("#groups_delete_modal_delete_button").prop("disabled", false);
|
||||
}
|
||||
};
|
||||
var delete_group_button_listener = function(){
|
||||
delete_list = [];
|
||||
var delete_list_names = "";
|
||||
$(".group_entry_checkbox").each(function(){
|
||||
if($(this).prop('checked')) {
|
||||
delete_list.push($(this).data("group-id"));
|
||||
delete_list_names = delete_list_names + $(this).data("group-name")+", ";
|
||||
}
|
||||
})
|
||||
|
||||
if(delete_list.length == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
$("#groups_delete_modal_delete_button").prop("disabled", true);
|
||||
$("#groups_delete_modal_group_list").html("");
|
||||
$("#groups_delete_modal_confirmation").val("");
|
||||
|
||||
$("#groups_delete_modal_group_list").html(delete_list_names);
|
||||
$("#groups_delete_modal").modal();
|
||||
};
|
||||
var check_all_button_listener = function(){
|
||||
if(all_checked){
|
||||
$(".group_entry_checkbox").prop('checked', false);
|
||||
all_checked = false;
|
||||
}else {
|
||||
$(".group_entry_checkbox").prop('checked', true);
|
||||
all_checked = true;
|
||||
}
|
||||
};
|
||||
var delete_groups_request = function(){
|
||||
if(delete_list.length===0){
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
url: "/api/groups",
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(delete_list),
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
location.reload(); //TODO: delete row in table instead of reloading page
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
var add_member_click_listener = function(element){
|
||||
GroupModule.add_member_to_group(active_group_id, $(element).data("entity-id"), $(element).data("firstname"), $(element).data("lastname"));
|
||||
}
|
||||
var add_member_to_group = function(group_id, member_id, member_firstname, member_lastname){
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/api/groups/"+group_id+"/members/"+member_id,
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
$(".group_detailed_view_tbody").append("<tr class=\"group_detailed_view_member_tr\" data-member-id=\""+member_id+"\">\n" +
|
||||
" <td><input type=\"checkbox\" class=\"group_detailed_view_member_selected\" data-member-id=\""+member_id+"\"></td>\n" +
|
||||
" <td>"+member_firstname+"</td>\n" +
|
||||
" <td>"+member_lastname+"</td>\n" +
|
||||
" </tr>");
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
var check_all_members_listener = function(){
|
||||
if(all_members_checked){
|
||||
$(".group_detailed_view_member_selected").prop('checked', false);
|
||||
all_members_checked = false;
|
||||
}else {
|
||||
$(".group_detailed_view_member_selected").prop('checked', true);
|
||||
all_members_checked = true;
|
||||
}
|
||||
};
|
||||
var remove_member_from_group_listener = function(){
|
||||
$(".group_detailed_view_member_selected").each(function(){
|
||||
var member_to_delete = $(this).data("member-id");
|
||||
if($(this).prop("checked")) {
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
url: "/api/groups/"+active_group_id+"/members/"+member_to_delete,
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
$(".group_detailed_view_member_tr").each(function(){
|
||||
if($(this).data("member-id") === member_to_delete){
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
var save_core_data_button_listener = function(){
|
||||
var group = {};
|
||||
group.group_name = $("#group_detailed_view_name_input").val();
|
||||
var description = $("#group_detailed_view_description_input").val();
|
||||
if(description!=null && description!==""){
|
||||
group.group_description = description;
|
||||
}
|
||||
|
||||
console.log(group);
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/api/groups/"+active_group_id,
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(group),
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
return{
|
||||
create_group_button_listener: create_group_button_listener,
|
||||
delete_group_button_listener: delete_group_button_listener,
|
||||
check_all_button_listener: check_all_button_listener,
|
||||
delete_group_confirmation_check: delete_group_confirmation_check,
|
||||
delete_groups_request: delete_groups_request,
|
||||
load_group_detailed_view: load_group_detailed_view,
|
||||
add_member_to_group: add_member_to_group,
|
||||
add_member_click_listener: add_member_click_listener,
|
||||
check_all_members_listener: check_all_members_listener,
|
||||
remove_member_from_group_listener: remove_member_from_group_listener,
|
||||
save_core_data_button_listener: save_core_data_button_listener,
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,509 @@
|
|||
var member_id = "";
|
||||
var qualification_id = "";
|
||||
|
||||
$( document ).ready(function() {
|
||||
member_id = $("#member_entity_id").val();
|
||||
if(member_id !== null){
|
||||
$.getJSON("/api/member_management/get_qualifications?member_id=" + member_id, function (data) {
|
||||
QualificationList.load_list(data)
|
||||
});
|
||||
$(".qualification_delete").on("click", QualificationList.delete_qualification);
|
||||
$("#add_qualification_category").change(QualificationList.get_qualification_for_category);
|
||||
$("#add_qualification_submit").on("click", QualificationList.add_qualification);
|
||||
$(".drive_permission_update").on("click", DrivingPermissionModule.update);
|
||||
CommunicationModule.update_communication_types();
|
||||
$(".communication_edit_button").on("click", CommunicationModule.handle_edit_button);
|
||||
$(".communication_save_new_button").on("click", CommunicationModule.handle_save_new_button);
|
||||
$(".communication_delete_button").on("click", CommunicationModule.handle_delete_button);
|
||||
$(".communication_edit_save_button").on("click", CommunicationModule.handle_edit_save_button);
|
||||
|
||||
UnitModule.get_units();
|
||||
$("#add_operation_unit_submit").on("click", UnitModule.add_unit_submit_listener);
|
||||
$(".delete_unit_from_member_button").on("click", UnitModule.delete_unit);
|
||||
|
||||
$("#login_allowed").on("change", LoginModule.login_allowed_checkbox_listener);
|
||||
$("#login_save_button").on("click", LoginModule.login_submit);
|
||||
}
|
||||
});
|
||||
|
||||
QualificationList = ( function() {
|
||||
var list = [];
|
||||
var qualification_list = [];
|
||||
var load_list = function (data) {
|
||||
list = data;
|
||||
};
|
||||
var get_qualification_for_category = function (){
|
||||
$("#add_qualification_qualification").children().remove();
|
||||
if($("#add_qualification_category").val() === "none"){
|
||||
$("#add_qualification_qualification").attr('disabled','disabled');
|
||||
$("#add_qualification_submit").attr('disabled','disabled');
|
||||
}else {
|
||||
$.getJSON("/api/member_management/get_qualifications_for_category?category_id=" + $(this).val(), function (data) {
|
||||
if (is_ok(data)) {
|
||||
qualification_list = data;
|
||||
for (var i = 0; i < qualification_list.length; i++) {
|
||||
var option = "<option value=\"" + qualification_list[i].id + "\">" + qualification_list[i].name + "</option>";
|
||||
$("#add_qualification_qualification").append(option);
|
||||
}
|
||||
if (qualification_list.length > 0) {
|
||||
$("#add_qualification_qualification").removeAttr("disabled");
|
||||
$("#add_qualification_submit").removeAttr("disabled");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
var delete_qualification = function () {
|
||||
qualification_id = $(this).data("qualification-id");
|
||||
var category = $(this).data("category-id");
|
||||
|
||||
for(var i=0; i<list.categories.length;i++){ //Search for category
|
||||
if (list.categories[i].id === category){ //Category found
|
||||
for(var v=0;v<list.categories[i].qualifications.length;v++){
|
||||
if (list.categories[i].qualifications[v].id === qualification_id){ //Qualification found
|
||||
list.categories[i].qualifications.splice(v, 1);
|
||||
if ($(this).parent().parent().children().length === 3){
|
||||
$(this).parent().parent().remove();
|
||||
}else {
|
||||
$(this).parent().remove();
|
||||
}
|
||||
delete_qualification_save()
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
var delete_qualification_save = function(){
|
||||
console.log("Trying to save");
|
||||
$.ajax({
|
||||
url: '/api/member_management/remove_qualification?member_id='+member_id+'&qualification_id='+qualification_id,
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
|
||||
}
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
};
|
||||
var add_qualification = function(){
|
||||
var qual_id = $("#add_qualification_qualification").val();
|
||||
var qual_category = $("#add_qualification_category").val();
|
||||
|
||||
$.ajax({
|
||||
url: '/api/member_management/add_qualification?member_id='+member_id+'&qualification_id='+qual_id,
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
location.assign("/portal/mm/profile?action=edit&id="+member_id);
|
||||
}
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
};
|
||||
return{
|
||||
load_list: load_list,
|
||||
delete_qualification: delete_qualification,
|
||||
get_qualification_for_category: get_qualification_for_category,
|
||||
delete_qualification_save: delete_qualification_save,
|
||||
add_qualification: add_qualification,
|
||||
};
|
||||
})();
|
||||
|
||||
DrivingPermissionModule = ( function() {
|
||||
var update = function(){
|
||||
var permission_name = $(this).data("permission-name");
|
||||
var permission_level = $(this).data("permission-level");
|
||||
|
||||
if(permission_name === undefined || permission_level === undefined){
|
||||
return;
|
||||
}
|
||||
|
||||
switch(permission_level){
|
||||
case "none":
|
||||
update_to_license(this, permission_name);
|
||||
break;
|
||||
case "license":
|
||||
update_to_drive_permission(this,permission_name);
|
||||
break;
|
||||
case "drive_permission":
|
||||
update_to_none(this,permission_name);
|
||||
break;
|
||||
}
|
||||
};
|
||||
var update_to_license = function(element, permission_name){
|
||||
if(permission_name === undefined){
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url: '/api/member_management/add_driving_license?member_id='+member_id+'&license_name='+permission_name,
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
$(element).removeClass("btn-success").addClass("btn-warning");
|
||||
$(element).data("permission-level", "license");
|
||||
}
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
};
|
||||
var update_to_drive_permission = function(element,permission_name){
|
||||
if(permission_name === undefined){
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url: '/api/member_management/add_driving_permission?member_id='+member_id+'&license_name='+permission_name,
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
$(element).removeClass("btn-warning").addClass("btn-success");
|
||||
$(element).data("permission-level", "drive_permission");
|
||||
}
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
};
|
||||
var update_to_none = function(element,permission_name){
|
||||
if(permission_name === undefined){
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url: '/api/member_management/remove_driving_license?member_id='+member_id+'&license_name='+permission_name,
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
$(element).removeClass("btn-warning").removeClass("btn-success");
|
||||
$(element).data("permission-level", "none");
|
||||
}
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
}
|
||||
return{
|
||||
update: update,
|
||||
};
|
||||
}
|
||||
)();
|
||||
|
||||
CommunicationModule = ( function() {
|
||||
var update_communication_types = function(){
|
||||
$.getJSON("/api/member_management/get_communication_types", function (data){
|
||||
if(is_ok(data)) {
|
||||
$(".communication_types").empty().each(function () {
|
||||
var type_id = $(this).data("type-id");
|
||||
|
||||
for (var i = 0; i < data.types.length; i++) {
|
||||
var selected = "";
|
||||
if (type_id === data.types[i].type_id) {
|
||||
selected = "selected";
|
||||
}
|
||||
var option = "<option value=\"" + data.types[i].type_id + "\"" + selected + ">" + data.types[i].type_name + "</option>";
|
||||
$(this).append(option);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
var handle_edit_button = function(){
|
||||
var row = $(this).parent().parent();
|
||||
row.find("input").prop("readonly", false);
|
||||
row.find("select").prop("disabled", false);
|
||||
row.find(".communication_edit_save_button").prop("hidden", false);
|
||||
$(this).hide();
|
||||
};
|
||||
var handle_save_new_button = function(){
|
||||
var row = $(this).parent().parent();
|
||||
var type_id = row.find(".communication_types").val();
|
||||
var value = row.find(".communication_value").val();
|
||||
var description = row.find(".communication_description").val();
|
||||
|
||||
if(type_id===""||value===""){
|
||||
return;
|
||||
}
|
||||
|
||||
var com_entry = {};
|
||||
com_entry.entity_id = member_id;
|
||||
com_entry.target_id = "00000000-0000-0000-0000-000000000000";
|
||||
com_entry.type_id = type_id;
|
||||
com_entry.type_name = "";
|
||||
com_entry.value = value;
|
||||
com_entry.description = description;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/member_management/add_communication_target",
|
||||
data: JSON.stringify(com_entry),
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data){
|
||||
if(is_ok(data)) {
|
||||
row.before("<tr data-target-id=\"" + data.target_id + "\"><td><select data-type-id=\"" + com_entry.type_id + "\" disabled class=\"communication_types form-control\"></select></td><td><input type=\"text\" value=\"" + com_entry.value + "\" class=\"form-control\" readonly></td><td><input type=\"text\" value=\"" + com_entry.description + "\" class=\"form-control\" readonly></td><td><button class=\"iconbutton communication_edit_button\"><svg width=\"1.5em\" height=\"1.5em\" viewBox=\"0 0 16 16\" class=\"bi bi-pencil-square\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n" +
|
||||
" <path d=\"M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z\"/>\n" +
|
||||
" <path fill-rule=\"evenodd\" d=\"M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z\"/>\n" +
|
||||
" </svg></button>\n" +
|
||||
" <button class=\"iconbutton communication_save_button\" hidden><svg width=\"1.5em\" height=\"1.5em\" viewBox=\"0 0 16 16\" class=\"bi bi-check-square\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n" +
|
||||
" <path fill-rule=\"evenodd\" d=\"M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z\"/>\n" +
|
||||
" <path fill-rule=\"evenodd\" d=\"M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.236.236 0 0 1 .02-.022z\"/>\n" +
|
||||
" </svg></button>\n" +
|
||||
" <button class=\"iconbutton communication_delete_button\"><svg width=\"1.5em\" height=\"1.5em\" viewBox=\"0 0 16 16\" class=\"bi bi-trash\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n" +
|
||||
" <path d=\"M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z\"></path>\n" +
|
||||
" <path fill-rule=\"evenodd\" d=\"M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z\"></path>\n" +
|
||||
" </svg></button>\n" +
|
||||
" </td></tr>");
|
||||
|
||||
//Apply button handlers to new buttons
|
||||
$(".communication_edit_button").on("click", CommunicationModule.handle_edit_button);
|
||||
$(".communication_save_new_button").on("click", CommunicationModule.handle_save_new_button);
|
||||
$(".communication_delete_button").on("click", CommunicationModule.handle_delete_button);
|
||||
//Clear input fields
|
||||
row.find(".communication_types").val("");
|
||||
row.find(".communication_value").val("");
|
||||
row.find(".communication_description").val("");
|
||||
update_communication_types()
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
var handle_delete_button = function(){
|
||||
var row = $(this).parent().parent();
|
||||
var target_id = row.data("target-id");
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/api/member_management/remove_communication_target?target_id="+target_id,
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
row.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
var handle_edit_save_button = function(){
|
||||
var row = $(this).parent().parent();
|
||||
|
||||
var com_entry = {};
|
||||
com_entry.com_type = row.find(".communication_types").val();
|
||||
com_entry.value = row.find(".communication_value").val();
|
||||
com_entry.description = row.find(".communication_description").val();
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/api/communication_targets/"+row.data("target-id"),
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(com_entry),
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
row.find("input").prop("readonly", true);
|
||||
row.find("select").prop("disabled", true);
|
||||
row.find(".communication_edit_save_button").prop("hidden", true);
|
||||
row.find(".communication_edit_button").show();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
return{
|
||||
update_communication_types: update_communication_types,
|
||||
handle_edit_button: handle_edit_button,
|
||||
handle_save_new_button: handle_save_new_button,
|
||||
handle_delete_button: handle_delete_button,
|
||||
handle_edit_save_button: handle_edit_save_button,
|
||||
}
|
||||
})();
|
||||
|
||||
UnitModule = (
|
||||
function(){
|
||||
var get_units = function(){
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/api/units",
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
var count = 0;
|
||||
$(data).each(function(){
|
||||
count++;
|
||||
$("#add_operation_unit_unit").append("<option value=\""+this.unit_id+"\" data-unit_name=\""+this.name+"\">"+this.name+"</option>");
|
||||
});
|
||||
|
||||
if(count>0 && $("#add_operation_unit_unit").val() != null){
|
||||
$("#add_operation_unit_submit").prop("disabled", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
var add_unit_submit_listener = function(){
|
||||
var unit_id = $("#add_operation_unit_unit").val();
|
||||
var unit_name = $("#add_operation_unit_unit option:selected").data("unit_name");
|
||||
var crew = $("#add_operation_unit_crew").val();
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/api/units/"+unit_id+"/members/"+member_id+"?crew="+crew,
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
$("#units_tbody").append("<tr>\n" +
|
||||
" <td>"+unit_name+"</td>\n" +
|
||||
" <td>"+crew+". Besetzung</td>\n" +
|
||||
" <td><button type=\"button\" class=\"iconbutton delete_unit_from_member_button\" data-unit-id=\""+unit_id+"\">\n" +
|
||||
" <svg width=\"1.5em\" height=\"1.5em\" fill=\"currentColor\">\n" +
|
||||
" <use xlink:href=\"/img/bootstrap-icons.svg#trash\"/>\n" +
|
||||
" </svg>\n" +
|
||||
" </button></td>\n" +
|
||||
" </tr>");
|
||||
$(".delete_unit_from_member_button").on("click", UnitModule.delete_unit);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
var delete_unit = function(){
|
||||
var unit_id = $(this).data("unit-id");
|
||||
var row = $(this).parent().parent();
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
url: "/api/units/"+unit_id+"/members/"+member_id,
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
row.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return{
|
||||
get_units: get_units,
|
||||
add_unit_submit_listener: add_unit_submit_listener,
|
||||
delete_unit: delete_unit,
|
||||
}
|
||||
}
|
||||
)();
|
||||
|
||||
LoginModule = (function(){
|
||||
var login_allowed_checkbox_listener = function(){
|
||||
var ro = $("#login_email").prop('readonly');
|
||||
if(ro){
|
||||
$("#login_email").prop('readonly', false);
|
||||
}else{
|
||||
$("#login_email").prop('readonly', true);
|
||||
}
|
||||
};
|
||||
var login_submit = function(){
|
||||
var login_allowed = $("#login_allowed").prop('checked');
|
||||
|
||||
if(login_allowed){
|
||||
var email = $("#login_email").val();
|
||||
if(email.trim() === ""){
|
||||
alert("Bitte eine Email-Adresse für den Login angeben!");
|
||||
}else{
|
||||
var user_id = $("#login_save_button").data("user-id");
|
||||
if(user_id === ""){//New entry
|
||||
var user = $();
|
||||
user.email = email.trim();
|
||||
user.member_id = member_id;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/users/",
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(user),
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
$("#login_save_button").data("user-id", data.user_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}else{ //Update existing entry
|
||||
var user = $();
|
||||
user.user_id = user_id;
|
||||
user.email = email.trim();
|
||||
user.member_id = member_id;
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/api/users/"+user_id,
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(user),
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//Delete user for member
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
url: "/api/users/"+$("#login_save_button").data("user-id"),
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
$("#login_email").val("");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return{
|
||||
login_allowed_checkbox_listener: login_allowed_checkbox_listener,
|
||||
login_submit: login_submit,
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,113 @@
|
|||
$( document ).ready(function() {
|
||||
$('body').click(function(evt){
|
||||
if(evt.target.class === "search-result-overlay")
|
||||
return;
|
||||
if($(evt.target).closest('.search-result-overlay').length)
|
||||
return;
|
||||
$(".search-result-overlay").hide();
|
||||
});
|
||||
|
||||
Searchbar.set_active_entity();
|
||||
//Remove current searchbar value on click
|
||||
$("#searchbar").on("click", Searchbar.searchbar_onclick);
|
||||
$("#searchbar").on("keyup", Searchbar.searchbar_typing);
|
||||
$("#searchbar").bind("paste", Searchbar.searchbar_typing);
|
||||
$("#searchbar").bind("cut", Searchbar.sWearchbar_typing);
|
||||
$("#searchbar").on("mouseenter", function (){
|
||||
$(".search-result-overlay").show();
|
||||
});
|
||||
$(".search-result-overlay").on("focusout", function (){
|
||||
$(".search-result-overlay").hide();
|
||||
})
|
||||
$(".search-result-overlay").on("mouseleave", function (){
|
||||
$(".search-result-overlay").hide();
|
||||
})
|
||||
});
|
||||
|
||||
Searchbar = ( function() {
|
||||
var set_active_entity = function(){
|
||||
var entity_id = $("#searchbar").data("active-entity");
|
||||
var type = $("#searchbar").data("entity-type");
|
||||
|
||||
if(type === "member" && entity_id !== ""){
|
||||
$("#searchbar-type").html("Mitglied: ");
|
||||
|
||||
$.ajax({
|
||||
url: '/api/members/'+entity_id,
|
||||
type: 'GET',
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
$("#searchbar").val(data.firstname+" "+data.lastname);
|
||||
}
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
}else{
|
||||
console.log("Unknown search entity type: "+type);
|
||||
|
||||
}
|
||||
};
|
||||
var searchbar_onclick = function(){
|
||||
$("#searchbar").val("");
|
||||
$(".search-result-overlay").show()
|
||||
};
|
||||
var searchbar_typing = function (){
|
||||
var type = $("#searchbar").data("entity-type");
|
||||
$("#searchbar-type").html("");
|
||||
$(".search-result-overlay-list").html("");
|
||||
$(".search-result-overlay").show();
|
||||
|
||||
if(type === "member"){
|
||||
$.ajax({
|
||||
url: '/api/members/',
|
||||
type: 'GET',
|
||||
data: {
|
||||
"name": $("#searchbar").val()
|
||||
},
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
$.each(data.members, function(index, value){
|
||||
$(".search-result-overlay-list").append("<a href=\"/portal/mm/profile?action=view&id="+value.entity_id+"\"><li class='list-group-item'><span class=\"badge badge-secondary\">Mitglied</span> "+value.firstname+" "+value.lastname+"</li></a>")
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
}else{
|
||||
$.ajax({
|
||||
url: '/api/members/',
|
||||
type: 'GET',
|
||||
data: {
|
||||
"name": $("#searchbar").val()
|
||||
},
|
||||
contentType: 'application/json',
|
||||
success: function(data) {
|
||||
if(is_ok(data)) {
|
||||
$.each(data.members, function(index, value){
|
||||
$(".search-result-overlay-list").append("<a href=\"/portal/mm/profile?action=view&id="+value.entity_id+"\"><li class='list-group-item'><span class=\"badge badge-secondary\">Mitglied</span> "+value.firstname+" "+value.lastname+"</li></a>")
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
timeout: 3000,
|
||||
error: function() {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
return {
|
||||
set_active_entity : set_active_entity,
|
||||
searchbar_onclick : searchbar_onclick,
|
||||
searchbar_typing : searchbar_typing,
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,7 @@
|
|||
Jemand hat das Zurücksetzen des Passworts auf {{frontpage}} für die Email
|
||||
{{email}}
|
||||
angefordert.
|
||||
Falls dies nicht beabsichtigt war, ignoriere einfach diese E-Mail. Dein altes Passwort bleibt wirksam.
|
||||
|
||||
Um dein Passwort zurückzusetzen, besuche folgende Adresse:
|
||||
{{reset_url}}
|
|
@ -0,0 +1 @@
|
|||
<div class="alert {{alert.class}}" role="alert">{{alert.content}}</div>
|
|
@ -0,0 +1,21 @@
|
|||
<div class="modal fade" id="groups_delete_modal" tabindex="-1" role="dialog" aria-labelledby="groups_delete_modal_label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="groups_delete_modal_label">Gruppen löschen</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<b>Sollen die Gruppen <span id="groups_delete_modal_group_list"></span> UNWIDERRUFLICH gelöscht werden?</b>
|
||||
<p>Zur Bestätigung GRUPPEN LÖSCHEN eintragen:</p>
|
||||
<input type="text" class="form-control" id="groups_delete_modal_confirmation" placeholder="GRUPPEN LÖSCHEN">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" id="groups_delete_modal_abort_button" data-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" class="btn btn-danger" id="groups_delete_modal_delete_button">Gruppen löschen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,21 @@
|
|||
<div class="modal fade" id="member_delete_modal" tabindex="-1" role="dialog" aria-labelledby="member_delete_modal_label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="member_delete_modal_label">Mitglied löschen</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<b>Soll das Mitglied <span id="member_delete_modal_member_name"></span> UNWIDERRUFLICH gelöscht werden?</b>
|
||||
<p>Zur Bestätigung den vollständigen Namen eintragen:</p>
|
||||
<input type="text" class="form-control" id="member_delete_modal_name_confirmation">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" id="member_delete_modal_abort_button" data-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" class="btn btn-danger" id="member_delete_modal_delete_button">Mitglied löschen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,10 @@
|
|||
<script src="/js/lib/jquery-3.5.1.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||||
<script src="/js/global.js"></script>
|
||||
<script src="/js/searchbar.js"></script>
|
||||
{{#each footer.scripts}}
|
||||
<script src="{{path}}"></script>
|
||||
{{/each}}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{header.html_language}}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{header.site_title}}</title>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
{{#each header.stylesheets}}
|
||||
<link rel="stylesheet" href="{{path}}">
|
||||
{{/each}}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
|
@ -0,0 +1,15 @@
|
|||
{{> header }}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="wrapper">
|
||||
{{> sidebar }}
|
||||
<div id="content">
|
||||
{{> searchbar}}
|
||||
<hr>
|
||||
{{#if alert}}
|
||||
{{> alert}}
|
||||
{{/if}}
|
||||
<h1>Willkommen beim ERMS!</h1>
|
||||
</div>
|
||||
</div>
|
||||
{{> footer }}
|
|
@ -0,0 +1,79 @@
|
|||
{{> header }}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="wrapper">
|
||||
{{> sidebar }}
|
||||
<div id="content">
|
||||
{{> searchbar}}
|
||||
<hr>
|
||||
{{#if alert}}
|
||||
{{> alert}}
|
||||
{{/if}}
|
||||
<h1>Neues Mitglied anlegen</h1>
|
||||
<div class="col">
|
||||
<form action="#" method="post">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group row">
|
||||
<label for="salutation" class="col-sm-3 col-form-label">Anrede*</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="salutation" name="salutation" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="academic_titles" class="col-sm-3 col-form-label">akadem. Titel</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="academic_titles" name="academic_titles">
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group row">
|
||||
<label for="firstname" class="col-sm-3 col-form-label">Vorname*</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="firstname" name="firstname" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="lastname" class="col-sm-3 col-form-label">Nachname*</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="lastname" name="lastname" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="sex" class="col-sm-3 col-form-label">Geschlecht*</label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control" id="sex" name="sex" required>
|
||||
<option value="0"></option>
|
||||
<option value="1">männlich</option>
|
||||
<option value="2">weiblich</option>
|
||||
<option value="9">anderes</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="form-group row">
|
||||
<label for="personnel_number" class="col-sm-3 col-form-label">Personalnummer</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="personnel_number" name="personnel_number">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="entrance_date" class="col-sm-3 col-form-label">Eintrittsdatum</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="date" class="form-control" id="entrance_date" name="entrance_date">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p>* Pflichtfelder</p>
|
||||
<div class="col text-center">
|
||||
<button type="submit" class="btn btn-success">Mitglied anlegen</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{> footer }}
|
|
@ -0,0 +1,165 @@
|
|||
{{> header }}
|
||||
{{> delete-groups-modal}}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="wrapper">
|
||||
{{> sidebar }}
|
||||
<div id="content">
|
||||
{{> searchbar}}
|
||||
<hr>
|
||||
{{#if alert}}
|
||||
{{> alert}}
|
||||
{{/if}}
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{{#if_in_list ../../caller_permissions "modules.member_management.groups.view"}}
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Gruppen</div>
|
||||
<div class="card-body" id="groups">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead class="thead">
|
||||
<tr>
|
||||
<th><button class="iconbutton check_all_groups"><svg width="1.25em" height="1.25em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#check-all"/>
|
||||
</svg></button></th>
|
||||
<th>Name</th>
|
||||
<th>Beschreibung</th>
|
||||
<th># Mitglieder</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each groups}}
|
||||
<tr data-group-id="{{group_id}}" {{#if (or permission_edit_core permission_view_members)}}style="cursor: pointer;"{{/if}} class="{{#if (or permission_edit_core permission_view_members)}}group_list_row{{/if}} {{#if (not permission_delete)}}text-muted{{/if}}">
|
||||
<td>{{#if (not permission_delete)}}<input type="checkbox" disabled>{{else}}<input type="checkbox" class="group_entry_checkbox" data-group-id="{{group_id}}" data-group-name="{{name}}">{{/if}}</td>
|
||||
<td>{{name}}</td>
|
||||
<td>{{description}}</td>
|
||||
<td>{{members_count}}</td>
|
||||
</tr></span>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
<span><button class="iconbutton check_all_groups" data-check-all-selector=".check_all_groups"><svg width="1.25em" height="1.25em" fill="currentColor" style="margin-left: 12px;margin-right: 12px;"><use xlink:href="/img/bootstrap-icons.svg#check-all"/></svg></button><!-- <button style="margin-right: 12px;" type="button" class="btn btn-secondary btn-sm">Duplizieren</button>-->{{#if_in_list ../../caller_permissions "modules.member_management.groups.delete"}}<button type="button" class="btn btn-danger btn-sm groups_delete_button">Löschen</button>{{/if_in_list}}</span>
|
||||
</div>
|
||||
</div>{{/if_in_list}}
|
||||
{{#if_in_list caller_permissions "modules.member_management.groups.create"}}<div class="card bg-light mb-3">
|
||||
<div class="card-header">Neue Gruppe anlegen</div>
|
||||
<div class="card-body" id="groups">
|
||||
<form>
|
||||
<div class="form-group row">
|
||||
<label for="new_group_name" class="col-sm-2 col-form-label">Name</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="new_group_name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="new_group_description" class="col-sm-2 col-form-label">Beschreibung</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="new_group_description">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<b>Zugriff beschränken</b>
|
||||
<p>Hinweis: Einer Rolle kann nur ein Recht für die neue Gruppe gegeben werden, wenn die Rolle das Recht bereits besitzt. Beispiel: Wenn die Rolle "admin" das Recht "Gruppen löschen" nicht besitzt, kann ihr nicht das Recht gegeben werden, die neue Gruppe zu löschen.</p>
|
||||
<div>
|
||||
<table class="table table-header-rotated">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Rolle</th>
|
||||
<th scope="col" class="rotate"><div><span>Name/Beschreibung ändern</span></div></th>
|
||||
<th scope="col" class="rotate"><div><span>löschen</span></div></th>
|
||||
<th scope="col" class="rotate"><div><span>Gruppenmitglieder sehen</span></div></th>
|
||||
<th scope="col" class="rotate"><div><span>Gruppenmitglieder ändern</span></div></th>
|
||||
<th scope="col" class="rotate"><div><span>Zugriffsbeschränkungen sehen</span></div></th>
|
||||
<th scope="col" class="rotate"><div><span>Zugriffsbeschränkungen ändern</span></div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each roles}}
|
||||
<tr data-role-id="{{id}}" class="new_group_role_row">
|
||||
<td title="{{description}}">{{id}}</td>
|
||||
<td><input type="checkbox" class="permission_groups_core_edit"></td>
|
||||
<td><input type="checkbox" class="permission_groups_delete"></td>
|
||||
<td><input type="checkbox" class="permission_groups_members_view"></td>
|
||||
<td><input type="checkbox" class="permission_groups_members_edit"></td>
|
||||
<td><input type="checkbox" class="permission_groups_permissions_edit"></td>
|
||||
<td><input type="checkbox" class="permission_groups_permissions_view"></td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary new_group_button" style="float: right">Gruppe Hinzufügen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>{{/if_in_list}}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card bg-light mb-3 group_detailed_view" hidden>
|
||||
<div class="card-header">Gruppe <span class="group_detailed_view_name"></span></div>
|
||||
<div class="card-body" id="groups">
|
||||
<div class="group_detailed_view_core_data">
|
||||
<div class="form-group row">
|
||||
<label for="group_detailed_view_name_input" class="col-sm-2 col-form-label">Name</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="group_detailed_view_name_input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="group_detailed_view_description_input" class="col-sm-2 col-form-label">Beschreibung</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="group_detailed_view_description_input">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="group_detailed_view_submit_core_data btn btn-primary" style="float: right; margin-bottom:15px;">Änderungen Speichern</button>
|
||||
</div>
|
||||
<div class="group_detailed_view_member_list">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><button class="iconbutton group_detailed_view_check_all_members"><svg width="1.25em" height="1.25em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#check-all"/>
|
||||
</svg></button></th>
|
||||
<th>Vorname</th>
|
||||
<th>Nachname</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="group_detailed_view_tbody"></tbody>
|
||||
</table>
|
||||
{{#if_in_list caller_permissions "modules.member_management.groups.members.edit"}}
|
||||
<div>
|
||||
<button class="iconbutton group_detailed_view_check_all_members"><svg width="1.25em" height="1.25em" fill="currentColor" style="margin-left: 12px;margin-right: 12px;"><use xlink:href="/img/bootstrap-icons.svg#check-all"/></svg></button><button type="button" class="btn btn-warning btn-sm group-detailed-view-remove-member-button">Entfernen</button>
|
||||
</div><br>
|
||||
<div class="group_detailed_view_add_member">
|
||||
<div class="form-group row">
|
||||
<label for="group-detailed-view-member-searchbar" class="col-sm-2 col-form-label">Mitglied hinzufügen:</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="group-detailed-view-member-searchbar">
|
||||
<span class="input-group-append">
|
||||
<span class="btn btn-outline-secondary" type="button">
|
||||
<svg width="16" height="16" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#search"/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="group-detailed-view-member-search-result-overlay" style="display: none">
|
||||
<ul class="list-group group-detailed-view-member-search-result-overlay-list">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>{{/if_in_list}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{> footer }}
|
|
@ -0,0 +1,281 @@
|
|||
{{> header }}
|
||||
{{> delete-member-modal}}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="wrapper">
|
||||
{{> sidebar }}
|
||||
<div id="content">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col-10">
|
||||
<h1>Mein Profil</h1>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
{{#if member}}<div class="iconbar">
|
||||
{{#if readonly}}<a href="/portal/personal_profile?action=edit"><svg width="2em" height="2em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#pencil-square"/>
|
||||
</svg></a>{{else}}
|
||||
<button type="submit" class="iconbutton" form="member_management_profile_form">
|
||||
<svg width="2.1em" height="2.1em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#check2-square"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="/portal/personal_profile?action=view"><svg width="2em" height="2em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#x-square"/>
|
||||
</svg></a>
|
||||
{{/if}}
|
||||
</div>{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="col">
|
||||
<form id="member_management_profile_form" action="/portal/personal_profile?action=view" method="post">
|
||||
<input type="hidden" id="member_entity_id" value="{{member.entity_id}}" name="entity_id">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div class="form-group row">
|
||||
<label for="salutation" class="col-sm-3 col-form-label">Anrede</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="salutation" name="salutation" readonly value="{{member.salutation}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="academic_titles" class="col-sm-3 col-form-label">akadem. Titel</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="academic_titles" name="academic_titles" readonly value="{{member.academic_titles}}">
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group row">
|
||||
<label for="firstname" class="col-sm-3 col-form-label">Vorname</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="firstname" name="firstname" readonly value="{{member.firstname}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="lastname" class="col-sm-3 col-form-label">Nachname</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="lastname" name="lastname" readonly value="{{member.lastname}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="sex" class="col-sm-3 col-form-label">Geschlecht</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="hidden" id="sex" name="sex" value="{{member.sex}}">
|
||||
<select class="form-control" id="sex" name="sex" disabled readonly>
|
||||
<option value="0" {{#if (eq member.sex 0)}}selected{{/if}}></option>
|
||||
<option value="1" {{#if (eq member.sex 1)}}selected{{/if}}>männlich</option>
|
||||
<option value="2" {{#if (eq member.sex 2)}}selected{{/if}}>weiblich</option>
|
||||
<option value="9" {{#if (eq member.sex 9)}}selected{{/if}}>anderes</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="date_of_birth" class="col-sm-3 col-form-label">Geburtsdatum</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="date" class="form-control" id="date_of_birth" name="date_of_birth" {{#if readonly}}readonly{{/if}} value="{{member.date_of_birth}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="age" class="col-sm-3 col-form-label">Alter (berechnet)</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" class="form-control" id="age" {{#if readonly}}readonly{{/if}} value="{{member_age}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="place_of_birth" class="col-sm-3 col-form-label">Geburtsort</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="place_of_birth" name="place_of_birth" {{#if readonly}}readonly{{/if}} value="{{member.place_of_birth}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="birth_name" class="col-sm-3 col-form-label">Geburtsname</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="birth_name" name="birth_name" {{#if readonly}}readonly{{/if}} value="{{member.birth_name}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="nationality" class="col-sm-3 col-form-label">Staatsangehörigkeit</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="nationality" name="nationality" {{#if readonly}}readonly{{/if}} value="{{member.nationality}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<div class="form-group row">
|
||||
<label for="personnel_number" class="col-sm-3 col-form-label">Personalnummer</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="personnel_number" name="personnel_number" readonly value="{{member.personnel_number}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="entrance_date" class="col-sm-3 col-form-label">Eintrittsdatum</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="date" class="form-control" id="entrance_date" name="entrance_date" readonly value="{{member.entrance_date}}">
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<input type="hidden" value="{{address.id}}" name="address_id">
|
||||
<div class="form-group row">
|
||||
<label for="street" class="col-sm-3 col-form-label">Straße</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="street" name="address_street" {{#if readonly}}readonly{{/if}} value="{{address.street}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="street_number" class="col-sm-3 col-form-label">Hausnummer</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="street_number" name="address_number" {{#if readonly}}readonly{{/if}} value="{{address.number}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="zipcode" class="col-sm-3 col-form-label">Postleitzahl</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" class="form-control" id="zipcode" name="address_zipcode" {{#if readonly}}readonly{{/if}} value="{{address.zipcode}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="city" class="col-sm-3 col-form-label">Ort</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="city" name="address_city" {{#if readonly}}readonly{{/if}} value="{{address.city}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="country" class="col-sm-3 col-form-label">Land</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="country" name="address_country" {{#if readonly}}readonly{{/if}} value="{{address.country}}">
|
||||
</div>
|
||||
</div>
|
||||
<hr>{{#if_in_list caller_permissions "modules.member_management.profile.iban_bic.view"}}
|
||||
<div class="form-group row">
|
||||
<label for="iban" class="col-sm-3 col-form-label">IBAN:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="iban" name="iban" {{#if_in_list caller_permissions "modules.member_management.profile.iban_bic.edit"}}{{#if ../readonly}}readonly{{/if}}{{else}}readonly{{/if_in_list}} value="{{member.iban}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="bic" class="col-sm-3 col-form-label">BIC:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="bic" name="bic" {{#if_in_list caller_permissions "modules.member_management.profile.iban_bic.edit"}}{{#if ../readonly}}readonly{{/if}}{{else}}readonly{{/if_in_list}} value="{{member.bic}}">
|
||||
</div>
|
||||
</div>
|
||||
<hr>{{/if_in_list}}
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<!-- <img class="profile_image img-fluid" src="">-->
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Qualifikationen </div>
|
||||
<div class="card-body" id="qualification_categories">
|
||||
{{#each qualification_categories}}
|
||||
{{#if visible}}
|
||||
<span data-category-id="{{id}}"><b>{{name}}: </b>{{#each qualifications}}<span class="badge badge-secondary" data-type="qualification">{{name}}
|
||||
</span>
|
||||
{{/each}}<br></span>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Kommunikation</div>
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Typ</th>
|
||||
<th scope="col">Wert</th>
|
||||
<th scope="col">Beschreibung</th>
|
||||
{{#if (not ../readonly)}}<th scope="col">Aktionen</th>{{/if}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each communication_targets}}
|
||||
<tr data-target-id="{{target_id}}">
|
||||
{{#if ../readonly}}
|
||||
<td><input type="text" value="{{type_name}}" readonly size="8" readonly></td>
|
||||
{{else}}
|
||||
<td><select data-type-id="{{type_id}}" disabled class="communication_types form-control"></select></td>
|
||||
{{/if}}
|
||||
<td><input type="text" value="{{value}}" class="form-control communication_value" readonly></td>
|
||||
<td><input type="text" value="{{description}}" class="form-control communication_description" readonly></td>
|
||||
{{#if (not ../readonly)}}<td><button class="iconbutton communication_edit_button"><svg width="1.5em" height="1.5em" viewBox="0 0 16 16" class="bi bi-pencil-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
|
||||
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
|
||||
</svg></button>
|
||||
<button class="iconbutton communication_edit_save_button" hidden><svg width="1.5em" height="1.5em" viewBox="0 0 16 16" class="bi bi-check-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
<path fill-rule="evenodd" d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.236.236 0 0 1 .02-.022z"/>
|
||||
</svg></button>
|
||||
<button class="iconbutton communication_delete_button"><svg width="1.5em" height="1.5em" viewBox="0 0 16 16" class="bi bi-trash" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"></path>
|
||||
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"></path>
|
||||
</svg></button>
|
||||
</td>{{/if}}
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{#if (not ../readonly)}}
|
||||
<tr>
|
||||
<td><select class="communication_types form-control"></select></td>
|
||||
<td><input type="text" class="form-control communication_value"></td>
|
||||
<td><input type="text" class="form-control communication_description"></td>
|
||||
<td><button class="iconbutton communication_save_new_button"><svg width="1.5em" height="1.5em" viewBox="0 0 16 16" class="bi bi-check-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
<path fill-rule="evenodd" d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.236.236 0 0 1 .02-.022z"/>
|
||||
</svg></button></td>
|
||||
</tr>{{/if}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Fahrberechtigungen & Führerscheine</div>
|
||||
<div class="card-body">
|
||||
{{#each licenses}}
|
||||
<button type="button" data-permission-name="{{category_name}}" data-permission-level="{{#if drive_permission}}drive_permission{{else}}{{#if license}}license{{else}}none{{/if}}{{/if}}" class="btn {{#if drive_permission}}btn-success{{else}}{{#if license}}btn-warning{{/if}}{{/if}} drive_permission_update" data-toggle="tooltip" data-placement="bottom" title="{{category_description}}">{{category_name}}</button>
|
||||
{{/each}}
|
||||
|
||||
<hr>
|
||||
<button type="button" class="btn btn-success">Führerschein + Fahrerlaubnis vorhanden</button>
|
||||
<button type="button" class="btn btn-warning">Führerschein vorhanden, keine Fahrerlaubnis</button>
|
||||
</div>
|
||||
</div>{{#if_in_list ../../caller_permissions "modules.member_management.profile.units.view"}}
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Einsatzeinheiten</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Einheit</th>
|
||||
<th>Besetzung</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="units_tbody">{{#each units}}
|
||||
<tr>
|
||||
<td>{{name}}</td>
|
||||
<td>{{crew}}. Besetzung</td>
|
||||
<td></td>
|
||||
</tr>{{/each}}
|
||||
</tbody>
|
||||
</table></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>{{/if_in_list}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{> footer }}
|
|
@ -0,0 +1,335 @@
|
|||
{{> header }}
|
||||
{{> delete-member-modal}}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="wrapper">
|
||||
{{> sidebar }}
|
||||
<div id="content">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col-10">
|
||||
{{> searchbar active_entity=member.entity_id entity_type="member"}}
|
||||
</div>
|
||||
<div class="col-2">
|
||||
{{#if member}}<div class="iconbar">
|
||||
{{#if readonly}}<a href="/portal/mm/profile?action=edit&id={{member.entity_id}}"><svg width="2em" height="2em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#pencil-square"/>
|
||||
</svg></a>{{else}}
|
||||
<button type="submit" class="iconbutton" form="member_management_profile_form">
|
||||
<svg width="2.1em" height="2.1em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#check2-square"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="/portal/mm/profile?action=view&id={{member.entity_id}}"><svg width="2em" height="2em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#x-square"/>
|
||||
</svg></a>
|
||||
{{/if}}
|
||||
<button class="iconbutton delete_member_button" data-member-id="{{member.entity_id}}" data-member-firstname="{{member.firstname}}" data-member-lastname="{{member.lastname}}">
|
||||
<svg width="2em" height="2em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#trash"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>{{#if member}}
|
||||
<div class="col">
|
||||
<form id="member_management_profile_form" action="/portal/mm/profile?action=view&id={{member.entity_id}}" method="post">
|
||||
<input type="hidden" id="member_entity_id" value="{{member.entity_id}}" name="entity_id">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div class="form-group row">
|
||||
<label for="salutation" class="col-sm-3 col-form-label">Anrede</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="salutation" name="salutation" {{#if readonly}}readonly{{/if}} value="{{member.salutation}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="academic_titles" class="col-sm-3 col-form-label">akadem. Titel</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="academic_titles" name="academic_titles" {{#if readonly}}readonly{{/if}} value="{{member.academic_titles}}">
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group row">
|
||||
<label for="firstname" class="col-sm-3 col-form-label">Vorname</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="firstname" name="firstname" {{#if readonly}}readonly{{/if}} value="{{member.firstname}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="lastname" class="col-sm-3 col-form-label">Nachname</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="lastname" name="lastname" {{#if readonly}}readonly{{/if}} value="{{member.lastname}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="sex" class="col-sm-3 col-form-label">Geschlecht</label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control" id="sex" name="sex" {{#if readonly}}readonly disabled{{/if}}>
|
||||
<option value="0" {{#if (eq member.sex 0)}}selected{{/if}}></option>
|
||||
<option value="1" {{#if (eq member.sex 1)}}selected{{/if}}>männlich</option>
|
||||
<option value="2" {{#if (eq member.sex 2)}}selected{{/if}}>weiblich</option>
|
||||
<option value="9" {{#if (eq member.sex 9)}}selected{{/if}}>anderes</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="date_of_birth" class="col-sm-3 col-form-label">Geburtsdatum</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="date" class="form-control" id="date_of_birth" name="date_of_birth" {{#if readonly}}readonly{{/if}} value="{{member.date_of_birth}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="age" class="col-sm-3 col-form-label">Alter (berechnet)</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" class="form-control" id="age" {{#if readonly}}readonly{{/if}} value="{{member_age}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="place_of_birth" class="col-sm-3 col-form-label">Geburtsort</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="place_of_birth" name="place_of_birth" {{#if readonly}}readonly{{/if}} value="{{member.place_of_birth}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="birth_name" class="col-sm-3 col-form-label">Geburtsname</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="birth_name" name="birth_name" {{#if readonly}}readonly{{/if}} value="{{member.birth_name}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="nationality" class="col-sm-3 col-form-label">Staatsangehörigkeit</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="nationality" name="nationality" {{#if readonly}}readonly{{/if}} value="{{member.nationality}}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<div class="form-group row">
|
||||
<label for="personnel_number" class="col-sm-3 col-form-label">Personalnummer</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="personnel_number" name="personnel_number" {{#if readonly}}readonly{{/if}} value="{{member.personnel_number}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="entrance_date" class="col-sm-3 col-form-label">Eintrittsdatum</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="date" class="form-control" id="entrance_date" name="entrance_date" {{#if readonly}}readonly{{/if}} value="{{member.entrance_date}}">
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<input type="hidden" value="{{address.id}}" name="address_id">
|
||||
<div class="form-group row">
|
||||
<label for="street" class="col-sm-3 col-form-label">Straße</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="street" name="address_street" {{#if readonly}}readonly{{/if}} value="{{address.street}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="street_number" class="col-sm-3 col-form-label">Hausnummer</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="street_number" name="address_number" {{#if readonly}}readonly{{/if}} value="{{address.number}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="zipcode" class="col-sm-3 col-form-label">Postleitzahl</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="number" class="form-control" id="zipcode" name="address_zipcode" {{#if readonly}}readonly{{/if}} value="{{address.zipcode}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="city" class="col-sm-3 col-form-label">Ort</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="city" name="address_city" {{#if readonly}}readonly{{/if}} value="{{address.city}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="country" class="col-sm-3 col-form-label">Land</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="country" name="address_country" {{#if readonly}}readonly{{/if}} value="{{address.country}}">
|
||||
</div>
|
||||
</div>
|
||||
<hr>{{#if_in_list caller_permissions "modules.member_management.profile.iban_bic.view"}}
|
||||
<div class="form-group row">
|
||||
<label for="iban" class="col-sm-3 col-form-label">IBAN:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="iban" name="iban" {{#if_in_list caller_permissions "modules.member_management.profile.iban_bic.edit"}}{{#if ../readonly}}readonly{{/if}}{{else}}readonly{{/if_in_list}} value="{{member.iban}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="bic" class="col-sm-3 col-form-label">BIC:</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="bic" name="bic" {{#if_in_list caller_permissions "modules.member_management.profile.iban_bic.edit"}}{{#if ../readonly}}readonly{{/if}}{{else}}readonly{{/if_in_list}} value="{{member.bic}}">
|
||||
</div>
|
||||
</div>
|
||||
<hr>{{/if_in_list}}
|
||||
</div>
|
||||
<div class="col-lg-2">
|
||||
<!-- <img class="profile_image img-fluid" src="">-->
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Qualifikationen </div>
|
||||
<div class="card-body" id="qualification_categories">
|
||||
{{#each qualification_categories}}
|
||||
{{#if visible}}
|
||||
<span data-category-id="{{id}}"><b>{{name}}: </b>{{#each qualifications}}<span class="badge badge-secondary" data-type="qualification">{{name}}
|
||||
<span class="qualification_delete" data-qualification-id="{{id}}" data-category-id="{{../id}}" {{#if (../../readonly)}}hidden{{/if}}><svg width="1em" height="1em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#trash"/>
|
||||
</svg></span></span>
|
||||
{{/each}}<br></span>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
{{#if (not ../../readonly)}}{{#if_in_list ../../caller_permissions "modules.member_management.profile.qualifications.edit"}}<hr><div class="row">
|
||||
<div class="col-3">
|
||||
<b>Qualifikation hinzufügen:</b></div>
|
||||
<div class="col-3"><select id="add_qualification_category" class="form-control">
|
||||
<option value="none"></option>
|
||||
{{#each qualification_categories}}<option value="{{id}}">{{name}}</option>{{/each}}
|
||||
</select></div>
|
||||
<div class="col-3"><select id="add_qualification_qualification" class="form-control" disabled><option value="none"></option></select>
|
||||
</div><div class="col-3"><button id="add_qualification_submit" class="btn btn-primary" disabled>Hinzufügen</button>
|
||||
</div></div>{{/if_in_list}}{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{#if_in_list ../../caller_permissions "modules.member_management.profile.communication.view"}}
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Kommunikation</div>
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Typ</th>
|
||||
<th scope="col">Wert</th>
|
||||
<th scope="col">Beschreibung</th>
|
||||
{{#if (not ../readonly)}}<th scope="col">Aktionen</th>{{/if}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each communication_targets}}
|
||||
<tr data-target-id="{{target_id}}">
|
||||
{{#if ../readonly}}
|
||||
<td><input type="text" value="{{type_name}}" readonly size="8" readonly></td>
|
||||
{{else}}
|
||||
<td><select data-type-id="{{type_id}}" disabled class="communication_types form-control"></select></td>
|
||||
{{/if}}
|
||||
<td><input type="text" value="{{value}}" class="form-control communication_value" readonly></td>
|
||||
<td><input type="text" value="{{description}}" class="form-control communication_description" readonly></td>
|
||||
{{#if (not ../readonly)}}{{#if_in_list ../../caller_permissions "modules.member_management.profile.communication.edit"}}<td><button class="iconbutton communication_edit_button"><svg width="1.5em" height="1.5em" viewBox="0 0 16 16" class="bi bi-pencil-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
|
||||
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
|
||||
</svg></button>
|
||||
<button class="iconbutton communication_edit_save_button" hidden><svg width="1.5em" height="1.5em" viewBox="0 0 16 16" class="bi bi-check-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
<path fill-rule="evenodd" d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.236.236 0 0 1 .02-.022z"/>
|
||||
</svg></button>
|
||||
<button class="iconbutton communication_delete_button"><svg width="1.5em" height="1.5em" viewBox="0 0 16 16" class="bi bi-trash" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"></path>
|
||||
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"></path>
|
||||
</svg></button>
|
||||
</td>{{/if_in_list}}{{/if}}
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{#if (not ../readonly)}}{{#if_in_list ../../caller_permissions "modules.member_management.profile.communication.edit"}}
|
||||
<tr>
|
||||
<td><select class="communication_types form-control"></select></td>
|
||||
<td><input type="text" class="form-control communication_value"></td>
|
||||
<td><input type="text" class="form-control communication_description"></td>
|
||||
<td><button class="iconbutton communication_save_new_button"><svg width="1.5em" height="1.5em" viewBox="0 0 16 16" class="bi bi-check-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||
<path fill-rule="evenodd" d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.236.236 0 0 1 .02-.022z"/>
|
||||
</svg></button></td>
|
||||
</tr>
|
||||
{{/if_in_list}}{{/if}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>{{/if_in_list}}
|
||||
{{#if_in_list ../../caller_permissions "modules.member_management.profile.login.view"}}
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Login</div>
|
||||
<div class="card-body">
|
||||
<div class="col">
|
||||
<p>Achtung: Wird der Login deaktiviert, werden Email-Adresse und Passwort dauerhaft gelöscht und müssen nach dann ggf. neu gesetzt werden.</p>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" {{#if login.login_allowed}}checked{{/if}} id="login_allowed" {{#if ../readonly}}disabled{{/if}}>
|
||||
<label class="form-check-label" for="login_allowed">Login aktiviert</label>
|
||||
</div><hr>
|
||||
<div class="form-group row">
|
||||
<label for="login_email" class="col-sm-3">Email-Adresse</label>
|
||||
<input type="email" class="form-control col-sm-9" id="login_email" value="{{login.email}}" {{#if (not login.login_allowed)}}readonly{{/if}}{{#if ../readonly}}readonly{{/if}}>
|
||||
</div>
|
||||
{{#if (not ../readonly)}}{{#if_in_list ../../caller_permissions "modules.member_management.profile.login.edit"}}<button type="button" id="login_save_button" class="btn btn-primary" data-user-id="{{login.user_id}}" style="float: right">Speichern</button>{{/if_in_list}}{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>{{/if_in_list}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Fahrberechtigungen & Führerscheine</div>
|
||||
<div class="card-body">
|
||||
{{#each licenses}}
|
||||
<button type="button" data-permission-name="{{category_name}}" data-permission-level="{{#if drive_permission}}drive_permission{{else}}{{#if license}}license{{else}}none{{/if}}{{/if}}" class="btn {{#if drive_permission}}btn-success{{else}}{{#if license}}btn-warning{{/if}}{{/if}} drive_permission_update" data-toggle="tooltip" data-placement="bottom" title="{{category_description}}">{{category_name}}</button>
|
||||
{{/each}}
|
||||
|
||||
<hr>
|
||||
<button type="button" class="btn btn-success">Führerschein + Fahrerlaubnis vorhanden</button>
|
||||
<button type="button" class="btn btn-warning">Führerschein vorhanden, keine Fahrerlaubnis</button>
|
||||
</div>
|
||||
</div>{{#if_in_list ../../caller_permissions "modules.member_management.profile.units.view"}}
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Einsatzeinheiten</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Einheit</th>
|
||||
<th>Besetzung</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="units_tbody">{{#each units}}
|
||||
<tr>
|
||||
<td>{{name}}</td>
|
||||
<td>{{crew}}. Besetzung</td>
|
||||
<td>{{#if (not ../../readonly)}}{{#if_in_list ../../caller_permissions "modules.member_management.profile.units.edit"}}<button type="button" class="iconbutton delete_unit_from_member_button" data-unit-id="{{unit_id}}">
|
||||
<svg width="1.5em" height="1.5em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#trash"/>
|
||||
</svg>
|
||||
</button>{{/if_in_list}}{{/if}}</td>
|
||||
</tr>{{/each}}
|
||||
</tbody>
|
||||
</table></div>
|
||||
</div>{{#if_in_list ../../caller_permissions "modules.member_management.profile.units.edit"}}{{#if (not ../readonly)}}
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<b>Einsatzeinheit hinzufügen:</b></div>
|
||||
<div class="col-3"><select id="add_operation_unit_unit" class="form-control">
|
||||
</select></div>
|
||||
<div class="col-3">
|
||||
<select id="add_operation_unit_crew" class="form-control">
|
||||
<option value="1">1. Besetzung</option>
|
||||
<option value="2">2. Besetzung</option>
|
||||
</select>
|
||||
</div><div class="col-3"><button id="add_operation_unit_submit" class="btn btn-primary" disabled="">Hinzufügen</button>
|
||||
</div></div>{{/if}}{{/if_in_list}}
|
||||
</div>
|
||||
</div>{{/if_in_list}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}<h3>Bitte Mitglied auswählen.</h3>{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{> footer }}
|
|
@ -0,0 +1,107 @@
|
|||
{{> header }}
|
||||
{{> delete-member-modal}}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="wrapper">
|
||||
{{> sidebar }}
|
||||
<div id="content">
|
||||
{{> searchbar}}
|
||||
<hr>
|
||||
<h1>Member List</h1>
|
||||
<div class="col-md-6">
|
||||
<div class="filter">
|
||||
<form action="/portal/mm" method="post">
|
||||
<ul class="nav nav-tabs" id="filterTab" role="tablist">
|
||||
<li class="nav-it<em">
|
||||
<a class="nav-link active" id="group-tab" data-toggle="tab" href="#group" role="tab" aria-controls="group" aria-selected="true">Group Selection</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link disabled" id="searchfields-tab" data-toggle="tab" href="#searchfields" role="tab" aria-controls="searchfields" aria-selected="false">Search fields</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="filterTabContent">
|
||||
<div class="tab-pane fade show active" id="group" role="tabpanel" aria-labelledby="group-tab">
|
||||
<div class="group_selection_list">
|
||||
{{#each group_list}}
|
||||
<span class="form-check group_selection_group">
|
||||
<input type="checkbox" class="form-check-input" id="{{entity_id}}" name="selected_groups" value="{{entity_id}}" {{#if selected}}checked{{/if}}>
|
||||
<label class="form-check-label" for="{{entity_id}}">{{group_name}}</label>
|
||||
</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="searchfields" role="tabpanel" aria-labelledby="searchfields-tab">
|
||||
<div class="form-row">
|
||||
<div class="col form-group">
|
||||
<select class="form-control">
|
||||
<option>first name</option>
|
||||
<option>last name</option>
|
||||
<option>date of birth</option>
|
||||
<option>sex</option>
|
||||
<option>salutation</option>
|
||||
<option>place of birth</option>
|
||||
<option>academic titles</option>
|
||||
<option>personnel number</option>
|
||||
<option>ui language</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col form-group">
|
||||
<select class="form-control">
|
||||
<option>=</option>
|
||||
<option>!=</option>
|
||||
<option>LIKE</option>
|
||||
<option>></option>
|
||||
<option><</option>
|
||||
<option>>=</option>
|
||||
<option><=</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col form-group">
|
||||
<input type="text" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" class="form-control btn btn-primary filter_select" style="width: fit-content;float: right;">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">personnel number</th>
|
||||
<th scope="col">first name</th>
|
||||
<th scope="col">last name</th>
|
||||
<th scope="col">date of birth</th>
|
||||
<th scope="col">actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each member_list}}
|
||||
<tr>
|
||||
<td>{{member.personnel_number}}</td>
|
||||
<td>{{member.firstname}}</td>
|
||||
<td>{{member.lastname}}</td>
|
||||
<td>{{member.date_of_birth}}</td>
|
||||
<td>{{#if read}}
|
||||
<a href="/portal/mm/profile?action=view&id={{member.entity_id}}"><svg width="1.5em" height="1.5em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#eye-fill"/>
|
||||
</svg></a>{{/if}}{{#if write}}
|
||||
<a href="/portal/mm/profile?action=edit&id={{member.entity_id}}"><svg width="1.5em" height="1.5em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#pencil-square"/>
|
||||
</svg></a>{{/if}}{{#if delete}}
|
||||
<button class="iconbutton delete_member_button" data-member-id="{{member.entity_id}}" data-member-firstname="{{member.firstname}}" data-member-lastname="{{member.lastname}}">
|
||||
<svg width="1.5em" height="1.5em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#trash"/>
|
||||
</svg>
|
||||
</button>{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{> footer }}
|
|
@ -0,0 +1,43 @@
|
|||
{{> header }}
|
||||
<div class="jumbotron">
|
||||
<div class="row">
|
||||
<div class="col-lq">
|
||||
<h1>Willkommen bei einsatz.online!</h1>
|
||||
</div>
|
||||
{{#if logo_path}}
|
||||
<div class="col">
|
||||
<img class="welcome_logo" src="{{logo_path}}">
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="login row">
|
||||
<div class="col">
|
||||
<form method="post">
|
||||
{{#if alert}}
|
||||
{{> alert}}
|
||||
{{/if}}
|
||||
<h3>Anmelden</h3>
|
||||
<div class="form-group">
|
||||
<label for="login_email">E-Mail Adresse</label>
|
||||
<input type="email" class="form-control" name="login_email" id="login_email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="login_password">Passwort</label>
|
||||
<input type="password" class="form-control" name="login_password" id="login_password" required>
|
||||
</div>
|
||||
<button type="submit" class="login_submit btn btn-primary">Anmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col">
|
||||
<form method="post" action="/password_reset">
|
||||
<h3>Passwort vergessen?</h3>
|
||||
<div class="form-group">
|
||||
<label for="email">E-Mail Adresse</label>
|
||||
<input type="email" class="form-control" name="email" id="email" required>
|
||||
</div>
|
||||
<button type="submit" class="login_submit btn btn-secondary">Passwort zurücksetzen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{> footer }}
|
|
@ -0,0 +1,30 @@
|
|||
{{> header }}
|
||||
<div class="jumbotron">
|
||||
<div class="row">
|
||||
<div class="col-lq">
|
||||
<h1>Willkommen bei einsatz.online!</h1>
|
||||
</div>
|
||||
{{#if logo_path}}
|
||||
<div class="col">
|
||||
<img class="welcome_logo" src="{{logo_path}}">
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-10">
|
||||
<form method="post">
|
||||
{{#if alert}}
|
||||
{{> alert}}
|
||||
{{/if}}
|
||||
<h3>Neues Passwort festlegen</h3>
|
||||
<p>Ihr Passwort muss mindestens 10 Zeichen lang sein und sollte Buchstaben, Zahlen und Sonderzeichen enthalten.</p>
|
||||
<div class="form-group">
|
||||
<label for="password">Passwort</label>
|
||||
<input type="password" class="form-control" name="password" id="password" required>
|
||||
</div>
|
||||
<button type="submit" class="login_submit btn btn-primary">Passwort ändern</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{> footer }}
|
|
@ -0,0 +1,18 @@
|
|||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text" id="searchbar-type"></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="searchbar" data-active-entity="{{active_entity}}" data-entity-type="{{entity_type}}">
|
||||
<span class="input-group-append">
|
||||
<span class="btn btn-outline-secondary" type="button">
|
||||
<svg width="16" height="16" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#search"/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="search-result-overlay" style="display: none">
|
||||
<ul class="list-group search-result-overlay-list">
|
||||
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,76 @@
|
|||
<!-- Sidebar -->
|
||||
<nav id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h3>ERMS</h3>
|
||||
<hr>
|
||||
<div class="user-info">
|
||||
<p><a href="/portal/personal_profile?action=view"><button class="btn text-left btn-sm" ><svg width="1.5em" height="1.5em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#file-earmark-person"/>
|
||||
</svg> {{sidebar.firstname}} {{sidebar.lastname}}</button></a></p>
|
||||
</div><hr>
|
||||
<a href="/portal/logout"><button class="btn btn-sm"><svg width="1.5em" height="1.5em" fill="currentColor">
|
||||
<use xlink:href="/img/bootstrap-icons.svg#door-open"/>
|
||||
</svg> Abmelden</button></a>
|
||||
</div>
|
||||
|
||||
<ul class="components sidebar-navigation">
|
||||
{{#if sidebar.summary.visible}}
|
||||
<li {{#if sidebar.summary.active}}class="sidebar_entry_active"{{/if}}>
|
||||
<a href="/portal">Übersicht</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if sidebar.member_management.visible}}
|
||||
<li>
|
||||
<a {{#if sidebar.member_management.active}}class="sidebar_entry_active"{{/if}} href="/portal/mm">Mitglieder</a>
|
||||
{{#if sidebar.member_management.active}}
|
||||
<ul>
|
||||
<li><a href="/portal/mm">Mitgliederliste</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/portal/mm/profile">Profil</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/portal/mm/groups">Gruppen</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/portal/mm/trainings">Aus- und Fortbildungen</a></li>
|
||||
</ul>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if sidebar.resource_management.visible}}
|
||||
<li>
|
||||
<a {{#if sidebar.resource_management.active}}class="sidebar_entry_active"{{/if}} href="/portal/rm">Ressourcen</a>
|
||||
{{#if sidebar.resource_management.active}}
|
||||
<ul>
|
||||
<li><a href="/portal/rm">List</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/portal/rm/vehicles">Vehicles</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/portal/rm/items">Items</a></li>
|
||||
</ul>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if sidebar.event_management.visible}}
|
||||
<li>
|
||||
<a {{#if sidebar.event_management.active}}class="sidebar_entry_active"{{/if}} href="/portal/em">Einsätze</a>
|
||||
{{#if sidebar.event_management.active}}
|
||||
<ul>
|
||||
<li><a href="/portal/em">Calendar</a></li>
|
||||
</ul>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
</ul>
|
||||
<ul class="components sidebar-actions">
|
||||
{{#if sidebar.member_management.active}}
|
||||
<li>
|
||||
<a href="/portal/mm/add_member">Mitglied hinzufügen</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
|
@ -0,0 +1,235 @@
|
|||
create table __diesel_schema_migrations
|
||||
(
|
||||
version varchar(50) not null
|
||||
constraint __diesel_schema_migrations_pkey
|
||||
primary key,
|
||||
run_on timestamp default CURRENT_TIMESTAMP not null
|
||||
);
|
||||
|
||||
create table users
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint pk___users___id
|
||||
primary key,
|
||||
password text,
|
||||
email text
|
||||
);
|
||||
|
||||
create table communication_types
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint pk___communication_types___id
|
||||
primary key,
|
||||
name text not null
|
||||
);
|
||||
|
||||
create table addresses
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint addresses_pk
|
||||
primary key,
|
||||
title text,
|
||||
street text not null,
|
||||
number text not null,
|
||||
zipcode text not null,
|
||||
city text not null,
|
||||
geo_location point
|
||||
);
|
||||
|
||||
create table entities
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null
|
||||
constraint entities_pk
|
||||
primary key
|
||||
);
|
||||
|
||||
create table members
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null
|
||||
constraint pk___members___id
|
||||
primary key
|
||||
constraint members_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
users_id uuid
|
||||
constraint fk___members___users_id___users
|
||||
references users,
|
||||
firstname text not null,
|
||||
lastname text not null,
|
||||
date_of_birth date,
|
||||
sex smallint,
|
||||
salutation text,
|
||||
place_of_birth text,
|
||||
academic_titles text,
|
||||
personnel_number integer,
|
||||
ui_language text
|
||||
);
|
||||
|
||||
create unique index members_personnel_number_uindex
|
||||
on members (personnel_number);
|
||||
|
||||
create table addresses_entities
|
||||
(
|
||||
address_id uuid not null
|
||||
constraint addresses_entities_addresses_id_fk
|
||||
references addresses
|
||||
on update cascade on delete cascade,
|
||||
entitiy_id uuid not null
|
||||
constraint addresses_entities_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
constraint addresses_entities_pk
|
||||
primary key (address_id, entitiy_id)
|
||||
);
|
||||
|
||||
create table buildings
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null
|
||||
constraint buildings_pk
|
||||
primary key
|
||||
constraint buildings_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
name text not null,
|
||||
description text
|
||||
);
|
||||
|
||||
create table vehicle_categories
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint vehicle_categories_pk
|
||||
primary key,
|
||||
name text not null,
|
||||
description text
|
||||
);
|
||||
|
||||
create table vehicles
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null
|
||||
constraint vehicles_pk
|
||||
primary key
|
||||
constraint vehicles_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade
|
||||
constraint vehicles_vehicle_categories_id_fk
|
||||
references vehicle_categories
|
||||
on update cascade on delete set null,
|
||||
identifier text not null,
|
||||
numberplate text,
|
||||
description text,
|
||||
vehicle_category uuid default uuid_generate_v1(),
|
||||
next_inspection date,
|
||||
is_operational boolean default true not null,
|
||||
admissible_total_weight real,
|
||||
required_license uuid
|
||||
);
|
||||
|
||||
create table communication_targets
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint pk___communication_targets___id
|
||||
primary key
|
||||
constraint communication_targets_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
entity text not null,
|
||||
entity_id uuid not null,
|
||||
com_type uuid not null
|
||||
constraint fk___communication_target___type___communication_types
|
||||
references communication_types,
|
||||
value text not null,
|
||||
description text,
|
||||
visibility boolean default false not null
|
||||
);
|
||||
|
||||
create table permissions
|
||||
(
|
||||
permission text not null
|
||||
constraint permissions_pk
|
||||
primary key,
|
||||
description text
|
||||
);
|
||||
|
||||
create table roles
|
||||
(
|
||||
id text not null
|
||||
constraint roles_pk
|
||||
primary key,
|
||||
description text
|
||||
);
|
||||
|
||||
create table roles_permissions
|
||||
(
|
||||
role_id text not null
|
||||
constraint roles_permissions_roles_id_fk
|
||||
references roles
|
||||
on update cascade on delete cascade,
|
||||
permission_id text not null
|
||||
constraint roles_permissions_permissions_permission_fk
|
||||
references permissions
|
||||
on update cascade on delete cascade,
|
||||
role_permission_id uuid default uuid_generate_v1() not null
|
||||
constraint roles_permissions_pk_2
|
||||
primary key
|
||||
);
|
||||
|
||||
create unique index roles_permissions_role_permission_id_uindex
|
||||
on roles_permissions (role_permission_id);
|
||||
|
||||
create table members_roles
|
||||
(
|
||||
member_id uuid not null
|
||||
constraint members_roles_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
role_id text not null
|
||||
constraint members_roles_roles_id_fk
|
||||
references roles
|
||||
on update cascade on delete cascade,
|
||||
constraint members_roles_pk
|
||||
primary key (member_id, role_id)
|
||||
);
|
||||
|
||||
create table groups
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null
|
||||
constraint groups_pk
|
||||
primary key
|
||||
constraint groups_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
group_name text not null,
|
||||
group_description text
|
||||
);
|
||||
|
||||
create unique index groups_group_name_uindex
|
||||
on groups (group_name);
|
||||
|
||||
create table groups_entities
|
||||
(
|
||||
group_id uuid not null
|
||||
constraint groups_entities_groups_group_id_fk
|
||||
references groups
|
||||
on update cascade on delete cascade,
|
||||
entity_id uuid not null
|
||||
constraint groups_entities_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
constraint groups_entities_pk
|
||||
primary key (group_id, entity_id)
|
||||
);
|
||||
|
||||
create table roles_permissions_context
|
||||
(
|
||||
role_permission_id uuid not null
|
||||
constraint roles_permissions_contexts_roles_permissions_role_permission_id
|
||||
references roles_permissions
|
||||
on update cascade on delete cascade,
|
||||
entity uuid not null
|
||||
constraint roles_permissions_contexts_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
constraint roles_permissions_context_pk
|
||||
primary key (role_permission_id, entity)
|
||||
);
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::database::model::addresses::Address;
|
||||
use crate::diesel::RunQueryDsl;
|
||||
use crate::helper::settings::Settings;
|
||||
use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl};
|
||||
use rocket::State;
|
||||
|
||||
/// Reads all addresses for entity_id specified
|
||||
/// Returns None if error occurred or there are no addresses
|
||||
pub fn get_addresses_for_entity(
|
||||
settings: &State<Settings>,
|
||||
entity_id_to_get: uuid::Uuid,
|
||||
) -> Option<Vec<Address>> {
|
||||
use crate::schema::addresses::dsl::*;
|
||||
use crate::schema::addresses_entities::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let data: Result<Vec<Address>, diesel::result::Error> = addresses
|
||||
.left_join(addresses_entities.on(id.eq(address_id)))
|
||||
.filter(entitiy_id.eq(entity_id_to_get))
|
||||
.select((id, title, street, number, zipcode, city, country))
|
||||
.load(&connection);
|
||||
|
||||
match data {
|
||||
Ok(data_addresses) => Some(data_addresses),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Error reading addresses for {} from database: {}",
|
||||
entity_id_to_get, e
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_addresses_for_entity_only_one_allowed(
|
||||
settings: &State<Settings>,
|
||||
entity_id_to_get: uuid::Uuid,
|
||||
) -> Option<Address> {
|
||||
match get_addresses_for_entity(settings, entity_id_to_get) {
|
||||
Some(addresses) => {
|
||||
if addresses.len() == 0 {
|
||||
None
|
||||
} else if addresses.len() != 1 {
|
||||
error!("There is more then one address saved for entity {}, but called with get_addresses_for_entity_only_one_allowed()! Check for data inconsistency.", entity_id_to_get);
|
||||
None
|
||||
} else {
|
||||
Some(addresses.first().unwrap().clone()) //Yes, we are allowed to unwrap because we checked that there is exact one element 3 lines before
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::api::members::member_communication::CommunicationTarget;
|
||||
use crate::schema::communication_targets::dsl::{communication_targets, target_id};
|
||||
use diesel::query_dsl::filter_dsl::FilterDsl;
|
||||
use diesel::{ExpressionMethods, RunQueryDsl};
|
||||
use rocket::State;
|
||||
|
||||
/// Updates communication target via API.
|
||||
/// Will only change field if field is Some, will ignore field if it's None.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * settings: Settings as managed State
|
||||
/// * com_target_id: uuid of communication target to change
|
||||
/// * com_target: transmitted CommunicationTarget struct, (all fields optional)
|
||||
pub fn update_communication_target(
|
||||
settings: &State<Settings>,
|
||||
com_target_id: uuid::Uuid,
|
||||
com_target: CommunicationTarget,
|
||||
) -> Result<usize, diesel::result::Error> {
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match diesel::update(communication_targets.filter(target_id.eq(com_target_id)))
|
||||
.set(&com_target)
|
||||
.execute(&connection)
|
||||
{
|
||||
Ok(affected_rows) => Ok(affected_rows),
|
||||
Err(e) => {
|
||||
warn!("Couldn't update communication target: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::database::controller::groups::get_groups_for_member;
|
||||
use crate::database::model::api_members::RawMemberSearchResult;
|
||||
use crate::diesel::query_dsl::filter_dsl::OrFilterDsl;
|
||||
use crate::diesel::query_dsl::limit_dsl::LimitDsl;
|
||||
use crate::diesel::query_dsl::select_dsl::SelectDsl;
|
||||
use crate::diesel::RunQueryDsl;
|
||||
use crate::helper::check_access::check_access;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::api::members::get_member::MemberSearchResult;
|
||||
use diesel::query_dsl::filter_dsl::FilterDsl;
|
||||
use diesel::{ExpressionMethods, TextExpressionMethods};
|
||||
use rocket::State;
|
||||
|
||||
pub fn get_raw_member_search_result(
|
||||
settings: &State<Settings>,
|
||||
member_entity_id: uuid::Uuid,
|
||||
) -> Result<RawMemberSearchResult, diesel::result::Error> {
|
||||
use crate::schema::members::dsl::*;
|
||||
|
||||
let connection = establish_connection(&settings);
|
||||
|
||||
let short_member: Result<RawMemberSearchResult, diesel::result::Error> = members
|
||||
.filter(entity_id.eq(member_entity_id))
|
||||
.select((entity_id, firstname, lastname))
|
||||
.first(&connection);
|
||||
|
||||
match short_member {
|
||||
Ok(raw_member) => Ok(raw_member),
|
||||
Err(e) => {
|
||||
warn!("Couldn't load RawMemberSearchResult: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_member_search_result(
|
||||
settings: &State<Settings>,
|
||||
member_entity_id: uuid::Uuid,
|
||||
caller_entity_id: uuid::Uuid,
|
||||
) -> Result<MemberSearchResult, diesel::result::Error> {
|
||||
let member = get_raw_member_search_result(settings, member_entity_id)?;
|
||||
let groups = get_groups_for_member(settings, member_entity_id);
|
||||
|
||||
let readable = check_access(
|
||||
settings,
|
||||
member_entity_id,
|
||||
groups,
|
||||
caller_entity_id,
|
||||
"modules.member_management.profile.view".to_string(),
|
||||
);
|
||||
Ok(MemberSearchResult {
|
||||
entity_id: member.entity_id,
|
||||
firstname: member.firstname,
|
||||
lastname: member.lastname,
|
||||
readable,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_raw_member_search_result_by_name(
|
||||
settings: &State<Settings>,
|
||||
member_name: String,
|
||||
) -> Result<Vec<RawMemberSearchResult>, diesel::result::Error> {
|
||||
use crate::schema::members::dsl::*;
|
||||
|
||||
let connection = establish_connection(&settings);
|
||||
|
||||
let splitted_name: Vec<&str> = member_name.split_whitespace().collect();
|
||||
|
||||
let short_members: Result<Vec<RawMemberSearchResult>, diesel::result::Error>;
|
||||
|
||||
if splitted_name.len() == 0 {
|
||||
return Ok(vec![]);
|
||||
} else if splitted_name.len() == 1 {
|
||||
short_members = members
|
||||
.filter(firstname.like(format!("{}%", member_name)))
|
||||
.or_filter(lastname.like(format!("{}%", member_name)))
|
||||
.select((entity_id, firstname, lastname))
|
||||
.limit(5)
|
||||
.load(&connection);
|
||||
} else {
|
||||
short_members = members
|
||||
.filter(firstname.like(format!("{}%", splitted_name[0])))
|
||||
.filter(lastname.like(format!("{}%", splitted_name[1])))
|
||||
.select((entity_id, firstname, lastname))
|
||||
.limit(5)
|
||||
.load(&connection);
|
||||
}
|
||||
|
||||
match short_members {
|
||||
Ok(raw_member) => Ok(raw_member),
|
||||
Err(e) => {
|
||||
warn!("Couldn't load RawMemberSearchResult: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_member_search_result_by_name(
|
||||
settings: &State<Settings>,
|
||||
name: String,
|
||||
caller_entity_id: uuid::Uuid,
|
||||
) -> Result<Vec<MemberSearchResult>, diesel::result::Error> {
|
||||
let raw_members = get_raw_member_search_result_by_name(settings, name)?;
|
||||
let mut members: Vec<MemberSearchResult> = vec![];
|
||||
|
||||
for member in raw_members {
|
||||
let groups = get_groups_for_member(settings, member.entity_id);
|
||||
let readable = check_access(
|
||||
settings,
|
||||
member.entity_id,
|
||||
groups,
|
||||
caller_entity_id,
|
||||
"modules.member_management.profile.view".to_string(),
|
||||
);
|
||||
members.push(MemberSearchResult {
|
||||
entity_id: member.entity_id,
|
||||
firstname: member.firstname,
|
||||
lastname: member.lastname,
|
||||
readable,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(members)
|
||||
}
|
||||
|
||||
pub fn delete_entity(
|
||||
settings: &State<Settings>,
|
||||
entity_id_to_delete: uuid::Uuid,
|
||||
) -> Result<usize, diesel::result::Error> {
|
||||
use crate::schema::entities::dsl::*;
|
||||
|
||||
let connection = establish_connection(&settings);
|
||||
|
||||
match diesel::delete(entities.filter(entity_id.eq(entity_id_to_delete))).execute(&connection) {
|
||||
Ok(size) => Ok(size),
|
||||
Err(e) => {
|
||||
warn!("Couldn't delete entity: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
use crate::helper::settings::Settings;
|
||||
use diesel::{Connection, PgConnection};
|
||||
use rocket::State;
|
||||
|
||||
pub fn establish_connection(settings: &State<Settings>) -> PgConnection {
|
||||
//TODO: return Error
|
||||
let db_url = settings.database.connection_string.clone();
|
||||
PgConnection::establish(&db_url).expect(&format!("Error connecting to database."))
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
use rocket::State;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::member_management::model::member::CreateMemberForm;
|
||||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::schema::entities::dsl::entities;
|
||||
use diesel::{RunQueryDsl, ExpressionMethods};
|
||||
use crate::schema::entities::dsl::entity_id;
|
||||
use crate::schema::members::dsl::members;
|
||||
use crate::database::model::members::CreateMember;
|
||||
use crate::helper::forms::into_option;
|
||||
use chrono::NaiveDate;
|
||||
use crate::database::controller::members_groups::add_member_to_group;
|
||||
use diesel::result::Error;
|
||||
|
||||
pub fn create_member(settings: &State<Settings>, form: CreateMemberForm) -> Result<uuid::Uuid, diesel::result::Error>{
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let entity_id_result: Result<uuid::Uuid, diesel::result::Error> =diesel::insert_into(entities).default_values().returning(crate::schema::entities::dsl::entity_id).get_result(&connection);
|
||||
let id = match entity_id_result{
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
error!("Konnte keine entity anlegen: {}", e);
|
||||
return Err(e)
|
||||
}
|
||||
};
|
||||
|
||||
use crate::schema::members::dsl::*;
|
||||
|
||||
let personnel_number2 = if form.personnel_number.is_empty(){
|
||||
None
|
||||
}else{
|
||||
match form.personnel_number.parse::<i32>(){
|
||||
Ok(num) => Some(num),
|
||||
Err(e) => {
|
||||
warn!("Couldn't parse personnel number: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let entrance_date2 = if form.entrance_date.is_empty(){
|
||||
None
|
||||
}else {
|
||||
match NaiveDate::parse_from_str(&form.entrance_date, "%Y-%m-%d") {
|
||||
Ok(date) => Some(date),
|
||||
Err(e) => {
|
||||
warn!("Couldn't parse date: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let member = CreateMember{
|
||||
entity_id: id,
|
||||
firstname: form.firstname,
|
||||
lastname: form.lastname,
|
||||
sex: form.sex.parse::<i16>().unwrap(),
|
||||
salutation: form.salutation,
|
||||
academic_titles: into_option(form.academic_titles),
|
||||
personnel_number: personnel_number2,
|
||||
entrance_date: entrance_date2
|
||||
};
|
||||
|
||||
match add_member_to_group(settings, id, uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap()){
|
||||
Ok(_) => {},
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
match diesel::insert_into(members).values(member).execute(&connection){
|
||||
Ok(_) => Ok(id),
|
||||
Err(e) => {
|
||||
error!("Couldn't create member: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::database::controller::members::check_member_has_access;
|
||||
use crate::database::model::groups::{GroupMemberCount, RawGroup};
|
||||
use crate::helper::check_access::check_access;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::member_management::model::groups::{Group, GroupData, GroupUpdateData};
|
||||
use crate::schema::groups_entities::dsl::group_id;
|
||||
use crate::schema::roles_permissions_context::columns::entity;
|
||||
use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl, RunQueryDsl};
|
||||
use rocket::State;
|
||||
use std::collections::HashMap;
|
||||
use diesel::associations::HasTable;
|
||||
|
||||
/// Returns list of all groups (database::model::groups::RawGroup)
|
||||
///
|
||||
/// #Arguments
|
||||
/// * 'settings' - Settings, as managed State
|
||||
///
|
||||
/// #Returns
|
||||
/// * Result<Vec<RawGroup>, diesel::result::Error> - RawGroup items inside Vector or diesel database error.
|
||||
pub fn get_raw_groups(settings: &State<Settings>) -> Result<Vec<RawGroup>, diesel::result::Error> {
|
||||
use crate::schema::groups::dsl::*;
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let group_vec: Result<Vec<RawGroup>, diesel::result::Error> = groups.order(group_name.asc()).load(&connection);
|
||||
match group_vec {
|
||||
Ok(group_vec) => Ok(group_vec),
|
||||
Err(e) => {
|
||||
error!("Error occured while loading groups: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns member count for a group
|
||||
///
|
||||
/// #Arguments
|
||||
/// * 'settings' - Settings, as managed State
|
||||
/// * 'group_id_to_check'
|
||||
///
|
||||
/// #Returns
|
||||
/// * Result<i64, diesel::result::Error> - member count for group or diesel database error.
|
||||
pub fn get_members_in_groups_count(
|
||||
settings: &State<Settings>,
|
||||
group_id_to_check: uuid::Uuid,
|
||||
) -> Result<i64, diesel::result::Error> {
|
||||
use crate::schema::groups_entities::dsl::*;
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let count: Result<i64, diesel::result::Error> = groups_entities
|
||||
.filter(group_id.eq(group_id_to_check))
|
||||
.count()
|
||||
.get_result(&connection);
|
||||
|
||||
match count {
|
||||
Ok(count) => Ok(count),
|
||||
Err(e) => {
|
||||
error!("Couldn't count members in group: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all groups with member count caller is allowed to see
|
||||
///
|
||||
/// #Arguments
|
||||
/// * 'settings' - Settings, as managed State
|
||||
/// * 'caller_uuid'
|
||||
///
|
||||
/// #Returns
|
||||
/// * Result<Vec<GroupMemberCount, diesel::result::Error> - Vector of all groups member has access to or diesel database error.
|
||||
pub fn get_group_member_count(
|
||||
settings: &State<Settings>,
|
||||
caller_id: uuid::Uuid,
|
||||
) -> Result<Vec<GroupMemberCount>, diesel::result::Error> {
|
||||
let groups = get_raw_groups(settings)?;
|
||||
let mut groups_member_count: Vec<GroupMemberCount> = vec![];
|
||||
|
||||
for group in groups {
|
||||
groups_member_count.push(GroupMemberCount {
|
||||
group_id: group.group_id,
|
||||
name: group.name,
|
||||
description: group.description,
|
||||
members_count: get_members_in_groups_count(settings, group.group_id)?,
|
||||
permission_edit_core: check_member_has_access(
|
||||
settings,
|
||||
caller_id,
|
||||
group.group_id,
|
||||
crate::permissions::modules::member_management::groups::core::EDIT,
|
||||
),
|
||||
permission_delete: check_member_has_access(
|
||||
settings,
|
||||
caller_id,
|
||||
group.group_id,
|
||||
crate::permissions::modules::member_management::groups::DELETE,
|
||||
),
|
||||
permission_view_members: check_member_has_access(
|
||||
settings,
|
||||
caller_id,
|
||||
group.group_id,
|
||||
crate::permissions::modules::member_management::groups::members::VIEW,
|
||||
),
|
||||
permission_edit_members: check_member_has_access(
|
||||
settings,
|
||||
caller_id,
|
||||
group.group_id,
|
||||
crate::permissions::modules::member_management::groups::members::EDIT,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
Ok(groups_member_count)
|
||||
}
|
||||
|
||||
/// Returns HashMap of all groups (modules::member_management::model::group::Group)
|
||||
/// Calls get_raw_groups and adding selected: false for every group except default group
|
||||
/// Key is group_id
|
||||
///
|
||||
/// #Arguments
|
||||
/// * 'settings' - Settings, as managed State
|
||||
///
|
||||
/// #Returns
|
||||
/// * HashMap<Group> - Group items inside Vector.
|
||||
pub fn get_groups(
|
||||
settings: &State<Settings>,
|
||||
) -> Result<HashMap<uuid::Uuid, Group>, diesel::result::Error> {
|
||||
let raw_groups: Vec<RawGroup> = get_raw_groups(settings)?;
|
||||
let mut groups: HashMap<uuid::Uuid, Group> = HashMap::new();
|
||||
|
||||
for raw_group in raw_groups {
|
||||
groups.insert(
|
||||
raw_group.group_id,
|
||||
Group {
|
||||
entity_id: raw_group.group_id,
|
||||
group_name: raw_group.name,
|
||||
selected: false,
|
||||
group_description: raw_group.description,
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(groups)
|
||||
}
|
||||
|
||||
pub fn get_groups_for_member(settings: &State<Settings>, member_id2: uuid::Uuid) -> Vec<Group> {
|
||||
//TODO: return errors
|
||||
use crate::schema::groups::dsl::*;
|
||||
use crate::schema::groups_entities::dsl::entity_id as groups_entities_entity_id;
|
||||
use crate::schema::groups_entities::dsl::group_id as groups_entities_group_id;
|
||||
use crate::schema::groups_entities::dsl::groups_entities;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let data: Result<Vec<RawGroup>, diesel::result::Error> = groups
|
||||
.left_join(groups_entities.on(group_id.eq(crate::schema::groups::dsl::entity_id)))
|
||||
.filter(groups_entities_entity_id.eq(member_id2))
|
||||
.select((entity_id, group_name, group_description))
|
||||
.load(&connection);
|
||||
|
||||
match data {
|
||||
Ok(data) => {
|
||||
let mut groups_result: Vec<Group> = Vec::new();
|
||||
for raw_group in data {
|
||||
groups_result.push(Group {
|
||||
entity_id: raw_group.group_id,
|
||||
group_name: raw_group.name,
|
||||
selected: false,
|
||||
group_description: raw_group.description,
|
||||
});
|
||||
}
|
||||
groups_result
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Couldn't get groups for member: {}", e);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_group(
|
||||
settings: &State<Settings>,
|
||||
group: GroupData,
|
||||
) -> Result<RawGroup, diesel::result::Error> {
|
||||
use crate::schema::entities::dsl::*;
|
||||
use crate::schema::groups::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let group_entity_id: uuid::Uuid = match diesel::insert_into(entities)
|
||||
.default_values()
|
||||
.returning(crate::schema::entities::dsl::entity_id)
|
||||
.get_result(&connection)
|
||||
{
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
error!("Couldn't create entity_id for new group: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
let inserted: Result<RawGroup, diesel::result::Error> = diesel::insert_into(groups)
|
||||
.values((
|
||||
crate::schema::groups::dsl::entity_id.eq(group_entity_id),
|
||||
group_name.eq(group.group_name.unwrap()),
|
||||
group_description.eq(group.group_description),
|
||||
))
|
||||
.get_result(&connection);
|
||||
|
||||
match inserted {
|
||||
Ok(inserted) => Ok(inserted),
|
||||
Err(e) => {
|
||||
error!("Couldn't insert new group: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_group(
|
||||
settings: &State<Settings>,
|
||||
group_id2: uuid::Uuid,
|
||||
) -> Result<(), diesel::result::Error> {
|
||||
use crate::schema::groups::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match diesel::delete(groups.filter(entity_id.eq(group_id2))).execute(&connection) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("Couldn't delete group: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_group(
|
||||
settings: &State<Settings>,
|
||||
group_id2: uuid::Uuid,
|
||||
) -> Result<RawGroup, diesel::result::Error> {
|
||||
use crate::schema::groups::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let group: Result<RawGroup, diesel::result::Error> =
|
||||
groups.filter(entity_id.eq(group_id2)).first(&connection);
|
||||
|
||||
match group {
|
||||
Ok(group) => Ok(group),
|
||||
Err(e) => {
|
||||
warn!("Couldn't load group: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_group_core_data(settings: &State<Settings>, group_id2: uuid::Uuid, changeset: GroupUpdateData) -> Result<RawGroup, diesel::result::Error>{
|
||||
use crate::schema::groups::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let updated: Result<RawGroup, diesel::result::Error> = diesel::update(groups).filter(entity_id.eq(group_id2)).set(&changeset).get_result(&connection);
|
||||
match updated{
|
||||
Ok(updated) => Ok(updated),
|
||||
Err(e) => {
|
||||
error!("Couldn't update group core data: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::database::controller::roles::{
|
||||
add_permission, add_permission_context, get_role_permission_id, get_roles,
|
||||
};
|
||||
use crate::helper::check_access::check_access;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::api::groups::create::GroupRolePermission;
|
||||
use crate::schema::roles_permissions_context::dsl::roles_permissions_context;
|
||||
use diesel::pg::types::sql_types::Uuid;
|
||||
use diesel::result::Error;
|
||||
use diesel::sql_types::Text;
|
||||
use diesel::{sql_query, RunQueryDsl};
|
||||
use rocket::State;
|
||||
|
||||
/// Execute add_permission_context for given group_id and permission
|
||||
/// Warning: Will return Ok, even if permission context wasn't added due to missing permission for role
|
||||
pub fn add_group_role_permission(
|
||||
settings: &State<Settings>,
|
||||
group_id: uuid::Uuid,
|
||||
role_id: String,
|
||||
permission: &str,
|
||||
) -> Result<(), diesel::result::Error> {
|
||||
let role_permission_id = get_role_permission_id(settings, role_id, permission);
|
||||
match role_permission_id {
|
||||
Some(id) => match add_permission_context(settings, id, group_id) {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
None => return Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_group_role_permissions(
|
||||
settings: &State<Settings>,
|
||||
group_id: uuid::Uuid,
|
||||
permissions: Vec<GroupRolePermission>,
|
||||
) -> Result<(), diesel::result::Error> {
|
||||
for permission in permissions {
|
||||
if permission.role_id != "admin".to_string() {
|
||||
if permission.permission_groups_core_edit {
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::core::EDIT,
|
||||
)?;
|
||||
}
|
||||
if permission.permission_groups_delete {
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::DELETE,
|
||||
)?;
|
||||
}
|
||||
if permission.permission_groups_members_view {
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::members::VIEW,
|
||||
)?;
|
||||
}
|
||||
if permission.permission_groups_members_edit {
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::members::EDIT,
|
||||
)?;
|
||||
}
|
||||
if permission.permission_groups_permissions_view {
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::permissions::VIEW,
|
||||
)?;
|
||||
}
|
||||
if permission.permission_groups_permissions_edit {
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::permissions::EDIT,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
//Manual overwrite for admin. Gives admin all permissions
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::core::EDIT,
|
||||
)?;
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::DELETE,
|
||||
)?;
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::members::VIEW,
|
||||
)?;
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::members::EDIT,
|
||||
)?;
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::permissions::EDIT,
|
||||
)?;
|
||||
add_group_role_permission(
|
||||
settings,
|
||||
group_id,
|
||||
permission.role_id.clone(),
|
||||
crate::permissions::modules::member_management::groups::permissions::VIEW,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_role_has_permission_on_entity(
|
||||
settings: &State<Settings>,
|
||||
role_id: &str,
|
||||
permission: &str,
|
||||
entity_id: uuid::Uuid,
|
||||
) -> Result<bool, diesel::result::Error> {
|
||||
use crate::database::controller::members::TempQuery;
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let permission_count: Result<TempQuery, diesel::result::Error> = sql_query(
|
||||
"SELECT COUNT(role_permission_id) FROM roles_permissions_context WHERE entity = $1 AND role_permission_id IN (SELECT role_permission_id FROM roles_permissions WHERE role_id = $2 AND permission_id = $3)",
|
||||
)
|
||||
.bind::<Uuid, _>(entity_id)
|
||||
.bind::<Text, _>(role_id)
|
||||
.bind::<Text, _>(permission)
|
||||
.get_result(&connection);
|
||||
|
||||
match permission_count {
|
||||
Ok(count) => {
|
||||
if count.count > 0 {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error while checking members permissions (context): {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_group_permission_for_role(
|
||||
settings: &State<Settings>,
|
||||
group_id: uuid::Uuid,
|
||||
role_id: String,
|
||||
) -> Result<GroupRolePermission, diesel::result::Error> {
|
||||
Ok(GroupRolePermission {
|
||||
role_id: role_id.clone(),
|
||||
permission_groups_core_edit: check_role_has_permission_on_entity(
|
||||
settings,
|
||||
&role_id,
|
||||
crate::permissions::modules::member_management::groups::core::EDIT,
|
||||
group_id,
|
||||
)?,
|
||||
permission_groups_delete: check_role_has_permission_on_entity(
|
||||
settings,
|
||||
&role_id,
|
||||
crate::permissions::modules::member_management::groups::DELETE,
|
||||
group_id,
|
||||
)?,
|
||||
permission_groups_members_view: check_role_has_permission_on_entity(
|
||||
settings,
|
||||
&role_id,
|
||||
crate::permissions::modules::member_management::groups::members::VIEW,
|
||||
group_id,
|
||||
)?,
|
||||
permission_groups_members_edit: check_role_has_permission_on_entity(
|
||||
settings,
|
||||
&role_id,
|
||||
crate::permissions::modules::member_management::groups::members::EDIT,
|
||||
group_id,
|
||||
)?,
|
||||
permission_groups_permissions_view: check_role_has_permission_on_entity(
|
||||
settings,
|
||||
&role_id,
|
||||
crate::permissions::modules::member_management::groups::permissions::VIEW,
|
||||
group_id,
|
||||
)?,
|
||||
permission_groups_permissions_edit: check_role_has_permission_on_entity(
|
||||
settings,
|
||||
&role_id,
|
||||
crate::permissions::modules::member_management::groups::permissions::EDIT,
|
||||
group_id,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_group_role_permissions(
|
||||
settings: &State<Settings>,
|
||||
group_id: uuid::Uuid,
|
||||
) -> Result<Vec<GroupRolePermission>, diesel::result::Error> {
|
||||
let mut grp: Vec<GroupRolePermission> = vec![];
|
||||
|
||||
let roles = get_roles(settings)?;
|
||||
|
||||
for role in roles {
|
||||
match get_group_permission_for_role(settings, group_id, role.id) {
|
||||
Ok(sgrp) => grp.push(sgrp),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(grp)
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
use rocket::State;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::database::controller::connector::establish_connection;
|
||||
use diesel::{sql_query, RunQueryDsl, ExpressionMethods};
|
||||
use diesel::sql_types::{Integer, Text};
|
||||
use crate::database::model::login_protection::LoginAttemptsResult;
|
||||
use chrono::{NaiveDateTime, Duration};
|
||||
use std::ops::Add;
|
||||
use crate::schema::login_attempts::dsl::login_attempts;
|
||||
|
||||
/// Checks if maximum login attempts exceeded. Locks account if exceeded
|
||||
/// Returns:
|
||||
/// * Ok(true) if login attempts exceeded and account got locked
|
||||
/// * Ok(false) if login attempts not exceeded
|
||||
/// * Err(diesel::result::Error) if database error occured
|
||||
pub fn login_attempts_exceeded(settings: &State<Settings>, email: String) -> Result<bool, diesel::result::Error>{
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let result : Result<LoginAttemptsResult, diesel::result::Error> = sql_query("SELECT COUNT(*) AS count, MAX(timestamp) AS last_timestamp FROM login_attempts WHERE email=$1 AND timestamp > (now() - interval '1800 seconds')").bind::<Text, _>(email.clone()).get_result(&connection);
|
||||
let result = match result{
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
error!("Couldn't check for login attempts: {}", e);
|
||||
return Err(e)
|
||||
}
|
||||
};
|
||||
|
||||
if result.count > settings.application.max_login_attempts as i64 {
|
||||
Ok(true)
|
||||
}else{
|
||||
add_login_attempt(settings, email)?;
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn add_login_attempt(settings: &State<Settings>, email2: String) -> Result<(), diesel::result::Error>{
|
||||
use crate::schema::login_attempts::dsl::{login_attempts, email};
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match diesel::insert_into(login_attempts).values(email.eq(email2)).execute(&connection){
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("Couldn't write login attempt into DB! {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::database::model::member_communication::CommunicationTarget;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::api::member_management::model::member_communication::CommunicationType;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use rocket::State;
|
||||
|
||||
pub fn get_member_communication(
|
||||
settings: &State<Settings>,
|
||||
member_entity_id: uuid::Uuid,
|
||||
) -> Vec<CommunicationTarget> {
|
||||
use crate::schema::communication_targets::dsl::*;
|
||||
use crate::schema::communication_types::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let data: Result<Vec<CommunicationTarget>, diesel::result::Error> = communication_targets
|
||||
.inner_join(communication_types)
|
||||
.filter(entity_id.eq(member_entity_id))
|
||||
.select((target_id, entity_id, type_name, type_id, value, description))
|
||||
.load(&connection);
|
||||
|
||||
match data {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Couldn't get communication targets for {}: {}",
|
||||
member_entity_id, e
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_communication_target(
|
||||
settings: &State<Settings>,
|
||||
target_id2: uuid::Uuid,
|
||||
) -> Result<CommunicationTarget, diesel::result::Error> {
|
||||
use crate::schema::communication_targets::dsl::*;
|
||||
use crate::schema::communication_types::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let data: Result<CommunicationTarget, diesel::result::Error> = communication_targets
|
||||
.inner_join(communication_types)
|
||||
.filter(target_id.eq(target_id2))
|
||||
.select((target_id, entity_id, type_name, type_id, value, description)).get_result(&connection);
|
||||
|
||||
match data {
|
||||
Ok(data) => Ok(data),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Couldn't get communication target: {}",
|
||||
e
|
||||
);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_member_communication_types(settings: &State<Settings>) -> Vec<CommunicationType> {
|
||||
use crate::schema::communication_types::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let data: Result<Vec<CommunicationType>, diesel::result::Error> = communication_types
|
||||
.select((type_id, type_name))
|
||||
.load(&connection);
|
||||
|
||||
match data {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!("Couldn't get communication types: {}", e);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_communication_target(
|
||||
settings: &State<Settings>,
|
||||
target: CommunicationTarget,
|
||||
) -> Result<uuid::Uuid, diesel::result::Error> {
|
||||
use crate::schema::communication_targets::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
let result = diesel::insert_into(communication_targets)
|
||||
.values((
|
||||
entity_id.eq(target.entity_id),
|
||||
com_type.eq(target.type_id),
|
||||
value.eq(target.value),
|
||||
description.eq(target.description),
|
||||
))
|
||||
.returning(target_id)
|
||||
.get_result(&connection);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn remove_communication_target(
|
||||
settings: &State<Settings>,
|
||||
target_id_to_delete: uuid::Uuid,
|
||||
) -> Result<usize, diesel::result::Error> {
|
||||
use crate::schema::communication_targets::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
let result = diesel::delete(communication_targets.filter(target_id.eq(target_id_to_delete)))
|
||||
.execute(&connection);
|
||||
|
||||
match result {
|
||||
Ok(result) => Ok(result),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Couldn't delete communication target {}: {}",
|
||||
target_id_to_delete, e
|
||||
);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::database::model::addresses::Address;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::schema::addresses::columns::id;
|
||||
use crate::schema::addresses::dsl::addresses;
|
||||
use crate::schema::addresses_entities::columns::{address_id, entitiy_id};
|
||||
use crate::schema::addresses_entities::dsl::addresses_entities;
|
||||
use diesel::{ExpressionMethods, RunQueryDsl};
|
||||
use rocket::State;
|
||||
|
||||
pub fn insert_address(
|
||||
settings: &State<Settings>,
|
||||
new_address: Address,
|
||||
belongs_to: uuid::Uuid,
|
||||
) -> bool {
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let address = diesel::insert_into(addresses)
|
||||
.default_values()
|
||||
.get_result(&connection);
|
||||
|
||||
let address: Address = match address {
|
||||
Ok(address) => address,
|
||||
Err(e) => {
|
||||
warn!("Couldn't receive inserted address: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let mut new_address = new_address;
|
||||
new_address.id = address.id;
|
||||
|
||||
let update = diesel::update(addresses)
|
||||
.filter(id.eq(address.id))
|
||||
.set(&new_address)
|
||||
.get_result(&connection);
|
||||
|
||||
let address: Address = match update {
|
||||
Ok(address) => address,
|
||||
Err(e) => {
|
||||
warn!("Couldn't receive updated address: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let result = diesel::insert_into(addresses_entities)
|
||||
.values((address_id.eq(address.id), entitiy_id.eq(belongs_to.clone())))
|
||||
.execute(&connection);
|
||||
|
||||
match result {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"Couldn't insert new address: {} for entity {}",
|
||||
e, belongs_to
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::database::model::member_licenses::RawMemberLicense;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::member_management::model::licenses::MemberLicense;
|
||||
use diesel::pg::types::sql_types::Uuid;
|
||||
use diesel::{sql_query, ExpressionMethods, RunQueryDsl};
|
||||
use rocket::State;
|
||||
|
||||
pub fn get_member_licenses(
|
||||
settings: &State<Settings>,
|
||||
entity_id: uuid::Uuid,
|
||||
) -> Vec<MemberLicense> {
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let data: Result<Vec<RawMemberLicense>, diesel::result::Error> = sql_query(
|
||||
"SELECT * FROM license_categories as lc
|
||||
LEFT JOIN licenses_members lm on lc.name = lm.license_name AND lm.member_id=$1 ORDER BY lc.name ASC;",
|
||||
)
|
||||
.bind::<Uuid, _>(entity_id)
|
||||
.load(&connection);
|
||||
|
||||
match data {
|
||||
Ok(data) => {
|
||||
let mut output: Vec<MemberLicense> = Vec::new();
|
||||
|
||||
for license in data {
|
||||
output.push(MemberLicense::from(license.clone()));
|
||||
trace!("LICENSE: {}", license.name);
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Couldn't get licenses for member {} : {}", entity_id, e);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_member_license(
|
||||
settings: &State<Settings>,
|
||||
member_id2: uuid::Uuid,
|
||||
license_name2: String,
|
||||
) -> Result<usize, diesel::result::Error> {
|
||||
use crate::schema::licenses_members::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let result = diesel::insert_into(licenses_members)
|
||||
.values((
|
||||
member_id.eq(member_id2),
|
||||
license_name.eq(license_name2.clone()),
|
||||
))
|
||||
.execute(&connection);
|
||||
|
||||
match result {
|
||||
Ok(num) => Ok(num),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Couldn't add license {} for member {} : {}",
|
||||
license_name2, member_id2, e
|
||||
);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_member_driving_permission(
|
||||
settings: &State<Settings>,
|
||||
member_id2: uuid::Uuid,
|
||||
license_name2: String,
|
||||
) -> Result<usize, diesel::result::Error> {
|
||||
use crate::schema::licenses_members::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let result = diesel::update(licenses_members)
|
||||
.filter(member_id.eq(member_id2))
|
||||
.filter(license_name.eq(license_name2.clone()))
|
||||
.set(drive_permission.eq(true))
|
||||
.execute(&connection);
|
||||
|
||||
match result {
|
||||
Ok(num) => Ok(num),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Couldn't add drive_permission to license {} for member {} : {}",
|
||||
license_name2, member_id2, e
|
||||
);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_member_license(
|
||||
settings: &State<Settings>,
|
||||
member_id2: uuid::Uuid,
|
||||
license_name2: String,
|
||||
) -> Result<usize, diesel::result::Error> {
|
||||
use crate::schema::licenses_members::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let result = diesel::delete(licenses_members)
|
||||
.filter(member_id.eq(member_id2))
|
||||
.filter(license_name.eq(license_name2.clone()))
|
||||
.execute(&connection);
|
||||
|
||||
match result {
|
||||
Ok(num) => Ok(num),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Couldn't remove license {} for member {} : {}",
|
||||
license_name2, member_id2, e
|
||||
);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::database::model::member_qualifications::RawQualificationCategory;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::member_management::model::qualifications::{
|
||||
Qualification, QualificationCategory,
|
||||
};
|
||||
use crate::schema::qualification_categories::dsl::qualification_categories;
|
||||
use crate::schema::qualifications_members::columns::{member_id, qualification_id};
|
||||
use crate::schema::qualifications_members::dsl::qualifications_members;
|
||||
use diesel::result::Error;
|
||||
use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl, RunQueryDsl};
|
||||
use rocket::State;
|
||||
|
||||
pub fn get_raw_qualification_categories(
|
||||
settings: &State<Settings>,
|
||||
) -> Vec<RawQualificationCategory> {
|
||||
use crate::schema::qualification_categories::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let data: Result<Vec<RawQualificationCategory>, diesel::result::Error> =
|
||||
qualification_categories.load(&connection);
|
||||
|
||||
match data {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!("Couldn't read Qualification categories: {}", e);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_qualifcation_categories(
|
||||
settings: &State<Settings>,
|
||||
member_entity_id: uuid::Uuid,
|
||||
) -> Vec<QualificationCategory> {
|
||||
let raw_categories = get_raw_qualification_categories(settings);
|
||||
let mut categories: Vec<QualificationCategory> = vec![];
|
||||
|
||||
for category in raw_categories {
|
||||
let qualifications =
|
||||
get_qualifications_for_member_for_category(settings, member_entity_id, category.id);
|
||||
categories.push(QualificationCategory {
|
||||
id: category.id,
|
||||
name: category.name,
|
||||
description: category.description,
|
||||
qualifications,
|
||||
visible: true,
|
||||
})
|
||||
}
|
||||
|
||||
for mut category in categories.iter_mut() {
|
||||
if category.qualifications.is_empty() {
|
||||
category.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
categories
|
||||
}
|
||||
|
||||
pub fn get_qualifications_for_member_for_category(
|
||||
settings: &State<Settings>,
|
||||
member_entity_id: uuid::Uuid,
|
||||
category_id: uuid::Uuid,
|
||||
) -> Vec<Qualification> {
|
||||
use crate::schema::qualifications::dsl::*;
|
||||
use crate::schema::qualifications_members::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let data: Result<Vec<Qualification>, diesel::result::Error> = qualifications
|
||||
.left_join(qualifications_members.on(qualification_id.eq(id)))
|
||||
.filter(member_id.eq(member_entity_id))
|
||||
.filter(category.eq(category_id))
|
||||
.select((id, name, description))
|
||||
.load(&connection);
|
||||
|
||||
match data {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Couldn't read Qualifications for category {} for member {}: {}",
|
||||
category_id, member_entity_id, e
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_qualification_for_member(
|
||||
settings: &State<Settings>,
|
||||
member_entity_id: uuid::Uuid,
|
||||
qualification_id2: uuid::Uuid,
|
||||
) -> Result<usize, Error> {
|
||||
let connection = establish_connection(settings);
|
||||
let result = diesel::delete(
|
||||
qualifications_members
|
||||
.filter(member_id.eq(member_entity_id))
|
||||
.filter(qualification_id.eq(qualification_id2)),
|
||||
)
|
||||
.execute(&connection);
|
||||
|
||||
match result {
|
||||
Ok(result) => Ok(result),
|
||||
Err(e) => {
|
||||
error!("Couldn't delete Qualifications {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_qualification_for_member(
|
||||
settings: &State<Settings>,
|
||||
member_entity_id: uuid::Uuid,
|
||||
qualification_id2: uuid::Uuid,
|
||||
) -> Result<usize, Error> {
|
||||
let connection = establish_connection(settings);
|
||||
let result = diesel::insert_into(qualifications_members)
|
||||
.values((
|
||||
member_id.eq(member_entity_id),
|
||||
qualification_id.eq(qualification_id2),
|
||||
))
|
||||
.execute(&connection);
|
||||
|
||||
match result {
|
||||
Ok(result) => Ok(result),
|
||||
Err(e) => {
|
||||
error!("Couldn't insert Qualification {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_qualifications_for_category(
|
||||
settings: &State<Settings>,
|
||||
category_id2: uuid::Uuid,
|
||||
) -> Vec<Qualification> {
|
||||
use crate::schema::qualifications::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let data: Result<Vec<Qualification>, diesel::result::Error> = qualifications
|
||||
.left_join(qualification_categories.on(category.eq(id)))
|
||||
.filter(category.eq(category_id2))
|
||||
.select((id, name, description))
|
||||
.load(&connection);
|
||||
|
||||
match data {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Couldn't read Qualifications for category {} : {}",
|
||||
category_id2, e
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::database::controller::member_insert::insert_address;
|
||||
use crate::database::model::addresses::Address;
|
||||
use crate::database::model::members::RawMember;
|
||||
use crate::helper::forms::into_option;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::member_management::model::member::{Member, MemberProfileForm};
|
||||
use crate::schema::addresses::dsl::{addresses, id};
|
||||
use crate::schema::members;
|
||||
use chrono::NaiveDate;
|
||||
use diesel::{ExpressionMethods, RunQueryDsl};
|
||||
use rocket::State;
|
||||
use std::str::FromStr;
|
||||
use uuid::Uuid;
|
||||
use iban::{Iban, ParseIbanError};
|
||||
|
||||
pub fn update_member(
|
||||
settings: &State<Settings>,
|
||||
member_form: MemberProfileForm,
|
||||
old_member_data: Member,
|
||||
old_address: Option<Address>,
|
||||
personal_profile_update: bool,
|
||||
) -> bool {
|
||||
use crate::schema::members::columns::entity_id;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
let member_id = old_member_data.entity_id.clone();
|
||||
let mut success = false;
|
||||
|
||||
let new_address =
|
||||
match parse_profile_member_form_address(member_form.clone(), old_address.clone()) {
|
||||
Some(address) => address,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
match old_address {
|
||||
Some(old_address) => {
|
||||
let address_id_old = old_address.id.clone();
|
||||
let affected_rows_address = diesel::update(addresses)
|
||||
.filter(id.eq(address_id_old))
|
||||
.set(&new_address)
|
||||
.execute(&connection);
|
||||
match affected_rows_address {
|
||||
Ok(num) => {
|
||||
if num != 1 {
|
||||
error!(
|
||||
"Updated address {}, but query affected {} rows!",
|
||||
address_id_old, num
|
||||
);
|
||||
success = false;
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Tried to update address {}, but query failed: {}",
|
||||
address_id_old, e
|
||||
);
|
||||
success = false
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let result = insert_address(settings, new_address, member_id);
|
||||
if !result {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let new_member = match parse_profile_member_form_member(member_form.clone(), old_member_data, personal_profile_update) {
|
||||
Some(member) => member,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let affected_rows_member = diesel::update(members::table)
|
||||
.filter(entity_id.eq(member_id))
|
||||
.set(&new_member)
|
||||
.execute(&connection);
|
||||
|
||||
match affected_rows_member {
|
||||
Ok(num) => {
|
||||
if num != 1 {
|
||||
error!(
|
||||
"Updated member {}, but query affected {} rows!",
|
||||
member_id, num
|
||||
);
|
||||
success = false;
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Tried to update member {}, but query failed: {}",
|
||||
member_id, e
|
||||
);
|
||||
success = false
|
||||
}
|
||||
}
|
||||
|
||||
success
|
||||
}
|
||||
|
||||
fn parse_profile_member_form_address(
|
||||
member_form: MemberProfileForm,
|
||||
old_address: Option<Address>,
|
||||
) -> Option<Address> {
|
||||
let address_id: uuid::Uuid = if old_address.is_some() {
|
||||
match Uuid::from_str(&member_form.address_id) {
|
||||
Ok(uuid) => {
|
||||
if uuid != old_address.clone().unwrap().id {
|
||||
warn!(
|
||||
"Submitted address_id doesn't match with old entity_id! {} vs {}",
|
||||
uuid,
|
||||
old_address.unwrap().id
|
||||
);
|
||||
return None;
|
||||
} else {
|
||||
uuid
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"Couldn't parse address_id submitted via MemberProfileForm: {} {}",
|
||||
member_form.address_id, e
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let title = if old_address.is_some() {
|
||||
old_address.unwrap().title
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Some(Address {
|
||||
id: address_id,
|
||||
title,
|
||||
street: Some(member_form.address_street),
|
||||
number: Some(member_form.address_number),
|
||||
zipcode: Some(member_form.address_zipcode),
|
||||
city: Some(member_form.address_city),
|
||||
country: Some(member_form.address_country),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_profile_member_form_member(
|
||||
member_form: MemberProfileForm,
|
||||
old_member_data: Member,
|
||||
personnel_profile_change: bool,
|
||||
) -> Option<RawMember> {
|
||||
let entity_id = match Uuid::from_str(&member_form.entity_id) {
|
||||
Ok(uuid) => {
|
||||
if uuid != old_member_data.entity_id {
|
||||
warn!(
|
||||
"Submitted entity_id doesn't match with old entity_id! {} vs {}",
|
||||
uuid, old_member_data.entity_id
|
||||
);
|
||||
return None;
|
||||
} else {
|
||||
uuid
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"Couldn't parse entity_id submitted via MemberProfileForm: {}",
|
||||
e
|
||||
);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let date_of_birth = if member_form.date_of_birth.is_empty() {
|
||||
None
|
||||
} else {
|
||||
match NaiveDate::parse_from_str(&member_form.date_of_birth, "%Y-%m-%d") {
|
||||
Ok(date) => Some(date),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"Couldn't parse date_of_birth submitted via MemberProfileForm: {}",
|
||||
e
|
||||
);
|
||||
old_member_data.date_of_birth
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let sex = if member_form.sex.is_empty() {
|
||||
None
|
||||
} else {
|
||||
match i16::from_str(&member_form.sex) {
|
||||
Ok(value) => match value {
|
||||
0 | 1 | 2 | 9 => Some(value),
|
||||
_ => old_member_data.sex,
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("Couldn't parse sex submitted via MemberProfileForm: {}", e);
|
||||
old_member_data.sex
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let personnel_number = match i32::from_str(&member_form.personnel_number) {
|
||||
Ok(value) => Some(value),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"Couldn't parse personnel_number submitted via MemberProfileForm: {}",
|
||||
e
|
||||
);
|
||||
old_member_data.personnel_number
|
||||
}
|
||||
};
|
||||
|
||||
let entrance_date = if member_form.entrance_date.is_empty() {
|
||||
None
|
||||
} else {
|
||||
match NaiveDate::parse_from_str(&member_form.entrance_date, "%Y-%m-%d") {
|
||||
Ok(date) => Some(date),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"Couldn't parse entrance_date submitted via MemberProfileForm: {}",
|
||||
e
|
||||
);
|
||||
old_member_data.entrance_date
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let iban = if member_form.iban.is_empty(){
|
||||
None
|
||||
}else{
|
||||
match member_form.iban.parse::<Iban>(){
|
||||
Ok(iban) => Some(iban.to_string()),
|
||||
Err(e) => {warn!("Couldn't parse iban submitted via MemberProfileForm: {}", e);
|
||||
old_member_data.iban
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(personnel_profile_change){
|
||||
Some(RawMember {
|
||||
entity_id,
|
||||
users_id: old_member_data.users_id,
|
||||
firstname: old_member_data.firstname,
|
||||
lastname: old_member_data.lastname,
|
||||
date_of_birth,
|
||||
sex: old_member_data.sex,
|
||||
salutation: old_member_data.salutation,
|
||||
place_of_birth: into_option(member_form.place_of_birth),
|
||||
academic_titles: old_member_data.academic_titles,
|
||||
personnel_number: old_member_data.personnel_number,
|
||||
ui_language: old_member_data.ui_language,
|
||||
nationality: into_option(member_form.nationality),
|
||||
entrance_date,
|
||||
birth_name: into_option(member_form.birth_name),
|
||||
iban,
|
||||
bic: into_option(member_form.bic),
|
||||
})
|
||||
}else{
|
||||
Some(RawMember {
|
||||
entity_id,
|
||||
users_id: old_member_data.users_id,
|
||||
firstname: member_form.firstname,
|
||||
lastname: member_form.lastname,
|
||||
date_of_birth,
|
||||
sex,
|
||||
salutation: into_option(member_form.salutation),
|
||||
place_of_birth: into_option(member_form.place_of_birth),
|
||||
academic_titles: into_option(member_form.academic_titles),
|
||||
personnel_number,
|
||||
ui_language: old_member_data.ui_language,
|
||||
nationality: into_option(member_form.nationality),
|
||||
entrance_date,
|
||||
birth_name: into_option(member_form.birth_name),
|
||||
iban,
|
||||
bic: into_option(member_form.bic),
|
||||
})
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue