1 |
85ad3d82
|
Assos Assos
|
<?php
|
2 |
|
|
/**
|
3 |
|
|
* @file
|
4 |
|
|
* Views style plugin for the Date iCal module.
|
5 |
|
|
*/
|
6 |
|
|
|
7 |
|
|
/**
|
8 |
8a7e43dd
|
Florent Torregrosa
|
* Defines a Views style plugin that renders iCal feeds.
|
9 |
85ad3d82
|
Assos Assos
|
*/
|
10 |
|
|
class date_ical_plugin_style_ical_feed extends views_plugin_style {
|
11 |
204e4d33
|
Assos Assos
|
|
12 |
8a7e43dd
|
Florent Torregrosa
|
/**
|
13 |
|
|
* Internal helper function.
|
14 |
|
|
*/
|
15 |
85ad3d82
|
Assos Assos
|
protected function _get_option($option_name) {
|
16 |
|
|
return isset($this->options[$option_name]) ? $this->options[$option_name] : '';
|
17 |
|
|
}
|
18 |
204e4d33
|
Assos Assos
|
|
19 |
8a7e43dd
|
Florent Torregrosa
|
/**
|
20 |
|
|
* Sets up the iCal feed icon on calendar pages.
|
21 |
|
|
*/
|
22 |
|
|
public function attach_to($display_id, $path, $title) {
|
23 |
85ad3d82
|
Assos Assos
|
$url_options = array();
|
24 |
|
|
$input = $this->view->get_exposed_input();
|
25 |
|
|
if ($input) {
|
26 |
|
|
$url_options['query'] = $input;
|
27 |
|
|
}
|
28 |
|
|
$url_options['absolute'] = TRUE;
|
29 |
204e4d33
|
Assos Assos
|
|
30 |
62e0cc08
|
Assos Assos
|
// Only add arguments to ical path for the contextual filters in the feed display.
|
31 |
|
|
// Clone view to prevent affecting the page view where this is attached.
|
32 |
|
|
$clone = $this->view->clone_view();
|
33 |
|
|
$clone->set_display($this->display->id);
|
34 |
|
|
$contextual_filters = $clone->get_items('argument');
|
35 |
|
|
$arg_number = count($contextual_filters);
|
36 |
|
|
|
37 |
|
|
// Only include as many arguments as the feed display supports.
|
38 |
|
|
foreach($clone->args as $key => $arg) {
|
39 |
|
|
if ($key >= $arg_number) {
|
40 |
|
|
unset($clone->args[$key]);
|
41 |
|
|
}
|
42 |
|
|
}
|
43 |
|
|
|
44 |
|
|
$url = url($clone->get_url(NULL, $path), $url_options);
|
45 |
|
|
|
46 |
8a7e43dd
|
Florent Torregrosa
|
// If the user didn't disable the option, change the scheme to webcal://
|
47 |
|
|
// so calendar clients can automatically subscribe via the iCal link.
|
48 |
85ad3d82
|
Assos Assos
|
if (!$this->_get_option('disable_webcal')) {
|
49 |
|
|
$url = str_replace(array('http://', 'https://'), 'webcal://', $url);
|
50 |
|
|
}
|
51 |
204e4d33
|
Assos Assos
|
|
52 |
62e0cc08
|
Assos Assos
|
// Render the feed icon and header tag (except during a View Preview and
|
53 |
|
|
// if the display is disabled).
|
54 |
|
|
if (empty($this->view->live_preview) && (!isset($this->display->display_options['enabled']) || $this->display->display_options['enabled'])) {
|
55 |
85ad3d82
|
Assos Assos
|
$tooltip = t('Add to My Calendar');
|
56 |
|
|
if (!isset($this->view->feed_icon)) {
|
57 |
8a7e43dd
|
Florent Torregrosa
|
// In PHP 5.5, you're not allowed to concatinate onto an unset
|
58 |
|
|
// property. But we need to do a concat, because there may be
|
59 |
|
|
// other attachments.
|
60 |
85ad3d82
|
Assos Assos
|
$this->view->feed_icon = '';
|
61 |
|
|
}
|
62 |
8a7e43dd
|
Florent Torregrosa
|
$variables = array(
|
63 |
|
|
'url' => check_url($url),
|
64 |
|
|
'tooltip' => $tooltip,
|
65 |
|
|
'view' => $this->view,
|
66 |
|
|
);
|
67 |
|
|
$this->view->feed_icon .= theme('date_ical_icon', $variables);
|
68 |
85ad3d82
|
Assos Assos
|
drupal_add_html_head_link(array(
|
69 |
|
|
'rel' => 'alternate',
|
70 |
|
|
'type' => 'text/calendar',
|
71 |
|
|
'title' => $tooltip,
|
72 |
8a7e43dd
|
Florent Torregrosa
|
'href' => $url,
|
73 |
85ad3d82
|
Assos Assos
|
));
|
74 |
|
|
}
|
75 |
|
|
}
|
76 |
204e4d33
|
Assos Assos
|
|
77 |
8a7e43dd
|
Florent Torregrosa
|
/**
|
78 |
|
|
* Set up the options for the style plugin.
|
79 |
|
|
*/
|
80 |
|
|
public function option_definition() {
|
81 |
|
|
$false_bool = array('default' => FALSE, 'bool' => TRUE);
|
82 |
204e4d33
|
Assos Assos
|
|
83 |
85ad3d82
|
Assos Assos
|
$options = parent::option_definition();
|
84 |
|
|
$options['cal_name'] = array('default' => array());
|
85 |
8a7e43dd
|
Florent Torregrosa
|
$options['no_calname'] = $false_bool;
|
86 |
|
|
$options['disable_webcal'] = $false_bool;
|
87 |
|
|
$options['exclude_dtstamp'] = $false_bool;
|
88 |
|
|
$options['unescape_punctuation'] = $false_bool;
|
89 |
85ad3d82
|
Assos Assos
|
return $options;
|
90 |
|
|
}
|
91 |
204e4d33
|
Assos Assos
|
|
92 |
8a7e43dd
|
Florent Torregrosa
|
/**
|
93 |
|
|
* Build the form for setting the style plugin's options.
|
94 |
|
|
*/
|
95 |
|
|
public function options_form(&$form, &$form_state) {
|
96 |
85ad3d82
|
Assos Assos
|
parent::options_form($form, $form_state);
|
97 |
|
|
// Allow users to override the default Calendar name (X-WR-CALNAME).
|
98 |
|
|
$form['cal_name'] = array(
|
99 |
|
|
'#type' => 'textfield',
|
100 |
|
|
'#title' => t('iCal Calendar Name'),
|
101 |
|
|
'#default_value' => $this->_get_option('cal_name'),
|
102 |
|
|
'#description' => t('This will appear as the title of the iCal feed. If left blank, the View Title will be used.
|
103 |
|
|
If that is also blank, the site name will be inserted as the iCal feed title.'),
|
104 |
|
|
);
|
105 |
|
|
$form['no_calname'] = array(
|
106 |
|
|
'#type' => 'checkbox',
|
107 |
|
|
'#title' => t('Exclude Calendar Name'),
|
108 |
|
|
'#default_value' => $this->_get_option('no_calname'),
|
109 |
|
|
'#description' => t("Excluding the X-WR-CALNAME value from the iCal Feed causes
|
110 |
|
|
some calendar clients to add the events in the feed to an existing calendar, rather
|
111 |
|
|
than creating a whole new calendar for them."),
|
112 |
|
|
);
|
113 |
|
|
$form['disable_webcal'] = array(
|
114 |
|
|
'#type' => 'checkbox',
|
115 |
|
|
'#title' => t('Disable webcal://'),
|
116 |
|
|
'#default_value' => $this->_get_option('disable_webcal'),
|
117 |
|
|
'#description' => t("By default, the feed URL will use the webcal:// scheme, which allows calendar
|
118 |
|
|
clients to easily subscribe to the feed. If you want your users to instead download this iCal
|
119 |
|
|
feed as a file, activate this option."),
|
120 |
|
|
);
|
121 |
|
|
$form['exclude_dtstamp'] = array(
|
122 |
|
|
'#type' => 'checkbox',
|
123 |
|
|
'#title' => t('Exclude DTSTAMP'),
|
124 |
|
|
'#default_value' => $this->_get_option('exclude_dtstamp'),
|
125 |
|
|
'#description' => t("By default, the feed will set each event's DTSTAMP property to the time at which the feed got downloaded.
|
126 |
|
|
Some feed readers will (incorrectly) look at the DTSTAMP value when they compare different downloads of the same feed, and
|
127 |
|
|
conclcude that the event has been updated (even though it hasn't actually changed). Enable this option to exclude the DTSTAMP
|
128 |
|
|
field from your feeds, so that these buggy feed readers won't mark every event as updated every time they check."),
|
129 |
|
|
);
|
130 |
8a7e43dd
|
Florent Torregrosa
|
$form['unescape_punctuation'] = array(
|
131 |
|
|
'#type' => 'checkbox',
|
132 |
|
|
'#title' => t('Unescape Commas and Semicolons'),
|
133 |
|
|
'#default_value' => $this->_get_option('unescape_punctuation'),
|
134 |
|
|
'#description' => t('In order to comply with the iCal spec, Date iCal will "escape" commas and semicolons (prepend them with backslashes).
|
135 |
|
|
However, many calendar clients are bugged to not unescape these characters, leaving the backslashes littered throughout your events.
|
136 |
|
|
Enable this option to have Date iCal unescape these characters before it exports the iCal feed.'),
|
137 |
|
|
);
|
138 |
85ad3d82
|
Assos Assos
|
}
|
139 |
204e4d33
|
Assos Assos
|
|
140 |
8a7e43dd
|
Florent Torregrosa
|
/**
|
141 |
|
|
* Render the event arrays returned by the row plugin into a VCALENDAR.
|
142 |
|
|
*/
|
143 |
|
|
public function render() {
|
144 |
85ad3d82
|
Assos Assos
|
if (empty($this->row_plugin) || !in_array($this->row_plugin->plugin_name, array('date_ical', 'date_ical_fields'))) {
|
145 |
|
|
debug('date_ical_plugin_style_ical_feed: This style plugin supports only the "iCal Entity" and "iCal Fields" row plugins.', NULL, TRUE);
|
146 |
8a7e43dd
|
Florent Torregrosa
|
return t('To enable iCal output, the view Format must be configured to Show: iCal Entity or iCal Fields.');
|
147 |
85ad3d82
|
Assos Assos
|
}
|
148 |
|
|
if ($this->row_plugin->plugin_name == 'date_ical_fields' && empty($this->row_plugin->options['date_field'])) {
|
149 |
8a7e43dd
|
Florent Torregrosa
|
// Because the Date field is required by the form, this error state will
|
150 |
|
|
// rarely occur. But I ran across it during testing, and the error that
|
151 |
|
|
// resulted was totally non-sensical, so I'm adding this just in case.
|
152 |
85ad3d82
|
Assos Assos
|
return t("When using the iCal Fields row plugin, the Date field is required. Please set it up using the Settings link under 'Format -> Show: iCal Fields'.");
|
153 |
|
|
}
|
154 |
|
|
$events = array();
|
155 |
|
|
foreach ($this->view->result as $row_index => $row) {
|
156 |
|
|
$this->view->row_index = $row_index;
|
157 |
8a7e43dd
|
Florent Torregrosa
|
$row->index = $row_index;
|
158 |
85ad3d82
|
Assos Assos
|
try {
|
159 |
8a7e43dd
|
Florent Torregrosa
|
$events[] = $this->row_plugin->render($row);
|
160 |
85ad3d82
|
Assos Assos
|
}
|
161 |
|
|
catch (Exception $e) {
|
162 |
|
|
debug($e->getMessage(), NULL, TRUE);
|
163 |
|
|
return $e->getMessage();
|
164 |
|
|
}
|
165 |
|
|
}
|
166 |
|
|
unset($this->view->row_index);
|
167 |
204e4d33
|
Assos Assos
|
|
168 |
85ad3d82
|
Assos Assos
|
// Try to load the iCalcreator library.
|
169 |
|
|
$library = libraries_load('iCalcreator');
|
170 |
|
|
if (!$library['loaded']) {
|
171 |
|
|
// The iCalcreator library isn't available, so we can't output anything.
|
172 |
|
|
$output = t('Please install the iCalcreator library to enable iCal output.');
|
173 |
|
|
}
|
174 |
|
|
else {
|
175 |
|
|
// Create a vcalendar object using the iCalcreator library.
|
176 |
d756b39a
|
Assos Assos
|
$config = array('unique_id' => 'Date iCal');
|
177 |
85ad3d82
|
Assos Assos
|
$vcalendar = new vcalendar($config);
|
178 |
|
|
$vcalendar->setMethod('PUBLISH');
|
179 |
204e4d33
|
Assos Assos
|
|
180 |
8a7e43dd
|
Florent Torregrosa
|
// Only include the X-WR-CALNAME property if the user didn't enable
|
181 |
|
|
// the "Exclude Calendar Name" option.
|
182 |
|
|
if (!$this->_get_option('no_calname')) {
|
183 |
|
|
$cal_name = $this->_get_option('cal_name');
|
184 |
|
|
if (empty($cal_name)) {
|
185 |
|
|
$cal_name = $this->view->get_title();
|
186 |
|
|
if (empty($cal_name)) {
|
187 |
|
|
$cal_name = variable_get('site_name', 'Drupal');
|
188 |
|
|
}
|
189 |
85ad3d82
|
Assos Assos
|
}
|
190 |
8a7e43dd
|
Florent Torregrosa
|
if (!empty($cal_name)) {
|
191 |
|
|
$vcalendar->setProperty('X-WR-CALNAME', $cal_name, array('VALUE' => 'TEXT'));
|
192 |
85ad3d82
|
Assos Assos
|
}
|
193 |
|
|
}
|
194 |
204e4d33
|
Assos Assos
|
|
195 |
85ad3d82
|
Assos Assos
|
// Now add the VEVENTs.
|
196 |
|
|
$timezones = array();
|
197 |
|
|
foreach ($events as $event) {
|
198 |
|
|
if (empty($event)) {
|
199 |
|
|
// The row plugin returned NULL for this row, which can happen due to
|
200 |
|
|
// either various error conditions, or because an RRULE is involved.
|
201 |
|
|
// When this happens, just skip it.
|
202 |
|
|
continue;
|
203 |
|
|
}
|
204 |
204e4d33
|
Assos Assos
|
|
205 |
85ad3d82
|
Assos Assos
|
$vevent = $vcalendar->newComponent('vevent');
|
206 |
8a7e43dd
|
Florent Torregrosa
|
$vevent->setUid($event['uid']);
|
207 |
|
|
$vevent->setSummary($event['summary']);
|
208 |
204e4d33
|
Assos Assos
|
|
209 |
85ad3d82
|
Assos Assos
|
// Get the start date as an array.
|
210 |
|
|
$start = $event['start']->toArray();
|
211 |
8a7e43dd
|
Florent Torregrosa
|
$start_timezone = $event['start']->getTimezone()->getName();
|
212 |
|
|
$timezones[$start_timezone] = $start_timezone;
|
213 |
204e4d33
|
Assos Assos
|
|
214 |
85ad3d82
|
Assos Assos
|
if ($event['all_day']) {
|
215 |
8a7e43dd
|
Florent Torregrosa
|
// All Day events need to be DATEs, rather than DATE-TIMEs.
|
216 |
85ad3d82
|
Assos Assos
|
$vevent->setDtstart($start['year'], $start['month'], $start['day'],
|
217 |
|
|
FALSE, FALSE, FALSE, FALSE, array('VALUE' => 'DATE'));
|
218 |
|
|
}
|
219 |
|
|
else {
|
220 |
|
|
$vevent->setDtstart(
|
221 |
|
|
$start['year'],
|
222 |
|
|
$start['month'],
|
223 |
|
|
$start['day'],
|
224 |
|
|
$start['hour'],
|
225 |
|
|
$start['minute'],
|
226 |
|
|
$start['second'],
|
227 |
8a7e43dd
|
Florent Torregrosa
|
$start_timezone
|
228 |
85ad3d82
|
Assos Assos
|
);
|
229 |
|
|
}
|
230 |
204e4d33
|
Assos Assos
|
|
231 |
85ad3d82
|
Assos Assos
|
// Add the Timezone info to the start date, for use later.
|
232 |
|
|
$start['tz'] = $event['start']->getTimezone();
|
233 |
204e4d33
|
Assos Assos
|
|
234 |
85ad3d82
|
Assos Assos
|
// Only add the end date if there is one.
|
235 |
|
|
if (!empty($event['end'])) {
|
236 |
|
|
$end = $event['end']->toArray();
|
237 |
8a7e43dd
|
Florent Torregrosa
|
$end_timezone = $event['end']->getTimezone()->getName();
|
238 |
|
|
$timezones[$end_timezone] = $end_timezone;
|
239 |
204e4d33
|
Assos Assos
|
|
240 |
85ad3d82
|
Assos Assos
|
if ($event['all_day']) {
|
241 |
|
|
$vevent->setDtend($end['year'], $end['month'], $end['day'],
|
242 |
|
|
FALSE, FALSE, FALSE, FALSE, array('VALUE' => 'DATE'));
|
243 |
|
|
}
|
244 |
|
|
else {
|
245 |
|
|
$vevent->setDtend(
|
246 |
|
|
$end['year'],
|
247 |
|
|
$end['month'],
|
248 |
|
|
$end['day'],
|
249 |
|
|
$end['hour'],
|
250 |
|
|
$end['minute'],
|
251 |
|
|
$end['second'],
|
252 |
8a7e43dd
|
Florent Torregrosa
|
$end_timezone
|
253 |
85ad3d82
|
Assos Assos
|
);
|
254 |
|
|
}
|
255 |
|
|
$end['tz'] = $event['end']->getTimezone();
|
256 |
|
|
}
|
257 |
204e4d33
|
Assos Assos
|
|
258 |
85ad3d82
|
Assos Assos
|
// Handle repeating dates from the date_repeat module.
|
259 |
|
|
if (!empty($event['rrule']) && module_exists('date_repeat')) {
|
260 |
|
|
// Split the rrule into an RRULE and any additions and exceptions.
|
261 |
|
|
module_load_include('inc', 'date_api', 'date_api_ical');
|
262 |
|
|
module_load_include('inc', 'date_repeat', 'date_repeat_calc');
|
263 |
|
|
list($rrule, $exceptions, $additions) = date_repeat_split_rrule($event['rrule']);
|
264 |
204e4d33
|
Assos Assos
|
|
265 |
85ad3d82
|
Assos Assos
|
// Add the RRULE itself. We need to massage the data a bit, since
|
266 |
|
|
// iCalcreator expects RRULEs to be in a different format than how
|
267 |
|
|
// Date API gives them to us.
|
268 |
8a7e43dd
|
Florent Torregrosa
|
$vevent->setRrule(_date_ical_convert_rrule_for_icalcreator($rrule));
|
269 |
204e4d33
|
Assos Assos
|
|
270 |
85ad3d82
|
Assos Assos
|
// Convert any exceptions to EXDATE properties.
|
271 |
|
|
if (!empty($exceptions)) {
|
272 |
|
|
$exdates = array();
|
273 |
|
|
foreach ($exceptions as $exception) {
|
274 |
|
|
$except = date_ical_date($exception, 'UTC');
|
275 |
204e4d33
|
Assos Assos
|
$except->setTimezone($start['tz']);
|
276 |
85ad3d82
|
Assos Assos
|
$exception_array = $except->toArray();
|
277 |
|
|
$exdates[] = array(
|
278 |
|
|
'year' => $exception_array['year'],
|
279 |
|
|
'month' => $exception_array['month'],
|
280 |
|
|
'day' => $exception_array['day'],
|
281 |
|
|
// Use the time information from the start date, since Date
|
282 |
|
|
// doesn't store time info for EXDATEs.
|
283 |
|
|
'hour' => $start['hour'],
|
284 |
|
|
'min' => $start['minute'],
|
285 |
|
|
'second' => $start['second'],
|
286 |
|
|
'tz' => $start['tz']->getName(),
|
287 |
|
|
);
|
288 |
|
|
}
|
289 |
|
|
// Add each exclusion as a separate EXDATE property.
|
290 |
|
|
// The spec supports putting multiple date values into one EXDATE,
|
291 |
|
|
// but several popular calendar clients (*cough* Apple *cough*)
|
292 |
|
|
// are bugged, and do not recognize multi-value EXDATEs.
|
293 |
62e0cc08
|
Assos Assos
|
$value = $event['all_day'] == 1 ? "DATE" : "DATE-TIME";
|
294 |
85ad3d82
|
Assos Assos
|
foreach ($exdates as $exdate) {
|
295 |
62e0cc08
|
Assos Assos
|
$vevent->setExdate(array($exdate), array( "VALUE" => $value ));
|
296 |
85ad3d82
|
Assos Assos
|
}
|
297 |
|
|
}
|
298 |
204e4d33
|
Assos Assos
|
|
299 |
85ad3d82
|
Assos Assos
|
// Convert any additions to RDATE properties.
|
300 |
|
|
if (!empty($additions)) {
|
301 |
|
|
$rdates = array();
|
302 |
|
|
foreach ($additions as $addition) {
|
303 |
|
|
$add = date_ical_date($addition, 'UTC');
|
304 |
204e4d33
|
Assos Assos
|
$add->setTimezone($start['tz']);
|
305 |
85ad3d82
|
Assos Assos
|
$addition_array = $add->toArray();
|
306 |
204e4d33
|
Assos Assos
|
|
307 |
85ad3d82
|
Assos Assos
|
$rdate = array(
|
308 |
|
|
'year' => $addition_array['year'],
|
309 |
|
|
'month' => $addition_array['month'],
|
310 |
|
|
'day' => $addition_array['day'],
|
311 |
c7b88c87
|
Assos Assos
|
// If the user's copy of Date has support for time in RDATEs,
|
312 |
|
|
// use that. Otherwise use the time from the start date.
|
313 |
204e4d33
|
Assos Assos
|
'hour' => !empty($addition_array['hour']) ? $addition_array['hour'] : $start['hour'],
|
314 |
|
|
'min' => !empty($addition_array['minute']) ? $addition_array['minute'] : $start['minute'],
|
315 |
|
|
'second' => !empty($addition_array['second']) ? $addition_array['second'] : $start['second'],
|
316 |
85ad3d82
|
Assos Assos
|
'tz' => $start['tz']->getName(),
|
317 |
|
|
);
|
318 |
204e4d33
|
Assos Assos
|
|
319 |
85ad3d82
|
Assos Assos
|
// If an end date was was calculated above, use that too.
|
320 |
|
|
// iCalcreator expects RDATEs that have end dates to be
|
321 |
|
|
// specified as array($start_rdate, $end_rdate).
|
322 |
|
|
if (isset($end)) {
|
323 |
|
|
$rdate_with_end = array($rdate);
|
324 |
|
|
$rdate_with_end[] = array(
|
325 |
|
|
'year' => $addition_array['year'],
|
326 |
|
|
'month' => $addition_array['month'],
|
327 |
|
|
'day' => $addition_array['day'],
|
328 |
c7b88c87
|
Assos Assos
|
// If the user's copy of Date has support for time in RDATEs,
|
329 |
|
|
// use that. Otherwise use the time from the end date.
|
330 |
204e4d33
|
Assos Assos
|
'hour' => !empty($addition_array['hour']) ? $addition_array['hour'] : $end['hour'],
|
331 |
|
|
'min' => !empty($addition_array['minute']) ? $addition_array['minute'] : $end['minute'],
|
332 |
|
|
'second' => !empty($addition_array['second']) ? $addition_array['second'] : $end['second'],
|
333 |
85ad3d82
|
Assos Assos
|
'tz' => $end['tz']->getName(),
|
334 |
|
|
);
|
335 |
|
|
$rdate = $rdate_with_end;
|
336 |
|
|
}
|
337 |
204e4d33
|
Assos Assos
|
|
338 |
85ad3d82
|
Assos Assos
|
$rdates[] = $rdate;
|
339 |
|
|
}
|
340 |
|
|
// Add each addition as a separate RDATE property.
|
341 |
|
|
// The spec supports putting multiple date values into one RDATE,
|
342 |
|
|
// but several popular calendar clients (*cough* Apple *cough*)
|
343 |
|
|
// are bugged, and do not recognize multi-value RDATEs.
|
344 |
|
|
foreach ($rdates as $rdate) {
|
345 |
|
|
$vevent->setRdate(array($rdate));
|
346 |
|
|
}
|
347 |
|
|
}
|
348 |
|
|
}
|
349 |
|
|
if (!empty($event['url'])) {
|
350 |
|
|
$vevent->setUrl($event['url'], array('type' => 'URI'));
|
351 |
|
|
}
|
352 |
|
|
if (!empty($event['location'])) {
|
353 |
8a7e43dd
|
Florent Torregrosa
|
$vevent->setLocation($event['location']);
|
354 |
85ad3d82
|
Assos Assos
|
}
|
355 |
|
|
if (!empty($event['description'])) {
|
356 |
8a7e43dd
|
Florent Torregrosa
|
$vevent->setDescription($event['description']);
|
357 |
85ad3d82
|
Assos Assos
|
}
|
358 |
62e0cc08
|
Assos Assos
|
if (!empty($event['categories'])) {
|
359 |
|
|
$vevent->setCategories($event['categories']);
|
360 |
|
|
}
|
361 |
85ad3d82
|
Assos Assos
|
if (!empty($event['last-modified'])) {
|
362 |
|
|
$lm = $event['last-modified']->toArray();
|
363 |
|
|
$vevent->setLastModified(
|
364 |
|
|
$lm['year'],
|
365 |
|
|
$lm['month'],
|
366 |
|
|
$lm['day'],
|
367 |
|
|
$lm['hour'],
|
368 |
|
|
$lm['minute'],
|
369 |
|
|
$lm['second'],
|
370 |
|
|
$lm['timezone']
|
371 |
|
|
);
|
372 |
|
|
}
|
373 |
|
|
if (!empty($event['created'])) {
|
374 |
|
|
$created = $event['created']->toArray();
|
375 |
|
|
$vevent->setCreated(
|
376 |
|
|
$created['year'],
|
377 |
|
|
$created['month'],
|
378 |
|
|
$created['day'],
|
379 |
|
|
$created['hour'],
|
380 |
|
|
$created['minute'],
|
381 |
|
|
$created['second'],
|
382 |
|
|
$created['timezone']
|
383 |
|
|
);
|
384 |
|
|
}
|
385 |
204e4d33
|
Assos Assos
|
|
386 |
8a7e43dd
|
Florent Torregrosa
|
// Allow other modules to alter the vevent before it's exported.
|
387 |
|
|
drupal_alter('date_ical_export_vevent', $vevent, $this->view, $event);
|
388 |
85ad3d82
|
Assos Assos
|
}
|
389 |
204e4d33
|
Assos Assos
|
|
390 |
85ad3d82
|
Assos Assos
|
// Now add to the calendar all the timezones used by the events.
|
391 |
|
|
foreach ($timezones as $timezone) {
|
392 |
|
|
if (strtoupper($timezone) != 'UTC') {
|
393 |
|
|
iCalUtilityFunctions::createTimezone($vcalendar, $timezone);
|
394 |
|
|
}
|
395 |
|
|
}
|
396 |
204e4d33
|
Assos Assos
|
|
397 |
8a7e43dd
|
Florent Torregrosa
|
// Allow other modules to alter the vcalendar before it's exported.
|
398 |
|
|
drupal_alter('date_ical_export_vcalendar', $vcalendar, $this->view);
|
399 |
204e4d33
|
Assos Assos
|
|
400 |
85ad3d82
|
Assos Assos
|
$output = $vcalendar->createCalendar();
|
401 |
8a7e43dd
|
Florent Torregrosa
|
// iCalcreator escapes all commas and semicolons in string values, as the
|
402 |
|
|
// spec demands. However, some calendar clients are buggy and fail to
|
403 |
|
|
// unescape these characters. Users may choose to unescape them here to
|
404 |
|
|
// sidestep those clients' bugs.
|
405 |
|
|
// NOTE: This results in a non-compliant iCal feed, but it seems like a
|
406 |
|
|
// LOT of major clients are bugged this way.
|
407 |
|
|
if ($this->_get_option('unescape_punctuation')) {
|
408 |
|
|
$output = str_replace('\,', ',', $output);
|
409 |
|
|
$output = str_replace('\;', ';', $output);
|
410 |
|
|
}
|
411 |
204e4d33
|
Assos Assos
|
|
412 |
85ad3d82
|
Assos Assos
|
// In order to respect the Exclude DTSTAMP option, we unfortunately have
|
413 |
|
|
// to parse out the DTSTAMP properties after they get rendered. Simply
|
414 |
|
|
// using deleteProperty('DTSTAMP') doesn't work, because iCalcreator
|
415 |
|
|
// considers the DTSTAMP to be essential, and will re-create it when
|
416 |
|
|
// createCalendar() is called.
|
417 |
|
|
if ($this->_get_option('exclude_dtstamp')) {
|
418 |
|
|
$filtered_lines = array();
|
419 |
|
|
foreach (explode("\r\n", $output) as $line) {
|
420 |
|
|
if (strpos($line, 'DTSTAMP') === 0) {
|
421 |
|
|
continue;
|
422 |
|
|
}
|
423 |
|
|
$filtered_lines[] = $line;
|
424 |
|
|
}
|
425 |
|
|
$output = implode("\r\n", $filtered_lines);
|
426 |
|
|
}
|
427 |
|
|
}
|
428 |
204e4d33
|
Assos Assos
|
|
429 |
c7b88c87
|
Assos Assos
|
// These steps shouldn't be run during Preview on the View page.
|
430 |
85ad3d82
|
Assos Assos
|
if (empty($this->view->live_preview)) {
|
431 |
|
|
// Prevent devel module from appending queries to ical export.
|
432 |
|
|
$GLOBALS['devel_shutdown'] = FALSE;
|
433 |
204e4d33
|
Assos Assos
|
|
434 |
55670b15
|
Assos Assos
|
drupal_add_http_header('Content-Type', 'text/calendar; charset=UTF-8');
|
435 |
|
|
drupal_add_http_header('Cache-Control', 'no-cache, must-revalidate');
|
436 |
|
|
drupal_add_http_header('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT');
|
437 |
204e4d33
|
Assos Assos
|
|
438 |
8a7e43dd
|
Florent Torregrosa
|
// For sites with Clean URLs disabled, the Display's "path" value ends
|
439 |
|
|
// up only in the query args, meaning the filename won't be set properly
|
440 |
|
|
// when users download the feed. So we need to manually instruct browsers
|
441 |
|
|
// to download a .ics file.
|
442 |
85ad3d82
|
Assos Assos
|
if (!variable_get('clean_url', FALSE)) {
|
443 |
|
|
$path_array = explode('/', $this->display->display_options['path']);
|
444 |
8a7e43dd
|
Florent Torregrosa
|
$filename = end($path_array);
|
445 |
85ad3d82
|
Assos Assos
|
drupal_add_http_header('Content-Disposition', "attachment; filename=\"$filename\"");
|
446 |
|
|
}
|
447 |
|
|
}
|
448 |
204e4d33
|
Assos Assos
|
|
449 |
8a7e43dd
|
Florent Torregrosa
|
// Allow other modules to alter the rendered calendar.
|
450 |
|
|
drupal_alter('date_ical_export_post_render', $output, $this->view);
|
451 |
204e4d33
|
Assos Assos
|
|
452 |
85ad3d82
|
Assos Assos
|
return $output;
|
453 |
|
|
}
|
454 |
|
|
} |