clean repo
This commit is contained in:
parent
13d24ec766
commit
f01b073654
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[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 = { version = "0.5.0-rc.2", features = ["secrets"]}
|
||||||
rocket_dyn_templates = { version = "0.1.0-rc.2", features= ["tera"] }
|
rocket_dyn_templates = { version = "0.1.0-rc.2", features= ["tera"] }
|
||||||
chrono = { version = "0.4", features = ["serde"]}
|
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]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
mod models;
|
|
||||||
mod rest;
|
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
async fn rocket() -> _ {
|
async fn rocket() -> _ {
|
||||||
env_logger::init();
|
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 %}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user