xref: /plugin/calendar/classes/EventManager.php (revision 815440faa45e800c80f925739a5d3cff27fa36d2)
1*815440faSAtari911<?php
2*815440faSAtari911/**
3*815440faSAtari911 * Calendar Plugin - Event Manager
4*815440faSAtari911 *
5*815440faSAtari911 * Consolidates event CRUD operations with proper file locking and caching.
6*815440faSAtari911 * This class is the single point of entry for all event data operations.
7*815440faSAtari911 *
8*815440faSAtari911 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
9*815440faSAtari911 * @author  DokuWiki Community
10*815440faSAtari911 * @version 7.0.8
11*815440faSAtari911 */
12*815440faSAtari911
13*815440faSAtari911if (!defined('DOKU_INC')) die();
14*815440faSAtari911
15*815440faSAtari911// Require dependencies
16*815440faSAtari911require_once __DIR__ . '/FileHandler.php';
17*815440faSAtari911require_once __DIR__ . '/EventCache.php';
18*815440faSAtari911
19*815440faSAtari911class CalendarEventManager {
20*815440faSAtari911
21*815440faSAtari911    /** @var string Base data directory */
22*815440faSAtari911    private static $baseDir = null;
23*815440faSAtari911
24*815440faSAtari911    /**
25*815440faSAtari911     * Get the base calendar data directory
26*815440faSAtari911     *
27*815440faSAtari911     * @return string Base directory path
28*815440faSAtari911     */
29*815440faSAtari911    private static function getBaseDir() {
30*815440faSAtari911        if (self::$baseDir === null) {
31*815440faSAtari911            self::$baseDir = DOKU_INC . 'data/meta/';
32*815440faSAtari911        }
33*815440faSAtari911        return self::$baseDir;
34*815440faSAtari911    }
35*815440faSAtari911
36*815440faSAtari911    /**
37*815440faSAtari911     * Get the data directory for a namespace
38*815440faSAtari911     *
39*815440faSAtari911     * @param string $namespace Namespace (empty for default)
40*815440faSAtari911     * @return string Directory path
41*815440faSAtari911     */
42*815440faSAtari911    private static function getNamespaceDir($namespace = '') {
43*815440faSAtari911        $dir = self::getBaseDir();
44*815440faSAtari911        if ($namespace) {
45*815440faSAtari911            $dir .= str_replace(':', '/', $namespace) . '/';
46*815440faSAtari911        }
47*815440faSAtari911        $dir .= 'calendar/';
48*815440faSAtari911        return $dir;
49*815440faSAtari911    }
50*815440faSAtari911
51*815440faSAtari911    /**
52*815440faSAtari911     * Get the event file path for a specific month
53*815440faSAtari911     *
54*815440faSAtari911     * @param string $namespace Namespace
55*815440faSAtari911     * @param int $year Year
56*815440faSAtari911     * @param int $month Month
57*815440faSAtari911     * @return string File path
58*815440faSAtari911     */
59*815440faSAtari911    private static function getEventFile($namespace, $year, $month) {
60*815440faSAtari911        $dir = self::getNamespaceDir($namespace);
61*815440faSAtari911        return $dir . sprintf('%04d-%02d.json', $year, $month);
62*815440faSAtari911    }
63*815440faSAtari911
64*815440faSAtari911    /**
65*815440faSAtari911     * Load events for a specific month
66*815440faSAtari911     *
67*815440faSAtari911     * @param string $namespace Namespace filter
68*815440faSAtari911     * @param int $year Year
69*815440faSAtari911     * @param int $month Month
70*815440faSAtari911     * @param bool $useCache Whether to use caching
71*815440faSAtari911     * @return array Events indexed by date
72*815440faSAtari911     */
73*815440faSAtari911    public static function loadMonth($namespace, $year, $month, $useCache = true) {
74*815440faSAtari911        // Check cache first
75*815440faSAtari911        if ($useCache) {
76*815440faSAtari911            $cached = CalendarEventCache::getMonthEvents($namespace, $year, $month);
77*815440faSAtari911            if ($cached !== null) {
78*815440faSAtari911                return $cached;
79*815440faSAtari911            }
80*815440faSAtari911        }
81*815440faSAtari911
82*815440faSAtari911        $events = [];
83*815440faSAtari911
84*815440faSAtari911        // Handle wildcards and multiple namespaces
85*815440faSAtari911        if (strpos($namespace, '*') !== false || strpos($namespace, ';') !== false) {
86*815440faSAtari911            $events = self::loadMonthMultiNamespace($namespace, $year, $month);
87*815440faSAtari911        } else {
88*815440faSAtari911            $eventFile = self::getEventFile($namespace, $year, $month);
89*815440faSAtari911            $events = CalendarFileHandler::readJson($eventFile);
90*815440faSAtari911        }
91*815440faSAtari911
92*815440faSAtari911        // Store in cache
93*815440faSAtari911        if ($useCache) {
94*815440faSAtari911            CalendarEventCache::setMonthEvents($namespace, $year, $month, $events);
95*815440faSAtari911        }
96*815440faSAtari911
97*815440faSAtari911        return $events;
98*815440faSAtari911    }
99*815440faSAtari911
100*815440faSAtari911    /**
101*815440faSAtari911     * Load events from multiple namespaces
102*815440faSAtari911     *
103*815440faSAtari911     * @param string $namespacePattern Namespace pattern (with * or ;)
104*815440faSAtari911     * @param int $year Year
105*815440faSAtari911     * @param int $month Month
106*815440faSAtari911     * @return array Merged events indexed by date
107*815440faSAtari911     */
108*815440faSAtari911    private static function loadMonthMultiNamespace($namespacePattern, $year, $month) {
109*815440faSAtari911        $allEvents = [];
110*815440faSAtari911        $namespaces = self::expandNamespacePattern($namespacePattern);
111*815440faSAtari911
112*815440faSAtari911        foreach ($namespaces as $ns) {
113*815440faSAtari911            $eventFile = self::getEventFile($ns, $year, $month);
114*815440faSAtari911            $events = CalendarFileHandler::readJson($eventFile);
115*815440faSAtari911
116*815440faSAtari911            foreach ($events as $date => $dateEvents) {
117*815440faSAtari911                if (!isset($allEvents[$date])) {
118*815440faSAtari911                    $allEvents[$date] = [];
119*815440faSAtari911                }
120*815440faSAtari911                foreach ($dateEvents as $event) {
121*815440faSAtari911                    // Ensure namespace is set
122*815440faSAtari911                    if (!isset($event['namespace'])) {
123*815440faSAtari911                        $event['namespace'] = $ns;
124*815440faSAtari911                    }
125*815440faSAtari911                    $allEvents[$date][] = $event;
126*815440faSAtari911                }
127*815440faSAtari911            }
128*815440faSAtari911        }
129*815440faSAtari911
130*815440faSAtari911        return $allEvents;
131*815440faSAtari911    }
132*815440faSAtari911
133*815440faSAtari911    /**
134*815440faSAtari911     * Expand namespace pattern to list of namespaces
135*815440faSAtari911     *
136*815440faSAtari911     * @param string $pattern Namespace pattern
137*815440faSAtari911     * @return array List of namespace paths
138*815440faSAtari911     */
139*815440faSAtari911    private static function expandNamespacePattern($pattern) {
140*815440faSAtari911        $namespaces = [];
141*815440faSAtari911
142*815440faSAtari911        // Handle semicolon-separated namespaces
143*815440faSAtari911        if (strpos($pattern, ';') !== false) {
144*815440faSAtari911            $parts = explode(';', $pattern);
145*815440faSAtari911            foreach ($parts as $part) {
146*815440faSAtari911                $expanded = self::expandNamespacePattern(trim($part));
147*815440faSAtari911                $namespaces = array_merge($namespaces, $expanded);
148*815440faSAtari911            }
149*815440faSAtari911            return array_unique($namespaces);
150*815440faSAtari911        }
151*815440faSAtari911
152*815440faSAtari911        // Handle wildcard
153*815440faSAtari911        if (strpos($pattern, '*') !== false) {
154*815440faSAtari911            // Get base directory
155*815440faSAtari911            $basePattern = str_replace('*', '', $pattern);
156*815440faSAtari911            $basePattern = rtrim($basePattern, ':');
157*815440faSAtari911
158*815440faSAtari911            $searchDir = self::getBaseDir();
159*815440faSAtari911            if ($basePattern) {
160*815440faSAtari911                $searchDir .= str_replace(':', '/', $basePattern) . '/';
161*815440faSAtari911            }
162*815440faSAtari911
163*815440faSAtari911            // Always include the base namespace
164*815440faSAtari911            $namespaces[] = $basePattern;
165*815440faSAtari911
166*815440faSAtari911            // Find subdirectories with calendar data
167*815440faSAtari911            if (is_dir($searchDir)) {
168*815440faSAtari911                $iterator = new RecursiveIteratorIterator(
169*815440faSAtari911                    new RecursiveDirectoryIterator($searchDir, RecursiveDirectoryIterator::SKIP_DOTS),
170*815440faSAtari911                    RecursiveIteratorIterator::SELF_FIRST
171*815440faSAtari911                );
172*815440faSAtari911
173*815440faSAtari911                foreach ($iterator as $file) {
174*815440faSAtari911                    if ($file->isDir() && $file->getFilename() === 'calendar') {
175*815440faSAtari911                        // Extract namespace from path
176*815440faSAtari911                        $path = dirname($file->getPathname());
177*815440faSAtari911                        $relPath = str_replace(self::getBaseDir(), '', $path);
178*815440faSAtari911                        $ns = str_replace('/', ':', trim($relPath, '/'));
179*815440faSAtari911                        if ($ns && !in_array($ns, $namespaces)) {
180*815440faSAtari911                            $namespaces[] = $ns;
181*815440faSAtari911                        }
182*815440faSAtari911                    }
183*815440faSAtari911                }
184*815440faSAtari911            }
185*815440faSAtari911
186*815440faSAtari911            return $namespaces;
187*815440faSAtari911        }
188*815440faSAtari911
189*815440faSAtari911        // Simple namespace
190*815440faSAtari911        return [$pattern];
191*815440faSAtari911    }
192*815440faSAtari911
193*815440faSAtari911    /**
194*815440faSAtari911     * Save an event
195*815440faSAtari911     *
196*815440faSAtari911     * @param array $eventData Event data
197*815440faSAtari911     * @param string|null $oldDate Previous date (for moves)
198*815440faSAtari911     * @param string|null $oldNamespace Previous namespace (for moves)
199*815440faSAtari911     * @return array Result with success status and event data
200*815440faSAtari911     */
201*815440faSAtari911    public static function saveEvent(array $eventData, $oldDate = null, $oldNamespace = null) {
202*815440faSAtari911        // Validate required fields
203*815440faSAtari911        if (empty($eventData['date']) || empty($eventData['title'])) {
204*815440faSAtari911            return ['success' => false, 'error' => 'Missing required fields'];
205*815440faSAtari911        }
206*815440faSAtari911
207*815440faSAtari911        $date = $eventData['date'];
208*815440faSAtari911        $namespace = $eventData['namespace'] ?? '';
209*815440faSAtari911        $eventId = $eventData['id'] ?? uniqid();
210*815440faSAtari911
211*815440faSAtari911        // Parse date
212*815440faSAtari911        if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $date, $matches)) {
213*815440faSAtari911            return ['success' => false, 'error' => 'Invalid date format'];
214*815440faSAtari911        }
215*815440faSAtari911        list(, $year, $month, $day) = $matches;
216*815440faSAtari911        $year = (int)$year;
217*815440faSAtari911        $month = (int)$month;
218*815440faSAtari911
219*815440faSAtari911        // Ensure ID is set
220*815440faSAtari911        $eventData['id'] = $eventId;
221*815440faSAtari911
222*815440faSAtari911        // Set created timestamp if new
223*815440faSAtari911        if (!isset($eventData['created'])) {
224*815440faSAtari911            $eventData['created'] = date('Y-m-d H:i:s');
225*815440faSAtari911        }
226*815440faSAtari911
227*815440faSAtari911        // Handle event move (different date or namespace)
228*815440faSAtari911        $dateChanged = $oldDate && $oldDate !== $date;
229*815440faSAtari911        $namespaceChanged = $oldNamespace !== null && $oldNamespace !== $namespace;
230*815440faSAtari911
231*815440faSAtari911        if ($dateChanged || $namespaceChanged) {
232*815440faSAtari911            // Delete from old location
233*815440faSAtari911            if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $oldDate ?: $date, $oldMatches)) {
234*815440faSAtari911                return ['success' => false, 'error' => 'Invalid old date format'];
235*815440faSAtari911            }
236*815440faSAtari911            list(, $oldYear, $oldMonth, $oldDay) = $oldMatches;
237*815440faSAtari911
238*815440faSAtari911            $oldEventFile = self::getEventFile($oldNamespace ?? $namespace, (int)$oldYear, (int)$oldMonth);
239*815440faSAtari911            $oldEvents = CalendarFileHandler::readJson($oldEventFile);
240*815440faSAtari911
241*815440faSAtari911            $deleteDate = $oldDate ?: $date;
242*815440faSAtari911            if (isset($oldEvents[$deleteDate])) {
243*815440faSAtari911                $oldEvents[$deleteDate] = array_values(array_filter(
244*815440faSAtari911                    $oldEvents[$deleteDate],
245*815440faSAtari911                    function($evt) use ($eventId) {
246*815440faSAtari911                        return $evt['id'] !== $eventId;
247*815440faSAtari911                    }
248*815440faSAtari911                ));
249*815440faSAtari911
250*815440faSAtari911                if (empty($oldEvents[$deleteDate])) {
251*815440faSAtari911                    unset($oldEvents[$deleteDate]);
252*815440faSAtari911                }
253*815440faSAtari911
254*815440faSAtari911                CalendarFileHandler::writeJson($oldEventFile, $oldEvents);
255*815440faSAtari911
256*815440faSAtari911                // Invalidate old location cache
257*815440faSAtari911                CalendarEventCache::invalidateMonth($oldNamespace ?? $namespace, (int)$oldYear, (int)$oldMonth);
258*815440faSAtari911            }
259*815440faSAtari911        }
260*815440faSAtari911
261*815440faSAtari911        // Load current events
262*815440faSAtari911        $eventFile = self::getEventFile($namespace, $year, $month);
263*815440faSAtari911        $events = CalendarFileHandler::readJson($eventFile);
264*815440faSAtari911
265*815440faSAtari911        // Ensure date array exists
266*815440faSAtari911        if (!isset($events[$date]) || !is_array($events[$date])) {
267*815440faSAtari911            $events[$date] = [];
268*815440faSAtari911        }
269*815440faSAtari911
270*815440faSAtari911        // Update or add event
271*815440faSAtari911        $found = false;
272*815440faSAtari911        foreach ($events[$date] as $key => $evt) {
273*815440faSAtari911            if ($evt['id'] === $eventId) {
274*815440faSAtari911                $events[$date][$key] = $eventData;
275*815440faSAtari911                $found = true;
276*815440faSAtari911                break;
277*815440faSAtari911            }
278*815440faSAtari911        }
279*815440faSAtari911
280*815440faSAtari911        if (!$found) {
281*815440faSAtari911            $events[$date][] = $eventData;
282*815440faSAtari911        }
283*815440faSAtari911
284*815440faSAtari911        // Save with atomic write
285*815440faSAtari911        if (!CalendarFileHandler::writeJson($eventFile, $events)) {
286*815440faSAtari911            return ['success' => false, 'error' => 'Failed to save event'];
287*815440faSAtari911        }
288*815440faSAtari911
289*815440faSAtari911        // Invalidate cache
290*815440faSAtari911        CalendarEventCache::invalidateMonth($namespace, $year, $month);
291*815440faSAtari911
292*815440faSAtari911        return ['success' => true, 'event' => $eventData];
293*815440faSAtari911    }
294*815440faSAtari911
295*815440faSAtari911    /**
296*815440faSAtari911     * Delete an event
297*815440faSAtari911     *
298*815440faSAtari911     * @param string $eventId Event ID
299*815440faSAtari911     * @param string $date Event date
300*815440faSAtari911     * @param string $namespace Namespace
301*815440faSAtari911     * @return array Result with success status
302*815440faSAtari911     */
303*815440faSAtari911    public static function deleteEvent($eventId, $date, $namespace = '') {
304*815440faSAtari911        if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $date, $matches)) {
305*815440faSAtari911            return ['success' => false, 'error' => 'Invalid date format'];
306*815440faSAtari911        }
307*815440faSAtari911        list(, $year, $month, $day) = $matches;
308*815440faSAtari911        $year = (int)$year;
309*815440faSAtari911        $month = (int)$month;
310*815440faSAtari911
311*815440faSAtari911        $eventFile = self::getEventFile($namespace, $year, $month);
312*815440faSAtari911        $events = CalendarFileHandler::readJson($eventFile);
313*815440faSAtari911
314*815440faSAtari911        if (!isset($events[$date])) {
315*815440faSAtari911            return ['success' => false, 'error' => 'Event not found'];
316*815440faSAtari911        }
317*815440faSAtari911
318*815440faSAtari911        $originalCount = count($events[$date]);
319*815440faSAtari911        $events[$date] = array_values(array_filter(
320*815440faSAtari911            $events[$date],
321*815440faSAtari911            function($evt) use ($eventId) {
322*815440faSAtari911                return $evt['id'] !== $eventId;
323*815440faSAtari911            }
324*815440faSAtari911        ));
325*815440faSAtari911
326*815440faSAtari911        if (count($events[$date]) === $originalCount) {
327*815440faSAtari911            return ['success' => false, 'error' => 'Event not found'];
328*815440faSAtari911        }
329*815440faSAtari911
330*815440faSAtari911        if (empty($events[$date])) {
331*815440faSAtari911            unset($events[$date]);
332*815440faSAtari911        }
333*815440faSAtari911
334*815440faSAtari911        if (!CalendarFileHandler::writeJson($eventFile, $events)) {
335*815440faSAtari911            return ['success' => false, 'error' => 'Failed to delete event'];
336*815440faSAtari911        }
337*815440faSAtari911
338*815440faSAtari911        // Invalidate cache
339*815440faSAtari911        CalendarEventCache::invalidateMonth($namespace, $year, $month);
340*815440faSAtari911
341*815440faSAtari911        return ['success' => true];
342*815440faSAtari911    }
343*815440faSAtari911
344*815440faSAtari911    /**
345*815440faSAtari911     * Get a single event by ID
346*815440faSAtari911     *
347*815440faSAtari911     * @param string $eventId Event ID
348*815440faSAtari911     * @param string $date Event date
349*815440faSAtari911     * @param string $namespace Namespace (use * for all)
350*815440faSAtari911     * @return array|null Event data or null if not found
351*815440faSAtari911     */
352*815440faSAtari911    public static function getEvent($eventId, $date, $namespace = '') {
353*815440faSAtari911        if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $date, $matches)) {
354*815440faSAtari911            return null;
355*815440faSAtari911        }
356*815440faSAtari911        list(, $year, $month, $day) = $matches;
357*815440faSAtari911
358*815440faSAtari911        $events = self::loadMonth($namespace, (int)$year, (int)$month);
359*815440faSAtari911
360*815440faSAtari911        if (!isset($events[$date])) {
361*815440faSAtari911            return null;
362*815440faSAtari911        }
363*815440faSAtari911
364*815440faSAtari911        foreach ($events[$date] as $event) {
365*815440faSAtari911            if ($event['id'] === $eventId) {
366*815440faSAtari911                return $event;
367*815440faSAtari911            }
368*815440faSAtari911        }
369*815440faSAtari911
370*815440faSAtari911        return null;
371*815440faSAtari911    }
372*815440faSAtari911
373*815440faSAtari911    /**
374*815440faSAtari911     * Find which namespace an event is in
375*815440faSAtari911     *
376*815440faSAtari911     * @param string $eventId Event ID
377*815440faSAtari911     * @param string $date Event date
378*815440faSAtari911     * @return string|null Namespace or null if not found
379*815440faSAtari911     */
380*815440faSAtari911    public static function findEventNamespace($eventId, $date) {
381*815440faSAtari911        if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $date, $matches)) {
382*815440faSAtari911            return null;
383*815440faSAtari911        }
384*815440faSAtari911        list(, $year, $month, $day) = $matches;
385*815440faSAtari911
386*815440faSAtari911        // Load all namespaces
387*815440faSAtari911        $events = self::loadMonth('*', (int)$year, (int)$month, false);
388*815440faSAtari911
389*815440faSAtari911        if (!isset($events[$date])) {
390*815440faSAtari911            return null;
391*815440faSAtari911        }
392*815440faSAtari911
393*815440faSAtari911        foreach ($events[$date] as $event) {
394*815440faSAtari911            if ($event['id'] === $eventId) {
395*815440faSAtari911                return $event['namespace'] ?? '';
396*815440faSAtari911            }
397*815440faSAtari911        }
398*815440faSAtari911
399*815440faSAtari911        return null;
400*815440faSAtari911    }
401*815440faSAtari911
402*815440faSAtari911    /**
403*815440faSAtari911     * Search events across all namespaces
404*815440faSAtari911     *
405*815440faSAtari911     * @param string $query Search query
406*815440faSAtari911     * @param array $options Search options (dateFrom, dateTo, namespace)
407*815440faSAtari911     * @return array Matching events
408*815440faSAtari911     */
409*815440faSAtari911    public static function searchEvents($query, array $options = []) {
410*815440faSAtari911        $results = [];
411*815440faSAtari911        $query = strtolower(trim($query));
412*815440faSAtari911
413*815440faSAtari911        $namespace = $options['namespace'] ?? '*';
414*815440faSAtari911        $dateFrom = $options['dateFrom'] ?? date('Y-m-01');
415*815440faSAtari911        $dateTo = $options['dateTo'] ?? date('Y-m-d', strtotime('+1 year'));
416*815440faSAtari911
417*815440faSAtari911        // Parse date range
418*815440faSAtari911        $startDate = new DateTime($dateFrom);
419*815440faSAtari911        $endDate = new DateTime($dateTo);
420*815440faSAtari911
421*815440faSAtari911        // Iterate through months
422*815440faSAtari911        $current = clone $startDate;
423*815440faSAtari911        $current->modify('first day of this month');
424*815440faSAtari911
425*815440faSAtari911        while ($current <= $endDate) {
426*815440faSAtari911            $year = (int)$current->format('Y');
427*815440faSAtari911            $month = (int)$current->format('m');
428*815440faSAtari911
429*815440faSAtari911            $events = self::loadMonth($namespace, $year, $month);
430*815440faSAtari911
431*815440faSAtari911            foreach ($events as $date => $dateEvents) {
432*815440faSAtari911                if ($date < $dateFrom || $date > $dateTo) {
433*815440faSAtari911                    continue;
434*815440faSAtari911                }
435*815440faSAtari911
436*815440faSAtari911                foreach ($dateEvents as $event) {
437*815440faSAtari911                    $titleMatch = stripos($event['title'] ?? '', $query) !== false;
438*815440faSAtari911                    $descMatch = stripos($event['description'] ?? '', $query) !== false;
439*815440faSAtari911
440*815440faSAtari911                    if ($titleMatch || $descMatch) {
441*815440faSAtari911                        $event['_date'] = $date;
442*815440faSAtari911                        $results[] = $event;
443*815440faSAtari911                    }
444*815440faSAtari911                }
445*815440faSAtari911            }
446*815440faSAtari911
447*815440faSAtari911            $current->modify('+1 month');
448*815440faSAtari911        }
449*815440faSAtari911
450*815440faSAtari911        // Sort by date
451*815440faSAtari911        usort($results, function($a, $b) {
452*815440faSAtari911            return strcmp($a['_date'], $b['_date']);
453*815440faSAtari911        });
454*815440faSAtari911
455*815440faSAtari911        return $results;
456*815440faSAtari911    }
457*815440faSAtari911
458*815440faSAtari911    /**
459*815440faSAtari911     * Get all namespaces that have calendar data
460*815440faSAtari911     *
461*815440faSAtari911     * @return array List of namespaces
462*815440faSAtari911     */
463*815440faSAtari911    public static function getNamespaces() {
464*815440faSAtari911        return self::expandNamespacePattern('*');
465*815440faSAtari911    }
466*815440faSAtari911
467*815440faSAtari911    /**
468*815440faSAtari911     * Debug log helper
469*815440faSAtari911     *
470*815440faSAtari911     * @param string $message Message to log
471*815440faSAtari911     */
472*815440faSAtari911    private static function log($message) {
473*815440faSAtari911        if (defined('CALENDAR_DEBUG') && CALENDAR_DEBUG) {
474*815440faSAtari911            error_log("[Calendar EventManager] $message");
475*815440faSAtari911        }
476*815440faSAtari911    }
477*815440faSAtari911}
478