Completed
Push — release-2.1 ( 2ca19b...643a63 )
by Mathias
08:07
created

Subs-Calendar.php ➔ getUserTimezone()   D

Complexity

Conditions 10
Paths 26

Size

Total Lines 36
Code Lines 18

Duplication

Lines 15
Ratio 41.67 %

Importance

Changes 0
Metric Value
cc 10
eloc 18
nc 26
nop 1
dl 15
loc 36
rs 4.8196
c 0
b 0
f 0

How to fix   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
	if ($smcFunc['db_title'] != "PostgreSQL")
37
	{
38
		// Collect all of the birthdays for this month.  I know, it's a painful query.
39
		$result = $smcFunc['db_query']('birthday_array', '
40
			SELECT id_member, real_name, YEAR(birthdate) AS birth_year, birthdate
41
			FROM {db_prefix}members
42
			WHERE YEAR(birthdate) != {string:year_one}
43
				AND MONTH(birthdate) != {int:no_month}
44
				AND DAYOFMONTH(birthdate) != {int:no_day}
45
				AND YEAR(birthdate) <= {int:max_year}
46
				AND (
47
					DATE_FORMAT(birthdate, {string:year_low}) BETWEEN {date:low_date} AND {date:high_date}' . ($year_low == $year_high ? '' : '
48
					OR DATE_FORMAT(birthdate, {string:year_high}) BETWEEN {date:low_date} AND {date:high_date}') . '
49
				)
50
				AND is_activated = {int:is_activated}',
51
			array(
52
				'is_activated' => 1,
53
				'no_month' => 0,
54
				'no_day' => 0,
55
				'year_one' => '0001',
56
				'year_low' => $year_low . '-%m-%d',
57
				'year_high' => $year_high . '-%m-%d',
58
				'low_date' => $low_date,
59
				'high_date' => $high_date,
60
				'max_year' => $year_high,
61
			)
62
		);
63
	}
64
	else
65
	{
66
		$result = $smcFunc['db_query']('birthday_array', '
67
			SELECT id_member, real_name, YEAR(birthdate) AS birth_year, birthdate
68
			FROM {db_prefix}members
69
			WHERE YEAR(birthdate) != {string:year_one}
70
				AND MONTH(birthdate) != {int:no_month}
71
				AND DAYOFMONTH(birthdate) != {int:no_day}
72
				AND (
73
					indexable_month_day(birthdate) BETWEEN indexable_month_day({date:year_low_low_date}) AND indexable_month_day({date:year_low_high_date})' . ($year_low == $year_high ? '' : '
74
					OR  indexable_month_day(birthdate) BETWEEN indexable_month_day({date:year_high_low_date}) AND indexable_month_day({date:year_high_high_date})') . '
75
				)
76
				AND is_activated = {int:is_activated}',
77
			array(
78
				'is_activated' => 1,
79
				'no_month' => 0,
80
				'no_day' => 0,
81
				'year_one' => '0001',
82
				'year_low' => $year_low . '-%m-%d',
83
				'year_high' => $year_high . '-%m-%d',
84
				'year_low_low_date' => $low_date,
85
				'year_low_high_date' => ($year_low == $year_high ? $high_date : $year_low . '-12-31'),
86
				'year_high_low_date' => ($year_low == $year_high ? $low_date : $year_high . '-01-01'),
87
				'year_high_high_date' => $high_date,
88
			)
89
		);
90
	}
91
	$bday = array();
92
	while ($row = $smcFunc['db_fetch_assoc']($result))
93
	{
94
		if ($year_low != $year_high)
95
			$age_year = substr($row['birthdate'], 5) < substr($high_date, 5) ? $year_high : $year_low;
96
		else
97
			$age_year = $year_low;
98
99
		$bday[$age_year . substr($row['birthdate'], 4)][] = array(
100
			'id' => $row['id_member'],
101
			'name' => $row['real_name'],
102
			'age' => $row['birth_year'] > 4 && $row['birth_year'] <= $age_year ? $age_year - $row['birth_year'] : null,
103
			'is_last' => false
104
		);
105
	}
106
	$smcFunc['db_free_result']($result);
107
108
	ksort($bday);
109
110
	// Set is_last, so the themes know when to stop placing separators.
111 View Code Duplication
	foreach ($bday as $mday => $array)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
112
		$bday[$mday][count($array) - 1]['is_last'] = true;
113
114
	return $bday;
115
}
116
117
/**
118
 * Get all calendar events within the given time range.
119
 *
120
 * - finds all the posted calendar events within a date range.
121
 * - both the earliest_date and latest_date should be in the standard YYYY-MM-DD format.
122
 * - censors the posted event titles.
123
 * - uses the current user's permissions if use_permissions is true, otherwise it does nothing "permission specific"
124
 *
125
 * @param string $low_date The low end of the range, inclusive, in YYYY-MM-DD format
126
 * @param string $high_date The high end of the range, inclusive, in YYYY-MM-DD format
127
 * @param bool $use_permissions Whether to use permissions
128
 * @return array Contextual information if use_permissions is true, and an array of the data needed to build that otherwise
129
 */
130
function getEventRange($low_date, $high_date, $use_permissions = true)
131
{
132
	global $scripturl, $modSettings, $user_info, $smcFunc, $context, $sourcedir;
133
	require_once($sourcedir . '/Subs.php');
134
135
	$low_object = date_create($low_date);
136
	$high_object = date_create($high_date);
137
138
	// Find all the calendar info...
139
	$result = $smcFunc['db_query']('', '
140
		SELECT
141
			cal.id_event, cal.title, cal.id_member, cal.id_topic, cal.id_board,
142
			cal.start_date, cal.end_date, cal.start_time, cal.end_time, cal.timezone, cal.location,
143
			b.member_groups, t.id_first_msg, t.approved, b.id_board
144
		FROM {db_prefix}calendar AS cal
145
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = cal.id_board)
146
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic)
147
		WHERE cal.start_date <= {date:high_date}
148
			AND cal.end_date >= {date:low_date}' . ($use_permissions ? '
149
			AND (cal.id_board = {int:no_board_link} OR {query_wanna_see_board})' : ''),
150
		array(
151
			'high_date' => $high_date,
152
			'low_date' => $low_date,
153
			'no_board_link' => 0,
154
		)
155
	);
156
	$events = array();
157
	while ($row = $smcFunc['db_fetch_assoc']($result))
158
	{
159
		// If the attached topic is not approved then for the moment pretend it doesn't exist
160
		if (!empty($row['id_first_msg']) && $modSettings['postmod_active'] && !$row['approved'])
161
			continue;
162
163
		// Force a censor of the title - as often these are used by others.
164
		censorText($row['title'], $use_permissions ? false : true);
165
166
		// Get the various time and date properties for this event
167
		list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
168
169
		// Sanity check
170 View Code Duplication
		if (!empty($start['error_count']) || !empty($start['warning_count']) || !empty($end['error_count']) || !empty($end['warning_count']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
171
			continue;
172
173
		// Get set up for the loop
174
		$start_object = date_create($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''), timezone_open($tz));
175
		$end_object = date_create($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''), timezone_open($tz));
176
		date_timezone_set($start_object, timezone_open(date_default_timezone_get()));
177
		date_timezone_set($end_object, timezone_open(date_default_timezone_get()));
178
		date_time_set($start_object, 0, 0, 0);
179
		date_time_set($end_object, 0, 0, 0);
180
		$start_date_string = date_format($start_object, 'Y-m-d');
181
		$end_date_string = date_format($end_object, 'Y-m-d');
182
183
		$cal_date = ($start_object >= $low_object) ? $start_object : $low_object;
184
		while ($cal_date <= $end_object && $cal_date <= $high_object)
185
		{
186
			$starts_today = (date_format($cal_date, 'Y-m-d') == $start_date_string);
187
			$ends_today = (date_format($cal_date, 'Y-m-d') == $end_date_string);
188
189
			$eventProperties = array(
190
					'id' => $row['id_event'],
191
					'title' => $row['title'],
192
					'year' => $start['year'],
193
					'month' => $start['month'],
194
					'day' => $start['day'],
195
					'hour' => !$allday ? $start['hour'] : null,
196
					'minute' => !$allday ? $start['minute'] : null,
197
					'second' => !$allday ? $start['second'] : null,
198
					'start_date' => $row['start_date'],
199
					'start_date_local' => $start['date_local'],
200
					'start_date_orig' => $start['date_orig'],
201
					'start_time' => !$allday ? $row['start_time'] : null,
202
					'start_time_local' => !$allday ? $start['time_local'] : null,
203
					'start_time_orig' => !$allday ? $start['time_orig'] : null,
204
					'start_timestamp' => $start['timestamp'],
205
					'start_datetime' => $start['datetime'],
206
					'start_iso_gmdate' => $start['iso_gmdate'],
207
					'end_year' => $end['year'],
208
					'end_month' => $end['month'],
209
					'end_day' => $end['day'],
210
					'end_hour' => !$allday ? $end['hour'] : null,
211
					'end_minute' => !$allday ? $end['minute'] : null,
212
					'end_second' => !$allday ? $end['second'] : null,
213
					'end_date' => $row['end_date'],
214
					'end_date_local' => $end['date_local'],
215
					'end_date_orig' => $end['date_orig'],
216
					'end_time' => !$allday ? $row['end_time'] : null,
217
					'end_time_local' => !$allday ? $end['time_local'] : null,
218
					'end_time_orig' => !$allday ? $end['time_orig'] : null,
219
					'end_timestamp' => $end['timestamp'],
220
					'end_datetime' => $end['datetime'],
221
					'end_iso_gmdate' => $end['iso_gmdate'],
222
					'allday' => $allday,
223
					'tz' => !$allday ? $tz : null,
224
					'tz_abbrev' => !$allday ? $tz_abbrev : null,
225
					'span' => $span,
226
					'is_last' => false,
227
					'id_board' => $row['id_board'],
228
					'is_selected' => !empty($context['selected_event']) && $context['selected_event'] == $row['id_event'],
229
					'starts_today' => $starts_today,
230
					'ends_today' => $ends_today,
231
					'location' => $row['location'],
232
			);
233
234
			// If we're using permissions (calendar pages?) then just ouput normal contextual style information.
235
			if ($use_permissions)
236
				$events[date_format($cal_date, 'Y-m-d')][] = array_merge($eventProperties, array(
237
					'href' => $row['id_board'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
238
					'link' => $row['id_board'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
239
					'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
240
					'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'],
241
					'can_export' => !empty($modSettings['cal_export']) ? true : false,
242
					'export_href' => $scripturl . '?action=calendar;sa=ical;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
243
				));
244
			// Otherwise, this is going to be cached and the VIEWER'S permissions should apply... just put together some info.
245
			else
246
				$events[date_format($cal_date, 'Y-m-d')][] = array_merge($eventProperties, array(
247
					'href' => $row['id_topic'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
248
					'link' => $row['id_topic'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
249
					'can_edit' => false,
250
					'can_export' => !empty($modSettings['cal_export']) ? true : false,
251
					'topic' => $row['id_topic'],
252
					'msg' => $row['id_first_msg'],
253
					'poster' => $row['id_member'],
254
					'allowed_groups' => explode(',', $row['member_groups']),
255
				));
256
257
			date_add($cal_date, date_interval_create_from_date_string('1 day'));
258
		}
259
	}
260
	$smcFunc['db_free_result']($result);
261
262
	// If we're doing normal contextual data, go through and make things clear to the templates ;).
263
	if ($use_permissions)
264
	{
265 View Code Duplication
		foreach ($events as $mday => $array)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
266
			$events[$mday][count($array) - 1]['is_last'] = true;
267
	}
268
269
	ksort($events);
270
271
	return $events;
272
}
273
274
/**
275
 * Get all holidays within the given time range.
276
 *
277
 * @param string $low_date The low end of the range, inclusive, in YYYY-MM-DD format
278
 * @param string $high_date The high end of the range, inclusive, in YYYY-MM-DD format
279
 * @return array An array of days, which are all arrays of holiday names.
280
 */
281
function getHolidayRange($low_date, $high_date)
282
{
283
	global $smcFunc;
284
285
	// Get the lowest and highest dates for "all years".
286
	if (substr($low_date, 0, 4) != substr($high_date, 0, 4))
287
		$allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_dec}
288
			OR event_date BETWEEN {date:all_year_jan} AND {date:all_year_high}';
289
	else
290
		$allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_high}';
291
292
	// Find some holidays... ;).
293
	$result = $smcFunc['db_query']('', '
294
		SELECT event_date, YEAR(event_date) AS year, title
295
		FROM {db_prefix}calendar_holidays
296
		WHERE event_date BETWEEN {date:low_date} AND {date:high_date}
297
			OR ' . $allyear_part,
298
		array(
299
			'low_date' => $low_date,
300
			'high_date' => $high_date,
301
			'all_year_low' => '0004' . substr($low_date, 4),
302
			'all_year_high' => '0004' . substr($high_date, 4),
303
			'all_year_jan' => '0004-01-01',
304
			'all_year_dec' => '0004-12-31',
305
		)
306
	);
307
	$holidays = array();
308
	while ($row = $smcFunc['db_fetch_assoc']($result))
309
	{
310
		if (substr($low_date, 0, 4) != substr($high_date, 0, 4))
311
			$event_year = substr($row['event_date'], 5) < substr($high_date, 5) ? substr($high_date, 0, 4) : substr($low_date, 0, 4);
312
		else
313
			$event_year = substr($low_date, 0, 4);
314
315
		$holidays[$event_year . substr($row['event_date'], 4)][] = $row['title'];
316
	}
317
	$smcFunc['db_free_result']($result);
318
319
	ksort($holidays);
320
321
	return $holidays;
322
}
323
324
/**
325
 * Does permission checks to see if an event can be linked to a board/topic.
326
 * checks if the current user can link the current topic to the calendar, permissions et al.
327
 * this requires the calendar_post permission, a forum moderator, or a topic starter.
328
 * expects the $topic and $board variables to be set.
329
 * if the user doesn't have proper permissions, an error will be shown.
330
 */
331
function canLinkEvent()
332
{
333
	global $user_info, $topic, $board, $smcFunc;
334
335
	// If you can't post, you can't link.
336
	isAllowedTo('calendar_post');
337
338
	// No board?  No topic?!?
339
	if (empty($board))
340
		fatal_lang_error('missing_board_id', false);
341
	if (empty($topic))
342
		fatal_lang_error('missing_topic_id', false);
343
344
	// Administrator, Moderator, or owner.  Period.
345
	if (!allowedTo('admin_forum') && !allowedTo('moderate_board'))
346
	{
347
		// Not admin or a moderator of this board. You better be the owner - or else.
348
		$result = $smcFunc['db_query']('', '
349
			SELECT id_member_started
350
			FROM {db_prefix}topics
351
			WHERE id_topic = {int:current_topic}
352
			LIMIT 1',
353
			array(
354
				'current_topic' => $topic,
355
			)
356
		);
357
		if ($row = $smcFunc['db_fetch_assoc']($result))
358
		{
359
			// Not the owner of the topic.
360
			if ($row['id_member_started'] != $user_info['id'])
361
				fatal_lang_error('not_your_topic', 'user');
362
		}
363
		// Topic/Board doesn't exist.....
364
		else
365
			fatal_lang_error('calendar_no_topic', 'general');
366
		$smcFunc['db_free_result']($result);
367
	}
368
}
369
370
/**
371
 * Returns date information about 'today' relative to the users time offset.
372
 * returns an array with the current date, day, month, and year.
373
 * takes the users time offset into account.
374
 * @return array An array of info about today, based on forum time. Has 'day', 'month', 'year' and 'date' (in YYYY-MM-DD format)
375
 */
376
function getTodayInfo()
377
{
378
	return array(
379
		'day' => (int) strftime('%d', forum_time()),
380
		'month' => (int) strftime('%m', forum_time()),
381
		'year' => (int) strftime('%Y', forum_time()),
382
		'date' => strftime('%Y-%m-%d', forum_time()),
383
	);
384
}
385
386
/**
387
 * Provides information (link, month, year) about the previous and next month.
388
 * @param int $month The month to display
389
 * @param int $year The year
390
 * @param array $calendarOptions An array of calendar options
391
 * @param bool $is_previous Whether this is the previous month
392
 * @return array A large array containing all the information needed to show a calendar grid for the given month
393
 */
394
function getCalendarGrid($month, $year, $calendarOptions, $is_previous = false)
395
{
396
	global $scripturl, $modSettings;
397
398
	// Eventually this is what we'll be returning.
399
	$calendarGrid = array(
400
		'week_days' => array(),
401
		'weeks' => array(),
402
		'short_day_titles' => !empty($calendarOptions['short_day_titles']),
403
		'short_month_titles' => !empty($calendarOptions['short_month_titles']),
404
		'highlight' => array(
405
			'events' => !empty($calendarOptions['highlight']['events']) && !empty($calendarOptions['show_events']) ? $calendarOptions['highlight']['events'] : 0,
406
			'holidays' => !empty($calendarOptions['highlight']['holidays']) && !empty($calendarOptions['show_holidays']) ? $calendarOptions['highlight']['holidays'] : 0,
407
			'birthdays' => !empty($calendarOptions['highlight']['birthdays']) && !empty($calendarOptions['show_birthdays']) ? $calendarOptions['highlight']['birthdays'] : 0,
408
		),
409
		'current_month' => $month,
410
		'current_year' => $year,
411
		'show_next_prev' => !empty($calendarOptions['show_next_prev']),
412
		'show_week_links' => isset($calendarOptions['show_week_links']) ? $calendarOptions['show_week_links'] : 0,
413
		'previous_calendar' => array(
414
			'year' => $month == 1 ? $year - 1 : $year,
415
			'month' => $month == 1 ? 12 : $month - 1,
416
			'disabled' => $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year),
417
		),
418
		'next_calendar' => array(
419
			'year' => $month == 12 ? $year + 1 : $year,
420
			'month' => $month == 12 ? 1 : $month + 1,
421
			'disabled' => $modSettings['cal_maxyear'] < ($month == 12 ? $year + 1 : $year),
422
		),
423
		'size' => empty($modSettings['cal_display_type']) ? 'large' : 'small',
424
	);
425
426
	// Get today's date.
427
	$today = getTodayInfo();
428
429
	// Get information about this month.
430
	$month_info = array(
431
		'first_day' => array(
432
			'day_of_week' => (int) strftime('%w', mktime(0, 0, 0, $month, 1, $year)),
433
			'week_num' => (int) strftime('%U', mktime(0, 0, 0, $month, 1, $year)),
434
			'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month, 1, $year)),
435
		),
436
		'last_day' => array(
437
			'day_of_month' => (int) strftime('%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)),
438
			'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)),
439
		),
440
		'first_day_of_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year)),
441
		'first_day_of_next_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year + 1)),
442
	);
443
444
	// The number of days the first row is shifted to the right for the starting day.
445
	$nShift = $month_info['first_day']['day_of_week'];
446
447
	$calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day'];
448
449
	// Starting any day other than Sunday means a shift...
450
	if (!empty($calendarOptions['start_day']))
451
	{
452
		$nShift -= $calendarOptions['start_day'];
453
		if ($nShift < 0)
454
			$nShift = 7 + $nShift;
455
	}
456
457
	// Number of rows required to fit the month.
458
	$nRows = floor(($month_info['last_day']['day_of_month'] + $nShift) / 7);
459
	if (($month_info['last_day']['day_of_month'] + $nShift) % 7)
460
		$nRows++;
461
462
	// Fetch the arrays for birthdays, posted events, and holidays.
463
	$bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
464
	$events = $calendarOptions['show_events'] ? getEventRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
465
	$holidays = $calendarOptions['show_holidays'] ? getHolidayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
466
467
	// Days of the week taking into consideration that they may want it to start on any day.
468
	$count = $calendarOptions['start_day'];
469
	for ($i = 0; $i < 7; $i++)
470
	{
471
		$calendarGrid['week_days'][] = $count;
472
		$count++;
473
		if ($count == 7)
474
			$count = 0;
475
	}
476
477
	// Iterate through each week.
478
	$calendarGrid['weeks'] = array();
479
	for ($nRow = 0; $nRow < $nRows; $nRow++)
480
	{
481
		// Start off the week - and don't let it go above 52, since that's the number of weeks in a year.
482
		$calendarGrid['weeks'][$nRow] = array(
483
			'days' => array(),
484
		);
485
486
		// And figure out all the days.
487
		for ($nCol = 0; $nCol < 7; $nCol++)
488
		{
489
			$nDay = ($nRow * 7) + $nCol - $nShift + 1;
490
491
			if ($nDay < 1 || $nDay > $month_info['last_day']['day_of_month'])
492
				$nDay = 0;
493
494
			$date = sprintf('%04d-%02d-%02d', $year, $month, $nDay);
495
496
			$calendarGrid['weeks'][$nRow]['days'][$nCol] = array(
497
				'day' => $nDay,
498
				'date' => $date,
499
				'is_today' => $date == $today['date'],
500
				'is_first_day' => !empty($calendarOptions['show_week_num']) && (($month_info['first_day']['day_of_week'] + $nDay - 1) % 7 == $calendarOptions['start_day']),
501
				'is_first_of_month' => $nDay === 1,
502
				'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
503
				'events' => !empty($events[$date]) ? $events[$date] : array(),
504
				'birthdays' => !empty($bday[$date]) ? $bday[$date] : array(),
505
			);
506
		}
507
	}
508
509
	// What is the last day of the month?
510
	if ($is_previous === true)
511
		$calendarGrid['last_of_month'] = $month_info['last_day']['day_of_month'];
512
513
	// We'll use the shift in the template.
514
	$calendarGrid['shift'] = $nShift;
515
516
	// Set the previous and the next month's links.
517
	$calendarGrid['previous_calendar']['href'] = $scripturl . '?action=calendar;viewmonth;year=' . $calendarGrid['previous_calendar']['year'] . ';month=' . $calendarGrid['previous_calendar']['month'];
518
	$calendarGrid['next_calendar']['href'] = $scripturl . '?action=calendar;viewmonth;year=' . $calendarGrid['next_calendar']['year'] . ';month=' . $calendarGrid['next_calendar']['month'];
519
520
	return $calendarGrid;
521
}
522
523
/**
524
 * Returns the information needed to show a calendar for the given week.
525
 * @param int $month The month
526
 * @param int $year The year
527
 * @param int $day The day
528
 * @param array $calendarOptions An array of calendar options
529
 * @return array An array of information needed to display the grid for a single week on the calendar
530
 */
531
function getCalendarWeek($month, $year, $day, $calendarOptions)
532
{
533
	global $scripturl, $modSettings, $txt;
534
535
	// Get today's date.
536
	$today = getTodayInfo();
537
538
	// What is the actual "start date" for the passed day.
539
	$calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day'];
540
	$day_of_week = (int) strftime('%w', mktime(0, 0, 0, $month, $day, $year));
541
	if ($day_of_week != $calendarOptions['start_day'])
542
	{
543
		// Here we offset accordingly to get things to the real start of a week.
544
		$date_diff = $day_of_week - $calendarOptions['start_day'];
545
		if ($date_diff < 0)
546
			$date_diff += 7;
547
		$new_timestamp = mktime(0, 0, 0, $month, $day, $year) - $date_diff * 86400;
548
		$day = (int) strftime('%d', $new_timestamp);
549
		$month = (int) strftime('%m', $new_timestamp);
550
		$year = (int) strftime('%Y', $new_timestamp);
551
	}
552
553
	// Now start filling in the calendar grid.
554
	$calendarGrid = array(
555
		'show_next_prev' => !empty($calendarOptions['show_next_prev']),
556
		// Previous week is easy - just step back one day.
557
		'previous_week' => array(
558
			'year' => $day == 1 ? ($month == 1 ? $year - 1 : $year) : $year,
559
			'month' => $day == 1 ? ($month == 1 ? 12 : $month - 1) : $month,
560
			'day' => $day == 1 ? 28 : $day - 1,
561
			'disabled' => $day < 7 && $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year),
562
		),
563
		'next_week' => array(
564
			'disabled' => $day > 25 && $modSettings['cal_maxyear'] < ($month == 12 ? $year + 1 : $year),
565
		),
566
		'size' => empty($modSettings['cal_display_type']) ? 'large' : 'small',
567
	);
568
569
	// The next week calculation requires a bit more work.
570
	$curTimestamp = mktime(0, 0, 0, $month, $day, $year);
571
	$nextWeekTimestamp = $curTimestamp + 604800;
572
	$calendarGrid['next_week']['day'] = (int) strftime('%d', $nextWeekTimestamp);
573
	$calendarGrid['next_week']['month'] = (int) strftime('%m', $nextWeekTimestamp);
574
	$calendarGrid['next_week']['year'] = (int) strftime('%Y', $nextWeekTimestamp);
575
576
	// Fetch the arrays for birthdays, posted events, and holidays.
577
	$startDate = strftime('%Y-%m-%d', $curTimestamp);
578
	$endDate = strftime('%Y-%m-%d', $nextWeekTimestamp);
579
	$bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($startDate, $endDate) : array();
580
	$events = $calendarOptions['show_events'] ? getEventRange($startDate, $endDate) : array();
581
	$holidays = $calendarOptions['show_holidays'] ? getHolidayRange($startDate, $endDate) : array();
582
583
	// An adjustment value to apply to all calculated week numbers.
584
	if (!empty($calendarOptions['show_week_num']))
585
	{
586
		$timestamp = mktime(0, 0, 0, $month, $day, $year);
587
		$calendarGrid['week_title'] = sprintf($txt['calendar_week_beginning'], date('F', $timestamp), date('j', $timestamp), date('Y', $timestamp));
588
	}
589
590
	// This holds all the main data - there is at least one month!
591
	$calendarGrid['months'] = array();
592
	$lastDay = 99;
593
	$curDay = $day;
594
	$curDayOfWeek = $calendarOptions['start_day'];
595
	for ($i = 0; $i < 7; $i++)
596
	{
597
		// Have we gone into a new month (Always happens first cycle too)
598
		if ($lastDay > $curDay)
599
		{
600
			$curMonth = $lastDay == 99 ? $month : ($month == 12 ? 1 : $month + 1);
601
			$curYear = $lastDay == 99 ? $year : ($curMonth == 1 && $month == 12 ? $year + 1 : $year);
602
			$calendarGrid['months'][$curMonth] = array(
603
				'current_month' => $curMonth,
604
				'current_year' => $curYear,
605
				'days' => array(),
606
			);
607
		}
608
609
		// Add todays information to the pile!
610
		$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...
611
612
		$calendarGrid['months'][$curMonth]['days'][$curDay] = array(
613
			'day' => $curDay,
614
			'day_of_week' => $curDayOfWeek,
615
			'date' => $date,
616
			'is_today' => $date == $today['date'],
617
			'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
618
			'events' => !empty($events[$date]) ? $events[$date] : array(),
619
			'birthdays' => !empty($bday[$date]) ? $bday[$date] : array()
620
		);
621
622
		// Make the last day what the current day is and work out what the next day is.
623
		$lastDay = $curDay;
624
		$curTimestamp += 86400;
625
		$curDay = (int) strftime('%d', $curTimestamp);
626
627
		// Also increment the current day of the week.
628
		$curDayOfWeek = $curDayOfWeek >= 6 ? 0 : ++$curDayOfWeek;
629
	}
630
631
	// Set the previous and the next week's links.
632
	$calendarGrid['previous_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['previous_week']['year'] . ';month=' . $calendarGrid['previous_week']['month'] . ';day=' . $calendarGrid['previous_week']['day'];
633
	$calendarGrid['next_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['next_week']['year'] . ';month=' . $calendarGrid['next_week']['month'] . ';day=' . $calendarGrid['next_week']['day'];
634
635
	return $calendarGrid;
636
}
637
638
639
/**
640
 * Returns the information needed to show a list of upcoming events, birthdays, and holidays on the calendar.
641
 * @param int $start_date The start of a date range
642
 * @param int $end_date The end of a date range
643
 * @param array $calendarOptions An array of calendar options
644
 * @return array An array of information needed to display a list of upcoming events, etc., on the calendar
645
 */
646
function getCalendarList($start_date, $end_date, $calendarOptions)
647
{
648
	global $scripturl, $modSettings, $user_info, $txt, $context, $sourcedir;
649
	require_once($sourcedir . '/Subs.php');
650
651
	// DateTime objects make life easier
652
	$start_object = date_create($start_date);
653
	$end_object = date_create($end_date);
654
655
	$calendarGrid = array(
656
		'start_date' => $start_date,
657
		'start_year' => date_format($start_object, 'Y'),
658
		'start_month' => date_format($start_object, 'm'),
659
		'start_day' => date_format($start_object, 'd'),
660
		'end_date' => $end_date,
661
		'end_year' => date_format($end_object, 'Y'),
662
		'end_month' => date_format($end_object, 'm'),
663
		'end_day' => date_format($end_object, 'd'),
664
	);
665
666
	$calendarGrid['birthdays'] = $calendarOptions['show_birthdays'] ? getBirthdayRange($start_date, $end_date) : array();
667
	$calendarGrid['holidays'] = $calendarOptions['show_holidays'] ? getHolidayRange($start_date, $end_date) : array();
668
	$calendarGrid['events'] = $calendarOptions['show_events'] ? getEventRange($start_date, $end_date) : array();
669
670
	// Get rid of duplicate events
671
	$temp = array();
672
	foreach ($calendarGrid['events'] as $date => $date_events)
673
	{
674
		foreach ($date_events as $event_key => $event_val)
675
		{
676
			if (in_array($event_val['id'], $temp))
677
				unset($calendarGrid['events'][$date][$event_key]);
678
			else
679
				$temp[] = $event_val['id'];
680
		}
681
	}
682
683
	// Give birthdays and holidays a friendly format, without the year
684 View Code Duplication
	if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
685
		$date_format = '%b %d';
686
	else
687
		$date_format = str_replace(array('%Y', '%y', '%G', '%g', '%C', '%c', '%D'), array('', '', '', '', '', '%b %d', '%m/%d'), $matches[0]);
688
689
	foreach (array('birthdays', 'holidays') as $type)
690
	{
691
		foreach ($calendarGrid[$type] as $date => $date_content)
692
		{
693
			$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...
694
695
			$calendarGrid[$type][$date]['date_local'] = $date_local;
696
		}
697
	}
698
699
	loadCSSFile('jquery-ui.datepicker.css', array('defer' => false), 'smf_datepicker');
700
	loadJavaScriptFile('jquery-ui.datepicker.min.js', array('defer' => true), 'smf_datepicker');
701
	loadJavaScriptFile('jquery.timepicker.min.js', array('defer' => true), 'smf_timepicker');
702
	loadJavaScriptFile('datepair.min.js', array('defer' => true), 'smf_datepair');
703
	addInlineJavaScript('
704
	$("#calendar_range .date_input").datepicker({
705
		dateFormat: "yy-mm-dd",
706
		autoSize: true,
707
		isRTL: ' . ($context['right_to_left'] ? 'true' : 'false') . ',
708
		constrainInput: true,
709
		showAnim: "",
710
		showButtonPanel: false,
711
		minDate: "' . $modSettings['cal_minyear'] . '-01-01",
712
		maxDate: "' . $modSettings['cal_maxyear'] . '-12-31",
713
		yearRange: "' . $modSettings['cal_minyear'] . ':' . $modSettings['cal_maxyear'] . '",
714
		hideIfNoPrevNext: true,
715
		monthNames: ["' . implode('", "', $txt['months_titles']) . '"],
716
		monthNamesShort: ["' . implode('", "', $txt['months_short']) . '"],
717
		dayNames: ["' . implode('", "', $txt['days']) . '"],
718
		dayNamesShort: ["' . implode('", "', $txt['days_short']) . '"],
719
		dayNamesMin: ["' . implode('", "', $txt['days_short']) . '"],
720
		prevText: "' . $txt['prev_month'] . '",
721
		nextText: "' . $txt['next_month'] . '",
722
	});
723
	var date_entry = document.getElementById("calendar_range");
724
	var date_entry_pair = new Datepair(date_entry, {
725
		dateClass: "date_input",
726
		autoclose: true,
727
		parseDate: function (el) {
728
		    var utc = new Date($(el).datepicker("getDate"));
729
		    return utc && new Date(utc.getTime() + (utc.getTimezoneOffset() * 60000));
730
		},
731
		updateDate: function (el, v) {
732
		    $(el).datepicker("setDate", new Date(v.getTime() - (v.getTimezoneOffset() * 60000)));
733
		}
734
	});
735
	', true);
736
737
	return $calendarGrid;
738
}
739
740
/**
741
 * Retrieve all events for the given days, independently of the users offset.
742
 * cache callback function used to retrieve the birthdays, holidays, and events between now and now + days_to_index.
743
 * widens the search range by an extra 24 hours to support time offset shifts.
744
 * used by the cache_getRecentEvents function to get the information needed to calculate the events taking the users time offset into account.
745
 *
746
 * @param int $days_to_index How many days' worth of info to index
747
 * @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
748
 */
749
function cache_getOffsetIndependentEvents($days_to_index)
750
{
751
	$low_date = strftime('%Y-%m-%d', forum_time(false) - 24 * 3600);
752
	$high_date = strftime('%Y-%m-%d', forum_time(false) + $days_to_index * 24 * 3600);
753
754
	return array(
755
		'data' => array(
756
			'holidays' => getHolidayRange($low_date, $high_date),
757
			'birthdays' => getBirthdayRange($low_date, $high_date),
758
			'events' => getEventRange($low_date, $high_date, false),
759
		),
760
		'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\']);',
761
		'expires' => time() + 3600,
762
	);
763
}
764
765
/**
766
 * cache callback function used to retrieve the upcoming birthdays, holidays, and events within the given period, taking into account the users time offset.
767
 * Called from the BoardIndex to display the current day's events on the board index
768
 * used by the board index and SSI to show the upcoming events.
769
 * @param array $eventOptions An array of event options. Only 'num_days_shown' is used here
770
 * @return array An array containing the info that was cached as well as a few other relevant things
771
 */
772
function cache_getRecentEvents($eventOptions)
773
{
774
	// With the 'static' cached data we can calculate the user-specific data.
775
	$cached_data = cache_quick_get('calendar_index', 'Subs-Calendar.php', 'cache_getOffsetIndependentEvents', array($eventOptions['num_days_shown']));
776
777
	// Get the information about today (from user perspective).
778
	$today = getTodayInfo();
779
780
	$return_data = array(
781
		'calendar_holidays' => array(),
782
		'calendar_birthdays' => array(),
783
		'calendar_events' => array(),
784
	);
785
786
	// Set the event span to be shown in seconds.
787
	$days_for_index = $eventOptions['num_days_shown'] * 86400;
788
789
	// Get the current member time/date.
790
	$now = forum_time();
791
792
	// Holidays between now and now + days.
793
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
794
	{
795
		if (isset($cached_data['holidays'][strftime('%Y-%m-%d', $i)]))
796
			$return_data['calendar_holidays'] = array_merge($return_data['calendar_holidays'], $cached_data['holidays'][strftime('%Y-%m-%d', $i)]);
797
	}
798
799
	// Happy Birthday, guys and gals!
800
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
801
	{
802
		$loop_date = strftime('%Y-%m-%d', $i);
803
		if (isset($cached_data['birthdays'][$loop_date]))
804
		{
805
			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...
806
				$cached_data['birthdays'][strftime('%Y-%m-%d', $i)][$index]['is_today'] = $loop_date === $today['date'];
807
			$return_data['calendar_birthdays'] = array_merge($return_data['calendar_birthdays'], $cached_data['birthdays'][$loop_date]);
808
		}
809
	}
810
811
	$duplicates = array();
812
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
813
	{
814
		// Determine the date of the current loop step.
815
		$loop_date = strftime('%Y-%m-%d', $i);
816
817
		// No events today? Check the next day.
818
		if (empty($cached_data['events'][$loop_date]))
819
			continue;
820
821
		// Loop through all events to add a few last-minute values.
822
		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...
823
		{
824
			// Create a shortcut variable for easier access.
825
			$this_event = &$cached_data['events'][$loop_date][$ev];
826
827
			// Skip duplicates.
828
			if (isset($duplicates[$this_event['topic'] . $this_event['title']]))
829
			{
830
				unset($cached_data['events'][$loop_date][$ev]);
831
				continue;
832
			}
833
			else
834
				$duplicates[$this_event['topic'] . $this_event['title']] = true;
835
836
			// Might be set to true afterwards, depending on the permissions.
837
			$this_event['can_edit'] = false;
838
			$this_event['is_today'] = $loop_date === $today['date'];
839
			$this_event['date'] = $loop_date;
840
		}
841
842
		if (!empty($cached_data['events'][$loop_date]))
843
			$return_data['calendar_events'] = array_merge($return_data['calendar_events'], $cached_data['events'][$loop_date]);
844
	}
845
846
	// Mark the last item so that a list separator can be used in the template.
847 View Code Duplication
	for ($i = 0, $n = count($return_data['calendar_birthdays']); $i < $n; $i++)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
848
		$return_data['calendar_birthdays'][$i]['is_last'] = !isset($return_data['calendar_birthdays'][$i + 1]);
849 View Code Duplication
	for ($i = 0, $n = count($return_data['calendar_events']); $i < $n; $i++)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
850
		$return_data['calendar_events'][$i]['is_last'] = !isset($return_data['calendar_events'][$i + 1]);
851
852
	return array(
853
		'data' => $return_data,
854
		'expires' => time() + 3600,
855
		'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\']);',
856
		'post_retri_eval' => '
857
			global $context, $scripturl, $user_info;
858
859
			foreach ($cache_block[\'data\'][\'calendar_events\'] as $k => $event)
860
			{
861
				// Remove events that the user may not see or wants to ignore.
862
				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\']))
863
					unset($cache_block[\'data\'][\'calendar_events\'][$k]);
864
				else
865
				{
866
					// Whether the event can be edited depends on the permissions.
867
					$cache_block[\'data\'][\'calendar_events\'][$k][\'can_edit\'] = allowedTo(\'calendar_edit_any\') || ($event[\'poster\'] == $user_info[\'id\'] && allowedTo(\'calendar_edit_own\'));
868
869
					// The added session code makes this URL not cachable.
870
					$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\'];
871
				}
872
			}
873
874
			if (empty($params[0][\'include_holidays\']))
875
				$cache_block[\'data\'][\'calendar_holidays\'] = array();
876
			if (empty($params[0][\'include_birthdays\']))
877
				$cache_block[\'data\'][\'calendar_birthdays\'] = array();
878
			if (empty($params[0][\'include_events\']))
879
				$cache_block[\'data\'][\'calendar_events\'] = array();
880
881
			$cache_block[\'data\'][\'show_calendar\'] = !empty($cache_block[\'data\'][\'calendar_holidays\']) || !empty($cache_block[\'data\'][\'calendar_birthdays\']) || !empty($cache_block[\'data\'][\'calendar_events\']);',
882
	);
883
}
884
885
/**
886
 * Makes sure the calendar post is valid.
887
 */
888
function validateEventPost()
889
{
890
	global $modSettings, $smcFunc;
891
892
	if (!isset($_POST['deleteevent']))
893
	{
894
		// The 2.1 way
895
		if (isset($_POST['start_date']))
896
		{
897
			$d = date_parse($_POST['start_date']);
898 View Code Duplication
			if (!empty($d['error_count']) || !empty($d['warning_count']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
899
				fatal_lang_error('invalid_date', false);
900
			if (empty($d['year']))
901
				fatal_lang_error('event_year_missing', false);
902
			if (empty($d['month']))
903
				fatal_lang_error('event_month_missing', false);
904
		}
905
		elseif (isset($_POST['start_datetime']))
906
		{
907
			$d = date_parse($_POST['start_datetime']);
908 View Code Duplication
			if (!empty($d['error_count']) || !empty($d['warning_count']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
909
				fatal_lang_error('invalid_date', false);
910
			if (empty($d['year']))
911
				fatal_lang_error('event_year_missing', false);
912
			if (empty($d['month']))
913
				fatal_lang_error('event_month_missing', false);
914
		}
915
		// The 2.0 way
916
		else
917
		{
918
			// No month?  No year?
919
			if (!isset($_POST['month']))
920
				fatal_lang_error('event_month_missing', false);
921
			if (!isset($_POST['year']))
922
				fatal_lang_error('event_year_missing', false);
923
924
			// Check the month and year...
925
			if ($_POST['month'] < 1 || $_POST['month'] > 12)
926
				fatal_lang_error('invalid_month', false);
927 View Code Duplication
			if ($_POST['year'] < $modSettings['cal_minyear'] || $_POST['year'] > $modSettings['cal_maxyear'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
928
				fatal_lang_error('invalid_year', false);
929
		}
930
	}
931
932
	// Make sure they're allowed to post...
933
	isAllowedTo('calendar_post');
934
935
	// If they want to us to calculate an end date, make sure it will fit in an acceptable range.
936
	if (isset($_POST['span']))
937
	{
938
		if (($_POST['span'] < 1) || (!empty($modSettings['cal_maxspan']) && $_POST['span'] > $modSettings['cal_maxspan']))
939
			fatal_lang_error('invalid_days_numb', false);
940
	}
941
942
	// There is no need to validate the following values if we are just deleting the event.
943
	if (!isset($_POST['deleteevent']))
944
	{
945
		// If we're doing things the 2.0 way, check the day
946
		if (empty($_POST['start_date']) && empty($_POST['start_datetime']))
947
		{
948
			// No day?
949
			if (!isset($_POST['day']))
950
				fatal_lang_error('event_day_missing', false);
951
952
			// Bad day?
953
			if (!checkdate($_POST['month'], $_POST['day'], $_POST['year']))
954
				fatal_lang_error('invalid_date', false);
955
		}
956
957
		if (!isset($_POST['evtitle']) && !isset($_POST['subject']))
958
			fatal_lang_error('event_title_missing', false);
959
		elseif (!isset($_POST['evtitle']))
960
			$_POST['evtitle'] = $_POST['subject'];
961
962
		// No title?
963
		if ($smcFunc['htmltrim']($_POST['evtitle']) === '')
964
			fatal_lang_error('no_event_title', false);
965 View Code Duplication
		if ($smcFunc['strlen']($_POST['evtitle']) > 100)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
966
			$_POST['evtitle'] = $smcFunc['substr']($_POST['evtitle'], 0, 100);
967
		$_POST['evtitle'] = str_replace(';', '', $_POST['evtitle']);
968
	}
969
}
970
971
/**
972
 * Get the event's poster.
973
 *
974
 * @param int $event_id The ID of the event
975
 * @return int|bool The ID of the poster or false if the event was not found
976
 */
977 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...
978
{
979
	global $smcFunc;
980
981
	// A simple database query, how hard can that be?
982
	$request = $smcFunc['db_query']('', '
983
		SELECT id_member
984
		FROM {db_prefix}calendar
985
		WHERE id_event = {int:id_event}
986
		LIMIT 1',
987
		array(
988
			'id_event' => $event_id,
989
		)
990
	);
991
992
	// No results, return false.
993
	if ($smcFunc['db_num_rows'] === 0)
994
		return false;
995
996
	// Grab the results and return.
997
	list ($poster) = $smcFunc['db_fetch_row']($request);
998
	$smcFunc['db_free_result']($request);
999
	return (int) $poster;
1000
}
1001
1002
/**
1003
 * Consolidating the various INSERT statements into this function.
1004
 * Inserts the passed event information into the calendar table.
1005
 * Allows to either set a time span (in days) or an end_date.
1006
 * Does not check any permissions of any sort.
1007
 *
1008
 * @param array $eventOptions An array of event options ('title', 'span', 'start_date', 'end_date', etc.)
1009
 */
1010
function insertEvent(&$eventOptions)
1011
{
1012
	global $smcFunc, $context;
1013
1014
	// Add special chars to the title.
1015
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
1016
1017
	$eventOptions['location'] = isset($eventOptions['location']) ? $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES) : '';
1018
1019
	// Set the start and end dates and times
1020
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
1021
1022
	// If no topic and board are given, they are not linked to a topic.
1023
	$eventOptions['board'] = isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0;
1024
	$eventOptions['topic'] = isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0;
1025
1026
	$event_columns = array(
1027
		'id_board' => 'int', 'id_topic' => 'int', 'title' => 'string-60', 'id_member' => 'int',
1028
		'start_date' => 'date', 'end_date' => 'date', 'location' => 'string-255',
1029
	);
1030
	$event_parameters = array(
1031
		$eventOptions['board'], $eventOptions['topic'], $eventOptions['title'], $eventOptions['member'],
1032
		$start_date, $end_date, $eventOptions['location'],
1033
	);
1034 View Code Duplication
	if (!empty($start_time) && !empty($end_time) && !empty($tz) && in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1035
	{
1036
		$event_columns['start_time'] = 'time';
1037
		$event_parameters[] = $start_time;
1038
		$event_columns['end_time'] = 'time';
1039
		$event_parameters[] = $end_time;
1040
		$event_columns['timezone'] = 'string';
1041
		$event_parameters[] = $tz;
1042
	}
1043
1044
	call_integration_hook('integrate_create_event', array(&$eventOptions, &$event_columns, &$event_parameters));
1045
1046
	// Insert the event!
1047
	$eventOptions['id'] = $smcFunc['db_insert']('',
1048
		'{db_prefix}calendar',
1049
		$event_columns,
1050
		$event_parameters,
1051
		array('id_event'),
1052
		1
1053
	);
1054
1055
	// If this isn't tied to a topic, we need to notify people about it.
1056
	if (empty($eventOptions['topic']))
1057
	{
1058
		$smcFunc['db_insert']('insert',
1059
			'{db_prefix}background_tasks',
1060
			array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
1061
			array('$sourcedir/tasks/EventNew-Notify.php', 'EventNew_Notify_Background', json_encode(array(
1062
				'event_title' => $eventOptions['title'],
1063
				'event_id' => $eventOptions['id'],
1064
				'sender_id' => $eventOptions['member'],
1065
				'sender_name' => $eventOptions['member'] == $context['user']['id'] ? $context['user']['name'] : '',
1066
				'time' => time(),
1067
			)), 0),
1068
			array('id_task')
1069
		);
1070
	}
1071
1072
	// Update the settings to show something calendar-ish was updated.
1073
	updateSettings(array(
1074
		'calendar_updated' => time(),
1075
	));
1076
}
1077
1078
/**
1079
 * modifies an event.
1080
 * allows to either set a time span (in days) or an end_date.
1081
 * does not check any permissions of any sort.
1082
 *
1083
 * @param int $event_id The ID of the event
1084
 * @param array $eventOptions An array of event information
1085
 */
1086
function modifyEvent($event_id, &$eventOptions)
1087
{
1088
	global $smcFunc;
1089
1090
	// Properly sanitize the title and location
1091
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
1092
	$eventOptions['location'] = $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES);
1093
1094
	// Set the new start and end dates and times
1095
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
1096
1097
	$event_columns = array(
1098
		'start_date' => '{date:start_date}',
1099
		'end_date' => '{date:end_date}',
1100
		'title' => 'SUBSTRING({string:title}, 1, 60)',
1101
		'id_board' => '{int:id_board}',
1102
		'id_topic' => '{int:id_topic}',
1103
		'location' => 'SUBSTRING({string:location}, 1, 255)',
1104
	);
1105
	$event_parameters = array(
1106
		'start_date' => $start_date,
1107
		'end_date' => $end_date,
1108
		'title' => $eventOptions['title'],
1109
		'location' => $eventOptions['location'],
1110
		'id_board' => isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0,
1111
		'id_topic' => isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0,
1112
	);
1113 View Code Duplication
	if (!empty($start_time) && !empty($end_time) && !empty($tz) && in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1114
	{
1115
		$event_columns['start_time'] = '{time:start_time}';
1116
		$event_parameters['start_time'] = $start_time;
1117
		$event_columns['end_time'] = '{time:end_time}';
1118
		$event_parameters['end_time'] = $end_time;
1119
		$event_columns['timezone'] = '{string:timezone}';
1120
		$event_parameters['timezone'] = $tz;
1121
	}
1122
1123
	// This is to prevent hooks to modify the id of the event
1124
	$real_event_id = $event_id;
1125
	call_integration_hook('integrate_modify_event', array($event_id, &$eventOptions, &$event_columns, &$event_parameters));
1126
1127
	$column_clauses = array();
1128
	foreach ($event_columns as $col => $crit)
1129
		$column_clauses[] = $col . ' = ' . $crit;
1130
1131
	$smcFunc['db_query']('', '
1132
		UPDATE {db_prefix}calendar
1133
		SET
1134
			' . implode(', ', $column_clauses) . '
1135
		WHERE id_event = {int:id_event}',
1136
		array_merge(
1137
			$event_parameters,
1138
			array(
1139
				'id_event' => $real_event_id
1140
			)
1141
		)
1142
	);
1143
1144
	if (empty($start_time) || empty($end_time) || empty($tz) || !in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1145
	{
1146
		$smcFunc['db_query']('', '
1147
			UPDATE {db_prefix}calendar
1148
			SET start_time = NULL, end_time = NULL, timezone = NULL
1149
			WHERE id_event = {int:id_event}',
1150
			array(
1151
				'id_event' => $real_event_id
1152
			)
1153
		);
1154
	}
1155
1156
	updateSettings(array(
1157
		'calendar_updated' => time(),
1158
	));
1159
}
1160
1161
/**
1162
 * Remove an event
1163
 * removes an event.
1164
 * does no permission checks.
1165
 *
1166
 * @param int $event_id The ID of the event to remove
1167
 */
1168
function removeEvent($event_id)
1169
{
1170
	global $smcFunc;
1171
1172
	$smcFunc['db_query']('', '
1173
		DELETE FROM {db_prefix}calendar
1174
		WHERE id_event = {int:id_event}',
1175
		array(
1176
			'id_event' => $event_id,
1177
		)
1178
	);
1179
1180
	call_integration_hook('integrate_remove_event', array($event_id));
1181
1182
	updateSettings(array(
1183
		'calendar_updated' => time(),
1184
	));
1185
}
1186
1187
/**
1188
 * Gets all the events properties
1189
 *
1190
 * @param int $event_id The ID of the event
1191
 * @return array An array of event information
1192
 */
1193
function getEventProperties($event_id)
1194
{
1195
	global $smcFunc;
1196
1197
	$request = $smcFunc['db_query']('', '
1198
		SELECT
1199
			c.id_event, c.id_board, c.id_topic, c.id_member, c.title,
1200
			c.start_date, c.end_date, c.start_time, c.end_time, c.timezone, c.location,
1201
			t.id_first_msg, t.id_member_started,
1202
			mb.real_name, m.modified_time
1203
		FROM {db_prefix}calendar AS c
1204
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = c.id_topic)
1205
			LEFT JOIN {db_prefix}members AS mb ON (mb.id_member = t.id_member_started)
1206
			LEFT JOIN {db_prefix}messages AS m ON (m.id_msg  = t.id_first_msg)
1207
		WHERE c.id_event = {int:id_event}',
1208
		array(
1209
			'id_event' => $event_id,
1210
		)
1211
	);
1212
1213
	// If nothing returned, we are in poo, poo.
1214
	if ($smcFunc['db_num_rows']($request) === 0)
1215
		return false;
1216
1217
	$row = $smcFunc['db_fetch_assoc']($request);
1218
	$smcFunc['db_free_result']($request);
1219
1220
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1221
1222
	// Sanity check
1223 View Code Duplication
	if (!empty($start['error_count']) || !empty($start['warning_count']) || !empty($end['error_count']) || !empty($end['warning_count']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1224
		return false;
1225
1226
	$return_value = array(
1227
		'boards' => array(),
1228
		'board' => $row['id_board'],
1229
		'new' => 0,
1230
		'eventid' => $event_id,
1231
		'year' => $start['year'],
1232
		'month' => $start['month'],
1233
		'day' => $start['day'],
1234
		'hour' => !$allday ? $start['hour'] : null,
1235
		'minute' => !$allday ? $start['minute'] : null,
1236
		'second' => !$allday ? $start['second'] : null,
1237
		'start_date' => $row['start_date'],
1238
		'start_date_local' => $start['date_local'],
1239
		'start_date_orig' => $start['date_orig'],
1240
		'start_time' => !$allday ? $row['start_time'] : null,
1241
		'start_time_local' => !$allday ? $start['time_local'] : null,
1242
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1243
		'start_timestamp' => $start['timestamp'],
1244
		'start_datetime' => $start['datetime'],
1245
		'start_iso_gmdate' => $start['iso_gmdate'],
1246
		'end_year' => $end['year'],
1247
		'end_month' => $end['month'],
1248
		'end_day' => $end['day'],
1249
		'end_hour' => !$allday ? $end['hour'] : null,
1250
		'end_minute' => !$allday ? $end['minute'] : null,
1251
		'end_second' => !$allday ? $end['second'] : null,
1252
		'end_date' => $row['end_date'],
1253
		'end_date_local' => $end['date_local'],
1254
		'end_date_orig' => $end['date_orig'],
1255
		'end_time' => !$allday ? $row['end_time'] : null,
1256
		'end_time_local' => !$allday ? $end['time_local'] : null,
1257
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1258
		'end_timestamp' => $end['timestamp'],
1259
		'end_datetime' => $end['datetime'],
1260
		'end_iso_gmdate' => $end['iso_gmdate'],
1261
		'allday' => $allday,
1262
		'tz' => !$allday ? $tz : null,
1263
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1264
		'span' => $span,
1265
		'title' => $row['title'],
1266
		'location' => $row['location'],
1267
		'member' => $row['id_member'],
1268
		'realname' => $row['real_name'],
1269
		'sequence' => $row['modified_time'],
1270
		'topic' => array(
1271
			'id' => $row['id_topic'],
1272
			'member_started' => $row['id_member_started'],
1273
			'first_msg' => $row['id_first_msg'],
1274
		),
1275
	);
1276
1277
	$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']));
1278
1279
	return $return_value;
1280
}
1281
1282
/**
1283
 * Gets an initial set of date and time values for creating a new event.
1284
 *
1285
 * @return array An array containing an initial set of date and time values for an event.
1286
 */
1287
function getNewEventDatetimes()
1288
{
1289
	// Ensure setEventStartEnd() has something to work with
1290
	$now = date_create();
1291
	$_POST['year'] = !empty($_POST['year']) ? $_POST['year'] : date_format($now, 'Y');
1292
	$_POST['month'] = !empty($_POST['month']) ? $_POST['month'] : date_format($now, 'm');
1293
	$_POST['day'] = !empty($_POST['day']) ? $_POST['day'] : date_format($now, 'd');
1294
	$_POST['hour'] = !empty($_POST['hour']) ? $_POST['hour'] : date_format($now, 'H');
1295
	$_POST['minute'] = !empty($_POST['minute']) ? $_POST['minute'] : date_format($now, 'i');
1296
	$_POST['second'] = !empty($_POST['second']) ? $_POST['second'] : date_format($now, 's');
1297
1298
	// Set the basic values for the new event
1299
	$row_keys = array('start_date', 'end_date', 'start_time', 'end_time', 'timezone');
1300
	$row = array_combine($row_keys, setEventStartEnd());
1301
1302
	// And now set the full suite of values
1303
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1304
1305
	// Default theme only uses some of this info, but others might want it all
1306
	$eventProperties = array(
1307
		'year' => $start['year'],
1308
		'month' => $start['month'],
1309
		'day' => $start['day'],
1310
		'hour' => !$allday ? $start['hour'] : null,
1311
		'minute' => !$allday ? $start['minute'] : null,
1312
		'second' => !$allday ? $start['second'] : null,
1313
		'start_date' => $row['start_date'],
1314
		'start_date_local' => $start['date_local'],
1315
		'start_date_orig' => $start['date_orig'],
1316
		'start_time' => !$allday ? $row['start_time'] : null,
1317
		'start_time_local' => !$allday ? $start['time_local'] : null,
1318
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1319
		'start_timestamp' => $start['timestamp'],
1320
		'start_datetime' => $start['datetime'],
1321
		'start_iso_gmdate' => $start['iso_gmdate'],
1322
		'end_year' => $end['year'],
1323
		'end_month' => $end['month'],
1324
		'end_day' => $end['day'],
1325
		'end_hour' => !$allday ? $end['hour'] : null,
1326
		'end_minute' => !$allday ? $end['minute'] : null,
1327
		'end_second' => !$allday ? $end['second'] : null,
1328
		'end_date' => $row['end_date'],
1329
		'end_date_local' => $end['date_local'],
1330
		'end_date_orig' => $end['date_orig'],
1331
		'end_time' => !$allday ? $row['end_time'] : null,
1332
		'end_time_local' => !$allday ? $end['time_local'] : null,
1333
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1334
		'end_timestamp' => $end['timestamp'],
1335
		'end_datetime' => $end['datetime'],
1336
		'end_iso_gmdate' => $end['iso_gmdate'],
1337
		'allday' => $allday,
1338
		'tz' => !$allday ? $tz : null,
1339
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1340
		'span' => $span,
1341
	);
1342
1343
	return $eventProperties;
1344
}
1345
1346
/**
1347
 * Set the start and end dates and times for a posted event for insertion into the database.
1348
 * Validates all date and times given to it.
1349
 * Makes sure events do not exceed the maximum allowed duration (if any).
1350
 * If passed an array that defines any time or date parameters, they will be used. Otherwise, gets the values from $_POST.
1351
 *
1352
 * @param array $eventOptions An array of optional time and date parameters (span, start_year, end_month, etc., etc.)
1353
 * @return array An array containing $start_date, $end_date, $start_time, $end_time
1354
 */
1355
function setEventStartEnd($eventOptions = array())
1356
{
1357
	global $modSettings, $user_info;
1358
1359
	// Set $span, in case we need it
1360
	$span = isset($eventOptions['span']) ? $eventOptions['span'] : (isset($_POST['span']) ? $_POST['span'] : 0);
1361
	if ($span > 0)
1362
		$span = !empty($modSettings['cal_maxspan']) ? min($modSettings['cal_maxspan'], $span - 1) : $span - 1;
1363
1364
	// Define the timezone for this event, falling back to the default if not provided
1365
	if (!empty($eventOptions['tz']) && in_array($eventOptions['tz'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1366
		$tz = $eventOptions['tz'];
1367 View Code Duplication
	elseif (!empty($_POST['tz']) && in_array($_POST['tz'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1368
		$tz = $_POST['tz'];
1369
	else
1370
		$tz = getUserTimezone();
1371
1372
	// Is this supposed to be an all day event, or should it have specific start and end times?
1373
	if (isset($eventOptions['allday']))
1374
		$allday = $eventOptions['allday'];
1375
	elseif (empty($_POST['allday']))
1376
		$allday = false;
1377
	else
1378
		$allday = true;
1379
1380
	// Input might come as individual parameters...
1381
	$start_year = isset($eventOptions['year']) ? $eventOptions['year'] : (isset($_POST['year']) ? $_POST['year'] : null);
1382
	$start_month = isset($eventOptions['month']) ? $eventOptions['month'] : (isset($_POST['month']) ? $_POST['month'] : null);
1383
	$start_day = isset($eventOptions['day']) ? $eventOptions['day'] : (isset($_POST['day']) ? $_POST['day'] : null);
1384
	$start_hour = isset($eventOptions['hour']) ? $eventOptions['hour'] : (isset($_POST['hour']) ? $_POST['hour'] : null);
1385
	$start_minute = isset($eventOptions['minute']) ? $eventOptions['minute'] : (isset($_POST['minute']) ? $_POST['minute'] : null);
1386
	$start_second = isset($eventOptions['second']) ? $eventOptions['second'] : (isset($_POST['second']) ? $_POST['second'] : null);
1387
	$end_year = isset($eventOptions['end_year']) ? $eventOptions['end_year'] : (isset($_POST['end_year']) ? $_POST['end_year'] : null);
1388
	$end_month = isset($eventOptions['end_month']) ? $eventOptions['end_month'] : (isset($_POST['end_month']) ? $_POST['end_month'] : null);
1389
	$end_day = isset($eventOptions['end_day']) ? $eventOptions['end_day'] : (isset($_POST['end_day']) ? $_POST['end_day'] : null);
1390
	$end_hour = isset($eventOptions['end_hour']) ? $eventOptions['end_hour'] : (isset($_POST['end_hour']) ? $_POST['end_hour'] : null);
1391
	$end_minute = isset($eventOptions['end_minute']) ? $eventOptions['end_minute'] : (isset($_POST['end_minute']) ? $_POST['end_minute'] : null);
1392
	$end_second = isset($eventOptions['end_second']) ? $eventOptions['end_second'] : (isset($_POST['end_second']) ? $_POST['end_second'] : null);
1393
1394
	// ... or as datetime strings ...
1395
	$start_string = isset($eventOptions['start_datetime']) ? $eventOptions['start_datetime'] : (isset($_POST['start_datetime']) ? $_POST['start_datetime'] : null);
1396
	$end_string = isset($eventOptions['end_datetime']) ? $eventOptions['end_datetime'] : (isset($_POST['end_datetime']) ? $_POST['end_datetime'] : null);
1397
1398
	// ... or as date strings and time strings.
1399
	$start_date_string = isset($eventOptions['start_date']) ? $eventOptions['start_date'] : (isset($_POST['start_date']) ? $_POST['start_date'] : null);
1400
	$start_time_string = isset($eventOptions['start_time']) ? $eventOptions['start_time'] : (isset($_POST['start_time']) ? $_POST['start_time'] : null);
1401
	$end_date_string = isset($eventOptions['end_date']) ? $eventOptions['end_date'] : (isset($_POST['end_date']) ? $_POST['end_date'] : null);
1402
	$end_time_string = isset($eventOptions['end_time']) ? $eventOptions['end_time'] : (isset($_POST['end_time']) ? $_POST['end_time'] : null);
1403
1404
	// If the date and time were given in separate strings, combine them
1405
	if (empty($start_string) && isset($start_date_string))
1406
		$start_string = $start_date_string . (isset($start_time_string) ? ' ' . $start_time_string : '');
1407
	if (empty($end_string) && isset($end_date_string))
1408
		$end_string = $end_date_string . (isset($end_time_string) ? ' ' . $end_time_string : '');
1409
1410
	// If some form of string input was given, override individually defined options with it
1411 View Code Duplication
	if (isset($start_string))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1412
	{
1413
		$start_string_parsed = date_parse($start_string);
1414
		if (empty($start_string_parsed['error_count']) && empty($start_string_parsed['warning_count']))
1415
		{
1416
			if ($start_string_parsed['year'] != false)
1417
			{
1418
				$start_year = $start_string_parsed['year'];
1419
				$start_month = $start_string_parsed['month'];
1420
				$start_day = $start_string_parsed['day'];
1421
			}
1422
			if ($start_string_parsed['hour'] != false)
1423
			{
1424
				$start_hour = $start_string_parsed['hour'];
1425
				$start_minute = $start_string_parsed['minute'];
1426
				$start_second = $start_string_parsed['second'];
1427
			}
1428
		}
1429
	}
1430 View Code Duplication
	if (isset($end_string))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1431
	{
1432
		$end_string_parsed = date_parse($end_string);
1433
		if (empty($end_string_parsed['error_count']) && empty($end_string_parsed['warning_count']))
1434
		{
1435
			if ($end_string_parsed['year'] != false)
1436
			{
1437
				$end_year = $end_string_parsed['year'];
1438
				$end_month = $end_string_parsed['month'];
1439
				$end_day = $end_string_parsed['day'];
1440
			}
1441
			if ($end_string_parsed['hour'] != false)
1442
			{
1443
				$end_hour = $end_string_parsed['hour'];
1444
				$end_minute = $end_string_parsed['minute'];
1445
				$end_second = $end_string_parsed['second'];
1446
			}
1447
		}
1448
	}
1449
1450
	// Validate input
1451
	$start_date_isvalid = checkdate($start_month, $start_day, $start_year);
1452
	$end_date_isvalid = checkdate($end_month, $end_day, $end_year);
1453
1454
	$start_time_isset = (isset($start_hour) && isset($start_minute) && isset($start_second));
1455
	$d = date_parse(sprintf('%02d:%02d:%02d', $start_hour, $start_minute, $start_second));
1456
	$start_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1457
1458
	$end_time_isset = (isset($end_hour) && isset($end_minute) && isset($end_second));
1459
	$d = date_parse(sprintf('%02d:%02d:%02d', $end_hour, $end_minute, $end_second));
1460
	$end_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1461
1462
	// Uh-oh...
1463
	if ($start_date_isvalid === false)
1464
	{
1465
		fatal_lang_error('invalid_date', false);
1466
	}
1467
1468
	// Make sure we use valid values for everything
1469
	if ($end_date_isvalid === false)
1470
	{
1471
		$end_year = $start_year;
1472
		$end_month = $start_month;
1473
		$end_day = $start_day;
1474
	}
1475
1476
	if ($allday === true || $start_time_isset === false || $start_time_isvalid === false)
1477
	{
1478
		$allday = true;
1479
		$start_hour = 0;
1480
		$start_minute = 0;
1481
		$start_second = 0;
1482
	}
1483
1484
	if ($allday === true || $end_time_isvalid === false || $end_time_isset === false)
1485
	{
1486
		$end_hour = $start_hour;
1487
		$end_minute = $start_minute;
1488
		$end_second = $start_second;
1489
	}
1490
1491
	// Now create our datetime objects
1492
	$start_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1493
	$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $end_year, $end_month, $end_day, $end_hour, $end_minute, $end_second) . ' ' . $tz);
1494
1495
	// Is $end_object too early?
1496
	if ($start_object >= $end_object)
1497
	{
1498
		$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1499
		if ($span > 0)
1500
			date_add($end_object, date_interval_create_from_date_string($span . ' days'));
1501
		else
1502
			date_add($end_object, date_interval_create_from_date_string('1 hour'));
1503
	}
1504
1505
	// Is $end_object too late?
1506
	if (!empty($modSettings['cal_maxspan']))
1507
	{
1508
		$date_diff = date_diff($start_object, $end_object);
1509
		if ($date_diff->days > $modSettings['cal_maxspan'])
1510
		{
1511
			if ($modSettings['cal_maxspan'] > 1)
1512
			{
1513
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1514
				date_add($end_object, date_interval_create_from_date_string($modSettings['cal_maxspan'] . ' days'));
1515
			}
1516
			else
1517
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, '11', '59', '59') . ' ' . $tz);
1518
		}
1519
	}
1520
1521
	// Finally, make our strings
1522
	$start_date = date_format($start_object, 'Y-m-d');
1523
	$end_date = date_format($end_object, 'Y-m-d');
1524
1525
	if ($allday == true)
1526
	{
1527
		$start_time = null;
1528
		$end_time = null;
1529
		$tz = null;
1530
	}
1531
	else
1532
	{
1533
		$start_time = date_format($start_object, 'H:i:s');
1534
		$end_time = date_format($end_object, 'H:i:s');
1535
	}
1536
1537
	return array($start_date, $end_date, $start_time, $end_time, $tz);
1538
}
1539
1540
/**
1541
 * Helper function for getEventRange, getEventProperties, getNewEventDatetimes, etc.
1542
 *
1543
 * @param array $row A database row representing an event from the calendar table
1544
 * @return array An array containing the start and end date and time properties for the event
1545
 */
1546
function buildEventDatetimes($row)
1547
{
1548
	global $sourcedir, $user_info;
1549
	require_once($sourcedir . '/Subs.php');
1550
1551
	// First, try to create a better date format, ignoring the "time" elements.
1552
	if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
1553
		$date_format = '%F';
1554
	else
1555
		$date_format = $matches[0];
1556
1557
	// We want a fairly compact version of the time, but as close as possible to the user's settings.
1558 View Code Duplication
	if (preg_match('~%[HkIlMpPrRSTX](?:[^%]*%[HkIlMpPrRSTX])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1559
		$time_format = '%k:%M';
1560
	else
1561
		$time_format = str_replace(array('%I', '%H', '%S', '%r', '%R', '%T'), array('%l', '%k', '', '%l:%M %p', '%k:%M', '%l:%M'), $matches[0]);
1562
1563
	// Should this be an all day event?
1564
	$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;
1565
1566
	// How many days does this event span?
1567
	$span = 1 + date_interval_format(date_diff(date_create($row['start_date']), date_create($row['end_date'])), '%d');
1568
1569
	// We need to have a defined timezone in the steps below
1570
	if (empty($row['timezone']))
1571
		$row['timezone'] = getUserTimezone();
1572
1573
	// Get most of the standard date information for the start and end datetimes
1574
	$start = date_parse($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''));
1575
	$end = date_parse($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''));
1576
1577
	// But we also want more info, so make some DateTime objects we can use
1578
	$start_object = date_create($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''), timezone_open($row['timezone']));
1579
	$end_object = date_create($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''), timezone_open($row['timezone']));
1580
1581
	// Unix timestamps are good
1582
	$start['timestamp'] = date_format($start_object, 'U');
1583
	$end['timestamp'] = date_format($end_object, 'U');
1584
1585
	// Datetime string without timezone  (e.g. '2016-12-28 22:45:30')
1586
	$start['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1587
	$end['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1588
1589
	// ISO formatted datetime string, relative to UTC (e.g. '2016-12-29T05:45:30+00:00')
1590
	$start['iso_gmdate'] = gmdate('c', $start['timestamp']);
1591
	$end['iso_gmdate'] = gmdate('c', $end['timestamp']);
1592
1593
	// Strings showing the datetimes in the user's preferred format, relative to the user's time zone
1594
	$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...
1595
	$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...
1596
	$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...
1597
	$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...
1598
1599
	// Strings showing the datetimes in the user's preferred format, relative to the event's time zone
1600
	$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...
1601
	$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...
1602
	$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...
1603
	$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...
1604
1605
	// The time zone identifier (e.g. 'Europe/London') and abbreviation (e.g. 'GMT')
1606
	$tz = date_format($start_object, 'e');
1607
	$tz_abbrev = date_format($start_object, 'T');
1608
1609
	// There are a handful of time zones that PHP doesn't know the abbreviation for. Fix 'em if we can.
1610
	if (strspn($tz_abbrev, '+-') > 0)
1611
	{
1612
		$tz_location = timezone_location_get(timezone_open($row['timezone']));
1613
1614
		// Kazakstan
1615
		if ($tz_location['country_code'] == 'KZ')
1616
			$tz_abbrev = str_replace(array('+05', '+06'), array('AQTT', 'ALMT'), $tz_abbrev);
1617
1618
		// Russia likes to experiment with time zones
1619
		if ($tz_location['country_code'] == 'RU')
1620
		{
1621
			$msk_offset = intval($tz_abbrev) - 3;
1622
			$msk_offset = !empty($msk_offset) ? sprintf('%+0d', $msk_offset) : '';
1623
			$tz_abbrev = 'MSK' . $msk_offset;
1624
		}
1625
1626
		// Still no good? We'll just mark it as a UTC offset
1627
		if (strspn($tz_abbrev, '+-') > 0)
1628
			$tz_abbrev = 'UTC' . $tz_abbrev;
1629
	}
1630
1631
	return array($start, $end, $allday, $span, $tz, $tz_abbrev);
1632
}
1633
1634
/**
1635
 * Gets a member's selected timezone identifier directly from the database
1636
 *
1637
 * @param int $id_member The member id to look up. If not provided, the current user's id will be used.
1638
 * @return string The timezone identifier string for the user's timezone.
1639
 */
1640
function getUserTimezone($id_member = null)
1641
{
1642
	global $smcFunc, $context, $sourcedir, $user_info, $modSettings;
1643
	static $member_cache = array();
1644
1645
	if (is_null($id_member) && $user_info['is_guest'] == false)
1646
		$id_member = $context['user']['id'];
1647
	
1648
	//check if the cache got the data
1649
	if (isset($id_member) && isset($member_cache[$id_member]))
1650
	{
1651
		return $member_cache[$id_member];
1652
	}
1653
1654 View Code Duplication
	if (isset($id_member))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1655
	{
1656
		$request = $smcFunc['db_query']('', '
1657
			SELECT timezone
1658
			FROM {db_prefix}members
1659
			WHERE id_member = {int:id_member}',
1660
			array(
1661
				'id_member' => $id_member,
1662
			)
1663
		);
1664
		list($timezone) = $smcFunc['db_fetch_row']($request);
1665
		$smcFunc['db_free_result']($request);
1666
	}
1667
1668 View Code Duplication
	if (empty($timezone) || !in_array($timezone, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1669
		$timezone = isset($modSettings['default_timezone']) ? $modSettings['default_timezone'] : date_default_timezone_get();
1670
1671
	if (isset($id_member))
1672
		$member_cache[$id_member] = $timezone;
1673
	
1674
	return $timezone;
1675
}
1676
1677
/**
1678
 * Gets all of the holidays for the listing
1679
 *
1680
 * @param int $start The item to start with (for pagination purposes)
1681
 * @param int $items_per_page How many items to show on each page
1682
 * @param string $sort A string indicating how to sort the results
1683
 * @return array An array of holidays, each of which is an array containing the id, year, month, day and title of the holiday
1684
 */
1685 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...
1686
{
1687
	global $smcFunc;
1688
1689
	$request = $smcFunc['db_query']('', '
1690
		SELECT id_holiday, YEAR(event_date) AS year, MONTH(event_date) AS month, DAYOFMONTH(event_date) AS day, title
1691
		FROM {db_prefix}calendar_holidays
1692
		ORDER BY {raw:sort}
1693
		LIMIT {int:start}, {int:max}',
1694
		array(
1695
			'sort' => $sort,
1696
			'start' => $start,
1697
			'max' => $items_per_page,
1698
		)
1699
	);
1700
	$holidays = array();
1701
	while ($row = $smcFunc['db_fetch_assoc']($request))
1702
		$holidays[] = $row;
1703
	$smcFunc['db_free_result']($request);
1704
1705
	return $holidays;
1706
}
1707
1708
/**
1709
 * Helper function to get the total number of holidays
1710
 *
1711
 * @return int The total number of holidays
1712
 */
1713
function list_getNumHolidays()
1714
{
1715
	global $smcFunc;
1716
1717
	$request = $smcFunc['db_query']('', '
1718
		SELECT COUNT(*)
1719
		FROM {db_prefix}calendar_holidays',
1720
		array(
1721
		)
1722
	);
1723
	list($num_items) = $smcFunc['db_fetch_row']($request);
1724
	$smcFunc['db_free_result']($request);
1725
1726
	return (int) $num_items;
1727
}
1728
1729
/**
1730
 * Remove a holiday from the calendar
1731
 *
1732
 * @param array $holiday_ids An array of IDs of holidays to delete
1733
 */
1734
function removeHolidays($holiday_ids)
1735
{
1736
	global $smcFunc;
1737
1738
	$smcFunc['db_query']('', '
1739
		DELETE FROM {db_prefix}calendar_holidays
1740
		WHERE id_holiday IN ({array_int:id_holiday})',
1741
		array(
1742
			'id_holiday' => $holiday_ids,
1743
		)
1744
	);
1745
1746
	updateSettings(array(
1747
		'calendar_updated' => time(),
1748
	));
1749
}
1750
1751
?>
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...