From e034e414e80feb25b497172d07eeaa7b6486467d Mon Sep 17 00:00:00 2001 From: Moritz Strohm <strohm@data-quest.de> Date: Mon, 9 Sep 2024 15:33:40 +0000 Subject: [PATCH] add repetition from monday until friday for resource bookings, re #2013 Merge request studip/studip!2539 --- app/controllers/resources/booking.php | 40 +++++++++++++------ .../resources/booking/_add_edit_form.php | 6 +++ ....1.1_add_weekdays_to_resource_bookings.php | 26 ++++++++++++ lib/models/resources/BrokenResource.php | 3 +- lib/models/resources/Building.php | 3 +- lib/models/resources/Location.php | 3 +- lib/models/resources/Resource.php | 8 +++- lib/models/resources/ResourceBooking.php | 39 ++++++++++++++++-- lib/models/resources/ResourceLabel.php | 3 +- 9 files changed, 109 insertions(+), 22 deletions(-) create mode 100644 db/migrations/6.0.1.1_add_weekdays_to_resource_bookings.php diff --git a/app/controllers/resources/booking.php b/app/controllers/resources/booking.php index a4722eda5c9..53735198208 100644 --- a/app/controllers/resources/booking.php +++ b/app/controllers/resources/booking.php @@ -309,7 +309,8 @@ class Resources_BookingController extends AuthenticatedController $repetition_interval = null, $notification_enabled = false, $included_room_parts = [], - $overwrite_bookings = false + $overwrite_bookings = false, + $weekdays = '' ) { $result = [ @@ -352,7 +353,8 @@ class Resources_BookingController extends AuthenticatedController $booking_type == ResourceBooking::TYPE_LOCK ? $overwrite_bookings : false - ) + ), + $weekdays ); } else { $matching_bookings = ResourceBooking::findByResourceAndTimeRanges( @@ -397,7 +399,8 @@ class Resources_BookingController extends AuthenticatedController $booking_type == ResourceBooking::TYPE_LOCK ? $overwrite_bookings : false - ) + ), + $weekdays ); } } @@ -423,6 +426,7 @@ class Resources_BookingController extends AuthenticatedController } $a->repetition_interval = $repetition_interval->format('P%YY%MM%DD'); $a->repeat_end = $repetition_end->getTimestamp(); + $a->weekdays = $weekdays; } try { @@ -466,7 +470,8 @@ class Resources_BookingController extends AuthenticatedController $booking_type == ResourceBooking::TYPE_LOCK ? $overwrite_bookings : false - ) + ), + $weekdays ); $result['bookings'] = [$booking]; } catch (Exception $e) { @@ -1033,8 +1038,12 @@ class Resources_BookingController extends AuthenticatedController $this->repetition_style = 'monthly'; $this->repetition_interval = $interval->m; } else if (($interval->d % 7) == 0) { - $this->repetition_style = 'weekly'; - $this->repetition_interval = $interval->d / 7; + $this->repetition_style = 'daily'; + if ($this->booking->weekdays === '12345') { + $this->repetition_interval = 'workdays'; + } else { + $this->repetition_interval = $interval->d / 7; + } } else { $this->repetition_style = 'daily'; $this->repetition_interval = $interval->d; @@ -1125,17 +1134,21 @@ class Resources_BookingController extends AuthenticatedController intval($this->end->format('s')) ); if ($this->repetition_style) { - if ($this->repetition_style == 'daily' && $this->repetition_interval) { - $this->repetition_date_interval = new DateInterval( - 'P' . intval($this->repetition_interval) . 'D' - ); + if ($this->repetition_style === 'daily' && $this->repetition_interval) { + if ($this->repetition_interval === 'workdays') { + $this->repetition_date_interval = new DateInterval('P7D'); + } else { + $this->repetition_date_interval = new DateInterval( + 'P' . intval($this->repetition_interval) . 'D' + ); + } } - if ($this->repetition_style == 'weekly' && $this->repetition_interval) { + if ($this->repetition_style === 'weekly' && $this->repetition_interval) { $this->repetition_date_interval = new DateInterval( 'P' . intval($this->repetition_interval) . 'W' ); } - if ($this->repetition_style == 'monthly') { + if ($this->repetition_style === 'monthly') { $this->repetition_date_interval = new DateInterval( 'P1M' ); @@ -1413,7 +1426,8 @@ class Resources_BookingController extends AuthenticatedController ? $this->other_room_parts[$resource->id] : [] ), - $this->overwrite_bookings + $this->overwrite_bookings, + $this->repetition_interval === 'workdays' ? '12345' : '' ); $errors = array_merge($errors, $results['errors']); $room_part_errors = array_merge( diff --git a/app/views/resources/booking/_add_edit_form.php b/app/views/resources/booking/_add_edit_form.php index f982c377b58..8b921adf88e 100644 --- a/app/views/resources/booking/_add_edit_form.php +++ b/app/views/resources/booking/_add_edit_form.php @@ -374,6 +374,12 @@ : '' ?>> <?= _('jeden sechsten Tag') ?> </option> + <option value="workdays" + <?= $repetition_interval == 'workdays' + ? 'selected' + : '' ?>> + <?= _('jeden Werktag') ?> + </option> </select> </div> <label> diff --git a/db/migrations/6.0.1.1_add_weekdays_to_resource_bookings.php b/db/migrations/6.0.1.1_add_weekdays_to_resource_bookings.php new file mode 100644 index 00000000000..049f941dcc2 --- /dev/null +++ b/db/migrations/6.0.1.1_add_weekdays_to_resource_bookings.php @@ -0,0 +1,26 @@ +<?php + + +class AddWeekdaysToResourceBookings extends Migration +{ + public function description() + { + return 'Adds the weekdays column to the resource_bookings table.'; + } + + protected function up() + { + DBManager::get()->exec( + "ALTER TABLE `resource_bookings` + ADD COLUMN weekdays VARCHAR(7) COLLATE `latin1_bin` NOT NULL DEFAULT ''" + ); + } + + protected function down() + { + DBManager::get()->exec( + "ALTER TABLE `resource_bookings` + DROP COLUMN weekdays" + ); + } +} diff --git a/lib/models/resources/BrokenResource.php b/lib/models/resources/BrokenResource.php index fc44e821306..38d0c81df16 100644 --- a/lib/models/resources/BrokenResource.php +++ b/lib/models/resources/BrokenResource.php @@ -104,7 +104,8 @@ class BrokenResource extends Resource $description = '', $internal_comment = '', $booking_type = ResourceBooking::TYPE_NORMAL, - $force_booking = false + $force_booking = false, + string $weekdays = '' ) { return null; } diff --git a/lib/models/resources/Building.php b/lib/models/resources/Building.php index a1c071a095a..7f1402f9c3b 100644 --- a/lib/models/resources/Building.php +++ b/lib/models/resources/Building.php @@ -458,7 +458,8 @@ class Building extends Resource $description = '', $internal_comment = '', $booking_type = ResourceBooking::TYPE_NORMAL, - $force_booking = false + $force_booking = false, + string $weekdays = '' ) { return null; diff --git a/lib/models/resources/Location.php b/lib/models/resources/Location.php index 9da2e113703..788b4b35da1 100644 --- a/lib/models/resources/Location.php +++ b/lib/models/resources/Location.php @@ -379,7 +379,8 @@ class Location extends Resource $description = '', $internal_comment = '', $booking_type = ResourceBooking::TYPE_NORMAL, - $force_booking = false + $force_booking = false, + string $weekdays = '' ) { return null; diff --git a/lib/models/resources/Resource.php b/lib/models/resources/Resource.php index 32766e4106b..e1d8b674296 100644 --- a/lib/models/resources/Resource.php +++ b/lib/models/resources/Resource.php @@ -661,6 +661,10 @@ class Resource extends SimpleORMap implements StudipItem * @param bool $force_booking If this parameter is set to true, * overlapping bookings are removed before storing this booking. * + * @param string $weekdays The weekdays (1 - 7) on which the booking + * shall take place. This is only used when a booking with repetitions + * shall only take place on some weekdays. + * * @return ResourceBooking object. * @throws InvalidArgumentException If no time ranges are specified * or if there is an error regarding the time ranges. @@ -684,7 +688,8 @@ class Resource extends SimpleORMap implements StudipItem $description = '', $internal_comment = '', $booking_type = ResourceBooking::TYPE_NORMAL, - $force_booking = false + $force_booking = false, + string $weekdays = '' ) { if (!is_array($time_ranges)) { @@ -843,6 +848,7 @@ class Resource extends SimpleORMap implements StudipItem } $booking->repetition_interval = $repetition_interval->format('P%YY%MM%DD'); + $booking->weekdays = $weekdays; } if ($preparation_time) { diff --git a/lib/models/resources/ResourceBooking.php b/lib/models/resources/ResourceBooking.php index 977cf32ec49..3c84cb84651 100644 --- a/lib/models/resources/ResourceBooking.php +++ b/lib/models/resources/ResourceBooking.php @@ -41,6 +41,7 @@ * @property int $booking_type database column * @property string $booking_user_id database column * @property string $repetition_interval database column + * @property string $weekdays database column * @property SimpleORMapCollection|ResourceBookingInterval[] $time_intervals has_many ResourceBookingInterval * @property Resource $resource belongs_to Resource * @property User $assigned_user belongs_to User @@ -663,8 +664,13 @@ class ResourceBooking extends SimpleORMap implements PrivacyObject, Studip\Calen $duration = $repetition_begin->diff($date_end); + $weekdays = []; + if (preg_match('/^1?2?3?4?5?6?7?$/', $this->weekdays)) { + $weekdays = str_split($this->weekdays); + } + //Loop over all exceptions and check if they belong to - //one of the repetions: + //one of the repetitions: $obsolete_exception_ids = []; foreach ($exceptions as $exception) { @@ -697,7 +703,13 @@ class ResourceBooking extends SimpleORMap implements PrivacyObject, Studip\Calen break; } - $current_repetition->add($repetition_interval); + if ($weekdays) { + while (!in_array($current_repetition->format('N'), $weekdays)) { + $current_repetition = $current_repetition->add(new DateInterval('P1D')); + } + } else { + $current_repetition->add($repetition_interval); + } } if ($exception_obsolete) { @@ -1280,8 +1292,19 @@ class ResourceBooking extends SimpleORMap implements PrivacyObject, Studip\Calen //Check if end is later than begin to avoid //infinite loops. if ($repetition_end > $booking_begin) { + $weekdays = []; + if ($this->weekdays && preg_match('/^1?2?3?4?5?6?7?$/', $this->weekdays)) { + $weekdays = str_split($this->weekdays); + } $current_begin = clone $booking_begin; - $current_begin->add($repetition_interval); + //Move to the first weekday of the booking or the next interval: + if ($weekdays) { + while (!in_array($current_begin->format('N'), $weekdays)) { + $current_begin = $current_begin->add(new DateInterval('P1D')); + } + } else { + $current_begin->add($repetition_interval); + } while ($current_begin < $repetition_end) { $current_end = clone $current_begin; $current_end->add($duration); @@ -1297,7 +1320,15 @@ class ResourceBooking extends SimpleORMap implements PrivacyObject, Studip\Calen : $current_end->getTimestamp() ) ]; - $current_begin->add($repetition_interval); + if ($weekdays) { + //Move to the next weekday that is in the array of weekday numbers: + do { + $current_begin = $current_begin->add(new DateInterval('P1D')); + } while (!in_array($current_begin->format('N'), $weekdays)); + } else { + //Move to the next step according to the repetition interval: + $current_begin->add($repetition_interval); + } } } else { //end timestamp is before begin timestamp: diff --git a/lib/models/resources/ResourceLabel.php b/lib/models/resources/ResourceLabel.php index c46572396a1..1d1968d8d8a 100644 --- a/lib/models/resources/ResourceLabel.php +++ b/lib/models/resources/ResourceLabel.php @@ -97,7 +97,8 @@ class ResourceLabel extends Resource $description = '', $internal_comment = '', $booking_type = ResourceBooking::TYPE_NORMAL, - $force_booking = false + $force_booking = false, + string $weekdays = '' ) { return null; -- GitLab