From edb42717bc35dc2fac86d2c76f86a8179b75858b Mon Sep 17 00:00:00 2001 From: philipp <philipp@hofer.link> Date: Wed, 6 Mar 2024 15:55:13 +0100 Subject: [PATCH] add schnupper management --- src/model/user.rs | 37 ++++++++++++++ src/tera/admin/mod.rs | 2 + src/tera/admin/schnupper.rs | 60 +++++++++++++++++++++++ templates/admin/schnupper/index.html.tera | 19 +++++++ templates/index.html.tera | 15 ++++++ 5 files changed, 133 insertions(+) create mode 100644 src/tera/admin/schnupper.rs create mode 100644 templates/admin/schnupper/index.html.tera diff --git a/src/model/user.rs b/src/model/user.rs index 5a8c3d8..d2b0cde 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -842,6 +842,43 @@ impl<'r> FromRequest<'r> for DonauLinzUser { } } +#[derive(Debug, Serialize, Deserialize)] +pub struct SchnupperBetreuerUser(pub(crate) User); + +impl From<SchnupperBetreuerUser> for User { + fn from(val: SchnupperBetreuerUser) -> Self { + val.0 + } +} + +impl Deref for SchnupperBetreuerUser { + type Target = User; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[async_trait] +impl<'r> FromRequest<'r> for SchnupperBetreuerUser { + type Error = LoginError; + + async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> { + let db = req.rocket().state::<SqlitePool>().unwrap(); + match User::from_request(req).await { + Outcome::Success(user) => { + if user.has_role(db, "schnupper-betreuer").await { + Outcome::Success(SchnupperBetreuerUser(user)) + } else { + Outcome::Forward(Status::Forbidden) + } + } + Outcome::Error(f) => Outcome::Error(f), + Outcome::Forward(f) => Outcome::Forward(f), + } + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct VorstandUser(pub(crate) User); diff --git a/src/tera/admin/mod.rs b/src/tera/admin/mod.rs index 04b23cd..7f3dae4 100644 --- a/src/tera/admin/mod.rs +++ b/src/tera/admin/mod.rs @@ -11,6 +11,7 @@ use crate::{ pub mod boat; pub mod mail; pub mod planned_event; +pub mod schnupper; pub mod user; #[get("/rss?<key>")] @@ -74,6 +75,7 @@ async fn list(db: &State<SqlitePool>, _admin: AdminUser, list_form: Form<ListFor pub fn routes() -> Vec<Route> { let mut ret = Vec::new(); ret.append(&mut user::routes()); + ret.append(&mut schnupper::routes()); ret.append(&mut boat::routes()); ret.append(&mut mail::routes()); ret.append(&mut planned_event::routes()); diff --git a/src/tera/admin/schnupper.rs b/src/tera/admin/schnupper.rs new file mode 100644 index 0000000..41d536d --- /dev/null +++ b/src/tera/admin/schnupper.rs @@ -0,0 +1,60 @@ +use crate::model::{ + role::Role, + user::{SchnupperBetreuerUser, User, UserWithRoles}, +}; +use futures::future::join_all; +use rocket::{ + get, + http::Status, + request::{FlashMessage, FromRequest, Outcome}, + routes, Request, Route, State, +}; +use rocket_dyn_templates::{tera::Context, Template}; +use sqlx::SqlitePool; + +// Custom request guard to extract the Referer header +struct Referer(String); + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for Referer { + type Error = (); + + async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { + match request.headers().get_one("Referer") { + Some(referer) => Outcome::Success(Referer(referer.to_string())), + None => Outcome::Error((Status::BadRequest, ())), + } + } +} + +#[get("/schnupper")] +async fn index( + db: &State<SqlitePool>, + user: SchnupperBetreuerUser, + flash: Option<FlashMessage<'_>>, +) -> Template { + let schnupperant = Role::find_by_name(db, "schnupperant").await.unwrap(); + + let user_futures: Vec<_> = User::all_with_role(db, &schnupperant) + .await + .into_iter() + .map(|u| async move { UserWithRoles::from_user(u, db).await }) + .collect(); + let users: Vec<UserWithRoles> = join_all(user_futures).await; + + let mut context = Context::new(); + if let Some(msg) = flash { + context.insert("flash", &msg.into_inner()); + } + context.insert("schnupperanten", &users); + context.insert( + "loggedin_user", + &UserWithRoles::from_user(user.into(), db).await, + ); + + Template::render("admin/schnupper/index", context.into_json()) +} + +pub fn routes() -> Vec<Route> { + routes![index] +} diff --git a/templates/admin/schnupper/index.html.tera b/templates/admin/schnupper/index.html.tera new file mode 100644 index 0000000..a3e9d5a --- /dev/null +++ b/templates/admin/schnupper/index.html.tera @@ -0,0 +1,19 @@ +{% import "includes/macros" as macros %} +{% extends "base" %} +{% block content %} + <div class="max-w-screen-lg w-full"> + {% if flash %}{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}{% endif %} + <h1 class="h1">Schnupper Verwaltung</h1> + <div class="grid gap-3"> + <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" + role="alert"> + <h2 class="h2">Angemeldete Personen: {{ schnupperanten | length }}</h2> + <div class="text-sm p-3"> + <ol class="ms-2" style="list-style: number;"> + {% for user in schnupperanten %}<li class="py-1">{{ user.name }} ({{ user.mail }} | {{ user.notes }})</li>{% endfor %} + </ol> + </div> + </div> + </div> + </div> +{% endblock content %} diff --git a/templates/index.html.tera b/templates/index.html.tera index dec52f8..06c03e7 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -79,6 +79,21 @@ </div> </div> {% endif %} + {% if "schnupper-betreuer" in loggedin_user.roles %} + <div class="grid gap-3"> + <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" + role="alert"> + <h2 class="h2">Schnupper-Betreuer</h2> + <div class="text-sm p-3"> + <ul class="list-disc ms-2"> + <li class="py-1"> + <a href="/admin/schnupper" class="link-primary">Schnuppern</a> + </li> + </ul> + </div> + </div> + </div> + {% endif %} {% if "Vorstand" in loggedin_user.roles %} <div class="grid gap-3"> <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"