From 67e33d7836cb60e8dbd5b684e3fd62ffd6501250 Mon Sep 17 00:00:00 2001 From: blackcoder87 Date: Sun, 8 Dec 2024 13:52:23 +0100 Subject: [PATCH 1/6] Training module: Add support for recurrent events --- .../training/boxes/views/nexttraining.php | 2 +- .../modules/training/config/config.php | 29 +- .../modules/training/controllers/Index.php | 4 + .../training/controllers/Trainings.php | 5 +- .../training/controllers/admin/Index.php | 24 +- .../modules/training/mappers/Training.php | 36 ++- .../modules/training/models/Training.php | 124 ++++++++- .../modules/training/translations/de.php | 21 +- .../modules/training/translations/en.php | 21 +- .../training/views/admin/index/index.php | 42 ++- .../training/views/admin/index/treat.php | 247 +++++++++++++++++- .../modules/training/views/index/index.php | 9 +- .../modules/training/views/index/show.php | 61 ++++- .../training/views/trainings/index.php | 21 +- 14 files changed, 593 insertions(+), 53 deletions(-) diff --git a/application/modules/training/boxes/views/nexttraining.php b/application/modules/training/boxes/views/nexttraining.php index 1a6c25674..f8c8feb49 100644 --- a/application/modules/training/boxes/views/nexttraining.php +++ b/application/modules/training/boxes/views/nexttraining.php @@ -14,7 +14,7 @@ countdown(new \Ilch\Date($model->getDate()), $model->getTime()); + $countdown = $trainingMapper->countdown(new \Ilch\Date($model->getDate())); if ($countdown === false) { continue; } diff --git a/application/modules/training/config/config.php b/application/modules/training/config/config.php index d897c8036..0c407d768 100644 --- a/application/modules/training/config/config.php +++ b/application/modules/training/config/config.php @@ -13,7 +13,7 @@ class Config extends \Ilch\Config\Install { public array $config = [ 'key' => 'training', - 'version' => '1.9.2', + 'version' => '1.10.0', 'icon_small' => 'fa-solid fa-graduation-cap', 'author' => 'Veldscholten, Kevin', 'link' => 'https://ilch.de', @@ -74,7 +74,10 @@ public function getInstallSql(): string `id` INT(11) NOT NULL AUTO_INCREMENT, `title` VARCHAR(100) NOT NULL, `date` DATETIME NOT NULL, - `time` INT(11) NOT NULL, + `end` DATETIME DEFAULT NULL, + `period_type` VARCHAR(100) NOT NULL, + `period_day` INT(11) NOT NULL, + `repeat_until` DATETIME DEFAULT NULL, `place` VARCHAR(100) NOT NULL, `contact` INT(11) UNSIGNED NOT NULL, `voice_server` INT(11) NOT NULL, @@ -273,6 +276,28 @@ public function getUpdate(string $installedVersion): string $this->db()->insert('calendar_events', ['url' => 'training/trainings/index/'])->execute(); } // no break + case "1.9.1": + case "1.9.2": + // Add new columns for recurrent trainings feature. + $this->db()->queryMulti('ALTER TABLE `[prefix]_training` ADD `end` DATETIME DEFAULT NULL AFTER `date`; + ALTER TABLE `[prefix]_training` ADD `period_type` VARCHAR(100) NOT NULL AFTER `time`; + ALTER TABLE `[prefix]_training` ADD `period_day` INT(11) NOT NULL AFTER `period_type`; + ALTER TABLE `[prefix]_training` ADD `repeat_until` DATETIME DEFAULT NULL AFTER `period_day`;'); + + // Calculate the value for the end column with the columns date and time. Update the end column in the table. + $trainings = $this->db()->select(['id', 'date', 'time']) + ->from(['training']) + ->execute() + ->fetchRows(); + + foreach($trainings ?? [] as $training) { + $end = date("Y-m-d H:i:s", strtotime('+' . $training['time'] . ' minutes', strtotime($training['date']))); + $this->db()->update('training', ['end' => $end], ['id' => $training['id']]); + } + + // Drop time column + $this->db()->query('ALTER TABLE `[prefix]_training` DROP COLUMN `time`;'); + // no break } return '"' . $this->config['key'] . '" Update-function executed.'; diff --git a/application/modules/training/controllers/Index.php b/application/modules/training/controllers/Index.php index e8c021615..3a51e118f 100644 --- a/application/modules/training/controllers/Index.php +++ b/application/modules/training/controllers/Index.php @@ -11,6 +11,7 @@ use Modules\Training\Mappers\Entrants as EntrantsMapper; use Modules\Training\Models\Entrants as EntrantsModel; use Modules\User\Mappers\User as UserMapper; +use Modules\Calendar\Mappers\Calendar as CalendarMapper; class Index extends \Ilch\Controller\Frontend { @@ -41,6 +42,7 @@ public function showAction() $entrantsMapper = new EntrantsMapper(); $entrantsModel = new EntrantsModel(); $userMapper = new UserMapper(); + $calendarMapper = new CalendarMapper(); $groupIds = [3]; if ($this->getUser()) { @@ -85,6 +87,8 @@ public function showAction() $trainEntrantsUser = $entrantsMapper->getEntrantsById($training->getId()); $this->getView()->set('training', $training) + ->set('calendarMapper', $calendarMapper) + ->set('iteration', $this->getRequest()->getParam('iteration')) ->set('trainEntrantsUser', $trainEntrantsUser); } } diff --git a/application/modules/training/controllers/Trainings.php b/application/modules/training/controllers/Trainings.php index 859fdb600..e48fe6979 100644 --- a/application/modules/training/controllers/Trainings.php +++ b/application/modules/training/controllers/Trainings.php @@ -9,6 +9,7 @@ use Modules\Training\Mappers\Training as TrainingMapper; use Modules\User\Mappers\User as UserMapper; +use Modules\Calendar\Mappers\Calendar as CalendarMapper; class Trainings extends \Ilch\Controller\Frontend { @@ -16,6 +17,7 @@ public function indexAction() { $trainingMapper = new TrainingMapper(); $userMapper = new UserMapper(); + $calendarMapper = new CalendarMapper(); $this->getLayout()->setFile('modules/calendar/layouts/events'); $groupIds = [3]; if ($this->getUser()) { @@ -27,6 +29,7 @@ public function indexAction() } } - $this->getView()->set('trainingList', $trainingMapper->getTrainingsForJson($this->getRequest()->getQuery('start'), $this->getRequest()->getQuery('end'), $groupIds)); + $this->getView()->set('trainingList', $trainingMapper->getTrainingsForJson($this->getRequest()->getQuery('start'), $this->getRequest()->getQuery('end'), $groupIds)) + ->set('calendarMapper', $calendarMapper); } } diff --git a/application/modules/training/controllers/admin/Index.php b/application/modules/training/controllers/admin/Index.php index 4082f2c6a..a612dcd5f 100644 --- a/application/modules/training/controllers/admin/Index.php +++ b/application/modules/training/controllers/admin/Index.php @@ -85,16 +85,32 @@ public function treatAction() $this->getView()->set('training', $model); if ($this->getRequest()->isPost()) { + Validation::setCustomFieldAliases([ + 'periodDay' => 'periodEntry', + 'periodDays' => 'periodEntry', + 'periodType' => 'periodEntry', + ]); + $rules = [ 'title' => 'required', 'contact' => 'required|integer|min:1|exists:users,id,id,' . $this->getRequest()->getPost('contact'), 'voiceServer' => 'required|integer|min:0|max:1', 'gameServer' => 'required|integer|min:0|max:1', 'groups' => 'required', - 'time' => 'required|integer', 'calendarShow' => 'required|integer|min:0|max:1', ]; + if ($this->getRequest()->getPost('periodType') == 'days') { + $_POST['periodDay'] = $this->getRequest()->getPost('periodDays'); + $rules['periodDay'] = 'required|numeric|min:1|max:7'; + } elseif ($this->getRequest()->getPost('periodType') != '') { + $rules['periodDay'] = 'required|numeric|min:1'; + } + + if ($this->getRequest()->getPost('periodType') != '') { + $rules['repeatUntil'] = 'required|date:d.m.Y H\:i'; + } + // Require atleast the address of the voice or gameserver if enabled. if ($this->getRequest()->getPost('voiceServer')) { $rules['voiceServerIP'] = 'required'; @@ -107,7 +123,10 @@ public function treatAction() if ($validation->isValid()) { $model->setTitle($this->getRequest()->getPost('title')) ->setDate(new \Ilch\Date($this->getRequest()->getPost('date'))) - ->setTime($this->getRequest()->getPost('time')) + ->setEnd(new \Ilch\Date($this->getRequest()->getPost('end'))) + ->setPeriodDay($this->getRequest()->getPost('periodDay')) + ->setPeriodType($this->getRequest()->getPost('periodType')) + ->setRepeatUntil($this->getRequest()->getPost('repeatUntil') ? new \Ilch\Date($this->getRequest()->getPost('repeatUntil')) : '1000-01-01 00:00:00') ->setPlace($this->getRequest()->getPost('place')) ->setContact($this->getRequest()->getPost('contact')) ->setVoiceServer($this->getRequest()->getPost('voiceServer') ?? false) @@ -119,7 +138,6 @@ public function treatAction() ->setText($this->getRequest()->getPost('text')) ->setShow($this->getRequest()->getPost('calendarShow')) ->setReadAccess(implode(',', $this->getRequest()->getPost('groups'))); - $trainingMapper->save($model); $this->redirect(['action' => 'index']) diff --git a/application/modules/training/mappers/Training.php b/application/modules/training/mappers/Training.php index 77f9ccce2..bf8fabcb5 100644 --- a/application/modules/training/mappers/Training.php +++ b/application/modules/training/mappers/Training.php @@ -7,6 +7,7 @@ namespace Modules\Training\Mappers; +use Ilch\Date; use Ilch\Pagination; use Modules\Training\Models\Training as TrainingModel; @@ -39,7 +40,7 @@ public function getEntriesBy(array $where = [], array $orderBy = ['t.date' => 'A } $select = $this->db()->select(); - $select->fields(['t.id', 't.title', 't.date', 't.time', 't.place', 't.contact', 't.voice_server', 't.voice_server_ip', 't.voice_server_pw', 't.game_server', 't.game_server_ip', 't.game_server_pw', 't.text', 't.show', 't.access_all']) + $select->fields(['t.id', 't.title', 't.date', 't.end', 't.period_type', 't.period_day', 't.repeat_until', 't.place', 't.contact', 't.voice_server', 't.voice_server_ip', 't.voice_server_pw', 't.game_server', 't.game_server_ip', 't.game_server_pw', 't.text', 't.show', 't.access_all']) ->from(['t' => $this->tablename]) ->join(['ra' => $this->tablenameAccess], 't.id = ra.training_id', 'LEFT', ['read_access' => 'GROUP_CONCAT(ra.group_id)']) ->where(array_merge($where, ($access ? [$select->orX(['ra.group_id' => $access, 't.access_all' => '1'])] : []))) @@ -114,11 +115,36 @@ public function getTrainingById(int $id, $groupIds = '3'): ?TrainingModel */ public function getTrainingsForJson(?string $start, ?string $end, $groupIds = '3'): ?array { - if ($start && $end) { - $start = new \Ilch\Date($start); - $end = new \Ilch\Date($end); + if (\is_string($groupIds)) { + $groupIds = explode(',', $groupIds); + } - return $this->getTraining(['t.date >=' => $start, 't.date <=' => $end, 't.show' => 1], $groupIds); + if ($start && $end) { + if (!is_a($start, Date::class)) { + $start = new Date($start); + } + if (!is_a($end, Date::class)) { + $end = new Date($end); + } + $select = $this->db()->select(); + return $this->getEntriesBy( + [ + $select->orX( + [ + $select->andX(['t.end <=' => $end->format('Y-m-d') . ' 23:59:59']), + $select->andX(['t.date >=' => $start->format('Y-m-d') . ' 00:00:00', 't.end <=' => $end->format('Y-m-d') . ' 23:59:59']), + $select->andX( + [ + 't.period_type !=' => '', + 't.date <=' => $end->format('Y-m-d') . ' 00:00:00', + $select->orX(['t.end >=' => $start->format('Y-m-d') . ' 23:59:59', 't.end =' => '1000-01-01 00:00:00']) + ] + ) + ] + ), + 'ra.group_id' => $groupIds + ] + ); } else { return null; } diff --git a/application/modules/training/models/Training.php b/application/modules/training/models/Training.php index a8305092d..1ba9b3167 100644 --- a/application/modules/training/models/Training.php +++ b/application/modules/training/models/Training.php @@ -31,11 +31,32 @@ class Training extends \Ilch\Model protected string $date = ''; /** - * The time of the training. + * The end of the training. + * + * @var string + */ + protected string $end = ''; + + /** + * The period day of the training. * * @var int */ - protected int $time = 30; + protected int $periodDay = 0; + + /** + * period day of the training. + * + * @var string + */ + protected string $periodType = ''; + + /** + * Repeat event until a specific date. + * + * @var string + */ + protected string $repeatUntil = ''; /** * The place of the training. @@ -129,8 +150,17 @@ public function setByArray(array $entries): Training if (isset($entries['date'])) { $this->setDate($entries['date']); } - if (isset($entries['time'])) { - $this->setTime($entries['time']); + if (isset($entries['end'])) { + $this->setEnd($entries['end']); + } + if (isset($entries['period_type'])) { + $this->setPeriodType($entries['period_type']); + } + if (isset($entries['period_day'])) { + $this->setPeriodDay($entries['period_day']); + } + if (isset($entries['repeat_until'])) { + $this->setRepeatUntil($entries['repeat_until']); } if (isset($entries['place'])) { $this->setPlace($entries['place']); @@ -243,25 +273,90 @@ public function setDate(string $date): Training } /** - * Gets the time of the training. + * Get the end of the training. + * + * @return string + */ + public function getEnd(): string + { + return $this->end; + } + + /** + * Set the end of the training. + * + * @param string $end + * @return $this + */ + public function setEnd(string $end): Training + { + $this->end = $end; + return $this; + } + + /** + * Gets the period day of the training. * * @return int */ - public function getTime(): int + public function getPeriodDay(): int { - return $this->time; + return $this->periodDay; } /** - * Sets the time of the training. + * Sets the period day of the training. * - * @param int $time + * @param int $periodDay * @return $this */ - public function setTime(int $time): Training + public function setPeriodDay(int $periodDay): Training + { + $this->periodDay = $periodDay; + return $this; + } + + /** + * Gets the period type of the training. + * + * @return string + */ + public function getPeriodType(): string { - $this->time = $time; + return $this->periodType; + } + /** + * Sets the period type of the training. + * + * @param string $periodType + * @return $this + */ + public function setPeriodType(string $periodType): Training + { + $this->periodType = $periodType; + return $this; + } + + /** + * Gets the date of until which date the event should be repeated. + * + * @return string + */ + public function getRepeatUntil(): string + { + return $this->repeatUntil; + } + + /** + * Sets the date of until which date the event should be repeated. + * + * @param string $repeatUntil + * @return $this + */ + public function setRepeatUntil(string $repeatUntil): Training + { + $this->repeatUntil = $repeatUntil; return $this; } @@ -532,7 +627,10 @@ public function getArray(bool $withId = true): array [ 'title' => $this->getTitle(), 'date' => $this->getDate(), - 'time' => $this->getTime(), + 'end' => $this->getEnd(), + 'period_day' => $this->getPeriodDay(), + 'period_type' => $this->getPeriodType(), + 'repeat_until' => $this->getRepeatUntil(), 'place' => $this->getPlace(), 'contact' => $this->getContact(), 'voice_server' => $this->getVoiceServer(), @@ -543,7 +641,7 @@ public function getArray(bool $withId = true): array 'game_server_pw' => $this->getGameServerPW(), 'text' => $this->getText(), 'show' => $this->getShow(), - 'access_all' => ($this->getReadAccess() === 'all' ? 1 : 0) + 'access_all' => ($this->getReadAccess() === 'all' ? 1 : 0) ] ); } diff --git a/application/modules/training/translations/de.php b/application/modules/training/translations/de.php index badf38e60..aa52f67a3 100644 --- a/application/modules/training/translations/de.php +++ b/application/modules/training/translations/de.php @@ -10,8 +10,8 @@ 'noTraining' => 'Keine Einträge vorhanden', 'title' => 'Titel', 'text' => 'Text', - 'dateTime' => 'Datum/Uhrzeit', - 'time' => 'Trainingszeit in Minuten', + 'start' => 'Beginnt', + 'end' => 'Endet', 'place' => 'Treffpunkt', 'clock' => 'Uhr', 'at' => 'um', @@ -36,4 +36,21 @@ 'noTrainings' => 'Keine Trainings vorhanden', 'boxNexttrainingLimit' => 'Next Trainings Limit', + + 'periodEntry' => 'Zyklus', + 'noPeriodEntry' => 'kein Zyklus', + 'repeatUntil' => 'Wiederholen bis zum', + + 'daily' => 'täglich', + 'weekly' => 'wöchentlich', + 'monthly' => 'monatlich', + 'quarterly' => 'vierteljährlich', + 'yearly' => 'jährlich', + 'days' => 'Tag', + + 'periodEvery' => 'Alle', + 'weeks' => 'Woche(n)', + 'months' => 'Monat(e)', + 'quarter' => 'Quartal(e)', + 'years' => 'Jahr(e)', ]; diff --git a/application/modules/training/translations/en.php b/application/modules/training/translations/en.php index af81e9290..c602b46db 100644 --- a/application/modules/training/translations/en.php +++ b/application/modules/training/translations/en.php @@ -10,8 +10,8 @@ 'noTraining' => 'No entry available', 'title' => 'Title', 'text' => 'Text', - 'dateTime' => 'Date/Time', - 'time' => 'Training time in minutes', + 'start' => 'Begins', + 'end' => 'Ends', 'place' => 'Meeting point', 'clock' => 'o\'clock', 'at' => 'at', @@ -36,4 +36,21 @@ 'noTrainings' => 'No Trainings', 'boxNexttrainingLimit' => 'Next Trainings Limit', + + 'periodEntry' => 'Cycle', + 'noPeriodEntry' => 'no cycle', + 'repeatUntil' => 'Repeat until the', + + 'daily' => 'daily', + 'weekly' => 'weekly', + 'monthly' => 'monthly', + 'quarterly' => 'quarterly', + 'yearly' => 'yearly', + 'days' => 'day', + + 'periodEvery' => 'Every', + 'weeks' => 'week(s)', + 'months' => 'month(s)', + 'quarter' => 'quarter', + 'years' => 'year(s)', ]; diff --git a/application/modules/training/views/admin/index/index.php b/application/modules/training/views/admin/index/index.php index 18ffa0839..e56543717 100644 --- a/application/modules/training/views/admin/index/index.php +++ b/application/modules/training/views/admin/index/index.php @@ -4,6 +4,24 @@ /** @var \Modules\Training\Models\Training[]|null $training */ $training = $this->get('training'); + +$periodDays = [ + '1' => $this->getTranslator()->trans('Monday'), + '2' => $this->getTranslator()->trans('Tuesday'), + '3' => $this->getTranslator()->trans('Wednesday'), + '4' => $this->getTranslator()->trans('Thursday'), + '5' => $this->getTranslator()->trans('Friday'), + '6' => $this->getTranslator()->trans('Saturday'), + '7' => $this->getTranslator()->trans('Sunday') +]; +$periodTypes = [ + 'daily' => $this->getTranslator()->trans('daily'), + 'weekly' => $this->getTranslator()->trans('weekly'), + 'monthly' => $this->getTranslator()->trans('monthly'), + 'quarterly' => $this->getTranslator()->trans('quarterly'), + 'yearly' => $this->getTranslator()->trans('yearly'), + 'days' => $this->getTranslator()->trans('days'), +]; ?>

getTrans('manage') ?>

@@ -16,15 +34,21 @@ + + + getCheckAllCheckbox('check_training') ?> - getTrans('dateTime') ?> + getTrans('start') ?> + getTrans('end') ?> getTrans('title') ?> + getTrans('periodEntry') ?> + getTrans('repeatUntil') ?> @@ -41,8 +65,22 @@ getDeleteCheckbox('check_trainings', $model->getId()) ?> getEditIcon(['action' => 'treat', 'id' => $model->getId()]) ?> getDeleteIcon(['action' => 'del', 'id' => $model->getId()]) ?> - getDate())) ?> + getDate())) ?> + getEnd())) ?> escape($model->getTitle()) ?> + + getPeriodType()) { + echo $periodTypes[$model->getPeriodType()]; + if ($model->getPeriodType() != 'days') { + echo ' (x ' . $model->getPeriodDay() . ')'; + } else { + echo ' (' . $periodDays[$model->getPeriodDay()] . ')'; + } + } + ?> + + getPeriodType()) ? date('d.m.Y - H:i', strtotime($model->getRepeatUntil())) : '' ?> diff --git a/application/modules/training/views/admin/index/treat.php b/application/modules/training/views/admin/index/treat.php index 300379d8c..589f50159 100644 --- a/application/modules/training/views/admin/index/treat.php +++ b/application/modules/training/views/admin/index/treat.php @@ -4,6 +4,33 @@ /** @var \Modules\Training\Models\Training $training */ $training = $this->get('training'); + +$periodDays = [ + '1' => $this->getTranslator()->trans('Monday'), + '2' => $this->getTranslator()->trans('Tuesday'), + '3' => $this->getTranslator()->trans('Wednesday'), + '4' => $this->getTranslator()->trans('Thursday'), + '5' => $this->getTranslator()->trans('Friday'), + '6' => $this->getTranslator()->trans('Saturday'), + '7' => $this->getTranslator()->trans('Sunday') +]; +$periodTypes = [ + 'daily' => $this->getTranslator()->trans('daily'), + 'weekly' => $this->getTranslator()->trans('weekly'), + 'monthly' => $this->getTranslator()->trans('monthly'), + 'quarterly' => $this->getTranslator()->trans('quarterly'), + 'yearly' => $this->getTranslator()->trans('yearly'), + 'days' => $this->getTranslator()->trans('days'), +]; + +$periodAppendix = [ + 'daily' => $this->getTranslator()->trans('daily'), + 'weekly' => $this->getTranslator()->trans('weeks'), + 'monthly' => $this->getTranslator()->trans('months'), + 'yearly' => $this->getTranslator()->trans('years'), + 'quarterly' => $this->getTranslator()->trans('quarter'), + 'days' => $this->getTranslator()->trans('days'), +]; ?> @@ -26,8 +53,8 @@ class="form-control"
-
-
-