be able to update data individually; Fixes #952
This commit is contained in:
parent
c8d5c633d7
commit
d2914f9287
@ -7,7 +7,7 @@ use super::user::User;
|
||||
|
||||
#[derive(FromRow, Serialize, Clone)]
|
||||
pub struct Family {
|
||||
id: i64,
|
||||
pub(crate) id: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
@ -91,4 +91,18 @@ GROUP BY family.id;"
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn clean_families_without_members(db: &SqlitePool) {
|
||||
sqlx::query(
|
||||
"DELETE FROM family
|
||||
WHERE id NOT IN (
|
||||
SELECT DISTINCT family_id
|
||||
FROM user
|
||||
WHERE family_id IS NOT NULL
|
||||
);",
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ pub struct TripUpdate<'a> {
|
||||
pub is_locked: bool,
|
||||
}
|
||||
|
||||
impl<'a> TripUpdate<'a> {
|
||||
impl TripUpdate<'_> {
|
||||
fn cancelled(&self) -> bool {
|
||||
self.max_people == -1
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
// TODO: put back in `src/model/user/mod.rs` once that is cleaned up
|
||||
|
||||
use super::{AllowedToEditPaymentStatusUser, ManageUserUser, User};
|
||||
use crate::model::{log::Log, mail::valid_mails, role::Role};
|
||||
use crate::model::{family::Family, log::Log, mail::valid_mails, role::Role};
|
||||
use chrono::NaiveDate;
|
||||
use rocket::{fs::TempFile, tokio::io::AsyncReadExt};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
impl User {
|
||||
@ -60,6 +62,35 @@ impl User {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn update_address(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
updated_by: &ManageUserUser,
|
||||
new_address: &str,
|
||||
) -> Result<(), String> {
|
||||
let new_address = new_address.trim();
|
||||
|
||||
let query = if new_address.is_empty() {
|
||||
sqlx::query!("UPDATE user SET address = NULL where id = ?", self.id)
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"UPDATE user SET address = ? where id = ?",
|
||||
new_address,
|
||||
self.id
|
||||
)
|
||||
};
|
||||
query.execute(db).await.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
let msg = match &self.address {
|
||||
Some(old_address) if new_address.is_empty() => format!("{updated_by} has removed the address of {self} (old address: {old_address})"),
|
||||
Some(old_address) => format!("{updated_by} has changed the address of {self} from {old_address} to {new_address}"),
|
||||
None => format!("{updated_by} has added an address for {self}: {new_address}")
|
||||
};
|
||||
Log::create(db, msg).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn update_nickname(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
@ -89,6 +120,82 @@ impl User {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn update_member_since(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
updated_by: &ManageUserUser,
|
||||
new_member_since_date: &NaiveDate,
|
||||
) {
|
||||
sqlx::query!(
|
||||
"UPDATE user SET member_since_date = ? where id = ?",
|
||||
new_member_since_date,
|
||||
self.id
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
let msg = match &self.member_since_date {
|
||||
Some(old_member_since_date) => format!("{updated_by} has changed the member_since date of {self} from {old_member_since_date} to {new_member_since_date}"),
|
||||
None => format!("{updated_by} has added a member_since_date for {self}: {new_member_since_date}")
|
||||
};
|
||||
Log::create(db, msg).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn update_birthdate(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
updated_by: &ManageUserUser,
|
||||
new_birthdate: &NaiveDate,
|
||||
) {
|
||||
sqlx::query!(
|
||||
"UPDATE user SET birthdate = ? where id = ?",
|
||||
new_birthdate,
|
||||
self.id
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap(); //Okay, because we can only create a User of a valid id
|
||||
|
||||
let msg = match &self.birthdate{
|
||||
Some(old_birthdate) => format!("{updated_by} has changed the birthdate of {self} from {old_birthdate} to {new_birthdate}"),
|
||||
None => format!("{updated_by} has added a birthdate for {self}: {new_birthdate}")
|
||||
};
|
||||
Log::create(db, msg).await;
|
||||
}
|
||||
|
||||
pub(crate) async fn update_family(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
updated_by: &ManageUserUser,
|
||||
family: Option<Family>,
|
||||
) {
|
||||
if let Some(family) = family {
|
||||
let family_id = family.id;
|
||||
sqlx::query!(
|
||||
"UPDATE user SET family_id = ? where id = ?",
|
||||
family_id,
|
||||
self.id
|
||||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap();
|
||||
} else {
|
||||
sqlx::query!("UPDATE user SET family_id = NULL where id = ?", self.id)
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap();
|
||||
};
|
||||
|
||||
Family::clean_families_without_members(db).await;
|
||||
|
||||
Log::create(
|
||||
db,
|
||||
format!("{updated_by} hat die Familie von {self} aktualisiert."),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(crate) async fn remove_role(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
@ -196,4 +303,35 @@ impl User {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn add_membership_pdf(
|
||||
&self,
|
||||
db: &SqlitePool,
|
||||
updated_by: &ManageUserUser,
|
||||
membership_pdf: &TempFile<'_>,
|
||||
) -> Result<(), String> {
|
||||
if self.has_membership_pdf(db).await {
|
||||
return Err(format!("User {self} hat bereits eine Beitrittserklärung."));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Log::create(
|
||||
db,
|
||||
format!("{updated_by} has added the membership pdf for user {self}"),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ impl User {
|
||||
.await?;
|
||||
} else if self.has_role(db, "schnupperant").await {
|
||||
self.send_welcome_mail_schnupper(db, mail, smtp_pw).await?;
|
||||
} else if let Some(scheckbuch) = ScheckbuchUser::new(db, &self).await {
|
||||
} else if let Some(scheckbuch) = ScheckbuchUser::new(db, self).await {
|
||||
scheckbuch.notify(db, mail, smtp_pw).await?;
|
||||
} else {
|
||||
return Err(format!(
|
||||
@ -272,7 +272,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
|
||||
}
|
||||
|
||||
pub async fn allowed_to_update_always_show_trip(&self, db: &SqlitePool) -> bool {
|
||||
AllowedToUpdateTripToAlwaysBeShownUser::new(db, &self)
|
||||
AllowedToUpdateTripToAlwaysBeShownUser::new(db, self)
|
||||
.await
|
||||
.is_some()
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use crate::{
|
||||
},
|
||||
tera::Config,
|
||||
};
|
||||
use chrono::NaiveDate;
|
||||
use futures::future::join_all;
|
||||
use rocket::{
|
||||
form::Form,
|
||||
@ -389,6 +390,110 @@ async fn update_phone(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct AddressUpdateForm {
|
||||
address: String,
|
||||
}
|
||||
|
||||
#[post("/user/<id>/change-address", data = "<data>")]
|
||||
async fn update_address(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<AddressUpdateForm>,
|
||||
admin: ManageUserUser,
|
||||
id: i32,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(user) = User::find_by_id(db, id).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("User with ID {} does not exist!", id),
|
||||
);
|
||||
};
|
||||
|
||||
match user.update_address(db, &admin, &data.address).await {
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||
"Adresse erfolgreich geändert",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", user.id)), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct FamilyUpdateForm {
|
||||
family_id: Option<i64>,
|
||||
}
|
||||
|
||||
#[post("/user/<id>/change-family", data = "<data>")]
|
||||
async fn update_family(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FamilyUpdateForm>,
|
||||
admin: ManageUserUser,
|
||||
id: i32,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(user) = User::find_by_id(db, id).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("User with ID {} does not exist!", id),
|
||||
);
|
||||
};
|
||||
|
||||
let family = match data.family_id {
|
||||
Some(-1) => Some(
|
||||
Family::find_by_id(db, Family::insert(db).await)
|
||||
.await
|
||||
.unwrap(),
|
||||
),
|
||||
Some(id) => match Family::find_by_id(db, id).await {
|
||||
Some(f) => Some(f),
|
||||
None => {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user/{id}"),
|
||||
format!("Family with ID {} does not exist!", id),
|
||||
);
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
user.update_family(db, &admin, family).await;
|
||||
|
||||
Flash::success(
|
||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||
"Familie erfolgreich geändert",
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct AddMembershipPDFForm<'a> {
|
||||
membership_pdf: TempFile<'a>,
|
||||
}
|
||||
|
||||
#[post("/user/<id>/add-membership-pdf", data = "<data>")]
|
||||
async fn add_membership_pdf(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<AddMembershipPDFForm<'_>>,
|
||||
admin: ManageUserUser,
|
||||
id: i32,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(user) = User::find_by_id(db, id).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("User with ID {} does not exist!", id),
|
||||
);
|
||||
};
|
||||
|
||||
match user
|
||||
.add_membership_pdf(db, &admin, &data.membership_pdf)
|
||||
.await
|
||||
{
|
||||
Ok(_) => Flash::success(
|
||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||
"Beitrittserklärung erfolgreich hinzugefügt",
|
||||
),
|
||||
Err(e) => Flash::error(Redirect::to(format!("/admin/user/{}", user.id)), e),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct NicknameUpdateForm {
|
||||
nickname: String,
|
||||
@ -417,6 +522,77 @@ async fn update_nickname(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct MemberSinceUpdateForm {
|
||||
member_since: String,
|
||||
}
|
||||
|
||||
#[post("/user/<id>/change-member-since", data = "<data>")]
|
||||
async fn update_member_since(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<MemberSinceUpdateForm>,
|
||||
admin: ManageUserUser,
|
||||
id: i32,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(user) = User::find_by_id(db, id).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("User with ID {} does not exist!", id),
|
||||
);
|
||||
};
|
||||
let Ok(new_member_since_date) = NaiveDate::parse_from_str(&data.member_since, "%Y-%m-%d")
|
||||
else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user/{id}"),
|
||||
format!(
|
||||
"Datum {} ist nicht im YYYY-MM-DD Format",
|
||||
&data.member_since
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
user.update_member_since(db, &admin, &new_member_since_date)
|
||||
.await;
|
||||
|
||||
Flash::success(
|
||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||
"Beitrittsdatum erfolgreich geändert",
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct BirthdateUpdateForm {
|
||||
birthdate: String,
|
||||
}
|
||||
|
||||
#[post("/user/<id>/change-birthdate", data = "<data>")]
|
||||
async fn update_birthdate(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<BirthdateUpdateForm>,
|
||||
admin: ManageUserUser,
|
||||
id: i32,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(user) = User::find_by_id(db, id).await else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user"),
|
||||
format!("User with ID {} does not exist!", id),
|
||||
);
|
||||
};
|
||||
let Ok(new_birthdate) = NaiveDate::parse_from_str(&data.birthdate, "%Y-%m-%d") else {
|
||||
return Flash::error(
|
||||
Redirect::to("/admin/user/{id}"),
|
||||
format!("Datum {} ist nicht im YYYY-MM-DD Format", &data.birthdate),
|
||||
);
|
||||
};
|
||||
|
||||
user.update_birthdate(db, &admin, &new_birthdate).await;
|
||||
|
||||
Flash::success(
|
||||
Redirect::to(format!("/admin/user/{}", user.id)),
|
||||
"Geburtstag erfolgreich geändert",
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct AddRoleForm {
|
||||
role_id: i32,
|
||||
@ -649,6 +825,11 @@ pub fn routes() -> Vec<Route> {
|
||||
update_mail,
|
||||
update_phone,
|
||||
update_nickname,
|
||||
update_member_since,
|
||||
update_birthdate,
|
||||
update_address,
|
||||
update_family,
|
||||
add_membership_pdf,
|
||||
add_role,
|
||||
remove_role,
|
||||
]
|
||||
|
@ -41,7 +41,8 @@
|
||||
</details>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li>Spitzname: {{ user.nickname }}
|
||||
<li>
|
||||
Spitzname: {{ user.nickname }}
|
||||
{% if allowed_to_edit %}
|
||||
<details>
|
||||
<summary>✏️</summary>
|
||||
@ -76,7 +77,9 @@
|
||||
<fieldset>
|
||||
<select name="role_id">
|
||||
{% for role in roles %}
|
||||
{% if not role.cluster and role not in user.proper_roles %}<option value="{{ role.id }}">{{ role.name }}</option>{% endif %}
|
||||
{% if not role.cluster and role not in user.proper_roles %}
|
||||
<option value="{{ role.id }}">{{ role.name }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input value="Rolle hinzufügen" type="submit" class="btn btn-primary ml-1" />
|
||||
@ -93,11 +96,56 @@
|
||||
<div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600">
|
||||
<div class="py-3">
|
||||
<ul class="list-disc ms-4">
|
||||
<li>Mitglied seit: {{ user.member_since_date }}</li>
|
||||
<li>Geburtsdatum: {{ user.birthdate }}</li>
|
||||
<li>Adresse: {{ user.address }}</li>
|
||||
<li>
|
||||
Familie: {{ macros::select(label="Familie", data=families, name='family_id', selected_id=user.family_id, display=['names'], default="Keine Familie", new_last_entry='Neue Familie anlegen') }}
|
||||
Mitglied seit: {{ user.member_since_date }}
|
||||
{% if allowed_to_edit %}
|
||||
<details>
|
||||
<summary>✏️</summary>
|
||||
<form action="/admin/user/{{ user.id }}/change-member-since" method="post">
|
||||
{{ macros::input(label='Mitglied seit', name='member_since', type="date", value=user.member_since_date) }}
|
||||
<input value="Ändern" type="submit" class="btn btn-primary ml-1" />
|
||||
</form>
|
||||
</details>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li>
|
||||
Geburtsdatum: {{ user.birthdate }}
|
||||
{% if allowed_to_edit %}
|
||||
<details>
|
||||
<summary>✏️</summary>
|
||||
<form action="/admin/user/{{ user.id }}/change-birthdate" method="post">
|
||||
{{ macros::input(label='Geburtstag', name='birthdate', type="date", value=user.birthdate) }}
|
||||
<input value="Ändern" type="submit" class="btn btn-primary ml-1" />
|
||||
</form>
|
||||
</details>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li>
|
||||
Adresse: {{ user.address }}
|
||||
{% if allowed_to_edit %}
|
||||
<details>
|
||||
<summary>✏️</summary>
|
||||
<form action="/admin/user/{{ user.id }}/change-address" method="post">
|
||||
{{ macros::input(label='Neue Adresse', name='address', type="text", value=user.address) }}
|
||||
<input value="Ändern" type="submit" class="btn btn-primary ml-1" />
|
||||
</form>
|
||||
</details>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li>
|
||||
Familie:
|
||||
{% for family in families %}
|
||||
{% if user.family_id == family.id %}{{ family.names }}{% endif %}
|
||||
{% endfor %}
|
||||
{% if allowed_to_edit %}
|
||||
<details>
|
||||
<summary>✏️</summary>
|
||||
<form action="/admin/user/{{ user.id }}/change-family" method="post">
|
||||
{{ macros::select(label="Familie", data=families, name='family_id', selected_id=user.family_id, display=['names'], default="Keine Familie", new_last_entry='Neue Familie anlegen') }}
|
||||
<input value="Ändern" type="submit" class="btn btn-primary ml-1" />
|
||||
</form>
|
||||
</details>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user