Completed
Push — master ( 10cddd...9c5625 )
by Tim
01:59
created

TimeTimeTable::addFrequencyItems()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 8.6506
c 0
b 0
f 0
cc 7
nc 4
nop 3
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\Extbase\Utility\LocalizationUtility;
19
20
/**
21
 * Time service.
22
 */
23
class TimeTimeTable extends AbstractTimeTable
24
{
25
    /**
26
     * Modify the given times via the configuration.
27
     *
28
     * @param array         $times
29
     * @param Configuration $configuration
30
     */
31
    public function handleConfiguration(array &$times, Configuration $configuration)
32
    {
33
        $startTime = $configuration->isAllDay() ? null : $configuration->getStartTime();
34
        $endTime = $configuration->isAllDay() ? null : $configuration->getEndTime();
35
        $baseEntry = [
36
            'pid' => $configuration->getPid(),
37
            'start_date' => $configuration->getStartDate(),
38
            'end_date' => $configuration->getEndDate() ?: $configuration->getStartDate(),
39
            'start_time' => $startTime,
40
            'end_time' => $endTime,
41
            'all_day' => $configuration->isAllDay(),
42
            'open_end_time' => $configuration->isOpenEndTime(),
43
            'state' => $configuration->getState(),
44
        ];
45
        if (!$this->validateBaseEntry($baseEntry)) {
46
            return;
47
        }
48
        $times[$this->calculateEntryKey($baseEntry)] = $baseEntry;
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
        $tillDate = $configuration->getTillDate();
199
        $maxLimit = $this->getFrequencyLimitPerItem();
200
        $lastLoop = $baseEntry;
201
        for ($i = 0; $i < $maxLimit && (0 === $amountCounter || $i < $amountCounter); ++$i) {
202
            $loopEntry = $this->createNextLoopEntry($lastLoop, $frequencyIncrement);
203
204
            if ($tillDate instanceof \DateTimeInterface && $loopEntry['start_date'] > $tillDate) {
205
                break;
206
            }
207
208
            $lastLoop = $loopEntry;
209
            $times[$this->calculateEntryKey($loopEntry)] = $loopEntry;
210
        }
211
    }
212
213
    /**
214
     * Create the next loop entry.
215
     *
216
     * @param array  $loopEntry
217
     * @param string $modification
218
     *
219
     * @return array
220
     */
221
    protected function createNextLoopEntry(array $loopEntry, string $modification): array
222
    {
223
        /** @var $startDate \DateTime */
224
        $startDate = clone $loopEntry['start_date'];
225
        $startDate->modify($modification);
226
        $loopEntry['start_date'] = $startDate;
227
228
        /** @var $endDate \DateTime */
229
        $endDate = clone $loopEntry['end_date'];
230
        $endDate->modify($modification);
231
        $loopEntry['end_date'] = $endDate;
232
233
        return $loopEntry;
234
    }
235
236
    /**
237
     * Get the frequency date increment.
238
     *
239
     * @param Configuration $configuration
240
     *
241
     * @return string
242
     */
243
    protected function getFrequencyIncrement(Configuration $configuration)
244
    {
245
        $interval = $configuration->getCounterInterval() <= 1 ? 1 : $configuration->getCounterInterval();
246
        switch ($configuration->getFrequency()) {
247
            case Configuration::FREQUENCY_DAILY:
248
                $intervalValue = '+' . $interval . ' days';
249
                break;
250
            case Configuration::FREQUENCY_WEEKLY:
251
                $intervalValue = '+' . $interval . ' weeks';
252
                break;
253
            case Configuration::FREQUENCY_MONTHLY:
254
                if (Configuration::RECURRENCE_NONE !== $configuration->getRecurrence()) {
255
                    return false;
256
                }
257
                $intervalValue = '+' . $interval . ' months';
258
                break;
259
            case Configuration::FREQUENCY_YEARLY:
260
                if (Configuration::RECURRENCE_NONE !== $configuration->getRecurrence()) {
261
                    return false;
262
                }
263
                $intervalValue = '+' . $interval . ' years';
264
                break;
265
            default:
266
                $intervalValue = false;
267
        }
268
269
        return $intervalValue;
270
    }
271
272
    /**
273
     * Add recurrence items.
274
     *
275
     * @param array         $times
276
     * @param Configuration $configuration
277
     * @param array         $baseEntry
278
     */
279
    protected function addRecurrenceItems(array &$times, Configuration $configuration, array $baseEntry)
280
    {
281
        if (Configuration::RECURRENCE_NONE === $configuration->getRecurrence() || Configuration::DAY_NONE === $configuration->getDay()) {
282
            return;
283
        }
284
285
        $recurrenceService = GeneralUtility::makeInstance(RecurrenceService::class);
286
        $amountCounter = $configuration->getCounterAmount();
287
        $tillDate = $configuration->getTillDate();
288
        $maxLimit = $this->getFrequencyLimitPerItem();
289
        $lastLoop = $baseEntry;
290
        $intervalCounter = $configuration->getCounterInterval() <= 1 ? 1 : $configuration->getCounterInterval();
291
        for ($i = 0; $i < $maxLimit && (0 === $amountCounter || $i < $amountCounter); ++$i) {
292
            $loopEntry = $lastLoop;
293
294
            $dateTime = false;
295
            if (Configuration::FREQUENCY_MONTHLY === $configuration->getFrequency()) {
296
                $dateTime = $recurrenceService->getRecurrenceForNextMonth(
297
                    $loopEntry['start_date'],
298
                    $configuration->getRecurrence(),
299
                    $configuration->getDay(),
300
                    $intervalCounter
301
                );
302
            } elseif (Configuration::FREQUENCY_YEARLY === $configuration->getFrequency()) {
303
                $dateTime = $recurrenceService->getRecurrenceForNextYear(
304
                    $loopEntry['start_date'],
305
                    $configuration->getRecurrence(),
306
                    $configuration->getDay(),
307
                    $intervalCounter
308
                );
309
            }
310
            if (false === $dateTime) {
311
                break;
312
            }
313
314
            /** @var \DateInterval $interval */
315
            $interval = $loopEntry['start_date']->diff($dateTime);
316
            $frequencyIncrement = $interval->format('%R%a days');
317
318
            $loopEntry = $this->createNextLoopEntry($loopEntry, $frequencyIncrement);
319
320
            if ($tillDate instanceof \DateTimeInterface && $loopEntry['start_date'] > $tillDate) {
321
                break;
322
            }
323
324
            $lastLoop = $loopEntry;
325
            $times[$this->calculateEntryKey($loopEntry)] = $loopEntry;
326
        }
327
    }
328
329
    /**
330
     * Get the limit of the frequency.
331
     *
332
     * @return int
333
     */
334
    protected function getFrequencyLimitPerItem(): int
335
    {
336
        $maxLimit = (int) ConfigurationUtility::get('frequencyLimitPerItem');
337
        if ($maxLimit <= 0) {
338
            $maxLimit = 300;
339
        }
340
341
        return $maxLimit;
342
    }
343
}
344