more-activities #1035
@@ -17,10 +17,31 @@ pub struct Activity {
 | 
			
		||||
    pub keep_until: Option<NaiveDateTime>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
pub struct ActivityWithDetails {
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    pub(crate) activity: Activity,
 | 
			
		||||
    keep_until_days: Option<i64>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Activity> for ActivityWithDetails {
 | 
			
		||||
    fn from(activity: Activity) -> Self {
 | 
			
		||||
        let keep_until_days = activity.keep_until.map(|keep_until| {
 | 
			
		||||
            let now = Utc::now().naive_utc();
 | 
			
		||||
            let duration = keep_until.signed_duration_since(now);
 | 
			
		||||
            duration.num_days()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            keep_until_days,
 | 
			
		||||
            activity,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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),
 | 
			
		||||
    Auth(ReasonAuth<'a>),
 | 
			
		||||
    // `User` changed the data of `User`, explanation in `String`
 | 
			
		||||
    UserDataChange(&'a ManageUserUser, &'a User, String),
 | 
			
		||||
    // New Note for User
 | 
			
		||||
@@ -30,11 +51,7 @@ pub enum Reason<'a> {
 | 
			
		||||
impl From<Reason<'_>> for ActivityBuilder {
 | 
			
		||||
    fn from(value: Reason<'_>) -> Self {
 | 
			
		||||
        match value {
 | 
			
		||||
            Reason::SuccLogin(user, agent) => {
 | 
			
		||||
                Self::new(&format!("{user} hat sich eingeloggt (User-Agent: {agent})"))
 | 
			
		||||
                    .relevant_for_user(user)
 | 
			
		||||
                    .keep_until_days(7)
 | 
			
		||||
            }
 | 
			
		||||
            Reason::Auth(auth) => auth.into(),
 | 
			
		||||
            Reason::UserDataChange(changed_by, changed_user, explanation) => Self::new(&format!(
 | 
			
		||||
                "{changed_by} hat die Daten von {changed_user} aktualisiert: {explanation}"
 | 
			
		||||
            ))
 | 
			
		||||
@@ -46,6 +63,43 @@ impl From<Reason<'_>> for ActivityBuilder {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum ReasonAuth<'a> {
 | 
			
		||||
    // `User` tried to login with `String` as UserAgent
 | 
			
		||||
    SuccLogin(&'a User, String),
 | 
			
		||||
    // `User` tried to login which was already deleted
 | 
			
		||||
    DeletedUserLogin(&'a User),
 | 
			
		||||
    // `User` tried to login, supplied wrong PW
 | 
			
		||||
    WrongPw(&'a User),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> From<ReasonAuth<'a>> for Reason<'a> {
 | 
			
		||||
    fn from(auth_reason: ReasonAuth<'a>) -> Self {
 | 
			
		||||
        Reason::Auth(auth_reason)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<ReasonAuth<'_>> for ActivityBuilder {
 | 
			
		||||
    fn from(value: ReasonAuth<'_>) -> Self {
 | 
			
		||||
        match value {
 | 
			
		||||
            ReasonAuth::SuccLogin(user, agent) => {
 | 
			
		||||
                Self::new(&format!("{user} hat sich eingeloggt (User-Agent: {agent})"))
 | 
			
		||||
                    .relevant_for_user(user)
 | 
			
		||||
                    .keep_until_days(7)
 | 
			
		||||
            }
 | 
			
		||||
            ReasonAuth::DeletedUserLogin(user) => Self::new(&format!(
 | 
			
		||||
                "User {user} wollte sich einloggen, klappte jedoch nicht weil er gelöscht wurde."
 | 
			
		||||
            ))
 | 
			
		||||
            .relevant_for_user(user)
 | 
			
		||||
            .keep_until_days(30),
 | 
			
		||||
            ReasonAuth::WrongPw(user) => Self::new(&format!(
 | 
			
		||||
                "User {user} wollte sich einloggen, hat jedoch das falsche Passwort angegeben."
 | 
			
		||||
            ))
 | 
			
		||||
            .relevant_for_user(user)
 | 
			
		||||
            .keep_until_days(7),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct ActivityBuilder {
 | 
			
		||||
    text: String,
 | 
			
		||||
    relevant_for: String,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
use std::ops::DerefMut;
 | 
			
		||||
 | 
			
		||||
use chrono::NaiveDateTime;
 | 
			
		||||
use rocket::serde::{Deserialize, Serialize};
 | 
			
		||||
use rocket::FromForm;
 | 
			
		||||
use rocket::serde::{Deserialize, Serialize};
 | 
			
		||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
 | 
			
		||||
 | 
			
		||||
use crate::model::boathouse::Boathouse;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
use std::ops::DerefMut;
 | 
			
		||||
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use sqlx::{sqlite::SqliteQueryResult, FromRow, Sqlite, SqlitePool, Transaction};
 | 
			
		||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction, sqlite::SqliteQueryResult};
 | 
			
		||||
 | 
			
		||||
use super::user::User;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
use std::{error::Error, fs};
 | 
			
		||||
 | 
			
		||||
use lettre::{
 | 
			
		||||
    message::{header::ContentType, Attachment, MultiPart, SinglePart},
 | 
			
		||||
    transport::smtp::authentication::Credentials,
 | 
			
		||||
    Address, Message, SmtpTransport, Transport,
 | 
			
		||||
    message::{Attachment, MultiPart, SinglePart, header::ContentType},
 | 
			
		||||
    transport::smtp::authentication::Credentials,
 | 
			
		||||
};
 | 
			
		||||
use sqlx::{Sqlite, SqlitePool, Transaction};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
use super::User;
 | 
			
		||||
use crate::{
 | 
			
		||||
    model::family::Family, BOAT_STORAGE, DUAL_MEMBERSHIP, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE,
 | 
			
		||||
    FAMILY_TWO, FOERDERND, REGULAR, RENNRUDERBEITRAG, STUDENT_OR_PUPIL, TRIAL_ROWING,
 | 
			
		||||
    TRIAL_ROWING_REDUCED, UNTERSTUETZEND,
 | 
			
		||||
    BOAT_STORAGE, DUAL_MEMBERSHIP, EINSCHREIBGEBUEHR, FAMILY_THREE_OR_MORE, FAMILY_TWO, FOERDERND,
 | 
			
		||||
    REGULAR, RENNRUDERBEITRAG, STUDENT_OR_PUPIL, TRIAL_ROWING, TRIAL_ROWING_REDUCED,
 | 
			
		||||
    UNTERSTUETZEND, model::family::Family,
 | 
			
		||||
};
 | 
			
		||||
use chrono::{Datelike, Local, NaiveDate};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,21 @@
 | 
			
		||||
use std::{fmt::Display, ops::DerefMut};
 | 
			
		||||
 | 
			
		||||
use argon2::{password_hash::SaltString, Argon2, PasswordHasher};
 | 
			
		||||
use argon2::{Argon2, PasswordHasher, password_hash::SaltString};
 | 
			
		||||
use chrono::{Datelike, Local, NaiveDate};
 | 
			
		||||
use log::info;
 | 
			
		||||
use rocket::async_trait;
 | 
			
		||||
use rocket::{
 | 
			
		||||
    Request,
 | 
			
		||||
    http::{Cookie, Status},
 | 
			
		||||
    request::{FromRequest, Outcome},
 | 
			
		||||
    time::{Duration, OffsetDateTime},
 | 
			
		||||
    Request,
 | 
			
		||||
};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
 | 
			
		||||
 | 
			
		||||
use super::activity::ActivityBuilder;
 | 
			
		||||
use super::activity::{ActivityBuilder, ReasonAuth};
 | 
			
		||||
use super::{
 | 
			
		||||
    Day,
 | 
			
		||||
    log::Log,
 | 
			
		||||
    logbook::Logbook,
 | 
			
		||||
    mail::Mail,
 | 
			
		||||
@@ -23,7 +24,6 @@ use super::{
 | 
			
		||||
    role::Role,
 | 
			
		||||
    stat::Stat,
 | 
			
		||||
    tripdetails::TripDetails,
 | 
			
		||||
    Day,
 | 
			
		||||
};
 | 
			
		||||
use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD;
 | 
			
		||||
use scheckbuch::ScheckbuchUser;
 | 
			
		||||
@@ -465,53 +465,29 @@ ASKÖ Ruderverein Donau Linz", self.name),
 | 
			
		||||
    pub async fn login(db: &SqlitePool, name: &str, pw: &str) -> Result<Self, LoginError> {
 | 
			
		||||
        let name = name.trim().to_lowercase(); // just to make sure...
 | 
			
		||||
        let Some(user) = User::find_by_name(db, &name).await else {
 | 
			
		||||
            if ![
 | 
			
		||||
                "n-sageder",
 | 
			
		||||
                "p-hofer",
 | 
			
		||||
                "marie-birner",
 | 
			
		||||
                "daniel-kortschak",
 | 
			
		||||
                "rudernlinz",
 | 
			
		||||
                "m-birner",
 | 
			
		||||
                "s-sollberger",
 | 
			
		||||
                "d-kortschak",
 | 
			
		||||
                "wwwadmin",
 | 
			
		||||
                "wadminw",
 | 
			
		||||
                "admin",
 | 
			
		||||
                "m sageder",
 | 
			
		||||
                "d kortschak",
 | 
			
		||||
                "a almousa",
 | 
			
		||||
                "p hofer",
 | 
			
		||||
                "s sollberger",
 | 
			
		||||
                "n sageder",
 | 
			
		||||
                "wp-system",
 | 
			
		||||
                "s.sollberger",
 | 
			
		||||
                "m.birner",
 | 
			
		||||
                "m-sageder",
 | 
			
		||||
                "a-almousa",
 | 
			
		||||
                "m.sageder",
 | 
			
		||||
                "n.sageder",
 | 
			
		||||
                "a.almousa",
 | 
			
		||||
                "p.hofer",
 | 
			
		||||
                "philipp-hofer",
 | 
			
		||||
                "d.kortschak",
 | 
			
		||||
                "[login]",
 | 
			
		||||
            ]
 | 
			
		||||
            .contains(&name.as_str())
 | 
			
		||||
            {
 | 
			
		||||
                Log::create(db, format!("Username ({name}) not found (tried to login)")).await;
 | 
			
		||||
            }
 | 
			
		||||
            Log::create(db, format!("Username ({name}) not found (tried to login)")).await;
 | 
			
		||||
            return Err(LoginError::InvalidAuthenticationCombo); // Username not found
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if user.deleted {
 | 
			
		||||
            ActivityBuilder::new(&format!(
 | 
			
		||||
            if let Some(board) = Role::find_by_name(db, "Vorstand").await {
 | 
			
		||||
                Notification::create_for_role(
 | 
			
		||||
                    db,
 | 
			
		||||
                    &board,
 | 
			
		||||
                    &format!(
 | 
			
		||||
                "User {user} wollte sich einloggen, klappte jedoch nicht weil er gelöscht wurde."
 | 
			
		||||
            ))
 | 
			
		||||
            .relevant_for_user(&user)
 | 
			
		||||
            .save(db)
 | 
			
		||||
            .await;
 | 
			
		||||
            ),
 | 
			
		||||
                    "Fehlgeschlagener Login",
 | 
			
		||||
                    None,
 | 
			
		||||
                    None,
 | 
			
		||||
                )
 | 
			
		||||
                .await;
 | 
			
		||||
            }
 | 
			
		||||
            ActivityBuilder::from(ReasonAuth::DeletedUserLogin(&user))
 | 
			
		||||
                .save(db)
 | 
			
		||||
                .await;
 | 
			
		||||
            return Err(LoginError::InvalidAuthenticationCombo); //User existed sometime ago; has
 | 
			
		||||
                                                                //been deleted
 | 
			
		||||
            //been deleted
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(user_pw) = user.pw.as_ref() {
 | 
			
		||||
@@ -519,12 +495,9 @@ ASKÖ Ruderverein Donau Linz", self.name),
 | 
			
		||||
            if password_hash == user_pw {
 | 
			
		||||
                return Ok(user);
 | 
			
		||||
            }
 | 
			
		||||
            ActivityBuilder::new(&format!(
 | 
			
		||||
                "User {user} wollte sich einloggen, hat jedoch das falsche Passwort angegeben."
 | 
			
		||||
            ))
 | 
			
		||||
            .relevant_for_user(&user)
 | 
			
		||||
            .save(db)
 | 
			
		||||
            .await;
 | 
			
		||||
            ActivityBuilder::from(ReasonAuth::WrongPw(&user))
 | 
			
		||||
                .save(db)
 | 
			
		||||
                .await;
 | 
			
		||||
            Err(LoginError::InvalidAuthenticationCombo)
 | 
			
		||||
        } else {
 | 
			
		||||
            info!("User {name} has no PW set");
 | 
			
		||||
@@ -617,9 +590,9 @@ ASKÖ Ruderverein Donau Linz", self.name),
 | 
			
		||||
    pub(crate) async fn amount_days_to_show(&self, db: &SqlitePool) -> i64 {
 | 
			
		||||
        if self.allowed_to_steer(db).await {
 | 
			
		||||
            let end_of_year = NaiveDate::from_ymd_opt(Local::now().year(), 12, 31).unwrap(); //Ok,
 | 
			
		||||
                                                                                             //december
 | 
			
		||||
                                                                                             //has 31
 | 
			
		||||
                                                                                             //days
 | 
			
		||||
            //december
 | 
			
		||||
            //has 31
 | 
			
		||||
            //days
 | 
			
		||||
            let days_left_in_year = end_of_year
 | 
			
		||||
                .signed_duration_since(Local::now().date_naive())
 | 
			
		||||
                .num_days()
 | 
			
		||||
@@ -628,9 +601,9 @@ ASKÖ Ruderverein Donau Linz", self.name),
 | 
			
		||||
            if days_left_in_year <= 31 {
 | 
			
		||||
                let end_of_next_year =
 | 
			
		||||
                    NaiveDate::from_ymd_opt(Local::now().year() + 1, 12, 31).unwrap(); //Ok,
 | 
			
		||||
                                                                                       //december
 | 
			
		||||
                                                                                       //has 31
 | 
			
		||||
                                                                                       //days
 | 
			
		||||
                //december
 | 
			
		||||
                //has 31
 | 
			
		||||
                //days
 | 
			
		||||
                end_of_next_year
 | 
			
		||||
                    .signed_duration_since(Local::now().date_naive())
 | 
			
		||||
                    .num_days()
 | 
			
		||||
@@ -862,8 +835,8 @@ special_user!(SteeringUser, +"cox", +"Bootsführer");
 | 
			
		||||
special_user!(AdminUser, +"admin");
 | 
			
		||||
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch", +"Förderndes Mitglied");
 | 
			
		||||
special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied"); // TODO:
 | 
			
		||||
                                                                                       // remove ->
 | 
			
		||||
                                                                                       // RegularUser
 | 
			
		||||
// remove ->
 | 
			
		||||
// RegularUser
 | 
			
		||||
special_user!(SchnupperBetreuerUser, +"schnupper-betreuer");
 | 
			
		||||
special_user!(VorstandUser, +"admin", +"Vorstand");
 | 
			
		||||
special_user!(EventUser, +"manage_events");
 | 
			
		||||
@@ -977,17 +950,21 @@ mod test {
 | 
			
		||||
    #[sqlx::test]
 | 
			
		||||
    fn wrong_pw() {
 | 
			
		||||
        let pool = testdb!();
 | 
			
		||||
        assert!(User::login(&pool, "admin".into(), "admi".into())
 | 
			
		||||
            .await
 | 
			
		||||
            .is_err());
 | 
			
		||||
        assert!(
 | 
			
		||||
            User::login(&pool, "admin".into(), "admi".into())
 | 
			
		||||
                .await
 | 
			
		||||
                .is_err()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[sqlx::test]
 | 
			
		||||
    fn wrong_username() {
 | 
			
		||||
        let pool = testdb!();
 | 
			
		||||
        assert!(User::login(&pool, "admi".into(), "admin".into())
 | 
			
		||||
            .await
 | 
			
		||||
            .is_err());
 | 
			
		||||
        assert!(
 | 
			
		||||
            User::login(&pool, "admi".into(), "admin".into())
 | 
			
		||||
                .await
 | 
			
		||||
                .is_err()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[sqlx::test]
 | 
			
		||||
@@ -1007,9 +984,11 @@ mod test {
 | 
			
		||||
        let pool = testdb!();
 | 
			
		||||
        let user = User::find_by_id(&pool, 1).await.unwrap();
 | 
			
		||||
 | 
			
		||||
        assert!(User::login(&pool, "admin".into(), "abc".into())
 | 
			
		||||
            .await
 | 
			
		||||
            .is_err());
 | 
			
		||||
        assert!(
 | 
			
		||||
            User::login(&pool, "admin".into(), "abc".into())
 | 
			
		||||
                .await
 | 
			
		||||
                .is_err()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        user.update_pw(&pool, "abc".into()).await;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
use super::{ManageUserUser, User};
 | 
			
		||||
use crate::{
 | 
			
		||||
    NonEmptyString,
 | 
			
		||||
    model::{activity::ActivityBuilder, mail::Mail, notification::Notification, role::Role},
 | 
			
		||||
    special_user, NonEmptyString,
 | 
			
		||||
    special_user,
 | 
			
		||||
};
 | 
			
		||||
use chrono::NaiveDate;
 | 
			
		||||
use rocket::{async_trait, fs::TempFile, tokio::io::AsyncReadExt};
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,13 @@ use super::foerdernd::FoerderndUser;
 | 
			
		||||
use super::regular::RegularUser;
 | 
			
		||||
use super::unterstuetzend::UnterstuetzendUser;
 | 
			
		||||
use super::{ManageUserUser, User};
 | 
			
		||||
use crate::NonEmptyString;
 | 
			
		||||
use crate::model::activity::ActivityBuilder;
 | 
			
		||||
use crate::model::role::Role;
 | 
			
		||||
use crate::NonEmptyString;
 | 
			
		||||
use crate::{
 | 
			
		||||
    SCHECKBUCH,
 | 
			
		||||
    model::{mail::Mail, notification::Notification},
 | 
			
		||||
    special_user, SCHECKBUCH,
 | 
			
		||||
    special_user,
 | 
			
		||||
};
 | 
			
		||||
use chrono::NaiveDate;
 | 
			
		||||
use rocket::async_trait;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,9 @@ use super::scheckbuch::ScheckbuchUser;
 | 
			
		||||
use super::schnupperinterest::SchnupperInterestUser;
 | 
			
		||||
use super::unterstuetzend::UnterstuetzendUser;
 | 
			
		||||
use super::{ManageUserUser, User};
 | 
			
		||||
use crate::NonEmptyString;
 | 
			
		||||
use crate::model::activity::ActivityBuilder;
 | 
			
		||||
use crate::model::role::Role;
 | 
			
		||||
use crate::NonEmptyString;
 | 
			
		||||
use crate::{
 | 
			
		||||
    model::{mail::Mail, notification::Notification},
 | 
			
		||||
    special_user,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
use csv::ReaderBuilder;
 | 
			
		||||
use rocket::{form::Form, get, post, routes, FromForm, Route, State};
 | 
			
		||||
use rocket_dyn_templates::{context, Template};
 | 
			
		||||
use rocket::{FromForm, Route, State, form::Form, get, post, routes};
 | 
			
		||||
use rocket_dyn_templates::{Template, context};
 | 
			
		||||
use sqlx::SqlitePool;
 | 
			
		||||
 | 
			
		||||
use crate::model::{activity::Activity, role::Role, user::AdminUser};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,17 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    model::{
 | 
			
		||||
        activity::Activity,
 | 
			
		||||
        activity::{Activity, ActivityWithDetails},
 | 
			
		||||
        family::Family,
 | 
			
		||||
        log::Log,
 | 
			
		||||
        logbook::Logbook,
 | 
			
		||||
        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,6 +19,7 @@ use crate::{
 | 
			
		||||
use chrono::NaiveDate;
 | 
			
		||||
use futures::future::join_all;
 | 
			
		||||
use rocket::{
 | 
			
		||||
    FromForm, Request, Route, State,
 | 
			
		||||
    form::Form,
 | 
			
		||||
    fs::TempFile,
 | 
			
		||||
    get,
 | 
			
		||||
@@ -26,9 +27,9 @@ use rocket::{
 | 
			
		||||
    post,
 | 
			
		||||
    request::{FlashMessage, FromRequest, Outcome},
 | 
			
		||||
    response::{Flash, Redirect},
 | 
			
		||||
    routes, FromForm, Request, Route, State,
 | 
			
		||||
    routes,
 | 
			
		||||
};
 | 
			
		||||
use rocket_dyn_templates::{tera::Context, Template};
 | 
			
		||||
use rocket_dyn_templates::{Template, tera::Context};
 | 
			
		||||
use sqlx::SqlitePool;
 | 
			
		||||
 | 
			
		||||
// Custom request guard to extract the Referer header
 | 
			
		||||
@@ -141,7 +142,11 @@ async fn view(
 | 
			
		||||
 | 
			
		||||
    let member = Member::from(db, user.clone()).await;
 | 
			
		||||
    let fee = user.fee(db).await;
 | 
			
		||||
    let activities = Activity::for_user(db, &user).await;
 | 
			
		||||
    let activities: Vec<ActivityWithDetails> = Activity::for_user(db, &user)
 | 
			
		||||
        .await
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .map(Into::into)
 | 
			
		||||
        .collect();
 | 
			
		||||
    let financial = Role::all_cluster(db, "financial").await;
 | 
			
		||||
    let user_financial = user.financial(db).await;
 | 
			
		||||
    let skill = Role::all_cluster(db, "skill").await;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
use rocket::{
 | 
			
		||||
    FromForm, Request, Route, State,
 | 
			
		||||
    form::Form,
 | 
			
		||||
    get,
 | 
			
		||||
    http::{Cookie, CookieJar},
 | 
			
		||||
@@ -8,13 +9,12 @@ use rocket::{
 | 
			
		||||
    response::{Flash, Redirect},
 | 
			
		||||
    routes,
 | 
			
		||||
    time::{Duration, OffsetDateTime},
 | 
			
		||||
    FromForm, Request, Route, State,
 | 
			
		||||
};
 | 
			
		||||
use rocket_dyn_templates::{context, tera, Template};
 | 
			
		||||
use rocket_dyn_templates::{Template, context, tera};
 | 
			
		||||
use sqlx::SqlitePool;
 | 
			
		||||
 | 
			
		||||
use crate::model::{
 | 
			
		||||
    activity::{self, ActivityBuilder},
 | 
			
		||||
    activity::{self, ActivityBuilder, ReasonAuth},
 | 
			
		||||
    log::Log,
 | 
			
		||||
    user::{LoginError, User},
 | 
			
		||||
};
 | 
			
		||||
@@ -83,7 +83,7 @@ async fn login(
 | 
			
		||||
 | 
			
		||||
    cookies.add_private(Cookie::new("loggedin_user", format!("{}", user.id)));
 | 
			
		||||
 | 
			
		||||
    ActivityBuilder::from(activity::Reason::SuccLogin(&user, agent.0))
 | 
			
		||||
    ActivityBuilder::from(ReasonAuth::SuccLogin(&user, agent.0))
 | 
			
		||||
        .save(db)
 | 
			
		||||
        .await;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,3 @@ INSERT INTO user(name) VALUES('Marie');
 | 
			
		||||
INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Marie'),(SELECT id FROM role where name = 'Donau Linz'));
 | 
			
		||||
INSERT INTO user(name) VALUES('Philipp');
 | 
			
		||||
INSERT INTO "user_role" (user_id, role_id) VALUES((SELECT id from user where name = 'Philipp'),(SELECT id FROM role where name = 'Donau Linz'));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
insert into role(name, cluster, formatted_name) values('dual_membership', 'financial', 'Doppelmitgliedschaft mit anderem österr. Ruderverein');
 | 
			
		||||
insert into role(name, hide_in_lists) values('participated_schnupperkurs', true);
 | 
			
		||||
 
 | 
			
		||||
@@ -411,7 +411,9 @@
 | 
			
		||||
                        <ul class="list-disc ms-4">
 | 
			
		||||
                            {% for activity in activities %}
 | 
			
		||||
                                <li>
 | 
			
		||||
                                    <strong>{{ activity.created_at | date(format="%d. %m. %Y") }}:</strong> <small>{{ activity.text }}</small>
 | 
			
		||||
                                    <strong>{{ activity.created_at | date(format="%d. %m. %Y") }}:</strong> <small>{{ activity.text }}
 | 
			
		||||
                                        {% if activity.keep_until_days %}(⏳ {{ activity.keep_until_days }} Tage){% endif %}
 | 
			
		||||
                                    </small>
 | 
			
		||||
                                </li>
 | 
			
		||||
                            {% else %}
 | 
			
		||||
                                <li>Noch keine Aktivität... Stay tuned 😆</li>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user