clean repo
This commit is contained in:
		
							
								
								
									
										3479
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3479
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -6,9 +6,6 @@ edition = "2021" | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
|  | ||||
| [dependencies] | ||||
| sea-orm = { version = "^0", features = [ "sqlx-sqlite", "runtime-tokio-rustls", "macros" ] } | ||||
| sea-orm-migration = { version = "0.11", features = [ "runtime-tokio-rustls", "sqlx-sqlite" ] } | ||||
| serde = { version = "1.0", features = [ "derive" ]} | ||||
| rocket = { version = "0.5.0-rc.2", features = ["secrets"]} | ||||
| rocket_dyn_templates = { version = "0.1.0-rc.2", features= ["tera"] } | ||||
| chrono =  { version = "0.4", features = ["serde"]} | ||||
|   | ||||
							
								
								
									
										7
									
								
								mod.rs
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								mod.rs
									
									
									
									
									
								
							| @@ -1,7 +0,0 @@ | ||||
| //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.0 | ||||
|  | ||||
| pub mod prelude; | ||||
|  | ||||
| pub mod day; | ||||
| pub mod trip; | ||||
| pub mod user; | ||||
| @@ -1,5 +0,0 @@ | ||||
| //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.0 | ||||
|  | ||||
| pub use super::day::Entity as Day; | ||||
| pub use super::trip::Entity as Trip; | ||||
| pub use super::user::Entity as User; | ||||
| @@ -1,11 +1,7 @@ | ||||
| #[macro_use] | ||||
| extern crate rocket; | ||||
|  | ||||
| mod models; | ||||
| mod rest; | ||||
|  | ||||
| #[launch] | ||||
| async fn rocket() -> _ { | ||||
|     env_logger::init(); | ||||
|     rest::start().await | ||||
| } | ||||
|   | ||||
| @@ -1,41 +0,0 @@ | ||||
| use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter}; | ||||
| use serde::Serialize; | ||||
|  | ||||
| use super::{day, trip, user}; | ||||
|  | ||||
| #[derive(Serialize, Debug)] | ||||
| pub struct TripWithUser { | ||||
|     pub trip: trip::Model, | ||||
|     pub user: user::Model, | ||||
| } | ||||
|  | ||||
| impl TripWithUser { | ||||
|     fn new(trip: trip::Model, user: user::Model) -> Self { | ||||
|         Self { trip, user } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Debug)] | ||||
| pub struct DayWithTrips { | ||||
|     pub day: day::Model, | ||||
|     pub trips: Vec<TripWithUser>, | ||||
| } | ||||
|  | ||||
| impl DayWithTrips { | ||||
|     pub async fn new(day: day::Model, db: &DatabaseConnection) -> Self { | ||||
|         let mut trips = Vec::new(); | ||||
|  | ||||
|         let trips_with_users: Vec<(trip::Model, Option<user::Model>)> = trip::Entity::find() | ||||
|             .filter(trip::Column::Day.eq(day.day)) | ||||
|             .find_also_related(user::Entity) | ||||
|             .all(db) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         for (trip, users) in trips_with_users { | ||||
|             trips.push(TripWithUser::new(trip, users.unwrap())); | ||||
|         } | ||||
|  | ||||
|         Self { day, trips } | ||||
|     } | ||||
| } | ||||
| @@ -1,43 +0,0 @@ | ||||
| //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.0 | ||||
|  | ||||
| use sea_orm::{entity::prelude::*, Set}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] | ||||
| #[sea_orm(table_name = "day")] | ||||
| pub struct Model { | ||||
|     #[sea_orm(primary_key, auto_increment = false)] | ||||
|     pub day: chrono::NaiveDate, | ||||
|     pub planned_amount_cox: i32, | ||||
|     pub planned_starting_time: Option<String>, | ||||
|     pub open_registration: bool, | ||||
| } | ||||
|  | ||||
| impl Model { | ||||
|     pub async fn find_or_create_day(date: chrono::NaiveDate, db: &DatabaseConnection) -> Model { | ||||
|         let day = Entity::find_by_id(date).one(db).await.unwrap(); | ||||
|         if let Some(day) = day { | ||||
|             day | ||||
|         } else { | ||||
|             let new_day = ActiveModel { | ||||
|                 day: Set(date), | ||||
|                 ..Default::default() | ||||
|             }; | ||||
|             new_day.insert(db).await.unwrap() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] | ||||
| pub enum Relation { | ||||
|     #[sea_orm(has_many = "super::trip::Entity")] | ||||
|     Trip, | ||||
| } | ||||
|  | ||||
| impl Related<super::trip::Entity> for Entity { | ||||
|     fn to() -> RelationDef { | ||||
|         Relation::Trip.def() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ActiveModelBehavior for ActiveModel {} | ||||
| @@ -1,9 +0,0 @@ | ||||
| //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.0 | ||||
|  | ||||
| pub mod prelude; | ||||
|  | ||||
| pub mod all; | ||||
|  | ||||
| pub mod day; | ||||
| pub mod trip; | ||||
| pub mod user; | ||||
| @@ -1,5 +0,0 @@ | ||||
| //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.0 | ||||
|  | ||||
| pub use super::day::Entity as Day; | ||||
| pub use super::trip::Entity as Trip; | ||||
| pub use super::user::Entity as User; | ||||
| @@ -1,58 +0,0 @@ | ||||
| //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.0 | ||||
|  | ||||
| use sea_orm::entity::prelude::*; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] | ||||
| #[sea_orm(table_name = "trip")] | ||||
| pub struct Model { | ||||
|     #[sea_orm(primary_key)] | ||||
|     pub id: i32, | ||||
|     pub day: String, | ||||
|     pub user_id: i32, | ||||
|     pub cox_id: Option<i32>, | ||||
|     pub begin: Option<String>, | ||||
|     pub created: chrono::DateTime<chrono::Utc>, | ||||
| } | ||||
|  | ||||
| #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] | ||||
| pub enum Relation { | ||||
|     #[sea_orm( | ||||
|         belongs_to = "super::day::Entity", | ||||
|         from = "Column::Day", | ||||
|         to = "super::day::Column::Day", | ||||
|         on_update = "NoAction", | ||||
|         on_delete = "NoAction" | ||||
|     )] | ||||
|     Day, | ||||
|     #[sea_orm( | ||||
|         belongs_to = "super::user::Entity", | ||||
|         from = "Column::CoxId", | ||||
|         to = "super::user::Column::Id", | ||||
|         on_update = "NoAction", | ||||
|         on_delete = "NoAction" | ||||
|     )] | ||||
|     Cox, | ||||
|     #[sea_orm( | ||||
|         belongs_to = "super::user::Entity", | ||||
|         from = "Column::UserId", | ||||
|         to = "super::user::Column::Id", | ||||
|         on_update = "NoAction", | ||||
|         on_delete = "NoAction" | ||||
|     )] | ||||
|     User, | ||||
| } | ||||
|  | ||||
| impl Related<super::day::Entity> for Entity { | ||||
|     fn to() -> RelationDef { | ||||
|         Relation::Day.def() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Related<super::user::Entity> for Entity { | ||||
|     fn to() -> RelationDef { | ||||
|         Relation::User.def() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ActiveModelBehavior for ActiveModel {} | ||||
| @@ -1,94 +0,0 @@ | ||||
| //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.0 | ||||
|  | ||||
| use rocket::{ | ||||
|     http::Status, | ||||
|     request::{self, FromRequest, Outcome}, | ||||
|     Request, State, | ||||
| }; | ||||
| use sea_orm::{entity::prelude::*, Set}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] | ||||
| #[sea_orm(table_name = "user")] | ||||
| pub struct Model { | ||||
|     #[sea_orm(primary_key)] | ||||
|     pub id: i32, | ||||
|     pub name: String, | ||||
|     pub pw: Option<String>, | ||||
|     pub is_cox: bool, | ||||
|     pub add_different_user: bool, | ||||
|     pub is_admin: bool, | ||||
| } | ||||
|  | ||||
| #[derive(Serialize)] | ||||
| pub struct AdminUser(Model); | ||||
|  | ||||
| impl Model { | ||||
|     pub async fn find_or_create_user(name: &str, db: &DatabaseConnection) -> Model { | ||||
|         let user = Entity::find() | ||||
|             .filter(Column::Name.eq(name)) | ||||
|             .one(db) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|  | ||||
|         if let Some(user) = user { | ||||
|             user | ||||
|         } else { | ||||
|             let user = ActiveModel { | ||||
|                 name: Set(name.into()), | ||||
|                 ..Default::default() | ||||
|             }; | ||||
|             log::info!("User {:?} created", user); | ||||
|             user.insert(db).await.unwrap() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub enum Error { | ||||
|     NoCookieSet, | ||||
|     NoAdmin, | ||||
| } | ||||
|  | ||||
| #[rocket::async_trait] | ||||
| impl<'r> FromRequest<'r> for Model { | ||||
|     type Error = Error; | ||||
|  | ||||
|     async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> { | ||||
|         match req.cookies().get_private("name") { | ||||
|             Some(name) => { | ||||
|                 let db = req.guard::<&'r State<DatabaseConnection>>(); | ||||
|                 let name = name.value(); | ||||
|                 let user = Model::find_or_create_user(name, db.await.unwrap().inner()).await; | ||||
|                 Outcome::Success(user) | ||||
|             } | ||||
|             None => Outcome::Failure((Status::Unauthorized, Error::NoCookieSet)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[rocket::async_trait] | ||||
| impl<'r> FromRequest<'r> for AdminUser { | ||||
|     type Error = Error; | ||||
|  | ||||
|     async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> { | ||||
|         match req.cookies().get_private("name") { | ||||
|             Some(name) => { | ||||
|                 let db = req.guard::<&'r State<DatabaseConnection>>(); | ||||
|                 let name = name.value(); | ||||
|                 let user = Model::find_or_create_user(name, db.await.unwrap().inner()).await; | ||||
|                 if user.is_admin { | ||||
|                     Outcome::Success(AdminUser(user)) | ||||
|                 } else { | ||||
|                     Outcome::Failure((Status::Unauthorized, Error::NoAdmin)) | ||||
|                 } | ||||
|             } | ||||
|             None => Outcome::Failure((Status::Unauthorized, Error::NoCookieSet)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] | ||||
| pub enum Relation {} | ||||
|  | ||||
| impl ActiveModelBehavior for ActiveModel {} | ||||
							
								
								
									
										148
									
								
								src/rest/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								src/rest/mod.rs
									
									
									
									
									
								
							| @@ -1,148 +0,0 @@ | ||||
| mod restday; | ||||
| mod restreg; | ||||
| mod restuser; | ||||
|  | ||||
| use std::ops::Deref; | ||||
|  | ||||
| use chrono::{Datelike, Duration, Local, NaiveDate}; | ||||
| use rocket::{ | ||||
|     form::{self, Form, ValueField}, | ||||
|     fs::FileServer, | ||||
|     http::{Cookie, CookieJar}, | ||||
|     request::FlashMessage, | ||||
|     response::{Flash, Redirect}, | ||||
|     Build, Rocket, State, | ||||
| }; | ||||
| use rocket_dyn_templates::{tera, Template}; | ||||
| use sea_orm::{Database, DatabaseConnection}; | ||||
| use sha3::{Digest, Sha3_256}; | ||||
|  | ||||
| use super::models::{all::DayWithTrips, day, user}; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct NaiveDateForm(NaiveDate); | ||||
|  | ||||
| impl<'v> rocket::form::FromFormField<'v> for NaiveDateForm { | ||||
|     fn from_value(field: ValueField<'v>) -> form::Result<'v, NaiveDateForm> { | ||||
|         let naivedate = chrono::NaiveDate::parse_from_str(field.value, "%Y-%m-%d").unwrap(); //TODO: | ||||
|                                                                                              //fixme | ||||
|         Ok(NaiveDateForm(naivedate)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Deref for NaiveDateForm { | ||||
|     type Target = NaiveDate; | ||||
|     fn deref(&self) -> &Self::Target { | ||||
|         &self.0 | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[get("/?<all>")] | ||||
| async fn index( | ||||
|     db: &State<DatabaseConnection>, | ||||
|     user: user::Model, | ||||
|     all: Option<String>, | ||||
|     flash: Option<FlashMessage<'_>>, | ||||
| ) -> Template { | ||||
|     let mut data = Vec::new(); | ||||
|  | ||||
|     let mut show_next_n_days = 6; | ||||
|     if all.is_some() && user.is_cox { | ||||
|         let end_of_year = NaiveDate::from_ymd_opt(Local::now().year(), 12, 31).unwrap(); | ||||
|         show_next_n_days = end_of_year | ||||
|             .signed_duration_since(Local::now().date_naive()) | ||||
|             .num_days(); | ||||
|     } | ||||
|  | ||||
|     for i in 0..show_next_n_days { | ||||
|         let date = (Local::now() + Duration::days(i)).date_naive(); | ||||
|         let day = day::Model::find_or_create_day(date, db.inner()).await; | ||||
|         data.push(DayWithTrips::new(day, db.inner()).await); | ||||
|     } | ||||
|  | ||||
|     let mut context = tera::Context::new(); | ||||
|  | ||||
|     if let Some(msg) = flash { | ||||
|         context.insert("flash", &msg.into_inner()); | ||||
|     } | ||||
|     context.insert("data", &data); | ||||
|     context.insert("user", &user); | ||||
|     Template::render("index", context.into_json()) | ||||
| } | ||||
|  | ||||
| #[get("/name")] | ||||
| fn name(flash: Option<FlashMessage<'_>>) -> Template { | ||||
|     let mut context = tera::Context::new(); | ||||
|  | ||||
|     if let Some(msg) = flash { | ||||
|         context.insert("flash", &msg.into_inner()); | ||||
|     } | ||||
|     Template::render("name", context.into_json()) | ||||
| } | ||||
|  | ||||
| #[derive(FromForm)] | ||||
| struct NameForm { | ||||
|     name: String, | ||||
|     pw: Option<String>, | ||||
| } | ||||
|  | ||||
| #[put("/name", data = "<name>")] | ||||
| async fn savename( | ||||
|     name: Form<NameForm>, | ||||
|     cookies: &CookieJar<'_>, | ||||
|     db: &State<DatabaseConnection>, | ||||
| ) -> Flash<Redirect> { | ||||
|     let user = user::Model::find_or_create_user(&name.name, db.inner()).await; | ||||
|     if let Some(pw) = user.pw { | ||||
|         match &name.pw { | ||||
|             Some(entered_pw) => { | ||||
|                 let mut hasher = Sha3_256::new(); | ||||
|                 hasher.update(entered_pw); | ||||
|                 let entered_pw = hasher.finalize(); | ||||
|  | ||||
|                 if hex::encode(entered_pw) == pw { | ||||
|                     log::info!("{} hat sich erfolgreich eingeloggt (mit PW)", name.name); | ||||
|                     cookies.add_private(Cookie::new("name", name.name.clone())); | ||||
|                     Flash::success(Redirect::to("/"), "Erfolgreich eingeloggt") | ||||
|                 } else { | ||||
|                     log::warn!("Somebody tried to login as {} with a WRONG pw", name.name); | ||||
|                     Flash::error(Redirect::to("/name"), "Falsches Passwort") | ||||
|                 } | ||||
|             } | ||||
|             None => { | ||||
|                 log::warn!( | ||||
|                     "Somebody tried to login as {}, w/o specifying a pw", | ||||
|                     name.name | ||||
|                 ); | ||||
|                 Flash::error(Redirect::to("/name"), "Benutzer besitzt hat Passwort, du hast jedoch keines eingegeben. Bitte nochmal probieren") | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         log::info!("{} hat sich erfolgreich eingeloggt (ohne PW)", name.name); | ||||
|         cookies.add_private(Cookie::new("name", name.name.clone())); | ||||
|         Flash::success(Redirect::to("/"), "Name erfolgreich ausgewählt") | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[get("/logout")] | ||||
| fn logout(cookies: &CookieJar) -> Redirect { | ||||
|     cookies.remove_private(Cookie::new("name", "")); | ||||
|     Redirect::to("/") | ||||
| } | ||||
|  | ||||
| #[catch(401)] //unauthorized | ||||
| fn unauthorized_error() -> Redirect { | ||||
|     Redirect::to("/name") | ||||
| } | ||||
|  | ||||
| pub async fn start() -> Rocket<Build> { | ||||
|     rocket::build() | ||||
|         .attach(Template::fairing()) | ||||
|         .manage(Database::connect("sqlite://db.sqlite").await.unwrap()) | ||||
|         .mount("/public", FileServer::from("static/")) | ||||
|         .mount("/", routes![index, name, savename, logout]) | ||||
|         .mount("/day", restday::routes()) | ||||
|         .mount("/register", restreg::routes()) | ||||
|         .mount("/user", restuser::routes()) | ||||
|         .register("/", catchers![unauthorized_error]) | ||||
| } | ||||
| @@ -1,43 +0,0 @@ | ||||
| use rocket::{form::Form, response::Redirect, Route, State}; | ||||
| use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set}; | ||||
|  | ||||
| use crate::models::day; | ||||
|  | ||||
| use super::NaiveDateForm; | ||||
|  | ||||
| #[derive(FromForm, Debug)] | ||||
| struct DayForm { | ||||
|     day: NaiveDateForm, | ||||
|     #[field(validate = range(0..20))] | ||||
|     planned_amount_cox: i32, | ||||
|     planned_starting_time: Option<String>, | ||||
|     open_registration: bool, | ||||
| } | ||||
|  | ||||
| #[put("/", data = "<day>")] | ||||
| async fn create(db: &State<DatabaseConnection>, day: Form<DayForm>) -> Redirect { | ||||
|     let new_day = day::ActiveModel { | ||||
|         day: Set(*day.day), | ||||
|         planned_amount_cox: Set(day.planned_amount_cox), | ||||
|         planned_starting_time: Set(day.planned_starting_time.clone()), | ||||
|         open_registration: Set(day.open_registration), | ||||
|     }; | ||||
|  | ||||
|     let day: Option<day::Model> = day::Entity::find_by_id(*day.day) | ||||
|         .one(db.inner()) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     if let Some(day) = day { | ||||
|         log::info!("{:?} got updated to {:?}", day, new_day); | ||||
|         new_day.update(db.inner()).await.unwrap(); //TODO: fixme | ||||
|     } else { | ||||
|         log::info!("{:?} got inserted", new_day); | ||||
|         new_day.insert(db.inner()).await.unwrap(); //TODO: fixme | ||||
|     } | ||||
|  | ||||
|     Redirect::to("/") | ||||
| } | ||||
|  | ||||
| pub fn routes() -> Vec<Route> { | ||||
|     routes![create] | ||||
| } | ||||
| @@ -1,139 +0,0 @@ | ||||
| use rocket::{ | ||||
|     form::Form, | ||||
|     response::{Flash, Redirect}, | ||||
|     Route, State, | ||||
| }; | ||||
| use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set}; | ||||
|  | ||||
| use crate::models::{day, trip, user}; | ||||
|  | ||||
| use super::NaiveDateForm; | ||||
|  | ||||
| #[derive(FromForm)] | ||||
| struct RegisterForm { | ||||
|     day: NaiveDateForm, | ||||
|     #[field(validate = len(3..))] | ||||
|     name: String, | ||||
|     time: Option<String>, | ||||
|     cox_id: Option<i32>, | ||||
| } | ||||
|  | ||||
| #[put("/", data = "<register>")] | ||||
| async fn register( | ||||
|     db: &State<DatabaseConnection>, | ||||
|     register: Form<RegisterForm>, | ||||
|     user: user::Model, | ||||
| ) -> Flash<Redirect> { | ||||
|     let day = day::Entity::find_by_id(*register.day) | ||||
|         .one(db.inner()) | ||||
|         .await | ||||
|         .unwrap() | ||||
|         .expect("There's no trip on this date (yet)"); | ||||
|  | ||||
|     if register.cox_id.is_none() && !day.open_registration && register.time.is_none() { | ||||
|         log::error!("{} tried to register, even though the user it should not be possible to do so via UI -> manually crafted request?", user.name); | ||||
|         return Flash::error( | ||||
|             Redirect::to("/"), | ||||
|             "Don't (try to ;)) abuse this system! Incident has been reported...", | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     if !user.add_different_user && user.name != register.name { | ||||
|         log::error!("{} tried to register a different person, even though the user has no add_different_user flag and thus it should not be possible to do so via UI -> manually crafted request?", user.name); | ||||
|         return Flash::error( | ||||
|             Redirect::to("/"), | ||||
|             "Don't (try to ;)) abuse this system! Incident has been reported...", | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     let user = user::Model::find_or_create_user(®ister.name, db.inner()).await; | ||||
|  | ||||
|     if let Some(cox_id) = register.cox_id { | ||||
|         let trip = trip::Entity::find_by_id(cox_id) | ||||
|             .one(db.inner()) | ||||
|             .await | ||||
|             .unwrap() | ||||
|             .unwrap(); | ||||
|         if trip.user_id == user.id { | ||||
|             log::warn!( | ||||
|                 "{} tried to register for his own trip ({})", | ||||
|                 user.name, | ||||
|                 trip.id | ||||
|             ); | ||||
|             return Flash::error( | ||||
|                 Redirect::to("/"), | ||||
|                 "Du kannst an deinen eigenen Ausfahrten nicht teilnehmen...", | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let day = format!("{}", day.day.format("%Y-%m-%d")); | ||||
|     let trip = trip::ActiveModel { | ||||
|         day: Set(day.clone()), | ||||
|         user_id: Set(user.id), | ||||
|         begin: Set(register.time.clone()), | ||||
|         cox_id: Set(register.cox_id), | ||||
|         ..Default::default() | ||||
|     }; | ||||
|     if trip.insert(db.inner()).await.is_ok() { | ||||
|         log::info!("{} registered for {:?}", user.name, day); | ||||
|         Flash::success(Redirect::to("/"), "Erfolgreich angemeldet!") | ||||
|     } else { | ||||
|         log::warn!( | ||||
|             "{} tried to register for {:?}, but is already registered", | ||||
|             user.name, | ||||
|             day | ||||
|         ); | ||||
|         Flash::error(Redirect::to("/"), "Du bist bereits angemeldet") | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(FromForm)] | ||||
| struct DeleteForm { | ||||
|     id: i32, | ||||
| } | ||||
|  | ||||
| #[delete("/", data = "<delete>")] | ||||
| async fn delete( | ||||
|     db: &State<DatabaseConnection>, | ||||
|     delete: Form<DeleteForm>, | ||||
|     user: user::Model, | ||||
| ) -> Flash<Redirect> { | ||||
|     let trip = trip::Entity::find_by_id(delete.id) | ||||
|         .one(db.inner()) | ||||
|         .await | ||||
|         .unwrap(); | ||||
|  | ||||
|     match trip { | ||||
|         None => { | ||||
|             log::error!("Tried to delete registration of non-existing trip (prob. hand crafted request (user.name = {})", user.name); | ||||
|             return Flash::error(Redirect::to("/"), "Du bist gar nicht angemeldet!"); | ||||
|         } | ||||
|         Some(trip) => { | ||||
|             if trip.user_id != user.id { | ||||
|                 log::error!( | ||||
|             "{} tried to delete a registration from user_id {} (probably hand-crafted request)", | ||||
|             user.name, | ||||
|             delete.id | ||||
|         ); | ||||
|                 return Flash::error( | ||||
|                     Redirect::to("/"), | ||||
|                     "Du kannst nur deine eigenen Anmeldungen löschen!", | ||||
|                 ); | ||||
|             } | ||||
|             log::info!("User {} deleted the registration for {:?}", user.name, trip); | ||||
|             trip::Entity::delete(trip::ActiveModel { | ||||
|                 id: Set(trip.id), | ||||
|                 ..Default::default() | ||||
|             }) | ||||
|             .exec(db.inner()) | ||||
|             .await | ||||
|             .unwrap(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Flash::success(Redirect::to("/"), "Abmeldung erfolgreich") | ||||
| } | ||||
| pub fn routes() -> Vec<Route> { | ||||
|     routes![register, delete] | ||||
| } | ||||
| @@ -1,54 +0,0 @@ | ||||
| use rocket::{form::Form, response::Redirect, Route, State}; | ||||
| use rocket_dyn_templates::{context, Template}; | ||||
| use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set}; | ||||
| use sha3::{Digest, Sha3_256}; | ||||
|  | ||||
| use crate::models::user; | ||||
|  | ||||
| #[get("/")] | ||||
| async fn index(db: &State<DatabaseConnection>, user: user::AdminUser) -> Template { | ||||
|     let users = user::Entity::find().all(db.inner()).await.unwrap(); | ||||
|  | ||||
|     Template::render("user/index", context! {user, users}) | ||||
| } | ||||
|  | ||||
| #[derive(FromForm)] | ||||
| struct UserEditForm { | ||||
|     pw: Option<String>, | ||||
|     is_cox: bool, | ||||
|     add_different_user: bool, | ||||
|     is_admin: bool, | ||||
| } | ||||
|  | ||||
| #[put("/<id>", data = "<data>")] | ||||
| async fn update( | ||||
|     db: &State<DatabaseConnection>, | ||||
|     id: i32, | ||||
|     data: Form<UserEditForm>, | ||||
|     _user: user::AdminUser, | ||||
| ) -> Redirect { | ||||
|     let mut new_user = user::ActiveModel { | ||||
|         id: Set(id), | ||||
|         is_cox: Set(data.is_cox), | ||||
|         is_admin: Set(data.is_admin), | ||||
|         add_different_user: Set(data.add_different_user), | ||||
|         ..Default::default() | ||||
|     }; | ||||
|     if let Some(pw) = &data.pw { | ||||
|         if !pw.is_empty() { | ||||
|             let mut hasher = Sha3_256::new(); | ||||
|             hasher.update(pw); | ||||
|             let entered_pw = hasher.finalize(); | ||||
|  | ||||
|             let pw = hex::encode(entered_pw); | ||||
|             new_user.pw = Set(Some(pw)); | ||||
|         } | ||||
|     } | ||||
|     new_user.update(db.inner()).await.unwrap(); | ||||
|  | ||||
|     Redirect::to("/user") | ||||
| } | ||||
|  | ||||
| pub fn routes() -> Vec<Route> { | ||||
|     routes![index, update] | ||||
| } | ||||
							
								
								
									
										427
									
								
								static/css/normalize.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										427
									
								
								static/css/normalize.css
									
									
									
									
										vendored
									
									
								
							| @@ -1,427 +0,0 @@ | ||||
| /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ | ||||
|  | ||||
| /** | ||||
|  * 1. Set default font family to sans-serif. | ||||
|  * 2. Prevent iOS text size adjust after orientation change, without disabling | ||||
|  *    user zoom. | ||||
|  */ | ||||
|  | ||||
| html { | ||||
|   font-family: sans-serif; /* 1 */ | ||||
|   -ms-text-size-adjust: 100%; /* 2 */ | ||||
|   -webkit-text-size-adjust: 100%; /* 2 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Remove default margin. | ||||
|  */ | ||||
|  | ||||
| body { | ||||
|   margin: 0; | ||||
| } | ||||
|  | ||||
| /* HTML5 display definitions | ||||
|    ========================================================================== */ | ||||
|  | ||||
| /** | ||||
|  * Correct `block` display not defined for any HTML5 element in IE 8/9. | ||||
|  * Correct `block` display not defined for `details` or `summary` in IE 10/11 | ||||
|  * and Firefox. | ||||
|  * Correct `block` display not defined for `main` in IE 11. | ||||
|  */ | ||||
|  | ||||
| article, | ||||
| aside, | ||||
| details, | ||||
| figcaption, | ||||
| figure, | ||||
| footer, | ||||
| header, | ||||
| hgroup, | ||||
| main, | ||||
| menu, | ||||
| nav, | ||||
| section, | ||||
| summary { | ||||
|   display: block; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 1. Correct `inline-block` display not defined in IE 8/9. | ||||
|  * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. | ||||
|  */ | ||||
|  | ||||
| audio, | ||||
| canvas, | ||||
| progress, | ||||
| video { | ||||
|   display: inline-block; /* 1 */ | ||||
|   vertical-align: baseline; /* 2 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Prevent modern browsers from displaying `audio` without controls. | ||||
|  * Remove excess height in iOS 5 devices. | ||||
|  */ | ||||
|  | ||||
| audio:not([controls]) { | ||||
|   display: none; | ||||
|   height: 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address `[hidden]` styling not present in IE 8/9/10. | ||||
|  * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. | ||||
|  */ | ||||
|  | ||||
| [hidden], | ||||
| template { | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
| /* Links | ||||
|    ========================================================================== */ | ||||
|  | ||||
| /** | ||||
|  * Remove the gray background color from active links in IE 10. | ||||
|  */ | ||||
|  | ||||
| a { | ||||
|   background-color: transparent; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Improve readability when focused and also mouse hovered in all browsers. | ||||
|  */ | ||||
|  | ||||
| a:active, | ||||
| a:hover { | ||||
|   outline: 0; | ||||
| } | ||||
|  | ||||
| /* Text-level semantics | ||||
|    ========================================================================== */ | ||||
|  | ||||
| /** | ||||
|  * Address styling not present in IE 8/9/10/11, Safari, and Chrome. | ||||
|  */ | ||||
|  | ||||
| abbr[title] { | ||||
|   border-bottom: 1px dotted; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. | ||||
|  */ | ||||
|  | ||||
| b, | ||||
| strong { | ||||
|   font-weight: bold; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address styling not present in Safari and Chrome. | ||||
|  */ | ||||
|  | ||||
| dfn { | ||||
|   font-style: italic; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address variable `h1` font-size and margin within `section` and `article` | ||||
|  * contexts in Firefox 4+, Safari, and Chrome. | ||||
|  */ | ||||
|  | ||||
| h1 { | ||||
|   font-size: 2em; | ||||
|   margin: 0.67em 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address styling not present in IE 8/9. | ||||
|  */ | ||||
|  | ||||
| mark { | ||||
|   background: #ff0; | ||||
|   color: #000; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address inconsistent and variable font size in all browsers. | ||||
|  */ | ||||
|  | ||||
| small { | ||||
|   font-size: 80%; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Prevent `sub` and `sup` affecting `line-height` in all browsers. | ||||
|  */ | ||||
|  | ||||
| sub, | ||||
| sup { | ||||
|   font-size: 75%; | ||||
|   line-height: 0; | ||||
|   position: relative; | ||||
|   vertical-align: baseline; | ||||
| } | ||||
|  | ||||
| sup { | ||||
|   top: -0.5em; | ||||
| } | ||||
|  | ||||
| sub { | ||||
|   bottom: -0.25em; | ||||
| } | ||||
|  | ||||
| /* Embedded content | ||||
|    ========================================================================== */ | ||||
|  | ||||
| /** | ||||
|  * Remove border when inside `a` element in IE 8/9/10. | ||||
|  */ | ||||
|  | ||||
| img { | ||||
|   border: 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Correct overflow not hidden in IE 9/10/11. | ||||
|  */ | ||||
|  | ||||
| svg:not(:root) { | ||||
|   overflow: hidden; | ||||
| } | ||||
|  | ||||
| /* Grouping content | ||||
|    ========================================================================== */ | ||||
|  | ||||
| /** | ||||
|  * Address margin not present in IE 8/9 and Safari. | ||||
|  */ | ||||
|  | ||||
| figure { | ||||
|   margin: 1em 40px; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address differences between Firefox and other browsers. | ||||
|  */ | ||||
|  | ||||
| hr { | ||||
|   -moz-box-sizing: content-box; | ||||
|   box-sizing: content-box; | ||||
|   height: 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Contain overflow in all browsers. | ||||
|  */ | ||||
|  | ||||
| pre { | ||||
|   overflow: auto; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address odd `em`-unit font size rendering in all browsers. | ||||
|  */ | ||||
|  | ||||
| code, | ||||
| kbd, | ||||
| pre, | ||||
| samp { | ||||
|   font-family: monospace, monospace; | ||||
|   font-size: 1em; | ||||
| } | ||||
|  | ||||
| /* Forms | ||||
|    ========================================================================== */ | ||||
|  | ||||
| /** | ||||
|  * Known limitation: by default, Chrome and Safari on OS X allow very limited | ||||
|  * styling of `select`, unless a `border` property is set. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * 1. Correct color not being inherited. | ||||
|  *    Known issue: affects color of disabled elements. | ||||
|  * 2. Correct font properties not being inherited. | ||||
|  * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. | ||||
|  */ | ||||
|  | ||||
| button, | ||||
| input, | ||||
| optgroup, | ||||
| select, | ||||
| textarea { | ||||
|   color: inherit; /* 1 */ | ||||
|   font: inherit; /* 2 */ | ||||
|   margin: 0; /* 3 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address `overflow` set to `hidden` in IE 8/9/10/11. | ||||
|  */ | ||||
|  | ||||
| button { | ||||
|   overflow: visible; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address inconsistent `text-transform` inheritance for `button` and `select`. | ||||
|  * All other form control elements do not inherit `text-transform` values. | ||||
|  * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. | ||||
|  * Correct `select` style inheritance in Firefox. | ||||
|  */ | ||||
|  | ||||
| button, | ||||
| select { | ||||
|   text-transform: none; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` | ||||
|  *    and `video` controls. | ||||
|  * 2. Correct inability to style clickable `input` types in iOS. | ||||
|  * 3. Improve usability and consistency of cursor style between image-type | ||||
|  *    `input` and others. | ||||
|  */ | ||||
|  | ||||
| button, | ||||
| html input[type="button"], /* 1 */ | ||||
| input[type="reset"], | ||||
| input[type="submit"] { | ||||
|   -webkit-appearance: button; /* 2 */ | ||||
|   cursor: pointer; /* 3 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Re-set default cursor for disabled elements. | ||||
|  */ | ||||
|  | ||||
| button[disabled], | ||||
| html input[disabled] { | ||||
|   cursor: default; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Remove inner padding and border in Firefox 4+. | ||||
|  */ | ||||
|  | ||||
| button::-moz-focus-inner, | ||||
| input::-moz-focus-inner { | ||||
|   border: 0; | ||||
|   padding: 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Address Firefox 4+ setting `line-height` on `input` using `!important` in | ||||
|  * the UA stylesheet. | ||||
|  */ | ||||
|  | ||||
| input { | ||||
|   line-height: normal; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * It's recommended that you don't attempt to style these elements. | ||||
|  * Firefox's implementation doesn't respect box-sizing, padding, or width. | ||||
|  * | ||||
|  * 1. Address box sizing set to `content-box` in IE 8/9/10. | ||||
|  * 2. Remove excess padding in IE 8/9/10. | ||||
|  */ | ||||
|  | ||||
| input[type="checkbox"], | ||||
| input[type="radio"] { | ||||
|   box-sizing: border-box; /* 1 */ | ||||
|   padding: 0; /* 2 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Fix the cursor style for Chrome's increment/decrement buttons. For certain | ||||
|  * `font-size` values of the `input`, it causes the cursor style of the | ||||
|  * decrement button to change from `default` to `text`. | ||||
|  */ | ||||
|  | ||||
| input[type="number"]::-webkit-inner-spin-button, | ||||
| input[type="number"]::-webkit-outer-spin-button { | ||||
|   height: auto; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 1. Address `appearance` set to `searchfield` in Safari and Chrome. | ||||
|  * 2. Address `box-sizing` set to `border-box` in Safari and Chrome | ||||
|  *    (include `-moz` to future-proof). | ||||
|  */ | ||||
|  | ||||
| input[type="search"] { | ||||
|   -webkit-appearance: textfield; /* 1 */ | ||||
|   -moz-box-sizing: content-box; | ||||
|   -webkit-box-sizing: content-box; /* 2 */ | ||||
|   box-sizing: content-box; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Remove inner padding and search cancel button in Safari and Chrome on OS X. | ||||
|  * Safari (but not Chrome) clips the cancel button when the search input has | ||||
|  * padding (and `textfield` appearance). | ||||
|  */ | ||||
|  | ||||
| input[type="search"]::-webkit-search-cancel-button, | ||||
| input[type="search"]::-webkit-search-decoration { | ||||
|   -webkit-appearance: none; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Define consistent border, margin, and padding. | ||||
|  */ | ||||
|  | ||||
| fieldset { | ||||
|   border: 1px solid #c0c0c0; | ||||
|   margin: 0 2px; | ||||
|   padding: 0.35em 0.625em 0.75em; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 1. Correct `color` not being inherited in IE 8/9/10/11. | ||||
|  * 2. Remove padding so people aren't caught out if they zero out fieldsets. | ||||
|  */ | ||||
|  | ||||
| legend { | ||||
|   border: 0; /* 1 */ | ||||
|   padding: 0; /* 2 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Remove default vertical scrollbar in IE 8/9/10/11. | ||||
|  */ | ||||
|  | ||||
| textarea { | ||||
|   overflow: auto; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Don't inherit the `font-weight` (applied by a rule above). | ||||
|  * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. | ||||
|  */ | ||||
|  | ||||
| optgroup { | ||||
|   font-weight: bold; | ||||
| } | ||||
|  | ||||
| /* Tables | ||||
|    ========================================================================== */ | ||||
|  | ||||
| /** | ||||
|  * Remove most spacing between table cells. | ||||
|  */ | ||||
|  | ||||
| table { | ||||
|   border-collapse: collapse; | ||||
|   border-spacing: 0; | ||||
| } | ||||
|  | ||||
| td, | ||||
| th { | ||||
|   padding: 0; | ||||
| } | ||||
							
								
								
									
										559
									
								
								static/css/skeleton.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										559
									
								
								static/css/skeleton.css
									
									
									
									
										vendored
									
									
								
							| @@ -1,559 +0,0 @@ | ||||
| /* | ||||
| * Skeleton V2.0.4 | ||||
| * Copyright 2014, Dave Gamache | ||||
| * www.getskeleton.com | ||||
| * Free to use under the MIT license. | ||||
| * http://www.opensource.org/licenses/mit-license.php | ||||
| * 12/29/2014 | ||||
| */ | ||||
|  | ||||
|  | ||||
| /* Table of contents | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– | ||||
| - Grid | ||||
| - Base Styles | ||||
| - Typography | ||||
| - Links | ||||
| - Buttons | ||||
| - Forms | ||||
| - Lists | ||||
| - Code | ||||
| - Tables | ||||
| - Spacing | ||||
| - Utilities | ||||
| - Clearing | ||||
| - Media Queries | ||||
| - Custom Code | ||||
| */ | ||||
|  | ||||
| /* Fonts | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| @font-face { | ||||
|   font-family: 'DejaVu Sans'; | ||||
|   font-style: normal; | ||||
|   font-weight: 300; | ||||
|   font-display: swap; | ||||
|   src: | ||||
|           url('../fonts/DejaVuSans-ExtraLight.woff2') format('woff2') | ||||
| } | ||||
|  | ||||
| @font-face { | ||||
|   font-family: 'DejaVu Sans'; | ||||
|   font-style: normal; | ||||
|   font-weight: 500; | ||||
|   font-display: swap; | ||||
|   src: | ||||
|           url('../fonts/DejaVuSansMono.woff2') format('woff2') | ||||
| } | ||||
|  | ||||
| @font-face { | ||||
|   font-family: 'DejaVu Sans'; | ||||
|   font-style: normal; | ||||
|   font-weight: 800; | ||||
|   font-display: swap; | ||||
|   src: | ||||
|           url('../fonts/DejaVuSans-Bold.woff2') format('woff2') | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Grid | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| .container { | ||||
|   position: relative; | ||||
|   width: 100%; | ||||
|   max-width: 960px; | ||||
|   margin: 0 auto; | ||||
|   padding: 0 20px; | ||||
|   box-sizing: border-box; } | ||||
| .column, | ||||
| .columns { | ||||
|   width: 100%; | ||||
|   float: left; | ||||
|   box-sizing: border-box; } | ||||
|  | ||||
| /* For devices larger than 400px */ | ||||
| @media (min-width: 400px) { | ||||
|   .container { | ||||
|     width: 85%; | ||||
|     padding: 0; } | ||||
| } | ||||
|  | ||||
| /* For devices larger than 550px */ | ||||
| @media (min-width: 550px) { | ||||
|   .container { | ||||
|     width: 80%; } | ||||
|   .column, | ||||
|   .columns { | ||||
|     margin-left: 4%; } | ||||
|   .column:first-child, | ||||
|   .columns:first-child { | ||||
|     margin-left: 0; } | ||||
|  | ||||
|   .one.column, | ||||
|   .one.columns                    { width: 4.66666666667%; } | ||||
|   .two.columns                    { width: 13.3333333333%; } | ||||
|   .three.columns                  { width: 22%;            } | ||||
|   .four.columns                   { width: 30.6666666667%; } | ||||
|   .five.columns                   { width: 39.3333333333%; } | ||||
|   .six.columns                    { width: 48%;            } | ||||
|   .seven.columns                  { width: 56.6666666667%; } | ||||
|   .eight.columns                  { width: 65.3333333333%; } | ||||
|   .nine.columns                   { width: 74.0%;          } | ||||
|   .ten.columns                    { width: 82.6666666667%; } | ||||
|   .eleven.columns                 { width: 91.3333333333%; } | ||||
|   .twelve.columns                 { width: 100%; margin-left: 0; } | ||||
|  | ||||
|   .one-third.column               { width: 30.6666666667%; } | ||||
|   .two-thirds.column              { width: 65.3333333333%; } | ||||
|  | ||||
|   .one-half.column                { width: 48%; } | ||||
|  | ||||
|   /* Offsets */ | ||||
|   .offset-by-one.column, | ||||
|   .offset-by-one.columns          { margin-left: 8.66666666667%; } | ||||
|   .offset-by-two.column, | ||||
|   .offset-by-two.columns          { margin-left: 17.3333333333%; } | ||||
|   .offset-by-three.column, | ||||
|   .offset-by-three.columns        { margin-left: 26%;            } | ||||
|   .offset-by-four.column, | ||||
|   .offset-by-four.columns         { margin-left: 34.6666666667%; } | ||||
|   .offset-by-five.column, | ||||
|   .offset-by-five.columns         { margin-left: 43.3333333333%; } | ||||
|   .offset-by-six.column, | ||||
|   .offset-by-six.columns          { margin-left: 52%;            } | ||||
|   .offset-by-seven.column, | ||||
|   .offset-by-seven.columns        { margin-left: 60.6666666667%; } | ||||
|   .offset-by-eight.column, | ||||
|   .offset-by-eight.columns        { margin-left: 69.3333333333%; } | ||||
|   .offset-by-nine.column, | ||||
|   .offset-by-nine.columns         { margin-left: 78.0%;          } | ||||
|   .offset-by-ten.column, | ||||
|   .offset-by-ten.columns          { margin-left: 86.6666666667%; } | ||||
|   .offset-by-eleven.column, | ||||
|   .offset-by-eleven.columns       { margin-left: 95.3333333333%; } | ||||
|  | ||||
|   .offset-by-one-third.column, | ||||
|   .offset-by-one-third.columns    { margin-left: 34.6666666667%; } | ||||
|   .offset-by-two-thirds.column, | ||||
|   .offset-by-two-thirds.columns   { margin-left: 69.3333333333%; } | ||||
|  | ||||
|   .offset-by-one-half.column, | ||||
|   .offset-by-one-half.columns     { margin-left: 52%; } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Base Styles | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| /* NOTE | ||||
| html is set to 62.5% so that all the REM measurements throughout Skeleton | ||||
| are based on 10px sizing. So basically 1.5rem = 15px :) */ | ||||
| html { | ||||
|   font-size: 62.5%; } | ||||
| body { | ||||
|   font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ | ||||
|   line-height: 1.6; | ||||
|   font-weight: 400; | ||||
|   font-family: "DejaVu Sans", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; | ||||
|   color: #222;  | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Typography | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| h1, h2, h3, h4, h5, h6 { | ||||
|   margin-top: 0; | ||||
|   margin-bottom: 2rem; | ||||
|   font-weight: 300; } | ||||
| h1 { font-size: 4.0rem; line-height: 1.2;  letter-spacing: -.1rem;} | ||||
| h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } | ||||
| h3 { font-size: 3.0rem; line-height: 1.3;  letter-spacing: -.1rem; } | ||||
| h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } | ||||
| h5 { font-size: 1.8rem; line-height: 1.5;  letter-spacing: -.05rem; } | ||||
| h6 { font-size: 1.5rem; line-height: 1.6;  letter-spacing: 0; } | ||||
|  | ||||
| /* Larger than phablet */ | ||||
| @media (min-width: 550px) { | ||||
|   h1 { font-size: 5.0rem; } | ||||
|   h2 { font-size: 4.2rem; } | ||||
|   h3 { font-size: 3.6rem; } | ||||
|   h4 { font-size: 3.0rem; } | ||||
|   h5 { font-size: 2.4rem; } | ||||
|   h6 { font-size: 1.5rem; } | ||||
| } | ||||
|  | ||||
| p { | ||||
|   margin-top: 0; } | ||||
|  | ||||
|  | ||||
| /* Links | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| a { | ||||
|   color: #1EAEDB; } | ||||
| a:hover { | ||||
|   color: #0FA0CE; } | ||||
|  | ||||
|  | ||||
| /* Buttons | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| .button, | ||||
| button, | ||||
| input[type="submit"], | ||||
| input[type="reset"], | ||||
| input[type="button"] { | ||||
|   display: inline-block; | ||||
|   height: 38px; | ||||
|   padding: 0 30px; | ||||
|   color: #555; | ||||
|   text-align: center; | ||||
|   font-size: 11px; | ||||
|   font-weight: 600; | ||||
|   line-height: 38px; | ||||
|   letter-spacing: .1rem; | ||||
|   text-transform: uppercase; | ||||
|   text-decoration: none; | ||||
|   white-space: nowrap; | ||||
|   background-color: white; | ||||
|   border-radius: 4px; | ||||
|   border: 1px solid transparent; | ||||
|   cursor: pointer; | ||||
|   box-sizing: border-box; } | ||||
| .button:hover, | ||||
| button:hover, | ||||
| input[type="submit"]:hover, | ||||
| input[type="reset"]:hover, | ||||
| input[type="button"]:hover, | ||||
| .button:focus, | ||||
| button:focus, | ||||
| input[type="submit"]:focus, | ||||
| input[type="reset"]:focus, | ||||
| input[type="button"]:focus { | ||||
|   color: #333; | ||||
|   border-color: #888; | ||||
|   outline: 0; } | ||||
| .button.button-primary, | ||||
| button.button-primary, | ||||
| input[type="submit"].button-primary, | ||||
| input[type="reset"].button-primary, | ||||
| input[type="button"].button-primary { | ||||
|   color: #FFF; | ||||
|   background-color: red; | ||||
|   border-color: red; } | ||||
| .button.button-primary:hover, | ||||
| button.button-primary:hover, | ||||
| input[type="submit"].button-primary:hover, | ||||
| input[type="reset"].button-primary:hover, | ||||
| input[type="button"].button-primary:hover, | ||||
| .button.button-primary:focus, | ||||
| button.button-primary:focus, | ||||
| input[type="submit"].button-primary:focus, | ||||
| input[type="reset"].button-primary:focus, | ||||
| input[type="button"].button-primary:focus { | ||||
|   color: #FFF; | ||||
|   background-color: darkred; | ||||
|   border-color: darkred; } | ||||
|  | ||||
|  | ||||
| /* Forms | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| input[type="email"], | ||||
| input[type="number"], | ||||
| input[type="search"], | ||||
| input[type="text"], | ||||
| input[type="tel"], | ||||
| input[type="url"], | ||||
| input[type="password"], | ||||
| input[type="time"], | ||||
| textarea, | ||||
| select { | ||||
|   height: 38px; | ||||
|   padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ | ||||
|   background-color: #fff; | ||||
|   border: 1px solid transparent; | ||||
|   border-radius: 4px; | ||||
|   box-shadow: none; | ||||
|   box-sizing: border-box; } | ||||
| /* Removes awkward default styles on some inputs for iOS */ | ||||
| input[type="email"], | ||||
| input[type="number"], | ||||
| input[type="search"], | ||||
| input[type="text"], | ||||
| input[type="tel"], | ||||
| input[type="url"], | ||||
| input[type="password"], | ||||
| input[type="time"], | ||||
| textarea { | ||||
|   -webkit-appearance: none; | ||||
|      -moz-appearance: none; | ||||
|           appearance: none; } | ||||
| textarea { | ||||
|   min-height: 65px; | ||||
|   padding-top: 6px; | ||||
|   padding-bottom: 6px; } | ||||
| input[type="email"]:focus, | ||||
| input[type="number"]:focus, | ||||
| input[type="search"]:focus, | ||||
| input[type="text"]:focus, | ||||
| input[type="tel"]:focus, | ||||
| input[type="url"]:focus, | ||||
| input[type="password"]:focus, | ||||
| input[type="time"]:focus, | ||||
| textarea:focus, | ||||
| select:focus { | ||||
|   border: 1px solid #222; | ||||
|   outline: 0; } | ||||
| label, | ||||
| legend { | ||||
|   display: block; | ||||
|   margin-bottom: .5rem; | ||||
|   font-weight: 600; } | ||||
| fieldset { | ||||
|   padding: 0; | ||||
|   border-width: 0; } | ||||
| input[type="checkbox"], | ||||
| input[type="radio"] { | ||||
|   display: inline; } | ||||
| label > .label-body { | ||||
|   display: inline-block; | ||||
|   margin-left: .5rem; | ||||
|   font-weight: normal; } | ||||
|  | ||||
|  | ||||
| /* Lists | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| ul { | ||||
|   list-style: circle inside; } | ||||
| ol { | ||||
|   list-style: decimal inside; } | ||||
| ol, ul { | ||||
|   padding-left: 0; | ||||
|   margin-top: 0; } | ||||
| ul ul, | ||||
| ul ol, | ||||
| ol ol, | ||||
| ol ul { | ||||
|   margin: 1.5rem 0 1.5rem 3rem; | ||||
|   font-size: 90%; } | ||||
| li { | ||||
|   margin-bottom: 1rem; } | ||||
|  | ||||
|  | ||||
| /* Code | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| code { | ||||
|   padding: .2rem .5rem; | ||||
|   margin: 0 .2rem; | ||||
|   font-size: 90%; | ||||
|   white-space: nowrap; | ||||
|   background: #F1F1F1; | ||||
|   border: 1px solid #E1E1E1; | ||||
|   border-radius: 4px; } | ||||
| pre > code { | ||||
|   display: block; | ||||
|   padding: 1rem 1.5rem; | ||||
|   white-space: pre; } | ||||
|  | ||||
|  | ||||
| /* Tables | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| th, | ||||
| td { | ||||
|   padding: 12px 15px; | ||||
|   text-align: left; | ||||
|   border-bottom: 1px solid #E1E1E1; } | ||||
| th:first-child, | ||||
| td:first-child { | ||||
|   padding-left: 0; } | ||||
| th:last-child, | ||||
| td:last-child { | ||||
|   padding-right: 0; } | ||||
|  | ||||
|  | ||||
| /* Spacing | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| button, | ||||
| .button { | ||||
|   margin-bottom: 1rem; } | ||||
| input, | ||||
| textarea, | ||||
| select, | ||||
| fieldset { | ||||
|   margin-bottom: 1.5rem; } | ||||
| pre, | ||||
| blockquote, | ||||
| dl, | ||||
| figure, | ||||
| table, | ||||
| p, | ||||
| ul, | ||||
| ol, | ||||
| form { | ||||
|   margin-bottom: 2.5rem; } | ||||
|  | ||||
|  | ||||
| /* Utilities | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| .u-full-width { | ||||
|   width: 100%; | ||||
|   box-sizing: border-box; } | ||||
| .u-max-full-width { | ||||
|   max-width: 100%; | ||||
|   box-sizing: border-box; } | ||||
| .u-pull-right { | ||||
|   float: right; } | ||||
| .u-pull-left { | ||||
|   float: left; } | ||||
|  | ||||
|  | ||||
| /* Misc | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| hr { | ||||
|   margin-top: 3rem; | ||||
|   margin-bottom: 3.5rem; | ||||
|   border-width: 0; | ||||
|   border-top: 1px solid #E1E1E1; } | ||||
|  | ||||
|  | ||||
| /* Clearing | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
|  | ||||
| /* Self Clearing Goodness */ | ||||
| .container:after, | ||||
| .row:after, | ||||
| .u-cf { | ||||
|   content: ""; | ||||
|   display: table; | ||||
|   clear: both; } | ||||
|  | ||||
|  | ||||
| /* Media Queries | ||||
| –––––––––––––––––––––––––––––––––––––––––––––––––– */ | ||||
| /* | ||||
| Note: The best way to structure the use of media queries is to create the queries | ||||
| near the relevant code. For example, if you wanted to change the styles for buttons | ||||
| on small devices, paste the mobile query code up in the buttons section and style it | ||||
| there. | ||||
| */ | ||||
|  | ||||
|  | ||||
| /* Larger than mobile */ | ||||
| @media (min-width: 400px) {} | ||||
|  | ||||
| /* Larger than phablet (also point when grid becomes active) */ | ||||
| @media (min-width: 550px) {} | ||||
|  | ||||
| /* Larger than tablet */ | ||||
| @media (min-width: 750px) {} | ||||
|  | ||||
| /* Larger than desktop */ | ||||
| @media (min-width: 1000px) {} | ||||
|  | ||||
| /* Larger than Desktop HD */ | ||||
| @media (min-width: 1200px) {} | ||||
|  | ||||
| /* custom */ | ||||
| .content-center-all { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
| } | ||||
|  | ||||
| .content-center-end { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: end; | ||||
| } | ||||
|  | ||||
| .content-align-bottom { | ||||
|   display: flex; | ||||
|   align-items: flex-end; | ||||
| } | ||||
|  | ||||
| .font-base { | ||||
|   font-size: 1.2rem !important; | ||||
| } | ||||
|  | ||||
| .full-height { | ||||
|   min-height: 100vh; | ||||
|   height: 100%; | ||||
| } | ||||
|  | ||||
| .p-3 { | ||||
|     padding: 3rem; | ||||
| } | ||||
|  | ||||
| .p-1 { | ||||
|   padding: 1rem; | ||||
| } | ||||
|  | ||||
| .mb-0 { | ||||
|   margin-bottom: 0; | ||||
| } | ||||
|  | ||||
| .mb-1 { | ||||
|   margin-bottom: 1rem; | ||||
| } | ||||
|  | ||||
| .mb-3 { | ||||
|   margin-bottom: 3rem; | ||||
| } | ||||
|  | ||||
| .mr-2 { | ||||
|   margin-right: 2rem; | ||||
| } | ||||
|  | ||||
| .mt-1 { | ||||
|   margin-top: 1rem; | ||||
| } | ||||
|  | ||||
| .w-full { | ||||
|   width: 100%; | ||||
| } | ||||
|  | ||||
| .block { | ||||
|   display: block; | ||||
| } | ||||
|  | ||||
| .bold { | ||||
|   font-weight: 800; | ||||
| } | ||||
|  | ||||
| .light { | ||||
|   font-weight: 300; | ||||
| } | ||||
|  | ||||
| .bg-gray { | ||||
|   background-color: #F1F1F1; | ||||
| } | ||||
|  | ||||
| .bg-green { | ||||
|   background-color: #2b8c68; | ||||
| } | ||||
|  | ||||
| .bg-red { | ||||
|   background-color: red; | ||||
| } | ||||
|  | ||||
| .text-white { | ||||
|   color: #fff; | ||||
| } | ||||
|  | ||||
| .text-red { | ||||
|   color: red; | ||||
| } | ||||
|  | ||||
| .text-center { | ||||
|   text-align: center; | ||||
| } | ||||
|  | ||||
| .text-right { | ||||
|   text-align: right; | ||||
| } | ||||
|  | ||||
| .text-left { | ||||
|   text-align: left; | ||||
| } | ||||
|  | ||||
| .data-block[data-needed="true"] { | ||||
|   border: 3px solid red; | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 613 B | 
| @@ -1,82 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|  | ||||
|   <!-- Basic Page Needs | ||||
|   –––––––––––––––––––––––––––––––––––––––––––––––––– --> | ||||
|   <meta charset="utf-8"> | ||||
|   <title>Ro(wing)T(rips)</title> | ||||
|   <meta name="description" content=""> | ||||
|   <meta name="author" content=""> | ||||
|  | ||||
|   <!-- Mobile Specific Metas | ||||
|   –––––––––––––––––––––––––––––––––––––––––––––––––– --> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|  | ||||
|   <!-- CSS | ||||
|   –––––––––––––––––––––––––––––––––––––––––––––––––– --> | ||||
|   <link rel="stylesheet" href="/public/css/normalize.css"> | ||||
|   <link rel="stylesheet" href="/public/css/skeleton.css"> | ||||
|   <style> | ||||
| 	.button{ | ||||
| 		font-size: 25px; | ||||
| 	} | ||||
|   </style> | ||||
|  | ||||
|   <!-- Favicon | ||||
|   –––––––––––––––––––––––––––––––––––––––––––––––––– --> | ||||
|   <link rel="icon" type="image/png" href="public/images/favicon.png"> | ||||
|  | ||||
| </head> | ||||
| <body> | ||||
|  | ||||
|  | ||||
|   {% if user %} | ||||
|     <div class="bg-gray p-1 mb-3"> | ||||
|       <div class="container content-center-end"> | ||||
|         {% if user.is_admin %}	 | ||||
|           <a class="button mb-0 mr-2" href="/">🚣</a> | ||||
|           <a class="button mb-0 mr-2" href="/user">👥</a> | ||||
|         {% endif %} | ||||
|         <a class="button button-primary mb-0 font-base light" href="/logout">LOGOUT</a> | ||||
|       </div> | ||||
|     </div> | ||||
|   {% endif %} | ||||
|      | ||||
|   <!-- Primary Page Layout | ||||
|   –––––––––––––––––––––––––––––––––––––––––––––––––– --> | ||||
|   <div class="container"> | ||||
|     {% if flash %} | ||||
|       {% if flash.0 == "success" %} | ||||
|         <div class="row"> | ||||
|           <div class="one-column p-1 text-white bg-green mb-3 light text-center"> | ||||
|             {{ flash.1 }} | ||||
|           </div> | ||||
|         </div> | ||||
|       {% endif %} | ||||
|       {% if flash.0 == "error" %} | ||||
|         <div class="row"> | ||||
|           <div class="one-column p-1 text-white bg-red mb-3 bold text-center"> | ||||
|             {{ flash.1 }} | ||||
|           </div> | ||||
|         </div> | ||||
|       {% endif %} | ||||
|     {% endif %} | ||||
|  | ||||
|  | ||||
|     <div class="row"> | ||||
|       <div class="column"> | ||||
| 	{% block content %} | ||||
|  | ||||
| 	{% endblock content %} | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|  | ||||
|  | ||||
| <!-- End Document | ||||
|   –––––––––––––––––––––––––––––––––––––––––––––––––– --> | ||||
| </body> | ||||
| </html> | ||||
|  | ||||
|  | ||||
| @@ -1,204 +0,0 @@ | ||||
| {% extends "base" %} | ||||
| {% block content %} | ||||
|  | ||||
| <h1 class="bold">Ausfahrten</h1> | ||||
| {% for day_with_trip in data %} | ||||
| 	{% set day = day_with_trip.day %} | ||||
| 	{% set day_string = day.day | date(format="%Y-%m-%d") %} | ||||
| 	{% set_global default_trips = [] %} | ||||
| 	{% set_global indep_trips = [] %} | ||||
| 	{% for trip in day_with_trip.trips %} | ||||
| 		{% if trip.trip.begin or trip.trip.cox_id %} | ||||
| 			{% set_global indep_trips = indep_trips | concat(with=trip) %} | ||||
| 		{% else %} | ||||
| 			{% set_global default_trips = default_trips | concat(with=trip) %} | ||||
| 		{% endif %} | ||||
| 	{% endfor %} | ||||
|  | ||||
|  | ||||
|   {% set cox = default_trips | filter(attribute="user.is_cox", value=true) %} | ||||
|   {% set amount_cox = cox | length %} | ||||
|   {% set cox_needed = amount_cox < day.planned_amount_cox %} | ||||
|  | ||||
|   <div class="bg-gray p-3 mb-1 data-block" data-needed="{{ cox_needed}}"> | ||||
|     <strong class="block">{{ day.day | date(format="%A, %d.%m.%Y", locale="de_AT")}}</strong> | ||||
|  | ||||
|       {% if user.is_cox %} | ||||
|         <details class="text-right"> | ||||
|           <summary class="button">NEUE AUSFAHRT</summary> | ||||
|           <form method="post" class="text-left" action="/register"> | ||||
|             <input type="hidden" name="_method" value="put" /> | ||||
|             <input type="hidden" name="day" value="{{ day_string }}" /> | ||||
|             <div class="row content-align-bottom"> | ||||
|                 <input class="u-full-width" type="hidden" id="name" name="name" value="{{ user.name }}" /> | ||||
|               <div class="six columns"> | ||||
|                 <label for="time">Time</label> | ||||
|                 <input class="u-full-width" type="text" id="time" name="time" value="17:00" /> | ||||
|               </div> | ||||
|               <div class="six columns"> | ||||
|                 <input class="button-primary" type="submit" value="Speichern"> | ||||
|               </div> | ||||
|             </div> | ||||
|           </form> | ||||
|         </details> | ||||
|       {% endif %} | ||||
|     {% if user.is_admin %} | ||||
| 		<details class="text-right" style="margin-top: -3rem;"> | ||||
| 			<summary class="button">✎</summary> | ||||
| 			<form method="post" class="text-left" action="/day"> | ||||
| 				<input type="hidden" name="_method" value="put" /> | ||||
| 				<input type="hidden" name="day" value="{{ day_string }}" /> | ||||
| 				<div class="row content-align-bottom"> | ||||
| 					<div class="three columns"> | ||||
| 						<label for="planned_amount_cox">Steuerpersonen</label> | ||||
| 						<input class="u-full-width" type="number" id="planned_amount_cox" name="planned_amount_cox" value="{{ day.planned_amount_cox }}"> | ||||
| 					</div> | ||||
| 					<div class="three columns"> | ||||
| 						<label for="planned_starting_time">Abfahrtszeit</label> | ||||
| 						<input class="u-full-width" type="time" id="planned_starting_time" name="planned_starting_time" value="{% if day.planned_starting_time %}{{ day.planned_starting_time }}{% else %}17:00{%endif%}"> | ||||
| 					</div> | ||||
| 					<div class="three columns"> | ||||
| 						<label for="open_registration">Registrierung offen</label> | ||||
| 						<input class="u-full-width" type="checkbox" id="open_registration" name="open_registration" {% if not day or day.open_registration %} checked="true" {% endif %}/> | ||||
| 					</div> | ||||
| 					<div class="three columns"> | ||||
| 						<input class="button-primary" type="submit" value="Speichern"> | ||||
| 					</div> | ||||
|  | ||||
| 				</div> | ||||
| 			</form> | ||||
| 		</details> | ||||
| 	{% endif %} | ||||
|  | ||||
|     {% if day.planned_amount_cox > 0%} | ||||
|       {% set rowers = default_trips | filter(attribute="user.is_cox", value=false) | sort(attribute="trip.created") %} | ||||
|       {% if cox_needed %} | ||||
|         {% set cox_left = day.planned_amount_cox - amount_cox %} | ||||
|         <div class="block text-red">Es {{ cox_left | pluralize(singular="wird", plural="werden")}} noch {{ cox_left }} Steuerperson{{ cox_left | pluralize(plural="en")}} gesucht!</div> | ||||
|       {% endif %} | ||||
|       {% set_global user_registered = false %} | ||||
|       <strong class="block mt-1">Abfahrtszeit: {{ day.planned_starting_time }} Uhr</strong> | ||||
|  | ||||
| 		  <div style="max-width: 75%">{{ default_trips | length }} angemeldete Person{{ default_trips | length | pluralize(plural="en") }}: {{ cox | length }} Steuerperson{{ cox | length | pluralize(plural="en") }} ({% for c in cox %}{{ c.user.name }} {% if c.user.name == user.name %} | ||||
|       {% set_global user_registered = true %} | ||||
|           <form method="post" action="/register"> | ||||
|             <input type="hidden" name="_method" value="delete" /> | ||||
|             <input type="hidden" name="id" value="{{ c.trip.id }}" /> | ||||
|             <input type="submit" value="Abmelden" style="float: left;" /> | ||||
|           </form> | ||||
| 		  {% endif %} {% endfor %}), {{ rowers | length }} Ruderer:</div> | ||||
|  | ||||
|       <ol style="max-width: 75%"> | ||||
|       {% for r in rowers %} | ||||
|         <li> | ||||
|           {{ r.user.name }} (angemeldet seit {{ r.trip.created | date(format="%d.%m. %H:%M", timezone="Europe/Vienna") }}) | ||||
|           {% if r.user.name == user.name %} | ||||
|       {% set_global user_registered = true %} | ||||
|           <form method="post" action="/register"> | ||||
|             <input type="hidden" name="_method" value="delete" /> | ||||
|             <input type="hidden" name="id" value="{{ r.trip.id }}" /> | ||||
|             <input type="submit" value="Abmelden" /> | ||||
|              | ||||
|           </form> | ||||
|           {% endif %} | ||||
|         </li> | ||||
|       {% endfor %} | ||||
|       </ol> | ||||
|  | ||||
|       {% if day.open_registration or user.is_cox %} | ||||
|       	{% if not user_registered or user.add_different_user %} | ||||
| 		<details class="text-right" style="margin-top: -6rem;"> | ||||
| 		  <summary class="button">+</summary> | ||||
| 		  <form method="post" class="text-left" action="/register"> | ||||
| 		    <input type="hidden" name="_method" value="put" /> | ||||
| 		    <input type="hidden" name="day" value="{{ day_string }}" /> | ||||
| 		    <div class="row content-align-bottom"> | ||||
| 		      <div class="six columns"> | ||||
| 		      	{% if user.add_different_user %} | ||||
| 				<label for="name">Name</label> | ||||
| 				<input class="u-full-width" type="text" id="name" name="name" value="{{ user.name }}" /> | ||||
| 		      	{% else %} | ||||
| 				<input class="u-full-width" type="hidden" id="name" name="name" value="{{ user.name }}" /> | ||||
| 		      	{% endif %} | ||||
| 		      </div> | ||||
| 		      <div class="six columns"> | ||||
| 			<input class="button-primary" type="submit" value="Speichern"> | ||||
| 		      </div> | ||||
| 		    </div> | ||||
| 		  </form> | ||||
| 		</details> | ||||
| 	{% else %} | ||||
| 	{% endif %} | ||||
|       {% else %} | ||||
|         Anmeldung an diesem Tag leider nicht möglich (zB bei USI Kursen) | ||||
|       {% endif %} | ||||
|     {% endif %} | ||||
| 	{% for trip in indep_trips %} | ||||
|       		{% set_global user_registered = false %} | ||||
| 		{% if trip.trip.begin %} | ||||
| 			{{trip.user.name}} @ {{trip.trip.begin}} | ||||
| 			{% set rowers = indep_trips | filter(attribute="trip.cox_id", value=trip.trip.id) | sort(attribute="trip.created")%} | ||||
| 			{% if trip.user.name == user.name and rowers | length == 0 %} | ||||
|       				{% set_global user_registered = true %} | ||||
| 				  <form method="post" action="/register"> | ||||
| 				    <input type="hidden" name="_method" value="delete" /> | ||||
| 				    <input type="hidden" name="id" value="{{ trip.trip.id }}" /> | ||||
| 				    <input type="submit" value="Abmelden" style="float: left;" /> | ||||
| 				  </form> | ||||
| 			{% endif %} | ||||
| 			: | ||||
| 			<ol> | ||||
| 				{% for r in rowers %} | ||||
| 					<li> | ||||
| 					  {{ r.user.name }} (angemeldet seit {{ r.trip.created | date(format="%d.%m. %H:%M", timezone="Europe/Vienna") }}) | ||||
| 					  {% if r.user.name == user.name %} | ||||
|       					  {% set_global user_registered = true %} | ||||
| 					  <form method="post" action="/register"> | ||||
| 					    <input type="hidden" name="_method" value="delete" /> | ||||
| 					    <input type="hidden" name="id" value="{{ r.trip.id }}" /> | ||||
| 					    <input type="submit" value="Abmelden" /> | ||||
| 					     | ||||
| 					  </form> | ||||
| 					  {% endif %} | ||||
| 					</li> | ||||
| 				{% endfor %} | ||||
| 			</ol> | ||||
| 			{% if not user_registered or user.add_different_user %} | ||||
| 			<details class="text-right"> | ||||
| 			  <summary class="button">+</summary> | ||||
| 			  <form method="post" class="text-left" action="/register"> | ||||
| 			    <input type="hidden" name="_method" value="put" /> | ||||
| 			    <input type="hidden" name="day" value="{{ day_string }}" /> | ||||
| 			    <input type="hidden" name="cox_id" value="{{ trip.trip.id }}" /> | ||||
| 			    <div class="row content-align-bottom"> | ||||
| 			      <div class="six columns"> | ||||
|  | ||||
| 				{% if user.add_different_user %} | ||||
| 					<label for="name">Name</label> | ||||
| 					<input class="u-full-width" type="text" id="name" name="name" value="{{ user.name }}" /> | ||||
| 				{% else %} | ||||
| 					<input class="u-full-width" type="hidden" id="name" name="name" value="{{ user.name }}" /> | ||||
| 				{% endif %} | ||||
| 			      </div> | ||||
| 			      <div class="six columns"> | ||||
| 				<input class="button-primary" type="submit" value="Speichern"> | ||||
| 			      </div> | ||||
| 			    </div> | ||||
| 			  </form> | ||||
| 			</details> | ||||
| 			{% endif %} | ||||
| 		{% endif %} | ||||
| 	{% endfor %} | ||||
|  | ||||
|  | ||||
|  | ||||
|   </div> | ||||
| {% endfor %} | ||||
|  | ||||
|  | ||||
| {% if user.is_cox %} | ||||
| 	<a class="button button-primary light font-base mb-3" href="/?all">Alle heurigen Ausfahrten anzeigen</a> | ||||
| {% endif %} | ||||
|  | ||||
| {% endblock content %} | ||||
|  | ||||
| @@ -1,18 +0,0 @@ | ||||
| {% extends "base" %} | ||||
| {% block content %} | ||||
|  | ||||
| <div class="content-center-all full-height"> | ||||
|   <form action="/name" method="post" class="p-3 bg-gray"> | ||||
|     <input type="hidden" name="_method" value="put" /> | ||||
|  | ||||
|     <label for="name">Bitte deinen Namen eingeben</label> | ||||
|     <input type="text" class="w-full" id="name" name="name"/> | ||||
|  | ||||
|     <label for="pw">(Optional) Passwort eingeben</label> | ||||
|     <input type="password" class="w-full" id="pw" name="pw"/> | ||||
|  | ||||
|     <input type="submit" class="button button-primary mb-0 d-block w-full b" value="Weiter"/> | ||||
|   </form> | ||||
| </div> | ||||
| {% endblock content %} | ||||
|  | ||||
| @@ -1,7 +0,0 @@ | ||||
| <details> | ||||
| 	<summary class="button">+</summary> | ||||
| 	<form method="post" action="/register"> | ||||
| 		<input type="hidden" name="_method" value="put" /> | ||||
|  | ||||
| 	</form> | ||||
| </details> | ||||
| @@ -1,46 +0,0 @@ | ||||
| {% extends "base" %} | ||||
| {% block content %} | ||||
|  | ||||
| <table class="u-full-width"> | ||||
|   <thead> | ||||
|     <tr> | ||||
|       <th>Name</th> | ||||
|       <th>Pw</th> | ||||
|       <th>Add Different User</th> | ||||
|       <th>Cox</th> | ||||
|       <th>Admin</th> | ||||
|       <th>Action</th> | ||||
|     </tr> | ||||
|   </thead> | ||||
|   <tbody> | ||||
|   {% for user in users %} | ||||
|   	<tr> | ||||
| 		<form action="/user/{{ user.id }}" method="post"> | ||||
| 			<input type="hidden" name="_method" value="put" /> | ||||
| 			<td>{{user.name}}</td> | ||||
| 			<td> | ||||
| 				{% if user.pw %} | ||||
| 				<input type="checkbox" checked disabled> | ||||
| 				{% endif %} | ||||
| 				<input type="password" name="pw" /> | ||||
| 			</td> | ||||
| 			<td> | ||||
| 				<input type="checkbox" name="add_different_user" {% if user.add_different_user %} checked="true"{% endif %} | ||||
| 			</td> | ||||
| 			<td> | ||||
| 				<input type="checkbox" name="is_cox" {% if user.is_cox %} checked="true"{% endif %} | ||||
| 			</td> | ||||
| 			<td> | ||||
| 				<input type="checkbox" name="is_admin" {% if user.is_admin %} checked="true"{% endif %} | ||||
| 			</td> | ||||
| 			<td> | ||||
| 				<input class="button-primary" type="submit" value="Speichern"> | ||||
| 			</td> | ||||
| 		</form> | ||||
| 	</tr> | ||||
|   {% endfor %} | ||||
|   </tbody> | ||||
| </table> | ||||
|  | ||||
| {% endblock content %} | ||||
|  | ||||
		Reference in New Issue
	
	Block a user