Projet

Général

Profil

Paste
Télécharger (23,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / date_ical / libraries / ParserVcalendar.inc @ 651307cd

1 be880f98 Florent Torregrosa
<?php
2
/**
3
 * @file
4
 * Defines a class that parses iCalcreator vcalendar objects into
5
 * Feeds-compatible data arrays.
6
 */
7
8
class ParserVcalendar {
9
  /**
10
   * Variables used for parsing.
11
   */
12
  protected $calendar;
13
  protected $source;
14
  protected $fetcherResult;
15
  protected $config;
16
  protected $timezones = array();
17
  protected $xtimezone;
18 204e4d33 Assos Assos
19 55670b15 Assos Assos
  /**
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 204e4d33 Assos Assos
30 be880f98 Florent Torregrosa
  /**
31
   * Variables used for batch processing.
32
   */
33
  protected $totalComponents = 0;
34
  protected $lastComponentParsed = 0;
35 204e4d33 Assos Assos
36 ca0757b9 Assos Assos
  /**
37
   * This is the list of iCal properties which are allowed to have more than
38
   * one entry in a single VEVENT. If we ever support parsing more than just
39
   * the first one, this listing will be useful.
40
   */
41
  protected $multi_entry_properties = array(
42
    'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION',
43
    'EXDATE', 'EXRULE', 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES',
44
    'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP'
45
  );
46 204e4d33 Assos Assos
47 be880f98 Florent Torregrosa
  /**
48
   * Constructor.
49
   */
50
  public function __construct($calendar, $source, $fetcher_result, $config) {
51
    $this->calendar = $calendar;
52
    $this->source = $source;
53 55670b15 Assos Assos
    $this->mapping_sources = feeds_importer($source->id)->parser->getMappingSources();
54 be880f98 Florent Torregrosa
    $this->fetcherResult = $fetcher_result;
55
    $this->config = $config;
56
  }
57 204e4d33 Assos Assos
58 be880f98 Florent Torregrosa
  /**
59
   * Parses the vcalendar object into an array of event data arrays.
60
   *
61
   * @param int $offset
62
   *   Specifies which section of the feed to start parsing at.
63
   *
64
   * @param int $limit
65
   *   Specifies how many components to parse on this run.
66
   *
67
   * @return array
68 55670b15 Assos Assos
   *   An array of parsed event data keyed by our mapping source property keys.
69 be880f98 Florent Torregrosa
   */
70
  public function parse($offset, $limit) {
71
    // Sometimes, the feed will set a timezone for every event in the calendar
72
    // using the non-standard X-WR-TIMEZONE property. Date iCal uses this
73
    // timezone only if the date property is not in UTC and has no TZID.
74
    $xtimezone = $this->calendar->getProperty('X-WR-TIMEZONE');
75
    if (!empty($xtimezone[1])) {
76
      // Allow modules to alter the timezone string before it gets converted
77
      // into a DateTimeZone.
78
      $context = array(
79
        'property_key' => NULL,
80
        'calendar_component' => NULL,
81
        'calendar' => $this->calendar,
82
        'feeeds_source' => $this->source,
83
        'feeds_fetcher_result' => $this->fetcherResult,
84
      );
85
      drupal_alter('date_ical_import_timezone', $xtimezone[1], $context);
86
      $this->xtimezone = $this->_tzid_to_datetimezone($xtimezone[1]);
87
    }
88
89
    // Collect the timezones into an array, for easier access.
90
    while ($component = $this->calendar->getComponent('VTIMEZONE')) {
91
      $this->timezones[] = $component;
92
    }
93 204e4d33 Assos Assos
94 c7b88c87 Assos Assos
    // This context array is used by date_ical_import_component_alter() and
95 55670b15 Assos Assos
    // date_ical_import_parsed_data_alter().
96
    $context2 = array(
97
      'calendar' => $this->calendar,
98
      'source' => $this->source,
99
      'fetcher_result' => $this->fetcherResult,
100
    );
101 204e4d33 Assos Assos
102 be880f98 Florent Torregrosa
    // Collect each component, so we can batch them properly in the next loop.
103
    $raw_components = array();
104
    $types = array('VEVENT', 'VTODO', 'VJOURNAL', 'VFREEBUSY', 'VALARM');
105
    foreach ($types as $type) {
106
      while ($vcalendar_component = $this->calendar->getComponent($type)) {
107
        // Allow modules to alter the vcalendar component before we parse it
108
        // into a Feeds-compatible data array.
109 55670b15 Assos Assos
        drupal_alter('date_ical_import_component', $vcalendar_component, $context2);
110 be880f98 Florent Torregrosa
        $raw_components[] = $vcalendar_component;
111
      }
112
    }
113 204e4d33 Assos Assos
114 be880f98 Florent Torregrosa
    // Store this for use by DateiCalFeedsParser's batch processing code.
115
    $this->totalComponents = count($raw_components);
116 204e4d33 Assos Assos
117 be880f98 Florent Torregrosa
    // Parse each raw component in the current batch into a Feeds-compatible
118
    // event data array.
119
    $events = array();
120
    $batch = array_slice($raw_components, $offset, $limit, TRUE);
121
    foreach ($batch as $ndx => $raw_component) {
122 55670b15 Assos Assos
      $this->parsed_data = array();
123
      foreach ($this->mapping_sources as $property_key => $data) {
124
        $handler = NULL;
125
        if (isset($data['date_ical_parse_handler'])) {
126
          $handler = $data['date_ical_parse_handler'];
127
        }
128
        else {
129
          // This is not one of our sources, so if we don't recognize and
130
          // support it, we'll have to pass a warning to the user.
131
          if ($property_key == 'geofield') {
132
            $handler = 'parseGeofield';
133
          }
134
          else {
135
            // We can safely ignore certain sources.
136
            $known_unknowns = array(
137 ca0757b9 Assos Assos
              'Blank source 1', // "Black Source 1" is from Feeds Tamper.
138
              'parent:nid', // Defined in FeedsParser
139
              'parent:uid', // Defined in FeedsParser
140 55670b15 Assos Assos
            );
141
            if (!in_array($property_key, $known_unknowns)) {
142
              // Only warn the user if this mapping source is in use.
143
              foreach ($this->source->importer->processor->config['mappings'] as $mapping) {
144
                if ($mapping['source'] == $property_key) {
145
                  drupal_set_message(t('Date iCal does not recognize the "@name" Mapping Source, and must skip it.', array('@name' => $data['name'])), 'warning', FALSE);
146
                  break;
147
                }
148
              }
149
            }
150
          }
151
        }
152
        if ($handler) {
153
          $this->parsed_data[$property_key] = $this->$handler($property_key, $raw_component);
154
        }
155 204e4d33 Assos Assos
156 55670b15 Assos Assos
        if ($property_key == 'geofield' && !empty($this->parsed_data['geofield'])) {
157
          // To make our data readable by geofield_feeds_combined_source(), we
158
          // need to put it into the format output by Simplepie 1.3.
159
          $this->parsed_data['location_latitude'] = array($this->parsed_data['geofield']['lat']);
160
          $this->parsed_data['location_longitude'] = array($this->parsed_data['geofield']['lon']);
161
        }
162
      }
163 204e4d33 Assos Assos
164 55670b15 Assos Assos
      // Allow modules to alter the final parsed data before we send it to the
165
      // Feeds processor.
166
      drupal_alter('date_ical_import_post_parse', $this->parsed_data, $context2);
167 204e4d33 Assos Assos
168 55670b15 Assos Assos
      // Skip this event if it's earlier than the user's specified skip time.
169
      if (!$this->_skip_current_event()) {
170
        $events[] = $this->parsed_data;
171 be880f98 Florent Torregrosa
      }
172
      // The indices of the original $raw_components array are preserved in
173
      // $batch, so using the $ndx value here lets us communicate our progress
174
      // through the full collection of components.
175
      $this->lastComponentParsed = $ndx;
176
    }
177 204e4d33 Assos Assos
178 be880f98 Florent Torregrosa
    return $events;
179
  }
180 204e4d33 Assos Assos
181 be880f98 Florent Torregrosa
  /**
182
   * Getter for the protected totalComponents property.
183
   */
184
  public function getTotalComponents() {
185
    return $this->totalComponents;
186
  }
187 204e4d33 Assos Assos
188 be880f98 Florent Torregrosa
  /**
189
   * Getter for the protected lastComponentParsed property.
190
   */
191
  public function getLastComponentParsed() {
192
    return $this->lastComponentParsed;
193
  }
194 204e4d33 Assos Assos
195 ca0757b9 Assos Assos
  /**
196
   * Handler that parses GEO fields.
197
   *
198
   * @return array
199
   *   The latitude and longitude values, keyed by 'lat' and 'lon'.
200
   */
201
  public function parseGeofield($property_key, $vcalendar_component) {
202
    $geo = array();
203
    if (!empty($vcalendar_component->geo['value'])) {
204
      $geo['lat'] = $vcalendar_component->geo['value']['latitude'];
205
      $geo['lon'] = $vcalendar_component->geo['value']['longitude'];
206
    }
207
    return $geo;
208
  }
209 204e4d33 Assos Assos
210 be880f98 Florent Torregrosa
  /**
211 55670b15 Assos Assos
   * Handler that parses text fields.
212 be880f98 Florent Torregrosa
   *
213
   * @return string
214
   *   The parsed text property.
215
   */
216
  public function parseTextProperty($property_key, $vcalendar_component) {
217
    $text = $vcalendar_component->getProperty($property_key);
218 ca0757b9 Assos Assos
    // In case someone writes a hook that adds a source for a multi-entry
219
    // property and a parameter of that same property, we need to force
220
    // iCalcreator to assume it has not accessed that property, yet.
221
    // TODO: This is really just a hack. If/when multi-entry properties
222
    // become supported, this will need to be redesigned.
223
    if (in_array($property_key, $this->multi_entry_properties)) {
224
      unset($vcalendar_component->propix[$property_key]);
225
    }
226 204e4d33 Assos Assos
227 d756b39a Assos Assos
    if (is_array($text) && isset($vcalendar_component->xprop[$property_key])) {
228
      # This is an X-PROPERTY, which iCalcreator returns as an array like array('X-PROP-NAME', value).
229
      # We only care about the value, though.
230
      $text = $text[1];
231
    }
232
233 be880f98 Florent Torregrosa
    if ($text === FALSE) {
234 c7b88c87 Assos Assos
      if ($property_key != 'SUMMARY') {
235
        return NULL;
236
      }
237
      else {
238 be880f98 Florent Torregrosa
        $uid = $vcalendar_component->getProperty('UID');
239 c7b88c87 Assos Assos
        if ($vcalendar_component->objName == 'vfreebusy') {
240
          // FREEBUSY elements can't have SUMMARY, but they can have COMMENT.
241
          // So if the feed has been configured to ask for SUMMARY, use COMMENT
242
          // instead. If COMMENT is also missing, we can't import.
243
          $text = $vcalendar_component->getProperty('COMMENT');
244
          if ($text === FALSE) {
245
            throw new DateIcalParseException(t('The VFREEBUSY component with UID %uid is invalid because it has no COMMENT.
246
              Nodes require a title, and since VFREEBUSY components can\'t have SUMMARY, Date iCal pulls that title from the COMMENT.',
247
              array('%uid' => $uid))
248
            );
249
          }
250
        }
251
        else {
252
          // Non-VFREEBUSY components must have a SUMMARY.
253
          throw new DateIcalParseException(t('The component with UID %uid is invalid because it has no SUMMARY (nodes require a title).',
254
            array('%uid' => $uid))
255
          );
256
        }
257 be880f98 Florent Torregrosa
      }
258
    }
259
    // Convert literal \n and \N into newline characters.
260
    $text = str_replace(array('\n', '\N'), "\n", $text);
261
    return $text;
262
  }
263 204e4d33 Assos Assos
264 55670b15 Assos Assos
  /**
265
   * Handler that parses field parameters.
266 be880f98 Florent Torregrosa
   *
267
   * @return string
268
   *   The parsed field parameter.
269
   */
270
  public function parsePropertyParameter($property_key, $vcalendar_component) {
271
    list($key, $attr) = explode(':', $property_key);
272
    $property = $vcalendar_component->getProperty($key, FALSE, TRUE);
273 ca0757b9 Assos Assos
    // See parseTextProperty() for why this is here.
274
    if (in_array($property_key, $this->multi_entry_properties)) {
275
      unset($vcalendar_component->propix[$property_key]);
276
    }
277 204e4d33 Assos Assos
278 be880f98 Florent Torregrosa
    if ($property === FALSE) {
279
      // If the component doesn't have this property, return NULL.
280
      return NULL;
281
    }
282 55670b15 Assos Assos
    $param = isset($property['params'][$attr]) ? $property['params'][$attr] : '';
283
    return $param;
284 be880f98 Florent Torregrosa
  }
285 204e4d33 Assos Assos
286 be880f98 Florent Torregrosa
  /**
287 55670b15 Assos Assos
   * Handler that parses DATE-TIME and DATE fields.
288 be880f98 Florent Torregrosa
   *
289
   * @return FeedsDateTime
290
   *   The parsed datetime object.
291
   */
292
  public function parseDateTimeProperty($property_key, $vcalendar_component) {
293
    $property = $vcalendar_component->getProperty($property_key, FALSE, TRUE);
294
    // Gather all the other date properties, so we can work with them later.
295
    $duration = $vcalendar_component->getProperty('DURATION', FALSE, TRUE);
296
    $dtstart = $vcalendar_component->getProperty('DTSTART', FALSE, TRUE);
297
    $uid = $vcalendar_component->getProperty('UID');
298 204e4d33 Assos Assos
299 be880f98 Florent Torregrosa
    // DATE-type properties are treated as All Day events which can span over
300
    // multiple days.
301
    // The Date module's All Day event handling was never finalized
302
    // (http://drupal.org/node/874322), which requires us to do some some
303
    // special coddling later.
304
    $is_all_day = (isset($property['params']['VALUE']) && $property['params']['VALUE'] == 'DATE');
305 204e4d33 Assos Assos
306 be880f98 Florent Torregrosa
    // Cover various conditions in which either DTSTART or DTEND are not set.
307
    if ($property === FALSE) {
308
      // When DTEND isn't defined, we may need to emulate it.
309
      if ($property_key == 'DTEND') {
310
        // Unset DTENDs need to emulate the DATE type from DTSTART.
311
        $is_all_day = (isset($dtstart['params']['VALUE']) && $dtstart['params']['VALUE'] == 'DATE');
312 204e4d33 Assos Assos
313 be880f98 Florent Torregrosa
        if ($duration !== FALSE) {
314
          // If a DURATION is defined, emulate DTEND as DTSTART + DURATION.
315
          $property = array(
316
            'value' => iCalUtilityFunctions::_duration2date($dtstart['value'], $duration['value']),
317
            'params' => $dtstart['params'],
318
          );
319
        }
320
        elseif ($is_all_day) {
321
          // If this is an all-day event with no end or duration, treat this
322
          // as a single-day event by emulating DTEND as 1 day after DTSTART.
323
          $property = $dtstart;
324 55670b15 Assos Assos
          $property['value'] = iCalUtilityFunctions::_duration2date($property['value'], array('day' => 1));
325 be880f98 Florent Torregrosa
        }
326
        else {
327
          // This event has no end date.
328
          return NULL;
329
        }
330
      }
331
      elseif ($property_key == 'DTSTART') {
332
        // DTSTART can only be legally unset in non-VEVENT components.
333
        if ($vcalendar_component->objName == 'vevent') {
334
          throw new DateIcalParseException(t('Feed import failed! The VEVENT with UID %uid is invalid: it has no DTSTART.', array('%uid' => $uid)));
335
        }
336
        else {
337
          return NULL;
338
        }
339
      }
340
    }
341 204e4d33 Assos Assos
342 be880f98 Florent Torregrosa
    // When iCalcreator parses a UTC date (one that ends with Z) from an iCal
343
    // feed, it stores that 'Z' into the $property['value']['tz'] value.
344
    if (isset($property['value']['tz'])) {
345
      $property['params']['TZID'] = 'UTC';
346
    }
347 204e4d33 Assos Assos
348 be880f98 Florent Torregrosa
    if ($is_all_day) {
349
      if ($property_key == 'DTEND') {
350
        if ($dtstart === FALSE) {
351
          // This will almost certainly never happen, but the error message
352
          // would be incomprehensible without this check.
353
          throw new DateIcalParseException(t('Feed import failed! The event with UID %uid is invalid: it has a DTEND but no DTSTART!', array('%uid' => $uid)));
354
        }
355
356
        if (module_exists('date_all_day')) {
357
          // If the Date All Day module is installed, we need to rewind the
358
          // DTEND by one day, because of the problem with FeedsDateTime
359
          // mentioned below.
360
          $prev_day = iCalUtilityFunctions::_duration2date($property['value'], array('day' => -1));
361
          $property['value'] = $prev_day;
362
        }
363
      }
364 204e4d33 Assos Assos
365 be880f98 Florent Torregrosa
      // FeedsDateTime->setTimezone() ignores timezone changes made to dates
366
      // with no time element, which means we can't compensate for the Date
367
      // module's automatic timezone conversion when it writes to the DB. To
368
      // get around that, we must add 00:00:00 explicitly, even though this
369
      // causes other problems (see above and below).
370
      $date_string = sprintf('%d-%d-%d 00:00:00', $property['value']['year'], $property['value']['month'], $property['value']['day']);
371
      // Use the server's timezone rather than letting it default to UTC.
372
      // This will help ensure that the date value doesn't get messed up when
373
      // Date converts its timezone as the value is read from the database.
374
      // This is *essential* for All Day events, because Date stores them as
375
      // '2013-10-03 00:00:00' in the database, rather than doing the sensible
376
      // thing and storing them as '2013-10-03'.
377
      // NOTE TO MAINTAINERS:
378
      // This will not work properly if the site is configured to allow users
379
      // to set their own timezone. Unfortunately, there isn't anything that
380
      // Date iCal can do about that, as far as I can tell.
381
      $datetimezone = new DateTimeZone(date_default_timezone_get());
382
    }
383
    else {
384
      // This is a DATE-TIME property.
385
      $date_string = iCalUtilityFunctions::_format_date_time($property['value']);
386 204e4d33 Assos Assos
387 be880f98 Florent Torregrosa
      // Allow modules to alter the timezone string. This also allows for
388
      // setting a TZID when one was not originally set for this property.
389
      $tzid = isset($property['params']['TZID']) ? $property['params']['TZID'] : NULL;
390
      $context = array(
391
        'property_key' => $property_key,
392
        'calendar_component' => $vcalendar_component,
393
        'calendar' => $this->calendar,
394
        'feeeds_source' => $this->source,
395
        'feeds_fetcher_result' => $this->fetcherResult,
396
      );
397
      drupal_alter('date_ical_import_timezone', $tzid, $context);
398 204e4d33 Assos Assos
399 be880f98 Florent Torregrosa
      if (isset($tzid)) {
400
        $datetimezone = $this->_tzid_to_datetimezone($tzid);
401
      }
402
      elseif (isset($this->xtimezone)) {
403
        // No timezone was set on the parsed date property, so if a timezone
404
        // was detected for the entire iCal feed, use it.
405
        $datetimezone = $this->xtimezone;
406
      }
407
      else {
408
        $msg = t("No timezone was detected for one or more of the events in this feed, forcing Date iCal to use this server's timezone as a fallback.<br>
409
            To make timezone-less events use a different timezone, implement hook_date_ical_import_timezone_alter() in a custom module.");
410
        drupal_set_message($msg, 'status', FALSE);
411
        $this->source->log('parse', $msg, array(), WATCHDOG_NOTICE);
412
        $datetimezone = new DateTimeZone(date_default_timezone_get());
413
      }
414
    }
415 204e4d33 Assos Assos
416 55670b15 Assos Assos
    $datetime = new FeedsDateTime($date_string, $datetimezone);
417
    return $datetime;
418 be880f98 Florent Torregrosa
  }
419 204e4d33 Assos Assos
420 be880f98 Florent Torregrosa
  /**
421 55670b15 Assos Assos
   * Handler that parses multi-value fields, like the CATEGORIES component.
422 be880f98 Florent Torregrosa
   *
423
   * @return array
424
   *   An array of strings contaning the individual values.
425
   */
426
  public function parseMultivalueProperty($property_key, $vcalendar_component) {
427
    // Since we're not telling it to give us the params data, $property will
428
    // be either FALSE, a string, or an array of strings.
429
    $property = $vcalendar_component->getProperty($property_key);
430
    if (empty($property)) {
431
      // If this multi-value property is being mapped to a Taxonomy field,
432
      // Feeds will interpret anything besides empty array as an array of
433
      // empty values (e.g. array('')). This will create a term for that
434
      // empty value, rather than leaving the field blank.
435
      return array();
436
    }
437
    if (!is_array($property)) {
438
      $property = array($property);
439
    }
440
    return $property;
441
  }
442 204e4d33 Assos Assos
443 be880f98 Florent Torregrosa
  /**
444 55670b15 Assos Assos
   * Handler that parses RRULE, RDATE, EXRULE, and EXDATE together.
445 be880f98 Florent Torregrosa
   *
446
   * @return string
447 55670b15 Assos Assos
   *   The RRULE, RDATE, EXRULE, and EXDATE values concatinated with |.
448 be880f98 Florent Torregrosa
   */
449
  public function parseRepeatProperty($property_key, $vcalendar_component) {
450
    if ($vcalendar_component->getProperty($property_key) === FALSE) {
451
      return NULL;
452
    }
453 204e4d33 Assos Assos
454 55670b15 Assos Assos
    $uid = $vcalendar_component->getProperty('UID');
455
    $count = $this->config['indefinite_count'];
456 be880f98 Florent Torregrosa
    // Due to a few bugs and limitations with Date Repeat, we need to massage
457
    // the RRULE a bit.
458
    if (count($vcalendar_component->rrule) > 1) {
459 fa691419 Assos Assos
      // TODO: Is Date Repeat still limited to 1 RRULE with Feeds 2.0-beta1?
460 55670b15 Assos Assos
      $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.
461
        If your events need to have a complex repeat pattern, using RDATEs should help.';
462
      watchdog('date_ical', $msg, array('%uid' => $uid), 'warning');
463
      drupal_set_message('At least one of the events in this iCal feed has multiple RRULEs, but the Date Repeat module only supports one.
464
        Only the first RRULE in an event will be used.', 'warning', FALSE);
465 204e4d33 Assos Assos
466 be880f98 Florent Torregrosa
      // Date Repeat will get extremely confused if it's sent multiple RRULE
467
      // values, so we need to manually pare it down to only the first one.
468
      $vcalendar_component->rrule = array($vcalendar_component->rrule[0]);
469
    }
470
    foreach ($vcalendar_component->rrule as &$rrule_data) {
471
      // RRULEs must have an INTERVAL, or Date Repeat will throw errors.
472 fa691419 Assos Assos
      // TODO: Is this still true with Feeds 2.0-beta1?
473 be880f98 Florent Torregrosa
      if (!isset($rrule_data['value']['INTERVAL'])) {
474
        $rrule_data['value']['INTERVAL'] = '1';
475
      }
476 204e4d33 Assos Assos
477 ca0757b9 Assos Assos
      if ((!isset($rrule_data['value']['COUNT']) && !isset($rrule_data['value']['UNTIL']))) {
478 55670b15 Assos Assos
        $msg = "The event with UID %uid has an indefinitely repeating RRULE, which the Date Repeat module doesn't support.
479
          As a workaround, Date iCal set the repeat count to @count. This value can be customized in the iCal parser settings.";
480
        watchdog('date_ical', $msg, array('%uid' => $uid, '@count' => $count), WATCHDOG_WARNING);
481 d756b39a Assos Assos
        if (!empty($this->config['indefinite_message_display'])) {
482 ca0757b9 Assos Assos
          drupal_set_message(
483
            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>
484
              As a workaround, Date iCal set the repeat count to @count. This value can be customized in the iCal parser settings.",
485
            array('@count' => $count)),
486
            'warning',
487
            FALSE
488
          );
489
        }
490 d756b39a Assos Assos
        if (isset($this->config['indefinite_count'])) {
491
          $rrule_data['value']['COUNT'] = $this->config['indefinite_count'];
492
        }
493
        else {
494
          // Somehow, it's possible for the config values to have no setting, even though we've given them a default.
495
          // So make really sure we set the COUNT to *something*.
496
          $rrule_data['value']['COUNT'] = 52;
497
        }
498 be880f98 Florent Torregrosa
      }
499
    }
500 204e4d33 Assos Assos
501 d756b39a Assos Assos
    # Due to a bug in iCalcreator 2.20.2, any repeat property that's too long to fit on one line will be returned
502
    # verbatim, including the endline and the single-space indentation that starts the second line. Thus, we need to
503
    # remove those things ourselves.
504
    $rrule = trim(preg_replace('/\s/', '', $vcalendar_component->createRrule()));
505
    $rdate = trim(preg_replace('/\s/', '', $vcalendar_component->createRdate()));
506
    $exrule = trim(preg_replace('/\s/', '', $vcalendar_component->createExrule()));
507
    $exdate = trim(preg_replace('/\s/', '', $vcalendar_component->createExdate()));
508
509 be880f98 Florent Torregrosa
    return "$rrule|$rdate|$exrule|$exdate";
510
  }
511 204e4d33 Assos Assos
512 be880f98 Florent Torregrosa
  /**
513
   * Internal helper function for creating DateTimeZone objects.
514
   */
515
  protected function _tzid_to_datetimezone($tzid) {
516
    try {
517
      $datetimezone = new DateTimeZone($tzid);
518
    }
519
    catch (Exception $e) {
520
      // In case this is a Windows TZID, read the mapping file to try and
521
      // convert it to a real TZID.
522
      $zones = file_get_contents(drupal_get_path('module', 'date_ical') . '/libraries/windowsZones.json');
523
      $zones_assoc = json_decode($zones, TRUE);
524
      $windows_to_olson_map = array();
525
      foreach ($zones_assoc['supplemental']['windowsZones']['mapTimezones'] as $mapTimezone) {
526
        if ($mapTimezone['mapZone']['_other'] == $tzid) {
527 55670b15 Assos Assos
          // Parse out the space-separated TZIDs from $mapTimezone['mapZone']['_type'].
528 be880f98 Florent Torregrosa
          $tzids = preg_split('/\s/', $mapTimezone['mapZone']['_type']);
529
          try {
530
            // They all have the same UTC offset, so for our purposes we can
531
            // just take the first one.
532
            return new DateTimeZone($tzids[0]);
533
          }
534
          catch (Exception $e) {
535
            // If this one also fails, we're out of luck, so just fall through
536
            // to the regular error report code.
537
            break;
538
          }
539
        }
540
      }
541 204e4d33 Assos Assos
542 be880f98 Florent Torregrosa
      $tz_wiki = l(t('here'), 'http://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List');
543
      $help = l(t('README'), 'admin/help/date_ical', array('absolute' => TRUE));
544
      $msg = t(
545
          '"@tz" is not a valid timezone (see the TZ column !here), so Date iCal had to fall back to UTC (which is probably wrong!).<br>
546
          Please read the Date iCal !readme for instructions on how to fix this.',
547
          array('@tz' => $tzid, '!here' => $tz_wiki, '!readme' => $help)
548
      );
549
      $this->source->log('parse', $msg, array(), WATCHDOG_WARNING);
550
      drupal_set_message($msg, 'warning', FALSE);
551
      $datetimezone = new DateTimeZone('UTC');
552
    }
553
    return $datetimezone;
554
  }
555 204e4d33 Assos Assos
556 55670b15 Assos Assos
  /**
557
   * Internal helper function for skipping old events.
558
   */
559
  protected function _skip_current_event() {
560
    // Must use !isset() here, because 0 and NULL mean different things.
561
    if (!isset($this->config['skip_days'])) {
562
      return FALSE;
563
    }
564
    $compare_date = isset($this->parsed_data['DTEND']) ? $this->parsed_data['DTEND'] : $this->parsed_data['DTSTART'];
565
    $skip_date = new FeedsDateTime("today -{$this->config['skip_days']} days", $compare_date->getTimezone());
566
    $skip = ($skip_date > $compare_date);
567
    return $skip;
568
  }
569 be880f98 Florent Torregrosa
}