diff --git a/app/controllers/consultation/admin.php b/app/controllers/consultation/admin.php index 37cb36431e7920869b950c2efac47d8cf148bdfb..2c48d1894029210878ef2c53ee85c46ac1379456 100644 --- a/app/controllers/consultation/admin.php +++ b/app/controllers/consultation/admin.php @@ -158,7 +158,9 @@ class Consultation_AdminController extends ConsultationController $this->getDateAndTime('end'), Request::int('day-of-week'), Request::int('interval'), - Request::int('duration') + Request::int('duration'), + Request::bool('pause') ? Request::int('pause_time') : null, + Request::bool('pause') ? Request::int('pause_duration') : null ); if ($slot_count >= self::SLOT_COUNT_THRESHOLD && !Request::int('confirmed')) { $this->flash['confirm-many'] = $slot_count; @@ -183,7 +185,11 @@ class Consultation_AdminController extends ConsultationController $block->note = Request::get('note'); $block->size = Request::int('size', 1); - $block->createSlots(Request::int('duration')); + $block->createSlots( + Request::int('duration'), + Request::bool('pause') ? Request::int('pause_time') : null, + Request::bool('pause') ? Request::int('pause_duration') : null + ); $stored += $block->store(); // Store block responsibilites diff --git a/app/views/consultation/admin/create.php b/app/views/consultation/admin/create.php index 6511d79105250eae0f1763ba7a48535b7a2c072f..4f1a5991b3dc4280a333fb567cafa0f8a09fd1ac 100644 --- a/app/views/consultation/admin/create.php +++ b/app/views/consultation/admin/create.php @@ -119,6 +119,25 @@ $intervals = [ <input required type="text" name="size" id="size" min="1" max="50" value="<?= Request::int('size', 1) ?>"> </label> + + <label> + <input type="checkbox" name="pause" value="1" data-shows=".pause-inputs" data-activates=".pause-inputs input"> + <?= _('Pausen zwischen den Terminen einfügen?') ?> + </label> + + <label class="col-3 pause-inputs"> + <?= _('Eine Pause nach wie vielen Minuten einfügen?') ?> + <input type="number" name="pause_time" + value="<?= htmlReady(Request::int('pause_time', 45)) ?>" + min="1"> + </label> + + <label class="col-3 pause-inputs"> + <?= _('Dauer der Pause in Minuten') ?> + <input type="number" name="pause_duration" + value="<?= Request::int('pause_duration', 15) ?>" + min="1"> + </label> </fieldset> <? if ($responsible): ?> diff --git a/lib/models/ConsultationBlock.php b/lib/models/ConsultationBlock.php index 01b688a3da26e05c66cbb00899a602f6f378988f..4104ad8ca4f5cb7215086cfdda7549729a16351d 100644 --- a/lib/models/ConsultationBlock.php +++ b/lib/models/ConsultationBlock.php @@ -118,15 +118,17 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject /** * Count generated blocks according to the given data. * - * @param int $start Start of the time range as unix timestamp - * @param int $end End of the time range as unix timestamp - * @param int $week_day Day of the week the blocks should be created - * (0 = sunday, 1 = monday ...) - * @param int $interval Week interval (skip $interval weeks between - * blocks) - * @param int $duration Duration of a slot in minutes + * @param int $start Start of the time range as unix timestamp + * @param int $end End of the time range as unix timestamp + * @param int $week_day Day of the week the blocks should be + * created (0 = sunday, 1 = monday ...) + * @param int $interval Week interval (skip $interval weeks + * between blocks) + * @param int $duration Duration of a slot in minutes + * @param int|null $pause_time Create a pause after $pause_time minutes + * @param int|null $pause_duration Duration of the pause */ - public static function countBlocks($start, $end, $week_day, $interval, $duration) + public static function countBlocks($start, $end, $week_day, $interval, $duration, $pause_time = null, $pause_duration = null) { $count = 0; @@ -147,9 +149,23 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject $block_start = strtotime("today {$start_time}", $current); $block_end = strtotime("today {$end_time}", $current); - while ($block_start < $block_end) { - $count += 1; - $block_start = strtotime("+{$duration} minutes", $block_start); + $now = $block_start; + while ($now < $block_end) { + $is_in_pause = false; + if ($pause_time !== null) { + $is_in_pause = self::checkIfSlotIsInPause( + $now, + strtotime("+{$duration} minutes", $now), + $block_start, + $block_end, + $pause_time, + $pause_duration + ); + } + if (!$is_in_pause) { + $count += 1; + } + $now = strtotime("+{$duration} minutes", $now); } } @@ -254,20 +270,35 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject * Creates individual slots according to the defined data and given * duration. * - * @param int $duration Duration of a slot in minutes + * @param int $duration Duration of a slot in minutes + * @param int|null $pause_time Create a pause after $pause_time minutes + * @param int|null $pause_duration Duration of the pause */ - public function createSlots($duration) + public function createSlots($duration, int $pause_time = null, int $pause_duration = null) { - $start = $this->start; - while ($start < $this->end) { - $slot = new ConsultationSlot(); - $slot->block_id = $this->id; - $slot->start_time = $start; - $slot->end_time = strtotime("+{$duration} minutes", $start); + $now = $this->start; + while ($now < $this->end) { + $is_in_pause = false; + if ($pause_time !== null) { + $is_in_pause = self::checkIfSlotIsInPause( + $now, + strtotime("+{$duration} minutes", $now), + $this->start, + $this->end, + $pause_time, + $pause_duration + ); + } + if (!$is_in_pause) { + $slot = new ConsultationSlot(); + $slot->block_id = $this->id; + $slot->start_time = $now; + $slot->end_time = strtotime("+{$duration} minutes", $now); - $this->slots[] = $slot; + $this->slots[] = $slot; + } - $start = $slot->end_time; + $now = strtotime("+{$duration} minutes", $now); } } @@ -396,6 +427,36 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject } + /** + * Checks if a given time span (defined by $begin and $end) is inside a + * defined pause of a block. + * + * @param int $begin + * @param int $end + * @param int $block_begin + * @param int $block_end + * @param int $pause_time + * @param int $pause_duration + * + * @return bool + */ + private static function checkIfSlotIsInPause($begin, $end, $block_begin, $block_end, $pause_time, $pause_duration): bool + { + $now = $block_begin; + while ($now < $block_end) { + $pause_begin = strtotime("+{$pause_time} minutes", $now); + $pause_end = strtotime("+{$pause_duration} minutes", $pause_begin); + + if ($begin < $pause_end && $end > $pause_begin) { + return true; + } + + $now = $pause_end; + } + + return false; + } + /** * @return string A string representation of the consultation block instance. */ diff --git a/resources/assets/javascripts/bootstrap/studip_helper_attributes.js b/resources/assets/javascripts/bootstrap/studip_helper_attributes.js index 491d2ac5d9845adee85b59db34731d2093ce885f..7af9f9a8e18b8b2869a55d58feb796770207efd5 100644 --- a/resources/assets/javascripts/bootstrap/studip_helper_attributes.js +++ b/resources/assets/javascripts/bootstrap/studip_helper_attributes.js @@ -124,6 +124,32 @@ STUDIP.ready((event) => { $('select[data-activates]', event.target).trigger('change'); }); +// +$(document).on('change', '[data-hides],[data-shows]', function () { + if (!$(this).is(':checkbox,:radio')) { + return; + } + + ['hides', 'shows'].forEach((type) => { + var selector = $(this).data(type); + if (selector === undefined || $(this).prop('disabled')) { + return; + } + + var state = $(this).prop('checked') || $(this).prop('indeterminate') || false; + $(selector).each(function() { + var condition = $(this).data(`${type}Condition`), + toggle = state && (!condition || $(condition).length > 0); + $(this) + .toggle(type === 'shows' ? toggle : !toggle) + .trigger('update.proxy'); + }); + }); +}); +STUDIP.ready(event => { + $('[data-hides],[data-shows]', event.target).trigger('change'); +}); + // Enable the user to set the checked state on a subset of related // checkboxes by clicking the first checkbox of the subset and then // clicking the last checkbox of the subset while holding down the shift