1<?php 2/** 3 * DokuWiki Plugin calendar (Action Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author DokuWiki Community 7 */ 8 9if (!defined('DOKU_INC')) die(); 10 11class action_plugin_calendar extends DokuWiki_Action_Plugin { 12 13 public function register(Doku_Event_Handler $controller) { 14 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjax'); 15 $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'addAssets'); 16 } 17 18 public function handleAjax(Doku_Event $event, $param) { 19 if ($event->data !== 'plugin_calendar') return; 20 $event->preventDefault(); 21 $event->stopPropagation(); 22 23 $action = $_REQUEST['action'] ?? ''; 24 25 switch ($action) { 26 case 'save_event': 27 $this->saveEvent(); 28 break; 29 case 'delete_event': 30 $this->deleteEvent(); 31 break; 32 case 'get_event': 33 $this->getEvent(); 34 break; 35 case 'load_month': 36 $this->loadMonth(); 37 break; 38 case 'toggle_task': 39 $this->toggleTaskComplete(); 40 break; 41 default: 42 echo json_encode(['success' => false, 'error' => 'Unknown action']); 43 } 44 } 45 46 private function saveEvent() { 47 global $INPUT; 48 49 $namespace = $INPUT->str('namespace', ''); 50 $date = $INPUT->str('date'); 51 $eventId = $INPUT->str('eventId', ''); 52 $title = $INPUT->str('title'); 53 $time = $INPUT->str('time', ''); 54 $description = $INPUT->str('description', ''); 55 $color = $INPUT->str('color', '#3498db'); 56 $oldDate = $INPUT->str('oldDate', ''); // Track original date for moves 57 $isTask = $INPUT->bool('isTask', false); 58 $completed = $INPUT->bool('completed', false); 59 $endDate = $INPUT->str('endDate', ''); 60 61 if (!$date || !$title) { 62 echo json_encode(['success' => false, 'error' => 'Missing required fields']); 63 return; 64 } 65 66 list($year, $month, $day) = explode('-', $date); 67 68 $dataDir = DOKU_INC . 'data/meta/'; 69 if ($namespace) { 70 $dataDir .= str_replace(':', '/', $namespace) . '/'; 71 } 72 $dataDir .= 'calendar/'; 73 74 if (!is_dir($dataDir)) { 75 mkdir($dataDir, 0755, true); 76 } 77 78 $eventFile = $dataDir . sprintf('%04d-%02d.json', $year, $month); 79 80 $events = []; 81 if (file_exists($eventFile)) { 82 $events = json_decode(file_get_contents($eventFile), true); 83 } 84 85 // If editing and date changed, remove from old date first 86 if ($eventId && $oldDate && $oldDate !== $date) { 87 list($oldYear, $oldMonth, $oldDay) = explode('-', $oldDate); 88 $oldEventFile = $dataDir . sprintf('%04d-%02d.json', $oldYear, $oldMonth); 89 90 if (file_exists($oldEventFile)) { 91 $oldEvents = json_decode(file_get_contents($oldEventFile), true); 92 if (isset($oldEvents[$oldDate])) { 93 $oldEvents[$oldDate] = array_filter($oldEvents[$oldDate], function($evt) use ($eventId) { 94 return $evt['id'] !== $eventId; 95 }); 96 97 if (empty($oldEvents[$oldDate])) { 98 unset($oldEvents[$oldDate]); 99 } 100 101 file_put_contents($oldEventFile, json_encode($oldEvents, JSON_PRETTY_PRINT)); 102 } 103 } 104 } 105 106 if (!isset($events[$date])) { 107 $events[$date] = []; 108 } 109 110 $eventData = [ 111 'id' => $eventId ?: uniqid(), 112 'title' => $title, 113 'time' => $time, 114 'description' => $description, 115 'color' => $color, 116 'isTask' => $isTask, 117 'completed' => $completed, 118 'endDate' => $endDate, 119 'created' => date('Y-m-d H:i:s') 120 ]; 121 122 // If editing, replace existing event 123 if ($eventId) { 124 $found = false; 125 foreach ($events[$date] as $key => $evt) { 126 if ($evt['id'] === $eventId) { 127 $events[$date][$key] = $eventData; 128 $found = true; 129 break; 130 } 131 } 132 if (!$found) { 133 $events[$date][] = $eventData; 134 } 135 } else { 136 $events[$date][] = $eventData; 137 } 138 139 file_put_contents($eventFile, json_encode($events, JSON_PRETTY_PRINT)); 140 141 echo json_encode(['success' => true, 'events' => $events, 'eventId' => $eventData['id']]); 142 } 143 144 private function deleteEvent() { 145 global $INPUT; 146 147 $namespace = $INPUT->str('namespace', ''); 148 $date = $INPUT->str('date'); 149 $eventId = $INPUT->str('eventId'); 150 151 list($year, $month, $day) = explode('-', $date); 152 153 $dataDir = DOKU_INC . 'data/meta/'; 154 if ($namespace) { 155 $dataDir .= str_replace(':', '/', $namespace) . '/'; 156 } 157 $dataDir .= 'calendar/'; 158 159 $eventFile = $dataDir . sprintf('%04d-%02d.json', $year, $month); 160 161 if (file_exists($eventFile)) { 162 $events = json_decode(file_get_contents($eventFile), true); 163 164 if (isset($events[$date])) { 165 $events[$date] = array_filter($events[$date], function($event) use ($eventId) { 166 return $event['id'] !== $eventId; 167 }); 168 169 if (empty($events[$date])) { 170 unset($events[$date]); 171 } 172 173 file_put_contents($eventFile, json_encode($events, JSON_PRETTY_PRINT)); 174 } 175 } 176 177 echo json_encode(['success' => true]); 178 } 179 180 private function getEvent() { 181 global $INPUT; 182 183 $namespace = $INPUT->str('namespace', ''); 184 $date = $INPUT->str('date'); 185 $eventId = $INPUT->str('eventId'); 186 187 list($year, $month, $day) = explode('-', $date); 188 189 $dataDir = DOKU_INC . 'data/meta/'; 190 if ($namespace) { 191 $dataDir .= str_replace(':', '/', $namespace) . '/'; 192 } 193 $dataDir .= 'calendar/'; 194 195 $eventFile = $dataDir . sprintf('%04d-%02d.json', $year, $month); 196 197 if (file_exists($eventFile)) { 198 $events = json_decode(file_get_contents($eventFile), true); 199 200 if (isset($events[$date])) { 201 foreach ($events[$date] as $event) { 202 if ($event['id'] === $eventId) { 203 echo json_encode(['success' => true, 'event' => $event]); 204 return; 205 } 206 } 207 } 208 } 209 210 echo json_encode(['success' => false, 'error' => 'Event not found']); 211 } 212 213 private function loadMonth() { 214 global $INPUT; 215 216 $namespace = $INPUT->str('namespace', ''); 217 $year = $INPUT->int('year'); 218 $month = $INPUT->int('month'); 219 220 $dataDir = DOKU_INC . 'data/meta/'; 221 if ($namespace) { 222 $dataDir .= str_replace(':', '/', $namespace) . '/'; 223 } 224 $dataDir .= 'calendar/'; 225 226 $eventFile = $dataDir . sprintf('%04d-%02d.json', $year, $month); 227 228 $events = []; 229 if (file_exists($eventFile)) { 230 $events = json_decode(file_get_contents($eventFile), true); 231 } 232 233 echo json_encode(['success' => true, 'events' => $events, 'year' => $year, 'month' => $month]); 234 } 235 236 private function toggleTaskComplete() { 237 global $INPUT; 238 239 $namespace = $INPUT->str('namespace', ''); 240 $date = $INPUT->str('date'); 241 $eventId = $INPUT->str('eventId'); 242 $completed = $INPUT->bool('completed', false); 243 244 list($year, $month, $day) = explode('-', $date); 245 246 $dataDir = DOKU_INC . 'data/meta/'; 247 if ($namespace) { 248 $dataDir .= str_replace(':', '/', $namespace) . '/'; 249 } 250 $dataDir .= 'calendar/'; 251 252 $eventFile = $dataDir . sprintf('%04d-%02d.json', $year, $month); 253 254 if (file_exists($eventFile)) { 255 $events = json_decode(file_get_contents($eventFile), true); 256 257 if (isset($events[$date])) { 258 foreach ($events[$date] as $key => $event) { 259 if ($event['id'] === $eventId) { 260 $events[$date][$key]['completed'] = $completed; 261 break; 262 } 263 } 264 265 file_put_contents($eventFile, json_encode($events, JSON_PRETTY_PRINT)); 266 echo json_encode(['success' => true, 'events' => $events]); 267 return; 268 } 269 } 270 271 echo json_encode(['success' => false, 'error' => 'Event not found']); 272 } 273 274 public function addAssets(Doku_Event $event, $param) { 275 $event->data['link'][] = array( 276 'type' => 'text/css', 277 'rel' => 'stylesheet', 278 'href' => DOKU_BASE . 'lib/plugins/calendar/style.css' 279 ); 280 281 $event->data['script'][] = array( 282 'type' => 'text/javascript', 283 'src' => DOKU_BASE . 'lib/plugins/calendar/script.js' 284 ); 285 } 286} 287