DateiCalFeedsParser.inc 9.03 KB
Newer Older
Robert Rollins's avatar
Robert Rollins committed
1
2
<?php
/**
3
 * @file
4
 * DateiCalFeedsParser is Date iCal's Feeds parser plugin.
Robert Rollins's avatar
Robert Rollins committed
5
6
 */

7
8
9
10
11
12
13
14
15
16
17
18
19
class DateiCalFeedsParser extends FeedsParser {
  
  /**
   * Implements FeedsParser::getMappingSources().
   */
  public function getMappingSources() {
    return parent::getMappingSources() + self::getiCalMappingSources();
  }
  
  /**
   * Implements FeedsParser::parse().
   */
  public function parse(FeedsSource $source, FeedsFetcherResult $fetcher_result) {
20
21
22
    $library = libraries_load('iCalcreator');
    if (!$library['loaded']) {
      throw new DateIcalException(t('Unable to load the iCalcreator library. Please ensure that it is properly installed.'));
23
    }
24
    $state = $source->state(FEEDS_PARSE);
25
26
27
28
29
30
31
32
33
34
35
36
    
    // Read the iCal feed into memory.
    $ical_feed_contents = $fetcher_result->getRaw();
    
    // Parse the feed into an iCalcreator vcalendar object.
    $calendar = new vcalendar();
    if ($calendar->parse($ical_feed_contents) === FALSE) {
      $plugin = $source->importer->config['fetcher']['plugin_key'];
      $url = $source->config[$plugin]['source'];
      throw new DateIcalException(t('Parsing the data from %url failed. Please ensure that this URL leads to a valid iCal feed.', array('%url' => $url)));
    }
    
37
38
39
40
41
    // Total hack to get around iCalcreator's mistreatment of UID "0".
    if (empty($calendar->components[0]->uid) || empty($calendar->components[0]->uid['value'])) {
      $calendar->components[0]->uid = array('value' => 'zero', 'params' => NULL);
    }
    
42
43
44
45
46
    // Allow modules to alter the vcalendar object before we interpret it.
    $context = array(
      'source' => $source,
      'fetcher_result' => $fetcher_result,
    );
47
    drupal_alter('date_ical_import_vcalendar', $calendar, $context);
48
    
Robert Rollins's avatar
Robert Rollins committed
49
50
    // We've got a vcalendar object created from the feed data. Now we need to
    // convert that vcalendar into an array of Feeds-compatible data arrays.
51
    // ParserVcalendar->parse() does that.
52
    require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'date_ical') . '/libraries/ParserVcalendar.inc';
53
54
    $parser = new ParserVcalendar($calendar, $source, $fetcher_result, $source->getConfigFor($this));
    
55
    // Using the stored progress pointer (or 0 if it's not set),
56
57
58
59
60
61
62
63
    // determine which section of the feed to parse, then parse it.
    $offset = isset($state->pointer) ? $state->pointer : 0;
    $limit = $source->importer->getLimit();
    $rows = $parser->parse($offset, $limit);
    
    // Report progress.
    $state->total = $parser->getTotalComponents();
    // We need to add 1 to the index of the last parsed componenent so that
64
    // the subsequent batch starts on the first unparsed component.
65
66
    $state->pointer = $parser->getLastComponentParsed() + 1;
    $state->progress($state->total, $state->pointer);
67
    
68
    return new FeedsParserResult($rows);
69
70
  }
  
Robert Rollins's avatar
Robert Rollins committed
71
  /**
72
   * Defines the default configuration settings for an actual import.
Robert Rollins's avatar
Robert Rollins committed
73
74
75
76
   */
  public function sourceDefaults() {
    return array(
      'indefinite_count' => $this->config['indefinite_count'],
77
      'until_not_utc' => $this->config['until_not_utc'],
78
      'skip_days' => $this->config['skip_days'],
Robert Rollins's avatar
Robert Rollins committed
79
80
81
82
    );
  }
  
  /**
83
   * Defines the default settings shown on the configuration form.
Robert Rollins's avatar
Robert Rollins committed
84
85
86
87
   */
  public function configDefaults() {
    return array(
      'indefinite_count' => '52',
88
      'indefinite_message_display' => TRUE,
89
      'until_not_utc' => FALSE,
90
      'skip_days' => NULL,
Robert Rollins's avatar
Robert Rollins committed
91
92
93
94
    );
  }
  
  /**
95
   * Builds the configuration form.
Robert Rollins's avatar
Robert Rollins committed
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
   */
  public function configForm(&$form_state) {
    $form = array();
    $form['indefinite_count'] = array(
      '#title' => t('Indefinite COUNT'),
      '#type' => 'select',
      '#options' => array(
        '31' => '31',
        '52' => '52',
        '90' => '90',
        '365' => '365',
      ),
      '#description' => t('Indefinitely repeating events are not supported. The repeat count will instead be set to this number.'),
      '#default_value' => $this->config['indefinite_count'],
    );
111
112
113
114
115
116
    $form['indefinite_message_display'] = array(
      '#title' => t('Display message when RRULE is missing COUNT'),
      '#type' => 'checkbox',
      '#default_value' => $this->config['indefinite_message_display'],
      '#description' => t('Display a message when an indefinitely repeating rule is adjusted by the "Indefinite COUNT" setting above.'),
    );
117
118
119
    $form['until_not_utc'] = array(
      '#title' => t('RRULE UNTILs are not in UTC'),
      '#type' => 'checkbox',
120
      '#description' => t('Enable this setting if your reccuring events are not repeating the correct number of times. ' .
121
          'The iCal spec requires that the UNTIL value in an RRULE almost always be specified in UTC, but some iCal feed creators fail to follow that rule ' .
122
123
          '(the UNTIL values in those feeds\' RRULEs don\'t end is "Z"). This causes the UNTIL value to be off by several hours, ' .
          'which can cause the repeat calculator to miss or add repeats.'),
124
125
      '#default_value' => $this->config['until_not_utc'],
    );
126
127
128
129
130
131
132
133
    $form['skip_days'] = array(
      '#title' => t('Skip events more than X days old'),
      '#type' => 'textfield',
      '#size' => 5,
      '#description' => t('Set this value to any positive integer (or 0) to skip events which ended more than that many days before the import. ' .
        'Leave it blank to import all events.'),
      '#default_value' => $this->config['skip_days'],
    );
Robert Rollins's avatar
Robert Rollins committed
134
135
136
    return $form;
  }
  
137
138
139
140
141
142
143
144
145
146
147
148
  /**
   * Validation handler for configForm.
   */
  public function configFormValidate(&$source_config) {
    if (!preg_match('/^\d+$/', $source_config['skip_days']) && $source_config['skip_days'] !== '') {
      form_set_error('skip_days', 'You must enter a positive integer.');
    }
    if ($source_config['skip_days'] === '') {
      $source_config['skip_days'] = NULL;
    }
  }
  
Robert Rollins's avatar
Robert Rollins committed
149
150
151
  /**
   * Creates the list of mapping sources offered by DateiCalFeedsParser.
   */
152
153
154
155
  public static function getiCalMappingSources() {
    $sources = array();
    $sources['SUMMARY'] = array(
      'name' => t('Summary/Title'),
156
157
158
159
160
161
162
163
      'description' => t('The SUMMARY property. A short summary (usually the title) of the event.
        A title is required for every node, so you need to include this source and have it mapped to the node title, except under unusual circumstances.'
      ),
      'date_ical_parse_handler' => 'parseTextProperty',
    );
    $sources['COMMENT'] = array(
      'name' => t('Comment'),
      'description' => t('The COMMENT property. A text comment is allowed on most components.'),
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
      'date_ical_parse_handler' => 'parseTextProperty',
    );
    $sources['DESCRIPTION'] = array(
      'name' => t('Description'),
      'description' => t('The DESCRIPTION property. A more complete description of the event than what is provided by the Summary.'),
      'date_ical_parse_handler' => 'parseTextProperty',
    );
    $sources['DTSTART'] = array(
      'name' => t('Date: Start'),
      'description' => t('The DTSTART property. The start time of each event in the feed.'),
      'date_ical_parse_handler' => 'parseDateTimeProperty',
    );
    $sources['DTEND'] = array(
      'name' => t('Date: End'),
      'description' => t('THE DTEND or DURATION property. The end time (or duration) of each event in the feed.'),
      'date_ical_parse_handler' => 'parseDateTimeProperty',
    );
    $sources['RRULE'] = array(
182
      'name' => t('Date: Repeat Rule'),
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
      'description' => t('The RRULE property. Describes when and how often this event should repeat.
        The date field for the target node must be configured to support repeating dates, using the Date Repeat Field module (a submodule of Date).'),
      'date_ical_parse_handler' => 'parseRepeatProperty',
    );
    $sources['UID'] = array(
      'name' => 'UID',
      'description' => t('The UID property. Each event must have a UID if you wish for the import process to be able to update previously-imported nodes.
        If used, this field MUST be set to Unique.'),
      'date_ical_parse_handler' => 'parseTextProperty',
    );
    $sources['URL'] = array(
      'name' => 'URL',
      'description' => t('The URL property. Some feeds specify a URL for the event using this property.'),
      'date_ical_parse_handler' => 'parseTextProperty',
    );
    $sources['LOCATION'] = array(
      'name' => t('Location'),
      'description' => t('The LOCATION property. Can be mapped to a text field, or the title of a referenced node.'),
      'date_ical_parse_handler' => 'parseTextProperty',
    );
    $sources['LOCATION:ALTREP'] = array(
      'name' => t('Location: ALTREP'),
      'description' => t('The ALTREP value of the LOCATION property. Additional location information, usually a URL to a page with more info.'),
      'date_ical_parse_handler' => 'parsePropertyParameter',
    );
    $sources['CATEGORIES'] = array(
      'name' => t('Categories'),
      'description' => t('The CATEGORIES property. Catagories that describe the event, which can be imported into taxonomy terms.'),
      'date_ical_parse_handler' => 'parseMultivalueProperty',
    );
213
214
215
216
    
    // Allow other modules to add custom source fields.
    drupal_alter('date_ical_mapping_sources', $sources);
    
217
218
219
    return $sources;
  }
}