diff --git a/src/model/activity.rs b/src/model/activity.rs index a2f0759..5a83180 100644 --- a/src/model/activity.rs +++ b/src/model/activity.rs @@ -17,11 +17,14 @@ pub struct Activity { pub keep_until: Option, } +// TODO: add `reason` as additional db field, to be able to query and show this to the users pub enum Reason<'a> { // `User` tried to login with `String` as UserAgent SuccLogin(&'a User, String), // `User` changed the data of `User`, explanation in `String` UserDataChange(&'a ManageUserUser, &'a User, String), + // New Note for User + NewUserNote(&'a ManageUserUser, &'a User, String), } impl From> for ActivityBuilder { @@ -36,6 +39,9 @@ impl From> for ActivityBuilder { "{changed_by} hat die Daten von {changed_user} aktualisiert: {explanation}" )) .relevant_for_user(changed_user), + Reason::NewUserNote(changed_by, user, explanation) => { + Self::new(&format!("({changed_by}) {explanation}")).relevant_for_user(user) + } } } } diff --git a/src/model/boat.rs b/src/model/boat.rs index 22818f4..051d645 100644 --- a/src/model/boat.rs +++ b/src/model/boat.rs @@ -1,9 +1,8 @@ use std::ops::DerefMut; use chrono::NaiveDateTime; -use itertools::Itertools; -use rocket::FromForm; use rocket::serde::{Deserialize, Serialize}; +use rocket::FromForm; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use crate::model::boathouse::Boathouse; @@ -102,24 +101,10 @@ impl Boat { } pub async fn shipmaster_allowed(&self, db: &SqlitePool, user: &User) -> bool { - if let Some(owner_id) = self.owner { - return owner_id == user.id; - } - - if user.has_role(db, "Rennrudern").await { - let ottensheim = Location::find_by_name(db, "Ottensheim".into()) - .await - .unwrap(); - if self.location_id == ottensheim.id { - return true; - } - } - - if self.amount_seats == 1 { - return true; - } - - user.allowed_to_steer(db).await + let mut tx = db.begin().await.unwrap(); + let ret = self.shipmaster_allowed_tx(&mut tx, user).await; + tx.commit().await.unwrap(); + ret } pub async fn shipmaster_allowed_tx( @@ -127,10 +112,27 @@ impl Boat { db: &mut Transaction<'_, Sqlite>, user: &User, ) -> bool { + if user.has_role_tx(db, "admin").await { + return true; + } + if let Some(owner_id) = self.owner { return owner_id == user.id; } + if user.has_role_tx(db, "Rennrudern").await { + let ottensheim = Location::find_by_name_tx(db, "Ottensheim".into()) + .await + .unwrap(); + if self.location_id == ottensheim.id { + return true; + } + } + + if self.name == "Externes Boot" { + return true; + } + if self.amount_seats == 1 { return true; } @@ -257,58 +259,16 @@ ORDER BY } pub async fn for_user(db: &SqlitePool, user: &User) -> Vec { - if user.has_role(db, "admin").await { - return Self::all(db).await; - } - let mut boats = if user.allowed_to_steer(db).await { - sqlx::query_as!( - Boat, - " -SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible -FROM boat -WHERE (owner is null or owner = ?) AND deleted = 0 -ORDER BY amount_seats DESC - ", - user.id - ) - .fetch_all(db) - .await - .unwrap() //TODO: fixme - } else { - sqlx::query_as!( - Boat, - " -SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible -FROM boat -WHERE (owner = ? OR (owner is null and amount_seats = 1)) AND deleted = 0 -ORDER BY amount_seats DESC - ", - user.id - ) - .fetch_all(db) - .await - .unwrap() //TODO: fixme - }; + let all_boats = Self::all(db).await; + let mut filtered_boats = Vec::new(); - if user.has_role(db, "Rennrudern").await { - let ottensheim = Location::find_by_name(db, "Ottensheim".into()) - .await - .unwrap(); - let boats_in_ottensheim = sqlx::query_as!( - Boat, - "SELECT id, name, amount_seats, location_id, owner, year_built, boatbuilder, default_shipmaster_only_steering, default_destination, skull, external, deleted, convert_handoperated_possible -FROM boat -WHERE (owner is null and location_id = ?) AND deleted = 0 -ORDER BY amount_seats DESC - ",ottensheim.id) - .fetch_all(db) - .await - .unwrap(); //TODO: fixme - boats.extend(boats_in_ottensheim.into_iter()); + for boat in all_boats { + if boat.boat.shipmaster_allowed(db, user).await { + filtered_boats.push(boat); + } } - let boats = boats.into_iter().unique().collect(); - Self::boats_to_details(db, boats).await + filtered_boats } pub async fn all_at_location(db: &SqlitePool, location: String) -> Vec { diff --git a/src/model/location.rs b/src/model/location.rs index 7d174ec..17a84ac 100644 --- a/src/model/location.rs +++ b/src/model/location.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; -use sqlx::{FromRow, SqlitePool}; +use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; +use std::ops::DerefMut; #[derive(FromRow, Debug, Serialize, Deserialize)] pub struct Location { @@ -37,6 +38,20 @@ impl Location { .await .ok() } + pub async fn find_by_name_tx(db: &mut Transaction<'_, Sqlite>, name: String) -> Option { + sqlx::query_as!( + Self, + " + SELECT id, name + FROM location + WHERE name=? + ", + name + ) + .fetch_one(db.deref_mut()) + .await + .ok() + } pub async fn all(db: &SqlitePool) -> Vec { sqlx::query_as!(Self, "SELECT id, name FROM location") diff --git a/src/model/user/basic.rs b/src/model/user/basic.rs index fb9eaad..2e879e7 100644 --- a/src/model/user/basic.rs +++ b/src/model/user/basic.rs @@ -17,7 +17,6 @@ impl User { &self, db: &SqlitePool, updated_by: &ManageUserUser, - user: &User, note: &str, ) -> Result<(), String> { let note = note.trim(); @@ -25,7 +24,7 @@ impl User { ActivityBuilder::from(activity::Reason::UserDataChange( updated_by, self, - format!("Neue Notizen: {note}"), + note.to_string(), )) .save(db) .await; diff --git a/src/tera/admin/user.rs b/src/tera/admin/user.rs index d023935..4a5c04f 100644 --- a/src/tera/admin/user.rs +++ b/src/tera/admin/user.rs @@ -7,11 +7,11 @@ use crate::{ mail::valid_mails, role::Role, user::{ - AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails, - UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser, clubmember::ClubMemberUser, foerdernd::FoerderndUser, member::Member, regular::RegularUser, scheckbuch::ScheckbuchUser, schnupperant::SchnupperantUser, schnupperinterest::SchnupperInterestUser, unterstuetzend::UnterstuetzendUser, + AdminUser, AllowedToEditPaymentStatusUser, ManageUserUser, User, UserWithDetails, + UserWithMembershipPdf, UserWithRolesAndMembershipPdf, VorstandUser, }, }, tera::Config, @@ -19,7 +19,6 @@ use crate::{ use chrono::NaiveDate; use futures::future::join_all; use rocket::{ - FromForm, Request, Route, State, form::Form, fs::TempFile, get, @@ -27,9 +26,9 @@ use rocket::{ post, request::{FlashMessage, FromRequest, Outcome}, response::{Flash, Redirect}, - routes, + routes, FromForm, Request, Route, State, }; -use rocket_dyn_templates::{Template, tera::Context}; +use rocket_dyn_templates::{tera::Context, Template}; use sqlx::SqlitePool; // Custom request guard to extract the Referer header @@ -350,7 +349,7 @@ async fn add_note( ); }; - match user.add_note(db, &admin, &user, &data.note).await { + match user.add_note(db, &admin, &data.note).await { Ok(_) => Flash::success( Redirect::to(format!("/admin/user/{}", user.id)), "Notiz hinzugefügt",