Feature: Send emails

This commit is contained in:
Keanu D?lle 2021-01-14 16:46:56 +01:00
parent 4f906cb73b
commit dcd5a4e052
26 changed files with 750 additions and 32 deletions

View File

@ -20,4 +20,6 @@ user_support_email = "support@einsatz.online"
[mail]
from = "No Reply <noreply@localhost>"
reply_to = "support@localhost"
reply_to = "support@localhost"
#type_id for emails in communication_types table
communication_email_type_id = "a0a93b6e-f200-11ea-ad50-e86a6451da25"

View File

@ -11,3 +11,15 @@ DELETE
FROM permissions
WHERE permission LIKE 'modules.communicator.email.send' ESCAPE '#';
DELETE
FROM roles_permissions
WHERE role_permission_id = '7d433bac-4f89-11eb-86be-e86a64d41d89';
DELETE
FROM roles_permissions
WHERE role_permission_id = '7d447c92-4f89-11eb-86be-e86a64d41d89';
DELETE
FROM roles_permissions
WHERE role_permission_id = '7d4202c8-4f89-11eb-86be-e86a64d41d89';

View File

@ -8,3 +8,12 @@ VALUES ('modules.communicator.edit', null);
INSERT INTO permissions (permission, description)
VALUES ('modules.communicator.email.send', 'Permission to send email via communicator');
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id)
VALUES ('admin', 'modules.communicator.view', DEFAULT);
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id)
VALUES ('admin', 'modules.communicator.edit', DEFAULT);
INSERT INTO roles_permissions (role_id, permission_id, role_permission_id)
VALUES ('admin', 'modules.communicator.email.send', DEFAULT);

View File

@ -135,12 +135,6 @@ ul ul a {
color: black;
}
/*[readonly]:focus{
outline: none !important;
-webkit-appearance:none;
box-shadow: none !important;
}*/
label{
color: dimgrey;
}
@ -188,4 +182,11 @@ th.rotate > div > span {
.versiontag{
padding: 10px;
font-size: 10px;
}
.quicksearch-overlay{
position:absolute;
z-index: 1;
}
.quicksearch-overlay-li{
cursor: pointer;
}

View File

@ -0,0 +1,239 @@
$( document ).ready(function() {
Communicator.load_groups();
Communicator.load_units();
$("#send-email-submit").on("click", Communicator.send_email);
$(".email-to-searchbar").on('keyup', function(event){
if(event.key === "Enter"){
$(".email-to-searchbar-overlay").children("li").first().trigger("click");
$(".email-to-searchbar").val("");
}
QuickSearch.handle_typing(QuickSearch.SEARCHTYPE.MEMBER, ".email-to-searchbar", ".email-to-searchbar-overlay", "email-to-searchbar-overlay-result", "#email-to");
});
$(".email-to-searchbar-overlay").on("focusout", function (){
$(".email-to-searchbar-overlay").hide();
})
$(".email-to-searchbar-overlay").on("mouseleave", function (){
$(".email-to-searchbar-overlay").hide();
});
$(".email-to-search").on("mouseenter", function (){
$(".email-to-searchbar-overlay").show();
});
$(".email-to-search").on("focusin", function (){
$(".email-to-searchbar-overlay").html("");
});
$(".email-to-searchbar").on("click", function (){
$(".email-to-searchbar").val("");
});
$(".email-cc-searchbar").on('keyup', function(event){
if(event.key === "Enter"){
$(".email-cc-searchbar-overlay").children("li").first().trigger("click");
$(".email-cc-searchbar").val("");
}
QuickSearch.handle_typing(QuickSearch.SEARCHTYPE.MEMBER, ".email-cc-searchbar", ".email-cc-searchbar-overlay", "email-cc-searchbar-overlay-result", "#email-cc");
});
$(".email-cc-searchbar-overlay").on("focusout", function (){
$(".email-cc-searchbar-overlay").hide();
})
$(".email-cc-searchbar-overlay").on("mouseleave", function (){
$(".email-cc-searchbar-overlay").hide();
});
$(".email-cc-search").on("mouseenter", function (){
$(".email-cc-searchbar-overlay").show();
});
$(".email-cc-search").on("focusin", function (){
$(".email-cc-searchbar-overlay").html("");
});
$(".email-cc-searchbar").on("click", function (){
$(".email-cc-searchbar").val("");
});
$(".email-bcc-searchbar").on('keyup', function(event){
if(event.key === "Enter"){
$(".email-bcc-searchbar-overlay").children("li").first().trigger("click");
$(".email-bcc-searchbar").val("");
}
QuickSearch.handle_typing(QuickSearch.SEARCHTYPE.MEMBER, ".email-bcc-searchbar", ".email-bcc-searchbar-overlay", "email-bcc-searchbar-overlay-result", "#email-bcc");
});
$(".email-bcc-searchbar-overlay").on("focusout", function (){
$(".email-bcc-searchbar-overlay").hide();
})
$(".email-bcc-searchbar-overlay").on("mouseleave", function (){
$(".email-bcc-searchbar-overlay").hide();
});
$(".email-bcc-search").on("mouseenter", function (){
$(".email-bcc-searchbar-overlay").show();
});
$(".email-bcc-search").on("focusin", function (){
$(".email-bcc-searchbar-overlay").html("");
});
$(".email-bcc-searchbar").on("click", function (){
$(".email-bcc-searchbar").val("");
});
});
QuickSearch = (function(){
const SEARCHTYPE = {
MEMBER: 1,
};
var remove_email = function(){
$(this).parent().remove();
};
var handle_typing = function(type, input, overlay, overlayresult, rec){
if(type === 1){
$(overlay).html("");
$.ajax({
url: '/api/members/',
type: 'GET',
data: {
"name": $(input).val()
},
contentType: 'application/json',
success: function(data) {
if(is_ok(data)) {
$.each(data.members, function(index, value){
$(overlay).append("<li data-entity-id=\""+value.entity_id+"\" data-firstname=\""+value.firstname+"\" data-lastname=\""+value.lastname+"\" class=\"quicksearch-overlay-li list-group-item "+overlayresult+"\"><span>"+value.firstname+" "+value.lastname+"</span></li>")
});
$("."+overlayresult).off("click").on("click", function(){
$(rec).append("<span data-entity-id=\""+$(this).data("entity-id")+"\" class=\"badge badge-secondary\">"+$(this).data("firstname")+" "+$(this).data("lastname")+" <svg width=\"1.5em\" height=\"1.5em\" fill=\"currentColor\" class=\"remove-email\"><use xlink:href=\"/img/bootstrap-icons.svg#trash\"/></svg></span>");
$(".remove-email").unbind("click").on("click", QuickSearch.remove_email);
});
}
},
timeout: 3000,
error: function() {
alert("Verbindung zum Server unterbrochen!");
}
});
$(overlay).show();
}else{
console.log("Unkown searchtype: "+type);
}
};
return{
handle_typing: handle_typing,
remove_email: remove_email,
SEARCHTYPE: SEARCHTYPE,
}
})();
Communicator = (function(){
var load_groups = function(){
$.ajax({
url: '/api/groups?with_caller_permission=modules.communicator.email.send',
type: 'GET',
contentType: 'application/json',
success: function(data) {
if(is_ok(data)) {
$(data.groups).each(function(){
$(".group_selection_list").append("<span class=\"form-check group_selection_group\">\n" +
" <input type=\"checkbox\" class=\"form-check-input selected_group\" id=\""+this.group_id+"\" data-group-id=\""+this.group_id+"\">\n" +
" <label class=\"form-check-label\" for=\""+this.group_id+"\">"+this.name+"</label>\n" +
" </span>")
})
}
},
timeout: 3000,
error: function() {
alert("Verbindung zum Server unterbrochen!");
}
});
};
var load_units = function(){
$.ajax({
url: '/api/units?with_caller_permission=modules.communicator.email.send',
type: 'GET',
contentType: 'application/json',
success: function(data) {
if(is_ok(data)) {
$(data).each(function(){
$(".unit_selection_list").append("<span class=\"form-check unit_selection_group\">\n" +
" <input type=\"checkbox\" class=\"form-check-input selected_unit\" id=\""+this.unit_id+"\" data-unit-id=\""+this.unit_id+"\" name=\"selected_groups\">" +
" <label class=\"form-check-label\" for=\""+this.unit_id+"\">"+this.name+"</label>\n" +
" </span>")
})
}
},
timeout: 3000,
error: function() {
alert("Verbindung zum Server unterbrochen!");
}
});
};
var send_email = function(){
var email = $();
if($("#email-to > span").length > 0){
email.to_members = [];
$("#email-to > span").each(function(){email.to_members.push($(this).data("entity-id"))});
}
if($("#email-cc > span").length > 0){
email.cc_members = [];
$("#email-cc > span").each(function(){email.cc_members.push($(this).data("entity-id"))});
}
if($("#email-bcc > span").length > 0){
email.bcc_members = [];
$("#email-bcc > span").each(function(){email.bcc_members.push($(this).data("entity-id"))});
}
if($("#email-subject").val()){
email.subject = $("#email-subject").val();
}else{
alert("Email konnte nicht gesendet werden: Es muss ein Betreff angegeben werden!");
return;
}
if($("#email-body").val()){
email.body = $("#email-body").val();
}else{
alert("Email konnte nicht gesendet werden: Die Nachricht darf nicht leer sein!");
return;
}
email.selected_groups = [];
email.selected_units = [];
$(".selected_group").each(function(){
if($(this).prop('checked')){
email.selected_groups.push($(this).data("group-id"));
}
});
$(".selected_units").each(function(){
if($(this).prop('checked')){
email.selected_units.push($(this).data("unit-id"));
}
});
$.ajax({
url: '/api/communicator/email',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(email),
success: function(data) {
if(is_ok(data)) {
$("#email-to").html("");
$("#email-cc").html("");
$("#email-bcc").html("");
$(".selected_group").prop("checked", false);
$(".selected_unit").prop("checked", false);
$("#email-subject").val("");
$("#email-body").val("");
$(".alert").append("<div class=\"alert alert-success\" role=\"alert\">Die Email wird nun versendet!</div>");
}
},
timeout: 3000,
error: function() {
alert("Verbindung zum Server unterbrochen!");
}
});
};
var to_searchbar = function(){
var searchterm = $("#email-to-searchbar").val();
console.log(searchterm);
};
return{
load_groups: load_groups,
load_units: load_units,
send_email: send_email,
to_searchbar: to_searchbar,
};
})();

View File

@ -12,7 +12,7 @@ $( document ).ready(function() {
$("#searchbar").on("click", Searchbar.searchbar_onclick);
$("#searchbar").on("keyup", Searchbar.searchbar_typing);
$("#searchbar").bind("paste", Searchbar.searchbar_typing);
$("#searchbar").bind("cut", Searchbar.sWearchbar_typing);
$("#searchbar").bind("cut", Searchbar.searchbar_typing);
$("#searchbar").on("mouseenter", function (){
$(".search-result-overlay").show();
});

View File

@ -0,0 +1,86 @@
{{> header }}
<div class="container-fluid">
<div class="row">
<div class="wrapper">
{{> sidebar }}
<div id="content">
{{> searchbar}}
<hr>
<span class="alert"></span>
<div class="col">
<div class="card bg-light">
<div class="card-header">E-Mail versenden</div>
<div class="card-body">
<div class="row">
<div class="col">
<div class="form-group row">
<label for="email-to" class="col-sm-1 col-form-label">An: </label>
<div class="col-sm-11">
<span id="email-to"></span>
<div class="email-to-search">
<input type="text" class="form-control email-to-searchbar" value="{{to}}">
<ul class="list-group"><span class="quicksearch-overlay email-to-searchbar-overlay" style="display: none"></span></ul>
</div>
</div>
</div>
<div class="form-group row">
<label for="email-cc" class="col-sm-1 col-form-label">Cc: </label>
<div class="col-sm-11">
<span id="email-cc"></span>
<div class="email-cc-search">
<input type="text" class="form-control email-cc-searchbar" value="{{cc}}">
<ul class="list-group"><span class="quicksearch-overlay email-cc-searchbar-overlay" style="display: none"></span></ul>
</div>
</div>
</div>
<div class="form-group row">
<label for="email-bcc" class="col-sm-1 col-form-label">Bcc: </label>
<div class="col-sm-11">
<span id="email-bcc"></span>
<div class="email-bcc-search">
<input type="text" class="form-control email-bcc-searchbar" value="{{bcc}}">
<ul class="list-group"><span class="quicksearch-overlay email-bcc-searchbar-overlay" style="display: none"></span></ul>
</div>
</div>
</div>
<p>Hinweis: E-Mails an Gruppen werden aus Gründen des Datenschutzes immer nur im BCC gesendet.</p>
<div class="filter">
<ul class="nav nav-tabs" id="filterTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="group-tab" data-toggle="tab" href="#group" role="tab" aria-controls="group" aria-selected="true">Gruppen</a>
</li>
<li class="nav-item">
<a class="nav-link" id="units-tab" data-toggle="tab" href="#units" role="tab" aria-controls="units" aria-selected="false">Einsatzeinheiten</a>
</li>
</ul>
<div class="tab-content" id="filterTabContent">
<div class="tab-pane fade show active" id="group" role="tabpanel" aria-labelledby="group-tab">
<div class="group_selection_list">
</div>
</div>
<div class="tab-pane fade" id="units" role="tabpanel" aria-labelledby="units-tab">
<div class="unit_selection_list">
</div>
</div>
</div>
</div>
<div class="form-group row">
<label for="email-subject" class="col-sm-1 col-form-label">Betreff: </label>
<div class="col-sm-11">
<input type="text" class="form-control" id="email-subject" name="email-subject">
</div>
</div>
<div class="form-group">
<label for="email-body" class="col-form-label">Nachricht: </label>
<textarea class="form-control" id="email-body" name="email-body" rows="10"></textarea>
</div>
<button class="btn btn-primary" id="send-email-submit" style="float: right;">E-Mail senden</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{> footer }}

View File

@ -38,6 +38,16 @@
{{/if}}
</li>
{{/if}}
{{#if sidebar.communicator.visible}}
<li>
<a {{#if sidebar.communicator.active}}class="sidebar_entry_active"{{/if}} href="/portal/communicator/email">Kommunikator</a>
{{#if sidebar.communicator.active}}
<ul>
<li><a href="/portal/communicator/email">Email</a></li>
</ul>
{{/if}}
</li>
{{/if}}
{{#if sidebar.resource_management.visible}}
<li>
<a {{#if sidebar.resource_management.active}}class="sidebar_entry_active"{{/if}} href="/portal/rm">Ressourcen</a>

View File

@ -1 +1 @@
unknown
v0.1-16-g4f906cb

View File

@ -2,10 +2,10 @@ use crate::database::controller::connector::establish_connection;
use crate::helper::settings::Settings;
use crate::modules::api::members::member_communication::{CommunicationTarget, CommunicationType};
use crate::schema::communication_targets::dsl::{communication_targets, target_id};
use diesel::query_dsl::filter_dsl::FilterDsl;
use diesel::{ExpressionMethods, RunQueryDsl};
use rocket::State;
use diesel::query_dsl::select_dsl::SelectDsl;
use crate::schema::groups_entities::dsl::groups_entities;
use diesel::{ExpressionMethods, RunQueryDsl, QueryDsl, JoinOnDsl};
use crate::schema::units_members::dsl::units_members;
/// Updates communication target via API.
/// Will only change field if field is Some, will ignore field if it's None.
@ -49,4 +49,56 @@ pub fn get_member_communication_types(settings: &State<Settings>) -> Result<Vec<
Err(e)
}
}
}
/// Returns list of all email addresses belonging to the specified member
pub fn get_member_email_addresses(settings: &State<Settings>, member_id: uuid::Uuid) -> Result<Vec<String>, diesel::result::Error>{
use crate::schema::communication_targets::dsl::*;
let connection = establish_connection(settings);
let email_type_id = uuid::Uuid::parse_str(&settings.mail.communication_email_type_id).expect("No valid communication_email_type_id set in config! Cant send emails!");
let data: Result<Vec<String>, diesel::result::Error> = communication_targets.select((value)).filter(com_type.eq(email_type_id)).filter(entity_id.eq(member_id)).load(&connection);
match data{
Ok(data) => Ok(data),
Err(e) => {
error!("Couldn't get email addresses for member: {}", e);
Err(e)
},
}
}
pub fn get_group_email_addresses(settings: &State<Settings>, group_id2: uuid::Uuid) -> Result<Vec<String>, diesel::result::Error>{
use crate::schema::communication_targets::dsl::*;
use crate::schema::groups_entities::dsl::*;
let connection = establish_connection(settings);
let email_type_id = uuid::Uuid::parse_str(&settings.mail.communication_email_type_id).expect("No valid communication_email_type_id set in config! Cant send emails!");
let data: Result<Vec<String>, diesel::result::Error> = communication_targets.distinct().left_join(groups_entities.on(crate::schema::groups_entities::entity_id.eq(crate::schema::communication_targets::entity_id))).filter(group_id.eq(group_id2)).select((value)).filter(com_type.eq(email_type_id)).load(&connection);
match data{
Ok(data) => Ok(data),
Err(e) => {
error!("Couldn't get email addresses for group: {}", e);
Err(e)
},
}
}
pub fn get_unit_email_addresses(settings: &State<Settings>,unit_id2: uuid::Uuid) -> Result<Vec<String>, diesel::result::Error>{
use crate::schema::communication_targets::dsl::*;
use crate::schema::units_members::dsl::*;
let connection = establish_connection(settings);
let email_type_id = uuid::Uuid::parse_str(&settings.mail.communication_email_type_id).expect("No valid communication_email_type_id set in config! Cant send emails!");
let data: Result<Vec<String>, diesel::result::Error> = communication_targets.distinct().left_join(units_members.on(crate::schema::units_members::member_id.eq(crate::schema::communication_targets::entity_id))).filter(unit_id.eq(unit_id2)).select((value)).filter(com_type.eq(email_type_id)).load(&connection);
match data{
Ok(data) => Ok(data),
Err(e) => {
error!("Couldn't get email addresses for unit: {}", e);
Err(e)
},
}
}

View File

@ -25,6 +25,7 @@ pub struct Application {
pub struct Mail{
pub from : String,
pub reply_to : String,
pub communication_email_type_id: String,
}
#[derive(Debug, Deserialize, Default)]

View File

@ -1,7 +1,5 @@
use crate::helper::sitebuilder::model::alerts::{Alert, AlertClass};
use crate::helper::sitebuilder::model::sidebar::{
EventManagement, MemberManagement, ResourceManagement, Sidebar, Summary,
};
use crate::helper::sitebuilder::model::sidebar::{EventManagement, MemberManagement, ResourceManagement, Sidebar, Summary, Communicator};
use crate::modules::member_management::model::member::Member;
impl Alert {
@ -16,7 +14,7 @@ impl Sidebar {
firstname: member.firstname.clone(),
lastname: member.lastname.clone(),
summary: Summary {
visible: member.has_permission("modules.dashboard.view".to_string()),
visible: member.has_permission(crate::permissions::modules::dashboard::VIEW.to_string()),
active: false,
},
resource_management: ResourceManagement {
@ -31,6 +29,10 @@ impl Sidebar {
visible: member.has_permission("modules.event_management.view".to_string()),
active: false,
},
communicator: Communicator {
visible: member.has_permission("modules.communicator.view".to_string()),
active: false,
}
}
}
}

View File

@ -6,6 +6,7 @@ pub struct Sidebar {
pub resource_management: ResourceManagement,
pub member_management: MemberManagement,
pub event_management: EventManagement,
pub communicator: Communicator,
}
#[derive(Serialize)]
@ -31,3 +32,9 @@ pub struct EventManagement {
pub visible: bool,
pub active: bool,
}
#[derive(Serialize)]
pub struct Communicator {
pub visible: bool,
pub active: bool,
}

View File

@ -133,6 +133,7 @@ fn main() {
modules::api::members::member_qualification::api_member_qualifications_read,
modules::api::groups::create::create_group,
modules::api::groups::delete::delete_groups,
modules::api::groups::read::get_groups,
modules::api::groups::read::read_group,
modules::api::groups::update::put_member_in_group,
modules::api::groups::delete::delete_member_from_group,
@ -143,10 +144,12 @@ fn main() {
modules::api::users::create::create_user,
modules::api::users::delete::delete_user,
modules::api::users::update::update_user,
modules::api::communicator::create::create_email,
modules::member_management::view::personal_profile::member_management_personal_profile_get,
modules::member_management::view::personal_profile::member_management_personal_profile_post,
modules::member_management::view::create_member::member_management_add_member,
modules::member_management::view::create_member::member_management_add_member_post,
modules::communicator::send_email::communicator_email_get,
],
)
.mount("/css", StaticFiles::from("resources/css"))

View File

@ -0,0 +1,156 @@
use rocket::State;
use crate::helper::settings::Settings;
use crate::helper::session_cookies::model::SessionCookie;
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
use rocket_contrib::json::Json;
use crate::modules::api::member_management::controller::parser::parse_member_cookie;
use crate::helper::mail_queue::queue::{Mail, MailQueue};
use crate::helper::mail_queue::worker::send_mail;
use std::sync::{Arc, PoisonError, RwLockWriteGuard};
use std::collections::VecDeque;
use crate::database::controller::api_communication_targets::{get_member_email_addresses, get_group_email_addresses, get_unit_email_addresses};
use diesel::result::Error;
use crate::helper::translate_diesel_error::translate_diesel;
use uuid::Uuid;
#[derive(Queryable, Clone, Deserialize, Serialize)]
pub struct ApiEmail{
pub(crate) to: Option<Vec<String>>,
pub(crate) to_members: Option<Vec<uuid::Uuid>>,
pub(crate) cc: Option<Vec<String>>,
pub(crate) cc_members: Option<Vec<uuid::Uuid>>,
pub(crate) bcc: Option<Vec<String>>,
pub(crate) bcc_members: Option<Vec<uuid::Uuid>>,
pub(crate) reply_to: Option<String>,
pub(crate) selected_groups: Option<Vec<uuid::Uuid>>,
pub(crate) selected_units: Option<Vec<uuid::Uuid>>,
pub(crate) subject: String,
pub(crate) body: String,
}
#[post("/api/communicator/email", format = "json", data = "<mail>")]
pub fn create_email(mq: State<Arc<MailQueue>>, settings: State<Settings>, cookie: SessionCookie, mail: Json<ApiEmail>) -> Result<(), Json<ApiErrorWrapper>>{
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::communicator::email::SEND.to_string()){
return Err(Json(ApiError::new(403, "Keine Berechtigung Email zu versenden!".to_string()).to_wrapper()));
}
//TODO: Check for each receiver if caller has permission to send email
let mail = mail.into_inner();
let mut to: Vec<String> = vec![];
let mut cc: Vec<String> = vec![];
let mut bcc: Vec<String> = vec![];
match mail.to{
Some(mut mail) => {
to.append(mail.as_mut())
}
None => {}
}
match mail.to_members{
Some(members) => {
for member_id in members{
match get_member_email_addresses(&settings, member_id){
Ok(mut addresses) => {
to.append(addresses.as_mut());
}
Err(e) => {
return Err(translate_diesel(e))
}
}
}
}
None => {}
}
match mail.cc{
Some(mut mail) => {
cc.append(mail.as_mut())
}
None => {}
}
match mail.cc_members{
Some(members) => {
for member_id in members{
match get_member_email_addresses(&settings, member_id){
Ok(mut addresses) => {
cc.append(addresses.as_mut());
}
Err(e) => {
return Err(translate_diesel(e))
}
}
}
}
None => {}
}
match mail.bcc{
Some(mut mail) => {
bcc.append(mail.as_mut())
}
None => {}
}
match mail.bcc_members{
Some(members) => {
for member_id in members{
match get_member_email_addresses(&settings, member_id){
Ok(mut addresses) => {
bcc.append(addresses.as_mut());
}
Err(e) => {
return Err(translate_diesel(e))
}
}
}
}
None => {}
}
match mail.selected_groups{
None => {}
Some(groups) => {
for group in groups{
match get_group_email_addresses(&settings, group){
Ok(mut emails) => {
bcc.append(emails.as_mut());
}
Err(e) => {
return Err(translate_diesel(e))
}
}
}
}
}
match mail.selected_units{
None => {}
Some(units) => {
for unit in units{
match get_unit_email_addresses(&settings, unit){
Ok(mut emails) => {
bcc.append(emails.as_mut());
}
Err(e) => {
return Err(translate_diesel(e))
}
}
}
}
}
if to.is_empty() && cc.is_empty() && bcc.is_empty(){
return Err(Json(ApiError::new(422, "Es muss mindestens ein Empfänger angegeben werden!".to_string()).to_wrapper()))
}
let composed_mail = Mail::new(settings.mail.from.clone(), to,mail.subject,cc,bcc,mail.reply_to,mail.body,None);
match mq.add_mail(composed_mail){
Ok(_) => Ok(()),
Err(_) => Err(Json(ApiError::new(500, "Couldn't add mail to mail queue!".to_string()).to_wrapper()))
}
}

View File

@ -0,0 +1 @@
pub mod create;

View File

@ -60,10 +60,7 @@ pub fn create_group(
) {
Ok(()) => Ok(Json(group)),
Err(e) => {
return Err(Json(
ApiError::new(500, "Es ist ein Datenbankfehler aufgetreten.".to_string())
.to_wrapper(),
))
return Err(translate_diesel(e))
}
}
}

View File

@ -1,7 +1,5 @@
use crate::database::controller::groups::get_group;
use crate::database::controller::groups_permissions::{
get_group_permission_for_role, get_group_role_permissions,
};
use crate::database::controller::groups::{get_group, get_raw_groups};
use crate::database::controller::groups_permissions::{get_group_permission_for_role, get_group_role_permissions, check_role_has_permission_on_entity};
use crate::database::controller::members::check_member_has_access;
use crate::database::controller::members_groups::get_member_search_results_in_group;
use crate::database::model::groups::RawGroup;
@ -34,6 +32,49 @@ pub struct DetailedGroup {
pub(crate) caller_permissions: CallerGroupPermissions,
}
#[derive(Serialize)]
pub struct GetGroupsResult{
pub(crate) groups: Vec<RawGroup>
}
/// Api method to get list of groups
/// Filters:
/// * with_caller_permission (String): Only show groups where caller has permission x
#[get("/api/groups?<with_caller_permission>", format = "json")]
pub fn get_groups(settings: State<Settings>, cookie: SessionCookie, with_caller_permission: Option<String>) -> Result<Json<GetGroupsResult>, Json<ApiErrorWrapper>>{
let caller = parse_member_cookie(cookie.member)?;
if !caller.has_permission(crate::permissions::modules::member_management::groups::VIEW.to_string()) {
return Err(Json(ApiError::new(403, "Keine Berechtigung, Gruppen abzurufen!".to_string()).to_wrapper()));
}
let groups = match get_raw_groups(&settings){
Ok(groups) => groups,
Err(e) => return Err(translate_diesel(e))
};
let mut groups_with_permission : Vec<RawGroup> = vec![];
for group in groups{
match &with_caller_permission {
Some(caller_permission) => {
if check_member_has_access(&settings, caller.entity_id, group.group_id, &caller_permission){
groups_with_permission.push(group);
}
},
None => {
groups_with_permission.push(group);
}
}
}
let res = GetGroupsResult{
groups: groups_with_permission
};
Ok(Json(res))
}
#[get("/api/groups/<entity_id>", format = "json")]
pub fn read_group(
settings: State<Settings>,

View File

@ -3,4 +3,5 @@ pub mod member_management;
pub mod members;
pub mod model;
pub mod units;
pub mod users;
pub mod users;
pub mod communicator;

View File

@ -9,15 +9,30 @@ use crate::database::controller::members::check_member_has_access;
use crate::database::controller::units::get_units;
use crate::helper::translate_diesel_error::translate_diesel;
#[get("/api/units", format = "json")]
#[get("/api/units?<with_caller_permission>", format = "json")]
pub fn read_unit_list(
settings: State<Settings>,
cookie: SessionCookie,
with_caller_permission: Option<String>
) -> Result<Json<Vec<RawUnit>>, Json<ApiErrorWrapper>> {
let _caller = parse_member_cookie(cookie.member)?;
let caller = parse_member_cookie(cookie.member)?;
match get_units(&settings){
Ok(units) => Ok(Json(units)),
Err(e) => Err(translate_diesel(e))
let units = match get_units(&settings){
Ok(units) => units,
Err(e) => return Err(translate_diesel(e))
};
match with_caller_permission{
None => Ok(Json(units)),
Some(permission) => {
let mut unit_list : Vec<RawUnit> = vec![];
for unit in units{
if check_member_has_access(&settings, caller.entity_id, unit.unit_id, &permission){
unit_list.push(unit);
}
}
Ok(Json(unit_list))
}
}
}

View File

@ -0,0 +1 @@
pub mod send_email;

View File

@ -0,0 +1,57 @@
use rocket_contrib::templates::Template;
use rocket::http::Status;
use rocket::State;
use crate::helper::settings::Settings;
use crate::helper::session_cookies::model::SessionCookie;
use crate::helper::sitebuilder::model::general::{Header, Footer, Stylesheet, Script};
use crate::helper::sitebuilder::model::sidebar::Sidebar;
use crate::helper::sitebuilder::model::alerts::Alert;
#[derive(Serialize)]
pub struct CommunicatorEmailTemplate{
pub header: Header,
pub footer: Footer,
pub sidebar: Sidebar,
pub alert: Option<Alert>,
pub to: Option<String>,
pub cc: Option<String>,
pub bcc: Option<String>
}
#[get("/portal/communicator/email?<to>&<cc>&<bcc>")]
pub fn communicator_email_get(cookie: SessionCookie, to: Option<String>, cc: Option<String>, bcc: Option<String>) -> Result<Template, Status> {
let caller = 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 !caller.has_permission(crate::permissions::modules::communicator::VIEW.to_string()){
return Err(Status::Forbidden)
}
let header = Header{
html_language: "de".to_string(),
site_title: "Email versenden".to_string(),
stylesheets: vec![Stylesheet {
path: "/css/errms.css".to_string(),
}]
};
let footer = Footer { scripts: vec![Script{ path: "/js/communicator.js".to_string() }] };
let mut sidebar = Sidebar::new(caller);
sidebar.communicator.active=true;
let templ = CommunicatorEmailTemplate{
header,
footer,
sidebar,
alert: None,
to,
cc,
bcc
};
Ok(Template::render("module_communicator_email", templ))
}

View File

@ -16,7 +16,7 @@ pub fn render(settings: &State<Settings>, caller: Member) -> Result<Template, St
let header = Header {
html_language: "de".to_string(),
site_title: "Mitgliedprofil".to_string(),
site_title: "Gruppen".to_string(),
stylesheets: vec![Stylesheet {
path: "/css/errms.css".to_string(),
}],

View File

@ -12,7 +12,6 @@ use crate::database::controller::create_member::create_member;
#[get("/portal/mm/add_member")]
pub fn member_management_add_member(
cookie: SessionCookie,
settings: State<Settings>,
) -> Result<Template, Status> {
let caller = match cookie.member {
//Unwraps member from cookie or send user to login if no member specified (user skipped member selection)

View File

@ -5,3 +5,4 @@ pub mod member_management;
pub mod operation_management;
pub mod resource_management;
pub mod welcome;
pub mod communicator;

View File

@ -15,6 +15,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
addresses_entities (address_id, entitiy_id) {
address_id -> Uuid,
@ -24,6 +25,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
buildings (entity_id) {
entity_id -> Uuid,
@ -47,6 +49,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
communication_types (type_id) {
type_id -> Uuid,
@ -56,6 +59,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
cost_centres (short_id) {
short_id -> Int4,
@ -75,6 +79,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
entities (entity_id) {
entity_id -> Uuid,
@ -83,6 +88,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
groups (entity_id) {
entity_id -> Uuid,
@ -93,6 +99,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
groups_entities (group_id, entity_id) {
group_id -> Uuid,
@ -102,6 +109,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
license_categories (name) {
name -> Text,
@ -111,6 +119,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
licenses_members (member_id, license_name) {
member_id -> Uuid,
@ -121,6 +130,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
login_attempts (id) {
id -> Uuid,
@ -154,6 +164,9 @@ table! {
}
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
members_roles (member_id, role_id) {
member_id -> Uuid,
role_id -> Text,
@ -162,6 +175,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
password_resets (token) {
token -> Text,
@ -172,6 +186,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
permissions (permission) {
permission -> Text,
@ -181,6 +196,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
qualification_categories (id) {
id -> Uuid,
@ -191,6 +207,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
qualifications (id) {
id -> Uuid,
@ -202,6 +219,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
qualifications_members (member_id, qualification_id) {
member_id -> Uuid,
@ -211,6 +229,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
roles (id) {
id -> Text,
@ -220,6 +239,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
roles_permissions (role_permission_id) {
role_id -> Text,
@ -230,6 +250,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
roles_permissions_context (role_permission_id, entity) {
role_permission_id -> Uuid,
@ -239,6 +260,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
units (unit_id) {
unit_id -> Uuid,
@ -248,6 +270,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
units_members (unit_id, member_id) {
unit_id -> Uuid,
@ -258,6 +281,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
users (id) {
id -> Uuid,
@ -268,6 +292,7 @@ table! {
table! {
use diesel::sql_types::*;
use diesel_geometry::sql_types::*;
vehicles (entity_id) {
entity_id -> Uuid,