Completed
Push — release-2.1 ( ab13ea...2ca19b )
by Mathias
10:09
created

Subs-Calendar.php ➔ getBirthdayRange()   C

Complexity

Conditions 12
Paths 52

Size

Total Lines 87
Code Lines 49

Duplication

Lines 2
Ratio 2.3 %

Importance

Changes 0
Metric Value
cc 12
eloc 49
nc 52
nop 2
dl 2
loc 87
rs 5.034
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
	if($smcFunc['db_title'] != "PostgreSQL") {
37
		// Collect all of the birthdays for this month.  I know, it's a painful query.
38
		$result = $smcFunc['db_query']('birthday_array', '
39
			SELECT id_member, real_name, YEAR(birthdate) AS birth_year, birthdate
40
			FROM {db_prefix}members
41
			WHERE YEAR(birthdate) != {string:year_one}
42
				AND MONTH(birthdate) != {int:no_month}
43
				AND DAYOFMONTH(birthdate) != {int:no_day}
44
				AND YEAR(birthdate) <= {int:max_year}
45
				AND (
46
					DATE_FORMAT(birthdate, {string:year_low}) BETWEEN {date:low_date} AND {date:high_date}' . ($year_low == $year_high ? '' : '
47
					OR DATE_FORMAT(birthdate, {string:year_high}) BETWEEN {date:low_date} AND {date:high_date}') . '
48
				)
49
				AND is_activated = {int:is_activated}',
50
			array(
51
				'is_activated' => 1,
52
				'no_month' => 0,
53
				'no_day' => 0,
54
				'year_one' => '0001',
55
				'year_low' => $year_low . '-%m-%d',
56
				'year_high' => $year_high . '-%m-%d',
57
				'low_date' => $low_date,
58
				'high_date' => $high_date,
59
				'max_year' => $year_high,
60
			)
61
		);
62
	}
63
	else
64
	{
65
		$result = $smcFunc['db_query']('birthday_array', '
66
			SELECT id_member, real_name, YEAR(birthdate) AS birth_year, birthdate
67
			FROM {db_prefix}members
68
			WHERE YEAR(birthdate) != {string:year_one}
69
				AND MONTH(birthdate) != {int:no_month}
70
				AND DAYOFMONTH(birthdate) != {int:no_day}
71
				AND (
72
					indexable_month_day(birthdate) BETWEEN indexable_month_day({date:year_low_low_date}) AND indexable_month_day({date:year_low_high_date})' . ($year_low == $year_high ? '' : '
73
					OR  indexable_month_day(birthdate) BETWEEN indexable_month_day({date:year_high_low_date}) AND indexable_month_day({date:year_high_high_date})') . '
74
				)
75
				AND is_activated = {int:is_activated}',
76
			array(
77
				'is_activated' => 1,
78
				'no_month' => 0,
79
				'no_day' => 0,
80
				'year_one' => '0001',
81
				'year_low' => $year_low . '-%m-%d',
82
				'year_high' => $year_high . '-%m-%d',
83
				'year_low_low_date' => $low_date,
84
				'year_low_high_date' => ($year_low == $year_high ? $high_date : $year_low .'-12-31'),
85
				'year_high_low_date' => ($year_low == $year_high ? $low_date : $year_high .'-01-01'),
86
				'year_high_high_date' => $high_date,
87
			)
88
		);
89
	}
90
	$bday = array();
91
	while ($row = $smcFunc['db_fetch_assoc']($result))
92
	{
93
		if ($year_low != $year_high)
94
			$age_year = substr($row['birthdate'], 5) < substr($high_date, 5) ? $year_high : $year_low;
95
		else
96
			$age_year = $year_low;
97
98
		$bday[$age_year . substr($row['birthdate'], 4)][] = array(
99
			'id' => $row['id_member'],
100
			'name' => $row['real_name'],
101
			'age' => $row['birth_year'] > 4 && $row['birth_year'] <= $age_year ? $age_year - $row['birth_year'] : null,
102
			'is_last' => false
103
		);
104
	}
105
	$smcFunc['db_free_result']($result);
106
107
	ksort($bday);
108
109
	// Set is_last, so the themes know when to stop placing separators.
110 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...
111
		$bday[$mday][count($array) - 1]['is_last'] = true;
112
113
	return $bday;
114
}
115
116
/**
117
 * Get all calendar events within the given time range.
118
 *
119
 * - finds all the posted calendar events within a date range.
120
 * - both the earliest_date and latest_date should be in the standard YYYY-MM-DD format.
121
 * - censors the posted event titles.
122
 * - uses the current user's permissions if use_permissions is true, otherwise it does nothing "permission specific"
123
 *
124
 * @param string $low_date The low end of the range, inclusive, in YYYY-MM-DD format
125
 * @param string $high_date The high end of the range, inclusive, in YYYY-MM-DD format
126
 * @param bool $use_permissions Whether to use permissions
127
 * @return array Contextual information if use_permissions is true, and an array of the data needed to build that otherwise
128
 */
129
function getEventRange($low_date, $high_date, $use_permissions = true)
130
{
131
	global $scripturl, $modSettings, $user_info, $smcFunc, $context, $sourcedir;
132
	require_once($sourcedir . '/Subs.php');
133
134
	$low_object = date_create($low_date);
135
	$high_object = date_create($high_date);
136
137
	// Find all the calendar info...
138
	$result = $smcFunc['db_query']('', '
139
		SELECT
140
			cal.id_event, cal.title, cal.id_member, cal.id_topic, cal.id_board,
141
			cal.start_date, cal.end_date, cal.start_time, cal.end_time, cal.timezone, cal.location,
142
			b.member_groups, t.id_first_msg, t.approved, b.id_board
143
		FROM {db_prefix}calendar AS cal
144
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = cal.id_board)
145
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic)
146
		WHERE cal.start_date <= {date:high_date}
147
			AND cal.end_date >= {date:low_date}' . ($use_permissions ? '
148
			AND (cal.id_board = {int:no_board_link} OR {query_wanna_see_board})' : ''),
149
		array(
150
			'high_date' => $high_date,
151
			'low_date' => $low_date,
152
			'no_board_link' => 0,
153
		)
154
	);
155
	$events = array();
156
	while ($row = $smcFunc['db_fetch_assoc']($result))
157
	{
158
		// If the attached topic is not approved then for the moment pretend it doesn't exist
159
		if (!empty($row['id_first_msg']) && $modSettings['postmod_active'] && !$row['approved'])
160
			continue;
161
162
		// Force a censor of the title - as often these are used by others.
163
		censorText($row['title'], $use_permissions ? false : true);
164
165
		// Get the various time and date properties for this event
166
		list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
167
168
		// Sanity check
169 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...
170
			continue;
171
172
		// Get set up for the loop
173
		$start_object = date_create($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''), timezone_open($tz));
174
		$end_object = date_create($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''), timezone_open($tz));
175
		date_timezone_set($start_object, timezone_open(date_default_timezone_get()));
176
		date_timezone_set($end_object, timezone_open(date_default_timezone_get()));
177
		date_time_set($start_object, 0, 0, 0);
178
		date_time_set($end_object, 0, 0, 0);
179
		$start_date_string = date_format($start_object, 'Y-m-d');
180
		$end_date_string = date_format($end_object, 'Y-m-d');
181
182
		$cal_date = ($start_object >= $low_object) ? $start_object : $low_object;
183
		while ($cal_date <= $end_object && $cal_date <= $high_object)
184
		{
185
			$starts_today = (date_format($cal_date, 'Y-m-d') == $start_date_string);
186
			$ends_today = (date_format($cal_date, 'Y-m-d') == $end_date_string);
187
188
			$eventProperties = array(
189
					'id' => $row['id_event'],
190
					'title' => $row['title'],
191
					'year' => $start['year'],
192
					'month' => $start['month'],
193
					'day' => $start['day'],
194
					'hour' => !$allday ? $start['hour'] : null,
195
					'minute' => !$allday ? $start['minute'] : null,
196
					'second' => !$allday ? $start['second'] : null,
197
					'start_date' => $row['start_date'],
198
					'start_date_local' => $start['date_local'],
199
					'start_date_orig' => $start['date_orig'],
200
					'start_time' => !$allday ? $row['start_time'] : null,
201
					'start_time_local' => !$allday ? $start['time_local'] : null,
202
					'start_time_orig' => !$allday ? $start['time_orig'] : null,
203
					'start_timestamp' => $start['timestamp'],
204
					'start_datetime' => $start['datetime'],
205
					'start_iso_gmdate' => $start['iso_gmdate'],
206
					'end_year' => $end['year'],
207
					'end_month' => $end['month'],
208
					'end_day' => $end['day'],
209
					'end_hour' => !$allday ? $end['hour'] : null,
210
					'end_minute' => !$allday ? $end['minute'] : null,
211
					'end_second' => !$allday ? $end['second'] : null,
212
					'end_date' => $row['end_date'],
213
					'end_date_local' => $end['date_local'],
214
					'end_date_orig' => $end['date_orig'],
215
					'end_time' => !$allday ? $row['end_time'] : null,
216
					'end_time_local' => !$allday ? $end['time_local'] : null,
217
					'end_time_orig' => !$allday ? $end['time_orig'] : null,
218
					'end_timestamp' => $end['timestamp'],
219
					'end_datetime' => $end['datetime'],
220
					'end_iso_gmdate' => $end['iso_gmdate'],
221
					'allday' => $allday,
222
					'tz' => !$allday ? $tz : null,
223
					'tz_abbrev' => !$allday ? $tz_abbrev : null,
224
					'span' => $span,
225
					'is_last' => false,
226
					'id_board' => $row['id_board'],
227
					'is_selected' => !empty($context['selected_event']) && $context['selected_event'] == $row['id_event'],
228
					'starts_today' => $starts_today,
229
					'ends_today' => $ends_today,
230
					'location' => $row['location'],
231
			);
232
233
			// If we're using permissions (calendar pages?) then just ouput normal contextual style information.
234
			if ($use_permissions)
235
				$events[date_format($cal_date, 'Y-m-d')][] = array_merge($eventProperties, array(
236
					'href' => $row['id_board'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
237
					'link' => $row['id_board'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
238
					'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
239
					'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'],
240
					'can_export' => !empty($modSettings['cal_export']) ? true : false,
241
					'export_href' => $scripturl . '?action=calendar;sa=ical;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
242
				));
243
			// Otherwise, this is going to be cached and the VIEWER'S permissions should apply... just put together some info.
244
			else
245
				$events[date_format($cal_date, 'Y-m-d')][] = array_merge($eventProperties, array(
246
					'href' => $row['id_topic'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
247
					'link' => $row['id_topic'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
248
					'can_edit' => false,
249
					'can_export' => !empty($modSettings['cal_export']) ? true : false,
250
					'topic' => $row['id_topic'],
251
					'msg' => $row['id_first_msg'],
252
					'poster' => $row['id_member'],
253
					'allowed_groups' => explode(',', $row['member_groups']),
254
				));
255
256
			date_add($cal_date, date_interval_create_from_date_string('1 day'));
257
		}
258
	}
259
	$smcFunc['db_free_result']($result);
260
261
	// If we're doing normal contextual data, go through and make things clear to the templates ;).
262
	if ($use_permissions)
263
	{
264 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...
265
			$events[$mday][count($array) - 1]['is_last'] = true;
266
	}
267
268
	ksort($events);
269
270
	return $events;
271
}
272
273
/**
274
 * Get all holidays within the given time range.
275
 *
276
 * @param string $low_date The low end of the range, inclusive, in YYYY-MM-DD format
277
 * @param string $high_date The high end of the range, inclusive, in YYYY-MM-DD format
278
 * @return array An array of days, which are all arrays of holiday names.
279
 */
280
function getHolidayRange($low_date, $high_date)
281
{
282
	global $smcFunc;
283
284
	// Get the lowest and highest dates for "all years".
285
	if (substr($low_date, 0, 4) != substr($high_date, 0, 4))
286
		$allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_dec}
287
			OR event_date BETWEEN {date:all_year_jan} AND {date:all_year_high}';
288
	else
289
		$allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_high}';
290
291
	// Find some holidays... ;).
292
	$result = $smcFunc['db_query']('', '
293
		SELECT event_date, YEAR(event_date) AS year, title
294
		FROM {db_prefix}calendar_holidays
295
		WHERE event_date BETWEEN {date:low_date} AND {date:high_date}
296
			OR ' . $allyear_part,
297
		array(
298
			'low_date' => $low_date,
299
			'high_date' => $high_date,
300
			'all_year_low' => '0004' . substr($low_date, 4),
301
			'all_year_high' => '0004' . substr($high_date, 4),
302
			'all_year_jan' => '0004-01-01',
303
			'all_year_dec' => '0004-12-31',
304
		)
305
	);
306
	$holidays = array();
307
	while ($row = $smcFunc['db_fetch_assoc']($result))
308
	{
309
		if (substr($low_date, 0, 4) != substr($high_date, 0, 4))
310
			$event_year = substr($row['event_date'], 5) < substr($high_date, 5) ? substr($high_date, 0, 4) : substr($low_date, 0, 4);
311
		else
312
			$event_year = substr($low_date, 0, 4);
313
314
		$holidays[$event_year . substr($row['event_date'], 4)][] = $row['title'];
315
	}
316
	$smcFunc['db_free_result']($result);
317
318
	ksort($holidays);
319
320
	return $holidays;
321
}
322
323
/**
324
 * Does permission checks to see if an event can be linked to a board/topic.
325
 * checks if the current user can link the current topic to the calendar, permissions et al.
326
 * this requires the calendar_post permission, a forum moderator, or a topic starter.
327
 * expects the $topic and $board variables to be set.
328
 * if the user doesn't have proper permissions, an error will be shown.
329
 */
330
function canLinkEvent()
331
{
332
	global $user_info, $topic, $board, $smcFunc;
333
334
	// If you can't post, you can't link.
335
	isAllowedTo('calendar_post');
336
337
	// No board?  No topic?!?
338
	if (empty($board))
339
		fatal_lang_error('missing_board_id', false);
340
	if (empty($topic))
341
		fatal_lang_error('missing_topic_id', false);
342
343
	// Administrator, Moderator, or owner.  Period.
344
	if (!allowedTo('admin_forum') && !allowedTo('moderate_board'))
345
	{
346
		// Not admin or a moderator of this board. You better be the owner - or else.
347
		$result = $smcFunc['db_query']('', '
348
			SELECT id_member_started
349
			FROM {db_prefix}topics
350
			WHERE id_topic = {int:current_topic}
351
			LIMIT 1',
352
			array(
353
				'current_topic' => $topic,
354
			)
355
		);
356
		if ($row = $smcFunc['db_fetch_assoc']($result))
357
		{
358
			// Not the owner of the topic.
359
			if ($row['id_member_started'] != $user_info['id'])
360
				fatal_lang_error('not_your_topic', 'user');
361
		}
362
		// Topic/Board doesn't exist.....
363
		else
364
			fatal_lang_error('calendar_no_topic', 'general');
365
		$smcFunc['db_free_result']($result);
366
	}
367
}
368
369
/**
370
 * Returns date information about 'today' relative to the users time offset.
371
 * returns an array with the current date, day, month, and year.
372
 * takes the users time offset into account.
373
 * @return array An array of info about today, based on forum time. Has 'day', 'month', 'year' and 'date' (in YYYY-MM-DD format)
374
 */
375
function getTodayInfo()
376
{
377
	return array(
378
		'day' => (int) strftime('%d', forum_time()),
379
		'month' => (int) strftime('%m', forum_time()),
380
		'year' => (int) strftime('%Y', forum_time()),
381
		'date' => strftime('%Y-%m-%d', forum_time()),
382
	);
383
}
384
385
/**
386
 * Provides information (link, month, year) about the previous and next month.
387
 * @param int $month The month to display
388
 * @param int $year The year
389
 * @param array $calendarOptions An array of calendar options
390
 * @param bool $is_previous Whether this is the previous month
391
 * @return array A large array containing all the information needed to show a calendar grid for the given month
392
 */
393
function getCalendarGrid($month, $year, $calendarOptions, $is_previous = false)
394
{
395
	global $scripturl, $modSettings;
396
397
	// Eventually this is what we'll be returning.
398
	$calendarGrid = array(
399
		'week_days' => array(),
400
		'weeks' => array(),
401
		'short_day_titles' => !empty($calendarOptions['short_day_titles']),
402
		'short_month_titles' => !empty($calendarOptions['short_month_titles']),
403
		'highlight' => array(
404
			'events' => !empty($calendarOptions['highlight']['events']) && !empty($calendarOptions['show_events']) ? $calendarOptions['highlight']['events'] : 0,
405
			'holidays' => !empty($calendarOptions['highlight']['holidays']) && !empty($calendarOptions['show_holidays']) ? $calendarOptions['highlight']['holidays'] : 0,
406
			'birthdays' => !empty($calendarOptions['highlight']['birthdays']) && !empty($calendarOptions['show_birthdays']) ? $calendarOptions['highlight']['birthdays'] : 0,
407
		),
408
		'current_month' => $month,
409
		'current_year' => $year,
410
		'show_next_prev' => !empty($calendarOptions['show_next_prev']),
411
		'show_week_links' => isset($calendarOptions['show_week_links']) ? $calendarOptions['show_week_links'] : 0,
412
		'previous_calendar' => array(
413
			'year' => $month == 1 ? $year - 1 : $year,
414
			'month' => $month == 1 ? 12 : $month - 1,
415
			'disabled' => $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year),
416
		),
417
		'next_calendar' => array(
418
			'year' => $month == 12 ? $year + 1 : $year,
419
			'month' => $month == 12 ? 1 : $month + 1,
420
			'disabled' => $modSettings['cal_maxyear'] < ($month == 12 ? $year + 1 : $year),
421
		),
422
		'size' => empty($modSettings['cal_display_type']) ? 'large' : 'small',
423
	);
424
425
	// Get today's date.
426
	$today = getTodayInfo();
427
428
	// Get information about this month.
429
	$month_info = array(
430
		'first_day' => array(
431
			'day_of_week' => (int) strftime('%w', mktime(0, 0, 0, $month, 1, $year)),
432
			'week_num' => (int) strftime('%U', mktime(0, 0, 0, $month, 1, $year)),
433
			'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month, 1, $year)),
434
		),
435
		'last_day' => array(
436
			'day_of_month' => (int) strftime('%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)),
437
			'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)),
438
		),
439
		'first_day_of_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year)),
440
		'first_day_of_next_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year + 1)),
441
	);
442
443
	// The number of days the first row is shifted to the right for the starting day.
444
	$nShift = $month_info['first_day']['day_of_week'];
445
446
	$calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day'];
447
448
	// Starting any day other than Sunday means a shift...
449
	if (!empty($calendarOptions['start_day']))
450
	{
451
		$nShift -= $calendarOptions['start_day'];
452
		if ($nShift < 0)
453
			$nShift = 7 + $nShift;
454
	}
455
456
	// Number of rows required to fit the month.
457
	$nRows = floor(($month_info['last_day']['day_of_month'] + $nShift) / 7);
458
	if (($month_info['last_day']['day_of_month'] + $nShift) % 7)
459
		$nRows++;
460
461
	// Fetch the arrays for birthdays, posted events, and holidays.
462
	$bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
463
	$events = $calendarOptions['show_events'] ? getEventRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
464
	$holidays = $calendarOptions['show_holidays'] ? getHolidayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
465
466
	// Days of the week taking into consideration that they may want it to start on any day.
467
	$count = $calendarOptions['start_day'];
468
	for ($i = 0; $i < 7; $i++)
469
	{
470
		$calendarGrid['week_days'][] = $count;
471
		$count++;
472
		if ($count == 7)
473
			$count = 0;
474
	}
475
476
	// Iterate through each week.
477
	$calendarGrid['weeks'] = array();
478
	for ($nRow = 0; $nRow < $nRows; $nRow++)
479
	{
480
		// Start off the week - and don't let it go above 52, since that's the number of weeks in a year.
481
		$calendarGrid['weeks'][$nRow] = array(
482
			'days' => array(),
483
		);
484
485
		// And figure out all the days.
486
		for ($nCol = 0; $nCol < 7; $nCol++)
487
		{
488
			$nDay = ($nRow * 7) + $nCol - $nShift + 1;
489
490
			if ($nDay < 1 || $nDay > $month_info['last_day']['day_of_month'])
491
				$nDay = 0;
492
493
			$date = sprintf('%04d-%02d-%02d', $year, $month, $nDay);
494
495
			$calendarGrid['weeks'][$nRow]['days'][$nCol] = array(
496
				'day' => $nDay,
497
				'date' => $date,
498
				'is_today' => $date == $today['date'],
499
				'is_first_day' => !empty($calendarOptions['show_week_num']) && (($month_info['first_day']['day_of_week'] + $nDay - 1) % 7 == $calendarOptions['start_day']),
500
				'is_first_of_month' => $nDay === 1,
501
				'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
502
				'events' => !empty($events[$date]) ? $events[$date] : array(),
503
				'birthdays' => !empty($bday[$date]) ? $bday[$date] : array(),
504
			);
505
		}
506
	}
507
508
	// What is the last day of the month?
509
	if ($is_previous === true)
510
		$calendarGrid['last_of_month'] = $month_info['last_day']['day_of_month'];
511
512
	// We'll use the shift in the template.
513
	$calendarGrid['shift'] = $nShift;
514
515
	// Set the previous and the next month's links.
516
	$calendarGrid['previous_calendar']['href'] = $scripturl . '?action=calendar;viewmonth;year=' . $calendarGrid['previous_calendar']['year'] . ';month=' . $calendarGrid['previous_calendar']['month'];
517
	$calendarGrid['next_calendar']['href'] = $scripturl . '?action=calendar;viewmonth;year=' . $calendarGrid['next_calendar']['year'] . ';month=' . $calendarGrid['next_calendar']['month'];
518
519
	return $calendarGrid;
520
}
521
522
/**
523
 * Returns the information needed to show a calendar for the given week.
524
 * @param int $month The month
525
 * @param int $year The year
526
 * @param int $day The day
527
 * @param array $calendarOptions An array of calendar options
528
 * @return array An array of information needed to display the grid for a single week on the calendar
529
 */
530
function getCalendarWeek($month, $year, $day, $calendarOptions)
531
{
532
	global $scripturl, $modSettings, $txt;
533
534
	// Get today's date.
535
	$today = getTodayInfo();
536
537
	// What is the actual "start date" for the passed day.
538
	$calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day'];
539
	$day_of_week = (int) strftime('%w', mktime(0, 0, 0, $month, $day, $year));
540
	if ($day_of_week != $calendarOptions['start_day'])
541
	{
542
		// Here we offset accordingly to get things to the real start of a week.
543
		$date_diff = $day_of_week - $calendarOptions['start_day'];
544
		if ($date_diff < 0)
545
			$date_diff += 7;
546
		$new_timestamp = mktime(0, 0, 0, $month, $day, $year) - $date_diff * 86400;
547
		$day = (int) strftime('%d', $new_timestamp);
548
		$month = (int) strftime('%m', $new_timestamp);
549
		$year = (int) strftime('%Y', $new_timestamp);
550
	}
551
552
	// Now start filling in the calendar grid.
553
	$calendarGrid = array(
554
		'show_next_prev' => !empty($calendarOptions['show_next_prev']),
555
		// Previous week is easy - just step back one day.
556
		'previous_week' => array(
557
			'year' => $day == 1 ? ($month == 1 ? $year - 1 : $year) : $year,
558
			'month' => $day == 1 ? ($month == 1 ? 12 : $month - 1) : $month,
559
			'day' => $day == 1 ? 28 : $day - 1,
560
			'disabled' => $day < 7 && $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year),
561
		),
562
		'next_week' => array(
563
			'disabled' => $day > 25 && $modSettings['cal_maxyear'] < ($month == 12 ? $year + 1 : $year),
564
		),
565
		'size' => empty($modSettings['cal_display_type']) ? 'large' : 'small',
566
	);
567
568
	// The next week calculation requires a bit more work.
569
	$curTimestamp = mktime(0, 0, 0, $month, $day, $year);
570
	$nextWeekTimestamp = $curTimestamp + 604800;
571
	$calendarGrid['next_week']['day'] = (int) strftime('%d', $nextWeekTimestamp);
572
	$calendarGrid['next_week']['month'] = (int) strftime('%m', $nextWeekTimestamp);
573
	$calendarGrid['next_week']['year'] = (int) strftime('%Y', $nextWeekTimestamp);
574
575
	// Fetch the arrays for birthdays, posted events, and holidays.
576
	$startDate = strftime('%Y-%m-%d', $curTimestamp);
577
	$endDate = strftime('%Y-%m-%d', $nextWeekTimestamp);
578
	$bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($startDate, $endDate) : array();
579
	$events = $calendarOptions['show_events'] ? getEventRange($startDate, $endDate) : array();
580
	$holidays = $calendarOptions['show_holidays'] ? getHolidayRange($startDate, $endDate) : array();
581
582
	// An adjustment value to apply to all calculated week numbers.
583
	if (!empty($calendarOptions['show_week_num']))
584
	{
585
		$timestamp = mktime(0, 0, 0, $month, $day, $year);
586
		$calendarGrid['week_title'] = sprintf($txt['calendar_week_beginning'], date('F', $timestamp), date('j', $timestamp), date('Y', $timestamp));
587
	}
588
589
	// This holds all the main data - there is at least one month!
590
	$calendarGrid['months'] = array();
591
	$lastDay = 99;
592
	$curDay = $day;
593
	$curDayOfWeek = $calendarOptions['start_day'];
594
	for ($i = 0; $i < 7; $i++)
595
	{
596
		// Have we gone into a new month (Always happens first cycle too)
597
		if ($lastDay > $curDay)
598
		{
599
			$curMonth = $lastDay == 99 ? $month : ($month == 12 ? 1 : $month + 1);
600
			$curYear = $lastDay == 99 ? $year : ($curMonth == 1 && $month == 12 ? $year + 1 : $year);
601
			$calendarGrid['months'][$curMonth] = array(
602
				'current_month' => $curMonth,
603
				'current_year' => $curYear,
604
				'days' => array(),
605
			);
606
		}
607
608
		// Add todays information to the pile!
609
		$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...
610
611
		$calendarGrid['months'][$curMonth]['days'][$curDay] = array(
612
			'day' => $curDay,
613
			'day_of_week' => $curDayOfWeek,
614
			'date' => $date,
615
			'is_today' => $date == $today['date'],
616
			'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
617
			'events' => !empty($events[$date]) ? $events[$date] : array(),
618
			'birthdays' => !empty($bday[$date]) ? $bday[$date] : array()
619
		);
620
621
		// Make the last day what the current day is and work out what the next day is.
622
		$lastDay = $curDay;
623
		$curTimestamp += 86400;
624
		$curDay = (int) strftime('%d', $curTimestamp);
625
626
		// Also increment the current day of the week.
627
		$curDayOfWeek = $curDayOfWeek >= 6 ? 0 : ++$curDayOfWeek;
628
	}
629
630
	// Set the previous and the next week's links.
631
	$calendarGrid['previous_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['previous_week']['year'] . ';month=' . $calendarGrid['previous_week']['month'] . ';day=' . $calendarGrid['previous_week']['day'];
632
	$calendarGrid['next_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['next_week']['year'] . ';month=' . $calendarGrid['next_week']['month'] . ';day=' . $calendarGrid['next_week']['day'];
633
634
	return $calendarGrid;
635
}
636
637
638
/**
639
 * Returns the information needed to show a list of upcoming events, birthdays, and holidays on the calendar.
640
 * @param int $start_date The start of a date range
641
 * @param int $end_date The end of a date range
642
 * @param array $calendarOptions An array of calendar options
643
 * @return array An array of information needed to display a list of upcoming events, etc., on the calendar
644
 */
645
function getCalendarList($start_date, $end_date, $calendarOptions)
646
{
647
	global $scripturl, $modSettings, $user_info, $txt, $context, $sourcedir;
648
	require_once($sourcedir . '/Subs.php');
649
650
	// DateTime objects make life easier
651
	$start_object = date_create($start_date);
652
	$end_object = date_create($end_date);
653
654
	$calendarGrid = array(
655
		'start_date' => $start_date,
656
		'start_year' => date_format($start_object, 'Y'),
657
		'start_month' => date_format($start_object, 'm'),
658
		'start_day' => date_format($start_object, 'd'),
659
		'end_date' => $end_date,
660
		'end_year' => date_format($end_object, 'Y'),
661
		'end_month' => date_format($end_object, 'm'),
662
		'end_day' => date_format($end_object, 'd'),
663
	);
664
665
	$calendarGrid['birthdays'] = $calendarOptions['show_birthdays'] ? getBirthdayRange($start_date, $end_date) : array();
666
	$calendarGrid['holidays'] = $calendarOptions['show_holidays'] ? getHolidayRange($start_date, $end_date) : array();
667
	$calendarGrid['events'] = $calendarOptions['show_events'] ? getEventRange($start_date, $end_date) : array();
668
669
	// Get rid of duplicate events
670
	$temp = array();
671
	foreach ($calendarGrid['events'] as $date => $date_events)
672
	{
673
		foreach ($date_events as $event_key => $event_val)
674
		{
675
			if (in_array($event_val['id'], $temp))
676
				unset($calendarGrid['events'][$date][$event_key]);
677
			else
678
				$temp[] = $event_val['id'];
679
		}
680
	}
681
682
	// Give birthdays and holidays a friendly format, without the year
683 View Code Duplication
	if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
684
		$date_format = '%b %d';
685
	else
686
		$date_format = str_replace(array('%Y', '%y', '%G', '%g', '%C', '%c', '%D'), array('', '', '', '', '', '%b %d', '%m/%d'), $matches[0]);
687
688
	foreach (array('birthdays', 'holidays') as $type)
689
	{
690
		foreach ($calendarGrid[$type] as $date => $date_content)
691
		{
692
			$date_local = preg_replace('~(?<=\s)0+(\d)~', '$1', trim(timeformat(strtotime($date), $date_format), " \t\n\r\0\x0B,./;:<>()[]{}\\|-_=+"));
0 ignored issues
show
Documentation introduced by
$date_format is of type string, but the function expects a boolean.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
693
694
			$calendarGrid[$type][$date]['date_local'] = $date_local;
695
		}
696
	}
697
698
	loadCSSFile('jquery-ui.datepicker.css', array('defer' => false), 'smf_datepicker');
699
	loadJavaScriptFile('jquery-ui.datepicker.min.js', array('defer' => true), 'smf_datepicker');
700
	loadJavaScriptFile('jquery.timepicker.min.js', array('defer' => true), 'smf_timepicker');
701
	loadJavaScriptFile('datepair.min.js', array('defer' => true), 'smf_datepair');
702
	addInlineJavaScript('
703
	$("#calendar_range .date_input").datepicker({
704
		dateFormat: "yy-mm-dd",
705
		autoSize: true,
706
		isRTL: ' . ($context['right_to_left'] ? 'true' : 'false') . ',
707
		constrainInput: true,
708
		showAnim: "",
709
		showButtonPanel: false,
710
		minDate: "' . $modSettings['cal_minyear'] . '-01-01",
711
		maxDate: "' . $modSettings['cal_maxyear'] . '-12-31",
712
		yearRange: "' . $modSettings['cal_minyear'] . ':' . $modSettings['cal_maxyear'] . '",
713
		hideIfNoPrevNext: true,
714
		monthNames: ["' . implode('", "', $txt['months_titles']) . '"],
715
		monthNamesShort: ["' . implode('", "', $txt['months_short']) . '"],
716
		dayNames: ["' . implode('", "', $txt['days']) . '"],
717
		dayNamesShort: ["' . implode('", "', $txt['days_short']) . '"],
718
		dayNamesMin: ["' . implode('", "', $txt['days_short']) . '"],
719
		prevText: "' . $txt['prev_month'] . '",
720
		nextText: "' . $txt['next_month'] . '",
721
	});
722
	var date_entry = document.getElementById("calendar_range");
723
	var date_entry_pair = new Datepair(date_entry, {
724
		dateClass: "date_input",
725
		autoclose: true,
726
		parseDate: function (el) {
727
		    var utc = new Date($(el).datepicker("getDate"));
728
		    return utc && new Date(utc.getTime() + (utc.getTimezoneOffset() * 60000));
729
		},
730
		updateDate: function (el, v) {
731
		    $(el).datepicker("setDate", new Date(v.getTime() - (v.getTimezoneOffset() * 60000)));
732
		}
733
	});
734
	', true);
735
736
	return $calendarGrid;
737
}
738
739
/**
740
 * Retrieve all events for the given days, independently of the users offset.
741
 * cache callback function used to retrieve the birthdays, holidays, and events between now and now + days_to_index.
742
 * widens the search range by an extra 24 hours to support time offset shifts.
743
 * used by the cache_getRecentEvents function to get the information needed to calculate the events taking the users time offset into account.
744
 *
745
 * @param int $days_to_index How many days' worth of info to index
746
 * @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
747
 */
748
function cache_getOffsetIndependentEvents($days_to_index)
749
{
750
	$low_date = strftime('%Y-%m-%d', forum_time(false) - 24 * 3600);
751
	$high_date = strftime('%Y-%m-%d', forum_time(false) + $days_to_index * 24 * 3600);
752
753
	return array(
754
		'data' => array(
755
			'holidays' => getHolidayRange($low_date, $high_date),
756
			'birthdays' => getBirthdayRange($low_date, $high_date),
757
			'events' => getEventRange($low_date, $high_date, false),
758
		),
759
		'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\']);',
760
		'expires' => time() + 3600,
761
	);
762
}
763
764
/**
765
 * cache callback function used to retrieve the upcoming birthdays, holidays, and events within the given period, taking into account the users time offset.
766
 * Called from the BoardIndex to display the current day's events on the board index
767
 * used by the board index and SSI to show the upcoming events.
768
 * @param array $eventOptions An array of event options. Only 'num_days_shown' is used here
769
 * @return array An array containing the info that was cached as well as a few other relevant things
770
 */
771
function cache_getRecentEvents($eventOptions)
772
{
773
	// With the 'static' cached data we can calculate the user-specific data.
774
	$cached_data = cache_quick_get('calendar_index', 'Subs-Calendar.php', 'cache_getOffsetIndependentEvents', array($eventOptions['num_days_shown']));
775
776
	// Get the information about today (from user perspective).
777
	$today = getTodayInfo();
778
779
	$return_data = array(
780
		'calendar_holidays' => array(),
781
		'calendar_birthdays' => array(),
782
		'calendar_events' => array(),
783
	);
784
785
	// Set the event span to be shown in seconds.
786
	$days_for_index = $eventOptions['num_days_shown'] * 86400;
787
788
	// Get the current member time/date.
789
	$now = forum_time();
790
791
	// Holidays between now and now + days.
792
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
793
	{
794
		if (isset($cached_data['holidays'][strftime('%Y-%m-%d', $i)]))
795
			$return_data['calendar_holidays'] = array_merge($return_data['calendar_holidays'], $cached_data['holidays'][strftime('%Y-%m-%d', $i)]);
796
	}
797
798
	// Happy Birthday, guys and gals!
799
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
800
	{
801
		$loop_date = strftime('%Y-%m-%d', $i);
802
		if (isset($cached_data['birthdays'][$loop_date]))
803
		{
804
			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...
805
				$cached_data['birthdays'][strftime('%Y-%m-%d', $i)][$index]['is_today'] = $loop_date === $today['date'];
806
			$return_data['calendar_birthdays'] = array_merge($return_data['calendar_birthdays'], $cached_data['birthdays'][$loop_date]);
807
		}
808
	}
809
810
	$duplicates = array();
811
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
812
	{
813
		// Determine the date of the current loop step.
814
		$loop_date = strftime('%Y-%m-%d', $i);
815
816
		// No events today? Check the next day.
817
		if (empty($cached_data['events'][$loop_date]))
818
			continue;
819
820
		// Loop through all events to add a few last-minute values.
821
		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...
822
		{
823
			// Create a shortcut variable for easier access.
824
			$this_event = &$cached_data['events'][$loop_date][$ev];
825
826
			// Skip duplicates.
827
			if (isset($duplicates[$this_event['topic'] . $this_event['title']]))
828
			{
829
				unset($cached_data['events'][$loop_date][$ev]);
830
				continue;
831
			}
832
			else
833
				$duplicates[$this_event['topic'] . $this_event['title']] = true;
834
835
			// Might be set to true afterwards, depending on the permissions.
836
			$this_event['can_edit'] = false;
837
			$this_event['is_today'] = $loop_date === $today['date'];
838
			$this_event['date'] = $loop_date;
839
		}
840
841
		if (!empty($cached_data['events'][$loop_date]))
842
			$return_data['calendar_events'] = array_merge($return_data['calendar_events'], $cached_data['events'][$loop_date]);
843
	}
844
845
	// Mark the last item so that a list separator can be used in the template.
846 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...
847
		$return_data['calendar_birthdays'][$i]['is_last'] = !isset($return_data['calendar_birthdays'][$i + 1]);
848 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...
849
		$return_data['calendar_events'][$i]['is_last'] = !isset($return_data['calendar_events'][$i + 1]);
850
851
	return array(
852
		'data' => $return_data,
853
		'expires' => time() + 3600,
854
		'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\']);',
855
		'post_retri_eval' => '
856
			global $context, $scripturl, $user_info;
857
858
			foreach ($cache_block[\'data\'][\'calendar_events\'] as $k => $event)
859
			{
860
				// Remove events that the user may not see or wants to ignore.
861
				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\']))
862
					unset($cache_block[\'data\'][\'calendar_events\'][$k]);
863
				else
864
				{
865
					// Whether the event can be edited depends on the permissions.
866
					$cache_block[\'data\'][\'calendar_events\'][$k][\'can_edit\'] = allowedTo(\'calendar_edit_any\') || ($event[\'poster\'] == $user_info[\'id\'] && allowedTo(\'calendar_edit_own\'));
867
868
					// The added session code makes this URL not cachable.
869
					$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\'];
870
				}
871
			}
872
873
			if (empty($params[0][\'include_holidays\']))
874
				$cache_block[\'data\'][\'calendar_holidays\'] = array();
875
			if (empty($params[0][\'include_birthdays\']))
876
				$cache_block[\'data\'][\'calendar_birthdays\'] = array();
877
			if (empty($params[0][\'include_events\']))
878
				$cache_block[\'data\'][\'calendar_events\'] = array();
879
880
			$cache_block[\'data\'][\'show_calendar\'] = !empty($cache_block[\'data\'][\'calendar_holidays\']) || !empty($cache_block[\'data\'][\'calendar_birthdays\']) || !empty($cache_block[\'data\'][\'calendar_events\']);',
881
	);
882
}
883
884
/**
885
 * Makes sure the calendar post is valid.
886
 */
887
function validateEventPost()
888
{
889
	global $modSettings, $smcFunc;
890
891
	if (!isset($_POST['deleteevent']))
892
	{
893
		// The 2.1 way
894
		if (isset($_POST['start_date']))
895
		{
896
			$d = date_parse($_POST['start_date']);
897 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...
898
				fatal_lang_error('invalid_date', false);
899
			if (empty($d['year']))
900
				fatal_lang_error('event_year_missing', false);
901
			if (empty($d['month']))
902
				fatal_lang_error('event_month_missing', false);
903
		}
904
		elseif (isset($_POST['start_datetime']))
905
		{
906
			$d = date_parse($_POST['start_datetime']);
907 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...
908
				fatal_lang_error('invalid_date', false);
909
			if (empty($d['year']))
910
				fatal_lang_error('event_year_missing', false);
911
			if (empty($d['month']))
912
				fatal_lang_error('event_month_missing', false);
913
		}
914
		// The 2.0 way
915
		else
916
		{
917
			// No month?  No year?
918
			if (!isset($_POST['month']))
919
				fatal_lang_error('event_month_missing', false);
920
			if (!isset($_POST['year']))
921
				fatal_lang_error('event_year_missing', false);
922
923
			// Check the month and year...
924
			if ($_POST['month'] < 1 || $_POST['month'] > 12)
925
				fatal_lang_error('invalid_month', false);
926 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...
927
				fatal_lang_error('invalid_year', false);
928
		}
929
	}
930
931
	// Make sure they're allowed to post...
932
	isAllowedTo('calendar_post');
933
934
	// If they want to us to calculate an end date, make sure it will fit in an acceptable range.
935
	if (isset($_POST['span']))
936
	{
937
		if (($_POST['span'] < 1) || (!empty($modSettings['cal_maxspan']) && $_POST['span'] > $modSettings['cal_maxspan']))
938
			fatal_lang_error('invalid_days_numb', false);
939
	}
940
941
	// There is no need to validate the following values if we are just deleting the event.
942
	if (!isset($_POST['deleteevent']))
943
	{
944
		// If we're doing things the 2.0 way, check the day
945
		if (empty($_POST['start_date']) && empty($_POST['start_datetime']))
946
		{
947
			// No day?
948
			if (!isset($_POST['day']))
949
				fatal_lang_error('event_day_missing', false);
950
951
			// Bad day?
952
			if (!checkdate($_POST['month'], $_POST['day'], $_POST['year']))
953
				fatal_lang_error('invalid_date', false);
954
		}
955
956
		if (!isset($_POST['evtitle']) && !isset($_POST['subject']))
957
			fatal_lang_error('event_title_missing', false);
958
		elseif (!isset($_POST['evtitle']))
959
			$_POST['evtitle'] = $_POST['subject'];
960
961
		// No title?
962
		if ($smcFunc['htmltrim']($_POST['evtitle']) === '')
963
			fatal_lang_error('no_event_title', false);
964 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...
965
			$_POST['evtitle'] = $smcFunc['substr']($_POST['evtitle'], 0, 100);
966
		$_POST['evtitle'] = str_replace(';', '', $_POST['evtitle']);
967
	}
968
}
969
970
/**
971
 * Get the event's poster.
972
 *
973
 * @param int $event_id The ID of the event
974
 * @return int|bool The ID of the poster or false if the event was not found
975
 */
976 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...
977
{
978
	global $smcFunc;
979
980
	// A simple database query, how hard can that be?
981
	$request = $smcFunc['db_query']('', '
982
		SELECT id_member
983
		FROM {db_prefix}calendar
984
		WHERE id_event = {int:id_event}
985
		LIMIT 1',
986
		array(
987
			'id_event' => $event_id,
988
		)
989
	);
990
991
	// No results, return false.
992
	if ($smcFunc['db_num_rows'] === 0)
993
		return false;
994
995
	// Grab the results and return.
996
	list ($poster) = $smcFunc['db_fetch_row']($request);
997
	$smcFunc['db_free_result']($request);
998
	return (int) $poster;
999
}
1000
1001
/**
1002
 * Consolidating the various INSERT statements into this function.
1003
 * Inserts the passed event information into the calendar table.
1004
 * Allows to either set a time span (in days) or an end_date.
1005
 * Does not check any permissions of any sort.
1006
 *
1007
 * @param array $eventOptions An array of event options ('title', 'span', 'start_date', 'end_date', etc.)
1008
 */
1009
function insertEvent(&$eventOptions)
1010
{
1011
	global $smcFunc, $context;
1012
1013
	// Add special chars to the title.
1014
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
1015
1016
	$eventOptions['location'] = isset($eventOptions['location']) ? $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES) : '';
1017
1018
	// Set the start and end dates and times
1019
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
1020
1021
	// If no topic and board are given, they are not linked to a topic.
1022
	$eventOptions['board'] = isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0;
1023
	$eventOptions['topic'] = isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0;
1024
1025
	$event_columns = array(
1026
		'id_board' => 'int', 'id_topic' => 'int', 'title' => 'string-60', 'id_member' => 'int',
1027
		'start_date' => 'date', 'end_date' => 'date', 'location' => 'string-255',
1028
	);
1029
	$event_parameters = array(
1030
		$eventOptions['board'], $eventOptions['topic'], $eventOptions['title'], $eventOptions['member'],
1031
		$start_date, $end_date, $eventOptions['location'],
1032
	);
1033 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...
1034
	{
1035
		$event_columns['start_time'] = 'time';
1036
		$event_parameters[] = $start_time;
1037
		$event_columns['end_time'] = 'time';
1038
		$event_parameters[] = $end_time;
1039
		$event_columns['timezone'] = 'string';
1040
		$event_parameters[] = $tz;
1041
	}
1042
1043
	call_integration_hook('integrate_create_event', array(&$eventOptions, &$event_columns, &$event_parameters));
1044
1045
	// Insert the event!
1046
	$eventOptions['id'] = $smcFunc['db_insert']('',
1047
		'{db_prefix}calendar',
1048
		$event_columns,
1049
		$event_parameters,
1050
		array('id_event'),
1051
		1
1052
	);
1053
1054
	// If this isn't tied to a topic, we need to notify people about it.
1055
	if (empty($eventOptions['topic']))
1056
	{
1057
		$smcFunc['db_insert']('insert',
1058
			'{db_prefix}background_tasks',
1059
			array('task_file' => 'string', 'task_class' => 'string', 'task_data' => 'string', 'claimed_time' => 'int'),
1060
			array('$sourcedir/tasks/EventNew-Notify.php', 'EventNew_Notify_Background', json_encode(array(
1061
				'event_title' => $eventOptions['title'],
1062
				'event_id' => $eventOptions['id'],
1063
				'sender_id' => $eventOptions['member'],
1064
				'sender_name' => $eventOptions['member'] == $context['user']['id'] ? $context['user']['name'] : '',
1065
				'time' => time(),
1066
			)), 0),
1067
			array('id_task')
1068
		);
1069
	}
1070
1071
	// Update the settings to show something calendar-ish was updated.
1072
	updateSettings(array(
1073
		'calendar_updated' => time(),
1074
	));
1075
}
1076
1077
/**
1078
 * modifies an event.
1079
 * allows to either set a time span (in days) or an end_date.
1080
 * does not check any permissions of any sort.
1081
 *
1082
 * @param int $event_id The ID of the event
1083
 * @param array $eventOptions An array of event information
1084
 */
1085
function modifyEvent($event_id, &$eventOptions)
1086
{
1087
	global $smcFunc;
1088
1089
	// Properly sanitize the title and location
1090
	$eventOptions['title'] = $smcFunc['htmlspecialchars']($eventOptions['title'], ENT_QUOTES);
1091
	$eventOptions['location'] = $smcFunc['htmlspecialchars']($eventOptions['location'], ENT_QUOTES);
1092
1093
	// Set the new start and end dates and times
1094
	list($start_date, $end_date, $start_time, $end_time, $tz) = setEventStartEnd($eventOptions);
1095
1096
	$event_columns = array(
1097
		'start_date' => '{date:start_date}',
1098
		'end_date' => '{date:end_date}',
1099
		'title' => 'SUBSTRING({string:title}, 1, 60)',
1100
		'id_board' => '{int:id_board}',
1101
		'id_topic' => '{int:id_topic}',
1102
		'location' => 'SUBSTRING({string:location}, 1, 255)',
1103
	);
1104
	$event_parameters = array(
1105
		'start_date' => $start_date,
1106
		'end_date' => $end_date,
1107
		'title' => $eventOptions['title'],
1108
		'location' => $eventOptions['location'],
1109
		'id_board' => isset($eventOptions['board']) ? (int) $eventOptions['board'] : 0,
1110
		'id_topic' => isset($eventOptions['topic']) ? (int) $eventOptions['topic'] : 0,
1111
	);
1112 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...
1113
	{
1114
		$event_columns['start_time'] = '{time:start_time}';
1115
		$event_parameters['start_time'] = $start_time;
1116
		$event_columns['end_time'] = '{time:end_time}';
1117
		$event_parameters['end_time'] = $end_time;
1118
		$event_columns['timezone'] = '{string:timezone}';
1119
		$event_parameters['timezone'] = $tz;
1120
	}
1121
1122
	// This is to prevent hooks to modify the id of the event
1123
	$real_event_id = $event_id;
1124
	call_integration_hook('integrate_modify_event', array($event_id, &$eventOptions, &$event_columns, &$event_parameters));
1125
1126
	$column_clauses = array();
1127
	foreach ($event_columns as $col => $crit)
1128
		$column_clauses[] = $col . ' = ' . $crit;
1129
1130
	$smcFunc['db_query']('', '
1131
		UPDATE {db_prefix}calendar
1132
		SET
1133
			' . implode(', ', $column_clauses) . '
1134
		WHERE id_event = {int:id_event}',
1135
		array_merge(
1136
			$event_parameters,
1137
			array(
1138
				'id_event' => $real_event_id
1139
			)
1140
		)
1141
	);
1142
1143
	if (empty($start_time) || empty($end_time) || empty($tz) || !in_array($tz, timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1144
	{
1145
		$smcFunc['db_query']('', '
1146
			UPDATE {db_prefix}calendar
1147
			SET start_time = NULL, end_time = NULL, timezone = NULL
1148
			WHERE id_event = {int:id_event}',
1149
			array(
1150
				'id_event' => $real_event_id
1151
			)
1152
		);
1153
	}
1154
1155
	updateSettings(array(
1156
		'calendar_updated' => time(),
1157
	));
1158
}
1159
1160
/**
1161
 * Remove an event
1162
 * removes an event.
1163
 * does no permission checks.
1164
 *
1165
 * @param int $event_id The ID of the event to remove
1166
 */
1167
function removeEvent($event_id)
1168
{
1169
	global $smcFunc;
1170
1171
	$smcFunc['db_query']('', '
1172
		DELETE FROM {db_prefix}calendar
1173
		WHERE id_event = {int:id_event}',
1174
		array(
1175
			'id_event' => $event_id,
1176
		)
1177
	);
1178
1179
	call_integration_hook('integrate_remove_event', array($event_id));
1180
1181
	updateSettings(array(
1182
		'calendar_updated' => time(),
1183
	));
1184
}
1185
1186
/**
1187
 * Gets all the events properties
1188
 *
1189
 * @param int $event_id The ID of the event
1190
 * @return array An array of event information
1191
 */
1192
function getEventProperties($event_id)
1193
{
1194
	global $smcFunc;
1195
1196
	$request = $smcFunc['db_query']('', '
1197
		SELECT
1198
			c.id_event, c.id_board, c.id_topic, c.id_member, c.title,
1199
			c.start_date, c.end_date, c.start_time, c.end_time, c.timezone, c.location,
1200
			t.id_first_msg, t.id_member_started,
1201
			mb.real_name, m.modified_time
1202
		FROM {db_prefix}calendar AS c
1203
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = c.id_topic)
1204
			LEFT JOIN {db_prefix}members AS mb ON (mb.id_member = t.id_member_started)
1205
			LEFT JOIN {db_prefix}messages AS m ON (m.id_msg  = t.id_first_msg)
1206
		WHERE c.id_event = {int:id_event}',
1207
		array(
1208
			'id_event' => $event_id,
1209
		)
1210
	);
1211
1212
	// If nothing returned, we are in poo, poo.
1213
	if ($smcFunc['db_num_rows']($request) === 0)
1214
		return false;
1215
1216
	$row = $smcFunc['db_fetch_assoc']($request);
1217
	$smcFunc['db_free_result']($request);
1218
1219
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1220
1221
	// Sanity check
1222 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...
1223
		return false;
1224
1225
	$return_value = array(
1226
		'boards' => array(),
1227
		'board' => $row['id_board'],
1228
		'new' => 0,
1229
		'eventid' => $event_id,
1230
		'year' => $start['year'],
1231
		'month' => $start['month'],
1232
		'day' => $start['day'],
1233
		'hour' => !$allday ? $start['hour'] : null,
1234
		'minute' => !$allday ? $start['minute'] : null,
1235
		'second' => !$allday ? $start['second'] : null,
1236
		'start_date' => $row['start_date'],
1237
		'start_date_local' => $start['date_local'],
1238
		'start_date_orig' => $start['date_orig'],
1239
		'start_time' => !$allday ? $row['start_time'] : null,
1240
		'start_time_local' => !$allday ? $start['time_local'] : null,
1241
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1242
		'start_timestamp' => $start['timestamp'],
1243
		'start_datetime' => $start['datetime'],
1244
		'start_iso_gmdate' => $start['iso_gmdate'],
1245
		'end_year' => $end['year'],
1246
		'end_month' => $end['month'],
1247
		'end_day' => $end['day'],
1248
		'end_hour' => !$allday ? $end['hour'] : null,
1249
		'end_minute' => !$allday ? $end['minute'] : null,
1250
		'end_second' => !$allday ? $end['second'] : null,
1251
		'end_date' => $row['end_date'],
1252
		'end_date_local' => $end['date_local'],
1253
		'end_date_orig' => $end['date_orig'],
1254
		'end_time' => !$allday ? $row['end_time'] : null,
1255
		'end_time_local' => !$allday ? $end['time_local'] : null,
1256
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1257
		'end_timestamp' => $end['timestamp'],
1258
		'end_datetime' => $end['datetime'],
1259
		'end_iso_gmdate' => $end['iso_gmdate'],
1260
		'allday' => $allday,
1261
		'tz' => !$allday ? $tz : null,
1262
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1263
		'span' => $span,
1264
		'title' => $row['title'],
1265
		'location' => $row['location'],
1266
		'member' => $row['id_member'],
1267
		'realname' => $row['real_name'],
1268
		'sequence' => $row['modified_time'],
1269
		'topic' => array(
1270
			'id' => $row['id_topic'],
1271
			'member_started' => $row['id_member_started'],
1272
			'first_msg' => $row['id_first_msg'],
1273
		),
1274
	);
1275
1276
	$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']));
1277
1278
	return $return_value;
1279
}
1280
1281
/**
1282
 * Gets an initial set of date and time values for creating a new event.
1283
 *
1284
 * @return array An array containing an initial set of date and time values for an event.
1285
 */
1286
function getNewEventDatetimes()
1287
{
1288
	// Ensure setEventStartEnd() has something to work with
1289
	$now = date_create();
1290
	$_POST['year'] = !empty($_POST['year']) ? $_POST['year'] : date_format($now, 'Y');
1291
	$_POST['month'] = !empty($_POST['month']) ? $_POST['month'] : date_format($now, 'm');
1292
	$_POST['day'] = !empty($_POST['day']) ? $_POST['day'] : date_format($now, 'd');
1293
	$_POST['hour'] = !empty($_POST['hour']) ? $_POST['hour'] : date_format($now, 'H');
1294
	$_POST['minute'] = !empty($_POST['minute']) ? $_POST['minute'] : date_format($now, 'i');
1295
	$_POST['second'] = !empty($_POST['second']) ? $_POST['second'] : date_format($now, 's');
1296
1297
	// Set the basic values for the new event
1298
	$row_keys = array('start_date', 'end_date', 'start_time', 'end_time', 'timezone');
1299
	$row = array_combine($row_keys, setEventStartEnd());
1300
1301
	// And now set the full suite of values
1302
	list($start, $end, $allday, $span, $tz, $tz_abbrev) = buildEventDatetimes($row);
1303
1304
	// Default theme only uses some of this info, but others might want it all
1305
	$eventProperties = array(
1306
		'year' => $start['year'],
1307
		'month' => $start['month'],
1308
		'day' => $start['day'],
1309
		'hour' => !$allday ? $start['hour'] : null,
1310
		'minute' => !$allday ? $start['minute'] : null,
1311
		'second' => !$allday ? $start['second'] : null,
1312
		'start_date' => $row['start_date'],
1313
		'start_date_local' => $start['date_local'],
1314
		'start_date_orig' => $start['date_orig'],
1315
		'start_time' => !$allday ? $row['start_time'] : null,
1316
		'start_time_local' => !$allday ? $start['time_local'] : null,
1317
		'start_time_orig' => !$allday ? $start['time_orig'] : null,
1318
		'start_timestamp' => $start['timestamp'],
1319
		'start_datetime' => $start['datetime'],
1320
		'start_iso_gmdate' => $start['iso_gmdate'],
1321
		'end_year' => $end['year'],
1322
		'end_month' => $end['month'],
1323
		'end_day' => $end['day'],
1324
		'end_hour' => !$allday ? $end['hour'] : null,
1325
		'end_minute' => !$allday ? $end['minute'] : null,
1326
		'end_second' => !$allday ? $end['second'] : null,
1327
		'end_date' => $row['end_date'],
1328
		'end_date_local' => $end['date_local'],
1329
		'end_date_orig' => $end['date_orig'],
1330
		'end_time' => !$allday ? $row['end_time'] : null,
1331
		'end_time_local' => !$allday ? $end['time_local'] : null,
1332
		'end_time_orig' => !$allday ? $end['time_orig'] : null,
1333
		'end_timestamp' => $end['timestamp'],
1334
		'end_datetime' => $end['datetime'],
1335
		'end_iso_gmdate' => $end['iso_gmdate'],
1336
		'allday' => $allday,
1337
		'tz' => !$allday ? $tz : null,
1338
		'tz_abbrev' => !$allday ? $tz_abbrev : null,
1339
		'span' => $span,
1340
	);
1341
1342
	return $eventProperties;
1343
}
1344
1345
/**
1346
 * Set the start and end dates and times for a posted event for insertion into the database.
1347
 * Validates all date and times given to it.
1348
 * Makes sure events do not exceed the maximum allowed duration (if any).
1349
 * If passed an array that defines any time or date parameters, they will be used. Otherwise, gets the values from $_POST.
1350
 *
1351
 * @param array $eventOptions An array of optional time and date parameters (span, start_year, end_month, etc., etc.)
1352
 * @return array An array containing $start_date, $end_date, $start_time, $end_time
1353
 */
1354
function setEventStartEnd($eventOptions = array())
1355
{
1356
	global $modSettings, $user_info;
1357
1358
	// Set $span, in case we need it
1359
	$span = isset($eventOptions['span']) ? $eventOptions['span'] : (isset($_POST['span']) ? $_POST['span'] : 0);
1360
	if ($span > 0)
1361
		$span = !empty($modSettings['cal_maxspan']) ? min($modSettings['cal_maxspan'], $span - 1) : $span - 1;
1362
1363
	// Define the timezone for this event, falling back to the default if not provided
1364
	if (!empty($eventOptions['tz']) && in_array($eventOptions['tz'], timezone_identifiers_list(DateTimeZone::ALL_WITH_BC)))
1365
		$tz = $eventOptions['tz'];
1366 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...
1367
		$tz = $_POST['tz'];
1368
	else
1369
		$tz = getUserTimezone();
1370
1371
	// Is this supposed to be an all day event, or should it have specific start and end times?
1372
	if (isset($eventOptions['allday']))
1373
		$allday = $eventOptions['allday'];
1374
	elseif (empty($_POST['allday']))
1375
		$allday = false;
1376
	else
1377
		$allday = true;
1378
1379
	// Input might come as individual parameters...
1380
	$start_year = isset($eventOptions['year']) ? $eventOptions['year'] : (isset($_POST['year']) ? $_POST['year'] : null);
1381
	$start_month = isset($eventOptions['month']) ? $eventOptions['month'] : (isset($_POST['month']) ? $_POST['month'] : null);
1382
	$start_day = isset($eventOptions['day']) ? $eventOptions['day'] : (isset($_POST['day']) ? $_POST['day'] : null);
1383
	$start_hour = isset($eventOptions['hour']) ? $eventOptions['hour'] : (isset($_POST['hour']) ? $_POST['hour'] : null);
1384
	$start_minute = isset($eventOptions['minute']) ? $eventOptions['minute'] : (isset($_POST['minute']) ? $_POST['minute'] : null);
1385
	$start_second = isset($eventOptions['second']) ? $eventOptions['second'] : (isset($_POST['second']) ? $_POST['second'] : null);
1386
	$end_year = isset($eventOptions['end_year']) ? $eventOptions['end_year'] : (isset($_POST['end_year']) ? $_POST['end_year'] : null);
1387
	$end_month = isset($eventOptions['end_month']) ? $eventOptions['end_month'] : (isset($_POST['end_month']) ? $_POST['end_month'] : null);
1388
	$end_day = isset($eventOptions['end_day']) ? $eventOptions['end_day'] : (isset($_POST['end_day']) ? $_POST['end_day'] : null);
1389
	$end_hour = isset($eventOptions['end_hour']) ? $eventOptions['end_hour'] : (isset($_POST['end_hour']) ? $_POST['end_hour'] : null);
1390
	$end_minute = isset($eventOptions['end_minute']) ? $eventOptions['end_minute'] : (isset($_POST['end_minute']) ? $_POST['end_minute'] : null);
1391
	$end_second = isset($eventOptions['end_second']) ? $eventOptions['end_second'] : (isset($_POST['end_second']) ? $_POST['end_second'] : null);
1392
1393
	// ... or as datetime strings ...
1394
	$start_string = isset($eventOptions['start_datetime']) ? $eventOptions['start_datetime'] : (isset($_POST['start_datetime']) ? $_POST['start_datetime'] : null);
1395
	$end_string = isset($eventOptions['end_datetime']) ? $eventOptions['end_datetime'] : (isset($_POST['end_datetime']) ? $_POST['end_datetime'] : null);
1396
1397
	// ... or as date strings and time strings.
1398
	$start_date_string = isset($eventOptions['start_date']) ? $eventOptions['start_date'] : (isset($_POST['start_date']) ? $_POST['start_date'] : null);
1399
	$start_time_string = isset($eventOptions['start_time']) ? $eventOptions['start_time'] : (isset($_POST['start_time']) ? $_POST['start_time'] : null);
1400
	$end_date_string = isset($eventOptions['end_date']) ? $eventOptions['end_date'] : (isset($_POST['end_date']) ? $_POST['end_date'] : null);
1401
	$end_time_string = isset($eventOptions['end_time']) ? $eventOptions['end_time'] : (isset($_POST['end_time']) ? $_POST['end_time'] : null);
1402
1403
	// If the date and time were given in separate strings, combine them
1404
	if (empty($start_string) && isset($start_date_string))
1405
		$start_string = $start_date_string . (isset($start_time_string) ? ' ' . $start_time_string : '');
1406
	if (empty($end_string) && isset($end_date_string))
1407
		$end_string = $end_date_string . (isset($end_time_string) ? ' ' . $end_time_string : '');
1408
1409
	// If some form of string input was given, override individually defined options with it
1410 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...
1411
	{
1412
		$start_string_parsed = date_parse($start_string);
1413
		if (empty($start_string_parsed['error_count']) && empty($start_string_parsed['warning_count']))
1414
		{
1415
			if ($start_string_parsed['year'] != false)
1416
			{
1417
				$start_year = $start_string_parsed['year'];
1418
				$start_month = $start_string_parsed['month'];
1419
				$start_day = $start_string_parsed['day'];
1420
			}
1421
			if ($start_string_parsed['hour'] != false)
1422
			{
1423
				$start_hour = $start_string_parsed['hour'];
1424
				$start_minute = $start_string_parsed['minute'];
1425
				$start_second = $start_string_parsed['second'];
1426
			}
1427
		}
1428
	}
1429 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...
1430
	{
1431
		$end_string_parsed = date_parse($end_string);
1432
		if (empty($end_string_parsed['error_count']) && empty($end_string_parsed['warning_count']))
1433
		{
1434
			if ($end_string_parsed['year'] != false)
1435
			{
1436
				$end_year = $end_string_parsed['year'];
1437
				$end_month = $end_string_parsed['month'];
1438
				$end_day = $end_string_parsed['day'];
1439
			}
1440
			if ($end_string_parsed['hour'] != false)
1441
			{
1442
				$end_hour = $end_string_parsed['hour'];
1443
				$end_minute = $end_string_parsed['minute'];
1444
				$end_second = $end_string_parsed['second'];
1445
			}
1446
		}
1447
	}
1448
1449
	// Validate input
1450
	$start_date_isvalid = checkdate($start_month, $start_day, $start_year);
1451
	$end_date_isvalid = checkdate($end_month, $end_day, $end_year);
1452
1453
	$start_time_isset = (isset($start_hour) && isset($start_minute) && isset($start_second));
1454
	$d = date_parse(sprintf('%02d:%02d:%02d', $start_hour, $start_minute, $start_second));
1455
	$start_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1456
1457
	$end_time_isset = (isset($end_hour) && isset($end_minute) && isset($end_second));
1458
	$d = date_parse(sprintf('%02d:%02d:%02d', $end_hour, $end_minute, $end_second));
1459
	$end_time_isvalid = ($d['error_count'] == 0 && $d['warning_count'] == 0) ? true : false;
1460
1461
	// Uh-oh...
1462
	if ($start_date_isvalid === false)
1463
	{
1464
		fatal_lang_error('invalid_date', false);
1465
	}
1466
1467
	// Make sure we use valid values for everything
1468
	if ($end_date_isvalid === false)
1469
	{
1470
		$end_year = $start_year;
1471
		$end_month = $start_month;
1472
		$end_day = $start_day;
1473
	}
1474
1475
	if ($allday === true || $start_time_isset === false || $start_time_isvalid === false)
1476
	{
1477
		$allday = true;
1478
		$start_hour = 0;
1479
		$start_minute = 0;
1480
		$start_second = 0;
1481
	}
1482
1483
	if ($allday === true || $end_time_isvalid === false || $end_time_isset === false)
1484
	{
1485
		$end_hour = $start_hour;
1486
		$end_minute = $start_minute;
1487
		$end_second = $start_second;
1488
	}
1489
1490
	// Now create our datetime objects
1491
	$start_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1492
	$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $end_year, $end_month, $end_day, $end_hour, $end_minute, $end_second) . ' ' . $tz);
1493
1494
	// Is $end_object too early?
1495
	if ($start_object >= $end_object)
1496
	{
1497
		$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1498
		if ($span > 0)
1499
			date_add($end_object, date_interval_create_from_date_string($span . ' days'));
1500
		else
1501
			date_add($end_object, date_interval_create_from_date_string('1 hour'));
1502
	}
1503
1504
	// Is $end_object too late?
1505
	if (!empty($modSettings['cal_maxspan']))
1506
	{
1507
		$date_diff = date_diff($start_object, $end_object);
1508
		if ($date_diff->days > $modSettings['cal_maxspan'])
1509
		{
1510
			if ($modSettings['cal_maxspan'] > 1)
1511
			{
1512
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, $start_hour, $start_minute, $start_second) . ' ' . $tz);
1513
				date_add($end_object, date_interval_create_from_date_string($modSettings['cal_maxspan'] . ' days'));
1514
			}
1515
			else
1516
				$end_object = date_create(sprintf('%04d-%02d-%02d %02d:%02d:%02d', $start_year, $start_month, $start_day, '11', '59', '59') . ' ' . $tz);
1517
		}
1518
	}
1519
1520
	// Finally, make our strings
1521
	$start_date = date_format($start_object, 'Y-m-d');
1522
	$end_date = date_format($end_object, 'Y-m-d');
1523
1524
	if ($allday == true)
1525
	{
1526
		$start_time = null;
1527
		$end_time = null;
1528
		$tz = null;
1529
	}
1530
	else
1531
	{
1532
		$start_time = date_format($start_object, 'H:i:s');
1533
		$end_time = date_format($end_object, 'H:i:s');
1534
	}
1535
1536
	return array($start_date, $end_date, $start_time, $end_time, $tz);
1537
}
1538
1539
/**
1540
 * Helper function for getEventRange, getEventProperties, getNewEventDatetimes, etc.
1541
 *
1542
 * @param array $row A database row representing an event from the calendar table
1543
 * @return array An array containing the start and end date and time properties for the event
1544
 */
1545
function buildEventDatetimes($row)
1546
{
1547
	global $sourcedir, $user_info;
1548
	require_once($sourcedir . '/Subs.php');
1549
1550
	// First, try to create a better date format, ignoring the "time" elements.
1551
	if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
1552
		$date_format = '%F';
1553
	else
1554
		$date_format = $matches[0];
1555
1556
	// We want a fairly compact version of the time, but as close as possible to the user's settings.
1557 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...
1558
		$time_format = '%k:%M';
1559
	else
1560
		$time_format = str_replace(array('%I', '%H', '%S', '%r', '%R', '%T'), array('%l', '%k', '', '%l:%M %p', '%k:%M', '%l:%M'), $matches[0]);
1561
1562
	// Should this be an all day event?
1563
	$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;
1564
1565
	// How many days does this event span?
1566
	$span = 1 + date_interval_format(date_diff(date_create($row['start_date']), date_create($row['end_date'])), '%d');
1567
1568
	// We need to have a defined timezone in the steps below
1569
	if (empty($row['timezone']))
1570
		$row['timezone'] = getUserTimezone();
1571
1572
	// Get most of the standard date information for the start and end datetimes
1573
	$start = date_parse($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''));
1574
	$end = date_parse($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''));
1575
1576
	// But we also want more info, so make some DateTime objects we can use
1577
	$start_object = date_create($row['start_date'] . (!$allday ? ' ' . $row['start_time'] : ''), timezone_open($row['timezone']));
1578
	$end_object = date_create($row['end_date'] . (!$allday ? ' ' . $row['end_time'] : ''), timezone_open($row['timezone']));
1579
1580
	// Unix timestamps are good
1581
	$start['timestamp'] = date_format($start_object, 'U');
1582
	$end['timestamp'] = date_format($end_object, 'U');
1583
1584
	// Datetime string without timezone  (e.g. '2016-12-28 22:45:30')
1585
	$start['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1586
	$end['datetime'] = date_format($start_object, 'Y-m-d H:i:s');
1587
1588
	// ISO formatted datetime string, relative to UTC (e.g. '2016-12-29T05:45:30+00:00')
1589
	$start['iso_gmdate'] = gmdate('c', $start['timestamp']);
1590
	$end['iso_gmdate'] = gmdate('c', $end['timestamp']);
1591
1592
	// Strings showing the datetimes in the user's preferred format, relative to the user's time zone
1593
	$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...
1594
	$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...
1595
	$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...
1596
	$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...
1597
1598
	// Strings showing the datetimes in the user's preferred format, relative to the event's time zone
1599
	$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...
1600
	$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...
1601
	$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...
1602
	$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...
1603
1604
	// The time zone identifier (e.g. 'Europe/London') and abbreviation (e.g. 'GMT')
1605
	$tz = date_format($start_object, 'e');
1606
	$tz_abbrev = date_format($start_object, 'T');
1607
1608
	// There are a handful of time zones that PHP doesn't know the abbreviation for. Fix 'em if we can.
1609
	if (strspn($tz_abbrev, '+-') > 0)
1610
	{
1611
		$tz_location = timezone_location_get(timezone_open($row['timezone']));
1612
1613
		// Kazakstan
1614
		if ($tz_location['country_code'] == 'KZ')
1615
			$tz_abbrev = str_replace(array('+05', '+06'), array('AQTT', 'ALMT'), $tz_abbrev);
1616
1617
		// Russia likes to experiment with time zones
1618
		if ($tz_location['country_code'] == 'RU')
1619
		{
1620
			$msk_offset = intval($tz_abbrev) - 3;
1621
			$msk_offset = !empty($msk_offset) ? sprintf('%+0d', $msk_offset) : '';
1622
			$tz_abbrev = 'MSK' . $msk_offset;
1623
		}
1624
1625
		// Still no good? We'll just mark it as a UTC offset
1626
		if (strspn($tz_abbrev, '+-') > 0)
1627
			$tz_abbrev = 'UTC' . $tz_abbrev;
1628
	}
1629
1630
	return array($start, $end, $allday, $span, $tz, $tz_abbrev);
1631
}
1632
1633
/**
1634
 * Gets a member's selected timezone identifier directly from the database
1635
 *
1636
 * @param int $id_member The member id to look up. If not provided, the current user's id will be used.
1637
 * @return string The timezone identifier string for the user's timezone.
1638
 */
1639
function getUserTimezone($id_member = null)
1640
{
1641
	global $smcFunc, $context, $sourcedir, $user_info, $modSettings;
1642
1643
	if (is_null($id_member) && $user_info['is_guest'] == false)
1644
		$id_member = $context['user']['id'];
1645
1646 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...
1647
	{
1648
		$request = $smcFunc['db_query']('', '
1649
			SELECT timezone
1650
			FROM {db_prefix}members
1651
			WHERE id_member = {int:id_member}',
1652
			array(
1653
				'id_member' => $id_member,
1654
			)
1655
		);
1656
		list($timezone) = $smcFunc['db_fetch_row']($request);
1657
		$smcFunc['db_free_result']($request);
1658
	}
1659
1660 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...
1661
		$timezone = isset($modSettings['default_timezone']) ? $modSettings['default_timezone'] : date_default_timezone_get();
1662
1663
	return $timezone;
1664
}
1665
1666
/**
1667
 * Gets all of the holidays for the listing
1668
 *
1669
 * @param int $start The item to start with (for pagination purposes)
1670
 * @param int $items_per_page How many items to show on each page
1671
 * @param string $sort A string indicating how to sort the results
1672
 * @return array An array of holidays, each of which is an array containing the id, year, month, day and title of the holiday
1673
 */
1674 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...
1675
{
1676
	global $smcFunc;
1677
1678
	$request = $smcFunc['db_query']('', '
1679
		SELECT id_holiday, YEAR(event_date) AS year, MONTH(event_date) AS month, DAYOFMONTH(event_date) AS day, title
1680
		FROM {db_prefix}calendar_holidays
1681
		ORDER BY {raw:sort}
1682
		LIMIT {int:start}, {int:max}',
1683
		array(
1684
			'sort' => $sort,
1685
			'start' => $start,
1686
			'max' => $items_per_page,
1687
		)
1688
	);
1689
	$holidays = array();
1690
	while ($row = $smcFunc['db_fetch_assoc']($request))
1691
		$holidays[] = $row;
1692
	$smcFunc['db_free_result']($request);
1693
1694
	return $holidays;
1695
}
1696
1697
/**
1698
 * Helper function to get the total number of holidays
1699
 *
1700
 * @return int The total number of holidays
1701
 */
1702
function list_getNumHolidays()
1703
{
1704
	global $smcFunc;
1705
1706
	$request = $smcFunc['db_query']('', '
1707
		SELECT COUNT(*)
1708
		FROM {db_prefix}calendar_holidays',
1709
		array(
1710
		)
1711
	);
1712
	list($num_items) = $smcFunc['db_fetch_row']($request);
1713
	$smcFunc['db_free_result']($request);
1714
1715
	return (int) $num_items;
1716
}
1717
1718
/**
1719
 * Remove a holiday from the calendar
1720
 *
1721
 * @param array $holiday_ids An array of IDs of holidays to delete
1722
 */
1723
function removeHolidays($holiday_ids)
1724
{
1725
	global $smcFunc;
1726
1727
	$smcFunc['db_query']('', '
1728
		DELETE FROM {db_prefix}calendar_holidays
1729
		WHERE id_holiday IN ({array_int:id_holiday})',
1730
		array(
1731
			'id_holiday' => $holiday_ids,
1732
		)
1733
	);
1734
1735
	updateSettings(array(
1736
		'calendar_updated' => time(),
1737
	));
1738
}
1739
1740
?>
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...