| [ Index ] |
|
Code source de eGroupWare 1.2.106-2 |
1 <?php 2 /**************************************************************************\ 3 * eGroupWare - iCalendar Parser * 4 * http://www.egroupware.org * 5 * Written by Lars Kneschke <lkneschke@egroupware.org> * 6 * -------------------------------------------- * 7 * This program is free software; you can redistribute it and/or modify it * 8 * under the terms of the GNU General Public License as published by the * 9 * Free Software Foundation; either version 2 of the License. * 10 \**************************************************************************/ 11 12 /* $Id: class.boical.inc.php 22835 2006-11-13 08:34:39Z lkneschke $ */ 13 14 require_once EGW_SERVER_ROOT.'/calendar/inc/class.bocalupdate.inc.php'; 15 require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/Horde/iCalendar.php'; 16 17 /** 18 * iCal import and export via Horde iCalendar classes 19 * 20 * @package calendar 21 * @author Lars Kneschke <lkneschke@egroupware.org> 22 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> 23 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License 24 */ 25 class boical extends bocalupdate 26 { 27 /** 28 * @var array $supportedFields array containing the supported fields of the importing device 29 */ 30 var $supportedFields; 31 32 var $recur_days_1_0 = array( 33 MCAL_M_MONDAY => 'MO', 34 MCAL_M_TUESDAY => 'TU', 35 MCAL_M_WEDNESDAY => 'WE', 36 MCAL_M_THURSDAY => 'TH', 37 MCAL_M_FRIDAY => 'FR', 38 MCAL_M_SATURDAY => 'SA', 39 MCAL_M_SUNDAY => 'SU', 40 ); 41 42 /** 43 * @var array $status_egw2ical conversation of the participant status egw => ical 44 */ 45 var $status_egw2ical = array( 46 'U' => 'NEEDS-ACTION', 47 'A' => 'ACCEPTED', 48 'R' => 'DECLINED', 49 'T' => 'TENTATIVE', 50 ); 51 /** 52 * @var array conversation of the participant status ical => egw 53 */ 54 var $status_ical2egw = array( 55 'NEEDS-ACTION' => 'U', 56 'ACCEPTED' => 'A', 57 'DECLINED' => 'R', 58 'TENTATIVE' => 'T', 59 ); 60 61 /** 62 * @var array $status_ical2egw conversation of the priority egw => ical 63 */ 64 var $priority_egw2ical = array( 65 0 => 0, // undefined 66 1 => 9, // low 67 2 => 5, // normal 68 3 => 1, // high 69 ); 70 /** 71 * @var array $status_ical2egw conversation of the priority ical => egw 72 */ 73 var $priority_ical2egw = array( 74 0 => 0, // undefined 75 9 => 1, 8 => 1, 7 => 1, 6 => 1, // low 76 5 => 2, // normal 77 4 => 3, 2 => 3, 3 => 3, 1 => 3, // high 78 ); 79 80 /** 81 * @var array $recur_egw2ical_2_0 converstaion of egw recur-type => ical FREQ 82 */ 83 var $recur_egw2ical_2_0 = array( 84 MCAL_RECUR_DAILY => 'DAILY', 85 MCAL_RECUR_WEEKLY => 'WEEKLY', 86 MCAL_RECUR_MONTHLY_MDAY => 'MONTHLY', // BYMONHTDAY={1..31} 87 MCAL_RECUR_MONTHLY_WDAY => 'MONTHLY', // BYDAY={1..5}{MO..SO} 88 MCAL_RECUR_YEARLY => 'YEARLY', 89 ); 90 91 /** 92 * @var array $recur_egw2ical_1_0 converstaion of egw recur-type => ical FREQ 93 */ 94 var $recur_egw2ical_1_0 = array( 95 MCAL_RECUR_DAILY => 'D', 96 MCAL_RECUR_WEEKLY => 'W', 97 MCAL_RECUR_MONTHLY_MDAY => 'MD', // BYMONHTDAY={1..31} 98 MCAL_RECUR_MONTHLY_WDAY => 'MP', // BYDAY={1..5}{MO..SO} 99 MCAL_RECUR_YEARLY => 'YM', 100 ); 101 102 /** 103 * manufacturer and name of the sync-client 104 * 105 * @var string 106 */ 107 var $productManufacturer = 'file'; 108 var $productName = ''; 109 110 /** 111 * Exports one calendar event to an iCalendar item 112 * 113 * @param int/array $events (array of) cal_id or array of the events 114 * @param string $method='PUBLISH' 115 * @return string/boolean string with vCal or false on error (eg. no permission to read the event) 116 */ 117 function &exportVCal($events,$version='1.0', $method='PUBLISH') 118 { 119 $egwSupportedFields = array( 120 'CLASS' => array('dbName' => 'public'), 121 'SUMMARY' => array('dbName' => 'title'), 122 'DESCRIPTION' => array('dbName' => 'description'), 123 'LOCATION' => array('dbName' => 'location'), 124 'DTSTART' => array('dbName' => 'start'), 125 'DTEND' => array('dbName' => 'end'), 126 'ORGANIZER' => array('dbName' => 'owner'), 127 'ATTENDEE' => array('dbName' => 'participants'), 128 'RRULE' => array('dbName' => 'recur_type'), 129 'EXDATE' => array('dbName' => 'recur_exception'), 130 'PRIORITY' => array('dbName' => 'priority'), 131 'TRANSP' => array('dbName' => 'non_blocking'), 132 'CATEGORIES' => array('dbName' => 'category'), 133 ); 134 if(!is_array($this->supportedFields)) 135 { 136 $this->setSupportedFields(); 137 } 138 $vcal = &new Horde_iCalendar; 139 $vcal->setAttribute('PRODID','-//eGroupWare//NONSGML eGroupWare Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'. 140 strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang'])); 141 $vcal->setAttribute('VERSION',$version); 142 $vcal->setAttribute('METHOD',$method); 143 144 if (!is_array($events)) $events = array($events); 145 146 foreach($events as $event) 147 { 148 if (!is_array($event) && !($event = $this->read($event,null,false,'server'))) // server = timestamp in server-time(!) 149 { 150 return false; // no permission to read $cal_id 151 } 152 //_debug_array($event); 153 154 $eventGUID = $GLOBALS['egw']->common->generate_uid('calendar',$event['id']); 155 156 $vevent = Horde_iCalendar::newComponent('VEVENT',$vcal); 157 $parameters = $attributes = array(); 158 159 foreach($egwSupportedFields as $icalFieldName => $egwFieldInfo) 160 { 161 if($this->supportedFields[$egwFieldInfo['dbName']]) 162 { 163 switch($icalFieldName) 164 { 165 case 'ATTENDEE': 166 foreach((array)$event['participants'] as $uid => $status) 167 { 168 // ToDo, this needs to deal with resources too!!! 169 if (!is_numeric($uid)) continue; 170 171 $mailto = $GLOBALS['egw']->accounts->id2name($uid,'account_email'); 172 $cn = trim($GLOBALS['egw']->accounts->id2name($uid,'account_firstname'). ' ' . 173 $GLOBALS['egw']->accounts->id2name($uid,'account_lastname')); 174 $attributes['ATTENDEE'][] = $mailto ? 'MAILTO:'. $cn .'<'. $mailto .'>' : ''; 175 // ROLE={CHAIR|REQ-PARTICIPANT|OPT-PARTICIPANT|NON-PARTICIPANT} NOT used by eGW atm. 176 $role = $uid == $event['owner'] ? 'CHAIR' : 'REQ-PARTICIPANT'; 177 // RSVP={TRUE|FALSE} // resonse expected, not set in eGW => status=U 178 $rsvp = $status == 'U' ? 'TRUE' : 'FALSE'; 179 // PARTSTAT={NEEDS-ACTION|ACCEPTED|DECLINED|TENTATIVE|DELEGATED|COMPLETED|IN-PROGRESS} everything from delegated is NOT used by eGW atm. 180 $status = $this->status_egw2ical[$status]; 181 // CUTYPE={INDIVIDUAL|GROUP|RESOURCE|ROOM|UNKNOWN} 182 $cutype = $GLOBALS['egw']->accounts->get_type($uid) == 'g' ? 'GROUP' : 'INDIVIDUAL'; 183 $parameters['ATTENDEE'][] = array( 184 'CN' => $cn, 185 'ROLE' => $role, 186 'PARTSTAT' => $status, 187 'CUTYPE' => $cutype, 188 'RSVP' => $rsvp, 189 ); 190 } 191 break; 192 193 case 'CLASS': 194 $attributes['CLASS'] = $event['public'] ? 'PUBLIC' : 'PRIVATE'; 195 break; 196 197 case 'ORGANIZER': // according to iCalendar standard, ORGANIZER not used for events in the own calendar 198 if (!isset($event['participants'][$event['owner']]) || count($event['participants']) > 1) 199 { 200 $mailtoOrganizer = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email'); 201 $attributes['ORGANIZER'] = $mailtoOrganizer ? 'MAILTO:'.$mailtoOrganizer : ''; 202 $parameters['ORGANIZER']['CN'] = trim($GLOBALS['egw']->accounts->id2name($event['owner'],'account_firstname').' '. 203 $GLOBALS['egw']->accounts->id2name($event['owner'],'account_lastname')); 204 } 205 break; 206 207 case 'DTEND': 208 if(date('H:i:s',$event['end']) == '23:59:59') $event['end']++; 209 $attributes[$icalFieldName] = $event['end']; 210 break; 211 212 case 'RRULE': 213 if ($event['recur_type'] == MCAL_RECUR_NONE) break; // no recuring event 214 if ($version == '1.0') { 215 $interval = ($event['recur_interval'] > 1) ? $event['recur_interval'] : 1; 216 $rrule = array('FREQ' => $this->recur_egw2ical_1_0[$event['recur_type']].$interval); 217 switch ($event['recur_type']) 218 { 219 case MCAL_RECUR_WEEKLY: 220 $days = array(); 221 foreach($this->recur_days_1_0 as $id => $day) 222 { 223 if ($event['recur_data'] & $id) $days[] = strtoupper(substr($day,0,2)); 224 } 225 $rrule['BYDAY'] = implode(' ',$days); 226 $rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY']; 227 break; 228 229 case MCAL_RECUR_MONTHLY_MDAY: // date of the month: BYMONTDAY={1..31} 230 break; 231 232 case MCAL_RECUR_MONTHLY_WDAY: // weekday of the month: BDAY={1..5}{MO..SO} 233 $rrule['BYDAY'] = (1 + (int) ((date('d',$event['start'])-1) / 7)).'+ '. 234 strtoupper(substr(date('l',$event['start']),0,2)); 235 $rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY']; 236 break; 237 } 238 $rrule['UNTIL'] = ($event['recur_enddate']) ? date('Ymd',$event['recur_enddate']) : '#0'; 239 240 $attributes['RRULE'] = $rrule['FREQ'].' '.$rrule['UNTIL']; 241 } else { 242 $rrule = array('FREQ' => $this->recur_egw2ical_2_0[$event['recur_type']]); 243 switch ($event['recur_type']) 244 { 245 case MCAL_RECUR_WEEKLY: 246 $days = array(); 247 foreach($this->recur_days as $id => $day) 248 { 249 if ($event['recur_data'] & $id) $days[] = strtoupper(substr($day,0,2)); 250 } 251 $rrule['BYDAY'] = implode(',',$days); 252 break; 253 254 case MCAL_RECUR_MONTHLY_MDAY: // date of the month: BYMONTDAY={1..31} 255 $rrule['BYMONTHDAY'] = (int) date('d',$event['start']); 256 break; 257 258 case MCAL_RECUR_MONTHLY_WDAY: // weekday of the month: BDAY={1..5}{MO..SO} 259 $rrule['BYDAY'] = (1 + (int) ((date('d',$event['start'])-1) / 7)). 260 strtoupper(substr(date('l',$event['start']),0,2)); 261 break; 262 } 263 if ($event['recur_interval'] > 1) $rrule['INTERVAL'] = $event['recur_interval']; 264 if ($event['recur_enddate']) $rrule['UNTIL'] = date('Ymd',$event['recur_enddate']); // only day is set in eGW 265 266 // no idea how to get the Horde parser to produce a standard conformant 267 // RRULE:FREQ=... (note the double colon after RRULE, we cant use the $parameter array) 268 // so we create one value manual ;-) 269 foreach($rrule as $name => $value) 270 { 271 $attributes['RRULE'][] = $name . '=' . $value; 272 } 273 $attributes['RRULE'] = implode(';',$attributes['RRULE']); 274 } 275 break; 276 277 case 'EXDATE': 278 if ($event['recur_exception']) 279 { 280 $days = array(); 281 foreach($event['recur_exception'] as $day) 282 { 283 $days[] = date('Ymd',$day); 284 } 285 $attributes['EXDATE'] = implode(';',$days); 286 $parameters['EXDATE']['VALUE'] = 'DATE'; 287 } 288 break; 289 290 case 'PRIORITY': 291 $attributes['PRIORITY'] = (int) $this->priority_egw2ical[$event['priority']]; 292 break; 293 294 case 'TRANSP': 295 if ($version == '1.0') { 296 $attributes['TRANSP'] = $event['non_blocking'] ? 1 : 0; 297 } else { 298 $attributes['TRANSP'] = $event['non_blocking'] ? 'TRANSPARENT' : 'OPAQUE'; 299 } 300 break; 301 302 case 'CATEGORIES': 303 if ($event['category']) 304 { 305 $attributes['CATEGORIES'] = implode(',',$this->categories($event['category'],$nul)); 306 } 307 break; 308 default: 309 if ($event[$egwFieldInfo['dbName']]) // dont write empty fields 310 { 311 $attributes[$icalFieldName] = $event[$egwFieldInfo['dbName']]; 312 } 313 break; 314 } 315 } 316 } 317 $modified = $GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'modify'); 318 $created = $GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'add'); 319 if (!$created && !$modified) $created = $event['modified']; 320 if ($created) $attributes['CREATED'] = $created; 321 if (!$modified) $modified = $event['modified']; 322 if ($modified) $attributes['LAST-MODIFIED'] = $modified; 323 324 foreach($event['alarm'] as $alarmID => $alarmData) 325 { 326 $attributes['DALARM'] = $vcal->_exportDateTime($alarmData['time']); 327 $attributes['AALARM'] = $vcal->_exportDateTime($alarmData['time']); 328 // lets take only the first alarm 329 break; 330 } 331 332 $attributes['UID'] = $eventGUID; 333 334 foreach($attributes as $key => $value) 335 { 336 foreach(is_array($value) ? $value : array($value) as $valueID => $valueData) 337 { 338 $valueData = $GLOBALS['egw']->translation->convert($valueData,$GLOBALS['egw']->translation->charset(),'UTF-8'); 339 $paramData = (array) $GLOBALS['egw']->translation->convert(is_array($value) ? $parameters[$key][$valueID] : $parameters[$key], 340 $GLOBALS['egw']->translation->charset(),'UTF-8'); 341 //echo "$key:$valueID: value=$valueData, param=".print_r($paramDate,true)."\n"; 342 $vevent->setAttribute($key, $valueData, $paramData); 343 $options = array(); 344 if($key != 'RRULE' && preg_match('/([\000-\012\015\016\020-\037\075])/',$valueData)) 345 { 346 $options['ENCODING'] = 'QUOTED-PRINTABLE'; 347 } 348 if(preg_match('/([\177-\377])/',$valueData)) 349 { 350 $options['CHARSET'] = 'UTF-8'; 351 } 352 $vevent->setParameter($key, $options); 353 } 354 } 355 $vcal->addComponent($vevent); 356 } 357 //_debug_array($vcal->exportvCalendar()); 358 359 return $vcal->exportvCalendar(); 360 } 361 362 function importVCal($_vcalData, $cal_id=-1) 363 { 364 // our (patched) horde classes, do NOT unfold folded lines, which causes a lot trouble in the import 365 $_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData); 366 367 $vcal = &new Horde_iCalendar; 368 if(!$vcal->parsevCalendar($_vcalData)) 369 { 370 return FALSE; 371 } 372 373 $version = $vcal->getAttribute('VERSION'); 374 375 if(!is_array($this->supportedFields)) 376 { 377 $this->setSupportedFields(); 378 } 379 //echo "supportedFields="; _debug_array($this->supportedFields); 380 381 $Ok = false; // returning false, if file contains no components 382 foreach($vcal->getComponents() as $component) 383 { 384 if(is_a($component, 'Horde_iCalendar_vevent')) 385 { 386 $supportedFields = $this->supportedFields; 387 #$event = array('participants' => array()); 388 $event = array(); 389 $alarms = array(); 390 $vcardData = array('recur_type' => 0); 391 392 // lets see what we can get from the vcard 393 foreach($component->_attributes as $attributes) 394 { 395 switch($attributes['name']) 396 { 397 case 'AALARM': 398 case 'DALARM': 399 if (preg_match('/.*Z$/',$attributes['value'],$matches)) { 400 $alarmTime = $vcal->_parseDateTime($attributes['value']); 401 $alarms[$alarmTime] = array( 402 'time' => $alarmTime 403 ); 404 } elseif (preg_match('/(........T......);;;$/',$attributes['value'],$matches)) { 405 #error_log(print_r($matches,true)); 406 $alarmTime = $vcal->_parseDateTime($matches[1]); 407 $alarms[$alarmTime] = array( 408 'time' => $alarmTime 409 ); 410 } 411 break; 412 case 'CLASS': 413 $vcardData['public'] = (int)(strtolower($attributes['value']) == 'public'); 414 break; 415 case 'DESCRIPTION': 416 $vcardData['description'] = $attributes['value']; 417 break; 418 case 'DTEND': 419 if(date('H:i:s',$attributes['value']) == '00:00:00') 420 $attributes['value']--; 421 $vcardData['end'] = $attributes['value']; 422 break; 423 case 'DTSTART': 424 $vcardData['start'] = $attributes['value']; 425 break; 426 case 'LOCATION': 427 $vcardData['location'] = $attributes['value']; 428 break; 429 case 'RRULE': 430 $recurence = $attributes['value']; 431 $type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence{0}; 432 // vCard 2.0 values for all types 433 if (preg_match('/UNTIL=([0-9T]+)/',$recurence,$matches)) 434 { 435 $vcardData['recur_enddate'] = $vcal->_parseDateTime($matches[1]); 436 } 437 if (preg_match('/INTERVAL=([0-9]+)/',$recurence,$matches)) 438 { 439 $vcardData['recur_interval'] = (int) $matches[1]; 440 } 441 $vcardData['recur_data'] = 0; 442 switch($type) 443 { 444 case 'W': 445 case 'WEEKLY': 446 $days = array(); 447 if(preg_match('/W(\d+) (.*) (.*)/',$recurence, $recurenceMatches)) // 1.0 448 { 449 $vcardData['recur_interval'] = $recurenceMatches[1]; 450 $days = explode(' ',trim($recurenceMatches[2])); 451 if($recurenceMatches[3] != '#0') 452 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[3]); 453 $recur_days = $this->recur_days_1_0; 454 } 455 elseif (preg_match('/BYDAY=([^;: ]+)/',$recurence,$recurenceMatches)) // 2.0 456 { 457 $days = explode(',',$recurenceMatches[1]); 458 $recur_days = $this->recur_days; 459 } 460 if ($days) 461 { 462 foreach($recur_days as $id => $day) 463 { 464 if (in_array(strtoupper(substr($day,0,2)),$days)) 465 { 466 $vcardData['recur_data'] |= $id; 467 } 468 } 469 $vcardData['recur_type'] = MCAL_RECUR_WEEKLY; 470 } 471 break; 472 473 case 'D': // 1.0 474 if(preg_match('/D(\d+) #(.\d)/', $recurence, $recurenceMatches)) { 475 $vcardData['recur_interval'] = $recurenceMatches[1]; 476 if($recurenceMatches[2] > 0 && $vcardData['end']) { 477 $vcardData['recur_enddate'] = mktime( 478 date('H', $vcardData['end']), 479 date('i', $vcardData['end']), 480 date('s', $vcardData['end']), 481 date('m', $vcardData['end']), 482 date('d', $vcardData['end']) + ($recurenceMatches[2] * $vcardData['recur_interval']), 483 date('Y', $vcardData['end']) 484 ); 485 } 486 } elseif(preg_match('/D(\d+) (.*)/', $recurence, $recurenceMatches)) { 487 $vcardData['recur_interval'] = $recurenceMatches[1]; 488 if($recurenceMatches[2] != '#0') { 489 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]); 490 } 491 } else { 492 break; 493 } 494 // fall-through 495 case 'DAILY': // 2.0 496 $vcardData['recur_type'] = MCAL_RECUR_DAILY; 497 break; 498 499 case 'M': 500 if(preg_match('/MD(\d+) #(.\d)/', $recurence, $recurenceMatches)) { 501 $vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY; 502 $vcardData['recur_interval'] = $recurenceMatches[1]; 503 if($recurenceMatches[2] > 0 && $vcardData['end']) { 504 $vcardData['recur_enddate'] = mktime( 505 date('H', $vcardData['end']), 506 date('i', $vcardData['end']), 507 date('s', $vcardData['end']), 508 date('m', $vcardData['end']) + ($recurenceMatches[2] * $vcardData['recur_interval']), 509 date('d', $vcardData['end']), 510 date('Y', $vcardData['end']) 511 ); 512 } 513 } elseif(preg_match('/MD(\d+) (.*)/',$recurence, $recurenceMatches)) { 514 $vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY; 515 if($recurenceMatches[1] > 1) 516 $vcardData['recur_interval'] = $recurenceMatches[1]; 517 if($recurenceMatches[2] != '#0') 518 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]); 519 } elseif(preg_match('/MP(\d+) (.*) (.*) (.*)/',$recurence, $recurenceMatches)) { 520 $vcardData['recur_type'] = MCAL_RECUR_MONTHLY_WDAY; 521 if($recurenceMatches[1] > 1) 522 $vcardData['recur_interval'] = $recurenceMatches[1]; 523 if($recurenceMatches[4] != '#0') 524 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[4]); 525 } 526 break; 527 case 'MONTHLY': 528 $vcardData['recur_type'] = strstr($recurence,'BYDAY') ? 529 MCAL_RECUR_MONTHLY_WDAY : MCAL_RECUR_MONTHLY_MDAY; 530 break; 531 532 case 'Y': // 1.0 533 if(preg_match('/YM(\d+) #(.\d)/', $recurence, $recurenceMatches)) { 534 $vcardData['recur_interval'] = $recurenceMatches[1]; 535 if($recurenceMatches[2] > 0 && $vcardData['end']) { 536 $vcardData['recur_enddate'] = mktime( 537 date('H', $vcardData['end']), 538 date('i', $vcardData['end']), 539 date('s', $vcardData['end']), 540 date('m', $vcardData['end']), 541 date('d', $vcardData['end']), 542 date('Y', $vcardData['end']) + ($recurenceMatches[2] * $vcardData['recur_interval']) 543 ); 544 } 545 } elseif(preg_match('/YM(\d+) (.*)/',$recurence, $recurenceMatches)) { 546 $vcardData['recur_interval'] = $recurenceMatches[1]; 547 if($recurenceMatches[2] != '#0') { 548 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]); 549 } 550 } else { 551 break; 552 } 553 // fall-through 554 case 'YEARLY': // 2.0 555 $vcardData['recur_type'] = MCAL_RECUR_YEARLY; 556 break; 557 } 558 break; 559 case 'EXDATE': 560 $vcardData['recur_exception'] = $attributes['value']; 561 break; 562 case 'SUMMARY': 563 $vcardData['title'] = $attributes['value']; 564 break; 565 case 'UID': 566 $event['uid'] = $vcardData['uid'] = $attributes['value']; 567 if ($cal_id <= 0 && !empty($vcardData['uid']) && ($uid_event = $this->read($vcardData['uid']))) 568 { 569 $event['id'] = $uid_event['id']; 570 unset($uid_event); 571 } 572 break; 573 case 'TRANSP': 574 if($version == '1.0') { 575 $vcardData['non_blocking'] = $attributes['value'] == 1; 576 } else { 577 $vcardData['non_blocking'] = $attributes['value'] == 'TRANSPARENT'; 578 } 579 break; 580 case 'PRIORITY': 581 $vcardData['priority'] = (int) $this->priority_ical2egw[$attributes['value']]; 582 break; 583 case 'CATEGORIES': 584 $vcardData['category'] = array(); 585 if ($attributes['value']) 586 { 587 if (!is_object($this->cat)) 588 { 589 if (!is_object($GLOBALS['egw']->categories)) 590 { 591 $GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$this->owner,'calendar'); 592 } 593 $this->cat =& $GLOBALS['egw']->categories; 594 } 595 foreach(explode(',',$attributes['value']) as $cat_name) 596 { 597 if (!($cat_id = $this->cat->name2id($cat_name))) 598 { 599 $cat_id = $this->cat->add( array('name' => $cat_name,'descr' => $cat_name )); 600 } 601 $vcardData['category'][] = $cat_id; 602 } 603 } 604 break; 605 case 'ATTENDEE': 606 if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) && 607 ($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email'))) 608 { 609 $event['participants'][$uid] = isset($attributes['params']['PARTSTAT']) ? 610 $this->status_ical2egw[strtoupper($attributes['params']['PARTSTAT'])] : 611 ($uid == $event['owner'] ? 'A' : 'U'); 612 } 613 614 if (preg_match('/<([@.a-z0-9_-]+)>/i',$attributes['value'],$matches)) { 615 $uid = ''; 616 $uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email'); 617 if(!empty($uid)) { 618 $event['participants'][$uid] = isset($attributes['params']['PARTSTAT']) ? 619 $this->status_ical2egw[strtoupper($attributes['params']['PARTSTAT'])] : 620 ($uid == $event['owner'] ? 'A' : 'U'); 621 } 622 } 623 624 if($attributes['value'] == 'Unknown') { 625 $event['participants'][$GLOBALS['egw_info']['user']['account_id']] = 'A'; 626 } 627 628 break; 629 case 'ORGANIZER': // will be written direct to the event 630 if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) && 631 ($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email'))) 632 { 633 $event['owner'] = $uid; 634 } 635 break; 636 case 'CREATED': // will be written direct to the event 637 if ($event['modified']) break; 638 // fall through 639 case 'LAST-MODIFIED': // will be written direct to the event 640 $event['modified'] = $attributes['value']; 641 break; 642 } 643 } 644 if(!empty($vcardData['recur_enddate'])) 645 { 646 // reset recure_enddate to 00:00:00 on the last day 647 $vcardData['recur_enddate'] = mktime(0, 0, 0, 648 date('m',$vcardData['recur_enddate']), 649 date('d',$vcardData['recur_enddate']), 650 date('Y',$vcardData['recur_enddate']) 651 ); 652 } 653 //echo "event=";_debug_array($vcardData); 654 655 // now that we know what the vard provides, we merge that data with the information we have about the device 656 $event['priority'] = 2; 657 if($cal_id > 0) 658 { 659 $event['id'] = $cal_id; 660 } 661 while(($fieldName = array_shift($supportedFields))) 662 { 663 switch($fieldName) 664 { 665 case 'alarms': 666 // not handled here 667 break; 668 case 'recur_type': 669 $event['recur_type'] = $vcardData['recur_type']; 670 if ($event['recur_type'] != MCAL_RECUR_NONE) 671 { 672 foreach(array('recur_interval','recur_enddate','recur_data','recur_exception') as $r) 673 { 674 if(isset($vcardData[$r])) 675 { 676 $event[$r] = $vcardData[$r]; 677 } 678 } 679 } 680 unset($supportedFields['recur_type']); 681 unset($supportedFields['recur_interval']); 682 unset($supportedFields['recur_enddate']); 683 unset($supportedFields['recur_data']); 684 break; 685 default: 686 if (isset($vcardData[$fieldName])) 687 { 688 $event[$fieldName] = $vcardData[$fieldName]; 689 } 690 unset($supportedFields[$fieldName]); 691 break; 692 } 693 } 694 695 // add ourself to new events as participant 696 if($cal_id == -1 && !isset($this->supportedFields['participants'])) 697 { 698 $event['participants'] = array($GLOBALS['egw_info']['user']['account_id'] => 'A'); 699 } 700 701 #error_log('ALARMS'); 702 #error_log(print_r($event['participants'], true)); 703 704 if (!($Ok = $this->update($event, TRUE))) { 705 break; // stop with the first error 706 } 707 else 708 { 709 $eventID =& $Ok; 710 711 // handle the alarms 712 if(count($alarms) > 0 || (isset($this->supportedFields['alarms']) && count($alarms) == 0)) 713 { 714 // delete the old alarms 715 $updatedEvent = $this->read($eventID); 716 foreach($updatedEvent['alarm'] as $alarmID => $alarmData) 717 { 718 $this->delete_alarm($alarmID); 719 } 720 } 721 722 foreach($alarms as $alarm) 723 { 724 $alarm['offset'] = $event['start'] - $alarm['time']; 725 $alarm['owner'] = $GLOBALS['egw_info']['user']['account_id']; 726 $this->save_alarm($eventID, $alarm); 727 } 728 } 729 } 730 } 731 return $Ok; 732 } 733 734 function setSupportedFields($_productManufacturer='file', $_productName='') 735 { 736 // save them vor later use 737 $this->productManufacturere = $_productManufacturer; 738 $this->productName = $_productName; 739 740 $defaultFields = array('public' => 'public', 'description' => 'description', 'end' => 'end', 741 'start' => 'start', 'location' => 'location', 'recur_type' => 'recur_type', 742 'recur_interval' => 'recur_interval', 'recur_data' => 'recur_data', 'recur_enddate' => 'recur_enddate', 743 'title' => 'title', 'priority' => 'priority', 'alarms' => 'alarms', 744 745 ); 746 747 switch(strtolower($_productManufacturer)) 748 { 749 case 'nexthaus corporation': 750 switch(strtolower($_productName)) 751 { 752 default: 753 $this->supportedFields = $defaultFields + array('participants' => 'participants'); 754 #$this->supportedFields = $defaultFields; 755 break; 756 } 757 break; 758 759 // multisync does not provide anymore information then the manufacturer 760 // we suppose multisync with evolution 761 case 'the multisync project': 762 switch(strtolower($_productName)) 763 { 764 default: 765 $this->supportedFields = $defaultFields; 766 break; 767 } 768 break; 769 770 case 'sonyericsson': 771 switch(strtolower($_productName)) 772 { 773 case 'd750i': 774 default: 775 $this->supportedFields = $defaultFields; 776 break; 777 } 778 break; 779 780 case 'synthesis ag': 781 switch(strtolower($_productName)) 782 { 783 default: 784 $this->supportedFields = $defaultFields + array( 785 'recur_exception' => 'recur_exception', 786 'non_blocking' => 'non_blocking', 787 ); 788 break; 789 } 790 break; 791 792 //Syncevolution compatibility 793 case 'patrick ohly': 794 $this->supportedFields = $defaultFields + array( 795 'participants' => 'participants', 796 'owner' => 'owner', 797 'category' => 'category', 798 ); 799 break; 800 801 case 'file': // used outside of SyncML, eg. by the calendar itself ==> all possible fields 802 $this->supportedFields = $defaultFields + array( 803 'participants' => 'participants', 804 'owner' => 'owner', 805 'non_blocking' => 'non_blocking', 806 'category' => 'category', 807 ); 808 break; 809 810 // the fallback for SyncML 811 default: 812 error_log("Client not found: $_productManufacturer $_productName"); 813 $this->supportedFields = $defaultFields; 814 break; 815 } 816 } 817 818 function icaltoegw($_vcalData) { 819 // our (patched) horde classes, do NOT unfold folded lines, which causes a lot trouble in the import 820 $_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData); 821 822 $vcal = &new Horde_iCalendar; 823 if(!$vcal->parsevCalendar($_vcalData)) 824 { 825 return FALSE; 826 } 827 828 if(!is_array($this->supportedFields)) 829 { 830 $this->setSupportedFields(); 831 } 832 //echo "supportedFields="; _debug_array($this->supportedFields); 833 834 $Ok = false; // returning false, if file contains no components 835 foreach($vcal->getComponents() as $component) 836 { 837 if(is_a($component, 'Horde_iCalendar_vevent')) 838 { 839 $supportedFields = $this->supportedFields; 840 #$event = array('participants' => array()); 841 $event = array(); 842 $alarms = array(); 843 $vcardData = array('recur_type' => 0); 844 845 // lets see what we can get from the vcard 846 foreach($component->_attributes as $attributes) 847 { 848 switch($attributes['name']) 849 { 850 case 'AALARM': 851 case 'DALARM': 852 if (preg_match('/.*Z$/',$attributes['value'],$matches)) 853 { 854 $alarmTime = $vcal->_parseDateTime($attributes['value']); 855 $alarms[$alarmTime] = array( 856 'time' => $alarmTime 857 ); 858 } 859 break; 860 case 'CLASS': 861 $vcardData['public'] = (int)(strtolower($attributes['value']) == 'public'); 862 break; 863 case 'DESCRIPTION': 864 $vcardData['description'] = $attributes['value']; 865 break; 866 case 'DTEND': 867 if(date('H:i:s',$attributes['value']) == '00:00:00') 868 $attributes['value']--; 869 $vcardData['end'] = $attributes['value']; 870 break; 871 case 'DTSTART': 872 $vcardData['start'] = $attributes['value']; 873 break; 874 case 'LOCATION': 875 $vcardData['location'] = $attributes['value']; 876 break; 877 case 'RRULE': 878 $recurence = $attributes['value']; 879 $type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence{0}; 880 // vCard 2.0 values for all types 881 if (preg_match('/UNTIL=([0-9T]+)/',$recurence,$matches)) 882 { 883 $vcardData['recur_enddate'] = $vcal->_parseDateTime($matches[1]); 884 } 885 if (preg_match('/INTERVAL=([0-9]+)/',$recurence,$matches)) 886 { 887 $vcardData['recur_interval'] = (int) $matches[1]; 888 } 889 $vcardData['recur_data'] = 0; 890 switch($type) 891 { 892 case 'W': 893 case 'WEEKLY': 894 $days = array(); 895 if(preg_match('/W(\d+) (.*) (.*)/',$recurence, $recurenceMatches)) // 1.0 896 { 897 $vcardData['recur_interval'] = $recurenceMatches[1]; 898 $days = explode(' ',trim($recurenceMatches[2])); 899 if($recurenceMatches[3] != '#0') 900 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[3]); 901 $recur_days = $this->recur_days_1_0; 902 } 903 elseif (preg_match('/BYDAY=([^;: ]+)/',$recurence,$recurenceMatches)) // 2.0 904 { 905 $days = explode(',',$recurenceMatches[1]); 906 $recur_days = $this->recur_days; 907 } 908 if ($days) 909 { 910 foreach($recur_days as $id => $day) 911 { 912 if (in_array(strtoupper(substr($day,0,2)),$days)) 913 { 914 $vcardData['recur_data'] |= $id; 915 } 916 } 917 $vcardData['recur_type'] = MCAL_RECUR_WEEKLY; 918 } 919 break; 920 921 case 'D': // 1.0 922 if(!preg_match('/D(\d+) (.*)/',$recurence, $recurenceMatches)) break; 923 $vcardData['recur_interval'] = $recurenceMatches[1]; 924 if($recurenceMatches[2] != '#0') 925 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]); 926 // fall-through 927 case 'DAILY': // 2.0 928 $vcardData['recur_type'] = MCAL_RECUR_DAILY; 929 break; 930 931 case 'M': 932 if(preg_match('/MD(\d+) (.*)/',$recurence, $recurenceMatches)) 933 { 934 $vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY; 935 if($recurenceMatches[1] > 1) 936 $vcardData['recur_interval'] = $recurenceMatches[1]; 937 if($recurenceMatches[2] != '#0') 938 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]); 939 } 940 elseif(preg_match('/MP(\d+) (.*) (.*) (.*)/',$recurence, $recurenceMatches)) 941 { 942 $vcardData['recur_type'] = MCAL_RECUR_MONTHLY_WDAY; 943 if($recurenceMatches[1] > 1) 944 $vcardData['recur_interval'] = $recurenceMatches[1]; 945 if($recurenceMatches[4] != '#0') 946 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[4]); 947 } 948 break; 949 case 'MONTHLY': 950 $vcardData['recur_type'] = strstr($recurence,'BYDAY') ? 951 MCAL_RECUR_MONTHLY_WDAY : MCAL_RECUR_MONTHLY_MDAY; 952 break; 953 954 case 'Y': // 1.0 955 if(!preg_match('/YM(\d+) (.*)/',$recurence, $recurenceMatches)) break; 956 $vcardData['recur_interval'] = $recurenceMatches[1]; 957 if($recurenceMatches[2] != '#0') 958 $vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]); 959 // fall-through 960 case 'YEARLY': // 2.0 961 $vcardData['recur_type'] = MCAL_RECUR_YEARLY; 962 break; 963 } 964 break; 965 case 'EXDATE': 966 $vcardData['recur_exception'] = $attributes['value']; 967 break; 968 case 'SUMMARY': 969 $vcardData['title'] = $attributes['value']; 970 break; 971 case 'UID': 972 $event['uid'] = $vcardData['uid'] = $attributes['value']; 973 if ($cal_id <= 0 && !empty($vcardData['uid']) && ($uid_event = $this->read($vcardData['uid']))) 974 { 975 $event['id'] = $uid_event['id']; 976 unset($uid_event); 977 } 978 break; 979 case 'TRANSP': 980 $vcardData['non_blocking'] = $attributes['value'] == 'TRANSPARENT'; 981 break; 982 case 'PRIORITY': 983 if ($this->productManufacturer == 'nexthaus corporation') 984 { 985 $vcardData['priority'] = $attributes['value'] == 1 ? 3 : 2; // 1=high, 2=normal 986 } 987 else 988 { 989 $vcardData['priority'] = (int) $this->priority_ical2egw[$attributes['value']]; 990 } 991 break; 992 case 'CATEGORIES': 993 $vcardData['category'] = array(); 994 if ($attributes['value']) 995 { 996 if (!is_object($this->cat)) 997 { 998 if (!is_object($GLOBALS['egw']->categories)) 999 { 1000 $GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories',$this->owner,'calendar'); 1001 } 1002 $this->cat =& $GLOBALS['egw']->categories; 1003 } 1004 foreach(explode(',',$attributes['value']) as $cat_name) 1005 { 1006 if (!($cat_id = $this->cat->name2id($cat_name))) 1007 { 1008 $cat_id = $this->cat->add( array('name' => $cat_name,'descr' => $cat_name )); 1009 } 1010 $vcardData['category'][] = $cat_id; 1011 } 1012 } 1013 break; 1014 case 'ATTENDEE': 1015 if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) && 1016 ($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email'))) 1017 { 1018 $event['participants'][$uid] = isset($attributes['params']['PARTSTAT']) ? 1019 $this->status_ical2egw[strtoupper($attributes['params']['PARTSTAT'])] : 1020 ($uid == $event['owner'] ? 'A' : 'U'); 1021 } 1022 break; 1023 case 'ORGANIZER': // will be written direct to the event 1024 if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) && 1025 ($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email'))) 1026 { 1027 $event['owner'] = $uid; 1028 } 1029 break; 1030 case 'CREATED': // will be written direct to the event 1031 if ($event['modified']) break; 1032 // fall through 1033 case 'LAST-MODIFIED': // will be written direct to the event 1034 $event['modified'] = $attributes['value']; 1035 break; 1036 } 1037 } 1038 if(!empty($vcardData['recur_enddate'])) 1039 { 1040 // reset recure_enddate to 00:00:00 on the last day 1041 $vcardData['recur_enddate'] = mktime(0, 0, 0, 1042 date('m',$vcardData['recur_enddate']), 1043 date('d',$vcardData['recur_enddate']), 1044 date('Y',$vcardData['recur_enddate']) 1045 ); 1046 } 1047 //echo "event=";_debug_array($vcardData); 1048 1049 // now that we know what the vard provides, we merge that data with the information we have about the device 1050 $event['priority'] = 2; 1051 if($cal_id > 0) 1052 { 1053 $event['id'] = $cal_id; 1054 } 1055 while(($fieldName = array_shift($supportedFields))) 1056 { 1057 switch($fieldName) 1058 { 1059 case 'alarms': 1060 // not handled here 1061 break; 1062 case 'recur_type': 1063 $event['recur_type'] = $vcardData['recur_type']; 1064 if ($event['recur_type'] != MCAL_RECUR_NONE) 1065 { 1066 foreach(array('recur_interval','recur_enddate','recur_data','recur_exception') as $r) 1067 { 1068 if(isset($vcardData[$r])) 1069 { 1070 $event[$r] = $vcardData[$r]; 1071 } 1072 } 1073 } 1074 unset($supportedFields['recur_type']); 1075 unset($supportedFields['recur_interval']); 1076 unset($supportedFields['recur_enddate']); 1077 unset($supportedFields['recur_data']); 1078 break; 1079 default: 1080 if (isset($vcardData[$fieldName])) 1081 { 1082 $event[$fieldName] = $vcardData[$fieldName]; 1083 } 1084 unset($supportedFields[$fieldName]); 1085 break; 1086 } 1087 } 1088 1089 return $event; 1090 } 1091 } 1092 1093 return false; 1094 } 1095 1096 function search($_vcalData) { 1097 if(!$event = $this->icaltoegw($_vcalData)) { 1098 return false; 1099 } 1100 $query = array( 1101 'cal_start='.$this->date2ts($event['start'],true), // true = Server-time 1102 'cal_end='.$this->date2ts($event['end'],true), 1103 ); 1104 foreach(array('title','location','priority','public','non_blocking') as $name) { 1105 if (isset($event[$name])) $query['cal_'.$name] = $event[$name]; 1106 } 1107 1108 if($foundEvents = parent::search(array( 1109 'user' => $this->user, 1110 'query' => $query, 1111 ))) { 1112 if(is_array($foundEvents)) { 1113 $event = array_shift($foundEvents); 1114 return $event['id']; 1115 } 1116 } 1117 return false; 1118 } 1119 1120 /** 1121 * Create a freebusy vCal for the given user(s) 1122 * 1123 * @param int $user account_id 1124 * @param mixed $end=null end-date, default now+1 month 1125 * @return string 1126 */ 1127 function freebusy($user,$end=null) 1128 { 1129 if (!$end) $end = $this->now_su + 100*DAY_s; // default next 100 days 1130 1131 $vcal = &new Horde_iCalendar; 1132 $vcal->setAttribute('PRODID','-//eGroupWare//NONSGML eGroupWare Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'. 1133 strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang'])); 1134 $vcal->setAttribute('VERSION','2.0'); 1135 1136 $vfreebusy = Horde_iCalendar::newComponent('VFREEBUSY',$vcal); 1137 $parameters = array( 1138 'ORGANIZER' => $GLOBALS['egw']->translation->convert( 1139 $GLOBALS['egw']->accounts->id2name($user,'account_firstname').' '. 1140 $GLOBALS['egw']->accounts->id2name($user,'account_lastname'), 1141 $GLOBALS['egw']->translation->charset(),'utf-8'), 1142 ); 1143 foreach(array( 1144 'URL' => $this->freebusy_url($user), 1145 'DTSTART' => $this->date2ts($this->now_su,true), // true = server-time 1146 'DTEND' => $this->date2ts($end,true), // true = server-time 1147 'ORGANIZER' => $GLOBALS['egw']->accounts->id2name($user,'account_email'), 1148 'DTSTAMP' => time(), 1149 ) as $attr => $value) 1150 { 1151 $vfreebusy->setAttribute($attr, $value, $parameters[$name]); 1152 } 1153 foreach(parent::search(array( 1154 'start' => $this->now_su, 1155 'end' => $end, 1156 'users' => $user, 1157 'date_format' => 'server', 1158 'show_rejected' => false, 1159 )) as $event) 1160 { 1161 if ($event['non_blocking']) continue; 1162 1163 $vfreebusy->setAttribute('FREEBUSY',array(array( 1164 'start' => $event['start'], 1165 'end' => $event['end'], 1166 ))); 1167 } 1168 $vcal->addComponent($vfreebusy); 1169 1170 return $vcal->exportvCalendar(); 1171 } 1172 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sun Feb 25 17:20:01 2007 | par Balluche grâce à PHPXref 0.7 |