From e30390a0ddd6af5fe7e7058cf87e18ff8fc79513 Mon Sep 17 00:00:00 2001 From: Sam Hemelryk Date: Mon, 15 Oct 2012 16:31:12 +0800 Subject: [PATCH] MDL-16660 calendar: Cleaned u several areas of the iCal patch in preparation of the next peer-review * Added a bit of AMOS to copy existing strings and save a little translation effort * Cleaned up fixed strings in several places * Cleaned up some existing strings as required. * Fixed install and upgrade code. Split upgrade into two parts (one for each table). * Fixed fatal error caused by missing forms lib inclusion * Added param types to forms. * Converted file_get_content to use curl for URL's. * Cleaned things up per coding style. * Separated subscription management and form into separate files. * Tidied up bennu inclusion to just where required. * Lots of other small fixes as well. AMOS BEGIN CPY [calendar,calendar],[colcalendar,calendar] CPY [actions,moodle],[actions,calendar] CPY [never,moodle],[never,calendar] AMOS END --- calendar/lib.php | 423 +++++++------------------- calendar/managesubscriptions.php | 107 +++++++ calendar/managesubscriptions_form.php | 133 ++++++++ calendar/renderer.php | 99 ++++++ calendar/view.php | 14 +- lang/en/calendar.php | 14 +- lib/db/install.xml | 8 +- lib/db/upgrade.php | 12 +- 8 files changed, 477 insertions(+), 333 deletions(-) create mode 100644 calendar/managesubscriptions.php create mode 100644 calendar/managesubscriptions_form.php diff --git a/calendar/lib.php b/calendar/lib.php index 7804e9988f6b3..b726bde6a8e74 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -23,7 +23,9 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -require_once($CFG->libdir.'/bennu/bennu.inc.php'); +if (!defined('MOODLE_INTERNAL')) { + die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page +} /** * These are read by the administration component to provide default values @@ -1059,7 +1061,7 @@ function calendar_get_link_href($linkbase, $d, $m, $y) { return ''; } if (!($linkbase instanceof moodle_url)) { - $linkbase = new moodle_url(); + $linkbase = new moodle_url($linkbase); } if (!empty($d)) { $linkbase->param('cal_d', $d); @@ -2000,7 +2002,7 @@ protected function calculate_context(stdClass $data) { $cm = get_coursemodule_from_instance($data->modulename, $data->instance, 0, false, MUST_EXIST); $context = context_course::instance($cm->course); } else { - $context = context_user::instance(); + $context = context_user::instance($data->userid); } return $context; @@ -2635,7 +2637,7 @@ public function timestamp_tomorrow() { return make_timestamp($this->year, $this->month, $this->day+1); } /** - * Adds the pretend blocks for teh calendar + * Adds the pretend blocks for the calendar * * @param core_calendar_renderer $renderer * @param bool $showfilters display filters, false is set as default @@ -2658,25 +2660,26 @@ public function add_sidecalendar_blocks(core_calendar_renderer $renderer, $showf } /** - * Returns option list for the pollinterval setting. - * @return array option list + * Returns option list for the poll interval setting. + * + * @return array An array of poll interval options. Interval => description. */ function calendar_get_pollinterval_choices() { return array( - '0' => get_string('never', 'calendar'), - '3600' => get_string('hourly', 'calendar'), - '86400' => get_string('daily', 'calendar'), - '604800' => get_string('weekly', 'calendar'), - '2628000' => get_string('monthly', 'calendar'), - '31536000' => get_string('annually', 'calendar'), - ); + '0' => new lang_string('never', 'calendar'), + '3600' => new lang_string('hourly', 'calendar'), + '86400' => new lang_string('daily', 'calendar'), + '604800' => new lang_string('weekly', 'calendar'), + '2628000' => new lang_string('monthly', 'calendar'), + '31536000' => new lang_string('annually', 'calendar') + ); } /** - * Returns option list of available options for the calendar event type, given - * the current user and course. + * Returns option list of available options for the calendar event type, given the current user and course. + * * @param int $courseid The id of the course - * @return array option list + * @return array An array containing the event types the user can create. */ function calendar_get_eventtype_choices($courseid) { $choices = array(); @@ -2699,106 +2702,22 @@ function calendar_get_eventtype_choices($courseid) { return array($choices, $allowed->groups); } -/** - * Form for adding a subscription to a Moodle course calendar. - */ -class calendar_addsubscription_form extends moodleform { - - function definition() { - $mform =& $this->_form; - $courseid = optional_param('course', 0, PARAM_INT); - - // code to show/hide the form from the heading - $mform->addElement('html', ''); - $mform->addElement('header', 'addsubscriptionform', ''.get_string('importcalendarheading', 'calendar').''); - - $mform->addElement('text', 'name', get_string('subscriptionname', 'calendar'), 'maxlength="255" size="40"'); - $mform->addRule('name', get_string('required'), 'required'); - - $mform->addElement('html', get_string('importfrominstructions', 'calendar')); - $choices = array(CALENDAR_IMPORT_FROM_FILE => get_string('importfromfile', 'calendar'), - CALENDAR_IMPORT_FROM_URL => get_string('importfromurl', 'calendar')); - $mform->addElement('select', 'importfrom', get_string('importcalendarfrom', 'calendar'), $choices); - $mform->setDefault('importfrom', CALENDAR_IMPORT_FROM_URL); - - $mform->addElement('text', 'url', get_string('importfromurl', 'calendar'), 'maxlength="255" size="50"'); - $mform->addElement('filepicker', 'importfile', get_string('importfromfile', 'calendar')); - - $mform->disabledIf('url', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_FILE); - $mform->disabledIf('importfile', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_URL); - - $choices = calendar_get_pollinterval_choices(); - $mform->addElement('select', 'pollinterval', get_string('pollinterval', 'calendar'), $choices); - - $mform->setDefault('pollinterval', 604800); - $mform->addHelpButton('pollinterval', 'pollinterval', 'calendar'); - - // eventtype: 0 = user, 1 = global, anything else = course ID - list($choices, $groups) = calendar_get_eventtype_choices($courseid); - $mform->addElement('select', 'eventtype', get_string('eventkind', 'calendar'), $choices); - $mform->addRule('eventtype', get_string('required'), 'required'); - - if (!empty($groups) and is_array($groups)) { - $groupoptions = array(); - foreach ($groups as $group) { - $groupoptions[$group->id] = $group->name; - } - $mform->addElement('select', 'groupid', get_string('typegroup', 'calendar'), $groupoptions); - $mform->disabledIf('groupid', 'eventtype', 'noteq', 'group'); - } - - $mform->addElement('hidden', 'course', optional_param('course', 0, PARAM_INT)); - $mform->addElement('hidden', 'view', optional_param('view', 'upcoming', PARAM_ALPHA)); - $mform->addElement('hidden', 'cal_d', optional_param('cal_d', 0, PARAM_INT)); - $mform->addElement('hidden', 'cal_m', optional_param('cal_m', 0, PARAM_INT)); - $mform->addElement('hidden', 'cal_y', optional_param('cal_y', 0, PARAM_INT)); - $mform->addElement('hidden', 'id', optional_param('id', 0, PARAM_INT)); - - $mform->addElement('submit', 'add', get_string('add')); - - // *sigh* folding up the form breaks the filepicker control - // $mform->addElement('html', ''); - } - - function get_ical_data() { - $formdata = $this->get_data(); - switch ($formdata->importfrom) { - case CALENDAR_IMPORT_FROM_FILE: - $calendar = $this->get_file_content('importfile'); - break; - case CALENDAR_IMPORT_FROM_URL: - $calendar = download_file_content($formdata->importurl); - break; - } - return $calendar; - } -} - /** * Add an iCalendar subscription to the database. - * @param object $sub The subscription object (e.g. from the form) - * @return int The insert ID, if any. + * + * @param stdClass $sub The subscription object (e.g. from the form) + * @return int The insert ID, if any. */ function calendar_add_subscription($sub) { global $DB, $USER; + $sub->courseid = $sub->eventtype; if ($sub->eventtype == 'group') { $sub->courseid = $sub->course; } $sub->userid = $USER->id; - // file subscriptions never update. + // File subscriptions never update. if (empty($sub->url)) { $sub->pollinterval = 0; } @@ -2818,17 +2737,16 @@ function calendar_add_subscription($sub) { /** * Add an iCalendar event to the Moodle calendar. - * @param object $event The RFC-2445 iCalendar event - * @param int $courseid The course ID - * @param int $subscriptionid The iCalendar subscription ID - * @return int Code: 1=updated, 2=inserted, 0=error + * + * @param object $event The RFC-2445 iCalendar event + * @param int $courseid The course ID + * @param int $subscriptionid The iCalendar subscription ID + * @return int Code: 1=updated, 2=inserted, 0=error */ -function calendar_add_icalendar_event($event, $courseid, $subscriptionid=null) { +function calendar_add_icalendar_event($event, $courseid, $subscriptionid = null) { global $DB, $USER; - $eventrecord = new stdClass; - - // probably an unsupported X-MICROSOFT-CDO-BUSYSTATUS event. + // Probably an unsupported X-MICROSOFT-CDO-BUSYSTATUS event. if (empty($event->properties['SUMMARY'])) { return 0; } @@ -2837,6 +2755,8 @@ function calendar_add_icalendar_event($event, $courseid, $subscriptionid=null) { $name = str_replace('\n', '
', $name); $name = str_replace('\\', '', $name); $name = preg_replace('/\s+/', ' ', $name); + + $eventrecord = new stdClass; $eventrecord->name = clean_param($name, PARAM_NOTAGS); if (empty($event->properties['DESCRIPTION'][0]->value)) { @@ -2849,7 +2769,7 @@ function calendar_add_icalendar_event($event, $courseid, $subscriptionid=null) { } $eventrecord->description = clean_param($description, PARAM_NOTAGS); - // probably a repeating event with RRULE etc. TODO: skip for now + // Probably a repeating event with RRULE etc. TODO: skip for now. if (empty($event->properties['DTSTART'][0]->value)) { return 0; } @@ -2863,7 +2783,7 @@ function calendar_add_icalendar_event($event, $courseid, $subscriptionid=null) { $eventrecord->uuid = $event->properties['UID'][0]->value; $eventrecord->timemodified = time(); - // Add the iCal subscription details if required + // Add the iCal subscription details if required. if ($sub = $DB->get_record('event_subscriptions', array('id' => $subscriptionid))) { $eventrecord->subscriptionid = $subscriptionid; $eventrecord->userid = $sub->userid; @@ -2892,197 +2812,67 @@ function calendar_add_icalendar_event($event, $courseid, $subscriptionid=null) { } /** - * Create the list of iCalendar subscriptions for a course calendar. - * @param int $courseid The course ID - * @return string The table output. - */ -function calendar_show_subscriptions($courseid, $importresults='') { - global $DB, $OUTPUT, $CFG, $USER; - - $view = optional_param('view', '', PARAM_ALPHA); - $sesskey = sesskey(); - $out = ''; - - $str = new object(); - $str->update = get_string('update'); - $str->remove = get_string('remove'); - $str->add = get_string('add'); - $str->colcalendar = get_string('colcalendar', 'calendar'); - $str->collastupdated = get_string('collastupdated', 'calendar'); - $str->colpoll = get_string('colpoll', 'calendar'); - $str->colactions = get_string('colactions', 'calendar'); - $str->nocalendarsubscriptions = get_string('nocalendarsubscriptions', 'calendar'); - - $out .= $OUTPUT->box_start('generalbox calendarsubs'); - $out .= $importresults; - - $table = new html_table(); - $table->head = array($str->colcalendar, $str->collastupdated, $str->colpoll, $str->colactions); - $table->align = array('left', 'left', 'left', 'center'); - $table->width = '100%'; - $table->data = array(); - - $subs = $DB->get_records_sql('select * from {event_subscriptions} - where courseid = :courseid - or (courseid = 0 and userid = :userid)', - array('courseid' => $courseid, 'userid' => $USER->id)); - if (empty($subs)) { - $c = new html_table_cell($str->nocalendarsubscriptions); - $c->colspan = 4; - $table->data[] = new html_table_row(array($c)); - } - foreach ($subs as $id => $sub) { - $label = empty($sub->url) ? $sub->name : "url}\">{$sub->name}"; - $cellurl = new html_table_cell($label); - $lastupdated = empty($sub->lastupdated) - ? get_string('never', 'calendar') - : userdate($sub->lastupdated, get_string('strftimedatetimeshort', 'langconfig')); - $cellupdated = new html_table_cell($lastupdated); - - if (empty($sub->url)) { - // don't update an iCal file, which has no URL. - $pollinterval = ''; - } else { - // assemble pollinterval control - $pollinterval = "
-
"; - } - - // assemble form for the subscription row - $rowform = " -
wwwroot}/calendar/view.php\" method=\"post\"> - {$pollinterval} -
- - - - id}\" /> - " . (empty($sub->url) - ? '' - : "update}\" />") . " - remove}\" /> -
-
"; - $cellform = new html_table_cell($rowform); - $cellform->colspan = 2; - $table->data[] = new html_table_row(array($cellurl, $cellupdated, $cellform)); - } - $out .= html_writer::table($table); - - // form for adding a new subscription - $form = new calendar_addsubscription_form(); - $formdata = $form->get_data(); - if (empty($formdata)) { - $formdata = new stdClass; - $formdata->course = $courseid; - $form->set_data($formdata); - } - - // *sigh* there appears to be no function that returns Moodle Form HTML. - ob_start(); - $form->display(); - $buffer = ob_get_contents(); - $out .= $buffer; - ob_end_clean(); - - $out .= $OUTPUT->box_end(); - return $out; -} - -/** - * Add a subscription from the form data and add its events to the calendar. - * The form data will be either from the new subscription form, or from a form - * on one of the rows in the existing subscriptions table. - * @param int $courseid The course ID - * @return string A log of the import progress, including errors - */ -function calendar_process_subscription_form($courseid) { - global $DB; - - $form = new calendar_addsubscription_form(); - $formdata = $form->get_data(); - if (!empty($formdata)) { - if (empty($formdata->url) and empty($formdata->importfile)) { - print_error('errorrequiredurlorfile', 'calendar'); - } - if ($formdata->importfrom == CALENDAR_IMPORT_FROM_FILE) { - // blank the URL if it's a file import - $formdata->url = ''; - $subscriptionid = calendar_add_subscription($formdata); - $calendar = $form->get_ical_data(); - $ical = new iCalendar(); - $ical->unserialize($calendar); - return calendar_import_icalendar_events($ical, $courseid, $subscriptionid); - } else { - $subscriptionid = calendar_add_subscription($formdata); - return calendar_update_subscription_events($subscriptionid); - } - } else { - // process any subscription row form data - return calendar_process_subscription_row(); - } -} - -/** - * Update a subscription from the form data in one of the rows in the existing - * subscriptions table. - * @return string A log of the import progress, including errors + * Update a subscription from the form data in one of the rows in the existing subscriptions table. + * + * @param int $subscriptionid The ID of the subscription we are acting upon. + * @param int $pollinterval The poll interval to use. + * @param int $action The action to be performed. One of update or remove. + * @return string A log of the import progress, including errors */ -function calendar_process_subscription_row() { +function calendar_process_subscription_row($subscriptionid, $pollinterval, $action) { global $DB; - $id = optional_param('id', 0, PARAM_INT); - $courseid = optional_param('course', 0, PARAM_INT); - $pollinterval = optional_param('pollinterval', 0, PARAM_INT); - $action = optional_param('action', '', PARAM_ALPHA); - - if (empty($id)) { + if (empty($subscriptionid)) { return ''; } - $str->update = get_string('update'); - $str->remove = get_string('remove'); + // Fetch the subscription from the database making sure it exists. + $sub = $DB->get_record('event_subscriptions', array('id' => $subscriptionid), '*', MUST_EXIST); - // update or remove the subscription, based on action. - $sub = $DB->get_record('event_subscriptions', array('id' => $id), '*', MUST_EXIST); + $strupdate = get_string('update'); + $strremove = get_string('remove'); + // Update or remove the subscription, based on action. switch ($action) { - case $str->update: - // skip updating file subscriptions - if (empty($sub->url)) { - break; - } - $sub->pollinterval = $pollinterval; - $DB->update_record('event_subscriptions', $sub); + case $strupdate: + // Skip updating file subscriptions. + if (empty($sub->url)) { + break; + } + $sub->pollinterval = $pollinterval; + $DB->update_record('event_subscriptions', $sub); - // update the events - return "

Calendar subscription '{$sub->name}' updated.

" . calendar_update_subscription_events($id); - break; + // Update the events. + return "

".get_string('subscriptionupdated', 'calendar', $sub->name)."

" . calendar_update_subscription_events($subscriptionid); - case $str->remove: - $sesskey = required_param('sesskey', PARAM_ALPHANUM); - $DB->delete_records('event', array('subscriptionid' => $id)); - $DB->delete_records('event_subscriptions', array('id' => $id)); - return "Calendar subscription '{$sub->name}' removed."; - break; + case $strremove: + $DB->delete_records('event', array('subscriptionid' => $subscriptionid)); + $DB->delete_records('event_subscriptions', array('id' => $subscriptionid)); + return get_string('subscriptionremoved', 'calendar', $sub->name); + break; - default: - break; + default: + break; } return ''; } /** * From a URL, fetch the calendar and return an iCalendar object. - * @param string $url The iCalendar URL - * @return object The iCalendar object + * + * @param string $url The iCalendar URL + * @return stdClass The iCalendar object */ function calendar_get_icalendar($url) { - $calendar = file_get_contents($url); + global $CFG; + + require_once($CFG->libdir.'/filelib.php'); + + $curl = new curl(); + $calendar = $curl->get($url); + if (!$calendar) { + throw new moodle_exception('errorinvalidicalurl', 'calendar'); + } + $ical = new iCalendar(); $ical->unserialize($calendar); return $ical; @@ -3090,24 +2880,24 @@ function calendar_get_icalendar($url) { /** * Import events from an iCalendar object into a course calendar. - * @param object $ical The iCalendar object - * @param integer $courseid The course ID for the calendar - * @param integer $subscriptionid The subscription ID - * @return string A log of the import progress, including - * errors + * + * @param stdClass $ical The iCalendar object. + * @param int $courseid The course ID for the calendar. + * @param int $subscriptionid The subscription ID. + * @return string A log of the import progress, including errors. */ -function calendar_import_icalendar_events($ical, $courseid, $subscriptionid=null) { +function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = null) { global $DB; $return = ''; $eventcount = 0; $updatecount = 0; - // large calendars take a while... - ini_set('max_execution_time', 300); + // Large calendars take a while... + set_time_limit(300); - // mark all events in a subscription with a zero timestamp + // Mark all events in a subscription with a zero timestamp. if (!empty($subscriptionid)) { - $sql = "update {event} set timemodified = :time where subscriptionid = :id"; + $sql = "UPDATE {event} SET timemodified = :time WHERE subscriptionid = :id"; $DB->execute($sql, array('time' => 0, 'id' => $subscriptionid)); } foreach ($ical->components['VEVENT'] as $event) { @@ -3124,14 +2914,14 @@ function calendar_import_icalendar_events($ical, $courseid, $subscriptionid=null break; } } - $return .= "

".get_string('eventsimported', 'calendar').": {$eventcount}

\n"; - $return .= "

".get_string('eventsupdated', 'calendar').": {$updatecount}

\n"; + $return .= "

".get_string('eventsimported', 'calendar', $eventcount)."

"; + $return .= "

".get_string('eventsupdated', 'calendar', $updatecount)."

"; - // delete remaining zero-marked events since they're not in remote calendar + // Delete remaining zero-marked events since they're not in remote calendar. if (!empty($subscriptionid)) { $deletecount = $DB->count_records('event', array('timemodified' => 0, 'subscriptionid' => $subscriptionid)); if (!empty($deletecount)) { - $sql = "delete from {event} where timemodified = :time and subscriptionid = :id"; + $sql = "DELETE FROM {event} WHERE timemodified = :time AND subscriptionid = :id"; $DB->execute($sql, array('time' => 0, 'id' => $subscriptionid)); $return .= "

".get_string('eventsdeleted', 'calendar').": {$deletecount}

\n"; } @@ -3142,19 +2932,18 @@ function calendar_import_icalendar_events($ical, $courseid, $subscriptionid=null /** * Fetch a calendar subscription and update the events in the calendar. - * @param integer $subscriptionid The course ID for the calendar - * @return string A log of the import progress, including - * errors + * + * @param int $subscriptionid The course ID for the calendar. + * @return string A log of the import progress, including errors. */ function calendar_update_subscription_events($subscriptionid) { global $DB; - $return = ''; $sub = $DB->get_record('event_subscriptions', array('id' => $subscriptionid)); if (empty($sub)) { print_error('errorbadsubscription', 'calendar'); } - // Don't update a file subscription. TODO: Update from a new uploaded file? + // Don't update a file subscription. TODO: Update from a new uploaded file. if (empty($sub->url)) { return 'File subscription not updated.'; } @@ -3167,18 +2956,30 @@ function calendar_update_subscription_events($subscriptionid) { /** * Update calendar subscriptions. + * + * @return bool */ function calendar_cron() { - global $DB; + global $CFG, $DB; + + // In order to execute this we need bennu. + require_once($CFG->libdir.'/bennu/bennu.inc.php'); + mtrace(get_string('cronupdate', 'calendar')); + $time = time(); - $subscriptions = $DB->get_records_sql('select * from {event_subscriptions} where pollinterval > 0 and lastupdated + pollinterval < ?', array($time)); + $subscriptions = $DB->get_records_sql('SELECT * FROM {event_subscriptions} WHERE pollinterval > 0 AND lastupdated + pollinterval < ?', array($time)); foreach ($subscriptions as $sub) { mtrace(get_string('cronupdatesub', 'calendar', $sub)); - $log = calendar_update_subscription_events($sub->id); + try { + $log = calendar_update_subscription_events($sub->id); + } catch (moodle_exception $ex) { + + } mtrace(trim(strip_tags($log))); } + mtrace(get_string('cronupdatefinished', 'calendar')); - return true; -} + return true; +} \ No newline at end of file diff --git a/calendar/managesubscriptions.php b/calendar/managesubscriptions.php new file mode 100644 index 0000000000000..5171147daa702 --- /dev/null +++ b/calendar/managesubscriptions.php @@ -0,0 +1,107 @@ +. + +/** + * Allows the user to manage calendar subscriptions. + * + * @copyright 2012 Jonathan Harker + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package calendar + */ + +require_once('../config.php'); +require_once($CFG->libdir.'/bennu/bennu.inc.php'); +require_once($CFG->dirroot.'/course/lib.php'); +require_once($CFG->dirroot.'/calendar/lib.php'); +require_once($CFG->dirroot.'/calendar/managesubscriptions_form.php'); + +// Required use. +$courseid = optional_param('course', SITEID, PARAM_INT); +// Used for processing subscription actions. +$subscriptionid = optional_param('id', 0, PARAM_INT); +$pollinterval = optional_param('pollinterval', 0, PARAM_INT); +$action = optional_param('action', '', PARAM_ALPHA); + +$url = new moodle_url('/calendar/managesubscriptions.php'); +if ($courseid != SITEID) { + $url->param('course', $courseid); +} +navigation_node::override_active_url(new moodle_url('/calendar/view.php', array('view' => 'month'))); +$PAGE->set_url($url); +$PAGE->set_pagelayout('standard'); +$PAGE->navbar->add(get_string('managesubscriptions', 'calendar')); + +if ($courseid != SITEID && !empty($courseid)) { + $course = $DB->get_record('course', array('id' => $courseid)); + $courses = array($course->id => $course); +} else { + $course = get_site(); + $courses = calendar_get_default_courses(); +} +require_course_login($course); +if (!calendar_user_can_add_event($course)) { + print_error('errorcannotimport', 'calendar'); +} + +$form = new calendar_addsubscription_form(null); +$form->set_data(array( + 'course' => $course->id +)); + +$importresults = ''; + +$formdata = $form->get_data(); +if (!empty($formdata)) { + require_sesskey(); // Must have sesskey for all actions. + $subscriptionid = calendar_add_subscription($formdata); + if ($formdata->importfrom == CALENDAR_IMPORT_FROM_FILE) { + // Blank the URL if it's a file import. + $formdata->url = ''; + $calendar = $form->get_ical_data(); + $ical = new iCalendar(); + $ical->unserialize($calendar); + $importresults = calendar_import_icalendar_events($ical, $courseid, $subscriptionid); + } else { + $importresults = calendar_update_subscription_events($subscriptionid); + } + // Redirect to prevent refresh issues. + redirect($PAGE->url); +} else if (!empty($subscriptionid)) { + // The user is wanting to perform an action upon an existing subscription. + require_sesskey(); // Must have sesskey for all actions. + $importresults = calendar_process_subscription_row($subscriptionid, $pollinterval, $action); +} + +$sql = 'SELECT * + FROM {event_subscriptions} + WHERE courseid = :courseid + OR (courseid = 0 AND userid = :userid)'; +$params = array('courseid' => $courseid, 'userid' => $USER->id); +$subscriptions = $DB->get_records_sql($sql, $params); + +// Print title and header. +$PAGE->set_title("$course->shortname: ".get_string('calendar', 'calendar').": ".get_string('subscriptions', 'calendar')); +$PAGE->set_heading($course->fullname); +$PAGE->set_button(calendar_preferences_button($course)); + +$renderer = $PAGE->get_renderer('core_calendar'); + +echo $OUTPUT->header(); +// Display a table of subscriptions. +echo $renderer->subscription_details($courseid, $subscriptions, $importresults); +// Display the add subscription form. +$form->display(); +echo $OUTPUT->footer(); \ No newline at end of file diff --git a/calendar/managesubscriptions_form.php b/calendar/managesubscriptions_form.php new file mode 100644 index 0000000000000..2e49e6beae321 --- /dev/null +++ b/calendar/managesubscriptions_form.php @@ -0,0 +1,133 @@ +. + +/** + * Allows the user to manage calendar subscriptions. + * + * @copyright 2012 Jonathan Harker + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package calendar + */ + +if (!defined('MOODLE_INTERNAL')) { + die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page +} + +require_once($CFG->libdir.'/formslib.php'); + +/** + * Form for adding a subscription to a Moodle course calendar. + * @copyright 2012 Jonathan Harker + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class calendar_addsubscription_form extends moodleform { + + /** + * Defines the form used to add calendar subscriptions. + */ + public function definition() { + $mform = $this->_form; + $courseid = optional_param('course', 0, PARAM_INT); + + $mform->addElement('header', 'addsubscriptionform', get_string('importcalendarheading', 'calendar')); + + // Name. + $mform->addElement('text', 'name', get_string('subscriptionname', 'calendar'), array('maxsize' => '255', 'size' => '40')); + $mform->addRule('name', get_string('required'), 'required'); + $mform->setType('name', PARAM_TEXT); + + // Import from (url | importfile). + $mform->addElement('html', get_string('importfrominstructions', 'calendar')); + $choices = array(CALENDAR_IMPORT_FROM_FILE => get_string('importfromfile', 'calendar'), + CALENDAR_IMPORT_FROM_URL => get_string('importfromurl', 'calendar')); + $mform->addElement('select', 'importfrom', get_string('importcalendarfrom', 'calendar'), $choices); + $mform->setDefault('importfrom', CALENDAR_IMPORT_FROM_URL); + + // URL. + $mform->addElement('text', 'url', get_string('importfromurl', 'calendar'), array('maxsize' => '255', 'size' => '50')); + $mform->setType('url', PARAM_URL); + + // Import file + $mform->addElement('filepicker', 'importfile', get_string('importfromfile', 'calendar')); + + $mform->disabledIf('url', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_FILE); + $mform->disabledIf('importfile', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_URL); + + // Poll interval + $choices = calendar_get_pollinterval_choices(); + $mform->addElement('select', 'pollinterval', get_string('pollinterval', 'calendar'), $choices); + $mform->setDefault('pollinterval', 604800); + $mform->addHelpButton('pollinterval', 'pollinterval', 'calendar'); + $mform->setType('pollinterval', PARAM_INT); + + // Eventtype: 0 = user, 1 = global, anything else = course ID. + list($choices, $groups) = calendar_get_eventtype_choices($courseid); + $mform->addElement('select', 'eventtype', get_string('eventkind', 'calendar'), $choices); + $mform->addRule('eventtype', get_string('required'), 'required'); + $mform->setType('eventtype', PARAM_INT); + + if (!empty($groups) and is_array($groups)) { + $groupoptions = array(); + foreach ($groups as $group) { + $groupoptions[$group->id] = $group->name; + } + $mform->addElement('select', 'groupid', get_string('typegroup', 'calendar'), $groupoptions); + $mform->setType('groupid', PARAM_INT); + $mform->disabledIf('groupid', 'eventtype', 'noteq', 'group'); + } + + $mform->addElement('hidden', 'course'); + $mform->addElement('hidden', 'sesskey', sesskey()); + $mform->addElement('submit', 'add', get_string('add')); + } + + /** + * Validates the returned data. + * + * @param array $data + * @param array $files + * @return array + */ + public function validation($data, $files) { + $errors = parent::validation($data, $files); + if (empty($data['url']) && empty($data['importfile'])) { + if (!empty($data['importfrom']) && $data['importfrom'] == CALENDAR_IMPORT_FROM_FILE) { + $errors['importfile'] = get_string('errorrequiredurlorfile', 'calendar'); + } else { + $errors['url'] = get_string('errorrequiredurlorfile', 'calendar'); + } + } + return $errors; + } + + /** + * Returns the ical content either from the uploaded file, or from the URL. + * + * @return bool|mixed|string + */ + public function get_ical_data() { + $formdata = $this->get_data(); + switch ($formdata->importfrom) { + case CALENDAR_IMPORT_FROM_FILE: + $calendar = $this->get_file_content('importfile'); + break; + case CALENDAR_IMPORT_FROM_URL: + $calendar = download_file_content($formdata->importurl); + break; + } + return $calendar; + } +} \ No newline at end of file diff --git a/calendar/renderer.php b/calendar/renderer.php index 51ea81c41d690..3b3b455f46794 100644 --- a/calendar/renderer.php +++ b/calendar/renderer.php @@ -23,6 +23,10 @@ * @package calendar */ +if (!defined('MOODLE_INTERNAL')) { + die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page +} + /** * The primary renderer for the calendar. */ @@ -718,4 +722,99 @@ protected function course_filter_selector(moodle_url $returnurl, $label=null) { } return $this->output->render($select); } + + /** + * Renders a table containing information about calendar subscriptions. + * + * @param int $courseid + * @param array $subscriptions + * @param string $importresults + * @return string + */ + public function subscription_details($courseid, $subscriptions, $importresults = '') { + $table = new html_table(); + $table->head = array( + get_string('colcalendar', 'calendar'), + get_string('collastupdated', 'calendar'), + get_string('colpoll', 'calendar'), + get_string('colactions', 'calendar') + ); + $table->align = array('left', 'left', 'left', 'center'); + $table->width = '100%'; + $table->data = array(); + + if (empty($subscriptions)) { + $cell = new html_table_cell(get_string('nocalendarsubscriptions', 'calendar')); + $cell->colspan = 4; + $table->data[] = new html_table_row(array($cell)); + } + $strnever = new lang_string('never', 'calendar'); + foreach ($subscriptions as $sub) { + $label = $sub->name; + if (!empty($sub->url)) { + $label = html_writer::link($sub->url, $label); + } + if (empty($sub->lastupdated)) { + $lastupdated = $strnever->out(); + } else { + $lastupdated = userdate($sub->lastupdated, get_string('strftimedatetimeshort', 'langconfig')); + } + + $cell = new html_table_cell($this->subscription_action_form($sub, $courseid)); + $cell->colspan = 2; + + $table->data[] = new html_table_row(array( + new html_table_cell($label), + new html_table_cell($lastupdated), + $cell + )); + } + + $out = $this->output->box_start('generalbox calendarsubs'); + + $out .= $importresults; + $out .= html_writer::table($table); + $out .= $this->output->box_end(); + return $out; + } + + /** + * Creates a form to perform actions on a given subscription. + * + * @param stdClass $subscription + * @param int $courseid + * @return string + */ + protected function subscription_action_form($subscription, $courseid) { + // Assemble form for the subscription row. + $html = html_writer::start_tag('form', array('action' => new moodle_url('/calendar/managesubscriptions.php'), 'method' => 'post')); + if (empty($subscription->url)) { + // Don't update an iCal file, which has no URL. + $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'pollinterval', 'value' => '0')); + } else { + // Assemble pollinterval control. + $html .= html_writer::start_tag('div', array('style' => 'float:left;')); + $html .= html_writer::start_tag('select', array('name' => 'pollinterval')); + foreach (calendar_get_pollinterval_choices() as $k => $v) { + $attributes = array(); + if ($k == $subscription->pollinterval) { + $attributes['selected'] = 'selected'; + } + $html .= html_writer::tag('option', $v, $attributes); + } + $html .= html_writer::end_tag('select'); + $html .= html_writer::end_tag('div'); + } + $html .= html_writer::start_tag('div', array('style' => 'float:right;')); + $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey())); + $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'course', 'value' => $courseid)); + $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'id', 'value' => $subscription->id)); + if (!empty($subscription->url)) { + $html .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'action', 'value' => get_string('update'))); + } + $html .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'action', 'value' => get_string('remove'))); + $html .= html_writer::end_tag('div'); + $html .= html_writer::end_tag('form'); + return $html; + } } \ No newline at end of file diff --git a/calendar/view.php b/calendar/view.php index 01dead64a0fbd..40a2d82a13541 100644 --- a/calendar/view.php +++ b/calendar/view.php @@ -80,10 +80,6 @@ } require_course_login($course); -if (calendar_user_can_add_event($course)) { - $importresults = calendar_process_subscription_form($courseid); -} - $calendar = new calendar_information($day, $mon, $yr); $calendar->prepare_for_view($course, $courses); @@ -149,13 +145,13 @@ break; } -//Link to calendar export page +//Link to calendar export page. echo $OUTPUT->container_start('bottom'); -if (calendar_user_can_add_event($course)) { - echo calendar_show_subscriptions($courseid, $importresults); -} if (!empty($CFG->enablecalendarexport)) { echo $OUTPUT->single_button(new moodle_url('export.php', array('course'=>$courseid)), get_string('exportcalendar', 'calendar')); + if (calendar_user_can_add_event($course)) { + echo $OUTPUT->single_button(new moodle_url('/calendar/managesubscriptions.php', array('course'=>$courseid)), get_string('managesubscriptions', 'calendar')); + } if (isloggedin()) { $authtoken = sha1($USER->id . $USER->password . $CFG->calendar_exportsalt); $link = new moodle_url('/calendar/export_execute.php', array('preset_what'=>'all', 'preset_time'=>'recentupcoming', 'userid' => $USER->id, 'authtoken'=>$authtoken)); @@ -167,4 +163,4 @@ echo $OUTPUT->container_end(); echo html_writer::end_tag('div'); echo $renderer->complete_layout(); -echo $OUTPUT->footer(); +echo $OUTPUT->footer(); \ No newline at end of file diff --git a/lang/en/calendar.php b/lang/en/calendar.php index 14303f7e39126..e1d17e48c257f 100644 --- a/lang/en/calendar.php +++ b/lang/en/calendar.php @@ -33,7 +33,7 @@ $string['clickhide'] = 'click to hide'; $string['clickshow'] = 'click to show'; $string['colcalendar'] = 'Calendar'; -$string['collastupdated'] = 'Last Updated'; +$string['collastupdated'] = 'Last updated'; $string['colpoll'] = 'Poll'; $string['colactions'] = 'Actions'; $string['commontasks'] = 'Options'; @@ -61,9 +61,11 @@ $string['erroraddingevent'] = 'Failed to add event'; $string['errorbadsubscription'] = 'Calendar subscription not found.'; $string['errorbeforecoursestart'] = 'Cannot set event before course start date'; +$string['errorcannotimport'] = 'You cannot set up a calendar subscription at this time.'; $string['errorinvaliddate'] = 'Invalid date'; $string['errorinvalidminutes'] = 'Specify duration in minutes by giving a number between 1 and 999.'; $string['errorinvalidrepeats'] = 'Specify the number of events by giving a number between 1 and 99.'; +$string['errorinvalidicalurl'] = 'The given iCal URL is invalid.'; $string['errornodescription'] = 'Description is required'; $string['errornoeventname'] = 'Name is required'; $string['errorrequiredurlorfile'] = 'Either a URL or a file is required to import a calendar.'; @@ -78,8 +80,8 @@ $string['eventrepeat'] = 'Repeats'; $string['eventsall'] = 'All events'; $string['eventsdeleted'] = 'Events deleted'; -$string['eventsimported'] = 'Events imported'; -$string['eventsupdated'] = 'Events updated'; +$string['eventsimported'] = 'Events imported: {$a}'; +$string['eventsupdated'] = 'Events updated: {$a}'; $string['eventsfor'] = '{$a} events'; $string['eventskey'] = 'Events key'; $string['eventsrelatedtocourses'] = 'Events related to courses'; @@ -118,6 +120,7 @@ $string['invalidtimedurationminutes'] = 'The duration in minutes you have entered is invalid. Please enter the duration in minutes greater than 0 or select no duration.'; $string['invalidtimedurationuntil'] = 'The date and time you selected for duration until is before the start time of the event. Please correct this before proceeding.'; $string['iwanttoexport'] = 'Export'; +$string['managesubscriptions'] = 'Manage subscriptions'; $string['manyevents'] = '{$a} events'; $string['mon'] = 'Mon'; $string['monday'] = 'Monday'; @@ -129,7 +132,7 @@ $string['newevent'] = 'New event'; $string['notitle'] = 'no title'; $string['noupcomingevents'] = 'There are no upcoming events'; -$string['nocalendarsubscriptions'] = 'No calendar subscriptions.'; +$string['nocalendarsubscriptions'] = 'You have no calendar subscriptions.'; $string['oneevent'] = '1 event'; $string['pollinterval'] = 'Poll interval'; $string['pollinterval_help'] = 'How often you would like the calendar to update with new events.'; @@ -162,7 +165,10 @@ $string['showuserevents'] = 'Show user events'; $string['shown'] = 'shown'; $string['spanningevents'] = 'Events underway'; +$string['subscriptions'] = 'Subscriptions'; $string['subscriptionname'] = 'Calendar name'; +$string['subscriptionremoved'] = 'Calendar subscription {$a} removed'; +$string['subscriptionupdated'] = 'Calendar subscription {$a} updated'; $string['sun'] = 'Sun'; $string['sunday'] = 'Sunday'; $string['thu'] = 'Thu'; diff --git a/lib/db/install.xml b/lib/db/install.xml index 0b2ac107d372b..b6f01ece6b391 100755 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -1,5 +1,5 @@ - @@ -490,7 +490,7 @@ - + @@ -2867,14 +2867,14 @@ - +
- + diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index db37df0d003ed..3bdb24d5abe14 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -1263,14 +1263,14 @@ function xmldb_main_upgrade($oldversion) { if ($oldversion < 2012103000.00) { // create new event_subscriptions table $table = new xmldb_table('event_subscriptions'); - $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('url', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); $table->add_field('groupid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); $table->add_field('pollinterval', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); $table->add_field('lastupdated', XMLDB_TYPE_INTEGER, '10', null, null, null, null); - $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, ''); + $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); if (!$dbman->table_exists($table)) { $dbman->create_table($table); @@ -1282,17 +1282,19 @@ function xmldb_main_upgrade($oldversion) { if ($oldversion < 2012103000.01) { // Add subscription field to the event table $table = new xmldb_table('event'); - $field = new xmldb_field('subscriptionid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, 'timemodified'); + $field = new xmldb_field('subscriptionid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'timemodified'); + + // Conditionally launch add field subscriptionid if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Fix uuid field in event table to match RFC-2445 UID property - $field = new xmldb_field('uuid', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null, '', 'visible'); + $field = new xmldb_field('uuid', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, 'visible'); if ($dbman->field_exists($table, $field)) { + // Changing precision of field uuid on table event to (255) $dbman->change_field_precision($table, $field); } - // Main savepoint reached upgrade_main_savepoint(true, 2012103000.01); }