/** * Creates or modifies an existing event * TODO: detect if we are defining a new recurrence-id */ function modify() { // Important data to be filled later $etag = ''; $href = ''; $calendar = ''; $resource = null; // Default new properties. To be cleaned // on Icshelper library $p = $this->input->post(null, TRUE); // XSS $this->load->library('form_validation'); $this->form_validation->set_rules('calendar', $this->i18n->_('labels', 'calendar'), 'required'); $this->form_validation->set_rules('summary', $this->i18n->_('labels', 'summary'), 'required'); $this->form_validation->set_rules('start_date', $this->i18n->_('labels', 'startdate'), 'required|callback__valid_date'); $this->form_validation->set_rules('end_date', $this->i18n->_('labels', 'enddate'), 'required|callback__valid_date'); $this->form_validation->set_rules('recurrence_count', $this->i18n->_('labels', 'repeatcount'), 'callback__empty_or_natural_no_zero'); $this->form_validation->set_rules('recurrence_until', $this->i18n->_('labels', 'repeatuntil'), 'callback__empty_or_valid_date'); if ($this->form_validation->run() === FALSE) { $this->_throw_exception(validation_errors()); } $this->load->library('caldav'); // DateTime objects $start = null; $end = null; $tz = isset($p['timezone']) ? $this->timezonemanager->getTz($p['timezone']) : $this->timezonemanager->getTz($this->config->item('default_timezone')); // Additional validations // 1. All day? If all day, require start_time, end_date and end_time // If not, generate our own values if (isset($p['allday']) && $p['allday'] == 'true') { // Start and end days, 00:00 $start = $this->dates->frontend2datetime($p['start_date'] . ' ' . date($this->time_format, mktime(0, 0)), $this->tz_utc); $end = $this->dates->frontend2datetime($p['end_date'] . ' ' . date($this->time_format, mktime(0, 0)), $this->tz_utc); // Add 1 day (iCalendar needs this) $end->add(new DateInterval('P1D')); } else { // Create new form validation rules $this->form_validation->set_rules('start_time', $this->i18n->_('labels', 'starttime'), 'required|callback__valid_time'); $this->form_validation->set_rules('end_time', $this->i18n->_('labels', 'endtime'), 'required|callback__valid_time'); if ($this->form_validation->run() === FALSE) { $this->_throw_exception(validation_errors()); } // 2. Check if start date <= end date $start = $this->dates->frontend2datetime($p['start_date'] . ' ' . $p['start_time'], $tz); $end = $this->dates->frontend2datetime($p['end_date'] . ' ' . $p['end_time'], $tz); if ($end->getTimestamp() < $start->getTimestamp()) { $this->_throw_exception($this->i18n->_('messages', 'error_startgreaterend')); } } $p['dtstart'] = $start; $p['dtend'] = $end; // Recurrence checks unset($p['rrule']); if (isset($p['recurrence_type'])) { if ($p['recurrence_type'] != 'none') { if (isset($p['recurrence_until']) && !empty($p['recurrence_until'])) { $p['recurrence_until'] .= date($this->time_format, mktime(0, 0)); // Tricky } $rrule = $this->recurrence->build($p, $rrule_err); if (FALSE === $rrule) { // Couldn't build rrule $this->extended_logs->message('ERROR', 'Error building RRULE (' . $rrule_err . ')'); $this->_throw_exception($this->i18n->_('messages', 'error_bogusrepeatrule') . ': ' . $rrule_err); } } else { // Deleted RRULE // TODO in the future, consider recurrence-id and so $rrule = ''; } $p['rrule'] = $rrule; } // Reminders $reminders = array(); // Contains a list of old parseable (visible on UI) reminders. // Used to remove reminders that were deleted by user $visible_reminders = isset($p['visible_reminders']) ? $p['visible_reminders'] : array(); if (isset($p['reminders']) && is_array($p['reminders'])) { $data_reminders = $p['reminders']; $num_reminders = count($data_reminders['is_absolute']); for ($i = 0; $i < $num_reminders; $i++) { $this_reminder = null; $data_reminders['is_absolute'][$i] = $data_reminders['is_absolute'][$i] == 'true' ? TRUE : FALSE; if ($data_reminders['is_absolute'][$i]) { $when = $this->dates->frontend2datetime($data_reminders['tdate'][$i] . ' ' . $data_reminders['ttime'][$i], $this->tz); $when->setTimezone($this->tz_utc); $this_reminder = Reminder::createFrom($when); } else { $when = array('before' => $data_reminders['before'][$i] == 'true', 'relatedStart' => $data_reminders['relatedStart'][$i] == 'true'); $interval = $data_reminders['interval'][$i]; $when[$interval] = $data_reminders['qty'][$i]; $this_reminder = Reminder::createFrom($when); } if (!empty($data_reminders['order'][$i])) { $this_reminder->order = $data_reminders['order'][$i]; } log_message('INTERNALS', 'Adding reminder ' . $this_reminder); $reminders[] = $this_reminder; } } // Is this a new event or a modification? // Valid destination calendar? if (!$this->caldav->is_valid_calendar($this->auth->get_user(), $this->auth->get_passwd(), $p['calendar'])) { $this->_throw_exception($this->i18n->_('messages', 'error_calendarnotfound', array('%calendar' => $p['calendar']))); } else { $calendar = $p['calendar']; } if (!isset($p['modification'])) { // New event (resource) $new_uid = $this->icshelper->new_resource($p, $resource, $this->tz, $reminders); $href = $new_uid . '.ics'; $etag = '*'; } else { // Load existing resource // Valid original calendar? if (!isset($p['original_calendar'])) { $this->_throw_exception($this->i18n->_('messages', 'error_interfacefailure')); } else { $original_calendar = $p['original_calendar']; } if (!$this->caldav->is_valid_calendar($this->auth->get_user(), $this->auth->get_passwd(), $original_calendar)) { $this->_throw_exception($this->i18n->_('messages', 'error_calendarnotfound', array('%calendar' => $original_calendar))); } $uid = $p['uid']; $href = $p['href']; $etag = $p['etag']; $res = $this->caldav->fetch_resource_by_uid($this->auth->get_user(), $this->auth->get_passwd(), $uid, $original_calendar); if (is_null($res)) { $this->_throw_error($this->i18n->_('messages', 'error_eventnotfound')); } if ($etag != $res['etag']) { $this->_throw_error($this->i18n->_('messages', 'error_eventchanged')); } $resource = $this->icshelper->parse_icalendar($res['data']); $timezones = $this->icshelper->get_timezones($resource); $vevent = null; // TODO: recurrence-id? $modify_pos = $this->icshelper->find_component_position($resource, 'VEVENT', array(), $vevent); if (is_null($vevent)) { $this->_throw_error($this->i18n->_('messages', 'error_eventnofound')); } $tz = $this->icshelper->detect_tz($vevent, $timezones); // Change every property $force_new_value = isset($p['allday']) && $p['allday'] == 'true' ? 'DATE' : 'DATE-TIME'; $vevent = $this->icshelper->make_start($vevent, $tz, $start, null, $force_new_value); $vevent = $this->icshelper->make_end($vevent, $tz, $end, null, $force_new_value); $properties = array('summary' => $p['summary'], 'location' => $p['location'], 'description' => $p['description']); // Only change RRULE when we are able to if (isset($p['rrule'])) { $properties['rrule'] = $p['rrule']; } // CLASS and TRANSP if (isset($p['class'])) { $properties['class'] = $p['class']; } if (isset($p['transp'])) { $properties['transp'] = strtoupper($p['transp']); } $vevent = $this->icshelper->change_properties($vevent, $properties); // Add/change/remove reminders $vevent = $this->icshelper->set_valarms($vevent, $reminders, $visible_reminders); $vevent = $this->icshelper->set_last_modified($vevent); $resource = $this->icshelper->replace_component($resource, 'vevent', $modify_pos, $vevent); if ($resource === FALSE) { $this->_throw_error($this->i18n->_('messages', 'error_internalgen')); } // Moving event between calendars if ($original_calendar != $calendar) { // We will need this etag later $original_etag = $etag; $etag = '*'; } } // PUT on server $new_etag = $this->caldav->put_resource($this->auth->get_user(), $this->auth->get_passwd(), $href, $calendar, $resource, $etag); if (FALSE === $new_etag) { $code = $this->caldav->get_last_response(); switch ($code[0]) { case '412': // TODO new events + already used UIDs! if (isset($p['modification'])) { $this->_throw_exception($this->i18n->_('messages', 'error_eventchanged')); } else { // Already used UID on new event. What a bad luck! // TODO propose a solution $this->_throw_error('Bad luck' . ' Repeated UID'); } break; case '403': $this->_throw_error($this->i18n->_('messages', 'error_denied')); break; default: $this->_throw_error($this->i18n->_('messages', 'error_unknownhttpcode', array('%res' => $code[0]))); break; } } else { // Remove original event if (isset($p['modification']) && $original_calendar != $calendar) { $res = $this->caldav->delete_resource($this->auth->get_user(), $this->auth->get_passwd(), $href, $original_calendar, $original_etag); if ($res === TRUE) { $this->extended_logs->message('INTERNALS', 'Deleted event (moved) with uid=' . $uid . ' from calendar ' . $original_calendar); } else { // There was an error $this->extended_logs->message('INTERNALS', 'Error deleting event (moved) with uid=' . $uid . ' from calendar ' . $original_calendar . ': ' . $res); $this->_throw_exception($res); } } // Return a list of affected calendars (original_calendar, new // calendar) $affected_calendars = array($calendar); if (isset($original_calendar) && $original_calendar != $calendar) { $affected_calendars[] = $original_calendar; } $this->_throw_success($affected_calendars); } }
/** * Parses a VEVENT resource VALARM definitions * * Returns an associative array ('n1#' => new Reminder, 'n2#' => new * Reminder...), where 'n#' is the order where this VALARM was found */ function parse_valarms($vevent, $timezones = array()) { $parsed_reminders = array(); $order = 0; while ($valarm = $vevent->getComponent('valarm')) { $order++; // TODO parse more actions $action = $valarm->getProperty('action'); if ($action == 'DISPLAY') { $trigger = $valarm->getProperty('trigger'); $reminder = null; if (isset($trigger['before'])) { // Related to event start/end $reminder = Reminder::createFrom($trigger); } else { // Absolute date-time trigger $tz = $this->detect_tz($valarm, $timezones, 'trigger'); $datetime = $this->CI->dates->idt2datetime($trigger, $tz); // Use default timezone $datetime->setTimezone($this->tz); $reminder = Reminder::createFrom($datetime); $reminder->tdate = $datetime->format($this->date_frontend_format); $reminder->ttime = $datetime->format($this->time_frontend_format); } if ($reminder !== null) { $reminder->order = $order; $parsed_reminders[$order] = $reminder; } } } return $parsed_reminders; }