Compare commits

...

3 Commits

Author SHA1 Message Date
d9aa7cafe1 Merge pull request 'welcome-mail' (#490) from welcome-mail into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m23s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 5m11s
Reviewed-on: #490
2024-05-15 14:52:46 +02:00
a465dfcce5 reformat tera
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-15 14:42:58 +02:00
6371366a96 send welcome mail to new members
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-15 14:41:18 +02:00
5 changed files with 168 additions and 10 deletions

View File

@ -14,6 +14,58 @@ use super::{family::Family, log::Log, role::Role, user::User};
pub struct Mail {} pub struct Mail {}
impl Mail { impl Mail {
pub async fn send_single(
db: &SqlitePool,
to: &str,
subject: &str,
body: String,
smtp_pw: &str,
) {
let mut email = Message::builder()
.from(
"ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap(),
)
.reply_to(
"ASKÖ Ruderverein Donau Linz <info@rudernlinz.at>"
.parse()
.unwrap(),
)
.to("ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
.parse()
.unwrap());
let splitted = to.split(',');
for single_rec in splitted {
match single_rec.parse() {
Ok(new_bcc_mail) => email = email.bcc(new_bcc_mail),
Err(_) => {
Log::create(
db,
format!("Mail not sent to {single_rec}, because it could not be parsed"),
)
.await;
}
}
}
let email = email
.subject(subject)
.header(ContentType::TEXT_PLAIN)
.body(body)
.unwrap();
let creds = Credentials::new("no-reply@rudernlinz.at".to_owned(), smtp_pw.into());
let mailer = SmtpTransport::relay("mail.your-server.de")
.unwrap()
.credentials(creds)
.build();
// Send the email
mailer.send(&email).unwrap();
}
pub async fn send(db: &SqlitePool, data: MailToSend<'_>, smtp_pw: String) -> bool { pub async fn send(db: &SqlitePool, data: MailToSend<'_>, smtp_pw: String) -> bool {
let mut email = Message::builder() let mut email = Message::builder()
.from( .from(

View File

@ -69,6 +69,18 @@ impl Notification {
} }
} }
pub async fn create_for_role(
db: &SqlitePool,
role: &Role,
message: &str,
category: &str,
link: Option<&str>,
) {
let mut tx = db.begin().await.unwrap();
Self::create_for_role_tx(&mut tx, role, message, category, link).await;
tx.commit().await.unwrap();
}
pub async fn for_user(db: &SqlitePool, user: &User) -> Vec<Self> { pub async fn for_user(db: &SqlitePool, user: &User) -> Vec<Self> {
let rows = sqlx::query!( let rows = sqlx::query!(
" "

View File

@ -14,7 +14,10 @@ use rocket::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use super::{family::Family, log::Log, role::Role, tripdetails::TripDetails, Day}; use super::{
family::Family, log::Log, mail::Mail, notification::Notification, role::Role,
tripdetails::TripDetails, Day,
};
use crate::tera::admin::user::UserEditForm; use crate::tera::admin::user::UserEditForm;
const RENNRUDERBEITRAG: i32 = 11000; const RENNRUDERBEITRAG: i32 = 11000;
@ -137,6 +140,69 @@ impl Fee {
} }
impl User { impl User {
pub async fn send_welcome_email(&self, db: &SqlitePool, smtp_pw: &str) -> Result<(), String> {
if !self.has_role(db, "Donau Linz").await {
return Err(format!(
"Could not send welcome mail, because user {} is not in Donau Linz group",
self.name
));
}
let Some(mail) = &self.mail else {
return Err(format!(
"Could not send welcome mail, because user {} has no email address",
self.name
));
};
Mail::send_single(
db,
mail,
"Willkommen im ASKÖ Ruderverein Donau Linz!",
format!(
"Hallo {0},
herzlich willkommen im ASKÖ Ruderverein Donau Linz! Wir freuen uns sehr, dich als neues Mitglied in unserem Verein begrüßen zu dürfen.
Um dir den Einstieg zu erleichtern, findest du in unserem Handbuch alle wichtigen Informationen über unseren Verein: https://rudernlinz.at/book. Bei weiteren Fragen stehen dir die Adressen info@rudernlinz.at und it@rudernlinz.at jederzeit zur Verfügung.
Du kannst auch gerne unserer Signal-Gruppe beitreten, um auf dem Laufenden zu bleiben und dich mit anderen Mitgliedern auszutauschen: https://signal.group/#CjQKICFrq6zSsRHxrucS3jEcQn6lknEXacAykwwLV3vNLKxPEhA17jxz7cpjfu3JZokLq1TH
Für die Organisation unserer Ausfahrten nutzen wir app.rudernlinz.at. Logge dich einfach mit deinem Namen ('{0}' ohne Anführungszeichen) ein, beim ersten Mal kannst du das Passwortfeld leer lassen. Unter 'Geplante Ausfahrten' kannst du dich jederzeit zu den Ausfahrten anmelden.
Beim nächsten Treffen im Verein, erinnere mich (Philipp Hofer) bitte daran, deinen Fingerabdruck zu registrieren, damit du eigenständig Zugang zum Bootshaus erhältst.
Wir freuen uns darauf, dich bald am Wasser zu sehen und gemeinsam tolle Erfahrungen zu sammeln!
Riemen- & Dollenbruch
ASKÖ Ruderverein Donau Linz
", self.name),
smtp_pw,
).await;
Log::create(
db,
format!("Willkommensemail wurde an {} versandt", self.name),
)
.await;
let coxes = Role::find_by_name(db, "cox").await.unwrap();
Notification::create_for_role(
db,
&coxes,
&format!(
"Liebe Steuerberechtigte, seit {} gibt es ein neues Mitglied: {}",
self.member_since_date.clone().unwrap(),
self.name
),
"Neues Vereinsmitglied",
None,
)
.await;
Ok(())
}
pub async fn fee(&self, db: &SqlitePool) -> Option<Fee> { pub async fn fee(&self, db: &SqlitePool) -> Option<Fee> {
if !self.has_role(db, "Donau Linz").await { if !self.has_role(db, "Donau Linz").await {
return None; return None;

View File

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::model::{ use crate::{
model::{
family::Family, family::Family,
log::Log, log::Log,
logbook::Logbook, logbook::Logbook,
@ -9,6 +10,8 @@ use crate::model::{
AdminUser, User, UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf, AdminUser, User, UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf,
VorstandUser, VorstandUser,
}, },
},
tera::Config,
}; };
use futures::future::join_all; use futures::future::join_all;
use rocket::{ use rocket::{
@ -202,6 +205,26 @@ async fn fees_paid(
) )
} }
#[get("/user/<user>/send-welcome-mail")]
async fn send_welcome_mail(
db: &State<SqlitePool>,
_admin: AdminUser,
config: &State<Config>,
user: i32,
) -> Flash<Redirect> {
let Some(user) = User::find_by_id(db, user).await else {
return Flash::error(Redirect::to("/admin/user"), "User does not exist");
};
match user.send_welcome_email(db, &config.smtp_pw).await {
Ok(()) => Flash::success(
Redirect::to("/admin/user"),
format!("Willkommens-Email wurde an {} versandt.", user.name),
),
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
}
}
#[get("/user/<user>/reset-pw")] #[get("/user/<user>/reset-pw")]
async fn resetpw(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<Redirect> { async fn resetpw(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<Redirect> {
let user = User::find_by_id(db, user).await; let user = User::find_by_id(db, user).await;
@ -332,6 +355,7 @@ pub fn routes() -> Vec<Route> {
fees, fees,
fees_paid, fees_paid,
scheckbuch, scheckbuch,
download_membership_pdf download_membership_pdf,
send_welcome_mail
] ]
} }

View File

@ -58,6 +58,10 @@
<a class="block mt-1 font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline" <a class="block mt-1 font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
href="/admin/user/{{ user.id }}/reset-pw">Passwort zurücksetzen</a> href="/admin/user/{{ user.id }}/reset-pw">Passwort zurücksetzen</a>
{% endif %} {% endif %}
{% if not user.last_access and "Donau Linz" in user.roles and "admin" in loggedin_user.roles %}
<a class="block mt-1 font-normal text-primary-600 dark:text-primary-200 hover:text-primary-900 dark:hover:text-primary-300 underline"
href="/admin/user/{{ user.id }}/send-welcome-mail">Willkommensmail verschicken</a>
{% endif %}
</div> </div>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3"> <div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
{% for role in roles %} {% for role in roles %}