Completed
Pull Request — master (#308)
by
unknown
16:43
created

SRFiCalendar::getParamDefinitions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * Create iCalendar exports
4
 * @file
5
 * @ingroup SemanticResultFormats
6
 */
7
8
/**
9
 * Printer class for creating iCalendar exports
10
 * 
11
 * @author Markus Krötzsch
12
 * @author Denny Vrandecic
13
 * @author Jeroen De Dauw
14
 * 
15
 * @ingroup SemanticResultFormats
16
 */
17
class SRFiCalendar extends SMWExportPrinter {
18
	
19
	protected $m_title;
20
	protected $m_description;
21
22
	protected function handleParameters( array $params, $outputmode ) {
23
		parent::handleParameters( $params, $outputmode );
24
		
25
		$this->m_title = trim( $params['title'] );
26
		$this->m_description = trim( $params['description'] );
27
	}
28
29
	/**
30
	 * @see SMWIExportPrinter::getMimeType
31
	 *
32
	 * @since 1.8
33
	 *
34
	 * @param SMWQueryResult $queryResult
35
	 *
36
	 * @return string
37
	 */
38
	public function getMimeType( SMWQueryResult $queryResult ) {
39
		return 'text/calendar';
40
	}
41
42
	/**
43
	 * @see SMWIExportPrinter::getFileName
44
	 *
45
	 * @since 1.8
46
	 *
47
	 * @param SMWQueryResult $queryResult
48
	 *
49
	 * @return string|boolean
50
	 */
51
	public function getFileName( SMWQueryResult $queryResult ) {
52
		if ( $this->m_title != '' ) {
53
			return str_replace( ' ', '_', $this->m_title ) . '.ics';
54
		} else {
55
			return 'iCalendar.ics';
56
		}
57
	}
58
59
	public function getQueryMode( $context ) {
60
		return ( $context == SMWQueryProcessor::SPECIAL_PAGE ) ? SMWQuery::MODE_INSTANCES : SMWQuery::MODE_NONE;
61
	}
62
63
	public function getName() {
64
		return wfMessage( 'srf_printername_icalendar' )->text();
65
	}
66
67
	protected function getResultText( SMWQueryResult $res, $outputmode ) {
68
		return $outputmode == SMW_OUTPUT_FILE ? $this->getIcal( $res ) : $this->getIcalLink( $res, $outputmode );
69
	}
70
	
71
	/**
72
	 * Returns the query result in iCal.
73
	 * 
74
	 * @since 1.5.2
75
	 *  
76
	 * @param SMWQueryResult $res
77
	 * 
78
	 * @return string
79
	 */
80
	protected function getIcal( SMWQueryResult $res ) {
81
        global $wgLocalTimezone;
82
        
83
		$result = '';
84
		
85
		if ( $this->m_title == '' ) {
86
			global $wgSitename;
87
			$this->m_title = $wgSitename;
88
		}
89
		
90
		$result .= "BEGIN:VCALENDAR\r\n";
91
		$result .= "PRODID:-//SMW Project//Semantic Result Formats\r\n";
92
		$result .= "VERSION:2.0\r\n";
93
		$result .= "METHOD:PUBLISH\r\n";
94
		$result .= "X-WR-CALNAME:" . $this->m_title . "\r\n";
95
		
96
		if ( $this->m_description !== '' ) {
97
			$result .= "X-WR-CALDESC:" . $this->m_description . "\r\n";
98
		}
99
		
100
		$timezone = ($wgLocalTimezone !== null) ? $wgLocalTimezone : date_default_timezone_get();
101
		$from = $to = null;
102
103
		$events = '';
104
		$row = $res->getNext();
105
		while ( $row !== false ) {
106
			$events .= $this->getIcalForItem( $row, $from, $to );
107
			
108
			$row = $res->getNext();
109
		}
110
		
111
		$result .= $this->getIcalForTimezone( $timezone, $from, $to );
112
		
113
		$result .= $events;
114
		
115
		$result .= "END:VCALENDAR\r\n";
116
117
		return $result;
118
	}
119
120
	/**
121
	 * Returns html for a link to a query that returns the iCal.
122
	 * 
123
	 * @since 1.5.2
124
	 *  
125
	 * @param SMWQueryResult $res
126
	 * @param $outputmode
127
	 * 
128
	 * @return string
129
	 */	
130
	protected function getIcalLink( SMWQueryResult $res, $outputmode ) {
131
		if ( $this->getSearchLabel( $outputmode ) ) {
132
			$label = $this->getSearchLabel( $outputmode );
133
		} else {
134
			$label = wfMessage( 'srf_icalendar_link' )->inContentLanguage()->text();
135
		}
136
		
137
		$link = $res->getQueryLink( $label );
0 ignored issues
show
Deprecated Code introduced by
The method SMWQueryResult::getQueryLink() has been deprecated with message: since SMW 1.8

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
138
		$link->setParameter( 'icalendar', 'format' );
139
		
140
		if ( $this->m_title !== '' ) {
141
			$link->setParameter( $this->m_title, 'title' );
142
		}
143
		
144
		if ( $this->m_description !== '' ) {
145
			$link->setParameter( $this->m_description, 'description' );
146
		}
147
		
148
		if ( array_key_exists( 'limit', $this->params ) ) {
149
			$link->setParameter( $this->params['limit'], 'limit' );
150
		} else { // use a reasonable default limit
151
			$link->setParameter( 20, 'limit' );
152
		}
153
154
		// yes, our code can be viewed as HTML if requested, no more parsing needed
155
		$this->isHTML = ( $outputmode == SMW_OUTPUT_HTML ); 
156
		return $link->getText( $outputmode, $this->mLinker );
157
	}
158
	
159
	/**
160
	 * Returns the iCal for a single item.
161
	 * 
162
	 * @since 1.5.2
163
	 * 
164
	 * @param array $row
165
	 * 
166
	 * @return string
167
	 */
168
	protected function getIcalForItem( array $row, &$from, &$to ) {
169
		$result = '';
170
		
171
		$wikipage = $row[0]->getResultSubject(); // get the object
172
		$wikipage = SMWDataValueFactory::newDataItemValue( $wikipage, null );
173
174
		$params = [
175
			'summary' => $wikipage->getShortWikiText()
176
		];
177
		
178
		foreach ( $row as /* SMWResultArray */ $field ) {
179
			// later we may add more things like a generic
180
			// mechanism to add whatever you want :)
181
			// could include funny things like geo, description etc. though
182
			$req = $field->getPrintRequest();
183
			$label = strtolower( $req->getLabel() );
184
			
185
			switch ( $label ) {
186
				case 'start': case 'end':
187
					if ( $req->getTypeID() == '_dat' ) {
188
						$dataValue = $field->getNextDataValue();
189
190
						if ( $dataValue === false ) {
191
							unset( $params[$label] );
192
						}
193
						else {
194
							$params[$label] = $this->parsedate( $dataValue, $label == 'end' );
195
							
196
							$timestamp = strtotime( $params[$label] );
197
							if ( $from === null || $timestamp < $from )
198
								$from = $timestamp;
199
							if ( $to === null || $timestamp > $to )
200
								$to = $timestamp;
201
						}
202
					}
203
					break;
204
				case 'location': case 'description': case 'summary':
205
					$value = $field->getNextDataValue();
206
					if ( $value !== false ) {
207
						$params[$label] = $value->getShortWikiText();
208
					}
209
					break;
210
			}
211
		}
212
		
213
		$title = $wikipage->getTitle();
214
		$article = new Article( $title );
215
		$url = $title->getFullURL();
216
		
217
		$result .= "BEGIN:VEVENT\r\n";
218
		$result .= "SUMMARY:" . $this->escapeICalendarText( $params['summary'] ) . "\r\n";
219
		$result .= "URL:$url\r\n";
220
		$result .= "UID:$url\r\n";
221
		
222
		if ( array_key_exists( 'start', $params ) ) $result .= "DTSTART:" . $params['start'] . "\r\n";
223
		if ( array_key_exists( 'end', $params ) )   $result .= "DTEND:" . $params['end'] . "\r\n";
224
		if ( array_key_exists( 'location', $params ) ) {
225
			$result .= "LOCATION:" . $this->escapeICalendarText( $params['location'] ) . "\r\n";
226
		}
227
		if ( array_key_exists( 'description', $params ) ) {
228
			$result .= "DESCRIPTION:" . $this->escapeICalendarText( $params['description'] ) . "\r\n";
229
		}
230
		
231
		$t = strtotime( str_replace( 'T', ' ', $article->getTimestamp() ) );
232
		$result .= "DTSTAMP:" . date( "Ymd", $t ) . "T" . date( "His", $t ) . "\r\n";
233
		$result .= "SEQUENCE:" . $title->getLatestRevID() . "\r\n";
234
		$result .= "END:VEVENT\r\n";
235
		
236
		return $result;
237
	}
238
239
	/**
240
	 * Extract a date string formatted for iCalendar from a SMWTimeValue object.
241
	 */
242
	static private function parsedate( SMWTimeValue $dv, $isend = false ) {
243
		$year = $dv->getYear();
244
		if ( ( $year > 9999 ) || ( $year < -9998 ) ) return ''; // ISO range is limited to four digits
245
		
246
		$year = number_format( $year, 0, '.', '' );
247
		$time = str_replace( ':', '', $dv->getTimeString( false ) );
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
248
		
249
		if ( ( $time == false ) && ( $isend ) ) { // increment by one day, compute date to cover leap years etc.
250
			$dv = SMWDataValueFactory::newTypeIDValue( '_dat', $dv->getWikiValue() . 'T00:00:00-24:00' );
251
		}
252
		
253
		$month = $dv->getMonth();
254
		if ( strlen( $month ) == 1 ) $month = '0' . $month;
255
		
256
		$day = $dv->getDay();
257
		if ( strlen( $day ) == 1 ) $day = '0' . $day;
258
		
259
		$result = $year . $month . $day;
260
		
261
		if ( $time != false ) $result .= "T$time";
262
		
263
		return $result;
264
	}
265
	
266
	/**
267
	 * Generate all the timezone's transitions that are needed by the events.
268
	 *
269
	 * @param string $tzid The timezone identifier (e.g. Europe/London)
270
	 * @param int $from The minimum timestamp in the list of events
271
	 * @param int $to The maximum timestamp in the list of events
272
	 */
273
	protected function getIcalForTimezone( $tzid, $from, $to ) {
274
		if ( $from === null || $to === null )
275
			return false;
276
			
277
		try {
278
			$timezone = new DateTimeZone( $tzid );
279
		} catch( Exception $e ) {
280
			return false;
281
		}
282
		
283
		$transitions = $timezone->getTransitions();
284
		
285
		$min = 0;
286
		$max = 1;
287
		foreach ( $transitions as $i => $transition ) {
288
			if ( $transition['ts'] < $from ) {
289
				$min = $i;
290
				continue;
291
			}
292
293
			if ( $transition['ts'] > $to ) {
294
				$max = $i;
295
				break;
296
			}
297
		}
298
		
299
		// cf. http://www.kanzaki.com/docs/ical/vtimezone.html
300
		$result = "BEGIN:VTIMEZONE\r\n";
301
		$result .= "TZID:$tzid\r\n";
302
		
303
		$transition = ( $min > 0 ) ? $transitions[$min-1] : $transitions[0];
304
		$tzfrom = $transition['offset'] / 3600;
305
		
306
		foreach ( array_slice( $transitions, $min, $max - $min ) as $transition) {
307
			$dst = ( $transition['isdst'] ) ? "DAYLIGHT" : "STANDARD";
308
			$result .= "BEGIN:$dst\r\n";
309
			
310
			$start_date = date( 'Ymd\THis', $transition['ts'] );
311
			$result .= "DTSTART:$start_date\r\n";
312
			
313
			$offset = $transition['offset'] / 3600;
314
			
315
			$offset_from = $this->formatTimezoneOffset( $tzfrom );
316
			$result .= "TZOFFSETFROM:$offset_from\r\n";
317
318
			$offset_to = $this->formatTimezoneOffset( $offset );
319
			$result .= "TZOFFSETTO:$offset_to\r\n";
320
			
321
			if ( !empty( $transition['abbr'] ) )
322
				$result .= "TZNAME:{$transition['abbr']}\r\n";
323
			
324
			$result .= "END:$dst\r\n";
325
			
326
			$tzfrom = $offset;
327
		}
328
		
329
		$result .= "END:VTIMEZONE\r\n";
330
		
331
		return $result;
332
	}
333
334
	/**
335
	 * Format an integer offset to '+hhii', where hh are the hours, and ii the minutes
336
	 *
337
	 * @param int $offset
338
	 */
339
	static private function formatTimezoneOffset( $offset ) {
340
		return sprintf('%s%02d%02d', $offset >= 0 ? '+' : '', floor($offset), ($offset - floor($offset)) * 60);
341
	}
342
	
343
	/**
344
	 * Implements esaping of special characters for iCalendar properties of type TEXT. This is defined
345
	 * in RFC2445 Section 4.3.11.
346
	 */
347
	static private function escapeICalendarText( $text ) {
348
		// Note that \\ is a PHP escaped single \ here
349
		return str_replace( [ "\\", "\n", ";", "," ], [ "\\\\", "\\n", "\\;", "\\," ], $text );
350
	}
351
352
353
	/**
354
	 * @see SMWResultPrinter::getParamDefinitions
355
	 *
356
	 * @since 1.8
357
	 *
358
	 * @param $definitions array of IParamDefinition
359
	 *
360
	 * @return array of IParamDefinition|array
361
	 */
362
	public function getParamDefinitions( array $definitions ) {
363
		$params = parent::getParamDefinitions( $definitions );
364
365
		$params['title'] = [
366
			'default' => '',
367
			'message' => 'srf_paramdesc_icalendartitle',
368
		];
369
370
		$params['description'] = [
371
			'default' => '',
372
			'message' => 'srf_paramdesc_icalendardescription',
373
		];
374
375
		return $params;
376
	}
377
378
}
379