diff --git a/migration.sql b/migration.sql index cdc2a37..4504f92 100644 --- a/migration.sql +++ b/migration.sql @@ -16,7 +16,8 @@ CREATE TABLE IF NOT EXISTS "user" ( "notes" text, "phone" text, "address" text, - "family_id" INTEGER REFERENCES family(id) + "family_id" INTEGER REFERENCES family(id), + "membership_pdf" BLOB ); CREATE TABLE IF NOT EXISTS "family" ( diff --git a/src/model/family.rs b/src/model/family.rs index f648cfd..6bff530 100644 --- a/src/model/family.rs +++ b/src/model/family.rs @@ -75,7 +75,7 @@ GROUP BY family.id;" } pub async fn members(&self, db: &SqlitePool) -> Vec { - sqlx::query_as!(User, "SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id FROM user WHERE family_id = ?", self.id) + sqlx::query_as!(User, "SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE family_id = ?", self.id) .fetch_all(db) .await .unwrap() diff --git a/src/model/rower.rs b/src/model/rower.rs index 796a2ad..a953738 100644 --- a/src/model/rower.rs +++ b/src/model/rower.rs @@ -16,7 +16,7 @@ impl Rower { sqlx::query_as!( User, " -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?) ", diff --git a/src/model/user.rs b/src/model/user.rs index ca6b710..5057f23 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -8,6 +8,7 @@ use rocket::{ http::{Cookie, Status}, request::{self, FromRequest, Outcome}, time::{Duration, OffsetDateTime}, + tokio::io::AsyncReadExt, Request, }; use serde::{Deserialize, Serialize}; @@ -43,6 +44,7 @@ pub struct User { pub phone: Option, pub address: Option, pub family_id: Option, + pub membership_pdf: Option>, } #[derive(Debug, Serialize, Deserialize)] @@ -285,7 +287,7 @@ impl User { sqlx::query_as!( Self, " -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE id like ? ", @@ -300,7 +302,7 @@ WHERE id like ? sqlx::query_as!( Self, " -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE id like ? ", @@ -315,7 +317,7 @@ WHERE id like ? sqlx::query_as!( Self, " -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE name like ? ", @@ -357,7 +359,7 @@ WHERE name like ? sqlx::query_as!( Self, " -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE deleted = 0 ORDER BY last_access DESC @@ -372,7 +374,7 @@ ORDER BY last_access DESC sqlx::query_as!( Self, " -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user u JOIN user_role ur ON u.id = ur.user_id WHERE ur.role_id = ? AND deleted = 0 @@ -388,14 +390,14 @@ ORDER BY name; sqlx::query_as!( Self, " -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id FROM user +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE family_id IS NOT NULL GROUP BY family_id UNION -- Select users with a null family_id, without grouping -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id FROM user +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE family_id IS NULL; " ) @@ -408,7 +410,7 @@ WHERE family_id IS NULL; sqlx::query_as!( Self, " -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE deleted = 0 AND dob != '' and weight != '' and sex != '' ORDER BY name @@ -423,7 +425,7 @@ ORDER BY name sqlx::query_as!( Self, " -SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id +SELECT id, name, pw, deleted, last_access, dob, weight, sex, member_since_date, birthdate, mail, nickname, notes, phone, address, family_id, membership_pdf FROM user WHERE deleted = 0 AND (SELECT COUNT(*) FROM user_role WHERE user_id=user.id AND role_id = (SELECT id FROM role WHERE name = 'cox')) > 0 ORDER BY last_access DESC @@ -441,13 +443,29 @@ ORDER BY last_access DESC .is_ok() } - pub async fn update(&self, db: &SqlitePool, data: UserEditForm) { + pub async fn update(&self, db: &SqlitePool, data: UserEditForm<'_>) { let mut family_id = data.family_id; if family_id.is_some_and(|x| x == -1) { family_id = Some(Family::insert(db).await) } + if self.membership_pdf.is_none() { + if let Some(membership_pdf) = data.membership_pdf { + let mut stream = membership_pdf.open().await.unwrap(); + let mut buffer = Vec::new(); + stream.read_to_end(&mut buffer).await.unwrap(); + sqlx::query!( + "UPDATE user SET membership_pdf = ? where id = ?", + buffer, + self.id + ) + .execute(db) + .await + .unwrap(); //Okay, because we can only create a User of a valid id + } + } + sqlx::query!( "UPDATE user SET dob = ?, weight = ?, sex = ?, member_since_date=?, birthdate=?, mail=?, nickname=?, notes=?, phone=?, address=?, family_id = ? where id = ?", data.dob, @@ -1046,6 +1064,7 @@ mod test { phone: None, address: None, family_id: None, + membership_pdf: None, }, ) .await; diff --git a/src/tera/admin/user.rs b/src/tera/admin/user.rs index 7a55d85..aca09e9 100644 --- a/src/tera/admin/user.rs +++ b/src/tera/admin/user.rs @@ -10,8 +10,9 @@ use crate::model::{ use futures::future::join_all; use rocket::{ form::Form, + fs::TempFile, get, - http::Status, + http::{ContentType, Status}, post, request::{FlashMessage, FromRequest, Outcome}, response::{Flash, Redirect}, @@ -231,7 +232,7 @@ async fn delete(db: &State, admin: AdminUser, user: i32) -> Flash { pub(crate) id: i32, pub(crate) dob: Option, pub(crate) weight: Option, @@ -245,12 +246,13 @@ pub struct UserEditForm { pub(crate) phone: Option, pub(crate) address: Option, pub(crate) family_id: Option, + pub(crate) membership_pdf: Option>, } -#[post("/user", data = "")] +#[post("/user", data = "", format = "multipart/form-data")] async fn update( db: &State, - data: Form, + data: Form>, admin: AdminUser, ) -> Flash { let user = User::find_by_id(db, data.id).await; @@ -271,6 +273,25 @@ async fn update( Flash::success(Redirect::to("/admin/user"), "Successfully updated user") } +#[get("/user//membership")] +async fn download_membership_pdf( + db: &State, + admin: AdminUser, + user: i32, +) -> (ContentType, Vec) { + let user = User::find_by_id(db, user).await.unwrap(); + Log::create( + db, + format!( + "{} downloaded membership application for user: {user:?}", + admin.user.name + ), + ) + .await; + + (ContentType::PDF, user.membership_pdf.unwrap()) +} + #[derive(FromForm, Debug)] struct UserAddForm<'r> { name: &'r str, @@ -307,6 +328,7 @@ pub fn routes() -> Vec { delete, fees, fees_paid, - scheckbuch + scheckbuch, + download_membership_pdf ] } diff --git a/templates/admin/user/index.html.tera b/templates/admin/user/index.html.tera index 068ce64..985ce62 100644 --- a/templates/admin/user/index.html.tera +++ b/templates/admin/user/index.html.tera @@ -33,7 +33,7 @@ name="name" id="filter-js" class="search-bar" - placeholder="Suchen nach (Name, [yes|no]-role:)" /> + placeholder="Suchen nach (Name, [yes|no]-role:, has-[no-]membership-pdf)" />
@@ -41,9 +41,10 @@ class="text-primary-950 dark:text-white text-right">
{% for user in users %}
+ data-filter="{{ user.name }} {% for role in roles %} {% if role.name in user.roles %} yes-role:{{ role.name }} {% else %} no-role:{{ role.name }} {% endif %} role-{{ role }} {% endfor %} {% if user.membership_pdf %}has-membership-pdf{% else %}has-no-membership-pdf{% endif %} ">
@@ -62,6 +63,12 @@ {% for role in roles %} {{ macros::checkbox(label=role.name, name="roles[" ~ role.id ~ "]", id=loop.index , checked=role.name in user.roles, disabled=allowed_to_edit == false) }} {% endfor %} + {% if user.membership_pdf %} + Beitrittserklärung herunterladen + {% else %} + {{ macros::input(label='Beitrittserklärung', name='membership_pdf', id=loop.index, type="file", readonly=allowed_to_edit == false, accept='application/pdf') }} + {% endif %} {{ macros::input(label='DOB', name='dob', id=loop.index, type="text", value=user.dob, readonly=allowed_to_edit == false) }} {{ macros::input(label='Weight (kg)', name='weight', id=loop.index, type="text", value=user.weight, readonly=allowed_to_edit == false) }} {{ macros::input(label='Sex', name='sex', id=loop.index, type="text", value=user.sex, readonly=allowed_to_edit == false) }} diff --git a/templates/includes/macros.html.tera b/templates/includes/macros.html.tera index b958f4f..497b872 100644 --- a/templates/includes/macros.html.tera +++ b/templates/includes/macros.html.tera @@ -115,7 +115,7 @@
{% endmacro header %} - {% macro input(label, name, type, required=false, class='rounded-md', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='', pattern='', readonly=false) %} + {% macro input(label, name, type, required=false, class='rounded-md', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='', pattern='', readonly=false, accept='') %}