Completed
Push — master ( aba4f0...63dc99 )
by Tim
03:38 queued 01:46
created

TimeTimeTable::getFrequencyLimitPerItem()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 0
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
        $this->addFrequencyItems($times, $configuration, $baseEntry);
50
        $this->addRecurrenceItems($times, $configuration, $baseEntry);
51
        $this->respectDynamicEndDates($times, $configuration);
52
    }
53
54
    /**
55
     * Respect the selection of dynamic enddates.
56
     *
57
     * @param array         $times
58
     * @param Configuration $configuration
59
     */
60
    protected function respectDynamicEndDates(array &$times, Configuration $configuration)
61
    {
62
        switch ($configuration->getEndDateDynamic()) {
63
            case Configuration::END_DYNAMIC_1_DAY:
64
                $callback = function ($entry) {
65
                    if ($entry['start_date'] instanceof \DateTime) {
66
                        $entry['end_date'] = clone $entry['start_date'];
67
                        $entry['end_date']->modify('+1 day');
68
                    }
69
70
                    return $entry;
71
                };
72
                break;
73
            case Configuration::END_DYNAMIC_1_WEEK:
74
                $callback = function ($entry) {
75
                    if ($entry['start_date'] instanceof \DateTime) {
76
                        $entry['end_date'] = clone $entry['start_date'];
77
                        $entry['end_date']->modify('+1 week');
78
                    }
79
80
                    return $entry;
81
                };
82
                break;
83
            case Configuration::END_DYNAMIC_END_WEEK:
84
                $callback = function ($entry) {
85
                    if ($entry['start_date'] instanceof \DateTime) {
86
                        $entry['end_date'] = clone $entry['start_date'];
87
                        $entry['end_date']->modify('monday next week');
88
                        $entry['end_date']->modify('-1 day');
89
                    }
90
91
                    return $entry;
92
                };
93
                break;
94
            case Configuration::END_DYNAMIC_END_MONTH:
95
                $callback = function ($entry) {
96
                    if ($entry['start_date'] instanceof \DateTime) {
97
                        $entry['end_date'] = clone $entry['start_date'];
98
                        $entry['end_date']->modify('last day of this month');
99
                    }
100
101
                    return $entry;
102
                };
103
                break;
104
            case Configuration::END_DYNAMIC_END_YEAR:
105
106
                $callback = function ($entry) {
107
                    if ($entry['start_date'] instanceof \DateTime) {
108
                        $entry['end_date'] = clone $entry['start_date'];
109
                        $entry['end_date']->setDate((int) $entry['end_date']->format('Y'), 12, 31);
110
                    }
111
112
                    return $entry;
113
                };
114
                break;
115
        }
116
117
        if (!isset($callback)) {
118
            return;
119
        }
120
121
        $new = [];
122
        foreach ($times as $hash => $record) {
123
            $target = $callback($record);
124
            $new[$this->calculateEntryKey($target)] = $target;
125
        }
126
        $times = $new;
127
    }
128
129
    /**
130
     * Validate the base entry, if there are logica mistakes.
131
     *
132
     * @param array $baseEntry
133
     *
134
     * @return bool
135
     */
136
    protected function validateBaseEntry(array $baseEntry): bool
137
    {
138
        $message = null;
139
        if (!($baseEntry['start_date'] instanceof \DateTimeInterface)) {
140
            $message = GeneralUtility::makeInstance(
141
                FlashMessage::class,
142
                'There is no usage for a event configuration without start date?!',
143
                'No start date?',
144
                FlashMessage::ERROR
145
            );
146
        } elseif ($baseEntry['end_date'] instanceof \DateTimeInterface && $baseEntry['start_date'] > $baseEntry['end_date']) {
147
            $message = GeneralUtility::makeInstance(
148
                FlashMessage::class,
149
                LocalizationUtility::translate(
150
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.date.message',
151
                    'calendarize'
152
                ),
153
                LocalizationUtility::translate(
154
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.date',
155
                    'calendarize'
156
                ),
157
                FlashMessage::ERROR
158
            );
159
        } 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) {
160
            $message = GeneralUtility::makeInstance(
161
                FlashMessage::class,
162
                LocalizationUtility::translate(
163
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.time.message',
164
                    'calendarize'
165
                ),
166
                LocalizationUtility::translate(
167
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.time',
168
                    'calendarize'
169
                ),
170
                FlashMessage::ERROR
171
            );
172
        }
173
        if ($message) {
174
            $flashMessageService = HelperUtility::create(FlashMessageService::class);
175
            $messageQueue = $flashMessageService->getMessageQueueByIdentifier();
176
            $messageQueue->addMessage($message);
177
178
            return false;
179
        }
180
181
        return true;
182
    }
183
184
    /**
185
     * Add frequency items.
186
     *
187
     * @param array         $times
188
     * @param Configuration $configuration
189
     * @param array         $baseEntry
190
     */
191
    protected function addFrequencyItems(array &$times, Configuration $configuration, array $baseEntry)
192
    {
193
        $frequencyIncrement = $this->getFrequencyIncrement($configuration);
194
        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...
195
            return;
196
        }
197
        $amountCounter = $configuration->getCounterAmount();
198
        $tillDateConfiguration = $this->getTillDateConfiguration($configuration, $baseEntry);
199
        $maxLimit = $this->getFrequencyLimitPerItem();
200
        $lastLoop = $baseEntry;
201
        for ($i = 0; $i < $maxLimit && (0 === $amountCounter || $i < $amountCounter); ++$i) {
202
            if ($i === 0) {
203
                $loopEntry = $lastLoop;
204
            } else {
205
                $loopEntry = $this->createNextLoopEntry($lastLoop, $frequencyIncrement);
206
            }
207
208
            if ($tillDateConfiguration['tillDate'] instanceof \DateTimeInterface && $loopEntry['start_date'] > $tillDateConfiguration['tillDate']) {
209
                break;
210
            }
211
212
            $lastLoop = $loopEntry;
213
214
            if ($tillDateConfiguration['tillDatePast'] instanceof \DateTimeInterface && $loopEntry['end_date'] < $tillDateConfiguration['tillDatePast']) {
215
                continue;
216
            }
217
218
            $times[$this->calculateEntryKey($loopEntry)] = $loopEntry;
219
        }
220
    }
221
222
    /**
223
     * Create the next loop entry.
224
     *
225
     * @param array  $loopEntry
226
     * @param string $modification
227
     *
228
     * @return array
229
     */
230
    protected function createNextLoopEntry(array $loopEntry, string $modification): array
231
    {
232
        /** @var $startDate \DateTime */
233
        $startDate = clone $loopEntry['start_date'];
234
        $startDate->modify($modification);
235
        $loopEntry['start_date'] = $startDate;
236
237
        /** @var $endDate \DateTime */
238
        $endDate = clone $loopEntry['end_date'];
239
        $endDate->modify($modification);
240
        $loopEntry['end_date'] = $endDate;
241
242
        return $loopEntry;
243
    }
244
245
    /**
246
     * Get the frequency date increment.
247
     *
248
     * @param Configuration $configuration
249
     *
250
     * @return string
251
     */
252
    protected function getFrequencyIncrement(Configuration $configuration)
253
    {
254
        $interval = $configuration->getCounterInterval() <= 1 ? 1 : $configuration->getCounterInterval();
255
        switch ($configuration->getFrequency()) {
256
            case Configuration::FREQUENCY_DAILY:
257
                $intervalValue = '+' . $interval . ' days';
258
                break;
259
            case Configuration::FREQUENCY_WEEKLY:
260
                $intervalValue = '+' . $interval . ' weeks';
261
                break;
262
            case Configuration::FREQUENCY_MONTHLY:
263
                if (Configuration::RECURRENCE_NONE !== $configuration->getRecurrence()) {
264
                    return false;
265
                }
266
                $intervalValue = '+' . $interval . ' months';
267
                break;
268
            case Configuration::FREQUENCY_YEARLY:
269
                if (Configuration::RECURRENCE_NONE !== $configuration->getRecurrence()) {
270
                    return false;
271
                }
272
                $intervalValue = '+' . $interval . ' years';
273
                break;
274
            default:
275
                $intervalValue = false;
276
        }
277
278
        return $intervalValue;
279
    }
280
281
    /**
282
     * Add recurrence items.
283
     *
284
     * @param array         $times
285
     * @param Configuration $configuration
286
     * @param array         $baseEntry
287
     */
288
    protected function addRecurrenceItems(array &$times, Configuration $configuration, array $baseEntry)
289
    {
290
        if (Configuration::RECURRENCE_NONE === $configuration->getRecurrence() || Configuration::DAY_NONE === $configuration->getDay()) {
291
            return;
292
        }
293
294
        $recurrenceService = GeneralUtility::makeInstance(RecurrenceService::class);
295
        $amountCounter = $configuration->getCounterAmount();
296
        $tillDateConfiguration = $this->getTillDateConfiguration($configuration, $baseEntry);
297
        $maxLimit = $this->getFrequencyLimitPerItem();
298
        $lastLoop = $baseEntry;
299
        $intervalCounter = $configuration->getCounterInterval() <= 1 ? 1 : $configuration->getCounterInterval();
300
        for ($i = 0; $i < $maxLimit && (0 === $amountCounter || $i < $amountCounter); ++$i) {
301
            $loopEntry = $lastLoop;
302
303
            $dateTime = false;
304
            if (Configuration::FREQUENCY_MONTHLY === $configuration->getFrequency()) {
305
                $dateTime = $recurrenceService->getRecurrenceForNextMonth(
306
                    $loopEntry['start_date'],
307
                    $configuration->getRecurrence(),
308
                    $configuration->getDay(),
309
                    $intervalCounter
310
                );
311
            } elseif (Configuration::FREQUENCY_YEARLY === $configuration->getFrequency()) {
312
                $dateTime = $recurrenceService->getRecurrenceForNextYear(
313
                    $loopEntry['start_date'],
314
                    $configuration->getRecurrence(),
315
                    $configuration->getDay(),
316
                    $intervalCounter
317
                );
318
            }
319
            if (false === $dateTime) {
320
                break;
321
            }
322
323
            /** @var \DateInterval $interval */
324
            $interval = $loopEntry['start_date']->diff($dateTime);
325
            $frequencyIncrement = $interval->format('%R%a days');
326
327
            if ($i > 0) {
328
                $loopEntry = $this->createNextLoopEntry($loopEntry, $frequencyIncrement);
329
            }
330
331
            if ($tillDateConfiguration['tillDate'] instanceof \DateTimeInterface && $loopEntry['start_date'] > $tillDateConfiguration['tillDate']) {
332
                break;
333
            }
334
335
            $lastLoop = $loopEntry;
336
337
            if ($tillDateConfiguration['tillDatePast'] instanceof \DateTimeInterface && $loopEntry['end_date'] < $tillDateConfiguration['tillDatePast']) {
338
                continue;
339
            }
340
341
            $times[$this->calculateEntryKey($loopEntry)] = $loopEntry;
342
        }
343
    }
344
345
    /**
346
     * @param  Configuration  $configuration
347
     * @param  array  $baseEntry
348
     * @return array
349
     */
350
    protected function getTillDateConfiguration(Configuration $configuration, array $baseEntry): array
351
    {
352
        // get values from item configuration
353
        $tillDate = $configuration->getTillDate();
354
        $tillDays = $configuration->getTillDays();
355
        $tillDaysRelative = $configuration->isTillDaysRelative();
356
        $tillDaysPast = $configuration->getTillDaysPast();
357
        $tillDatePast = null;
358
359
        // if not set get values from extension configuration
360
        if ($tillDays === null && $tillDaysPast === null) {
361
            $tillDays = ConfigurationUtility::get('tillDays');
362
            $tillDays = MathUtility::canBeInterpretedAsInteger($tillDays) ? (int)$tillDays : null;
363
            $tillDaysPast = ConfigurationUtility::get('tillDaysPast');
364
            $tillDaysPast = MathUtility::canBeInterpretedAsInteger($tillDaysPast) ? (int)$tillDaysPast : null;
365
        }
366
367
        if ($tillDaysRelative === null) {
368
            $tillDaysRelative = (bool)ConfigurationUtility::get('tillDaysRelative');
369
        }
370
371
        // calculate tillDate and tillDatePast based on configuration
372
        if (!$tillDate instanceof \DateTimeInterface && (is_int($tillDays) || is_int($tillDaysPast))) {
373
            // get base date for till tillDate and tillDatePast calculation
374
            /** @var \DateTime $tillDaysBaseDate */
375
            $tillDaysBaseDate = $baseEntry['start_date'];
376
            if ($tillDaysRelative) {
377
                $tillDaysBaseDate = DateTimeUtility::resetTime();
378
            }
379
380
            // get actual tillDate
381
            if (is_int($tillDays)) {
382
                --$tillDays; // - 1 day because we already take the current day into account
383
                $tillDate = clone $tillDaysBaseDate;
384
                $tillDate->modify('+' . $tillDays . ' day');
385
            }
386
387
            // get actual tillDatePast
388
            if ($tillDaysPast === 0) {
389
                $tillDatePast = clone $tillDaysBaseDate;
390
            } elseif (is_int($tillDaysPast) && $tillDaysPast > 0) {
391
                $tillDatePast = clone $tillDaysBaseDate;
392
                $tillDatePast->modify('-' . $tillDaysPast . ' day');
393
            }
394
        }
395
396
        return [
397
            'tillDate' => $tillDate,
398
            'tillDatePast' => $tillDatePast,
399
        ];
400
    }
401
402
    /**
403
     * Get the limit of the frequency.
404
     *
405
     * @return int
406
     */
407
    protected function getFrequencyLimitPerItem(): int
408
    {
409
        $maxLimit = (int) ConfigurationUtility::get('frequencyLimitPerItem');
410
        if ($maxLimit <= 0) {
411
            $maxLimit = 300;
412
        }
413
414
        return $maxLimit;
415
    }
416
}
417