From e1e37dde0e0b3cd781fe6e684ca4d5e757e12e18 Mon Sep 17 00:00:00 2001 From: Clemens Gogoljn Date: Wed, 4 Oct 2023 11:39:27 +0200 Subject: [PATCH 1/2] Add an reasons for prevention service (interface for the lending extension connection). Data model change: Event begin time and begin date stored as integer. --- Classes/Controller/DutyRosterController.php | 95 ++++++++++++-- Classes/Domain/Model/Commitment.php | 9 +- Classes/Domain/Model/Event.php | 73 ++++++++--- Classes/Domain/Model/FrontendUserGroup.php | 15 +++ Classes/Domain/PublicOption.php | 4 +- .../Repository/CommitmentRepository.php | 9 +- Classes/Domain/Repository/EventRepository.php | 120 +++++++++++++++--- Classes/Domain/TCA/EventTca.php | 9 +- .../Task/PersonalDutyRosterPlanningTask.php | 8 +- Classes/Upgrades/BeginTimeConverterWizard.php | 110 ++++++++++++++++ ...x_participants_domain_model_commitment.php | 20 +-- .../tx_participants_domain_model_event.php | 36 +++--- ...tx_participants_domain_model_eventtype.php | 4 +- Configuration/TypoScript/setup.typoscript | 85 ++++++++++--- .../Private/Language/de.locallang_db.xlf | 8 +- Resources/Private/Language/locallang_db.xlf | 6 +- .../DutyRoster/DefaultICalendarEntries.html | 4 +- .../DefaultICalendarEntries.html | 4 +- .../Private/Templates/DutyRoster/Show.html | 10 +- .../Templates/PersonalDutyRoster/Show.html | 1 + ext_emconf.php | 4 +- ext_localconf.php | 6 +- ext_tables.sql | 2 + 23 files changed, 509 insertions(+), 133 deletions(-) create mode 100644 Classes/Upgrades/BeginTimeConverterWizard.php diff --git a/Classes/Controller/DutyRosterController.php b/Classes/Controller/DutyRosterController.php index c20bdd2..626638f 100755 --- a/Classes/Controller/DutyRosterController.php +++ b/Classes/Controller/DutyRosterController.php @@ -2,6 +2,7 @@ namespace Cylancer\Participants\Controller; use Cylancer\Participants\Domain\Model\Event; +use Cylancer\Participants\Domain\PublicOption; use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Database\ConnectionPool; @@ -23,9 +24,11 @@ class DutyRosterController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionContr const LIST_TYPE = 'participants_dutyroster'; + const DATE_PATTERN = '/^\d{4}-(((0[13578]|1[02])-(([012]\d)|3[01]))|((0[469]|11)-(([012]\d)|30))|02-[012]\d)$/m'; + /** * - * @var EventRepository + * @var EventRepository eventRepository */ private $eventRepository; @@ -39,23 +42,23 @@ public function __construct(EventRepository $eventRepository) * @var \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array $events * @return array */ - private function prepareEvents(Array $events) + private function prepareEvents(array $events) { $now = time(); $c = count($events); if ($c == 0) { - $afterNow = - 1; + $afterNow = -1; } else { /** @var Event $firstEvent */ $firstEvent = $events[0]; /** @var Event $firstEvent */ $lastEvent = $events[count($events) - 1]; - if ($lastEvent->getDateTime()->getTimestamp() < $now) { - $afterNow = count($events) * (- 1); + if ($lastEvent->getBeginTimeStamp() < $now) { + $afterNow = count($events) * (-1); } else { - $afterNow = - 1; + $afterNow = -1; $c = 0; /** @@ -63,12 +66,12 @@ private function prepareEvents(Array $events) * @var Event $e */ foreach ($events as $e) { - if ($afterNow == - 1 && $e->getDateTime()->getTimestamp() > $now) { + if ($afterNow == -1 && $e->getBeginTimeStamp() > $now) { $afterNow = $c; } - $c ++; + $c++; } - $afterNow = $afterNow * (- 1); + $afterNow = $afterNow * (-1); } } /** @@ -76,7 +79,7 @@ private function prepareEvents(Array $events) * @var Event $e */ foreach ($events as $e) { - $e->setCurrent($afterNow ++); + $e->setCurrent($afterNow++); // debug($e); } return $events; @@ -104,7 +107,7 @@ private function getStorageUid(string $id): array $s = $qb->select('list_type', 'pages', 'pi_flexform') ->from('tt_content') ->where($qb->expr() - ->eq('uid', $qb->createNamedParameter($id))) + ->eq('uid', $qb->createNamedParameter($id))) ->execute(); if ($row = $s->fetchAssociative()) { $contentElement = $row; @@ -153,4 +156,72 @@ public function downloadIcsAction(string $id) $this->view->assign('domain', parse_url($baseUri, PHP_URL_HOST)); $this->view->assign('events', $this->prepareEvents($this->eventRepository->findPublicEvents(EventRepository::UNLIMITED, $this->getStorageUid($id), false))); } -} + + + + /** + * @param string $id + */ + public function reasonsForPreventionAction(string $id): string + { + + $parsedBody = $this->request->getParsedBody(); + + if (is_array($parsedBody)) { + + + $from = $this->getValidDate($parsedBody['from']); + $until = $this->getValidDate($parsedBody['until']); + $visibility = $this->getEventVisiblity($parsedBody); + + if ($from != null && $until != null) { + return json_encode($this->eventRepository->findEventsAt($this->getStorageUid($id), $from, $until + (24 * 3600), $visibility)); + } + } + + $tmp = date_parse_from_format('Y-m-d', $parsedBody['from']); + return json_encode([ + 'parsedBody' => $parsedBody, + + 'isArray' => is_array($parsedBody), + + 'from' => $from, + 'preg' => preg_match(DutyRosterController::DATE_PATTERN, $parsedBody['from']) > 0, + 'fd' => $tmp, + 'e' => isset($tmp['errors']), + 'ec' => count($tmp['errors']) + ]); + + } + + private function getEventVisiblity($parsedBody): int + { + if (!isset($parsedBody['visibility'])) { + return PublicOption::PUBLIC; + } else if (strtoupper($parsedBody['visibility']) === 'ALL') { + return PublicOption::ALL; + } else if (strtoupper($parsedBody['visibility']) === 'INTERNAL') { + return PublicOption::INTERNAL; + } else { + return PublicOption::PUBLIC; + } + } + + + private function getValidDate(string $rawDate): string + { + if (preg_match(DutyRosterController::DATE_PATTERN, $rawDate)) { + $date = date_parse_from_format('Y-m-d', $rawDate); + if (!isset($date['errors']) || count($date['errors']) == 0) { + $dt = new \DateTime(); + $dt->setTimezone(new \DateTimeZone('Europe/Berlin')); + $dt->setTime(0, 0, 0, 0); + $dt->setDate($date['year'], $date['month'], $date['day']); + return $dt->getTimestamp(); + } + } + return null; + } + + +} \ No newline at end of file diff --git a/Classes/Domain/Model/Commitment.php b/Classes/Domain/Model/Commitment.php index 529ac15..ea9e5ed 100755 --- a/Classes/Domain/Model/Commitment.php +++ b/Classes/Domain/Model/Commitment.php @@ -58,7 +58,8 @@ public function __construct() * @return void */ protected function initStorageObjects() - {} + { + } /** * Returns the user @@ -169,8 +170,6 @@ public function setEvent($event): void */ public function getIsNotChangable(): bool { - return $this->getEvent() - ->getDateTime() - ->getTimestamp() < time(); + return $this->getEvent()->getBeginTimeStamp() < time(); } -} +} \ No newline at end of file diff --git a/Classes/Domain/Model/Event.php b/Classes/Domain/Model/Event.php index d04c6f6..a8510df 100644 --- a/Classes/Domain/Model/Event.php +++ b/Classes/Domain/Model/Event.php @@ -10,11 +10,12 @@ * For the full copyright and license information, please read the * LICENSE.txt file that was distributed with this source code. * - * (c) 2022 C. Gogolin + * (c) 2023 C. Gogolin */ class Event extends AbstractEntity { - + + /** * * @var ObjectStorage @@ -73,15 +74,15 @@ class Event extends AbstractEntity /** * - * @var string + * @var int */ - protected $date = '0000-00-00'; + protected $beginDate = 0 ; /** * - * @var string + * @var int */ - protected $time = '00:00'; + protected $beginTime = 68400; // 19:00 /** * @@ -145,6 +146,7 @@ public function __construct() // Do not remove the next line: It would break the functionality $this->initStorageObjects(); + } /** @@ -325,42 +327,70 @@ public function setFullDay($fullDay): void $this->fullDay = $fullDay; } + + + /** * - * @return \DateTime + * @return int */ - public function getDate(): \DateTime + public function getBeginDate(): int { - return \DateTime::createFromFormat('Y-m-d', $this->date); + return $this->beginDate; } /** * - * @param \DateTime $date + * @param int $beginDate * @return void */ - public function setDate(\DateTime $date): void + public function setBeginDate(\DateTime $beginDate): void { - $this->date = $date->format('Y-m-d'); + $this->beginDate = $beginDate; + } + + /** + * + * @return \DateTime + */ + public function getBeginDateUTC(): \DateTime + { + $return = new \DateTime(); + $return->setTimestamp($this->beginDate); + $return->setTimezone(new \DateTimeZone ('UTC')); + return $return; + } + + + /** + * + * @return int + */ + public function getBeginTime(): int + { + return $this->beginTime; } /** * * @return \DateTime */ - public function getTime(): \DateTime - { - return \DateTime::createFromFormat('H:i:s', $this->time); + public function getBeginTimeUTC(): \DateTime + { + $return = new \DateTime(); + $return->setTimestamp($this->beginTime); + $return->setTimezone(new \DateTimeZone ('UTC')); + return $return; } /** * - * @param \DateTime $time + * @param int $time * @return void */ - public function setTime(\DateTime $time): void + public function setTime(int $beginTime): void { - $this->time = $time->format('H:i:s'); + $this->beginTime = $beginTime; } /** @@ -384,13 +414,14 @@ public function setDuration($duration): void /** * - * @return \DateTime + * @return int */ - public function getDateTime(): \DateTime + public function getBeginTimestamp(): int { - return $this->getFullDay() ? \DateTime::createFromFormat('Y-m-d', $this->date)->setTime(0, 0) : \DateTime::createFromFormat('Y-m-d H:i:s', $this->date . ' ' . $this->time); + return $this->getFullDay() ? $this->getBeginDate() : $this->getBeginDate() + $this->getBeginTime(); } + /** * * @return void diff --git a/Classes/Domain/Model/FrontendUserGroup.php b/Classes/Domain/Model/FrontendUserGroup.php index 06c5abe..ee9960e 100644 --- a/Classes/Domain/Model/FrontendUserGroup.php +++ b/Classes/Domain/Model/FrontendUserGroup.php @@ -30,6 +30,13 @@ class FrontendUserGroup extends AbstractEntity */ protected $subgroup; + + /** + * + * @var string + */ + protected $accronym = ''; + /** * Constructs a new Frontend User Group */ @@ -102,4 +109,12 @@ public function getSubgroup() { return $this->subgroup; } + + /** + * + * @return string + */ + public function getAccronym() { + return $this->accronym; + } } diff --git a/Classes/Domain/PublicOption.php b/Classes/Domain/PublicOption.php index 6234fdd..831a2ac 100644 --- a/Classes/Domain/PublicOption.php +++ b/Classes/Domain/PublicOption.php @@ -7,11 +7,13 @@ * For the full copyright and license information, please read the * LICENSE.txt file that was distributed with this source code. * - * (c) 2022 C. Gogolin + * (c) 2023 C. Gogolin */ abstract class PublicOption { + const ALL = -1; + const INTERNAL = 0; const PUBLIC = 1; diff --git a/Classes/Domain/Repository/CommitmentRepository.php b/Classes/Domain/Repository/CommitmentRepository.php index b8e3c04..e0ecfe1 100755 --- a/Classes/Domain/Repository/CommitmentRepository.php +++ b/Classes/Domain/Repository/CommitmentRepository.php @@ -67,11 +67,11 @@ public function findCurrentEventCommitments(FrontendUser $user, array $dutyRoste ->in('tx_participants_domain_model_event.pid', $dutyRosterStrorageUids))) ->where($qb->expr() ->andX($qb->expr() - ->gte('tx_participants_domain_model_event.date', $qb->createNamedParameter($startMoment->format('Y-m-d'))), $qb->expr() + ->gte('tx_participants_domain_model_event.begin_date', $qb->createNamedParameter($startMoment->getTimestamp())), $qb->expr() ->eq('tx_participants_domain_model_commitment.user', $qb->createNamedParameter($user->getUid())), $qb->expr() ->eq('tx_participants_domain_model_commitment.pid', $planningStorageUid))) - ->orderby('tx_participants_domain_model_event.date', 'ASC') - ->addOrderby('tx_participants_domain_model_event.time', 'ASC') + ->orderby('tx_participants_domain_model_event.begin_date', 'ASC') + ->addOrderby('tx_participants_domain_model_event.begin_time', 'ASC') ->groupBy('tx_participants_domain_model_commitment.uid'); if (! $withCanceledEvents) { @@ -141,6 +141,7 @@ public function findMissingCommitmentsOf(int $userUid, int $commitmentStorageUid { $yesterday = new \DateTime(); $yesterday->sub(new \DateInterval('P1D')); + $yesterday->setTimezone(new \DateTimeZone('UTC')); $qb = $this->getQueryBuilder('tx_participants_domain_model_event'); $qb->select('tx_participants_domain_model_event.uid', 'tx_participants_domain_model_commitment.pid') @@ -155,7 +156,7 @@ public function findMissingCommitmentsOf(int $userUid, int $commitmentStorageUid ->andWhere($qb->expr() ->in('tx_participants_domain_model_event.pid', $eventStorageUids)) ->andWhere($qb->expr() - ->gt('tx_participants_domain_model_event.date', $yesterday->format('y-m-d'))); + ->gt('tx_participants_domain_model_event.begin_date', $yesterday->getTimestamp())); // debug($qb->getSQL()); $s = $qb->execute(); diff --git a/Classes/Domain/Repository/EventRepository.php b/Classes/Domain/Repository/EventRepository.php index ecbf719..5d8b62e 100644 --- a/Classes/Domain/Repository/EventRepository.php +++ b/Classes/Domain/Repository/EventRepository.php @@ -27,7 +27,7 @@ class EventRepository extends Repository 'time' => QueryInterface::ORDER_ASCENDING ); - const UNLIMITED = - 1; + const UNLIMITED = -1; /** * @@ -43,7 +43,7 @@ protected function getQueryBuilder(string $table): QueryBuilder * * @return Event[] */ - public function findTomorrowsEvents(): Array + public function findTomorrowsEvents(): array { $tmp = new \DateTime(); $tmp->modify('+1 day'); @@ -53,10 +53,10 @@ public function findTomorrowsEvents(): Array $qb->select('tx_participants_domain_model_event.uid') ->from('tx_participants_domain_model_event') ->where($qb->expr() - ->eq('date', $qb->expr() - ->literal($tomorrow))) + ->eq('date', $qb->expr() + ->literal($tomorrow))) ->andWhere($qb->expr() - ->eq('canceled', 0)) + ->eq('canceled', 0)) ->orderBy('date', QueryInterface::ORDER_ASCENDING) ->addOrderBy('time', QueryInterface::ORDER_ASCENDING); @@ -64,7 +64,7 @@ public function findTomorrowsEvents(): Array $s = $qb->execute(); $return = array(); - while ($row = $s->fetch()) { + while ($row = $s->fetchAssociative()) { $return[] = $this->findByUid($row['uid']); } // debug($return); @@ -77,21 +77,21 @@ public function findPublicEvents(int $limit = EventRepository::UNLIMITED, array $qb->select('tx_participants_domain_model_event.uid') ->from('tx_participants_domain_model_event') ->join('tx_participants_domain_model_event', 'tx_participants_domain_model_eventtype', 'tx_participants_domain_model_eventtype', $qb->expr() - ->eq('tx_participants_domain_model_event.event_type', $qb->quoteIdentifier('tx_participants_domain_model_eventtype.uid'))) + ->eq('tx_participants_domain_model_event.event_type', $qb->quoteIdentifier('tx_participants_domain_model_eventtype.uid'))) ->where($qb->expr() - ->orX($qb->expr() - ->eq('tx_participants_domain_model_event.public', PublicOption::PUBLIC), $qb->expr() - ->andX($qb->expr() - ->eq('tx_participants_domain_model_event.public', PublicOption::INHERITED), $qb->expr() - ->eq('tx_participants_domain_model_eventtype.public', PublicOption::PUBLIC)))) - ->orderBy('date', QueryInterface::ORDER_ASCENDING) - ->addOrderBy('time', QueryInterface::ORDER_ASCENDING); + ->orX($qb->expr() + ->eq('tx_participants_domain_model_event.public', PublicOption::PUBLIC ), $qb->expr() + ->andX($qb->expr() + ->eq('tx_participants_domain_model_event.public', PublicOption::INHERITED), $qb->expr() + ->eq('tx_participants_domain_model_eventtype.public', PublicOption::PUBLIC )))) + ->orderBy('begin_date', QueryInterface::ORDER_ASCENDING) + ->addOrderBy('begin_time', QueryInterface::ORDER_ASCENDING); if ($storageUids == null) { $qb->andWhere($qb->expr() ->in('tx_participants_domain_model_event.pid', $this->createQuery() - ->getQuerySettings() - ->getStoragePageIds())); + ->getQuerySettings() + ->getStoragePageIds())); } else { $qb->andWhere($qb->expr() ->in('tx_participants_domain_model_event.pid', $storageUids)); @@ -101,21 +101,101 @@ public function findPublicEvents(int $limit = EventRepository::UNLIMITED, array $qb->setMaxResults($limit); } - if (! $inclusiveCanceledEvents) { + if (!$inclusiveCanceledEvents) { $qb->andWhere($qb->expr() ->eq('tx_participants_domain_model_event.canceled', 0)); } if ($startWithToday) { $qb->andWhere($qb->expr() - ->gte('tx_participants_domain_model_event.date', $qb->createNamedParameter(date('Y-m-d')))); + ->gte('tx_participants_domain_model_event.begin_date', time())); } // debug($qb->getSql()); $s = $qb->execute(); $return = array(); - while ($row = $s->fetch()) { + while ($row = $s->fetchAssociative()) { $return[] = $this->findByUid($row['uid']); } return $return; } -} + + + public function findEventsAt(array $storageUids, int $from, int $until, int $visibility, int $limit = EventRepository::UNLIMITED, bool $inclusiveCanceledEvents = false): array + { + $qb = $this->getQueryBuilder('tx_participants_domain_model_event'); + + switch ($visibility) { + case PublicOption::PUBLIC: + case PublicOption::INTERNAL: + $visibilityRule = $qb->expr()->orX($qb->expr() + ->eq('tx_participants_domain_model_event.public', PublicOption::PUBLIC ), $qb->expr() + ->andX($qb->expr() + ->eq('tx_participants_domain_model_event.public', PublicOption::INHERITED), $qb->expr() + ->eq('tx_participants_domain_model_eventtype.public', PublicOption::PUBLIC ))); + + break; + default: + $visibilityRule = null; + break; + } + + $qb = $this->getQueryBuilder('tx_participants_domain_model_event'); + $qb->select('tx_participants_domain_model_event.uid') + ->from('tx_participants_domain_model_event') + ->join('tx_participants_domain_model_event', 'tx_participants_domain_model_eventtype', 'tx_participants_domain_model_eventtype', $qb->expr() + ->eq('tx_participants_domain_model_event.event_type', $qb->quoteIdentifier('tx_participants_domain_model_eventtype.uid'))) + ->where( + $qb->expr()->andX( + $qb->expr()->gte('begin_date', $qb->createNamedParameter($from)), + $qb->expr()->lt('begin_date', $qb->createNamedParameter($until)) + ) + ) + ->orderBy('date', QueryInterface::ORDER_ASCENDING) + ->addOrderBy('time', QueryInterface::ORDER_ASCENDING); + if ($visibilityRule != null) { + $qb->andWhere($visibilityRule); + } + + if ($storageUids == null) { + $qb->andWhere($qb->expr() + ->in('tx_participants_domain_model_event.pid', $this->createQuery() + ->getQuerySettings() + ->getStoragePageIds())); + } else { + $qb->andWhere($qb->expr() + ->in('tx_participants_domain_model_event.pid', $storageUids)); + } + + if ($limit != EventRepository::UNLIMITED) { + $qb->setMaxResults($limit); + } + + if (!$inclusiveCanceledEvents) { + $qb->andWhere($qb->expr() + ->eq('tx_participants_domain_model_event.canceled', 0)); + } + + $s = $qb->execute(); + $return = []; + if (1 == 0) { // add debug infos + $debug = []; + $debug['sql'] = $qb->getSql(); + $debug['from'] = $from; + $debug['until'] = $until; + $return['debug'] = $debug; + } + $data = []; + while ($row = $s->fetchAssociative()) { + /** @var Event $e */ + $e = $this->findByUid($row['uid']); + $tmp = []; + $tmp['description'] = htmlspecialchars($e->getEventType()->getTitle() .' ('.date('d.m.Y', $e->getBeginDate()).')'); + $tmp['title'] = htmlspecialchars($e->getEventType()->getTitle()); + $tmp['date'] = htmlspecialchars(date('d.m.Y', $e->getBeginDate())); + $data[] = $tmp; + } + $return['data'] = $data; + return $return; + } + +} \ No newline at end of file diff --git a/Classes/Domain/TCA/EventTca.php b/Classes/Domain/TCA/EventTca.php index 80931a6..21045e6 100644 --- a/Classes/Domain/TCA/EventTca.php +++ b/Classes/Domain/TCA/EventTca.php @@ -11,7 +11,7 @@ * For the full copyright and license information, please read the * LICENSE.txt file that was distributed with this source code. * - * (c) 2022 C. Gogolin + * (c) 2023 C. Gogolin * * This class contains a tca configuration function. * @@ -43,7 +43,12 @@ public function computeTitle(&$parameters) } $record = BackendUtility::getRecord($parameters['table'], $parameters['row']['uid']); if ($record != null) { - $parameters['title'] = $typeTitle . ' (' . $record['date'] . ')'; + if($record['full_day']) { + $parameters['title'] = $typeTitle . ' (' . date('d.m.Y', $record['begin_date']) . ')'; + } else { + $parameters['title'] = $typeTitle . ' (' . date('d.m.Y H:i', $record['begin_date'] + $record['begin_time'] ) . ')'; + } + } else { $parameters['title'] = $typeTitle; } diff --git a/Classes/Task/PersonalDutyRosterPlanningTask.php b/Classes/Task/PersonalDutyRosterPlanningTask.php index 23d89db..7e22e65 100644 --- a/Classes/Task/PersonalDutyRosterPlanningTask.php +++ b/Classes/Task/PersonalDutyRosterPlanningTask.php @@ -267,7 +267,7 @@ public function execute() $this->initialize(); if ($this->validate()) { - $now = new \DateTime(); + $now = time(); // -------------------------- // statistic values @@ -311,7 +311,7 @@ public function execute() $c = $this->commitmentRepository->findByUid($uids['commitment']); $e = $this->eventRepository->findByUid(intval($uids['event'])); - if ($e != null && $e->getDatetime() > $now) { + if ($e != null && $e->getBeginTimeStamp() > $now) { $d = $u->getCurrentlyOffDuty() ? false : $this->calculatePlanningPresent($u, $e); // debug($ds); @@ -353,7 +353,7 @@ public function execute() * @var Commitment $c */ $c = GeneralUtility::makeInstance(Commitment::class); - if ($e->getDateTime() > $now) { + if ($e->getBeginTimeStamp() > $now) { $c->setEvent($e); $c->setUser($u); @@ -428,7 +428,7 @@ private function sendInfoMail(FrontendUser $user, array $inserts, array $updates */ public function getAdditionalInformation() { - return 'Duty roster stroarge uids:' . $this->dutyRosterStorageUids . // + return 'Duty roster storage uids:' . $this->dutyRosterStorageUids . // ' / planning storage uid: ' . $this->planningStorageUid . // ' / frontend user storage uids: ' . $this->feUserStorageUids . // ' / frontend user group storage uids: ' . $this->feUsergroupStorageUids . // diff --git a/Classes/Upgrades/BeginTimeConverterWizard.php b/Classes/Upgrades/BeginTimeConverterWizard.php new file mode 100644 index 0000000..355ed86 --- /dev/null +++ b/Classes/Upgrades/BeginTimeConverterWizard.php @@ -0,0 +1,110 @@ +getQueryBuilderForTable('tx_participants_domain_model_event '); + $source->select('uid', 'time', 'date')->from('tx_participants_domain_model_event'); + + $sourceStatement = $source->execute(); + + /** @var QueryBuilder $target */ + $target = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_participants_domain_model_event'); + $target->update('tx_participants_domain_model_event'); + + while ($row = $sourceStatement->fetchAssociative()) { + $target->where($target->expr()->eq('uid', $row['uid'])); + $tmp = explode(':',$row['time']); + $ut = intval($tmp[0]) * 3600 + intval($tmp[1]) * 60 + intval($tmp[2]); + $target->set('begin_time', $ut); + + + $target->set('begin_date', strtotime($row['date'])); + $target->set('begin_time', $ut); + + $target->execute(); + } + return true; + } + + /** + * Is an update necessary? + * + * Is used to determine whether a wizard needs to be run. + * Check if data for migration exists. + * + * @return bool Whether an update is required (TRUE) or not (FALSE) + */ + public function updateNecessary(): bool + { + + try { + /** @var QueryBuilder $source */ + $source = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_participants_domain_model_event'); + $source->select('uid') + ->from('tx_participants_domain_model_event') + ->setMaxResults(1) + ->where( + $source->expr()->eq('begin_date', 0) + ); + + $sourceStatement = $source->execute(); + if ($sourceStatement->fetchAssociative() !== false) { + return true; + } + } catch (\Exception $e) { + return false; + } + return false; + } + + /** + * Returns an array of class names of prerequisite classes + * + * This way a wizard can define dependencies like "database up-to-date" or + * "reference index updated" + * + * @return string[] + */ + public function getPrerequisites(): array + { + // Add your logic here + return []; + } + + public function getIdentifier(): string + { + return 'participants_beginTimeConverterWizard'; + } +} \ No newline at end of file diff --git a/Configuration/TCA/tx_participants_domain_model_commitment.php b/Configuration/TCA/tx_participants_domain_model_commitment.php index f481fc5..4f105f4 100755 --- a/Configuration/TCA/tx_participants_domain_model_commitment.php +++ b/Configuration/TCA/tx_participants_domain_model_commitment.php @@ -72,9 +72,11 @@ 'label' => 'LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_commitment.present', 'config' => [ 'type' => 'check', + 'renderType' => 'checkboxToggle', 'items' => [ - '1' => [ - '0' => 'LLL:EXT:lang/locallang_core.xlf:labels.enabled' + [ + 0 => '', + 1 => '', ] ], 'default' => 0, @@ -84,10 +86,12 @@ 'label' => 'LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_commitment.present_default', 'config' => [ 'type' => 'check', - 'readOnly' => true, + 'readOnly' => true, + 'renderType' => 'checkboxToggle', 'items' => [ - '1' => [ - '0' => 'LLL:EXT:lang/locallang_core.xlf:labels.enabled' + [ + 0 => '', + 1 => '', ] ], 'default' => 0, @@ -96,7 +100,7 @@ 'event' => [ 'label' => 'LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_commitment.event', 'config' => [ - 'readOnly' => true, + 'readOnly' => true, 'type' => 'select', 'dbType' => 'event', 'renderType' => 'selectSingle', @@ -115,6 +119,6 @@ 'maxitems' => 1, ], ], - + ], -]; +]; \ No newline at end of file diff --git a/Configuration/TCA/tx_participants_domain_model_event.php b/Configuration/TCA/tx_participants_domain_model_event.php index 0f073fe..cb631c7 100644 --- a/Configuration/TCA/tx_participants_domain_model_event.php +++ b/Configuration/TCA/tx_participants_domain_model_event.php @@ -6,7 +6,7 @@ 'title' => 'LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_event.title', 'label' => 'title', 'label_userFunc' => \Cylancer\Participants\Domain\TCA\EventTca::class . '->computeTitle', - 'sortby' => 'date', + 'sortby' => 'begin_date', 'tstamp' => 'tstamp', 'crdate' => 'crdate', 'cruser_id' => 'cruser_id', @@ -20,9 +20,9 @@ 'starttime' => 'starttime', 'endtime' => 'endtime', 'type' => 'type', - 'date' => 'date', + 'begin_date' => 'begin_date', 'full_day' => 'full_day', - 'time' => 'time', + 'begin_time' => 'begin_time', 'duration' => 'duration', 'usergroups' => 'usergroups', 'public' => 'public', @@ -37,12 +37,14 @@ 'iconfile' => 'EXT:participants/Resources/Public/Icons/tx_participants_domain_model_event_type.gif' ], 'interface' => [ - 'showRecordFieldList' => 'l10n_parent, l10n_diffsource, hidden, canceled, event_type, date, full_day, time, ' . // - 'duration, usergroups, show_public_usergroups, public_usergroups, description, public, public_description, show_public_description ' + 'showRecordFieldList' => ' l10n_parent, l10n_diffsource, hidden, canceled, event_type, begin_date, full_day, begin_time, ' . // + 'duration, usergroups, show_public_usergroups, public_usergroups, description, public, public_description, show_public_description ' ], 'types' => [ '1' => [ - 'showitem' => ' l10n_parent, l10n_diffsource, hidden, canceled, event_type, public, date, full_day, time, duration,' . '--div--;LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_event.tabGroupSettings, usergroups, show_public_usergroups, public_usergroups, ' . '--div--;LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_event.tabDescription, description, show_public_description, public_description ' + 'showitem' => ' l10n_parent, l10n_diffsource, hidden, canceled, event_type, public, begin_date, full_day, begin_time, duration,' + . '--div--;LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_event.tabGroupSettings, usergroups, show_public_usergroups, public_usergroups, ' + . '--div--;LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_event.tabDescription, description, show_public_description, public_description ' ] ], 'columns' => [ @@ -63,7 +65,7 @@ 'items' => [ [ 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages', - - 1, + -1, 'flags-multiple' ] ], @@ -185,15 +187,14 @@ ] ], - 'date' => [ - 'label' => 'LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_event.date', + 'begin_date' => [ + 'label' => 'LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_event.beginDate', 'config' => [ 'type' => 'input', 'renderType' => 'inputDateTime', - 'format' => 'date', - 'dbType' => 'date', +// 'format' => 'date', 'eval' => 'date', - 'default' => time() + 'default' => intdiv(time() , (86400)) * 86400 ] ], @@ -206,15 +207,13 @@ ] ], - 'time' => [ - 'label' => 'LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_event.time', + 'begin_time' => [ + 'label' => 'LLL:EXT:participants/Resources/Private/Language/locallang_db.xlf:tx_participants_domain_model_event.beginTime', 'config' => [ 'type' => 'input', 'renderType' => 'inputDateTime', - 'dbType' => 'time', 'eval' => 'time', - 'default' => DateTime::createFromFormat("Y-m-d H:i:s", "1970-01-01 19:00:00")->getTimestamp(), // 68400 <=> 19:00h - // 'mode' => 'useOrOverridePlaceholder' + 'default' => 68400 // <=> 19:00h ] ], @@ -282,5 +281,4 @@ ] ] ] -]; - +]; \ No newline at end of file diff --git a/Configuration/TCA/tx_participants_domain_model_eventtype.php b/Configuration/TCA/tx_participants_domain_model_eventtype.php index 582fc28..c102f77 100644 --- a/Configuration/TCA/tx_participants_domain_model_eventtype.php +++ b/Configuration/TCA/tx_participants_domain_model_eventtype.php @@ -25,11 +25,11 @@ 'iconfile' => 'EXT:participants/Resources/Public/Icons/tx_participants_domain_model_event_type.gif' ], 'interface' => [ - 'showRecordFieldList' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, title, description, public' + 'showRecordFieldList' => ' l10n_parent, l10n_diffsource, hidden, title, description, public' ], 'types' => [ '1' => [ - 'showitem' => 'sys_language_uid, l10n_parent, l10n_diffsource, hidden, title, public, description, usergroups, --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.access, starttime, endtime' + 'showitem' => ' l10n_parent, l10n_diffsource, hidden, title, public, description, usergroups, --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.access, starttime, endtime' ] ], 'columns' => [ diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript index 9ab7d63..ef1c7ab 100755 --- a/Configuration/TypoScript/setup.typoscript +++ b/Configuration/TypoScript/setup.typoscript @@ -15,54 +15,108 @@ plugin.tx_participants { 1 = {$plugin.tx_participants.view.layoutRootPath} } + settings { + # default items per page for asynchronous requests + defaultItemsPerPage = 12 + personalDutyRosterTypeNum = 196 + personalDutyRoster{ + # JSON format + ajax{ + pageType = 103 + } + # iCal format + iCal { + pageType = 104 + } + } + dutyRoster{ + # JSON format + ajax{ + pageType = 105 + } + # iCal format + iCal { + pageType = 102 + } + } + } } } -tx_participants_dutyRosterICal = PAGE -tx_participants_dutyRosterICal { - typeNum = 102 + +tx_participants_personalDutyRoster_ajaxConnect = PAGE +tx_participants_personalDutyRoster_ajaxConnect { + typeNum < plugin.tx_participants.view.settings.personalDutyRoster.ajax.pageType + + 10 = USER + 10 { + userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run + extensionName = Participants + pluginName = PersonalDutyRoster + vendorName = Cylancer + } + + config { + disableAllHeaderCode = 1 + additionalHeaders = Content-type:application/json + xhtml_cleaning = 0 + admPanel = 0 + debug = 0 + no_cache = 1 + } +} + + +tx_participants_personalDutyRoster_iCal = PAGE +tx_participants_personalDutyRoster_iCal { + typeNum < plugin.tx_participants.view.settings.personalDutyRoster.iCal.pageType + config { disableAllHeaderCode = 1 xhtml_cleaning = none admPanel = 0 metaCharset = utf-8 additionalHeaders.10.header = Content-Type: text/calendar;charset=utf-8 - additionalHeaders.20.header = Content-Disposition: inline; filename=export.ics - #additionalHeaders = Content-Type:application/calendar;charset=utf-8| Content-Disposition: inline; filename=export.ics + additionalHeaders.20.header = Content-Disposition: inline; filename=export.txt disablePrefixComment = 1 } 10 = USER 10 { userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run extensionName = Participants - pluginName = DutyRoster + pluginName = PersonalDutyRoster vendorName = Cylancer } } -tx_participants_personalDutyRoster_ajaxConnect = PAGE -tx_participants_personalDutyRoster_ajaxConnect { - typeNum = 103 + + +tx_participants_dutyRoster_ajaxConnect = PAGE +tx_participants_dutyRoster_ajaxConnect { + typeNum < plugin.tx_participants.view.settings.dutyRoster.ajax.pageType config { disableAllHeaderCode = 1 additionalHeaders = Content-type:application/json xthml_cleaning = 0 admPanel = 0 + debug = 0 + no_cache = 1 } 10 = USER 10 { userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run extensionName = Participants - pluginName = PersonalDutyRoster + pluginName = DutyRoster vendorName = Cylancer } } -tx_participants_personalDutyRoster_iCal = PAGE -tx_participants_personalDutyRoster_iCal { - typeNum = 104 +tx_participants_dutyRosterICal = PAGE +tx_participants_dutyRosterICal { + typeNum < plugin.tx_participants.view.settings.dutyRoster.iCal.pageType + config { disableAllHeaderCode = 1 xhtml_cleaning = none @@ -70,15 +124,14 @@ tx_participants_personalDutyRoster_iCal { metaCharset = utf-8 additionalHeaders.10.header = Content-Type: text/calendar;charset=utf-8 additionalHeaders.20.header = Content-Disposition: inline; filename=export.ics + #additionalHeaders = Content-Type:application/calendar;charset=utf-8| Content-Disposition: inline; filename=export.ics disablePrefixComment = 1 } 10 = USER 10 { userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run extensionName = Participants - pluginName = PersonalDutyRoster + pluginName = DutyRoster vendorName = Cylancer } } - - diff --git a/Resources/Private/Language/de.locallang_db.xlf b/Resources/Private/Language/de.locallang_db.xlf index 2e8c135..3afcd18 100755 --- a/Resources/Private/Language/de.locallang_db.xlf +++ b/Resources/Private/Language/de.locallang_db.xlf @@ -167,7 +167,7 @@ Website - Benutzergruppen (Alternative für die öffentliche Anzeige) - + Date Datum @@ -175,9 +175,9 @@ Full day Ganztägig - - Time - Uhrzeit + + Begin time + Start-Zeit Duration in hours diff --git a/Resources/Private/Language/locallang_db.xlf b/Resources/Private/Language/locallang_db.xlf index e450796..e082184 100755 --- a/Resources/Private/Language/locallang_db.xlf +++ b/Resources/Private/Language/locallang_db.xlf @@ -133,14 +133,14 @@ Website user groups (alternative for the public view) - + Date Full day - - Time + + Begin time Duration in hours diff --git a/Resources/Private/Partials/DutyRoster/DefaultICalendarEntries.html b/Resources/Private/Partials/DutyRoster/DefaultICalendarEntries.html index b362fa6..5ea3a8d 100755 --- a/Resources/Private/Partials/DutyRoster/DefaultICalendarEntries.html +++ b/Resources/Private/Partials/DutyRoster/DefaultICalendarEntries.html @@ -27,8 +27,8 @@ CREATED:{event.crdate}T{event.crdate}Z DTSTAMP:{event.tstamp}T{event.tstamp}Z UID:dutyRoster-{event.uid}@{domain} -DTSTART;VALUE=DATETZID=Europe/Berlin:{event.date}{event.time} -DTEND;VALUE=DATETZID=Europe/Berlin: +1 day +{event.duration} hours +DTSTART;VALUE=DATETZID=Europe/Berlin:{event.beginTimestamp}{event.beginTimestamp} +DTEND;VALUE=DATETZID=Europe/Berlin: +1 day+{event.duration} hour SUMMARY:🚒 {event.eventType.title -> f:format.htmlspecialchars()} DESCRIPTION:{ug.title}, ( {event.description -> f:format.stripTags()} ) END:VEVENT diff --git a/Resources/Private/Partials/PersonalDutyRoster/DefaultICalendarEntries.html b/Resources/Private/Partials/PersonalDutyRoster/DefaultICalendarEntries.html index 83aa087..22570d3 100755 --- a/Resources/Private/Partials/PersonalDutyRoster/DefaultICalendarEntries.html +++ b/Resources/Private/Partials/PersonalDutyRoster/DefaultICalendarEntries.html @@ -27,8 +27,8 @@ CREATED:{commitment.event.crdate}T{commitment.event.crdate}Z DTSTAMP:{commitment.event.tstamp}T{commitment.event.tstamp}Z UID:dutyRoster-{commitment.event.uid}@{domain} -DTSTART;VALUE=DATETZID=Europe/Berlin:{commitment.event.date}{commitment.event.time} -DTEND;VALUE=DATETZID=Europe/Berlin: +1 day +{commitment.event.duration} hours +DTSTART;VALUE=DATETZID=Europe/Berlin:{commitment.event.beginTimestamp}{commitment.event.beginTimestamp} +DTEND;VALUE=DATETZID=Europe/Berlin: +1 day+{commitment.event.duration} hour SUMMARY:🚒 {commitment.event.eventType.title -> f:format.htmlspecialchars()} DESCRIPTION:{ug.title}, ( {commitment.event.description -> f:format.stripTags()} ) END:VEVENT diff --git a/Resources/Private/Templates/DutyRoster/Show.html b/Resources/Private/Templates/DutyRoster/Show.html index 7428bde..b3e27ab 100755 --- a/Resources/Private/Templates/DutyRoster/Show.html +++ b/Resources/Private/Templates/DutyRoster/Show.html @@ -51,10 +51,10 @@

{f:if(condition='{event.current} == 1', then:'upcomingDateNext')} {f:if(condition='{event.current} == 2', then:'upcomingDateNextNext')}" > -