| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 /* $Id: date_api_ical.inc,v 1.35.4.34 2010/10/26 10:21:36 karens Exp $ */ 3 /** 4 * @file 5 * Parse iCal data. 6 * 7 * This file must be included when these functions are needed. 8 */ 9 /** 10 * Return an array of iCalendar information from an iCalendar file. 11 * 12 * No timezone adjustment is performed in the import since the timezone 13 * conversion needed will vary depending on whether the value is being 14 * imported into the database (when it needs to be converted to UTC), is being 15 * viewed on a site that has user-configurable timezones (when it needs to be 16 * converted to the user's timezone), if it needs to be converted to the 17 * site timezone, or if it is a date without a timezone which should not have 18 * any timezone conversion applied. 19 * 20 * Properties that have dates and times are converted to sub-arrays like: 21 * 'datetime' => date in YYYY-MM-DD HH:MM format, not timezone adjusted 22 * 'all_day' => whether this is an all-day event 23 * 'tz' => the timezone of the date, could be blank for absolute 24 * times that should get no timezone conversion. 25 * 26 * Exception dates can have muliple values and are returned as arrays 27 * like the above for each exception date. 28 * 29 * Most other properties are returned as PROPERTY => VALUE. 30 * 31 * Each item in the VCALENDAR will return an array like: 32 * [0] => Array ( 33 * [TYPE] => VEVENT 34 * [UID] => 104 35 * [SUMMARY] => An example event 36 * [URL] => http://example.com/node/1 37 * [DTSTART] => Array ( 38 * [datetime] => 1997-09-07 09:00:00 39 * [all_day] => 0 40 * [tz] => US/Eastern 41 * ) 42 * [DTEND] => Array ( 43 * [datetime] => 1997-09-07 11:00:00 44 * [all_day] => 0 45 * [tz] => US/Eastern 46 * ) 47 * [RRULE] => Array ( 48 * [FREQ] => Array ( 49 * [0] => MONTHLY 50 * ) 51 * [BYDAY] => Array ( 52 * [0] => 1SU 53 * [1] => -1SU 54 * ) 55 * ) 56 * [EXDATE] => Array ( 57 * [0] = Array ( 58 * [datetime] => 1997-09-21 09:00:00 59 * [all_day] => 0 60 * [tz] => US/Eastern 61 * ) 62 * [1] = Array ( 63 * [datetime] => 1997-10-05 09:00:00 64 * [all_day] => 0 65 * [tz] => US/Eastern 66 * ) 67 * ) 68 * [RDATE] => Array ( 69 * [0] = Array ( 70 * [datetime] => 1997-09-21 09:00:00 71 * [all_day] => 0 72 * [tz] => US/Eastern 73 * ) 74 * [1] = Array ( 75 * [datetime] => 1997-10-05 09:00:00 76 * [all_day] => 0 77 * [tz] => US/Eastern 78 * ) 79 * ) 80 * ) 81 * 82 * @param $filename 83 * Location (local or remote) of a valid iCalendar file 84 * @return array 85 * An array with all the elements from the ical 86 * @todo 87 * figure out how to handle this if subgroups are nested, 88 * like a VALARM nested inside a VEVENT. 89 */ 90 function date_ical_import($filename) { 91 // Fetch the iCal data. If file is a URL, use drupal_http_request. fopen 92 // isn't always configured to allow network connections. 93 if (substr($filename, 0, 4) == 'http') { 94 // Fetch the ical data from the specified network location 95 $icaldatafetch = drupal_http_request($filename); 96 // Check the return result 97 if ($icaldatafetch->error) { 98 watchdog('date ical', 'HTTP Request Error importing %filename: @error', array('%filename' => $filename, '@error' => $icaldatafetch->error)); 99 return false; 100 } 101 // Break the return result into one array entry per lines 102 $icaldatafolded = explode("\n", $icaldatafetch->data); 103 } 104 else { 105 $icaldatafolded = @file($filename, FILE_IGNORE_NEW_LINES); 106 if ($icaldatafolded === FALSE) { 107 watchdog('date ical', 'Failed to open file: %filename', array('%filename' => $filename)); 108 return false; 109 } 110 } 111 // Verify this is iCal data 112 if (trim($icaldatafolded[0]) != 'BEGIN:VCALENDAR') { 113 watchdog('date ical', 'Invalid calendar file: %filename', array('%filename' => $filename)); 114 return false; 115 } 116 return date_ical_parse($icaldatafolded); 117 } 118 119 /** 120 * Return an array of iCalendar information from an iCalendar file. 121 * 122 * As date_ical_import() but different param. 123 * 124 * @param $icaldatafolded 125 * an array of lines from an ical feed. 126 * @return array 127 * An array with all the elements from the ical. 128 */ 129 function date_ical_parse($icaldatafolded = array()) { 130 $items = array(); 131 132 // Verify this is iCal data 133 if (trim($icaldatafolded[0]) != 'BEGIN:VCALENDAR') { 134 watchdog('date ical', 'Invalid calendar file.'); 135 return false; 136 } 137 138 139 // "unfold" wrapped lines 140 $icaldata = array(); 141 foreach ($icaldatafolded as $line) { 142 $out = array(); 143 // See if this looks like the beginning of a new property or value. 144 // If not, it is a continuation of the previous line. 145 // The regex is to ensure that wrapped QUOTED-PRINTABLE data 146 // is kept intact. 147 if (!preg_match('/([A-Z]+)[:;](.*)/', $line, $out)) { 148 $line = array_pop($icaldata) . ($line); 149 } 150 $icaldata[] = $line; 151 } 152 unset($icaldatafolded); 153 154 // Parse the iCal information 155 $parents = array(); 156 $subgroups = array(); 157 $vcal = ''; 158 foreach ($icaldata as $line) { 159 $line = trim($line); 160 $vcal .= $line ."\n"; 161 // Deal with begin/end tags separately 162 if (preg_match('/(BEGIN|END):V(\S+)/', $line, $matches)) { 163 $closure = $matches[1]; 164 $type = 'V'. $matches[2]; 165 if ($closure == 'BEGIN') { 166 array_push($parents, $type); 167 array_push($subgroups, array()); 168 } 169 else if ($closure == 'END') { 170 end($subgroups); 171 $subgroup =& $subgroups[key($subgroups)]; 172 switch ($type) { 173 case 'VCALENDAR': 174 if (prev($subgroups) == false) { 175 $items[] = array_pop($subgroups); 176 } 177 else { 178 $parent[array_pop($parents)][] = array_pop($subgroups); 179 } 180 break; 181 // Add the timezones in with their index their TZID 182 case 'VTIMEZONE': 183 $subgroup = end($subgroups); 184 $id = $subgroup['TZID']; 185 unset($subgroup['TZID']); 186 187 // Append this subgroup onto the one above it 188 prev($subgroups); 189 $parent =& $subgroups[key($subgroups)]; 190 191 $parent[$type][$id] = $subgroup; 192 193 array_pop($subgroups); 194 array_pop($parents); 195 break; 196 // Do some fun stuff with durations and all_day events 197 // and then append to parent 198 case 'VEVENT': 199 case 'VALARM': 200 case 'VTODO': 201 case 'VJOURNAL': 202 case 'VVENUE': 203 case 'VFREEBUSY': 204 default: 205 // Can't be sure whether DTSTART is before or after DURATION, 206 // so parse DURATION at the end. 207 if (isset($subgroup['DURATION'])) { 208 date_ical_parse_duration($subgroup, 'DURATION'); 209 } 210 // Add a top-level indication for the 'All day' condition. 211 // Leave it in the individual date components, too, so it 212 // is always available even when you are working with only 213 // a portion of the VEVENT array, like in Feed API parsers. 214 $subgroup['all_day'] = FALSE; 215 if (!empty($subgroup['DTSTART']) && (!empty($subgroup['DTSTART']['all_day']) || 216 (empty($subgroup['DTEND']) && empty($subgroup['RRULE']) && empty($subgroup['RRULE']['COUNT'])))) { 217 // Many programs output DTEND for an all day event as the 218 // following day, reset this to the same day for internal use. 219 $subgroup['all_day'] = TRUE; 220 $subgroup['DTEND'] = $subgroup['DTSTART']; 221 } 222 // Add this element to the parent as an array under the 223 // component name 224 default: 225 prev($subgroups); 226 $parent =& $subgroups[key($subgroups)]; 227 228 $parent[$type][] = $subgroup; 229 230 array_pop($subgroups); 231 array_pop($parents); 232 break; 233 } 234 } 235 } 236 // Handle all other possibilities 237 else { 238 // Grab current subgroup 239 end($subgroups); 240 $subgroup =& $subgroups[key($subgroups)]; 241 242 // Split up the line into nice pieces for PROPERTYNAME, 243 // PROPERTYATTRIBUTES, and PROPERTYVALUE 244 preg_match('/([^;:]+)(?:;([^:]*))?:(.+)/', $line, $matches); 245 $name = !empty($matches[1]) ? strtoupper(trim($matches[1])) : ''; 246 $field = !empty($matches[2]) ? $matches[2] : ''; 247 $data = !empty($matches[3]) ? $matches[3] : ''; 248 $parse_result = ''; 249 switch ($name) { 250 // Keep blank lines out of the results. 251 case '': 252 break; 253 254 // Lots of properties have date values that must be parsed out. 255 case 'CREATED': 256 case 'LAST-MODIFIED': 257 case 'DTSTART': 258 case 'DTEND': 259 case 'DTSTAMP': 260 case 'FREEBUSY': 261 case 'DUE': 262 case 'COMPLETED': 263 $parse_result = date_ical_parse_date($field, $data); 264 break; 265 266 case 'EXDATE': 267 case 'RDATE': 268 $parse_result = date_ical_parse_exceptions($field, $data); 269 break; 270 271 case 'TRIGGER': 272 // A TRIGGER can either be a date or in the form -PT1H. 273 if (!empty($field)) { 274 $parse_result = date_ical_parse_date($field, $data); 275 } 276 else { 277 $parse_result = array('DATA' => $data); 278 } 279 break; 280 281 case 'DURATION': 282 // Can't be sure whether DTSTART is before or after DURATION in 283 // the VEVENT, so store the data and parse it at the end. 284 $parse_result = array('DATA' => $data); 285 break; 286 287 case 'RRULE': 288 case 'EXRULE': 289 $parse_result = date_ical_parse_rrule($field, $data); 290 break; 291 292 case 'SUMMARY': 293 case 'DESCRIPTION': 294 $parse_result = date_ical_parse_text($field, $data); 295 break; 296 297 case 'LOCATION': 298 $parse_result = date_ical_parse_location($field, $data); 299 break; 300 301 // For all other properties, just store the property and the value. 302 // This can be expanded on in the future if other properties should 303 // be given special treatment. 304 default: 305 $parse_result = $data; 306 break; 307 } 308 309 // Store the result of our parsing 310 $subgroup[$name] = $parse_result; 311 } 312 } 313 return $items; 314 } 315 316 /** 317 * Parse a ical date element. 318 * 319 * Possible formats to parse include: 320 * PROPERTY:YYYYMMDD[T][HH][MM][SS][Z] 321 * PROPERTY;VALUE=DATE:YYYYMMDD[T][HH][MM][SS][Z] 322 * PROPERTY;VALUE=DATE-TIME:YYYYMMDD[T][HH][MM][SS][Z] 323 * PROPERTY;TZID=XXXXXXXX;VALUE=DATE:YYYYMMDD[T][HH][MM][SS] 324 * PROPERTY;TZID=XXXXXXXX:YYYYMMDD[T][HH][MM][SS] 325 * 326 * The property and the colon before the date are removed in the import 327 * process above and we are left with $field and $data. 328 * 329 * @param $field 330 * The text before the colon and the date, i.e. 331 * ';VALUE=DATE:', ';VALUE=DATE-TIME:', ';TZID=' 332 * @param $data 333 * The date itself, after the colon, in the format YYYYMMDD[T][HH][MM][SS][Z] 334 * 'Z', if supplied, means the date is in UTC. 335 * 336 * @return array 337 * $items array, consisting of: 338 * 'datetime' => date in YYYY-MM-DD HH:MM format, not timezone adjusted 339 * 'all_day' => whether this is an all-day event with no time 340 * 'tz' => the timezone of the date, could be blank if the ical 341 * has no timezone; the ical specs say no timezone 342 * conversion should be done if no timezone info is 343 * supplied 344 * @todo 345 * Another option for dates is the format PROPERTY;VALUE=PERIOD:XXXX. The period 346 * may include a duration, or a date and a duration, or two dates, so would 347 * have to be split into parts and run through date_ical_parse_date() and 348 * date_ical_parse_duration(). This is not commonly used, so ignored for now. 349 * It will take more work to figure how to support that. 350 */ 351 function date_ical_parse_date($field, $data) { 352 $items = array('datetime' => '', 'all_day' => '', 'tz' => ''); 353 if (empty($data)) { 354 return $items; 355 } 356 // Make this a little more whitespace independent 357 $data = trim($data); 358 359 // Turn the properties into a nice indexed array of 360 // array(PROPERTYNAME => PROPERTYVALUE); 361 $field_parts = preg_split('/[;:]/', $field); 362 $properties = array(); 363 foreach ($field_parts as $part) { 364 if (strpos($part, '=') !== false) { 365 $tmp = explode('=', $part); 366 $properties[$tmp[0]] = $tmp[1]; 367 } 368 } 369 370 // Make this a little more whitespace independent 371 $data = trim($data); 372 373 // Record if a time has been found 374 $has_time = false; 375 376 // If a format is specified, parse it according to that format 377 if (isset($properties['VALUE'])) { 378 switch ($properties['VALUE']) { 379 case 'DATE': 380 preg_match (DATE_REGEX_ICAL_DATE, $data, $regs); 381 $datetime = date_pad($regs[1]) .'-'. date_pad($regs[2]) .'-'. date_pad($regs[3]); // Date 382 break; 383 case 'DATE-TIME': 384 preg_match (DATE_REGEX_ICAL_DATETIME, $data, $regs); 385 $datetime = date_pad($regs[1]) .'-'. date_pad($regs[2]) .'-'. date_pad($regs[3]); // Date 386 $datetime .= ' '. date_pad($regs[4]) .':'. date_pad($regs[5]) .':'. date_pad($regs[6]); // Time 387 $has_time = true; 388 break; 389 } 390 } 391 // If no format is specified, attempt a loose match 392 else { 393 preg_match (DATE_REGEX_LOOSE, $data, $regs); 394 $datetime = date_pad($regs[1]) .'-'. date_pad($regs[2]) .'-'. date_pad($regs[3]); // Date 395 if (isset($regs[4])) { 396 $has_time = true; 397 $has_time = TRUE; 398 $datetime .= ' ' . (!empty($regs[5]) ? date_pad($regs[5]) : '00') . 399 ':' . (!empty($regs[6]) ? date_pad($regs[6]) : '00') . 400 ':' . (!empty($regs[7]) ? date_pad($regs[7]) : '00'); // Time 401 } 402 } 403 404 // Use timezone if explicitly declared 405 if (isset($properties['TZID'])) { 406 $tz = $properties['TZID']; 407 // Fix commonly used alternatives like US-Eastern which should be US/Eastern. 408 $tz = str_replace('-', '/', $tz); 409 // Unset invalid timezone names. 410 module_load_include('install', 'date_timezone'); 411 $tz = _date_timezone_replacement($tz); 412 if (!date_timezone_is_valid($tz)) { 413 $tz = ''; 414 } 415 } 416 // If declared as UTC with terminating 'Z', use that timezone 417 else if (strpos($data, 'Z') !== false) { 418 $tz = 'UTC'; 419 } 420 // Otherwise this date is floating... 421 else { 422 $tz = ''; 423 } 424 425 $items['datetime'] = $datetime; 426 $items['all_day'] = $has_time ? false : true; 427 $items['tz'] = $tz; 428 return $items; 429 } 430 431 /** 432 * Parse an ical repeat rule. 433 * 434 * @return array 435 * Array in the form of PROPERTY => array(VALUES) 436 * PROPERTIES include FREQ, INTERVAL, COUNT, BYDAY, BYMONTH, BYYEAR, UNTIL 437 */ 438 function date_ical_parse_rrule($field, $data) { 439 $data = preg_replace("/RRULE.*:/", '', $data); 440 $items = array('DATA' => $data); 441 $rrule = explode(';', $data); 442 foreach ($rrule as $key => $value) { 443 $param = explode('=', $value); 444 // Must be some kind of invalid data. 445 if (count($param) != 2) { 446 continue; 447 } 448 if ($param[0] == 'UNTIL') { 449 $values = date_ical_parse_date('', $param[1]); 450 } 451 else { 452 $values = explode(',', $param[1]); 453 } 454 // Different treatment for items that can have multiple values and those that cannot. 455 if (in_array($param[0], array('FREQ', 'INTERVAL', 'COUNT', 'WKST'))) { 456 $items[$param[0]] = $param[1]; 457 } 458 else { 459 $items[$param[0]] = $values; 460 } 461 } 462 return $items; 463 } 464 465 /** 466 * Parse exception dates (can be multiple values). 467 * 468 * @return array 469 * an array of date value arrays. 470 */ 471 function date_ical_parse_exceptions($field, $data) { 472 $data = str_replace($field.':', '', $data); 473 $items = array('DATA' => $data); 474 $ex_dates = explode(',', $data); 475 foreach ($ex_dates as $ex_date) { 476 $items[] = date_ical_parse_date('', $ex_date); 477 } 478 return $items; 479 } 480 481 /** 482 * Parse the duration of the event. 483 * Example: 484 * DURATION:PT1H30M 485 * DURATION:P1Y2M 486 * 487 * @param $subgroup 488 * array of other values in the vevent so we can check for DTSTART 489 */ 490 function date_ical_parse_duration(&$subgroup, $field = 'DURATION') { 491 $items = $subgroup[$field]; 492 $data = $items['DATA']; 493 preg_match('/^P(\d{1,4}[Y])?(\d{1,2}[M])?(\d{1,2}[W])?(\d{1,2}[D])?([T]{0,1})?(\d{1,2}[H])?(\d{1,2}[M])?(\d{1,2}[S])?/', $data, $duration); 494 $items['year'] = isset($duration[1]) ? str_replace('Y', '', $duration[1]) : ''; 495 $items['month'] = isset($duration[2]) ?str_replace('M', '', $duration[2]) : ''; 496 $items['week'] = isset($duration[3]) ?str_replace('W', '', $duration[3]) : ''; 497 $items['day'] = isset($duration[4]) ?str_replace('D', '', $duration[4]) : ''; 498 $items['hour'] = isset($duration[6]) ?str_replace('H', '', $duration[6]) : ''; 499 $items['minute'] = isset($duration[7]) ?str_replace('M', '', $duration[7]) : ''; 500 $items['second'] = isset($duration[8]) ?str_replace('S', '', $duration[8]) : ''; 501 $start_date = array_key_exists('DTSTART', $subgroup) ? $subgroup['DTSTART']['datetime'] : date_format(date_now(), DATE_FORMAT_ISO); 502 $timezone = array_key_exists('DTSTART', $subgroup) ? $subgroup['DTSTART']['tz'] : variable_get('date_default_timezone_name', NULL); 503 if (empty($timezone)) { 504 $timezone = 'UTC'; 505 } 506 $date = date_make_date($start_date, $timezone); 507 $date2 = drupal_clone($date); 508 foreach ($items as $item => $count) { 509 if ($count > 0) { 510 date_modify($date2, '+'. $count .' '. $item); 511 } 512 } 513 $format = isset($subgroup['DTSTART']['type']) && $subgroup['DTSTART']['type'] == 'DATE' ? 'Y-m-d' : 'Y-m-d H:i:s'; 514 $subgroup['DTEND'] = array( 515 'datetime' => date_format($date2, DATE_FORMAT_DATETIME), 516 'all_day' => isset($subgroup['DTSTART']['all_day']) ? $subgroup['DTSTART']['all_day'] : 0, 517 'tz' => $timezone, 518 ); 519 $duration = date_format($date2, 'U') - date_format($date, 'U'); 520 $subgroup['DURATION'] = array('DATA' => $data, 'DURATION' => $duration); 521 } 522 523 /** 524 * Parse and clean up ical text elements. 525 */ 526 function date_ical_parse_text($field, $data) { 527 if (strstr($field, 'QUOTED-PRINTABLE')) { 528 $data = quoted_printable_decode($data); 529 } 530 // Strip line breaks within element 531 $data = str_replace(array("\r\n ", "\n ", "\r "), '', $data); 532 // Put in line breaks where encoded 533 $data = str_replace(array("\\n", "\\N"), "\n", $data); 534 // Remove other escaping 535 $data = stripslashes($data); 536 return $data; 537 } 538 539 /** 540 * Parse location elements. 541 * 542 * Catch situations like the upcoming.org feed that uses 543 * LOCATION;VENUE-UID="http://upcoming.yahoo.com/venue/104/":111 First Street... 544 * or more normal LOCATION;UID=123:111 First Street... 545 * Upcoming feed would have been improperly broken on the ':' in http:// 546 * so we paste the $field and $data back together first. 547 * 548 * Use non-greedy check for ':' in case there are more of them in the address. 549 */ 550 function date_ical_parse_location($field, $data) { 551 if (preg_match('/UID=[?"](.+)[?"][*?:](.+)/', $field .':'. $data, $matches)) { 552 $location = array(); 553 $location['UID'] = $matches[1]; 554 $location['DESCRIPTION'] = stripslashes($matches[2]); 555 return $location; 556 } 557 else { 558 // Remove other escaping 559 $location = stripslashes($data); 560 return $location; 561 } 562 } 563 564 /** 565 * Return a date object for the ical date, adjusted to its local timezone. 566 * 567 * @param $ical_date 568 * an array of ical date information created in the ical import. 569 * @param $to_tz 570 * the timezone to convert the date's value to. 571 * @return object 572 * a timezone-adjusted date object 573 */ 574 function date_ical_date($ical_date, $to_tz = FALSE) { 575 // If the ical date has no timezone, must assume it is stateless 576 // so treat it as a local date. 577 if (empty($ical_date['datetime'])) { 578 return NULL; 579 } 580 elseif (empty($ical_date['tz'])) { 581 $from_tz = date_default_timezone_name(); 582 } 583 else { 584 $from_tz = $ical_date['tz']; 585 } 586 $date = date_make_date($ical_date['datetime'], $from_tz); 587 if ($to_tz && $ical_date['tz'] != '' && $to_tz != $ical_date['tz']) { 588 date_timezone_set($date, timezone_open($to_tz)); 589 } 590 return $date; 591 } 592 593 /** 594 * Escape #text elements for safe iCal use 595 * 596 * @param $text 597 * Text to escape 598 * 599 * @return 600 * Escaped text 601 * 602 */ 603 function date_ical_escape_text($text) { 604 $text = drupal_html_to_text($text); 605 $text = trim($text); 606 // TODO Per #38130 the iCal specs don't want : and " escaped 607 // but there was some reason for adding this in. Need to watch 608 // this and see if anything breaks. 609 //$text = str_replace('"', '\"', $text); 610 //$text = str_replace(":", "\:", $text); 611 $text = preg_replace("/\\\b/","\\\\", $text); 612 $text = str_replace(",", "\,", $text); 613 $text = str_replace(";", "\;", $text); 614 $text = str_replace("\n", "\\n ", $text); 615 return trim($text); 616 } 617 618 /** 619 * Build an iCal RULE from $form_values. 620 * 621 * @param $form_values 622 * an array constructed like the one created by date_ical_parse_rrule() 623 * 624 * [RRULE] => Array ( 625 * [FREQ] => Array ( 626 * [0] => MONTHLY 627 * ) 628 * [BYDAY] => Array ( 629 * [0] => 1SU 630 * [1] => -1SU 631 * ) 632 * [UNTIL] => Array ( 633 * [datetime] => 1997-21-31 09:00:00 634 * [all_day] => 0 635 * [tz] => US/Eastern 636 * ) 637 * ) 638 * [EXDATE] => Array ( 639 * [0] = Array ( 640 * [datetime] => 1997-09-21 09:00:00 641 * [all_day] => 0 642 * [tz] => US/Eastern 643 * ) 644 * [1] = Array ( 645 * [datetime] => 1997-10-05 09:00:00 646 * [all_day] => 0 647 * [tz] => US/Eastern 648 * ) 649 * ) 650 * [RDATE] => Array ( 651 * [0] = Array ( 652 * [datetime] => 1997-09-21 09:00:00 653 * [all_day] => 0 654 * [tz] => US/Eastern 655 * ) 656 * [1] = Array ( 657 * [datetime] => 1997-10-05 09:00:00 658 * [all_day] => 0 659 * [tz] => US/Eastern 660 * ) 661 * ) 662 * 663 */ 664 function date_api_ical_build_rrule($form_values) { 665 $RRULE = ''; 666 if (empty($form_values) || !is_array($form_values)) { 667 return $RRULE; 668 } 669 //grab the RRULE data and put them into iCal RRULE format 670 $RRULE .= 'RRULE:FREQ='. (!array_key_exists('FREQ', $form_values) ? 'DAILY' : $form_values['FREQ']); 671 $RRULE .= ';INTERVAL='. (!array_key_exists('INTERVAL', $form_values) ? 1 : $form_values['INTERVAL']); 672 673 // Unset the empty 'All' values. 674 if (array_key_exists('BYDAY', $form_values)) unset($form_values['BYDAY']['']); 675 if (array_key_exists('BYMONTH', $form_values)) unset($form_values['BYMONTH']['']); 676 if (array_key_exists('BYMONTHDAY', $form_values)) unset($form_values['BYMONTHDAY']['']); 677 678 if (array_key_exists('BYDAY', $form_values) && $BYDAY = implode(",", $form_values['BYDAY'])) { 679 $RRULE .= ';BYDAY='. $BYDAY; 680 } 681 if (array_key_exists('BYMONTH', $form_values) && $BYMONTH = implode(",", $form_values['BYMONTH'])) { 682 $RRULE .= ';BYMONTH='. $BYMONTH; 683 } 684 if (array_key_exists('BYMONTHDAY', $form_values) && $BYMONTHDAY = implode(",", $form_values['BYMONTHDAY'])) { 685 $RRULE .= ';BYMONTHDAY='. $BYMONTHDAY; 686 } 687 // The UNTIL date is supposed to always be expressed in UTC. 688 if (array_key_exists('UNTIL', $form_values) && array_key_exists('datetime', $form_values['UNTIL']) && !empty($form_values['UNTIL']['datetime'])) { 689 $until = date_ical_date($form_values['UNTIL'], 'UTC'); 690 $RRULE .= ';UNTIL='. date_format($until, DATE_FORMAT_ICAL) .'Z'; 691 } 692 // Our form doesn't allow a value for COUNT, but it may be needed by 693 // modules using the API, so add it to the rule. 694 if (array_key_exists('COUNT', $form_values)) { 695 $RRULE .= ';COUNT='. $form_values['COUNT']; 696 } 697 698 // iCal rules presume the week starts on Monday unless otherwise specified, 699 // so we'll specify it. 700 if (array_key_exists('WKST', $form_values)) { 701 $RRULE .= ';WKST='. $form_values['WKST']; 702 } 703 else { 704 $RRULE .= ';WKST='. date_repeat_dow2day(variable_get('date_first_day', 1)); 705 } 706 707 // Exceptions dates go last, on their own line. 708 if (isset($form_values['EXDATE']) && is_array($form_values['EXDATE'])) { 709 $ex_dates = array(); 710 foreach ($form_values['EXDATE'] as $value) { 711 $ex_date = date_convert($value['datetime'], DATE_DATETIME, DATE_ICAL); 712 if (!empty($ex_date)) { 713 $ex_dates[] = $ex_date; 714 } 715 } 716 if (!empty($ex_dates)) { 717 sort($ex_dates); 718 $RRULE .= chr(13) . chr(10) .'EXDATE:'. implode(',', $ex_dates); 719 } 720 } 721 elseif (!empty($form_values['EXDATE'])) { 722 $RRULE .= chr(13) . chr(10) .'EXDATE:'. $form_values['EXDATE']; 723 } 724 725 // Exceptions dates go last, on their own line. 726 if (isset($form_values['RDATE']) && is_array($form_values['RDATE'])) { 727 $ex_dates = array(); 728 foreach ($form_values['RDATE'] as $value) { 729 $ex_date = date_convert($value['datetime'], DATE_DATETIME, DATE_ICAL); 730 if (!empty($ex_date)) { 731 $ex_dates[] = $ex_date; 732 } 733 } 734 if (!empty($ex_dates)) { 735 sort($ex_dates); 736 $RRULE .= chr(13) . chr(10) .'RDATE:'. implode(',', $ex_dates); 737 } 738 } 739 elseif (!empty($form_values['RDATE'])) { 740 $RRULE .= chr(13) . chr(10) .'RDATE:'. $form_values['RDATE']; 741 } 742 743 return $RRULE; 744 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |