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