Projet

Général

Profil

Paste
Télécharger (15 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / date_ical / includes / date_ical_plugin_row_ical_entity.inc @ c7b88c87

1
<?php
2

    
3
/**
4
 * @file
5
 * Contains the iCal row style plugin.
6
 */
7

    
8
/**
9
 * A Views plugin which builds an iCal VEVENT from a single node.
10
 */
11
class date_ical_plugin_row_ical_entity extends views_plugin_row {
12
  
13
  // Basic properties that let the row style follow relationships.
14
  protected $base_table = 'node';
15
  protected $base_field = 'nid';
16
  
17
  // Stores the nodes loaded with pre_render.
18
  protected $entities = array();
19
  
20
  /**
21
   * Initialize the row plugin.
22
   */
23
  public function init(&$view, &$display, $options = NULL) {
24
    parent::init($view, $display, $options);
25
    $this->base_table = $view->base_table;
26
    $this->base_field = $view->base_field;
27
  }
28
  
29
  /**
30
   * Set up the options for the row plugin.
31
   */
32
  public function option_definition() {
33
    $options = parent::option_definition();
34
    $options['date_field'] = array('default' => array());
35
    $options['summary_field'] = array('default' => array());
36
    $options['location_field'] = array('default' => array());
37
    return $options;
38
  }
39
  
40
  /**
41
   * Build the form for setting the row plugin's options.
42
   */
43
  public function options_form(&$form, &$form_state) {
44
    parent::options_form($form, $form_state);
45
    
46
    // Build the select dropdown for the Date field that the user wants to use
47
    // to populate the date properties in VEVENTs.
48
    $data = date_views_fields($this->base_table);
49
    $options = array();
50
    foreach ($data['name'] as $item => $value) {
51
      // We only want to see one value for each field, so we need to
52
      // skip '_value2' and other columns.
53
      if ($item == $value['fromto'][0]) {
54
        $options[$item] = $value['label'];
55
      }
56
    }
57
    $form['date_field'] = array(
58
      '#type' => 'select',
59
      '#title' => t('Date field'),
60
      '#options' => $options,
61
      '#default_value' => $this->options['date_field'],
62
      '#description' => t('Please identify the field to use as the iCal date for each item in this view.
63
          Add a Date Filter or a Date Argument to the view to limit results to content in a specified date range.'),
64
      '#required' => TRUE,
65
    );
66
    $form['instructions'] = array(
67
      // The surrounding <div> ensures that the settings dialog expands.
68
      '#prefix' => '<div style="font-size: 90%">',
69
      '#suffix' => '</div>',
70
      '#markup' => t("Each item's Title will be the SUMMARY and the rendered iCal view mode will be the DESCRIPTION in the VEVENTs output by this View.
71
        <br>To change the iCal view mode, configure it on the 'Manage Display' page for each Content Type.
72
        Please note that all HTML will be stripped from the output, to comply with iCal standards."),
73
    );
74
    
75
    // Build the select dropdown for the text/node_reference field that the user
76
    // wants to use to (optionally) populate the SUMMARY.
77
    $summary_fields = date_ical_get_summary_fields($this->base_table);
78
    $summary_options = array('default_title' => t('- Default Title -'));
79
    foreach ($summary_fields['name'] as $item => $value) {
80
      $summary_options[$item] = $value['label'];
81
    }
82
    $form['summary_field'] = array(
83
      '#type' => 'select',
84
      '#title' => t('SUMMARY field'),
85
      '#options' => $summary_options,
86
      '#default_value' => $this->options['summary_field'],
87
      '#description' => t('You may optionally change the SUMMARY component for each event in the iCal output.
88
        Choose which text, taxonomy term reference or Node Reference field you would like to be output as the SUMMARY.
89
        If using a Node Reference, the Title of the referenced node will be used.'),
90
    );
91
    
92
    // Build the select dropdown for the text/node_reference field that the user
93
    // wants to use to (optionally) populate the LOCATION.
94
    $location_fields = date_ical_get_location_fields($this->base_table);
95
    $location_options = array('none' => t('- None -'));
96
    foreach ($location_fields['name'] as $item => $value) {
97
      $location_options[$item] = $value['label'];
98
    }
99
    $form['location_field'] = array(
100
      '#type' => 'select',
101
      '#title' => t('LOCATION field'),
102
      '#options' => $location_options,
103
      '#default_value' => $this->options['location_field'],
104
      '#description' => t('You may optionally include a LOCATION component for each event in the iCal output.
105
        Choose which text or Node Reference field you would like to be output as the LOCATION.
106
        If using a Node Reference, the Title of the referenced node will be used.'),
107
    );
108
  }
109
  
110
  /**
111
   * Preload the list of entities which will appear in the view.
112
   *
113
   * TODO: When the date is coming in through a relationship, the nid
114
   * of the view is not the right node to use: we need the related node.
115
   * Need to sort out how that should be handled.
116
   */
117
  public function pre_render($values) {
118
    // Preload each entity used in this view from the cache.
119
    // Provides all the entity values relatively cheaply, and we don't
120
    // need to do it repeatedly for the same entity if there are
121
    // multiple results for one entity.
122
    $ids = array();
123
    foreach ($values as $row) {
124
      // Use the $id as the key so we create only value per entity.
125
      $id = $row->{$this->field_alias};
126
      
127
      // Node revisions need special loading.
128
      if ($this->view->base_table == 'node_revision') {
129
        $this->entities[$id] = node_load(NULL, $id);
130
      }
131
      // For other entities we just create an array of ids to pass
132
      // to entity_load().
133
      else {
134
        $ids[$id] = $id;
135
      }
136
    }
137
    
138
    $base_tables = date_views_base_tables();
139
    $this->entity_type = $base_tables[$this->view->base_table];
140
    if (!empty($ids)) {
141
      $this->entities = entity_load($this->entity_type, $ids);
142
    }
143
    
144
    // Get the language for this view.
145
    $this->language = $this->display->handler->get_option('field_language');
146
    $substitutions = views_views_query_substitutions($this->view);
147
    if (array_key_exists($this->language, $substitutions)) {
148
      $this->language = $substitutions[$this->language];
149
    }
150
  }
151
  
152
  /**
153
   * Renders the entities returned by the view into event arrays.
154
   */
155
  public function render($row) {
156
    $id = $row->{$this->field_alias};
157
    if (!is_numeric($id)) {
158
      return NULL;
159
    }
160
    
161
    // Load the specified entity:
162
    $entity = $this->entities[$id];
163
    if (empty($entity)) {
164
      // This can happen when an RRULE is involved.
165
      return NULL;
166
    }
167
    
168
    $date_fields = date_views_fields($this->base_table);
169
    $date_info = $date_fields['name'][$this->options['date_field']];
170
    $field_name  = str_replace(array('_value', '_value2'), '', $date_info['real_field_name']);
171
    $delta_field = $date_info['delta_field'];
172
    $is_field    = $date_info['is_field'];
173
    
174
    // Sometimes the timestamp is actually the revision timestamp.
175
    if ($this->view->base_table == 'node_revision' && $field_name == 'timestamp') {
176
      $field_name = 'revision_timestamp';
177
    }
178
    
179
    if (!isset($entity->$field_name)) {
180
      // This entity doesn't have the date property that the user configured
181
      // our view to use. We can't do anything with it.
182
      return NULL;
183
    }
184
    $date_field = $entity->$field_name;
185
    
186
    // Pull the date value from the specified field of the entity.
187
    $entity->date_id = array();
188
    $start = NULL;
189
    $end   = NULL;
190
    $delta = isset($row->$delta_field) ? $row->$delta_field : 0;
191
    if ($is_field) {
192
      $items = field_get_items($this->entity_type, $entity, $field_name);
193
      if (!$items) {
194
        // This entity doesn't have data in the date field that the user
195
        // configured our view to use. We can't do anything with it.
196
        return;
197
      }
198
      $date_field = $items[$delta];
199
      global $base_url;
200
      $domain = preg_replace('#^https?://#', '', $base_url);
201
      $entity->date_id[] = "calendar.$id.$field_name.$delta@$domain";
202
      
203
      if (!empty($date_field['value'])) {
204
        $start = new DateObject($date_field['value'], $date_field['timezone_db']);
205
        if (!empty($date_field['value2'])) {
206
          $end = new DateObject($date_field['value2'], $date_field['timezone_db']);
207
        }
208
        else {
209
          $end = clone $start;
210
        }
211
      }
212
    }
213
    elseif (!$is_field && !empty($date_field)) {
214
      $start = new DateObject($date_field, $date_field['timezone_db']);
215
      $end   = new DateObject($date_field, $date_field['timezone_db']);
216
    }
217
    
218
    // Set the item date to the proper display timezone.
219
    $start->setTimezone(new DateTimeZone($date_field['timezone']));
220
    $end->setTimezone(new DateTimeZone($date_field['timezone']));
221
    
222
    // Check if the start and end dates indicate that this is an All Day event.
223
    $all_day = date_is_all_day(
224
      date_format($start, DATE_FORMAT_DATETIME),
225
      date_format($end, DATE_FORMAT_DATETIME),
226
      date_granularity_precision($date_info['granularity'])
227
    );
228
    
229
    if ($all_day) {
230
      // According to RFC 2445 (clarified in RFC 5545) the DTEND value is
231
      // non-inclusive. When dealing with All Day values, they're DATEs rather
232
      // than DATETIMEs, so we need to add a day to conform to RFC.
233
      $end->modify("+1 day");
234
    }
235
    
236
    // If the user specified a LOCATION field, pull that data from the entity.
237
    $location = '';
238
    if (!empty($this->options['location_field']) && $this->options['location_field'] != 'none') {
239
      $location_fields = date_ical_get_location_fields($this->base_table);
240
      $location_info = $location_fields['name'][$this->options['location_field']];
241
      $location_field_name = $location_info['real_field_name'];
242
      
243
      // Only attempt this is the entity actually has this field.
244
      $items = field_get_items($this->entity_type, $entity, $location_field_name);
245
      if ($items) {
246
        $location_field = $items[0];
247
        if ($location_info['type'] == 'node_reference') {
248
          // Make sure this Node Reference actually references a node.
249
          if ($location_field['nid']) {
250
            $node = node_load($location_field['nid']);
251
            $location = $node->title;
252
          }
253
        }
254
        elseif ($location_info['type'] == 'addressfield') {
255
          $locations = array();
256
          foreach ($location_field as $key => $loc) {
257
            if ($loc && !in_array($key, array('first_name', 'last_name'))) {
258
              $locations[] = $loc;
259
            }
260
          }
261
          $location = implode(', ', array_reverse($locations));
262
        }
263
        elseif ($location_info['type'] == 'location') {
264
          $included_fields = array(
265
            'name',
266
            'additional',
267
            'street',
268
            'city',
269
            'province_name',
270
            'postal_code',
271
            'country_name'
272
          );
273
          $location_data = array();
274
          foreach ($included_fields as $included_field) {
275
            if (!empty($location_field[$included_field])) {
276
              $location_data[] = $location_field[$included_field];
277
            }
278
          }
279
          $location = implode(', ', $location_data);
280
        }
281
        else {
282
          $location = $location_field['value'];
283
        }
284
      }
285
    }
286
    
287
    // Create the rendered event using the display settings from the
288
    // iCal view mode.
289
    $rendered_array = entity_view($this->entity_type, array($entity), 'ical', $this->language, TRUE);
290
    $data = array(
291
      'description' => drupal_render($rendered_array),
292
      'summary' => entity_label($this->entity_type, $entity),
293
    );
294
    if (!empty($this->options['summary_field']) && $this->options['summary_field'] != 'default_title') {
295
      $summary_fields = date_ical_get_summary_fields();
296
      $summary_info = $summary_fields['name'][$this->options['summary_field']];
297
      $summary_field_name = $summary_info['real_field_name'];
298
      // Only attempt this is the entity actually has this field.
299
      $items = field_get_items($this->entity_type, $entity, $summary_field_name);
300
      $summary = '';
301
      if ($items) {
302
        $summary_field = $items[0];
303
        if ($summary_info['type'] == 'node_reference') {
304
          // Make sure this Node Reference actually references a node.
305
          if ($summary_field['nid']) {
306
            $node = node_load($summary_field['nid']);
307
            $summary = $node->title;
308
          }
309
        }
310
        elseif ($summary_info['type'] == 'taxonomy_term_reference') {
311
          $terms = taxonomy_term_load_multiple($items);
312
          // Make sure that there are terms that were loaded.
313
          if ($terms) {
314
            $term_names = array();
315
            foreach ($terms as $term) {
316
              $term_names[] = $term->name;
317
            }
318
            $summary = implode(', ', $term_names);
319
          }
320
        }
321
        else {
322
          $summary = trim($summary_field['value']);
323
        }
324
        $data['summary'] = $summary ? $summary : $data['summary'];
325
      }
326
    }
327
    // Allow other modules to alter the HTML of the Summary and Description,
328
    // before it gets converted to iCal-compliant plaintext. This allows users
329
    // to set up a newline between fields, for instance.
330
    $context = array(
331
      'entity' => $entity,
332
      'entity_type' => $this->entity_type,
333
      'language' => $this->language,
334
    );
335
    drupal_alter('date_ical_export_html', $data, $this->view, $context);
336
    
337
    $event = array();
338
    $event['summary'] = date_ical_sanitize_text($data['summary']);
339
    $event['description'] = date_ical_sanitize_text($data['description']);
340
    $event['all_day'] = $all_day;
341
    $event['start'] = $start;
342
    $event['end'] = $end;
343
    $uri = entity_uri($this->entity_type, $entity);
344
    $uri['options']['absolute'] = TRUE;
345
    $event['url'] = url($uri['path'], $uri['options']);
346
    $event['rrule'] = $is_field && array_key_exists('rrule', $date_field) ? $date_field['rrule'] : '';
347
    if ($location) {
348
      $event['location'] = date_ical_sanitize_text($location);
349
    }
350
    
351
    // For this event's UID, use either the date_id generated by the Date
352
    // module, or the event page's URL if the date_id isn't available.
353
    $event['uid'] = !empty($entity->date_id) ? $entity->date_id[0] : $event['url'];
354
    
355
    // If we are using a repeat rule (and not just multi-day events) we
356
    // remove the item from the entities list so that its VEVENT won't be
357
    // re-created.
358
    if ($event['rrule']) {
359
      $this->entities[$id] = NULL;
360
    }
361
    
362
    // According to the iCal standard, CREATED and LAST-MODIFIED must be UTC.
363
    // Fortunately, Drupal stores timestamps in the DB as UTC, so we just need
364
    // to tell DateObject to treat the timestamp as UTC from the start.
365
    if (isset($entity->created)) {
366
      $event['created'] = new DateObject($entity->created, 'UTC');
367
    }
368
    // Pull the 'changed' date from the entity (if available), so that
369
    // subscription clients can tell if the event has been updated.
370
    if (isset($entity->changed)) {
371
      $event['last-modified'] = new DateObject($entity->changed, 'UTC');
372
    }
373
    elseif (isset($entity->created)) {
374
      // If changed is unset, but created is, use that for last-modified.
375
      $event['last-modified'] = new DateObject($entity->created, 'UTC');
376
    }
377
    
378
    // Allow other modules to alter the structured event object, before it gets
379
    // passed to the style plugin to be converted into an iCalcreator vevent.
380
    drupal_alter('date_ical_export_raw_event', $event, $this->view, $context);
381
    
382
    return $event;
383
  }
384
}