Projet

Général

Profil

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

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

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
          // Get full country name
257
          if (!empty($location_field['country'])) {
258
            require_once DRUPAL_ROOT . '/includes/locale.inc';
259
            $countries = country_get_list();
260
            $location_field['country'] = $countries[$location_field['country']];
261
          }
262
          foreach ($location_field as $key => $loc) {
263
            if ($loc && !in_array($key, array('first_name', 'last_name'))) {
264
              $locations[] = $loc;
265
            }
266
          }
267
          $location = implode(', ', array_reverse($locations));
268
        }
269
        elseif ($location_info['type'] == 'location') {
270
          $included_fields = array(
271
            'name',
272
            'additional',
273
            'street',
274
            'city',
275
            'province_name',
276
            'postal_code',
277
            'country_name'
278
          );
279
          $location_data = array();
280
          foreach ($included_fields as $included_field) {
281
            if (!empty($location_field[$included_field])) {
282
              $location_data[] = $location_field[$included_field];
283
            }
284
          }
285
          $location = implode(', ', $location_data);
286
        }
287
        else {
288
          $location = $location_field['value'];
289
        }
290
      }
291
    }
292

    
293
    // Create the rendered event using the display settings from the
294
    // iCal view mode.
295
    $rendered_array = entity_view($this->entity_type, array($entity), 'ical', $this->language, TRUE);
296
    $data = array(
297
      'description' => drupal_render($rendered_array),
298
      'summary' => entity_label($this->entity_type, $entity),
299
    );
300
    if (!empty($this->options['summary_field']) && $this->options['summary_field'] != 'default_title') {
301
      $summary_fields = date_ical_get_summary_fields();
302
      $summary_info = $summary_fields['name'][$this->options['summary_field']];
303
      $summary_field_name = $summary_info['real_field_name'];
304
      // Only attempt this is the entity actually has this field.
305
      $items = field_get_items($this->entity_type, $entity, $summary_field_name);
306
      $summary = '';
307
      if ($items) {
308
        $summary_field = $items[0];
309
        if ($summary_info['type'] == 'node_reference') {
310
          // Make sure this Node Reference actually references a node.
311
          if ($summary_field['nid']) {
312
            $node = node_load($summary_field['nid']);
313
            $summary = $node->title;
314
          }
315
        }
316
        elseif ($summary_info['type'] == 'taxonomy_term_reference') {
317
          $terms = taxonomy_term_load_multiple($items);
318
          // Make sure that there are terms that were loaded.
319
          if ($terms) {
320
            $term_names = array();
321
            foreach ($terms as $term) {
322
              $term_names[] = $term->name;
323
            }
324
            $summary = implode(', ', $term_names);
325
          }
326
        }
327
        else {
328
          $summary = trim($summary_field['value']);
329
        }
330
        $data['summary'] = $summary ? $summary : $data['summary'];
331
      }
332
    }
333
    // Allow other modules to alter the HTML of the Summary and Description,
334
    // before it gets converted to iCal-compliant plaintext. This allows users
335
    // to set up a newline between fields, for instance.
336
    $context = array(
337
      'entity' => $entity,
338
      'entity_type' => $this->entity_type,
339
      'language' => $this->language,
340
    );
341
    drupal_alter('date_ical_export_html', $data, $this->view, $context);
342

    
343
    $event = array();
344
    $event['summary'] = date_ical_sanitize_text($data['summary']);
345
    $event['description'] = date_ical_sanitize_text($data['description']);
346
    $event['all_day'] = $all_day;
347
    $event['start'] = $start;
348
    $event['end'] = $end;
349
    $uri = entity_uri($this->entity_type, $entity);
350
    $uri['options']['absolute'] = TRUE;
351
    $event['url'] = url($uri['path'], $uri['options']);
352
    if ($is_field && !empty($date_field['rrule'])) {
353
      $event['rrule'] = $date_field['rrule'];
354
    }
355
    if ($location) {
356
      $event['location'] = date_ical_sanitize_text($location);
357
    }
358

    
359
    // For this event's UID, use either the date_id generated by the Date
360
    // module, or the event page's URL if the date_id isn't available.
361
    $event['uid'] = !empty($entity->date_id) ? $entity->date_id[0] : $event['url'];
362

    
363
    // If we are using a repeat rule (and not just multi-day events) we
364
    // remove the item from the entities list so that its VEVENT won't be
365
    // re-created.
366
    if (!empty($event['rrule'])) {
367
      $this->entities[$id] = NULL;
368
    }
369

    
370
    // According to the iCal standard, CREATED and LAST-MODIFIED must be UTC.
371
    // Fortunately, Drupal stores timestamps in the DB as UTC, so we just need
372
    // to tell DateObject to treat the timestamp as UTC from the start.
373
    if (isset($entity->created)) {
374
      $event['created'] = new DateObject($entity->created, 'UTC');
375
    }
376
    // Pull the 'changed' date from the entity (if available), so that
377
    // subscription clients can tell if the event has been updated.
378
    if (isset($entity->changed)) {
379
      $event['last-modified'] = new DateObject($entity->changed, 'UTC');
380
    }
381
    elseif (isset($entity->created)) {
382
      // If changed is unset, but created is, use that for last-modified.
383
      $event['last-modified'] = new DateObject($entity->created, 'UTC');
384
    }
385

    
386
    // Allow other modules to alter the structured event object, before it gets
387
    // passed to the style plugin to be converted into an iCalcreator vevent.
388
    drupal_alter('date_ical_export_raw_event', $event, $this->view, $context);
389

    
390
    return $event;
391
  }
392
}