PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/sources/controllers/Calendar.controller.php

https://github.com/Arantor/Elkarte
PHP | 436 lines | 266 code | 64 blank | 106 comment | 64 complexity | 1a6e7b08f3fb5fa693ffd0f42204ec0d MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  1. <?php
  2. /**
  3. * @name ElkArte Forum
  4. * @copyright ElkArte Forum contributors
  5. * @license BSD http://opensource.org/licenses/BSD-3-Clause
  6. *
  7. * This software is a derived product, based on:
  8. *
  9. * Simple Machines Forum (SMF)
  10. * copyright: 2011 Simple Machines (http://www.simplemachines.org)
  11. * license: BSD, See included LICENSE.TXT for terms and conditions.
  12. *
  13. * @version 1.0 Alpha
  14. *
  15. * This file has only one real task, showing the calendar.
  16. * Original module by Aaron O'Neil - aaron@mud-master.com
  17. */
  18. if (!defined('ELKARTE'))
  19. die('No access...');
  20. /**
  21. * Show the calendar.
  22. * It loads the specified month's events, holidays, and birthdays.
  23. * It requires the calendar_view permission.
  24. * It depends on the cal_enabled setting, and many of the other cal_ settings.
  25. * It uses the calendar_start_day theme option. (Monday/Sunday)
  26. * It uses the main sub template in the Calendar template.
  27. * It goes to the month and year passed in 'month' and 'year' by get or post.
  28. * It is accessed through ?action=calendar.
  29. */
  30. function action_calendar()
  31. {
  32. global $txt, $context, $modSettings, $scripturl, $options;
  33. // Permissions, permissions, permissions.
  34. isAllowedTo('calendar_view');
  35. // Doing something other than calendar viewing?
  36. $subActions = array(
  37. 'ical' => 'action_ical',
  38. 'post' => 'CalendarPost',
  39. );
  40. if (isset($_GET['sa']) && isset($subActions[$_GET['sa']]))
  41. return $subActions[$_GET['sa']]();
  42. // This is gonna be needed...
  43. loadTemplate('Calendar');
  44. // You can't do anything if the calendar is off.
  45. if (empty($modSettings['cal_enabled']))
  46. fatal_lang_error('calendar_off', false);
  47. // Set the page title to mention the calendar ;).
  48. $context['page_title'] = $txt['calendar'];
  49. // Is this a week view?
  50. $context['view_week'] = isset($_GET['viewweek']);
  51. // Don't let search engines index weekly calendar pages.
  52. if ($context['view_week'])
  53. $context['robot_no_index'] = true;
  54. // Get the current day of month...
  55. require_once(SUBSDIR . '/Calendar.subs.php');
  56. $today = getTodayInfo();
  57. // If the month and year are not passed in, use today's date as a starting point.
  58. $curPage = array(
  59. 'day' => isset($_REQUEST['day']) ? (int) $_REQUEST['day'] : $today['day'],
  60. 'month' => isset($_REQUEST['month']) ? (int) $_REQUEST['month'] : $today['month'],
  61. 'year' => isset($_REQUEST['year']) ? (int) $_REQUEST['year'] : $today['year']
  62. );
  63. // Make sure the year and month are in valid ranges.
  64. if ($curPage['month'] < 1 || $curPage['month'] > 12)
  65. fatal_lang_error('invalid_month', false);
  66. if ($curPage['year'] < $modSettings['cal_minyear'] || $curPage['year'] > $modSettings['cal_maxyear'])
  67. fatal_lang_error('invalid_year', false);
  68. // If we have a day clean that too.
  69. if ($context['view_week'])
  70. {
  71. // Note $isValid is -1 < PHP 5.1
  72. $isValid = mktime(0, 0, 0, $curPage['month'], $curPage['day'], $curPage['year']);
  73. if ($curPage['day'] > 31 || !$isValid || $isValid == -1)
  74. fatal_lang_error('invalid_day', false);
  75. }
  76. // Load all the context information needed to show the calendar grid.
  77. $calendarOptions = array(
  78. 'start_day' => !empty($options['calendar_start_day']) ? $options['calendar_start_day'] : 0,
  79. 'show_birthdays' => in_array($modSettings['cal_showbdays'], array(1, 2)),
  80. 'show_events' => in_array($modSettings['cal_showevents'], array(1, 2)),
  81. 'show_holidays' => in_array($modSettings['cal_showholidays'], array(1, 2)),
  82. 'show_week_num' => true,
  83. 'short_day_titles' => false,
  84. 'show_next_prev' => true,
  85. 'show_week_links' => true,
  86. 'size' => 'large',
  87. );
  88. // Load up the main view.
  89. if ($context['view_week'])
  90. $context['calendar_grid_main'] = getCalendarWeek($curPage['month'], $curPage['year'], $curPage['day'], $calendarOptions);
  91. else
  92. $context['calendar_grid_main'] = getCalendarGrid($curPage['month'], $curPage['year'], $calendarOptions);
  93. // Load up the previous and next months.
  94. $calendarOptions['show_birthdays'] = $calendarOptions['show_events'] = $calendarOptions['show_holidays'] = false;
  95. $calendarOptions['short_day_titles'] = true;
  96. $calendarOptions['show_next_prev'] = false;
  97. $calendarOptions['show_week_links'] = false;
  98. $calendarOptions['size'] = 'small';
  99. $context['calendar_grid_current'] = getCalendarGrid($curPage['month'], $curPage['year'], $calendarOptions);
  100. // Only show previous month if it isn't pre-January of the min-year
  101. if ($context['calendar_grid_current']['previous_calendar']['year'] > $modSettings['cal_minyear'] || $curPage['month'] != 1)
  102. $context['calendar_grid_prev'] = getCalendarGrid($context['calendar_grid_current']['previous_calendar']['month'], $context['calendar_grid_current']['previous_calendar']['year'], $calendarOptions);
  103. // Only show next month if it isn't post-December of the max-year
  104. if ($context['calendar_grid_current']['next_calendar']['year'] < $modSettings['cal_maxyear'] || $curPage['month'] != 12)
  105. $context['calendar_grid_next'] = getCalendarGrid($context['calendar_grid_current']['next_calendar']['month'], $context['calendar_grid_current']['next_calendar']['year'], $calendarOptions);
  106. // Basic template stuff.
  107. $context['can_post'] = allowedTo('calendar_post');
  108. $context['current_day'] = $curPage['day'];
  109. $context['current_month'] = $curPage['month'];
  110. $context['current_year'] = $curPage['year'];
  111. $context['show_all_birthdays'] = isset($_GET['showbd']);
  112. // Set the page title to mention the month or week, too
  113. $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']);
  114. // Load up the linktree!
  115. $context['linktree'][] = array(
  116. 'url' => $scripturl . '?action=calendar',
  117. 'name' => $txt['calendar']
  118. );
  119. // Add the current month to the linktree.
  120. $context['linktree'][] = array(
  121. 'url' => $scripturl . '?action=calendar;year=' . $context['current_year'] . ';month=' . $context['current_month'],
  122. 'name' => $txt['months'][$context['current_month']] . ' ' . $context['current_year']
  123. );
  124. // If applicable, add the current week to the linktree.
  125. if ($context['view_week'])
  126. $context['linktree'][] = array(
  127. 'url' => $scripturl . '?action=calendar;viewweek;year=' . $context['current_year'] . ';month=' . $context['current_month'] . ';day=' . $context['current_day'],
  128. 'name' => $txt['calendar_week'] . ' ' . $context['calendar_grid_main']['week_number']
  129. );
  130. // Build the calendar button array.
  131. $context['calendar_buttons'] = array(
  132. 'post_event' => array('test' => 'can_post', 'text' => 'calendar_post_event', 'image' => 'calendarpe.png', 'lang' => true, 'url' => $scripturl . '?action=calendar;sa=post;month=' . $context['current_month'] . ';year=' . $context['current_year'] . ';' . $context['session_var'] . '=' . $context['session_id']),
  133. );
  134. // Allow mods to add additional buttons here
  135. call_integration_hook('integrate_calendar_buttons');
  136. }
  137. /**
  138. * This function processes posting/editing/deleting a calendar event.
  139. *
  140. * - calls action_post() function if event is linked to a post.
  141. * - calls insertEvent() to insert the event if not linked to post.
  142. *
  143. * It requires the calendar_post permission to use.
  144. * It uses the event_post sub template in the Calendar template.
  145. * It is accessed with ?action=calendar;sa=post.
  146. */
  147. function CalendarPost()
  148. {
  149. global $context, $txt, $user_info, $scripturl;
  150. global $modSettings, $topic, $smcFunc;
  151. // Well - can they?
  152. isAllowedTo('calendar_post');
  153. // We need this for all kinds of useful functions.
  154. require_once(SUBSDIR . '/Calendar.subs.php');
  155. // Cast this for safety...
  156. if (isset($_REQUEST['eventid']))
  157. $_REQUEST['eventid'] = (int) $_REQUEST['eventid'];
  158. // Submitting?
  159. if (isset($_POST[$context['session_var']], $_REQUEST['eventid']))
  160. {
  161. checkSession();
  162. // Validate the post...
  163. if (!isset($_POST['link_to_board']))
  164. validateEventPost();
  165. // If you're not allowed to edit any events, you have to be the poster.
  166. if ($_REQUEST['eventid'] > 0 && !allowedTo('calendar_edit_any'))
  167. isAllowedTo('calendar_edit_' . (!empty($user_info['id']) && getEventPoster($_REQUEST['eventid']) == $user_info['id'] ? 'own' : 'any'));
  168. // New - and directing?
  169. if ($_REQUEST['eventid'] == -1 && isset($_POST['link_to_board']))
  170. {
  171. $_REQUEST['calendar'] = 1;
  172. require_once(CONTROLLERDIR . '/Post.controller.php');
  173. return action_post();
  174. }
  175. // New...
  176. elseif ($_REQUEST['eventid'] == -1)
  177. {
  178. $eventOptions = array(
  179. 'board' => 0,
  180. 'topic' => 0,
  181. 'title' => $smcFunc['substr']($_REQUEST['evtitle'], 0, 100),
  182. 'member' => $user_info['id'],
  183. 'start_date' => sprintf('%04d-%02d-%02d', $_POST['year'], $_POST['month'], $_POST['day']),
  184. 'span' => isset($_POST['span']) && $_POST['span'] > 0 ? min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1) : 0,
  185. );
  186. insertEvent($eventOptions);
  187. }
  188. // Deleting...
  189. elseif (isset($_REQUEST['deleteevent']))
  190. removeEvent($_REQUEST['eventid']);
  191. // ... or just update it?
  192. else
  193. {
  194. // There could be already a topic you are not allowed to modify
  195. if (!allowedTo('post_new') && empty($modSettings['disableNoPostingCalendarEdits']))
  196. {
  197. $request = $smcFunc['db_query']('', '
  198. SELECT id_board, id_topic
  199. FROM {db_prefix}calendar
  200. WHERE id_event = {int:id_event}
  201. LIMIT 1',
  202. array(
  203. 'id_event' => $_REQUEST['eventid'],
  204. ));
  205. list ($id_board, $id_topic) = $smcFunc['db_fetch_row']($request);
  206. $smcFunc['db_free_result']($request);
  207. }
  208. $eventOptions = array(
  209. 'title' => $smcFunc['substr']($_REQUEST['evtitle'], 0, 100),
  210. 'span' => empty($modSettings['cal_allowspan']) || empty($_POST['span']) || $_POST['span'] == 1 || empty($modSettings['cal_maxspan']) || $_POST['span'] > $modSettings['cal_maxspan'] ? 0 : min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1),
  211. 'start_date' => strftime('%Y-%m-%d', mktime(0, 0, 0, (int) $_REQUEST['month'], (int) $_REQUEST['day'], (int) $_REQUEST['year'])),
  212. );
  213. modifyEvent($_REQUEST['eventid'], $eventOptions);
  214. }
  215. updateSettings(array(
  216. 'calendar_updated' => time(),
  217. ));
  218. // No point hanging around here now...
  219. redirectexit($scripturl . '?action=calendar;month=' . $_POST['month'] . ';year=' . $_POST['year']);
  220. }
  221. // If we are not enabled... we are not enabled.
  222. if (empty($modSettings['cal_allow_unlinked']) && empty($_REQUEST['eventid']))
  223. {
  224. $_REQUEST['calendar'] = 1;
  225. require_once(CONTROLLERDIR . '/Post.controller.php');
  226. return action_post();
  227. }
  228. // New?
  229. if (!isset($_REQUEST['eventid']))
  230. {
  231. $today = getdate();
  232. $context['event'] = array(
  233. 'boards' => array(),
  234. 'board' => 0,
  235. 'new' => 1,
  236. 'eventid' => -1,
  237. 'year' => isset($_REQUEST['year']) ? $_REQUEST['year'] : $today['year'],
  238. 'month' => isset($_REQUEST['month']) ? $_REQUEST['month'] : $today['mon'],
  239. 'day' => isset($_REQUEST['day']) ? $_REQUEST['day'] : $today['mday'],
  240. 'title' => '',
  241. 'span' => 1,
  242. );
  243. $context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year']));
  244. // Get list of boards that can be posted in.
  245. $boards = boardsAllowedTo('post_new');
  246. if (empty($boards))
  247. fatal_lang_error('cannot_post_new', 'permission');
  248. // Load the list of boards and categories in the context.
  249. require_once(SUBSDIR . '/MessageIndex.subs.php');
  250. $boardListOptions = array(
  251. 'included_boards' => in_array(0, $boards) ? null : $boards,
  252. 'not_redirection' => true,
  253. 'use_permissions' => true,
  254. 'selected_board' => $modSettings['cal_defaultboard'],
  255. );
  256. $context['event']['categories'] = getBoardList($boardListOptions);
  257. }
  258. else
  259. {
  260. $context['event'] = getEventProperties($_REQUEST['eventid']);
  261. if ($context['event'] === false)
  262. fatal_lang_error('no_access', false);
  263. // If it has a board, then they should be editing it within the topic.
  264. if (!empty($context['event']['topic']['id']) && !empty($context['event']['topic']['first_msg']))
  265. {
  266. // We load the board up, for a check on the board access rights...
  267. $topic = $context['event']['topic']['id'];
  268. loadBoard();
  269. }
  270. // Make sure the user is allowed to edit this event.
  271. if ($context['event']['member'] != $user_info['id'])
  272. isAllowedTo('calendar_edit_any');
  273. elseif (!allowedTo('calendar_edit_any'))
  274. isAllowedTo('calendar_edit_own');
  275. }
  276. // Template, sub template, etc.
  277. loadTemplate('Calendar');
  278. $context['sub_template'] = 'event_post';
  279. $context['page_title'] = isset($_REQUEST['eventid']) ? $txt['calendar_edit'] : $txt['calendar_post_event'];
  280. $context['linktree'][] = array(
  281. 'name' => $context['page_title'],
  282. );
  283. }
  284. /**
  285. * This function offers up a download of an event in iCal 2.0 format.
  286. *
  287. * follows the conventions in RFC5546 http://tools.ietf.org/html/rfc5546
  288. * sets events as all day events since we don't have hourly events
  289. * will honor and set multi day events
  290. * sets a sequence number if the event has been modified.
  291. * Accessed by action=calendar;sa=ical
  292. *
  293. * @todo .... allow for week or month export files as well?
  294. */
  295. function action_ical()
  296. {
  297. global $smcFunc, $forum_version, $context, $modSettings, $webmaster_email, $mbname;
  298. // You can't export if the calendar export feature is off.
  299. if (empty($modSettings['cal_export']))
  300. fatal_lang_error('calendar_export_off', false);
  301. // Goes without saying that this is required.
  302. if (!isset($_REQUEST['eventid']))
  303. fatal_lang_error('no_access', false);
  304. // This is kinda wanted.
  305. require_once(SUBSDIR . '/Calendar.subs.php');
  306. // Load up the event in question and check it exists.
  307. $event = getEventProperties($_REQUEST['eventid']);
  308. if ($event === false)
  309. fatal_lang_error('no_access', false);
  310. // Check the title isn't too long - iCal requires some formatting if so.
  311. $title = str_split($event['title'], 30);
  312. foreach ($title as $id => $line)
  313. {
  314. if ($id != 0)
  315. $title[$id] = ' ' . $title[$id];
  316. $title[$id] .= "\n";
  317. }
  318. // Format the dates.
  319. $datestamp = date('Ymd\THis\Z', time());
  320. $datestart = $event['year'] . ($event['month'] < 10 ? '0' . $event['month'] : $event['month']) . ($event['day'] < 10 ? '0' . $event['day'] : $event['day']);
  321. // Do we have a event that spans several days?
  322. if ($event['span'] > 1)
  323. {
  324. $dateend = strtotime($event['year'] . '-' . ($event['month'] < 10 ? '0' . $event['month'] : $event['month']) . '-' . ($event['day'] < 10 ? '0' . $event['day'] : $event['day']));
  325. $dateend += ($event['span'] - 1) * 86400;
  326. $dateend = date('Ymd', $dateend);
  327. }
  328. // This is what we will be sending later
  329. $filecontents = '';
  330. $filecontents .= 'BEGIN:VCALENDAR' . "\n";
  331. $filecontents .= 'METHOD:PUBLISH' . "\n";
  332. $filecontents .= 'PRODID:-//ElkarteCommunity//ELKARTE ' . (empty($forum_version) ? 2.0 : strtr($forum_version, array('ELKARTE ' => ''))) . '//EN' . "\n";
  333. $filecontents .= 'VERSION:2.0' . "\n";
  334. $filecontents .= 'BEGIN:VEVENT' . "\n";
  335. $filecontents .= 'ORGANIZER;CN="' . $event['realname'] . '":MAILTO:' . $webmaster_email . "\n";
  336. $filecontents .= 'DTSTAMP:' . $datestamp . "\n";
  337. $filecontents .= 'DTSTART;VALUE=DATE:' . $datestart . "\n";
  338. // more than one day
  339. if ($event['span'] > 1)
  340. $filecontents .= 'DTEND;VALUE=DATE:' . $dateend . "\n";
  341. // event has changed? advance the sequence for this UID
  342. if ($event['sequence'] > 0)
  343. $filecontents .= 'SEQUENCE:' . $event['sequence'] . "\n";
  344. $filecontents .= 'SUMMARY:' . implode('', $title);
  345. $filecontents .= 'UID:' . $event['eventid'] . '@' . str_replace(' ', '-', $mbname) . "\n";
  346. $filecontents .= 'END:VEVENT' . "\n";
  347. $filecontents .= 'END:VCALENDAR';
  348. // Send some standard headers.
  349. ob_end_clean();
  350. if (!empty($modSettings['enableCompressedOutput']))
  351. @ob_start('ob_gzhandler');
  352. else
  353. ob_start();
  354. // Send the file headers
  355. header('Pragma: ');
  356. header('Cache-Control: no-cache');
  357. if (!isBrowser('gecko'))
  358. header('Content-Transfer-Encoding: binary');
  359. header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT');
  360. header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . 'GMT');
  361. header('Accept-Ranges: bytes');
  362. header('Connection: close');
  363. header('Content-Disposition: attachment; filename="' . $event['title'] . '.ics"');
  364. if (empty($modSettings['enableCompressedOutput']))
  365. header('Content-Length: ' . $smcFunc['strlen']($filecontents));
  366. // This is a calendar item!
  367. header('Content-Type: text/calendar');
  368. // Chuck out the card.
  369. echo $filecontents;
  370. // Off we pop - lovely!
  371. obExit(false);
  372. }