From 51f738d2fca6731f9b5f0a9a98f5e1df4eceff26 Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 12:36:55 +0100 Subject: [PATCH 01/10] fix: user constructor in get_user --- lbplanner/services/user/get_user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lbplanner/services/user/get_user.php b/lbplanner/services/user/get_user.php index a03120b1..2a1c7e37 100644 --- a/lbplanner/services/user/get_user.php +++ b/lbplanner/services/user/get_user.php @@ -57,7 +57,7 @@ public static function get_user(): array { // Checks if the user is enrolled in LB Planner. if (!user_helper::check_user_exists($USER->id)) { // Register user if not found. - $lbplanneruser = new user(0, $USER->id, 'default', 'none', 1); + $lbplanneruser = new user(0, $USER->id, 'default', 'none', 1, false); $lbpid = $DB->insert_record(user_helper::LB_PLANNER_USER_TABLE, $lbplanneruser->prepare_for_db()); $lbplanneruser->set_fresh($lbpid); From 31fa951b59c6c441b0e9bd07c5fabb353868ff30 Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 12:47:06 +0100 Subject: [PATCH 02/10] fix: return all slots in get_supervisor_slots if user is slotmaster --- lbplanner/services/slots/get_supervisor_slots.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lbplanner/services/slots/get_supervisor_slots.php b/lbplanner/services/slots/get_supervisor_slots.php index b6301dcf..1ac080f2 100644 --- a/lbplanner/services/slots/get_supervisor_slots.php +++ b/lbplanner/services/slots/get_supervisor_slots.php @@ -17,12 +17,14 @@ namespace local_lbplanner_services; use core_external\{external_api, external_function_parameters, external_multiple_structure}; - +use local_lbplanner\enums\CAPABILITY_FLAG; use local_lbplanner\helpers\slot_helper; use local_lbplanner\model\slot; +use local_lbplanner\model\user; /** * Returns all slots a supervisor can see. + * If current user is slotmaster, return *all* slots. * * @package local_lbplanner * @subpackage services_slots @@ -43,8 +45,13 @@ public static function get_supervisor_slots_parameters(): external_function_para */ public static function get_supervisor_slots(): array { global $USER; + $user = user::from_db($USER); - $slots = slot_helper::get_supervisor_slots($USER->id); + if ($user->get_capabilitybitmask() & CAPABILITY_FLAG::SLOTMASTER) { + $slots = slot_helper::get_all_slots(); + } else { + $slots = slot_helper::get_supervisor_slots($USER->id); + } return array_map(fn(slot $slot) => $slot->prepare_for_api(), $slots); } From b3c1be734c0d7e082626c4312aa5afebc6c01666 Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 13:08:33 +0100 Subject: [PATCH 03/10] feat: move slotmaster get_all_slots into its own API call --- lbplanner/services/slots/get_all_slots.php | 67 +++++++++++++++++++ .../services/slots/get_supervisor_slots.php | 12 +--- 2 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 lbplanner/services/slots/get_all_slots.php diff --git a/lbplanner/services/slots/get_all_slots.php b/lbplanner/services/slots/get_all_slots.php new file mode 100644 index 00000000..45d9bd39 --- /dev/null +++ b/lbplanner/services/slots/get_all_slots.php @@ -0,0 +1,67 @@ +. + +namespace local_lbplanner_services; + +use core_external\{external_api, external_function_parameters, external_multiple_structure}; +use local_lbplanner\enums\CAPABILITY_FLAG; +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\model\slot; +use local_lbplanner\model\user; + +/** + * Returns all slots. + * Throws exception if the current user is not a slotmaster. + * + * @package local_lbplanner + * @subpackage services_slots + * @copyright 2025 necodeIT + * @license https://creativecommons.org/licenses/by-nc-sa/4.0/ CC-BY-NC-SA 4.0 International or later + */ +class slots_get_all_slots extends external_api { + /** + * Parameters for get_all_slots. + * @return external_function_parameters + */ + public static function get_all_slots_parameters(): external_function_parameters { + return new external_function_parameters([]); + } + + /** + * Returns all slots. + */ + public static function get_all_slots(): array { + global $USER; + $user = user::from_db($USER); + + if (!($user->get_capabilitybitmask() & CAPABILITY_FLAG::SLOTMASTER)) { + throw new \moodle_exception('current user is not slotmaster'); + } + $slots = slot_helper::get_all_slots(); + + return array_map(fn(slot $slot) => $slot->prepare_for_api(), $slots); + } + + /** + * Returns the structure of the slot array + * @return external_multiple_structure + */ + public static function get_all_slots_returns(): external_multiple_structure { + return new external_multiple_structure( + slot::api_structure() + ); + } +} diff --git a/lbplanner/services/slots/get_supervisor_slots.php b/lbplanner/services/slots/get_supervisor_slots.php index 1ac080f2..da7d49d9 100644 --- a/lbplanner/services/slots/get_supervisor_slots.php +++ b/lbplanner/services/slots/get_supervisor_slots.php @@ -17,18 +17,15 @@ namespace local_lbplanner_services; use core_external\{external_api, external_function_parameters, external_multiple_structure}; -use local_lbplanner\enums\CAPABILITY_FLAG; use local_lbplanner\helpers\slot_helper; use local_lbplanner\model\slot; -use local_lbplanner\model\user; /** * Returns all slots a supervisor can see. - * If current user is slotmaster, return *all* slots. * * @package local_lbplanner * @subpackage services_slots - * @copyright 2024 necodeIT + * @copyright 2025 necodeIT * @license https://creativecommons.org/licenses/by-nc-sa/4.0/ CC-BY-NC-SA 4.0 International or later */ class slots_get_supervisor_slots extends external_api { @@ -45,13 +42,8 @@ public static function get_supervisor_slots_parameters(): external_function_para */ public static function get_supervisor_slots(): array { global $USER; - $user = user::from_db($USER); - if ($user->get_capabilitybitmask() & CAPABILITY_FLAG::SLOTMASTER) { - $slots = slot_helper::get_all_slots(); - } else { - $slots = slot_helper::get_supervisor_slots($USER->id); - } + $slots = slot_helper::get_supervisor_slots($USER->id); return array_map(fn(slot $slot) => $slot->prepare_for_api(), $slots); } From 0926049ca10e66fdb188f76540cdbad0cf67bfcb Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 13:23:03 +0100 Subject: [PATCH 04/10] fix: docs for filter are nullable --- lbplanner/classes/model/slot_filter.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lbplanner/classes/model/slot_filter.php b/lbplanner/classes/model/slot_filter.php index 9b34f4dd..68f9361a 100644 --- a/lbplanner/classes/model/slot_filter.php +++ b/lbplanner/classes/model/slot_filter.php @@ -120,8 +120,20 @@ public static function api_structure(): external_single_structure { [ 'id' => new external_value(PARAM_INT, 'filter ID'), 'slotid' => new external_value(PARAM_INT, 'ID of associated slot'), - 'courseid' => new external_value(PARAM_INT, 'ID of course to filter for (or null if "any")'), - 'vintage' => new external_value(PARAM_INT, 'class name to filter for (or null if "any")'), + 'courseid' => new external_value( + PARAM_INT, + 'ID of course to filter for (or null if "any")', + VALUE_REQUIRED, + null, + NULL_ALLOWED + ), + 'vintage' => new external_value( + PARAM_INT, + 'class name to filter for (or null if "any")', + VALUE_REQUIRED, + null, + NULL_ALLOWED + ), ] ); } From 46bab50d7a0d23ea0d9e8ca1c3e7ed6443edb605 Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 14:08:07 +0100 Subject: [PATCH 05/10] fix: various reservation bugs --- lbplanner/classes/helpers/slot_helper.php | 35 +++++++------------ lbplanner/classes/model/reservation.php | 26 +++++++++++++- lbplanner/classes/model/slot.php | 2 ++ .../services/slots/get_my_reservations.php | 1 + 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php index e80694d4..24b4f2fd 100644 --- a/lbplanner/classes/helpers/slot_helper.php +++ b/lbplanner/classes/helpers/slot_helper.php @@ -31,6 +31,7 @@ use DateTime; use DateTimeImmutable; use DateTimeInterface; +use DateTimeZone; use local_lbplanner\enums\CAPABILITY; use local_lbplanner\enums\WEEKDAY; use local_lbplanner\model\{slot, reservation, slot_filter}; @@ -183,9 +184,7 @@ public static function get_reservation(int $reservationid): reservation { throw new \moodle_exception('requested reservation does not exist'); } - $reservation['date'] = new DateTimeImmutable($reservation['date']); - - return new reservation(...$reservation); + return reservation::from_obj($reservation); } /** @@ -213,8 +212,7 @@ public static function get_reservations_for_slot(int $slotid): array { $reservationsobj = []; foreach ($reservations as $reservation) { - $reservation['date'] = new DateTimeImmutable($reservation['date']); - array_push($reservationsobj, new reservation(...$reservation)); + array_push($reservationsobj, reservation::from_obj($reservation)); } return self::filter_reservations_for_recency($reservationsobj); @@ -232,11 +230,10 @@ public static function get_reservations_for_user(int $userid): array { $reservationsobj = []; foreach ($reservations as $reservation) { - $reservation['date'] = new DateTimeImmutable($reservation['date']); - array_push($reservationsobj, new reservation(...$reservation)); + array_push($reservationsobj, reservation::from_obj($reservation)); } - return self::filter_reservations_for_recency($reservationsobj); + return $reservationsobj; } /** @@ -246,22 +243,13 @@ public static function get_reservations_for_user(int $userid): array { * @return reservation[] reservations that pass */ public static function filter_reservations_for_recency(array $reservations): array { - global $DB; - - $now = new DateTimeImmutable(); - $goodeggs = []; - $badeggs = []; foreach ($reservations as $reservation) { - if ($now->diff($reservation->get_datetime_end())->invert === 0) { + if (!$reservation->is_outdated()) { array_push($goodeggs, $reservation); - } else { - array_push($badeggs, $reservation->id); } } - $DB->delete_records_list(self::TABLE_RESERVATIONS, 'id', $badeggs); - return $goodeggs; } @@ -326,7 +314,8 @@ public static function filter_slots_for_user(array $allslots, \stdClass $user): * @return slot[] the filtered slot array */ public static function filter_slots_for_time(array $allslots, int $range): array { - $now = new DateTimeImmutable(); + $utctz = new DateTimeZone('UTC'); + $now = new DateTimeImmutable('now', $utctz); $slots = []; // Calculate date and time each slot happens next, and add it to the return list if within reach from today. foreach ($allslots as $slot) { @@ -346,9 +335,10 @@ public static function filter_slots_for_time(array $allslots, int $range): array * @return DateTimeImmutable the next time this slot will occur */ public static function calculate_slot_datetime(slot $slot, DateTimeInterface $now): DateTimeImmutable { + $utctz = new DateTimeZone('UTC'); $slotdaytime = self::SCHOOL_UNITS[$slot->startunit]; // NOTE: format and fromFormat use different date formatting conventions. - $slotdatetime = DateTime::createFromFormat('YY-MM-DD tHH:MM', $now->format('Y-m-d ').$slotdaytime); + $slotdatetime = DateTime::createFromFormat('YY-MM-DD tHH:MM', $now->format('Y-m-d ').$slotdaytime, $utctz); // Move to next day this weekday occurs (doesn't move if it's the same as today). $slotdatetime->modify('this '.WEEKDAY::name_from($slot->weekday)); @@ -357,7 +347,7 @@ public static function calculate_slot_datetime(slot $slot, DateTimeInterface $no $slotdatetime->add(new DateInterval('P1W')); } - return new DateTimeImmutable($slotdatetime); + return new DateTimeImmutable($slotdatetime, $utctz); } /** @@ -369,9 +359,10 @@ public static function calculate_slot_datetime(slot $slot, DateTimeInterface $no * @link slot_helper::SCHOOL_UNITS */ public static function amend_date_with_unit_time(int $unit, DateTimeInterface $date): DateTimeImmutable { + $utctz = new DateTimeZone('UTC'); $daytime = self::SCHOOL_UNITS[$unit]; - return DateTimeImmutable::createFromFormat('YY-MM-DD tHH:MM', $date->format('Y-m-d ').$daytime); + return DateTimeImmutable::createFromFormat('YY-MM-DD tHH:MM', $date->format('Y-m-d ').$daytime, $utctz); } /** diff --git a/lbplanner/classes/model/reservation.php b/lbplanner/classes/model/reservation.php index f09b8ee1..9ccf3976 100644 --- a/lbplanner/classes/model/reservation.php +++ b/lbplanner/classes/model/reservation.php @@ -28,7 +28,7 @@ use DateTimeImmutable; use core_external\{external_single_structure, external_value}; - +use DateTimeZone; use local_lbplanner\model\slot; use local_lbplanner\helpers\slot_helper; @@ -85,6 +85,19 @@ public function __construct(int $id, int $slotid, DateTimeImmutable $date, int $ $this->userid = $userid; $this->reserverid = $reserverid; $this->slot = null; + $this->datetime = null; + $this->datetimeend = null; + } + + /** + * Initializes reservation object from a DB object + * @param array $obj the DB obj + * @return reservation the reservation obj + */ + public static function from_obj(array $obj): self { + $utctz = new DateTimeZone('UTC'); + $obj['date'] = new DateTimeImmutable($obj['date'], $utctz); + return new self(...$obj); } /** @@ -151,6 +164,17 @@ public function get_datetime_end(): DateTimeImmutable { return $this->datetimeend; } + /** + * Calculates whether the reservation is outdated + * + * @return DateTimeImmutable + */ + public function is_outdated(): bool { + $utctz = new DateTimeZone('UTC'); + $yesterday = new DateTimeImmutable('yesterday', $utctz); + return $yesterday->diff($this->date)->invert; + } + /** * Returns whether this and $other overlap in their time and room. * @param reservation $other the other reservation diff --git a/lbplanner/classes/model/slot.php b/lbplanner/classes/model/slot.php index 3e8363e1..0b8bfab1 100644 --- a/lbplanner/classes/model/slot.php +++ b/lbplanner/classes/model/slot.php @@ -307,7 +307,9 @@ public static function api_structure(): external_single_structure { */ private function check_reservations(): void { global $USER; + $reservations = slot_helper::get_reservations_for_slot($this->id); + $reservations = slot_helper::filter_reservations_for_recency($reservations); $this->fullness = count($reservations); diff --git a/lbplanner/services/slots/get_my_reservations.php b/lbplanner/services/slots/get_my_reservations.php index b53b444c..9242b399 100644 --- a/lbplanner/services/slots/get_my_reservations.php +++ b/lbplanner/services/slots/get_my_reservations.php @@ -45,6 +45,7 @@ public static function get_my_reservations(): array { global $USER; $reservations = slot_helper::get_reservations_for_user($USER->id); + $reservations = slot_helper::filter_reservations_for_recency($reservations); return array_map(fn(reservation $reservation) => $reservation->prepare_for_api(), $reservations); } From 906d07b42c6362a975fe87bbfdbb9e170418cc6b Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 14:13:32 +0100 Subject: [PATCH 06/10] fix: make code checker happy --- lbplanner/classes/model/slot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lbplanner/classes/model/slot.php b/lbplanner/classes/model/slot.php index 0b8bfab1..da60299c 100644 --- a/lbplanner/classes/model/slot.php +++ b/lbplanner/classes/model/slot.php @@ -307,7 +307,7 @@ public static function api_structure(): external_single_structure { */ private function check_reservations(): void { global $USER; - + $reservations = slot_helper::get_reservations_for_slot($this->id); $reservations = slot_helper::filter_reservations_for_recency($reservations); From 55f2ea70d2a8b309ade7be491c8aec6a5934f499 Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 14:14:15 +0100 Subject: [PATCH 07/10] fix: possible problem in forcuruser check --- lbplanner/classes/model/slot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lbplanner/classes/model/slot.php b/lbplanner/classes/model/slot.php index da60299c..17e22c70 100644 --- a/lbplanner/classes/model/slot.php +++ b/lbplanner/classes/model/slot.php @@ -314,7 +314,7 @@ private function check_reservations(): void { $this->fullness = count($reservations); foreach ($reservations as $reservation) { - if ($reservation->userid === $USER['id']) { + if ($reservation->userid === intval($USER->id)) { $this->forcuruser = true; return; } From 7c624e67b98f8aeff5b4f43d574fdabae74c5f90 Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 16:21:18 +0100 Subject: [PATCH 08/10] fix: actually registered get_all_slots in services.php --- lbplanner/db/services.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lbplanner/db/services.php b/lbplanner/db/services.php index c737853b..331195a6 100644 --- a/lbplanner/db/services.php +++ b/lbplanner/db/services.php @@ -269,6 +269,15 @@ 'capabilities' => 'local/lb_planner:teacher', 'ajax' => true, ], + 'local_lbplanner_slots_get_all_slots' => [ + 'classname' => 'local_lbplanner_services\slots_get_all_slots', + 'methodname' => 'get_all_slots', + 'classpath' => 'local/lbplanner/services/slots/get_all_slots.php', + 'description' => 'Get all slots.', + 'type' => 'read', + 'capabilities' => 'local/lb_planner:slotmaster', + 'ajax' => true, + ], 'local_lbplanner_slots_book_reservation' => [ 'classname' => 'local_lbplanner_services\slots_book_reservation', 'methodname' => 'book_reservation', @@ -413,6 +422,7 @@ 'local_lbplanner_slots_create_slot', 'local_lbplanner_slots_delete_slot', 'local_lbplanner_slots_delete_slot_filter', + 'local_lbplanner_slots_get_all_slots', 'local_lbplanner_slots_get_my_slots', 'local_lbplanner_slots_get_slot_filters', 'local_lbplanner_slots_get_student_slots', From fa2e98d075d8d1613c91825f1f1ec1b39853d6b4 Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 16:31:53 +0100 Subject: [PATCH 09/10] fix: bump version --- lbplanner/version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lbplanner/version.php b/lbplanner/version.php index b8d851c9..32190c06 100644 --- a/lbplanner/version.php +++ b/lbplanner/version.php @@ -28,7 +28,7 @@ $plugin->component = 'local_lbplanner'; $plugin->release = 'Alpha v.'.$release; -$plugin->version = 2025012201; +$plugin->version = 2025012400; $plugin->dependencies = [ // Depend upon version 2023110600 of local_modcustomfields. 'local_modcustomfields' => 2023110600, From 4e603e83bc8016f0475702472d53be72e18cf5fa Mon Sep 17 00:00:00 2001 From: Riedler Date: Fri, 24 Jan 2025 16:43:32 +0100 Subject: [PATCH 10/10] fix: add and use helper function to make user model from moodle user object --- lbplanner/classes/model/user.php | 12 ++++++++++++ lbplanner/services/slots/get_all_slots.php | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lbplanner/classes/model/user.php b/lbplanner/classes/model/user.php index 5d6b7749..59afafc0 100644 --- a/lbplanner/classes/model/user.php +++ b/lbplanner/classes/model/user.php @@ -133,6 +133,18 @@ public static function from_db(object $obj): self { return new self($obj->id, $obj->userid, $obj->theme, $obj->colorblindness, $obj->displaytaskcount, $obj->ekenabled); } + /** + * Takes moodle user obj and makes new user obj out of it + * + * @param object $obj the moodleuser object to get data from + * @return user a representation of this user and its data + */ + public static function from_mdlobj(object $obj): self { + $newobj = user_helper::get_user($obj->id); + $newobj->set_mdluser($obj); + return $newobj; + } + /** * Mark the object as freshly created and sets the new ID * @param int $lbpid the new ID after inserting into the DB diff --git a/lbplanner/services/slots/get_all_slots.php b/lbplanner/services/slots/get_all_slots.php index 45d9bd39..a92626b0 100644 --- a/lbplanner/services/slots/get_all_slots.php +++ b/lbplanner/services/slots/get_all_slots.php @@ -45,7 +45,7 @@ public static function get_all_slots_parameters(): external_function_parameters */ public static function get_all_slots(): array { global $USER; - $user = user::from_db($USER); + $user = user::from_mdlobj($USER); if (!($user->get_capabilitybitmask() & CAPABILITY_FLAG::SLOTMASTER)) { throw new \moodle_exception('current user is not slotmaster');