Calendar::action_ical()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 61
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 28
dl 0
loc 61
rs 9.1608
c 0
b 0
f 0
cc 5
nc 5
nop 0
ccs 0
cts 24
cp 0
crap 30

How to fix   Long Method   

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 has only one real task, showing the calendar.
5
 * Original module by Aaron O'Neil - [email protected]
6
 *
7
 * @package   ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
10
 *
11
 * This file contains code covered by:
12
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
13
 *
14
 * @version 2.0 dev
15
 *
16
 */
17
18
namespace ElkArte\Controller;
19
20
use ElkArte\AbstractController;
21
use ElkArte\CalendarEvent;
22
use ElkArte\EventManager;
23
use ElkArte\Exceptions\Exception;
24
use ElkArte\Helper\Util;
25
use ElkArte\Http\Headers;
26
use ElkArte\User;
27
28
/**
29
 * Displays the calendar for the site and provides for its navigation
30
 */
31
class Calendar extends AbstractController
32
{
33
	/**
34
	 * Default action handler for requests on the calendar
35
	 *
36
	 * @see AbstractController::action_index
37 2
	 */
38
	public function action_index()
39
	{
40 2
		// when you don't know what you're doing... we know! :P
41
		$this->action_calendar();
42
	}
43
44
	/**
45
	 * Show the calendar.
46
	 *
47
	 * - It loads the specified month's events, holidays, and birthdays.
48
	 * - It requires the calendar_view permission.
49
	 * - It depends on the cal_enabled setting, and many of the other cal_ settings.
50
	 * - It uses the calendar_start_day theme option. (Monday/Sunday)
51
	 * - It goes to the month and year passed in 'month' and 'year' by get or post.
52
	 * - It is accessed through ?action=calendar.
53
	 *
54
	 * @uses the main sub template in the Calendar template.
55 2
	 */
56
	public function action_calendar()
57 2
	{
58
		global $txt, $context, $modSettings, $options;
59
60 2
		// Permissions, permissions, permissions.
61
		isAllowedTo('calendar_view');
62
63 2
		// This is gonna be needed...
64
		theme()->getTemplates()->load('Calendar');
65
66 2
		// You can't do anything if the calendar is off.
67
		if (empty($modSettings['cal_enabled']))
68 2
		{
69
			throw new Exception('calendar_off', false);
70
		}
71
72
		if (empty($modSettings['cal_limityear']))
73
		{
74
			$modSettings['cal_limityear'] = 20;
75
		}
76
77
		// Set the page title to mention the calendar ;).
78
		$context['page_title'] = $txt['calendar'];
79
		$context['sub_template'] = 'show_calendar';
80
81
		// Is this a week view?
82
		$context['view_week'] = isset($_GET['viewweek']);
83
		$context['cal_minyear'] = $modSettings['cal_minyear'];
84
		$context['cal_maxyear'] = (int) date('Y') + (int) $modSettings['cal_limityear'];
85
86
		// Don't let search engines index weekly calendar pages.
87
		if ($context['view_week'])
88
		{
89
			$context['robot_no_index'] = true;
90
		}
91
92
		// Get the current day of month...
93
		require_once(SUBSDIR . '/Calendar.subs.php');
94
		$today = getTodayInfo();
95
96
		// If the month and year are not passed in, use today's date as a starting point.
97
		$curPage = [
98
			'day' => isset($_REQUEST['day']) ? (int) $_REQUEST['day'] : $today['day'],
99
			'month' => isset($_REQUEST['month']) ? (int) $_REQUEST['month'] : $today['month'],
100
			'year' => isset($_REQUEST['year']) ? (int) $_REQUEST['year'] : $today['year']
101
		];
102
103
		// Make sure the year and month are in valid ranges.
104
		if ($curPage['month'] < 1 || $curPage['month'] > 12)
105
		{
106
			throw new Exception('invalid_month', false);
107
		}
108
109
		if ($curPage['year'] < $context['cal_minyear'] || $curPage['year'] > $context['cal_maxyear'])
110
		{
111
			throw new Exception('invalid_year', false);
112
		}
113
114
		// If we have a day clean that too.
115
		if ($context['view_week'] && ($curPage['day'] > 31 || !mktime(0, 0, 0, $curPage['month'], $curPage['day'], $curPage['year'])))
116
		{
117
			throw new Exception('invalid_day', false);
118
		}
119
120
		// Load all the context information needed to show the calendar grid.
121
		$calendarOptions = array(
122
			'start_day' => empty($options['calendar_start_day']) ? 0 : $options['calendar_start_day'],
123
			'show_birthdays' => in_array((int) $modSettings['cal_showbdays'], array(1, 2)),
124
			'show_events' => in_array((int) $modSettings['cal_showevents'], array(1, 2)),
125
			'show_holidays' => in_array((int) $modSettings['cal_showholidays'], array(1, 2)),
126
			'show_week_num' => true,
127
			'short_day_titles' => false,
128
			'show_next_prev' => true,
129
			'show_week_links' => true,
130
			'size' => 'large',
131
		);
132
133
		// Load up the main view.
134
		if ($context['view_week'])
135
		{
136
			$context['calendar_grid_main'] = getCalendarWeek($curPage['month'], $curPage['year'], $curPage['day'], $calendarOptions);
137
		}
138
		else
139
		{
140
			$context['calendar_grid_main'] = getCalendarGrid($curPage['month'], $curPage['year'], $calendarOptions);
141
		}
142
143
		// Load up the previous and next months.
144
		$calendarOptions['show_birthdays'] = $calendarOptions['show_events'] = $calendarOptions['show_holidays'] = false;
145
		$calendarOptions['short_day_titles'] = true;
146
		$calendarOptions['show_next_prev'] = false;
147
		$calendarOptions['show_week_links'] = false;
148
		$calendarOptions['size'] = 'small';
149
		$context['calendar_grid_current'] = getCalendarGrid($curPage['month'], $curPage['year'], $calendarOptions);
150
151
		// Only show previous month if it isn't pre-January of the min-year
152
		if ($context['calendar_grid_current']['previous_calendar']['year'] > $context['cal_minyear'] || $curPage['month'] != 1)
153
		{
154
			$context['calendar_grid_prev'] = getCalendarGrid($context['calendar_grid_current']['previous_calendar']['month'], $context['calendar_grid_current']['previous_calendar']['year'], $calendarOptions);
155
		}
156
157
		// Only show next month if it isn't post-December of the max-year
158
		if ($context['calendar_grid_current']['next_calendar']['year'] < $context['cal_maxyear'] || $curPage['month'] != 12)
159
		{
160
			$context['calendar_grid_next'] = getCalendarGrid($context['calendar_grid_current']['next_calendar']['month'], $context['calendar_grid_current']['next_calendar']['year'], $calendarOptions);
161
		}
162
163
		// Basic template stuff.
164
		$context['can_post'] = allowedTo('calendar_post');
165
		$context['current_day'] = $curPage['day'];
166
		$context['current_month'] = $curPage['month'];
167
		$context['current_year'] = $curPage['year'];
168
		$context['show_all_birthdays'] = isset($_GET['showbd']);
169
170
		// Set the page title to mention the month or week, too
171
		$context['page_title'] .= ' - ' . ($context['view_week'] ? sprintf($txt['calendar_week_title'], $context['calendar_grid_main']['week_number'], ($context['calendar_grid_main']['week_number'] == 53 ? $context['current_year'] - 1 : $context['current_year'])) : $txt['months'][$context['current_month']] . ' ' . $context['current_year']);
172
173
		// Load up the breadcrumbs!
174
		$context['breadcrumbs'][] = [
175
			'url' => getUrl('action', ['action' => 'calendar']),
176
			'name' => $txt['calendar']
177
		];
178
179
		// Add the current month to the breadcrumbs.
180
		$context['breadcrumbs'][] = [
181
			'url' => getUrl('action', ['action' => 'calendar', 'year' => $context['current_year'], 'month' => $context['current_month']]),
182
			'name' => $txt['months'][$context['current_month']] . ' ' . $context['current_year']
183
		];
184
185
		// If applicable, add the current week to the breadcrumbs.
186
		if ($context['view_week'])
187
		{
188
			$context['breadcrumbs'][] = [
189
				'url' => getUrl('action', ['action' => 'calendar', 'year' => $context['current_year'], 'month' => $context['current_month'], 'day' => $context['current_day'], 'viewweek']),
190
				'name' => $txt['calendar_week'] . ' ' . $context['calendar_grid_main']['week_number']
191
			];
192
		}
193
194
		// Build the calendar button array.
195
		$context['calendar_buttons'] = array(
196
			'post_event' => array(
197
				'test' => 'can_post',
198
				'text' => 'calendar_post_event',
199
				'lang' => true,
200
				'url' => getUrl('action', ['action' => 'calendar', 'sa' => 'post', 'year' => $context['current_year'], 'month' => $context['current_month'], '{session_data}'])
201
			),
202
		);
203
204
		// Allow mods to add additional buttons here
205
		call_integration_hook('integrate_calendar_buttons');
206
	}
207
208
	/**
209
	 * This function processes posting/editing/deleting a calendar event.
210
	 *
211
	 *  - Calls action_post() function if event is linked to a post.
212
	 *  - Calls insertEvent() to insert the event if not linked to post.
213
	 *  - It requires the calendar_post permission to use.
214
	 *  - It is accessed with ?action=calendar;sa=post.
215
	 *
216
	 * @uses the event_post sub template in the Calendar template.
217
	 */
218
	public function action_post()
219
	{
220
		global $context, $txt, $modSettings;
221
222
		// You need to view what you're doing :P
223
		isAllowedTo('calendar_view');
224
225
		// Well - can they post?
226
		isAllowedTo('calendar_post');
227
228
		// Cast this for safety...
229
		$event_id = isset($_REQUEST['eventid']) ? (int) $_REQUEST['eventid'] : null;
230
231
		// Submitting?
232
		if (isset($_POST[$context['session_var']], $event_id))
233
		{
234
			return $this->action_save();
235
		}
236
237
		// If we are not enabled... we are not enabled.
238
		if (empty($modSettings['cal_allow_unlinked']) && empty($event_id))
239
		{
240
			$_REQUEST['calendar'] = 1;
241
242
			return $this->_returnToPost();
243
		}
244
245
		$event = new CalendarEvent($event_id, $modSettings);
246
247
		$context['event'] = $event->load($_REQUEST, $this->user->id);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
248
249
		if ($event->isNew())
250
		{
251
			// Get list of boards that can be posted in.
252
			$boards = boardsAllowedTo('post_new');
253
			if (empty($boards))
254
			{
255
				throw new Exception('cannot_post_new', 'permission');
256
			}
257
258
			// Load the list of boards and categories in the context.
259
			require_once(SUBSDIR . '/Boards.subs.php');
260
			$boardListOptions = [
261
				'included_boards' => in_array(0, $boards) ? null : $boards,
262
				'not_redirection' => true,
263
				'selected_board' => $modSettings['cal_defaultboard'],
264
			];
265
			$context += getBoardList($boardListOptions);
266
		}
267
268
		// Template, sub template, etc.
269
		theme()->getTemplates()->load('Calendar');
270
		$context['sub_template'] = 'unlinked_event_post';
271
272
		$modSettings['cal_limityear'] = empty($modSettings['cal_limityear']) ? 20 : (int) $modSettings['cal_limityear'];
273
274
		$context['cal_minyear'] = $modSettings['cal_minyear'];
275
		$context['cal_maxyear'] = (int) date('Y') + $modSettings['cal_limityear'];
276
		$context['page_title'] = $event->isNew() ? $txt['calendar_edit'] : $txt['calendar_post_event'];
277
		$context['breadcrumbs'][] = [
278
			'name' => $context['page_title'],
279
		];
280
	}
281
282
	/**
283
	 * Takes care of the saving process.
284
	 */
285
	public function action_save()
286
	{
287
		global $modSettings;
288
289
		checkSession();
290
291
		// Cast this for safety...
292
		$event_id = $this->_req->get('eventid', 'intval');
293
294
		$event = new CalendarEvent($event_id, $modSettings);
295
296
		// Validate the post...
297
		$save_data = [];
298
		if (!isset($_POST['link_to_board']))
299
		{
300
			try
301
			{
302
				$save_data = $event->validate($_POST);
303
			}
304
			catch (Exception $e)
305
			{
306
				// @todo This should really integrate into $post_errors.
307
				$e->fatalLangError();
308
			}
309
		}
310
311
		// If you're not allowed to edit any events, you have to be the poster.
312
		if (!$event->isNew() && !allowedTo('calendar_edit_any'))
313
		{
314
			isAllowedTo('calendar_edit_' . ($event->isStarter($this->user->id) ? 'own' : 'any'));
315
		}
316
317
		// New - and directing?
318
		if ($event->isNew() && isset($_POST['link_to_board']))
319
		{
320
			$_REQUEST['calendar'] = 1;
321
322
			return $this->_returnToPost();
323
		}
324
325
		// New...
326
		if ($event->isNew())
327
		{
328
			$event->insert($save_data, $this->user->id);
329
		}
330
		elseif (isset($_REQUEST['deleteevent']))
331
		{
332
			$event->remove();
333
		}
334
		else
335
		{
336
			$event->update($save_data);
337
		}
338
339
		// No point hanging around here now...
340
		redirectexit('action=calendar;month=' . $this->_req->get('month', 'intval') . ';year=' . $this->_req->get('year', 'intval'));
341
	}
342
343
	/**
344
	 *
345
	 * What it does:
346
	 *  - require_once modules of the controller (not addons because these are always all require'd by the dispatcher),
347
	 *  - Creates the event manager and registers addons and modules,
348
	 *  - Instantiate the controller
349
	 *  - Runs pre_dispatch
350
	 */
351
	protected function _returnToPost()
352
	{
353
		$controller = new Post(new EventManager());
354
		$controller->setUser(User::$info);
355
356
		$hook = $controller->getHook();
357
		$controller->pre_dispatch();
358
		$function_name = 'action_post';
359
360
		call_integration_hook('integrate_action_' . $hook . '_before', [$function_name]);
361
362
		$controller->{$function_name}();
363
364
		call_integration_hook('integrate_action_' . $hook . '_after', [$function_name]);
365
366
		return true;
367
	}
368
369
	/**
370
	 * This function offers up a download of an event in iCal 2.0 format.
371
	 *
372
	 * What it does:
373
	 *
374
	 * - Follows the conventions in RFC5546 https://tools.ietf.org/html/rfc5546
375
	 * - Sets events as all day events since we don't have hourly events
376
	 * - Will honor and set multi day events
377
	 * - Sets a sequence number if the event has been modified.
378
	 * - Accessed by action=calendar;sa=ical
379
	 */
380
	public function action_ical()
381
	{
382
		global $modSettings;
383
384
		// What do you think you export?
385
		isAllowedTo('calendar_view');
386
387
		// You can't export if the calendar export feature is off.
388
		if (empty($modSettings['cal_export']))
389
		{
390
			throw new Exception('calendar_export_off', false);
391
		}
392
393
		// Goes without saying that this is required.
394
		if (!isset($_REQUEST['eventid']))
395
		{
396
			throw new Exception('no_access', false);
397
		}
398
399
		// This is kinda wanted.
400
		require_once(SUBSDIR . '/Calendar.subs.php');
401
402
		// Load up the event in question and check it exists.
403
		$event = getEventProperties($_REQUEST['eventid']);
404
405
		if ($event === false)
406
		{
407
			throw new Exception('no_access', false);
408
		}
409
410
		$filecontents = build_ical_content($event);
0 ignored issues
show
Bug introduced by
It seems like $event can also be of type true; however, parameter $event of build_ical_content() does only seem to accept array<mixed,mixed>, maybe add an additional type check? ( Ignorable by Annotation )

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

410
		$filecontents = build_ical_content(/** @scrutinizer ignore-type */ $event);
Loading history...
411
412
		// Send some standard headers.
413
		obStart(!empty($modSettings['enableCompressedOutput']));
414
415
		// Send the file headers
416
		$headers = Headers::instance();
417
		$headers
418
			->header('Pragma', 'no-cache')
419
			->header('Cache-Control', 'no-cache')
420
			->header('Expires', gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT')
421
			->header('Last-Modified', gmdate('D, d M Y H:i:s', time()) . 'GMT')
422
			->header('Accept-Ranges', 'bytes')
423
			->header('Connection', 'close')
424
			->header('Content-Disposition', 'attachment; filename="' . $event['title'] . '.ics"');
425
		if (empty($modSettings['enableCompressedOutput']))
426
		{
427
			$headers->header('Content-Length', Util::strlen($filecontents));
428
		}
429
430
		// This is a calendar item!
431
432
		$headers
433
			->contentType('text/calendar', 'UTF-8')
434
			->sendHeaders();
435
436
		// Chuck out the card.
437
		echo $filecontents;
438
439
		// Off we pop - lovely!
440
		obExit(false);
441
	}
442
}
443