Project

General

Profile

Revision 55670b15

Added by Assos Assos over 10 years ago

Weekly update of contrib modules

View differences:

drupal7/sites/all/modules/date_ical/libraries/ParserVcalendar.inc
16 16
  protected $timezones = array();
17 17
  protected $xtimezone;
18 18
  
19
  /**
20
   * The parsed data for the component that's currently being processed.
21
   *
22
   * ParserVcalendar parses one component at a time. This array is stored as a
23
   * property so that each handler can tell what work the previous handlers
24
   * have already completed on the current component.
25
   *
26
   * @var array
27
   */
28
  protected $parsed_data = array();
29
  
19 30
  /**
20 31
   * Variables used for batch processing.
21 32
   */
......
28 39
  public function __construct($calendar, $source, $fetcher_result, $config) {
29 40
    $this->calendar = $calendar;
30 41
    $this->source = $source;
42
    $this->mapping_sources = feeds_importer($source->id)->parser->getMappingSources();
31 43
    $this->fetcherResult = $fetcher_result;
32 44
    $this->config = $config;
33 45
  }
34

  
46
  
35 47
  /**
36 48
   * Parses the vcalendar object into an array of event data arrays.
37 49
   *
......
42 54
   *   Specifies how many components to parse on this run.
43 55
   *
44 56
   * @return array
45
   *   An array of parsed event data keyed by the same strings as the array
46
   *   returned by DateiCalFeedsParser::getiCalMappingSources().
57
   *   An array of parsed event data keyed by our mapping source property keys.
47 58
   */
48 59
  public function parse($offset, $limit) {
49 60
    // Sometimes, the feed will set a timezone for every event in the calendar
......
69 80
      $this->timezones[] = $component;
70 81
    }
71 82
    
83
    // This content array is used by date_ical_import_component_alter() and
84
    // date_ical_import_parsed_data_alter().
85
    $context2 = array(
86
      'calendar' => $this->calendar,
87
      'source' => $this->source,
88
      'fetcher_result' => $this->fetcherResult,
89
    );
90
    
72 91
    // Collect each component, so we can batch them properly in the next loop.
73 92
    $raw_components = array();
74 93
    $types = array('VEVENT', 'VTODO', 'VJOURNAL', 'VFREEBUSY', 'VALARM');
......
76 95
      while ($vcalendar_component = $this->calendar->getComponent($type)) {
77 96
        // Allow modules to alter the vcalendar component before we parse it
78 97
        // into a Feeds-compatible data array.
79
        $context = array(
80
          'calendar' => $this->calendar,
81
          'source' => $this->source,
82
          'fetcher_result' => $this->fetcherResult,
83
        );
84
        drupal_alter('date_ical_import_component', $vcalendar_component, $context);
98
        drupal_alter('date_ical_import_component', $vcalendar_component, $context2);
85 99
        $raw_components[] = $vcalendar_component;
86 100
      }
87 101
    }
......
92 106
    // Parse each raw component in the current batch into a Feeds-compatible
93 107
    // event data array.
94 108
    $events = array();
95
    $sources = DateiCalFeedsParser::getiCalMappingSources();
96 109
    $batch = array_slice($raw_components, $offset, $limit, TRUE);
97 110
    foreach ($batch as $ndx => $raw_component) {
98
      $parsed_component = array();
99
      foreach ($sources as $property_key => $data) {
100
        $handler = $data['date_ical_parse_handler'];
101
        $parsed_component[$property_key] = $this->$handler($property_key, $raw_component);
111
      $this->parsed_data = array();
112
      foreach ($this->mapping_sources as $property_key => $data) {
113
        $handler = NULL;
114
        if (isset($data['date_ical_parse_handler'])) {
115
          $handler = $data['date_ical_parse_handler'];
116
        }
117
        else {
118
          // This is not one of our sources, so if we don't recognize and
119
          // support it, we'll have to pass a warning to the user.
120
          if ($property_key == 'geofield') {
121
            $handler = 'parseGeofield';
122
          }
123
          else {
124
            // We can safely ignore certain sources.
125
            $known_unknowns = array(
126
              // "Black Source 1" is from Feeds Tamper.
127
              'Blank source 1',
128
            );
129
            if (!in_array($property_key, $known_unknowns)) {
130
              // Only warn the user if this mapping source is in use.
131
              foreach ($this->source->importer->processor->config['mappings'] as $mapping) {
132
                if ($mapping['source'] == $property_key) {
133
                  drupal_set_message(t('Date iCal does not recognize the "@name" Mapping Source, and must skip it.', array('@name' => $data['name'])), 'warning', FALSE);
134
                  break;
135
                }
136
              }
137
            }
138
          }
139
        }
140
        if ($handler) {
141
          $this->parsed_data[$property_key] = $this->$handler($property_key, $raw_component);
142
        }
143
        
144
        if ($property_key == 'geofield' && !empty($this->parsed_data['geofield'])) {
145
          // To make our data readable by geofield_feeds_combined_source(), we
146
          // need to put it into the format output by Simplepie 1.3.
147
          $this->parsed_data['location_latitude'] = array($this->parsed_data['geofield']['lat']);
148
          $this->parsed_data['location_longitude'] = array($this->parsed_data['geofield']['lon']);
149
        }
150
      }
151
      
152
      // Allow modules to alter the final parsed data before we send it to the
153
      // Feeds processor.
154
      drupal_alter('date_ical_import_post_parse', $this->parsed_data, $context2);
155
      
156
      // Skip this event if it's earlier than the user's specified skip time.
157
      if (!$this->_skip_current_event()) {
158
        $events[] = $this->parsed_data;
102 159
      }
103
      $events[] = $parsed_component;
104 160
      // The indices of the original $raw_components array are preserved in
105 161
      // $batch, so using the $ndx value here lets us communicate our progress
106 162
      // through the full collection of components.
......
125 181
  }
126 182
  
127 183
  /**
128
   * Parses text fields.
184
   * Handler that parses text fields.
129 185
   *
130 186
   * @return string
131 187
   *   The parsed text property.
......
144 200
    $text = str_replace(array('\n', '\N'), "\n", $text);
145 201
    return $text;
146 202
  }
147

  
203
  
148 204
  /**
149
   * Parses field parameters.
205
   * Handler that parses GEO fields.
206
   *
207
   * @return array
208
   *   The latitude and longitude values, keyed by 'lat' and 'lon'.
209
   */
210
  public function parseGeofield($property_key, $vcalendar_component) {
211
    $geo = array();
212
    if (!empty($vcalendar_component->geo['value'])) {
213
      $geo['lat'] = $vcalendar_component->geo['value']['latitude'];
214
      $geo['lon'] = $vcalendar_component->geo['value']['longitude'];
215
    }
216
    return $geo;
217
  }
218
  
219
  /**
220
   * Handler that parses field parameters.
150 221
   *
151 222
   * @return string
152 223
   *   The parsed field parameter.
......
158 229
      // If the component doesn't have this property, return NULL.
159 230
      return NULL;
160 231
    }
161
    return isset($property['params'][$attr]) ? $property['params'][$attr] : '';
232
    $param = isset($property['params'][$attr]) ? $property['params'][$attr] : '';
233
    return $param;
162 234
  }
163

  
235
  
164 236
  /**
165
   * Parses DATE-TIME and DATE fields.
237
   * Handler that parses DATE-TIME and DATE fields.
166 238
   *
167 239
   * @return FeedsDateTime
168 240
   *   The parsed datetime object.
......
199 271
          // If this is an all-day event with no end or duration, treat this
200 272
          // as a single-day event by emulating DTEND as 1 day after DTSTART.
201 273
          $property = $dtstart;
202
          $property['value']['day'] = $dtstart['value']['day'] + 1;
274
          $property['value'] = iCalUtilityFunctions::_duration2date($property['value'], array('day' => 1));
203 275
        }
204 276
        else {
205 277
          // This event has no end date.
......
239 311
          $property['value'] = $prev_day;
240 312
        }
241 313
      }
242

  
314
      
243 315
      // FeedsDateTime->setTimezone() ignores timezone changes made to dates
244 316
      // with no time element, which means we can't compensate for the Date
245 317
      // module's automatic timezone conversion when it writes to the DB. To
......
290 362
        $datetimezone = new DateTimeZone(date_default_timezone_get());
291 363
      }
292 364
    }
293

  
294
    return new FeedsDateTime($date_string, $datetimezone);
365
    
366
    $datetime = new FeedsDateTime($date_string, $datetimezone);
367
    return $datetime;
295 368
  }
296

  
369
  
297 370
  /**
298
   * Parses multi-value fields, like the CATEGORIES component.
371
   * Handler that parses multi-value fields, like the CATEGORIES component.
299 372
   *
300 373
   * @return array
301 374
   *   An array of strings contaning the individual values.
......
316 389
    }
317 390
    return $property;
318 391
  }
319

  
392
  
320 393
  /**
321
   * Format RRULEs, which specify when and how often the event is repeated.
394
   * Handler that parses RRULE, RDATE, EXRULE, and EXDATE together.
322 395
   *
323 396
   * @return string
324
   *   An RRULE string, with EXDATE and RDATE values separated by \n.
325
   *   This is to make the RRULE compatible with date_repeat_split_rrule().
397
   *   The RRULE, RDATE, EXRULE, and EXDATE values concatinated with |.
326 398
   */
327 399
  public function parseRepeatProperty($property_key, $vcalendar_component) {
328 400
    if ($vcalendar_component->getProperty($property_key) === FALSE) {
329 401
      return NULL;
330 402
    }
331

  
403
    
404
    $uid = $vcalendar_component->getProperty('UID');
405
    $count = $this->config['indefinite_count'];
332 406
    // Due to a few bugs and limitations with Date Repeat, we need to massage
333 407
    // the RRULE a bit.
334 408
    if (count($vcalendar_component->rrule) > 1) {
335
      drupal_set_message(t('The event with UID %uid has multiple RRULEs, but the Date Repeat module only supports one. Only the first RRULE in the event will be used.<br>
336
          If your events need to have a complex repeat pattern, using RDATEs should help.',
337
        array('%uid' => $vcalendar_component->getProperty('UID'))), 'warning'
338
      );
409
      $msg = 'The event with UID %uid has multiple RRULEs, but the Date Repeat module only supports one. Only the first RRULE in the event will be used.
410
        If your events need to have a complex repeat pattern, using RDATEs should help.';
411
      watchdog('date_ical', $msg, array('%uid' => $uid), 'warning');
412
      drupal_set_message('At least one of the events in this iCal feed has multiple RRULEs, but the Date Repeat module only supports one.
413
        Only the first RRULE in an event will be used.', 'warning', FALSE);
414
      
339 415
      // Date Repeat will get extremely confused if it's sent multiple RRULE
340 416
      // values, so we need to manually pare it down to only the first one.
341 417
      $vcalendar_component->rrule = array($vcalendar_component->rrule[0]);
......
345 421
      if (!isset($rrule_data['value']['INTERVAL'])) {
346 422
        $rrule_data['value']['INTERVAL'] = '1';
347 423
      }
348

  
424
      
349 425
      if (!isset($rrule_data['value']['COUNT']) && !isset($rrule_data['value']['UNTIL'])) {
350
        drupal_set_message(t("The event with UID %uid has an indefinitely repeating RRULE, which the Date Repeat module doesn't support.<br>
426
        $msg = "The event with UID %uid has an indefinitely repeating RRULE, which the Date Repeat module doesn't support.
427
          As a workaround, Date iCal set the repeat count to @count. This value can be customized in the iCal parser settings.";
428
        watchdog('date_ical', $msg, array('%uid' => $uid, '@count' => $count), WATCHDOG_WARNING);
429
        drupal_set_message(
430
          t("At least one of the events in this iCal feed has an indefinitely repeating RRULE, which the Date Repeat module doesn't support.<br>
351 431
            As a workaround, Date iCal set the repeat count to @count. This value can be customized in the iCal parser settings.",
352
          array('%uid' => $vcalendar_component->getProperty('UID'), '@count' => $this->config['indefinite_count'])), 'warning'
432
          array('@count' => $count)),
433
          'warning',
434
          FALSE
353 435
        );
354 436
        $rrule_data['value']['COUNT'] = $this->config['indefinite_count'];
355 437
      }
356 438
    }
357

  
439
    
358 440
    $rrule = trim($vcalendar_component->createRrule());
359 441
    $rdate = trim($vcalendar_component->createRdate());
360 442
    $exrule = trim($vcalendar_component->createExrule());
361 443
    $exdate = trim($vcalendar_component->createExdate());
362 444
    return "$rrule|$rdate|$exrule|$exdate";
363 445
  }
364

  
446
  
365 447
  /**
366 448
   * Internal helper function for creating DateTimeZone objects.
367 449
   */
......
377 459
      $windows_to_olson_map = array();
378 460
      foreach ($zones_assoc['supplemental']['windowsZones']['mapTimezones'] as $mapTimezone) {
379 461
        if ($mapTimezone['mapZone']['_other'] == $tzid) {
380
          // $mapTimezone['mapZone']['_type'] is space-separated TZIDs.
462
          // Parse out the space-separated TZIDs from $mapTimezone['mapZone']['_type'].
381 463
          $tzids = preg_split('/\s/', $mapTimezone['mapZone']['_type']);
382 464
          try {
383 465
            // They all have the same UTC offset, so for our purposes we can
......
405 487
    }
406 488
    return $datetimezone;
407 489
  }
490
  
491
  /**
492
   * Internal helper function for skipping old events.
493
   */
494
  protected function _skip_current_event() {
495
    // Must use !isset() here, because 0 and NULL mean different things.
496
    if (!isset($this->config['skip_days'])) {
497
      return FALSE;
498
    }
499
    $compare_date = isset($this->parsed_data['DTEND']) ? $this->parsed_data['DTEND'] : $this->parsed_data['DTSTART'];
500
    $skip_date = new FeedsDateTime("today -{$this->config['skip_days']} days", $compare_date->getTimezone());
501
    $skip = ($skip_date > $compare_date);
502
    return $skip;
503
  }
408 504
}

Also available in: Unified diff