Completed
Push — master ( 520c8e...7c36f5 )
by Tim
10:13
created

TimeTimeTable::createNextLoopEntry()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
cc 1
nc 1
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\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' => 0 === $endTime ? self::DAY_END : $endTime,
41
            'all_day' => $configuration->isAllDay(),
42
            'state' => $configuration->getState(),
43
        ];
44
        if (!$this->validateBaseEntry($baseEntry)) {
45
            return;
46
        }
47
        $times[$this->calculateEntryKey($baseEntry)] = $baseEntry;
48
        $this->addFrequencyItems($times, $configuration, $baseEntry);
49
        $this->addRecurrenceItems($times, $configuration, $baseEntry);
50
        $this->respectDynamicEndDates($times, $configuration);
51
    }
52
53
    /**
54
     * Respect the selection of dynamic enddates.
55
     *
56
     * @param array         $times
57
     * @param Configuration $configuration
58
     */
59
    protected function respectDynamicEndDates(array &$times, Configuration $configuration)
60
    {
61
        switch ($configuration->getEndDateDynamic()) {
62
            case Configuration::END_DYNAMIC_1_DAY:
63
                $callback = function ($entry) {
64
                    if ($entry['start_date'] instanceof \DateTime) {
65
                        $entry['end_date'] = clone $entry['start_date'];
66
                        $entry['end_date']->modify('+1 day');
67
                    }
68
69
                    return $entry;
70
                };
71
                break;
72
            case Configuration::END_DYNAMIC_1_WEEK:
73
                $callback = function ($entry) {
74
                    if ($entry['start_date'] instanceof \DateTime) {
75
                        $entry['end_date'] = clone $entry['start_date'];
76
                        $entry['end_date']->modify('+1 week');
77
                    }
78
79
                    return $entry;
80
                };
81
                break;
82
            case Configuration::END_DYNAMIC_END_WEEK:
83
                $callback = function ($entry) {
84
                    if ($entry['start_date'] instanceof \DateTime) {
85
                        $entry['end_date'] = clone $entry['start_date'];
86
                        $entry['end_date']->modify('monday next week');
87
                        $entry['end_date']->modify('-1 day');
88
                    }
89
90
                    return $entry;
91
                };
92
                break;
93
            case Configuration::END_DYNAMIC_END_MONTH:
94
                $callback = function ($entry) {
95
                    if ($entry['start_date'] instanceof \DateTime) {
96
                        $entry['end_date'] = clone $entry['start_date'];
97
                        $entry['end_date']->modify('last day of this month');
98
                    }
99
100
                    return $entry;
101
                };
102
                break;
103
            case Configuration::END_DYNAMIC_END_YEAR:
104
105
                $callback = function ($entry) {
106
                    if ($entry['start_date'] instanceof \DateTime) {
107
                        $entry['end_date'] = clone $entry['start_date'];
108
                        $entry['end_date']->setDate((int) $entry['end_date']->format('Y'), 12, 31);
109
                    }
110
111
                    return $entry;
112
                };
113
                break;
114
        }
115
116
        if (!isset($callback)) {
117
            return;
118
        }
119
120
        $new = [];
121
        foreach ($times as $hash => $record) {
122
            $target = $callback($record);
123
            $new[$this->calculateEntryKey($target)] = $target;
124
        }
125
        $times = $new;
126
    }
127
128
    /**
129
     * Validate the base entry, if there are logica mistakes.
130
     *
131
     * @param array $baseEntry
132
     *
133
     * @return bool
134
     */
135
    protected function validateBaseEntry(array $baseEntry): bool
136
    {
137
        $message = null;
138
        if (!($baseEntry['start_date'] instanceof \DateTimeInterface)) {
139
            $message = GeneralUtility::makeInstance(
140
                FlashMessage::class,
141
                'There is no usage for a event configuration without start date?!',
142
                'No start date?',
143
                FlashMessage::ERROR
144
            );
145
        } elseif ($baseEntry['end_date'] instanceof \DateTimeInterface && $baseEntry['start_date'] > $baseEntry['end_date']) {
146
            $message = GeneralUtility::makeInstance(
147
                FlashMessage::class,
148
                LocalizationUtility::translate(
149
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.date.message',
150
                    'calendarize'
151
                ),
152
                LocalizationUtility::translate(
153
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.date',
154
                    'calendarize'
155
                ),
156
                FlashMessage::ERROR
157
            );
158
        } 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) {
159
            $message = GeneralUtility::makeInstance(
160
                FlashMessage::class,
161
                LocalizationUtility::translate(
162
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.time.message',
163
                    'calendarize'
164
                ),
165
                LocalizationUtility::translate(
166
                    'LLL:EXT:calendarize/Resources/Private/Language/locallang.xlf:wrong.time',
167
                    'calendarize'
168
                ),
169
                FlashMessage::ERROR
170
            );
171
        }
172
        if ($message) {
173
            $flashMessageService = HelperUtility::create(FlashMessageService::class);
174
            $messageQueue = $flashMessageService->getMessageQueueByIdentifier();
175
            $messageQueue->addMessage($message);
176
177
            return false;
178
        }
179
180
        return true;
181
    }
182
183
    /**
184
     * Add frequency items.
185
     *
186
     * @param array         $times
187
     * @param Configuration $configuration
188
     * @param array         $baseEntry
189
     */
190
    protected function addFrequencyItems(array &$times, Configuration $configuration, array $baseEntry)
191
    {
192
        $frequencyIncrement = $this->getFrequencyIncrement($configuration);
193
        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...
194
            return;
195
        }
196
        $amountCounter = $configuration->getCounterAmount();
197
        $tillDate = $configuration->getTillDate();
198
        $maxLimit = $this->getFrequencyLimitPerItem();
199
        $lastLoop = $baseEntry;
200
        for ($i = 0; $i < $maxLimit && (0 === $amountCounter || $i < $amountCounter); ++$i) {
201
            $loopEntry = $this->createNextLoopEntry($lastLoop, $frequencyIncrement);
202
203
            if ($tillDate instanceof \DateTimeInterface && $loopEntry['start_date'] > $tillDate) {
204
                break;
205
            }
206
207
            $lastLoop = $loopEntry;
208
            $times[$this->calculateEntryKey($loopEntry)] = $loopEntry;
209
        }
210
    }
211
212
    /**
213
     * Create the next loop entry.
214
     *
215
     * @param array  $loopEntry
216
     * @param string $modification
217
     *
218
     * @return array
219
     */
220
    protected function createNextLoopEntry(array $loopEntry, string $modification): array
221
    {
222
        /** @var $startDate \DateTime */
223
        $startDate = clone $loopEntry['start_date'];
224
        $startDate->modify($modification);
225
        $loopEntry['start_date'] = $startDate;
226
227
        /** @var $endDate \DateTime */
228
        $endDate = clone $loopEntry['end_date'];
229
        $endDate->modify($modification);
230
        $loopEntry['end_date'] = $endDate;
231
232
        return $loopEntry;
233
    }
234
235
    /**
236
     * Get the frequency date increment.
237
     *
238
     * @param Configuration $configuration
239
     *
240
     * @return string
241
     */
242
    protected function getFrequencyIncrement(Configuration $configuration)
243
    {
244
        $interval = $configuration->getCounterInterval() <= 1 ? 1 : $configuration->getCounterInterval();
245
        switch ($configuration->getFrequency()) {
246
            case Configuration::FREQUENCY_DAILY:
247
                $intervalValue = '+' . $interval . ' days';
248
                break;
249
            case Configuration::FREQUENCY_WEEKLY:
250
                $intervalValue = '+' . $interval . ' weeks';
251
                break;
252
            case Configuration::FREQUENCY_MONTHLY:
253
                if (Configuration::RECURRENCE_NONE !== $configuration->getRecurrence()) {
254
                    return false;
255
                }
256
                $intervalValue = '+' . $interval . ' months';
257
                break;
258
            case Configuration::FREQUENCY_YEARLY:
259
                if (Configuration::RECURRENCE_NONE !== $configuration->getRecurrence()) {
260
                    return false;
261
                }
262
                $intervalValue = '+' . $interval . ' years';
263
                break;
264
            default:
265
                $intervalValue = false;
266
        }
267
268
        return $intervalValue;
269
    }
270
271
    /**
272
     * Add recurrence items.
273
     *
274
     * @param array         $times
275
     * @param Configuration $configuration
276
     * @param array         $baseEntry
277
     */
278
    protected function addRecurrenceItems(array &$times, Configuration $configuration, array $baseEntry)
279
    {
280
        if (Configuration::RECURRENCE_NONE === $configuration->getRecurrence() || Configuration::DAY_NONE === $configuration->getDay()) {
281
            return;
282
        }
283
284
        $recurrenceService = GeneralUtility::makeInstance(RecurrenceService::class);
285
        $amountCounter = $configuration->getCounterAmount();
286
        $tillDate = $configuration->getTillDate();
287
        $maxLimit = $this->getFrequencyLimitPerItem();
288
        $lastLoop = $baseEntry;
289
        for ($i = 0; $i < $maxLimit && (0 === $amountCounter || $i < $amountCounter); ++$i) {
290
            $loopEntry = $lastLoop;
291
292
            $dateTime = false;
293
            if (Configuration::FREQUENCY_MONTHLY === $configuration->getFrequency()) {
294
                $dateTime = $recurrenceService->getRecurrenceForNextMonth(
295
                    $loopEntry['start_date'],
296
                    $configuration->getRecurrence(),
297
                    $configuration->getDay()
298
                );
299
            } elseif (Configuration::FREQUENCY_YEARLY === $configuration->getFrequency()) {
300
                $dateTime = $recurrenceService->getRecurrenceForNextYear(
301
                    $loopEntry['start_date'],
302
                    $configuration->getRecurrence(),
303
                    $configuration->getDay()
304
                );
305
            }
306
            if (false === $dateTime) {
307
                break;
308
            }
309
310
            /** @var \DateInterval $interval */
311
            $interval = $loopEntry['start_date']->diff($dateTime);
312
            $frequencyIncrement = $interval->format('%R%a days');
313
314
            $loopEntry = $this->createNextLoopEntry($loopEntry, $frequencyIncrement);
315
316
            if ($tillDate instanceof \DateTimeInterface && $loopEntry['start_date'] > $tillDate) {
317
                break;
318
            }
319
320
            $lastLoop = $loopEntry;
321
            $times[$this->calculateEntryKey($loopEntry)] = $loopEntry;
322
        }
323
    }
324
325
    /**
326
     * Get the limit of the frequency.
327
     *
328
     * @return int
329
     */
330
    protected function getFrequencyLimitPerItem(): int
331
    {
332
        $maxLimit = (int) ConfigurationUtility::get('frequencyLimitPerItem');
333
        if ($maxLimit <= 0) {
334
            $maxLimit = 300;
335
        }
336
337
        return $maxLimit;
338
    }
339
}
340