From 45c3a642a7a7bd24d38b4c6dfa042f249135e000 Mon Sep 17 00:00:00 2001 From: albertlast Date: Sat, 29 Aug 2026 08:22:24 +0200 Subject: [PATCH 1/2] Opens the calendar's event form on a date instead of refusing to open Clicking "Post Event" in the calendar produced "Invalid date" and nothing else. It happened on a default install, because cal_allow_unlinked is empty until somebody turns it on, and Calendar::post() then hands a new event to Post::call() rather than to the standalone editor. There were two faults in that path, the second hidden behind the first. Event::setRequestedStartAndDuration() ends by fatalling with 'invalid_date' when it cannot find a start date. That is right for the five places that call it while saving, but initiateEvent() calls it while *opening the form*, where no date has been chosen yet. Event(-1) already defaults its start to now for exactly this case - it is what Calendar::post() relies on when it builds the standalone editor with no properties at all - so the method is now only asked for a start when something actually supplied one. That method also reads only $_POST, so the year, month and day the calendar puts in its day links never arrived and the form ignored the day that was clicked. Those values are now passed through, and a date in the query string opens the form on it. With the fatal gone, a second one appeared underneath: title and location were passed as null when the request did not carry them, and both are typed string on Event, so assigning null threw. They are omitted instead, which is what Event(-1) expects - both already default to an empty string. Checked on a default install with the setting untouched: the button now opens the form on today, a link carrying year=2027;month=3;day=14 opens it on that date, and posting the form creates the event and its topic. Also checked with cal_allow_unlinked enabled, so the standalone editor still works, and the error log stayed empty throughout. Signed-off-by: albertlast --- Sources/Actions/Post.php | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/Sources/Actions/Post.php b/Sources/Actions/Post.php index 3f507aa79a..908c8b307a 100644 --- a/Sources/Actions/Post.php +++ b/Sources/Actions/Post.php @@ -785,12 +785,41 @@ protected function initiateEvent(): void } if (!isset(Utils::$context['event']) || !(Utils::$context['event'] instanceof Event)) { - $props = [ - 'title' => isset($_REQUEST['evtitle']) ? Utils::htmlspecialchars(stripslashes($_REQUEST['evtitle'])) : null, - 'location' => isset($_REQUEST['event_location']) ? Utils::htmlspecialchars(stripslashes($_REQUEST['event_location'])) : null, - ]; + // Both of these are typed as string and already default to '', so + // they have to be left out rather than passed as null when the + // request does not carry them. + $props = []; + + if (isset($_REQUEST['evtitle'])) { + $props['title'] = Utils::htmlspecialchars(stripslashes($_REQUEST['evtitle'])); + } + + if (isset($_REQUEST['event_location'])) { + $props['location'] = Utils::htmlspecialchars(stripslashes($_REQUEST['event_location'])); + } + + // The calendar links here with the day the member picked in the + // query string, but setRequestedStartAndDuration() only ever looks + // at $_POST, so on a plain GET those values never arrive. Hand + // them over. + foreach (['year', 'month', 'day', 'hour', 'minute', 'second'] as $key) { + if (isset($_GET[$key]) && !isset($_POST[$key])) { + $props[$key] = (int) $_GET[$key]; + } + } + + // And when nothing asked for a date at all, leave that method + // alone. It ends by fatalling with 'invalid_date' if it cannot + // find one, which is right when an event is being saved but not + // here: this is the posting form being opened, and Event(-1) + // already defaults to now for exactly this case. That is what + // Calendar::post() relies on when it builds the standalone editor. + $date_params = array_flip(['year', 'month', 'day', 'start_date', 'start_datetime', 'start_year', 'start_month', 'start_day']); + + if (array_intersect_key($props, $date_params) !== [] || array_intersect_key($_POST, $date_params) !== []) { + Event::setRequestedStartAndDuration($props); + } - Event::setRequestedStartAndDuration($props); Event::setRequestedRRule($props); Utils::$context['event'] = new Event(-1, $props); Event::setRequestedRDatesAndExDates(Utils::$context['event']); From c1df1521dc2bc0fca93e30d863019f226f7ee006 Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Sat, 29 Aug 2026 16:54:41 -0600 Subject: [PATCH 2/2] Reduces unnecessary comments Co-authored-by: Jon Stovell --- Sources/Actions/Post.php | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/Sources/Actions/Post.php b/Sources/Actions/Post.php index 908c8b307a..ce69ae8188 100644 --- a/Sources/Actions/Post.php +++ b/Sources/Actions/Post.php @@ -785,9 +785,6 @@ protected function initiateEvent(): void } if (!isset(Utils::$context['event']) || !(Utils::$context['event'] instanceof Event)) { - // Both of these are typed as string and already default to '', so - // they have to be left out rather than passed as null when the - // request does not carry them. $props = []; if (isset($_REQUEST['evtitle'])) { @@ -798,22 +795,14 @@ protected function initiateEvent(): void $props['location'] = Utils::htmlspecialchars(stripslashes($_REQUEST['event_location'])); } - // The calendar links here with the day the member picked in the - // query string, but setRequestedStartAndDuration() only ever looks - // at $_POST, so on a plain GET those values never arrive. Hand - // them over. + // Default to the date and time the member was viewing. foreach (['year', 'month', 'day', 'hour', 'minute', 'second'] as $key) { if (isset($_GET[$key]) && !isset($_POST[$key])) { $props[$key] = (int) $_GET[$key]; } } - // And when nothing asked for a date at all, leave that method - // alone. It ends by fatalling with 'invalid_date' if it cannot - // find one, which is right when an event is being saved but not - // here: this is the posting form being opened, and Event(-1) - // already defaults to now for exactly this case. That is what - // Calendar::post() relies on when it builds the standalone editor. + // Only try to set the start and duration if we were given the relevant data. $date_params = array_flip(['year', 'month', 'day', 'start_date', 'start_datetime', 'start_year', 'start_month', 'start_day']); if (array_intersect_key($props, $date_params) !== [] || array_intersect_key($_POST, $date_params) !== []) {