Merge pull request 'rebase to rowt' (#12) from upd into main
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled

Reviewed-on: #12
This commit is contained in:
philipp 2025-04-28 22:41:32 +02:00
commit f8b16b2a95
11 changed files with 238 additions and 63 deletions

View File

@ -2,7 +2,7 @@
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w==" secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
rss_key = "rss-key-for-ci" rss_key = "rss-key-for-ci"
limits = { file = "10 MiB", data-form = "10 MiB"} limits = { file = "10 MiB", data-form = "10 MiB"}
smtp_pw = "8kIjlLH79Ky6D3j" smtp_pw = "my-smtp-password"
usage_log_path = "./usage.txt" usage_log_path = "./usage.txt"
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5" openweathermap_key = "openweather-key"
wordpress_key = "pw-to-allow-sending-notifications" wordpress_key = "pw-to-allow-sending-notifications"

94
doc/nextcloud-notes.md Normal file
View File

@ -0,0 +1,94 @@
# Nextcloud integration
- Based on [this plugin](https://github.com/nextcloud/user_external)
- Install that plugin via web
- Connect to server, enter nextcloud-docker-image: `docker exec -it nextcloud-aio-nextcloud bash`
- Adapt `/var/www/html/custom_apps/user_external/lib/BasicAuth.php` to switch from BasicAuth to RowtAuth:
```php
<?php
/**
* Copyright (c) 2019 Lutz Freitag <lutz.freitag@gottliebtfreitag.de>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace OCA\UserExternal;
class BasicAuth extends Base {
private $authUrl;
public function __construct($authUrl) {
parent::__construct($authUrl);
$this->authUrl = $authUrl;
}
/**
* Check if the password is correct without logging in the user
*
* @param string $uid The username
* @param string $password The password
*
* @return true/false
*/
public function checkPassword($uid, $password) {
// Prepare POST data with credentials
$postData = http_build_query([
'name' => $uid,
'password' => $password
]);
// Create context with POST method
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => $postData,
'follow_location' => 0
]
]);
// Get the content of the response
$content = @file_get_contents($this->authUrl, false, $context);
if ($content === false) {
\OC::$server->getLogger()->error(
'ERROR: Failed to get content from Auth Url: '.$this->authUrl,
['app' => 'user_external']
);
return false;
}
// Check if the content is "SUCC"
if (trim($content) === "SUCC") {
$this->storeUser($uid);
return $uid;
}
return false;
}
}
```
- In `/var/www/html/config/config.php` add this:
```
'user_backends' =>
array (
0 =>
array (
'class' => '\\OCA\\UserExternal\\BasicAuth',
'arguments' =>
array (
0 => 'https://app.rudernlinz.at/nxauth',
),
),
),
```
- In `/var/www/html/config/config.php` add this `'skeletondirectory' => '',` to disable default folders for new users
- To automatically add users to a group (e.g. `vorstand`), use the `Auto Groups` plugin
- Shared folders are not shared with new members due to [this bug](https://github.com/nextcloud/server/issues/25062#issuecomment-766445043)
- Find DB config: `docker exec nextcloud-aio-database env | grep POSTGRES`
- Workaround: Connect to docker-db: `docker exec -it nextcloud-aio-database bash`
- Connect to db: `psql -U nextcloud -d nextcloud_database`
- (with `\l` you see all dbs)
- Connect to nextcloud db: `\c nextcloud_database`
- Do query from issue: `UPDATE oc_share SET accepted = 1 WHERE share_type = 1;`

View File

@ -120,18 +120,69 @@ test.describe("cox can edit trips", () => {
}); });
test("call off trip", async () => { test("call off trip", async () => {
// Someone registers...
await sharedPage.goto("/auth/logout");
await sharedPage.goto("/auth");
await sharedPage.getByPlaceholder("Name").click();
await sharedPage.getByPlaceholder("Name").fill("rower");
await sharedPage.getByPlaceholder("Name").press("Tab");
await sharedPage.getByPlaceholder("Passwort").fill("rower");
await sharedPage.getByPlaceholder("Passwort").press("Enter");
await sharedPage.goto("/"); await sharedPage.goto("/");
await sharedPage.getByRole('link', { name: 'Mitrudern' }).nth(1).click();
// Login as cox again
await sharedPage.goto("/auth/logout");
await sharedPage.goto("/auth");
await sharedPage.getByPlaceholder("Name").click();
await sharedPage.getByPlaceholder("Name").fill("cox");
await sharedPage.getByPlaceholder("Name").press("Tab");
await sharedPage.getByPlaceholder("Passwort").fill("cox");
await sharedPage.getByPlaceholder("Passwort").press("Enter");
await sharedPage.goto("/");
// ... now I can cancel trip
await sharedPage.getByRole("link", { name: "Details" }).nth(1).click(); await sharedPage.getByRole("link", { name: "Details" }).nth(1).click();
await expect(sharedPage.locator("#sidebar")).toContainText( await sharedPage.getByRole("button", { name: "Ausfahrt absagen" }).click();
"Freie Plätze: 3",
);
await sharedPage.getByRole("spinbutton").click();
await sharedPage.getByRole("spinbutton").fill("0");
await sharedPage.getByRole("button", { name: "Speichern" }).click();
await expect(sharedPage.locator("body")).toContainText( await expect(sharedPage.locator("body")).toContainText(
"Ausfahrt erfolgreich aktualisiert.", "Ausfahrt erfolgreich aktualisiert.",
); );
await expect(sharedPage.locator("body")).toContainText("(Absage cox)"); await expect(sharedPage.locator("body")).toContainText("(Absage cox)");
// Done with the test -> cancel the cancellation of the trip, otherwise the afterAll function below fails
await sharedPage.getByRole("link", { name: "Details" }).nth(1).click();
await sharedPage.getByRole("spinbutton").click();
await sharedPage.getByRole("spinbutton").fill("3");
await sharedPage.getByRole("button", { name: "Speichern" }).click();
// deregistering
await sharedPage.goto("/auth/logout");
await sharedPage.goto("/auth");
await sharedPage.getByPlaceholder("Name").click();
await sharedPage.getByPlaceholder("Name").fill("rower");
await sharedPage.getByPlaceholder("Name").press("Tab");
await sharedPage.getByPlaceholder("Passwort").fill("rower");
await sharedPage.getByPlaceholder("Passwort").press("Enter");
await sharedPage.goto("/");
await sharedPage.getByRole('link', { name: 'Abmelden' }).click();
// now cox can delete trip again in afterAll
await sharedPage.goto("/auth/logout");
await sharedPage.goto("/auth");
await sharedPage.getByPlaceholder("Name").click();
await sharedPage.getByPlaceholder("Name").fill("cox");
await sharedPage.getByPlaceholder("Name").press("Tab");
await sharedPage.getByPlaceholder("Passwort").fill("cox");
await sharedPage.getByPlaceholder("Passwort").press("Enter");
}); });
test.afterAll(async () => { test.afterAll(async () => {

View File

@ -11,7 +11,6 @@ use sqlx::{FromRow, Row, SqlitePool};
use super::{ use super::{
log::Log, log::Log,
notification::Notification, notification::Notification,
role::Role,
tripdetails::TripDetails, tripdetails::TripDetails,
triptype::TripType, triptype::TripType,
user::{EventUser, User}, user::{EventUser, User},
@ -34,11 +33,13 @@ pub struct Event {
} }
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct EventWithUserAndTriptype { pub struct EventWithDetails {
#[serde(flatten)] #[serde(flatten)]
pub event: Event, pub event: Event,
trip_type: Option<TripType>, trip_type: Option<TripType>,
tripdetails: TripDetails,
cox_needed: bool, cox_needed: bool,
cancelled: bool,
cox: Vec<Registration>, cox: Vec<Registration>,
rower: Vec<Registration>, rower: Vec<Registration>,
} }
@ -116,6 +117,12 @@ pub struct EventUpdate<'a> {
pub trip_type_id: Option<i64>, pub trip_type_id: Option<i64>,
} }
impl EventUpdate<'_> {
fn cancelled(&self) -> bool {
self.max_people == -1
}
}
impl Event { impl Event {
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> { pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!( sqlx::query_as!(
@ -134,16 +141,13 @@ WHERE planned_event.id like ?
.ok() .ok()
} }
pub async fn get_pinned_for_day( pub async fn get_pinned_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<EventWithDetails> {
db: &SqlitePool,
day: NaiveDate,
) -> Vec<EventWithUserAndTriptype> {
let mut events = Self::get_for_day(db, day).await; let mut events = Self::get_for_day(db, day).await;
events.retain(|e| e.event.always_show); events.retain(|e| e.event.always_show);
events events
} }
pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<EventWithUserAndTriptype> { pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<EventWithDetails> {
let day = format!("{day}"); let day = format!("{day}");
let events = sqlx::query_as!( let events = sqlx::query_as!(
Event, Event,
@ -164,10 +168,15 @@ WHERE day=?",
if let Some(trip_type_id) = event.trip_type_id { if let Some(trip_type_id) = event.trip_type_id {
trip_type = TripType::find_by_id(db, trip_type_id).await; trip_type = TripType::find_by_id(db, trip_type_id).await;
} }
ret.push(EventWithUserAndTriptype { let tripdetails = TripDetails::find_by_id(db, event.trip_details_id)
.await
.expect("db constraints");
ret.push(EventWithDetails {
cox_needed: event.planned_amount_cox > cox.len() as i64, cox_needed: event.planned_amount_cox > cox.len() as i64,
cox, cox,
rower: Registration::all_rower(db, event.trip_details_id).await, rower: Registration::all_rower(db, event.trip_details_id).await,
cancelled: tripdetails.cancelled(),
tripdetails,
event, event,
trip_type, trip_type,
}); });
@ -313,7 +322,7 @@ WHERE trip_details.id=?
.unwrap(); //Okay, as planned_event can only be created with proper DB backing .unwrap(); //Okay, as planned_event can only be created with proper DB backing
let tripdetails = self.trip_details(db).await; let tripdetails = self.trip_details(db).await;
let was_already_cancelled = tripdetails.max_people == 0; let was_already_cancelled = tripdetails.cancelled();
sqlx::query!( sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ?, trip_type_id = ? WHERE id = ?", "UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ?, trip_type_id = ? WHERE id = ?",
@ -338,7 +347,7 @@ WHERE trip_details.id=?
.await; .await;
} }
if update.max_people == 0 && !was_already_cancelled { if update.cancelled() && !was_already_cancelled {
let coxes = Registration::all_cox(db, self.id).await; let coxes = Registration::all_cox(db, self.id).await;
for user in coxes { for user in coxes {
if let Some(user) = User::find_by_name(db, &user.name).await { if let Some(user) = User::find_by_name(db, &user.name).await {
@ -387,7 +396,7 @@ WHERE trip_details.id=?
} }
} }
} }
if update.max_people > 0 && was_already_cancelled { if !update.cancelled() && was_already_cancelled {
Notification::delete_by_action( Notification::delete_by_action(
db, db,
&format!("remove_user_trip_with_trip_details_id:{}", tripdetails.id), &format!("remove_user_trip_with_trip_details_id:{}", tripdetails.id),
@ -425,7 +434,7 @@ WHERE trip_details.id=?
} }
pub fn is_cancelled(&self) -> bool { pub fn is_cancelled(&self) -> bool {
self.max_people == 0 self.max_people == -1
} }
pub async fn get_ics_feed(db: &SqlitePool) -> String { pub async fn get_ics_feed(db: &SqlitePool) -> String {
@ -442,10 +451,16 @@ WHERE trip_details.id=?
pub(crate) async fn get_vevent(self, db: &SqlitePool) -> ics::Event { pub(crate) async fn get_vevent(self, db: &SqlitePool) -> ics::Event {
let mut vevent = ics::Event::new(format!("event-{}@ruad.at", self.id), "19900101T180000"); let mut vevent = ics::Event::new(format!("event-{}@ruad.at", self.id), "19900101T180000");
let time_str = self.planned_starting_time.replace(':', "");
let formatted_time = if time_str.len() == 3 {
format!("0{}", time_str)
} else {
time_str.clone() // TODO: remove again
};
vevent.push(DtStart::new(format!( vevent.push(DtStart::new(format!(
"{}T{}00", "{}T{}00",
self.day.replace('-', ""), self.day.replace('-', ""),
self.planned_starting_time.replace(':', "") formatted_time
))); )));
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M") let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")

View File

@ -6,8 +6,8 @@ use waterlevel::WaterlevelDay;
use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD; use crate::AMOUNT_DAYS_TO_SHOW_TRIPS_AHEAD;
use self::{ use self::{
event::{Event, EventWithUserAndTriptype}, event::{Event, EventWithDetails},
trip::{Trip, TripWithUserAndType}, trip::{Trip, TripWithDetails},
waterlevel::Waterlevel, waterlevel::Waterlevel,
weather::Weather, weather::Weather,
}; };
@ -28,8 +28,8 @@ pub mod weather;
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct Day { pub struct Day {
day: NaiveDate, day: NaiveDate,
events: Vec<EventWithUserAndTriptype>, events: Vec<EventWithDetails>,
trips: Vec<TripWithUserAndType>, trips: Vec<TripWithDetails>,
is_pinned: bool, is_pinned: bool,
regular_sees_this_day: bool, regular_sees_this_day: bool,
max_waterlevel: Option<WaterlevelDay>, max_waterlevel: Option<WaterlevelDay>,

View File

@ -284,7 +284,7 @@ mod test {
let cancel_update = EventUpdate { let cancel_update = EventUpdate {
name: &event.name, name: &event.name,
planned_amount_cox: event.planned_amount_cox as i32, planned_amount_cox: event.planned_amount_cox as i32,
max_people: 0, max_people: -1,
notes: event.notes.as_deref(), notes: event.notes.as_deref(),
always_show: event.always_show, always_show: event.always_show,
is_locked: event.is_locked, is_locked: event.is_locked,

View File

@ -30,11 +30,12 @@ pub struct Trip {
} }
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct TripWithUserAndType { pub struct TripWithDetails {
#[serde(flatten)] #[serde(flatten)]
pub trip: Trip, pub trip: Trip,
pub rower: Vec<Registration>, pub rower: Vec<Registration>,
trip_type: Option<TripType>, trip_type: Option<TripType>,
cancelled: bool,
} }
pub struct TripUpdate<'a> { pub struct TripUpdate<'a> {
@ -46,7 +47,13 @@ pub struct TripUpdate<'a> {
pub is_locked: bool, pub is_locked: bool,
} }
impl TripWithUserAndType { impl<'a> TripUpdate<'a> {
fn cancelled(&self) -> bool {
self.max_people == -1
}
}
impl TripWithDetails {
pub async fn from(db: &SqlitePool, trip: Trip) -> Self { pub async fn from(db: &SqlitePool, trip: Trip) -> Self {
let mut trip_type = None; let mut trip_type = None;
if let Some(trip_type_id) = trip.trip_type_id { if let Some(trip_type_id) = trip.trip_type_id {
@ -54,8 +61,9 @@ impl TripWithUserAndType {
} }
Self { Self {
rower: Registration::all_rower(db, trip.trip_details_id.unwrap()).await, rower: Registration::all_rower(db, trip.trip_details_id.unwrap()).await,
trip,
trip_type, trip_type,
cancelled: trip.is_cancelled(),
trip,
} }
} }
} }
@ -129,10 +137,17 @@ WHERE trip_details.id=?
pub(crate) async fn get_vevent(self, user: &User) -> ics::Event { pub(crate) async fn get_vevent(self, user: &User) -> ics::Event {
let mut vevent = ics::Event::new(format!("trip-{}@ruad.at", self.id), "19900101T180000"); let mut vevent = ics::Event::new(format!("trip-{}@ruad.at", self.id), "19900101T180000");
let time_str = self.planned_starting_time.replace(':', "");
let formatted_time = if time_str.len() == 3 {
format!("0{}", time_str)
} else {
time_str
};
vevent.push(DtStart::new(format!( vevent.push(DtStart::new(format!(
"{}T{}00", "{}T{}00",
self.day.replace('-', ""), self.day.replace('-', ""),
self.planned_starting_time.replace(':', "") formatted_time
))); )));
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M") let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")
@ -234,7 +249,7 @@ WHERE trip.id=?
return Err(CoxHelpError::DetailsLocked); return Err(CoxHelpError::DetailsLocked);
} }
if event.max_people == 0 { if event.is_cancelled() {
return Err(CoxHelpError::CanceledEvent); return Err(CoxHelpError::CanceledEvent);
} }
@ -251,12 +266,12 @@ WHERE trip.id=?
} }
} }
pub async fn get_for_today(db: &SqlitePool) -> Vec<TripWithUserAndType> { pub async fn get_for_today(db: &SqlitePool) -> Vec<TripWithDetails> {
let today = Local::now().date_naive(); let today = Local::now().date_naive();
Self::get_for_day(db, today).await Self::get_for_day(db, today).await
} }
pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<TripWithUserAndType> { pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<TripWithDetails> {
let day = format!("{day}"); let day = format!("{day}");
let trips = sqlx::query_as!( let trips = sqlx::query_as!(
Trip, Trip,
@ -275,7 +290,7 @@ WHERE day=?
let mut ret = Vec::new(); let mut ret = Vec::new();
for trip in trips { for trip in trips {
ret.push(TripWithUserAndType::from(db, trip).await); ret.push(TripWithDetails::from(db, trip).await);
} }
ret ret
} }
@ -298,9 +313,9 @@ WHERE day=?
}; };
let tripdetails = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); let tripdetails = TripDetails::find_by_id(db, trip_details_id).await.unwrap();
let was_already_cancelled = tripdetails.max_people == 0; let was_already_cancelled = tripdetails.cancelled();
let is_locked = if update.max_people == 0 { let is_locked = if update.cancelled() {
false false
} else { } else {
update.is_locked update.is_locked
@ -318,10 +333,8 @@ WHERE day=?
.await .await
.unwrap(); //Okay, as trip_details can only be created with proper DB backing .unwrap(); //Okay, as trip_details can only be created with proper DB backing
if update.max_people == 0 && !was_already_cancelled { if update.cancelled() && !was_already_cancelled {
let rowers = TripWithUserAndType::from(db, update.trip.clone()) let rowers = TripWithDetails::from(db, update.trip.clone()).await.rower;
.await
.rower;
for user in rowers { for user in rowers {
if let Some(user) = User::find_by_name(db, &user.name).await { if let Some(user) = User::find_by_name(db, &user.name).await {
let notes = match update.notes { let notes = match update.notes {
@ -357,7 +370,7 @@ WHERE day=?
.await; .await;
} }
if update.max_people > 0 && was_already_cancelled { if !update.cancelled() && was_already_cancelled {
Notification::delete_by_action( Notification::delete_by_action(
db, db,
&format!("remove_user_trip_with_trip_details_id:{}", trip_details_id), &format!("remove_user_trip_with_trip_details_id:{}", trip_details_id),
@ -445,14 +458,14 @@ WHERE day=?
pub(crate) async fn get_pinned_for_day( pub(crate) async fn get_pinned_for_day(
db: &sqlx::Pool<sqlx::Sqlite>, db: &sqlx::Pool<sqlx::Sqlite>,
day: NaiveDate, day: NaiveDate,
) -> Vec<TripWithUserAndType> { ) -> Vec<TripWithDetails> {
let mut trips = Self::get_for_day(db, day).await; let mut trips = Self::get_for_day(db, day).await;
trips.retain(|e| e.trip.always_show); trips.retain(|e| e.trip.always_show);
trips trips
} }
fn is_cancelled(&self) -> bool { fn is_cancelled(&self) -> bool {
self.max_people == 0 self.max_people == -1
} }
} }

View File

@ -6,7 +6,7 @@ use sqlx::{FromRow, SqlitePool};
use super::{ use super::{
notification::Notification, notification::Notification,
trip::{Trip, TripWithUserAndType}, trip::{Trip, TripWithDetails},
triptype::TripType, triptype::TripType,
}; };
@ -95,7 +95,7 @@ WHERE day = ? AND planned_starting_time = ?
} }
pub fn cancelled(&self) -> bool { pub fn cancelled(&self) -> bool {
self.max_people == 0 self.max_people == -1
} }
/// This function is called when a person registers to a trip or when the cox changes the /// This function is called when a person registers to a trip or when the cox changes the
@ -138,7 +138,7 @@ WHERE day = ? AND planned_starting_time = ?
// This trip_details belongs to a planned_event, no need to do anything // This trip_details belongs to a planned_event, no need to do anything
continue; continue;
}; };
let pot_coxes = TripWithUserAndType::from(db, trip.clone()).await; let pot_coxes = TripWithDetails::from(db, trip.clone()).await;
let pot_coxes = pot_coxes.rower; let pot_coxes = pot_coxes.rower;
for user in pot_coxes { for user in pot_coxes {
let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap(); let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
@ -196,7 +196,7 @@ WHERE day = ? AND planned_starting_time = ?
.fetch_one(db) .fetch_one(db)
.await .await
.unwrap(); //TODO: fixme .unwrap(); //TODO: fixme
let amount_currently_registered = i64::from(amount_currently_registered.count); let amount_currently_registered = amount_currently_registered.count;
amount_currently_registered >= self.max_people amount_currently_registered >= self.max_people
} }

View File

@ -3,7 +3,7 @@ use sqlx::{FromRow, SqlitePool};
use super::{ use super::{
notification::Notification, notification::Notification,
trip::{Trip, TripWithUserAndType}, trip::{Trip, TripWithDetails},
tripdetails::TripDetails, tripdetails::TripDetails,
user::{SteeringUser, User}, user::{SteeringUser, User},
}; };
@ -158,7 +158,7 @@ impl UserTrip {
.unwrap() .unwrap()
.cancelled() .cancelled()
{ {
let trip = TripWithUserAndType::from(db, trip.clone()).await; let trip = TripWithDetails::from(db, trip.clone()).await;
if trip.rower.len() == 1 { if trip.rower.len() == 1 {
trip_to_delete = Some(trip.trip); trip_to_delete = Some(trip.trip);
} }

View File

@ -71,7 +71,6 @@ async fn steering(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage
fn unauthorized_error(req: &Request) -> Redirect { fn unauthorized_error(req: &Request) -> Redirect {
// Save the URL the user tried to access, to be able to go there once logged in // Save the URL the user tried to access, to be able to go there once logged in
let mut redirect_cookie = Cookie::new("redirect_url", format!("{}", req.uri())); let mut redirect_cookie = Cookie::new("redirect_url", format!("{}", req.uri()));
println!("{}", req.uri());
redirect_cookie.set_expires(OffsetDateTime::now_utc() + Duration::hours(1)); redirect_cookie.set_expires(OffsetDateTime::now_utc() + Duration::hours(1));
req.cookies().add_private(redirect_cookie); req.cookies().add_private(redirect_cookie);

View File

@ -51,7 +51,7 @@
{% if event.always_show and not day.regular_sees_this_day %} {% if event.always_show and not day.regular_sees_this_day %}
<span title="Du siehst diese Ausfahrt schon, obwohl sie mehr als {{ amount_days_to_show_trips_ahead }} Tage in der Zukunft liegt. Du Magier!">🔮</span> <span title="Du siehst diese Ausfahrt schon, obwohl sie mehr als {{ amount_days_to_show_trips_ahead }} Tage in der Zukunft liegt. Du Magier!">🔮</span>
{% endif -%} {% endif -%}
{%- if event.max_people == 0 %} {%- if event.cancelled %}
<strong class="text-[#f43f5e]">&#9888; Absage <strong class="text-[#f43f5e]">&#9888; Absage
{{ event.planned_starting_time }} {{ event.planned_starting_time }}
Uhr Uhr
@ -129,7 +129,7 @@
<div id="event{{ event.trip_details_id }}"> <div id="event{{ event.trip_details_id }}">
{# --- START List Coxes --- #} {# --- START List Coxes --- #}
{% if event.planned_amount_cox > 0 %} {% if event.planned_amount_cox > 0 %}
{% if event.max_people == 0 %} {% if event.cancelled %}
{{ macros::box(participants=event.cox, empty_seats="", header='Absage', bg='[#f43f5e]') }} {{ macros::box(participants=event.cox, empty_seats="", header='Absage', bg='[#f43f5e]') }}
{% else %} {% else %}
{% if amount_cox_missing > 0 %} {% if amount_cox_missing > 0 %}
@ -142,9 +142,9 @@
{# --- END List Coxes --- #} {# --- END List Coxes --- #}
{# --- START List Rowers --- #} {# --- START List Rowers --- #}
{% set amount_cur_rower = event.rower | length %} {% set amount_cur_rower = event.rower | length %}
{% if event.max_people == 0 %} {% if event.cancelled %}
{{ macros::box(header='Absage', bg='[#f43f5e]', participants=event.rower, trip_details_id=event.trip_details_id, allow_removing="manage_events" in loggedin_user.roles) }} {{ macros::box(header='Absage', bg='[#f43f5e]', participants=event.rower, trip_details_id=event.trip_details_id, allow_removing="manage_events" in loggedin_user.roles) }}
{% else %} {% elif event.max_people > 0 %}
{{ macros::box(participants=event.rower, empty_seats=event.max_people - amount_cur_rower, bg='primary-100', color='black', trip_details_id=event.trip_details_id, allow_removing="manage_events" in loggedin_user.roles) }} {{ macros::box(participants=event.rower, empty_seats=event.max_people - amount_cur_rower, bg='primary-100', color='black', trip_details_id=event.trip_details_id, allow_removing="manage_events" in loggedin_user.roles) }}
{% endif %} {% endif %}
{# --- END List Rowers --- #} {# --- END List Rowers --- #}
@ -167,7 +167,11 @@
<input type="hidden" name="_method" value="put" /> <input type="hidden" name="_method" value="put" />
<input type="hidden" name="id" value="{{ event.id }}" /> <input type="hidden" name="id" value="{{ event.id }}" />
{{ macros::input(label='Titel', name='name', type='input', value=event.name) }} {{ macros::input(label='Titel', name='name', type='input', value=event.name) }}
{% if event.cancelled %}
<input type="hidden" name="max_people" value="-1" />
{% else %}
{{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=event.max_people, min='1') }} {{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=event.max_people, min='1') }}
{% endif %}
{{ macros::input(label='Anzahl Steuerleute', name='planned_amount_cox', type='number', value=event.planned_amount_cox, required=true, min='0') }} {{ macros::input(label='Anzahl Steuerleute', name='planned_amount_cox', type='number', value=event.planned_amount_cox, required=true, min='0') }}
{{ macros::checkbox(label='Immer anzeigen', name='always_show', id=event.id,checked=event.always_show, help="Grundsätzlich sehen Rudernde Ausfahrten 10 Tage im vorhinein. Wenn du diese Option aktivierst, ist diese Ausfahrt sofort allen ersichtlich.") }} {{ macros::checkbox(label='Immer anzeigen', name='always_show', id=event.id,checked=event.always_show, help="Grundsätzlich sehen Rudernde Ausfahrten 10 Tage im vorhinein. Wenn du diese Option aktivierst, ist diese Ausfahrt sofort allen ersichtlich.") }}
{{ macros::checkbox(label='Gesperrt', name='is_locked', id=event.id,checked=event.is_locked, help="Wenn diese Option aktiviert ist, kann sich keiner mehr an- und abmelden. Sinnvoll, wenn zB bereits die Bootseinteilung vorgenommen wurde") }} {{ macros::checkbox(label='Gesperrt', name='is_locked', id=event.id,checked=event.is_locked, help="Wenn diese Option aktiviert ist, kann sich keiner mehr an- und abmelden. Sinnvoll, wenn zB bereits die Bootseinteilung vorgenommen wurde") }}
@ -187,7 +191,7 @@
</a> </a>
</div> </div>
{% else %} {% else %}
{% if event.max_people == 0 %} {% if event.cancelled %}
Wenn du deine Absage absagen (:^)) willst, einfach entsprechende Anzahl an Ruderer oben eintragen. Wenn du deine Absage absagen (:^)) willst, einfach entsprechende Anzahl an Ruderer oben eintragen.
{% else %} {% else %}
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md"> <div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
@ -196,9 +200,8 @@
<input type="hidden" name="_method" value="put" /> <input type="hidden" name="_method" value="put" />
<input type="hidden" name="id" value="{{ event.id }}" /> <input type="hidden" name="id" value="{{ event.id }}" />
{{ macros::input(label='Grund der Absage', name='notes', type='input', value='') }} {{ macros::input(label='Grund der Absage', name='notes', type='input', value='') }}
{{ macros::input(label='', name='max_people', type='hidden', value=0) }} {{ macros::input(label='', name='max_people', type='hidden', value=-1) }}
{{ macros::input(label='', name='name', type='hidden', value=event.name) }} {{ macros::input(label='', name='name', type='hidden', value=event.name) }}
{{ macros::input(label='', name='max_people', type='hidden', value=event.max_people) }}
{{ macros::input(label='', name='planned_amount_cox', type='hidden', value=event.planned_amount_cox) }} {{ macros::input(label='', name='planned_amount_cox', type='hidden', value=event.planned_amount_cox) }}
{{ macros::input(label='', name='always_show', type='hidden', value=event.always_show) }} {{ macros::input(label='', name='always_show', type='hidden', value=event.always_show) }}
{{ macros::input(label='', name='is_locked', type='hidden', value=event.is_locked) }} {{ macros::input(label='', name='is_locked', type='hidden', value=event.is_locked) }}
@ -228,7 +231,7 @@
{% if trip.always_show and not day.regular_sees_this_day %} {% if trip.always_show and not day.regular_sees_this_day %}
<span title="Du siehst diese Ausfahrt schon, obwohl sie mehr als {{ amount_days_to_show_trips_ahead }} Tage in der Zukunft liegt. Du Magier!">🔮</span> <span title="Du siehst diese Ausfahrt schon, obwohl sie mehr als {{ amount_days_to_show_trips_ahead }} Tage in der Zukunft liegt. Du Magier!">🔮</span>
{% endif -%} {% endif -%}
{% if trip.max_people == 0 %} {% if trip.cancelled %}
<strong class="text-[#f43f5e]">&#9888; <strong class="text-[#f43f5e]">&#9888;
{{ trip.planned_starting_time }} {{ trip.planned_starting_time }}
Uhr</strong> Uhr</strong>
@ -250,7 +253,7 @@
{% endif %} {% endif %}
<br /> <br />
<a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong> <a href="#" data-sidebar="true" data-trigger="sidebar" data-header="<strong>
{% if trip.max_people == 0 %}&#9888;{% endif %} {% if trip.cancelled %}&#9888;{% endif %}
{{ trip.planned_starting_time }} Uhr</strong> ({{ trip.cox_name }}) {{ trip.planned_starting_time }} Uhr</strong> ({{ trip.cox_name }})
{% if trip.trip_type %}<small class='block'>{{ trip.trip_type.desc }}</small>{% endif %} {% if trip.trip_type %}<small class='block'>{{ trip.trip_type.desc }}</small>{% endif %}
{% if trip.notes %}<small class='block'>{{ trip.notes }}</small>{% endif %} {% if trip.notes %}<small class='block'>{{ trip.notes }}</small>{% endif %}
@ -279,7 +282,7 @@
{# --- START Sidebar Content --- #} {# --- START Sidebar Content --- #}
<div class="hidden"> <div class="hidden">
<div id="trip{{ trip.trip_details_id }}"> <div id="trip{{ trip.trip_details_id }}">
{% if trip.max_people == 0 %} {% if trip.cancelled %}
{# --- border-[#f43f5e] bg-[#f43f5e] --- #} {# --- border-[#f43f5e] bg-[#f43f5e] --- #}
{{ macros::box(participants=trip.rower,bg='[#f43f5e]',header='Absage', trip_details_id=trip.trip_details_id, allow_removing=loggedin_user.id == trip.cox_id) }} {{ macros::box(participants=trip.rower,bg='[#f43f5e]',header='Absage', trip_details_id=trip.trip_details_id, allow_removing=loggedin_user.id == trip.cox_id) }}
{% else %} {% else %}
@ -319,13 +322,13 @@
</a> </a>
</div> </div>
{% else %} {% else %}
{% if trip.max_people == 0 %} {% if trip.cancelled %}
Wenn du deine Absage absagen (:^)) willst, einfach entsprechende Anzahl an Ruderer oben eintragen. Wenn du deine Absage absagen (:^)) willst, einfach entsprechende Anzahl an Ruderer oben eintragen.
{% else %} {% else %}
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md"> <div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
<h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt absagen</h3> <h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt absagen</h3>
<form action="/cox/trip/{{ trip.id }}" method="post" class="grid"> <form action="/cox/trip/{{ trip.id }}" method="post" class="grid">
{{ macros::input(label='', name='max_people', type='hidden', value=0) }} {{ macros::input(label='', name='max_people', type='hidden', value=-1) }}
{{ macros::input(label='Grund der Absage', name='notes', type='input', value='') }} {{ macros::input(label='Grund der Absage', name='notes', type='input', value='') }}
{{ macros::input(label='', name='is_locked', type='hidden', value=trip.is_locked) }} {{ macros::input(label='', name='is_locked', type='hidden', value=trip.is_locked) }}
{{ macros::input(label='', name='trip_type', type='hidden', value=trip.trip_type_id) }} {{ macros::input(label='', name='trip_type', type='hidden', value=trip.trip_type_id) }}