Commit | Line | Data |
---|---|---|
93c91ee4 | 1 | <?php |
08b4a4e1 RW |
2 | // This file is part of Moodle - http://moodle.org/ |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
7423f116 | 16 | |
08b4a4e1 RW |
17 | /** |
18 | * Calendar extension | |
19 | * | |
20 | * @package core_calendar | |
21 | * @copyright 2004 Greek School Network (http://www.sch.gr), Jon Papaioannou, | |
22 | * Avgoustos Tsinakos | |
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
24 | */ | |
25 | ||
e30390a0 SH |
26 | if (!defined('MOODLE_INTERNAL')) { |
27 | die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page | |
28 | } | |
b5a52acd | 29 | |
08b4a4e1 RW |
30 | /** |
31 | * These are read by the administration component to provide default values | |
32 | */ | |
33 | ||
34 | /** | |
35 | * CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD - default value of upcoming event preference | |
36 | */ | |
bb4a2e85 | 37 | define('CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD', 21); |
08b4a4e1 RW |
38 | |
39 | /** | |
40 | * CALENDAR_DEFAULT_UPCOMING_MAXEVENTS - default value to display the maximum number of upcoming event | |
41 | */ | |
bb4a2e85 | 42 | define('CALENDAR_DEFAULT_UPCOMING_MAXEVENTS', 10); |
08b4a4e1 RW |
43 | |
44 | /** | |
45 | * CALENDAR_DEFAULT_STARTING_WEEKDAY - default value to display the starting weekday | |
46 | */ | |
47 | define('CALENDAR_DEFAULT_STARTING_WEEKDAY', 1); | |
48 | ||
bb4a2e85 | 49 | // This is a packed bitfield: day X is "weekend" if $field & (1 << X) is true |
50 | // Default value = 65 = 64 + 1 = 2^6 + 2^0 = Saturday & Sunday | |
08b4a4e1 RW |
51 | |
52 | /** | |
53 | * CALENDAR_DEFAULT_WEEKEND - default value for weekend (Saturday & Sunday) | |
54 | */ | |
55 | define('CALENDAR_DEFAULT_WEEKEND', 65); | |
56 | ||
57 | /** | |
58 | * CALENDAR_URL - path to calendar's folder | |
59 | */ | |
76d9df3f | 60 | define('CALENDAR_URL', $CFG->wwwroot.'/calendar/'); |
08b4a4e1 RW |
61 | |
62 | /** | |
63 | * CALENDAR_TF_24 - Calendar time in 24 hours format | |
64 | */ | |
76d9df3f | 65 | define('CALENDAR_TF_24', '%H:%M'); |
08b4a4e1 RW |
66 | |
67 | /** | |
68 | * CALENDAR_TF_12 - Calendar time in 12 hours format | |
69 | */ | |
76d9df3f | 70 | define('CALENDAR_TF_12', '%I:%M %p'); |
bb4a2e85 | 71 | |
08b4a4e1 RW |
72 | /** |
73 | * CALENDAR_EVENT_GLOBAL - Global calendar event types | |
74 | */ | |
797cedc7 | 75 | define('CALENDAR_EVENT_GLOBAL', 1); |
08b4a4e1 RW |
76 | |
77 | /** | |
78 | * CALENDAR_EVENT_COURSE - Course calendar event types | |
79 | */ | |
797cedc7 | 80 | define('CALENDAR_EVENT_COURSE', 2); |
08b4a4e1 RW |
81 | |
82 | /** | |
83 | * CALENDAR_EVENT_GROUP - group calendar event types | |
84 | */ | |
797cedc7 | 85 | define('CALENDAR_EVENT_GROUP', 4); |
08b4a4e1 RW |
86 | |
87 | /** | |
88 | * CALENDAR_EVENT_USER - user calendar event types | |
89 | */ | |
797cedc7 SH |
90 | define('CALENDAR_EVENT_USER', 8); |
91 | ||
7423f116 | 92 | |
b5a52acd JH |
93 | /** |
94 | * CALENDAR_IMPORT_FROM_FILE - import the calendar from a file | |
95 | */ | |
96 | define('CALENDAR_IMPORT_FROM_FILE', 0); | |
97 | ||
98 | /** | |
99 | * CALENDAR_IMPORT_FROM_URL - import the calendar from a URL | |
100 | */ | |
101 | define('CALENDAR_IMPORT_FROM_URL', 1); | |
102 | ||
103 | /** | |
104 | * CALENDAR_IMPORT_EVENT_UPDATED - imported event was updated | |
105 | */ | |
106 | define('CALENDAR_IMPORT_EVENT_UPDATED', 1); | |
107 | ||
108 | /** | |
109 | * CALENDAR_IMPORT_EVENT_INSERTED - imported event was added by insert | |
110 | */ | |
111 | define('CALENDAR_IMPORT_EVENT_INSERTED', 2); | |
112 | ||
797cedc7 SH |
113 | /** |
114 | * Return the days of the week | |
115 | * | |
08b4a4e1 | 116 | * @return array array of days |
797cedc7 SH |
117 | */ |
118 | function calendar_get_days() { | |
119 | return array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); | |
120 | } | |
f2bffd9e | 121 | |
76d9df3f SH |
122 | /** |
123 | * Gets the first day of the week | |
124 | * | |
125 | * Used to be define('CALENDAR_STARTING_WEEKDAY', blah); | |
126 | * | |
127 | * @return int | |
128 | */ | |
129 | function calendar_get_starting_weekday() { | |
130 | global $CFG; | |
37d87d11 | 131 | |
76d9df3f SH |
132 | if (isset($CFG->calendar_startwday)) { |
133 | $firstday = $CFG->calendar_startwday; | |
134 | } else { | |
0a933ae6 | 135 | $firstday = get_string('firstdayofweek', 'langconfig'); |
76d9df3f SH |
136 | } |
137 | ||
138 | if(!is_numeric($firstday)) { | |
139 | return CALENDAR_DEFAULT_STARTING_WEEKDAY; | |
140 | } else { | |
141 | return intval($firstday) % 7; | |
142 | } | |
143 | } | |
bd119567 | 144 | |
0f927f1e | 145 | /** |
14236cbd | 146 | * Generates the HTML for a miniature calendar |
0f927f1e | 147 | * |
08b4a4e1 RW |
148 | * @param array $courses list of course |
149 | * @param array $groups list of group | |
150 | * @param array $users user's info | |
151 | * @param int $cal_month calendar month in numeric, default is set to false | |
152 | * @param int $cal_year calendar month in numeric, default is set to false | |
153 | * @return string $content return html table for mini calendar | |
0f927f1e | 154 | */ |
7423f116 | 155 | function calendar_get_mini($courses, $groups, $users, $cal_month = false, $cal_year = false) { |
6b608f8f | 156 | global $CFG, $USER, $OUTPUT; |
7423f116 | 157 | |
dd97c328 | 158 | $display = new stdClass; |
76d9df3f | 159 | $display->minwday = get_user_preferences('calendar_startwday', calendar_get_starting_weekday()); |
7423f116 | 160 | $display->maxwday = $display->minwday + 6; |
161 | ||
162 | $content = ''; | |
163 | ||
164 | if(!empty($cal_month) && !empty($cal_year)) { | |
165 | $thisdate = usergetdate(time()); // Date and time the user sees at his location | |
166 | if($cal_month == $thisdate['mon'] && $cal_year == $thisdate['year']) { | |
167 | // Navigated to this month | |
168 | $date = $thisdate; | |
169 | $display->thismonth = true; | |
36dc3b71 | 170 | } else { |
7423f116 | 171 | // Navigated to other month, let's do a nice trick and save us a lot of work... |
172 | if(!checkdate($cal_month, 1, $cal_year)) { | |
173 | $date = array('mday' => 1, 'mon' => $thisdate['mon'], 'year' => $thisdate['year']); | |
174 | $display->thismonth = true; | |
36dc3b71 | 175 | } else { |
7423f116 | 176 | $date = array('mday' => 1, 'mon' => $cal_month, 'year' => $cal_year); |
177 | $display->thismonth = false; | |
178 | } | |
179 | } | |
36dc3b71 | 180 | } else { |
7423f116 | 181 | $date = usergetdate(time()); // Date and time the user sees at his location |
182 | $display->thismonth = true; | |
183 | } | |
184 | ||
185 | // Fill in the variables we 're going to use, nice and tidy | |
186 | list($d, $m, $y) = array($date['mday'], $date['mon'], $date['year']); // This is what we want to display | |
187 | $display->maxdays = calendar_days_in_month($m, $y); | |
188 | ||
d6198903 | 189 | if (get_user_timezone_offset() < 99) { |
190 | // We 'll keep these values as GMT here, and offset them when the time comes to query the db | |
61240489 | 191 | $display->tstart = gmmktime(0, 0, 0, $m, 1, $y); // This is GMT |
192 | $display->tend = gmmktime(23, 59, 59, $m, $display->maxdays, $y); // GMT | |
d6198903 | 193 | } else { |
194 | // no timezone info specified | |
61240489 | 195 | $display->tstart = mktime(0, 0, 0, $m, 1, $y); |
196 | $display->tend = mktime(23, 59, 59, $m, $display->maxdays, $y); | |
d6198903 | 197 | } |
7423f116 | 198 | |
69244b91 | 199 | $startwday = dayofweek(1, $m, $y); |
7423f116 | 200 | |
201 | // Align the starting weekday to fall in our display range | |
202 | // This is simple, not foolproof. | |
203 | if($startwday < $display->minwday) { | |
204 | $startwday += 7; | |
205 | } | |
206 | ||
c0b507d1 | 207 | // TODO: THIS IS TEMPORARY CODE! |
208 | // [pj] I was just reading through this and realized that I when writing this code I was probably | |
209 | // asking for trouble, as all these time manipulations seem to be unnecessary and a simple | |
210 | // make_timestamp would accomplish the same thing. So here goes a test: | |
1747ee05 | 211 | //$test_start = make_timestamp($y, $m, 1); |
212 | //$test_end = make_timestamp($y, $m, $display->maxdays, 23, 59, 59); | |
213 | //if($test_start != usertime($display->tstart) - dst_offset_on($display->tstart)) { | |
214 | //notify('Failed assertion in calendar/lib.php line 126; display->tstart = '.$display->tstart.', dst_offset = '.dst_offset_on($display->tstart).', usertime = '.usertime($display->tstart).', make_t = '.$test_start); | |
215 | //} | |
216 | //if($test_end != usertime($display->tend) - dst_offset_on($display->tend)) { | |
217 | //notify('Failed assertion in calendar/lib.php line 130; display->tend = '.$display->tend.', dst_offset = '.dst_offset_on($display->tend).', usertime = '.usertime($display->tend).', make_t = '.$test_end); | |
218 | //} | |
c0b507d1 | 219 | |
220 | ||
7423f116 | 221 | // Get the events matching our criteria. Don't forget to offset the timestamps for the user's TZ! |
8263f802 | 222 | $events = calendar_get_events( |
d3555a2f | 223 | usertime($display->tstart) - dst_offset_on($display->tstart), |
224 | usertime($display->tend) - dst_offset_on($display->tend), | |
225 | $users, $groups, $courses); | |
7423f116 | 226 | |
b4892fa2 | 227 | // Set event course class for course events |
228 | if (!empty($events)) { | |
13534ef7 | 229 | foreach ($events as $eventid => $event) { |
13534ef7 ML |
230 | if (!empty($event->modulename)) { |
231 | $cm = get_coursemodule_from_instance($event->modulename, $event->instance); | |
232 | if (!groups_course_module_visible($cm)) { | |
233 | unset($events[$eventid]); | |
234 | } | |
235 | } | |
b4892fa2 | 236 | } |
237 | } | |
6619eba4 | 238 | |
c635dcda | 239 | // This is either a genius idea or an idiot idea: in order to not complicate things, we use this rule: if, after |
9064751b | 240 | // possibly removing SITEID from $courses, there is only one course left, then clicking on a day in the month |
c635dcda | 241 | // will also set the $SESSION->cal_courses_shown variable to that one course. Otherwise, we 'd need to add extra |
242 | // arguments to this function. | |
243 | ||
0f927f1e | 244 | $hrefparams = array(); |
7bd1677c | 245 | if(!empty($courses)) { |
e749554e | 246 | $courses = array_diff($courses, array(SITEID)); |
7bd1677c | 247 | if(count($courses) == 1) { |
0f927f1e | 248 | $hrefparams['course'] = reset($courses); |
7bd1677c | 249 | } |
c635dcda | 250 | } |
251 | ||
7423f116 | 252 | // We want to have easy access by day, since the display is on a per-day basis. |
253 | // Arguments passed by reference. | |
7b38bfa6 | 254 | //calendar_events_by_day($events, $display->tstart, $eventsbyday, $durationbyday, $typesbyday); |
7c50db30 | 255 | calendar_events_by_day($events, $m, $y, $eventsbyday, $durationbyday, $typesbyday, $courses); |
7423f116 | 256 | |
f20c4d02 | 257 | //Accessibility: added summary and <abbr> elements. |
797cedc7 | 258 | $days_title = calendar_get_days(); |
f20c4d02 | 259 | |
2a06efcc | 260 | $summary = get_string('calendarheading', 'calendar', userdate(make_timestamp($y, $m), get_string('strftimemonthyear'))); |
90723839 | 261 | $content .= '<table class="minicalendar calendartable" summary="'.$summary.'">'; // Begin table |
f136e4c5 | 262 | $content .= '<tr class="weekdays">'; // Header row: day names |
7423f116 | 263 | |
264 | // Print out the names of the weekdays | |
265 | $days = array('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'); | |
266 | for($i = $display->minwday; $i <= $display->maxwday; ++$i) { | |
267 | // This uses the % operator to get the correct weekday no matter what shift we have | |
268 | // applied to the $display->minwday : $display->maxwday range from the default 0 : 6 | |
f20c4d02 | 269 | $content .= '<th scope="col"><abbr title="'. get_string($days_title[$i % 7], 'calendar') .'">'. |
270 | get_string($days[$i % 7], 'calendar') ."</abbr></th>\n"; | |
7423f116 | 271 | } |
272 | ||
f136e4c5 | 273 | $content .= '</tr><tr>'; // End of day names; prepare for day numbers |
7423f116 | 274 | |
275 | // For the table display. $week is the row; $dayweek is the column. | |
7423f116 | 276 | $dayweek = $startwday; |
277 | ||
278 | // Paddding (the first week may have blank days in the beginning) | |
279 | for($i = $display->minwday; $i < $startwday; ++$i) { | |
3bfd8bc8 | 280 | $content .= '<td class="dayblank"> </td>'."\n"; |
7423f116 | 281 | } |
282 | ||
797cedc7 SH |
283 | $weekend = CALENDAR_DEFAULT_WEEKEND; |
284 | if (isset($CFG->calendar_weekend)) { | |
285 | $weekend = intval($CFG->calendar_weekend); | |
286 | } | |
287 | ||
7423f116 | 288 | // Now display all the calendar |
289 | for($day = 1; $day <= $display->maxdays; ++$day, ++$dayweek) { | |
290 | if($dayweek > $display->maxwday) { | |
291 | // We need to change week (table row) | |
d56d4e23 | 292 | $content .= '</tr><tr>'; |
7423f116 | 293 | $dayweek = $display->minwday; |
7423f116 | 294 | } |
92668ad2 | 295 | |
7423f116 | 296 | // Reset vars |
7423f116 | 297 | $cell = ''; |
797cedc7 | 298 | if ($weekend & (1 << ($dayweek % 7))) { |
7423f116 | 299 | // Weekend. This is true no matter what the exact range is. |
e2aa618b | 300 | $class = 'weekend day'; |
90723839 | 301 | } else { |
7423f116 | 302 | // Normal working day. |
e2aa618b | 303 | $class = 'day'; |
7423f116 | 304 | } |
305 | ||
306 | // Special visual fx if an event is defined | |
307 | if(isset($eventsbyday[$day])) { | |
90723839 | 308 | $class .= ' hasevent'; |
0f927f1e SH |
309 | $hrefparams['view'] = 'day'; |
310 | $dayhref = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', $hrefparams), $day, $m, $y); | |
f434283f | 311 | |
7423f116 | 312 | $popupcontent = ''; |
313 | foreach($eventsbyday[$day] as $eventid) { | |
41d30a8e | 314 | if (!isset($events[$eventid])) { |
315 | continue; | |
4e17c6f3 | 316 | } |
41d30a8e | 317 | $event = $events[$eventid]; |
0f927f1e SH |
318 | $popupalt = ''; |
319 | $component = 'moodle'; | |
41d30a8e | 320 | if(!empty($event->modulename)) { |
0f927f1e | 321 | $popupicon = 'icon'; |
41d30a8e | 322 | $popupalt = $event->modulename; |
0f927f1e | 323 | $component = $event->modulename; |
9064751b | 324 | } else if ($event->courseid == SITEID) { // Site event |
5326675e | 325 | $popupicon = 'i/siteevent'; |
c3d3b6d4 | 326 | } else if ($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { // Course event |
5326675e | 327 | $popupicon = 'i/courseevent'; |
41d30a8e | 328 | } else if ($event->groupid) { // Group event |
5326675e | 329 | $popupicon = 'i/groupevent'; |
41d30a8e | 330 | } else if ($event->userid) { // User event |
5326675e | 331 | $popupicon = 'i/userevent'; |
4e17c6f3 | 332 | } |
1d5bd3d2 | 333 | |
0f927f1e | 334 | $dayhref->set_anchor('event_'.$event->id); |
1d5bd3d2 | 335 | |
0f927f1e | 336 | $popupcontent .= html_writer::start_tag('div'); |
adc3cfc7 | 337 | $popupcontent .= $OUTPUT->pix_icon($popupicon, $popupalt, $component); |
0f927f1e SH |
338 | $popupcontent .= html_writer::link($dayhref, format_string($event->name, true)); |
339 | $popupcontent .= html_writer::end_tag('div'); | |
7423f116 | 340 | } |
e295df44 | 341 | |
b5c42e70 | 342 | //Accessibility: functionality moved to calendar_get_popup. |
343 | if($display->thismonth && $day == $d) { | |
7df3ea15 | 344 | $popupid = calendar_get_popup(true, $events[$eventid]->timestart, $popupcontent); |
b5c42e70 | 345 | } else { |
7df3ea15 | 346 | $popupid = calendar_get_popup(false, $events[$eventid]->timestart, $popupcontent); |
e295df44 | 347 | } |
f20c4d02 | 348 | |
7423f116 | 349 | // Class and cell content |
350 | if(isset($typesbyday[$day]['startglobal'])) { | |
90723839 SH |
351 | $class .= ' calendar_event_global'; |
352 | } else if(isset($typesbyday[$day]['startcourse'])) { | |
353 | $class .= ' calendar_event_course'; | |
354 | } else if(isset($typesbyday[$day]['startgroup'])) { | |
355 | $class .= ' calendar_event_group'; | |
356 | } else if(isset($typesbyday[$day]['startuser'])) { | |
357 | $class .= ' calendar_event_user'; | |
7423f116 | 358 | } |
cfa4a78b | 359 | $cell = html_writer::link($dayhref, $day, array('aria-controls' => $popupid.'_panel', 'id' => $popupid)); |
90723839 | 360 | } else { |
05fcc5fd | 361 | $cell = $day; |
7423f116 | 362 | } |
363 | ||
90723839 SH |
364 | $durationclass = false; |
365 | if (isset($typesbyday[$day]['durationglobal'])) { | |
366 | $durationclass = ' duration_global'; | |
367 | } else if(isset($typesbyday[$day]['durationcourse'])) { | |
368 | $durationclass = ' duration_course'; | |
369 | } else if(isset($typesbyday[$day]['durationgroup'])) { | |
370 | $durationclass = ' duration_group'; | |
371 | } else if(isset($typesbyday[$day]['durationuser'])) { | |
372 | $durationclass = ' duration_user'; | |
7423f116 | 373 | } |
90723839 SH |
374 | if ($durationclass) { |
375 | $class .= ' duration '.$durationclass; | |
7423f116 | 376 | } |
377 | ||
b4892fa2 | 378 | // If event has a class set then add it to the table day <td> tag |
379 | // Note: only one colour for minicalendar | |
380 | if(isset($eventsbyday[$day])) { | |
381 | foreach($eventsbyday[$day] as $eventid) { | |
382 | if (!isset($events[$eventid])) { | |
383 | continue; | |
384 | } | |
385 | $event = $events[$eventid]; | |
386 | if (!empty($event->class)) { | |
387 | $class .= ' '.$event->class; | |
388 | } | |
389 | break; | |
390 | } | |
391 | } | |
392 | ||
7423f116 | 393 | // Special visual fx for today |
450a0a7d | 394 | //Accessibility: hidden text for today, and popup. |
7423f116 | 395 | if($display->thismonth && $day == $d) { |
edbe6c1b | 396 | $class .= ' today'; |
b5c42e70 | 397 | $today = get_string('today', 'calendar').' '.userdate(time(), get_string('strftimedayshort')); |
e295df44 | 398 | |
b5c42e70 | 399 | if(! isset($eventsbyday[$day])) { |
450a0a7d | 400 | $class .= ' eventnone'; |
7df3ea15 | 401 | $popupid = calendar_get_popup(true, false); |
cfa4a78b | 402 | $cell = html_writer::link('#', $day, array('aria-controls' => $popupid.'_panel', 'id' => $popupid)); |
b5c42e70 | 403 | } |
f79f2494 | 404 | $cell = get_accesshide($today.' ').$cell; |
7423f116 | 405 | } |
406 | ||
407 | // Just display it | |
92668ad2 | 408 | if(!empty($class)) { |
d56d4e23 | 409 | $class = ' class="'.$class.'"'; |
92668ad2 | 410 | } |
411 | $content .= '<td'.$class.'>'.$cell."</td>\n"; | |
7423f116 | 412 | } |
413 | ||
414 | // Paddding (the last week may have blank days at the end) | |
415 | for($i = $dayweek; $i <= $display->maxwday; ++$i) { | |
3bfd8bc8 | 416 | $content .= '<td class="dayblank"> </td>'; |
7423f116 | 417 | } |
418 | $content .= '</tr>'; // Last row ends | |
419 | ||
f136e4c5 | 420 | $content .= '</table>'; // Tabular display of days ends |
7423f116 | 421 | |
422 | return $content; | |
423 | } | |
424 | ||
b5c42e70 | 425 | /** |
08b4a4e1 RW |
426 | * Gets the calendar popup |
427 | * | |
428 | * It called at multiple points in from calendar_get_mini. | |
429 | * Copied and modified from calendar_get_mini. | |
430 | * | |
431 | * @param bool $is_today false except when called on the current day. | |
432 | * @param mixed $event_timestart $events[$eventid]->timestart, OR false if there are no events. | |
7df3ea15 AA |
433 | * @param string $popupcontent content for the popup window/layout. |
434 | * @return string eventid for the calendar_tooltip popup window/layout. | |
b5c42e70 | 435 | */ |
436 | function calendar_get_popup($is_today, $event_timestart, $popupcontent='') { | |
458eb0d1 | 437 | global $PAGE; |
438 | static $popupcount; | |
439 | if ($popupcount === null) { | |
440 | $popupcount = 1; | |
441 | } | |
b5c42e70 | 442 | $popupcaption = ''; |
443 | if($is_today) { | |
444 | $popupcaption = get_string('today', 'calendar').' '; | |
445 | } | |
446 | if (false === $event_timestart) { | |
447 | $popupcaption .= userdate(time(), get_string('strftimedayshort')); | |
448 | $popupcontent = get_string('eventnone', 'calendar'); | |
450a0a7d | 449 | |
b5c42e70 | 450 | } else { |
451 | $popupcaption .= get_string('eventsfor', 'calendar', userdate($event_timestart, get_string('strftimedayshort'))); | |
452 | } | |
458eb0d1 | 453 | $id = 'calendar_tooltip_'.$popupcount; |
33e48a1a | 454 | $PAGE->requires->yui_module('moodle-calendar-eventmanager', 'M.core_calendar.add_event', array(array('eventId'=>$id,'title'=>$popupcaption, 'content'=>$popupcontent))); |
bdbae764 | 455 | |
458eb0d1 | 456 | $popupcount++; |
7df3ea15 | 457 | return $id; |
b5c42e70 | 458 | } |
459 | ||
08b4a4e1 RW |
460 | /** |
461 | * Gets the calendar upcoming event | |
462 | * | |
463 | * @param array $courses array of courses | |
464 | * @param array|int|bool $groups array of groups, group id or boolean for all/no group events | |
465 | * @param array|int|bool $users array of users, user id or boolean for all/no user events | |
466 | * @param int $daysinfuture number of days in the future we 'll look | |
467 | * @param int $maxevents maximum number of events | |
468 | * @param int $fromtime start time | |
469 | * @return array $output array of upcoming events | |
470 | */ | |
9958a08c | 471 | function calendar_get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime=0) { |
62d11d77 | 472 | global $CFG, $COURSE, $DB; |
7423f116 | 473 | |
dd97c328 | 474 | $display = new stdClass; |
7423f116 | 475 | $display->range = $daysinfuture; // How many days in the future we 'll look |
476 | $display->maxevents = $maxevents; | |
477 | ||
478 | $output = array(); | |
479 | ||
480 | // Prepare "course caching", since it may save us a lot of queries | |
481 | $coursecache = array(); | |
482 | ||
483 | $processed = 0; | |
484 | $now = time(); // We 'll need this later | |
9d567178 | 485 | $usermidnighttoday = usergetmidnight($now); |
7423f116 | 486 | |
9958a08c | 487 | if ($fromtime) { |
488 | $display->tstart = $fromtime; | |
489 | } else { | |
9d567178 | 490 | $display->tstart = $usermidnighttoday; |
9958a08c | 491 | } |
7423f116 | 492 | |
1f473774 | 493 | // This works correctly with respect to the user's DST, but it is accurate |
494 | // only because $fromtime is always the exact midnight of some day! | |
495 | $display->tend = usergetmidnight($display->tstart + DAYSECS * $display->range + 3 * HOURSECS) - 1; | |
7423f116 | 496 | |
497 | // Get the events matching our criteria | |
8263f802 | 498 | $events = calendar_get_events($display->tstart, $display->tend, $users, $groups, $courses); |
7423f116 | 499 | |
c635dcda | 500 | // This is either a genius idea or an idiot idea: in order to not complicate things, we use this rule: if, after |
9064751b | 501 | // possibly removing SITEID from $courses, there is only one course left, then clicking on a day in the month |
c635dcda | 502 | // will also set the $SESSION->cal_courses_shown variable to that one course. Otherwise, we 'd need to add extra |
503 | // arguments to this function. | |
504 | ||
0f927f1e | 505 | $hrefparams = array(); |
7bd1677c | 506 | if(!empty($courses)) { |
e749554e | 507 | $courses = array_diff($courses, array(SITEID)); |
7bd1677c | 508 | if(count($courses) == 1) { |
0f927f1e | 509 | $hrefparams['course'] = reset($courses); |
7bd1677c | 510 | } |
c635dcda | 511 | } |
512 | ||
dd97c328 | 513 | if ($events !== false) { |
514 | ||
f20edd52 | 515 | $modinfo = get_fast_modinfo($COURSE); |
fa22fd5f | 516 | |
7423f116 | 517 | foreach($events as $event) { |
dc6cb74e | 518 | |
dd97c328 | 519 | |
520 | if (!empty($event->modulename)) { | |
521 | if ($event->courseid == $COURSE->id) { | |
522 | if (isset($modinfo->instances[$event->modulename][$event->instance])) { | |
523 | $cm = $modinfo->instances[$event->modulename][$event->instance]; | |
524 | if (!$cm->uservisible) { | |
525 | continue; | |
526 | } | |
527 | } | |
528 | } else { | |
529 | if (!$cm = get_coursemodule_from_instance($event->modulename, $event->instance)) { | |
dc6cb74e | 530 | continue; |
531 | } | |
dd97c328 | 532 | if (!coursemodule_visible_for_user($cm)) { |
dc6cb74e | 533 | continue; |
534 | } | |
dd97c328 | 535 | } |
536 | if ($event->modulename == 'assignment'){ | |
f4700b91 RW |
537 | // create calendar_event to test edit_event capability |
538 | // this new event will also prevent double creation of calendar_event object | |
539 | $checkevent = new calendar_event($event); | |
dd97c328 | 540 | // TODO: rewrite this hack somehow |
f4700b91 | 541 | if (!calendar_edit_event_allowed($checkevent)){ // cannot manage entries, eg. student |
62d11d77 | 542 | if (!$assignment = $DB->get_record('assignment', array('id'=>$event->instance))) { |
f581f8d6 | 543 | // print_error("invalidid", 'assignment'); |
dd97c328 | 544 | continue; |
545 | } | |
546 | // assign assignment to assignment object to use hidden_is_hidden method | |
547 | require_once($CFG->dirroot.'/mod/assignment/lib.php'); | |
548 | ||
549 | if (!file_exists($CFG->dirroot.'/mod/assignment/type/'.$assignment->assignmenttype.'/assignment.class.php')) { | |
550 | continue; | |
551 | } | |
552 | require_once ($CFG->dirroot.'/mod/assignment/type/'.$assignment->assignmenttype.'/assignment.class.php'); | |
553 | ||
554 | $assignmentclass = 'assignment_'.$assignment->assignmenttype; | |
555 | $assignmentinstance = new $assignmentclass($cm->id, $assignment, $cm); | |
556 | ||
557 | if ($assignmentinstance->description_is_hidden()){//force not to show description before availability | |
558 | $event->description = get_string('notavailableyet', 'assignment'); | |
559 | } | |
dc6cb74e | 560 | } |
561 | } | |
562 | } | |
9d567178 | 563 | |
dd97c328 | 564 | if ($processed >= $display->maxevents) { |
9d567178 | 565 | break; |
566 | } | |
7423f116 | 567 | |
0f927f1e | 568 | $event->time = calendar_format_event_time($event, $now, $hrefparams); |
3c134875 | 569 | $output[] = $event; |
570 | ++$processed; | |
571 | } | |
572 | } | |
573 | return $output; | |
574 | } | |
7423f116 | 575 | |
08b4a4e1 RW |
576 | /** |
577 | * Add calendar event metadata | |
578 | * | |
579 | * @param stdClass $event event info | |
580 | * @return stdClass $event metadata | |
581 | */ | |
9df8ff44 | 582 | function calendar_add_event_metadata($event) { |
6b608f8f | 583 | global $CFG, $OUTPUT; |
fb73f3b3 | 584 | |
e295df44 | 585 | //Support multilang in event->name |
fb73f3b3 | 586 | $event->name = format_string($event->name,true); |
e295df44 | 587 | |
3c134875 | 588 | if(!empty($event->modulename)) { // Activity event |
589 | // The module name is set. I will assume that it has to be displayed, and | |
590 | // also that it is an automatically-generated event. And of course that the | |
591 | // fields for get_coursemodule_from_instance are set correctly. | |
592 | $module = calendar_get_module_cached($coursecache, $event->modulename, $event->instance); | |
7423f116 | 593 | |
3c134875 | 594 | if ($module === false) { |
595 | return; | |
596 | } | |
9958a08c | 597 | |
3c134875 | 598 | $modulename = get_string('modulename', $event->modulename); |
a48bf079 DM |
599 | if (get_string_manager()->string_exists($event->eventtype, $event->modulename)) { |
600 | // will be used as alt text if the event icon | |
601 | $eventtype = get_string($event->eventtype, $event->modulename); | |
602 | } else { | |
603 | $eventtype = ''; | |
604 | } | |
b5d0cafc | 605 | $icon = $OUTPUT->pix_url('icon', $event->modulename) . ''; |
9958a08c | 606 | |
6ca657a7 | 607 | $context = context_course::instance($module->course); |
91d284c1 SH |
608 | $fullname = format_string($coursecache[$module->course]->fullname, true, array('context' => $context)); |
609 | ||
857c5f90 | 610 | $event->icon = '<img src="'.$icon.'" alt="'.$eventtype.'" title="'.$modulename.'" class="icon" />'; |
fb73f3b3 | 611 | $event->referer = '<a href="'.$CFG->wwwroot.'/mod/'.$event->modulename.'/view.php?id='.$module->id.'">'.$event->name.'</a>'; |
91d284c1 | 612 | $event->courselink = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$module->course.'">'.$fullname.'</a>'; |
3c134875 | 613 | $event->cmid = $module->id; |
9958a08c | 614 | |
9958a08c | 615 | |
3c134875 | 616 | } else if($event->courseid == SITEID) { // Site event |
857c5f90 | 617 | $event->icon = '<img src="'.$OUTPUT->pix_url('i/siteevent') . '" alt="'.get_string('globalevent', 'calendar').'" class="icon" />'; |
90723839 | 618 | $event->cssclass = 'calendar_event_global'; |
3c134875 | 619 | } else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { // Course event |
620 | calendar_get_course_cached($coursecache, $event->courseid); | |
91d284c1 | 621 | |
6ca657a7 | 622 | $context = context_course::instance($event->courseid); |
91d284c1 SH |
623 | $fullname = format_string($coursecache[$event->courseid]->fullname, true, array('context' => $context)); |
624 | ||
857c5f90 | 625 | $event->icon = '<img src="'.$OUTPUT->pix_url('i/courseevent') . '" alt="'.get_string('courseevent', 'calendar').'" class="icon" />'; |
91d284c1 | 626 | $event->courselink = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$event->courseid.'">'.$fullname.'</a>'; |
90723839 | 627 | $event->cssclass = 'calendar_event_course'; |
3c134875 | 628 | } else if ($event->groupid) { // Group event |
857c5f90 | 629 | $event->icon = '<img src="'.$OUTPUT->pix_url('i/groupevent') . '" alt="'.get_string('groupevent', 'calendar').'" class="icon" />'; |
90723839 | 630 | $event->cssclass = 'calendar_event_group'; |
3c134875 | 631 | } else if($event->userid) { // User event |
857c5f90 | 632 | $event->icon = '<img src="'.$OUTPUT->pix_url('i/userevent') . '" alt="'.get_string('userevent', 'calendar').'" class="icon" />'; |
90723839 | 633 | $event->cssclass = 'calendar_event_user'; |
3c134875 | 634 | } |
9df8ff44 | 635 | return $event; |
e295df44 | 636 | } |
9df8ff44 | 637 | |
8263f802 | 638 | /** |
639 | * Get calendar events | |
08b4a4e1 | 640 | * |
8263f802 | 641 | * @param int $tstart Start time of time range for events |
08b4a4e1 RW |
642 | * @param int $tend End time of time range for events |
643 | * @param array|int|boolean $users array of users, user id or boolean for all/no user events | |
644 | * @param array|int|boolean $groups array of groups, group id or boolean for all/no group events | |
645 | * @param array|int|boolean $courses array of courses, course id or boolean for all/no course events | |
8263f802 | 646 | * @param boolean $withduration whether only events starting within time range selected |
647 | * or events in progress/already started selected as well | |
648 | * @param boolean $ignorehidden whether to select only visible events or all events | |
08b4a4e1 | 649 | * @return array $events of selected events or an empty array if there aren't any (or there was an error) |
8263f802 | 650 | */ |
651 | function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withduration=true, $ignorehidden=true) { | |
62d11d77 | 652 | global $DB; |
653 | ||
7423f116 | 654 | $whereclause = ''; |
655 | // Quick test | |
656 | if(is_bool($users) && is_bool($groups) && is_bool($courses)) { | |
8263f802 | 657 | return array(); |
7423f116 | 658 | } |
482dbe0c | 659 | |
7423f116 | 660 | if(is_array($users) && !empty($users)) { |
661 | // Events from a number of users | |
662 | if(!empty($whereclause)) $whereclause .= ' OR'; | |
6e957c41 | 663 | $whereclause .= ' (userid IN ('.implode(',', $users).') AND courseid = 0 AND groupid = 0)'; |
36dc3b71 | 664 | } else if(is_numeric($users)) { |
7423f116 | 665 | // Events from one user |
666 | if(!empty($whereclause)) $whereclause .= ' OR'; | |
6e957c41 | 667 | $whereclause .= ' (userid = '.$users.' AND courseid = 0 AND groupid = 0)'; |
36dc3b71 | 668 | } else if($users === true) { |
7423f116 | 669 | // Events from ALL users |
670 | if(!empty($whereclause)) $whereclause .= ' OR'; | |
6e957c41 | 671 | $whereclause .= ' (userid != 0 AND courseid = 0 AND groupid = 0)'; |
36dc3b71 | 672 | } else if($users === false) { |
6e957c41 | 673 | // No user at all, do nothing |
f52f7413 | 674 | } |
482dbe0c | 675 | |
7423f116 | 676 | if(is_array($groups) && !empty($groups)) { |
677 | // Events from a number of groups | |
678 | if(!empty($whereclause)) $whereclause .= ' OR'; | |
679 | $whereclause .= ' groupid IN ('.implode(',', $groups).')'; | |
36dc3b71 | 680 | } else if(is_numeric($groups)) { |
7423f116 | 681 | // Events from one group |
682 | if(!empty($whereclause)) $whereclause .= ' OR '; | |
683 | $whereclause .= ' groupid = '.$groups; | |
36dc3b71 | 684 | } else if($groups === true) { |
7423f116 | 685 | // Events from ALL groups |
686 | if(!empty($whereclause)) $whereclause .= ' OR '; | |
687 | $whereclause .= ' groupid != 0'; | |
688 | } | |
482dbe0c | 689 | // boolean false (no groups at all): we don't need to do anything |
690 | ||
36dc3b71 SH |
691 | if(is_array($courses) && !empty($courses)) { |
692 | if(!empty($whereclause)) { | |
693 | $whereclause .= ' OR'; | |
f52f7413 | 694 | } |
36dc3b71 SH |
695 | $whereclause .= ' (groupid = 0 AND courseid IN ('.implode(',', $courses).'))'; |
696 | } else if(is_numeric($courses)) { | |
7423f116 | 697 | // One course |
698 | if(!empty($whereclause)) $whereclause .= ' OR'; | |
6e957c41 | 699 | $whereclause .= ' (groupid = 0 AND courseid = '.$courses.')'; |
36dc3b71 | 700 | } else if ($courses === true) { |
7423f116 | 701 | // Events from ALL courses |
702 | if(!empty($whereclause)) $whereclause .= ' OR'; | |
6e957c41 | 703 | $whereclause .= ' (groupid = 0 AND courseid != 0)'; |
7423f116 | 704 | } |
8c165fe9 | 705 | |
482dbe0c | 706 | // Security check: if, by now, we have NOTHING in $whereclause, then it means |
707 | // that NO event-selecting clauses were defined. Thus, we won't be returning ANY | |
708 | // events no matter what. Allowing the code to proceed might return a completely | |
709 | // valid query with only time constraints, thus selecting ALL events in that time frame! | |
710 | if(empty($whereclause)) { | |
8263f802 | 711 | return array(); |
482dbe0c | 712 | } |
713 | ||
7423f116 | 714 | if($withduration) { |
b4892fa2 | 715 | $timeclause = '(timestart >= '.$tstart.' OR timestart + timeduration > '.$tstart.') AND timestart <= '.$tend; |
7423f116 | 716 | } |
717 | else { | |
718 | $timeclause = 'timestart >= '.$tstart.' AND timestart <= '.$tend; | |
719 | } | |
720 | if(!empty($whereclause)) { | |
721 | // We have additional constraints | |
722 | $whereclause = $timeclause.' AND ('.$whereclause.')'; | |
723 | } | |
724 | else { | |
725 | // Just basic time filtering | |
726 | $whereclause = $timeclause; | |
727 | } | |
f52f7413 | 728 | |
0ad072de | 729 | if ($ignorehidden) { |
730 | $whereclause .= ' AND visible = 1'; | |
731 | } | |
732 | ||
62d11d77 | 733 | $events = $DB->get_records_select('event', $whereclause, null, 'timestart'); |
8263f802 | 734 | if ($events === false) { |
735 | $events = array(); | |
736 | } | |
737 | return $events; | |
7423f116 | 738 | } |
739 | ||
08b4a4e1 RW |
740 | /** |
741 | * Get control options for Calendar | |
742 | * | |
743 | * @param string $type of calendar | |
744 | * @param array $data calendar information | |
745 | * @return string $content return available control for the calender in html | |
746 | */ | |
7423f116 | 747 | function calendar_top_controls($type, $data) { |
797cedc7 | 748 | global $CFG; |
7423f116 | 749 | $content = ''; |
750 | if(!isset($data['d'])) { | |
751 | $data['d'] = 1; | |
752 | } | |
5147ad48 | 753 | |
f21ed0f3 | 754 | // Ensure course id passed if relevant |
755 | // Required due to changes in view/lib.php mainly (calendar_session_vars()) | |
756 | $courseid = ''; | |
757 | if (!empty($data['id'])) { | |
758 | $courseid = '&course='.$data['id']; | |
759 | } | |
760 | ||
5147ad48 | 761 | if(!checkdate($data['m'], $data['d'], $data['y'])) { |
762 | $time = time(); | |
763 | } | |
764 | else { | |
765 | $time = make_timestamp($data['y'], $data['m'], $data['d']); | |
766 | } | |
767 | $date = usergetdate($time); | |
e295df44 | 768 | |
7423f116 | 769 | $data['m'] = $date['mon']; |
770 | $data['y'] = $date['year']; | |
7423f116 | 771 | |
2a06efcc | 772 | //Accessibility: calendar block controls, replaced <table> with <div>. |
a84dea2c | 773 | //$nexttext = link_arrow_right(get_string('monthnext', 'access'), $url='', $accesshide=true); |
774 | //$prevtext = link_arrow_left(get_string('monthprev', 'access'), $url='', $accesshide=true); | |
2a06efcc | 775 | |
7423f116 | 776 | switch($type) { |
777 | case 'frontpage': | |
778 | list($prevmonth, $prevyear) = calendar_sub_month($data['m'], $data['y']); | |
779 | list($nextmonth, $nextyear) = calendar_add_month($data['m'], $data['y']); | |
a84dea2c | 780 | $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), 'index.php?', 0, $nextmonth, $nextyear, $accesshide=true); |
781 | $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), 'index.php?', 0, $prevmonth, $prevyear, true); | |
ae53bd1d | 782 | |
0f927f1e SH |
783 | $calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view'=>'month')), 1, $data['m'], $data['y']); |
784 | if (!empty($data['id'])) { | |
785 | $calendarlink->param('course', $data['id']); | |
786 | } | |
787 | ||
43c90b9b | 788 | if (right_to_left()) { |
0f927f1e SH |
789 | $left = $nextlink; |
790 | $right = $prevlink; | |
43c90b9b | 791 | } else { |
0f927f1e SH |
792 | $left = $prevlink; |
793 | $right = $nextlink; | |
43c90b9b | 794 | } |
ae53bd1d | 795 | |
0f927f1e SH |
796 | $content .= html_writer::start_tag('div', array('class'=>'calendar-controls')); |
797 | $content .= $left.'<span class="hide"> | </span>'; | |
e71e340d | 798 | $content .= html_writer::tag('span', html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear')), array('title'=>get_string('monththis','calendar'))), array('class'=>'current')); |
0f927f1e | 799 | $content .= '<span class="hide"> | </span>'. $right; |
6694ff84 | 800 | $content .= "<span class=\"clearer\"><!-- --></span>\n"; |
0f927f1e SH |
801 | $content .= html_writer::end_tag('div'); |
802 | ||
803 | break; | |
7423f116 | 804 | case 'course': |
805 | list($prevmonth, $prevyear) = calendar_sub_month($data['m'], $data['y']); | |
806 | list($nextmonth, $nextyear) = calendar_add_month($data['m'], $data['y']); | |
a84dea2c | 807 | $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), 'view.php?id='.$data['id'].'&', 0, $nextmonth, $nextyear, $accesshide=true); |
808 | $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), 'view.php?id='.$data['id'].'&', 0, $prevmonth, $prevyear, true); | |
0f927f1e SH |
809 | |
810 | $calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view'=>'month')), 1, $data['m'], $data['y']); | |
811 | if (!empty($data['id'])) { | |
812 | $calendarlink->param('course', $data['id']); | |
813 | } | |
814 | ||
815 | if (right_to_left()) { | |
816 | $left = $nextlink; | |
817 | $right = $prevlink; | |
818 | } else { | |
819 | $left = $prevlink; | |
820 | $right = $nextlink; | |
821 | } | |
822 | ||
823 | $content .= html_writer::start_tag('div', array('class'=>'calendar-controls')); | |
824 | $content .= $left.'<span class="hide"> | </span>'; | |
e71e340d | 825 | $content .= html_writer::tag('span', html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear')), array('title'=>get_string('monththis','calendar'))), array('class'=>'current')); |
0f927f1e SH |
826 | $content .= '<span class="hide"> | </span>'. $right; |
827 | $content .= "<span class=\"clearer\"><!-- --></span>"; | |
828 | $content .= html_writer::end_tag('div'); | |
829 | break; | |
7423f116 | 830 | case 'upcoming': |
0f927f1e SH |
831 | $calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view'=>'upcoming')), 1, $data['m'], $data['y']); |
832 | if (!empty($data['id'])) { | |
833 | $calendarlink->param('course', $data['id']); | |
834 | } | |
835 | $calendarlink = html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear'))); | |
836 | $content .= html_writer::tag('div', $calendarlink, array('class'=>'centered')); | |
837 | break; | |
7423f116 | 838 | case 'display': |
0f927f1e SH |
839 | $calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view'=>'month')), 1, $data['m'], $data['y']); |
840 | if (!empty($data['id'])) { | |
841 | $calendarlink->param('course', $data['id']); | |
842 | } | |
843 | $calendarlink = html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear'))); | |
844 | $content .= html_writer::tag('h3', $calendarlink); | |
845 | break; | |
7423f116 | 846 | case 'month': |
847 | list($prevmonth, $prevyear) = calendar_sub_month($data['m'], $data['y']); | |
848 | list($nextmonth, $nextyear) = calendar_add_month($data['m'], $data['y']); | |
5147ad48 | 849 | $prevdate = make_timestamp($prevyear, $prevmonth, 1); |
850 | $nextdate = make_timestamp($nextyear, $nextmonth, 1); | |
0f927f1e SH |
851 | $prevlink = calendar_get_link_previous(userdate($prevdate, get_string('strftimemonthyear')), 'view.php?view=month'.$courseid.'&', 1, $prevmonth, $prevyear); |
852 | $nextlink = calendar_get_link_next(userdate($nextdate, get_string('strftimemonthyear')), 'view.php?view=month'.$courseid.'&', 1, $nextmonth, $nextyear); | |
853 | ||
854 | if (right_to_left()) { | |
855 | $left = $nextlink; | |
856 | $right = $prevlink; | |
857 | } else { | |
858 | $left = $prevlink; | |
859 | $right = $nextlink; | |
860 | } | |
861 | ||
862 | $content .= html_writer::start_tag('div', array('class'=>'calendar-controls')); | |
863 | $content .= $left . '<span class="hide"> | </span><h1 class="current">'.userdate($time, get_string('strftimemonthyear'))."</h1>"; | |
864 | $content .= '<span class="hide"> | </span>' . $right; | |
865 | $content .= '<span class="clearer"><!-- --></span>'; | |
866 | $content .= html_writer::end_tag('div')."\n"; | |
867 | break; | |
7423f116 | 868 | case 'day': |
797cedc7 | 869 | $days = calendar_get_days(); |
7423f116 | 870 | $data['d'] = $date['mday']; // Just for convenience |
5147ad48 | 871 | $prevdate = usergetdate(make_timestamp($data['y'], $data['m'], $data['d'] - 1)); |
872 | $nextdate = usergetdate(make_timestamp($data['y'], $data['m'], $data['d'] + 1)); | |
797cedc7 SH |
873 | $prevname = calendar_wday_name($days[$prevdate['wday']]); |
874 | $nextname = calendar_wday_name($days[$nextdate['wday']]); | |
0f927f1e SH |
875 | $prevlink = calendar_get_link_previous($prevname, 'view.php?view=day'.$courseid.'&', $prevdate['mday'], $prevdate['mon'], $prevdate['year']); |
876 | $nextlink = calendar_get_link_next($nextname, 'view.php?view=day'.$courseid.'&', $nextdate['mday'], $nextdate['mon'], $nextdate['year']); | |
877 | ||
878 | if (right_to_left()) { | |
879 | $left = $nextlink; | |
880 | $right = $prevlink; | |
881 | } else { | |
882 | $left = $prevlink; | |
883 | $right = $nextlink; | |
884 | } | |
885 | ||
886 | $content .= html_writer::start_tag('div', array('class'=>'calendar-controls')); | |
887 | $content .= $left; | |
888 | $content .= '<span class="hide"> | </span><span class="current">'.userdate($time, get_string('strftimedaydate')).'</span>'; | |
889 | $content .= '<span class="hide"> | </span>'. $right; | |
890 | $content .= "<span class=\"clearer\"><!-- --></span>"; | |
891 | $content .= html_writer::end_tag('div')."\n"; | |
892 | ||
893 | break; | |
7423f116 | 894 | } |
895 | return $content; | |
896 | } | |
897 | ||
ef21ad21 FM |
898 | /** |
899 | * Formats a filter control element. | |
900 | * | |
901 | * @param moodle_url $url of the filter | |
902 | * @param int $type constant defining the type filter | |
903 | * @return string html content of the element | |
904 | */ | |
905 | function calendar_filter_controls_element(moodle_url $url, $type) { | |
906 | global $OUTPUT; | |
907 | switch ($type) { | |
908 | case CALENDAR_EVENT_GLOBAL: | |
909 | $typeforhumans = 'global'; | |
910 | $class = 'calendar_event_global'; | |
911 | break; | |
912 | case CALENDAR_EVENT_COURSE: | |
913 | $typeforhumans = 'course'; | |
914 | $class = 'calendar_event_course'; | |
915 | break; | |
916 | case CALENDAR_EVENT_GROUP: | |
917 | $typeforhumans = 'groups'; | |
918 | $class = 'calendar_event_group'; | |
919 | break; | |
920 | case CALENDAR_EVENT_USER: | |
921 | $typeforhumans = 'user'; | |
922 | $class = 'calendar_event_user'; | |
923 | break; | |
924 | } | |
925 | if (calendar_show_event_type($type)) { | |
926 | $icon = $OUTPUT->pix_icon('t/hide', get_string('hide')); | |
927 | $str = get_string('hide'.$typeforhumans.'events', 'calendar'); | |
928 | } else { | |
929 | $icon = $OUTPUT->pix_icon('t/show', get_string('show')); | |
930 | $str = get_string('show'.$typeforhumans.'events', 'calendar'); | |
931 | } | |
932 | $content = html_writer::start_tag('li', array('class' => 'calendar_event')); | |
933 | $content .= html_writer::start_tag('a', array('href' => $url)); | |
934 | $content .= html_writer::tag('span', $icon, array('class' => $class)); | |
935 | $content .= html_writer::tag('span', $str, array('class' => 'eventname')); | |
936 | $content .= html_writer::end_tag('a'); | |
937 | $content .= html_writer::end_tag('li'); | |
938 | return $content; | |
939 | } | |
940 | ||
08b4a4e1 RW |
941 | /** |
942 | * Get the controls filter for calendar. | |
943 | * | |
944 | * Filter is used to hide calendar info from the display page | |
945 | * | |
946 | * @param moodle_url $returnurl return-url for filter controls | |
947 | * @return string $content return filter controls in html | |
948 | */ | |
797cedc7 SH |
949 | function calendar_filter_controls(moodle_url $returnurl) { |
950 | global $CFG, $USER, $OUTPUT; | |
7423f116 | 951 | |
48f508ab | 952 | $groupevents = true; |
7f4d18fc | 953 | $id = optional_param( 'id',0,PARAM_INT ); |
eb59a448 | 954 | $seturl = new moodle_url('/calendar/set.php', array('return' => base64_encode($returnurl->out(false)), 'sesskey'=>sesskey())); |
ef21ad21 | 955 | $content = html_writer::start_tag('ul'); |
797cedc7 SH |
956 | |
957 | $seturl->param('var', 'showglobal'); | |
ef21ad21 | 958 | $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_GLOBAL); |
aa6c1ced | 959 | |
797cedc7 | 960 | $seturl->param('var', 'showcourses'); |
ef21ad21 | 961 | $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_COURSE); |
48f508ab | 962 | |
4f0c2d00 | 963 | if (isloggedin() && !isguestuser()) { |
797cedc7 | 964 | if ($groupevents) { |
43c3ffbe | 965 | // This course MIGHT have group events defined, so show the filter |
797cedc7 | 966 | $seturl->param('var', 'showgroups'); |
ef21ad21 | 967 | $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_GROUP); |
e3bb6401 | 968 | } else { |
43c3ffbe | 969 | // This course CANNOT have group events, so lose the filter |
fdbffa54 | 970 | } |
797cedc7 | 971 | $seturl->param('var', 'showuser'); |
ef21ad21 | 972 | $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_USER); |
48f508ab | 973 | } |
ef21ad21 | 974 | $content .= html_writer::end_tag('ul'); |
48f508ab | 975 | |
7423f116 | 976 | return $content; |
977 | } | |
978 | ||
08b4a4e1 RW |
979 | /** |
980 | * Return the representation day | |
981 | * | |
982 | * @param int $tstamp Timestamp in GMT | |
983 | * @param int $now current Unix timestamp | |
984 | * @param bool $usecommonwords | |
985 | * @return string the formatted date/time | |
986 | */ | |
7423f116 | 987 | function calendar_day_representation($tstamp, $now = false, $usecommonwords = true) { |
988 | ||
0ef7c973 | 989 | static $shortformat; |
990 | if(empty($shortformat)) { | |
e70fdac0 | 991 | $shortformat = get_string('strftimedayshort'); |
0ef7c973 | 992 | } |
993 | ||
7423f116 | 994 | if($now === false) { |
995 | $now = time(); | |
996 | } | |
997 | ||
998 | // To have it in one place, if a change is needed | |
e70fdac0 | 999 | $formal = userdate($tstamp, $shortformat); |
7423f116 | 1000 | |
7b38bfa6 | 1001 | $datestamp = usergetdate($tstamp); |
1002 | $datenow = usergetdate($now); | |
7423f116 | 1003 | |
1004 | if($usecommonwords == false) { | |
1005 | // We don't want words, just a date | |
1006 | return $formal; | |
1007 | } | |
7b38bfa6 | 1008 | else if($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday']) { |
7423f116 | 1009 | // Today |
1010 | return get_string('today', 'calendar'); | |
1011 | } | |
7b38bfa6 | 1012 | else if( |
1013 | ($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday'] - 1 ) || | |
1014 | ($datestamp['year'] == $datenow['year'] - 1 && $datestamp['mday'] == 31 && $datestamp['mon'] == 12 && $datenow['yday'] == 1) | |
1015 | ) { | |
7423f116 | 1016 | // Yesterday |
1017 | return get_string('yesterday', 'calendar'); | |
1018 | } | |
7b38bfa6 | 1019 | else if( |
1020 | ($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday'] + 1 ) || | |
1021 | ($datestamp['year'] == $datenow['year'] + 1 && $datenow['mday'] == 31 && $datenow['mon'] == 12 && $datestamp['yday'] == 1) | |
1022 | ) { | |
7423f116 | 1023 | // Tomorrow |
1024 | return get_string('tomorrow', 'calendar'); | |
1025 | } | |
1026 | else { | |
1027 | return $formal; | |
1028 | } | |
1029 | } | |
1030 | ||
08b4a4e1 RW |
1031 | /** |
1032 | * return the formatted representation time | |
1033 | * | |
1034 | * @param int $time the timestamp in UTC, as obtained from the database | |
1035 | * @return string the formatted date/time | |
1036 | */ | |
7423f116 | 1037 | function calendar_time_representation($time) { |
1b0ebe79 | 1038 | static $langtimeformat = NULL; |
1039 | if($langtimeformat === NULL) { | |
1040 | $langtimeformat = get_string('strftimetime'); | |
1041 | } | |
1042 | $timeformat = get_user_preferences('calendar_timeformat'); | |
c7dd2550 | 1043 | if(empty($timeformat)){ |
1044 | $timeformat = get_config(NULL,'calendar_site_timeformat'); | |
1045 | } | |
1b0ebe79 | 1046 | // The ? is needed because the preference might be present, but empty |
1047 | return userdate($time, empty($timeformat) ? $langtimeformat : $timeformat); | |
7423f116 | 1048 | } |
1049 | ||
e295df44 | 1050 | /** |
14236cbd SH |
1051 | * Adds day, month, year arguments to a URL and returns a moodle_url object. |
1052 | * | |
1053 | * @param string|moodle_url $linkbase | |
08b4a4e1 RW |
1054 | * @param int $d The number of the day. |
1055 | * @param int $m The number of the month. | |
1056 | * @param int $y The number of the year. | |
1057 | * @return moodle_url|null $linkbase | |
e295df44 | 1058 | */ |
7423f116 | 1059 | function calendar_get_link_href($linkbase, $d, $m, $y) { |
0f927f1e SH |
1060 | if (empty($linkbase)) { |
1061 | return ''; | |
1062 | } | |
1063 | if (!($linkbase instanceof moodle_url)) { | |
e30390a0 | 1064 | $linkbase = new moodle_url($linkbase); |
0f927f1e SH |
1065 | } |
1066 | if (!empty($d)) { | |
1067 | $linkbase->param('cal_d', $d); | |
1068 | } | |
1069 | if (!empty($m)) { | |
1070 | $linkbase->param('cal_m', $m); | |
1071 | } | |
1072 | if (!empty($y)) { | |
1073 | $linkbase->param('cal_y', $y); | |
1074 | } | |
1075 | return $linkbase; | |
7423f116 | 1076 | } |
1077 | ||
a84dea2c | 1078 | /** |
1079 | * Build and return a previous month HTML link, with an arrow. | |
14236cbd | 1080 | * |
a84dea2c | 1081 | * @param string $text The text label. |
14236cbd | 1082 | * @param string|moodle_url $linkbase The URL stub. |
08b4a4e1 RW |
1083 | * @param int $d The number of the date. |
1084 | * @param int $m The number of the month. | |
1085 | * @param int $y year The number of the year. | |
a84dea2c | 1086 | * @param bool $accesshide Default visible, or hide from all except screenreaders. |
1087 | * @return string HTML string. | |
1088 | */ | |
1089 | function calendar_get_link_previous($text, $linkbase, $d, $m, $y, $accesshide=false) { | |
0f927f1e SH |
1090 | $href = calendar_get_link_href(new moodle_url($linkbase), $d, $m, $y); |
1091 | if (empty($href)) { | |
1092 | return $text; | |
1093 | } | |
1094 | return link_arrow_left($text, (string)$href, $accesshide, 'previous'); | |
a84dea2c | 1095 | } |
1096 | ||
1097 | /** | |
1098 | * Build and return a next month HTML link, with an arrow. | |
1d5bd3d2 | 1099 | * |
a84dea2c | 1100 | * @param string $text The text label. |
14236cbd | 1101 | * @param string|moodle_url $linkbase The URL stub. |
08b4a4e1 RW |
1102 | * @param int $d the number of the Day |
1103 | * @param int $m The number of the month. | |
1104 | * @param int $y The number of the year. | |
a84dea2c | 1105 | * @param bool $accesshide Default visible, or hide from all except screenreaders. |
1106 | * @return string HTML string. | |
1107 | */ | |
1108 | function calendar_get_link_next($text, $linkbase, $d, $m, $y, $accesshide=false) { | |
0f927f1e SH |
1109 | $href = calendar_get_link_href(new moodle_url($linkbase), $d, $m, $y); |
1110 | if (empty($href)) { | |
1111 | return $text; | |
1112 | } | |
1113 | return link_arrow_right($text, (string)$href, $accesshide, 'next'); | |
a84dea2c | 1114 | } |
1115 | ||
08b4a4e1 RW |
1116 | /** |
1117 | * Return the name of the weekday | |
1118 | * | |
1119 | * @param string $englishname | |
1120 | * @return string of the weekeday | |
1121 | */ | |
7423f116 | 1122 | function calendar_wday_name($englishname) { |
1123 | return get_string(strtolower($englishname), 'calendar'); | |
1124 | } | |
1125 | ||
08b4a4e1 RW |
1126 | /** |
1127 | * Return the number of days in month | |
1128 | * | |
1129 | * @param int $month the number of the month. | |
1130 | * @param int $year the number of the year | |
1131 | * @return int | |
1132 | */ | |
7423f116 | 1133 | function calendar_days_in_month($month, $year) { |
7b38bfa6 | 1134 | return intval(date('t', mktime(0, 0, 0, $month, 1, $year))); |
7423f116 | 1135 | } |
1136 | ||
08b4a4e1 RW |
1137 | /** |
1138 | * Get the upcoming event block | |
1139 | * | |
1140 | * @param array $events list of events | |
1141 | * @param moodle_url|string $linkhref link to event referer | |
1142 | * @return string|null $content html block content | |
1143 | */ | |
6605ff8c | 1144 | function calendar_get_block_upcoming($events, $linkhref = NULL) { |
7423f116 | 1145 | $content = ''; |
1146 | $lines = count($events); | |
396b61f0 | 1147 | if (!$lines) { |
1148 | return $content; | |
1149 | } | |
7423f116 | 1150 | |
396b61f0 | 1151 | for ($i = 0; $i < $lines; ++$i) { |
b0ac9180 | 1152 | if (!isset($events[$i]->time)) { // Just for robustness |
1153 | continue; | |
1154 | } | |
9df8ff44 | 1155 | $events[$i] = calendar_add_event_metadata($events[$i]); |
857c5f90 | 1156 | $content .= '<div class="event"><span class="icon c0">'.$events[$i]->icon.'</span>'; |
43c3ffbe | 1157 | if (!empty($events[$i]->referer)) { |
7423f116 | 1158 | // That's an activity event, so let's provide the hyperlink |
396b61f0 | 1159 | $content .= $events[$i]->referer; |
1160 | } else { | |
e749554e | 1161 | if(!empty($linkhref)) { |
1162 | $ed = usergetdate($events[$i]->timestart); | |
0f927f1e SH |
1163 | $href = calendar_get_link_href(new moodle_url(CALENDAR_URL.$linkhref), $ed['mday'], $ed['mon'], $ed['year']); |
1164 | $href->set_anchor('event_'.$events[$i]->id); | |
1165 | $content .= html_writer::link($href, $events[$i]->name); | |
e749554e | 1166 | } |
1167 | else { | |
1168 | $content .= $events[$i]->name; | |
1169 | } | |
7423f116 | 1170 | } |
9ecf051d | 1171 | $events[$i]->time = str_replace('»', '<br />»', $events[$i]->time); |
1172 | $content .= '<div class="date">'.$events[$i]->time.'</div></div>'; | |
396b61f0 | 1173 | if ($i < $lines - 1) $content .= '<hr />'; |
7423f116 | 1174 | } |
1175 | ||
1176 | return $content; | |
7423f116 | 1177 | } |
1178 | ||
08b4a4e1 RW |
1179 | /** |
1180 | * Get the next following month | |
1181 | * | |
1182 | * If the current month is December, it will get the first month of the following year. | |
1183 | * | |
1184 | * | |
1185 | * @param int $month the number of the month. | |
1186 | * @param int $year the number of the year. | |
1187 | * @return array the following month | |
1188 | */ | |
7423f116 | 1189 | function calendar_add_month($month, $year) { |
1190 | if($month == 12) { | |
1191 | return array(1, $year + 1); | |
1192 | } | |
1193 | else { | |
1194 | return array($month + 1, $year); | |
1195 | } | |
1196 | } | |
1197 | ||
08b4a4e1 RW |
1198 | /** |
1199 | * Get the previous month | |
1200 | * | |
1201 | * If the current month is January, it will get the last month of the previous year. | |
1202 | * | |
1203 | * @param int $month the number of the month. | |
1204 | * @param int $year the number of the year. | |
1205 | * @return array previous month | |
1206 | */ | |
7423f116 | 1207 | function calendar_sub_month($month, $year) { |
1208 | if($month == 1) { | |
1209 | return array(12, $year - 1); | |
1210 | } | |
1211 | else { | |
1212 | return array($month - 1, $year); | |
1213 | } | |
1214 | } | |
1215 | ||
08b4a4e1 RW |
1216 | /** |
1217 | * Get per-day basis events | |
1218 | * | |
1219 | * @param array $events list of events | |
1220 | * @param int $month the number of the month | |
1221 | * @param int $year the number of the year | |
1222 | * @param array $eventsbyday event on specific day | |
1223 | * @param array $durationbyday duration of the event in days | |
1224 | * @param array $typesbyday event type (eg: global, course, user, or group) | |
1225 | * @param array $courses list of courses | |
1226 | * @return void | |
1227 | */ | |
7c50db30 | 1228 | function calendar_events_by_day($events, $month, $year, &$eventsbyday, &$durationbyday, &$typesbyday, &$courses) { |
7423f116 | 1229 | $eventsbyday = array(); |
1230 | $typesbyday = array(); | |
1231 | $durationbyday = array(); | |
1232 | ||
1233 | if($events === false) { | |
1234 | return; | |
1235 | } | |
1236 | ||
7423f116 | 1237 | foreach($events as $event) { |
7423f116 | 1238 | |
7b38bfa6 | 1239 | $startdate = usergetdate($event->timestart); |
b4892fa2 | 1240 | // Set end date = start date if no duration |
1241 | if ($event->timeduration) { | |
1242 | $enddate = usergetdate($event->timestart + $event->timeduration - 1); | |
1243 | } else { | |
1244 | $enddate = $startdate; | |
1245 | } | |
7423f116 | 1246 | |
7b38bfa6 | 1247 | // Simple arithmetic: $year * 13 + $month is a distinct integer for each distinct ($year, $month) pair |
ef618501 | 1248 | if(!($startdate['year'] * 13 + $startdate['mon'] <= $year * 13 + $month) && ($enddate['year'] * 13 + $enddate['mon'] >= $year * 13 + $month)) { |
7b38bfa6 | 1249 | // Out of bounds |
1250 | continue; | |
7423f116 | 1251 | } |
1252 | ||
7b38bfa6 | 1253 | $eventdaystart = intval($startdate['mday']); |
7423f116 | 1254 | |
7b38bfa6 | 1255 | if($startdate['mon'] == $month && $startdate['year'] == $year) { |
1256 | // Give the event to its day | |
1257 | $eventsbyday[$eventdaystart][] = $event->id; | |
7423f116 | 1258 | |
7b38bfa6 | 1259 | // Mark the day as having such an event |
9064751b | 1260 | if($event->courseid == SITEID && $event->groupid == 0) { |
7b38bfa6 | 1261 | $typesbyday[$eventdaystart]['startglobal'] = true; |
b4892fa2 | 1262 | // Set event class for global event |
90723839 | 1263 | $events[$event->id]->class = 'calendar_event_global'; |
7b38bfa6 | 1264 | } |
c3d3b6d4 | 1265 | else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { |
7b38bfa6 | 1266 | $typesbyday[$eventdaystart]['startcourse'] = true; |
b4892fa2 | 1267 | // Set event class for course event |
90723839 | 1268 | $events[$event->id]->class = 'calendar_event_course'; |
7b38bfa6 | 1269 | } |
1270 | else if($event->groupid) { | |
1271 | $typesbyday[$eventdaystart]['startgroup'] = true; | |
b4892fa2 | 1272 | // Set event class for group event |
90723839 | 1273 | $events[$event->id]->class = 'calendar_event_group'; |
7b38bfa6 | 1274 | } |
1275 | else if($event->userid) { | |
1276 | $typesbyday[$eventdaystart]['startuser'] = true; | |
b4892fa2 | 1277 | // Set event class for user event |
90723839 | 1278 | $events[$event->id]->class = 'calendar_event_user'; |
7b38bfa6 | 1279 | } |
1280 | } | |
7423f116 | 1281 | |
7b38bfa6 | 1282 | if($event->timeduration == 0) { |
1283 | // Proceed with the next | |
1284 | continue; | |
1285 | } | |
7423f116 | 1286 | |
7b38bfa6 | 1287 | // The event starts on $month $year or before. So... |
ef618501 | 1288 | $lowerbound = $startdate['mon'] == $month && $startdate['year'] == $year ? intval($startdate['mday']) : 0; |
7b38bfa6 | 1289 | |
1290 | // Also, it ends on $month $year or later... | |
1291 | $upperbound = $enddate['mon'] == $month && $enddate['year'] == $year ? intval($enddate['mday']) : calendar_days_in_month($month, $year); | |
1292 | ||
1293 | // Mark all days between $lowerbound and $upperbound (inclusive) as duration | |
1294 | for($i = $lowerbound + 1; $i <= $upperbound; ++$i) { | |
1295 | $durationbyday[$i][] = $event->id; | |
9064751b | 1296 | if($event->courseid == SITEID && $event->groupid == 0) { |
7b38bfa6 | 1297 | $typesbyday[$i]['durationglobal'] = true; |
1298 | } | |
c3d3b6d4 | 1299 | else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { |
7b38bfa6 | 1300 | $typesbyday[$i]['durationcourse'] = true; |
1301 | } | |
1302 | else if($event->groupid) { | |
1303 | $typesbyday[$i]['durationgroup'] = true; | |
1304 | } | |
1305 | else if($event->userid) { | |
1306 | $typesbyday[$i]['durationuser'] = true; | |
7423f116 | 1307 | } |
1308 | } | |
7b38bfa6 | 1309 | |
7423f116 | 1310 | } |
1311 | return; | |
1312 | } | |
1313 | ||
08b4a4e1 RW |
1314 | /** |
1315 | * Get current module cache | |
1316 | * | |
1317 | * @param array $coursecache list of course cache | |
1318 | * @param string $modulename name of the module | |
1319 | * @param int $instance module instance number | |
1320 | * @return stdClass|bool $module information | |
1321 | */ | |
b63c0ee5 | 1322 | function calendar_get_module_cached(&$coursecache, $modulename, $instance) { |
1323 | $module = get_coursemodule_from_instance($modulename, $instance); | |
7423f116 | 1324 | |
1325 | if($module === false) return false; | |
b63c0ee5 | 1326 | if(!calendar_get_course_cached($coursecache, $module->course)) { |
7423f116 | 1327 | return false; |
1328 | } | |
1329 | return $module; | |
1330 | } | |
1331 | ||
08b4a4e1 RW |
1332 | /** |
1333 | * Get current course cache | |
1334 | * | |
1335 | * @param array $coursecache list of course cache | |
1336 | * @param int $courseid id of the course | |
1337 | * @return stdClass $coursecache[$courseid] return the specific course cache | |
1338 | */ | |
7423f116 | 1339 | function calendar_get_course_cached(&$coursecache, $courseid) { |
62d11d77 | 1340 | global $COURSE, $DB; |
1341 | ||
b571c6b3 | 1342 | if (!isset($coursecache[$courseid])) { |
1343 | if ($courseid == $COURSE->id) { | |
1344 | $coursecache[$courseid] = $COURSE; | |
1345 | } else { | |
62d11d77 | 1346 | $coursecache[$courseid] = $DB->get_record('course', array('id'=>$courseid)); |
b571c6b3 | 1347 | } |
7423f116 | 1348 | } |
1349 | return $coursecache[$courseid]; | |
1350 | } | |
1351 | ||
797cedc7 SH |
1352 | /** |
1353 | * Returns the courses to load events for, the | |
1354 | * | |
797cedc7 | 1355 | * @param array $courseeventsfrom An array of courses to load calendar events for |
08b4a4e1 | 1356 | * @param bool $ignorefilters specify the use of filters, false is set as default |
797cedc7 SH |
1357 | * @return array An array of courses, groups, and user to load calendar events for based upon filters |
1358 | */ | |
1359 | function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) { | |
1360 | global $USER, $CFG, $DB; | |
7423f116 | 1361 | |
797cedc7 SH |
1362 | // For backwards compatability we have to check whether the courses array contains |
1363 | // just id's in which case we need to load course objects. | |
1364 | $coursestoload = array(); | |
1365 | foreach ($courseeventsfrom as $id => $something) { | |
1366 | if (!is_object($something)) { | |
1367 | $coursestoload[] = $id; | |
1368 | unset($courseeventsfrom[$id]); | |
753b799d | 1369 | } |
dd97c328 | 1370 | } |
797cedc7 SH |
1371 | if (!empty($coursestoload)) { |
1372 | // TODO remove this in 2.2 | |
1373 | debugging('calendar_set_filters now preferes an array of course objects with preloaded contexts', DEBUG_DEVELOPER); | |
1374 | $courseeventsfrom = array_merge($courseeventsfrom, $DB->get_records_list('course', 'id', $coursestoload)); | |
89adb174 | 1375 | } |
7423f116 | 1376 | |
797cedc7 SH |
1377 | $courses = array(); |
1378 | $user = false; | |
1379 | $group = false; | |
7423f116 | 1380 | |
7fd5786e HB |
1381 | // capabilities that allow seeing group events from all groups |
1382 | // TODO: rewrite so that moodle/calendar:manageentries is not necessary here | |
1383 | $allgroupscaps = array('moodle/site:accessallgroups', 'moodle/calendar:manageentries'); | |
1384 | ||
797cedc7 | 1385 | $isloggedin = isloggedin(); |
d12e3ff2 | 1386 | |
797cedc7 SH |
1387 | if ($ignorefilters || calendar_show_event_type(CALENDAR_EVENT_COURSE)) { |
1388 | $courses = array_keys($courseeventsfrom); | |
7423f116 | 1389 | } |
797cedc7 SH |
1390 | if ($ignorefilters || calendar_show_event_type(CALENDAR_EVENT_GLOBAL)) { |
1391 | $courses[] = SITEID; | |
7423f116 | 1392 | } |
797cedc7 SH |
1393 | $courses = array_unique($courses); |
1394 | sort($courses); | |
7c50db30 | 1395 | |
797cedc7 | 1396 | if (!empty($courses) && in_array(SITEID, $courses)) { |
7c50db30 | 1397 | // Sort courses for consistent colour highlighting |
1398 | // Effectively ignoring SITEID as setting as last course id | |
1399 | $key = array_search(SITEID, $courses); | |
797cedc7 SH |
1400 | unset($courses[$key]); |
1401 | $courses[] = SITEID; | |
0e6a8f4b | 1402 | } |
7423f116 | 1403 | |
797cedc7 SH |
1404 | if ($ignorefilters || ($isloggedin && calendar_show_event_type(CALENDAR_EVENT_USER))) { |
1405 | $user = $USER->id; | |
7423f116 | 1406 | } |
257e3f4c | 1407 | |
797cedc7 SH |
1408 | if (!empty($courseeventsfrom) && (calendar_show_event_type(CALENDAR_EVENT_GROUP) || $ignorefilters)) { |
1409 | ||
7fd5786e HB |
1410 | if (count($courseeventsfrom)==1) { |
1411 | $course = reset($courseeventsfrom); | |
6ca657a7 | 1412 | if (has_any_capability($allgroupscaps, context_course::instance($course->id))) { |
7fd5786e HB |
1413 | $coursegroups = groups_get_all_groups($course->id, 0, 0, 'g.id'); |
1414 | $group = array_keys($coursegroups); | |
7423f116 | 1415 | } |
7fd5786e HB |
1416 | } |
1417 | if ($group === false) { | |
1418 | if (!empty($CFG->calendar_adminseesall) && has_any_capability($allgroupscaps, get_system_context())) { | |
1419 | $group = true; | |
1420 | } else if ($isloggedin) { | |
1421 | $groupids = array(); | |
c46da759 | 1422 | |
7fd5786e HB |
1423 | // We already have the courses to examine in $courses |
1424 | // For each course... | |
1425 | foreach ($courseeventsfrom as $courseid => $course) { | |
1426 | // If the user is an editing teacher in there, | |
1427 | if (!empty($USER->groupmember[$course->id])) { | |
1428 | // We've already cached the users groups for this course so we can just use that | |
1429 | $groupids = array_merge($groupids, $USER->groupmember[$course->id]); | |
1430 | } else if ($course->groupmode != NOGROUPS || !$course->groupmodeforce) { | |
1431 | // If this course has groups, show events from all of those related to the current user | |
1432 | $coursegroups = groups_get_user_groups($course->id, $USER->id); | |
1433 | $groupids = array_merge($groupids, $coursegroups['0']); | |
1434 | } | |
1435 | } | |
1436 | if (!empty($groupids)) { | |
1437 | $group = $groupids; | |
1438 | } | |
6c9584d1 | 1439 | } |
7423f116 | 1440 | } |
7423f116 | 1441 | } |
797cedc7 SH |
1442 | if (empty($courses)) { |
1443 | $courses = false; | |
7423f116 | 1444 | } |
797cedc7 SH |
1445 | |
1446 | return array($courses, $group, $user); | |
7423f116 | 1447 | } |
1448 | ||
08b4a4e1 RW |
1449 | /** |
1450 | * Return the capability for editing calendar event | |
1451 | * | |
1452 | * @param calendar_event $event event object | |
1453 | * @return bool capability to edit event | |
1454 | */ | |
7423f116 | 1455 | function calendar_edit_event_allowed($event) { |
62d11d77 | 1456 | global $USER, $DB; |
7423f116 | 1457 | |
2ac8da76 | 1458 | // Must be logged in |
1459 | if (!isloggedin()) { | |
1460 | return false; | |
1461 | } | |
1462 | ||
89491dbd | 1463 | // can not be using guest account |
2396a414 | 1464 | if (isguestuser()) { |
e295df44 | 1465 | return false; |
89491dbd | 1466 | } |
e295df44 | 1467 | |
e6059873 SH |
1468 | // You cannot edit calendar subscription events presently. |
1469 | if (!empty($event->subscriptionid)) { | |
1470 | return false; | |
1471 | } | |
1472 | ||
6ca657a7 | 1473 | $sitecontext = context_system::instance(); |
89491dbd | 1474 | // if user has manageentries at site level, return true |
28ee98c5 | 1475 | if (has_capability('moodle/calendar:manageentries', $sitecontext)) { |
89491dbd | 1476 | return true; |
f52f7413 | 1477 | } |
e295df44 | 1478 | |
c0a2c361 | 1479 | // if groupid is set, it's definitely a group event |
438e4add | 1480 | if (!empty($event->groupid)) { |
f63d2922 | 1481 | // Allow users to add/edit group events if: |
1482 | // 1) They have manageentries (= entries for whole course) | |
1483 | // 2) They have managegroupentries AND are in the group | |
62d11d77 | 1484 | $group = $DB->get_record('groups', array('id'=>$event->groupid)); |
f63d2922 | 1485 | return $group && ( |
f4700b91 RW |
1486 | has_capability('moodle/calendar:manageentries', $event->context) || |
1487 | (has_capability('moodle/calendar:managegroupentries', $event->context) | |
f63d2922 | 1488 | && groups_is_member($event->groupid))); |
438e4add | 1489 | } else if (!empty($event->courseid)) { |
c0a2c361 | 1490 | // if groupid is not set, but course is set, |
1491 | // it's definiely a course event | |
f4700b91 | 1492 | return has_capability('moodle/calendar:manageentries', $event->context); |
438e4add | 1493 | } else if (!empty($event->userid) && $event->userid == $USER->id) { |
c0a2c361 | 1494 | // if course is not set, but userid id set, it's a user event |
f4700b91 RW |
1495 | return (has_capability('moodle/calendar:manageownentries', $event->context)); |
1496 | } else if (!empty($event->userid)) { | |
1497 | return (has_capability('moodle/calendar:manageentries', $event->context)); | |
e295df44 | 1498 | } |
7423f116 | 1499 | return false; |
1500 | } | |
1501 | ||
797cedc7 SH |
1502 | /** |
1503 | * Returns the default courses to display on the calendar when there isn't a specific | |
1504 | * course to display. | |
1505 | * | |
08b4a4e1 | 1506 | * @return array $courses Array of courses to display |
797cedc7 SH |
1507 | */ |
1508 | function calendar_get_default_courses() { | |
1509 | global $CFG, $DB; | |
7423f116 | 1510 | |
4f0c2d00 | 1511 | if (!isloggedin()) { |
2ef75eee | 1512 | return array(); |
1513 | } | |
1514 | ||
7423f116 | 1515 | $courses = array(); |
d083084a | 1516 | if (!empty($CFG->calendar_adminseesall) && has_capability('moodle/calendar:manageentries', context_system::instance())) { |
797cedc7 | 1517 | list ($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx'); |
d083084a | 1518 | $sql = "SELECT c.* $select |
797cedc7 | 1519 | FROM {course} c |
d083084a ARN |
1520 | $join |
1521 | WHERE EXISTS (SELECT 1 FROM {event} e WHERE e.courseid = c.id) | |
1522 | "; | |
797cedc7 SH |
1523 | $courses = $DB->get_records_sql($sql, null, 0, 20); |
1524 | foreach ($courses as $course) { | |
d083084a | 1525 | context_helper::preload_from_record($course); |
86f092d2 | 1526 | } |
797cedc7 | 1527 | return $courses; |
95a89225 | 1528 | } |
e295df44 | 1529 | |
df997f84 | 1530 | $courses = enrol_get_my_courses(); |
37d87d11 | 1531 | |
1532 | return $courses; | |
7423f116 | 1533 | } |
1534 | ||
08b4a4e1 RW |
1535 | /** |
1536 | * Display calendar preference button | |
1537 | * | |
1538 | * @param stdClass $course course object | |
1539 | * @return string return preference button in html | |
1540 | */ | |
797cedc7 SH |
1541 | function calendar_preferences_button(stdClass $course) { |
1542 | global $OUTPUT; | |
7423f116 | 1543 | |
1544 | // Guests have no preferences | |
4f0c2d00 | 1545 | if (!isloggedin() || isguestuser()) { |
7423f116 | 1546 | return ''; |
1547 | } | |
1548 | ||
797cedc7 | 1549 | return $OUTPUT->single_button(new moodle_url('/calendar/preferences.php', array('course' => $course->id)), get_string("preferences", "calendar")); |
7423f116 | 1550 | } |
1551 | ||
08b4a4e1 RW |
1552 | /** |
1553 | * Get event format time | |
1554 | * | |
1555 | * @param calendar_event $event event object | |
1556 | * @param int $now current time in gmt | |
1557 | * @param array $linkparams list of params for event link | |
1558 | * @param bool $usecommonwords the words as formatted date/time. | |
1559 | * @param int $showtime determine the show time GMT timestamp | |
1560 | * @return string $eventtime link/string for event time | |
1561 | */ | |
0f927f1e | 1562 | function calendar_format_event_time($event, $now, $linkparams = null, $usecommonwords = true, $showtime=0) { |
8f896582 | 1563 | $startdate = usergetdate($event->timestart); |
1564 | $enddate = usergetdate($event->timestart + $event->timeduration); | |
1565 | $usermidnightstart = usergetmidnight($event->timestart); | |
1566 | ||
1567 | if($event->timeduration) { | |
1568 | // To avoid doing the math if one IF is enough :) | |
1569 | $usermidnightend = usergetmidnight($event->timestart + $event->timeduration); | |
1570 | } | |
1571 | else { | |
1572 | $usermidnightend = $usermidnightstart; | |
1573 | } | |
1574 | ||
0f927f1e SH |
1575 | if (empty($linkparams) || !is_array($linkparams)) { |
1576 | $linkparams = array(); | |
1577 | } | |
1578 | $linkparams['view'] = 'day'; | |
1579 | ||
8f896582 | 1580 | // OK, now to get a meaningful display... |
1581 | // First of all we have to construct a human-readable date/time representation | |
1582 | ||
b4892fa2 | 1583 | if($event->timeduration) { |
8f896582 | 1584 | // It has a duration |
b4892fa2 | 1585 | if($usermidnightstart == $usermidnightend || |
1586 | ($event->timestart == $usermidnightstart) && ($event->timeduration == 86400 || $event->timeduration == 86399) || | |
1587 | ($event->timestart + $event->timeduration <= $usermidnightstart + 86400)) { | |
8f896582 | 1588 | // But it's all on the same day |
8f896582 | 1589 | $timestart = calendar_time_representation($event->timestart); |
1590 | $timeend = calendar_time_representation($event->timestart + $event->timeduration); | |
b4892fa2 | 1591 | $time = $timestart.' <strong>»</strong> '.$timeend; |
1592 | ||
1593 | if ($event->timestart == $usermidnightstart && ($event->timeduration == 86400 || $event->timeduration == 86399)) { | |
1594 | $time = get_string('allday', 'calendar'); | |
1595 | } | |
8f896582 | 1596 | |
1597 | // Set printable representation | |
b4892fa2 | 1598 | if (!$showtime) { |
1599 | $day = calendar_day_representation($event->timestart, $now, $usecommonwords); | |
0f927f1e SH |
1600 | $url = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', $linkparams), $enddate['mday'], $enddate['mon'], $enddate['year']); |
1601 | $eventtime = html_writer::link($url, $day).', '.$time; | |
b4892fa2 | 1602 | } else { |
1603 | $eventtime = $time; | |
1604 | } | |
1605 | } else { | |
8f896582 | 1606 | // It spans two or more days |
b4892fa2 | 1607 | $daystart = calendar_day_representation($event->timestart, $now, $usecommonwords).', '; |
1608 | if ($showtime == $usermidnightstart) { | |
1609 | $daystart = ''; | |
1610 | } | |
8f896582 | 1611 | $timestart = calendar_time_representation($event->timestart); |
b4892fa2 | 1612 | $dayend = calendar_day_representation($event->timestart + $event->timeduration, $now, $usecommonwords).', '; |
1613 | if ($showtime == $usermidnightend) { | |
1614 | $dayend = ''; | |
1615 | } | |
8f896582 | 1616 | $timeend = calendar_time_representation($event->timestart + $event->timeduration); |
1617 | ||
1618 | // Set printable representation | |
b4892fa2 | 1619 | if ($now >= $usermidnightstart && $now < ($usermidnightstart + 86400)) { |
0f927f1e SH |
1620 | $url = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', $linkparams), $enddate['mday'], $enddate['mon'], $enddate['year']); |
1621 | $eventtime = $timestart.' <strong>»</strong> '.html_writer::link($url, $dayend).$timeend; | |
b4892fa2 | 1622 | } else { |
0f927f1e SH |
1623 | $url = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', $linkparams), $enddate['mday'], $enddate['mon'], $enddate['year']); |
1624 | $eventtime = html_writer::link($url, $daystart).$timestart.' <strong>»</strong> '; | |
1d5bd3d2 | 1625 | |
0f927f1e SH |
1626 | $url = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', $linkparams), $startdate['mday'], $startdate['mon'], $startdate['year']); |
1627 | $eventtime .= html_writer::link($url, $dayend).$timeend; | |
b4892fa2 | 1628 | } |
8f896582 | 1629 | } |
b4892fa2 | 1630 | } else { |
2af268a1 | 1631 | $time = calendar_time_representation($event->timestart); |
8f896582 | 1632 | |
1633 | // Set printable representation | |
b4892fa2 | 1634 | if (!$showtime) { |
1635 | $day = calendar_day_representation($event->timestart, $now, $usecommonwords); | |
0f927f1e | 1636 | $url = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', $linkparams), $startdate['mday'], $startdate['mon'], $startdate['year']); |
2af268a1 | 1637 | $eventtime = html_writer::link($url, $day).', '.trim($time); |
b4892fa2 | 1638 | } else { |
1639 | $eventtime = $time; | |
1640 | } | |
1641 | } | |
1642 | ||
1643 | if($event->timestart + $event->timeduration < $now) { | |
1644 | // It has expired | |
1645 | $eventtime = '<span class="dimmed_text">'.str_replace(' href=', ' class="dimmed" href=', $eventtime).'</span>'; | |
8f896582 | 1646 | } |
09d36284 | 1647 | |
8f896582 | 1648 | return $eventtime; |
1649 | } | |
054193be | 1650 | |
08b4a4e1 RW |
1651 | /** |
1652 | * Display month selector options | |
1653 | * | |
1654 | * @param string $name for the select element | |
1655 | * @param string|array $selected options for select elements | |
1656 | */ | |
86f092d2 | 1657 | function calendar_print_month_selector($name, $selected) { |
86f092d2 | 1658 | $months = array(); |
86f092d2 | 1659 | for ($i=1; $i<=12; $i++) { |
76ab1c33 | 1660 | $months[$i] = userdate(gmmktime(12, 0, 0, $i, 15, 2000), '%B'); |
86f092d2 | 1661 | } |
5c576552 | 1662 | echo html_writer::label(get_string('months'), 'menu'. $name, false, array('class' => 'accesshide')); |
d776d59e | 1663 | echo html_writer::select($months, $name, $selected, false); |
86f092d2 | 1664 | } |
1665 | ||
797cedc7 SH |
1666 | /** |
1667 | * Checks to see if the requested type of event should be shown for the given user. | |
1668 | * | |
1669 | * @param CALENDAR_EVENT_GLOBAL|CALENDAR_EVENT_COURSE|CALENDAR_EVENT_GROUP|CALENDAR_EVENT_USER $type | |
1670 | * The type to check the display for (default is to display all) | |
1671 | * @param stdClass|int|null $user The user to check for - by default the current user | |
1672 | * @return bool True if the tyep should be displayed false otherwise | |
1673 | */ | |
1674 | function calendar_show_event_type($type, $user = null) { | |
1675 | $default = CALENDAR_EVENT_GLOBAL + CALENDAR_EVENT_COURSE + CALENDAR_EVENT_GROUP + CALENDAR_EVENT_USER; | |
1676 | if (get_user_preferences('calendar_persistflt', 0, $user) === 0) { | |
1677 | global $SESSION; | |
1678 | if (!isset($SESSION->calendarshoweventtype)) { | |
1679 | $SESSION->calendarshoweventtype = $default; | |
1680 | } | |
1681 | return $SESSION->calendarshoweventtype & $type; | |
1682 | } else { | |
1683 | return get_user_preferences('calendar_savedflt', $default, $user) & $type; | |
054193be | 1684 | } |
797cedc7 SH |
1685 | } |
1686 | ||
1687 | /** | |
1688 | * Sets the display of the event type given $display. | |
08b4a4e1 | 1689 | * |
797cedc7 SH |
1690 | * If $display = true the event type will be shown. |
1691 | * If $display = false the event type will NOT be shown. | |
1692 | * If $display = null the current value will be toggled and saved. | |
1693 | * | |
08b4a4e1 RW |
1694 | * @param CALENDAR_EVENT_GLOBAL|CALENDAR_EVENT_COURSE|CALENDAR_EVENT_GROUP|CALENDAR_EVENT_USER $type object of CALENDAR_EVENT_XXX |
1695 | * @param bool $display option to display event type | |
1696 | * @param stdClass|int $user moodle user object or id, null means current user | |
797cedc7 SH |
1697 | */ |
1698 | function calendar_set_event_type_display($type, $display = null, $user = null) { | |
1699 | $persist = get_user_preferences('calendar_persistflt', 0, $user); | |
1700 | $default = CALENDAR_EVENT_GLOBAL + CALENDAR_EVENT_COURSE + CALENDAR_EVENT_GROUP + CALENDAR_EVENT_USER; | |
1701 | if ($persist === 0) { | |
1702 | global $SESSION; | |
1703 | if (!isset($SESSION->calendarshoweventtype)) { | |
1704 | $SESSION->calendarshoweventtype = $default; | |
1705 | } | |
1706 | $preference = $SESSION->calendarshoweventtype; | |
1707 | } else { | |
1708 | $preference = get_user_preferences('calendar_savedflt', $default, $user); | |
054193be | 1709 | } |
797cedc7 SH |
1710 | $current = $preference & $type; |
1711 | if ($display === null) { | |
1712 | $display = !$current; | |
054193be | 1713 | } |
797cedc7 SH |
1714 | if ($display && !$current) { |
1715 | $preference += $type; | |
1716 | } else if (!$display && $current) { | |
1717 | $preference -= $type; | |
054193be | 1718 | } |
797cedc7 SH |
1719 | if ($persist === 0) { |
1720 | $SESSION->calendarshoweventtype = $preference; | |
1721 | } else { | |
1722 | if ($preference == $default) { | |
1723 | unset_user_preference('calendar_savedflt', $user); | |
1724 | } else { | |
1725 | set_user_preference('calendar_savedflt', $preference, $user); | |
1726 | } | |
054193be | 1727 | } |
054193be | 1728 | } |
1729 | ||
08b4a4e1 RW |
1730 | /** |
1731 | * Get calendar's allowed types | |
1732 | * | |
1733 | * @param stdClass $allowed list of allowed edit for event type | |
1734 | * @param stdClass|int $course object of a course or course id | |
1735 | */ | |
797cedc7 SH |
1736 | function calendar_get_allowed_types(&$allowed, $course = null) { |
1737 | global $USER, $CFG, $DB; | |
b85b25eb | 1738 | $allowed = new stdClass(); |
797cedc7 | 1739 | $allowed->user = has_capability('moodle/calendar:manageownentries', get_system_context()); |
86ac8b24 | 1740 | $allowed->groups = false; // This may change just below |
1741 | $allowed->courses = false; // This may change just below | |
6ca657a7 | 1742 | $allowed->site = has_capability('moodle/calendar:manageentries', context_course::instance(SITEID)); |
86ac8b24 | 1743 | |
797cedc7 SH |
1744 | if (!empty($course)) { |
1745 | if (!is_object($course)) { | |
1746 | $course = $DB->get_record('course', array('id' => $course), '*', MUST_EXIST); | |
1747 | } | |
1748 | if ($course->id != SITEID) { | |
6ca657a7 | 1749 | $coursecontext = context_course::instance($course->id); |
3c0ea9b2 | 1750 | $allowed->user = has_capability('moodle/calendar:manageownentries', $coursecontext); |
86ac8b24 | 1751 | |
797cedc7 SH |
1752 | if (has_capability('moodle/calendar:manageentries', $coursecontext)) { |
1753 | $allowed->courses = array($course->id => 1); | |
aa6c1ced | 1754 | |
797cedc7 | 1755 | if ($course->groupmode != NOGROUPS || !$course->groupmodeforce) { |
462e42cb JP |
1756 | if (has_capability('moodle/site:accessallgroups', $coursecontext)) { |
1757 | $allowed->groups = groups_get_all_groups($course->id); | |
1758 | } else { | |
1759 | $allowed->groups = groups_get_all_groups($course->id, $USER->id); | |
1760 | } | |
797cedc7 SH |
1761 | } |
1762 | } else if (has_capability('moodle/calendar:managegroupentries', $coursecontext)) { | |
1763 | if($course->groupmode != NOGROUPS || !$course->groupmodeforce) { | |
462e42cb JP |
1764 | if (has_capability('moodle/site:accessallgroups', $coursecontext)) { |
1765 | $allowed->groups = groups_get_all_groups($course->id); | |
1766 | } else { | |
1767 | $allowed->groups = groups_get_all_groups($course->id, $USER->id); | |
1768 | } | |
797cedc7 | 1769 | } |
f63d2922 | 1770 | } |
86ac8b24 | 1771 | } |
1772 | } | |
1773 | } | |
1774 | ||
1775 | /** | |
08b4a4e1 | 1776 | * See if user can add calendar entries at all |
86ac8b24 | 1777 | * used to print the "New Event" button |
08b4a4e1 RW |
1778 | * |
1779 | * @param stdClass $course object of a course or course id | |
1780 | * @return bool has the capability to add at least one event type | |
86ac8b24 | 1781 | */ |
797cedc7 SH |
1782 | function calendar_user_can_add_event($course) { |
1783 | if (!isloggedin() || isguestuser()) { | |
1784 | return false; | |
1785 | } | |
1786 | calendar_get_allowed_types($allowed, $course); | |
e295df44 | 1787 | return (bool)($allowed->user || $allowed->groups || $allowed->courses || $allowed->site); |
86ac8b24 | 1788 | } |
76d9df3f SH |
1789 | |
1790 | /** | |
1791 | * Check wether the current user is permitted to add events | |
1792 | * | |
08b4a4e1 RW |
1793 | * @param stdClass $event object of event |
1794 | * @return bool has the capability to add event | |
76d9df3f SH |
1795 | */ |
1796 | function calendar_add_event_allowed($event) { | |
1797 | global $USER, $DB; | |
1798 | ||
1799 | // can not be using guest account | |
4f0c2d00 | 1800 | if (!isloggedin() or isguestuser()) { |
76d9df3f SH |
1801 | return false; |
1802 | } | |
1803 | ||
6ca657a7 | 1804 | $sitecontext = context_system::instance(); |
76d9df3f SH |
1805 | // if user has manageentries at site level, always return true |
1806 | if (has_capability('moodle/calendar:manageentries', $sitecontext)) { | |
1807 | return true; | |
1808 | } | |
1809 | ||
1810 | switch ($event->eventtype) { | |
1811 | case 'course': | |
f4700b91 | 1812 | return has_capability('moodle/calendar:manageentries', $event->context); |
76d9df3f SH |
1813 | |
1814 | case 'group': | |
1815 | // Allow users to add/edit group events if: | |
1816 | // 1) They have manageentries (= entries for whole course) | |
1817 | // 2) They have managegroupentries AND are in the group | |
1818 | $group = $DB->get_record('groups', array('id'=>$event->groupid)); | |
1819 | return $group && ( | |
f4700b91 RW |
1820 | has_capability('moodle/calendar:manageentries', $event->context) || |
1821 | (has_capability('moodle/calendar:managegroupentries', $event->context) | |
76d9df3f SH |
1822 | && groups_is_member($event->groupid))); |
1823 | ||
1824 | case 'user': | |
1825 | if ($event->userid == $USER->id) { | |
f4700b91 | 1826 | return (has_capability('moodle/calendar:manageownentries', $event->context)); |
76d9df3f SH |
1827 | } |
1828 | //there is no 'break;' intentionally | |
1829 | ||
1830 | case 'site': | |
f4700b91 | 1831 | return has_capability('moodle/calendar:manageentries', $event->context); |
76d9df3f SH |
1832 | |
1833 | default: | |
f4700b91 | 1834 | return has_capability('moodle/calendar:manageentries', $event->context); |
76d9df3f SH |
1835 | } |
1836 | } | |
1837 | ||
1838 | /** | |
08b4a4e1 | 1839 | * Manage calendar events |
76d9df3f SH |
1840 | * |
1841 | * This class provides the required functionality in order to manage calendar events. | |
1842 | * It was introduced as part of Moodle 2.0 and was created in order to provide a | |
1843 | * better framework for dealing with calendar events in particular regard to file | |
1844 | * handling through the new file API | |
1845 | * | |
08b4a4e1 RW |
1846 | * @package core_calendar |
1847 | * @category calendar | |
1848 | * @copyright 2009 Sam Hemelryk | |
1849 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
1850 | * | |
76d9df3f SH |
1851 | * @property int $id The id within the event table |
1852 | * @property string $name The name of the event | |
1853 | * @property string $description The description of the event | |
1854 | * @property int $format The format of the description FORMAT_? | |
1855 | * @property int $courseid The course the event is associated with (0 if none) | |
1856 | * @property int $groupid The group the event is associated with (0 if none) | |
1857 | * @property int $userid The user the event is associated with (0 if none) | |
1858 | * @property int $repeatid If this is a repeated event this will be set to the | |
1859 | * id of the original | |
1860 | * @property string $modulename If added by a module this will be the module name | |
1861 | * @property int $instance If added by a module this will be the module instance | |
1862 | * @property string $eventtype The event type | |
1863 | * @property int $timestart The start time as a timestamp | |
1864 | * @property int $timeduration The duration of the event in seconds | |
1865 | * @property int $visible 1 if the event is visible | |
1866 | * @property int $uuid ? | |
1867 | * @property int $sequence ? | |
1868 | * @property int $timemodified The time last modified as a timestamp | |
1869 | */ | |
1870 | class calendar_event { | |
1871 | ||
08b4a4e1 | 1872 | /** @var array An object containing the event properties can be accessed via the magic __get/set methods */ |
76d9df3f | 1873 | protected $properties = null; |
08b4a4e1 | 1874 | |
76d9df3f | 1875 | /** |
08b4a4e1 | 1876 | * @var string The converted event discription with file paths resolved. This gets populated when someone requests description for the first time */ |
76d9df3f | 1877 | protected $_description = null; |
08b4a4e1 RW |
1878 | |
1879 | /** @var array The options to use with this description editor */ | |
76d9df3f SH |
1880 | protected $editoroptions = array( |
1881 | 'subdirs'=>false, | |
1882 | 'forcehttps'=>false, | |
2b35ed1a | 1883 | 'maxfiles'=>-1, |
76d9df3f SH |
1884 | 'maxbytes'=>null, |
1885 | 'trusttext'=>false); | |
08b4a4e1 RW |
1886 | |
1887 | /** @var object The context to use with the description editor */ | |
76d9df3f SH |
1888 | protected $editorcontext = null; |
1889 | ||
1890 | /** | |
1891 | * Instantiates a new event and optionally populates its properties with the | |
1892 | * data provided | |
1893 | * | |
1894 | * @param stdClass $data Optional. An object containing the properties to for | |
1895 | * an event | |
1896 | */ | |
1897 | public function __construct($data=null) { | |
c203a277 | 1898 | global $CFG, $USER; |
76d9df3f SH |
1899 | |
1900 | // First convert to object if it is not already (should either be object or assoc array) | |
1901 | if (!is_object($data)) { | |
1902 | $data = (object)$data; | |
1903 | } | |
1904 | ||
1905 | $this->editoroptions['maxbytes'] = $CFG->maxbytes; | |
1906 | ||
1907 | $data->eventrepeats = 0; | |
1908 | ||
1909 | if (empty($data->id)) { | |
1910 | $data->id = null; | |
1911 | } | |
1912 | ||
c203a277 SH |
1913 | // Default to a user event |
1914 | if (empty($data->eventtype)) { | |
1915 | $data->eventtype = 'user'; | |
1916 | } | |
1917 | ||
1918 | // Default to the current user | |
1919 | if (empty($data->userid)) { | |
1920 | $data->userid = $USER->id; | |
1921 | } | |
1922 | ||
76d9df3f SH |
1923 | if (!empty($data->timeduration) && is_array($data->timeduration)) { |
1924 | $data->timeduration = make_timestamp($data->timeduration['year'], $data->timeduration['month'], $data->timeduration['day'], $data->timeduration['hour'], $data->timeduration['minute']) - $data->timestart; | |
1925 | } | |
1926 | if (!empty($data->description) && is_array($data->description)) { | |
1927 | $data->format = $data->description['format']; | |
1928 | $data->description = $data->description['text']; | |
1929 | } else if (empty($data->description)) { | |
1930 | $data->description = ''; | |
20e5da7d | 1931 | $data->format = editors_get_preferred_format(); |
76d9df3f | 1932 | } |
c203a277 SH |
1933 | // Ensure form is defaulted correctly |
1934 | if (empty($data->format)) { | |
1935 | $data->format = editors_get_preferred_format(); | |
1936 | } | |
76d9df3f | 1937 | |
f4700b91 RW |
1938 | if (empty($data->context)) { |
1939 | $data->context = $this->calculate_context($data); | |
1940 | } | |
76d9df3f SH |
1941 | $this->properties = $data; |
1942 | } | |
1943 | ||
1944 | /** | |
1945 | * Magic property method | |
1946 | * | |
1947 | * Attempts to call a set_$key method if one exists otherwise falls back | |
1948 | * to simply set the property | |
1949 | * | |
08b4a4e1 RW |
1950 | * @param string $key property name |
1951 | * @param mixed $value value of the property | |
76d9df3f SH |
1952 | */ |
1953 | public function __set($key, $value) { | |
1954 | if (method_exists($this, 'set_'.$key)) { | |
1955 | $this->{'set_'.$key}($value); | |
1956 | } | |
1957 | $this->properties->{$key} = $value; | |
1958 | } | |
1959 | ||
1960 | /** | |
1961 | * Magic get method | |
1962 | * | |
1963 | * Attempts to call a get_$key method to return the property and ralls over | |
1964 | * to return the raw property | |
1965 | * | |
08b4a4e1 RW |
1966 | * @param string $key property name |
1967 | * @return mixed property value | |
76d9df3f SH |
1968 | */ |
1969 | public function __get($key) { | |
1970 | if (method_exists($this, 'get_'.$key)) { | |
1971 | return $this->{'get_'.$key}(); | |
1972 | } | |
c203a277 SH |
1973 | if (!isset($this->properties->{$key})) { |
1974 | throw new coding_exception('Undefined property requested'); | |
1975 | } | |
76d9df3f SH |
1976 | return $this->properties->{$key}; |
1977 | } | |
1978 | ||
1979 | /** | |
1980 | * Stupid PHP needs an isset magic method if you use the get magic method and | |
1981 | * still want empty calls to work.... blah ~! | |
1982 | * | |
08b4a4e1 RW |
1983 | * @param string $key $key property name |
1984 | * @return bool|mixed property value, false if property is not exist | |
76d9df3f SH |
1985 | */ |
1986 | public function __isset($key) { | |
1987 | return !empty($this->properties->{$key}); | |
1988 | } | |
1989 | ||
f4700b91 RW |
1990 | /** |
1991 | * Calculate the context value needed for calendar_event. | |
1992 | * Event's type can be determine by the available value store in $data | |
1993 | * It is important to check for the existence of course/courseid to determine | |
1994 | * the course event. | |
1995 | * Default value is set to CONTEXT_USER | |
1996 | * | |
08b4a4e1 RW |
1997 | * @param stdClass $data information about event |
1998 | * @return stdClass The context object. | |
f4700b91 RW |
1999 | */ |
2000 | protected function calculate_context(stdClass $data) { | |
025d82e0 | 2001 | global $USER, $DB; |
f4700b91 | 2002 | |
f4700b91 RW |
2003 | $context = null; |
2004 | if (isset($data->courseid) && $data->courseid > 0) { | |
6ca657a7 | 2005 | $context = context_course::instance($data->courseid); |
f4700b91 | 2006 | } else if (isset($data->course) && $data->course > 0) { |
6ca657a7 | 2007 | $context = context_course::instance($data->course); |
f4700b91 RW |
2008 | } else if (isset($data->groupid) && $data->groupid > 0) { |
2009 | $group = $DB->get_record('groups', array('id'=>$data->groupid)); | |
6ca657a7 | 2010 | $context = context_course::instance($group->courseid); |
f4700b91 | 2011 | } else if (isset($data->userid) && $data->userid > 0 && $data->userid == $USER->id) { |
6ca657a7 | 2012 | $context = context_user::instance($data->userid); |
f4700b91 RW |
2013 | } else if (isset($data->userid) && $data->userid > 0 && $data->userid != $USER->id && |
2014 | isset($data->instance) && $data->instance > 0) { | |
18d63ffb | 2015 | $cm = get_coursemodule_from_instance($data->modulename, $data->instance, 0, false, MUST_EXIST); |
6ca657a7 | 2016 | $context = context_course::instance($cm->course); |
f4700b91 | 2017 | } else { |
e30390a0 | 2018 | $context = context_user::instance($data->userid); |
f4700b91 RW |
2019 | } |
2020 | ||
2021 | return $context; | |
2022 | } | |
2023 | ||
76d9df3f SH |
2024 | /** |
2025 | * Returns an array of editoroptions for this event: Called by __get | |
2026 | * Please use $blah = $event->editoroptions; | |
08b4a4e1 RW |
2027 | * |
2028 | * @return array event editor options | |
76d9df3f SH |
2029 | */ |
2030 | protected function get_editoroptions() { | |
2031 | return $this->editoroptions; | |
2032 | } | |
2033 | ||
2034 | /** | |
2035 | * Returns an event description: Called by __get | |
2036 | * Please use $blah = $event->description; | |
2037 | * | |
08b4a4e1 | 2038 | * @return string event description |
76d9df3f SH |
2039 | */ |
2040 | protected function get_description() { | |
f4700b91 | 2041 | global $CFG; |
99d19c13 PS |
2042 | |
2043 | require_once($CFG->libdir . '/filelib.php'); | |
2044 | ||
76d9df3f SH |
2045 | if ($this->_description === null) { |
2046 | // Check if we have already resolved the context for this event | |
2047 | if ($this->editorcontext === null) { | |
2048 | // Switch on the event type to decide upon the appropriate context | |
2049 | // to use for this event | |
f4700b91 RW |
2050 | $this->editorcontext = $this->properties->context; |
2051 | if ($this->properties->eventtype != 'user' && $this->properties->eventtype != 'course' | |
2052 | && $this->properties->eventtype != 'site' && $this->properties->eventtype != 'group') { | |
2053 | return clean_text($this->properties->description, $this->properties->format); | |
76d9df3f SH |
2054 | } |
2055 | } | |
2056 | ||
2057 | // Work out the item id for the editor, if this is a repeated event then the files will | |
2058 | // be associated with the original | |
2059 | if (!empty($this->properties->repeatid) && $this->properties->repeatid > 0) { | |
2060 | $itemid = $this->properties->repeatid; | |
2061 | } else { | |
2062 | $itemid = $this->properties->id; | |
2063 | } | |
2064 | ||
2065 | // Convert file paths in the description so that things display correctly | |
64f93798 | 2066 | $this->_description = file_rewrite_pluginfile_urls($this->properties->description, 'pluginfile.php', $this->editorcontext->id, 'calendar', 'event_description', $itemid); |
76d9df3f SH |
2067 | // Clean the text so no nasties get through |
2068 | $this->_description = clean_text($this->_description, $this->properties->format); | |
2069 | } | |
2070 | // Finally return the description | |
2071 | return $this->_description; | |
2072 | } | |
2073 | ||
2074 | /** | |
2075 | * Return the number of repeat events there are in this events series | |
aa6c1ced | 2076 | * |
08b4a4e1 | 2077 | * @return int number of event repeated |
76d9df3f SH |
2078 | */ |
2079 | public function count_repeats() { | |
2080 | global $DB; | |
2081 | if (!empty($this->properties->repeatid)) { | |
2082 | $this->properties->eventrepeats = $DB->count_records('event', array('repeatid'=>$this->properties->repeatid)); | |
2083 | // We don't want to count ourselves | |
2084 | $this->properties->eventrepeats--; | |
2085 | } | |
2086 | return $this->properties->eventrepeats; | |
2087 | } | |
2088 | ||
2089 | /** | |
2090 | * Update or create an event within the database | |
2091 | * | |
2092 | * Pass in a object containing the event properties and this function will | |
2093 | * insert it into the database and deal with any associated files | |
2094 | * | |
2095 | * @see add_event() | |
2096 | * @see update_event() | |
2097 | * | |
08b4a4e1 RW |
2098 | * @param stdClass $data object of event |
2099 | * @param bool $checkcapability if moodle should check calendar managing capability or not | |
2100 | * @return bool event updated | |
76d9df3f | 2101 | */ |
1d5bd3d2 | 2102 | public function update($data, $checkcapability=true) { |
76d9df3f SH |
2103 | global $CFG, $DB, $USER; |
2104 | ||
438e4add SH |
2105 | foreach ($data as $key=>$value) { |
2106 | $this->properties->$key = $value; | |
2107 | } | |
2108 | ||
76d9df3f SH |
2109 | $this->properties->timemodified = time(); |
2110 | $usingeditor = (!empty($this->properties->description) && is_array($this->properties->description)); | |
2111 | ||
2112 | if (empty($this->properties->id) || $this->properties->id < 1) { | |
2113 | ||
1d5bd3d2 DC |
2114 | if ($checkcapability) { |
2115 | if (!calendar_add_event_allowed($this->properties)) { | |
2116 | print_error('nopermissiontoupdatecalendar'); | |
2117 | } | |
76d9df3f SH |
2118 | } |
2119 | ||
2120 | if ($usingeditor) { | |
2121 | switch ($this->properties->eventtype) { | |
2122 | case 'user': | |
76d9df3f | 2123 | $this->properties->courseid = 0; |
f19d57e5 | 2124 | $this->properties->course = 0; |
76d9df3f SH |
2125 | $this->properties->groupid = 0; |
2126 | $this->properties->userid = $USER->id; | |
2127 | break; | |
2128 | case 'site': | |
76d9df3f | 2129 | $this->properties->courseid = SITEID; |
f19d57e5 | 2130 | $this->properties->course = SITEID; |
76d9df3f SH |
2131 | $this->properties->groupid = 0; |
2132 | $this->properties->userid = $USER->id; | |
2133 | break; | |
2134 | case 'course': | |
76d9df3f SH |
2135 | $this->properties->groupid = 0; |
2136 | $this->properties->userid = $USER->id; | |
2137 | break; | |
2138 | case 'group': | |
76d9df3f SH |
2139 | $this->properties->userid = $USER->id; |
2140 | break; | |
2141 | default: | |
2142 | // Ewww we should NEVER get here, but just incase we do lets | |
2143 | // fail gracefully | |
2144 | $usingeditor = false; | |
2145 | break; | |
2146 | } | |
2147 | ||
f19d57e5 FM |
2148 | // If we are actually using the editor, we recalculate the context because some default values |
2149 | // were set when calculate_context() was called from the constructor. | |
2150 | if ($usingeditor) { | |
2151 | $this->properties->context = $this->calculate_context($this->properties); | |
2152 | $this->editorcontext = $this->properties->context; | |
2153 | } | |
2154 | ||
76d9df3f SH |
2155 | $editor = $this->properties->description; |
2156 | $this->properties->format = $this->properties->description['format']; | |
2157 | $this->properties->description = $this->properties->description['text']; | |
2158 | } | |
2159 | ||
2160 | // Insert the event into the database | |
2161 | $this->properties->id = $DB->insert_record('event', $this->properties); | |
2162 | ||
2163 | if ($usingeditor) { | |
2164 | $this->properties->description = file_save_draft_area_files( | |
2165 | $editor['itemid'], | |
2166 | $this->editorcontext->id, | |
64f93798 PS |
2167 | 'calendar', |
2168 | 'event_description', | |
76d9df3f SH |
2169 | $this->properties->id, |
2170 | $this->editoroptions, | |
2171 | $editor['text'], | |
2172 | $this->editoroptions['forcehttps']); | |
76d9df3f SH |
2173 | $DB->set_field('event', 'description', $this->properties->description, array('id'=>$this->properties->id)); |
2174 | } | |
aa6c1ced | 2175 | |
76d9df3f SH |
2176 | // Log the event entry. |
2177 | add_to_log($this->properties->courseid, 'calendar', 'add', 'event.php?action=edit&id='.$this->properties->id, $this->properties->name); | |
2178 | ||
2179 | $repeatedids = array(); | |
2180 | ||
2181 | if (!empty($this->properties->repeat)) { | |
2182 | $this->properties->repeatid = $this->properties->id; | |
2183 | $DB->set_field('event', 'repeatid', $this->properties->repeatid, array('id'=>$this->properties->id)); | |
2184 | ||
2185 | $eventcopy = clone($this->properties); | |
2186 | unset($eventcopy->id); | |
2187 | ||
2188 | for($i = 1; $i < $eventcopy->repeats; $i++) { | |
2189 | ||
2190 | $eventcopy->timestart = ($eventcopy->timestart+WEEKSECS) + dst_offset_on($eventcopy->timestart) - dst_offset_on($eventcopy->timestart+WEEKSECS); | |
2191 | ||
2192 | // Get the event id for the log record. | |
2193 | $eventcopyid = $DB->insert_record('event', $eventcopy); | |
2194 | ||
2195 | // If the context has been set delete all associated files | |
2196 | if ($usingeditor) { | |
2197 | $fs = get_file_storage(); | |
64f93798 | 2198 | $files = $fs->get_area_files($this->editorcontext->id, 'calendar', 'event_description', $this->properties->id); |
76d9df3f SH |
2199 | foreach ($files as $file) { |
2200 | $fs->create_file_from_storedfile(array('itemid'=>$eventcopyid), $file); | |
2201 | } | |
2202 | } | |
2203 | ||
2204 | $repeatedids[] = $eventcopyid; | |
2205 | // Log the event entry. | |
2206 | add_to_log($eventcopy->courseid, 'calendar', 'add', 'event.php?action=edit&id='.$eventcopyid, $eventcopy->name); | |
2207 | } | |
2208 | } | |
2209 | ||
2210 | // Hook for tracking added events | |
2211 | self::calendar_event_hook('add_event', array($this->properties, $repeatedids)); | |
2212 | return true; | |
2213 | } else { | |
2214 | ||
1d5bd3d2 DC |
2215 | if ($checkcapability) { |
2216 | if(!calendar_edit_event_allowed($this->properties)) { | |
2217 | print_error('nopermissiontoupdatecalendar'); | |
2218 | } | |
76d9df3f SH |
2219 | } |
2220 | ||
2221 | if ($usingeditor) { | |
2222 | if ($this->editorcontext !== null) { | |
2223 | $this->properties->description = file_save_draft_area_files( | |
2224 | $this->properties->description['itemid'], | |
2225 | $this->editorcontext->id, | |
64f93798 PS |
2226 | 'calendar', |
2227 | 'event_description', | |
76d9df3f SH |
2228 | $this->properties->id, |
2229 | $this->editoroptions, | |
2230 | $this->properties->description['text'], | |
2231 | $this->editoroptions['forcehttps']); | |
2232 | } else { | |
2233 | $this->properties->format = $this->properties->description['format']; | |
2234 | $this->properties->description = $this->properties->description['text']; | |
2235 | } | |
2236 | } | |
2237 | ||
2238 | $event = $DB->get_record('event', array('id'=>$this->properties->id)); | |
2239 | ||
2240 | $updaterepeated = (!empty($this->properties->repeatid) && !empty($this->properties->repeateditall)); | |
2241 | ||
2242 | if ($updaterepeated) { | |
2243 | // Update all | |
2244 | if ($this->properties->timestart != $event->timestart) { | |
2245 | $timestartoffset = $this->properties->timestart - $event->timestart; | |
2246 | $sql = "UPDATE {event} | |
2247 | SET name = ?, | |
2248 | description = ?, | |
2249 | timestart = timestart + ?, | |
2250 | timeduration = ?, | |
2251 | timemodified = ? | |
2252 | WHERE repeatid = ?"; | |
2253 | $params = array($this->properties->name, $this->properties->description, $timestartoffset, $this->properties->timeduration, time(), $event->repeatid); | |
2254 | } else { | |
2255 | $sql = "UPDATE {event} SET name = ?, description = ?, timeduration = ?, timemodified = ? WHERE repeatid = ?"; | |
2256 | $params = array($this->properties->name, $this->properties->description, $this->properties->timeduration, time(), $event->repeatid); | |
2257 | } | |
2258 | $DB->execute($sql, $params); | |
2259 | ||
2260 | // Log the event update. | |
2261 | add_to_log($this->properties->courseid, 'calendar', 'edit all', 'event.php?action=edit&id='.$this->properties->id, $this->properties->name); | |
2262 | } else { | |
2263 | $DB->update_record('event', $this->properties); | |
ddaff608 SH |
2264 | $event = calendar_event::load($this->properties->id); |
2265 | $this->properties = $event->properties(); | |
76d9df3f SH |
2266 | add_to_log($this->properties->courseid, 'calendar', 'edit', 'event.php?action=edit&id='.$this->properties->id, $this->properties->name); |
2267 | } | |
2268 | ||
2269 | // Hook for tracking event updates | |
2270 | self::calendar_event_hook('update_event', array($this->properties, $updaterepeated)); | |
2271 | return true; | |
2272 | } | |
2273 | } | |
2274 | ||
2275 | /** | |
2276 | * Deletes an event and if selected an repeated events in the same series | |
2277 | * | |
2278 | * This function deletes an event, any associated events if $deleterepeated=true, | |
2279 | * and cleans up any files associated with the events. | |
2280 | * | |
2281 | * @see delete_event() | |
2282 | * | |
08b4a4e1 RW |
2283 | * @param bool $deleterepeated delete event repeatedly |
2284 | * @return bool succession of deleting event | |
76d9df3f SH |
2285 | */ |
2286 | public function delete($deleterepeated=false) { | |
f4700b91 | 2287 | global $DB; |
76d9df3f SH |
2288 | |
2289 | // If $this->properties->id is not set then something is wrong | |
2290 | if (empty($this->properties->id)) { | |
2291 | debugging('Attempting to delete an event before it has been loaded', DEBUG_DEVELOPER); | |
2292 | return false; | |
2293 | } | |
2294 | ||
2295 | // Delete the event | |
2296 | $DB->delete_records('event', array('id'=>$this->properties->id)); | |
2297 | ||
ff284fac AA |
2298 | // If we are deleting parent of a repeated event series, promote the next event in the series as parent |
2299 | if (($this->properties->id == $this->properties->repeatid) && !$deleterepeated) { | |
2300 | $newparent = $DB->get_field_sql("SELECT id from {event} where repeatid = ? order by id ASC", array($this->properties->id), IGNORE_MULTIPLE); | |
2301 | if (!empty($newparent)) { | |
2302 | $DB->execute("UPDATE {event} SET repeatid = ? WHERE repeatid = ?", array($newparent, $this->properties->id)); | |
5ff114ad AA |
2303 | // Get all records where the repeatid is the same as the event being removed |
2304 | $events = $DB->get_records('event', array('repeatid' => $newparent)); | |
2305 | // For each of the returned events trigger the event_update hook. | |
2306 | foreach ($events as $event) { | |
2307 | self::calendar_event_hook('update_event', array($event, false)); | |
2308 | } | |
ff284fac AA |
2309 | } |
2310 | } | |
2311 | ||
76d9df3f SH |
2312 | // If the editor context hasn't already been set then set it now |
2313 | if ($this->editorcontext === null) { | |
f4700b91 | 2314 | $this->editorcontext = $this->properties->context; |
76d9df3f SH |
2315 | } |
2316 | ||
2317 | // If the context has been set delete all associated files | |
2318 | if ($this->editorcontext !== null) { | |
2319 | $fs = get_file_storage(); | |
64f93798 | 2320 | $files = $fs->get_area_files($this->editorcontext->id, 'calendar', 'event_description', $this->properties->id); |
76d9df3f SH |
2321 | foreach ($files as $file) { |
2322 | $file->delete(); | |
2323 | } | |
2324 | } | |
2325 | ||
2326 | // Fire the event deleted hook | |
2327 | self::calendar_event_hook('delete_event', array($this->properties->id, $deleterepeated)); | |
2328 | ||
2329 | // If we need to delete repeated events then we will fetch them all and delete one by one | |
2330 | if ($deleterepeated && !empty($this->properties->repeatid) && $this->properties->repeatid > 0) { | |
2331 | // Get all records where the repeatid is the same as the event being removed | |
2332 | $events = $DB->get_records('event', array('repeatid'=>$this->properties->repeatid)); | |
2333 | // For each of the returned events populate a calendar_event object and call delete | |
2334 | // make sure the arg passed is false as we are already deleting all repeats | |
2335 | foreach ($events as $event) { | |
2336 | $event = new calendar_event($event); | |
2337 | $event->delete(false); | |
2338 | } | |
2339 | } | |
2340 | ||
2341 | return true; | |
2342 | } | |
2343 | ||
2344 | /** | |
2345 | * Fetch all event properties | |
2346 | * | |
2347 | * This function returns all of the events properties as an object and optionally | |
2348 | * can prepare an editor for the description field at the same time. This is | |
2349 | * designed to work when the properties are going to be used to set the default | |
2350 | * values of a moodle forms form. | |
2351 | * | |
2352 | * @param bool $prepareeditor If set to true a editor is prepared for use with | |
2353 | * the mforms editor element. (for description) | |
2354 | * @return stdClass Object containing event properties | |
2355 | */ | |
2356 | public function properties($prepareeditor=false) { | |
2357 | global $USER, $CFG, $DB; | |
2358 | ||
2359 | // First take a copy of the properties. We don't want to actually change the | |
2360 | // properties or we'd forever be converting back and forwards between an | |
2361 | // editor formatted description and not | |
2362 | $properties = clone($this->properties); | |
2363 | // Clean the description here | |
2364 | $properties->description = clean_text($properties->description, $properties->format); | |
2365 | ||
2366 | // If set to true we need to prepare the properties for use with an editor | |
2367 | // and prepare the file area | |
2368 | if ($prepareeditor) { | |
2369 | ||
2370 | // We may or may not have a property id. If we do then we need to work | |
2371 | // out the context so we can copy the existing files to the draft area | |
2372 | if (!empty($properties->id)) { | |
2373 | ||
2374 | if ($properties->eventtype === 'site') { | |
2375 | // Site context | |
f4700b91 | 2376 | $this->editorcontext = $this->properties->context; |
76d9df3f SH |
2377 | } else if ($properties->eventtype === 'user') { |
2378 | // User context | |
f4700b91 | 2379 | $this->editorcontext = $this->properties->context; |
76d9df3f SH |
2380 | } else if ($properties->eventtype === 'group' || $properties->eventtype === 'course') { |
2381 | // First check the course is valid | |
2382 | $course = $DB->get_record('course', array('id'=>$properties->courseid)); | |
2383 | if (!$course) { | |
2384 | print_error('invalidcourse'); | |
2385 | } | |
2386 | // Course context | |
f4700b91 | 2387 | $this->editorcontext = $this->properties->context; |
76d9df3f SH |
2388 | // We have a course and are within the course context so we had |
2389 | // better use the courses max bytes value | |
2390 | $this->editoroptions['maxbytes'] = $course->maxbytes; | |
2391 | } else { | |
2392 | // If we get here we have a custom event type as used by some | |
2393 | // modules. In this case the event will have been added by | |
2394 | // code and we won't need the editor | |
2395 | $this->editoroptions['maxbytes'] = 0; | |
2396 | $this->editoroptions['maxfiles'] = 0; | |
2397 | } | |
2398 | ||
2399 | if (empty($this->editorcontext) || empty($this->editorcontext->id)) { | |
2400 | $contextid = false; | |
2401 | } else { | |
2402 | // Get the context id that is what we really want | |
2403 | $contextid = $this->editorcontext->id; | |
2404 | } | |
2405 | } else { | |
2406 | ||
2407 | // If we get here then this is a new event in which case we don't need a | |
2408 | // context as there is no existing files to copy to the draft area. | |
2409 | $contextid = null; | |
2410 | } | |
2411 | ||
2412 | // If the contextid === false we don't support files so no preparing | |
2413 | // a draft area | |
2414 | if ($contextid !== false) { | |
2415 | // Just encase it has already been submitted | |
2416 | $draftiddescription = file_get_submitted_draft_itemid('description'); | |
2417 | // Prepare the draft area, this copies existing files to the draft area as well | |
64f93798 | 2418 | $properties->description = file_prepare_draft_area($draftiddescription, $contextid, 'calendar', 'event_description', $properties->id, $this->editoroptions, $properties->description); |
76d9df3f SH |
2419 | } else { |
2420 | $draftiddescription = 0; | |
2421 | } | |
aa6c1ced | 2422 | |
76d9df3f SH |
2423 | // Structure the description field as the editor requires |
2424 | $properties->description = array('text'=>$properties->description, 'format'=>$properties->format, 'itemid'=>$draftiddescription); | |
2425 | } | |
2426 | ||
2427 | // Finally return the properties | |
2428 | return $properties; | |
2429 | } | |
2430 | ||
2431 | /** | |
2432 | * Toggles the visibility of an event | |
2433 | * | |
2434 | * @param null|bool $force If it is left null the events visibility is flipped, | |
2435 | * If it is false the event is made hidden, if it is true it | |
2436 | * is made visible. | |
08b4a4e1 | 2437 | * @return bool if event is successfully updated, toggle will be visible |
76d9df3f SH |
2438 | */ |
2439 | public function toggle_visibility($force=null) { | |
2440 | global $CFG, $DB; | |
2441 | ||
2442 | // Set visible to the default if it is not already set | |
2443 | if (empty($this->properties->visible)) { | |
2444 | $this->properties->visible = 1; | |
2445 | } | |
2446 | ||
2447 | if ($force === true || ($force !== false && $this->properties->visible == 0)) { | |
2448 | // Make this event visible | |
2449 | $this->properties->visible = 1; | |
2450 | // Fire the hook | |
2451 | self::calendar_event_hook('show_event', array($this->properties)); | |
2452 | } else { | |
2453 | // Make this event hidden | |
2454 | $this->properties->visible = 0; | |
2455 | // Fire the hook | |
2456 | self::calendar_event_hook('hide_event', array($this->properties)); | |
2457 | } | |
2458 | ||
2459 | // Update the database to reflect this change | |
2460 | return $DB->set_field('event', 'visible', $this->properties->visible, array('id'=>$this->properties->id)); | |
2461 | } | |
2462 | ||
2463 | /** | |
2464 | * Attempts to call the hook for the specified action should a calendar type | |
2465 | * by set $CFG->calendar, and the appopriate function defined | |
2466 | * | |
76d9df3f SH |
2467 | * @param string $action One of `update_event`, `add_event`, `delete_event`, `show_event`, `hide_event` |
2468 | * @param array $args The args to pass to the hook, usually the event is the first element | |
08b4a4e1 | 2469 | * @return bool attempts to call event hook |
76d9df3f SH |
2470 | */ |
2471 | public static function calendar_event_hook($action, array $args) { | |
2472 | global $CFG; | |
2473 | static $extcalendarinc; | |
2474 | if ($extcalendarinc === null) { | |
d2b46849 BJ |
2475 | if (!empty($CFG->calendar)) { |
2476 | if (is_readable($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) { | |
2477 | include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php'); | |
2478 | $extcalendarinc = true; | |
2479 | } else { | |
2480 | debugging("Calendar lib file missing or not readable at /calendar/{$CFG->calendar}/lib.php.", | |
2481 | DEBUG_DEVELOPER); | |
2482 | $extcalendarinc = false; | |
2483 | } | |
76d9df3f SH |
2484 | } else { |
2485 | $extcalendarinc = false; | |
2486 | } | |
2487 | } | |
2488 | if($extcalendarinc === false) { | |
2489 | return false; | |
2490 | } | |
438e4add | 2491 | $hook = $CFG->calendar .'_'.$action; |
76d9df3f SH |
2492 | if (function_exists($hook)) { |
2493 | call_user_func_array($hook, $args); | |
2494 | return true; | |
2495 | } | |
2496 | return false; | |
2497 | } | |
2498 | ||
2499 | /** | |
2500 | * Returns a calendar_event object when provided with an event id | |
2501 | * | |
2502 | * This function makes use of MUST_EXIST, if the event id passed in is invalid | |
2503 | * it will result in an exception being thrown | |
2504 | * | |
08b4a4e1 RW |
2505 | * @param int|object $param event object or event id |
2506 | * @return calendar_event|false status for loading calendar_event | |
76d9df3f | 2507 | */ |
fd699564 | 2508 | public static function load($param) { |
76d9df3f | 2509 | global $DB; |
fd699564 SH |
2510 | if (is_object($param)) { |
2511 | $event = new calendar_event($param); | |
2512 | } else { | |
2513 | $event = $DB->get_record('event', array('id'=>(int)$param), '*', MUST_EXIST); | |
2514 | $event = new calendar_event($event); | |
2515 | } | |
76d9df3f SH |
2516 | return $event; |
2517 | } | |
2518 | ||
2519 | /** | |
2520 | * Creates a new event and returns a calendar_event object | |
2521 | * | |
2522 | * @param object|array $properties An object containing event properties | |
2523 | * @return calendar_event|false The event object or false if it failed | |
2524 | */ | |
2525 | public static function create($properties) { | |
2526 | if (is_array($properties)) { | |
2527 | $properties = (object)$properties; | |
2528 | } | |
2529 | if (!is_object($properties)) { | |
2530 | throw new coding_exception('When creating an event properties should be either an object or an assoc array'); | |
2531 | } | |
f4700b91 | 2532 | $event = new calendar_event($properties); |
76d9df3f SH |
2533 | if ($event->update($properties)) { |
2534 | return $event; | |
2535 | } else { | |
2536 | return false; | |
2537 | } | |
2538 | } | |
2539 | } | |
36dc3b71 SH |
2540 | |
2541 | /** | |
2542 | * Calendar information class | |
2543 | * | |
2544 | * This class is used simply to organise the information pertaining to a calendar | |
2545 | * and is used primarily to make information easily available. | |
08b4a4e1 RW |
2546 | * |
2547 | * @package core_calendar | |
2548 | * @category calendar | |
2549 | * @copyright 2010 Sam Hemelryk | |
2550 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
36dc3b71 SH |
2551 | */ |
2552 | class calendar_information { | |
08b4a4e1 | 2553 | /** @var int The day */ |
36dc3b71 | 2554 | public $day; |
08b4a4e1 RW |
2555 | |
2556 | /** @var int The month */ | |
36dc3b71 | 2557 | public $month; |
08b4a4e1 RW |
2558 | |
2559 | /** @var int The year */ | |
36dc3b71 SH |
2560 | public $year; |
2561 | ||
08b4a4e1 | 2562 | /** @var int A course id */ |
36dc3b71 | 2563 | public $courseid = null; |
08b4a4e1 RW |
2564 | |
2565 | /** @var array An array of courses */ | |
36dc3b71 | 2566 | public $courses = array(); |
08b4a4e1 RW |
2567 | |
2568 | /** @var array An array of groups */ | |
36dc3b71 | 2569 | public $groups = array(); |
08b4a4e1 RW |
2570 | |
2571 | /** @var array An array of users */ | |
36dc3b71 SH |
2572 | public $users = array(); |
2573 | ||
2574 | /** | |
2575 | * Creates a new instance | |
2576 | * | |
08b4a4e1 RW |
2577 | * @param int $day the number of the day |
2578 | * @param int $month the number of the month | |
2579 | * @param int $year the number of the year | |
36dc3b71 | 2580 | */ |
d8d8bdd9 SH |
2581 | public function __construct($day=0, $month=0, $year=0) { |
2582 | ||
2583 | $date = usergetdate(time()); | |
2584 | ||
2585 | if (empty($day)) { | |
2586 | $day = $date['mday']; | |
2587 | } | |
2588 | ||
2589 | if (empty($month)) { | |
2590 | $month = $date['mon']; | |
2591 | } | |
2592 | ||
2593 | if (empty($year)) { | |
2594 | $year = $date['year']; | |
2595 | } | |
2596 | ||
36dc3b71 SH |
2597 | $this->day = $day; |
2598 | $this->month = $month; | |
2599 | $this->year = $year; | |
2600 | } | |
2601 | ||
797cedc7 | 2602 | /** |
08b4a4e1 | 2603 | * Initialize calendar information |
797cedc7 | 2604 | * |
08b4a4e1 | 2605 | * @param stdClass $course object |
797cedc7 | 2606 | * @param array $coursestoload An array of courses [$course->id => $course] |
08b4a4e1 | 2607 | * @param bool $ignorefilters options to use filter |
797cedc7 SH |
2608 | */ |
2609 | public function prepare_for_view(stdClass $course, array $coursestoload, $ignorefilters = false) { | |
2610 | $this->courseid = $course->id; | |
2611 | $this->course = $course; | |
2612 | list($courses, $group, $user) = calendar_set_filters($coursestoload, $ignorefilters); | |
2613 | $this->courses = $courses; | |
2614 | $this->groups = $group; | |
2615 | $this->users = $user; | |
2616 | } | |
2617 | ||
36dc3b71 SH |
2618 | /** |
2619 | * Ensures the date for the calendar is correct and either sets it to now | |
2620 | * or throws a moodle_exception if not | |
2621 | * | |
08b4a4e1 RW |
2622 | * @param bool $defaultonow use current time |
2623 | * @throws moodle_exception | |
2624 | * @return bool validation of checkdate | |
36dc3b71 SH |
2625 | */ |
2626 | public function checkdate($defaultonow = true) { | |
2627 | if (!checkdate($this->month, $this->day, $this->year)) { | |
2628 | if ($defaultonow) { | |
2629 | $now = usergetdate(time()); | |
2630 | $this->day = intval($now['mday']); | |
2631 | $this->month = intval($now['mon']); | |
2632 | $this->year = intval($now['year']); | |
2633 | return true; | |
2634 | } else { | |
2635 | throw new moodle_exception('invaliddate'); | |
2636 | } | |
2637 | } | |
2638 | return true; | |
2639 | } | |
2640 | /** | |
2641 | * Gets todays timestamp for the calendar | |
08b4a4e1 RW |
2642 | * |
2643 | * @return int today timestamp | |
36dc3b71 SH |
2644 | */ |
2645 | public function timestamp_today() { | |
2646 | return make_timestamp($this->year, $this->month, $this->day); | |
2647 | } | |
2648 | /** | |
2649 | * Gets tomorrows timestamp for the calendar | |
08b4a4e1 RW |
2650 | * |
2651 | * @return int tomorrow timestamp | |
36dc3b71 SH |
2652 | */ |
2653 | public function timestamp_tomorrow() { | |
2654 | return make_timestamp($this->year, $this->month, $this->day+1); | |
2655 | } | |
2656 | /** | |
e30390a0 | 2657 | * Adds the pretend blocks for the calendar |
36dc3b71 SH |
2658 | * |
2659 | * @param core_calendar_renderer $renderer | |
08b4a4e1 RW |
2660 | * @param bool $showfilters display filters, false is set as default |
2661 | * @param string|null $view preference view options (eg: day, month, upcoming) | |
36dc3b71 SH |
2662 | */ |
2663 | public function add_sidecalendar_blocks(core_calendar_renderer $renderer, $showfilters=false, $view=null) { | |
2664 | if ($showfilters) { | |
2665 | $filters = new block_contents(); | |
2666 | $filters->content = $renderer->fake_block_filters($this->courseid, $this->day, $this->month, $this->year, $view, $this->courses); | |
2667 | $filters->footer = ''; | |
2668 | $filters->title = get_string('eventskey', 'calendar'); | |
2669 | $renderer->add_pretend_calendar_block($filters, BLOCK_POS_RIGHT); | |
2670 | } | |
2671 | $block = new block_contents; | |
2672 | $block->content = $renderer->fake_block_threemonths($this); | |
2673 | $block->footer = ''; | |
2674 | $block->title = get_string('monthlyview', 'calendar'); | |
2675 | $renderer->add_pretend_calendar_block($block, BLOCK_POS_RIGHT); | |
2676 | } | |
1d5bd3d2 | 2677 | } |
b5a52acd JH |
2678 | |
2679 | /** | |
e30390a0 SH |
2680 | * Returns option list for the poll interval setting. |
2681 | * | |
2682 | * @return array An array of poll interval options. Interval => description. | |
b5a52acd JH |
2683 | */ |
2684 | function calendar_get_pollinterval_choices() { | |
2685 | return array( | |
e30390a0 SH |
2686 | '0' => new lang_string('never', 'calendar'), |
2687 | '3600' => new lang_string('hourly', 'calendar'), | |
2688 | '86400' => new lang_string('daily', 'calendar'), | |
2689 | '604800' => new lang_string('weekly', 'calendar'), | |
2690 | '2628000' => new lang_string('monthly', 'calendar'), | |
2691 | '31536000' => new lang_string('annually', 'calendar') | |
2692 | ); | |
b5a52acd JH |
2693 | } |
2694 | ||
2695 | /** | |
e30390a0 SH |
2696 | * Returns option list of available options for the calendar event type, given the current user and course. |
2697 | * | |
4c349ad7 | 2698 | * @param int $courseid The id of the course |
e30390a0 | 2699 | * @return array An array containing the event types the user can create. |
b5a52acd JH |
2700 | */ |
2701 | function calendar_get_eventtype_choices($courseid) { | |
2702 | $choices = array(); | |
2703 | $allowed = new stdClass; | |
2704 | calendar_get_allowed_types($allowed, $courseid); | |
2705 | ||
2706 | if ($allowed->user) { | |
35ad5fc6 | 2707 | $choices['user'] = get_string('userevents', 'calendar'); |
b5a52acd JH |
2708 | } |
2709 | if ($allowed->site) { | |
6bf410ed | 2710 | $choices['site'] = get_string('siteevents', 'calendar'); |
b5a52acd JH |
2711 | } |
2712 | if (!empty($allowed->courses)) { | |
35ad5fc6 | 2713 | $choices['course'] = get_string('courseevents', 'calendar'); |
b5a52acd JH |
2714 | } |
2715 | if (!empty($allowed->groups) and is_array($allowed->groups)) { | |
2716 | $choices['group'] = get_string('group'); | |
2717 | } | |
2718 | ||
2719 | return array($choices, $allowed->groups); | |
2720 | } | |
2721 | ||
b5a52acd JH |
2722 | /** |
2723 | * Add an iCalendar subscription to the database. | |
e30390a0 SH |
2724 | * |
2725 | * @param stdClass $sub The subscription object (e.g. from the form) | |
2726 | * @return int The insert ID, if any. | |
b5a52acd JH |
2727 | */ |
2728 | function calendar_add_subscription($sub) { | |
35ad5fc6 | 2729 | global $DB, $USER, $SITE; |
e30390a0 | 2730 | |
35ad5fc6 AA |
2731 | if ($sub->eventtype === 'site') { |
2732 | $sub->courseid = $SITE->id; | |
2733 | } else if ($sub->eventtype === 'group' || $sub->eventtype === 'course') { | |
b5a52acd | 2734 | $sub->courseid = $sub->course; |
35ad5fc6 AA |
2735 | } else { |
2736 | // User events. | |
2737 | $sub->courseid = 0; | |
b5a52acd JH |
2738 | } |
2739 | $sub->userid = $USER->id; | |
2740 | ||
e30390a0 | 2741 | // File subscriptions never update. |
b5a52acd JH |
2742 | if (empty($sub->url)) { |
2743 | $sub->pollinterval = 0; | |
2744 | } | |
2745 | ||
2746 | if (!empty($sub->name)) { | |
2747 | if (empty($sub->id)) { | |
2748 | $id = $DB->insert_record('event_subscriptions', $sub); | |
2749 | return $id; | |
2750 | } else { | |
2751 | $DB->update_record('event_subscriptions', $sub); | |
2752 | return $sub->id; | |
2753 | } | |
2754 | } else { | |
2755 | print_error('errorbadsubscription', 'importcalendar'); | |
2756 | } | |
2757 | } | |
2758 | ||
2759 | /** | |
2760 | * Add an iCalendar event to the Moodle calendar. | |
e30390a0 SH |
2761 | * |
2762 | * @param object $event The RFC-2445 iCalendar event | |
2763 | * @param int $courseid The course ID | |
2764 | * @param int $subscriptionid The iCalendar subscription ID | |
2765 | * @return int Code: 1=updated, 2=inserted, 0=error | |
b5a52acd | 2766 | */ |
e30390a0 | 2767 | function calendar_add_icalendar_event($event, $courseid, $subscriptionid = null) { |
b5a52acd JH |
2768 | global $DB, $USER; |
2769 | ||
e30390a0 | 2770 | // Probably an unsupported X-MICROSOFT-CDO-BUSYSTATUS event. |
b5a52acd JH |
2771 | if (empty($event->properties['SUMMARY'])) { |
2772 | return 0; | |
2773 | } | |
2774 | ||
2775 | $name = $event->properties['SUMMARY'][0]->value; | |
2776 | $name = str_replace('\n', '<br />', $name); | |
2777 | $name = str_replace('\\', '', $name); | |
2778 | $name = preg_replace('/\s+/', ' ', $name); | |
e30390a0 SH |
2779 | |
2780 | $eventrecord = new stdClass; | |
4c349ad7 | 2781 | $eventrecord->name = clean_param($name, PARAM_NOTAGS); |
b5a52acd JH |
2782 | |
2783 | if (empty($event->properties['DESCRIPTION'][0]->value)) { | |
2784 | $description = ''; | |
2785 | } else { | |
2786 | $description = $event->properties['DESCRIPTION'][0]->value; | |
2787 | $description = str_replace('\n', '<br />', $description); | |
2788 | $description = str_replace('\\', '', $description); | |
2789 | $description = preg_replace('/\s+/', ' ', $description); | |
2790 | } | |
4c349ad7 | 2791 | $eventrecord->description = clean_param($description, PARAM_NOTAGS); |
b5a52acd | 2792 | |
e30390a0 | 2793 | // Probably a repeating event with RRULE etc. TODO: skip for now. |
b5a52acd JH |
2794 | if (empty($event->properties['DTSTART'][0]->value)) { |
2795 | return 0; | |
2796 | } | |
2797 | ||
2798 | $eventrecord->timestart = strtotime($event->properties['DTSTART'][0]->value); | |
2799 | if (empty($event->properties['DTEND'])) { | |
2800 | $eventrecord->timeduration = 3600; // one hour if no end time specified | |
2801 | } else { | |
2802 | $eventrecord->timeduration = strtotime($event->properties['DTEND'][0]->value) - $eventrecord->timestart; | |
2803 | } | |
2804 | $eventrecord->uuid = $event->properties['UID'][0]->value; | |
2805 | $eventrecord->timemodified = time(); | |
2806 | ||
e30390a0 | 2807 | // Add the iCal subscription details if required. |
b5a52acd JH |
2808 | if ($sub = $DB->get_record('event_subscriptions', array('id' => $subscriptionid))) { |
2809 | $eventrecord->subscriptionid = $subscriptionid; | |
2810 | $eventrecord->userid = $sub->userid; | |
2811 | $eventrecord->groupid = $sub->groupid; | |
2812 | $eventrecord->courseid = $sub->courseid; | |
35ad5fc6 | 2813 | $eventrecord->eventtype = $sub->eventtype; |
b5a52acd | 2814 | } else { |
35ad5fc6 AA |
2815 | // We should never do anything with an event without a subscription reference. |
2816 | return 0; | |
b5a52acd JH |
2817 | } |
2818 | ||
2819 | if ($updaterecord = $DB->get_record('event', array('uuid' => $eventrecord->uuid))) { | |
2820 | $eventrecord->id = $updaterecord->id; | |
2821 | if ($DB->update_record('event', $eventrecord)) { | |
2822 | return CALENDAR_IMPORT_EVENT_UPDATED; | |
2823 | } else { | |
2824 | return 0; | |
2825 | } | |
2826 | } else { | |
2827 | if ($DB->insert_record('event', $eventrecord)) { | |
2828 | return CALENDAR_IMPORT_EVENT_INSERTED; | |
2829 | } else { | |
2830 | return 0; | |
2831 | } | |
2832 | } | |
2833 | } | |
2834 | ||
2835 | /** | |
e30390a0 SH |
2836 | * Update a subscription from the form data in one of the rows in the existing subscriptions table. |
2837 | * | |
2838 | * @param int $subscriptionid The ID of the subscription we are acting upon. | |
2839 | * @param int $pollinterval The poll interval to use. | |
2840 | * @param int $action The action to be performed. One of update or remove. | |
2841 | * @return string A log of the import progress, including errors | |
b5a52acd | 2842 | */ |
e30390a0 | 2843 | function calendar_process_subscription_row($subscriptionid, $pollinterval, $action) { |
b5a52acd JH |
2844 | global $DB; |
2845 | ||
e30390a0 | 2846 | if (empty($subscriptionid)) { |
b5a52acd JH |
2847 | return ''; |
2848 | } | |
2849 | ||
e30390a0 SH |
2850 | // Fetch the subscription from the database making sure it exists. |
2851 | $sub = $DB->get_record('event_subscriptions', array('id' => $subscriptionid), '*', MUST_EXIST); | |
b5a52acd | 2852 | |
e30390a0 SH |
2853 | $strupdate = get_string('update'); |
2854 | $strremove = get_string('remove'); | |
2855 | // Update or remove the subscription, based on action. | |
b5a52acd | 2856 | switch ($action) { |
e30390a0 SH |
2857 | case $strupdate: |
2858 | // Skip updating file subscriptions. | |
2859 | if (empty($sub->url)) { | |
2860 | break; | |
2861 | } | |
2862 | $sub->pollinterval = $pollinterval; | |
2863 | $DB->update_record('event_subscriptions', $sub); | |
b5a52acd | 2864 | |
e30390a0 SH |
2865 | // Update the events. |
2866 | return "<p>".get_string('subscriptionupdated', 'calendar', $sub->name)."</p>" . calendar_update_subscription_events($subscriptionid); | |
b5a52acd | 2867 | |
e30390a0 SH |
2868 | case $strremove: |
2869 | $DB->delete_records('event', array('subscriptionid' => $subscriptionid)); | |
2870 | $DB->delete_records('event_subscriptions', array('id' => $subscriptionid)); | |
2871 | return get_string('subscriptionremoved', 'calendar', $sub->name); | |
2872 | break; | |
b5a52acd | 2873 | |
e30390a0 SH |
2874 | default: |
2875 | break; | |
b5a52acd JH |
2876 | } |
2877 | return ''; | |
2878 | } | |
2879 | ||
2880 | /** | |
2881 | * From a URL, fetch the calendar and return an iCalendar object. | |
e30390a0 SH |
2882 | * |
2883 | * @param string $url The iCalendar URL | |
2884 | * @return stdClass The iCalendar object | |
b5a52acd JH |
2885 | */ |
2886 | function calendar_get_icalendar($url) { | |
e30390a0 SH |
2887 | global $CFG; |
2888 | ||
2889 | require_once($CFG->libdir.'/filelib.php'); | |
2890 | ||
2891 | $curl = new curl(); | |
20bdf997 | 2892 | $curl->setopt(array('CURLOPT_FOLLOWLOCATION' => 1, 'CURLOPT_MAXREDIRS' => 5)); |
e30390a0 | 2893 | $calendar = $curl->get($url); |
20bdf997 AA |
2894 | // Http code validation should actually be the job of curl class. |
2895 | if (!$calendar || $curl->info['http_code'] != 200 || !empty($curl->errorno)) { | |
e30390a0 SH |
2896 | throw new moodle_exception('errorinvalidicalurl', 'calendar'); |
2897 | } | |
2898 | ||
b5a52acd JH |
2899 | $ical = new iCalendar(); |
2900 | $ical->unserialize($calendar); | |
2901 | return $ical; | |
2902 | } | |
2903 | ||
2904 | /** | |
2905 | * Import events from an iCalendar object into a course calendar. | |
e30390a0 SH |
2906 | * |
2907 | * @param stdClass $ical The iCalendar object. | |
2908 | * @param int $courseid The course ID for the calendar. | |
2909 | * @param int $subscriptionid The subscription ID. | |
2910 | * @return string A log of the import progress, including errors. | |
b5a52acd | 2911 | */ |
e30390a0 | 2912 | function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = null) { |
b5a52acd JH |
2913 | global $DB; |
2914 | $return = ''; | |
2915 | $eventcount = 0; | |
2916 | $updatecount = 0; | |
2917 | ||
e30390a0 SH |
2918 | // Large calendars take a while... |
2919 | set_time_limit(300); | |
b5a52acd | 2920 | |
e30390a0 | 2921 | // Mark all events in a subscription with a zero timestamp. |
b5a52acd | 2922 | if (!empty($subscriptionid)) { |
e30390a0 | 2923 | $sql = "UPDATE {event} SET timemodified = :time WHERE subscriptionid = :id"; |
b5a52acd JH |
2924 | $DB->execute($sql, array('time' => 0, 'id' => $subscriptionid)); |
2925 | } | |
4c349ad7 | 2926 | foreach ($ical->components['VEVENT'] as $event) { |
b5a52acd JH |
2927 | $res = calendar_add_icalendar_event($event, $courseid, $subscriptionid); |
2928 | switch ($res) { | |
2929 | case CALENDAR_IMPORT_EVENT_UPDATED: | |
2930 | $updatecount++; | |
2931 | break; | |
2932 | case CALENDAR_IMPORT_EVENT_INSERTED: | |
2933 | $eventcount++; | |
2934 | break; | |
2935 | case 0: | |
4c349ad7 | 2936 | $return .= '<p>'.get_string('erroraddingevent', 'calendar').': '.(empty($event->properties['SUMMARY'])?'('.get_string('notitle', 'calendar').')':$event->properties['SUMMARY'][0]->value)." </p>\n"; |
b5a52acd JH |
2937 | break; |
2938 | } | |
2939 | } | |
e30390a0 SH |
2940 | $return .= "<p> ".get_string('eventsimported', 'calendar', $eventcount)."</p>"; |
2941 | $return .= "<p> ".get_string('eventsupdated', 'calendar', $updatecount)."</p>"; | |
b5a52acd | 2942 | |
e30390a0 | 2943 | // Delete remaining zero-marked events since they're not in remote calendar. |
b5a52acd JH |
2944 | if (!empty($subscriptionid)) { |
2945 | $deletecount = $DB->count_records('event', array('timemodified' => 0, 'subscriptionid' => $subscriptionid)); | |
2946 | if (!empty($deletecount)) { | |
e30390a0 | 2947 | $sql = "DELETE FROM {event} WHERE timemodified = :time AND subscriptionid = :id"; |
b5a52acd | 2948 | $DB->execute($sql, array('time' => 0, 'id' => $subscriptionid)); |
4c349ad7 | 2949 | $return .= "<p> ".get_string('eventsdeleted', 'calendar').": {$deletecount} </p>\n"; |
b5a52acd JH |
2950 | } |
2951 | } | |
2952 | ||
2953 | return $return; | |
2954 | } | |
2955 | ||
2956 | /** | |
2957 | * Fetch a calendar subscription and update the events in the calendar. | |
e30390a0 SH |
2958 | * |
2959 | * @param int $subscriptionid The course ID for the calendar. | |
2960 | * @return string A log of the import progress, including errors. | |
b5a52acd JH |
2961 | */ |
2962 | function calendar_update_subscription_events($subscriptionid) { | |
2963 | global $DB; | |
b5a52acd JH |
2964 | |
2965 | $sub = $DB->get_record('event_subscriptions', array('id' => $subscriptionid)); | |
2966 | if (empty($sub)) { | |
2967 | print_error('errorbadsubscription', 'calendar'); | |
2968 | } | |
e30390a0 | 2969 | // Don't update a file subscription. TODO: Update from a new uploaded file. |
b5a52acd JH |
2970 | if (empty($sub->url)) { |
2971 | return 'File subscription not updated.'; | |
2972 | } | |
2973 | $ical = calendar_get_icalendar($sub->url); | |
2974 | $return = calendar_import_icalendar_events($ical, $sub->courseid, $subscriptionid); | |
2975 | $sub->lastupdated = time(); | |
2976 | $DB->update_record('event_subscriptions', $sub); | |
2977 | return $return; | |
2978 | } | |
2979 | ||
2980 | /** | |
2981 | * Update calendar subscriptions. | |
e30390a0 SH |
2982 | * |
2983 | * @return bool | |
b5a52acd JH |
2984 | */ |
2985 | function calendar_cron() { | |
e30390a0 SH |
2986 | global $CFG, $DB; |
2987 | ||
2988 | // In order to execute this we need bennu. | |
2989 | require_once($CFG->libdir.'/bennu/bennu.inc.php'); | |
2990 | ||
24a3115c | 2991 | mtrace('Updating calendar subscriptions:'); |
e30390a0 | 2992 | |
b5a52acd | 2993 | $time = time(); |
e30390a0 | 2994 | $subscriptions = $DB->get_records_sql('SELECT * FROM {event_subscriptions} WHERE pollinterval > 0 AND lastupdated + pollinterval < ?', array($time)); |
4c349ad7 | 2995 | foreach ($subscriptions as $sub) { |
24a3115c | 2996 | mtrace("Updating calendar subscription {$sub->name} in course {$sub->courseid}"); |
e30390a0 SH |
2997 | try { |
2998 | $log = calendar_update_subscription_events($sub->id); | |
2999 | } catch (moodle_exception $ex) { | |
3000 | ||
3001 | } | |
b5a52acd JH |
3002 | mtrace(trim(strip_tags($log))); |
3003 | } | |
e30390a0 | 3004 | |
24a3115c | 3005 | mtrace('Finished updating calendar subscriptions.'); |
b5a52acd | 3006 | |
e30390a0 | 3007 | return true; |
24a3115c | 3008 | } |