course publication MDL-22762 improve the publication confirmation/error message
[moodle.git] / calendar / lib.php
CommitLineData
93c91ee4 1<?php
7423f116 2
3/////////////////////////////////////////////////////////////////////////////
4// //
5// NOTICE OF COPYRIGHT //
6// //
7// Moodle - Calendar extension //
8// //
9// Copyright (C) 2003-2004 Greek School Network www.sch.gr //
10// //
11// Designed by: //
bdcb26b7 12// Avgoustos Tsinakos (tsinakos@teikav.edu.gr) //
13// Jon Papaioannou (pj@moodle.org) //
7423f116 14// //
15// Programming and development: //
bdcb26b7 16// Jon Papaioannou (pj@moodle.org) //
7423f116 17// //
18// For bugs, suggestions, etc contact: //
bdcb26b7 19// Jon Papaioannou (pj@moodle.org) //
7423f116 20// //
21// The current module was developed at the University of Macedonia //
22// (www.uom.gr) under the funding of the Greek School Network (www.sch.gr) //
23// The aim of this project is to provide additional and improved //
24// functionality to the Asynchronous Distance Education service that the //
25// Greek School Network deploys. //
26// //
27// This program is free software; you can redistribute it and/or modify //
28// it under the terms of the GNU General Public License as published by //
29// the Free Software Foundation; either version 2 of the License, or //
30// (at your option) any later version. //
31// //
32// This program is distributed in the hope that it will be useful, //
33// but WITHOUT ANY WARRANTY; without even the implied warranty of //
34// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
35// GNU General Public License for more details: //
36// //
37// http://www.gnu.org/copyleft/gpl.html //
38// //
39/////////////////////////////////////////////////////////////////////////////
40
bb4a2e85 41// These are read by the administration component to provide default values
42define('CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD', 21);
43define('CALENDAR_DEFAULT_UPCOMING_MAXEVENTS', 10);
44define('CALENDAR_DEFAULT_STARTING_WEEKDAY', 1);
45// This is a packed bitfield: day X is "weekend" if $field & (1 << X) is true
46// Default value = 65 = 64 + 1 = 2^6 + 2^0 = Saturday & Sunday
47define('CALENDAR_DEFAULT_WEEKEND', 65);
76d9df3f
SH
48define('CALENDAR_UPCOMING_DAYS', isset($CFG->calendar_lookahead) ? intval($CFG->calendar_lookahead) : CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD);
49define('CALENDAR_UPCOMING_MAXEVENTS', isset($CFG->calendar_maxevents) ? intval($CFG->calendar_maxevents) : CALENDAR_DEFAULT_UPCOMING_MAXEVENTS);
50define('CALENDAR_WEEKEND', isset($CFG->calendar_weekend) ? intval($CFG->calendar_weekend) : CALENDAR_DEFAULT_WEEKEND);
51define('CALENDAR_URL', $CFG->wwwroot.'/calendar/');
52define('CALENDAR_TF_24', '%H:%M');
53define('CALENDAR_TF_12', '%I:%M %p');
bb4a2e85 54
76d9df3f
SH
55/**
56 * CALENDAR_STARTING_WEEKDAY has since been deprecated please call calendar_get_starting_weekday() instead
57 * @deprecated
58 */
59define('CALENDAR_STARTING_WEEKDAY', CALENDAR_DEFAULT_STARTING_WEEKDAY);
7423f116 60
f2bffd9e 61$CALENDARDAYS = array('sunday','monday','tuesday','wednesday','thursday','friday','saturday');
62
76d9df3f
SH
63/**
64 * Gets the first day of the week
65 *
66 * Used to be define('CALENDAR_STARTING_WEEKDAY', blah);
67 *
68 * @return int
69 */
70function calendar_get_starting_weekday() {
71 global $CFG;
37d87d11 72
76d9df3f
SH
73 if (isset($CFG->calendar_startwday)) {
74 $firstday = $CFG->calendar_startwday;
75 } else {
0a933ae6 76 $firstday = get_string('firstdayofweek', 'langconfig');
76d9df3f
SH
77 }
78
79 if(!is_numeric($firstday)) {
80 return CALENDAR_DEFAULT_STARTING_WEEKDAY;
81 } else {
82 return intval($firstday) % 7;
83 }
84}
bd119567 85
7423f116 86function calendar_get_mini($courses, $groups, $users, $cal_month = false, $cal_year = false) {
6b608f8f 87 global $CFG, $USER, $OUTPUT;
7423f116 88
dd97c328 89 $display = new stdClass;
76d9df3f 90 $display->minwday = get_user_preferences('calendar_startwday', calendar_get_starting_weekday());
7423f116 91 $display->maxwday = $display->minwday + 6;
92
93 $content = '';
94
95 if(!empty($cal_month) && !empty($cal_year)) {
96 $thisdate = usergetdate(time()); // Date and time the user sees at his location
97 if($cal_month == $thisdate['mon'] && $cal_year == $thisdate['year']) {
98 // Navigated to this month
99 $date = $thisdate;
100 $display->thismonth = true;
101 }
102 else {
103 // Navigated to other month, let's do a nice trick and save us a lot of work...
104 if(!checkdate($cal_month, 1, $cal_year)) {
105 $date = array('mday' => 1, 'mon' => $thisdate['mon'], 'year' => $thisdate['year']);
106 $display->thismonth = true;
107 }
108 else {
109 $date = array('mday' => 1, 'mon' => $cal_month, 'year' => $cal_year);
110 $display->thismonth = false;
111 }
112 }
113 }
114 else {
115 $date = usergetdate(time()); // Date and time the user sees at his location
116 $display->thismonth = true;
117 }
118
119 // Fill in the variables we 're going to use, nice and tidy
120 list($d, $m, $y) = array($date['mday'], $date['mon'], $date['year']); // This is what we want to display
121 $display->maxdays = calendar_days_in_month($m, $y);
122
d6198903 123 if (get_user_timezone_offset() < 99) {
124 // We 'll keep these values as GMT here, and offset them when the time comes to query the db
61240489 125 $display->tstart = gmmktime(0, 0, 0, $m, 1, $y); // This is GMT
126 $display->tend = gmmktime(23, 59, 59, $m, $display->maxdays, $y); // GMT
d6198903 127 } else {
128 // no timezone info specified
61240489 129 $display->tstart = mktime(0, 0, 0, $m, 1, $y);
130 $display->tend = mktime(23, 59, 59, $m, $display->maxdays, $y);
d6198903 131 }
7423f116 132
69244b91 133 $startwday = dayofweek(1, $m, $y);
7423f116 134
135 // Align the starting weekday to fall in our display range
136 // This is simple, not foolproof.
137 if($startwday < $display->minwday) {
138 $startwday += 7;
139 }
140
c0b507d1 141 // TODO: THIS IS TEMPORARY CODE!
142 // [pj] I was just reading through this and realized that I when writing this code I was probably
143 // asking for trouble, as all these time manipulations seem to be unnecessary and a simple
144 // make_timestamp would accomplish the same thing. So here goes a test:
1747ee05 145 //$test_start = make_timestamp($y, $m, 1);
146 //$test_end = make_timestamp($y, $m, $display->maxdays, 23, 59, 59);
147 //if($test_start != usertime($display->tstart) - dst_offset_on($display->tstart)) {
148 //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);
149 //}
150 //if($test_end != usertime($display->tend) - dst_offset_on($display->tend)) {
151 //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);
152 //}
c0b507d1 153
154
7423f116 155 // Get the events matching our criteria. Don't forget to offset the timestamps for the user's TZ!
8263f802 156 $events = calendar_get_events(
d3555a2f 157 usertime($display->tstart) - dst_offset_on($display->tstart),
158 usertime($display->tend) - dst_offset_on($display->tend),
159 $users, $groups, $courses);
7423f116 160
b4892fa2 161 // Set event course class for course events
162 if (!empty($events)) {
13534ef7 163 foreach ($events as $eventid => $event) {
13534ef7
ML
164 if (!empty($event->modulename)) {
165 $cm = get_coursemodule_from_instance($event->modulename, $event->instance);
166 if (!groups_course_module_visible($cm)) {
167 unset($events[$eventid]);
168 }
169 }
b4892fa2 170 }
171 }
6619eba4 172
c635dcda 173 // This is either a genius idea or an idiot idea: in order to not complicate things, we use this rule: if, after
9064751b 174 // possibly removing SITEID from $courses, there is only one course left, then clicking on a day in the month
c635dcda 175 // will also set the $SESSION->cal_courses_shown variable to that one course. Otherwise, we 'd need to add extra
176 // arguments to this function.
177
7bd1677c 178 $morehref = '';
179 if(!empty($courses)) {
e749554e 180 $courses = array_diff($courses, array(SITEID));
7bd1677c 181 if(count($courses) == 1) {
182 $morehref = '&amp;course='.reset($courses);
183 }
c635dcda 184 }
185
7423f116 186 // We want to have easy access by day, since the display is on a per-day basis.
187 // Arguments passed by reference.
7b38bfa6 188 //calendar_events_by_day($events, $display->tstart, $eventsbyday, $durationbyday, $typesbyday);
7c50db30 189 calendar_events_by_day($events, $m, $y, $eventsbyday, $durationbyday, $typesbyday, $courses);
7423f116 190
f20c4d02 191 //Accessibility: added summary and <abbr> elements.
192 ///global $CALENDARDAYS; appears to be broken.
193 $days_title = array('sunday','monday','tuesday','wednesday','thursday','friday','saturday');
194
2a06efcc 195 $summary = get_string('calendarheading', 'calendar', userdate(make_timestamp($y, $m), get_string('strftimemonthyear')));
196 $summary = get_string('tabledata', 'access', $summary);
90723839 197 $content .= '<table class="minicalendar calendartable" summary="'.$summary.'">'; // Begin table
f136e4c5 198 $content .= '<tr class="weekdays">'; // Header row: day names
7423f116 199
200 // Print out the names of the weekdays
201 $days = array('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat');
202 for($i = $display->minwday; $i <= $display->maxwday; ++$i) {
203 // This uses the % operator to get the correct weekday no matter what shift we have
204 // applied to the $display->minwday : $display->maxwday range from the default 0 : 6
f20c4d02 205 $content .= '<th scope="col"><abbr title="'. get_string($days_title[$i % 7], 'calendar') .'">'.
206 get_string($days[$i % 7], 'calendar') ."</abbr></th>\n";
7423f116 207 }
208
f136e4c5 209 $content .= '</tr><tr>'; // End of day names; prepare for day numbers
7423f116 210
211 // For the table display. $week is the row; $dayweek is the column.
7423f116 212 $dayweek = $startwday;
213
214 // Paddding (the first week may have blank days in the beginning)
215 for($i = $display->minwday; $i < $startwday; ++$i) {
3bfd8bc8 216 $content .= '<td class="dayblank">&nbsp;</td>'."\n";
7423f116 217 }
218
219 // Now display all the calendar
220 for($day = 1; $day <= $display->maxdays; ++$day, ++$dayweek) {
221 if($dayweek > $display->maxwday) {
222 // We need to change week (table row)
d56d4e23 223 $content .= '</tr><tr>';
7423f116 224 $dayweek = $display->minwday;
7423f116 225 }
92668ad2 226
7423f116 227 // Reset vars
7423f116 228 $cell = '';
bb4a2e85 229 if(CALENDAR_WEEKEND & (1 << ($dayweek % 7))) {
7423f116 230 // Weekend. This is true no matter what the exact range is.
e2aa618b 231 $class = 'weekend day';
90723839 232 } else {
7423f116 233 // Normal working day.
e2aa618b 234 $class = 'day';
7423f116 235 }
236
237 // Special visual fx if an event is defined
238 if(isset($eventsbyday[$day])) {
90723839 239 $class .= ' hasevent';
c635dcda 240 $dayhref = calendar_get_link_href(CALENDAR_URL.'view.php?view=day'.$morehref.'&amp;', $day, $m, $y);
f434283f 241
7423f116 242 // OverLib popup
243 $popupcontent = '';
244 foreach($eventsbyday[$day] as $eventid) {
41d30a8e 245 if (!isset($events[$eventid])) {
246 continue;
4e17c6f3 247 }
41d30a8e 248 $event = $events[$eventid];
41d30a8e 249 if(!empty($event->modulename)) {
b5d0cafc 250 $popupicon = $OUTPUT->pix_url('icon', $event->modulename) . '';
41d30a8e 251 $popupalt = $event->modulename;
252
9064751b 253 } else if ($event->courseid == SITEID) { // Site event
b5d0cafc 254 $popupicon = $OUTPUT->pix_url('c/site');
41d30a8e 255 $popupalt = '';
c3d3b6d4 256 } else if ($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { // Course event
b5d0cafc 257 $popupicon = $OUTPUT->pix_url('c/course');
c9b05b32 258 $popupalt = '';
41d30a8e 259 } else if ($event->groupid) { // Group event
b5d0cafc 260 $popupicon = $OUTPUT->pix_url('c/group');
41d30a8e 261 $popupalt = '';
262 } else if ($event->userid) { // User event
b5d0cafc 263 $popupicon = $OUTPUT->pix_url('c/user');
41d30a8e 264 $popupalt = '';
4e17c6f3 265 }
53d8fac0 266 $popupcontent .= '<div><img class="icon" src="'.$popupicon.'" alt="'.$popupalt.'" /><a href="'.$dayhref.'#event_'.$event->id.'">'.format_string($event->name, true).'</a></div>';
7423f116 267 }
e295df44 268
b5c42e70 269 //Accessibility: functionality moved to calendar_get_popup.
270 if($display->thismonth && $day == $d) {
450a0a7d 271 $popup = calendar_get_popup(true, $events[$eventid]->timestart, $popupcontent);
b5c42e70 272 } else {
450a0a7d 273 $popup = calendar_get_popup(false, $events[$eventid]->timestart, $popupcontent);
e295df44 274 }
f20c4d02 275
7423f116 276 // Class and cell content
277 if(isset($typesbyday[$day]['startglobal'])) {
90723839
SH
278 $class .= ' calendar_event_global';
279 } else if(isset($typesbyday[$day]['startcourse'])) {
280 $class .= ' calendar_event_course';
281 } else if(isset($typesbyday[$day]['startgroup'])) {
282 $class .= ' calendar_event_group';
283 } else if(isset($typesbyday[$day]['startuser'])) {
284 $class .= ' calendar_event_user';
7423f116 285 }
05fcc5fd 286 $cell = '<a href="'.$dayhref.'" '.$popup.'>'.$day.'</a>';
90723839 287 } else {
05fcc5fd 288 $cell = $day;
7423f116 289 }
290
90723839
SH
291 $durationclass = false;
292 if (isset($typesbyday[$day]['durationglobal'])) {
293 $durationclass = ' duration_global';
294 } else if(isset($typesbyday[$day]['durationcourse'])) {
295 $durationclass = ' duration_course';
296 } else if(isset($typesbyday[$day]['durationgroup'])) {
297 $durationclass = ' duration_group';
298 } else if(isset($typesbyday[$day]['durationuser'])) {
299 $durationclass = ' duration_user';
7423f116 300 }
90723839
SH
301 if ($durationclass) {
302 $class .= ' duration '.$durationclass;
7423f116 303 }
304
b4892fa2 305 // If event has a class set then add it to the table day <td> tag
306 // Note: only one colour for minicalendar
307 if(isset($eventsbyday[$day])) {
308 foreach($eventsbyday[$day] as $eventid) {
309 if (!isset($events[$eventid])) {
310 continue;
311 }
312 $event = $events[$eventid];
313 if (!empty($event->class)) {
314 $class .= ' '.$event->class;
315 }
316 break;
317 }
318 }
319
7423f116 320 // Special visual fx for today
450a0a7d 321 //Accessibility: hidden text for today, and popup.
7423f116 322 if($display->thismonth && $day == $d) {
edbe6c1b 323 $class .= ' today';
b5c42e70 324 $today = get_string('today', 'calendar').' '.userdate(time(), get_string('strftimedayshort'));
e295df44 325
b5c42e70 326 if(! isset($eventsbyday[$day])) {
450a0a7d 327 $class .= ' eventnone';
328 $popup = calendar_get_popup(true, false);
329 $cell = '<a href="#" '.$popup.'>'.$day.'</a>';
b5c42e70 330 }
f79f2494 331 $cell = get_accesshide($today.' ').$cell;
7423f116 332 }
333
334 // Just display it
92668ad2 335 if(!empty($class)) {
d56d4e23 336 $class = ' class="'.$class.'"';
92668ad2 337 }
338 $content .= '<td'.$class.'>'.$cell."</td>\n";
7423f116 339 }
340
341 // Paddding (the last week may have blank days at the end)
342 for($i = $dayweek; $i <= $display->maxwday; ++$i) {
3bfd8bc8 343 $content .= '<td class="dayblank">&nbsp;</td>';
7423f116 344 }
345 $content .= '</tr>'; // Last row ends
346
f136e4c5 347 $content .= '</table>'; // Tabular display of days ends
7423f116 348
349 return $content;
350}
351
b5c42e70 352/**
353 * calendar_get_popup, called at multiple points in from calendar_get_mini.
354 * Copied and modified from calendar_get_mini.
355 * @uses OverLib popup.
356 * @param $is_today bool, false except when called on the current day.
357 * @param $event_timestart mixed, $events[$eventid]->timestart, OR false if there are no events.
358 * @param $popupcontent string.
e295df44 359 * @return $popup string, contains onmousover and onmouseout events.
b5c42e70 360 */
361function calendar_get_popup($is_today, $event_timestart, $popupcontent='') {
458eb0d1 362 global $PAGE;
363 static $popupcount;
364 if ($popupcount === null) {
365 $popupcount = 1;
366 }
b5c42e70 367 $popupcaption = '';
368 if($is_today) {
369 $popupcaption = get_string('today', 'calendar').' ';
370 }
371 if (false === $event_timestart) {
372 $popupcaption .= userdate(time(), get_string('strftimedayshort'));
373 $popupcontent = get_string('eventnone', 'calendar');
450a0a7d 374
b5c42e70 375 } else {
376 $popupcaption .= get_string('eventsfor', 'calendar', userdate($event_timestart, get_string('strftimedayshort')));
377 }
458eb0d1 378 $id = 'calendar_tooltip_'.$popupcount;
53fc3e70 379 $PAGE->requires->js_init_call("M.core_calendar.init", array(array('id'=>$id,'title'=>$popupcaption, 'content'=>$popupcontent)));
bdbae764 380
458eb0d1 381 $popupcount++;
382 return 'id="'.$id.'"';
b5c42e70 383}
384
9958a08c 385function calendar_get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime=0) {
62d11d77 386 global $CFG, $COURSE, $DB;
7423f116 387
dd97c328 388 $display = new stdClass;
7423f116 389 $display->range = $daysinfuture; // How many days in the future we 'll look
390 $display->maxevents = $maxevents;
391
392 $output = array();
393
394 // Prepare "course caching", since it may save us a lot of queries
395 $coursecache = array();
396
397 $processed = 0;
398 $now = time(); // We 'll need this later
9d567178 399 $usermidnighttoday = usergetmidnight($now);
7423f116 400
9958a08c 401 if ($fromtime) {
402 $display->tstart = $fromtime;
403 } else {
9d567178 404 $display->tstart = $usermidnighttoday;
9958a08c 405 }
7423f116 406
1f473774 407 // This works correctly with respect to the user's DST, but it is accurate
408 // only because $fromtime is always the exact midnight of some day!
409 $display->tend = usergetmidnight($display->tstart + DAYSECS * $display->range + 3 * HOURSECS) - 1;
7423f116 410
411 // Get the events matching our criteria
8263f802 412 $events = calendar_get_events($display->tstart, $display->tend, $users, $groups, $courses);
7423f116 413
c635dcda 414 // This is either a genius idea or an idiot idea: in order to not complicate things, we use this rule: if, after
9064751b 415 // possibly removing SITEID from $courses, there is only one course left, then clicking on a day in the month
c635dcda 416 // will also set the $SESSION->cal_courses_shown variable to that one course. Otherwise, we 'd need to add extra
417 // arguments to this function.
418
7bd1677c 419 $morehref = '';
420 if(!empty($courses)) {
e749554e 421 $courses = array_diff($courses, array(SITEID));
7bd1677c 422 if(count($courses) == 1) {
423 $morehref = '&amp;course='.reset($courses);
424 }
c635dcda 425 }
426
dd97c328 427 if ($events !== false) {
428
429 $modinfo =& get_fast_modinfo($COURSE);
fa22fd5f 430
7423f116 431 foreach($events as $event) {
dc6cb74e 432
dd97c328 433
434 if (!empty($event->modulename)) {
435 if ($event->courseid == $COURSE->id) {
436 if (isset($modinfo->instances[$event->modulename][$event->instance])) {
437 $cm = $modinfo->instances[$event->modulename][$event->instance];
438 if (!$cm->uservisible) {
439 continue;
440 }
441 }
442 } else {
443 if (!$cm = get_coursemodule_from_instance($event->modulename, $event->instance)) {
dc6cb74e 444 continue;
445 }
dd97c328 446 if (!coursemodule_visible_for_user($cm)) {
dc6cb74e 447 continue;
448 }
dd97c328 449 }
450 if ($event->modulename == 'assignment'){
451 // TODO: rewrite this hack somehow
aa6c1ced 452 if (!calendar_edit_event_allowed($event)){ // cannot manage entries, eg. student
62d11d77 453 if (!$assignment = $DB->get_record('assignment', array('id'=>$event->instance))) {
f581f8d6 454 // print_error("invalidid", 'assignment');
dd97c328 455 continue;
456 }
457 // assign assignment to assignment object to use hidden_is_hidden method
458 require_once($CFG->dirroot.'/mod/assignment/lib.php');
459
460 if (!file_exists($CFG->dirroot.'/mod/assignment/type/'.$assignment->assignmenttype.'/assignment.class.php')) {
461 continue;
462 }
463 require_once ($CFG->dirroot.'/mod/assignment/type/'.$assignment->assignmenttype.'/assignment.class.php');
464
465 $assignmentclass = 'assignment_'.$assignment->assignmenttype;
466 $assignmentinstance = new $assignmentclass($cm->id, $assignment, $cm);
467
468 if ($assignmentinstance->description_is_hidden()){//force not to show description before availability
469 $event->description = get_string('notavailableyet', 'assignment');
470 }
dc6cb74e 471 }
472 }
473 }
9d567178 474
dd97c328 475 if ($processed >= $display->maxevents) {
9d567178 476 break;
477 }
7423f116 478
3c134875 479 $event->time = calendar_format_event_time($event, $now, $morehref);
480 $output[] = $event;
481 ++$processed;
482 }
483 }
484 return $output;
485}
7423f116 486
9df8ff44 487function calendar_add_event_metadata($event) {
6b608f8f 488 global $CFG, $OUTPUT;
fb73f3b3 489
e295df44 490 //Support multilang in event->name
fb73f3b3 491 $event->name = format_string($event->name,true);
e295df44 492
3c134875 493 if(!empty($event->modulename)) { // Activity event
494 // The module name is set. I will assume that it has to be displayed, and
495 // also that it is an automatically-generated event. And of course that the
496 // fields for get_coursemodule_from_instance are set correctly.
497 $module = calendar_get_module_cached($coursecache, $event->modulename, $event->instance);
7423f116 498
3c134875 499 if ($module === false) {
500 return;
501 }
9958a08c 502
3c134875 503 $modulename = get_string('modulename', $event->modulename);
a48bf079
DM
504 if (get_string_manager()->string_exists($event->eventtype, $event->modulename)) {
505 // will be used as alt text if the event icon
506 $eventtype = get_string($event->eventtype, $event->modulename);
507 } else {
508 $eventtype = '';
509 }
b5d0cafc 510 $icon = $OUTPUT->pix_url('icon', $event->modulename) . '';
9958a08c 511
10daca92 512 $event->icon = '<img height="16" width="16" src="'.$icon.'" alt="'.$eventtype.'" title="'.$modulename.'" style="vertical-align: middle;" />';
fb73f3b3 513 $event->referer = '<a href="'.$CFG->wwwroot.'/mod/'.$event->modulename.'/view.php?id='.$module->id.'">'.$event->name.'</a>';
3c134875 514 $event->courselink = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$module->course.'">'.$coursecache[$module->course]->fullname.'</a>';
515 $event->cmid = $module->id;
9958a08c 516
9958a08c 517
3c134875 518 } else if($event->courseid == SITEID) { // Site event
b5d0cafc 519 $event->icon = '<img height="16" width="16" src="'.$OUTPUT->pix_url('c/site') . '" alt="'.get_string('globalevent', 'calendar').'" style="vertical-align: middle;" />';
90723839 520 $event->cssclass = 'calendar_event_global';
3c134875 521 } else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { // Course event
522 calendar_get_course_cached($coursecache, $event->courseid);
b5d0cafc 523 $event->icon = '<img height="16" width="16" src="'.$OUTPUT->pix_url('c/course') . '" alt="'.get_string('courseevent', 'calendar').'" style="vertical-align: middle;" />';
3c134875 524 $event->courselink = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$event->courseid.'">'.$coursecache[$event->courseid]->fullname.'</a>';
90723839 525 $event->cssclass = 'calendar_event_course';
3c134875 526 } else if ($event->groupid) { // Group event
b5d0cafc 527 $event->icon = '<img height="16" width="16" src="'.$OUTPUT->pix_url('c/group') . '" alt="'.get_string('groupevent', 'calendar').'" style="vertical-align: middle;" />';
90723839 528 $event->cssclass = 'calendar_event_group';
3c134875 529 } else if($event->userid) { // User event
b5d0cafc 530 $event->icon = '<img height="16" width="16" src="'.$OUTPUT->pix_url('c/user') . '" alt="'.get_string('userevent', 'calendar').'" style="vertical-align: middle;" />';
90723839 531 $event->cssclass = 'calendar_event_user';
3c134875 532 }
9df8ff44 533 return $event;
e295df44 534}
9df8ff44 535
76d9df3f 536function calendar_print_event($event, $showactions=true) {
6b608f8f 537 global $CFG, $USER, $OUTPUT;
9df8ff44 538
539 static $strftimetime;
540
541 $event = calendar_add_event_metadata($event);
8bdc9cac
SH
542 if (!($event instanceof calendar_event)) {
543 $event = new calendar_event($event);
544 }
53d8fac0 545 echo '<a name="event_'.$event->id.'"></a><table class="event" cellspacing="0">';
ed8ea92b 546 echo '<tr><td class="picture">';
3c134875 547 if (!empty($event->icon)) {
548 echo $event->icon;
549 } else {
1ba862ec 550 echo $OUTPUT->spacer(array('height'=>16, 'width'=>16, 'br'=>true)); // should be done with CSS instead
3c134875 551 }
552 echo '</td>';
df349d1d 553 echo '<td class="topic">';
9958a08c 554
3c134875 555 if (!empty($event->referer)) {
e96ba29d 556 echo '<div class="referer">'.$event->referer.'</div>';
3c134875 557 } else {
ed8ea92b 558 echo '<div class="name">'.$event->name."</div>";
3c134875 559 }
560 if (!empty($event->courselink)) {
ed8ea92b 561 echo '<div class="course">'.$event->courselink.' </div>';
3c134875 562 }
563 if (!empty($event->time)) {
ed8ea92b 564 echo '<span class="date">'.$event->time.'</span>';
3c134875 565 } else {
ed8ea92b 566 echo '<span class="date">'.calendar_time_representation($event->timestart).'</span>';
3c134875 567 }
9958a08c 568
ed8ea92b 569 echo '</td></tr>';
570 echo '<tr><td class="side">&nbsp;</td>';
9ba76792 571 if (isset($event->cssclass)) {
572 echo '<td class="description '.$event->cssclass.'">';
573 } else {
aa6c1ced 574 echo '<td class="description">';
9ba76792 575 }
aa6c1ced 576
8bdc9cac 577 echo $event->description;
76d9df3f 578 if (calendar_edit_event_allowed($event) && $showactions) {
ed8ea92b 579 echo '<div class="commands">';
54052a7a 580 $calendarcourseid = '';
581 if (!empty($event->calendarcourseid)) {
582 $calendarcourseid = '&amp;course='.$event->calendarcourseid;
583 }
3c134875 584 if (empty($event->cmid)) {
54052a7a 585 $editlink = CALENDAR_URL.'event.php?action=edit&amp;id='.$event->id.$calendarcourseid;
76d9df3f 586 $deletelink = CALENDAR_URL.'delete.php?id='.$event->id.$calendarcourseid;
3c134875 587 } else {
6132768e 588 $editlink = $CFG->wwwroot.'/course/mod.php?update='.$event->cmid.'&amp;return=true&amp;sesskey='.sesskey();
a71a3aef 589 $deletelink = ''; // deleting activities directly from calendar is dangerous/confusing - see MDL-11843
7423f116 590 }
3c134875 591 echo ' <a href="'.$editlink.'"><img
b5d0cafc 592 src="'.$OUTPUT->pix_url('t/edit') . '" alt="'.get_string('tt_editevent', 'calendar').'"
3c134875 593 title="'.get_string('tt_editevent', 'calendar').'" /></a>';
a71a3aef 594 if ($deletelink) {
595 echo ' <a href="'.$deletelink.'"><img
b5d0cafc 596 src="'.$OUTPUT->pix_url('t/delete') . '" alt="'.get_string('tt_deleteevent', 'calendar').'"
a71a3aef 597 title="'.get_string('tt_deleteevent', 'calendar').'" /></a>';
598 }
3c134875 599 echo '</div>';
7423f116 600 }
3c134875 601 echo '</td></tr></table>';
602
7423f116 603}
604
8263f802 605/**
606 * Get calendar events
607 * @param int $tstart Start time of time range for events
608 * @param int $tend End time of time range for events
609 * @param array/int/boolean $users array of users, user id or boolean for all/no user events
610 * @param array/int/boolean $groups array of groups, group id or boolean for all/no group events
611 * @param array/int/boolean $courses array of courses, course id or boolean for all/no course events
612 * @param boolean $withduration whether only events starting within time range selected
613 * or events in progress/already started selected as well
614 * @param boolean $ignorehidden whether to select only visible events or all events
615 * @return array of selected events or an empty array if there aren't any (or there was an error)
616 */
617function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withduration=true, $ignorehidden=true) {
62d11d77 618 global $DB;
619
7423f116 620 $whereclause = '';
621 // Quick test
622 if(is_bool($users) && is_bool($groups) && is_bool($courses)) {
8263f802 623 return array();
7423f116 624 }
482dbe0c 625
7423f116 626 if(is_array($users) && !empty($users)) {
627 // Events from a number of users
628 if(!empty($whereclause)) $whereclause .= ' OR';
6e957c41 629 $whereclause .= ' (userid IN ('.implode(',', $users).') AND courseid = 0 AND groupid = 0)';
7423f116 630 }
631 else if(is_numeric($users)) {
632 // Events from one user
633 if(!empty($whereclause)) $whereclause .= ' OR';
6e957c41 634 $whereclause .= ' (userid = '.$users.' AND courseid = 0 AND groupid = 0)';
7423f116 635 }
636 else if($users === true) {
637 // Events from ALL users
638 if(!empty($whereclause)) $whereclause .= ' OR';
6e957c41 639 $whereclause .= ' (userid != 0 AND courseid = 0 AND groupid = 0)';
7423f116 640 }
f52f7413 641 else if($users === false) {
6e957c41 642 // No user at all, do nothing
f52f7413 643 }
482dbe0c 644
7423f116 645 if(is_array($groups) && !empty($groups)) {
646 // Events from a number of groups
647 if(!empty($whereclause)) $whereclause .= ' OR';
648 $whereclause .= ' groupid IN ('.implode(',', $groups).')';
649 }
650 else if(is_numeric($groups)) {
651 // Events from one group
652 if(!empty($whereclause)) $whereclause .= ' OR ';
653 $whereclause .= ' groupid = '.$groups;
654 }
655 else if($groups === true) {
656 // Events from ALL groups
657 if(!empty($whereclause)) $whereclause .= ' OR ';
658 $whereclause .= ' groupid != 0';
659 }
482dbe0c 660 // boolean false (no groups at all): we don't need to do anything
661
f52f7413 662 if(is_array($courses)) {
663 // A number of courses (maybe none at all!)
664 if(!empty($courses)) {
665 if(!empty($whereclause)) {
666 $whereclause .= ' OR';
667 }
6e957c41 668 $whereclause .= ' (groupid = 0 AND courseid IN ('.implode(',', $courses).'))';
f52f7413 669 }
670 else {
671 // This means NO courses, not that we don't care!
09d36284 672 // No need to do anything
f52f7413 673 }
7423f116 674 }
675 else if(is_numeric($courses)) {
676 // One course
677 if(!empty($whereclause)) $whereclause .= ' OR';
6e957c41 678 $whereclause .= ' (groupid = 0 AND courseid = '.$courses.')';
7423f116 679 }
680 else if($courses === true) {
681 // Events from ALL courses
682 if(!empty($whereclause)) $whereclause .= ' OR';
6e957c41 683 $whereclause .= ' (groupid = 0 AND courseid != 0)';
7423f116 684 }
8c165fe9 685
482dbe0c 686 // Security check: if, by now, we have NOTHING in $whereclause, then it means
687 // that NO event-selecting clauses were defined. Thus, we won't be returning ANY
688 // events no matter what. Allowing the code to proceed might return a completely
689 // valid query with only time constraints, thus selecting ALL events in that time frame!
690 if(empty($whereclause)) {
8263f802 691 return array();
482dbe0c 692 }
693
7423f116 694 if($withduration) {
b4892fa2 695 $timeclause = '(timestart >= '.$tstart.' OR timestart + timeduration > '.$tstart.') AND timestart <= '.$tend;
7423f116 696 }
697 else {
698 $timeclause = 'timestart >= '.$tstart.' AND timestart <= '.$tend;
699 }
700 if(!empty($whereclause)) {
701 // We have additional constraints
702 $whereclause = $timeclause.' AND ('.$whereclause.')';
703 }
704 else {
705 // Just basic time filtering
706 $whereclause = $timeclause;
707 }
f52f7413 708
0ad072de 709 if ($ignorehidden) {
710 $whereclause .= ' AND visible = 1';
711 }
712
62d11d77 713 $events = $DB->get_records_select('event', $whereclause, null, 'timestart');
8263f802 714 if ($events === false) {
715 $events = array();
716 }
717 return $events;
7423f116 718}
719
720function calendar_top_controls($type, $data) {
78946b9b 721 global $CFG, $CALENDARDAYS;
7423f116 722 $content = '';
723 if(!isset($data['d'])) {
724 $data['d'] = 1;
725 }
5147ad48 726
f21ed0f3 727 // Ensure course id passed if relevant
728 // Required due to changes in view/lib.php mainly (calendar_session_vars())
729 $courseid = '';
730 if (!empty($data['id'])) {
731 $courseid = '&amp;course='.$data['id'];
732 }
733
5147ad48 734 if(!checkdate($data['m'], $data['d'], $data['y'])) {
735 $time = time();
736 }
737 else {
738 $time = make_timestamp($data['y'], $data['m'], $data['d']);
739 }
740 $date = usergetdate($time);
e295df44 741
7423f116 742 $data['m'] = $date['mon'];
743 $data['y'] = $date['year'];
7423f116 744
2a06efcc 745 //Accessibility: calendar block controls, replaced <table> with <div>.
a84dea2c 746 //$nexttext = link_arrow_right(get_string('monthnext', 'access'), $url='', $accesshide=true);
747 //$prevtext = link_arrow_left(get_string('monthprev', 'access'), $url='', $accesshide=true);
2a06efcc 748
7423f116 749 switch($type) {
750 case 'frontpage':
751 list($prevmonth, $prevyear) = calendar_sub_month($data['m'], $data['y']);
752 list($nextmonth, $nextyear) = calendar_add_month($data['m'], $data['y']);
a84dea2c 753 $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), 'index.php?', 0, $nextmonth, $nextyear, $accesshide=true);
754 $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), 'index.php?', 0, $prevmonth, $prevyear, true);
755 $content .= "\n".'<div class="calendar-controls">'. $prevlink;
54052a7a 756 $content .= '<span class="hide"> | </span><span class="current"><a href="'.calendar_get_link_href(CALENDAR_URL.'view.php?view=month'.$courseid.'&amp;', 1, $data['m'], $data['y']).'">'.userdate($time, get_string('strftimemonthyear')).'</a></span>';
a84dea2c 757 $content .= '<span class="hide"> | </span>'. $nextlink ."\n";
f2de62da 758 $content .= "<span class=\"clearer\"><!-- --></span></div>\n";
7423f116 759 break;
760 case 'course':
761 list($prevmonth, $prevyear) = calendar_sub_month($data['m'], $data['y']);
762 list($nextmonth, $nextyear) = calendar_add_month($data['m'], $data['y']);
a84dea2c 763 $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), 'view.php?id='.$data['id'].'&amp;', 0, $nextmonth, $nextyear, $accesshide=true);
764 $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), 'view.php?id='.$data['id'].'&amp;', 0, $prevmonth, $prevyear, true);
765 $content .= "\n".'<div class="calendar-controls">'. $prevlink;
54052a7a 766 $content .= '<span class="hide"> | </span><span class="current"><a href="'.calendar_get_link_href(CALENDAR_URL.'view.php?view=month'.$courseid.'&amp;', 1, $data['m'], $data['y']).'">'.userdate($time, get_string('strftimemonthyear')).'</a></span>';
a84dea2c 767 $content .= '<span class="hide"> | </span>'. $nextlink ."\n";
f2de62da 768 $content .= "<span class=\"clearer\"><!-- --></span></div>\n";
7423f116 769 break;
770 case 'upcoming':
f21ed0f3 771 $content .= '<div style="text-align: center;"><a href="'.CALENDAR_URL.'view.php?view=upcoming"'.$courseid.'>'.userdate($time, get_string('strftimemonthyear'))."</a></div>\n";
7423f116 772 break;
773 case 'display':
e2ce18a5 774 $content .= '<h3><a href="'.calendar_get_link_href(CALENDAR_URL.'view.php?view=month'.$courseid.'&amp;', 1, $data['m'], $data['y']).'">'.userdate($time, get_string('strftimemonthyear'))."</a></h3>\n";
7423f116 775 break;
776 case 'month':
777 list($prevmonth, $prevyear) = calendar_sub_month($data['m'], $data['y']);
778 list($nextmonth, $nextyear) = calendar_add_month($data['m'], $data['y']);
5147ad48 779 $prevdate = make_timestamp($prevyear, $prevmonth, 1);
780 $nextdate = make_timestamp($nextyear, $nextmonth, 1);
a84dea2c 781 $content .= "\n".'<div class="calendar-controls">';
f21ed0f3 782 $content .= calendar_get_link_previous(userdate($prevdate, get_string('strftimemonthyear')), 'view.php?view=month'.$courseid.'&amp;', 1, $prevmonth, $prevyear);
ed9f9947 783 $content .= '<span class="hide"> | </span><h1 class="current">'.userdate($time, get_string('strftimemonthyear'))."</h1>\n";
f21ed0f3 784 $content .= '<span class="hide"> | </span>'.calendar_get_link_next(userdate($nextdate, get_string('strftimemonthyear')), 'view.php?view=month'.$courseid.'&amp;', 1, $nextmonth, $nextyear);
f2de62da 785 $content .= "<span class=\"clearer\"><!-- --></span></div>\n";
7423f116 786 break;
787 case 'day':
788 $data['d'] = $date['mday']; // Just for convenience
5147ad48 789 $prevdate = usergetdate(make_timestamp($data['y'], $data['m'], $data['d'] - 1));
790 $nextdate = usergetdate(make_timestamp($data['y'], $data['m'], $data['d'] + 1));
f2bffd9e 791 $prevname = calendar_wday_name($CALENDARDAYS[$prevdate['wday']]);
792 $nextname = calendar_wday_name($CALENDARDAYS[$nextdate['wday']]);
a84dea2c 793 $content .= "\n".'<div class="calendar-controls">';
f21ed0f3 794 $content .= calendar_get_link_previous($prevname, 'view.php?view=day'.$courseid.'&amp;', $prevdate['mday'], $prevdate['mon'], $prevdate['year']);
e295df44 795
6e8e8ec6 796 // Get the format string
797 $text = get_string('strftimedaydate');
3477a900 798 /*
6e8e8ec6 799 // Regexp hackery to make a link out of the month/year part
6dbcacee 800 $text = preg_replace('/(%B.+%Y|%Y.+%B|%Y.+%m[^ ]+)/', '<a href="'.calendar_get_link_href('view.php?view=month&amp;', 1, $data['m'], $data['y']).'">\\1</a>', $text);
801 $text = preg_replace('/(F.+Y|Y.+F|Y.+m[^ ]+)/', '<a href="'.calendar_get_link_href('view.php?view=month&amp;', 1, $data['m'], $data['y']).'">\\1</a>', $text);
3477a900 802 */
6e8e8ec6 803 // Replace with actual values and lose any day leading zero
3477a900 804 $text = userdate($time, $text);
6e8e8ec6 805 // Print the actual thing
a7c385e6 806 $content .= '<span class="hide"> | </span><span class="current">'.$text.'</span>';
6e8e8ec6 807
f21ed0f3 808 $content .= '<span class="hide"> | </span>'. calendar_get_link_next($nextname, 'view.php?view=day'.$courseid.'&amp;', $nextdate['mday'], $nextdate['mon'], $nextdate['year']);
f2de62da 809 $content .= "<span class=\"clearer\"><!-- --></span></div>\n";
7423f116 810 break;
811 }
812 return $content;
813}
814
b4892fa2 815function calendar_filter_controls($type, $vars = NULL, $course = NULL, $courses = NULL) {
6b608f8f 816 global $CFG, $SESSION, $USER, $OUTPUT;
7423f116 817
48f508ab 818 $groupevents = true;
3cb9ee39 819 $getvars = '';
e295df44 820
7f4d18fc 821 $id = optional_param( 'id',0,PARAM_INT );
d715f7c4 822
34bf3ad4 823 switch($type) {
eb15f829 824 case 'event':
34bf3ad4 825 case 'upcoming':
34bf3ad4 826 case 'day':
c3f463ca 827 case 'month':
eb15f829 828 $getvars = '&amp;from='.$type;
34bf3ad4 829 break;
830 case 'course':
fac01360 831 if ($id > 0) {
7f4d18fc 832 $getvars = '&amp;from=course&amp;id='.$id;
2eb68e6f 833 } else {
834 $getvars = '&amp;from=course';
835 }
5a74ffd3 836 if (isset($course->groupmode) and $course->groupmode == NOGROUPS and $course->groupmodeforce) {
34bf3ad4 837 $groupevents = false;
838 }
839 break;
d715f7c4 840 }
34bf3ad4 841
3cb9ee39 842 if (!empty($vars)) {
34bf3ad4 843 $getvars .= '&amp;'.$vars;
48f508ab 844 }
7423f116 845
2ad2cbc3 846 $content = '<table>';
7423f116 847
48f508ab 848 $content .= '<tr>';
849 if($SESSION->cal_show_global) {
90723839 850 $content .= '<td class="eventskey calendar_event_global" style="width: 11px;"><img src="'.$OUTPUT->pix_url('t/hide') . '" class="iconsmall" alt="'.get_string('hide').'" title="'.get_string('tt_hideglobal', 'calendar').'" style="cursor:pointer" onclick="location.href='."'".CALENDAR_URL.'set.php?var=showglobal'.$getvars."'".'" /></td>';
b4892fa2 851 $content .= '<td><a href="'.CALENDAR_URL.'set.php?var=showglobal'.$getvars.'" title="'.get_string('tt_hideglobal', 'calendar').'">'.get_string('global', 'calendar').'</a></td>'."\n";
fdbffa54 852 } else {
b5d0cafc 853 $content .= '<td style="width: 11px;"><img src="'.$OUTPUT->pix_url('t/show') . '" class="iconsmall" alt="'.get_string('show').'" title="'.get_string('tt_showglobal', 'calendar').'" style="cursor:pointer" onclick="location.href='."'".CALENDAR_URL.'set.php?var=showglobal'.$getvars."'".'" /></td>';
b4892fa2 854 $content .= '<td><a href="'.CALENDAR_URL.'set.php?var=showglobal'.$getvars.'" title="'.get_string('tt_showglobal', 'calendar').'">'.get_string('global', 'calendar').'</a></td>'."\n";
48f508ab 855 }
e3bb6401 856 if($SESSION->cal_show_course) {
90723839 857 $content .= '<td class="eventskey calendar_event_course" style="width: 11px;"><img src="'.$OUTPUT->pix_url('t/hide') . '" class="iconsmall" alt="'.get_string('hide').'" title="'.get_string('tt_hidecourse', 'calendar').'" style="cursor:pointer" onclick="location.href='."'".CALENDAR_URL.'set.php?var=showcourses'.$getvars."'".'" /></td>';
fdbffa54 858 $content .= '<td><a href="'.CALENDAR_URL.'set.php?var=showcourses'.$getvars.'" title="'.get_string('tt_hidecourse', 'calendar').'">'.get_string('course', 'calendar').'</a></td>'."\n";
859 } else {
b5d0cafc 860 $content .= '<td style="width: 11px;"><img src="'.$OUTPUT->pix_url('t/show') . '" class="iconsmall" alt="'.get_string('hide').'" title="'.get_string('tt_showcourse', 'calendar').'" style="cursor:pointer" onclick="location.href='."'".CALENDAR_URL.'set.php?var=showcourses'.$getvars."'".'" /></td>';
fdbffa54 861 $content .= '<td><a href="'.CALENDAR_URL.'set.php?var=showcourses'.$getvars.'" title="'.get_string('tt_showcourse', 'calendar').'">'.get_string('course', 'calendar').'</a></td>'."\n";
aa6c1ced 862
e3bb6401 863 }
48f508ab 864
b4892fa2 865
4f0c2d00 866 if (isloggedin() && !isguestuser()) {
e3bb6401 867 $content .= "</tr>\n<tr>";
10b1d598 868
869 if($groupevents) {
43c3ffbe 870 // This course MIGHT have group events defined, so show the filter
871 if($SESSION->cal_show_groups) {
90723839 872 $content .= '<td class="eventskey calendar_event_group" style="width: 11px;"><img src="'.$OUTPUT->pix_url('t/hide') . '" class="iconsmall" alt="'.get_string('hide').'" title="'.get_string('tt_hidegroups', 'calendar').'" style="cursor:pointer" onclick="location.href='."'".CALENDAR_URL.'set.php?var=showgroups'.$getvars."'".'" /></td>';
fdbffa54 873 $content .= '<td><a href="'.CALENDAR_URL.'set.php?var=showgroups'.$getvars.'" title="'.get_string('tt_hidegroups', 'calendar').'">'.get_string('group', 'calendar').'</a></td>'."\n";
e3bb6401 874 } else {
b5d0cafc 875 $content .= '<td style="width: 11px;"><img src="'.$OUTPUT->pix_url('t/show') . '" class="iconsmall" alt="'.get_string('show').'" title="'.get_string('tt_showgroups', 'calendar').'" style="cursor:pointer" onclick="location.href='."'".CALENDAR_URL.'set.php?var=showgroups'.$getvars."'".'" /></td>';
fdbffa54 876 $content .= '<td><a href="'.CALENDAR_URL.'set.php?var=showgroups'.$getvars.'" title="'.get_string('tt_showgroups', 'calendar').'">'.get_string('group', 'calendar').'</a></td>'."\n";
43c3ffbe 877 }
e3bb6401 878 } else {
43c3ffbe 879 // This course CANNOT have group events, so lose the filter
5c53988f 880 $content .= '<td style="width: 11px;"></td><td>&nbsp;</td>'."\n";
fdbffa54 881 }
882 if($SESSION->cal_show_user) {
90723839 883 $content .= '<td class="eventskey calendar_event_user" style="width: 11px;"><img src="'.$OUTPUT->pix_url('t/hide') . '" class="iconsmall" alt="'.get_string('hide').'" title="'.get_string('tt_hideuser', 'calendar').'" style="cursor:pointer" onclick="location.href='."'".CALENDAR_URL.'set.php?var=showuser'.$getvars."'".'" /></td>';
fdbffa54 884 $content .= '<td><a href="'.CALENDAR_URL.'set.php?var=showuser'.$getvars.'" title="'.get_string('tt_hideuser', 'calendar').'">'.get_string('user', 'calendar').'</a></td>'."\n";
885 } else {
b5d0cafc 886 $content .= '<td style="width: 11px;"><img src="'.$OUTPUT->pix_url('t/show') . '" class="iconsmall" alt="'.get_string('show').'" title="'.get_string('tt_showuser', 'calendar').'" style="cursor:pointer" onclick="location.href='."'".CALENDAR_URL.'set.php?var=showuser'.$getvars."'".'" /></td>';
fdbffa54 887 $content .= '<td><a href="'.CALENDAR_URL.'set.php?var=showuser'.$getvars.'" title="'.get_string('tt_showuser', 'calendar').'">'.get_string('user', 'calendar').'</a></td>'."\n";
48f508ab 888 }
889 }
890 $content .= "</tr>\n</table>\n";
891
7423f116 892 return $content;
893}
894
895function calendar_day_representation($tstamp, $now = false, $usecommonwords = true) {
896
0ef7c973 897 static $shortformat;
898 if(empty($shortformat)) {
e70fdac0 899 $shortformat = get_string('strftimedayshort');
0ef7c973 900 }
901
7423f116 902 if($now === false) {
903 $now = time();
904 }
905
906 // To have it in one place, if a change is needed
e70fdac0 907 $formal = userdate($tstamp, $shortformat);
7423f116 908
7b38bfa6 909 $datestamp = usergetdate($tstamp);
910 $datenow = usergetdate($now);
7423f116 911
912 if($usecommonwords == false) {
913 // We don't want words, just a date
914 return $formal;
915 }
7b38bfa6 916 else if($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday']) {
7423f116 917 // Today
918 return get_string('today', 'calendar');
919 }
7b38bfa6 920 else if(
921 ($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday'] - 1 ) ||
922 ($datestamp['year'] == $datenow['year'] - 1 && $datestamp['mday'] == 31 && $datestamp['mon'] == 12 && $datenow['yday'] == 1)
923 ) {
7423f116 924 // Yesterday
925 return get_string('yesterday', 'calendar');
926 }
7b38bfa6 927 else if(
928 ($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday'] + 1 ) ||
929 ($datestamp['year'] == $datenow['year'] + 1 && $datenow['mday'] == 31 && $datenow['mon'] == 12 && $datestamp['yday'] == 1)
930 ) {
7423f116 931 // Tomorrow
932 return get_string('tomorrow', 'calendar');
933 }
934 else {
935 return $formal;
936 }
937}
938
939function calendar_time_representation($time) {
1b0ebe79 940 static $langtimeformat = NULL;
941 if($langtimeformat === NULL) {
942 $langtimeformat = get_string('strftimetime');
943 }
944 $timeformat = get_user_preferences('calendar_timeformat');
c7dd2550 945 if(empty($timeformat)){
946 $timeformat = get_config(NULL,'calendar_site_timeformat');
947 }
1b0ebe79 948 // The ? is needed because the preference might be present, but empty
949 return userdate($time, empty($timeformat) ? $langtimeformat : $timeformat);
7423f116 950}
951
e295df44 952/**
953 * TODO document
954 */
7423f116 955function calendar_get_link_href($linkbase, $d, $m, $y) {
956 if(empty($linkbase)) return '';
957 $paramstr = '';
958 if(!empty($d)) $paramstr .= '&amp;cal_d='.$d;
959 if(!empty($m)) $paramstr .= '&amp;cal_m='.$m;
960 if(!empty($y)) $paramstr .= '&amp;cal_y='.$y;
961 if(!empty($paramstr)) $paramstr = substr($paramstr, 5);
962 return $linkbase.$paramstr;
963}
964
e295df44 965/**
966 * TODO document
967 */
7423f116 968function calendar_get_link_tag($text, $linkbase, $d, $m, $y) {
969 $href = calendar_get_link_href($linkbase, $d, $m, $y);
970 if(empty($href)) return $text;
971 return '<a href="'.$href.'">'.$text.'</a>';
972}
973
a84dea2c 974/**
975 * Build and return a previous month HTML link, with an arrow.
976 * @param string $text The text label.
977 * @param string $linkbase The URL stub.
978 * @param int $d $m $y Day of month, month and year numbers.
979 * @param bool $accesshide Default visible, or hide from all except screenreaders.
980 * @return string HTML string.
981 */
982function calendar_get_link_previous($text, $linkbase, $d, $m, $y, $accesshide=false) {
983 $href = calendar_get_link_href($linkbase, $d, $m, $y);
984 if(empty($href)) return $text;
985 return link_arrow_left($text, $href, $accesshide, 'previous');
986}
987
988/**
989 * Build and return a next month HTML link, with an arrow.
990 * @param string $text The text label.
991 * @param string $linkbase The URL stub.
992 * @param int $d $m $y Day of month, month and year numbers.
993 * @param bool $accesshide Default visible, or hide from all except screenreaders.
994 * @return string HTML string.
995 */
996function calendar_get_link_next($text, $linkbase, $d, $m, $y, $accesshide=false) {
997 $href = calendar_get_link_href($linkbase, $d, $m, $y);
998 if(empty($href)) return $text;
999 return link_arrow_right($text, $href, $accesshide, 'next');
1000}
1001
7423f116 1002function calendar_wday_name($englishname) {
1003 return get_string(strtolower($englishname), 'calendar');
1004}
1005
1006function calendar_days_in_month($month, $year) {
7b38bfa6 1007 return intval(date('t', mktime(0, 0, 0, $month, 1, $year)));
7423f116 1008}
1009
6605ff8c 1010function calendar_get_block_upcoming($events, $linkhref = NULL) {
7423f116 1011 $content = '';
1012 $lines = count($events);
396b61f0 1013 if (!$lines) {
1014 return $content;
1015 }
7423f116 1016
396b61f0 1017 for ($i = 0; $i < $lines; ++$i) {
b0ac9180 1018 if (!isset($events[$i]->time)) { // Just for robustness
1019 continue;
1020 }
9df8ff44 1021 $events[$i] = calendar_add_event_metadata($events[$i]);
ea21c1f4 1022 $content .= '<div class="event"><span class="icon c0">'.$events[$i]->icon.'</span> ';
43c3ffbe 1023 if (!empty($events[$i]->referer)) {
7423f116 1024 // That's an activity event, so let's provide the hyperlink
396b61f0 1025 $content .= $events[$i]->referer;
1026 } else {
e749554e 1027 if(!empty($linkhref)) {
1028 $ed = usergetdate($events[$i]->timestart);
1029 $href = calendar_get_link_href(CALENDAR_URL.$linkhref, $ed['mday'], $ed['mon'], $ed['year']);
53d8fac0 1030 $content .= '<a href="'.$href.'#event_'.$events[$i]->id.'">'.$events[$i]->name.'</a>';
e749554e 1031 }
1032 else {
1033 $content .= $events[$i]->name;
1034 }
7423f116 1035 }
9ecf051d 1036 $events[$i]->time = str_replace('&raquo;', '<br />&raquo;', $events[$i]->time);
1037 $content .= '<div class="date">'.$events[$i]->time.'</div></div>';
396b61f0 1038 if ($i < $lines - 1) $content .= '<hr />';
7423f116 1039 }
1040
1041 return $content;
7423f116 1042}
1043
1044function calendar_add_month($month, $year) {
1045 if($month == 12) {
1046 return array(1, $year + 1);
1047 }
1048 else {
1049 return array($month + 1, $year);
1050 }
1051}
1052
1053function calendar_sub_month($month, $year) {
1054 if($month == 1) {
1055 return array(12, $year - 1);
1056 }
1057 else {
1058 return array($month - 1, $year);
1059 }
1060}
1061
7c50db30 1062function calendar_events_by_day($events, $month, $year, &$eventsbyday, &$durationbyday, &$typesbyday, &$courses) {
7423f116 1063 $eventsbyday = array();
1064 $typesbyday = array();
1065 $durationbyday = array();
1066
1067 if($events === false) {
1068 return;
1069 }
1070
7423f116 1071 foreach($events as $event) {
7423f116 1072
7b38bfa6 1073 $startdate = usergetdate($event->timestart);
b4892fa2 1074 // Set end date = start date if no duration
1075 if ($event->timeduration) {
1076 $enddate = usergetdate($event->timestart + $event->timeduration - 1);
1077 } else {
1078 $enddate = $startdate;
1079 }
7423f116 1080
7b38bfa6 1081 // Simple arithmetic: $year * 13 + $month is a distinct integer for each distinct ($year, $month) pair
ef618501 1082 if(!($startdate['year'] * 13 + $startdate['mon'] <= $year * 13 + $month) && ($enddate['year'] * 13 + $enddate['mon'] >= $year * 13 + $month)) {
7b38bfa6 1083 // Out of bounds
1084 continue;
7423f116 1085 }
1086
7b38bfa6 1087 $eventdaystart = intval($startdate['mday']);
7423f116 1088
7b38bfa6 1089 if($startdate['mon'] == $month && $startdate['year'] == $year) {
1090 // Give the event to its day
1091 $eventsbyday[$eventdaystart][] = $event->id;
7423f116 1092
7b38bfa6 1093 // Mark the day as having such an event
9064751b 1094 if($event->courseid == SITEID && $event->groupid == 0) {
7b38bfa6 1095 $typesbyday[$eventdaystart]['startglobal'] = true;
b4892fa2 1096 // Set event class for global event
90723839 1097 $events[$event->id]->class = 'calendar_event_global';
7b38bfa6 1098 }
c3d3b6d4 1099 else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) {
7b38bfa6 1100 $typesbyday[$eventdaystart]['startcourse'] = true;
b4892fa2 1101 // Set event class for course event
90723839 1102 $events[$event->id]->class = 'calendar_event_course';
7b38bfa6 1103 }
1104 else if($event->groupid) {
1105 $typesbyday[$eventdaystart]['startgroup'] = true;
b4892fa2 1106 // Set event class for group event
90723839 1107 $events[$event->id]->class = 'calendar_event_group';
7b38bfa6 1108 }
1109 else if($event->userid) {
1110 $typesbyday[$eventdaystart]['startuser'] = true;
b4892fa2 1111 // Set event class for user event
90723839 1112 $events[$event->id]->class = 'calendar_event_user';
7b38bfa6 1113 }
1114 }
7423f116 1115
7b38bfa6 1116 if($event->timeduration == 0) {
1117 // Proceed with the next
1118 continue;
1119 }
7423f116 1120
7b38bfa6 1121 // The event starts on $month $year or before. So...
ef618501 1122 $lowerbound = $startdate['mon'] == $month && $startdate['year'] == $year ? intval($startdate['mday']) : 0;
7b38bfa6 1123
1124 // Also, it ends on $month $year or later...
1125 $upperbound = $enddate['mon'] == $month && $enddate['year'] == $year ? intval($enddate['mday']) : calendar_days_in_month($month, $year);
1126
1127 // Mark all days between $lowerbound and $upperbound (inclusive) as duration
1128 for($i = $lowerbound + 1; $i <= $upperbound; ++$i) {
1129 $durationbyday[$i][] = $event->id;
9064751b 1130 if($event->courseid == SITEID && $event->groupid == 0) {
7b38bfa6 1131 $typesbyday[$i]['durationglobal'] = true;
1132 }
c3d3b6d4 1133 else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) {
7b38bfa6 1134 $typesbyday[$i]['durationcourse'] = true;
1135 }
1136 else if($event->groupid) {
1137 $typesbyday[$i]['durationgroup'] = true;
1138 }
1139 else if($event->userid) {
1140 $typesbyday[$i]['durationuser'] = true;
7423f116 1141 }
1142 }
7b38bfa6 1143
7423f116 1144 }
1145 return;
1146}
1147
b63c0ee5 1148function calendar_get_module_cached(&$coursecache, $modulename, $instance) {
1149 $module = get_coursemodule_from_instance($modulename, $instance);
7423f116 1150
1151 if($module === false) return false;
b63c0ee5 1152 if(!calendar_get_course_cached($coursecache, $module->course)) {
7423f116 1153 return false;
1154 }
1155 return $module;
1156}
1157
1158function calendar_get_course_cached(&$coursecache, $courseid) {
62d11d77 1159 global $COURSE, $DB;
1160
b571c6b3 1161 if (!isset($coursecache[$courseid])) {
1162 if ($courseid == $COURSE->id) {
1163 $coursecache[$courseid] = $COURSE;
1164 } else {
62d11d77 1165 $coursecache[$courseid] = $DB->get_record('course', array('id'=>$courseid));
b571c6b3 1166 }
7423f116 1167 }
1168 return $coursecache[$courseid];
1169}
1170
dd97c328 1171function calendar_session_vars($course=null) {
7423f116 1172 global $SESSION, $USER;
1173
1174 if(!isset($SESSION->cal_course_referer)) {
1175 $SESSION->cal_course_referer = 0;
1176 }
1177 if(!isset($SESSION->cal_show_global)) {
1178 $SESSION->cal_show_global = true;
1179 }
1180 if(!isset($SESSION->cal_show_groups)) {
1181 $SESSION->cal_show_groups = true;
1182 }
1183 if(!isset($SESSION->cal_show_course)) {
1184 $SESSION->cal_show_course = true;
1185 }
1186 if(!isset($SESSION->cal_show_user)) {
89adb174 1187 $SESSION->cal_show_user = true;
7423f116 1188 }
76d9df3f 1189 if ($course !== null) {
dd97c328 1190 // speedup hack for calendar related blocks
1191 $SESSION->cal_courses_shown = array($course->id => $course);
aa6c1ced 1192 } else {
43c3ffbe 1193 $SESSION->cal_courses_shown = calendar_get_default_courses(true);
dd97c328 1194 }
89adb174 1195 if(empty($SESSION->cal_users_shown)) {
1196 // The empty() instead of !isset() here makes a whole world of difference,
1197 // as it will automatically change to the user's id when the user first logs
1198 // in. With !isset(), it would never do that.
4f0c2d00
PS
1199 $SESSION->cal_users_shown = isloggedin() ? $USER->id : false;
1200 } else if(is_numeric($SESSION->cal_users_shown) && isloggedin() && $SESSION->cal_users_shown != $USER->id) {
89adb174 1201 // Follow the white rabbit, for example if a teacher logs in as a student
1202 $SESSION->cal_users_shown = $USER->id;
1203 }
7423f116 1204}
1205
7423f116 1206function calendar_set_referring_course($courseid) {
1207 global $SESSION;
1208 $SESSION->cal_course_referer = intval($courseid);
1209}
1210
43c3ffbe 1211function calendar_set_filters(&$courses, &$group, &$user, $courseeventsfrom = NULL, $groupeventsfrom = NULL, $ignorefilters = false) {
62d11d77 1212 global $SESSION, $USER, $CFG, $DB;
257e3f4c 1213
43c3ffbe 1214 // Insidious bug-wannabe: setting $SESSION->cal_courses_shown to $course->id would cause
51f8a12f 1215 // the code to function incorrectly UNLESS we convert it to an integer. One case where
1216 // PHP's loose type system works against us.
43c3ffbe 1217 if(is_string($SESSION->cal_courses_shown)) {
1218 $SESSION->cal_courses_shown = intval($SESSION->cal_courses_shown);
51f8a12f 1219 }
43c3ffbe 1220 if($courseeventsfrom === NULL) {
aa6c1ced 1221 $courseeventsfrom = $SESSION->cal_courses_shown;
d12e3ff2 1222 }
aa6c1ced 1223
d12e3ff2 1224 // MDL-9059, $courseeventsfrom can be an int, or an array of ints, or an array of course objects
1225 // convert all to array of objects
1226 // we probably should do some clean up and make sure that session is set to use the proper form
1227 if (is_int($courseeventsfrom)) { // case of an int, e.g. calendar view page
1228 $c = array();
62d11d77 1229 $c[$courseeventsfrom] = $DB->get_record('course', array('id'=>$courseeventsfrom));
d12e3ff2 1230 $courseeventsfrom = $c;
1231 } else if (is_array($courseeventsfrom)) { // case of an array of ints, e.g. course home page
dd97c328 1232 foreach ($courseeventsfrom as $i=>$courseid) { // TODO: this seems wrong, the array is often constructed as [courseid] => 1 ???
d12e3ff2 1233 if (is_int($courseid)) {
62d11d77 1234 $courseeventsfrom[$i] = $DB->get_record('course', array('id'=>$courseid));
aa6c1ced
PS
1235 }
1236 }
7423f116 1237 }
d12e3ff2 1238
43c3ffbe 1239 if($groupeventsfrom === NULL) {
1240 $groupeventsfrom = $SESSION->cal_courses_shown;
7423f116 1241 }
1242
43c3ffbe 1243 if(($SESSION->cal_show_course && $SESSION->cal_show_global) || $ignorefilters) {
1244 if(is_int($courseeventsfrom)) {
9064751b 1245 $courses = array(SITEID, $courseeventsfrom);
7423f116 1246 }
43c3ffbe 1247 else if(is_array($courseeventsfrom)) {
1248 $courses = array_keys($courseeventsfrom);
9064751b 1249 $courses[] = SITEID;
7423f116 1250 }
1251 }
43c3ffbe 1252 else if($SESSION->cal_show_course) {
1253 if(is_int($courseeventsfrom)) {
1254 $courses = array($courseeventsfrom);
7423f116 1255 }
43c3ffbe 1256 else if(is_array($courseeventsfrom)) {
1257 $courses = array_keys($courseeventsfrom);
7423f116 1258 }
e749554e 1259 $courses = array_diff($courses, array(SITEID));
7423f116 1260 }
1261 else if($SESSION->cal_show_global) {
e749554e 1262 $courses = array(SITEID);
7423f116 1263 }
1264 else {
1265 $courses = false;
1266 }
e295df44 1267 //BUG 6130 clean $courses array as SESSION has bad entries.
0e6a8f4b 1268 // [pj] TODO: See if this has to do with my new change in get_default_courses and can be taken out
1384da1e 1269 if (is_array($courses)) {
1270 foreach ($courses as $index => $value) {
1271 if (empty($value)) unset($courses[$index]);
1272 }
7c50db30 1273
1274 // Sort courses for consistent colour highlighting
1275 // Effectively ignoring SITEID as setting as last course id
1276 $key = array_search(SITEID, $courses);
1277 if ($key !== false) {
1278 unset($courses[$key]);
1279 sort($courses);
1280 $courses[] = SITEID;
1281 } else {
1282 sort($courses);
1283 }
0e6a8f4b 1284 }
7423f116 1285
43c3ffbe 1286 if($SESSION->cal_show_user || $ignorefilters) {
89adb174 1287 // This doesn't work for arrays yet (maybe someday it will)
1288 $user = $SESSION->cal_users_shown;
7423f116 1289 }
1290 else {
1291 $user = false;
1292 }
43c3ffbe 1293 if($SESSION->cal_show_groups || $ignorefilters) {
1294 if(is_int($groupeventsfrom)) {
1295 $groupcourses = array($groupeventsfrom);
7423f116 1296 }
43c3ffbe 1297 else if(is_array($groupeventsfrom)) {
1298 $groupcourses = array_keys($groupeventsfrom);
1299 }
257e3f4c 1300
9c37662f 1301 // XXX TODO: not sure how to replace $CFG->calendar_adminseesall
ef35441d 1302 if(has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_SYSTEM)) && !empty($CFG->calendar_adminseesall)) {
257e3f4c 1303 $group = true;
1304 }
1305 else {
1306 $grouparray = array();
69762501 1307
257e3f4c 1308 // We already have the courses to examine in $courses
1309 // For each course...
37d87d11 1310
257e3f4c 1311 foreach($groupcourses as $courseid) {
69762501 1312
37875ac5 1313 if (!isset($courseeventsfrom[$courseid]->context)) { // SHOULD be set MDL-11221
8397874d 1314 if (is_object($courseeventsfrom[$courseid])) {
1315 $courseeventsfrom[$courseid]->context = get_context_instance(CONTEXT_COURSE, $courseid);
1316 }
37875ac5 1317 }
1318
257e3f4c 1319 // If the user is an editing teacher in there,
4f0c2d00 1320 if (isloggedin() && isset($courseeventsfrom[$courseid]->context) && has_capability('moodle/calendar:manageentries', $courseeventsfrom[$courseid]->context)) {
69762501 1321 // If this course has groups, show events from all of them
b8227b88 1322 if(is_int($groupeventsfrom)) {
37875ac5 1323 if (is_object($courseeventsfrom[$courseid])) { // SHOULD be set MDL-11221
1324 $courserecord = $courseeventsfrom[$courseid];
1325 } else {
62d11d77 1326 $courserecord = $DB->get_record('course', array('id'=>$courseid));
aa6c1ced 1327 }
62d11d77 1328 $courserecord = $DB->get_record('course', array('id'=>$courseid));
b8227b88 1329 if ($courserecord->groupmode != NOGROUPS || !$courserecord->groupmodeforce) {
1330 $groupids[] = $courseid;
1331 }
1332 }
1333 else if(isset($SESSION->cal_courses_shown[$courseid]) && ($SESSION->cal_courses_shown[$courseid]->groupmode != NOGROUPS || !$SESSION->cal_courses_shown[$courseid]->groupmodeforce)) {
37d87d11 1334 $groupids[] = $courseid;
257e3f4c 1335 }
1336 }
69762501 1337
1338 // Otherwise (not editing teacher) show events from the group he is a member of
257e3f4c 1339 else if(isset($USER->groupmember[$courseid])) {
fa22fd5f 1340 //changed to 2D array
1341 foreach ($USER->groupmember[$courseid] as $groupid){
1342 $grouparray[] = $groupid;
1343 }
7423f116 1344 }
1345 }
e295df44 1346
6b4aeb31 1347 if (!empty($groupids)) {
f6ee5e04 1348 $sql = "SELECT *
62d11d77 1349 FROM {groups}
6b4aeb31 1350 WHERE courseid IN (".implode(',', $groupids).')';
e295df44 1351
62d11d77 1352 if ($grouprecords = $DB->get_records_sql($sql, null)) {
f6ee5e04 1353 foreach ($grouprecords as $grouprecord) {
1354 $grouparray[] = $grouprecord->id;
1355 }
6b4aeb31 1356 }
1357 }
e295df44 1358
257e3f4c 1359 if(empty($grouparray)) {
1360 $group = false;
1361 }
1362 else {
1363 $group = $grouparray;
6c9584d1 1364 }
7423f116 1365 }
e295df44 1366
7423f116 1367 }
1368 else {
1369 $group = false;
1370 }
1371}
1372
1373function calendar_edit_event_allowed($event) {
62d11d77 1374 global $USER, $DB;
7423f116 1375
2ac8da76 1376 // Must be logged in
1377 if (!isloggedin()) {
1378 return false;
1379 }
1380
89491dbd 1381 // can not be using guest account
1382 if ($USER->username == "guest") {
e295df44 1383 return false;
89491dbd 1384 }
e295df44 1385
ef35441d 1386 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
89491dbd 1387 // if user has manageentries at site level, return true
28ee98c5 1388 if (has_capability('moodle/calendar:manageentries', $sitecontext)) {
89491dbd 1389 return true;
f52f7413 1390 }
e295df44 1391
c0a2c361 1392 // if groupid is set, it's definitely a group event
438e4add 1393 if (!empty($event->groupid)) {
f63d2922 1394 // Allow users to add/edit group events if:
1395 // 1) They have manageentries (= entries for whole course)
1396 // 2) They have managegroupentries AND are in the group
62d11d77 1397 $group = $DB->get_record('groups', array('id'=>$event->groupid));
f63d2922 1398 return $group && (
1399 has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_COURSE, $group->courseid)) ||
1400 (has_capability('moodle/calendar:managegroupentries', get_context_instance(CONTEXT_COURSE, $group->courseid))
1401 && groups_is_member($event->groupid)));
438e4add 1402 } else if (!empty($event->courseid)) {
c0a2c361 1403 // if groupid is not set, but course is set,
1404 // it's definiely a course event
e295df44 1405 return has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_COURSE, $event->courseid));
438e4add 1406 } else if (!empty($event->userid) && $event->userid == $USER->id) {
c0a2c361 1407 // if course is not set, but userid id set, it's a user event
1408 return (has_capability('moodle/calendar:manageownentries', $sitecontext));
e295df44 1409 }
7423f116 1410 return false;
1411}
1412
8c54cec6 1413function calendar_get_default_courses($ignoreref = false) {
62d11d77 1414 global $USER, $CFG, $SESSION, $DB;
9ff136e5 1415
8c54cec6 1416 if(!empty($SESSION->cal_course_referer) && !$ignoreref) {
4c7d1137 1417 return array($SESSION->cal_course_referer => 1);
9ff136e5 1418 }
7423f116 1419
4f0c2d00 1420 if (!isloggedin()) {
2ef75eee 1421 return array();
1422 }
1423
7423f116 1424 $courses = array();
d9d16e56 1425 if (has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_SYSTEM))) {
03bb25e1 1426 if (!empty($CFG->calendar_adminseesall)) {
62d11d77 1427 $courses = $DB->get_records_sql('SELECT id, 1 FROM {course}');
86f092d2 1428 return $courses;
1429 }
95a89225 1430 }
e295df44 1431
df997f84 1432 $courses = enrol_get_my_courses();
37d87d11 1433
1434 return $courses;
7423f116 1435}
1436
1e1ff33b 1437function calendar_preferences_button() {
1438 global $CFG, $USER;
7423f116 1439
1440 // Guests have no preferences
4f0c2d00 1441 if (!isloggedin() || isguestuser()) {
7423f116 1442 return '';
1443 }
1444
4aea3cc7 1445 return "<form method=\"get\" ".
1e1ff33b 1446 " action=\"$CFG->wwwroot/calendar/preferences.php\">".
2aab6488 1447 "<div><input type=\"submit\" value=\"".get_string("preferences", "calendar")." ...\" /></div></form>";
7423f116 1448}
1449
b4892fa2 1450function calendar_format_event_time($event, $now, $morehref, $usecommonwords = true, $showtime=0) {
8f896582 1451 $startdate = usergetdate($event->timestart);
1452 $enddate = usergetdate($event->timestart + $event->timeduration);
1453 $usermidnightstart = usergetmidnight($event->timestart);
1454
1455 if($event->timeduration) {
1456 // To avoid doing the math if one IF is enough :)
1457 $usermidnightend = usergetmidnight($event->timestart + $event->timeduration);
1458 }
1459 else {
1460 $usermidnightend = $usermidnightstart;
1461 }
1462
1463 // OK, now to get a meaningful display...
1464 // First of all we have to construct a human-readable date/time representation
1465
b4892fa2 1466 if($event->timeduration) {
8f896582 1467 // It has a duration
b4892fa2 1468 if($usermidnightstart == $usermidnightend ||
1469 ($event->timestart == $usermidnightstart) && ($event->timeduration == 86400 || $event->timeduration == 86399) ||
1470 ($event->timestart + $event->timeduration <= $usermidnightstart + 86400)) {
8f896582 1471 // But it's all on the same day
8f896582 1472 $timestart = calendar_time_representation($event->timestart);
1473 $timeend = calendar_time_representation($event->timestart + $event->timeduration);
b4892fa2 1474 $time = $timestart.' <strong>&raquo;</strong> '.$timeend;
1475
1476 if ($event->timestart == $usermidnightstart && ($event->timeduration == 86400 || $event->timeduration == 86399)) {
1477 $time = get_string('allday', 'calendar');
1478 }
8f896582 1479
1480 // Set printable representation
b4892fa2 1481 if (!$showtime) {
1482 $day = calendar_day_representation($event->timestart, $now, $usecommonwords);
1483 $eventtime = calendar_get_link_tag($day, CALENDAR_URL.'view.php?view=day'.$morehref.'&amp;', $enddate['mday'], $enddate['mon'], $enddate['year']).', '.$time;
1484 } else {
1485 $eventtime = $time;
1486 }
1487 } else {
8f896582 1488 // It spans two or more days
b4892fa2 1489 $daystart = calendar_day_representation($event->timestart, $now, $usecommonwords).', ';
1490 if ($showtime == $usermidnightstart) {
1491 $daystart = '';
1492 }
8f896582 1493 $timestart = calendar_time_representation($event->timestart);
b4892fa2 1494 $dayend = calendar_day_representation($event->timestart + $event->timeduration, $now, $usecommonwords).', ';
1495 if ($showtime == $usermidnightend) {
1496 $dayend = '';
1497 }
8f896582 1498 $timeend = calendar_time_representation($event->timestart + $event->timeduration);
1499
1500 // Set printable representation
b4892fa2 1501 if ($now >= $usermidnightstart && $now < ($usermidnightstart + 86400)) {
1502 $eventtime = $timestart.' <strong>&raquo;</strong> '.calendar_get_link_tag($dayend, CALENDAR_URL.'view.php?view=day'.$morehref.'&amp;', $enddate['mday'], $enddate['mon'], $enddate['year']).
1503 $timeend;
1504 } else {
1505 $eventtime = calendar_get_link_tag($daystart, CALENDAR_URL.'view.php?view=day'.$morehref.'&amp;', $startdate['mday'], $startdate['mon'], $startdate['year']).
1506 $timestart.' <strong>&raquo;</strong> '.calendar_get_link_tag($dayend, CALENDAR_URL.'view.php?view=day'.$morehref.'&amp;', $enddate['mday'], $enddate['mon'], $enddate['year']).
1507 $timeend;
1508 }
8f896582 1509 }
b4892fa2 1510 } else {
1c4bedd7 1511 $time = ' ';
8f896582 1512
1513 // Set printable representation
b4892fa2 1514 if (!$showtime) {
1515 $day = calendar_day_representation($event->timestart, $now, $usecommonwords);
1516 $eventtime = calendar_get_link_tag($day, CALENDAR_URL.'view.php?view=day'.$morehref.'&amp;', $startdate['mday'], $startdate['mon'], $startdate['year']).trim($time);
1517 } else {
1518 $eventtime = $time;
1519 }
1520 }
1521
1522 if($event->timestart + $event->timeduration < $now) {
1523 // It has expired
1524 $eventtime = '<span class="dimmed_text">'.str_replace(' href=', ' class="dimmed" href=', $eventtime).'</span>';
8f896582 1525 }
09d36284 1526
8f896582 1527 return $eventtime;
1528}
054193be 1529
86f092d2 1530function calendar_print_month_selector($name, $selected) {
86f092d2 1531 $months = array();
1532
1533 for ($i=1; $i<=12; $i++) {
76ab1c33 1534 $months[$i] = userdate(gmmktime(12, 0, 0, $i, 15, 2000), '%B');
86f092d2 1535 }
1536
d776d59e 1537 echo html_writer::select($months, $name, $selected, false);
86f092d2 1538}
1539
054193be 1540function calendar_get_filters_status() {
1541 global $SESSION;
1542
1543 $status = 0;
1544 if($SESSION->cal_show_global) {
1545 $status += 1;
1546 }
1547 if($SESSION->cal_show_course) {
1548 $status += 2;
1549 }
1550 if($SESSION->cal_show_groups) {
1551 $status += 4;
1552 }
1553 if($SESSION->cal_show_user) {
1554 $status += 8;
1555 }
1556 return $status;
1557}
1558
1559function calendar_set_filters_status($packed_bitfield) {
1560 global $SESSION, $USER;
1561
4f0c2d00 1562 if (!isloggedin()) {
054193be 1563 return false;
1564 }
1565
1566 $SESSION->cal_show_global = ($packed_bitfield & 1);
1567 $SESSION->cal_show_course = ($packed_bitfield & 2);
e295df44 1568 $SESSION->cal_show_groups = ($packed_bitfield & 4);
054193be 1569 $SESSION->cal_show_user = ($packed_bitfield & 8);
1570
1571 return true;
1572}
1573
86ac8b24 1574function calendar_get_allowed_types(&$allowed) {
62d11d77 1575 global $USER, $CFG, $SESSION, $DB;
86ac8b24 1576 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
1577 $allowed->user = has_capability('moodle/calendar:manageownentries', $sitecontext);
1578 $allowed->groups = false; // This may change just below
1579 $allowed->courses = false; // This may change just below
1580 $allowed->site = has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_COURSE, SITEID));
1581
f63d2922 1582 if(!empty($SESSION->cal_course_referer) && $SESSION->cal_course_referer != SITEID) {
62d11d77 1583 $course = $DB->get_record('course', array('id'=>$SESSION->cal_course_referer));
f63d2922 1584 $coursecontext = get_context_instance(CONTEXT_COURSE, $SESSION->cal_course_referer);
86ac8b24 1585
f63d2922 1586 if(has_capability('moodle/calendar:manageentries', $coursecontext)) {
1587 $allowed->courses = array($course->id => 1);
aa6c1ced 1588
f63d2922 1589 if($course->groupmode != NOGROUPS || !$course->groupmodeforce) {
1590 $allowed->groups = groups_get_all_groups($SESSION->cal_course_referer);
1591 }
1592 } else if(has_capability('moodle/calendar:managegroupentries', $coursecontext)) {
1593 if($course->groupmode != NOGROUPS || !$course->groupmodeforce) {
1594 $allowed->groups = groups_get_all_groups($SESSION->cal_course_referer, $USER->id);
1595 }
86ac8b24 1596 }
1597 }
1598}
1599
1600/**
1601 * see if user can add calendar entries at all
1602 * used to print the "New Event" button
1603 * @return bool
1604 */
1605function calendar_user_can_add_event() {
1606 calendar_get_allowed_types($allowed);
e295df44 1607 return (bool)($allowed->user || $allowed->groups || $allowed->courses || $allowed->site);
86ac8b24 1608}
76d9df3f
SH
1609
1610/**
1611 * Check wether the current user is permitted to add events
1612 *
1613 * @param object $event
1614 * @return bool
1615 */
1616function calendar_add_event_allowed($event) {
1617 global $USER, $DB;
1618
1619 // can not be using guest account
4f0c2d00 1620 if (!isloggedin() or isguestuser()) {
76d9df3f
SH
1621 return false;
1622 }
1623
1624 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
1625 // if user has manageentries at site level, always return true
1626 if (has_capability('moodle/calendar:manageentries', $sitecontext)) {
1627 return true;
1628 }
1629
1630 switch ($event->eventtype) {
1631 case 'course':
1632 return has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_COURSE, $event->courseid));
1633
1634 case 'group':
1635 // Allow users to add/edit group events if:
1636 // 1) They have manageentries (= entries for whole course)
1637 // 2) They have managegroupentries AND are in the group
1638 $group = $DB->get_record('groups', array('id'=>$event->groupid));
1639 return $group && (
1640 has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_COURSE, $group->courseid)) ||
1641 (has_capability('moodle/calendar:managegroupentries', get_context_instance(CONTEXT_COURSE, $group->courseid))
1642 && groups_is_member($event->groupid)));
1643
1644 case 'user':
1645 if ($event->userid == $USER->id) {
1646 return (has_capability('moodle/calendar:manageownentries', $sitecontext));
1647 }
1648 //there is no 'break;' intentionally
1649
1650 case 'site':
1651 return has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_COURSE, SITEID));
1652
1653 default:
ddaff608
SH
1654 if (isset($event->courseid) && $event->courseid > 0) {
1655 return has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_COURSE, $event->courseid));
1656 }
76d9df3f
SH
1657 return false;
1658 }
1659}
1660
1661/**
1662 * A class to manage calendar events
1663 *
1664 * This class provides the required functionality in order to manage calendar events.
1665 * It was introduced as part of Moodle 2.0 and was created in order to provide a
1666 * better framework for dealing with calendar events in particular regard to file
1667 * handling through the new file API
1668 *
1669 * @property int $id The id within the event table
1670 * @property string $name The name of the event
1671 * @property string $description The description of the event
1672 * @property int $format The format of the description FORMAT_?
1673 * @property int $courseid The course the event is associated with (0 if none)
1674 * @property int $groupid The group the event is associated with (0 if none)
1675 * @property int $userid The user the event is associated with (0 if none)
1676 * @property int $repeatid If this is a repeated event this will be set to the
1677 * id of the original
1678 * @property string $modulename If added by a module this will be the module name
1679 * @property int $instance If added by a module this will be the module instance
1680 * @property string $eventtype The event type
1681 * @property int $timestart The start time as a timestamp
1682 * @property int $timeduration The duration of the event in seconds
1683 * @property int $visible 1 if the event is visible
1684 * @property int $uuid ?
1685 * @property int $sequence ?
1686 * @property int $timemodified The time last modified as a timestamp
1687 */
1688class calendar_event {
1689
1690 /**
1691 * An object containing the event properties can be accessed via the
1692 * magic __get/set methods
1693 * @var array
1694 */
1695 protected $properties = null;
1696 /**
1697 * The converted event discription with file paths resolved
1698 * This gets populated when someone requests description for the first time
1699 * @var string
1700 */
1701 protected $_description = null;
1702 /**
1703 * The filearea to use with this event
1704 * @var string
1705 */
1706 protected static $filearea = 'calendar_event_description';
1707 /**
1708 * The options to use with this description editor
1709 * @var array
1710 */
1711 protected $editoroptions = array(
1712 'subdirs'=>false,
1713 'forcehttps'=>false,
1714 'maxfiles'=>EDITOR_UNLIMITED_FILES,
1715 'maxbytes'=>null,
1716 'trusttext'=>false);
1717 /**
1718 * The context to use with the description editor
1719 * @var object
1720 */
1721 protected $editorcontext = null;
1722
1723 /**
1724 * Instantiates a new event and optionally populates its properties with the
1725 * data provided
1726 *
1727 * @param stdClass $data Optional. An object containing the properties to for
1728 * an event
1729 */
1730 public function __construct($data=null) {
1731 global $CFG;
1732
1733 // First convert to object if it is not already (should either be object or assoc array)
1734 if (!is_object($data)) {
1735 $data = (object)$data;
1736 }
1737
1738 $this->editoroptions['maxbytes'] = $CFG->maxbytes;
1739
1740 $data->eventrepeats = 0;
1741
1742 if (empty($data->id)) {
1743 $data->id = null;
1744 }
1745
1746 if (!empty($data->timeduration) && is_array($data->timeduration)) {
1747 $data->timeduration = make_timestamp($data->timeduration['year'], $data->timeduration['month'], $data->timeduration['day'], $data->timeduration['hour'], $data->timeduration['minute']) - $data->timestart;
1748 }
1749 if (!empty($data->description) && is_array($data->description)) {
1750 $data->format = $data->description['format'];
1751 $data->description = $data->description['text'];
1752 } else if (empty($data->description)) {
1753 $data->description = '';
1754 }
1755
1756 if (empty($data->format)) {
1757 if (can_use_html_editor()) {
1758 $data->format = FORMAT_HTML;
1759 } else {
1760 $data->format = FORMAT_MOODLE;
1761 }
1762 }
1763
1764 $this->properties = $data;
1765 }
1766
1767 /**
1768 * Magic property method
1769 *
1770 * Attempts to call a set_$key method if one exists otherwise falls back
1771 * to simply set the property
1772 *
1773 * @param string $key
1774 * @param mixed $value
1775 */
1776 public function __set($key, $value) {
1777 if (method_exists($this, 'set_'.$key)) {
1778 $this->{'set_'.$key}($value);
1779 }
1780 $this->properties->{$key} = $value;
1781 }
1782
1783 /**
1784 * Magic get method
1785 *
1786 * Attempts to call a get_$key method to return the property and ralls over
1787 * to return the raw property
1788 *
1789 * @param str $key
1790 * @return mixed
1791 */
1792 public function __get($key) {
1793 if (method_exists($this, 'get_'.$key)) {
1794 return $this->{'get_'.$key}();
1795 }
1796 return $this->properties->{$key};
1797 }
1798
1799 /**
1800 * Stupid PHP needs an isset magic method if you use the get magic method and
1801 * still want empty calls to work.... blah ~!
1802 *
1803 * @param string $key
1804 * @return bool
1805 */
1806 public function __isset($key) {
1807 return !empty($this->properties->{$key});
1808 }
1809
1810 /**
1811 * Returns an array of editoroptions for this event: Called by __get
1812 * Please use $blah = $event->editoroptions;
1813 * @return array
1814 */
1815 protected function get_editoroptions() {
1816 return $this->editoroptions;
1817 }
1818
1819 /**
1820 * Returns an event description: Called by __get
1821 * Please use $blah = $event->description;
1822 *
1823 * @return string
1824 */
1825 protected function get_description() {
1826 global $USER;
1827 if ($this->_description === null) {
1828 // Check if we have already resolved the context for this event
1829 if ($this->editorcontext === null) {
1830 // Switch on the event type to decide upon the appropriate context
1831 // to use for this event
1832 switch ($this->properties->eventtype) {
1833 case 'course':
1834 case 'group':
1835 // Course and group event files are served from the course context
1836 // and there are checks in plugin.php to ensure permissions are
1837 // followed
1838 $this->editorcontext = get_context_instance(CONTEXT_COURSE, $this->properties->courseid);
1839 break;
1840 case 'user':
1841 // User context
1842 $this->editorcontext = get_context_instance(CONTEXT_USER, $USER->id);
1843 break;
1844 case 'site':
1845 // Site context
1846 $this->editorcontext = get_context_instance(CONTEXT_SYSTEM);
1847 break;
1848 default:
1849 // Hmmmm some modules use custom eventtype strings, if that is the
1850 // case we are going to abandon using files. Anything that uses a
1851 // custom type is being added manually through code
1852 return clean_text($this->properties->description, $this->properties->format);
1853 break;
1854 }
1855 }
1856
1857 // Work out the item id for the editor, if this is a repeated event then the files will
1858 // be associated with the original
1859 if (!empty($this->properties->repeatid) && $this->properties->repeatid > 0) {
1860 $itemid = $this->properties->repeatid;
1861 } else {
1862 $itemid = $this->properties->id;
1863 }
1864
1865 // Convert file paths in the description so that things display correctly
1866 $this->_description = file_rewrite_pluginfile_urls($this->properties->description, 'pluginfile.php', $this->editorcontext->id, self::$filearea, $itemid);
1867 // Clean the text so no nasties get through
1868 $this->_description = clean_text($this->_description, $this->properties->format);
1869 }
1870 // Finally return the description
1871 return $this->_description;
1872 }
1873
1874 /**
1875 * Return the number of repeat events there are in this events series
aa6c1ced 1876 *
76d9df3f
SH
1877 * @return int
1878 */
1879 public function count_repeats() {
1880 global $DB;
1881 if (!empty($this->properties->repeatid)) {
1882 $this->properties->eventrepeats = $DB->count_records('event', array('repeatid'=>$this->properties->repeatid));
1883 // We don't want to count ourselves
1884 $this->properties->eventrepeats--;
1885 }
1886 return $this->properties->eventrepeats;
1887 }
1888
1889 /**
1890 * Update or create an event within the database
1891 *
1892 * Pass in a object containing the event properties and this function will
1893 * insert it into the database and deal with any associated files
1894 *
1895 * @see add_event()
1896 * @see update_event()
1897 *
1898 * @param stdClass $data
1899 */
1900 public function update($data) {
1901 global $CFG, $DB, $USER;
1902
438e4add
SH
1903 foreach ($data as $key=>$value) {
1904 $this->properties->$key = $value;
1905 }
1906
76d9df3f
SH
1907 $this->properties->timemodified = time();
1908 $usingeditor = (!empty($this->properties->description) && is_array($this->properties->description));
1909
1910 if (empty($this->properties->id) || $this->properties->id < 1) {
1911
1912 if (!calendar_add_event_allowed($this->properties)) {
1913 print_error('nopermissions');
1914 }
1915
1916 if ($usingeditor) {
1917 switch ($this->properties->eventtype) {
1918 case 'user':
1919 $this->editorcontext = get_context_instance(CONTEXT_USER, $USER->id);
1920 $this->properties->courseid = 0;
1921 $this->properties->groupid = 0;
1922 $this->properties->userid = $USER->id;
1923 break;
1924 case 'site':
1925 $this->editorcontext = get_context_instance(CONTEXT_SYSTEM);
1926 $this->properties->courseid = SITEID;
1927 $this->properties->groupid = 0;
1928 $this->properties->userid = $USER->id;
1929 break;
1930 case 'course':
1931 $this->editorcontext = get_context_instance(CONTEXT_COURSE, $this->properties->courseid);
1932 $this->properties->groupid = 0;
1933 $this->properties->userid = $USER->id;
1934 break;
1935 case 'group':
1936 $this->editorcontext = get_context_instance(CONTEXT_COURSE, $this->properties->courseid);
1937 $this->properties->groupid = 0;
1938 $this->properties->userid = $USER->id;
1939 break;
1940 default:
1941 // Ewww we should NEVER get here, but just incase we do lets
1942 // fail gracefully
1943 $usingeditor = false;
1944 break;
1945 }
1946
1947 $editor = $this->properties->description;
1948 $this->properties->format = $this->properties->description['format'];
1949 $this->properties->description = $this->properties->description['text'];
1950 }
1951
1952 // Insert the event into the database
1953 $this->properties->id = $DB->insert_record('event', $this->properties);
1954
1955 if ($usingeditor) {
1956 $this->properties->description = file_save_draft_area_files(
1957 $editor['itemid'],
1958 $this->editorcontext->id,
1959 self::$filearea,
1960 $this->properties->id,
1961 $this->editoroptions,
1962 $editor['text'],
1963 $this->editoroptions['forcehttps']);
1964
1965 $DB->set_field('event', 'description', $this->properties->description, array('id'=>$this->properties->id));
1966 }
aa6c1ced 1967
76d9df3f
SH
1968 // Log the event entry.
1969 add_to_log($this->properties->courseid, 'calendar', 'add', 'event.php?action=edit&amp;id='.$this->properties->id, $this->properties->name);
1970
1971 $repeatedids = array();
1972
1973 if (!empty($this->properties->repeat)) {
1974 $this->properties->repeatid = $this->properties->id;
1975 $DB->set_field('event', 'repeatid', $this->properties->repeatid, array('id'=>$this->properties->id));
1976
1977 $eventcopy = clone($this->properties);
1978 unset($eventcopy->id);
1979
1980 for($i = 1; $i < $eventcopy->repeats; $i++) {
1981
1982 $eventcopy->timestart = ($eventcopy->timestart+WEEKSECS) + dst_offset_on($eventcopy->timestart) - dst_offset_on($eventcopy->timestart+WEEKSECS);
1983
1984 // Get the event id for the log record.
1985 $eventcopyid = $DB->insert_record('event', $eventcopy);
1986
1987 // If the context has been set delete all associated files
1988 if ($usingeditor) {
1989 $fs = get_file_storage();
1990 $files = $fs->get_area_files($this->editorcontext->id, self::$filearea, $this->properties->id);
1991 foreach ($files as $file) {
1992 $fs->create_file_from_storedfile(array('itemid'=>$eventcopyid), $file);
1993 }
1994 }
1995
1996 $repeatedids[] = $eventcopyid;
1997 // Log the event entry.
1998 add_to_log($eventcopy->courseid, 'calendar', 'add', 'event.php?action=edit&amp;id='.$eventcopyid, $eventcopy->name);
1999 }
2000 }
2001
2002 // Hook for tracking added events
2003 self::calendar_event_hook('add_event', array($this->properties, $repeatedids));
2004 return true;
2005 } else {
2006
2007 if(!calendar_edit_event_allowed($this->properties)) {
2008 print_error('nopermissions');
2009 }
2010
2011 if ($usingeditor) {
2012 if ($this->editorcontext !== null) {
2013 $this->properties->description = file_save_draft_area_files(
2014 $this->properties->description['itemid'],
2015 $this->editorcontext->id,
2016 self::$filearea,
2017 $this->properties->id,
2018 $this->editoroptions,
2019 $this->properties->description['text'],
2020 $this->editoroptions['forcehttps']);
2021 } else {
2022 $this->properties->format = $this->properties->description['format'];
2023 $this->properties->description = $this->properties->description['text'];
2024 }
2025 }
2026
2027 $event = $DB->get_record('event', array('id'=>$this->properties->id));
2028
2029 $updaterepeated = (!empty($this->properties->repeatid) && !empty($this->properties->repeateditall));
2030
2031 if ($updaterepeated) {
2032 // Update all
2033 if ($this->properties->timestart != $event->timestart) {
2034 $timestartoffset = $this->properties->timestart - $event->timestart;
2035 $sql = "UPDATE {event}
2036 SET name = ?,
2037 description = ?,
2038 timestart = timestart + ?,
2039 timeduration = ?,
2040 timemodified = ?
2041 WHERE repeatid = ?";
2042 $params = array($this->properties->name, $this->properties->description, $timestartoffset, $this->properties->timeduration, time(), $event->repeatid);
2043 } else {
2044 $sql = "UPDATE {event} SET name = ?, description = ?, timeduration = ?, timemodified = ? WHERE repeatid = ?";
2045 $params = array($this->properties->name, $this->properties->description, $this->properties->timeduration, time(), $event->repeatid);
2046 }
2047 $DB->execute($sql, $params);
2048
2049 // Log the event update.
2050 add_to_log($this->properties->courseid, 'calendar', 'edit all', 'event.php?action=edit&amp;id='.$this->properties->id, $this->properties->name);
2051 } else {
2052 $DB->update_record('event', $this->properties);
ddaff608
SH
2053 $event = calendar_event::load($this->properties->id);
2054 $this->properties = $event->properties();
76d9df3f
SH
2055 add_to_log($this->properties->courseid, 'calendar', 'edit', 'event.php?action=edit&amp;id='.$this->properties->id, $this->properties->name);
2056 }
2057
2058 // Hook for tracking event updates
2059 self::calendar_event_hook('update_event', array($this->properties, $updaterepeated));
2060 return true;
2061 }
2062 }
2063
2064 /**
2065 * Deletes an event and if selected an repeated events in the same series
2066 *
2067 * This function deletes an event, any associated events if $deleterepeated=true,
2068 * and cleans up any files associated with the events.
2069 *
2070 * @see delete_event()
2071 *
2072 * @param bool $deleterepeated
2073 * @return bool
2074 */
2075 public function delete($deleterepeated=false) {
2076 global $DB, $USER, $CFG;
2077
2078 // If $this->properties->id is not set then something is wrong
2079 if (empty($this->properties->id)) {
2080 debugging('Attempting to delete an event before it has been loaded', DEBUG_DEVELOPER);
2081 return false;
2082 }
2083
2084 // Delete the event
2085 $DB->delete_records('event', array('id'=>$this->properties->id));
2086
2087 // If the editor context hasn't already been set then set it now
2088 if ($this->editorcontext === null) {
2089 switch ($this->properties->eventtype) {
2090 case 'course':
2091 case 'group':
2092 $this->editorcontext = get_context_instance(CONTEXT_COURSE, $this->properties->courseid);
2093 break;
2094 case 'user':
2095 $this->editorcontext = get_context_instance(CONTEXT_USER, $USER->id);
2096 break;
2097 case 'site':
2098 $this->editorcontext = get_context_instance(CONTEXT_SYSTEM);
2099 break;
2100 default:
2101 // There is a chance we can get here as some modules use there own
2102 // eventtype strings. In this case the event has been added by code
2103 // and we don't need to worry about it anyway
2104 break;
2105 }
2106 }
2107
2108 // If the context has been set delete all associated files
2109 if ($this->editorcontext !== null) {
2110 $fs = get_file_storage();
2111 $files = $fs->get_area_files($this->editorcontext->id, self::$filearea, $this->properties->id);
2112 foreach ($files as $file) {
2113 $file->delete();
2114 }
2115 }
2116
2117 // Fire the event deleted hook
2118 self::calendar_event_hook('delete_event', array($this->properties->id, $deleterepeated));
2119
2120 // If we need to delete repeated events then we will fetch them all and delete one by one
2121 if ($deleterepeated && !empty($this->properties->repeatid) && $this->properties->repeatid > 0) {
2122 // Get all records where the repeatid is the same as the event being removed
2123 $events = $DB->get_records('event', array('repeatid'=>$this->properties->repeatid));
2124 // For each of the returned events populate a calendar_event object and call delete
2125 // make sure the arg passed is false as we are already deleting all repeats
2126 foreach ($events as $event) {
2127 $event = new calendar_event($event);
2128 $event->delete(false);
2129 }
2130 }
2131
2132 return true;
2133 }
2134
2135 /**
2136 * Fetch all event properties
2137 *
2138 * This function returns all of the events properties as an object and optionally
2139 * can prepare an editor for the description field at the same time. This is
2140 * designed to work when the properties are going to be used to set the default
2141 * values of a moodle forms form.
2142 *
2143 * @param bool $prepareeditor If set to true a editor is prepared for use with
2144 * the mforms editor element. (for description)
2145 * @return stdClass Object containing event properties
2146 */
2147 public function properties($prepareeditor=false) {
2148 global $USER, $CFG, $DB;
2149
2150 // First take a copy of the properties. We don't want to actually change the
2151 // properties or we'd forever be converting back and forwards between an
2152 // editor formatted description and not
2153 $properties = clone($this->properties);
2154 // Clean the description here
2155 $properties->description = clean_text($properties->description, $properties->format);
2156
2157 // If set to true we need to prepare the properties for use with an editor
2158 // and prepare the file area
2159 if ($prepareeditor) {
2160
2161 // We may or may not have a property id. If we do then we need to work
2162 // out the context so we can copy the existing files to the draft area
2163 if (!empty($properties->id)) {
2164
2165 if ($properties->eventtype === 'site') {
2166 // Site context
2167 $this->editorcontext = get_context_instance(CONTEXT_SYSTEM);
2168 } else if ($properties->eventtype === 'user') {
2169 // User context
2170 $this->editorcontext = get_context_instance(CONTEXT_USER, $USER->id);
2171 } else if ($properties->eventtype === 'group' || $properties->eventtype === 'course') {
2172 // First check the course is valid
2173 $course = $DB->get_record('course', array('id'=>$properties->courseid));
2174 if (!$course) {
2175 print_error('invalidcourse');
2176 }
2177 // Course context
2178 $this->editorcontext = get_context_instance(CONTEXT_COURSE, $course->id);
2179 // We have a course and are within the course context so we had
2180 // better use the courses max bytes value
2181 $this->editoroptions['maxbytes'] = $course->maxbytes;
2182 } else {
2183 // If we get here we have a custom event type as used by some
2184 // modules. In this case the event will have been added by
2185 // code and we won't need the editor
2186 $this->editoroptions['maxbytes'] = 0;
2187 $this->editoroptions['maxfiles'] = 0;
2188 }
2189
2190 if (empty($this->editorcontext) || empty($this->editorcontext->id)) {
2191 $contextid = false;
2192 } else {
2193 // Get the context id that is what we really want
2194 $contextid = $this->editorcontext->id;
2195 }
2196 } else {
2197
2198 // If we get here then this is a new event in which case we don't need a
2199 // context as there is no existing files to copy to the draft area.
2200 $contextid = null;
2201 }
2202
2203 // If the contextid === false we don't support files so no preparing
2204 // a draft area
2205 if ($contextid !== false) {
2206 // Just encase it has already been submitted
2207 $draftiddescription = file_get_submitted_draft_itemid('description');
2208 // Prepare the draft area, this copies existing files to the draft area as well
2209 $properties->description = file_prepare_draft_area($draftiddescription, $contextid, self::$filearea, $properties->id, $this->editoroptions, $properties->description);
2210 } else {
2211 $draftiddescription = 0;
2212 }
aa6c1ced 2213
76d9df3f
SH
2214 // Structure the description field as the editor requires
2215 $properties->description = array('text'=>$properties->description, 'format'=>$properties->format, 'itemid'=>$draftiddescription);
2216 }
2217
2218 // Finally return the properties
2219 return $properties;
2220 }
2221
2222 /**
2223 * Toggles the visibility of an event
2224 *
2225 * @param null|bool $force If it is left null the events visibility is flipped,
2226 * If it is false the event is made hidden, if it is true it
2227 * is made visible.
2228 */
2229 public function toggle_visibility($force=null) {
2230 global $CFG, $DB;
2231
2232 // Set visible to the default if it is not already set
2233 if (empty($this->properties->visible)) {
2234 $this->properties->visible = 1;
2235 }
2236
2237 if ($force === true || ($force !== false && $this->properties->visible == 0)) {
2238 // Make this event visible
2239 $this->properties->visible = 1;
2240 // Fire the hook
2241 self::calendar_event_hook('show_event', array($this->properties));
2242 } else {
2243 // Make this event hidden
2244 $this->properties->visible = 0;
2245 // Fire the hook
2246 self::calendar_event_hook('hide_event', array($this->properties));
2247 }
2248
2249 // Update the database to reflect this change
2250 return $DB->set_field('event', 'visible', $this->properties->visible, array('id'=>$this->properties->id));
2251 }
2252
2253 /**
2254 * Attempts to call the hook for the specified action should a calendar type
2255 * by set $CFG->calendar, and the appopriate function defined
2256 *
2257 * @static
2258 * @staticvar bool $extcalendarinc Used to track the inclusion of the calendar lib
2259 * @param string $action One of `update_event`, `add_event`, `delete_event`, `show_event`, `hide_event`
2260 * @param array $args The args to pass to the hook, usually the event is the first element
2261 * @return bool
2262 */
2263 public static function calendar_event_hook($action, array $args) {
2264 global $CFG;
2265 static $extcalendarinc;
2266 if ($extcalendarinc === null) {
2267 if (!empty($CFG->calendar) && file_exists($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php')) {
2268 include_once($CFG->dirroot .'/calendar/'. $CFG->calendar .'/lib.php');
2269 $extcalendarinc = true;
2270 } else {
2271 $extcalendarinc = false;
2272 }
2273 }
2274 if($extcalendarinc === false) {
2275 return false;
2276 }
438e4add 2277 $hook = $CFG->calendar .'_'.$action;
76d9df3f
SH
2278 if (function_exists($hook)) {
2279 call_user_func_array($hook, $args);
2280 return true;
2281 }
2282 return false;
2283 }
2284
2285 /**
2286 * Returns a calendar_event object when provided with an event id
2287 *
2288 * This function makes use of MUST_EXIST, if the event id passed in is invalid
2289 * it will result in an exception being thrown
2290 *
fd699564 2291 * @param int|object $param
76d9df3f
SH
2292 * @return calendar_event|false
2293 */
fd699564 2294 public static function load($param) {
76d9df3f 2295 global $DB;
fd699564
SH
2296 if (is_object($param)) {
2297 $event = new calendar_event($param);
2298 } else {
2299 $event = $DB->get_record('event', array('id'=>(int)$param), '*', MUST_EXIST);
2300 $event = new calendar_event($event);
2301 }
76d9df3f
SH
2302 return $event;
2303 }
2304
2305 /**
2306 * Creates a new event and returns a calendar_event object
2307 *
2308 * @param object|array $properties An object containing event properties
2309 * @return calendar_event|false The event object or false if it failed
2310 */
2311 public static function create($properties) {
2312 if (is_array($properties)) {
2313 $properties = (object)$properties;
2314 }
2315 if (!is_object($properties)) {
2316 throw new coding_exception('When creating an event properties should be either an object or an assoc array');
2317 }
2318 $event = new calendar_event();
2319 if ($event->update($properties)) {
2320 return $event;
2321 } else {
2322 return false;
2323 }
2324 }
2325}