Completed
Push — release-2.1 ( dd6be9...02c57b )
by John
33:22 queued 26:19
created

Subs-Calendar.php ➔ getCalendarList()   D

Complexity

Conditions 12
Paths 192

Size

Total Lines 93
Code Lines 50

Duplication

Lines 4
Ratio 4.3 %

Importance

Changes 0
Metric Value
cc 12
eloc 50
nc 192
nop 3
dl 4
loc 93
rs 4.6933
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file contains several functions for retrieving and manipulating calendar events, birthdays and holidays.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2017 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 3
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Get all birthdays within the given time range.
21
 * finds all the birthdays in the specified range of days.
22
 * works with birthdays set for no year, or any other year, and respects month and year boundaries.
23
 *
24
 * @param string $low_date The low end of the range, inclusive, in YYYY-MM-DD format
25
 * @param string $high_date The high end of the range, inclusive, in YYYY-MM-DD format
26
 * @return array An array of days, each of which is an array of birthday information for the context
27
 */
28
function getBirthdayRange($low_date, $high_date)
29
{
30
	global $smcFunc;
31
32
	// We need to search for any birthday in this range, and whatever year that birthday is on.
33
	$year_low = (int) substr($low_date, 0, 4);
34
	$year_high = (int) substr($high_date, 0, 4);
35
36
	// Collect all of the birthdays for this month.  I know, it's a painful query.
37
	$result = $smcFunc['db_query']('birthday_array', '
38
		SELECT id_member, real_name, YEAR(birthdate) AS birth_year, birthdate
39
		FROM {db_prefix}members
40
		WHERE YEAR(birthdate) != {string:year_one}
41
			AND MONTH(birthdate) != {int:no_month}
42
			AND DAYOFMONTH(birthdate) != {int:no_day}
43
			AND YEAR(birthdate) <= {int:max_year}
44
			AND (
45
				DATE_FORMAT(birthdate, {string:year_low}) BETWEEN {date:low_date} AND {date:high_date}' . ($year_low == $year_high ? '' : '
46
				OR DATE_FORMAT(birthdate, {string:year_high}) BETWEEN {date:low_date} AND {date:high_date}') . '
47
			)
48
			AND is_activated = {int:is_activated}',
49
		array(
50
			'is_activated' => 1,
51
			'no_month' => 0,
52
			'no_day' => 0,
53
			'year_one' => '0001',
54
			'year_low' => $year_low . '-%m-%d',
55
			'year_high' => $year_high . '-%m-%d',
56
			'low_date' => $low_date,
57
			'high_date' => $high_date,
58
			'max_year' => $year_high,
59
		)
60
	);
61
	$bday = array();
62
	while ($row = $smcFunc['db_fetch_assoc']($result))
63
	{
64
		if ($year_low != $year_high)
65
			$age_year = substr($row['birthdate'], 5) < substr($high_date, 5) ? $year_high : $year_low;
66
		else
67
			$age_year = $year_low;
68
69
		$bday[$age_year . substr($row['birthdate'], 4)][] = array(
70
			'id' => $row['id_member'],
71
			'name' => $row['real_name'],
72
			'age' => $row['birth_year'] > 4 && $row['birth_year'] <= $age_year ? $age_year - $row['birth_year'] : null,
73
			'is_last' => false
74
		);
75
	}
76
	$smcFunc['db_free_result']($result);
77
78
	ksort($bday);
79
80
	// Set is_last, so the themes know when to stop placing separators.
81 View Code Duplication
	foreach ($bday as $mday => $array)
82
		$bday[$mday][count($array) - 1]['is_last'] = true;
83
84
	return $bday;
85
}
86
87
/**
88
 * Get all calendar events within the given time range.
89
 *
90
 * - finds all the posted calendar events within a date range.
91
 * - both the earliest_date and latest_date should be in the standard YYYY-MM-DD format.
92
 * - censors the posted event titles.
93
 * - uses the current user's permissions if use_permissions is true, otherwise it does nothing "permission specific"
94
 *
95
 * @param string $low_date The low end of the range, inclusive, in YYYY-MM-DD format
96
 * @param string $high_date The high end of the range, inclusive, in YYYY-MM-DD format
97
 * @param bool $use_permissions Whether to use permissions
98
 * @return array Contextual information if use_permissions is true, and an array of the data needed to build that otherwise
99
 */
100
function getEventRange($low_date, $high_date, $use_permissions = true)
101
{
102
	global $scripturl, $modSettings, $user_info, $smcFunc, $context, $sourcedir;
103
	require_once($sourcedir . '/Subs.php');
104
105
	$low_object = date_create($low_date);
106
	$high_object = date_create($high_date);
107
108
	// Find all the calendar info...
109
	$result = $smcFunc['db_query']('', '
110
		SELECT
111
			cal.id_event, cal.title, cal.id_member, cal.id_topic, cal.id_board,
112
			cal.start_date, cal.end_date, cal.start_time, cal.end_time, cal.timezone, cal.location,
113
			b.member_groups, t.id_first_msg, t.approved, b.id_board
114
		FROM {db_prefix}calendar AS cal
115
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = cal.id_board)
116
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic)
117
		WHERE cal.start_date <= {date:high_date}
118
			AND cal.end_date >= {date:low_date}' . ($use_permissions ? '
119
			AND (cal.id_board = {int:no_board_link} OR {query_wanna_see_board})' : ''),
120
		array(
121
			'high_date' => $high_date,
122
			'low_date' => $low_date,
123
			'no_board_link' => 0,
124
		)
125
	);
126
	$events = array();
127
	while ($row = $smcFunc['db_fetch_assoc']($result))
128
	{
129
		// If the attached topic is not approved then for the moment pretend it doesn't exist
130
		if (!empty($row['id_first_msg']) && $modSettings['postmod_active'] && !$row['approved'])
131
			continue;
132
133
		// Force a censor of the title - as often these are used by others.
134
		censorText($row['title'], $use_permissions ? false : true);
135
136
		// Get the various time and date properties for this event
137
		list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
138
139
		// Sanity check
140 View Code Duplication
		if (!empty($start['error_count']) || !empty($start['warning_count']) || !empty($end['error_count']) || !empty($end['warning_count']))
141
			continue;
142
143
		// Get set up for the loop
144
		$start_object = date_create($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''), timezone_open($tz));
145
		$end_object = date_create($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''), timezone_open($tz));
146
		date_timezone_set($start_object, timezone_open(date_default_timezone_get()));
147
		date_timezone_set($end_object, timezone_open(date_default_timezone_get()));
148
		date_time_set($start_object, 0, 0, 0);
149
		date_time_set($end_object, 0, 0, 0);
150
		$start_date_string = date_format($start_object, 'Y-m-d');
151
		$end_date_string = date_format($end_object, 'Y-m-d');
152
153
		$cal_date = ($start_object >= $low_object) ? $start_object : $low_object;
154
		while ($cal_date <= $end_object && $cal_date <= $high_object)
155
		{
156
			$starts_today = (date_format($cal_date, 'Y-m-d') == $start_date_string);
157
			$ends_today = (date_format($cal_date, 'Y-m-d') == $end_date_string);
158
159
			$eventProperties = array(
160
					'id' => $row['id_event'],
161
					'title' => $row['title'],
162
					'year' => $start['year'],
163
					'month' => $start['month'],
164
					'day' => $start['day'],
165
					'hour' => !$allday ? $start['hour'] : null,
166
					'minute' => !$allday ? $start['minute'] : null,
167
					'second' => !$allday ? $start['second'] : null,
168
					'start_date' => $row['start_date'],
169
					'start_date_local' => $start['date_local'],
170
					'start_date_orig' => $start['date_orig'],
171
					'start_time' => !$allday ? $row['start_time'] : null,
172
					'start_time_local' => !$allday ? $start['time_local'] : null,
173
					'start_time_orig' => !$allday ? $start['time_orig'] : null,
174
					'start_timestamp' => $start['timestamp'],
175
					'start_datetime' => $start['datetime'],
176
					'start_iso_gmdate' => $start['iso_gmdate'],
177
					'end_year' => $end['year'],
178
					'end_month' => $end['month'],
179
					'end_day' => $end['day'],
180
					'end_hour' => !$allday ? $end['hour'] : null,
181
					'end_minute' => !$allday ? $end['minute'] : null,
182
					'end_second' => !$allday ? $end['second'] : null,
183
					'end_date' => $row['end_date'],
184
					'end_date_local' => $end['date_local'],
185
					'end_date_orig' => $end['date_orig'],
186
					'end_time' => !$allday ? $row['end_time'] : null,
187
					'end_time_local' => !$allday ? $end['time_local'] : null,
188
					'end_time_orig' => !$allday ? $end['time_orig'] : null,
189
					'end_timestamp' => $end['timestamp'],
190
					'end_datetime' => $end['datetime'],
191
					'end_iso_gmdate' => $end['iso_gmdate'],
192
					'allday' => $allday,
193
					'tz' => !$allday ? $tz : null,
194
					'tz_abbrev' => !$allday ? $tz_abbrev : null,
195
					'span' => $span,
196
					'is_last' => false,
197
					'id_board' => $row['id_board'],
198
					'is_selected' => !empty($context['selected_event']) && $context['selected_event'] == $row['id_event'],
199
					'starts_today' => $starts_today,
200
					'ends_today' => $ends_today,
201
					'location' => $row['location'],
202
			);
203
204
			// If we're using permissions (calendar pages?) then just ouput normal contextual style information.
205
			if ($use_permissions)
206
				$events[date_format($cal_date, 'Y-m-d')][] = array_merge($eventProperties, array(
207
					'href' => $row['id_board'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
208
					'link' => $row['id_board'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
209
					'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
210
					'modify_href' => $scripturl . '?action=' . ($row['id_board'] == 0 ? 'calendar;sa=post;' : 'post;msg=' . $row['id_first_msg'] . ';topic=' . $row['id_topic'] . '.0;calendar;') . 'eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
211
					'can_export' => !empty($modSettings['cal_export']) ? true : false,
212
					'export_href' => $scripturl . '?action=calendar;sa=ical;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
213
				));
214
			// Otherwise, this is going to be cached and the VIEWER'S permissions should apply... just put together some info.
215
			else
216
				$events[date_format($cal_date, 'Y-m-d')][] = array_merge($eventProperties, array(
217
					'href' => $row['id_topic'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
218
					'link' => $row['id_topic'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
219
					'can_edit' => false,
220
					'can_export' => !empty($modSettings['cal_export']) ? true : false,
221
					'topic' => $row['id_topic'],
222
					'msg' => $row['id_first_msg'],
223
					'poster' => $row['id_member'],
224
					'allowed_groups' => explode(',', $row['member_groups']),
225
				));
226
227
			date_add($cal_date, date_interval_create_from_date_string('1 day'));
228
		}
229
	}
230
	$smcFunc['db_free_result']($result);
231
232
	// If we're doing normal contextual data, go through and make things clear to the templates ;).
233
	if ($use_permissions)
234
	{
235 View Code Duplication
		foreach ($events as $mday => $array)
236
			$events[$mday][count($array) - 1]['is_last'] = true;
237
	}
238
239
	ksort($events);
240
241
	return $events;
242
}
243
244
/**
245
 * Get all holidays within the given time range.
246
 *
247
 * @param string $low_date The low end of the range, inclusive, in YYYY-MM-DD format
248
 * @param string $high_date The high end of the range, inclusive, in YYYY-MM-DD format
249
 * @return array An array of days, which are all arrays of holiday names.
250
 */
251
function getHolidayRange($low_date, $high_date)
252
{
253
	global $smcFunc;
254
255
	// Get the lowest and highest dates for "all years".
256
	if (substr($low_date, 0, 4) != substr($high_date, 0, 4))
257
		$allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_dec}
258
			OR event_date BETWEEN {date:all_year_jan} AND {date:all_year_high}';
259
	else
260
		$allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_high}';
261
262
	// Find some holidays... ;).
263
	$result = $smcFunc['db_query']('', '
264
		SELECT event_date, YEAR(event_date) AS year, title
265
		FROM {db_prefix}calendar_holidays
266
		WHERE event_date BETWEEN {date:low_date} AND {date:high_date}
267
			OR ' . $allyear_part,
268
		array(
269
			'low_date' => $low_date,
270
			'high_date' => $high_date,
271
			'all_year_low' => '0004' . substr($low_date, 4),
272
			'all_year_high' => '0004' . substr($high_date, 4),
273
			'all_year_jan' => '0004-01-01',
274
			'all_year_dec' => '0004-12-31',
275
		)
276
	);
277
	$holidays = array();
278
	while ($row = $smcFunc['db_fetch_assoc']($result))
279
	{
280
		if (substr($low_date, 0, 4) != substr($high_date, 0, 4))
281
			$event_year = substr($row['event_date'], 5) < substr($high_date, 5) ? substr($high_date, 0, 4) : substr($low_date, 0, 4);
282
		else
283
			$event_year = substr($low_date, 0, 4);
284
285
		$holidays[$event_year . substr($row['event_date'], 4)][] = $row['title'];
286
	}
287
	$smcFunc['db_free_result']($result);
288
289
	ksort($holidays);
290
291
	return $holidays;
292
}
293
294
/**
295
 * Does permission checks to see if an event can be linked to a board/topic.
296
 * checks if the current user can link the current topic to the calendar, permissions et al.
297
 * this requires the calendar_post permission, a forum moderator, or a topic starter.
298
 * expects the $topic and $board variables to be set.
299
 * if the user doesn't have proper permissions, an error will be shown.
300
 */
301
function canLinkEvent()
302
{
303
	global $user_info, $topic, $board, $smcFunc;
304
305
	// If you can't post, you can't link.
306
	isAllowedTo('calendar_post');
307
308
	// No board?  No topic?!?
309
	if (empty($board))
310
		fatal_lang_error('missing_board_id', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
311
	if (empty($topic))
312
		fatal_lang_error('missing_topic_id', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
313
314
	// Administrator, Moderator, or owner.  Period.
315
	if (!allowedTo('admin_forum') && !allowedTo('moderate_board'))
316
	{
317
		// Not admin or a moderator of this board. You better be the owner - or else.
318
		$result = $smcFunc['db_query']('', '
319
			SELECT id_member_started
320
			FROM {db_prefix}topics
321
			WHERE id_topic = {int:current_topic}
322
			LIMIT 1',
323
			array(
324
				'current_topic' => $topic,
325
			)
326
		);
327
		if ($row = $smcFunc['db_fetch_assoc']($result))
328
		{
329
			// Not the owner of the topic.
330
			if ($row['id_member_started'] != $user_info['id'])
331
				fatal_lang_error('not_your_topic', 'user');
332
		}
333
		// Topic/Board doesn't exist.....
334
		else
335
			fatal_lang_error('calendar_no_topic', 'general');
336
		$smcFunc['db_free_result']($result);
337
	}
338
}
339
340
/**
341
 * Returns date information about 'today' relative to the users time offset.
342
 * returns an array with the current date, day, month, and year.
343
 * takes the users time offset into account.
344
 * @return array An array of info about today, based on forum time. Has 'day', 'month', 'year' and 'date' (in YYYY-MM-DD format)
345
 */
346
function getTodayInfo()
347
{
348
	return array(
349
		'day' => (int) strftime('%d', forum_time()),
350
		'month' => (int) strftime('%m', forum_time()),
351
		'year' => (int) strftime('%Y', forum_time()),
352
		'date' => strftime('%Y-%m-%d', forum_time()),
353
	);
354
}
355
356
/**
357
 * Provides information (link, month, year) about the previous and next month.
358
 * @param int $month The month to display
359
 * @param int $year The year
360
 * @param array $calendarOptions An array of calendar options
361
 * @param bool $is_previous Whether this is the previous month
362
 * @return array A large array containing all the information needed to show a calendar grid for the given month
363
 */
364
function getCalendarGrid($month, $year, $calendarOptions, $is_previous = false)
365
{
366
	global $scripturl, $modSettings;
367
368
	// Eventually this is what we'll be returning.
369
	$calendarGrid = array(
370
		'week_days' => array(),
371
		'weeks' => array(),
372
		'short_day_titles' => !empty($calendarOptions['short_day_titles']),
373
		'short_month_titles' => !empty($calendarOptions['short_month_titles']),
374
		'highlight' => array(
375
			'events' => !empty($calendarOptions['highlight']['events']) && !empty($calendarOptions['show_events']) ? $calendarOptions['highlight']['events'] : 0,
376
			'holidays' => !empty($calendarOptions['highlight']['holidays']) && !empty($calendarOptions['show_holidays']) ? $calendarOptions['highlight']['holidays'] : 0,
377
			'birthdays' => !empty($calendarOptions['highlight']['birthdays']) && !empty($calendarOptions['show_birthdays']) ? $calendarOptions['highlight']['birthdays'] : 0,
378
		),
379
		'current_month' => $month,
380
		'current_year' => $year,
381
		'show_next_prev' => !empty($calendarOptions['show_next_prev']),
382
		'show_week_links' => isset($calendarOptions['show_week_links']) ? $calendarOptions['show_week_links'] : 0,
383
		'previous_calendar' => array(
384
			'year' => $month == 1 ? $year - 1 : $year,
385
			'month' => $month == 1 ? 12 : $month - 1,
386
			'disabled' => $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year),
387
		),
388
		'next_calendar' => array(
389
			'year' => $month == 12 ? $year + 1 : $year,
390
			'month' => $month == 12 ? 1 : $month + 1,
391
			'disabled' => $modSettings['cal_maxyear'] < ($month == 12 ? $year + 1 : $year),
392
		),
393
		'size' => empty($modSettings['cal_display_type']) ? 'large' : 'small',
394
	);
395
396
	// Get today's date.
397
	$today = getTodayInfo();
398
399
	// Get information about this month.
400
	$month_info = array(
401
		'first_day' => array(
402
			'day_of_week' => (int) strftime('%w', mktime(0, 0, 0, $month, 1, $year)),
403
			'week_num' => (int) strftime('%U', mktime(0, 0, 0, $month, 1, $year)),
404
			'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month, 1, $year)),
405
		),
406
		'last_day' => array(
407
			'day_of_month' => (int) strftime('%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)),
408
			'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)),
409
		),
410
		'first_day_of_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year)),
411
		'first_day_of_next_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year + 1)),
412
	);
413
414
	// The number of days the first row is shifted to the right for the starting day.
415
	$nShift = $month_info['first_day']['day_of_week'];
416
417
	$calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day'];
418
419
	// Starting any day other than Sunday means a shift...
420
	if (!empty($calendarOptions['start_day']))
421
	{
422
		$nShift -= $calendarOptions['start_day'];
423
		if ($nShift < 0)
424
			$nShift = 7 + $nShift;
425
	}
426
427
	// Number of rows required to fit the month.
428
	$nRows = floor(($month_info['last_day']['day_of_month'] + $nShift) / 7);
429
	if (($month_info['last_day']['day_of_month'] + $nShift) % 7)
430
		$nRows++;
431
432
	// Fetch the arrays for birthdays, posted events, and holidays.
433
	$bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
434
	$events = $calendarOptions['show_events'] ? getEventRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
435
	$holidays = $calendarOptions['show_holidays'] ? getHolidayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
436
437
	// Days of the week taking into consideration that they may want it to start on any day.
438
	$count = $calendarOptions['start_day'];
439
	for ($i = 0; $i < 7; $i++)
440
	{
441
		$calendarGrid['week_days'][] = $count;
442
		$count++;
443
		if ($count == 7)
444
			$count = 0;
445
	}
446
447
	// Iterate through each week.
448
	$calendarGrid['weeks'] = array();
449
	for ($nRow = 0; $nRow < $nRows; $nRow++)
450
	{
451
		// Start off the week - and don't let it go above 52, since that's the number of weeks in a year.
452
		$calendarGrid['weeks'][$nRow] = array(
453
			'days' => array(),
454
		);
455
456
		// And figure out all the days.
457
		for ($nCol = 0; $nCol < 7; $nCol++)
458
		{
459
			$nDay = ($nRow * 7) + $nCol - $nShift + 1;
460
461
			if ($nDay < 1 || $nDay > $month_info['last_day']['day_of_month'])
462
				$nDay = 0;
463
464
			$date = sprintf('%04d-%02d-%02d', $year, $month, $nDay);
465
466
			$calendarGrid['weeks'][$nRow]['days'][$nCol] = array(
467
				'day' => $nDay,
468
				'date' => $date,
469
				'is_today' => $date == $today['date'],
470
				'is_first_day' => !empty($calendarOptions['show_week_num']) && (($month_info['first_day']['day_of_week'] + $nDay - 1) % 7 == $calendarOptions['start_day']),
471
				'is_first_of_month' => $nDay === 1,
472
				'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
473
				'events' => !empty($events[$date]) ? $events[$date] : array(),
474
				'birthdays' => !empty($bday[$date]) ? $bday[$date] : array(),
475
			);
476
		}
477
	}
478
479
	// What is the last day of the month?
480
	if ($is_previous === true)
481
		$calendarGrid['last_of_month'] = $month_info['last_day']['day_of_month'];
482
483
	// We'll use the shift in the template.
484
	$calendarGrid['shift'] = $nShift;
485
486
	// Set the previous and the next month's links.
487
	$calendarGrid['previous_calendar']['href'] = $scripturl . '?action=calendar;viewmonth;year=' . $calendarGrid['previous_calendar']['year'] . ';month=' . $calendarGrid['previous_calendar']['month'];
488
	$calendarGrid['next_calendar']['href'] = $scripturl . '?action=calendar;viewmonth;year=' . $calendarGrid['next_calendar']['year'] . ';month=' . $calendarGrid['next_calendar']['month'];
489
490
	return $calendarGrid;
491
}
492
493
/**
494
 * Returns the information needed to show a calendar for the given week.
495
 * @param int $month The month
496
 * @param int $year The year
497
 * @param int $day The day
498
 * @param array $calendarOptions An array of calendar options
499
 * @return array An array of information needed to display the grid for a single week on the calendar
500
 */
501
function getCalendarWeek($month, $year, $day, $calendarOptions)
502
{
503
	global $scripturl, $modSettings, $txt;
504
505
	// Get today's date.
506
	$today = getTodayInfo();
507
508
	// What is the actual "start date" for the passed day.
509
	$calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day'];
510
	$day_of_week = (int) strftime('%w', mktime(0, 0, 0, $month, $day, $year));
511
	if ($day_of_week != $calendarOptions['start_day'])
512
	{
513
		// Here we offset accordingly to get things to the real start of a week.
514
		$date_diff = $day_of_week - $calendarOptions['start_day'];
515
		if ($date_diff < 0)
516
			$date_diff += 7;
517
		$new_timestamp = mktime(0, 0, 0, $month, $day, $year) - $date_diff * 86400;
518
		$day = (int) strftime('%d', $new_timestamp);
519
		$month = (int) strftime('%m', $new_timestamp);
520
		$year = (int) strftime('%Y', $new_timestamp);
521
	}
522
523
	// Now start filling in the calendar grid.
524
	$calendarGrid = array(
525
		'show_next_prev' => !empty($calendarOptions['show_next_prev']),
526
		// Previous week is easy - just step back one day.
527
		'previous_week' => array(
528
			'year' => $day == 1 ? ($month == 1 ? $year - 1 : $year) : $year,
529
			'month' => $day == 1 ? ($month == 1 ? 12 : $month - 1) : $month,
530
			'day' => $day == 1 ? 28 : $day - 1,
531
			'disabled' => $day < 7 && $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year),
532
		),
533
		'next_week' => array(
534
			'disabled' => $day > 25 && $modSettings['cal_maxyear'] < ($month == 12 ? $year + 1 : $year),
535
		),
536
		'size' => empty($modSettings['cal_display_type']) ? 'large' : 'small',
537
	);
538
539
	// The next week calculation requires a bit more work.
540
	$curTimestamp = mktime(0, 0, 0, $month, $day, $year);
541
	$nextWeekTimestamp = $curTimestamp + 604800;
542
	$calendarGrid['next_week']['day'] = (int) strftime('%d', $nextWeekTimestamp);
543
	$calendarGrid['next_week']['month'] = (int) strftime('%m', $nextWeekTimestamp);
544
	$calendarGrid['next_week']['year'] = (int) strftime('%Y', $nextWeekTimestamp);
545
546
	// Fetch the arrays for birthdays, posted events, and holidays.
547
	$startDate = strftime('%Y-%m-%d', $curTimestamp);
548
	$endDate = strftime('%Y-%m-%d', $nextWeekTimestamp);
549
	$bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($startDate, $endDate) : array();
550
	$events = $calendarOptions['show_events'] ? getEventRange($startDate, $endDate) : array();
551
	$holidays = $calendarOptions['show_holidays'] ? getHolidayRange($startDate, $endDate) : array();
552
553
	// An adjustment value to apply to all calculated week numbers.
554
	if (!empty($calendarOptions['show_week_num']))
555
	{
556
		$timestamp = mktime(0, 0, 0, $month, $day, $year);
557
		$calendarGrid['week_title'] = sprintf($txt['calendar_week_beginning'], date('F', $timestamp), date('j', $timestamp), date('Y', $timestamp));
558
	}
559
560
	// This holds all the main data - there is at least one month!
561
	$calendarGrid['months'] = array();
562
	$lastDay = 99;
563
	$curDay = $day;
564
	$curDayOfWeek = $calendarOptions['start_day'];
565
	for ($i = 0; $i < 7; $i++)
566
	{
567
		// Have we gone into a new month (Always happens first cycle too)
568
		if ($lastDay > $curDay)
569
		{
570
			$curMonth = $lastDay == 99 ? $month : ($month == 12 ? 1 : $month + 1);
571
			$curYear = $lastDay == 99 ? $year : ($curMonth == 1 && $month == 12 ? $year + 1 : $year);
572
			$calendarGrid['months'][$curMonth] = array(
573
				'current_month' => $curMonth,
574
				'current_year' => $curYear,
575
				'days' => array(),
576
			);
577
		}
578
579
		// Add todays information to the pile!
580
		$date = sprintf('%04d-%02d-%02d', $curYear, $curMonth, $curDay);
0 ignored issues
show
Bug introduced by
The variable $curYear does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $curMonth does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
581
582
		$calendarGrid['months'][$curMonth]['days'][$curDay] = array(
583
			'day' => $curDay,
584
			'day_of_week' => $curDayOfWeek,
585
			'date' => $date,
586
			'is_today' => $date == $today['date'],
587
			'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
588
			'events' => !empty($events[$date]) ? $events[$date] : array(),
589
			'birthdays' => !empty($bday[$date]) ? $bday[$date] : array()
590
		);
591
592
		// Make the last day what the current day is and work out what the next day is.
593
		$lastDay = $curDay;
594
		$curTimestamp += 86400;
595
		$curDay = (int) strftime('%d', $curTimestamp);
596
597
		// Also increment the current day of the week.
598
		$curDayOfWeek = $curDayOfWeek >= 6 ? 0 : ++$curDayOfWeek;
599
	}
600
601
	// Set the previous and the next week's links.
602
	$calendarGrid['previous_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['previous_week']['year'] . ';month=' . $calendarGrid['previous_week']['month'] . ';day=' . $calendarGrid['previous_week']['day'];
603
	$calendarGrid['next_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['next_week']['year'] . ';month=' . $calendarGrid['next_week']['month'] . ';day=' . $calendarGrid['next_week']['day'];
604
605
	return $calendarGrid;
606
}
607
608
609
/**
610
 * Returns the information needed to show a list of upcoming events, birthdays, and holidays on the calendar.
611
 * @param int $start_date The start of a date range
612
 * @param int $end_date The end of a date range
613
 * @param array $calendarOptions An array of calendar options
614
 * @return array An array of information needed to display a list of upcoming events, etc., on the calendar
615
 */
616
function getCalendarList($start_date, $end_date, $calendarOptions)
617
{
618
	global $scripturl, $modSettings, $user_info, $txt, $context, $sourcedir;
619
	require_once($sourcedir . '/Subs.php');
620
621
	// DateTime objects make life easier
622
	$start_object = date_create($start_date);
623
	$end_object = date_create($end_date);
624
625
	$calendarGrid = array(
626
		'start_date' => $start_date,
627
		'start_year' => date_format($start_object, 'Y'),
628
		'start_month' => date_format($start_object, 'm'),
629
		'start_day' => date_format($start_object, 'd'),
630
		'end_date' => $end_date,
631
		'end_year' => date_format($end_object, 'Y'),
632
		'end_month' => date_format($end_object, 'm'),
633
		'end_day' => date_format($end_object, 'd'),
634
	);
635
636
	$calendarGrid['birthdays'] = $calendarOptions['show_birthdays'] ? getBirthdayRange($start_date, $end_date) : array();
637
	$calendarGrid['holidays'] = $calendarOptions['show_holidays'] ? getHolidayRange($start_date, $end_date) : array();
638
	$calendarGrid['events'] = $calendarOptions['show_events'] ? getEventRange($start_date, $end_date) : array();
639
640
	// Get rid of duplicate events
641
	$temp = array();
642
	foreach ($calendarGrid['events'] as $date => $date_events)
643
	{
644
		foreach ($date_events as $event_key => $event_val)
645
		{
646
			if (in_array($event_val['id'], $temp))
647
				unset($calendarGrid['events'][$date][$event_key]);
648
			else
649
				$temp[] = $event_val['id'];
650
		}
651
	}
652
653
	// Give birthdays and holidays a friendly format, without the year
654 View Code Duplication
	if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
655
		$date_format = '%b %d';
656
	else
657
		$date_format = str_replace(array('%Y', '%y', '%G', '%g', '%C', '%c', '%D'), array('', '', '', '', '', '%b %d', '%m/%d'), $matches[0]);
658
659
	foreach (array('birthdays', 'holidays') as $type)
660
	{
661
		foreach ($calendarGrid[$type] as $date => $date_content)
662
		{
663
			$date_local = preg_replace('~(?<=\s)0+(\d)~', '$1', trim(timeformat(strtotime($date), $date_format), " \t\n\r\0\x0B,./;:<>()[]{}\\|-_=+"));
0 ignored issues
show
Documentation introduced by
$date_format is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
664
665
			$calendarGrid[$type][$date]['date_local'] = $date_local;
666
		}
667
	}
668
669
	loadCSSFile('jquery-ui.datepicker.css', array('defer' => false), 'smf_datepicker');
670
	loadJavaScriptFile('jquery-ui.datepicker.min.js', array('defer' => true), 'smf_datepicker');
671
	loadJavaScriptFile('jquery.timepicker.min.js', array('defer' => true), 'smf_timepicker');
672
	loadJavaScriptFile('datepair.min.js', array('defer' => true), 'smf_datepair');
673
	addInlineJavaScript('
674
	$("#calendar_range .date_input").datepicker({
675
		dateFormat: "yy-mm-dd",
676
		autoSize: true,
677
		isRTL: ' . ($context['right_to_left'] ? 'true' : 'false') . ',
678
		constrainInput: true,
679
		showAnim: "",
680
		showButtonPanel: false,
681
		minDate: "' . $modSettings['cal_minyear'] . '-01-01",
682
		maxDate: "' . $modSettings['cal_maxyear'] . '-12-31",
683
		yearRange: "' . $modSettings['cal_minyear'] . ':' . $modSettings['cal_maxyear'] . '",
684
		hideIfNoPrevNext: true,
685
		monthNames: ["' . implode('", "', $txt['months_titles']) . '"],
686
		monthNamesShort: ["' . implode('", "', $txt['months_short']) . '"],
687
		dayNames: ["' . implode('", "', $txt['days']) . '"],
688
		dayNamesShort: ["' . implode('", "', $txt['days_short']) . '"],
689
		dayNamesMin: ["' . implode('", "', $txt['days_short']) . '"],
690
		prevText: "' . $txt['prev_month'] . '",
691
		nextText: "' . $txt['next_month'] . '",
692
	});
693
	var date_entry = document.getElementById("calendar_range");
694
	var date_entry_pair = new Datepair(date_entry, {
695
		dateClass: "date_input",
696
		autoclose: true,
697
		parseDate: function (el) {
698
		    var utc = new Date($(el).datepicker("getDate"));
699
		    return utc && new Date(utc.getTime() + (utc.getTimezoneOffset() * 60000));
700
		},
701
		updateDate: function (el, v) {
702
		    $(el).datepicker("setDate", new Date(v.getTime() - (v.getTimezoneOffset() * 60000)));
703
		}
704
	});
705
	', true);
706
707
	return $calendarGrid;
708
}
709
710
/**
711
 * Retrieve all events for the given days, independently of the users offset.
712
 * cache callback function used to retrieve the birthdays, holidays, and events between now and now + days_to_index.
713
 * widens the search range by an extra 24 hours to support time offset shifts.
714
 * used by the cache_getRecentEvents function to get the information needed to calculate the events taking the users time offset into account.
715
 *
716
 * @param int $days_to_index How many days' worth of info to index
717
 * @return array An array containing the data that was cached as well as an expression to calculate whether the data should be refreshed and when it expires
718
 */
719
function cache_getOffsetIndependentEvents($days_to_index)
720
{
721
	$low_date = strftime('%Y-%m-%d', forum_time(false) - 24 * 3600);
722
	$high_date = strftime('%Y-%m-%d', forum_time(false) + $days_to_index * 24 * 3600);
723
724
	return array(
725
		'data' => array(
726
			'holidays' => getHolidayRange($low_date, $high_date),
727
			'birthdays' => getBirthdayRange($low_date, $high_date),
728
			'events' => getEventRange($low_date, $high_date, false),
729
		),
730
		'refresh_eval' => 'return \'' . strftime('%Y%m%d', forum_time(false)) . '\' != strftime(\'%Y%m%d\', forum_time(false)) || (!empty($modSettings[\'calendar_updated\']) && ' . time() . ' < $modSettings[\'calendar_updated\']);',
731
		'expires' => time() + 3600,
732
	);
733
}
734
735
/**
736
 * cache callback function used to retrieve the upcoming birthdays, holidays, and events within the given period, taking into account the users time offset.
737
 * Called from the BoardIndex to display the current day's events on the board index
738
 * used by the board index and SSI to show the upcoming events.
739
 * @param array $eventOptions An array of event options. Only 'num_days_shown' is used here
740
 * @return array An array containing the info that was cached as well as a few other relevant things
741
 */
742
function cache_getRecentEvents($eventOptions)
743
{
744
	// With the 'static' cached data we can calculate the user-specific data.
745
	$cached_data = cache_quick_get('calendar_index', 'Subs-Calendar.php', 'cache_getOffsetIndependentEvents', array($eventOptions['num_days_shown']));
746
747
	// Get the information about today (from user perspective).
748
	$today = getTodayInfo();
749
750
	$return_data = array(
751
		'calendar_holidays' => array(),
752
		'calendar_birthdays' => array(),
753
		'calendar_events' => array(),
754
	);
755
756
	// Set the event span to be shown in seconds.
757
	$days_for_index = $eventOptions['num_days_shown'] * 86400;
758
759
	// Get the current member time/date.
760
	$now = forum_time();
761
762
	// Holidays between now and now + days.
763
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
764
	{
765
		if (isset($cached_data['holidays'][strftime('%Y-%m-%d', $i)]))
766
			$return_data['calendar_holidays'] = array_merge($return_data['calendar_holidays'], $cached_data['holidays'][strftime('%Y-%m-%d', $i)]);
767
	}
768
769
	// Happy Birthday, guys and gals!
770
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
771
	{
772
		$loop_date = strftime('%Y-%m-%d', $i);
773
		if (isset($cached_data['birthdays'][$loop_date]))
774
		{
775
			foreach ($cached_data['birthdays'][$loop_date] as $index => $dummy)
0 ignored issues
show
Bug introduced by
The expression $cached_data['birthdays'][$loop_date] of type string is not traversable.
Loading history...
776
				$cached_data['birthdays'][strftime('%Y-%m-%d', $i)][$index]['is_today'] = $loop_date === $today['date'];
777
			$return_data['calendar_birthdays'] = array_merge($return_data['calendar_birthdays'], $cached_data['birthdays'][$loop_date]);
778
		}
779
	}
780
781
	$duplicates = array();
782
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
783
	{
784
		// Determine the date of the current loop step.
785
		$loop_date = strftime('%Y-%m-%d', $i);
786
787
		// No events today? Check the next day.
788
		if (empty($cached_data['events'][$loop_date]))
789
			continue;
790
791
		// Loop through all events to add a few last-minute values.
792
		foreach ($cached_data['events'][$loop_date] as $ev => $event)
0 ignored issues
show
Bug introduced by
The expression $cached_data['events'][$loop_date] of type string is not traversable.
Loading history...
793
		{
794
			// Create a shortcut variable for easier access.
795
			$this_event = &$cached_data['events'][$loop_date][$ev];
796
797
			// Skip duplicates.
798
			if (isset($duplicates[$this_event['topic'] . $this_event['title']]))
799
			{
800
				unset($cached_data['events'][$loop_date][$ev]);
801
				continue;
802
			}
803
			else
804
				$duplicates[$this_event['topic'] . $this_event['title']] = true;
805
806
			// Might be set to true afterwards, depending on the permissions.
807
			$this_event['can_edit'] = false;
808
			$this_event['is_today'] = $loop_date === $today['date'];
809
			$this_event['date'] = $loop_date;
810
		}
811
812
		if (!empty($cached_data['events'][$loop_date]))
813
			$return_data['calendar_events'] = array_merge($return_data['calendar_events'], $cached_data['events'][$loop_date]);
814
	}
815
816
	// Mark the last item so that a list separator can be used in the template.
817 View Code Duplication
	for ($i = 0, $n = count($return_data['calendar_birthdays']); $i < $n; $i++)
818
		$return_data['calendar_birthdays'][$i]['is_last'] = !isset($return_data['calendar_birthdays'][$i + 1]);
819 View Code Duplication
	for ($i = 0, $n = count($return_data['calendar_events']); $i < $n; $i++)
820
		$return_data['calendar_events'][$i]['is_last'] = !isset($return_data['calendar_events'][$i + 1]);
821
822
	return array(
823
		'data' => $return_data,
824
		'expires' => time() + 3600,
825
		'refresh_eval' => 'return \'' . strftime('%Y%m%d', forum_time(false)) . '\' != strftime(\'%Y%m%d\', forum_time(false)) || (!empty($modSettings[\'calendar_updated\']) && ' . time() . ' < $modSettings[\'calendar_updated\']);',
826
		'post_retri_eval' => '
827
			global $context, $scripturl, $user_info;
828
829
			foreach ($cache_block[\'data\'][\'calendar_events\'] as $k => $event)
830
			{
831
				// Remove events that the user may not see or wants to ignore.
832
				if ((count(array_intersect($user_info[\'groups\'], $event[\'allowed_groups\'])) === 0 && !allowedTo(\'admin_forum\') && !empty($event[\'id_board\'])) || in_array($event[\'id_board\'], $user_info[\'ignoreboards\']))
833
					unset($cache_block[\'data\'][\'calendar_events\'][$k]);
834
				else
835
				{
836
					// Whether the event can be edited depends on the permissions.
837
					$cache_block[\'data\'][\'calendar_events\'][$k][\'can_edit\'] = allowedTo(\'calendar_edit_any\') || ($event[\'poster\'] == $user_info[\'id\'] && allowedTo(\'calendar_edit_own\'));
838
839
					// The added session code makes this URL not cachable.
840
					$cache_block[\'data\'][\'calendar_events\'][$k][\'modify_href\'] = $scripturl . \'?action=\' . ($event[\'topic\'] == 0 ? \'calendar;sa=post;\' : \'post;msg=\' . $event[\'msg\'] . \';topic=\' . $event[\'topic\'] . \'.0;calendar;\') . \'eventid=\' . $event[\'id\'] . \';\' . $context[\'session_var\'] . \'=\' . $context[\'session_id\'];
841
				}
842
			}
843
844
			if (empty($params[0][\'include_holidays\']))
845
				$cache_block[\'data\'][\'calendar_holidays\'] = array();
846
			if (empty($params[0][\'include_birthdays\']))
847
				$cache_block[\'data\'][\'calendar_birthdays\'] = array();
848
			if (empty($params[0][\'include_events\']))
849
				$cache_block[\'data\'][\'calendar_events\'] = array();
850
851
			$cache_block[\'data\'][\'show_calendar\'] = !empty($cache_block[\'data\'][\'calendar_holidays\']) || !empty($cache_block[\'data\'][\'calendar_birthdays\']) || !empty($cache_block[\'data\'][\'calendar_events\']);',
852
	);
853
}
854
855
/**
856
 * Makes sure the calendar post is valid.
857
 */
858
function validateEventPost()
859
{
860
	global $modSettings, $smcFunc;
861
862
	if (!isset($_POST['deleteevent']))
863
	{
864
		// The 2.1 way
865
		if (isset($_POST['start_date']))
866
		{
867
			$d = date_parse($_POST['start_date']);
868 View Code Duplication
			if (!empty($d['error_count']) || !empty($d['warning_count']))
869
				fatal_lang_error('invalid_date', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
870
			if (empty($d['year']))
871
				fatal_lang_error('event_year_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
872
			if (empty($d['month']))
873
				fatal_lang_error('event_month_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
874
		}
875
		elseif (isset($_POST['start_datetime']))
876
		{
877
			$d = date_parse($_POST['start_datetime']);
878 View Code Duplication
			if (!empty($d['error_count']) || !empty($d['warning_count']))
879
				fatal_lang_error('invalid_date', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
880
			if (empty($d['year']))
881
				fatal_lang_error('event_year_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
882
			if (empty($d['month']))
883
				fatal_lang_error('event_month_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
884
		}
885
		// The 2.0 way
886
		else
887
		{
888
			// No month?  No year?
889
			if (!isset($_POST['month']))
890
				fatal_lang_error('event_month_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
891
			if (!isset($_POST['year']))
892
				fatal_lang_error('event_year_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
893
894
			// Check the month and year...
895
			if ($_POST['month'] < 1 || $_POST['month'] > 12)
896
				fatal_lang_error('invalid_month', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
897 View Code Duplication
			if ($_POST['year'] < $modSettings['cal_minyear'] || $_POST['year'] > $modSettings['cal_maxyear'])
898
				fatal_lang_error('invalid_year', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
899
		}
900
	}
901
902
	// Make sure they're allowed to post...
903
	isAllowedTo('calendar_post');
904
905
	// If they want to us to calculate an end date, make sure it will fit in an acceptable range.
906
	if (isset($_POST['span']))
907
	{
908
		if (($_POST['span'] < 1) || (!empty($modSettings['cal_maxspan']) && $_POST['span'] > $modSettings['cal_maxspan']))
909
			fatal_lang_error('invalid_days_numb', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
910
	}
911
912
	// There is no need to validate the following values if we are just deleting the event.
913
	if (!isset($_POST['deleteevent']))
914
	{
915
		// If we're doing things the 2.0 way, check the day
916
		if (empty($_POST['start_date']) && empty($_POST['start_datetime']))
917
		{
918
			// No day?
919
			if (!isset($_POST['day']))
920
				fatal_lang_error('event_day_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
921
922
			// Bad day?
923
			if (!checkdate($_POST['month'], $_POST['day'], $_POST['year']))
924
				fatal_lang_error('invalid_date', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
925
		}
926
927
		if (!isset($_POST['evtitle']) && !isset($_POST['subject']))
928
			fatal_lang_error('event_title_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
929
		elseif (!isset($_POST['evtitle']))
930
			$_POST['evtitle'] = $_POST['subject'];
931
932
		// No title?
933
		if ($smcFunc['htmltrim']($_POST['evtitle']) === '')
934
			fatal_lang_error('no_event_title', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
935 View Code Duplication
		if ($smcFunc['strlen']($_POST['evtitle']) > 100)
936
			$_POST['evtitle'] = $smcFunc['substr']($_POST['evtitle'], 0, 100);
937
		$_POST['evtitle'] = str_replace(';', '', $_POST['evtitle']);
938
	}
939
}
940
941
/**
942
 * Get the event's poster.
943
 *
944
 * @param int $event_id The ID of the event
945
 * @return int|bool The ID of the poster or false if the event was not found
946
 */
947 View Code Duplication
function getEventPoster($event_id)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
948
{
949
	global $smcFunc;
950
951
	// A simple database query, how hard can that be?
952
	$request = $smcFunc['db_query']('', '
953
		SELECT id_member
954
		FROM {db_prefix}calendar
955
		WHERE id_event = {int:id_event}
956
		LIMIT 1',
957
		array(
958
			'id_event' => $event_id,
959
		)
960
	);
961
962
	// No results, return false.
963
	if ($smcFunc['db_num_rows'] === 0)
964
		return false;
965
966
	// Grab the results and return.
967
	list ($poster) = $smcFunc['db_fetch_row']($request);
968
	$smcFunc['db_free_result']($request);
969
	return (int) $poster;
970
}
971
972
/**
973
 * Consolidating the various INSERT statements into this function.
974
 * Inserts the passed event information into the calendar table.
975
 * Allows to either set a time span (in days) or an end_date.
976
 * Does not check any permissions of any sort.
977
 *
978
 * @param array $eventOptions An array of event options ('title', 'span', 'start_date', 'end_date', etc.)
979
 */
980
function insertEvent(&$eventOptions)
981
{
982
	global $smcFunc, $context;
983
984
	// Add special chars to the title.
985
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
986
987
	$eventOptions['location'] = isset($eventOptions['location']) ? $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES) : '';
988
989
	// Set the start and end dates and times
990
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
991
992
	// If no topic and board are given, they are not linked to a topic.
993
	$eventOptions['board'] = isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0;
994
	$eventOptions['topic'] = isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0;
995
996
	$event_columns = array(
997
		'id_board' => 'int', 'id_topic' => 'int', 'title' => 'string-60', 'id_member' => 'int',
998
		'start_date' => 'date', 'end_date' => 'date', 'location' => 'string-255',
999
	);
1000
	$event_parameters = array(
1001
		$eventOptions['board'], $eventOptions['topic'], $eventOptions['title'], $eventOptions['member'],
1002
		$start_date, $end_date, $eventOptions['location'],
1003
	);
1004 View Code Duplication
	if (!empty($start_time) && !empty($end_time) && !empty($tz) && in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1005
	{
1006
		$event_columns['start_time'] = 'time';
1007
		$event_parameters[] = $start_time;
1008
		$event_columns['end_time'] = 'time';
1009
		$event_parameters[] = $end_time;
1010
		$event_columns['timezone'] = 'string';
1011
		$event_parameters[] = $tz;
1012
	}
1013
1014
	call_integration_hook('integrate_create_event', array(&$eventOptions, &$event_columns, &$event_parameters));
1015
1016
	// Insert the event!
1017
	$eventOptions['id'] = $smcFunc['db_insert']('',
1018
		'{db_prefix}calendar',
1019
		$event_columns,
1020
		$event_parameters,
1021
		array('id_event'),
1022
		1
1023
	);
1024
1025
	// If this isn't tied to a topic, we need to notify people about it.
1026
	if (empty($eventOptions['topic']))
1027
	{
1028
		$smcFunc['db_insert']('insert',
1029
			'{db_prefix}background_tasks',
1030
			array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
1031
			array('$sourcedir/tasks/EventNew-Notify.php', 'EventNew_Notify_Background', json_encode(array(
1032
				'event_title' => $eventOptions['title'],
1033
				'event_id' => $eventOptions['id'],
1034
				'sender_id' => $eventOptions['member'],
1035
				'sender_name' => $eventOptions['member'] == $context['user']['id'] ? $context['user']['name'] : '',
1036
				'time' => time(),
1037
			)), 0),
1038
			array('id_task')
1039
		);
1040
	}
1041
1042
	// Update the settings to show something calendar-ish was updated.
1043
	updateSettings(array(
1044
		'calendar_updated' => time(),
1045
	));
1046
}
1047
1048
/**
1049
 * modifies an event.
1050
 * allows to either set a time span (in days) or an end_date.
1051
 * does not check any permissions of any sort.
1052
 *
1053
 * @param int $event_id The ID of the event
1054
 * @param array $eventOptions An array of event information
1055
 */
1056
function modifyEvent($event_id, &$eventOptions)
1057
{
1058
	global $smcFunc;
1059
1060
	// Properly sanitize the title and location
1061
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
1062
	$eventOptions['location'] = $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES);
1063
1064
	// Set the new start and end dates and times
1065
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
1066
1067
	$event_columns = array(
1068
		'start_date' => '{date:start_date}',
1069
		'end_date' => '{date:end_date}',
1070
		'title' => 'SUBSTRING({string:title}, 1, 60)',
1071
		'id_board' => '{int:id_board}',
1072
		'id_topic' => '{int:id_topic}',
1073
		'location' => 'SUBSTRING({string:location}, 1, 255)',
1074
	);
1075
	$event_parameters = array(
1076
		'start_date' => $start_date,
1077
		'end_date' => $end_date,
1078
		'title' => $eventOptions['title'],
1079
		'location' => $eventOptions['location'],
1080
		'id_board' => isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0,
1081
		'id_topic' => isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0,
1082
	);
1083 View Code Duplication
	if (!empty($start_time) && !empty($end_time) && !empty($tz) && in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1084
	{
1085
		$event_columns['start_time'] = '{time:start_time}';
1086
		$event_parameters['start_time'] = $start_time;
1087
		$event_columns['end_time'] = '{time:end_time}';
1088
		$event_parameters['end_time'] = $end_time;
1089
		$event_columns['timezone'] = '{string:timezone}';
1090
		$event_parameters['timezone'] = $tz;
1091
	}
1092
1093
	// This is to prevent hooks to modify the id of the event
1094
	$real_event_id = $event_id;
1095
	call_integration_hook('integrate_modify_event', array($event_id, &$eventOptions, &$event_columns, &$event_parameters));
1096
1097
	$column_clauses = array();
1098
	foreach ($event_columns as $col => $crit)
1099
		$column_clauses[] = $col . ' = ' . $crit;
1100
1101
	$smcFunc['db_query']('', '
1102
		UPDATE {db_prefix}calendar
1103
		SET
1104
			' . implode(', ', $column_clauses) . '
1105
		WHERE id_event = {int:id_event}',
1106
		array_merge(
1107
			$event_parameters,
1108
			array(
1109
				'id_event' => $real_event_id
1110
			)
1111
		)
1112
	);
1113
1114
	if (empty($start_time) || empty($end_time) || empty($tz) || !in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1115
	{
1116
		$smcFunc['db_query']('', '
1117
			UPDATE {db_prefix}calendar
1118
			SET start_time = NULL, end_time = NULL, timezone = NULL
1119
			WHERE id_event = {int:id_event}',
1120
			array(
1121
				'id_event' => $real_event_id
1122
			)
1123
		);
1124
	}
1125
1126
	updateSettings(array(
1127
		'calendar_updated' => time(),
1128
	));
1129
}
1130
1131
/**
1132
 * Remove an event
1133
 * removes an event.
1134
 * does no permission checks.
1135
 *
1136
 * @param int $event_id The ID of the event to remove
1137
 */
1138
function removeEvent($event_id)
1139
{
1140
	global $smcFunc;
1141
1142
	$smcFunc['db_query']('', '
1143
		DELETE FROM {db_prefix}calendar
1144
		WHERE id_event = {int:id_event}',
1145
		array(
1146
			'id_event' => $event_id,
1147
		)
1148
	);
1149
1150
	call_integration_hook('integrate_remove_event', array($event_id));
1151
1152
	updateSettings(array(
1153
		'calendar_updated' => time(),
1154
	));
1155
}
1156
1157
/**
1158
 * Gets all the events properties
1159
 *
1160
 * @param int $event_id The ID of the event
1161
 * @return array An array of event information
1162
 */
1163
function getEventProperties($event_id)
1164
{
1165
	global $smcFunc;
1166
1167
	$request = $smcFunc['db_query']('', '
1168
		SELECT
1169
			c.id_event, c.id_board, c.id_topic, c.id_member, c.title,
1170
			c.start_date, c.end_date, c.start_time, c.end_time, c.timezone, c.location,
1171
			t.id_first_msg, t.id_member_started,
1172
			mb.real_name, m.modified_time
1173
		FROM {db_prefix}calendar AS c
1174
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = c.id_topic)
1175
			LEFT JOIN {db_prefix}members AS mb ON (mb.id_member = t.id_member_started)
1176
			LEFT JOIN {db_prefix}messages AS m ON (m.id_msg  = t.id_first_msg)
1177
		WHERE c.id_event = {int:id_event}',
1178
		array(
1179
			'id_event' => $event_id,
1180
		)
1181
	);
1182
1183
	// If nothing returned, we are in poo, poo.
1184
	if ($smcFunc['db_num_rows']($request) === 0)
1185
		return false;
1186
1187
	$row = $smcFunc['db_fetch_assoc']($request);
1188
	$smcFunc['db_free_result']($request);
1189
1190
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1191
1192
	// Sanity check
1193 View Code Duplication
	if (!empty($start['error_count']) || !empty($start['warning_count']) || !empty($end['error_count']) || !empty($end['warning_count']))
1194
		return false;
1195
1196
	$return_value = array(
1197
		'boards' => array(),
1198
		'board' => $row['id_board'],
1199
		'new' => 0,
1200
		'eventid' => $event_id,
1201
		'year' => $start['year'],
1202
		'month' => $start['month'],
1203
		'day' => $start['day'],
1204
		'hour' => !$allday ? $start['hour'] : null,
1205
		'minute' => !$allday ? $start['minute'] : null,
1206
		'second' => !$allday ? $start['second'] : null,
1207
		'start_date' => $row['start_date'],
1208
		'start_date_local' => $start['date_local'],
1209
		'start_date_orig' => $start['date_orig'],
1210
		'start_time' => !$allday ? $row['start_time'] : null,
1211
		'start_time_local' => !$allday ? $start['time_local'] : null,
1212
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1213
		'start_timestamp' => $start['timestamp'],
1214
		'start_datetime' => $start['datetime'],
1215
		'start_iso_gmdate' => $start['iso_gmdate'],
1216
		'end_year' => $end['year'],
1217
		'end_month' => $end['month'],
1218
		'end_day' => $end['day'],
1219
		'end_hour' => !$allday ? $end['hour'] : null,
1220
		'end_minute' => !$allday ? $end['minute'] : null,
1221
		'end_second' => !$allday ? $end['second'] : null,
1222
		'end_date' => $row['end_date'],
1223
		'end_date_local' => $end['date_local'],
1224
		'end_date_orig' => $end['date_orig'],
1225
		'end_time' => !$allday ? $row['end_time'] : null,
1226
		'end_time_local' => !$allday ? $end['time_local'] : null,
1227
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1228
		'end_timestamp' => $end['timestamp'],
1229
		'end_datetime' => $end['datetime'],
1230
		'end_iso_gmdate' => $end['iso_gmdate'],
1231
		'allday' => $allday,
1232
		'tz' => !$allday ? $tz : null,
1233
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1234
		'span' => $span,
1235
		'title' => $row['title'],
1236
		'location' => $row['location'],
1237
		'member' => $row['id_member'],
1238
		'realname' => $row['real_name'],
1239
		'sequence' => $row['modified_time'],
1240
		'topic' => array(
1241
			'id' => $row['id_topic'],
1242
			'member_started' => $row['id_member_started'],
1243
			'first_msg' => $row['id_first_msg'],
1244
		),
1245
	);
1246
1247
	$return_value['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $return_value['month'] == 12 ? 1 : $return_value['month'] + 1, 0, $return_value['month'] == 12 ? $return_value['year'] + 1 : $return_value['year']));
1248
1249
	return $return_value;
1250
}
1251
1252
/**
1253
 * Gets an initial set of date and time values for creating a new event.
1254
 *
1255
 * @return array An array containing an initial set of date and time values for an event.
1256
 */
1257
function getNewEventDatetimes()
1258
{
1259
	// Ensure setEventStartEnd() has something to work with
1260
	$now = date_create();
1261
	$_POST['year'] = !empty($_POST['year']) ? $_POST['year'] : date_format($now, 'Y');
1262
	$_POST['month'] = !empty($_POST['month']) ? $_POST['month'] : date_format($now, 'm');
1263
	$_POST['day'] = !empty($_POST['day']) ? $_POST['day'] : date_format($now, 'd');
1264
	$_POST['hour'] = !empty($_POST['hour']) ? $_POST['hour'] : date_format($now, 'H');
1265
	$_POST['minute'] = !empty($_POST['minute']) ? $_POST['minute'] : date_format($now, 'i');
1266
	$_POST['second'] = !empty($_POST['second']) ? $_POST['second'] : date_format($now, 's');
1267
1268
	// Set the basic values for the new event
1269
	$row_keys = array('start_date', 'end_date', 'start_time', 'end_time', 'timezone');
1270
	$row = array_combine($row_keys, setEventStartEnd());
1271
1272
	// And now set the full suite of values
1273
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1274
1275
	// Default theme only uses some of this info, but others might want it all
1276
	$eventProperties = array(
1277
		'year' => $start['year'],
1278
		'month' => $start['month'],
1279
		'day' => $start['day'],
1280
		'hour' => !$allday ? $start['hour'] : null,
1281
		'minute' => !$allday ? $start['minute'] : null,
1282
		'second' => !$allday ? $start['second'] : null,
1283
		'start_date' => $row['start_date'],
1284
		'start_date_local' => $start['date_local'],
1285
		'start_date_orig' => $start['date_orig'],
1286
		'start_time' => !$allday ? $row['start_time'] : null,
1287
		'start_time_local' => !$allday ? $start['time_local'] : null,
1288
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1289
		'start_timestamp' => $start['timestamp'],
1290
		'start_datetime' => $start['datetime'],
1291
		'start_iso_gmdate' => $start['iso_gmdate'],
1292
		'end_year' => $end['year'],
1293
		'end_month' => $end['month'],
1294
		'end_day' => $end['day'],
1295
		'end_hour' => !$allday ? $end['hour'] : null,
1296
		'end_minute' => !$allday ? $end['minute'] : null,
1297
		'end_second' => !$allday ? $end['second'] : null,
1298
		'end_date' => $row['end_date'],
1299
		'end_date_local' => $end['date_local'],
1300
		'end_date_orig' => $end['date_orig'],
1301
		'end_time' => !$allday ? $row['end_time'] : null,
1302
		'end_time_local' => !$allday ? $end['time_local'] : null,
1303
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1304
		'end_timestamp' => $end['timestamp'],
1305
		'end_datetime' => $end['datetime'],
1306
		'end_iso_gmdate' => $end['iso_gmdate'],
1307
		'allday' => $allday,
1308
		'tz' => !$allday ? $tz : null,
1309
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1310
		'span' => $span,
1311
	);
1312
1313
	return $eventProperties;
1314
}
1315
1316
/**
1317
 * Set the start and end dates and times for a posted event for insertion into the database.
1318
 * Validates all date and times given to it.
1319
 * Makes sure events do not exceed the maximum allowed duration (if any).
1320
 * If passed an array that defines any time or date parameters, they will be used. Otherwise, gets the values from $_POST.
1321
 *
1322
 * @param array $eventOptions An array of optional time and date parameters (span, start_year, end_month, etc., etc.)
1323
 * @return array An array containing $start_date, $end_date, $start_time, $end_time
1324
 */
1325
function setEventStartEnd($eventOptions = array())
1326
{
1327
	global $modSettings, $user_info;
1328
1329
	// Set $span, in case we need it
1330
	$span = isset($eventOptions['span']) ? $eventOptions['span'] : (isset($_POST['span']) ? $_POST['span'] : 0);
1331
	if ($span > 0)
1332
		$span = !empty($modSettings['cal_maxspan']) ? min($modSettings['cal_maxspan'], $span - 1) : $span - 1;
1333
1334
	// Define the timezone for this event, falling back to the default if not provided
1335
	if (!empty($eventOptions['tz']) && in_array($eventOptions['tz'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1336
		$tz = $eventOptions['tz'];
1337 View Code Duplication
	elseif (!empty($_POST['tz']) && in_array($_POST['tz'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1338
		$tz = $_POST['tz'];
1339
	else
1340
		$tz = getUserTimezone();
1341
1342
	// Is this supposed to be an all day event, or should it have specific start and end times?
1343
	if (isset($eventOptions['allday']))
1344
		$allday = $eventOptions['allday'];
1345
	elseif (empty($_POST['allday']))
1346
		$allday = false;
1347
	else
1348
		$allday = true;
1349
1350
	// Input might come as individual parameters...
1351
	$start_year = isset($eventOptions['year']) ? $eventOptions['year'] : (isset($_POST['year']) ? $_POST['year'] : null);
1352
	$start_month = isset($eventOptions['month']) ? $eventOptions['month'] : (isset($_POST['month']) ? $_POST['month'] : null);
1353
	$start_day = isset($eventOptions['day']) ? $eventOptions['day'] : (isset($_POST['day']) ? $_POST['day'] : null);
1354
	$start_hour = isset($eventOptions['hour']) ? $eventOptions['hour'] : (isset($_POST['hour']) ? $_POST['hour'] : null);
1355
	$start_minute = isset($eventOptions['minute']) ? $eventOptions['minute'] : (isset($_POST['minute']) ? $_POST['minute'] : null);
1356
	$start_second = isset($eventOptions['second']) ? $eventOptions['second'] : (isset($_POST['second']) ? $_POST['second'] : null);
1357
	$end_year = isset($eventOptions['end_year']) ? $eventOptions['end_year'] : (isset($_POST['end_year']) ? $_POST['end_year'] : null);
1358
	$end_month = isset($eventOptions['end_month']) ? $eventOptions['end_month'] : (isset($_POST['end_month']) ? $_POST['end_month'] : null);
1359
	$end_day = isset($eventOptions['end_day']) ? $eventOptions['end_day'] : (isset($_POST['end_day']) ? $_POST['end_day'] : null);
1360
	$end_hour = isset($eventOptions['end_hour']) ? $eventOptions['end_hour'] : (isset($_POST['end_hour']) ? $_POST['end_hour'] : null);
1361
	$end_minute = isset($eventOptions['end_minute']) ? $eventOptions['end_minute'] : (isset($_POST['end_minute']) ? $_POST['end_minute'] : null);
1362
	$end_second = isset($eventOptions['end_second']) ? $eventOptions['end_second'] : (isset($_POST['end_second']) ? $_POST['end_second'] : null);
1363
1364
	// ... or as datetime strings ...
1365
	$start_string = isset($eventOptions['start_datetime']) ? $eventOptions['start_datetime'] : (isset($_POST['start_datetime']) ? $_POST['start_datetime'] : null);
1366
	$end_string = isset($eventOptions['end_datetime']) ? $eventOptions['end_datetime'] : (isset($_POST['end_datetime']) ? $_POST['end_datetime'] : null);
1367
1368
	// ... or as date strings and time strings.
1369
	$start_date_string = isset($eventOptions['start_date']) ? $eventOptions['start_date'] : (isset($_POST['start_date']) ? $_POST['start_date'] : null);
1370
	$start_time_string = isset($eventOptions['start_time']) ? $eventOptions['start_time'] : (isset($_POST['start_time']) ? $_POST['start_time'] : null);
1371
	$end_date_string = isset($eventOptions['end_date']) ? $eventOptions['end_date'] : (isset($_POST['end_date']) ? $_POST['end_date'] : null);
1372
	$end_time_string = isset($eventOptions['end_time']) ? $eventOptions['end_time'] : (isset($_POST['end_time']) ? $_POST['end_time'] : null);
1373
1374
	// If the date and time were given in separate strings, combine them
1375
	if (empty($start_string) && isset($start_date_string))
1376
		$start_string = $start_date_string . (isset($start_time_string) ? ' ' . $start_time_string : '');
1377
	if (empty($end_string) && isset($end_date_string))
1378
		$end_string = $end_date_string . (isset($end_time_string) ? ' ' . $end_time_string : '');
1379
1380
	// If some form of string input was given, override individually defined options with it
1381 View Code Duplication
	if (isset($start_string))
1382
	{
1383
		$start_string_parsed = date_parse($start_string);
1384
		if (empty($start_string_parsed['error_count']) && empty($start_string_parsed['warning_count']))
1385
		{
1386
			if ($start_string_parsed['year'] != false)
1387
			{
1388
				$start_year = $start_string_parsed['year'];
1389
				$start_month = $start_string_parsed['month'];
1390
				$start_day = $start_string_parsed['day'];
1391
			}
1392
			if ($start_string_parsed['hour'] != false)
1393
			{
1394
				$start_hour = $start_string_parsed['hour'];
1395
				$start_minute = $start_string_parsed['minute'];
1396
				$start_second = $start_string_parsed['second'];
1397
			}
1398
		}
1399
	}
1400 View Code Duplication
	if (isset($end_string))
1401
	{
1402
		$end_string_parsed = date_parse($end_string);
1403
		if (empty($end_string_parsed['error_count']) && empty($end_string_parsed['warning_count']))
1404
		{
1405
			if ($end_string_parsed['year'] != false)
1406
			{
1407
				$end_year = $end_string_parsed['year'];
1408
				$end_month = $end_string_parsed['month'];
1409
				$end_day = $end_string_parsed['day'];
1410
			}
1411
			if ($end_string_parsed['hour'] != false)
1412
			{
1413
				$end_hour = $end_string_parsed['hour'];
1414
				$end_minute = $end_string_parsed['minute'];
1415
				$end_second = $end_string_parsed['second'];
1416
			}
1417
		}
1418
	}
1419
1420
	// Validate input
1421
	$start_date_isvalid = checkdate($start_month, $start_day, $start_year);
1422
	$end_date_isvalid = checkdate($end_month, $end_day, $end_year);
1423
1424
	$start_time_isset = (isset($start_hour) && isset($start_minute) && isset($start_second));
1425
	$d = date_parse(sprintf('%02d:%02d:%02d', $start_hour, $start_minute, $start_second));
1426
	$start_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1427
1428
	$end_time_isset = (isset($end_hour) && isset($end_minute) && isset($end_second));
1429
	$d = date_parse(sprintf('%02d:%02d:%02d', $end_hour, $end_minute, $end_second));
1430
	$end_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1431
1432
	// Uh-oh...
1433
	if ($start_date_isvalid === false)
1434
	{
1435
		fatal_lang_error('invalid_date', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1436
	}
1437
1438
	// Make sure we use valid values for everything
1439
	if ($end_date_isvalid === false)
1440
	{
1441
		$end_year = $start_year;
1442
		$end_month = $start_month;
1443
		$end_day = $start_day;
1444
	}
1445
1446
	if ($allday === true || $start_time_isset === false || $start_time_isvalid === false)
1447
	{
1448
		$allday = true;
1449
		$start_hour = 0;
1450
		$start_minute = 0;
1451
		$start_second = 0;
1452
	}
1453
1454
	if ($allday === true || $end_time_isvalid === false || $end_time_isset === false)
1455
	{
1456
		$end_hour = $start_hour;
1457
		$end_minute = $start_minute;
1458
		$end_second = $start_second;
1459
	}
1460
1461
	// Now create our datetime objects
1462
	$start_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1463
	$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $end_year, $end_month, $end_day, $end_hour, $end_minute, $end_second) . ' ' . $tz);
1464
1465
	// Is $end_object too early?
1466
	if ($start_object >= $end_object)
1467
	{
1468
		$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1469
		if ($span > 0)
1470
			date_add($end_object, date_interval_create_from_date_string($span . ' days'));
1471
		else
1472
			date_add($end_object, date_interval_create_from_date_string('1 hour'));
1473
	}
1474
1475
	// Is $end_object too late?
1476
	if (!empty($modSettings['cal_maxspan']))
1477
	{
1478
		$date_diff = date_diff($start_object, $end_object);
1479
		if ($date_diff->days > $modSettings['cal_maxspan'])
1480
		{
1481
			if ($modSettings['cal_maxspan'] > 1)
1482
			{
1483
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1484
				date_add($end_object, date_interval_create_from_date_string($modSettings['cal_maxspan'] . ' days'));
1485
			}
1486
			else
1487
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, '11', '59', '59') . ' ' . $tz);
1488
		}
1489
	}
1490
1491
	// Finally, make our strings
1492
	$start_date = date_format($start_object, 'Y-m-d');
1493
	$end_date = date_format($end_object, 'Y-m-d');
1494
1495
	if ($allday == true)
1496
	{
1497
		$start_time = null;
1498
		$end_time = null;
1499
		$tz = null;
1500
	}
1501
	else
1502
	{
1503
		$start_time = date_format($start_object, 'H:i:s');
1504
		$end_time = date_format($end_object, 'H:i:s');
1505
	}
1506
1507
	return array($start_date, $end_date, $start_time, $end_time, $tz);
1508
}
1509
1510
/**
1511
 * Helper function for getEventRange, getEventProperties, getNewEventDatetimes, etc.
1512
 *
1513
 * @param array $row A database row representing an event from the calendar table
1514
 * @return array An array containing the start and end date and time properties for the event
1515
 */
1516
function buildEventDatetimes($row)
1517
{
1518
	global $sourcedir, $user_info;
1519
	require_once($sourcedir . '/Subs.php');
1520
1521
	// First, try to create a better date format, ignoring the "time" elements.
1522
	if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
1523
		$date_format = '%F';
1524
	else
1525
		$date_format = $matches[0];
1526
1527
	// We want a fairly compact version of the time, but as close as possible to the user's settings.
1528 View Code Duplication
	if (preg_match('~%[HkIlMpPrRSTX](?:[^%]*%[HkIlMpPrRSTX])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
1529
		$time_format = '%k:%M';
1530
	else
1531
		$time_format = str_replace(array('%I', '%H', '%S', '%r', '%R', '%T'), array('%l', '%k', '', '%l:%M %p', '%k:%M', '%l:%M'), $matches[0]);
1532
1533
	// Should this be an all day event?
1534
	$allday = (empty($row['start_time']) || empty($row['end_time']) || empty($row['timezone']) || !in_array($row['timezone'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC))) ? true : false;
1535
1536
	// How many days does this event span?
1537
	$span = 1 + date_interval_format(date_diff(date_create($row['start_date']), date_create($row['end_date'])), '%d');
1538
1539
	// We need to have a defined timezone in the steps below
1540
	if (empty($row['timezone']))
1541
		$row['timezone'] = getUserTimezone();
1542
1543
	// Get most of the standard date information for the start and end datetimes
1544
	$start = date_parse($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''));
1545
	$end = date_parse($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''));
1546
1547
	// But we also want more info, so make some DateTime objects we can use
1548
	$start_object = date_create($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''), timezone_open($row['timezone']));
1549
	$end_object = date_create($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''), timezone_open($row['timezone']));
1550
1551
	// Unix timestamps are good
1552
	$start['timestamp'] = date_format($start_object, 'U');
1553
	$end['timestamp'] = date_format($end_object, 'U');
1554
1555
	// Datetime string without timezone  (e.g. '2016-12-28 22:45:30')
1556
	$start['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1557
	$end['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1558
1559
	// ISO formatted datetime string, relative to UTC (e.g. '2016-12-29T05:45:30+00:00')
1560
	$start['iso_gmdate'] = gmdate('c', $start['timestamp']);
1561
	$end['iso_gmdate'] = gmdate('c', $end['timestamp']);
1562
1563
	// Strings showing the datetimes in the user's preferred format, relative to the user's time zone
1564
	$start['date_local'] = timeformat($start['timestamp'], $date_format);
0 ignored issues
show
Documentation introduced by
$date_format is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1565
	$start['time_local'] = timeformat($start['timestamp'], $time_format);
0 ignored issues
show
Documentation introduced by
$time_format is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1566
	$end['date_local'] = timeformat($end['timestamp'], $date_format);
0 ignored issues
show
Documentation introduced by
$date_format is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1567
	$end['time_local'] = timeformat($end['timestamp'], $time_format);
0 ignored issues
show
Documentation introduced by
$time_format is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1568
1569
	// Strings showing the datetimes in the user's preferred format, relative to the event's time zone
1570
	$start['date_orig'] = timeformat(strtotime(date_format($start_object, 'Y-m-d H:i:s')), $date_format, 'none');
0 ignored issues
show
Documentation introduced by
$date_format is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1571
	$start['time_orig'] = timeformat(strtotime(date_format($start_object, 'Y-m-d H:i:s')), $time_format, 'none');
0 ignored issues
show
Documentation introduced by
$time_format is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1572
	$end['date_orig'] = timeformat(strtotime(date_format($end_object, 'Y-m-d H:i:s')), $date_format, 'none');
0 ignored issues
show
Documentation introduced by
$date_format is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1573
	$end['time_orig'] = timeformat(strtotime(date_format($end_object, 'Y-m-d H:i:s')), $time_format, 'none');
0 ignored issues
show
Documentation introduced by
$time_format is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1574
1575
	// The time zone identifier (e.g. 'Europe/London') and abbreviation (e.g. 'GMT')
1576
	$tz = date_format($start_object, 'e');
1577
	$tz_abbrev = date_format($start_object, 'T');
1578
1579
	// There are a handful of time zones that PHP doesn't know the abbreviation for. Fix 'em if we can.
1580
	if (strspn($tz_abbrev, '+-') > 0)
1581
	{
1582
		$tz_location = timezone_location_get(timezone_open($row['timezone']));
1583
1584
		// Kazakstan
1585
		if ($tz_location['country_code'] == 'KZ')
1586
			$tz_abbrev = str_replace(array('+05', '+06'), array('AQTT', 'ALMT'), $tz_abbrev);
1587
1588
		// Russia likes to experiment with time zones
1589
		if ($tz_location['country_code'] == 'RU')
1590
		{
1591
			$msk_offset = intval($tz_abbrev) - 3;
1592
			$msk_offset = !empty($msk_offset) ? sprintf('%+0d', $msk_offset) : '';
1593
			$tz_abbrev = 'MSK' . $msk_offset;
1594
		}
1595
1596
		// Still no good? We'll just mark it as a UTC offset
1597
		if (strspn($tz_abbrev, '+-') > 0)
1598
			$tz_abbrev = 'UTC' . $tz_abbrev;
1599
	}
1600
1601
	return array($start, $end, $allday, $span, $tz, $tz_abbrev);
1602
}
1603
1604
/**
1605
 * Gets a member's selected timezone identifier directly from the database
1606
 *
1607
 * @param int $id_member The member id to look up. If not provided, the current user's id will be used.
1608
 * @return string The timezone identifier string for the user's timezone.
1609
 */
1610
function getUserTimezone($id_member = null)
1611
{
1612
	global $smcFunc, $context, $sourcedir, $user_info, $modSettings;
1613
1614
	if (is_null($id_member) && $user_info['is_guest'] == false)
1615
		$id_member = $context['user']['id'];
1616
1617 View Code Duplication
	if (isset($id_member))
1618
	{
1619
		$request = $smcFunc['db_query']('', '
1620
			SELECT timezone
1621
			FROM {db_prefix}members
1622
			WHERE id_member = {int:id_member}',
1623
			array(
1624
				'id_member' => $id_member,
1625
			)
1626
		);
1627
		list($timezone) = $smcFunc['db_fetch_row']($request);
1628
		$smcFunc['db_free_result']($request);
1629
	}
1630
1631 View Code Duplication
	if (empty($timezone) || !in_array($timezone, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1632
		$timezone = isset($modSettings['default_timezone']) ? $modSettings['default_timezone'] : date_default_timezone_get();
1633
1634
	return $timezone;
1635
}
1636
1637
/**
1638
 * Gets all of the holidays for the listing
1639
 *
1640
 * @param int $start The item to start with (for pagination purposes)
1641
 * @param int $items_per_page How many items to show on each page
1642
 * @param string $sort A string indicating how to sort the results
1643
 * @return array An array of holidays, each of which is an array containing the id, year, month, day and title of the holiday
1644
 */
1645 View Code Duplication
function list_getHolidays($start, $items_per_page, $sort)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1646
{
1647
	global $smcFunc;
1648
1649
	$request = $smcFunc['db_query']('', '
1650
		SELECT id_holiday, YEAR(event_date) AS year, MONTH(event_date) AS month, DAYOFMONTH(event_date) AS day, title
1651
		FROM {db_prefix}calendar_holidays
1652
		ORDER BY {raw:sort}
1653
		LIMIT {int:start}, {int:max}',
1654
		array(
1655
			'sort' => $sort,
1656
			'start' => $start,
1657
			'max' => $items_per_page,
1658
		)
1659
	);
1660
	$holidays = array();
1661
	while ($row = $smcFunc['db_fetch_assoc']($request))
1662
		$holidays[] = $row;
1663
	$smcFunc['db_free_result']($request);
1664
1665
	return $holidays;
1666
}
1667
1668
/**
1669
 * Helper function to get the total number of holidays
1670
 *
1671
 * @return int The total number of holidays
1672
 */
1673
function list_getNumHolidays()
1674
{
1675
	global $smcFunc;
1676
1677
	$request = $smcFunc['db_query']('', '
1678
		SELECT COUNT(*)
1679
		FROM {db_prefix}calendar_holidays',
1680
		array(
1681
		)
1682
	);
1683
	list($num_items) = $smcFunc['db_fetch_row']($request);
1684
	$smcFunc['db_free_result']($request);
1685
1686
	return (int) $num_items;
1687
}
1688
1689
/**
1690
 * Remove a holiday from the calendar
1691
 *
1692
 * @param array $holiday_ids An array of IDs of holidays to delete
1693
 */
1694
function removeHolidays($holiday_ids)
1695
{
1696
	global $smcFunc;
1697
1698
	$smcFunc['db_query']('', '
1699
		DELETE FROM {db_prefix}calendar_holidays
1700
		WHERE id_holiday IN ({array_int:id_holiday})',
1701
		array(
1702
			'id_holiday' => $holiday_ids,
1703
		)
1704
	);
1705
1706
	updateSettings(array(
1707
		'calendar_updated' => time(),
1708
	));
1709
}
1710
1711
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...