FEA: added event requests, added permission editor

This commit is contained in:
Keanu D?lle 2021-08-16 10:10:12 +02:00
parent 6bab82fa57
commit eed52375d6
57 changed files with 3130 additions and 51 deletions

View File

@ -0,0 +1,19 @@
-- This file should undo anything in `up.sql`
drop table if exists event_requests;
DELETE
FROM permissions
WHERE permission LIKE 'modules.event_management.requests.create' ESCAPE '#';
DELETE
FROM permissions
WHERE permission LIKE 'modules.event_management.requests.edit' ESCAPE '#';
DELETE
FROM permissions
WHERE permission LIKE 'modules.event_management.requests.view' ESCAPE '#';
DELETE
FROM permissions
WHERE permission LIKE 'modules.event_management.requests.delete' ESCAPE '#';
DELETE
FROM permissions
WHERE permission LIKE 'modules.event_management.requests.assignments.assign' ESCAPE '#';
alter table events drop column related_request;
alter table events drop column state;

View File

@ -0,0 +1,53 @@
-- Your SQL goes here
create table event_requests
(
entity_id uuid not null
constraint event_requests_pk
primary key
constraint event_request_entities_entity_id_fk
references entities
on update cascade on delete cascade,
name text not null,
timescale text,
site text,
organiser_id uuid,
etype uuid,
related_group uuid
constraint event_requests_groups_entity_id_fk
references groups,
contact_on_site_name text,
contact_on_site_phone text,
specials text,
requested text,
expected_attendees integer,
risk_potential integer,
state smallint default 0 not null,
received timestamp default now() not null
);
INSERT INTO permissions (permission, description, context)
VALUES ('modules.event_management.requests.view', 'Permission to see all event requests', false);
INSERT INTO permissions (permission, description, context)
VALUES ('modules.event_management.requests.edit', 'Permission to modify event requests', false);
INSERT INTO permissions (permission, description, context)
VALUES ('modules.event_management.requests.delete', 'Permission to delete event requests', false);
INSERT INTO permissions (permission, description, context)
VALUES ('modules.event_management.requests.create', 'Permission to create new event requests', false);
INSERT INTO permissions (permission, description, context, context_type)
VALUES ('modules.event_management.requests.assignments.assign', 'Permission to assign requests to a group', true, 'groups');
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id)
VALUES ('admin', 'modules.event_management.requests.view', DEFAULT);
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id)
VALUES ('admin', 'modules.event_management.requests.edit', DEFAULT);
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id)
VALUES ('admin', 'modules.event_management.requests.delete', DEFAULT);
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id)
VALUES ('admin', 'modules.event_management.requests.create', DEFAULT);
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id)
VALUES ('admin', 'modules.event_management.requests.assignments.assign', DEFAULT);
alter table events
add related_request uuid;
alter table events
add state smallint default 0 not null;

View File

@ -0,0 +1,139 @@
<div class="card">
<a href="#collapse-{{entity_id}}" style="color: black !important" data-toggle="collapse"><div class="card-header"><span class="font-weight-bold">{{received}}: </span>{{name}}</div></a>
<div id="collapse-{{entity_id}}" class="collapse requestlist_accordion_card" data-entity-id="{{entity_id}}">
<div class="card-body">
<ul class="nav nav-tabs request_list_navtabs" id="evenlist_tab-{{entity_id}}" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" href="#requestlist_core_tab-{{entity_id}}" id="core-tab-{{entity_id}}" data-toggle="tab" data-bs-toggle="tab" data-bs-target="#requestlist_core_tab-{{entity_id}}" aria-controls="core-tab" aria-selected="true">Anfrage</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" href="#requestlist_state_tab-{{entity_id}}" id="state-tab-{{entity_id}}" data-toggle="tab" data-bs-toggle="tab" data-bs-target="#requestlist_state_tab-{{entity_id}}" aria-controls="state-tab" aria-selected="false">Vergabe</a>
</li>
<li class="nav-item" role="presentation">
<span style="cursor:pointer;" class="nav-link edit_request_button" data-request-id="{{entity_id}}" id="edit-tab-{{entity_id}}" aria-selected="false">Bearbeiten</span>
</li>
</ul><br>
<div class="tab-content">
<div class="tab-pane fade show active edit_event_core_data" role="tabpanel" aria-labelledby="core-tab" id="requestlist_core_tab-{{entity_id}}">
<div class="requestlist_accordion_card_overview row">
<div class="col-md-6">
<h5>Anfrage</h5>
<div class="form-group row">
<label for="{{entity_id}}-name" class="col-sm-3 col-form-label">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="{{entity_id}}-name" readonly value="{{name}}">
</div>
</div>
<div class="form-group row">
<label for="{{entity_id}}-etype" class="col-sm-3 col-form-label">Einsatzart</label>
<div class="col-sm-9">
<select class="form-control" id="{{entity_id}}-etype" disabled readonly>
{{#each etypes}}
<option value="{{type_id}}" {{#if active}}selected{{/if}}>{{name}} {{#if is_billable}}(€){{/if}}</option>
{{/each}}
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Zeitraum</label>
<div class="col-sm-9">
<div class="row m-0">
<textarea class="form-control" rows="4" id="{{entity_id}}-timescale" readonly>{{timescale}}</textarea>
</div>
</div>
</div>
<div class="form-group row">
<label for="{{entity_id}}-site" class="col-sm-3 col-form-label">Veranstaltungsort</label>
<div class="col-sm-9">
<textarea rows="4" class="form-control" id="{{entity_id}}-site" readonly>{{site}}</textarea>
</div>
</div>
<div class="form-group row">
<label for="{{entity_id}}-risk_potential" class="col-sm-3 col-form-label">Risikopotential</label>
<div class="col-sm-9">
<select readonly disabled class="form-control" id="{{entity_id}}-risk_potential">
<option value="1" {{#if risk_potential_low}}selected{{/if}}>geringes Risiko</option>
<option value="2" {{#if risk_potential_medium}}selected{{/if}}>mittleres Risiko</option>
<option value="3" {{#if risk_potential_high}}selected{{/if}}>hohes Risiko</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="{{entity_id}}-expected_attendees" class="col-sm-3 col-form-label">Erwartete Besucher</label>
<div class="col-sm-9">
<input id="{{entity_id}}-expected_attendees" type="number" class="form-control" readonly value="{{expected_attendees}}">
</div>
</div>
<div class="form-group row">
<label for="{{entity_id}}-specials" class="col-sm-3 col-form-label">Besonderheiten (z.B. Alkoholausschank)</label>
<div class="col-sm-9">
<div class="row m-0">
<textarea class="form-control" rows="4" id="{{entity_id}}-specials" readonly>{{specials}}</textarea>
</div>
</div>
</div>
<div class="form-group row">
<label for="{{entity_id}}-requested" class="col-sm-3 col-form-label">Angeforderte Einheiten</label>
<div class="col-sm-9">
<div class="row m-0">
<textarea class="form-control" rows="4" id="{{entity_id}}-requested" readonly>{{requested}}</textarea>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<h5>Status</h5>
<select id="{{entity_id}}-state" class="form-control" disabled readonly>
<option value="0"></option>
<option value="1" {{#if state_1}}selected{{/if}}>Neue Anfrage</option>
<option value="3" {{#if state_3}}selected{{/if}}>Anfrage geprüft</option>
<option value="5" {{#if state_5}}selected{{/if}}>Anfrage an Gruppen freigegeben</option>
<option value="7" {{#if state_7}}selected{{else}}disabled{{/if}}>Anfrage von Gruppe übernommen</option>
</select><br>
<h5>Veranstalter</h5>
{{> search base=this.osearch_base type="organiser"}}
{{#if organiser.phone}}<p><b>Telefon: </b>{{organiser.phone}}</p>{{/if}}
{{#if organiser.email}}<p><b>Email: </b>{{organiser.email}}</p>{{/if}}
{{#if organiser.other}}<p><b>Sonstiges: </b>{{organiser.other}}</p>{{/if}}
<br>
<h5>Ansprechpartner vor Ort</h5>
<div class="form-group row">
<label for="{{entity_id}}-contact_on_site_name" class="col-sm-3 col-form-label">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="{{entity_id}}-contact_on_site_name" readonly value="{{contact_on_site_name}}">
</div>
</div>
<div class="form-group row">
<label for="{{entity_id}}-contact_on_site_phone"
class="col-sm-3 col-form-label">Telefonnummer</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="{{entity_id}}-contact_on_site_phone" readonly value="{{contact_on_site_phone}}">
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" role="tabpanel" aria-labelledby="state-tab" id="requestlist_state_tab-{{entity_id}}">
{{#if enable_assignment}}
<div class="form-group row">
<label for="{{entity_id}}-assignment" class="col-sm-3 col-form-label">Vergeben an:</label>
<div class="col-sm-6">
<div class="row m-0">
<select class="form-control" id="{{entity_id}}-assignment">
<option value="none">unvergeben</option>
{{#each groups_with_assign_permission}}
<option value="{{id}}" {{#if active}}selected{{/if}}>{{name}}</option>
{{/each}}
</select>
</div>
</div>
</div>
{{else}}
Für die Vergabe an Gruppen muss der Status der Anfrage auf "Anfrage an Gruppen freigegeben" gesetzt werden!
{{/if}}
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,16 @@
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Permission</th>
<th scope="col">Beschreibung</th>
<th scope="col">Kontext</th>
</tr>
</thead>
<tbody id="permission_list_tbody">{{#each permissions}}<tr data-context="{{context}}" data-context-type="{{context_type}}" class=" {{#if has_permission}}{{else}} text-muted{{/if}}">
<td><input type="checkbox" class="permission_checkbox" data-permission="{{permission}}" {{#if has_permission}}checked{{/if}}></td>
<td class="permission_row">{{permission}}</td>
<td class="permission_row">{{description}}</td>
<td class="permission_row">{{context_combined}}</td>
</tr>{{/each}}</tbody>
</table>

View File

@ -0,0 +1,58 @@
{{#if groups}}
<h3>Gruppen</h3>
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Gruppe</th>
</tr>
</thead>
<tbody id="permission_context_list_tbody">{{#each groups}}
<tr
class=" {{#if has_this_context}}{{else}} text-muted{{/if}}">
<td><input type="checkbox" class="permission_context_checkbox" data-context-id="{{group_id}}" {{#if has_this_context}}checked{{/if}}>
</td>
<td class="permission_row" title="{{description}}">{{name}}</td>
</tr>
{{/each}}
</tbody>
</table>
{{/if}}
{{#if members}}
<h3>Mitglieder</h3>
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Mitglieder</th>
</tr>
</thead>
<tbody>{{#each members}}
<tr class=" {{#if has_this_context}}{{else}} text-muted{{/if}}">
<td><input type="checkbox" class="permission_context_checkbox" data-context-id="{{entity_id}}" {{#if has_this_context}}checked{{/if}}>
</td>
<td class="permission_row">{{firstname}} {{lastname}}</td>
</tr>
{{/each}}
</tbody>
</table>
{{/if}}
{{#if vehicles}}
<h3>Fahrzeuge</h3>
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Fahrzeug</th>
</tr>
</thead>
<tbody>{{#each vehicles}}
<tr class=" {{#if has_this_context}}{{else}} text-muted{{/if}}">
<td><input type="checkbox" class="permission_context_checkbox" data-context-id="{{entity_id}}" {{#if has_this_context}}checked{{/if}}>
</td>
<td class="permission_row" title="{{description}}">{{identifier}} ({{numberplate}})</td>
</tr>
{{/each}}
</tbody>
</table>
{{/if}}

View File

@ -0,0 +1,23 @@
<table class="table table-hover table-striped">
<thead class="thead">
<tr>
<th><button class="iconbutton check_all_roles"><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 this}}
<tr data-role-id="{{id}}" class="role-tr" style="cursor: pointer;">
<td><input type="checkbox" class="role_entry_checkbox" data-role-id="{{id}}" data-role-name="{{name}}"></td>
<td>{{id}}</td>
<td>{{description}}</td>
<td>{{member_count}}</td>
</tr></span>
{{/each}}
</tbody>
</table>
<span><button class="iconbutton check_all_roles" data-check-all-selector=".check_all_roles"><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-danger btn-sm roles_delete_button">Löschen</button></span>

View File

@ -0,0 +1,7 @@
{{#each this}}
<tr>
<td><input type="checkbox" class="member_checkbox" data-member-id="{{entity_id}}"></td>
<td>{{firstname}}</td>
<td>{{lastname}}</td>
</tr>
{{/each}}

View File

@ -6,7 +6,7 @@ $( document ).ready(function() {
$("#member_responsible_remove").on("click", EventCreateModule.member_responsible_remove);
$("#organiser_remove").on("click", EventCreateModule.organiser_remove);
load_event_types(EventCreateModule.set_event_types);
load_event_types_legacy(EventCreateModule.set_event_types);
load_groups(EventCreateModule.set_groups);
$("#create_event_btn").on("click", EventCreateModule.add_event);
});

View File

@ -0,0 +1,99 @@
$( document ).ready(function() {
var organiser_search = new MiniSearchbar("add_event_organiser_search", RequestCreateModule.organiser_callback);
organiser_search.setup();
$("#organiser_remove").on("click", RequestCreateModule.organiser_remove);
load_event_types_legacy(RequestCreateModule.set_event_types);
$("#create_request_btn").on("click", RequestCreateModule.add_request);
});
RequestCreateModule = ( function() {
let organiser_callback = function(caller){
$("#create-event-organiser-search").hide();
$("#organiser").val($(caller).data("company")+" "+$(caller).data("firstname")+" "+$(caller).data("lastname")).attr("data-entity-id", $(caller).data("entity-id"));
$("#organiser_input_group").show();
};
let organiser_remove = function(){
$("#organiser_input_group").hide();
$("#organiser").val("").removeData("entity-id").removeAttr("data-entity-id");
$("#add_event_organiser_search-searchbar").val("");
$(".add_event_organiser_search-search-result-overlay-list").html("");
$("#create-event-organiser-search").show();
};
let add_request = function(){
if($("#create_request_form")[0].checkValidity()) {
let request = {};
request.name = $("#name").val();
if($("#timescale").val().length > 0) {
request.timescale = $("#timescale").val();
}
if($("#site").val().length > 0){
request.site = $("#site").val();
}
if($("#etype").val().length > 0){
request.etype = $("#etype option:selected").data("type-id");
}
if($("#organiser").data("entity-id")){
request.organiser_id = $("#organiser").data("entity-id");
}
if($("#specials").val().length > 0){
request.specials = $("#specials").val();
}
if($("#requested").val().length > 0){
request.requested = $("#requested").val();
}
if($("#contact_on_site_name").val().length > 0){
request.contact_on_site_name = $("#contact_on_site_name").val();
}
if($("#contact_on_site_phone").val().length > 0){
request.contact_on_site_phone = $("#contact_on_site_phone").val();
}
if($("#expected_attendees").val().length > 0){
request.expected_attendees = $("#expected_attendees").val();
}
if($("#risk_potential").val().length > 0){
request.risk_potential = $("#risk_potential").val();
}
console.log(request);
$.ajax({
url: '/api/event_requests',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(request),
success: function (data) {
if (is_ok(data)) {
console.log(data);
}
},
timeout: 3000,
error: function () {
alert("Verbindung zum Server unterbrochen!");
}
});
}else{
$('<input type="submit">').hide().appendTo("#create_request_form").click().remove();
}
};
let set_event_types = function(res){
$(res).each(function(){
let is_billable = "";
if(this.is_billable){
is_billable = " (€) ";
}
$("#etype").append("<option data-type-id=\""+this.type_id+"\">"+this.name+is_billable+"</option>");
});
};
let set_groups = function(res){
$(res.groups).each(function(){
$("#related_group").append("<option data-group-id=\""+this.group_id+"\">"+this.name+"</option>");
});
}
return{
set_groups: set_groups,
set_event_types: set_event_types,
add_request: add_request,
organiser_remove: organiser_remove,
organiser_callback: organiser_callback,
}
}());

View File

@ -268,20 +268,6 @@ EventListModule = ( function() {
return res.name;
}
};
let get_event_type = async function(type_id){
const res = await $.ajax({
type: "GET",
url: "/api/events/types/" + type_id,
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Verbindung zum Server unterbrochen!");
},
});
if (is_ok(res)) {
return res.name;
}
};
let check_edit_permission_callback = function(has_permission){
if(has_permission === true){
$(".eventlist_navtabs").each(function(){

View File

@ -0,0 +1,283 @@
limit = 5;
$( document ).ready(async function() {
await RequestListModule.load_templates();
await RequestListModule.prepare();
RequestListModule.setup_pagination();
RequestListModule.load_requests();
});
RequestListModule = ( function() {
let old_offset = 0;
let templates = {};
let event_types = {};
let groups_with_assign_pem = [];
let prepare = async function(){
groups_with_assign_pem = await get_group_caller_has_asign_permissions();
event_types = await load_event_types();
};
let load_templates = async function(){
const em_request_card = $.get("/templates/em_request_card.hbs");
const pagination = $.get("/templates/pagination.hbs");
const search = $.get("/templates/search.hbs");
await Promise.all([em_request_card, pagination, search]).then(function(res){
templates.em_request_card = Handlebars.compile(res[0]);
templates.pagination = Handlebars.compile(res[1]);
templates.search = res[2];
});
Handlebars.registerPartial('search', templates.search);
};
let setup_pagination = function(){
pag = new Pagination("em_eventrequests_pagination", templates.pagination, ".requestpag", limit, load_requests);
};
let load_requests = function(offset, args){
if(offset === undefined || !Number.isInteger(offset)){
offset = 0;
}
old_offset = offset;
if(args === undefined){
args = "";
}
$.ajax({
type: "GET",
url: "/api/event_requests?limit=" + limit + "&offset=" + offset+args,
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
},
success: async function (data) {
if (is_ok(data)) {
console.log(data);
$("#requestlist_accordion").html("");
let loading_queue = [];
$(data.requests).each(function(){
loading_queue.push(load_request(this))
});
async function load_request(request){
if(request.etype){
let types = [];
$(event_types).each(function(){
let type = {};
type.type_id = this.type_id;
type.name = this.name;
type.description = this.description;
type.is_billable = this.is_billable;
if(type.type_id === request.etype){
type.active = true; //mark etype for request as active
}else{
type.active = false;
}
types.push(type);
});
request.etypes = types;
}
if(request.organiser_id){
request.organiser = await get_organiser(request.organiser_id);
}
if(request.risk_potential){
if(request.risk_potential === 1){
request.risk_potential_low = true;
} else if(request.risk_potential === 2){
request.risk_potential_medium = true;
}else if(request.risk_potential === 3){
request.risk_potential_high = true;
}
}
if(request.state !== 0){
if(request.state === 1){
request.state_1 = true;
}
if(request.state === 3){
request.state_3 = true;
}
if(request.state === 5){
request.enable_assignment = true;
request.state_5 = true;
}
if(request.state === 7){
request.enable_assignment = true;
request.state_7 = true;
}
}
let date = new Date(request.received);
request.received = ('0' + date.getDate()).slice(-2) + '.' + ('0' + (date.getMonth()+1)).slice(-2) + '.' + date.getFullYear() + ' ' + ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2);
request.osearch_base = request.entity_id+"_organiser_search";
let gwap = [];
$(groups_with_assign_pem).each(function(){
let temp = {};
temp.id = this.id;
temp.name = this.name;
temp.description = this.description;
if(this.id === request.related_group){
temp.active = true;
}else{
temp.active = false;
}
gwap.push(temp);
});
request.groups_with_assign_permission = gwap;
return request;
}
await $.when.apply($, loading_queue).then(function(){
var objects = arguments;
$(objects).each(function(){
$("#requestlist_accordion").append(templates.em_request_card(this));
add_organiser_search(this);
})
})
$(".edit_request_button").off("click").on("click", enable_edit);
pag.render(data.total_request_count, offset);
}
}
});
};
let get_group_caller_has_asign_permissions = async function(){
let res = [];
let groups_caller_has_assign_permissions = await get_caller_permission_context("modules.event_management.requests.assignments.assign");
let groups = await load_groups_async();
$(groups).each(function(){
let group = this;
if(groups_caller_has_assign_permissions.indexOf(group.group_id) !== -1){
let temp = {};
temp.name = group.name;
temp.description = group.description;
temp.id = group.group_id;
res.push(temp);
}
})
return res;
}
let enable_edit = function(){
let card = $(this).closest('.card-body');
card.find(".edit_request_button").text("Speichern").off("click").on("click", save_change);
card.find(":input[readonly='readonly']").removeAttr("readonly").removeAttr("disabled");
};
let save_change = function(){
let card = $(this).closest('.card-body');
let entity_id = $(this).data("request-id");
let name = $("#"+entity_id+"-name").val();
let etype = $("#"+entity_id+"-etype").val();
let timescale = $("#"+entity_id+"-timescale").val();
let site = $("#"+entity_id+"-site").val();
let risk_potential = $("#"+entity_id+"-risk_potential").val();
let expected_attendees = $("#"+entity_id+"-expected_attendees").val();
let specials = $("#"+entity_id+"-specials").val();
let requested = $("#"+entity_id+"-requested").val();
let organiser = $("#"+entity_id+"_organiser_search").data("entity-id");
let contact_on_site_name = $("#"+entity_id+"-contact_on_site_name").val();
let contact_on_site_phone = $("#"+entity_id+"-contact_on_site_phone").val();
let state = $("#"+entity_id+"-state").val();
let assignment = $("#"+entity_id+"-assignment").val();
let er = {};
er.entity_id = entity_id;
if(name !== ""){
er.name = name;
}
if(etype !== ""){
er.etype = etype;
}
if(timescale !== ""){
er.timescale = timescale;
}
if(site !== ""){
er.site = site;
}
if(risk_potential !== ""){
er.risk_potential = risk_potential;
}
if(expected_attendees !== "") {
er.expected_attendees = expected_attendees;
}
if(specials !== "") {
er.specials = specials;
}
if(requested !== "") {
er.requested = requested;
}
if(organiser !== ""){
er.organiser_id = organiser;
}
if(contact_on_site_name !== ""){
er.contact_on_site_name = contact_on_site_name;
}
if(contact_on_site_phone !== ""){
er.contact_on_site_phone = contact_on_site_phone;
}
if(state !== ""){
er.state = state;
if(state === "5" && assignment !== "" && assignment !== "none"){ //update state to request taken by group if related group is set and status is request published
er.state = "7";
}else{
console.log("ass:"+assignment+"state:"+state);
}
}
console.log(assignment);
if(assignment !== "" && assignment !== "none" && assignment != null){
er.related_group = assignment;
}
console.log(er);
$.ajax({
type: "PUT",
url: "/api/event_requests/" + entity_id,
data: JSON.stringify(er),
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
},
success: async function (data) {
if (is_ok(data)) {
window.location.reload(true)
//card.find(".edit_request_button").text("Bearbeiten").off("click").on("click", enable_edit);
//card.find(":input").attr("readonly", true).attr("disabled", true);
}
}
});
};
let add_organiser_search = function(request){
let delete_callback = function(){
};
var val = request.organiser_id;
var val_name = null;
if(request.organiser){
val_name = request.organiser.company;
if(request.organiser.firstname && request.organiser.lastname){
val_name += ": "+request.organiser.firstname+" "+request.organiser.lastname
}
}
var organiser_search = new MiniSearchbar(request.entity_id+"_organiser_search", null, val, val_name, delete_callback);
organiser_search.setup($("#"+this.entity_id+"_organiser_search"));
};
return{
load_requests: load_requests,
load_templates: load_templates,
setup_pagination: setup_pagination,
prepare: prepare,
save_change: save_change,
}
}());

View File

@ -17,6 +17,21 @@ function is_ok(data){
}
}
let get_event_type = async function(type_id){
const res = await $.ajax({
type: "GET",
url: "/api/events/types/" + type_id,
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Verbindung zum Server unterbrochen!");
},
});
if (is_ok(res)) {
return res.name;
}
}
function check_for_permission(callback, permission, entity_id){
let optional_entity = "";
if(entity_id){
@ -74,6 +89,21 @@ let get_member = async function (entity_id){
}
};
let get_members = async function (){
const res = await $.ajax({
type: "GET",
url: "/api/members?limit=10000&offset=0&sort=firstname:asc",
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Verbindung zum Server unterbrochen!");
},
});
if (is_ok(res)) {
return res;
}
};
let get_vehicle = async function (entity_id){
const res = await $.ajax({
type: "GET",
@ -93,6 +123,20 @@ let get_vehicle = async function (entity_id){
}
}
};
let get_vehicles = async function (){
const res = await $.ajax({
type: "GET",
url: "/api/resources/vehicles?entries=10000",
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Verbindung zum Server unterbrochen!");
},
});
if (is_ok(res)) {
return res;
}
};
let load_positions_for_instance = async function(instance_id){
const res = await $.ajax({
url: '/api/events/instances/'+instance_id+'/positions',
@ -140,7 +184,7 @@ let remove_instance = function(){
alert("Es ist ein Fehler aufgetreten!");
}
});
}
};
let add_entity_to_position = function(instance, position, entity){
$.ajax({
url: '/api/events/instances/'+instance+'/positions/'+position+'/entities/'+entity,
@ -188,7 +232,7 @@ let get_organiser = async function(entity_id){
}
};
let load_event_types = function(callback){
let load_event_types_legacy = function(callback){
$.ajax({
url: '/api/events/types',
type: 'GET',
@ -204,6 +248,20 @@ let load_event_types = function(callback){
}
});
};
let load_event_types = async function(){
const res = await $.ajax({
url: '/api/events/types',
type: 'GET',
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Verbindung zum Server unterbrochen!");
}
});
if(is_ok(res)){
return res;
}
};
let load_groups = function(callback){
$.ajax({
url: '/api/groups',
@ -264,6 +322,21 @@ let check_position_requirements = async function(position_id, member_id){
}
};
let get_caller_permission_context = async function(permission){
const res = await $.ajax({
url: '/api/info/caller/permission_context?permission='+permission,
type: 'GET',
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
if(is_ok(res)){
return res
}
}
$(document).ajaxStart(function() {
$(".loading_animation").show();
});

View File

@ -0,0 +1,294 @@
active_permission = null;
$(document).ready(async function () {
$("#settings_permissions_role").on("change", async function () {
await RequestListModule.load_role();
});
await RequestListModule.load_templates();
});
RequestListModule = (function () {
let templates = {};
let load_templates = async function () {
const permission_list = $.get("/templates/settings_role_permissions.hbs");
const context_list = $.get("/templates/settings_role_permissions_context.hbs");
await Promise.all([permission_list, context_list]).then(function (res) {
templates.permission_list = Handlebars.compile(res[0]);
templates.context_list = Handlebars.compile(res[1]);
});
};
let permission_checkbox_toggle = async function () {
let checkbox = $(this);
if (checkbox.prop("checked")) {
await add_permission_to_role($("#settings_permissions_role").val(), checkbox.data("permission"));
} else {
await remove_permission_from_role($("#settings_permissions_role").val(), checkbox.data("permission"));
}
};
let permission_context_checkbox_toggle = async function () {
console.log("removing/adding context")
let checkbox = $(this);
if (checkbox.prop("checked")) {
await add_context($("#settings_permissions_role").val(), active_permission, checkbox.data("context-id"));
} else {
await remove_context($("#settings_permissions_role").val(), active_permission, checkbox.data("context-id"));
}
};
let add_permission_to_role = async function (role, permission) {
const res = $.ajax({
type: "POST",
url: "/api/roles/" + role + "/permissions/" + permission,
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
if (is_ok(res)) {
return res;
}
};
let remove_permission_from_role = async function (role, permission) {
const res = $.ajax({
type: "DELETE",
url: "/api/roles/" + role + "/permissions/" + permission,
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
if (is_ok(res)) {
return res;
}
};
let add_context = async function (role, permission, context) {
const res = $.ajax({
type: "POST",
url: "/api/roles/" + role + "/permissions/" + permission+"/context/"+context,
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
if (is_ok(res)) {
return res;
}
};
let remove_context = async function (role, permission, context) {
const res = $.ajax({
type: "DELETE",
url: "/api/roles/" + role + "/permissions/" + permission+"/context/"+context,
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
if (is_ok(res)) {
return res;
}
};
let get_permissions = async function () {
const res = $.ajax({
type: "GET",
url: "/api/permissions/",
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
if (is_ok(res)) {
return res;
}
};
let combine_permissions = function (all_permissions, positive_permissions) {
let res = [];
function is_in_list(permission, list) {
for (let i = 0; i < list.length; i++) {
if (permission === list[i].permission) {
return true;
}
}
return false;
}
$(all_permissions).each(function () {
let permission = this.permission;
let temp = {};
temp.permission = permission;
temp.description = this.description;
temp.context = this.context;
temp.context_type = this.context_type;
temp.has_permission = is_in_list(permission, positive_permissions);
if (this.context === false) {
temp.context_combined = "ohne";
}
if (this.context === true) {
temp.context_combined = this.context_type;
}
res.push(temp);
});
return res;
};
let load_role = async function () {
let role = $("#settings_permissions_role").val();
let permission_card = $(".settings_permissions_permission_card");
if (role !== "none") {
$.ajax({
type: "GET",
url: "/api/roles/" + role + "/permissions/",
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
},
success: async function (data) {
if (is_ok(data)) {
console.log(data);
let res = {};
let all_permissions = await get_permissions();
res.permissions = combine_permissions(all_permissions, data);
console.log(res.permissions);
permission_card.html(templates.permission_list(res));
$(".permission_checkbox").off("change").on("change", RequestListModule.permission_checkbox_toggle);
$(".permission_row").off("click").on("click", load_context);
}
}
});
} else {
permission_card.html("Bitte Rolle wählen.");
}
};
let load_context = async function () {
let role = $("#settings_permissions_role").val();
let permission = $(this).parent().find(".permission_checkbox").data("permission");
active_permission = permission;
let has_context = $(this).parent().data("context");
let context_type = $(this).parent().data("context-type");
if(has_context) {
let res = {};
let context = await get_context(role, permission);
if(context_type === "groups" || context_type === "members" || context_type === "entity"){
let tempres = [];
let groups = await load_groups_async();
for(let i=0;i<groups.length;i++){
let temp = {};
temp.group_id = groups[i].group_id;
temp.name = groups[i].name;
temp.description = groups[i].description;
temp.has_this_context = false;
for(let b=0;b<context.length;b++){
if(!temp.has_this_context){
if(groups[i].group_id === context[b]){
temp.has_this_context = true;
break;
}
}
}
tempres.push(temp);
}
res.groups = tempres;
}
if(context_type === "vehicles" || context_type === "entity"){
let tempres = [];
let vehicles = (await get_vehicles()).vehicle_list;
for(let i=0;i<vehicles.length;i++){
let temp = {};
temp.entity_id = vehicles[i].entity_id;
temp.identifier = vehicles[i].identifier;
temp.description = vehicles[i].description;
temp.numberplate = vehicles[i].numberplate;
temp.has_this_context = false;
for(let b=0;b<context.length;b++){
if(!temp.has_this_context){
if(vehicles[i].entity_id === context[b]){
temp.has_this_context = true;
break;
}
}
}
tempres.push(temp);
}
res.vehicles = tempres;
}
if(context_type === "members" || context_type === "entity"){
let tempres = [];
let members = (await get_members()).members;
console.log(members);
for(let i=0;i<members.length;i++){
let temp = {};
temp.entity_id = members[i].entity_id;
temp.firstname = members[i].firstname;
temp.lastname = members[i].lastname;
temp.has_this_context = false;
for(let b=0;b<context.length;b++){
if(!temp.has_this_context){
if(members[i].entity_id === context[b]){
temp.has_this_context = true;
break;
}
}
}
tempres.push(temp);
}
res.members = tempres;
}
$(".settings_permissions_permission_context_card").html(templates.context_list(res));
$(".permission_context_checkbox").off("click").on("click", permission_context_checkbox_toggle);
}else{
$(".settings_permissions_permission_context_card").html("Kein Kontext");
}
};
let get_context = async function(role, permission){
const res = $.ajax({
type: "GET",
url: "/api/roles/" + role + "/permissions/" + permission + "/context",
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
if (is_ok(res)) {
return res;
}
}
return {
load_templates: load_templates,
load_role: load_role,
permission_checkbox_toggle: permission_checkbox_toggle,
}
}());

View File

@ -0,0 +1,184 @@
active_role = null;
$(document).ready(async function () {
await SettingsRoleModule.load_templates();
await SettingsRoleModule.load_role_list();
SettingsRoleModule.prepare();
});
SettingsRoleModule = (function () {
let templates = {};
let roles_list = [];
let load_templates = async function () {
const role_list = $.get("/templates/settings_roles.hbs");
const member_row = $.get("/templates/settings_roles_member_row.hbs");
const search = $.get("/templates/search.hbs");
await Promise.all([role_list, member_row, search]).then(function (res) {
templates.role_list = Handlebars.compile(res[0]);
templates.member_row = Handlebars.compile(res[1]);
templates.search = res[2];
});
Handlebars.registerPartial('search', templates.search);
};
let prepare = function(){
var member_search = new MiniSearchbar("role_member_search", add_member_to_role_listener, null, null, null);
member_search.setup($("#role_member_search"));
$(".role-detailed-view-remove-member-button").on("click", delete_members_listener);
$(".role_detailed_view_submit_core_data").on("click", update_core_data_listener);
};
let add_member_to_role_listener = async function(res){
let member_id = $(res).data("entity-id");
let firstname = $(res).data("firstname");
let lastname = $(res).data("lastname");
if(is_ok(await add_member_to_role(active_role, member_id))){
await load_role_list();
$("tr").each(function(){
if($(this).data("role-id") === active_role){
$(this).click();
}
})
}
};
let add_member_to_role = async function(role, member){
const res = $.ajax({
type: "POST",
url: "/api/roles/"+role+"/members/"+member,
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
return res;
};
let load_roles = async function(){
const res = $.ajax({
type: "GET",
url: "/api/roles",
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
if (is_ok(res)) {
return res;
}
};
let load_role_members = async function(role){
const res = $.ajax({
type: "GET",
url: "/api/roles/"+role+"/members",
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
if (is_ok(res)) {
return res;
}
};
let load_role_list = async function(){
let roles = await load_roles();
let res = [];
for(let i=0;i<roles.length;i++){
let temp = {};
temp.id = roles[i].id;
temp.description = roles[i].description;
temp.members = await load_role_members(roles[i].id);
temp.member_count = temp.members.length;
res.push(temp);
}
$("#roles").html(templates.role_list(res));
roles_list = res;
$(".role-tr").off("click").on("click", function(){
let role_id = $(this).data("role-id");
active_role = role_id;
let role = null;
for(let i=0;i<roles_list.length;i++){
if(roles_list[i].id === role_id){
role = roles_list[i];
break;
}
}
$("#role_detailed_view_name_input").val(role.id);
$("#role_detailed_view_name_old").val(role.id);
$("#role_detailed_view_description_input").val(role.description);
$(".role_detailed_view_tbody").html(templates.member_row(role.members));
$(".role_detailed_view").removeAttr('hidden');
});
};
let delete_members_listener = async function(){
let delete_list = [];
$(".member_checkbox:checked").each(function(){
delete_list.push($(this).data("member-id"));
});
let promises = [];
$(delete_list).each(function(){
promises.push(remove_member_from_role(active_role, this));
});
await Promise.all(promises).then(async function (res) {
await load_role_list();
$("tr").each(function(){
if($(this).data("role-id") === active_role){
$(this).click();
}
})
});
};
let remove_member_from_role = async function(role, member){
const res = $.ajax({
type: "DELETE",
url: "/api/roles/"+role+"/members/"+member,
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
return res;
};
let update_core_data_listener = async function(){
let old_id = $("#role_detailed_view_name_old").val();
let updated = {};
updated.id = $("#role_detailed_view_name_input").val();
updated.description = $("#role_detailed_view_description_input").val();
if(is_ok(await update_core_data(old_id, updated))){
await load_role_list();
$("tr").each(function(){
if($(this).data("role-id") === active_role){
$(this).click();
}
})
}
};
let update_core_data = async function(old_id, role){
const res = $.ajax({
type: "PUT",
url: "/api/roles/"+old_id,
data: JSON.stringify(role),
contentType: 'application/json',
timeout: 3000,
error: function () {
alert("Es ist ein Fehler aufgetreten!");
}
});
return res;
};
return {
load_templates: load_templates,
load_role_list: load_role_list,
prepare: prepare,
}
}());

View File

@ -0,0 +1,18 @@
Hallo {{firstname}},
es wurde eine neue Einsatzanfrage in Einsatz Online eingetragen:
=====
Name: {{request.name}}
Zeitraum: {{request.timescale}}
Veranstaltungsraum: {{request.site}}
Veranstalter: {{organiser.company}} ({{organiser.firstname}} {{organiser.lastname}})
Angefordert: {{request.requested}}
Erwartete Besucher: {{request.expected_attendees}}
=====
Möchtest du die Anfrage für deine Gruppe annehmen? Dann melde dich bei {{frontpage}} an und ordne dir diese Einsatzanfrage unter Anfragen zu.
Möchtest du solche E-Mails nicht mehr erhalten? Dann wende dich bitte an {{support_email}}.

View File

@ -0,0 +1,146 @@
{{> header }}
<div class="container-fluid">
<div class="row">
<div class="wrapper">
{{> sidebar }}
<div id="content">
{{> searchbar}}
<hr>
<h1>Einsatzanfrage anlegen</h1>
<div class="col">
<form id="create_request_form">
<div class="row">
<div class="col-lg-6">
<div class="form-group row">
<label for="name" class="col-sm-3 col-form-label">Name*</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="name" required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Zeitraum</label>
<div class="col-sm-9">
<div class="row m-0">
<textarea class="form-control" rows="4" id="timescale"></textarea>
</div>
</div>
</div>
<div class="form-group row">
<label for="site" class="col-sm-3 col-form-label">Veranstaltungsort</label>
<div class="col-sm-9">
<textarea rows="4" class="form-control" id="site"></textarea>
</div>
</div>
<hr>
<h3>Ansprechpartner vor Ort</h3>
<div class="form-group row">
<label for="contact_on_site_name" class="col-sm-3 col-form-label">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="contact_on_site_name">
</div>
</div>
<div class="form-group row">
<label for="contact_on_site_phone"
class="col-sm-3 col-form-label">Telefonnummer</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="contact_on_site_phone">
</div>
</div>
</div>
<div class="col-lg-6">
<div class="form-group row">
<label for="etype" class="col-sm-3 col-form-label">Einsatzart</label>
<div class="col-sm-9">
<select class="form-control" id="etype">
</select>
</div>
</div>
<div class="form-group row">
<label for="add_event_organiser_search-searchbar" class="col-sm-3 col-form-label">Veranstalter</label>
<div class="col-sm-9">
<div id="create-event-organiser-search">
<div class="input-group">
<input type="text" class="form-control"
id="add_event_organiser_search-searchbar"
data-search-type="organiser">
<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"></use>
</svg>
</span>
</span>
</div>
<div class="add_event_organiser_search-search-result-overlay"
style="display: none;">
<ul class="add_event_organiser_search-search-result-overlay-list"></ul>
</div>
</div>
<div class="input-group" id="organiser_input_group" style="display: none">
<input type="text" disabled id="organiser" class="form-control">
<span class="input-group-append">
<span class="btn btn-outline-secondary"
id="organiser_remove"
type="button">
<svg width="16" height="16"
fill="currentColor">
<use xlink:href="/img/bootstrap-icons.svg#pencil-square"></use>
</svg>
</span>
</span>
</div>
</div>
</div>
<div class="form-group row">
<label for="risk_potential" class="col-sm-3 col-form-label">Risikopotential</label>
<div class="col-sm-9">
<select class="form-control" id="risk_potential">
<option value="1">geringes Risiko</option>
<option value="2">mittleres Risiko</option>
<option value="3">hohes Risiko</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="expected_attendees" class="col-sm-3 col-form-label">Erwartete Besucher</label>
<div class="col-sm-9">
<input id="expected_attendees" type="number" class="form-control">
</div>
</div>
<div class="form-group row">
<label for="specials" class="col-sm-3 col-form-label">Besonderheiten (z.B. Alkoholausschank)</label>
<div class="col-sm-9">
<div class="row m-0">
<textarea class="form-control" rows="4" id="specials"></textarea>
</div>
</div>
</div>
<div class="form-group row">
<label for="requested" class="col-sm-3 col-form-label">Angeforderte Einheiten</label>
<div class="col-sm-9">
<div class="row m-0">
<textarea class="form-control" rows="4" id="requested"></textarea>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<p>* Pflichtfelder</p>
<div class="col text-center">
<button type="button" class="btn btn-success" id="create_request_btn">Anfrage
anlegen
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{{> footer }}

View File

@ -0,0 +1,29 @@
{{> header }}
<div class="container-fluid">
<div class="row">
<div class="wrapper">
{{> sidebar }}
<div id="content">
{{> searchbar}}
<hr>
<div class="col">
<div class="form-group row align-items-center">
<h1 class="col-sm-4">Einsatzanfragen</h1>
</div>
<div class="card">
<div class="card-header">Einsatzanfragen</div>
<div class="card-body">
<input type="hidden" id="caller_entity_id" value="{{caller}}">
<div id="requestlist_accordion">
</div>
<br>
<div class="row requestpag">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{> footer }}

View File

@ -12,16 +12,34 @@
<label for="settings_permissions_role"
class="col-auto col-form-label font-weight-bold">Rolle:</label>
<div class="col-auto">
<select class="form-control" id="settings_permissions_role"></select>
<select class="form-control" id="settings_permissions_role">
<option value="none">Rolle auswählen</option>
{{#each roles}}
<option title="{{description}}">{{id}}</option>
{{/each}}
</select>
</div>
<div class="col-auto">
<div class="alert alert-info" role="alert">
Alle Änderungen werden sofort angewendet, daher gibt es keinen Speichern Button :)
</div>
</div>
</div>
</div>
<div class="col">
<div class="row">
<div class="col-6">
</div>
<div class="row">
<div class="col-lg-9">
<div class="card">
<div class="card-header">Rechte für Rolle bearbeiten</div>
<div class="card-body">Bitte Rolle wählen.</div>
<div class="card-body settings_permissions_permission_card"></div>
</div>
</div>
<div class="col-lg-3">
<div class="card sticky-top" style="top: 15px;">
<div class="card-header">Kontext</div>
<div class="card-body settings_permissions_permission_context_card"></div>
</div>
</div>
</div>

View File

@ -0,0 +1,105 @@
{{> header }}
{{> delete-roles-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">
<div class="card bg-light mb-3">
<div class="card-header">Rollen</div>
<div class="card-body" id="roles">
</div>
</div>
<div class="card bg-light mb-3">
<div class="card-header">Neue Rolle anlegen</div>
<div class="card-body" id="new_role">
<form>
<div class="form-role row">
<label for="new_role_name" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="new_role_name">
</div>
</div>
<div class="form-role row">
<label for="new_role_description" class="col-sm-2 col-form-label">Beschreibung</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="new_role_description">
</div>
</div>
<button type="button" class="btn btn-primary new_role_button" style="float: right">Rolle Hinzufügen</button>
</form>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card bg-light mb-3 role_detailed_view" hidden>
<div class="card-header">Rolle <span class="role_detailed_view_name"></span></div>
<div class="card-body" id="role_list">
<div class="role_detailed_view_core_data">
<div class="form-role row">
<label for="role_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="role_detailed_view_name_input">
<input type="hidden" id="role_detailed_view_name_old">
</div>
</div>
<div class="form-role row">
<label for="role_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="role_detailed_view_description_input">
</div>
</div>
<button type="button" class="role_detailed_view_submit_core_data btn btn-primary" style="float: right; margin-bottom:15px;">Änderungen Speichern</button>
</div>
<div class="role_detailed_view_member_list">
<table class="table">
<thead>
<tr>
<th><button class="iconbutton role_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="role_detailed_view_tbody"></tbody>
</table>
<div>
<button class="iconbutton role_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 role-detailed-view-remove-member-button">Entfernen</button>
</div><br>
<div class="role_detailed_view_add_member">
<div id="role-member-search">
<div class="input-group">
<input type="text" class="form-control" id="role_member_search-searchbar" data-search-type="member">
<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"></use>
</svg>
</span>
</span>
</div>
<div class="role_member_search-search-result-overlay" style="display: none;">
<ul class="role_member_search-search-result-overlay-list"></ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{> footer }}

View File

@ -70,6 +70,7 @@
<li><a href="/portal/em/organisers">Veranstalter</a></li>
<li><a href="/portal/em/eu_templates">Vorlagen</a></li>
<li><a href="/portal/em/eu_positions">Positionen</a></li>
<li><a href="/portal/em/requests">Anfragen</a></li>
</ul>
{{/if}}
</li>
@ -81,6 +82,7 @@
{{#if sidebar.settings.active}}
<ul>
<li><a href="/portal/settings/permissions">Rechtevergabe</a></li>
<li><a href="/portal/settings/roles">Rollen</a></li>
</ul>
{{/if}}
</li>
@ -97,6 +99,9 @@
<li>
<a href="/portal/em/add_event">Einsatz hinzufügen</a>
</li>
<li>
<a href="/portal/em/add_request">Anfrage hinzufügen</a>
</li>
{{/if}}
</ul>
<div class="versiontag">

View File

@ -1 +1 @@
v0.2-39-g18868d8
v0.2-53-g6bab82f

View File

@ -0,0 +1,94 @@
use crate::helper::settings::Settings;
use rocket::State;
use crate::database::model::event_requests::EventRequest;
use diesel::{RunQueryDsl, ExpressionMethods};
use crate::database::controller::connector::establish_connection;
use crate::diesel::QueryDsl;
pub fn add_event_request(settings: &State<Settings>, data: EventRequest) -> Result<EventRequest, diesel::result::Error>{
use crate::schema::event_requests::dsl::*;
let connection = establish_connection(settings);
match diesel::insert_into(event_requests).values(data).get_result(&connection){
Ok(er) => Ok(er),
Err(e) => {
error!("Couldn't create event request: {}", e);
Err(e)
}
}
}
pub fn get_event_request(settings: &State<Settings>, request_id: uuid::Uuid) -> Result<Option<EventRequest>, diesel::result::Error>{
use crate::diesel::OptionalExtension;
use crate::schema::event_requests::dsl::*;
let connection = establish_connection(settings);
match event_requests.filter(entity_id.eq(request_id)).get_result(&connection).optional(){
Ok(res) => Ok(res),
Err(e) => {
error!("Couldn't get event request: {}", e);
Err(e)
}
}
}
pub fn get_event_requests(settings: &State<Settings>, limit: i64, offset: i64, state2: Option<i16>) -> Result<Vec<EventRequest>, diesel::result::Error>{
use crate::schema::event_requests::dsl::*;
let connection = establish_connection(settings);
let mut query = event_requests.order(received.desc()).limit(limit).offset(offset).into_boxed();
match state2{
Some(state2) => {
query = query.filter(state.eq(state2));
}
None => {}
};
match query.get_results(&connection){
Ok(requestlist) => Ok(requestlist),
Err(e) => {
error!("Couldn't get event requests: {}", e);
Err(e)
}
}
}
pub fn get_event_request_count(settings: &State<Settings>, state2: Option<i16>) -> Result<i64, diesel::result::Error>{
use crate::schema::event_requests::dsl::*;
use diesel::dsl::count;
let connection = establish_connection(settings);
let mut query = event_requests.select(count(entity_id)).into_boxed();
match state2{
Some(state2) => {query = query.filter(state.eq(state2))},
None => {}
};
match query.get_result(&connection){
Ok(requestcount) => Ok(requestcount),
Err(e) => {
error!("Couldn't get event request count: {}", e);
Err(e)
}
}
}
pub fn change_event_request(settings: &State<Settings>, data: EventRequest) -> Result<EventRequest, diesel::result::Error>{
use crate::schema::event_requests::dsl::*;
let connection = establish_connection(settings);
match diesel::update(event_requests.filter(entity_id.eq(data.entity_id))).set(data).get_result(&connection){
Ok(er) => Ok(er),
Err(e) => {
error!("Couldn't update event request: {}", e);
Err(e)
}
}
}

View File

@ -74,13 +74,22 @@ pub fn get_event(settings: &State<Settings>, event_id: uuid::Uuid) -> Result<Eve
}
}
pub fn get_event_count(settings: &State<Settings>, startdate: NaiveDateTime, enddate: NaiveDateTime) -> Result<i64, diesel::result::Error>{
pub fn get_event_count(settings: &State<Settings>, startdate: NaiveDateTime, enddate: NaiveDateTime, groups: Option<Vec<uuid::Uuid>>) -> Result<i64, diesel::result::Error>{
use crate::schema::events::dsl::*;
use diesel::dsl::count;
let connection = establish_connection(settings);
match events.select(count(entity_id)).filter(start.ge(startdate)).filter(end.le(enddate)).get_result(&connection){
let mut query = events.select(count(entity_id)).filter(start.ge(startdate)).filter(end.le(enddate)).into_boxed();
match groups{
Some(groups) => {
query = query.filter(related_group.eq(any(groups)))
},
None => {}
};
match query.get_result(&connection){
Ok(eventcount) => Ok(eventcount),
Err(e) => {
error!("Couldn't get event count: {}", e);

View File

@ -1,6 +1,6 @@
use crate::database::controller::connector::establish_connection;
use crate::database::controller::roles::{
add_permission_context, get_role_permission_id, get_roles,
add_permission_context, get_role_permission_id_deprecated, get_roles,
};
use crate::helper::settings::Settings;
use crate::modules::api::groups::create::GroupRolePermission;
@ -17,7 +17,7 @@ pub fn add_group_role_permission(
role_id: String,
permission: &str,
) -> Result<(), diesel::result::Error> {
let role_permission_id = get_role_permission_id(settings, role_id, permission);
let role_permission_id = get_role_permission_id_deprecated(settings, role_id, permission);
match role_permission_id {
Some(id) => match add_permission_context(settings, id, group_id) {
Ok(_) => return Ok(()),

View File

@ -22,4 +22,6 @@ pub mod vehicles;
pub mod appointments;
pub mod entities;
pub mod organisers;
pub mod events;
pub mod events;
pub mod event_requests;
pub mod permissions;

View File

@ -0,0 +1,120 @@
use rocket::State;
use crate::helper::settings::Settings;
use crate::database::model::permissions::Permission;
use crate::database::controller::connector::establish_connection;
use diesel::{ExpressionMethods, JoinOnDsl, QueryDsl, RunQueryDsl, OptionalExtension, sql_query};
use crate::schema::members_roles::dsl::members_roles;
use diesel::sql_types::Text;
use diesel::pg::types::sql_types::Uuid;
/// Retrieves permission row for specified permission
///
/// # Returns:
// /// * Ok(Some([`crate::database::model::permissions::Permission`])) if permission found
// /// * Ok(None) if no permission found but query returned no error
// /// * Err([`diesel::result::Error`]) if query returned error
pub fn get_permission(settings: &State<Settings>, permission_str: &str) -> Result<Option<Permission>, diesel::result::Error>{
use crate::schema::permissions::dsl::*;
let connection = establish_connection(settings);
match permissions.filter(permission.eq(permission_str)).get_result(&connection).optional(){
Ok(per) => Ok(per),
Err(e) => {
error!("Couldn't get permission: {}", e);
Err(e)
}
}
}
/// Retrieve role_permission_id for specified permission/role combo
///
/// # Returns:
/// * Ok(Some(role_permission_id)) if role_permission_id found
/// * Ok(None) if no role_permission_id found but query succeeded
/// * Err([`diesel::result::Error`]) if query returned error
///
pub fn get_role_permission_id(settings: &State<Settings>, permission_str: &str, role: &str) -> Result<Option<uuid::Uuid>, diesel::result::Error>{
use crate::schema::roles_permissions::dsl::*;
let connection = establish_connection(settings);
match roles_permissions.filter(permission_id.eq(permission_str)).filter(role_id.eq(role)).select(role_permission_id).get_result(&connection).optional(){
Ok(rpi) => Ok(rpi),
Err(e) => {
error!("Couldn't get role permission id: {}", e);
Err(e)
}
}
}
pub fn get_role_permission_context(settings: &State<Settings>, role_permission_id: uuid::Uuid) -> Result<Vec<uuid::Uuid>, diesel::result::Error>{
use crate::schema::roles_permissions_context::dsl::{roles_permissions_context, entity};
use crate::schema::roles_permissions_context::dsl::role_permission_id as rpi;
let connection = establish_connection(settings);
match roles_permissions_context.filter( rpi.eq(role_permission_id)).select(entity).get_results(&connection){
Ok(res) => Ok(res),
Err(e) => {
error!("Couldn't get list of entities for role_permission_id: {}", e);
Err(e)
}
}
}
#[derive(QueryableByName, PartialEq, Debug)]
struct TempQuery {
#[sql_type = "Uuid"]
pub(crate) member_id: uuid::Uuid,
}
pub fn get_members_with_permission(settings: &State<Settings>, permission: &str) -> Result<Vec<uuid::Uuid>, diesel::result::Error>{
let connection = establish_connection(settings);
let res : Result<Vec<TempQuery>, diesel::result::Error> = sql_query("SELECT member_id FROM members_roles WHERE role_id IN (SELECT role_id FROM roles_permissions WHERE permission_id = $1);").bind::<Text, _>(permission
).get_results(&connection);
match res{
Ok(res) => Ok(res.iter().map(|tq|tq.member_id).collect()),
Err(e) => {
error!("Couldn't get members with permission: {}", e);
Err(e)
}
}
}
pub fn get_permissions(settings: &State<Settings>) -> Result<Vec<Permission>, diesel::result::Error>{
use crate::schema::permissions::dsl::*;
let connection =establish_connection(settings);
match permissions.load(&connection){
Ok(per) => Ok(per),
Err(e) => {
error!("Couldn't get permission list: {}", e);
Err(e)
}
}
}
pub fn add_permission_to_role(settings: &State<Settings>, permission: &str, role: &str) -> Result<uuid::Uuid, diesel::result::Error>{
use crate::schema::roles_permissions::dsl::*;
let connection = establish_connection(settings);
match diesel::insert_into(roles_permissions).values((&role_id.eq(role), &permission_id.eq(permission))).returning(role_permission_id).get_result(&connection) {
Ok(res) => Ok(res),
Err(e) => {error!("Couldn't add permission to role: {}", e); Err(e)}
}
}
pub fn remove_permission_from_role(settings: &State<Settings>, permission: &str, role: &str) -> Result<(), diesel::result::Error>{
use crate::schema::roles_permissions::dsl::*;
let connection = establish_connection(settings);
match diesel::delete(roles_permissions).filter(permission_id.eq(permission)).filter(role_id.eq(role)).execute(&connection) {
Ok(_) => Ok(()),
Err(e) => {error!("Couldn't remove permission from role: {}", e); Err(e)}
}
}

View File

@ -1,10 +1,11 @@
use crate::database::controller::connector::establish_connection;
use crate::database::model::roles::Role;
use crate::diesel::query_dsl::select_dsl::SelectDsl;
use crate::helper::settings::Settings;
use diesel::query_dsl::filter_dsl::FilterDsl;
use diesel::{BoolExpressionMethods, ExpressionMethods, RunQueryDsl};
use diesel::{BoolExpressionMethods, ExpressionMethods, RunQueryDsl, QueryDsl, JoinOnDsl};
use rocket::State;
use crate::database::model::permissions::Permission;
use crate::modules::api::members::get_member::MemberSearchResult;
use crate::database::model::api_members::RawMemberSearchResult;
pub fn get_roles(settings: &State<Settings>) -> Result<Vec<Role>, diesel::result::Error> {
use crate::schema::roles::dsl::*;
@ -19,7 +20,32 @@ pub fn get_roles(settings: &State<Settings>) -> Result<Vec<Role>, diesel::result
}
}
pub fn get_role_permission_id(
pub fn get_roles_for_member(settings: &State<Settings>, member: uuid::Uuid) -> Result<Vec<String>, diesel::result::Error>{
use crate::schema::members_roles::dsl::*;
let connection = establish_connection(settings);
match members_roles.filter(member_id.eq(member)).select(role_id).get_results(&connection){
Ok(res) => Ok(res),
Err(e) => {error!("Couldn't get roles for member: {}", e); Err(e)}
}
}
pub fn get_members_for_role(settings: &State<Settings>, role: &str) -> Result<Vec<RawMemberSearchResult>, diesel::result::Error>{
use crate::schema::members_roles::dsl::*;
use crate::schema::members::dsl::*;
let connection = establish_connection(settings);
match members_roles.inner_join(members.on(member_id.eq(entity_id))).filter(role_id.eq(role)).select((crate::schema::members::dsl::entity_id, firstname, lastname)).get_results(&connection){
Ok(res) => Ok(res),
Err(e) => {error!("Couldn't get members in role: {}", e); Err(e)}
}
}
/// DEPRECATED DO NOT USE, WILL BE REMOVED SOON
/// Use [crate::database::controller:permissions::get_role_permission_id] instead!
pub fn get_role_permission_id_deprecated(
settings: &State<Settings>,
role_id2: String,
permission: &str,
@ -89,6 +115,26 @@ pub fn add_permission_context(
}
}
pub fn remove_permission_context(
settings: &State<Settings>,
role_permission_id2: uuid::Uuid,
context: uuid::Uuid,
) -> Result<(), diesel::result::Error> {
use crate::schema::roles_permissions_context::dsl::*;
let connection = establish_connection(settings);
let deleted = diesel::delete(roles_permissions_context)
.filter(
role_permission_id.eq(role_permission_id2)).filter(entity.eq(context)).execute(&connection);
match deleted {
Ok(_) => Ok(()),
Err(e) => {
error!("Couldn't delete permission context: {}", e);
Err(e)
}
}
}
pub fn add_member_to_role(settings: &State<Settings>, member_uuid: uuid::Uuid, role: String) -> Result<(), diesel::result::Error>{
use crate::schema::members_roles::dsl::*;
let connection = establish_connection(settings);
@ -100,4 +146,45 @@ pub fn add_member_to_role(settings: &State<Settings>, member_uuid: uuid::Uuid, r
Err(e)
}
}
}
pub fn remove_member_from_role(settings: &State<Settings>, member_uuid: uuid::Uuid, role: String) -> Result<(), diesel::result::Error>{
use crate::schema::members_roles::dsl::*;
let connection = establish_connection(settings);
match diesel::delete(members_roles).filter(member_id.eq(member_uuid)).filter(role_id.eq(role)).execute(&connection){
Ok(_) => Ok(()),
Err(e) => {
error!("Couldn't remove member from role: {}", e);
Err(e)
}
}
}
pub fn get_permissions_for_role(settings: &State<Settings>, role: String) -> Result<Vec<Permission>, diesel::result::Error>{
use crate::schema::permissions::dsl::*;
let connection = establish_connection(settings);
match crate::schema::roles_permissions::dsl::roles_permissions.inner_join(permissions.on(crate::schema::roles_permissions::dsl::permission_id.eq(permission))).filter(crate::schema::roles_permissions::dsl::role_id.eq(role)).select((permission, description, context, context_type)).get_results(&connection){
Ok(res) => Ok(res),
Err(e) => {
error!("Couldn't get permissions for role: {}", e);
Err(e)
}
}
}
pub fn update_role(settings: &State<Settings>, role_id: String, role: Role) -> Result<Role, diesel::result::Error>{
use crate::schema::roles::dsl::*;
let connection = establish_connection(settings);
match diesel::update(roles).filter(id.eq(role_id)).set(role).get_result(&connection){
Ok(res) => Ok(res),
Err(e) => {
error!("Couldn't update role: {}", e);
Err(e)
}
}
}

View File

@ -0,0 +1,40 @@
use crate::schema::event_requests;
use diesel::sql_types::{Uuid, Text, Nullable, Integer, SmallInt, Timestamp};
use chrono::NaiveDateTime;
#[derive(Queryable, Clone, Deserialize, Serialize, AsChangeset, Insertable, QueryableByName)]
#[table_name = "event_requests"]
#[changeset_options(treat_none_as_null = "true")]
#[primary_key(entity_id)]
pub struct EventRequest{
#[sql_type = "Uuid"]
pub(crate) entity_id: uuid::Uuid,
#[sql_type = "Text"]
pub(crate) name: String,
#[sql_type = "Nullable<Text>"]
pub(crate) timescale: Option<String>,
#[sql_type = "Nullable<Text>"]
pub(crate) site: Option<String>,
#[sql_type = "Nullable<Uuid>"]
pub(crate) organiser_id: Option<uuid::Uuid>,
#[sql_type = "Nullable<Uuid>"]
pub(crate) etype: Option<uuid::Uuid>,
#[sql_type = "Nullable<Uuid>"]
pub(crate) related_group: Option<uuid::Uuid>,
#[sql_type = "Nullable<Text>"]
pub(crate) contact_on_site_name: Option<String>,
#[sql_type = "Nullable<Text>"]
pub(crate) contact_on_site_phone: Option<String>,
#[sql_type = "Nullable<Text>"]
pub(crate) specials: Option<String>,
#[sql_type = "Nullable<Text>"]
pub(crate) requested: Option<String>,
#[sql_type = "Nullable<Integer>"]
pub(crate) expected_attendees: Option<i32>,
#[sql_type = "Nullable<Integer>"]
pub(crate) risk_potential: Option<i32>,
#[sql_type = "SmallInt"]
pub(crate) state: i16,
#[sql_type = "Timestamp"]
pub(crate) received: NaiveDateTime,
}

View File

@ -5,7 +5,7 @@ use crate::schema::eu_positions;
use crate::schema::eu_templates;
use crate::schema::eu_instances;
use crate::schema::eu_vehicle_positions;
use diesel::sql_types::{Uuid, Text, Nullable, Jsonb, Timestamp};
use diesel::sql_types::{Uuid, Text, Nullable, Jsonb, Timestamp, SmallInt};
#[derive(Queryable, Clone, Deserialize, Serialize, AsChangeset, Insertable, QueryableByName)]
#[table_name = "events"]
@ -38,6 +38,10 @@ pub struct Event{
pub(crate) other: Option<String>,
#[sql_type = "Nullable<Text>"]
pub(crate) other_intern: Option<String>,
#[sql_type = "Nullable<Uuid>"]
pub(crate) related_request: Option<uuid::Uuid>,
#[sql_type = "SmallInt"]
pub(crate) state: i16,
}
#[derive(Queryable, Clone, Deserialize, Serialize, AsChangeset, Insertable)]

View File

@ -12,4 +12,6 @@ pub mod login_protection;
pub mod vehicles;
pub mod appointments;
pub mod organisers;
pub mod events;
pub mod events;
pub mod event_requests;
pub mod permissions;

View File

@ -0,0 +1,11 @@
use crate::schema::permissions;
#[derive(AsChangeset, Queryable, Clone, Deserialize, Serialize, Insertable)]
#[table_name = "permissions"]
#[primary_key(permission)]
pub struct Permission{
pub permission: String,
pub description: Option<String>,
pub context: bool,
pub context_type: Option<String>,
}

View File

@ -4,6 +4,6 @@ use crate::schema::roles;
#[table_name = "roles"]
#[primary_key(id)]
pub struct Role {
pub(crate) id: String,
pub(crate) description: Option<String>,
pub id: String,
pub description: Option<String>,
}

View File

@ -92,7 +92,15 @@ fn rocket() -> _ {
{
Ok(_) => {}
Err(e) => {
error!("Couldn't register mail templates: {}", e);
error!("Couldn't register mail template: {}", e);
std::process::exit(1);
}
}
match mail_templates.registry.register_template_file("new_event_request_published-de", "resources/mail_templates/new_event_request_published-de.hbs")
{
Ok(_) => {}
Err(e) => {
error!("Couldn't register mail template: {}", e);
std::process::exit(1);
}
}
@ -162,6 +170,7 @@ fn rocket() -> _ {
modules::api::appointments::read::read_appointments_for_entity,
modules::api::appointments::delete::delete_appointment,
modules::api::info::caller::check_caller_has_permission,
modules::api::info::caller::get_caller_permission_context,
modules::event_management::eventlist::eventlist,
modules::event_management::organisers::organisers,
modules::event_management::add_event::add_event,
@ -212,6 +221,24 @@ fn rocket() -> _ {
modules::admin_settings::permissions::settings_permissions,
modules::api::members::get_member::api_member_list,
modules::api::events::read::check_event_cast_status,
modules::event_management::requests::request_list,
modules::event_management::add_request::add_request,
modules::api::events::requests::create::create_event_request,
modules::api::events::requests::read::read_event_requests,
modules::api::events::requests::update::update_event_request,
modules::api::permissions::roles::read::get_role_permissions,
modules::api::permissions::read::get_permissions,
modules::api::permissions::roles::update::api_add_permission_to_role,
modules::api::permissions::roles::update::api_remove_permission_from_role,
modules::api::permissions::roles::read::get_context_for_permission_role,
modules::api::permissions::roles::update::api_add_context,
modules::api::permissions::roles::update::api_remove_context,
modules::admin_settings::roles::settings_roles,
modules::api::permissions::roles::read::get_all_roles,
modules::api::permissions::roles::read::get_role_members,
modules::api::permissions::roles::update::api_add_member_to_role,
modules::api::permissions::roles::update::api_remove_member_from_role,
modules::api::permissions::roles::update::api_update_role,
],
)
.mount("/css", FileServer::from("resources/css"))

View File

@ -1 +1,2 @@
pub mod permissions;
pub mod permissions;
pub mod roles;

View File

@ -6,6 +6,8 @@ use rocket::http::Status;
use crate::helper::sitebuilder::model::sidebar::Sidebar;
use crate::helper::sitebuilder::model::general::{Header, Stylesheet, Footer, Script};
use rocket_dyn_templates::Template;
use crate::database::model::roles::Role;
use crate::database::controller::roles::get_roles;
#[derive(Serialize)]
pub struct SettingsModule {
@ -13,6 +15,7 @@ pub struct SettingsModule {
pub footer: Footer,
pub sidebar: Sidebar,
pub caller: uuid::Uuid,
pub roles: Vec<Role>
}
#[get("/portal/settings/permissions")]
@ -45,12 +48,18 @@ pub fn settings_permissions(cookie: SessionCookie, settings: &State<Settings>) -
};
let mut sidebar = Sidebar::new(member.clone());
sidebar.settings.active = true;
let roles = match get_roles(settings){
Ok(roles) => roles,
Err(e) => {error!("Couldn't get roles!");return Err(Status::InternalServerError)}
};
let module = SettingsModule{
let module = SettingsModule {
header,
footer,
sidebar,
caller: member.entity_id
caller: member.entity_id,
roles
};
Ok(Template::render("module_settings_permissions", module))

View File

@ -0,0 +1,58 @@
use crate::helper::session_cookies::model::SessionCookie;
use rocket::State;
use crate::helper::settings::Settings;
use rocket::http::Status;
use crate::helper::sitebuilder::model::sidebar::Sidebar;
use crate::helper::sitebuilder::model::general::{Header, Stylesheet, Footer, Script};
use rocket_dyn_templates::Template;
use crate::database::model::roles::Role;
use crate::database::controller::roles::get_roles;
use crate::modules::admin_settings::permissions::SettingsModule;
#[get("/portal/settings/roles")]
pub fn settings_roles(cookie: SessionCookie, settings: &State<Settings>) -> Result<Template, Status> {
let member = match cookie.member {
//Unwraps member from cookie or send user to login if no member specified (user skipped member selection)
Some(member) => member,
None => return Err(Status::Unauthorized),
};
if !member.has_permission(
crate::permissions::modules::event_management::events::VIEW.to_string(),
) {
return Err(Status::Forbidden);
}
let header = Header {
html_language: "de".to_string(),
site_title: "Rollen".to_string(),
stylesheets: vec![Stylesheet {
path: "/css/errms.css".to_string(),
}],
};
let footer = Footer {
scripts: vec![Script {
path: "/js/settings_roles.js".to_string(),
}, Script {
path: "/js/mini_searchbar.js".to_string(),
}],
};
let mut sidebar = Sidebar::new(member.clone());
sidebar.settings.active = true;
let roles = match get_roles(settings){
Ok(roles) => roles,
Err(e) => {error!("Couldn't get roles!");return Err(Status::InternalServerError)}
};
let module = SettingsModule {
header,
footer,
sidebar,
caller: member.entity_id,
roles
};
Ok(Template::render("module_settings_roles", module))
}

View File

@ -79,7 +79,9 @@ pub fn create_event(
member_responsible: parse_option_uuid(ecd.member_responsible)?,
related_group: parse_option_uuid(ecd.related_group)?,
other: ecd.other,
other_intern: ecd.other_intern
other_intern: ecd.other_intern,
related_request: None,
state: 0
};
match add_event(settings, input){

View File

@ -5,3 +5,4 @@ pub mod read;
pub mod event_units;
pub mod types;
pub mod instances;
pub mod requests;

View File

@ -79,11 +79,11 @@ pub fn read_events(
None => None,
};
let events = match get_events(settings, start, end, limit, offset, groups){
let events = match get_events(settings, start, end, limit, offset, groups.clone()){
Ok(events) => events,
Err(e) => return Err(translate_diesel(e)),
};
let total_event_count = match get_event_count(settings, start, end){
let total_event_count = match get_event_count(settings, start, end, groups){
Ok(count) => count,
Err(e) => return Err(translate_diesel(e)),
};

View File

@ -0,0 +1,114 @@
use rocket::State;
use crate::helper::settings::Settings;
use crate::helper::session_cookies::model::SessionCookie;
use rocket::serde::json::Json;
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
use crate::modules::api::member_management::controller::parser::{parse_member_cookie, parse_option_uuid};
use crate::database::model::event_requests::EventRequest;
use crate::database::controller::entities::generate_entity;
use diesel::sql_types::Integer;
use crate::helper::translate_diesel_error::translate_diesel;
use crate::database::controller::event_requests::add_event_request;
use chrono::{NaiveDateTime, ParseError, Local};
use std::str::FromStr;
#[derive(Queryable, Clone, Deserialize, Serialize)]
pub struct RequestData {
pub(crate) entity_id: Option<String>,
pub(crate) name: String,
pub(crate) timescale: Option<String>,
pub(crate) site: Option<String>,
pub(crate) organiser_id: Option<String>,
pub(crate) etype: Option<String>,
pub(crate) contact_on_site_name: Option<String>,
pub(crate) contact_on_site_phone: Option<String>,
pub(crate) related_group: Option<String>,
pub(crate) specials: Option<String>,
pub(crate) requested: Option<String>,
pub(crate) expected_attendees: Option<String>,
pub(crate) risk_potential: Option<String>,
pub(crate) received: Option<String>,
pub(crate) state: Option<String>,
}
#[post("/api/event_requests", format = "json", data = "<create_request_data>")]
pub fn create_event_request(
settings: &State<Settings>,
cookie: SessionCookie,
create_request_data: Json<RequestData>,
) -> Result<Json<EventRequest>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::event_management::requests::CREATE.to_string()) {
return Err(Json(
ApiError::new(403, "Keine Berechtigung, neue Einsatzanfragen anzulegen!".to_string()).to_wrapper(),
));
}
let crd = create_request_data.into_inner();
let entity_id = match generate_entity(settings){
Ok(ent) => ent,
Err(_e) => return Err(Json(ApiError::new(500, "Konnte keine neue Entität anlegen!".to_string()).to_wrapper()))
};
let risk_potential : Option<i32> = match crd.risk_potential{
Some(rp) => match rp.parse() {
Ok(rp) => Some(rp),
Err(e) => {
warn!("Couldn't parse risk potential for new request: {}", e);
return Err(Json(ApiError::new(500, "couldn't parse risk potential".to_string()).to_wrapper()))
}
},
None => None,
};
let expected_attendees : Option<i32> = match crd.expected_attendees{
Some(ea) => match ea.parse() {
Ok(ea) => Some(ea),
Err(e) => {
warn!("Couldn't parse expected attendees for new request: {}", e);
return Err(Json(ApiError::new(500, "couldn't parse expected attendees".to_string()).to_wrapper()))
}
},
None => None,
};
let received = match crd.received{
Some(mut rec) => {
rec += "T00:00";
match NaiveDateTime::parse_from_str(&rec, "%Y-%m-%dT%H:%M") {
Ok(rec) => rec,
Err(e) => {
error!("Couldn't parse start datetime: {}", e);
return Err(Json(ApiError::new(400, "Das eingegebene Datum konnte nicht verarbeitet werden.".to_string()).to_wrapper()))
}
}
},
None => {
Local::now().naive_local()
}
};
let input = EventRequest{
entity_id,
name: crd.name,
timescale: crd.timescale,
site: crd.site,
organiser_id: parse_option_uuid(crd.organiser_id)?,
etype: parse_option_uuid(crd.etype)?,
related_group: None,
contact_on_site_name: crd.contact_on_site_name,
contact_on_site_phone: crd.contact_on_site_phone,
specials: crd.specials,
requested: crd.requested,
expected_attendees,
risk_potential,
state: 1,
received,
};
match add_event_request(&settings, input){
Ok(request) => Ok(Json(request)),
Err(e) => Err(translate_diesel(e))
}
}

View File

@ -0,0 +1,3 @@
pub mod create;
pub mod read;
pub mod update;

View File

@ -0,0 +1,66 @@
use rocket::{State, Request};
use crate::helper::settings::Settings;
use crate::helper::session_cookies::model::SessionCookie;
use crate::database::model::event_requests::EventRequest;
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
use crate::modules::api::member_management::controller::parser::parse_member_cookie;
use crate::database::controller::event_requests::{get_event_requests, get_event_request_count};
use crate::helper::translate_diesel_error::translate_diesel;
use rocket::serde::json::Json;
#[derive(Queryable, Clone, Deserialize, Serialize)]
pub struct EventRequestList{
pub(crate) requests: Vec<EventRequest>,
pub(crate) total_request_count: i64,
}
/// get list of all event requests
///
/// Arguments:
/// * settings: [Settings] struct managed by rocket
/// * cookie: [SessionCookie]
/// * limit: max row count, optional
/// * offset: skip first x rows, optional
/// * state: representing request state:
/// * 0: unknown
/// * 1: new (new request from extern)
/// * 3: validated (request has been checked)
/// * 5: published (request published)
/// * 7: adopted (some group admin adopted request)
#[get("/api/event_requests?<limit>&<offset>&<state>", format = "json")]
pub fn read_event_requests(
settings: &State<Settings>,
cookie: SessionCookie,
limit: Option<i64>,
offset: Option<i64>,
state: Option<i16>,
) -> Result<Json<EventRequestList>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::event_management::requests::VIEW.to_string()) {
return Err(Json(ApiError::new(403, "Keine Berechtigung Einsatzanfragen abzurufen!".to_string()).to_wrapper()))
}
let limit = match limit{
Some(limit) => limit,
None => settings.api.default_pagination_limit,
};
let offset = match offset{
Some(offset) => offset,
None => 0,
};
let requests = match get_event_requests(settings, limit, offset, state){
Ok(events) => events,
Err(e) => return Err(translate_diesel(e)),
};
let total_request_count = match get_event_request_count(settings, state){
Ok(count) => count,
Err(e) => return Err(translate_diesel(e)),
};
Ok(Json(EventRequestList{
requests,
total_request_count
}))
}

View File

@ -0,0 +1,205 @@
use crate::helper::settings::Settings;
use rocket::State;
use rocket::serde::json::Json;
use crate::helper::session_cookies::model::SessionCookie;
use crate::modules::api::events::requests::create::RequestData;
use crate::database::model::event_requests::EventRequest;
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
use crate::modules::api::member_management::controller::parser::{parse_member_cookie, parse_option_uuid, parse_uuid};
use crate::database::controller::entities::generate_entity;
use chrono::{NaiveDateTime, Local};
use crate::database::controller::event_requests::{add_event_request, change_event_request, get_event_request};
use crate::helper::translate_diesel_error::translate_diesel;
use crate::helper::mail_templates::MailTemplates;
use crate::helper::mail_queue::queue::{MailQueue, Mail};
use std::sync::Arc;
use crate::database::model::organisers::Organiser;
use crate::database::controller::organisers::get_organiser;
use crate::database::controller::permissions::get_members_with_permission;
use crate::database::controller::api_communication_targets::get_member_email_addresses;
use crate::database::controller::members::get_member_by_uuid;
#[put("/api/event_requests/<request_id>", format = "json", data = "<create_request_data>")]
pub fn update_event_request(
settings: &State<Settings>,
cookie: SessionCookie,
create_request_data: Json<RequestData>,
request_id: String,
mt: &State<MailTemplates>, mq: &State<Arc<MailQueue>>
) -> Result<Json<EventRequest>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::event_management::requests::EDIT.to_string()) {
return Err(Json(
ApiError::new(403, "Keine Berechtigung, Einsatzanfragen zu verändern!".to_string()).to_wrapper(),
));
}
let entity_id = parse_uuid(&request_id)?;
let old_data = match get_event_request(settings, entity_id){
Ok(opt) => match opt{
Some(old) => old,
None => return Err(Json(ApiError::new(404, "Didn't found event request with specified id".to_string()).to_wrapper()))
},
Err(e) => return Err(translate_diesel(e))
};
let crd = create_request_data.into_inner();
let risk_potential : Option<i32> = match crd.risk_potential{
Some(rp) => match rp.parse() {
Ok(rp) => Some(rp),
Err(e) => {
warn!("Couldn't parse risk potential for new request: {}", e);
return Err(Json(ApiError::new(500, "couldn't parse risk potential".to_string()).to_wrapper()))
}
},
None => None,
};
let expected_attendees : Option<i32> = match crd.expected_attendees{
Some(ea) => match ea.parse() {
Ok(ea) => Some(ea),
Err(e) => {
warn!("Couldn't parse expected attendees for new request: {}", e);
return Err(Json(ApiError::new(500, "couldn't parse expected attendees".to_string()).to_wrapper()))
}
},
None => None,
};
let received = match crd.received{
Some(mut rec) => {
rec += "T00:00";
match NaiveDateTime::parse_from_str(&rec, "%Y-%m-%dT%H:%M") {
Ok(rec) => rec,
Err(e) => {
error!("Couldn't parse start datetime: {}", e);
return Err(Json(ApiError::new(400, "Das eingegebene Datum konnte nicht verarbeitet werden.".to_string()).to_wrapper()))
}
}
},
None => {
old_data.received
}
};
let state = match crd.state{
Some(state) => match state.parse::<i16>() {
Ok(state) => {
if state != 1 && state != 0 && state != 3 && state != 5 && state != 7{
warn!("Unknown state: {}", state);
return Err(Json(ApiError::new(400, "Unknown state number.".to_string()).to_wrapper()))
}else{
state
}
},
Err(e) => {
error!("Couldn't parse state: {}", e);
return Err(Json(ApiError::new(400, "Couldn't parse state.".to_string()).to_wrapper()))
}
},
None => 0
};
let input = EventRequest{
entity_id,
name: crd.name,
timescale: crd.timescale,
site: crd.site,
organiser_id: parse_option_uuid(crd.organiser_id)?,
etype: parse_option_uuid(crd.etype)?,
related_group: parse_option_uuid(crd.related_group)?,
contact_on_site_name: crd.contact_on_site_name,
contact_on_site_phone: crd.contact_on_site_phone,
specials: crd.specials,
requested: crd.requested,
expected_attendees,
risk_potential,
state,
received,
};
match change_event_request(&settings, input){
Ok(request) => {
if state == 5 && old_data.state != 5{
debug!("State 5 is new, sending emails!");
send_event_request_published_emails(mt, mq, settings, request.clone())
}else{
debug!("State is: {}, old state was: {}", state, old_data.state);
}
Ok(Json(request))
},
Err(e) => Err(translate_diesel(e))
}
}
#[derive(Serialize)]
struct NewEventRequestPublishedEmail{
firstname: String,
request: EventRequest,
frontpage: String,
support_email: String,
organiser: Option<Organiser>
}
fn send_event_request_published_emails(mt: &State<MailTemplates>, mq: &State<Arc<MailQueue>>, settings: &State<Settings>, request: EventRequest) {
let receivers = match get_members_with_permission(settings, crate::permissions::modules::event_management::requests::assignments::ASSIGN){
Ok(rec) => rec,
Err(e) => {
error!("Couldn't get email receiver list: {}", e);
return;
}
};
let organiser = match request.clone().organiser_id{
Some(organiser) => {
match get_organiser(settings, organiser){
Ok(organiser) => Some(organiser),
Err(e) => {
error!("Couldn't get organiser for new event request to send email: {}", e);
None
}
}
},
None => None
};
for receiver in receivers{
let emails = match get_member_email_addresses(settings, receiver){
Ok(emails) => emails,
Err(e) => {
error!("Couldn't get email addresses for member: {}", e);
continue
}
};
let member = match get_member_by_uuid(receiver, settings){
Some(member) => member,
None => {
error!("No member found for id {}", receiver);
continue;
}
};
let nerpe = NewEventRequestPublishedEmail{
firstname: member.firstname,
request: request.clone(),
frontpage: settings.application.url.clone(),
support_email: settings.application.user_support_email.clone(),
organiser: organiser.clone()
};
let body = match mt.registry.render("new_event_request_published-de", &nerpe){
Ok(body) => body,
Err(e) => {
error!("Couldn't render email template: {}", e);
return},
};
let mail = Mail::new(settings.mail.from.clone(), emails, format!("[{}] - Neue Einsatzanfrage", settings.application.name.clone()), vec![], vec![], Some(settings.mail.reply_to.clone()), body, None); //TODO: Add deliver_until
match mq.add_mail(mail){
Ok(_) => {},
Err(_) => {}
}
}
}

View File

@ -55,7 +55,9 @@ pub fn update_event(
member_responsible: parse_option_uuid(ecd.member_responsible)?,
related_group: parse_option_uuid(ecd.related_group)?,
other: ecd.other,
other_intern: ecd.other_intern
other_intern: ecd.other_intern,
related_request: None,
state: 0
};
match change_event(settings, input){

View File

@ -2,10 +2,13 @@ use rocket::State;
use crate::helper::settings::Settings;
use crate::helper::session_cookies::model::SessionCookie;
use rocket::serde::json::Json;
use crate::modules::api::model::api_outcome::ApiErrorWrapper;
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
use crate::modules::api::member_management::controller::parser::{parse_member_cookie, parse_uuid_string};
use crate::database::controller::members::check_access_to_resource;
use crate::database::controller::permissions::{get_permission, get_role_permission_id, get_role_permission_context};
use crate::helper::translate_diesel_error::translate_diesel;
use crate::database::controller::roles::get_roles_for_member;
/// Check if caller has permission
///
@ -36,4 +39,52 @@ pub fn check_caller_has_permission(
Ok(Json(caller.has_permission(permission)))
}
}
}
}
/// Get context entries caller has permissions for
///
/// # Api Call
/// * GET
/// * /api/info/caller/permission_context?permission=<permission_string : String>
///
/// # Api Result
/// * Result JSON<Vec<uuid::Uuid>> / ApiErrorWrapper
///
/// # Required permissions
/// * None
#[get("/api/info/caller/permission_context?<permission>", format = "json")]
pub fn get_caller_permission_context(
settings: &State<Settings>,
cookie: SessionCookie,
permission: String,
) -> Result<Json<Vec<uuid::Uuid>>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
let roles = match get_roles_for_member(settings, caller.entity_id){
Ok(roles) => roles,
Err(e) => return Err(translate_diesel(e))
};
let mut res : Vec<uuid::Uuid> = vec![];
for role in roles{
let rpi = get_role_permission_id(settings, &permission, &role);
match rpi{
Ok(rpi) => match rpi{
Some(rpi) => {
match get_role_permission_context(settings, rpi){
Ok(context) => {
res.append(&mut context.clone())
},
Err(e) => return Err(translate_diesel(e))
}},
None => {},
},
Err(e) => {
return Err(translate_diesel(e))
}
}
}
Ok(Json(res))
}

View File

@ -9,4 +9,5 @@ pub mod resources;
pub mod appointments;
pub mod info;
pub mod event_organisers;
pub mod events;
pub mod events;
pub mod permissions;

View File

@ -0,0 +1,2 @@
pub mod roles;
pub mod read;

View File

@ -0,0 +1,37 @@
use rocket::State;
use crate::helper::settings::Settings;
use crate::helper::session_cookies::model::SessionCookie;
use rocket::serde::json::Json;
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
use crate::modules::api::member_management::controller::parser::parse_member_cookie;
use crate::database::controller::roles::get_permissions_for_role;
use crate::helper::translate_diesel_error::translate_diesel;
use crate::database::model::permissions::Permission;
/// Get all permissions
///
/// # Api Call
/// * GET
/// * /api/permissions/
///
/// # Api Result
/// * Result JSON<Vec<[Permission]>> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.view
#[get("/api/permissions", format = "json")]
pub fn get_permissions(
settings: &State<Settings>,
cookie: SessionCookie,
) -> Result<Json<Vec<Permission>>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::VIEW.to_string()){
return Err(Json(ApiError::new(403, "No permission to read permission list".to_string()).to_wrapper()))
}
match crate::database::controller::permissions::get_permissions(settings) {
Ok(res) => Ok(Json(res)),
Err(e) => Err(translate_diesel(e))
}
}

View File

@ -0,0 +1,2 @@
pub mod read;
pub mod update;

View File

@ -0,0 +1,147 @@
use rocket::State;
use crate::helper::settings::Settings;
use crate::helper::session_cookies::model::SessionCookie;
use rocket::serde::json::Json;
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
use crate::modules::api::member_management::controller::parser::parse_member_cookie;
use crate::database::controller::roles::{get_permissions_for_role, get_roles, get_members_for_role};
use crate::helper::translate_diesel_error::translate_diesel;
use crate::database::model::permissions::Permission;
use crate::database::controller::permissions::{get_role_permission_id, get_role_permission_context};
use crate::database::model::roles::Role;
use crate::modules::api::members::get_member::MemberSearchResult;
use crate::database::model::api_members::RawMemberSearchResult;
/// Get all permissions for role
///
/// # Api Call
/// * GET
/// * /api/roles/<role_id>/permissions
///
/// # Api Result
/// * Result JSON<Vec<[Permission]>> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.view
#[get("/api/roles/<role_id>/permissions", format = "json")]
pub fn get_role_permissions(
settings: &State<Settings>,
cookie: SessionCookie,
role_id: String,
) -> Result<Json<Vec<Permission>>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::VIEW.to_string()){
return Err(Json(ApiError::new(403, "No permission to read role permissions".to_string()).to_wrapper()))
}
match get_permissions_for_role(settings, role_id) {
Ok(res) => Ok(Json(res)),
Err(e) => Err(translate_diesel(e))
}
}
/// Get context for permission role
///
/// # Api Call
/// * GET
/// * /api/roles/<role_id>/permissions/<permission>/context
///
/// # Api Result
/// * Result JSON<Vec<[uuid::Uuid]>> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.view
#[get("/api/roles/<role_id>/permissions/<permission>/context", format = "json")]
pub fn get_context_for_permission_role(
settings: &State<Settings>,
cookie: SessionCookie,
permission: String,
role_id: String,
) -> Result<Json<Vec<uuid::Uuid>>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::VIEW.to_string()){
return Err(Json(ApiError::new(403, "No permission to read role permissions".to_string()).to_wrapper()))
}
let mut res : Vec<uuid::Uuid> = vec![];
let rpi = get_role_permission_id(settings, &permission, &role_id);
match rpi{
Ok(rpi) => match rpi{
Some(rpi) => {
match get_role_permission_context(settings, rpi){
Ok(context) => {
res.append(&mut context.clone())
},
Err(e) => return Err(translate_diesel(e))
}},
None => {},
},
Err(e) => {
return Err(translate_diesel(e))
}
}
Ok(Json(res))
}
/// Get all roles
///
/// # Api Call
/// * GET
/// * /api/roles/
///
/// # Api Result
/// * Result JSON<Vec<[Role]>> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.view
#[get("/api/roles", format = "json")]
pub fn get_all_roles(
settings: &State<Settings>,
cookie: SessionCookie,
) -> Result<Json<Vec<Role>>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::VIEW.to_string()){
return Err(Json(ApiError::new(403, "No permission to read roles".to_string()).to_wrapper()))
}
match get_roles(settings) {
Ok(res) => Ok(Json(res)),
Err(e) => Err(translate_diesel(e))
}
}
/// Get all role members
///
/// # Api Call
/// * GET
/// * /api/roles/<role_id>/members
///
/// # Api Result
/// * Result JSON<Vec<[RawMemberSearchResult]>> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.view
#[get("/api/roles/<role_id>/members", format = "json")]
pub fn get_role_members(
settings: &State<Settings>,
cookie: SessionCookie,
role_id: String,
) -> Result<Json<Vec<RawMemberSearchResult>>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::VIEW.to_string()){
return Err(Json(ApiError::new(403, "No permission to read roles".to_string()).to_wrapper()))
}
match get_members_for_role(settings, &role_id) {
Ok(res) => Ok(Json(res)),
Err(e) => Err(translate_diesel(e))
}
}

View File

@ -0,0 +1,266 @@
use rocket::State;
use crate::helper::settings::Settings;
use crate::helper::session_cookies::model::SessionCookie;
use rocket::serde::json::Json;
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
use crate::modules::api::member_management::controller::parser::{parse_member_cookie, parse_uuid};
use crate::database::controller::roles::{get_permissions_for_role, add_permission_context, remove_permission_context, add_member_to_role, remove_member_from_role, update_role};
use crate::helper::translate_diesel_error::translate_diesel;
use crate::database::model::permissions::Permission;
use crate::database::controller::permissions::{add_permission_to_role, remove_permission_from_role, get_role_permission_id};
use crate::database::model::roles::Role;
/// Add permission to role
///
/// Returns new role_permission_id
///
/// # Api Call
/// * POST
/// * /api/roles/<role_id>/permissions/<permission_id>
///
/// # Api Result
/// * Result JSON<uuid::Uuid> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.edit
#[post("/api/roles/<role_id>/permissions/<permission_id>", format = "json")]
pub fn api_add_permission_to_role(
settings: &State<Settings>,
cookie: SessionCookie,
role_id: String,
permission_id: String,
) -> Result<Json<uuid::Uuid>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::EDIT.to_string()){
return Err(Json(ApiError::new(403, "No permission to edit role permissions".to_string()).to_wrapper()))
}
match add_permission_to_role(settings, &permission_id, &role_id) {
Ok(res) => Ok(Json(res)),
Err(e) => Err(translate_diesel(e))
}
}
/// Remove permission to role
///
/// Returns nothing
///
/// # Api Call
/// * DELETE
/// * /api/roles/<role_id>/permissions/<permission_id>
///
/// # Api Result
/// * Result JSON<()> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.edit
#[delete("/api/roles/<role_id>/permissions/<permission_id>", format = "json")]
pub fn api_remove_permission_from_role(
settings: &State<Settings>,
cookie: SessionCookie,
role_id: String,
permission_id: String,
) -> Result<Json<()>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::EDIT.to_string()){
return Err(Json(ApiError::new(403, "No permission to edit role permissions".to_string()).to_wrapper()))
}
match remove_permission_from_role(settings, &permission_id, &role_id) {
Ok(()) => Ok(Json(())),
Err(e) => Err(translate_diesel(e))
}
}
/// Add context to role & permission
///
/// Returns nothing
///
/// # Api Call
/// * POST
/// * /api/roles/<role_id>/permissions/<permission_id>/context/<context_id>
///
/// # Api Result
/// * Result JSON<()> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.edit
#[post("/api/roles/<role_id>/permissions/<permission_id>/context/<context_id>", format = "json")]
pub fn api_add_context(
settings: &State<Settings>,
cookie: SessionCookie,
role_id: String,
permission_id: String,
context_id: String,
) -> Result<Json<()>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::EDIT.to_string()){
return Err(Json(ApiError::new(403, "No permission to edit role permissions".to_string()).to_wrapper()))
}
let rpi = match get_role_permission_id(settings, &permission_id, &role_id){
Ok(rpi) => match rpi{
Some(rpi) => rpi,
None => {
warn!("Couldn't find role_permission_id");
return Err(Json(ApiError::new(404, "role_permission_id not found.".to_string()).to_wrapper()))
}
},
Err(e) => return Err(Json(ApiError::new(500, "Couldn't get role_permission_id.".to_string()).to_wrapper())),
};
let context_id = parse_uuid(&context_id)?;
match add_permission_context(settings, rpi, context_id){
Ok(()) => Ok(Json(())),
Err(e) => {Err(translate_diesel(e))}
}
}
/// Removes context from role & permission combo
///
/// Returns nothing
///
/// # Api Call
/// * POST
/// * /api/roles/<role_id>/permissions/<permission_id>/context/<context_id>
///
/// # Api Result
/// * Result JSON<()> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.edit
#[delete("/api/roles/<role_id>/permissions/<permission_id>/context/<context_id>", format = "json")]
pub fn api_remove_context(
settings: &State<Settings>,
cookie: SessionCookie,
role_id: String,
permission_id: String,
context_id: String,
) -> Result<Json<()>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::EDIT.to_string()){
return Err(Json(ApiError::new(403, "No permission to edit role permissions".to_string()).to_wrapper()))
}
let rpi = match get_role_permission_id(settings, &permission_id, &role_id){
Ok(rpi) => match rpi{
Some(rpi) => rpi,
None => return Err(Json(ApiError::new(404, "role_permission_id not found.".to_string()).to_wrapper()))
},
Err(e) => return Err(Json(ApiError::new(500, "Couldn't get role_permission_id.".to_string()).to_wrapper())),
};
let context_id = parse_uuid(&context_id)?;
match remove_permission_context(settings, rpi, context_id){
Ok(()) => Ok(Json(())),
Err(e) => {Err(translate_diesel(e))}
}
}
/// Add member to role
///
/// Returns nothing
///
/// # Api Call
/// * POST
/// * /api/roles/<role_id>/members/<member_id>
///
/// # Api Result
/// * Result JSON<()> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.edit
#[post("/api/roles/<role_id>/members/<member_id>", format = "json")]
pub fn api_add_member_to_role(
settings: &State<Settings>,
cookie: SessionCookie,
role_id: String,
member_id: String,
) -> Result<Json<()>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::EDIT.to_string()){
return Err(Json(ApiError::new(403, "No permission to edit roles".to_string()).to_wrapper()))
}
let member_id = parse_uuid(&member_id)?;
match add_member_to_role(settings, member_id, role_id) {
Ok(()) => Ok(Json(())),
Err(e) => Err(translate_diesel(e))
}
}
/// Removes member from role
///
/// Returns nothing
///
/// # Api Call
/// * DELETE
/// * /api/roles/<role_id>/members/<member_id>
///
/// # Api Result
/// * Result JSON<()> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.edit
#[delete("/api/roles/<role_id>/members/<member_id>", format = "json")]
pub fn api_remove_member_from_role(
settings: &State<Settings>,
cookie: SessionCookie,
role_id: String,
member_id: String,
) -> Result<Json<()>, Json<ApiErrorWrapper>> {
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::EDIT.to_string()){
return Err(Json(ApiError::new(403, "No permission to edit roles".to_string()).to_wrapper()))
}
let member_id = parse_uuid(&member_id)?;
match remove_member_from_role(settings, member_id, role_id) {
Ok(()) => Ok(Json(())),
Err(e) => Err(translate_diesel(e))
}
}
/// Updates role data
///
/// Returns Role
///
/// # Api Call
/// * PUT
/// * /api/roles/<role_id>
///
/// # Api Result
/// * Result JSON<[Role]> / ApiErrorWrapper
///
/// # Required permissions
/// * modules.settings.role_permissions.edit
#[put("/api/roles/<role_id>", format = "json", data = "<role_data>")]
pub fn api_update_role(
settings: &State<Settings>,
cookie: SessionCookie,
role_id: String,
role_data: Json<Role>,
) -> Result<Json<Role>, Json<ApiErrorWrapper>> {
let role_data = role_data.into_inner();
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::settings::role_permissions::EDIT.to_string()){
return Err(Json(ApiError::new(403, "No permission to edit roles".to_string()).to_wrapper()))
}
match update_role(settings, role_id, role_data) {
Ok(res) => Ok(Json(res)),
Err(e) => Err(translate_diesel(e))
}
}

View File

@ -9,7 +9,7 @@ use crate::modules::api::member_management::controller::parser::{parse_member_co
use crate::database::controller::entities::generate_entity;
use crate::database::controller::vehicles::add_vehicle;
use crate::helper::translate_diesel_error::translate_diesel;
use crate::database::controller::roles::{add_permission_context, get_role_permission_id};
use crate::database::controller::roles::{add_permission_context, get_role_permission_id_deprecated};
#[derive(Queryable, Clone, Deserialize, Serialize)]
@ -61,19 +61,19 @@ pub fn create_vehicle(
match add_vehicle(settings, vehicle){
Ok(vehicle) => {
let rpi = match get_role_permission_id(settings, "admin".to_string(), crate::permissions::modules::resource_management::vehicles::core::EDIT){
let rpi = match get_role_permission_id_deprecated(settings, "admin".to_string(), crate::permissions::modules::resource_management::vehicles::core::EDIT){
Some(rpi) => rpi,
None => return Err(Json(ApiError::new(500, "Couldn't add permission".to_string()).to_wrapper()))
};
let rpi2 = match get_role_permission_id(settings, "admin".to_string(), crate::permissions::modules::scheduler::appointments::VIEW){
let rpi2 = match get_role_permission_id_deprecated(settings, "admin".to_string(), crate::permissions::modules::scheduler::appointments::VIEW){
Some(rpi) => rpi,
None => return Err(Json(ApiError::new(500, "Couldn't add permission".to_string()).to_wrapper()))
};
let rpi3 = match get_role_permission_id(settings, "admin".to_string(), crate::permissions::modules::scheduler::appointments::EDIT){
let rpi3 = match get_role_permission_id_deprecated(settings, "admin".to_string(), crate::permissions::modules::scheduler::appointments::EDIT){
Some(rpi) => rpi,
None => return Err(Json(ApiError::new(500, "Couldn't add permission".to_string()).to_wrapper()))
};
let rpi4 = match get_role_permission_id(settings, "admin".to_string(), crate::permissions::modules::resource_management::vehicles::core::DELETE){
let rpi4 = match get_role_permission_id_deprecated(settings, "admin".to_string(), crate::permissions::modules::resource_management::vehicles::core::DELETE){
Some(rpi) => rpi,
None => return Err(Json(ApiError::new(500, "Couldn't add permission".to_string()).to_wrapper()))
};

View File

@ -0,0 +1,51 @@
use crate::helper::session_cookies::model::SessionCookie;
use rocket::State;
use crate::helper::settings::Settings;
use rocket::http::Status;
use crate::helper::sitebuilder::model::general::{Header, Stylesheet, Footer, Script};
use crate::helper::sitebuilder::model::sidebar::Sidebar;
use crate::modules::event_management::eventlist::EventTemplates;
use rocket_dyn_templates::Template;
#[get("/portal/em/add_request")]
pub fn add_request(cookie: SessionCookie, _settings: &State<Settings>) -> Result<Template, Status> {
let member = match cookie.member {
//Unwraps member from cookie or send user to login if no member specified (user skipped member selection)
Some(member) => member,
None => return Err(Status::Unauthorized),
};
if !member.has_permission(
crate::permissions::modules::event_management::requests::CREATE.to_string(),
) {
return Err(Status::Forbidden);
}
let header = Header {
html_language: "de".to_string(),
site_title: "Einsatzanfrage erstellen".to_string(),
stylesheets: vec![Stylesheet {
path: "/css/errms.css".to_string(),
}],
};
let footer = Footer {
scripts: vec![
Script {
path: "/js/mini_searchbar.js".to_string(),
},
Script {
path: "/js/em_create_request.js".to_string(),
}],
};
let mut sidebar = Sidebar::new(member.clone());
sidebar.event_management.active = true;
let eventlist = EventTemplates {
header,
footer,
sidebar,
caller: member.entity_id,
};
Ok(Template::render("module_em_create_request", eventlist))
}

View File

@ -4,4 +4,6 @@ pub mod add_event;
pub mod event_unit_positions;
pub mod event_unit_templates;
pub mod edit_event;
pub mod check_position_requirements;
pub mod check_position_requirements;
pub mod requests;
pub mod add_request;

View File

@ -0,0 +1,59 @@
use crate::helper::session_cookies::model::SessionCookie;
use rocket::State;
use crate::helper::settings::Settings;
use rocket_dyn_templates::Template;
use rocket::http::Status;
use crate::helper::sitebuilder::model::general::{Header, Stylesheet, Footer, Script};
use crate::helper::sitebuilder::model::sidebar::Sidebar;
use crate::database::controller::groups::get_raw_groups;
use crate::modules::event_management::eventlist::EventList;
#[get("/portal/em/requests")]
pub fn request_list(cookie: SessionCookie, settings: &State<Settings>) -> Result<Template, Status> {
let member = match cookie.member {
//Unwraps member from cookie or send user to login if no member specified (user skipped member selection)
Some(member) => member,
None => return Err(Status::Unauthorized),
};
if !member.has_permission(
crate::permissions::modules::event_management::events::VIEW.to_string(),
) {
return Err(Status::Forbidden);
}
let header = Header {
html_language: "de".to_string(),
site_title: "Einsatzanfragen".to_string(),
stylesheets: vec![Stylesheet {
path: "/css/errms.css".to_string(),
}],
};
let footer = Footer {
scripts: vec![Script {
path: "/js/em_eventrequests.js".to_string(),
}, Script {
path: "/js/mini_searchbar.js".to_string(),
},Script {
path: "/js/pagination.js".to_string(),
}],
};
let mut sidebar = Sidebar::new(member.clone());
sidebar.event_management.active = true;
let groups = match get_raw_groups(settings){
Ok(groups) => groups,
Err(e) => return Err(Status::InternalServerError)
};
let eventlist = EventList {
header,
footer,
sidebar,
caller: member.entity_id,
groups
};
Ok(Template::render("module_em_requests", eventlist))
}

View File

@ -86,6 +86,16 @@ pub mod modules {
pub const CREATE: &'static str = "modules.event_management.events.create";
pub const DELETE: &'static str = "modules.event_management.events.delete";
}
pub mod requests{
pub mod assignments{
pub const ASSIGN: &'static str = "modules.event_management.requests.assignments.assign";
pub const RESET: &'static str = "modules.event_management.requests.assignments.reset"; //TODO: add to database
}
pub const VIEW: &'static str = "modules.event_management.requests.view";
pub const EDIT: &'static str = "modules.event_management.requests.edit";
pub const CREATE: &'static str = "modules.event_management.requests.create";
pub const DELETE: &'static str = "modules.event_management.requests.delete";
}
}
pub mod resource_management{
pub const VIEW: &'static str = "modules.resource_management.view";

View File

@ -194,6 +194,29 @@ table! {
}
}
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
event_requests (entity_id) {
entity_id -> Uuid,
name -> Text,
timescale -> Nullable<Text>,
site -> Nullable<Text>,
organiser_id -> Nullable<Uuid>,
etype -> Nullable<Uuid>,
related_group -> Nullable<Uuid>,
contact_on_site_name -> Nullable<Text>,
contact_on_site_phone -> Nullable<Text>,
specials -> Nullable<Text>,
requested -> Nullable<Text>,
expected_attendees -> Nullable<Int4>,
risk_potential -> Nullable<Int4>,
state -> Int2,
received -> Timestamp,
}
}
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
@ -224,6 +247,8 @@ table! {
related_group -> Nullable<Uuid>,
other -> Nullable<Text>,
other_intern -> Nullable<Text>,
related_request -> Nullable<Uuid>,
state -> Int2,
}
}
@ -325,6 +350,16 @@ table! {
}
}
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
notification_types (name) {
name -> Text,
description -> Nullable<Text>,
}
}
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
@ -487,6 +522,8 @@ joinable!(eu_vehicle_positions -> entities (entity_id));
joinable!(eu_vehicle_positions -> eu_templates (template_id));
joinable!(eu_vehicle_positions -> vehicle_categories (required_vehicle_category));
joinable!(event_organisers -> entities (entity_id));
joinable!(event_requests -> entities (entity_id));
joinable!(event_requests -> groups (related_group));
joinable!(events -> event_organisers (organiser_id));
joinable!(events -> event_types (etype));
joinable!(events -> groups (related_group));
@ -532,6 +569,7 @@ allow_tables_to_appear_in_same_query!(
eu_templates,
eu_vehicle_positions,
event_organisers,
event_requests,
event_types,
events,
groups,
@ -542,6 +580,7 @@ allow_tables_to_appear_in_same_query!(
login_attempts_usernames,
members,
members_roles,
notification_types,
password_resets,
permissions,
qualification_categories,