Issues (1061)

Sources/Calendar.php (3 issues)

1
<?php
2
3
/**
4
 * This file has only one real task, showing the calendar.
5
 * Original module by Aaron O'Neil - [email protected]
6
 *
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines https://www.simplemachines.org
11
 * @copyright 2020 Simple Machines and individual contributors
12
 * @license https://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 RC2
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/**
21
 * Show the calendar.
22
 * It loads the specified month's events, holidays, and birthdays.
23
 * It requires the calendar_view permission.
24
 * It depends on the cal_enabled setting, and many of the other cal_ settings.
25
 * It uses the calendar_start_day theme option. (Monday/Sunday)
26
 * It uses the main sub template in the Calendar template.
27
 * It goes to the month and year passed in 'month' and 'year' by get or post.
28
 * It is accessed through ?action=calendar.
29
 *
30
 * @return void
31
 */
32
function CalendarMain()
33
{
34
	global $txt, $context, $modSettings, $scripturl, $options, $sourcedir, $user_info, $smcFunc;
35
36
	// Permissions, permissions, permissions.
37
	isAllowedTo('calendar_view');
38
39
	// Some global template resources.
40
	$context['calendar_resources'] = array(
41
		'min_year' => $modSettings['cal_minyear'],
42
		'max_year' => $modSettings['cal_maxyear'],
43
	);
44
45
	// Doing something other than calendar viewing?
46
	$subActions = array(
47
		'ical' => 'iCalDownload',
48
		'post' => 'CalendarPost',
49
	);
50
51
	if (isset($_GET['sa']) && isset($subActions[$_GET['sa']]))
52
		return call_helper($subActions[$_GET['sa']]);
53
54
	// You can't do anything if the calendar is off.
55
	if (empty($modSettings['cal_enabled']))
56
		fatal_lang_error('calendar_off', false);
57
58
	// This is gonna be needed...
59
	loadTemplate('Calendar');
60
	loadCSSFile('calendar.css', array('force_current' => false, 'validate' => true, 'rtl' => 'calendar.rtl.css'), 'smf_calendar');
61
62
	// Did the specify an individual event ID? If so, let's splice the year/month in to what we would otherwise be doing.
63
	if (isset($_GET['event']))
64
	{
65
		$evid = (int) $_GET['event'];
66
		if ($evid > 0)
67
		{
68
			$request = $smcFunc['db_query']('', '
69
				SELECT start_date
70
				FROM {db_prefix}calendar
71
				WHERE id_event = {int:event_id}',
72
				array(
73
					'event_id' => $evid,
74
				)
75
			);
76
			if ($row = $smcFunc['db_fetch_assoc']($request))
77
			{
78
				$_REQUEST['start_date'] = $row['start_date'];
79
80
				// We might use this later.
81
				$context['selected_event'] = $evid;
82
			}
83
			$smcFunc['db_free_result']($request);
84
		}
85
		unset ($_GET['event']);
86
	}
87
88
	// Set the page title to mention the calendar ;).
89
	$context['page_title'] = $txt['calendar'];
90
91
	// Ensure a default view is defined
92
	if (empty($options['calendar_default_view']))
93
		$options['calendar_default_view'] = 'viewlist';
94
95
	// What view do we want?
96
	if (isset($_GET['viewweek']))
97
		$context['calendar_view'] = 'viewweek';
98
	elseif (isset($_GET['viewmonth']))
99
		$context['calendar_view'] = 'viewmonth';
100
	elseif (isset($_GET['viewlist']))
101
		$context['calendar_view'] = 'viewlist';
102
	else
103
		$context['calendar_view'] = $options['calendar_default_view'];
104
105
	// Don't let search engines index the non-default calendar pages
106
	if ($context['calendar_view'] !== $options['calendar_default_view'])
107
		$context['robot_no_index'] = true;
108
109
	// Get the current day of month...
110
	require_once($sourcedir . '/Subs-Calendar.php');
111
	$today = getTodayInfo();
112
113
	// Need a start date for all views
114
	if (!empty($_REQUEST['start_date']))
115
	{
116
		$start_parsed = date_parse($_REQUEST['start_date']);
117
		if (empty($start_parsed['error_count']) && empty($start_parsed['warning_count']))
118
		{
119
			$_REQUEST['year'] = $start_parsed['year'];
120
			$_REQUEST['month'] = $start_parsed['month'];
121
			$_REQUEST['day'] = $start_parsed['day'];
122
		}
123
	}
124
	$year = !empty($_REQUEST['year']) ? (int) $_REQUEST['year'] : $today['year'];
125
	$month = !empty($_REQUEST['month']) ? (int) $_REQUEST['month'] : $today['month'];
126
	$day = !empty($_REQUEST['day']) ? (int) $_REQUEST['day'] : (!empty($_REQUEST['month']) ? 1 : $today['day']);
127
128
	$start_object = checkdate($month, $day, $year) === true ? date_create(implode('-', array($year, $month, $day))) : date_create(implode('-', array($today['year'], $today['month'], $today['day'])));
129
130
	// Need an end date for the list view
131
	if (!empty($_REQUEST['end_date']))
132
	{
133
		$end_parsed = date_parse($_REQUEST['end_date']);
134
		if (empty($end_parsed['error_count']) && empty($end_parsed['warning_count']))
135
		{
136
			$_REQUEST['end_year'] = $end_parsed['year'];
137
			$_REQUEST['end_month'] = $end_parsed['month'];
138
			$_REQUEST['end_day'] = $end_parsed['day'];
139
		}
140
	}
141
	$end_year = !empty($_REQUEST['end_year']) ? (int) $_REQUEST['end_year'] : null;
142
	$end_month = !empty($_REQUEST['end_month']) ? (int) $_REQUEST['end_month'] : null;
143
	$end_day = !empty($_REQUEST['end_day']) ? (int) $_REQUEST['end_day'] : null;
144
145
	$end_object = checkdate($end_month, $end_day, $end_year) === true ? date_create(implode('-', array($end_year, $end_month, $end_day))) : null;
146
147
	if (empty($end_object) || $start_object >= $end_object)
148
	{
149
		$num_days_shown = empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index'];
150
151
		$end_object = date_create(date_format($start_object, 'Y-m-d'));
152
153
		date_add($end_object, date_interval_create_from_date_string($num_days_shown . ' days'));
154
	}
155
156
	$curPage = array(
157
		'year' => date_format($start_object, 'Y'),
158
		'month' => date_format($start_object, 'n'),
159
		'day' => date_format($start_object, 'j'),
160
		'start_date' => date_format($start_object, 'Y-m-d'),
161
		'end_year' => date_format($end_object, 'Y'),
162
		'end_month' => date_format($end_object, 'n'),
163
		'end_day' => date_format($end_object, 'j'),
164
		'end_date' => date_format($end_object, 'Y-m-d'),
165
	);
166
167
	// Make sure the year and month are in valid ranges.
168
	if ($curPage['month'] < 1 || $curPage['month'] > 12)
169
		fatal_lang_error('invalid_month', false);
170
	if ($curPage['year'] < $modSettings['cal_minyear'] || $curPage['year'] > $modSettings['cal_maxyear'])
171
		fatal_lang_error('invalid_year', false);
172
	// If we have a day clean that too.
173
	if ($context['calendar_view'] != 'viewmonth')
174
	{
175
		$isValid = checkdate($curPage['month'], $curPage['day'], $curPage['year']);
176
		if (!$isValid)
177
			fatal_lang_error('invalid_day', false);
178
	}
179
180
	// Load all the context information needed to show the calendar grid.
181
	$calendarOptions = array(
182
		'start_day' => !empty($options['calendar_start_day']) ? $options['calendar_start_day'] : 0,
183
		'show_birthdays' => in_array($modSettings['cal_showbdays'], array(1, 2)),
184
		'show_events' => in_array($modSettings['cal_showevents'], array(1, 2)),
185
		'show_holidays' => in_array($modSettings['cal_showholidays'], array(1, 2)),
186
		'show_week_num' => true,
187
		'short_day_titles' => !empty($modSettings['cal_short_days']),
188
		'short_month_titles' => !empty($modSettings['cal_short_months']),
189
		'show_next_prev' => !empty($modSettings['cal_prev_next_links']),
190
		'show_week_links' => isset($modSettings['cal_week_links']) ? $modSettings['cal_week_links'] : 0,
191
	);
192
193
	// Load up the main view.
194
	if ($context['calendar_view'] == 'viewlist')
195
		$context['calendar_grid_main'] = getCalendarList($curPage['start_date'], $curPage['end_date'], $calendarOptions);
196
	elseif ($context['calendar_view'] == 'viewweek')
197
		$context['calendar_grid_main'] = getCalendarWeek($curPage['start_date'], $calendarOptions);
198
	else
199
		$context['calendar_grid_main'] = getCalendarGrid($curPage['start_date'], $calendarOptions);
200
201
	// Load up the previous and next months.
202
	$context['calendar_grid_current'] = getCalendarGrid($curPage['start_date'], $calendarOptions);
203
204
	// Only show previous month if it isn't pre-January of the min-year
205
	if ($context['calendar_grid_current']['previous_calendar']['year'] > $modSettings['cal_minyear'] || $curPage['month'] != 1)
206
		$context['calendar_grid_prev'] = getCalendarGrid($context['calendar_grid_current']['previous_calendar']['start_date'], $calendarOptions, true);
207
208
	// Only show next month if it isn't post-December of the max-year
209
	if ($context['calendar_grid_current']['next_calendar']['year'] < $modSettings['cal_maxyear'] || $curPage['month'] != 12)
210
		$context['calendar_grid_next'] = getCalendarGrid($context['calendar_grid_current']['next_calendar']['start_date'], $calendarOptions);
211
212
	// Basic template stuff.
213
	$context['allow_calendar_event'] = allowedTo('calendar_post');
214
215
	// If you don't allow events not linked to posts and you're not an admin, we have more work to do...
216
	if ($context['allow_calendar_event'] && empty($modSettings['cal_allow_unlinked']) && !$user_info['is_admin'])
217
	{
218
		$boards_can_post = boardsAllowedTo('post_new');
219
		$context['allow_calendar_event'] &= !empty($boards_can_post);
220
	}
221
222
	$context['can_post'] = $context['allow_calendar_event'];
223
	$context['current_day'] = $curPage['day'];
224
	$context['current_month'] = $curPage['month'];
225
	$context['current_year'] = $curPage['year'];
226
	$context['show_all_birthdays'] = isset($_GET['showbd']);
227
	$context['blocks_disabled'] = !empty($modSettings['cal_disable_prev_next']) ? 1 : 0;
228
229
	// Set the page title to mention the month or week, too
230
	if ($context['calendar_view'] != 'viewlist')
231
		$context['page_title'] .= ' - ' . ($context['calendar_view'] == 'viewweek' ? $context['calendar_grid_main']['week_title'] : $txt['months'][$context['current_month']] . ' ' . $context['current_year']);
232
233
	// Load up the linktree!
234
	$context['linktree'][] = array(
235
		'url' => $scripturl . '?action=calendar',
236
		'name' => $txt['calendar']
237
	);
238
	// Add the current month to the linktree.
239
	$context['linktree'][] = array(
240
		'url' => $scripturl . '?action=calendar;year=' . $context['current_year'] . ';month=' . $context['current_month'],
241
		'name' => $txt['months'][$context['current_month']] . ' ' . $context['current_year']
242
	);
243
	// If applicable, add the current week to the linktree.
244
	if ($context['calendar_view'] == 'viewweek')
245
		$context['linktree'][] = array(
246
			'url' => $scripturl . '?action=calendar;viewweek;year=' . $context['current_year'] . ';month=' . $context['current_month'] . ';day=' . $context['current_day'],
247
			'name' => $context['calendar_grid_main']['week_title'],
248
		);
249
250
	// Build the calendar button array.
251
	$context['calendar_buttons'] = array();
252
253
	if ($context['can_post'])
254
		$context['calendar_buttons']['post_event'] = array('text' => 'calendar_post_event', 'image' => 'calendarpe.png', 'url' => $scripturl . '?action=calendar;sa=post;month=' . $context['current_month'] . ';year=' . $context['current_year'] . ';' . $context['session_var'] . '=' . $context['session_id']);
255
256
	// Allow mods to add additional buttons here
257
	call_integration_hook('integrate_calendar_buttons');
258
}
259
260
/**
261
 * This function processes posting/editing/deleting a calendar event.
262
 *
263
 * 	- calls {@link Post.php|Post() Post()} function if event is linked to a post.
264
 *  - calls {@link Subs-Calendar.php|insertEvent() insertEvent()} to insert the event if not linked to post.
265
 *
266
 * It requires the calendar_post permission to use.
267
 * It uses the event_post sub template in the Calendar template.
268
 * It is accessed with ?action=calendar;sa=post.
269
 */
270
function CalendarPost()
271
{
272
	global $context, $txt, $user_info, $sourcedir, $scripturl;
273
	global $modSettings, $topic, $smcFunc;
274
275
	// Well - can they?
276
	isAllowedTo('calendar_post');
277
278
	// We need these for all kinds of useful functions.
279
	require_once($sourcedir . '/Subs-Calendar.php');
280
	require_once($sourcedir . '/Subs.php');
281
282
	// Cast this for safety...
283
	if (isset($_REQUEST['eventid']))
284
		$_REQUEST['eventid'] = (int) $_REQUEST['eventid'];
285
286
	// We want a fairly compact version of the time, but as close as possible to the user's settings.
287
	$time_string = strtr(get_date_or_time_format('time'), array(
288
		'%I' => '%l',
289
		'%H' => '%k',
290
		'%S' => '',
291
		'%r' => '%l:%M %p',
292
		'%R' => '%k:%M',
293
		'%T' => '%l:%M',
294
	));
295
296
	// Submitting?
297
	if (isset($_POST[$context['session_var']], $_REQUEST['eventid']))
298
	{
299
		checkSession();
300
301
		// Validate the post...
302
		if (!isset($_POST['link_to_board']))
303
			validateEventPost();
304
305
		// If you're not allowed to edit any events, you have to be the poster.
306
		if ($_REQUEST['eventid'] > 0 && !allowedTo('calendar_edit_any'))
307
			isAllowedTo('calendar_edit_' . (!empty($user_info['id']) && getEventPoster($_REQUEST['eventid']) == $user_info['id'] ? 'own' : 'any'));
308
309
		// New - and directing?
310
		if (isset($_POST['link_to_board']) || empty($modSettings['cal_allow_unlinked']))
311
		{
312
			$_REQUEST['calendar'] = 1;
313
			require_once($sourcedir . '/Post.php');
314
			return Post();
315
		}
316
		// New...
317
		elseif ($_REQUEST['eventid'] == -1)
318
		{
319
			$eventOptions = array(
320
				'board' => 0,
321
				'topic' => 0,
322
				'title' => $smcFunc['substr']($_REQUEST['evtitle'], 0, 100),
323
				'location' => $smcFunc['substr']($_REQUEST['event_location'], 0, 255),
324
				'member' => $user_info['id'],
325
			);
326
			insertEvent($eventOptions);
327
		}
328
329
		// Deleting...
330
		elseif (isset($_REQUEST['deleteevent']))
331
			removeEvent($_REQUEST['eventid']);
332
333
		// ... or just update it?
334
		else
335
		{
336
			$eventOptions = array(
337
				'title' => $smcFunc['substr']($_REQUEST['evtitle'], 0, 100),
338
				'location' => $smcFunc['substr']($_REQUEST['event_location'], 0, 255),
339
			);
340
			modifyEvent($_REQUEST['eventid'], $eventOptions);
341
		}
342
343
		updateSettings(array(
344
			'calendar_updated' => time(),
345
		));
346
347
		// No point hanging around here now...
348
		if (isset($_POST['start_date']))
349
		{
350
			$d = date_parse($_POST['start_date']);
351
			$year = $d['year'];
352
			$month = $d['month'];
353
			$day = $d['day'];
354
		}
355
		elseif (isset($_POST['start_datetime']))
356
		{
357
			$d = date_parse($_POST['start_datetime']);
358
			$year = $d['year'];
359
			$month = $d['month'];
360
			$day = $d['day'];
361
		}
362
		else
363
		{
364
			$today = getdate();
365
			$year = isset($_POST['year']) ? $_POST['year'] : $today['year'];
366
			$month = isset($_POST['month']) ? $_POST['month'] : $today['mon'];
367
			$day = isset($_POST['day']) ? $_POST['day'] : $today['mday'];
368
		}
369
		redirectexit($scripturl . '?action=calendar;month=' . $month . ';year=' . $year . ';day=' . $day);
370
	}
371
372
	// If we are not enabled... we are not enabled.
373
	if (empty($modSettings['cal_allow_unlinked']) && empty($_REQUEST['eventid']))
374
	{
375
		$_REQUEST['calendar'] = 1;
376
		require_once($sourcedir . '/Post.php');
377
		return Post();
378
	}
379
380
	// New?
381
	if (!isset($_REQUEST['eventid']))
382
	{
383
		$context['event'] = array(
384
			'boards' => array(),
385
			'board' => 0,
386
			'new' => 1,
387
			'eventid' => -1,
388
			'title' => '',
389
			'location' => '',
390
		);
391
392
		$eventDatetimes = getNewEventDatetimes();
393
		$context['event'] = array_merge($context['event'], $eventDatetimes);
394
395
		$context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year']));
396
	}
397
	else
398
	{
399
		$context['event'] = getEventProperties($_REQUEST['eventid']);
400
401
		if ($context['event'] === false)
402
			fatal_lang_error('no_access', false);
403
404
		// If it has a board, then they should be editing it within the topic.
405
		if (!empty($context['event']['topic']['id']) && !empty($context['event']['topic']['first_msg']))
406
		{
407
			// We load the board up, for a check on the board access rights...
408
			$topic = $context['event']['topic']['id'];
409
			loadBoard();
410
		}
411
412
		// Make sure the user is allowed to edit this event.
413
		if ($context['event']['member'] != $user_info['id'])
414
			isAllowedTo('calendar_edit_any');
415
		elseif (!allowedTo('calendar_edit_any'))
416
			isAllowedTo('calendar_edit_own');
417
	}
418
419
	// An all day event? Set up some nice defaults in case the user wants to change that
420
	if ($context['event']['allday'] == true)
421
	{
422
		$context['event']['tz'] = getUserTimezone();
423
		$context['event']['start_time'] = timeformat(time(), $time_string);
0 ignored issues
show
$time_string of type string is incompatible with the type boolean expected by parameter $show_today of timeformat(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

423
		$context['event']['start_time'] = timeformat(time(), /** @scrutinizer ignore-type */ $time_string);
Loading history...
424
		$context['event']['end_time'] = timeformat(time() + 3600, $time_string);
425
	}
426
	// Otherwise, just adjust these to look nice on the input form
427
	else
428
	{
429
		$context['event']['start_time'] = $context['event']['start_time_orig'];
430
		$context['event']['end_time'] = $context['event']['end_time_orig'];
431
	}
432
433
	// Need this so the user can select a timezone for the event.
434
	$context['all_timezones'] = smf_list_timezones($context['event']['start_date']);
435
436
	// If the event's timezone is not in SMF's standard list of time zones, try to fix it.
437
	if (!isset($context['all_timezones'][$context['event']['tz']]))
438
	{
439
		$later = strtotime('@' . $context['event']['start_timestamp'] . ' + 1 year');
440
		$tzinfo = timezone_transitions_get(timezone_open($context['event']['tz']), $context['event']['start_timestamp'], $later);
0 ignored issues
show
It seems like timezone_open($context['event']['tz']) can also be of type false; however, parameter $object of timezone_transitions_get() does only seem to accept DateTimeZone, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

440
		$tzinfo = timezone_transitions_get(/** @scrutinizer ignore-type */ timezone_open($context['event']['tz']), $context['event']['start_timestamp'], $later);
Loading history...
441
442
		$found = false;
443
		foreach ($context['all_timezones'] as $possible_tzid => $dummy)
444
		{
445
			$possible_tzinfo = timezone_transitions_get(timezone_open($possible_tzid), $context['event']['start_timestamp'], $later);
446
447
			if ($tzinfo === $possible_tzinfo)
448
			{
449
				$context['event']['tz'] = $possible_tzid;
450
				$found = true;
451
				break;
452
			}
453
		}
454
455
		// Hm. That's weird. Well, just prepend it to the list and let the user deal with it.
456
		if (!$found)
457
		{
458
			$d = date_create($context['event']['start_datetime'] . ' ' . $context['event']['tz']);
459
			$context['all_timezones'] = array($context['event']['tz'] => '[UTC' . date_format($d, 'P') . '] - ' . $context['event']['tz']) + $context['all_timezones'];
460
		}
461
	}
462
463
	// Get list of boards that can be posted in.
464
	$boards = boardsAllowedTo('post_new');
465
	if (empty($boards))
466
	{
467
		// You can post new events but can't link them to anything...
468
		$context['event']['categories'] = array();
469
	}
470
	else
471
	{
472
		// Load the list of boards and categories in the context.
473
		require_once($sourcedir . '/Subs-MessageIndex.php');
474
		$boardListOptions = array(
475
			'included_boards' => in_array(0, $boards) ? null : $boards,
476
			'not_redirection' => true,
477
			'use_permissions' => true,
478
			'selected_board' => $modSettings['cal_defaultboard'],
479
		);
480
		$context['event']['categories'] = getBoardList($boardListOptions);
481
	}
482
483
	// Template, sub template, etc.
484
	loadTemplate('Calendar');
485
	$context['sub_template'] = 'event_post';
486
487
	$context['page_title'] = isset($_REQUEST['eventid']) ? $txt['calendar_edit'] : $txt['calendar_post_event'];
488
	$context['linktree'][] = array(
489
		'name' => $context['page_title'],
490
	);
491
492
	loadDatePicker('#event_time_input .date_input');
493
	loadTimePicker('#event_time_input .time_input', $time_string);
494
	loadDatePair('#event_time_input', 'date_input', 'time_input');
495
	addInlineJavaScript('
496
	$("#allday").click(function(){
497
		$("#start_time").attr("disabled", this.checked);
498
		$("#end_time").attr("disabled", this.checked);
499
		$("#tz").attr("disabled", this.checked);
500
	});', true);
501
}
502
503
/**
504
 * This function offers up a download of an event in iCal 2.0 format.
505
 *
506
 * Follows the conventions in {@link https://tools.ietf.org/html/rfc5546 RFC5546}
507
 * Sets events as all day events since we don't have hourly events
508
 * Will honor and set multi day events
509
 * Sets a sequence number if the event has been modified
510
 *
511
 * @todo .... allow for week or month export files as well?
512
 */
513
function iCalDownload()
514
{
515
	global $smcFunc, $sourcedir, $modSettings, $webmaster_email, $mbname;
516
517
	// You can't export if the calendar export feature is off.
518
	if (empty($modSettings['cal_export']))
519
		fatal_lang_error('calendar_export_off', false);
520
521
	// Goes without saying that this is required.
522
	if (!isset($_REQUEST['eventid']))
523
		fatal_lang_error('no_access', false);
524
525
	// This is kinda wanted.
526
	require_once($sourcedir . '/Subs-Calendar.php');
527
528
	// Load up the event in question and check it exists.
529
	$event = getEventProperties($_REQUEST['eventid']);
530
531
	if ($event === false)
532
		fatal_lang_error('no_access', false);
533
534
	// Check the title isn't too long - iCal requires some formatting if so.
535
	$title = str_split($event['title'], 30);
536
	foreach ($title as $id => $line)
537
	{
538
		if ($id != 0)
539
			$title[$id] = ' ' . $title[$id];
540
		$title[$id] .= "\n";
541
	}
542
543
	// Format the dates.
544
	$datestamp = date('Ymd\THis\Z', time());
545
	$start_date = date_create($event['start_date'] . (isset($event['start_time']) ? ' ' . $event['start_time'] : '') . (isset($event['tz']) ? ' ' . $event['tz'] : ''));
546
	$end_date = date_create($event['end_date'] . (isset($event['end_time']) ? ' ' . $event['end_time'] : '') . (isset($event['tz']) ? ' ' . $event['tz'] : ''));
547
548
	if (!empty($event['start_time']))
549
	{
550
		$datestart = date_format($start_date, 'Ymd\THis');
551
		$dateend = date_format($end_date, 'Ymd\THis');
552
	}
553
	else
554
	{
555
		$datestart = date_format($start_date, 'Ymd');
556
557
		date_add($end_date, date_interval_create_from_date_string('1 day'));
0 ignored issues
show
It seems like $end_date can also be of type false; however, parameter $object of date_add() does only seem to accept DateTime, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

557
		date_add(/** @scrutinizer ignore-type */ $end_date, date_interval_create_from_date_string('1 day'));
Loading history...
558
		$dateend = date_format($end_date, 'Ymd');
559
	}
560
561
	// This is what we will be sending later
562
	$filecontents = '';
563
	$filecontents .= 'BEGIN:VCALENDAR' . "\n";
564
	$filecontents .= 'METHOD:PUBLISH' . "\n";
565
	$filecontents .= 'PRODID:-//SimpleMachines//' . SMF_FULL_VERSION . '//EN' . "\n";
566
	$filecontents .= 'VERSION:2.0' . "\n";
567
	$filecontents .= 'BEGIN:VEVENT' . "\n";
568
	// @TODO - Should be the members email who created the event rather than $webmaster_email.
569
	$filecontents .= 'ORGANIZER;CN="' . $event['realname'] . '":MAILTO:' . $webmaster_email . "\n";
570
	$filecontents .= 'DTSTAMP:' . $datestamp . "\n";
571
	$filecontents .= 'DTSTART' . (!empty($event['start_time']) ? ';TZID=' . $event['tz'] : ';VALUE=DATE') . ':' . $datestart . "\n";
572
573
	// event has a duration
574
	if ($event['start_iso_gmdate'] != $event['end_iso_gmdate'])
575
		$filecontents .= 'DTEND' . (!empty($event['end_time']) ? ';TZID=' . $event['tz'] : ';VALUE=DATE') . ':' . $dateend . "\n";
576
577
	// event has changed? advance the sequence for this UID
578
	if ($event['sequence'] > 0)
579
		$filecontents .= 'SEQUENCE:' . $event['sequence'] . "\n";
580
581
	if (!empty($event['location']))
582
		$filecontents .= 'LOCATION:' . str_replace(',', '\,', $event['location']) . "\n";
583
584
	$filecontents .= 'SUMMARY:' . implode('', $title);
585
	$filecontents .= 'UID:' . $event['eventid'] . '@' . str_replace(' ', '-', $mbname) . "\n";
586
	$filecontents .= 'END:VEVENT' . "\n";
587
	$filecontents .= 'END:VCALENDAR';
588
589
	// Send some standard headers.
590
	ob_end_clean();
591
	if (!empty($modSettings['enableCompressedOutput']))
592
		@ob_start('ob_gzhandler');
593
	else
594
		ob_start();
595
596
	// Send the file headers
597
	header('pragma: ');
598
	header('cache-control: no-cache');
599
	if (!isBrowser('gecko'))
600
		header('content-transfer-encoding: binary');
601
	header('expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT');
602
	header('last-modified: ' . gmdate('D, d M Y H:i:s', time()) . 'GMT');
603
	header('accept-ranges: bytes');
604
	header('connection: close');
605
	header('content-disposition: attachment; filename="' . $event['title'] . '.ics"');
606
	if (empty($modSettings['enableCompressedOutput']))
607
		header('content-length: ' . $smcFunc['strlen']($filecontents));
608
609
	// This is a calendar item!
610
	header('content-type: text/calendar');
611
612
	// Chuck out the card.
613
	echo $filecontents;
614
615
	// Off we pop - lovely!
616
	obExit(false);
617
}
618
619
/**
620
 * Nothing to see here. Move along.
621
 */
622
function clock()
623
{
624
	global $smcFunc, $settings, $context, $scripturl;
625
626
	$context['onimg'] = $settings['images_url'] . '/bbc/bbc_hoverbg.png';
627
	$context['offimg'] = $settings['images_url'] . '/bbc/bbc_bg.png';
628
629
	$context['page_title'] = 'Anyone know what time it is?';
630
	$context['linktree'][] = array(
631
		'url' => $scripturl . '?action=clock',
632
		'name' => 'Clock',
633
	);
634
	$context['robot_no_index'] = true;
635
636
	$omfg = isset($_REQUEST['omfg']);
637
	$bcd = !isset($_REQUEST['rb']) && !isset($_REQUEST['omfg']) && !isset($_REQUEST['time']);
638
639
	loadTemplate('Calendar');
640
641
	if ($bcd)
642
	{
643
		$context['sub_template'] = 'bcd';
644
		$context['linktree'][] = array('url' => $scripturl . '?action=clock;bcd', 'name' => 'BCD');
645
		$context['clockicons'] = $smcFunc['json_decode'](base64_decode('eyJoMSI6WzIsMV0sImgyIjpbOCw0LDIsMV0sIm0xIjpbNCwyLDFdLCJtMiI6WzgsNCwyLDFdLCJzMSI6WzQsMiwxXSwiczIiOls4LDQsMiwxXX0='), true);
646
	}
647
	elseif (!$omfg && !isset($_REQUEST['time']))
648
	{
649
		$context['sub_template'] = 'hms';
650
		$context['linktree'][] = array('url' => $scripturl . '?action=clock', 'name' => 'Binary');
651
		$context['clockicons'] = $smcFunc['json_decode'](base64_decode('eyJoIjpbMTYsOCw0LDIsMV0sIm0iOlszMiwxNiw4LDQsMiwxXSwicyI6WzMyLDE2LDgsNCwyLDFdfQ'), true);
652
	}
653
	elseif ($omfg)
654
	{
655
		$context['sub_template'] = 'omfg';
656
		$context['linktree'][] = array('url' => $scripturl . '?action=clock;omfg', 'name' => 'OMFG');
657
		$context['clockicons'] = $smcFunc['json_decode'](base64_decode('eyJ5ZWFyIjpbNjQsMzIsMTYsOCw0LDIsMV0sIm1vbnRoIjpbOCw0LDIsMV0sImRheSI6WzE2LDgsNCwyLDFdLCJob3VyIjpbMTYsOCw0LDIsMV0sIm1pbiI6WzMyLDE2LDgsNCwyLDFdLCJzZWMiOlszMiwxNiw4LDQsMiwxXX0='), true);
658
	}
659
	elseif (isset($_REQUEST['time']))
660
	{
661
		$context['sub_template'] = 'thetime';
662
		$time = getdate($_REQUEST['time'] == 'now' ? time() : (int) $_REQUEST['time']);
663
		$context['linktree'][] = array('url' => $scripturl . '?action=clock;time=' . $_REQUEST['time'], 'name' => 'Requested Time');
664
		$context['clockicons'] = array(
665
			'year' => array(
666
				64 => false,
667
				32 => false,
668
				16 => false,
669
				8 => false,
670
				4 => false,
671
				2 => false,
672
				1 => false
673
			),
674
			'month' => array(
675
				8 => false,
676
				4 => false,
677
				2 => false,
678
				1 => false
679
			),
680
			'day' => array(
681
				16 => false,
682
				4 => false,
683
				8 => false,
684
				2 => false,
685
				1 => false
686
			),
687
			'hour' => array(
688
				32 => false,
689
				16 => false,
690
				8 => false,
691
				4 => false,
692
				2 => false,
693
				1 => false
694
			),
695
			'min' => array(
696
				32 => false,
697
				16 => false,
698
				8 => false,
699
				4 => false,
700
				2 => false,
701
				1 => false
702
			),
703
			'sec' => array(
704
				32 => false,
705
				16 => false,
706
				8 => false,
707
				4 => false,
708
				2 => false,
709
				1 => false
710
			),
711
		);
712
713
		foreach ($context['clockicons'] as $t => $vs)
714
			foreach ($vs as $v => $dumb)
715
			{
716
				if ($$t >= $v)
717
				{
718
					$$t -= $v;
719
					$context['clockicons'][$t][$v] = true;
720
				}
721
			}
722
	}
723
}
724
725
?>