membership-pdf-new #284
| @@ -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); | ||||
|  | ||||
|   | ||||
| @@ -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()); | ||||
|   | ||||
							
								
								
									
										60
									
								
								src/tera/admin/schnupper.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/tera/admin/schnupper.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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] | ||||
| } | ||||
							
								
								
									
										19
									
								
								templates/admin/schnupper/index.html.tera
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								templates/admin/schnupper/index.html.tera
									
									
									
									
									
										Normal file
									
								
							| @@ -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 %} | ||||
| @@ -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" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user