Completed
Push — release-2.1 ( 19063e...99ca30 )
by John
06:48
created

Subs-Calendar.php ➔ buildEventDatetimes()   F

Complexity

Conditions 19
Paths 832

Size

Total Lines 87
Code Lines 46

Duplication

Lines 4
Ratio 4.6 %

Importance

Changes 0
Metric Value
cc 19
eloc 46
nc 832
nop 1
dl 4
loc 87
rs 2.3386
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
551
		$first_day_of_next_year = (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year + 1));
0 ignored issues
show
Unused Code introduced by
$first_day_of_next_year is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
552
		$last_day_of_last_year = (int) strftime('%w', mktime(0, 0, 0, 12, 31, $year - 1));
0 ignored issues
show
Unused Code introduced by
$last_day_of_last_year is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
553
554
		$timestamp = mktime(0, 0, 0, $month, $day, $year);
555
		$calendarGrid['week_title'] = sprintf($txt['calendar_week_beginning'], date('F', $timestamp), date('j', $timestamp), date('Y', $timestamp));
556
	}
557
558
	// This holds all the main data - there is at least one month!
559
	$calendarGrid['months'] = array();
560
	$lastDay = 99;
561
	$curDay = $day;
562
	$curDayOfWeek = $calendarOptions['start_day'];
563
	for ($i = 0; $i < 7; $i++)
564
	{
565
		// Have we gone into a new month (Always happens first cycle too)
566
		if ($lastDay > $curDay)
567
		{
568
			$curMonth = $lastDay == 99 ? $month : ($month == 12 ? 1 : $month + 1);
569
			$curYear = $lastDay == 99 ? $year : ($curMonth == 1 && $month == 12 ? $year + 1 : $year);
570
			$calendarGrid['months'][$curMonth] = array(
571
				'current_month' => $curMonth,
572
				'current_year' => $curYear,
573
				'days' => array(),
574
			);
575
		}
576
577
		// Add todays information to the pile!
578
		$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...
579
580
		$calendarGrid['months'][$curMonth]['days'][$curDay] = array(
581
			'day' => $curDay,
582
			'day_of_week' => $curDayOfWeek,
583
			'date' => $date,
584
			'is_today' => $date == $today['date'],
585
			'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
586
			'events' => !empty($events[$date]) ? $events[$date] : array(),
587
			'birthdays' => !empty($bday[$date]) ? $bday[$date] : array()
588
		);
589
590
		// Make the last day what the current day is and work out what the next day is.
591
		$lastDay = $curDay;
592
		$curTimestamp += 86400;
593
		$curDay = (int) strftime('%d', $curTimestamp);
594
595
		// Also increment the current day of the week.
596
		$curDayOfWeek = $curDayOfWeek >= 6 ? 0 : ++$curDayOfWeek;
597
	}
598
599
	// Set the previous and the next week's links.
600
	$calendarGrid['previous_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['previous_week']['year'] . ';month=' . $calendarGrid['previous_week']['month'] . ';day=' . $calendarGrid['previous_week']['day'];
601
	$calendarGrid['next_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['next_week']['year'] . ';month=' . $calendarGrid['next_week']['month'] . ';day=' . $calendarGrid['next_week']['day'];
602
603
	return $calendarGrid;
604
}
605
606
/**
607
 * Retrieve all events for the given days, independently of the users offset.
608
 * cache callback function used to retrieve the birthdays, holidays, and events between now and now + days_to_index.
609
 * widens the search range by an extra 24 hours to support time offset shifts.
610
 * used by the cache_getRecentEvents function to get the information needed to calculate the events taking the users time offset into account.
611
 *
612
 * @param int $days_to_index How many days' worth of info to index
613
 * @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
614
 */
615
function cache_getOffsetIndependentEvents($days_to_index)
616
{
617
	$low_date = strftime('%Y-%m-%d', forum_time(false) - 24 * 3600);
618
	$high_date = strftime('%Y-%m-%d', forum_time(false) + $days_to_index * 24 * 3600);
619
620
	return array(
621
		'data' => array(
622
			'holidays' => getHolidayRange($low_date, $high_date),
623
			'birthdays' => getBirthdayRange($low_date, $high_date),
624
			'events' => getEventRange($low_date, $high_date, false),
625
		),
626
		'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\']);',
627
		'expires' => time() + 3600,
628
	);
629
}
630
631
/**
632
 * cache callback function used to retrieve the upcoming birthdays, holidays, and events within the given period, taking into account the users time offset.
633
 * Called from the BoardIndex to display the current day's events on the board index
634
 * used by the board index and SSI to show the upcoming events.
635
 * @param array $eventOptions An array of event options. Only 'num_days_shown' is used here
636
 * @return array An array containing the info that was cached as well as a few other relevant things
637
 */
638
function cache_getRecentEvents($eventOptions)
639
{
640
	// With the 'static' cached data we can calculate the user-specific data.
641
	$cached_data = cache_quick_get('calendar_index', 'Subs-Calendar.php', 'cache_getOffsetIndependentEvents', array($eventOptions['num_days_shown']));
642
643
	// Get the information about today (from user perspective).
644
	$today = getTodayInfo();
645
646
	$return_data = array(
647
		'calendar_holidays' => array(),
648
		'calendar_birthdays' => array(),
649
		'calendar_events' => array(),
650
	);
651
652
	// Set the event span to be shown in seconds.
653
	$days_for_index = $eventOptions['num_days_shown'] * 86400;
654
655
	// Get the current member time/date.
656
	$now = forum_time();
657
658
	// Holidays between now and now + days.
659
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
660
	{
661
		if (isset($cached_data['holidays'][strftime('%Y-%m-%d', $i)]))
662
			$return_data['calendar_holidays'] = array_merge($return_data['calendar_holidays'], $cached_data['holidays'][strftime('%Y-%m-%d', $i)]);
663
	}
664
665
	// Happy Birthday, guys and gals!
666
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
667
	{
668
		$loop_date = strftime('%Y-%m-%d', $i);
669
		if (isset($cached_data['birthdays'][$loop_date]))
670
		{
671
			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...
672
				$cached_data['birthdays'][strftime('%Y-%m-%d', $i)][$index]['is_today'] = $loop_date === $today['date'];
673
			$return_data['calendar_birthdays'] = array_merge($return_data['calendar_birthdays'], $cached_data['birthdays'][$loop_date]);
674
		}
675
	}
676
677
	$duplicates = array();
678
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
679
	{
680
		// Determine the date of the current loop step.
681
		$loop_date = strftime('%Y-%m-%d', $i);
682
683
		// No events today? Check the next day.
684
		if (empty($cached_data['events'][$loop_date]))
685
			continue;
686
687
		// Loop through all events to add a few last-minute values.
688
		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...
689
		{
690
			// Create a shortcut variable for easier access.
691
			$this_event = &$cached_data['events'][$loop_date][$ev];
692
693
			// Skip duplicates.
694
			if (isset($duplicates[$this_event['topic'] . $this_event['title']]))
695
			{
696
				unset($cached_data['events'][$loop_date][$ev]);
697
				continue;
698
			}
699
			else
700
				$duplicates[$this_event['topic'] . $this_event['title']] = true;
701
702
			// Might be set to true afterwards, depending on the permissions.
703
			$this_event['can_edit'] = false;
704
			$this_event['is_today'] = $loop_date === $today['date'];
705
			$this_event['date'] = $loop_date;
706
		}
707
708
		if (!empty($cached_data['events'][$loop_date]))
709
			$return_data['calendar_events'] = array_merge($return_data['calendar_events'], $cached_data['events'][$loop_date]);
710
	}
711
712
	// Mark the last item so that a list separator can be used in the template.
713 View Code Duplication
	for ($i = 0, $n = count($return_data['calendar_birthdays']); $i < $n; $i++)
1 ignored issue
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...
714
		$return_data['calendar_birthdays'][$i]['is_last'] = !isset($return_data['calendar_birthdays'][$i + 1]);
715 View Code Duplication
	for ($i = 0, $n = count($return_data['calendar_events']); $i < $n; $i++)
1 ignored issue
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...
716
		$return_data['calendar_events'][$i]['is_last'] = !isset($return_data['calendar_events'][$i + 1]);
717
718
	return array(
719
		'data' => $return_data,
720
		'expires' => time() + 3600,
721
		'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\']);',
722
		'post_retri_eval' => '
723
			global $context, $scripturl, $user_info;
724
725
			foreach ($cache_block[\'data\'][\'calendar_events\'] as $k => $event)
726
			{
727
				// Remove events that the user may not see or wants to ignore.
728
				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\']))
729
					unset($cache_block[\'data\'][\'calendar_events\'][$k]);
730
				else
731
				{
732
					// Whether the event can be edited depends on the permissions.
733
					$cache_block[\'data\'][\'calendar_events\'][$k][\'can_edit\'] = allowedTo(\'calendar_edit_any\') || ($event[\'poster\'] == $user_info[\'id\'] && allowedTo(\'calendar_edit_own\'));
734
735
					// The added session code makes this URL not cachable.
736
					$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\'];
737
				}
738
			}
739
740
			if (empty($params[0][\'include_holidays\']))
741
				$cache_block[\'data\'][\'calendar_holidays\'] = array();
742
			if (empty($params[0][\'include_birthdays\']))
743
				$cache_block[\'data\'][\'calendar_birthdays\'] = array();
744
			if (empty($params[0][\'include_events\']))
745
				$cache_block[\'data\'][\'calendar_events\'] = array();
746
747
			$cache_block[\'data\'][\'show_calendar\'] = !empty($cache_block[\'data\'][\'calendar_holidays\']) || !empty($cache_block[\'data\'][\'calendar_birthdays\']) || !empty($cache_block[\'data\'][\'calendar_events\']);',
748
	);
749
}
750
751
/**
752
 * Makes sure the calendar post is valid.
753
 */
754
function validateEventPost()
755
{
756
	global $modSettings, $smcFunc;
757
758
	if (!isset($_POST['deleteevent']))
759
	{
760
		// The 2.1 way
761
		if (isset($_POST['start_date']))
762
		{
763
			$d = date_parse($_POST['start_date']);
764 View Code Duplication
			if (!empty($d['error_count']) || !empty($d['warning_count']))
1 ignored issue
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...
765
				fatal_lang_error('invalid_date', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
770
		}
771
		elseif (isset($_POST['start_datetime']))
772
		{
773
			$d = date_parse($_POST['start_datetime']);
774 View Code Duplication
			if (!empty($d['error_count']) || !empty($d['warning_count']))
1 ignored issue
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...
775
				fatal_lang_error('invalid_date', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
780
		}
781
		// The 2.0 way
782
		else
783
		{
784
			// No month?  No year?
785
			if (!isset($_POST['month']))
786
				fatal_lang_error('event_month_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
795
		}
796
	}
797
798
	// Make sure they're allowed to post...
799
	isAllowedTo('calendar_post');
800
801
	// If they want to us to calculate an end date, make sure it will fit in an acceptable range.
802
	if (isset($_POST['span']))
803
	{
804
		if (($_POST['span'] < 1) || (!empty($modSettings['cal_maxspan']) && $_POST['span'] > $modSettings['cal_maxspan']))
805
			fatal_lang_error('invalid_days_numb', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
806
	}
807
808
	// There is no need to validate the following values if we are just deleting the event.
809
	if (!isset($_POST['deleteevent']))
810
	{
811
		// If we're doing things the 2.0 way, check the day
812
		if (empty($_POST['start_date']) && empty($_POST['start_datetime']))
813
		{
814
			// No day?
815
			if (!isset($_POST['day']))
816
				fatal_lang_error('event_day_missing', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
831 View Code Duplication
		if ($smcFunc['strlen']($_POST['evtitle']) > 100)
1 ignored issue
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...
832
			$_POST['evtitle'] = $smcFunc['substr']($_POST['evtitle'], 0, 100);
833
		$_POST['evtitle'] = str_replace(';', '', $_POST['evtitle']);
834
	}
835
}
836
837
/**
838
 * Get the event's poster.
839
 *
840
 * @param int $event_id The ID of the event
841
 * @return int|bool The ID of the poster or false if the event was not found
842
 */
843 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...
844
{
845
	global $smcFunc;
846
847
	// A simple database query, how hard can that be?
848
	$request = $smcFunc['db_query']('', '
849
		SELECT id_member
850
		FROM {db_prefix}calendar
851
		WHERE id_event = {int:id_event}
852
		LIMIT 1',
853
		array(
854
			'id_event' => $event_id,
855
		)
856
	);
857
858
	// No results, return false.
859
	if ($smcFunc['db_num_rows'] === 0)
860
		return false;
861
862
	// Grab the results and return.
863
	list ($poster) = $smcFunc['db_fetch_row']($request);
864
	$smcFunc['db_free_result']($request);
865
	return (int) $poster;
866
}
867
868
/**
869
 * Consolidating the various INSERT statements into this function.
870
 * Inserts the passed event information into the calendar table.
871
 * Allows to either set a time span (in days) or an end_date.
872
 * Does not check any permissions of any sort.
873
 *
874
 * @param array $eventOptions An array of event options ('title', 'span', 'start_date', 'end_date', etc.)
875
 */
876
function insertEvent(&$eventOptions)
877
{
878
	global $smcFunc, $context;
879
880
	// Add special chars to the title.
881
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
882
883
	$eventOptions['location'] = isset($eventOptions['location']) ? $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES) : '';
884
885
	// Set the start and end dates and times
886
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
887
888
	// If no topic and board are given, they are not linked to a topic.
889
	$eventOptions['board'] = isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0;
890
	$eventOptions['topic'] = isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0;
891
892
	$event_columns = array(
893
		'id_board' => 'int', 'id_topic' => 'int', 'title' => 'string-60', 'id_member' => 'int',
894
		'start_date' => 'date', 'end_date' => 'date', 'location' => 'string-255',
895
	);
896
	$event_parameters = array(
897
		$eventOptions['board'], $eventOptions['topic'], $eventOptions['title'], $eventOptions['member'],
898
		$start_date, $end_date, $eventOptions['location'],
899
	);
900 View Code Duplication
	if (!empty($start_time) && !empty($end_time) && !empty($tz) && in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1 ignored issue
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...
901
	{
902
		$event_columns['start_time'] = 'time';
903
		$event_parameters[] = $start_time;
904
		$event_columns['end_time'] = 'time';
905
		$event_parameters[] = $end_time;
906
		$event_columns['timezone'] = 'string';
907
		$event_parameters[] = $tz;
908
	}
909
910
	call_integration_hook('integrate_create_event', array(&$eventOptions, &$event_columns, &$event_parameters));
911
912
	// Insert the event!
913
	$smcFunc['db_insert']('',
914
		'{db_prefix}calendar',
915
		$event_columns,
916
		$event_parameters,
917
		array('id_event')
918
	);
919
920
	// Store the just inserted id_event for future reference.
921
	$eventOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}calendar', 'id_event');
922
923
	// If this isn't tied to a topic, we need to notify people about it.
924
	if (empty($eventOptions['topic']))
925
	{
926
		$smcFunc['db_insert']('insert',
927
			'{db_prefix}background_tasks',
928
			array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
929
			array('$sourcedir/tasks/EventNew-Notify.php', 'EventNew_Notify_Background', json_encode(array(
930
				'event_title' => $eventOptions['title'],
931
				'event_id' => $eventOptions['id'],
932
				'sender_id' => $eventOptions['member'],
933
				'sender_name' => $eventOptions['member'] == $context['user']['id'] ? $context['user']['name'] : '',
934
				'time' => time(),
935
			)), 0),
936
			array('id_task')
937
		);
938
	}
939
940
	// Update the settings to show something calendar-ish was updated.
941
	updateSettings(array(
942
		'calendar_updated' => time(),
943
	));
944
}
945
946
/**
947
 * modifies an event.
948
 * allows to either set a time span (in days) or an end_date.
949
 * does not check any permissions of any sort.
950
 *
951
 * @param int $event_id The ID of the event
952
 * @param array $eventOptions An array of event information
953
 */
954
function modifyEvent($event_id, &$eventOptions)
955
{
956
	global $smcFunc;
957
958
	// Properly sanitize the title and location
959
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
960
	$eventOptions['location'] = $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES);
961
962
	// Set the new start and end dates and times
963
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
964
965
	$event_columns = array(
966
		'start_date' => '{date:start_date}',
967
		'end_date' => '{date:end_date}',
968
		'title' => 'SUBSTRING({string:title}, 1, 60)',
969
		'id_board' => '{int:id_board}',
970
		'id_topic' => '{int:id_topic}',
971
		'location' => 'SUBSTRING({string:location}, 1, 255)',
972
	);
973
	$event_parameters = array(
974
		'start_date' => $start_date,
975
		'end_date' => $end_date,
976
		'title' => $eventOptions['title'],
977
		'location' => $eventOptions['location'],
978
		'id_board' => isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0,
979
		'id_topic' => isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0,
980
	);
981 View Code Duplication
	if (!empty($start_time) && !empty($end_time) && !empty($tz) && in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1 ignored issue
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...
982
	{
983
		$event_columns['start_time'] = '{time:start_time}';
984
		$event_parameters['start_time'] = $start_time;
985
		$event_columns['end_time'] = '{time:end_time}';
986
		$event_parameters['end_time'] = $end_time;
987
		$event_columns['timezone'] = '{string:timezone}';
988
		$event_parameters['timezone'] = $tz;
989
	}
990
991
	// This is to prevent hooks to modify the id of the event
992
	$real_event_id = $event_id;
993
	call_integration_hook('integrate_modify_event', array($event_id, &$eventOptions, &$event_columns, &$event_parameters));
994
995
	$column_clauses = array();
996
	foreach ($event_columns as $col => $crit)
997
		$column_clauses[] = $col . ' = ' . $crit;
998
999
	$smcFunc['db_query']('', '
1000
		UPDATE {db_prefix}calendar
1001
		SET
1002
			' . implode(', ', $column_clauses) . '
1003
		WHERE id_event = {int:id_event}',
1004
		array_merge(
1005
			$event_parameters,
1006
			array(
1007
				'id_event' => $real_event_id
1008
			)
1009
		)
1010
	);
1011
1012
	if (empty($start_time) || empty($end_time) || empty($tz) || !in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1013
	{
1014
		$smcFunc['db_query']('', '
1015
			UPDATE {db_prefix}calendar
1016
			SET start_time = NULL, end_time = NULL, timezone = NULL
1017
			WHERE id_event = {int:id_event}',
1018
			array(
1019
				'id_event' => $real_event_id
1020
			)
1021
		);
1022
	}
1023
1024
	updateSettings(array(
1025
		'calendar_updated' => time(),
1026
	));
1027
}
1028
1029
/**
1030
 * Remove an event
1031
 * removes an event.
1032
 * does no permission checks.
1033
 *
1034
 * @param int $event_id The ID of the event to remove
1035
 */
1036
function removeEvent($event_id)
1037
{
1038
	global $smcFunc;
1039
1040
	$smcFunc['db_query']('', '
1041
		DELETE FROM {db_prefix}calendar
1042
		WHERE id_event = {int:id_event}',
1043
		array(
1044
			'id_event' => $event_id,
1045
		)
1046
	);
1047
1048
	call_integration_hook('integrate_remove_event', array($event_id));
1049
1050
	updateSettings(array(
1051
		'calendar_updated' => time(),
1052
	));
1053
}
1054
1055
/**
1056
 * Gets all the events properties
1057
 *
1058
 * @param int $event_id The ID of the event
1059
 * @return array An array of event information
1060
 */
1061
function getEventProperties($event_id)
1062
{
1063
	global $smcFunc;
1064
1065
	$request = $smcFunc['db_query']('', '
1066
		SELECT
1067
			c.id_event, c.id_board, c.id_topic, c.id_member, c.title,
1068
			c.start_date, c.end_date, c.start_time, c.end_time, c.timezone, c.location,
1069
			t.id_first_msg, t.id_member_started,
1070
			mb.real_name, m.modified_time
1071
		FROM {db_prefix}calendar AS c
1072
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = c.id_topic)
1073
			LEFT JOIN {db_prefix}members AS mb ON (mb.id_member = t.id_member_started)
1074
			LEFT JOIN {db_prefix}messages AS m ON (m.id_msg  = t.id_first_msg)
1075
		WHERE c.id_event = {int:id_event}',
1076
		array(
1077
			'id_event' => $event_id,
1078
		)
1079
	);
1080
1081
	// If nothing returned, we are in poo, poo.
1082
	if ($smcFunc['db_num_rows']($request) === 0)
1083
		return false;
1084
1085
	$row = $smcFunc['db_fetch_assoc']($request);
1086
	$smcFunc['db_free_result']($request);
1087
1088
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1089
1090
	// Sanity check
1091 View Code Duplication
	if (!empty($start['error_count']) || !empty($start['warning_count']) || !empty($end['error_count']) || !empty($end['warning_count']))
1 ignored issue
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...
1092
		return false;
1093
1094
	$return_value = array(
1095
		'boards' => array(),
1096
		'board' => $row['id_board'],
1097
		'new' => 0,
1098
		'eventid' => $event_id,
1099
		'year' => $start['year'],
1100
		'month' => $start['month'],
1101
		'day' => $start['day'],
1102
		'hour' => !$allday ? $start['hour'] : null,
1103
		'minute' => !$allday ? $start['minute'] : null,
1104
		'second' => !$allday ? $start['second'] : null,
1105
		'start_date' => $row['start_date'],
1106
		'start_date_local' => $start['date_local'],
1107
		'start_date_orig' => $start['date_orig'],
1108
		'start_time' => !$allday ? $row['start_time'] : null,
1109
		'start_time_local' => !$allday ? $start['time_local'] : null,
1110
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1111
		'start_timestamp' => $start['timestamp'],
1112
		'start_datetime' => $start['datetime'],
1113
		'start_iso_gmdate' => $start['iso_gmdate'],
1114
		'end_year' => $end['year'],
1115
		'end_month' => $end['month'],
1116
		'end_day' => $end['day'],
1117
		'end_hour' => !$allday ? $end['hour'] : null,
1118
		'end_minute' => !$allday ? $end['minute'] : null,
1119
		'end_second' => !$allday ? $end['second'] : null,
1120
		'end_date' => $row['end_date'],
1121
		'end_date_local' => $end['date_local'],
1122
		'end_date_orig' => $end['date_orig'],
1123
		'end_time' => !$allday ? $row['end_time'] : null,
1124
		'end_time_local' => !$allday ? $end['time_local'] : null,
1125
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1126
		'end_timestamp' => $end['timestamp'],
1127
		'end_datetime' => $end['datetime'],
1128
		'end_iso_gmdate' => $end['iso_gmdate'],
1129
		'allday' => $allday,
1130
		'tz' => !$allday ? $tz : null,
1131
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1132
		'span' => $span,
1133
		'title' => $row['title'],
1134
		'location' => $row['location'],
1135
		'member' => $row['id_member'],
1136
		'realname' => $row['real_name'],
1137
		'sequence' => $row['modified_time'],
1138
		'topic' => array(
1139
			'id' => $row['id_topic'],
1140
			'member_started' => $row['id_member_started'],
1141
			'first_msg' => $row['id_first_msg'],
1142
		),
1143
	);
1144
1145
	$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']));
1146
1147
	return $return_value;
1148
}
1149
1150
/**
1151
 * Gets an initial set of date and time values for creating a new event.
1152
 *
1153
 * @return array An array containing an initial set of date and time values for an event.
1154
 */
1155
function getNewEventDatetimes()
1156
{
1157
	// Ensure setEventStartEnd() has something to work with
1158
	$now = date_create();
1159
	$_POST['year'] = !empty($_POST['year']) ? $_POST['year'] : date_format($now, 'Y');
1160
	$_POST['month'] = !empty($_POST['month']) ? $_POST['month'] : date_format($now, 'm');
1161
	$_POST['day'] = !empty($_POST['day']) ? $_POST['day'] : date_format($now, 'd');
1162
	$_POST['hour'] = !empty($_POST['hour']) ? $_POST['hour'] : date_format($now, 'H');
1163
	$_POST['minute'] = !empty($_POST['minute']) ? $_POST['minute'] : date_format($now, 'i');
1164
	$_POST['second'] = !empty($_POST['second']) ? $_POST['second'] : date_format($now, 's');
1165
1166
	// Set the basic values for the new event
1167
	$row_keys = array('start_date', 'end_date', 'start_time', 'end_time', 'timezone');
1168
	$row = array_combine($row_keys, setEventStartEnd());
1169
1170
	// And now set the full suite of values
1171
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1172
1173
	// Default theme only uses some of this info, but others might want it all
1174
	$eventProperties = array(
1175
		'year' => $start['year'],
1176
		'month' => $start['month'],
1177
		'day' => $start['day'],
1178
		'hour' => !$allday ? $start['hour'] : null,
1179
		'minute' => !$allday ? $start['minute'] : null,
1180
		'second' => !$allday ? $start['second'] : null,
1181
		'start_date' => $row['start_date'],
1182
		'start_date_local' => $start['date_local'],
1183
		'start_date_orig' => $start['date_orig'],
1184
		'start_time' => !$allday ? $row['start_time'] : null,
1185
		'start_time_local' => !$allday ? $start['time_local'] : null,
1186
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1187
		'start_timestamp' => $start['timestamp'],
1188
		'start_datetime' => $start['datetime'],
1189
		'start_iso_gmdate' => $start['iso_gmdate'],
1190
		'end_year' => $end['year'],
1191
		'end_month' => $end['month'],
1192
		'end_day' => $end['day'],
1193
		'end_hour' => !$allday ? $end['hour'] : null,
1194
		'end_minute' => !$allday ? $end['minute'] : null,
1195
		'end_second' => !$allday ? $end['second'] : null,
1196
		'end_date' => $row['end_date'],
1197
		'end_date_local' => $end['date_local'],
1198
		'end_date_orig' => $end['date_orig'],
1199
		'end_time' => !$allday ? $row['end_time'] : null,
1200
		'end_time_local' => !$allday ? $end['time_local'] : null,
1201
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1202
		'end_timestamp' => $end['timestamp'],
1203
		'end_datetime' => $end['datetime'],
1204
		'end_iso_gmdate' => $end['iso_gmdate'],
1205
		'allday' => $allday,
1206
		'tz' => !$allday ? $tz : null,
1207
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1208
		'span' => $span,
1209
	);
1210
1211
	return $eventProperties;
1212
}
1213
1214
/**
1215
 * Set the start and end dates and times for a posted event for insertion into the database.
1216
 * Validates all date and times given to it.
1217
 * Makes sure events do not exceed the maximum allowed duration (if any).
1218
 * If passed an array that defines any time or date parameters, they will be used. Otherwise, gets the values from $_POST.
1219
 *
1220
 * @param array $eventOptions An array of optional time and date parameters (span, start_year, end_month, etc., etc.)
1221
 * @return array An array containing $start_date, $end_date, $start_time, $end_time
1222
 */
1223
function setEventStartEnd($eventOptions = array())
1224
{
1225
	global $modSettings, $user_info;
1226
1227
	// Set $span, in case we need it
1228
	$span = isset($eventOptions['span']) ? $eventOptions['span'] : (isset($_POST['span']) ? $_POST['span'] : 0);
1229
	if ($span > 0)
1230
		$span = !empty($modSettings['cal_maxspan']) ? min($modSettings['cal_maxspan'], $span - 1) : $span - 1;
1231
1232
	// Define the timezone for this event, falling back to the default if not provided
1233
	if (!empty($eventOptions['tz']) && in_array($eventOptions['tz'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1234
		$tz = $eventOptions['tz'];
1235 View Code Duplication
	elseif (!empty($_POST['tz']) && in_array($_POST['tz'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1 ignored issue
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...
1236
		$tz = $_POST['tz'];
1237
	else
1238
		$tz = getUserTimezone();
1239
1240
	// Is this supposed to be an all day event, or should it have specific start and end times?
1241
	if (isset($eventOptions['allday']))
1242
		$allday = $eventOptions['allday'];
1243
	elseif (empty($_POST['allday']))
1244
		$allday = false;
1245
	else
1246
		$allday = true;
1247
1248
	// Input might come as individual parameters...
1249
	$start_year = isset($eventOptions['year']) ? $eventOptions['year'] : (isset($_POST['year']) ? $_POST['year'] : null);
1250
	$start_month = isset($eventOptions['month']) ? $eventOptions['month'] : (isset($_POST['month']) ? $_POST['month'] : null);
1251
	$start_day = isset($eventOptions['day']) ? $eventOptions['day'] : (isset($_POST['day']) ? $_POST['day'] : null);
1252
	$start_hour = isset($eventOptions['hour']) ? $eventOptions['hour'] : (isset($_POST['hour']) ? $_POST['hour'] : null);
1253
	$start_minute = isset($eventOptions['minute']) ? $eventOptions['minute'] : (isset($_POST['minute']) ? $_POST['minute'] : null);
1254
	$start_second = isset($eventOptions['second']) ? $eventOptions['second'] : (isset($_POST['second']) ? $_POST['second'] : null);
1255
	$end_year = isset($eventOptions['end_year']) ? $eventOptions['end_year'] : (isset($_POST['end_year']) ? $_POST['end_year'] : null);
1256
	$end_month = isset($eventOptions['end_month']) ? $eventOptions['end_month'] : (isset($_POST['end_month']) ? $_POST['end_month'] : null);
1257
	$end_day = isset($eventOptions['end_day']) ? $eventOptions['end_day'] : (isset($_POST['end_day']) ? $_POST['end_day'] : null);
1258
	$end_hour = isset($eventOptions['end_hour']) ? $eventOptions['end_hour'] : (isset($_POST['end_hour']) ? $_POST['end_hour'] : null);
1259
	$end_minute = isset($eventOptions['end_minute']) ? $eventOptions['end_minute'] : (isset($_POST['end_minute']) ? $_POST['end_minute'] : null);
1260
	$end_second = isset($eventOptions['end_second']) ? $eventOptions['end_second'] : (isset($_POST['end_second']) ? $_POST['end_second'] : null);
1261
1262
	// ... or as datetime strings ...
1263
	$start_string = isset($eventOptions['start_datetime']) ? $eventOptions['start_datetime'] : (isset($_POST['start_datetime']) ? $_POST['start_datetime'] : null);
1264
	$end_string = isset($eventOptions['end_datetime']) ? $eventOptions['end_datetime'] : (isset($_POST['end_datetime']) ? $_POST['end_datetime'] : null);
1265
1266
	// ... or as date strings and time strings.
1267
	$start_date_string = isset($eventOptions['start_date']) ? $eventOptions['start_date'] : (isset($_POST['start_date']) ? $_POST['start_date'] : null);
1268
	$start_time_string = isset($eventOptions['start_time']) ? $eventOptions['start_time'] : (isset($_POST['start_time']) ? $_POST['start_time'] : null);
1269
	$end_date_string = isset($eventOptions['end_date']) ? $eventOptions['end_date'] : (isset($_POST['end_date']) ? $_POST['end_date'] : null);
1270
	$end_time_string = isset($eventOptions['end_time']) ? $eventOptions['end_time'] : (isset($_POST['end_time']) ? $_POST['end_time'] : null);
1271
1272
	// If the date and time were given in separate strings, combine them
1273
	if (empty($start_string) && isset($start_date_string))
1274
		$start_string = $start_date_string . (isset($start_time_string) ? ' ' . $start_time_string : '');
1275
	if (empty($end_string) && isset($end_date_string))
1276
		$end_string = $end_date_string . (isset($end_time_string) ? ' ' . $end_time_string : '');
1277
1278
	// If some form of string input was given, override individually defined options with it
1279 View Code Duplication
	if (isset($start_string))
1 ignored issue
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...
1280
	{
1281
		$start_string_parsed = date_parse($start_string);
1282
		if (empty($start_string_parsed['error_count']) && empty($start_string_parsed['warning_count']))
1283
		{
1284
			if ($start_string_parsed['year'] != false)
1285
			{
1286
				$start_year = $start_string_parsed['year'];
1287
				$start_month = $start_string_parsed['month'];
1288
				$start_day = $start_string_parsed['day'];
1289
			}
1290
			if ($start_string_parsed['hour'] != false)
1291
			{
1292
				$start_hour = $start_string_parsed['hour'];
1293
				$start_minute = $start_string_parsed['minute'];
1294
				$start_second = $start_string_parsed['second'];
1295
			}
1296
		}
1297
	}
1298 View Code Duplication
	if (isset($end_string))
1 ignored issue
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...
1299
	{
1300
		$end_string_parsed = date_parse($end_string);
1301
		if (empty($end_string_parsed['error_count']) && empty($end_string_parsed['warning_count']))
1302
		{
1303
			if ($end_string_parsed['year'] != false)
1304
			{
1305
				$end_year = $end_string_parsed['year'];
1306
				$end_month = $end_string_parsed['month'];
1307
				$end_day = $end_string_parsed['day'];
1308
			}
1309
			if ($end_string_parsed['hour'] != false)
1310
			{
1311
				$end_hour = $end_string_parsed['hour'];
1312
				$end_minute = $end_string_parsed['minute'];
1313
				$end_second = $end_string_parsed['second'];
1314
			}
1315
		}
1316
	}
1317
1318
	// Validate input
1319
	$start_date_isvalid = checkdate($start_month, $start_day, $start_year);
1320
	$end_date_isvalid = checkdate($end_month, $end_day, $end_year);
1321
1322
	$start_time_isset = (isset($start_hour) && isset($start_minute) && isset($start_second));
1323
	$d = date_parse(sprintf('%02d:%02d:%02d', $start_hour, $start_minute, $start_second));
1324
	$start_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1325
1326
	$end_time_isset = (isset($end_hour) && isset($end_minute) && isset($end_second));
1327
	$d = date_parse(sprintf('%02d:%02d:%02d', $end_hour, $end_minute, $end_second));
1328
	$end_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1329
1330
	// Uh-oh...
1331
	if ($start_date_isvalid === false)
1332
	{
1333
		fatal_lang_error('invalid_date', false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1334
	}
1335
1336
	// Make sure we use valid values for everything
1337
	if ($end_date_isvalid === false)
1338
	{
1339
		$end_year = $start_year;
1340
		$end_month = $start_month;
1341
		$end_day = $start_day;
1342
	}
1343
1344
	if ($allday === true || $start_time_isset === false || $start_time_isvalid === false)
1345
	{
1346
		$allday = true;
1347
		$start_hour = 0;
1348
		$start_minute = 0;
1349
		$start_second = 0;
1350
	}
1351
1352
	if ($allday === true || $end_time_isvalid === false || $end_time_isset === false)
1353
	{
1354
		$end_hour = $start_hour;
1355
		$end_minute = $start_minute;
1356
		$end_second = $start_second;
1357
	}
1358
1359
	// Now create our datetime objects
1360
	$start_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1361
	$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $end_year, $end_month, $end_day, $end_hour, $end_minute, $end_second) . ' ' . $tz);
1362
1363
	// Is $end_object too early?
1364
	if ($start_object >= $end_object)
1365
	{
1366
		$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1367
		if ($span > 0)
1368
			date_add($end_object, date_interval_create_from_date_string($span . ' days'));
1369
		else
1370
			date_add($end_object, date_interval_create_from_date_string('1 hour'));
1371
	}
1372
1373
	// Is $end_object too late?
1374
	if (!empty($modSettings['cal_maxspan']))
1375
	{
1376
		$date_diff = date_diff($start_object, $end_object);
1377
		if ($date_diff->days > $modSettings['cal_maxspan'])
1378
		{
1379
			if ($modSettings['cal_maxspan'] > 1)
1380
			{
1381
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1382
				date_add($end_object, date_interval_create_from_date_string($modSettings['cal_maxspan'] . ' days'));
1383
			}
1384
			else
1385
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, '11', '59', '59') . ' ' . $tz);
1386
		}
1387
	}
1388
1389
	// Finally, make our strings
1390
	$start_date = date_format($start_object, 'Y-m-d');
1391
	$end_date = date_format($end_object, 'Y-m-d');
1392
1393
	if ($allday == true)
1394
	{
1395
		$start_time = null;
1396
		$end_time = null;
1397
		$tz = null;
1398
	}
1399
	else
1400
	{
1401
		$start_time = date_format($start_object, 'H:i:s');
1402
		$end_time = date_format($end_object, 'H:i:s');
1403
	}
1404
1405
	return array($start_date, $end_date, $start_time, $end_time, $tz);
1406
}
1407
1408
/**
1409
 * Helper function for getEventRange, getEventProperties, getNewEventDatetimes, etc.
1410
 *
1411
 * @param array $row A database row representing an event from the calendar table
1412
 * @return array An array containing the start and end date and time properties for the event
1413
 */
1414
function buildEventDatetimes($row)
1415
{
1416
	global $sourcedir, $user_info;
1417
	require_once($sourcedir . '/Subs.php');
1418
1419
	// First, try to create a better date format, ignoring the "time" elements.
1420
	if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
1421
		$date_format = '%F';
1422
	else
1423
		$date_format = $matches[0];
1424
1425
	// We want a fairly compact version of the time, but as close as possible to the user's settings.
1426 View Code Duplication
	if (preg_match('~%[HkIlMpPrRSTX](?:[^%]*%[HkIlMpPrRSTX])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
1 ignored issue
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...
1427
		$time_format = '%k:%M';
1428
	else
1429
		$time_format = str_replace(array('%I', '%H', '%S', '%r', '%R', '%T'), array('%l', '%k', '', '%l:%M %p', '%k:%M', '%l:%M'), $matches[0]);
1430
1431
	// Should this be an all day event?
1432
	$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;
1433
1434
	// How many days does this event span?
1435
	$span = 1 + date_interval_format(date_diff(date_create($row['start_date']), date_create($row['end_date'])), '%d');
1436
1437
	// We need to have a defined timezone in the steps below
1438
	if (empty($row['timezone']))
1439
		$row['timezone'] = getUserTimezone();
1440
1441
	// Get most of the standard date information for the start and end datetimes
1442
	$start = date_parse($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''));
1443
	$end = date_parse($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''));
1444
1445
	// But we also want more info, so make some DateTime objects we can use
1446
	$start_object = date_create($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''), timezone_open($row['timezone']));
1447
	$end_object = date_create($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''), timezone_open($row['timezone']));
1448
1449
	// Unix timestamps are good
1450
	$start['timestamp'] = date_format($start_object, 'U');
1451
	$end['timestamp'] = date_format($end_object, 'U');
1452
1453
	// Datetime string without timezone  (e.g. '2016-12-28 22:45:30')
1454
	$start['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1455
	$end['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1456
1457
	// ISO formatted datetime string, relative to UTC (e.g. '2016-12-29T05:45:30+00:00')
1458
	$start['iso_gmdate'] = gmdate('c', $start['timestamp']);
1459
	$end['iso_gmdate'] = gmdate('c', $end['timestamp']);
1460
1461
	// Strings showing the datetimes in the user's preferred format, relative to the user's time zone
1462
	$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...
1463
	$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...
1464
	$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...
1465
	$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...
1466
1467
	// Strings showing the datetimes in the user's preferred format, relative to the event's time zone
1468
	$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...
1469
	$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...
1470
	$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...
1471
	$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...
1472
1473
	// The time zone identifier (e.g. 'Europe/London') and abbreviation (e.g. 'GMT')
1474
	$tz = date_format($start_object, 'e');
1475
	$tz_abbrev = date_format($start_object, 'T');
1476
1477
	// There are a handful of time zones that PHP doesn't know the abbreviation for. Fix 'em if we can.
1478
	if (strspn($tz_abbrev, '+-') > 0)
1479
	{
1480
		$tz_location = timezone_location_get(timezone_open($row['timezone']));
1481
1482
		// Kazakstan
1483
		if ($tz_location['country_code'] == 'KZ')
1484
			$tz_abbrev = str_replace(array('+05', '+06'), array('AQTT', 'ALMT'), $tz_abbrev);
1485
1486
		// Russia likes to experiment with time zones
1487
		if ($tz_location['country_code'] == 'RU')
1488
		{
1489
			$msk_offset = intval($tz_abbrev) - 3;
1490
			$msk_offset = !empty($msk_offset) ? sprintf('%+0d', $msk_offset) : '';
1491
			$tz_abbrev = 'MSK' . $msk_offset;
1492
		}
1493
1494
		// Still no good? We'll just mark it as a UTC offset
1495
		if (strspn($tz_abbrev, '+-') > 0)
1496
			$tz_abbrev = 'UTC' . $tz_abbrev;
1497
	}
1498
1499
	return array($start, $end, $allday, $span, $tz, $tz_abbrev);
1500
}
1501
1502
/**
1503
 * Gets a member's selected timezone identifier directly from the database
1504
 *
1505
 * @param int $id_member The member id to look up. If not provided, the current user's id will be used.
1506
 * @return string The timezone identifier string for the user's timezone.
1507
 */
1508
function getUserTimezone($id_member = null)
1509
{
1510
	global $smcFunc, $context, $sourcedir, $user_info, $modSettings;
1511
1512
	if (is_null($id_member) && $user_info['is_guest'] == false)
1513
		$id_member = $context['user']['id'];
1514
1515 View Code Duplication
	if (isset($id_member))
1 ignored issue
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...
1516
	{
1517
		$request = $smcFunc['db_query']('', '
1518
			SELECT timezone
1519
			FROM {db_prefix}members
1520
			WHERE id_member = {int:id_member}',
1521
			array(
1522
				'id_member' => $id_member,
1523
			)
1524
		);
1525
		list($timezone) = $smcFunc['db_fetch_row']($request);
1526
		$smcFunc['db_free_result']($request);
1527
	}
1528
1529 View Code Duplication
	if (empty($timezone) || !in_array($timezone, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1 ignored issue
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...
1530
		$timezone = isset($modSettings['default_timezone']) ? $modSettings['default_timezone'] : date_default_timezone_get();
1531
1532
	return $timezone;
1533
}
1534
1535
/**
1536
 * Gets all of the holidays for the listing
1537
 *
1538
 * @param int $start The item to start with (for pagination purposes)
1539
 * @param int $items_per_page How many items to show on each page
1540
 * @param string $sort A string indicating how to sort the results
1541
 * @return array An array of holidays, each of which is an array containing the id, year, month, day and title of the holiday
1542
 */
1543 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...
1544
{
1545
	global $smcFunc;
1546
1547
	$request = $smcFunc['db_query']('', '
1548
		SELECT id_holiday, YEAR(event_date) AS year, MONTH(event_date) AS month, DAYOFMONTH(event_date) AS day, title
1549
		FROM {db_prefix}calendar_holidays
1550
		ORDER BY {raw:sort}
1551
		LIMIT {int:start}, {int:max}',
1552
		array(
1553
			'sort' => $sort,
1554
			'start' => $start,
1555
			'max' => $items_per_page,
1556
		)
1557
	);
1558
	$holidays = array();
1559
	while ($row = $smcFunc['db_fetch_assoc']($request))
1560
		$holidays[] = $row;
1561
	$smcFunc['db_free_result']($request);
1562
1563
	return $holidays;
1564
}
1565
1566
/**
1567
 * Helper function to get the total number of holidays
1568
 *
1569
 * @return int The total number of holidays
1570
 */
1571
function list_getNumHolidays()
1572
{
1573
	global $smcFunc;
1574
1575
	$request = $smcFunc['db_query']('', '
1576
		SELECT COUNT(*)
1577
		FROM {db_prefix}calendar_holidays',
1578
		array(
1579
		)
1580
	);
1581
	list($num_items) = $smcFunc['db_fetch_row']($request);
1582
	$smcFunc['db_free_result']($request);
1583
1584
	return (int) $num_items;
1585
}
1586
1587
/**
1588
 * Remove a holiday from the calendar
1589
 *
1590
 * @param array $holiday_ids An array of IDs of holidays to delete
1591
 */
1592
function removeHolidays($holiday_ids)
1593
{
1594
	global $smcFunc;
1595
1596
	$smcFunc['db_query']('', '
1597
		DELETE FROM {db_prefix}calendar_holidays
1598
		WHERE id_holiday IN ({array_int:id_holiday})',
1599
		array(
1600
			'id_holiday' => $holiday_ids,
1601
		)
1602
	);
1603
1604
	updateSettings(array(
1605
		'calendar_updated' => time(),
1606
	));
1607
}
1608
1609
?>
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...