Completed
Pull Request — master (#3325)
by Emanuele
11:19
created

Calendar.subs.php ➔ getCalendarGrid()   F

Complexity

Conditions 39
Paths > 20000

Size

Total Lines 148

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 39
nc 429496.7295
nop 3
dl 0
loc 148
rs 0
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
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1
15
 *
16
 */
17
18
/**
19
 * Get all birthdays within the given time range.
20
 *
21
 * What it does:
22
 *
23
 * - finds all the birthdays in the specified range of days.
24
 * - works with birthdays set for no year, or any other year, and respects month and year boundaries.
25
 *
26
 * @package Calendar
27
 * @param string $low_date inclusive, YYYY-MM-DD
28
 * @param string $high_date inclusive, YYYY-MM-DD
29
 * @return mixed[] days, each of which an array of birthday information for the context
30
 */
31
function getBirthdayRange($low_date, $high_date)
32
{
33
	$db = database();
34
35
	// We need to search for any birthday in this range, and whatever year that birthday is on.
36
	$year_low = (int) substr($low_date, 0, 4);
37
	$year_high = (int) substr($high_date, 0, 4);
38
39
	// Collect all of the birthdays for this month.  I know, it's a painful query.
40
	$result = $db->query('birthday_array', '
41
		SELECT id_member, real_name, YEAR(birthdate) AS birth_year, birthdate
42
		FROM {db_prefix}members
43
		WHERE YEAR(birthdate) != {string:year_one}
44
			AND MONTH(birthdate) != {int:no_month}
45
			AND DAYOFMONTH(birthdate) != {int:no_day}
46
			AND YEAR(birthdate) <= {int:max_year}
47
			AND (
48
				DATE_FORMAT(birthdate, {string:year_low}) BETWEEN {date:low_date} AND {date:high_date}' . ($year_low == $year_high ? '' : '
49
				OR DATE_FORMAT(birthdate, {string:year_high}) BETWEEN {date:low_date} AND {date:high_date}') . '
50
			)
51
			AND is_activated = {int:is_activated}',
52
		array(
53
			'is_activated' => 1,
54
			'no_month' => 0,
55
			'no_day' => 0,
56
			'year_one' => '0001',
57
			'year_low' => $year_low . '-%m-%d',
58
			'year_high' => $year_high . '-%m-%d',
59
			'low_date' => $low_date,
60
			'high_date' => $high_date,
61
			'max_year' => $year_high,
62
		)
63
	);
64
	$bday = array();
65
	while ($row = $db->fetch_assoc($result))
66
	{
67
		if ($year_low != $year_high)
68
			$age_year = substr($row['birthdate'], 5) < substr($high_date, 5) ? $year_high : $year_low;
69
		else
70
			$age_year = $year_low;
71
72
		$bday[$age_year . substr($row['birthdate'], 4)][] = array(
73
			'id' => $row['id_member'],
74
			'name' => $row['real_name'],
75
			'age' => $row['birth_year'] > 4 && $row['birth_year'] <= $age_year ? $age_year - $row['birth_year'] : null,
76
			'is_last' => false
77
		);
78
	}
79
	$db->free_result($result);
80
81
	// Set is_last, so the themes know when to stop placing separators.
82
	foreach ($bday as $mday => $array)
83
		$bday[$mday][count($array) - 1]['is_last'] = true;
84
85
	return $bday;
86
}
87
88
/**
89
 * Get all calendar events within the given time range.
90
 *
91
 * What it does:
92
 *
93
 * - finds all the posted calendar events within a date range.
94
 * - both the earliest_date and latest_date should be in the standard YYYY-MM-DD format.
95
 * - censors the posted event titles.
96
 * - uses the current user's permissions if use_permissions is true, otherwise it does nothing "permission specific"
97
 *
98
 * @package Calendar
99
 * @param string $low_date
100
 * @param string $high_date
101
 * @param bool $use_permissions = true
102
 * @param integer|null $limit
103
 * @return array contextual information if use_permissions is true, and an array of the data needed to build that otherwise
104
 */
105
function getEventRange($low_date, $high_date, $use_permissions = true, $limit = null)
106
{
107
	global $scripturl, $modSettings, $user_info, $context;
108
109
	$db = database();
110
111
	$low_date_time = sscanf($low_date, '%04d-%02d-%02d');
112
	$low_date_time = mktime(0, 0, 0, $low_date_time[1], $low_date_time[2], $low_date_time[0]);
113
	$high_date_time = sscanf($high_date, '%04d-%02d-%02d');
114
	$high_date_time = mktime(0, 0, 0, $high_date_time[1], $high_date_time[2], $high_date_time[0]);
115
116
	// Find all the calendar info...
117
	$result = $db->query('', '
118
		SELECT
119
			cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, cal.id_topic,
120
			cal.id_board, b.member_groups, t.id_first_msg, t.approved, b.id_board
121
		FROM {db_prefix}calendar AS cal
122
			LEFT JOIN {db_prefix}boards AS b ON (b.id_board = cal.id_board)
123
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic)
124
		WHERE cal.start_date <= {date:high_date}
125
			AND cal.end_date >= {date:low_date}' . ($use_permissions ? '
126
			AND (cal.id_board = {int:no_board_link} OR {query_wanna_see_board})' : '') . (!empty($limit) ? '
127
		LIMIT {int:limit}' : ''),
128
		array(
129
			'high_date' => $high_date,
130
			'low_date' => $low_date,
131
			'no_board_link' => 0,
132
			'limit' => $limit,
133
		)
134
	);
135
	$events = array();
136
	while ($row = $db->fetch_assoc($result))
137
	{
138
		// If the attached topic is not approved then for the moment pretend it doesn't exist
139
		if (!empty($row['id_first_msg']) && $modSettings['postmod_active'] && !$row['approved'])
140
			continue;
141
142
		// Force a censor of the title - as often these are used by others.
143
		$row['title'] = censor($row['title'], $use_permissions ? false : true);
144
145
		$start_date = sscanf($row['start_date'], '%04d-%02d-%02d');
146
		$start_date = max(mktime(0, 0, 0, $start_date[1], $start_date[2], $start_date[0]), $low_date_time);
147
		$end_date = sscanf($row['end_date'], '%04d-%02d-%02d');
148
		$end_date = min(mktime(0, 0, 0, $end_date[1], $end_date[2], $end_date[0]), $high_date_time);
149
150
		$lastDate = '';
151
		for ($date = $start_date; $date <= $end_date; $date += 86400)
152
		{
153
			// Attempt to avoid DST problems.
154
			// @todo Resolve this properly at some point.
155
			if (strftime('%Y-%m-%d', $date) == $lastDate)
156
				$date += 3601;
157
			$lastDate = strftime('%Y-%m-%d', $date);
158
159
			// If we're using permissions (calendar pages?) then just ouput normal contextual style information.
160
			if ($use_permissions)
161
				$events[strftime('%Y-%m-%d', $date)][] = array(
162
					'id' => $row['id_event'],
163
					'title' => $row['title'],
164
					'start_date' => $row['start_date'],
165
					'end_date' => $row['end_date'],
166
					'is_last' => false,
167
					'id_board' => $row['id_board'],
168
					'id_topic' => $row['id_topic'],
169
					'href' => $row['id_board'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
170
					'link' => $row['id_board'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
171
					'can_edit' => allowedTo('calendar_edit_any') || ($row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own')),
172
					'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'],
173
					'can_export' => !empty($modSettings['cal_export']) ? true : false,
174
					'export_href' => $scripturl . '?action=calendar;sa=ical;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'],
175
				);
176
			// Otherwise, this is going to be cached and the VIEWER'S permissions should apply... just put together some info.
177
			else
178
				$events[strftime('%Y-%m-%d', $date)][] = array(
179
					'id' => $row['id_event'],
180
					'title' => $row['title'],
181
					'start_date' => $row['start_date'],
182
					'end_date' => $row['end_date'],
183
					'is_last' => false,
184
					'id_board' => $row['id_board'],
185
					'id_topic' => $row['id_topic'],
186
					'href' => $row['id_topic'] == 0 ? '' : $scripturl . '?topic=' . $row['id_topic'] . '.0',
187
					'link' => $row['id_topic'] == 0 ? $row['title'] : '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['title'] . '</a>',
188
					'can_edit' => false,
189
					'can_export' => !empty($modSettings['cal_export']) ? true : false,
190
					'topic' => $row['id_topic'],
191
					'msg' => $row['id_first_msg'],
192
					'poster' => $row['id_member'],
193
					'allowed_groups' => explode(',', $row['member_groups']),
194
				);
195
		}
196
	}
197
	$db->free_result($result);
198
199
	// If we're doing normal contextual data, go through and make things clear to the templates ;).
200
	if ($use_permissions)
201
	{
202
		foreach ($events as $mday => $array)
203
			$events[$mday][count($array) - 1]['is_last'] = true;
204
	}
205
206
	return $events;
207
}
208
209
/**
210
 * Get all holidays within the given time range.
211
 *
212
 * @package Calendar
213
 * @param string $low_date YYYY-MM-DD
214
 * @param string $high_date YYYY-MM-DD
215
 * @return array an array of days, which are all arrays of holiday names.
216
 */
217
function getHolidayRange($low_date, $high_date)
218
{
219
	$db = database();
220
221
	// Get the lowest and highest dates for "all years".
222
	if (substr($low_date, 0, 4) != substr($high_date, 0, 4))
223
		$allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_dec}
224
			OR event_date BETWEEN {date:all_year_jan} AND {date:all_year_high}';
225
	else
226
		$allyear_part = 'event_date BETWEEN {date:all_year_low} AND {date:all_year_high}';
227
228
	// Find some holidays... ;).
229
	$result = $db->query('', '
230
		SELECT event_date, YEAR(event_date) AS year, title
231
		FROM {db_prefix}calendar_holidays
232
		WHERE event_date BETWEEN {date:low_date} AND {date:high_date}
233
			OR ' . $allyear_part,
234
		array(
235
			'low_date' => $low_date,
236
			'high_date' => $high_date,
237
			'all_year_low' => '0004' . substr($low_date, 4),
238
			'all_year_high' => '0004' . substr($high_date, 4),
239
			'all_year_jan' => '0004-01-01',
240
			'all_year_dec' => '0004-12-31',
241
		)
242
	);
243
	$holidays = array();
244
	while ($row = $db->fetch_assoc($result))
245
	{
246
		if (substr($low_date, 0, 4) != substr($high_date, 0, 4))
247
			$event_year = substr($row['event_date'], 5) < substr($high_date, 5) ? substr($high_date, 0, 4) : substr($low_date, 0, 4);
248
		else
249
			$event_year = substr($low_date, 0, 4);
250
251
		$holidays[$event_year . substr($row['event_date'], 4)][] = $row['title'];
252
	}
253
	$db->free_result($result);
254
255
	return $holidays;
256
}
257
258
/**
259
 * Does permission checks to see if an event can be linked to a board/topic.
260
 *
261
 * What it does:
262
 *
263
 * - checks if the current user can link the current topic to the calendar, permissions et al.
264
 * - this requires the calendar_post permission, a forum moderator, or a topic starter.
265
 * - expects the $topic and $board variables to be set.
266
 * - if the user doesn't have proper permissions, an error will be shown.
267
 *
268
 * @package Calendar
269
 * @todo pass $board, $topic and $user_info['id'] as arguments with fallback for 1.1
270
 */
271
function canLinkEvent()
272
{
273
	global $user_info, $topic, $board;
274
275
	// If you can't post, you can't link.
276
	isAllowedTo('calendar_post');
277
278
	// No board?  No topic?!?
279
	if (empty($board))
280
		throw new Elk_Exception('missing_board_id', false);
281
	if (empty($topic))
282
		throw new Elk_Exception('missing_topic_id', false);
283
284
	// Administrator, Moderator, or owner.  Period.
285
	if (!allowedTo('admin_forum') && !allowedTo('moderate_board'))
286
	{
287
		// Not admin or a moderator of this board. You better be the owner - or else.
288
		$row = topicAttribute($topic, array('id_member_started'));
289
		if (!empty($row))
290
		{
291
			// Not the owner of the topic.
292
			if ($row['id_member_started'] != $user_info['id'])
293
				throw new Elk_Exception('not_your_topic', 'user');
294
		}
295
		// Topic/Board doesn't exist.....
296
		else
297
			throw new Elk_Exception('calendar_no_topic', 'general');
298
	}
299
}
300
301
/**
302
 * Returns date information about 'today' relative to the users time offset.
303
 *
304
 * - returns an array with the current date, day, month, and year.
305
 * takes the users time offset into account.
306
 *
307
 * @package Calendar
308
 */
309
function getTodayInfo()
310
{
311
	return array(
312
		'day' => (int) strftime('%d', forum_time()),
313
		'month' => (int) strftime('%m', forum_time()),
314
		'year' => (int) strftime('%Y', forum_time()),
315
		'date' => strftime('%Y-%m-%d', forum_time()),
316
	);
317
}
318
319
/**
320
 * Provides information (link, month, year) about the previous and next month.
321
 *
322
 * @package Calendar
323
 * @param int $month
324
 * @param int $year
325
 * @param mixed[] $calendarOptions
326
 * @return array containing all the information needed to show a calendar grid for the given month
327
 */
328
function getCalendarGrid($month, $year, $calendarOptions)
329
{
330
	global $scripturl, $modSettings;
331
332
	// Eventually this is what we'll be returning.
333
	$calendarGrid = array(
334
		'week_days' => array(),
335
		'weeks' => array(),
336
		'short_day_titles' => !empty($calendarOptions['short_day_titles']),
337
		'current_month' => $month,
338
		'current_year' => $year,
339
		'show_next_prev' => !empty($calendarOptions['show_next_prev']),
340
		'show_week_links' => !empty($calendarOptions['show_week_links']),
341
		'previous_calendar' => array(
342
			'year' => $month == 1 ? $year - 1 : $year,
343
			'month' => $month == 1 ? 12 : $month - 1,
344
			'disabled' => $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year),
345
		),
346
		'next_calendar' => array(
347
			'year' => $month == 12 ? $year + 1 : $year,
348
			'month' => $month == 12 ? 1 : $month + 1,
349
			'disabled' => date('Y') + $modSettings['cal_limityear'] < ($month == 12 ? $year + 1 : $year),
350
		),
351
		'size' => isset($calendarOptions['size']) ? $calendarOptions['size'] : 'large',
352
	);
353
354
	// Get todays date.
355
	$today = getTodayInfo();
356
357
	// Get information about this month.
358
	$month_info = array(
359
		'first_day' => array(
360
			'day_of_week' => (int) strftime('%w', mktime(0, 0, 0, $month, 1, $year)),
361
			'week_num' => (int) strftime('%U', mktime(0, 0, 0, $month, 1, $year)),
362
			'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month, 1, $year)),
363
		),
364
		'last_day' => array(
365
			'day_of_month' => (int) strftime('%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)),
366
			'date' => strftime('%Y-%m-%d', mktime(0, 0, 0, $month == 12 ? 1 : $month + 1, 0, $month == 12 ? $year + 1 : $year)),
367
		),
368
		'first_day_of_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year)),
369
		'first_day_of_next_year' => (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year + 1)),
370
	);
371
372
	// The number of days the first row is shifted to the right for the starting day.
373
	$nShift = $month_info['first_day']['day_of_week'];
374
375
	$calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day'];
376
377
	// Starting any day other than Sunday means a shift...
378
	if (!empty($calendarOptions['start_day']))
379
	{
380
		$nShift -= $calendarOptions['start_day'];
381
		if ($nShift < 0)
382
			$nShift = 7 + $nShift;
383
	}
384
385
	// Number of rows required to fit the month.
386
	$nRows = floor(($month_info['last_day']['day_of_month'] + $nShift) / 7);
387
	if (($month_info['last_day']['day_of_month'] + $nShift) % 7)
388
		$nRows++;
389
390
	// Fetch the arrays for birthdays, posted events, and holidays.
391
	$bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
392
	$events = $calendarOptions['show_events'] ? getEventRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
393
	$holidays = $calendarOptions['show_holidays'] ? getHolidayRange($month_info['first_day']['date'], $month_info['last_day']['date']) : array();
394
395
	// Days of the week taking into consideration that they may want it to start on any day.
396
	$count = $calendarOptions['start_day'];
397
	for ($i = 0; $i < 7; $i++)
398
	{
399
		$calendarGrid['week_days'][] = $count;
400
		$count++;
401
		if ($count == 7)
402
			$count = 0;
403
	}
404
405
	// An adjustment value to apply to all calculated week numbers.
406
	if (!empty($calendarOptions['show_week_num']))
407
	{
408
		// If the first day of the year is a Sunday, then there is no
409
		// adjustment to be made. However, if the first day of the year is not
410
		// a Sunday, then there is a partial week at the start of the year
411
		// that needs to be accounted for.
412
		if ($calendarOptions['start_day'] === 0)
413
			$nWeekAdjust = $month_info['first_day_of_year'] === 0 ? 0 : 1;
414
		// If we are viewing the weeks, with a starting date other than Sunday,
415
		// then things get complicated! Basically, as PHP is calculating the
416
		// weeks with a Sunday starting date, we need to take this into account
417
		// and offset the whole year dependant on whether the first day in the
418
		// year is above or below our starting date. Note that we offset by
419
		// two, as some of this will get undone quite quickly by the statement
420
		// below.
421
		else
422
			$nWeekAdjust = $calendarOptions['start_day'] > $month_info['first_day_of_year'] && $month_info['first_day_of_year'] !== 0 ? 2 : 1;
423
424
		// If our week starts on a day greater than the day the month starts
425
		// on, then our week numbers will be one too high. So we need to
426
		// reduce it by one - all these thoughts of offsets makes my head
427
		// hurt...
428
		if ($month_info['first_day']['day_of_week'] < $calendarOptions['start_day'] || $month_info['first_day_of_year'] > 4)
429
			$nWeekAdjust--;
430
	}
431
	else
432
		$nWeekAdjust = 0;
433
434
	// Iterate through each week.
435
	$calendarGrid['weeks'] = array();
436
	for ($nRow = 0; $nRow < $nRows; $nRow++)
437
	{
438
		// Start off the week - and don't let it go above 52, since that's the number of weeks in a year.
439
		$calendarGrid['weeks'][$nRow] = array(
440
			'days' => array(),
441
			'number' => $month_info['first_day']['week_num'] + $nRow + $nWeekAdjust
442
		);
443
444
		// Handle the dreaded "week 53", it can happen, but only once in a blue moon ;)
445
		if ($calendarGrid['weeks'][$nRow]['number'] == 53 && $nShift != 4 && $month_info['first_day_of_next_year'] < 4)
446
			$calendarGrid['weeks'][$nRow]['number'] = 1;
447
448
		// And figure out all the days.
449
		for ($nCol = 0; $nCol < 7; $nCol++)
450
		{
451
			$nDay = ($nRow * 7) + $nCol - $nShift + 1;
452
453
			if ($nDay < 1 || $nDay > $month_info['last_day']['day_of_month'])
454
				$nDay = 0;
455
456
			$date = sprintf('%04d-%02d-%02d', $year, $month, $nDay);
457
458
			$calendarGrid['weeks'][$nRow]['days'][$nCol] = array(
459
				'day' => $nDay,
460
				'date' => $date,
461
				'is_today' => $date == $today['date'],
462
				'is_first_day' => !empty($calendarOptions['show_week_num']) && (($month_info['first_day']['day_of_week'] + $nDay - 1) % 7 == $calendarOptions['start_day']),
463
				'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
464
				'events' => !empty($events[$date]) ? $events[$date] : array(),
465
				'birthdays' => !empty($bday[$date]) ? $bday[$date] : array()
466
			);
467
		}
468
	}
469
470
	// Set the previous and the next month's links.
471
	$calendarGrid['previous_calendar']['href'] = $scripturl . '?action=calendar;year=' . $calendarGrid['previous_calendar']['year'] . ';month=' . $calendarGrid['previous_calendar']['month'];
472
	$calendarGrid['next_calendar']['href'] = $scripturl . '?action=calendar;year=' . $calendarGrid['next_calendar']['year'] . ';month=' . $calendarGrid['next_calendar']['month'];
473
474
	return $calendarGrid;
475
}
476
477
/**
478
 * Returns the information needed to show a calendar for the given week.
479
 *
480
 * @package Calendar
481
 * @param int $month
482
 * @param int $year
483
 * @param int $day
484
 * @param mixed[] $calendarOptions
485
 * @return array
486
 */
487
function getCalendarWeek($month, $year, $day, $calendarOptions)
488
{
489
	global $scripturl, $modSettings;
490
491
	// Get todays date.
492
	$today = getTodayInfo();
493
494
	// What is the actual "start date" for the passed day.
495
	$calendarOptions['start_day'] = empty($calendarOptions['start_day']) ? 0 : (int) $calendarOptions['start_day'];
496
	$day_of_week = (int) strftime('%w', mktime(0, 0, 0, $month, $day, $year));
497
	if ($day_of_week != $calendarOptions['start_day'])
498
	{
499
		// Here we offset accordingly to get things to the real start of a week.
500
		$date_diff = $day_of_week - $calendarOptions['start_day'];
501
		if ($date_diff < 0)
502
			$date_diff += 7;
503
		$new_timestamp = mktime(0, 0, 0, $month, $day, $year) - $date_diff * 86400;
504
		$day = (int) strftime('%d', $new_timestamp);
505
		$month = (int) strftime('%m', $new_timestamp);
506
		$year = (int) strftime('%Y', $new_timestamp);
507
	}
508
509
	// Now start filling in the calendar grid.
510
	$calendarGrid = array(
511
		'show_next_prev' => !empty($calendarOptions['show_next_prev']),
512
		// Previous week is easy - just step back one day.
513
		'previous_week' => array(
514
			'year' => $day == 1 ? ($month == 1 ? $year - 1 : $year) : $year,
515
			'month' => $day == 1 ? ($month == 1 ? 12 : $month - 1) : $month,
516
			'day' => $day == 1 ? 28 : $day - 1,
517
			'disabled' => $day < 7 && $modSettings['cal_minyear'] > ($month == 1 ? $year - 1 : $year),
518
		),
519
		'next_week' => array(
520
			'disabled' => $day > 25 && date('Y') + $modSettings['cal_limityear'] < ($month == 12 ? $year + 1 : $year),
521
		),
522
	);
523
524
	// The next week calculation requires a bit more work.
525
	$curTimestamp = mktime(0, 0, 0, $month, $day, $year);
526
	$nextWeekTimestamp = $curTimestamp + 604800;
527
	$calendarGrid['next_week']['day'] = (int) strftime('%d', $nextWeekTimestamp);
528
	$calendarGrid['next_week']['month'] = (int) strftime('%m', $nextWeekTimestamp);
529
	$calendarGrid['next_week']['year'] = (int) strftime('%Y', $nextWeekTimestamp);
530
531
	// Fetch the arrays for birthdays, posted events, and holidays.
532
	$startDate = strftime('%Y-%m-%d', $curTimestamp);
533
	$endDate = strftime('%Y-%m-%d', $nextWeekTimestamp);
534
	$bday = $calendarOptions['show_birthdays'] ? getBirthdayRange($startDate, $endDate) : array();
535
	$events = $calendarOptions['show_events'] ? getEventRange($startDate, $endDate) : array();
536
	$holidays = $calendarOptions['show_holidays'] ? getHolidayRange($startDate, $endDate) : array();
537
538
	// An adjustment value to apply to all calculated week numbers.
539
	if (!empty($calendarOptions['show_week_num']))
540
	{
541
		$first_day_of_year = (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year));
542
		$first_day_of_next_year = (int) strftime('%w', mktime(0, 0, 0, 1, 1, $year + 1));
543
		// this one is not used in its scope
544
		// $last_day_of_last_year = (int) strftime('%w', mktime(0, 0, 0, 12, 31, $year - 1));
545
546
		// All this is as getCalendarGrid.
547
		if ($calendarOptions['start_day'] === 0)
548
			$nWeekAdjust = $first_day_of_year === 0 && $first_day_of_year > 3 ? 0 : 1;
549
		else
550
			$nWeekAdjust = $calendarOptions['start_day'] > $first_day_of_year && $first_day_of_year !== 0 ? 2 : 1;
551
552
		$calendarGrid['week_number'] = (int) strftime('%U', mktime(0, 0, 0, $month, $day, $year)) + $nWeekAdjust;
553
554
		// If this crosses a year boundary and includes january it should be week one.
555
		if ((int) strftime('%Y', $curTimestamp + 518400) != $year && $calendarGrid['week_number'] > 53 && $first_day_of_next_year < 5)
556
			$calendarGrid['week_number'] = 1;
557
	}
558
559
	// This holds all the main data - there is at least one month!
560
	$calendarGrid['months'] = array();
561
	$lastDay = 99;
562
	$curDay = $day;
563
	$curDayOfWeek = $calendarOptions['start_day'];
564
	for ($i = 0; $i < 7; $i++)
565
	{
566
		// Have we gone into a new month (Always happens first cycle too)
567
		if ($lastDay > $curDay)
568
		{
569
			$curMonth = $lastDay == 99 ? $month : ($month == 12 ? 1 : $month + 1);
570
			$curYear = $lastDay == 99 ? $year : ($curMonth == 1 && $month == 12 ? $year + 1 : $year);
571
			$calendarGrid['months'][$curMonth] = array(
572
				'current_month' => $curMonth,
573
				'current_year' => $curYear,
574
				'days' => array(),
575
			);
576
		}
577
578
		// Add todays information to the pile!
579
		$date = sprintf('%04d-%02d-%02d', $curYear, $curMonth, $curDay);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $curMonth does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $curYear does not seem to be defined for all execution paths leading up to this point.
Loading history...
580
581
		$calendarGrid['months'][$curMonth]['days'][$curDay] = array(
582
			'day' => $curDay,
583
			'day_of_week' => $curDayOfWeek,
584
			'date' => $date,
585
			'is_today' => $date == $today['date'],
586
			'holidays' => !empty($holidays[$date]) ? $holidays[$date] : array(),
587
			'events' => !empty($events[$date]) ? $events[$date] : array(),
588
			'birthdays' => !empty($bday[$date]) ? $bday[$date] : array()
589
		);
590
591
		// Make the last day what the current day is and work out what the next day is.
592
		$lastDay = $curDay;
593
		$curTimestamp += 86400;
594
		$curDay = (int) strftime('%d', $curTimestamp);
595
596
		// Also increment the current day of the week.
597
		$curDayOfWeek = $curDayOfWeek >= 6 ? 0 : ++$curDayOfWeek;
598
	}
599
600
	// Set the previous and the next week's links.
601
	$calendarGrid['previous_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['previous_week']['year'] . ';month=' . $calendarGrid['previous_week']['month'] . ';day=' . $calendarGrid['previous_week']['day'];
602
	$calendarGrid['next_week']['href'] = $scripturl . '?action=calendar;viewweek;year=' . $calendarGrid['next_week']['year'] . ';month=' . $calendarGrid['next_week']['month'] . ';day=' . $calendarGrid['next_week']['day'];
603
604
	return $calendarGrid;
605
}
606
607
/**
608
 * Retrieve all events for the given days, independently of the users offset.
609
 *
610
 * What it does:
611
 *
612
 * - cache callback function used to retrieve the birthdays, holidays, and events between now and now + days_to_index.
613
 * - widens the search range by an extra 24 hours to support time offset shifts.
614
 * - used by the cache_getRecentEvents function to get the information needed to calculate the events taking the users time offset into account.
615
 *
616
 * @package Calendar
617
 * @param int $days_to_index
618
 * @return array
619
 */
620
function cache_getOffsetIndependentEvents($days_to_index)
621
{
622
	$low_date = strftime('%Y-%m-%d', forum_time(false) - 24 * 3600);
623
	$high_date = strftime('%Y-%m-%d', forum_time(false) + $days_to_index * 24 * 3600);
624
625
	return array(
626
		'data' => array(
627
			'holidays' => getHolidayRange($low_date, $high_date),
628
			'birthdays' => getBirthdayRange($low_date, $high_date),
629
			'events' => getEventRange($low_date, $high_date, false),
630
		),
631
		'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\']);',
632
		'expires' => time() + 3600,
633
	);
634
}
635
636
/**
637
 * cache callback function used to retrieve the upcoming birthdays, holidays, and events
638
 * within the given period, taking into account the users time offset.
639
 *
640
 * - Called from the BoardIndex to display the current day's events on the board index
641
 * - used by the board index and SSI to show the upcoming events.
642
 *
643
 * @package Calendar
644
 * @param mixed[] $eventOptions
645
 * @return array
646
 */
647
function cache_getRecentEvents($eventOptions)
648
{
649
	// With the 'static' cached data we can calculate the user-specific data.
650
	$cached_data = cache_quick_get('calendar_index', 'subs/Calendar.subs.php', 'cache_getOffsetIndependentEvents', array($eventOptions['num_days_shown']));
651
652
	// Get the information about today (from user perspective).
653
	$today = getTodayInfo();
654
655
	$return_data = array(
656
		'calendar_holidays' => array(),
657
		'calendar_birthdays' => array(),
658
		'calendar_events' => array(),
659
	);
660
661
	// Set the event span to be shown in seconds.
662
	$days_for_index = $eventOptions['num_days_shown'] * 86400;
663
664
	// Get the current member time/date.
665
	$now = forum_time();
666
667
	// Holidays between now and now + days.
668
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
669
	{
670
		if (isset($cached_data['holidays'][strftime('%Y-%m-%d', $i)]))
671
			$return_data['calendar_holidays'] = array_merge($return_data['calendar_holidays'], $cached_data['holidays'][strftime('%Y-%m-%d', $i)]);
672
	}
673
674
	// Happy Birthday, guys and gals!
675
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
676
	{
677
		$loop_date = strftime('%Y-%m-%d', $i);
678
		if (isset($cached_data['birthdays'][$loop_date]))
679
		{
680
			foreach ($cached_data['birthdays'][$loop_date] as $index => $dummy)
681
				$cached_data['birthdays'][strftime('%Y-%m-%d', $i)][$index]['is_today'] = $loop_date === $today['date'];
682
			$return_data['calendar_birthdays'] = array_merge($return_data['calendar_birthdays'], $cached_data['birthdays'][$loop_date]);
683
		}
684
	}
685
686
	$duplicates = array();
687
	for ($i = $now; $i < $now + $days_for_index; $i += 86400)
688
	{
689
		// Determine the date of the current loop step.
690
		$loop_date = strftime('%Y-%m-%d', $i);
691
692
		// No events today? Check the next day.
693
		if (empty($cached_data['events'][$loop_date]))
694
			continue;
695
696
		// Loop through all events to add a few last-minute values.
697
		foreach ($cached_data['events'][$loop_date] as $ev => $event)
698
		{
699
			// Create a shortcut variable for easier access.
700
			$this_event = &$cached_data['events'][$loop_date][$ev];
701
702
			// Skip duplicates.
703
			if (isset($duplicates[$this_event['topic'] . $this_event['title']]))
704
			{
705
				unset($cached_data['events'][$loop_date][$ev]);
706
				continue;
707
			}
708
			else
709
				$duplicates[$this_event['topic'] . $this_event['title']] = true;
710
711
			// Might be set to true afterwards, depending on the permissions.
712
			$this_event['can_edit'] = false;
713
			$this_event['is_today'] = $loop_date === $today['date'];
714
			$this_event['date'] = $loop_date;
715
		}
716
717
		if (!empty($cached_data['events'][$loop_date]))
718
			$return_data['calendar_events'] = array_merge($return_data['calendar_events'], $cached_data['events'][$loop_date]);
719
	}
720
721
	// Mark the last item so that a list separator can be used in the template.
722
	for ($i = 0, $n = count($return_data['calendar_birthdays']); $i < $n; $i++)
723
		$return_data['calendar_birthdays'][$i]['is_last'] = !isset($return_data['calendar_birthdays'][$i + 1]);
724
	for ($i = 0, $n = count($return_data['calendar_events']); $i < $n; $i++)
725
		$return_data['calendar_events'][$i]['is_last'] = !isset($return_data['calendar_events'][$i + 1]);
726
727
	return array(
728
		'data' => $return_data,
729
		'expires' => time() + 3600,
730
		'refresh_eval' => 'return \'' . strftime('%Y%m%d', forum_time(false)) . '\' != strftime(\'%Y%m%d\', forum_time(false)) || (!empty($modSettings[\'calendar_updated\']) && ' . time() . ' < $modSettings[\'calendar_updated\']);',
731
		'post_retri_eval' => '
732
			global $context, $scripturl, $user_info;
733
734
			foreach ($cache_block[\'data\'][\'calendar_events\'] as $k => $event)
735
			{
736
				// Remove events that the user may not see or wants to ignore.
737
				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\']))
738
					unset($cache_block[\'data\'][\'calendar_events\'][$k]);
739
				else
740
				{
741
					// Whether the event can be edited depends on the permissions.
742
					$cache_block[\'data\'][\'calendar_events\'][$k][\'can_edit\'] = allowedTo(\'calendar_edit_any\') || ($event[\'poster\'] == $user_info[\'id\'] && allowedTo(\'calendar_edit_own\'));
743
744
					// The added session code makes this URL not cachable.
745
					$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\'];
746
				}
747
			}
748
749
			if (empty($params[0][\'include_holidays\']))
750
				$cache_block[\'data\'][\'calendar_holidays\'] = array();
751
			if (empty($params[0][\'include_birthdays\']))
752
				$cache_block[\'data\'][\'calendar_birthdays\'] = array();
753
			if (empty($params[0][\'include_events\']))
754
				$cache_block[\'data\'][\'calendar_events\'] = array();
755
756
			$cache_block[\'data\'][\'show_calendar\'] = !empty($cache_block[\'data\'][\'calendar_holidays\']) || !empty($cache_block[\'data\'][\'calendar_birthdays\']) || !empty($cache_block[\'data\'][\'calendar_events\']);',
757
	);
758
}
759
760
/**
761
 * Get the event's poster.
762
 *
763
 * @package Calendar
764
 * @param int $event_id
765
 * @return int|bool the id of the poster or false if the event was not found
766
 */
767
function getEventPoster($event_id)
768
{
769
	$db = database();
770
771
	// A simple database query, how hard can that be?
772
	$request = $db->query('', '
773
		SELECT id_member
774
		FROM {db_prefix}calendar
775
		WHERE id_event = {int:id_event}
776
		LIMIT 1',
777
		array(
778
			'id_event' => $event_id,
779
		)
780
	);
781
782
	// No results, return false.
783
	if ($db->num_rows($request) === 0)
784
		return false;
785
786
	// Grab the results and return.
787
	list ($poster) = $db->fetch_row($request);
788
	$db->free_result($request);
789
790
	return (int) $poster;
791
}
792
793
/**
794
 * Inserts events in to the calendar
795
 *
796
 * What it does:
797
 *
798
 * - Consolidating the various INSERT statements into this function.
799
 * - inserts the passed event information into the calendar table.
800
 * - allows to either set a time span (in days) or an end_date.
801
 * - does not check any permissions of any sort.
802
 *
803
 * @package Calendar
804
 * @param mixed[] $eventOptions
805
 */
806
function insertEvent(&$eventOptions)
807
{
808
	$db = database();
809
810
	// Add special chars to the title.
811
	$eventOptions['title'] = Util::htmlspecialchars($eventOptions['title'], ENT_QUOTES);
812
813
	// Add some sanity checking to the span.
814
	$eventOptions['span'] = isset($eventOptions['span']) && $eventOptions['span'] > 0 ? (int) $eventOptions['span'] : 0;
815
816
	// Make sure the start date is in ISO order.
817
	$year = '';
818
	$month = '';
819
	$day = '';
820
	if (($num_results = sscanf($eventOptions['start_date'], '%d-%d-%d', $year, $month, $day)) !== 3)
0 ignored issues
show
Unused Code introduced by
The assignment to $num_results is dead and can be removed.
Loading history...
821
		trigger_error('insertEvent(): invalid start date format given', E_USER_ERROR);
822
823
	// Set the end date (if not yet given)
824
	if (!isset($eventOptions['end_date']))
825
		$eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, $month, $day, $year) + $eventOptions['span'] * 86400);
0 ignored issues
show
Bug introduced by
$month of type string is incompatible with the type integer expected by parameter $month of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

825
		$eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, /** @scrutinizer ignore-type */ $month, $day, $year) + $eventOptions['span'] * 86400);
Loading history...
Bug introduced by
$day of type string is incompatible with the type integer expected by parameter $day of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

825
		$eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, $month, /** @scrutinizer ignore-type */ $day, $year) + $eventOptions['span'] * 86400);
Loading history...
826
827
	// If no topic and board are given, they are not linked to a topic.
828
	$eventOptions['id_board'] = isset($eventOptions['id_board']) ? (int) $eventOptions['id_board'] : 0;
829
	$eventOptions['id_topic'] = isset($eventOptions['id_topic']) ? (int) $eventOptions['id_topic'] : 0;
830
831
	$event_columns = array(
832
		'id_board' => 'int', 'id_topic' => 'int', 'title' => 'string-60', 'id_member' => 'int',
833
		'start_date' => 'date', 'end_date' => 'date',
834
	);
835
	$event_parameters = array(
836
		$eventOptions['id_board'], $eventOptions['id_topic'], $eventOptions['title'], $eventOptions['member'],
837
		$eventOptions['start_date'], $eventOptions['end_date'],
838
	);
839
840
	call_integration_hook('integrate_create_event', array(&$eventOptions, &$event_columns, &$event_parameters));
841
842
	// Insert the event!
843
	$db->insert('',
844
		'{db_prefix}calendar',
845
		$event_columns,
846
		$event_parameters,
847
		array('id_event')
848
	);
849
850
	// Store the just inserted id_event for future reference.
851
	$eventOptions['id'] = $db->insert_id('{db_prefix}calendar', 'id_event');
852
853
	// Update the settings to show something calendarish was updated.
854
	updateSettings(array(
855
		'calendar_updated' => time(),
856
	));
857
}
858
859
/**
860
 * Modifies an event.
861
 *
862
 * - allows to either set a time span (in days) or an end_date.
863
 * - does not check any permissions of any sort.
864
 *
865
 * @package Calendar
866
 * @param int $event_id
867
 * @param mixed[] $eventOptions
868
 */
869
function modifyEvent($event_id, &$eventOptions)
870
{
871
	$db = database();
872
873
	// Properly sanitize the title.
874
	$eventOptions['title'] = Util::htmlspecialchars($eventOptions['title'], ENT_QUOTES);
875
876
	// Scan the start date for validity and get its components.
877
	$year = '';
878
	$month = '';
879
	$day = '';
880
	if (($num_results = sscanf($eventOptions['start_date'], '%d-%d-%d', $year, $month, $day)) !== 3)
0 ignored issues
show
Unused Code introduced by
The assignment to $num_results is dead and can be removed.
Loading history...
881
		trigger_error('modifyEvent(): invalid start date format given', E_USER_ERROR);
882
883
	// Default span to 0 days.
884
	$eventOptions['span'] = isset($eventOptions['span']) ? (int) $eventOptions['span'] : 0;
885
886
	// Set the end date to the start date + span (if the end date wasn't already given).
887
	if (!isset($eventOptions['end_date']))
888
		$eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, $month, $day, $year) + $eventOptions['span'] * 86400);
0 ignored issues
show
Bug introduced by
$day of type string is incompatible with the type integer expected by parameter $day of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

888
		$eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, $month, /** @scrutinizer ignore-type */ $day, $year) + $eventOptions['span'] * 86400);
Loading history...
Bug introduced by
$month of type string is incompatible with the type integer expected by parameter $month of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

888
		$eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, /** @scrutinizer ignore-type */ $month, $day, $year) + $eventOptions['span'] * 86400);
Loading history...
889
890
	$event_columns = array(
891
		'start_date' => 'start_date = {date:start_date}',
892
		'end_date' => 'end_date = {date:end_date}',
893
		'title' => 'title = SUBSTRING({string:title}, 1, 60)',
894
		'id_board' => 'id_board = {int:id_board}',
895
		'id_topic' => 'id_topic = {int:id_topic}'
896
	);
897
898
	call_integration_hook('integrate_modify_event', array($event_id, &$eventOptions, &$event_columns));
899
900
	$eventOptions['id_event'] = $event_id;
901
902
	$to_update = array();
903
	foreach ($event_columns as $key => $value)
904
		if (isset($eventOptions[$key]))
905
			$to_update[] = $value;
906
907
	if (empty($to_update))
908
		return;
909
910
	$db->query('', '
911
		UPDATE {db_prefix}calendar
912
		SET
913
			' . implode(', ', $to_update) . '
914
		WHERE id_event = {int:id_event}',
915
		$eventOptions
916
	);
917
918
	updateSettings(array(
919
		'calendar_updated' => time(),
920
	));
921
}
922
923
/**
924
 * Remove an event
925
 *
926
 * - does no permission checks.
927
 *
928
 * @package Calendar
929
 * @param int $event_id
930
 */
931
function removeEvent($event_id)
932
{
933
	$db = database();
934
935
	$db->query('', '
936
		DELETE FROM {db_prefix}calendar
937
		WHERE id_event = {int:id_event}',
938
		array(
939
			'id_event' => $event_id,
940
		)
941
	);
942
943
	call_integration_hook('integrate_remove_event', array($event_id));
944
945
	updateSettings(array(
946
		'calendar_updated' => time(),
947
	));
948
}
949
950
/**
951
 * Gets all the events properties
952
 *
953
 * @package Calendar
954
 * @param int $event_id
955
 * @param bool $calendar_only
956
 * @return array
957
 */
958
function getEventProperties($event_id, $calendar_only = false)
959
{
960
	$db = database();
961
962
	$request = $db->query('', '
963
		SELECT
964
			c.id_event, c.id_board, c.id_topic, MONTH(c.start_date) AS month,
965
			DAYOFMONTH(c.start_date) AS day, YEAR(c.start_date) AS year,
966
			(TO_DAYS(c.end_date) - TO_DAYS(c.start_date)) AS span, c.id_member, c.title' . ($calendar_only ? '' : ',
967
			t.id_first_msg, t.id_member_started,
968
			mb.real_name, m.modified_time') . '
969
		FROM {db_prefix}calendar AS c' . ($calendar_only ? '' : '
970
			LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = c.id_topic)
971
			LEFT JOIN {db_prefix}members AS mb ON (mb.id_member = t.id_member_started)
972
			LEFT JOIN {db_prefix}messages AS m ON (m.id_msg  = t.id_first_msg)') . '
973
		WHERE c.id_event = {int:id_event}
974
		LIMIT 1',
975
		array(
976
			'id_event' => $event_id,
977
		)
978
	);
979
980
	// If nothing returned, we are in poo, poo.
981
	if ($db->num_rows($request) === 0)
982
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
983
984
	$row = $db->fetch_assoc($request);
985
	$db->free_result($request);
986
987
	if ($calendar_only)
988
		$return_value = $row;
989
	else
990
	{
991
		$return_value = array(
992
			'boards' => array(),
993
			'board' => $row['id_board'],
994
			'new' => 0,
995
			'eventid' => $event_id,
996
			'year' => $row['year'],
997
			'month' => $row['month'],
998
			'day' => $row['day'],
999
			'title' => $row['title'],
1000
			'span' => 1 + $row['span'],
1001
			'member' => $row['id_member'],
1002
			'realname' => $row['real_name'],
1003
			'sequence' => $row['modified_time'],
1004
			'topic' => array(
1005
				'id' => $row['id_topic'],
1006
				'member_started' => $row['id_member_started'],
1007
				'first_msg' => $row['id_first_msg'],
1008
			),
1009
		);
1010
1011
		$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']));
1012
	}
1013
1014
	return $return_value;
1015
}
1016
1017
/**
1018
 * Fetch and event that may be linked to a topic
1019
 *
1020
 * @package Calendar
1021
 * @param int $id_topic
1022
 */
1023
function eventInfoForTopic($id_topic)
1024
{
1025
	$db = database();
1026
1027
	// Get event for this topic. If we have one.
1028
	return $db->fetchQuery('
1029
		SELECT cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, mem.real_name
1030
		FROM {db_prefix}calendar AS cal
1031
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = cal.id_member)
1032
		WHERE cal.id_topic = {int:current_topic}
1033
		ORDER BY start_date',
1034
		array(
1035
			'current_topic' => $id_topic,
1036
		)
1037
	);
1038
}
1039
1040
/**
1041
 * Gets all of the holidays for the listing
1042
 *
1043
 * @package Calendar
1044
 * @param int $start The item to start with (for pagination purposes)
1045
 * @param int $items_per_page  The number of items to show per page
1046
 * @param string $sort A string indicating how to sort the results
1047
 * @return array
1048
 */
1049
function list_getHolidays($start, $items_per_page, $sort)
1050
{
1051
	$db = database();
1052
1053
	return $db->fetchQuery('
1054
		SELECT id_holiday, YEAR(event_date) AS year, MONTH(event_date) AS month, DAYOFMONTH(event_date) AS day, title
1055
		FROM {db_prefix}calendar_holidays
1056
		ORDER BY {raw:sort}
1057
		LIMIT ' . $start . ', ' . $items_per_page,
1058
		array(
1059
			'sort' => $sort,
1060
		)
1061
	);
1062
}
1063
1064
/**
1065
 * Helper function to get the total number of holidays
1066
 *
1067
 * @package Calendar
1068
 * @return int
1069
 */
1070
function list_getNumHolidays()
1071
{
1072
	$db = database();
1073
1074
	$request = $db->query('', '
1075
		SELECT COUNT(*)
1076
		FROM {db_prefix}calendar_holidays',
1077
		array(
1078
		)
1079
	);
1080
	list ($num_items) = $db->fetch_row($request);
1081
	$db->free_result($request);
1082
1083
	return (int) $num_items;
1084
}
1085
1086
/**
1087
 * Remove a holiday from the calendar.
1088
 *
1089
 * @package Calendar
1090
 * @param int|int[] $holiday_ids An array of ids for holidays.
1091
 */
1092
function removeHolidays($holiday_ids)
1093
{
1094
	$db = database();
1095
1096
	if (!is_array($holiday_ids))
1097
		$holiday_ids = array($holiday_ids);
1098
1099
	$db->query('', '
1100
		DELETE FROM {db_prefix}calendar_holidays
1101
		WHERE id_holiday IN ({array_int:id_holiday})',
1102
		array(
1103
			'id_holiday' => $holiday_ids,
1104
		)
1105
	);
1106
1107
	updateSettings(array(
1108
		'calendar_updated' => time(),
1109
	));
1110
}
1111
1112
/**
1113
 * Updates a calendar holiday
1114
 *
1115
 * @package Calendar
1116
 * @param int $holiday
1117
 * @param int $date
1118
 * @param string $title
1119
 */
1120
function editHoliday($holiday, $date, $title)
1121
{
1122
	$db = database();
1123
1124
	$db->query('', '
1125
		UPDATE {db_prefix}calendar_holidays
1126
		SET event_date = {date:holiday_date}, title = {string:holiday_title}
1127
		WHERE id_holiday = {int:selected_holiday}',
1128
		array(
1129
			'holiday_date' => $date,
1130
			'selected_holiday' => $holiday,
1131
			'holiday_title' => $title,
1132
		)
1133
	);
1134
1135
	updateSettings(array(
1136
		'calendar_updated' => time(),
1137
	));
1138
}
1139
1140
/**
1141
 * Insert a new holiday
1142
 *
1143
 * @package Calendar
1144
 * @param int $date
1145
 * @param string $title
1146
 */
1147
function insertHoliday($date, $title)
1148
{
1149
	$db = database();
1150
1151
	$db->insert('',
1152
		'{db_prefix}calendar_holidays',
1153
		array(
1154
			'event_date' => 'date', 'title' => 'string-60',
1155
		),
1156
		array(
1157
			$date, $title,
1158
		),
1159
		array('id_holiday')
1160
	);
1161
1162
	updateSettings(array(
1163
		'calendar_updated' => time(),
1164
	));
1165
}
1166
1167
/**
1168
 * Get a specific holiday
1169
 *
1170
 * @package Calendar
1171
 * @param int $id_holiday
1172
 * @return array
1173
 */
1174
function getHoliday($id_holiday)
1175
{
1176
	$db = database();
1177
1178
	$request = $db->query('', '
1179
		SELECT id_holiday, YEAR(event_date) AS year, MONTH(event_date) AS month, DAYOFMONTH(event_date) AS day, title
1180
		FROM {db_prefix}calendar_holidays
1181
		WHERE id_holiday = {int:selected_holiday}
1182
		LIMIT 1',
1183
			array(
1184
				'selected_holiday' => $id_holiday,
1185
			)
1186
		);
1187
	while ($row = $db->fetch_assoc($request))
1188
		$holiday = array(
1189
			'id' => $row['id_holiday'],
1190
			'day' => $row['day'],
1191
			'month' => $row['month'],
1192
			'year' => $row['year'] <= 4 ? 0 : $row['year'],
1193
			'title' => $row['title']
1194
		);
1195
	$db->free_result($request);
1196
1197
	return $holiday;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $holiday does not seem to be defined for all execution paths leading up to this point.
Loading history...
1198
}
1199
1200
/**
1201
 * Puts together the content of an ical thing
1202
 *
1203
 * @param mixed[] $event - An array holding event details like:
1204
 *                  - long
1205
 *                  - year
1206
 *                  - month
1207
 *                  - day
1208
 *                  - span
1209
 *                  - realname
1210
 *                  - sequence
1211
 *                  - eventid
1212
 */
1213
function build_ical_content($event)
1214
{
1215
	global $webmaster_email, $mbname;
1216
1217
	// Check the title isn't too long - iCal requires some formatting if so.
1218
	$title = str_split($event['title'], 30);
1219
	foreach ($title as $id => $line)
1220
	{
1221
		if ($id != 0)
1222
			$title[$id] = ' ' . $title[$id];
1223
		$title[$id] .= "\n";
1224
	}
1225
1226
	// Format the dates.
1227
	$datestamp = date('Ymd\THis\Z', time());
1228
	$datestart = $event['year'] . ($event['month'] < 10 ? '0' . $event['month'] : $event['month']) . ($event['day'] < 10 ? '0' . $event['day'] : $event['day']);
1229
1230
	// Do we have a event that spans several days?
1231
	if ($event['span'] > 1)
1232
	{
1233
		$dateend = strtotime($event['year'] . '-' . ($event['month'] < 10 ? '0' . $event['month'] : $event['month']) . '-' . ($event['day'] < 10 ? '0' . $event['day'] : $event['day']));
1234
		$dateend += ($event['span'] - 1) * 86400;
1235
		$dateend = date('Ymd', $dateend);
1236
	}
1237
1238
	// This is what we will be sending later
1239
	$filecontents = '';
1240
	$filecontents .= 'BEGIN:VCALENDAR' . "\n";
1241
	$filecontents .= 'METHOD:PUBLISH' . "\n";
1242
	$filecontents .= 'PRODID:-//ElkArteCommunity//ElkArte ' . (!defined('FORUM_VERSION') ? 2.0 : strtr(FORUM_VERSION, array('ElkArte ' => ''))) . '//EN' . "\n";
1243
	$filecontents .= 'VERSION:2.0' . "\n";
1244
	$filecontents .= 'BEGIN:VEVENT' . "\n";
1245
	$filecontents .= 'ORGANIZER;CN="' . $event['realname'] . '":MAILTO:' . $webmaster_email . "\n";
1246
	$filecontents .= 'DTSTAMP:' . $datestamp . "\n";
1247
	$filecontents .= 'DTSTART;VALUE=DATE:' . $datestart . "\n";
1248
1249
	// more than one day
1250
	if ($event['span'] > 1)
1251
		$filecontents .= 'DTEND;VALUE=DATE:' . $dateend . "\n";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dateend does not seem to be defined for all execution paths leading up to this point.
Loading history...
1252
1253
	// event has changed? advance the sequence for this UID
1254
	if ($event['sequence'] > 0)
1255
		$filecontents .= 'SEQUENCE:' . $event['sequence'] . "\n";
1256
1257
	$filecontents .= 'SUMMARY:' . implode('', $title);
1258
	$filecontents .= 'UID:' . $event['eventid'] . '@' . str_replace(' ', '-', $mbname) . "\n";
1259
	$filecontents .= 'END:VEVENT' . "\n";
1260
	$filecontents .= 'END:VCALENDAR';
1261
1262
	return $filecontents;
1263
}
1264