[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/date/ -> date_api_ical.inc (source)

   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  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7