Skip to content
35 changes: 13 additions & 22 deletions lbplanner/classes/helpers/slot_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

/**
Expand All @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand All @@ -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));

Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand Down
26 changes: 25 additions & 1 deletion lbplanner/classes/model/reservation.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion lbplanner/classes/model/slot.php
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,14 @@ 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);

foreach ($reservations as $reservation) {
if ($reservation->userid === $USER['id']) {
if ($reservation->userid === intval($USER->id)) {
$this->forcuruser = true;
return;
}
Expand Down
16 changes: 14 additions & 2 deletions lbplanner/classes/model/slot_filter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
),
]
);
}
Expand Down
12 changes: 12 additions & 0 deletions lbplanner/classes/model/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions lbplanner/db/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
67 changes: 67 additions & 0 deletions lbplanner/services/slots/get_all_slots.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
// This file is part of local_lbplanner.
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

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_mdlobj($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()
);
}
}
1 change: 1 addition & 0 deletions lbplanner/services/slots/get_my_reservations.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
3 changes: 1 addition & 2 deletions lbplanner/services/slots/get_supervisor_slots.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
namespace local_lbplanner_services;

use core_external\{external_api, external_function_parameters, external_multiple_structure};

use local_lbplanner\helpers\slot_helper;
use local_lbplanner\model\slot;

Expand All @@ -26,7 +25,7 @@
*
* @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 {
Expand Down
2 changes: 1 addition & 1 deletion lbplanner/services/user/get_user.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion lbplanner/version.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading