Completed
Push — release-2.1 ( 99ca30...250ee8 )
by Mathias
07:31
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)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
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
		$timestamp = mktime(0, 0, 0, $month, $day, $year);
551
		$calendarGrid['week_title'] = sprintf($txt['calendar_week_beginning'], date('F', $timestamp), date('j', $timestamp), date('Y', $timestamp));
552
	}
553
554
	// This holds all the main data - there is at least one month!
555
	$calendarGrid['months'] = array();
556
	$lastDay = 99;
557
	$curDay = $day;
558
	$curDayOfWeek = $calendarOptions['start_day'];
559
	for ($i = 0; $i < 7; $i++)
560
	{
561
		// Have we gone into a new month (Always happens first cycle too)
562
		if ($lastDay > $curDay)
563
		{
564
			$curMonth = $lastDay == 99 ? $month : ($month == 12 ? 1 : $month + 1);
565
			$curYear = $lastDay == 99 ? $year : ($curMonth == 1 && $month == 12 ? $year + 1 : $year);
566
			$calendarGrid['months'][$curMonth] = array(
567
				'current_month' => $curMonth,
568
				'current_year' => $curYear,
569
				'days' => array(),
570
			);
571
		}
572
573
		// Add todays information to the pile!
574
		$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...
575
576
		$calendarGrid['months'][$curMonth]['days'][$curDay] = array(
577
			'day' => $curDay,
578
			'day_of_week' => $curDayOfWeek,
579
			'date' => $date,
580
			'is_today' => $date == $today['date'],
581
			'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
582
			'events' => !empty($events[$date]) ? $events[$date] : array(),
583
			'birthdays' => !empty($bday[$date]) ? $bday[$date] : array()
584
		);
585
586
		// Make the last day what the current day is and work out what the next day is.
587
		$lastDay = $curDay;
588
		$curTimestamp += 86400;
589
		$curDay = (int) strftime('%d', $curTimestamp);
590
591
		// Also increment the current day of the week.
592
		$curDayOfWeek = $curDayOfWeek >= 6 ? 0 : ++$curDayOfWeek;
593
	}
594
595
	// Set the previous and the next week's links.
596
	$calendarGrid['previous_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['previous_week']['year'] . ';month=' . $calendarGrid['previous_week']['month'] . ';day=' . $calendarGrid['previous_week']['day'];
597
	$calendarGrid['next_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['next_week']['year'] . ';month=' . $calendarGrid['next_week']['month'] . ';day=' . $calendarGrid['next_week']['day'];
598
599
	return $calendarGrid;
600
}
601
602
/**
603
 * Retrieve all events for the given days, independently of the users offset.
604
 * cache callback function used to retrieve the birthdays, holidays, and events between now and now + days_to_index.
605
 * widens the search range by an extra 24 hours to support time offset shifts.
606
 * used by the cache_getRecentEvents function to get the information needed to calculate the events taking the users time offset into account.
607
 *
608
 * @param int $days_to_index How many days' worth of info to index
609
 * @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
610
 */
611
function cache_getOffsetIndependentEvents($days_to_index)
612
{
613
	$low_date = strftime('%Y-%m-%d', forum_time(false) - 24 * 3600);
614
	$high_date = strftime('%Y-%m-%d', forum_time(false) + $days_to_index * 24 * 3600);
615
616
	return array(
617
		'data' => array(
618
			'holidays' => getHolidayRange($low_date, $high_date),
619
			'birthdays' => getBirthdayRange($low_date, $high_date),
620
			'events' => getEventRange($low_date, $high_date, false),
621
		),
622
		'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\']);',
623
		'expires' => time() + 3600,
624
	);
625
}
626
627
/**
628
 * cache callback function used to retrieve the upcoming birthdays, holidays, and events within the given period, taking into account the users time offset.
629
 * Called from the BoardIndex to display the current day's events on the board index
630
 * used by the board index and SSI to show the upcoming events.
631
 * @param array $eventOptions An array of event options. Only 'num_days_shown' is used here
632
 * @return array An array containing the info that was cached as well as a few other relevant things
633
 */
634
function cache_getRecentEvents($eventOptions)
635
{
636
	// With the 'static' cached data we can calculate the user-specific data.
637
	$cached_data = cache_quick_get('calendar_index', 'Subs-Calendar.php', 'cache_getOffsetIndependentEvents', array($eventOptions['num_days_shown']));
638
639
	// Get the information about today (from user perspective).
640
	$today = getTodayInfo();
641
642
	$return_data = array(
643
		'calendar_holidays' => array(),
644
		'calendar_birthdays' => array(),
645
		'calendar_events' => array(),
646
	);
647
648
	// Set the event span to be shown in seconds.
649
	$days_for_index = $eventOptions['num_days_shown'] * 86400;
650
651
	// Get the current member time/date.
652
	$now = forum_time();
653
654
	// Holidays between now and now + days.
655
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
656
	{
657
		if (isset($cached_data['holidays'][strftime('%Y-%m-%d', $i)]))
658
			$return_data['calendar_holidays'] = array_merge($return_data['calendar_holidays'], $cached_data['holidays'][strftime('%Y-%m-%d', $i)]);
659
	}
660
661
	// Happy Birthday, guys and gals!
662
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
663
	{
664
		$loop_date = strftime('%Y-%m-%d', $i);
665
		if (isset($cached_data['birthdays'][$loop_date]))
666
		{
667
			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...
668
				$cached_data['birthdays'][strftime('%Y-%m-%d', $i)][$index]['is_today'] = $loop_date === $today['date'];
669
			$return_data['calendar_birthdays'] = array_merge($return_data['calendar_birthdays'], $cached_data['birthdays'][$loop_date]);
670
		}
671
	}
672
673
	$duplicates = array();
674
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
675
	{
676
		// Determine the date of the current loop step.
677
		$loop_date = strftime('%Y-%m-%d', $i);
678
679
		// No events today? Check the next day.
680
		if (empty($cached_data['events'][$loop_date]))
681
			continue;
682
683
		// Loop through all events to add a few last-minute values.
684
		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...
685
		{
686
			// Create a shortcut variable for easier access.
687
			$this_event = &$cached_data['events'][$loop_date][$ev];
688
689
			// Skip duplicates.
690
			if (isset($duplicates[$this_event['topic'] . $this_event['title']]))
691
			{
692
				unset($cached_data['events'][$loop_date][$ev]);
693
				continue;
694
			}
695
			else
696
				$duplicates[$this_event['topic'] . $this_event['title']] = true;
697
698
			// Might be set to true afterwards, depending on the permissions.
699
			$this_event['can_edit'] = false;
700
			$this_event['is_today'] = $loop_date === $today['date'];
701
			$this_event['date'] = $loop_date;
702
		}
703
704
		if (!empty($cached_data['events'][$loop_date]))
705
			$return_data['calendar_events'] = array_merge($return_data['calendar_events'], $cached_data['events'][$loop_date]);
706
	}
707
708
	// Mark the last item so that a list separator can be used in the template.
709 View Code Duplication
	for ($i = 0, $n = count($return_data['calendar_birthdays']); $i < $n; $i++)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
710
		$return_data['calendar_birthdays'][$i]['is_last'] = !isset($return_data['calendar_birthdays'][$i + 1]);
711 View Code Duplication
	for ($i = 0, $n = count($return_data['calendar_events']); $i < $n; $i++)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
712
		$return_data['calendar_events'][$i]['is_last'] = !isset($return_data['calendar_events'][$i + 1]);
713
714
	return array(
715
		'data' => $return_data,
716
		'expires' => time() + 3600,
717
		'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\']);',
718
		'post_retri_eval' => '
719
			global $context, $scripturl, $user_info;
720
721
			foreach ($cache_block[\'data\'][\'calendar_events\'] as $k => $event)
722
			{
723
				// Remove events that the user may not see or wants to ignore.
724
				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\']))
725
					unset($cache_block[\'data\'][\'calendar_events\'][$k]);
726
				else
727
				{
728
					// Whether the event can be edited depends on the permissions.
729
					$cache_block[\'data\'][\'calendar_events\'][$k][\'can_edit\'] = allowedTo(\'calendar_edit_any\') || ($event[\'poster\'] == $user_info[\'id\'] && allowedTo(\'calendar_edit_own\'));
730
731
					// The added session code makes this URL not cachable.
732
					$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\'];
733
				}
734
			}
735
736
			if (empty($params[0][\'include_holidays\']))
737
				$cache_block[\'data\'][\'calendar_holidays\'] = array();
738
			if (empty($params[0][\'include_birthdays\']))
739
				$cache_block[\'data\'][\'calendar_birthdays\'] = array();
740
			if (empty($params[0][\'include_events\']))
741
				$cache_block[\'data\'][\'calendar_events\'] = array();
742
743
			$cache_block[\'data\'][\'show_calendar\'] = !empty($cache_block[\'data\'][\'calendar_holidays\']) || !empty($cache_block[\'data\'][\'calendar_birthdays\']) || !empty($cache_block[\'data\'][\'calendar_events\']);',
744
	);
745
}
746
747
/**
748
 * Makes sure the calendar post is valid.
749
 */
750
function validateEventPost()
751
{
752
	global $modSettings, $smcFunc;
753
754
	if (!isset($_POST['deleteevent']))
755
	{
756
		// The 2.1 way
757
		if (isset($_POST['start_date']))
758
		{
759
			$d = date_parse($_POST['start_date']);
760 View Code Duplication
			if (!empty($d['error_count']) || !empty($d['warning_count']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
761
				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...
762
			if (empty($d['year']))
763
				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...
764
			if (empty($d['month']))
765
				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...
766
		}
767
		elseif (isset($_POST['start_datetime']))
768
		{
769
			$d = date_parse($_POST['start_datetime']);
770 View Code Duplication
			if (!empty($d['error_count']) || !empty($d['warning_count']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
771
				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...
772
			if (empty($d['year']))
773
				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...
774
			if (empty($d['month']))
775
				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...
776
		}
777
		// The 2.0 way
778
		else
779
		{
780
			// No month?  No year?
781
			if (!isset($_POST['month']))
782
				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...
783
			if (!isset($_POST['year']))
784
				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...
785
786
			// Check the month and year...
787
			if ($_POST['month'] < 1 || $_POST['month'] > 12)
788
				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...
789 View Code Duplication
			if ($_POST['year'] < $modSettings['cal_minyear'] || $_POST['year'] > $modSettings['cal_maxyear'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
790
				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...
791
		}
792
	}
793
794
	// Make sure they're allowed to post...
795
	isAllowedTo('calendar_post');
796
797
	// If they want to us to calculate an end date, make sure it will fit in an acceptable range.
798
	if (isset($_POST['span']))
799
	{
800
		if (($_POST['span'] < 1) || (!empty($modSettings['cal_maxspan']) && $_POST['span'] > $modSettings['cal_maxspan']))
801
			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...
802
	}
803
804
	// There is no need to validate the following values if we are just deleting the event.
805
	if (!isset($_POST['deleteevent']))
806
	{
807
		// If we're doing things the 2.0 way, check the day
808
		if (empty($_POST['start_date']) && empty($_POST['start_datetime']))
809
		{
810
			// No day?
811
			if (!isset($_POST['day']))
812
				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...
813
814
			// Bad day?
815
			if (!checkdate($_POST['month'], $_POST['day'], $_POST['year']))
816
				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...
817
		}
818
819
		if (!isset($_POST['evtitle']) && !isset($_POST['subject']))
820
			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...
821
		elseif (!isset($_POST['evtitle']))
822
			$_POST['evtitle'] = $_POST['subject'];
823
824
		// No title?
825
		if ($smcFunc['htmltrim']($_POST['evtitle']) === '')
826
			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...
827 View Code Duplication
		if ($smcFunc['strlen']($_POST['evtitle']) > 100)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
828
			$_POST['evtitle'] = $smcFunc['substr']($_POST['evtitle'], 0, 100);
829
		$_POST['evtitle'] = str_replace(';', '', $_POST['evtitle']);
830
	}
831
}
832
833
/**
834
 * Get the event's poster.
835
 *
836
 * @param int $event_id The ID of the event
837
 * @return int|bool The ID of the poster or false if the event was not found
838
 */
839 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...
840
{
841
	global $smcFunc;
842
843
	// A simple database query, how hard can that be?
844
	$request = $smcFunc['db_query']('', '
845
		SELECT id_member
846
		FROM {db_prefix}calendar
847
		WHERE id_event = {int:id_event}
848
		LIMIT 1',
849
		array(
850
			'id_event' => $event_id,
851
		)
852
	);
853
854
	// No results, return false.
855
	if ($smcFunc['db_num_rows'] === 0)
856
		return false;
857
858
	// Grab the results and return.
859
	list ($poster) = $smcFunc['db_fetch_row']($request);
860
	$smcFunc['db_free_result']($request);
861
	return (int) $poster;
862
}
863
864
/**
865
 * Consolidating the various INSERT statements into this function.
866
 * Inserts the passed event information into the calendar table.
867
 * Allows to either set a time span (in days) or an end_date.
868
 * Does not check any permissions of any sort.
869
 *
870
 * @param array $eventOptions An array of event options ('title', 'span', 'start_date', 'end_date', etc.)
871
 */
872
function insertEvent(&$eventOptions)
873
{
874
	global $smcFunc, $context;
875
876
	// Add special chars to the title.
877
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
878
879
	$eventOptions['location'] = isset($eventOptions['location']) ? $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES) : '';
880
881
	// Set the start and end dates and times
882
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
883
884
	// If no topic and board are given, they are not linked to a topic.
885
	$eventOptions['board'] = isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0;
886
	$eventOptions['topic'] = isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0;
887
888
	$event_columns = array(
889
		'id_board' => 'int', 'id_topic' => 'int', 'title' => 'string-60', 'id_member' => 'int',
890
		'start_date' => 'date', 'end_date' => 'date', 'location' => 'string-255',
891
	);
892
	$event_parameters = array(
893
		$eventOptions['board'], $eventOptions['topic'], $eventOptions['title'], $eventOptions['member'],
894
		$start_date, $end_date, $eventOptions['location'],
895
	);
896 View Code Duplication
	if (!empty($start_time) && !empty($end_time) && !empty($tz) && in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
897
	{
898
		$event_columns['start_time'] = 'time';
899
		$event_parameters[] = $start_time;
900
		$event_columns['end_time'] = 'time';
901
		$event_parameters[] = $end_time;
902
		$event_columns['timezone'] = 'string';
903
		$event_parameters[] = $tz;
904
	}
905
906
	call_integration_hook('integrate_create_event', array(&$eventOptions, &$event_columns, &$event_parameters));
907
908
	// Insert the event!
909
	$smcFunc['db_insert']('',
910
		'{db_prefix}calendar',
911
		$event_columns,
912
		$event_parameters,
913
		array('id_event')
914
	);
915
916
	// Store the just inserted id_event for future reference.
917
	$eventOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}calendar', 'id_event');
918
919
	// If this isn't tied to a topic, we need to notify people about it.
920
	if (empty($eventOptions['topic']))
921
	{
922
		$smcFunc['db_insert']('insert',
923
			'{db_prefix}background_tasks',
924
			array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
925
			array('$sourcedir/tasks/EventNew-Notify.php', 'EventNew_Notify_Background', json_encode(array(
926
				'event_title' => $eventOptions['title'],
927
				'event_id' => $eventOptions['id'],
928
				'sender_id' => $eventOptions['member'],
929
				'sender_name' => $eventOptions['member'] == $context['user']['id'] ? $context['user']['name'] : '',
930
				'time' => time(),
931
			)), 0),
932
			array('id_task')
933
		);
934
	}
935
936
	// Update the settings to show something calendar-ish was updated.
937
	updateSettings(array(
938
		'calendar_updated' => time(),
939
	));
940
}
941
942
/**
943
 * modifies an event.
944
 * allows to either set a time span (in days) or an end_date.
945
 * does not check any permissions of any sort.
946
 *
947
 * @param int $event_id The ID of the event
948
 * @param array $eventOptions An array of event information
949
 */
950
function modifyEvent($event_id, &$eventOptions)
951
{
952
	global $smcFunc;
953
954
	// Properly sanitize the title and location
955
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
956
	$eventOptions['location'] = $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES);
957
958
	// Set the new start and end dates and times
959
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
960
961
	$event_columns = array(
962
		'start_date' => '{date:start_date}',
963
		'end_date' => '{date:end_date}',
964
		'title' => 'SUBSTRING({string:title}, 1, 60)',
965
		'id_board' => '{int:id_board}',
966
		'id_topic' => '{int:id_topic}',
967
		'location' => 'SUBSTRING({string:location}, 1, 255)',
968
	);
969
	$event_parameters = array(
970
		'start_date' => $start_date,
971
		'end_date' => $end_date,
972
		'title' => $eventOptions['title'],
973
		'location' => $eventOptions['location'],
974
		'id_board' => isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0,
975
		'id_topic' => isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0,
976
	);
977 View Code Duplication
	if (!empty($start_time) && !empty($end_time) && !empty($tz) && in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
978
	{
979
		$event_columns['start_time'] = '{time:start_time}';
980
		$event_parameters['start_time'] = $start_time;
981
		$event_columns['end_time'] = '{time:end_time}';
982
		$event_parameters['end_time'] = $end_time;
983
		$event_columns['timezone'] = '{string:timezone}';
984
		$event_parameters['timezone'] = $tz;
985
	}
986
987
	// This is to prevent hooks to modify the id of the event
988
	$real_event_id = $event_id;
989
	call_integration_hook('integrate_modify_event', array($event_id, &$eventOptions, &$event_columns, &$event_parameters));
990
991
	$column_clauses = array();
992
	foreach ($event_columns as $col => $crit)
993
		$column_clauses[] = $col . ' = ' . $crit;
994
995
	$smcFunc['db_query']('', '
996
		UPDATE {db_prefix}calendar
997
		SET
998
			' . implode(', ', $column_clauses) . '
999
		WHERE id_event = {int:id_event}',
1000
		array_merge(
1001
			$event_parameters,
1002
			array(
1003
				'id_event' => $real_event_id
1004
			)
1005
		)
1006
	);
1007
1008
	if (empty($start_time) || empty($end_time) || empty($tz) || !in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1009
	{
1010
		$smcFunc['db_query']('', '
1011
			UPDATE {db_prefix}calendar
1012
			SET start_time = NULL, end_time = NULL, timezone = NULL
1013
			WHERE id_event = {int:id_event}',
1014
			array(
1015
				'id_event' => $real_event_id
1016
			)
1017
		);
1018
	}
1019
1020
	updateSettings(array(
1021
		'calendar_updated' => time(),
1022
	));
1023
}
1024
1025
/**
1026
 * Remove an event
1027
 * removes an event.
1028
 * does no permission checks.
1029
 *
1030
 * @param int $event_id The ID of the event to remove
1031
 */
1032
function removeEvent($event_id)
1033
{
1034
	global $smcFunc;
1035
1036
	$smcFunc['db_query']('', '
1037
		DELETE FROM {db_prefix}calendar
1038
		WHERE id_event = {int:id_event}',
1039
		array(
1040
			'id_event' => $event_id,
1041
		)
1042
	);
1043
1044
	call_integration_hook('integrate_remove_event', array($event_id));
1045
1046
	updateSettings(array(
1047
		'calendar_updated' => time(),
1048
	));
1049
}
1050
1051
/**
1052
 * Gets all the events properties
1053
 *
1054
 * @param int $event_id The ID of the event
1055
 * @return array An array of event information
1056
 */
1057
function getEventProperties($event_id)
1058
{
1059
	global $smcFunc;
1060
1061
	$request = $smcFunc['db_query']('', '
1062
		SELECT
1063
			c.id_event, c.id_board, c.id_topic, c.id_member, c.title,
1064
			c.start_date, c.end_date, c.start_time, c.end_time, c.timezone, c.location,
1065
			t.id_first_msg, t.id_member_started,
1066
			mb.real_name, m.modified_time
1067
		FROM {db_prefix}calendar AS c
1068
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = c.id_topic)
1069
			LEFT JOIN {db_prefix}members AS mb ON (mb.id_member = t.id_member_started)
1070
			LEFT JOIN {db_prefix}messages AS m ON (m.id_msg  = t.id_first_msg)
1071
		WHERE c.id_event = {int:id_event}',
1072
		array(
1073
			'id_event' => $event_id,
1074
		)
1075
	);
1076
1077
	// If nothing returned, we are in poo, poo.
1078
	if ($smcFunc['db_num_rows']($request) === 0)
1079
		return false;
1080
1081
	$row = $smcFunc['db_fetch_assoc']($request);
1082
	$smcFunc['db_free_result']($request);
1083
1084
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1085
1086
	// Sanity check
1087 View Code Duplication
	if (!empty($start['error_count']) || !empty($start['warning_count']) || !empty($end['error_count']) || !empty($end['warning_count']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1088
		return false;
1089
1090
	$return_value = array(
1091
		'boards' => array(),
1092
		'board' => $row['id_board'],
1093
		'new' => 0,
1094
		'eventid' => $event_id,
1095
		'year' => $start['year'],
1096
		'month' => $start['month'],
1097
		'day' => $start['day'],
1098
		'hour' => !$allday ? $start['hour'] : null,
1099
		'minute' => !$allday ? $start['minute'] : null,
1100
		'second' => !$allday ? $start['second'] : null,
1101
		'start_date' => $row['start_date'],
1102
		'start_date_local' => $start['date_local'],
1103
		'start_date_orig' => $start['date_orig'],
1104
		'start_time' => !$allday ? $row['start_time'] : null,
1105
		'start_time_local' => !$allday ? $start['time_local'] : null,
1106
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1107
		'start_timestamp' => $start['timestamp'],
1108
		'start_datetime' => $start['datetime'],
1109
		'start_iso_gmdate' => $start['iso_gmdate'],
1110
		'end_year' => $end['year'],
1111
		'end_month' => $end['month'],
1112
		'end_day' => $end['day'],
1113
		'end_hour' => !$allday ? $end['hour'] : null,
1114
		'end_minute' => !$allday ? $end['minute'] : null,
1115
		'end_second' => !$allday ? $end['second'] : null,
1116
		'end_date' => $row['end_date'],
1117
		'end_date_local' => $end['date_local'],
1118
		'end_date_orig' => $end['date_orig'],
1119
		'end_time' => !$allday ? $row['end_time'] : null,
1120
		'end_time_local' => !$allday ? $end['time_local'] : null,
1121
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1122
		'end_timestamp' => $end['timestamp'],
1123
		'end_datetime' => $end['datetime'],
1124
		'end_iso_gmdate' => $end['iso_gmdate'],
1125
		'allday' => $allday,
1126
		'tz' => !$allday ? $tz : null,
1127
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1128
		'span' => $span,
1129
		'title' => $row['title'],
1130
		'location' => $row['location'],
1131
		'member' => $row['id_member'],
1132
		'realname' => $row['real_name'],
1133
		'sequence' => $row['modified_time'],
1134
		'topic' => array(
1135
			'id' => $row['id_topic'],
1136
			'member_started' => $row['id_member_started'],
1137
			'first_msg' => $row['id_first_msg'],
1138
		),
1139
	);
1140
1141
	$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']));
1142
1143
	return $return_value;
1144
}
1145
1146
/**
1147
 * Gets an initial set of date and time values for creating a new event.
1148
 *
1149
 * @return array An array containing an initial set of date and time values for an event.
1150
 */
1151
function getNewEventDatetimes()
1152
{
1153
	// Ensure setEventStartEnd() has something to work with
1154
	$now = date_create();
1155
	$_POST['year'] = !empty($_POST['year']) ? $_POST['year'] : date_format($now, 'Y');
1156
	$_POST['month'] = !empty($_POST['month']) ? $_POST['month'] : date_format($now, 'm');
1157
	$_POST['day'] = !empty($_POST['day']) ? $_POST['day'] : date_format($now, 'd');
1158
	$_POST['hour'] = !empty($_POST['hour']) ? $_POST['hour'] : date_format($now, 'H');
1159
	$_POST['minute'] = !empty($_POST['minute']) ? $_POST['minute'] : date_format($now, 'i');
1160
	$_POST['second'] = !empty($_POST['second']) ? $_POST['second'] : date_format($now, 's');
1161
1162
	// Set the basic values for the new event
1163
	$row_keys = array('start_date', 'end_date', 'start_time', 'end_time', 'timezone');
1164
	$row = array_combine($row_keys, setEventStartEnd());
1165
1166
	// And now set the full suite of values
1167
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1168
1169
	// Default theme only uses some of this info, but others might want it all
1170
	$eventProperties = array(
1171
		'year' => $start['year'],
1172
		'month' => $start['month'],
1173
		'day' => $start['day'],
1174
		'hour' => !$allday ? $start['hour'] : null,
1175
		'minute' => !$allday ? $start['minute'] : null,
1176
		'second' => !$allday ? $start['second'] : null,
1177
		'start_date' => $row['start_date'],
1178
		'start_date_local' => $start['date_local'],
1179
		'start_date_orig' => $start['date_orig'],
1180
		'start_time' => !$allday ? $row['start_time'] : null,
1181
		'start_time_local' => !$allday ? $start['time_local'] : null,
1182
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1183
		'start_timestamp' => $start['timestamp'],
1184
		'start_datetime' => $start['datetime'],
1185
		'start_iso_gmdate' => $start['iso_gmdate'],
1186
		'end_year' => $end['year'],
1187
		'end_month' => $end['month'],
1188
		'end_day' => $end['day'],
1189
		'end_hour' => !$allday ? $end['hour'] : null,
1190
		'end_minute' => !$allday ? $end['minute'] : null,
1191
		'end_second' => !$allday ? $end['second'] : null,
1192
		'end_date' => $row['end_date'],
1193
		'end_date_local' => $end['date_local'],
1194
		'end_date_orig' => $end['date_orig'],
1195
		'end_time' => !$allday ? $row['end_time'] : null,
1196
		'end_time_local' => !$allday ? $end['time_local'] : null,
1197
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1198
		'end_timestamp' => $end['timestamp'],
1199
		'end_datetime' => $end['datetime'],
1200
		'end_iso_gmdate' => $end['iso_gmdate'],
1201
		'allday' => $allday,
1202
		'tz' => !$allday ? $tz : null,
1203
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1204
		'span' => $span,
1205
	);
1206
1207
	return $eventProperties;
1208
}
1209
1210
/**
1211
 * Set the start and end dates and times for a posted event for insertion into the database.
1212
 * Validates all date and times given to it.
1213
 * Makes sure events do not exceed the maximum allowed duration (if any).
1214
 * If passed an array that defines any time or date parameters, they will be used. Otherwise, gets the values from $_POST.
1215
 *
1216
 * @param array $eventOptions An array of optional time and date parameters (span, start_year, end_month, etc., etc.)
1217
 * @return array An array containing $start_date, $end_date, $start_time, $end_time
1218
 */
1219
function setEventStartEnd($eventOptions = array())
1220
{
1221
	global $modSettings, $user_info;
1222
1223
	// Set $span, in case we need it
1224
	$span = isset($eventOptions['span']) ? $eventOptions['span'] : (isset($_POST['span']) ? $_POST['span'] : 0);
1225
	if ($span > 0)
1226
		$span = !empty($modSettings['cal_maxspan']) ? min($modSettings['cal_maxspan'], $span - 1) : $span - 1;
1227
1228
	// Define the timezone for this event, falling back to the default if not provided
1229
	if (!empty($eventOptions['tz']) && in_array($eventOptions['tz'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1230
		$tz = $eventOptions['tz'];
1231 View Code Duplication
	elseif (!empty($_POST['tz']) && in_array($_POST['tz'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1232
		$tz = $_POST['tz'];
1233
	else
1234
		$tz = getUserTimezone();
1235
1236
	// Is this supposed to be an all day event, or should it have specific start and end times?
1237
	if (isset($eventOptions['allday']))
1238
		$allday = $eventOptions['allday'];
1239
	elseif (empty($_POST['allday']))
1240
		$allday = false;
1241
	else
1242
		$allday = true;
1243
1244
	// Input might come as individual parameters...
1245
	$start_year = isset($eventOptions['year']) ? $eventOptions['year'] : (isset($_POST['year']) ? $_POST['year'] : null);
1246
	$start_month = isset($eventOptions['month']) ? $eventOptions['month'] : (isset($_POST['month']) ? $_POST['month'] : null);
1247
	$start_day = isset($eventOptions['day']) ? $eventOptions['day'] : (isset($_POST['day']) ? $_POST['day'] : null);
1248
	$start_hour = isset($eventOptions['hour']) ? $eventOptions['hour'] : (isset($_POST['hour']) ? $_POST['hour'] : null);
1249
	$start_minute = isset($eventOptions['minute']) ? $eventOptions['minute'] : (isset($_POST['minute']) ? $_POST['minute'] : null);
1250
	$start_second = isset($eventOptions['second']) ? $eventOptions['second'] : (isset($_POST['second']) ? $_POST['second'] : null);
1251
	$end_year = isset($eventOptions['end_year']) ? $eventOptions['end_year'] : (isset($_POST['end_year']) ? $_POST['end_year'] : null);
1252
	$end_month = isset($eventOptions['end_month']) ? $eventOptions['end_month'] : (isset($_POST['end_month']) ? $_POST['end_month'] : null);
1253
	$end_day = isset($eventOptions['end_day']) ? $eventOptions['end_day'] : (isset($_POST['end_day']) ? $_POST['end_day'] : null);
1254
	$end_hour = isset($eventOptions['end_hour']) ? $eventOptions['end_hour'] : (isset($_POST['end_hour']) ? $_POST['end_hour'] : null);
1255
	$end_minute = isset($eventOptions['end_minute']) ? $eventOptions['end_minute'] : (isset($_POST['end_minute']) ? $_POST['end_minute'] : null);
1256
	$end_second = isset($eventOptions['end_second']) ? $eventOptions['end_second'] : (isset($_POST['end_second']) ? $_POST['end_second'] : null);
1257
1258
	// ... or as datetime strings ...
1259
	$start_string = isset($eventOptions['start_datetime']) ? $eventOptions['start_datetime'] : (isset($_POST['start_datetime']) ? $_POST['start_datetime'] : null);
1260
	$end_string = isset($eventOptions['end_datetime']) ? $eventOptions['end_datetime'] : (isset($_POST['end_datetime']) ? $_POST['end_datetime'] : null);
1261
1262
	// ... or as date strings and time strings.
1263
	$start_date_string = isset($eventOptions['start_date']) ? $eventOptions['start_date'] : (isset($_POST['start_date']) ? $_POST['start_date'] : null);
1264
	$start_time_string = isset($eventOptions['start_time']) ? $eventOptions['start_time'] : (isset($_POST['start_time']) ? $_POST['start_time'] : null);
1265
	$end_date_string = isset($eventOptions['end_date']) ? $eventOptions['end_date'] : (isset($_POST['end_date']) ? $_POST['end_date'] : null);
1266
	$end_time_string = isset($eventOptions['end_time']) ? $eventOptions['end_time'] : (isset($_POST['end_time']) ? $_POST['end_time'] : null);
1267
1268
	// If the date and time were given in separate strings, combine them
1269
	if (empty($start_string) && isset($start_date_string))
1270
		$start_string = $start_date_string . (isset($start_time_string) ? ' ' . $start_time_string : '');
1271
	if (empty($end_string) && isset($end_date_string))
1272
		$end_string = $end_date_string . (isset($end_time_string) ? ' ' . $end_time_string : '');
1273
1274
	// If some form of string input was given, override individually defined options with it
1275 View Code Duplication
	if (isset($start_string))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1276
	{
1277
		$start_string_parsed = date_parse($start_string);
1278
		if (empty($start_string_parsed['error_count']) && empty($start_string_parsed['warning_count']))
1279
		{
1280
			if ($start_string_parsed['year'] != false)
1281
			{
1282
				$start_year = $start_string_parsed['year'];
1283
				$start_month = $start_string_parsed['month'];
1284
				$start_day = $start_string_parsed['day'];
1285
			}
1286
			if ($start_string_parsed['hour'] != false)
1287
			{
1288
				$start_hour = $start_string_parsed['hour'];
1289
				$start_minute = $start_string_parsed['minute'];
1290
				$start_second = $start_string_parsed['second'];
1291
			}
1292
		}
1293
	}
1294 View Code Duplication
	if (isset($end_string))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1295
	{
1296
		$end_string_parsed = date_parse($end_string);
1297
		if (empty($end_string_parsed['error_count']) && empty($end_string_parsed['warning_count']))
1298
		{
1299
			if ($end_string_parsed['year'] != false)
1300
			{
1301
				$end_year = $end_string_parsed['year'];
1302
				$end_month = $end_string_parsed['month'];
1303
				$end_day = $end_string_parsed['day'];
1304
			}
1305
			if ($end_string_parsed['hour'] != false)
1306
			{
1307
				$end_hour = $end_string_parsed['hour'];
1308
				$end_minute = $end_string_parsed['minute'];
1309
				$end_second = $end_string_parsed['second'];
1310
			}
1311
		}
1312
	}
1313
1314
	// Validate input
1315
	$start_date_isvalid = checkdate($start_month, $start_day, $start_year);
1316
	$end_date_isvalid = checkdate($end_month, $end_day, $end_year);
1317
1318
	$start_time_isset = (isset($start_hour) && isset($start_minute) && isset($start_second));
1319
	$d = date_parse(sprintf('%02d:%02d:%02d', $start_hour, $start_minute, $start_second));
1320
	$start_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1321
1322
	$end_time_isset = (isset($end_hour) && isset($end_minute) && isset($end_second));
1323
	$d = date_parse(sprintf('%02d:%02d:%02d', $end_hour, $end_minute, $end_second));
1324
	$end_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1325
1326
	// Uh-oh...
1327
	if ($start_date_isvalid === false)
1328
	{
1329
		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...
1330
	}
1331
1332
	// Make sure we use valid values for everything
1333
	if ($end_date_isvalid === false)
1334
	{
1335
		$end_year = $start_year;
1336
		$end_month = $start_month;
1337
		$end_day = $start_day;
1338
	}
1339
1340
	if ($allday === true || $start_time_isset === false || $start_time_isvalid === false)
1341
	{
1342
		$allday = true;
1343
		$start_hour = 0;
1344
		$start_minute = 0;
1345
		$start_second = 0;
1346
	}
1347
1348
	if ($allday === true || $end_time_isvalid === false || $end_time_isset === false)
1349
	{
1350
		$end_hour = $start_hour;
1351
		$end_minute = $start_minute;
1352
		$end_second = $start_second;
1353
	}
1354
1355
	// Now create our datetime objects
1356
	$start_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1357
	$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $end_year, $end_month, $end_day, $end_hour, $end_minute, $end_second) . ' ' . $tz);
1358
1359
	// Is $end_object too early?
1360
	if ($start_object >= $end_object)
1361
	{
1362
		$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1363
		if ($span > 0)
1364
			date_add($end_object, date_interval_create_from_date_string($span . ' days'));
1365
		else
1366
			date_add($end_object, date_interval_create_from_date_string('1 hour'));
1367
	}
1368
1369
	// Is $end_object too late?
1370
	if (!empty($modSettings['cal_maxspan']))
1371
	{
1372
		$date_diff = date_diff($start_object, $end_object);
1373
		if ($date_diff->days > $modSettings['cal_maxspan'])
1374
		{
1375
			if ($modSettings['cal_maxspan'] > 1)
1376
			{
1377
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1378
				date_add($end_object, date_interval_create_from_date_string($modSettings['cal_maxspan'] . ' days'));
1379
			}
1380
			else
1381
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, '11', '59', '59') . ' ' . $tz);
1382
		}
1383
	}
1384
1385
	// Finally, make our strings
1386
	$start_date = date_format($start_object, 'Y-m-d');
1387
	$end_date = date_format($end_object, 'Y-m-d');
1388
1389
	if ($allday == true)
1390
	{
1391
		$start_time = null;
1392
		$end_time = null;
1393
		$tz = null;
1394
	}
1395
	else
1396
	{
1397
		$start_time = date_format($start_object, 'H:i:s');
1398
		$end_time = date_format($end_object, 'H:i:s');
1399
	}
1400
1401
	return array($start_date, $end_date, $start_time, $end_time, $tz);
1402
}
1403
1404
/**
1405
 * Helper function for getEventRange, getEventProperties, getNewEventDatetimes, etc.
1406
 *
1407
 * @param array $row A database row representing an event from the calendar table
1408
 * @return array An array containing the start and end date and time properties for the event
1409
 */
1410
function buildEventDatetimes($row)
1411
{
1412
	global $sourcedir, $user_info;
1413
	require_once($sourcedir . '/Subs.php');
1414
1415
	// First, try to create a better date format, ignoring the "time" elements.
1416
	if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
1417
		$date_format = '%F';
1418
	else
1419
		$date_format = $matches[0];
1420
1421
	// We want a fairly compact version of the time, but as close as possible to the user's settings.
1422 View Code Duplication
	if (preg_match('~%[HkIlMpPrRSTX](?:[^%]*%[HkIlMpPrRSTX])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1423
		$time_format = '%k:%M';
1424
	else
1425
		$time_format = str_replace(array('%I', '%H', '%S', '%r', '%R', '%T'), array('%l', '%k', '', '%l:%M %p', '%k:%M', '%l:%M'), $matches[0]);
1426
1427
	// Should this be an all day event?
1428
	$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;
1429
1430
	// How many days does this event span?
1431
	$span = 1 + date_interval_format(date_diff(date_create($row['start_date']), date_create($row['end_date'])), '%d');
1432
1433
	// We need to have a defined timezone in the steps below
1434
	if (empty($row['timezone']))
1435
		$row['timezone'] = getUserTimezone();
1436
1437
	// Get most of the standard date information for the start and end datetimes
1438
	$start = date_parse($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''));
1439
	$end = date_parse($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''));
1440
1441
	// But we also want more info, so make some DateTime objects we can use
1442
	$start_object = date_create($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''), timezone_open($row['timezone']));
1443
	$end_object = date_create($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''), timezone_open($row['timezone']));
1444
1445
	// Unix timestamps are good
1446
	$start['timestamp'] = date_format($start_object, 'U');
1447
	$end['timestamp'] = date_format($end_object, 'U');
1448
1449
	// Datetime string without timezone  (e.g. '2016-12-28 22:45:30')
1450
	$start['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1451
	$end['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1452
1453
	// ISO formatted datetime string, relative to UTC (e.g. '2016-12-29T05:45:30+00:00')
1454
	$start['iso_gmdate'] = gmdate('c', $start['timestamp']);
1455
	$end['iso_gmdate'] = gmdate('c', $end['timestamp']);
1456
1457
	// Strings showing the datetimes in the user's preferred format, relative to the user's time zone
1458
	$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...
1459
	$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...
1460
	$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...
1461
	$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...
1462
1463
	// Strings showing the datetimes in the user's preferred format, relative to the event's time zone
1464
	$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...
1465
	$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...
1466
	$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...
1467
	$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...
1468
1469
	// The time zone identifier (e.g. 'Europe/London') and abbreviation (e.g. 'GMT')
1470
	$tz = date_format($start_object, 'e');
1471
	$tz_abbrev = date_format($start_object, 'T');
1472
1473
	// There are a handful of time zones that PHP doesn't know the abbreviation for. Fix 'em if we can.
1474
	if (strspn($tz_abbrev, '+-') > 0)
1475
	{
1476
		$tz_location = timezone_location_get(timezone_open($row['timezone']));
1477
1478
		// Kazakstan
1479
		if ($tz_location['country_code'] == 'KZ')
1480
			$tz_abbrev = str_replace(array('+05', '+06'), array('AQTT', 'ALMT'), $tz_abbrev);
1481
1482
		// Russia likes to experiment with time zones
1483
		if ($tz_location['country_code'] == 'RU')
1484
		{
1485
			$msk_offset = intval($tz_abbrev) - 3;
1486
			$msk_offset = !empty($msk_offset) ? sprintf('%+0d', $msk_offset) : '';
1487
			$tz_abbrev = 'MSK' . $msk_offset;
1488
		}
1489
1490
		// Still no good? We'll just mark it as a UTC offset
1491
		if (strspn($tz_abbrev, '+-') > 0)
1492
			$tz_abbrev = 'UTC' . $tz_abbrev;
1493
	}
1494
1495
	return array($start, $end, $allday, $span, $tz, $tz_abbrev);
1496
}
1497
1498
/**
1499
 * Gets a member's selected timezone identifier directly from the database
1500
 *
1501
 * @param int $id_member The member id to look up. If not provided, the current user's id will be used.
1502
 * @return string The timezone identifier string for the user's timezone.
1503
 */
1504
function getUserTimezone($id_member = null)
1505
{
1506
	global $smcFunc, $context, $sourcedir, $user_info, $modSettings;
1507
1508
	if (is_null($id_member) && $user_info['is_guest'] == false)
1509
		$id_member = $context['user']['id'];
1510
1511 View Code Duplication
	if (isset($id_member))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1512
	{
1513
		$request = $smcFunc['db_query']('', '
1514
			SELECT timezone
1515
			FROM {db_prefix}members
1516
			WHERE id_member = {int:id_member}',
1517
			array(
1518
				'id_member' => $id_member,
1519
			)
1520
		);
1521
		list($timezone) = $smcFunc['db_fetch_row']($request);
1522
		$smcFunc['db_free_result']($request);
1523
	}
1524
1525 View Code Duplication
	if (empty($timezone) || !in_array($timezone, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1526
		$timezone = isset($modSettings['default_timezone']) ? $modSettings['default_timezone'] : date_default_timezone_get();
1527
1528
	return $timezone;
1529
}
1530
1531
/**
1532
 * Gets all of the holidays for the listing
1533
 *
1534
 * @param int $start The item to start with (for pagination purposes)
1535
 * @param int $items_per_page How many items to show on each page
1536
 * @param string $sort A string indicating how to sort the results
1537
 * @return array An array of holidays, each of which is an array containing the id, year, month, day and title of the holiday
1538
 */
1539 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...
1540
{
1541
	global $smcFunc;
1542
1543
	$request = $smcFunc['db_query']('', '
1544
		SELECT id_holiday, YEAR(event_date) AS year, MONTH(event_date) AS month, DAYOFMONTH(event_date) AS day, title
1545
		FROM {db_prefix}calendar_holidays
1546
		ORDER BY {raw:sort}
1547
		LIMIT {int:start}, {int:max}',
1548
		array(
1549
			'sort' => $sort,
1550
			'start' => $start,
1551
			'max' => $items_per_page,
1552
		)
1553
	);
1554
	$holidays = array();
1555
	while ($row = $smcFunc['db_fetch_assoc']($request))
1556
		$holidays[] = $row;
1557
	$smcFunc['db_free_result']($request);
1558
1559
	return $holidays;
1560
}
1561
1562
/**
1563
 * Helper function to get the total number of holidays
1564
 *
1565
 * @return int The total number of holidays
1566
 */
1567
function list_getNumHolidays()
1568
{
1569
	global $smcFunc;
1570
1571
	$request = $smcFunc['db_query']('', '
1572
		SELECT COUNT(*)
1573
		FROM {db_prefix}calendar_holidays',
1574
		array(
1575
		)
1576
	);
1577
	list($num_items) = $smcFunc['db_fetch_row']($request);
1578
	$smcFunc['db_free_result']($request);
1579
1580
	return (int) $num_items;
1581
}
1582
1583
/**
1584
 * Remove a holiday from the calendar
1585
 *
1586
 * @param array $holiday_ids An array of IDs of holidays to delete
1587
 */
1588
function removeHolidays($holiday_ids)
1589
{
1590
	global $smcFunc;
1591
1592
	$smcFunc['db_query']('', '
1593
		DELETE FROM {db_prefix}calendar_holidays
1594
		WHERE id_holiday IN ({array_int:id_holiday})',
1595
		array(
1596
			'id_holiday' => $holiday_ids,
1597
		)
1598
	);
1599
1600
	updateSettings(array(
1601
		'calendar_updated' => time(),
1602
	));
1603
}
1604
1605
?>
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...