Completed
Pull Request — master (#372)
by
unknown
02:00
created

TimeTimeTable::handleConfiguration()   B

Complexity

Conditions 7
Paths 24

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.5866
c 0
b 0
f 0
cc 7
nc 24
nop 2
1
<?php
2
3
/**
4
 * Time service.
5
 */
6
declare(strict_types=1);
7
8
namespace HDNET\Calendarize\Service\TimeTable;
9
10
use HDNET\Calendarize\Domain\Model\Configuration;
11
use HDNET\Calendarize\Service\RecurrenceService;
12
use HDNET\Calendarize\Utility\ConfigurationUtility;
13
use HDNET\Calendarize\Utility\DateTimeUtility;
14
use HDNET\Calendarize\Utility\HelperUtility;
15
use TYPO3\CMS\Core\Messaging\FlashMessage;
16
use TYPO3\CMS\Core\Messaging\FlashMessageService;
17
use TYPO3\CMS\Core\Utility\GeneralUtility;
18
use TYPO3\CMS\Core\Utility\MathUtility;
19
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
20
21
/**
22
 * Time service.
23
 */
24
class TimeTimeTable extends AbstractTimeTable
25
{
26
    /**
27
     * Modify the given times via the configuration.
28
     *
29
     * @param array         $times
30
     * @param Configuration $configuration
31
     */
32
    public function handleConfiguration(array &$times, Configuration $configuration)
33
    {
34
        $startTime = $configuration->isAllDay() ? null : $configuration->getStartTime();
35
        $endTime = $configuration->isAllDay() ? null : $configuration->getEndTime();
36
        $baseEntry = [
37
            'pid' => $configuration->getPid(),
38
            'start_date' => $configuration->getStartDate(),
39
            'end_date' => $configuration->getEndDate() ?: $configuration->getStartDate(),
40
            'start_time' => $startTime,
41
            'end_time' => $endTime,
42
            'all_day' => $configuration->isAllDay(),
43
            'open_end_time' => $configuration->isOpenEndTime(),
44
            'state' => $configuration->getState(),
45
        ];
46
        if (!$this->validateBaseEntry($baseEntry)) {
47
            return;
48
        }
49
        $frequency = $this->addFrequencyItems($times, $configuration, $baseEntry);
50
        $recurrence = $this->addRecurrenceItems($times, $configuration, $baseEntry);
51
        if ($frequency === false && $recurrence === false) {
52
            $times[$this->calculateEntryKey($baseEntry)] = $baseEntry;
53
        }
54
55
        $this->respectDynamicEndDates($times, $configuration);
56
    }
57
58
    /**
59
     * Respect the selection of dynamic enddates.
60
     *
61
     * @param array         $times
62
     * @param Configuration $configuration
63
     */
64
    protected function respectDynamicEndDates(array &$times, Configuration $configuration)
65
    {
66
        switch ($configuration->getEndDateDynamic()) {
67
            case Configuration::END_DYNAMIC_1_DAY:
68
                $callback = function ($entry) {
69
                    if ($entry['start_date'] instanceof \DateTime) {
70
                        $entry['end_date'] = clone $entry['start_date'];
71
                        $entry['end_date']->modify('+1 day');
72
                    }
73
74
                    return $entry;
75
                };
76
                break;
77
            case Configuration::END_DYNAMIC_1_WEEK:
78
                $callback = function ($entry) {
79
                    if ($entry['start_date'] instanceof \DateTime) {
80
                        $entry['end_date'] = clone $entry['start_date'];
81
                        $entry['end_date']->modify('+1 week');
82
                    }
83
84
                    return $entry;
85
                };
86
                break;
87
            case Configuration::END_DYNAMIC_END_WEEK:
88
                $callback = function ($entry) {
89
                    if ($entry['start_date'] instanceof \DateTime) {
90
                        $entry['end_date'] = clone $entry['start_date'];
91
                        $entry['end_date']->modify('monday next week');
92
                        $entry['end_date']->modify('-1 day');
93
                    }
94
95
                    return $entry;
96
                };
97
                break;
98
            case Configuration::END_DYNAMIC_END_MONTH:
99
                $callback = function ($entry) {
100
                    if ($entry['start_date'] instanceof \DateTime) {
101
                        $entry['end_date'] = clone $entry['start_date'];
102
                        $entry['end_date']->modify('last day of this month');
103
                    }
104
105
                    return $entry;
106
                };
107
                break;
108
            case Configuration::END_DYNAMIC_END_YEAR:
109
110
                $callback = function ($entry) {
111
                    if ($entry['start_date'] instanceof \DateTime) {
112
                        $entry['end_date'] = clone $entry['start_date'];
113
                        $entry['end_date']->setDate((int) $entry['end_date']->format('Y'), 12, 31);
114
                    }
115
116
                    return $entry;
117
                };
118
                break;
119
        }
120
121
        if (!isset($callback)) {
122
            return;
123
        }
124
125
        $new = [];
126
        foreach ($times as $hash => $record) {
127
            $target = $callback($record);
128
            $new[$this->calculateEntryKey($target)] = $target;
129
        }
130
        $times = $new;
131
    }
132
133
    /**
134
     * Validate the base entry, if there are logica mistakes.
135
     *
136
     * @param array $baseEntry
137
     *
138
     * @return bool
139
     */
140
    protected function validateBaseEntry(array $baseEntry): bool
141
    {
142
        $message = null;
143
        if (!($baseEntry['start_date'] instanceof \DateTimeInterface)) {
144
            $message = GeneralUtility::makeInstance(
145
                FlashMessage::class,
146
                'There is no usage for a event configuration without start date?!',
147
                'No start date?',
148
                FlashMessage::ERROR
149
            );
150
        } elseif ($baseEntry['end_date'] instanceof \DateTimeInterface && $baseEntry['start_date'] > $baseEntry['end_date']) {
151
            $message = GeneralUtility::makeInstance(
152
                FlashMessage::class,
153
                LocalizationUtility::translate(
154
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.date.message',
155
                    'calendarize'
156
                ),
157
                LocalizationUtility::translate(
158
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.date',
159
                    'calendarize'
160
                ),
161
                FlashMessage::ERROR
162
            );
163
        } elseif ($baseEntry['end_date'] instanceof \DateTimeInterface && !$baseEntry['all_day'] && $baseEntry['start_date']->format('d.m.Y') === $baseEntry['end_date']->format('d.m.Y') && $baseEntry['start_time'] % DateTimeUtility::SECONDS_DAY > $baseEntry['end_time'] % DateTimeUtility::SECONDS_DAY && $baseEntry['end_time'] > 0) {
164
            $message = GeneralUtility::makeInstance(
165
                FlashMessage::class,
166
                LocalizationUtility::translate(
167
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.time.message',
168
                    'calendarize'
169
                ),
170
                LocalizationUtility::translate(
171
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.time',
172
                    'calendarize'
173
                ),
174
                FlashMessage::ERROR
175
            );
176
        }
177
        if ($message) {
178
            $flashMessageService = HelperUtility::create(FlashMessageService::class);
179
            $messageQueue = $flashMessageService->getMessageQueueByIdentifier();
180
            $messageQueue->addMessage($message);
181
182
            return false;
183
        }
184
185
        return true;
186
    }
187
188
    /**
189
     * Add frequency items.
190
     *
191
     * @param  array  $times
192
     * @param  Configuration  $configuration
193
     * @param  array  $baseEntry
194
     * @return bool
195
     */
196
    protected function addFrequencyItems(array &$times, Configuration $configuration, array $baseEntry): bool
197
    {
198
        $frequencyIncrement = $this->getFrequencyIncrement($configuration);
199
        if (!$frequencyIncrement) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $frequencyIncrement of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
200
            return false;
201
        }
202
        $amountCounter = $configuration->getCounterAmount();
203
        $tillDateConfiguration = $this->getTillDateConfiguration($configuration, $baseEntry);
204
        $maxLimit = $this->getFrequencyLimitPerItem();
205
        $lastLoop = $baseEntry;
206
        for ($i = 0; $i < $maxLimit && (0 === $amountCounter || $i < $amountCounter); ++$i) {
207
            if ($i === 0) {
208
                $loopEntry = $lastLoop;
209
            } else {
210
                $loopEntry = $this->createNextLoopEntry($lastLoop, $frequencyIncrement);
211
            }
212
213
            if ($tillDateConfiguration['tillDate'] instanceof \DateTimeInterface && $loopEntry['start_date'] > $tillDateConfiguration['tillDate']) {
214
                break;
215
            }
216
217
            $lastLoop = $loopEntry;
218
219
            if ($tillDateConfiguration['tillDatePast'] instanceof \DateTimeInterface && $loopEntry['end_date'] < $tillDateConfiguration['tillDatePast']) {
220
                continue;
221
            }
222
223
            $times[$this->calculateEntryKey($loopEntry)] = $loopEntry;
224
        }
225
226
        return true;
227
    }
228
229
    /**
230
     * Create the next loop entry.
231
     *
232
     * @param array  $loopEntry
233
     * @param string $modification
234
     *
235
     * @return array
236
     */
237
    protected function createNextLoopEntry(array $loopEntry, string $modification): array
238
    {
239
        /** @var $startDate \DateTime */
240
        $startDate = clone $loopEntry['start_date'];
241
        $startDate->modify($modification);
242
        $loopEntry['start_date'] = $startDate;
243
244
        /** @var $endDate \DateTime */
245
        $endDate = clone $loopEntry['end_date'];
246
        $endDate->modify($modification);
247
        $loopEntry['end_date'] = $endDate;
248
249
        return $loopEntry;
250
    }
251
252
    /**
253
     * Get the frequency date increment.
254
     *
255
     * @param Configuration $configuration
256
     *
257
     * @return string
258
     */
259
    protected function getFrequencyIncrement(Configuration $configuration)
260
    {
261
        $interval = $configuration->getCounterInterval() <= 1 ? 1 : $configuration->getCounterInterval();
262
        switch ($configuration->getFrequency()) {
263
            case Configuration::FREQUENCY_DAILY:
264
                $intervalValue = '+' . $interval . ' days';
265
                break;
266
            case Configuration::FREQUENCY_WEEKLY:
267
                $intervalValue = '+' . $interval . ' weeks';
268
                break;
269
            case Configuration::FREQUENCY_MONTHLY:
270
                if (Configuration::RECURRENCE_NONE !== $configuration->getRecurrence()) {
271
                    return false;
272
                }
273
                $intervalValue = '+' . $interval . ' months';
274
                break;
275
            case Configuration::FREQUENCY_YEARLY:
276
                if (Configuration::RECURRENCE_NONE !== $configuration->getRecurrence()) {
277
                    return false;
278
                }
279
                $intervalValue = '+' . $interval . ' years';
280
                break;
281
            default:
282
                $intervalValue = false;
283
        }
284
285
        return $intervalValue;
286
    }
287
288
    /**
289
     * Add recurrence items.
290
     *
291
     * @param  array  $times
292
     * @param  Configuration  $configuration
293
     * @param  array  $baseEntry
294
     * @return bool
295
     */
296
    protected function addRecurrenceItems(array &$times, Configuration $configuration, array $baseEntry): bool
297
    {
298
        if (Configuration::RECURRENCE_NONE === $configuration->getRecurrence() || Configuration::DAY_NONE === $configuration->getDay()) {
299
            return false;
300
        }
301
302
        $recurrenceService = GeneralUtility::makeInstance(RecurrenceService::class);
303
        $amountCounter = $configuration->getCounterAmount();
304
        $tillDateConfiguration = $this->getTillDateConfiguration($configuration, $baseEntry);
305
        $maxLimit = $this->getFrequencyLimitPerItem();
306
        $lastLoop = $baseEntry;
307
        $intervalCounter = $configuration->getCounterInterval() <= 1 ? 1 : $configuration->getCounterInterval();
308
        for ($i = 0; $i < $maxLimit && (0 === $amountCounter || $i < $amountCounter); ++$i) {
309
            $loopEntry = $lastLoop;
310
311
            $dateTime = false;
312
            if (Configuration::FREQUENCY_MONTHLY === $configuration->getFrequency()) {
313
                $dateTime = $recurrenceService->getRecurrenceForNextMonth(
314
                    $loopEntry['start_date'],
315
                    $configuration->getRecurrence(),
316
                    $configuration->getDay(),
317
                    $intervalCounter
318
                );
319
            } elseif (Configuration::FREQUENCY_YEARLY === $configuration->getFrequency()) {
320
                $dateTime = $recurrenceService->getRecurrenceForNextYear(
321
                    $loopEntry['start_date'],
322
                    $configuration->getRecurrence(),
323
                    $configuration->getDay(),
324
                    $intervalCounter
325
                );
326
            }
327
            if (false === $dateTime) {
328
                break;
329
            }
330
331
            /** @var \DateInterval $interval */
332
            $interval = $loopEntry['start_date']->diff($dateTime);
333
            $frequencyIncrement = $interval->format('%R%a days');
334
335
            if ($i > 0) {
336
                $loopEntry = $this->createNextLoopEntry($loopEntry, $frequencyIncrement);
337
            }
338
339
            if ($tillDateConfiguration['tillDate'] instanceof \DateTimeInterface && $loopEntry['start_date'] > $tillDateConfiguration['tillDate']) {
340
                break;
341
            }
342
343
            $lastLoop = $loopEntry;
344
345
            if ($tillDateConfiguration['tillDatePast'] instanceof \DateTimeInterface && $loopEntry['end_date'] < $tillDateConfiguration['tillDatePast']) {
346
                continue;
347
            }
348
349
            $times[$this->calculateEntryKey($loopEntry)] = $loopEntry;
350
        }
351
352
        return true;
353
    }
354
355
    /**
356
     * @param  Configuration  $configuration
357
     * @param  array  $baseEntry
358
     * @return array
359
     */
360
    protected function getTillDateConfiguration(Configuration $configuration, array $baseEntry): array
361
    {
362
        // get values from item configuration
363
        $tillDate = $configuration->getTillDate();
364
        $tillDays = $configuration->getTillDays();
365
        $tillDaysRelative = $configuration->isTillDaysRelative();
366
        $tillDaysPast = $configuration->getTillDaysPast();
367
        $tillDatePast = null;
368
369
        // if not set get values from extension configuration
370
        if ($tillDays === null && $tillDaysPast === null) {
371
            $tillDays = ConfigurationUtility::get('tillDays');
372
            $tillDays = MathUtility::canBeInterpretedAsInteger($tillDays) ? (int)$tillDays : null;
373
            $tillDaysPast = ConfigurationUtility::get('tillDaysPast');
374
            $tillDaysPast = MathUtility::canBeInterpretedAsInteger($tillDaysPast) ? (int)$tillDaysPast : null;
375
        }
376
377
        if ($tillDaysRelative === null) {
378
            $tillDaysRelative = (bool)ConfigurationUtility::get('tillDaysRelative');
379
        }
380
381
        // calculate tillDate and tillDatePast based on configuration
382
        if (!$tillDate instanceof \DateTimeInterface && (is_int($tillDays) || is_int($tillDaysPast))) {
383
            // get base date for till tillDate and tillDatePast calculation
384
            /** @var \DateTime $tillDaysBaseDate */
385
            $tillDaysBaseDate = $baseEntry['start_date'];
386
            if ($tillDaysRelative) {
387
                $tillDaysBaseDate = DateTimeUtility::resetTime();
388
            }
389
390
            // get actual tillDate
391
            if (is_int($tillDays)) {
392
                --$tillDays; // - 1 day because we already take the current day into account
393
                $tillDate = clone $tillDaysBaseDate;
394
                $tillDate->modify('+' . $tillDays . ' day');
395
            }
396
397
            // get actual tillDatePast
398
            if ($tillDaysPast === 0) {
399
                $tillDatePast = clone $tillDaysBaseDate;
400
            } elseif (is_int($tillDaysPast) && $tillDaysPast > 0) {
401
                $tillDatePast = clone $tillDaysBaseDate;
402
                $tillDatePast->modify('-' . $tillDaysPast . ' day');
403
            }
404
        }
405
406
        return [
407
            'tillDate' => $tillDate,
408
            'tillDatePast' => $tillDatePast,
409
        ];
410
    }
411
412
    /**
413
     * Get the limit of the frequency.
414
     *
415
     * @return int
416
     */
417
    protected function getFrequencyLimitPerItem(): int
418
    {
419
        $maxLimit = (int) ConfigurationUtility::get('frequencyLimitPerItem');
420
        if ($maxLimit <= 0) {
421
            $maxLimit = 300;
422
        }
423
424
        return $maxLimit;
425
    }
426
}
427