/**
  * When flagging a recurring task as complete,
  * clone it and shift dates to the next occurrence
  */
 private function handle_recurrence(&$rec, $old)
 {
     $clone = null;
     if ($this->driver->is_complete($rec) && $old && !$this->driver->is_complete($old) && is_array($rec['recurrence'])) {
         $engine = libcalendaring::get_recurrence();
         $rrule = $rec['recurrence'];
         $updates = array();
         // compute the next occurrence of date attributes
         foreach (array('date' => 'time', 'startdate' => 'starttime') as $date_key => $time_key) {
             if (empty($rec[$date_key])) {
                 continue;
             }
             $date = new DateTime($rec[$date_key] . ' ' . $rec[$time_key], $this->timezone);
             $engine->init($rrule, $date);
             if ($next = $engine->next()) {
                 $updates[$date_key] = $next->format('Y-m-d');
                 if (!empty($rec[$time_key])) {
                     $updates[$time_key] = $next->format('H:i');
                 }
             }
         }
         // shift absolute alarm dates
         if (!empty($updates) && is_array($rec['valarms'])) {
             $updates['valarms'] = array();
             unset($rrule['UNTIL'], $rrule['COUNT']);
             // make recurrence rule unlimited
             foreach ($rec['valarms'] as $i => $alarm) {
                 if ($alarm['trigger'] instanceof DateTime) {
                     $engine->init($rrule, $alarm['trigger']);
                     if ($next = $engine->next()) {
                         $alarm['trigger'] = $next;
                     }
                 }
                 $updates['valarms'][$i] = $alarm;
             }
         }
         if (!empty($updates)) {
             // clone task to save a completed copy
             $clone = $rec;
             $clone['uid'] = $this->generate_uid();
             $clone['parent_id'] = $rec['id'];
             unset($clone['id'], $clone['recurrence'], $clone['attachments']);
             // update the task but unset completed flag
             $rec = array_merge($rec, $updates);
             $rec['complete'] = $old['complete'];
             $rec['status'] = $old['status'];
         }
     }
     return $clone;
 }
示例#2
0
 /**
  * repares new/edited task properties before save
  */
 private function prepare_task($rec)
 {
     // try to be smart and extract date from raw input
     if ($rec['raw']) {
         foreach (array('today', 'tomorrow', 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat') as $word) {
             $locwords[] = '/^' . preg_quote(mb_strtolower($this->gettext($word))) . '\\b/i';
             $normwords[] = $word;
             $datewords[] = $word;
         }
         foreach (array('jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'now', 'dec') as $month) {
             $locwords[] = '/(' . preg_quote(mb_strtolower($this->gettext('long' . $month))) . '|' . preg_quote(mb_strtolower($this->gettext($month))) . ')\\b/i';
             $normwords[] = $month;
             $datewords[] = $month;
         }
         foreach (array('on', 'this', 'next', 'at') as $word) {
             $fillwords[] = preg_quote(mb_strtolower($this->gettext($word)));
             $fillwords[] = $word;
         }
         $raw = trim($rec['raw']);
         $date_str = '';
         // translate localized keywords
         $raw = preg_replace('/^(' . join('|', $fillwords) . ')\\s*/i', '', $raw);
         $raw = preg_replace($locwords, $normwords, $raw);
         // find date pattern
         $date_pattern = '!^(\\d+[./-]\\s*)?((?:\\d+[./-])|' . join('|', $datewords) . ')\\.?(\\s+\\d{4})?[:;,]?\\s+!i';
         if (preg_match($date_pattern, $raw, $m)) {
             $date_str .= $m[1] . $m[2] . $m[3];
             $raw = preg_replace(array($date_pattern, '/^(' . join('|', $fillwords) . ')\\s*/i'), '', $raw);
             // add year to date string
             if ($m[1] && !$m[3]) {
                 $date_str .= date('Y');
             }
         }
         // find time pattern
         $time_pattern = '/^(\\d+([:.]\\d+)?(\\s*[hapm.]+)?),?\\s+/i';
         if (preg_match($time_pattern, $raw, $m)) {
             $has_time = true;
             $date_str .= ($date_str ? ' ' : 'today ') . $m[1];
             $raw = preg_replace($time_pattern, '', $raw);
         }
         // yes, raw input matched a (valid) date
         if (strlen($date_str) && strtotime($date_str) && ($date = new DateTime($date_str, $this->timezone))) {
             $rec['date'] = $date->format('Y-m-d');
             if ($has_time) {
                 $rec['time'] = $date->format('H:i');
             }
             $rec['title'] = $raw;
         } else {
             $rec['title'] = $rec['raw'];
         }
     }
     // normalize input from client
     if (isset($rec['complete'])) {
         $rec['complete'] = floatval($rec['complete']);
         if ($rec['complete'] > 1) {
             $rec['complete'] /= 100;
         }
     }
     if (isset($rec['flagged'])) {
         $rec['flagged'] = intval($rec['flagged']);
     }
     // fix for garbage input
     if ($rec['description'] == 'null') {
         $rec['description'] = '';
     }
     foreach ($rec as $key => $val) {
         if ($val === 'null') {
             $rec[$key] = null;
         }
     }
     if (!empty($rec['date'])) {
         $this->normalize_dates($rec, 'date', 'time');
     }
     if (!empty($rec['startdate'])) {
         $this->normalize_dates($rec, 'startdate', 'starttime');
     }
     // convert tags to array, filter out empty entries
     if (isset($rec['tags']) && !is_array($rec['tags'])) {
         $rec['tags'] = array_filter((array) $rec['tags']);
     }
     // Mod by Rosali (always save alarms)
     // alarms cannot work without a date
     /*
     if ($rec['alarms'] && !$rec['startdate'] && strpos($rec['alarms'], '@') === false)
         $rec['alarms'] = '';
     */
     // convert the submitted recurrence settings
     if (is_array($rec['recurrence'])) {
         $refdate = null;
         /* Mod by Rosali (startdate/starttime is the only reference date)
            if (!empty($rec['date'])) {
                $refdate = new DateTime($rec['date'] . ' ' . $rec['time'], $this->timezone);
            }
            
            else*/
         if (!empty($rec['startdate'])) {
             $refdate = new DateTime($rec['startdate'] . ' ' . $rec['starttime'], $this->timezone);
         }
         if ($refdate) {
             $rec['recurrence'] = $this->lib->from_client_recurrence($rec['recurrence'], $refdate);
             // translate count into an absolute end date.
             // why? because when shifting completed tasks to the next recurrence,
             // the initial start date to count from gets lost.
             if ($rec['recurrence']['COUNT']) {
                 $engine = libcalendaring::get_recurrence();
                 $engine->init($rec['recurrence'], $refdate);
                 if ($until = $engine->end()) {
                     $rec['recurrence']['UNTIL'] = $until;
                     unset($rec['recurrence']['COUNT']);
                 }
             }
         } else {
             // recurrence requires a reference date
             $rec['recurrence'] = '';
         }
     }
     $attachments = array();
     $taskid = $rec['id'];
     if (is_array($_SESSION[self::SESSION_KEY]) && $_SESSION[self::SESSION_KEY]['id'] == $taskid) {
         if (!empty($_SESSION[self::SESSION_KEY]['attachments'])) {
             foreach ($_SESSION[self::SESSION_KEY]['attachments'] as $id => $attachment) {
                 if (is_array($rec['attachments']) && in_array($id, $rec['attachments'])) {
                     $attachments[$id] = $this->rc->plugins->exec_hook('attachment_get', $attachment);
                     unset($attachments[$id]['abort'], $attachments[$id]['group']);
                 }
             }
         }
     }
     $rec['attachments'] = $attachments;
     if (is_numeric($rec['id']) && $rec['id'] < 0) {
         unset($rec['id']);
     }
     return $rec;
 }