1<?php 2$_SERVER['BASE_PAGE'] = 'cal.php'; 3include_once __DIR__ . '/include/prepend.inc'; 4 5$site_header_config = [ 6 "current" => "community", 7 "css" => ['calendar.css'], 8 "layout_span" => 12, 9]; 10 11/* 12 This script serves three different forms of the calendar data: 13 a monthly view ($cm, $cy) 14 a daily view ($cm, $cd, $cy) 15 an individual item view ($id) 16 For the last two, the month view is also displayed beneath the 17 specifically requested data. If we encounter an error, we have 18 a fallback to display the actual month/year. 19*/ 20 21$begun = false; $errors = []; 22$id = isset($_GET['id']) ? (int) $_GET['id'] : 0; 23$cy = isset($_GET['cy']) ? (int) $_GET['cy'] : 0; 24$cm = isset($_GET['cm']) ? (int) $_GET['cm'] : 0; 25$cd = isset($_GET['cd']) ? (int) $_GET['cd'] : 0; 26 27// If the year is not valid, set it to the current year 28// This excludes all the "too old", or "too far in the future" 29// calendar displays (so search engines can handle this page too) 30if ($cy != 0 && !valid_year($cy)) { 31 $cy = date("Y"); 32} 33 34// We need to look up an event with an ID 35if ($id) { 36 // Try to load event by ID and display header and info for that event 37 if ($event = load_event($id)) { 38 site_header("Event: " . stripslashes(htmlentities($event['sdesc'], ENT_QUOTES | ENT_IGNORE, 'UTF-8')), $site_header_config); 39 display_event($event, 0); 40 $begun = true; 41 } 42 // Unable to find event, put this to the error messages' list 43 else { 44 $errors[] = "There is no event for specified id ('" . htmlentities($id, ENT_QUOTES | ENT_IGNORE, 'UTF-8') . "')"; 45 } 46} 47 48// Year, month and day specified, display a daily view 49elseif ($cy && $cm && $cd) { 50 51 // Check if date is valid 52 if (checkdate($cm,$cd,$cy)) { 53 54 // Date integer for that day 55 $date = mktime(0, 0, 1, $cm, $cd, $cy); 56 57 // Try to load events for that day, and display them all 58 if ($events = load_events($date)) { 59 $site_header_config = ['classes' => 'calendar calendar-day'] + $site_header_config; 60 site_header("Events: " . date("F j, Y", $date), $site_header_config); 61 echo "<h2>", date("F j, Y", $date), "</h2>\n"; 62 foreach ($events as $event) { 63 display_event($event, 0); 64 echo "<br>"; 65 } 66 $begun = true; 67 } 68 69 // Unable to load events for that day 70 else { 71 $errors[] = "There are no events for the specified date (" . date("F j, Y",$date) . ")."; 72 } 73 } 74 75 // Wrong date specified 76 else { 77 $errors[] = "The specified date (" . htmlentities("$cy/$cm/$cd", ENT_QUOTES | ENT_IGNORE, 'UTF-8') . ") was not valid."; 78 unset($cm, $cd, $cy); 79 } 80} 81 82// Check if month and year is valid 83if ($cm && $cy && !checkdate($cm,1,$cy)) { 84 $errors[] = "The specified year and month (" . htmlentities("$cy, $cm", ENT_QUOTES | ENT_IGNORE, 'UTF-8') . ") are not valid."; 85 unset($cm, $cy); 86} 87 88// Give defaults for the month and day values if they were invalid 89if (empty($cm)) { $cm = date("m"); } 90if (empty($cy)) { $cy = date("Y"); } 91 92// Start of the month date 93$date = mktime(0, 0, 1, $cm, 1, $cy); 94 95if (!$begun) { 96 site_header("Events: " . date("F Y", $date), $site_header_config); 97?> 98<div class="tip"> 99 <p> 100 If you would like to suggest an upcoming event to be listed on this 101 calendar, you can use <a href="submit-event.php">our event submission 102 form</a>. 103 </p> 104 <p> 105 You can click on each of the events for details, or on the number for a day 106 to get the details for all of the events taking place that day. 107 </p> 108</div> 109<?php 110} 111 112// Get events list for a whole month 113$events = load_events($date, true); 114 115// If there was an error, or there are no events, this is an error 116if ($events === false || count($events) == 0) { 117 $errors[] = "No events found for this month"; 118} 119 120// If there were any error, display them 121if (count($errors) > 0) { 122 display_errors($errors); 123 site_footer(); 124 exit; 125} 126 127// Beginning and end of this month 128$bom = mktime(0, 0, 1, $cm, 1, $cy); 129$eom = mktime(0, 0, 1, $cm + 1, 0, $cy); 130 131// Link to previous month (but do not link to too early dates) 132$prev_link = (function () use ($cm, $cy) { 133 $lm = mktime(0, 0, 1, $cm, 0, $cy); 134 $year = date('Y', $lm); 135 if (!valid_year($year)) { 136 return ' '; 137 } 138 139 $month = date('m', $lm); 140 $monthName = date('F', $lm); 141 return sprintf('<a href="/cal.php?cm=%s&cy=%s">%s, %s</a>', 142 urlencode($month), 143 urlencode($year), 144 htmlentities($monthName), 145 htmlentities($year)); 146})(); 147 148// Link to next month (but do not link to too early dates) 149$next_link = (function () use ($cm, $cy) { 150 $nm = mktime(0, 0, 1, $cm + 1, 1, $cy); 151 $year = date('Y', $nm); 152 if (!valid_year($year)) { 153 return ' '; 154 } 155 156 $month = date('m', $nm); 157 $monthName = date('F', $nm); 158 return sprintf('<a href="/cal.php?cm=%s&cy=%s">%s, %s</a>', 159 urlencode($month), 160 urlencode($year), 161 htmlentities($monthName), 162 htmlentities($year)); 163})(); 164 165// Print out navigation links for previous and next month 166echo '<br><table id="calnav" width="100%" border="0" cellspacing="0" cellpadding="3">', 167 "\n<tr>", '<td align="left" width="33%">', $prev_link, '</td>', 168 '<td align="center" width="33%"><b>', htmlentities(date('F, Y', $bom)), '</b></td>', 169 '<td align="right" width="33%">', $next_link, "</td></tr>\n</table>\n"; 170 171// Begin the calendar table 172echo '<table id="cal" width="100%" border="1" cellspacing="0" cellpadding="3">', 173 "\n",'<tr>',"\n"; 174 175// Print out headers for weekdays 176for ($i = 0; $i < 7; $i++) { 177 echo '<th width="14%">', date("l",mktime(0,0,1,4,$i + 1,2001)), "</th>\n"; 178} 179echo "</tr>\n<tr>"; 180 181// Generate the requisite number of blank days to get things started 182for ($days = $i = date("w",$bom); $i > 0; $i--) { 183 echo '<td class="notaday"> </td>'; 184} 185 186// Print out all the days in this month 187for ($i = 1; $i <= date("t",$bom); $i++) { 188 189 // Print out day number and all events for the day 190 echo '<td><a class="day" href="/cal.php', "?cm=$cm&cd=$i&cy=$cy", 191 '">',$i,'</a>'; 192 display_events_for_day(date("Y-m-",$bom) . sprintf("%02d",$i), $events); 193 echo '</td>'; 194 195 // Break HTML table row if at end of week 196 if (++$days % 7 == 0) echo "</tr>\n<tr>"; 197} 198 199// Generate the requisite number of blank days to wrap things up 200for (; $days % 7; $days++) { 201 echo '<td class="notaday"> </td>'; 202} 203 204// End HTML table of events 205echo "</tr>\n</table>\n"; 206 207// Print out common footer 208site_footer(); 209 210// Generate the date on which a recurring event falls for a given month 211// $bom and $eom are the first and last day of the month to look at 212function date_for_recur($recur, $day, $bom, $eom) 213{ 214 215 // $day == 1 == 'Sunday' == date("w",'some sunday')+1 216 217 // ${recur}th $day of the month 218 if ($recur > 0) { 219 $bomd = date("w", $bom) + 1; 220 $days = (($day - $bomd + 7) % 7) + (($recur - 1) * 7); 221 return mktime(0,0,1, date("m",$bom), $days + 1, date("Y",$bom)); 222 } 223 224 // ${recur}th to last $day of the month 225 $eomd = date("w",$eom) + 1; 226 $days = (($eomd - $day + 7) % 7) + ((abs($recur) - 1) * 7); 227 228 return mktime(0, 0, 1, date("m", $bom) + 1, -$days, date("Y", $bom)); 229} 230 231// Display a <div> for each of the events that are on a given day 232function display_events_for_day($day, $events): void 233{ 234 // For preservation of state in the links 235 global $cm, $cy, $COUNTRY; 236 237 // For all events, try to find the events for this day 238 foreach ($events as $event) { 239 240 // Multiday event, which still lasts, or the event starts today 241 if (($event['type'] == 2 && $event['start'] <= $day && $event['end'] >= $day) 242 || ($event['start'] == $day)) { 243 echo '<div class="event">', 244 ($COUNTRY == $event['country'] ? "<strong>" : ""), 245 '<a class="cat' . $event['category'] . '" href="/cal.php', 246 "?id=$event[id]&cm=$cm&cy=$cy", '">', 247 stripslashes(htmlentities($event['sdesc'], ENT_QUOTES | ENT_IGNORE, 'UTF-8')), 248 '</a>', 249 ($COUNTRY == $event['country'] ? "</strong>" : ""), 250 '</div>'; 251 } 252 } 253} 254 255// Find a single event in the events file by ID 256function load_event($id) 257{ 258 // Open events CSV file, return on error 259 $fp = @fopen("backend/events.csv",'r'); 260 if (!$fp) { return false; } 261 262 // Read as we can, event by event 263 while (!feof($fp)) { 264 265 $event = read_event($fp); 266 267 // Return with the event, if it's ID is the one 268 // we search for (also close the file) 269 if ($event !== false && $event['id'] == $id) { 270 fclose($fp); 271 return $event; 272 } 273 } 274 275 // Close file, and return sign of failure 276 fclose($fp); 277 return false; 278} 279 280// Load a list of events. Either for a particular day ($from) or a whole 281// month (if second parameter specified with TRUE) 282function load_events($from, $whole_month = false) 283{ 284 // Take advantage of the equality behavior of this date format 285 $from_date = date("Y-m-d", $from); 286 $bom = mktime(0, 0, 1, date("m",$from), 1, date("Y",$from)); 287 $eom = mktime(0, 0, 1, date("m",$from) + 1, 0, date("Y",$from)); 288 $to_date = date("Y-m-d", $whole_month ? $eom : $from); 289 290 // Set arrays to their default 291 $events = $seen = []; 292 293 // Try to open the events file for reading, return if unable to 294 $fp = @fopen("backend/events.csv",'r'); 295 if (!$fp) { return false; } 296 297 // For all events, read in the event and check it if fits our scope 298 while (!feof($fp)) { 299 300 // Read the event data into $event, or continue with next 301 // line, if there was an error with this line 302 if (($event = read_event($fp)) === false) { 303 continue; 304 } 305 306 // Keep event's seen list up to date 307 // (for repeating events with the same ID) 308 if (!isset($seen[$event['id']])) { $seen[$event['id']] = 1; } 309 else { continue; } 310 311 // Check if event is in our scope, depending on type 312 switch ($event['type']) { 313 314 // Recurring event 315 case 3: 316 $date = date_for_recur($event['recur'], $event['recur_day'], $bom, $eom); 317 $event['start'] = date("Y-m-d", $date); 318 // Fall through. Now it is just like a single-day event 319 320 // Single-day event 321 case 1: 322 if ($event['start'] >= $from_date && $event['start'] <= $to_date) { 323 $events[] = $event; 324 } 325 break; 326 327 // Multi-day event 328 case 2: 329 if (($event['start'] >= $from_date && $event['start'] <= $to_date) 330 || ($event['end'] >= $from_date && $event['end'] <= $to_date) 331 || ($event['start'] <= $from_date && $event['end'] >= $to_date)) { 332 $events[] = $event; 333 } 334 break; 335 } 336 } 337 338 // Close file and return with results 339 fclose($fp); 340 return $events; 341} 342 343// Reads an event from the event listing 344// Parameter: opened event listing file 345function read_event($fp) 346{ 347 // We were unable to read a line from the file, return 348 if (($linearr = fgetcsv($fp, 8192)) === false) { 349 return false; 350 } 351 352 // Corrupt line in CSV file 353 if (count($linearr) < 13) { return false; } 354 355 // Get components 356 [ 357 $day, $month, $year, $country, 358 $sdesc, $id, $ldesc, $url, $recur, $tipo, $sdato, $edato, $category 359 ] = $linearr; 360 361 // Get info on recurring event 362 @[$recur, $recur_day] = explode(":", $recur, 2); 363 364 // Return with SQL-resultset like array 365 return [ 366 'id' => $id, 367 'type' => $tipo, 368 'start' => $sdato, 369 'end' => $edato, 370 'recur' => $recur, 371 'recur_day' => $recur_day, 372 'sdesc' => $sdesc, 373 'url' => $url, 374 'ldesc' => base64_decode($ldesc, false), 375 'country' => $country, 376 'category' => $category, 377 ]; 378} 379 380// We would not like to allow any year to be viewed, because 381// it would fool some [not clever enough] search engines 382function valid_year($year) 383{ 384 // Get current year and compare to one sent in 385 $current_year = date("Y"); 386 387 // We only allow this and the next year for displays 388 if ($year != $current_year && $year != $current_year + 1) { 389 return false; 390 } 391 392 // The year is all right 393 return true; 394} 395 396?> 397