Browse Source

updates

master
Keanu D?lle 2 years ago
parent
commit
1739300253
  1. 21
      Rocket.toml
  2. 4
      src/database/mod.rs
  3. 277
      src/database/profile.rs
  4. 104
      src/database/profile_communication.rs
  5. 3
      src/main.rs
  6. 193
      src/modules/mod.rs
  7. 59
      src/modules/profile.rs
  8. 2
      src/sitebuilder/builder.rs
  9. 1
      src/sitebuilder/mod.rs
  10. 24
      src/sitebuilder/profile_communication.rs
  11. 91
      static/css/portal.css
  12. 1
      static/html/templates/footer.html
  13. 2
      static/html/templates/header.html
  14. 2
      static/html/templates/internal-site.html
  15. 36
      static/html/templates/profile/communication.html
  16. 16
      static/html/templates/profile/communication_entry.html
  17. 84
      static/html/templates/profile/general.html
  18. 2
      static/html/templates/sidebar.html
  19. BIN
      static/img/edit.png
  20. BIN
      static/img/save.png
  21. 58
      static/js/portal.js

21
Rocket.toml

@ -0,0 +1,21 @@
[development]
address = "localhost"
port = 8000
keep_alive = 5
log = "debug"
secret_key = "DQJzyMUZ7uCNOmVRTdm7SQDz/XAZiFKHqRjL2y+FBLM="
limits = { forms = 32768 }
[staging]
address = "0.0.0.0"
port = 8000
keep_alive = 5
log = "normal"
limits = { forms = 32768 }
[production]
address = "0.0.0.0"
port = 8000
keep_alive = 5
log = "critical"
limits = { forms = 32768 }

4
src/database/mod.rs

@ -1,3 +1,5 @@
pub mod connection;
pub mod login;
pub mod permissions;
pub mod permissions;
pub mod profile;
pub mod profile_communication;

277
src/database/profile.rs

@ -0,0 +1,277 @@
use crate::database::connection::connect;
use chrono::NaiveDate;
use crate::modules::permissions::{UserPermissions, check_for_permission};
use rocket::request::Form;
pub enum Sex{
Unknown,
Male,
Female,
Other
}
#[derive(FromForm)]
pub struct ProfileFormData{
pub firstname : String,
pub lastname : String,
pub email : String,
pub date_of_birth : String,
pub birthplace : String,
pub sex : String,
pub academic_title : String,
pub salutation : String,
pub formal_salutation : String,
pub personnel_number : i32,
pub street : String,
pub street_number : String,
pub postcode : String,
pub place : String
}
pub struct ProfileData{
pub firstname : String,
pub lastname : String,
pub email : Option<String>,
pub date_of_birth : Option<NaiveDate>,
pub birthplace : Option<String>,
pub sex : Option<Sex>,
pub academic_title : Option<String>,
pub salutation : Option<String>,
pub formal_salutation : Option<String>,
pub personnel_number : Option<i32>,
pub street : Option<String>,
pub street_number : Option<String>,
pub postcode : Option<String>,
pub place : Option<String>
}
pub fn sex_from_i8(val : Option<i8>) -> Option<Sex>{
if val.is_some() {
Some(match val.unwrap() {
0 => Sex::Unknown,
1 => Sex::Male,
2 => Sex::Female,
9 => Sex::Other,
_ => Sex::Other
})
}else{
None
}
}
pub fn sex_to_i8(val : Sex) -> i8{
match val {
Sex::Unknown => 0,
Sex::Male => 1,
Sex::Female => 2,
Sex::Other => 9
}
}
impl ProfileData{
pub fn to_vec(&self) -> Vec<(String, String)>{
return vec!(("firstname".to_string(), self.firstname.clone()), ("lastname".to_string(), self.lastname.clone()), ("email".to_string(), self.email.string_convert()), ("date_of_birth".to_string(), self.date_of_birth.string_convert()), ("birthplace".to_string(), self.birthplace.string_convert()), ("birthplace".to_string(), self.birthplace.string_convert()), ("sex".to_string(), self.sex.string_convert()), ("academic_title".to_string(), self.academic_title.string_convert()), ("salutation".to_string(), self.salutation.string_convert()), ("formal_salutation".to_string(), self.formal_salutation.string_convert()), ("personnel_number".to_string(), self.personnel_number.string_convert()), ("street".to_string(), self.street.string_convert()), ("street_number".to_string(), self.street_number.string_convert()), ("postcode".to_string(), self.postcode.string_convert()), ("place".to_string(), self.place.string_convert()))
}
}
pub trait StringConvert<T>{
fn string_convert(&self) -> String;
}
impl StringConvert<String> for Option<String>{
fn string_convert(&self) -> String{
if self.is_some() {
self.as_ref().unwrap().to_string()
} else {
String::from("")
}.parse().unwrap()
}
}
impl StringConvert<i32> for Option<i32>{
fn string_convert(&self) -> String{
if self.is_some() {
self.as_ref().unwrap().to_string()
} else {
String::from("")
}.parse().unwrap()
}
}
impl StringConvert<Sex> for Option<Sex>{
fn string_convert(&self) -> String{
if self.is_some() {
match self.as_ref().unwrap(){
Sex::Unknown => String::from(""),
Sex::Male => String::from("männlich"),
Sex::Female => String::from("weiblich"),
Sex::Other => String::from("sonstiges")
}
} else {
String::from("")
}.parse().unwrap()
}
}
impl StringConvert<NaiveDate> for Option<NaiveDate>{
fn string_convert(&self) -> String{
if self.is_some() {
self.as_ref().unwrap().format("%d.%m.%Y").to_string()
} else {
String::from("")
}.parse().unwrap()
}
}
pub fn get_profile_data(id : i32) -> Option<ProfileData>{
let pool = connect();
pool.prep_exec(r"SELECT firstname, lastname, email, birthplace, date_of_birth, sex, academic_title, salutation, formal_salutation, personnel_number, street, street_number, postcode, place FROM users WHERE id = :id", params!{"id" => id}).map(| result| {
result.map(|x| x.unwrap()).map(|mut row| {
ProfileData{
firstname : row.take("firstname").unwrap(),
lastname : row.take("lastname").unwrap(),
email : row.take("email").unwrap(),
date_of_birth : row.take("date_of_birth").unwrap(),
birthplace : row.take("birthplace").unwrap(),
sex: sex_from_i8(row.take("sex").unwrap()),
academic_title : row.take("academic_title").unwrap(),
salutation : row.take("salutation").unwrap(),
formal_salutation : row.take("formal_salutation").unwrap(),
personnel_number : row.take("personnel_number").unwrap(),
street : row.take("street").unwrap(),
street_number : row.take("street_number").unwrap(),
postcode : row.take("postcode").unwrap(),
place : row.take("place").unwrap()
}
}).nth(0)
}).unwrap()
}
pub fn insert_profile_data(id : i32, user_permissions : &UserPermissions, data : Form<ProfileFormData>) -> bool{
let pool = connect();
let old_data = get_profile_data(id).unwrap();
let data = check_profile_data_permissions(user_permissions, data, old_data);
let mut stmt = pool.prepare(r"UPDATE users SET firstname = :firstname, lastname = :lastname, email = :email, birthplace = :birthplace, date_of_birth = :date_of_birth, sex = :sex, academic_title = :academic_title, salutation = :salutation, formal_salutation = :formal_salutation, personnel_number = :personnel_number, street = :street, street_number = :street_number, postcode = :postcode, place = :place WHERE id = :id").unwrap();
stmt.execute(params!{
"firstname" => data.firstname,
"lastname" => data.lastname,
"email" => data.email,
"birthplace" => data.birthplace,
"date_of_birth" => data.date_of_birth,
"sex" => sex_to_i8(data.sex.unwrap()),
"academic_title" => data.academic_title,
"salutation" => data.salutation,
"formal_salutation" => data.formal_salutation,
"personnel_number" => data.personnel_number,
"street" => data.street,
"street_number" => data.street_number,
"postcode" => data.postcode,
"place" => data.place,
"id" => id
}).unwrap();
true
}
fn check_profile_data_permissions(user_permissions : &UserPermissions, new_data: Form<ProfileFormData>, old_data : ProfileData) -> ProfileData{
let mut profile_data: ProfileData = ProfileData {
firstname: "".to_string(),
lastname: "".to_string(),
email: None,
date_of_birth: None,
birthplace: None,
sex: None,
academic_title: None,
salutation: None,
formal_salutation: None,
personnel_number: None,
street: None,
street_number: None,
postcode: None,
place: None
};
if !check_for_permission("modules.edit.profile.own.general.firstname", &user_permissions) {
profile_data.firstname = old_data.firstname;
}else{
profile_data.firstname = new_data.firstname.clone();
}
if !check_for_permission("modules.edit.profile.own.general.lastname", &user_permissions) {
profile_data.lastname = old_data.lastname;
}else{
profile_data.lastname = new_data.lastname.clone();
}
if !check_for_permission("modules.edit.profile.own.general.email", &user_permissions) {
profile_data.email = old_data.email;
}else{
profile_data.email = Some(new_data.email.clone());
}
if !check_for_permission("modules.edit.profile.own.general.birthplace", &user_permissions) {
profile_data.birthplace = old_data.birthplace;
}else{
profile_data.birthplace = Some(new_data.birthplace.clone());
}
if !check_for_permission("modules.edit.profile.own.general.date_of_birth", &user_permissions) {
profile_data.date_of_birth = old_data.date_of_birth;
}else{
debug!("date: {}", new_data.date_of_birth);
profile_data.date_of_birth = Some(NaiveDate::parse_from_str(&new_data.date_of_birth, "%d.%m.%Y").unwrap());
}
if !check_for_permission("modules.edit.profile.own.general.sex", &user_permissions) {
profile_data.sex = old_data.sex;
}else{
let sex = match new_data.sex.as_str(){
"" => Sex::Unknown,
"männlich" => Sex::Male,
"weiblich" => Sex::Female,
"sonstiges" => Sex::Other,
_ => Sex::Unknown
};
profile_data.sex = Some(sex);
}
if !check_for_permission("modules.edit.profile.own.general.academic_title", &user_permissions) {
profile_data.academic_title = old_data.academic_title;
}else{
profile_data.academic_title = Some(new_data.academic_title.clone());
}
if !check_for_permission("modules.edit.profile.own.general.salutation", &user_permissions) {
profile_data.salutation = old_data.salutation;
}else{
profile_data.salutation = Some(new_data.salutation.clone());
}
if !check_for_permission("modules.edit.profile.own.general.formal_salutation", &user_permissions) {
profile_data.formal_salutation = old_data.formal_salutation;
}else{
profile_data.formal_salutation = Some(new_data.formal_salutation.clone());
}
if !check_for_permission("modules.edit.profile.own.general.personnel_number", &user_permissions) {
profile_data.personnel_number = old_data.personnel_number;
}else{
profile_data.personnel_number = Some(new_data.personnel_number.clone());
}
if !check_for_permission("modules.edit.profile.own.general.street", &user_permissions) {
profile_data.street = old_data.street;
}else{
profile_data.street = Some(new_data.street.clone());
}
if !check_for_permission("modules.edit.profile.own.general.street_number", &user_permissions) {
profile_data.street_number = old_data.street_number;
}else{
profile_data.street_number = Some(new_data.street_number.clone());
}
if !check_for_permission("modules.edit.profile.own.general.postcode", &user_permissions) {
profile_data.postcode = old_data.postcode;
}else{
profile_data.postcode = Some(new_data.postcode.clone());
}
if !check_for_permission("modules.edit.profile.own.general.place", &user_permissions) {
profile_data.place = old_data.place;
}else{
profile_data.place = Some(new_data.place.clone());
}
profile_data
}

104
src/database/profile_communication.rs

@ -0,0 +1,104 @@
//TODO: CHECK if UserCommunicationEmail belongs to userid!!!!!!!!!!!!!!
use std::collections::HashMap;
use crate::database::connection::connect;
use crate::database::profile::StringConvert;
pub enum UserCommunicationVisibility{
Private,
Limited,
Public
}
#[derive(FromForm)]
pub struct UserCommunicationFormEntry{
pub profile_communication_id : i32,
pub profile_communication_description : String,
pub profile_communication_type : String,
pub profile_communication_visibility : String,
pub profile_communication_value : String
}
pub struct UserCommunicationEmail{
pub id : i32,
pub email : String,
pub description : Option<String>,
pub visibility : UserCommunicationVisibility
}
pub struct UserCommunicationPhoneNumber{
pub id : i32,
pub country_prefix : i16,
pub number : i64,
pub description : Option<String>,
pub visibility : UserCommunicationVisibility
}
pub struct UserCommunicationData{
pub emails : HashMap<i32, UserCommunicationEmail>,
pub phone_numbers : HashMap<i32, UserCommunicationPhoneNumber>
}
impl StringConvert<UserCommunicationVisibility> for UserCommunicationVisibility{
fn string_convert(&self) -> String{
match self{
UserCommunicationVisibility::Private => String::from("privat"),
UserCommunicationVisibility::Limited => String::from("bedingt sichtbar"),
UserCommunicationVisibility::Public => String::from("öffentlich")
}
}
}
fn visibility_from_i8(visibility : i8) -> UserCommunicationVisibility{
match visibility{
0 => UserCommunicationVisibility::Private,
1 => UserCommunicationVisibility::Limited,
2 => UserCommunicationVisibility::Public,
_ => UserCommunicationVisibility::Private
}
}
pub fn get_communication_data(userid : i32) -> UserCommunicationData{
let mut communication_data = UserCommunicationData{
emails: HashMap::new(),
phone_numbers: HashMap::new()
};
let pool = connect();
//Grep phone data
pool.prep_exec(r"SELECT id, country_prefix, number, description, visibility FROM communication_phone_numbers WHERE f_users_id = :user_id", params!{"user_id" => userid}).map(|mut result|{
while let Some(row) = result.next() {
let mut row = row.unwrap().clone();
let id = row.take("id").unwrap();
let number = UserCommunicationPhoneNumber{
id,
country_prefix: row.take("country_prefix").unwrap(),
number: row.take("number").unwrap(),
description: row.take("description"),
visibility: visibility_from_i8(row.take("visibility").unwrap())
};
communication_data.phone_numbers.insert(id, number);
}
}).unwrap();
//Grep email data
pool.prep_exec(r"SELECT id, email, description, visibility FROM communication_email_addresses WHERE f_users_id = :user_id", params!{"user_id" => userid}).map(|mut result|{
while let Some(row) = result.next() {
let mut row = row.unwrap().clone();
let id = row.take("id").unwrap();
let email = UserCommunicationEmail{
id,
email: row.take("email").unwrap(),
description: row.take("description"),
visibility: visibility_from_i8(row.take("visibility").unwrap())
};
communication_data.emails.insert(id, email);
}
}).unwrap();
//Return collected data
communication_data
}

3
src/main.rs

@ -68,8 +68,9 @@ fn main() {
sitebuilder::builder::load_static_files();
rocket::ignite()
.mount("/", routes![site_main, modules::login::login_get, modules::login::login_post, modules::portal::portal_get, modules::profile::profile_get])
.mount("/", routes![site_main, modules::login::login_get, modules::login::login_post, modules::portal::portal_get, modules::profile::profile_get, modules::profile::profile_post, modules::profile::profile_communication_post])
.mount("/style/", StaticFiles::from("static/css")) //TODO: Switch to own FileCache
.mount("/script/", StaticFiles::from("static/js"))
.mount("/img/", StaticFiles::from("static/img"))
.register(catchers![not_found])
.launch();

193
src/modules/mod.rs

@ -7,9 +7,196 @@ pub mod portal;
pub mod profile;
#[cfg(test)]
mod tests {
mod permissions_tests {
use crate::modules::permissions::{UserPermissions, check_for_permission};
use std::collections::HashMap;
#[test]
fn check_check_for_permissions_super_user() {
let mut user_permissions: HashMap<String, bool> = HashMap::new();
let group_permissions: HashMap<String, bool> = HashMap::new();
user_permissions.insert("*".to_string(), true);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("testpermission.test123", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_super_user2() {
let mut user_permissions: HashMap<String, bool> = HashMap::new();
let group_permissions: HashMap<String, bool> = HashMap::new();
user_permissions.insert("*".to_string(), true);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("testpermission", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_super_group() {
let user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
group_permissions.insert("*".to_string(), true);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("testpermission.test123", &user_all_permissions));
}
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
fn check_check_for_permissions_super_group2() {
let user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
group_permissions.insert("*".to_string(), true);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("testpermission", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_super_exclude_permission() {
let mut user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
user_permissions.insert("testpermission".to_string(), false); //Exclude permission explicit for user.
group_permissions.insert("*".to_string(), true);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(!check_for_permission("testpermission", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_super_additional_permission() {
let mut user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
user_permissions.insert("testpermission".to_string(), true); //Exclude permission explicit for user.
group_permissions.insert("*".to_string(), false);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("testpermission", &user_all_permissions));
assert!(!check_for_permission("testpermission2", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_empty() {
let user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
group_permissions.insert("*".to_string(), true);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_empty2() {
let user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
group_permissions.insert("*".to_string(), false);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(!check_for_permission("", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_user_before_group() {
let mut user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
group_permissions.insert("testpermission".to_string(), true);
user_permissions.insert("testpermission".to_string(), false);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(!check_for_permission("testpermission", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_super_sublevel() {
let user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
group_permissions.insert("testpermission.*".to_string(), true);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("testpermission.test123", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_super_sublevel2() {
let user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
group_permissions.insert("testpermission.*".to_string(), true);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("testpermission.test123.test321", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_super_sublevel3() {
let user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
group_permissions.insert("testpermission.*".to_string(), true);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("testpermission.test123.test321.*", &user_all_permissions));
}
#[test]
fn check_check_for_permissions_super_sublevel4_negation() { //Notice: This shows that you can't negate an super permission! TODO: Add to documentation
let user_permissions: HashMap<String, bool> = HashMap::new();
let mut group_permissions: HashMap<String, bool> = HashMap::new();
group_permissions.insert("testpermission.*".to_string(), true);
group_permissions.insert("testpermission.test.*".to_string(), false);
let user_all_permissions : UserPermissions = UserPermissions{
user_permissions,
group_permissions
};
assert!(check_for_permission("testpermission.test123", &user_all_permissions));
assert!(check_for_permission("testpermission.test", &user_all_permissions));
assert!(check_for_permission("testpermission.test.test123", &user_all_permissions));
}
}

59
src/modules/profile.rs

@ -5,6 +5,10 @@ use crate::sitebuilder::internal_site;
use rocket::http::Status;
use crate::modules::permissions::{check_for_permission, UserPermissions};
use crate::sitebuilder::builder::build;
use crate::{database, sitebuilder};
use crate::database::profile::ProfileFormData;
use rocket::request::Form;
use crate::database::profile_communication::{UserCommunicationData, UserCommunicationFormEntry};
#[get("/portal/profile?<section>")]
pub fn profile_get(userdata : UserData, section : Option<String>) -> Result<content::Html<String>, Status>{
@ -13,6 +17,38 @@ pub fn profile_get(userdata : UserData, section : Option<String>) -> Result<cont
return Ok(content::Html(format!("No access!")));
}
return Ok(content::Html(format!("{}", profile(userdata, section, &user_permissions))))
}
#[post("/portal/profile?<section>", data="<form_profile>")]
pub fn profile_post(userdata : UserData, section : Option<String>, form_profile : Form<ProfileFormData>) -> Result<content::Html<String>, Status>{
let user_permissions = crate::database::permissions::get_permissions(userdata.id);
if !check_for_permission("modules.edit.profile.own", &user_permissions){
return Ok(content::Html(format!("No access!")));
}
if check_for_permission("modules.edit.profile.own", &user_permissions){
database::profile::insert_profile_data(userdata.id, &user_permissions, form_profile);
}
return Ok(content::Html(format!("{}", profile(userdata, section, &user_permissions))))
}
#[post("/portal/profile?section=communication", data="<form_profile>")]
pub fn profile_communication_post(userdata : UserData, form_profile : Form<UserCommunicationFormEntry>) -> Result<content::Html<String>, Status>{
let user_permissions = crate::database::permissions::get_permissions(userdata.id);
if !check_for_permission("modules.edit.communication.own", &user_permissions){
return Ok(content::Html(format!("No access!")));
}
if check_for_permission("modules.edit.communication.own", &user_permissions){
debug!("WORKS!!!!");
}
return Ok(content::Html(format!("{}", profile(userdata, Some("communication".to_string()), &user_permissions))))
}
fn profile(userdata : UserData, section : Option<String>, user_permissions : &UserPermissions) -> String{
let mut general = SidebarItem::new("Allgemeine Angaben".to_string(), "?section=general".to_string(), "fa-address-card".to_string(), false);
let mut communication = SidebarItem::new("Kommunikation".to_string(), "?section=communication".to_string(), "fa-envelope".to_string(), false);
let mut hours_worked = SidebarItem::new("Geleistete Stunden".to_string(), "?section=hours_worked".to_string(), "fa-clock".to_string(), false);
@ -24,11 +60,11 @@ pub fn profile_get(userdata : UserData, section : Option<String>) -> Result<cont
let content_pane : String = match section.as_ref() {
"general" => {
general.set_active();
show_general(&user_permissions)
show_general(&userdata, &user_permissions)
},
"communication" => {
communication.set_active();
show_communication(&user_permissions)
show_communication(&userdata, &user_permissions)
},
"hours_worked" => {
hours_worked.set_active();
@ -65,24 +101,27 @@ pub fn profile_get(userdata : UserData, section : Option<String>) -> Result<cont
sidebar.add(skills);
}
let content = internal_site::parse(&userdata, &user_permissions, String::from("ERMS - Profil"), sidebar, content_pane);
return Ok(content::Html(format!("{}", content)))
internal_site::parse(&userdata, &user_permissions, String::from("ERMS - Profil"), sidebar, content_pane)
}
pub fn show_general(user_permissions : &UserPermissions) -> String{
pub fn show_general(userdata : &UserData, user_permissions : &UserPermissions) -> String {
if !check_for_permission("modules.view.profile.own.general", &user_permissions) {
String::from("No access!")
}else{
build("static/html/templates/profile/general.html", vec!()).unwrap_or(String::from("Error loading content."))
} else {
let profile_data = match database::profile::get_profile_data(userdata.id) {
Some(profile_data) => profile_data,
None => return String::from("Couldn't load data.")
};
build("static/html/templates/profile/general.html", profile_data.to_vec()).unwrap_or(String::from("Error loading content."))
}
}
pub fn show_communication(user_permissions : &UserPermissions) -> String{
pub fn show_communication(userdata : &UserData, user_permissions : &UserPermissions) -> String{
if !check_for_permission("modules.view.profile.own.communication", &user_permissions) {
String::from("No access!")
}else{
String::from("")
let communication_data : UserCommunicationData = database::profile_communication::get_communication_data(userdata.id);
build("static/html/templates/profile/communication.html", vec!(("communication_entries".to_string(), sitebuilder::profile_communication::parse(communication_data)))).unwrap_or(String::from("Error loading content."))
}
}

2
src/sitebuilder/builder.rs

@ -20,7 +20,7 @@ pub fn build(id: &str, parms: Vec<(String, String)>) -> Option<String> {
/// Loads all files in static/css static/html and static/js into filecache.
pub fn load_static_files() {
visit_dirs(Path::new("static"), &add_file_to_cache).unwrap();
visit_dirs(Path::new("static/html"), &add_file_to_cache).unwrap();
}
///Adds DirEntrys to Filecache.

1
src/sitebuilder/mod.rs

@ -3,6 +3,7 @@ pub mod stylesheets;
pub mod headerbar;
pub mod internal_site;
pub mod sidebar;
pub mod profile_communication;
#[cfg(test)]
mod tests {

24
src/sitebuilder/profile_communication.rs

@ -0,0 +1,24 @@
use crate::database::profile_communication::{UserCommunicationData, UserCommunicationEmail, UserCommunicationPhoneNumber};
use std::collections::HashMap;
use crate::sitebuilder::builder::build;
use crate::database::profile::StringConvert;
pub fn parse(communication_data : UserCommunicationData) -> String{
format!("{}{}", parse_email(communication_data.emails), parse_phone(communication_data.phone_numbers))
}
fn parse_email(emails : HashMap<i32, UserCommunicationEmail>) -> String {
let mut output = String::from("");
for (id, email) in emails{
output.push_str(&build("static/html/templates/profile/communication_entry.html", vec!(("id".to_string(), id.to_string()), ("type".to_string(), "E-Mail Adresse".to_string()), ("value".to_string(), email.email), ("description".to_string(), email.description.string_convert()), ("visibility".to_string(), email.visibility.string_convert()))).unwrap());
}
output
}
fn parse_phone(numbers : HashMap<i32, UserCommunicationPhoneNumber>) -> String{
let output = String::from("");
output
}

91
static/css/portal.css

@ -82,4 +82,95 @@ html, body{
a:hover {
text-decoration: none;
}
.profile-input-field{
padding-left:5px;
text-overflow:ellipsis;
}
.no-bg-image{
background-image: none !important;
background-color: transparent !important;
}
.profile-input-field:hover{
background-image: linear-gradient(0deg,#009688 2px,rgba(0,150,136,0) 0),linear-gradient(0deg,rgba(0,0,0,.26) 1px,transparent 0) !important;
}
.profile-input-field:focus{
background-image: linear-gradient(0deg,#009688 2px,rgba(0,150,136,0) 0),linear-gradient(0deg,rgba(0,0,0,.26) 1px,transparent 0) !important;
}
.form-control:focus {
border-color: inherit;
-webkit-box-shadow: none;
box-shadow: none;
}
.name-divider{
width:100%;
border-bottom: 1px solid #E5E4E4;
padding-top: 5px;
color: grey;
}
.bmd-label-floating{
color: black !important;
}
.profile-col{
display: flex;
flex-flow: row wrap;
}
.profile-input-label{
width: 200px;
justify-content: left !important;
}
.profile{
padding: 5px;
}
.profile_save{
}
.profile-communication-button{
background-color: transparent;
border: 0;
}
.profile-communication-img{
height: 30px;
}
.divTable{
display: table;
width: 100%;
}
.divTableRow {
display: table-row;
}
.divTableHeading {
background-color: #EEE;
display: table-header-group;
}
.divTableCell, .divTableHead {
border: 1px solid #999999;
display: table-cell;
padding: 3px 10px;
}
.divTableHeading {
background-color: #EEE;
display: table-header-group;
font-weight: bold;
}
.divTableFoot {
background-color: #EEE;
display: table-footer-group;
font-weight: bold;
}
.divTableBody {
display: table-row-group;
}

1
static/html/templates/footer.html

@ -1,5 +1,6 @@
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="/script/portal.js"></script>
</body>
</html>

2
static/html/templates/header.html

@ -6,7 +6,7 @@
<title>[[title]]</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/bootstrap-material-design@4.1.1/dist/css/bootstrap-material-design.min.css" integrity="sha384-wXznGJNEXNG1NFsbm0ugrLFMQPWswR3lds2VeinahP8N0zJw9VWSopbjv2x7WCvX" crossorigin="anonymous">
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<script src="https://kit.fontawesome.com/a076d05399.js" data-auto-replace-svg="nest"></script>
[[stylesheets]]
<link rel="stylesheet" href="/style/portal.css">
</head>

2
static/html/templates/internal-site.html

@ -1,6 +1,6 @@
<div class="container-fluid d-flex flex-column h-100 m-0 r-0">
<div class="row">[[headerbar]]</div>
<div class="row h-100">[[sidebar]]
<div class="col-sm-10 main">[[content]]</div>
<div class="col-lg-10 main">[[content]]</div>
</div>
</div>

36
static/html/templates/profile/communication.html

@ -0,0 +1,36 @@
<div class="profile">
<h2 class="name-divider">[[firstname]] [[lastname]]</h2><hr>
<div class="form-row w-100">
<div class="row w-100">
<div class="col-8">
<div class="divTable">
<!--<thead>
<tr>
<th scope="col">Typ</th>
<th scope="col">Wert</th>
<th scope="col">Beschreibung</th>
<th scope="col">Sichtbarkeit</th>
<th scope="col"></th>
</tr>
</thead>-->
<div class="divTableBody">
[[communication_entries]]<!--
<div class="divTableRow">
<td scope="row"></td>
<td><input type="text" class="form-control" name="profile_communication_new_value"></td>
<td><input type="text" class="form-control" name="profile_communication_new_description"></td>
<td><select class="form-control">
<option>privat</option>
<option>bedingt sichtbar</option>
<option>öffentlich</option>
</select></td>
</div>-->
</div>
</div>
</div>
<div class="col-4">
</div>
</div>
</div>
</div>

16
static/html/templates/profile/communication_entry.html

@ -0,0 +1,16 @@
<form action="/portal/profile?section=communication" class="profile-communication-form" method="post">
<div class="divTableRow">
<input name="profile_communication_id" type="hidden" value="[[id]]">
<div class="divTableCell"><span class="profile-communication-input-field-select-span">[[type]]</span><select class="form-control profile_communication_type profile-communication-input-field-select" name="profile_communication_type" value="[[type]]">
<option id="email">E-Mail Adresse</option>
<option id="phone">Telefonnummer</option>
</select></div>
<div class="divTableCell"><input class="form-control profile-communication-input-field" name="profile_communication_value" readonly="readonly" type="text" value="[[value]]" /></div>
<div class="divTableCell"><input class="form-control profile-communication-input-field" name="profile_communication_description" readonly="readonly" type="text" value="[[description]]" /></div>
<div class="divTableCell"><span class="profile-communication-input-field-select-span">[[visibility]]</span><select class="form-control profile_communication_visibility profile-communication-input-field-select" name="profile_communication_visibility" value="[[visibility]]">
<option>privat</option>
<option>bedingt sichtbar</option>
<option>öffentlich</option>
</select></div>
<div class="divTableCell"><button class="profile-communication-edit profile-communication-button" type="button"><img alt="Bearbeiten" class="profile-communication-img" src="/img/edit.png" /></button> <button class="profile-communication-save profile-communication-button" style="display: none;" type="button"><img alt="Speichern" class="profile-communication-img" src="/img/save.png" /></button></div>
</div></form>

84
static/html/templates/profile/general.html

@ -1,12 +1,78 @@
<form action="#" method="post">
<div class="profile">
<form action="#" method="post" id="form_profile" class="form-inline">
<h2 class="name-divider">[[firstname]] [[lastname]] <button type="button" id="profile_save" class="form-control" style="float: right;">Bearbeiten</button></h2><hr>
<div class="form-row">
<div class="form-group col-md-3">
<label for="forename" class="bmd-label-floating">Vorname</label>
<input type="text" class="form-control" id="forename">
<div class="row">
<div class="col-4 profile-col">
<div class="form-group">
<label for="firstname" class="bmd-label-floating profile-input-label">Vorname</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="firstname" size="30" id="firstname" value="[[firstname]]" readonly>
</div>
<div class="form-group">
<label for="lastname" class="bmd-label-floating profile-input-label">Nachname</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="lastname" size="30" id="lastname" value="[[lastname]]" readonly>
</div>
<div class="form-group">
<label for="sex" class="bmd-label-floating profile-input-label">Geschlecht</label>
<select class="form-control profile-input-field-select no-bg-image" id="sex" name="sex" value="[[sex]]" disabled>
<option value="männlich">männlich</option>
<option value="weiblich">weiblich</option>
<option value="sonstiges">sonstiges</option>
</select><noscript>Geschlecht : [[sex]]</noscript>
</div>
<div class="form-group">
<label for="academic_title" class="bmd-label-floating profile-input-label">akad. Titel</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="academic_title" size="10" id="academic_title" value="[[academic_title]]" readonly>
</div>
<div class="form-group">
<label for="salutation" class="bmd-label-floating profile-input-label">Anrede</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="salutation" size="20" id="salutation" value="[[salutation]]" readonly>
</div>
<div class="form-group">
<label for="formal_salutation" class="bmd-label-floating profile-input-label">formelle Anrede</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="formal_salutation" size="10" id="formal_salutation" value="[[formal_salutation]]" readonly>
</div>
<div class="form-group">
<label for="email" class="bmd-label-floating profile-input-label">Email</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="email" size="10" id="email" value="[[email]]" readonly>
</div>
</div>
<div class="form-group col-md-3">
<label for="lastname" class="bmd-label-floating">Nachname</label>
<input type="text" class="form-control" id="lastname">
<div class="col-4">
<div class="form-group">
<label for="personnel_number" class="bmd-label-floating profile-input-label">Personalnummer</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="personnel_number" size="10" id="personnel_number" value="[[personnel_number]]" readonly>
</div>
<div class="form-group">
<label for="date_of_birth" class="bmd-label-floating profile-input-label">Geburtsdatum</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="date_of_birth" size="10" id="date_of_birth" value="[[date_of_birth]]" readonly>
</div>
<div class="form-group">
<label for="birthplace" class="bmd-label-floating profile-input-label">Geburtsort</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="birthplace" size="20" id="birthplace" value="[[birthplace]]" readonly>
</div>
<br><b>Anschrift</b>
<div class="form-group">
<label for="street" class="bmd-label-floating profile-input-label">Straße</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="street" size="20" id="street" value="[[street]]" readonly>
</div>
<div class="form-group">
<label for="street_number" class="bmd-label-floating profile-input-label">Hausnummer</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="street_number" size="20" id="street_number" value="[[street_number]]" readonly>
</div>
<div class="form-group">
<label for="postcode" class="bmd-label-floating profile-input-label">Postleitzahl</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="postcode" size="20" id="postcode" value="[[postcode]]" readonly>
</div>
<div class="form-group">
<label for="place" class="bmd-label-floating profile-input-label">Ort</label>
<input type="text" class="form-control profile-input-field no-bg-image" name="place" size="20" id="place" value="[[place]]" readonly>
</div>
</div>
</div>
</form>
<div class="col-4">
</div>
</div>
</div>
</form>
</div>

2
static/html/templates/sidebar.html

@ -1,3 +1,3 @@
<div class="col-sm-2 sidebar">
<div class="col-lg-2 sidebar">
[[sidebar_items]]
</div>

BIN
static/img/edit.png

After

Width: 50  |  Height: 50  |  Size: 951 B

BIN
static/img/save.png

After

Width: 50  |  Height: 50  |  Size: 606 B

58
static/js/portal.js

@ -0,0 +1,58 @@
FontAwesomeConfig = { autoReplaceSvg: 'nest' };
var profileEditAcitve = false;
function resizeInput() {
$(this).attr('size', $(this).val().length);
}
function saveProfile(){
}
$( document ).ready(function() {
if($('#sex').length) {
$('#sex').val($('#sex').attr('value'));
}
if($('.profile-communication-input-field-select').length) {
$('.profile-communication-input-field-select').hide();
}
if($('.profile_communication_type').length) {
$('.profile_communication_type').each(function () {
var value = $(this).attr('value');
$(this).val(value);
});
}
if($('.profile_communication_visibility').length) {
$('.profile_communication_visibility').each(function () {
var value = $(this).attr('value');
$(this).val(value);
});
}
$('input[type="text"]').keyup(resizeInput).each(resizeInput);
$('#profile_save').click(function(){
if(!profileEditAcitve){
$('#profile_save').text("Speichern");
$('.profile-input-field').toggleClass("no-bg-image");
$('.profile-input-field').css('background-color', 'rgb(232, 232, 232)');
$('.profile-input-field').prop('readOnly', false);
$('.profile-input-field-select').prop('disabled', false);
profileEditAcitve = true;
}else{
profileEditActive = false;
$('#form_profile').submit();
}
});
$('.profile-communication-edit').click(function(){
$(this).parent().parent().find("input").prop('readOnly', false);
$(this).parent().parent().find(".profile-communication-input-field-select").show();
$(this).parent().parent().find(".profile-communication-input-field-select-span").hide();
$(this).parent().parent().find(".profile-communication-edit").hide();
$(this).parent().parent().find(".profile-communication-save").show();
});
$('.profile-communication-save').click(function(){
$('.profile-communication-form').submit();
})
});
Loading…
Cancel
Save