Completed
Push — master ( 5d1976...30add5 )
by mw
13s
created

includes/queryprinters/ListResultPrinter.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SMW;
4
5
use Html;
6
use Sanitizer;
7
use SMWDataItem;
8
use SMWQueryResult;
9
use SMWResultArray;
10
11
/**
12
 * Print query results in lists.
13
 *
14
 * @author Markus Krötzsch
15
 * @ingroup SMWQuery
16
 */
17
18
/**
19
 * New implementation of SMW's printer for results in lists.
20
 * The implementation covers comma-separated lists, ordered and unordered lists.
21
 * List items may be formatted using templates, and list output can be in
22
 * multiple columns (at least for ordered and unordered lists).
23
 *
24
 * In the code below, one list item (with all extra information displayed for
25
 * it) is called a "row", while one entry in this row is called a "field" to
26
 * avoid confusion with the "columns" that we have in multi-column display.
27
 * Every field may in turn contain many "values".
28
 *
29
 * @ingroup SMWQuery
30
 */
31
class ListResultPrinter extends ResultPrinter {
32
33
	protected $mTemplate;
34
	protected $mNamedArgs;
35
	protected $mUserParam;
36
	protected $mColumns;
37
	protected $mIntroTemplate;
38
	protected $mOutroTemplate;
39
40
	/**
41
	 * The text used to start the list.
42
	 * @var string
43
	 * @since 1.9
44
	 */
45
	protected $header;
46
	/**
47
	 * The text used to end the list.
48
	 * @var string
49
	 * @since 1.9
50
	 */
51
	protected $footer;
52
	/**
53
	 * The text used to start a row in the list.
54
	 * @var string
55
	 * @since 1.9
56
	 */
57
	protected $rowstart;
58
	/**
59
	 * The text used to end a row in the list.
60
	 * @var string
61
	 * @since 1.9
62
	 */
63
	protected $rowend;
64
	/**
65
	 * The text used to separate items in the list, other than the final
66
	 * one.
67
	 * @var string
68
	 * @since 1.9
69
	 */
70
	protected $listsep;
71
	/**
72
	 * The text used to separate the last item in the list from the rest.
73
	 * @var string
74
	 * @since 1.9
75
	 */
76
	protected $finallistsep;
77
	/**
78
	 * Width (in percent) of columns in multi-column display.
79
	 * @var integer
80
	 * @since 1.9
81
	 */
82
	protected $columnWidth;
83
	/**
84
	 * Number of results per column in multi-column display.
85
	 * @var integer
86
	 * @since 1.9
87
	 */
88
	protected $rowsPerColumn;
89
	/**
90
	 * Number of results in current column in multi-column display.
91
	 * @var integer
92
	 * @since 1.9
93
	 */
94
	protected $numRowsInColumn;
95
	/**
96
	 * Number of results printed so far (equals index of result
97
	 * to print next).
98
	 * @var integer
99
	 * @since 1.9
100
	 */
101
	protected $numRows;
102
103
104
	/**
105
	 * @see SMWResultPrinter::handleParameters
106
	 *
107
	 * @since 1.6
108
	 *
109
	 * @param array $params
110
	 * @param $outputmode
111
	 */
112 23
	protected function handleParameters( array $params, $outputmode ) {
113 23
		parent::handleParameters( $params, $outputmode );
114
115 23
		$this->mTemplate = trim( $params['template'] );
116 23
		$this->mNamedArgs = $params['named args'];
117 23
		$this->mUserParam = trim( $params['userparam'] );
118 23
		$this->mColumns = !$this->isPlainlist() ? $params['columns'] : 1;
119 23
		$this->mIntroTemplate = $params['introtemplate'];
120 23
		$this->mOutroTemplate = $params['outrotemplate'];
121 23
	}
122
123
	/**
124
	 * @see SMW\ResultPrinter::getName
125
	 *
126
	 */
127
	public function getName() {
128
		// Give grep a chance to find the usages:
129
		// smw_printername_list, smw_printername_ol,smw_printername_ul, smw_printername_template
130
		return $this->getContext()->msg( 'smw_printername_' . $this->mFormat )->text();
131
	}
132
133
	/**
134
	 * @see SMW\ResultPrinter::getResultText
135
	 *
136
	 * @param SMWQueryResult $queryResult
137
	 * @param $outputMode
138
	 *
139
	 * @return string
140
	 */
141 16
	protected function getResultText( SMWQueryResult $queryResult, $outputMode ) {
142 16
		if ( $this->mFormat == 'template' && !$this->mTemplate ) {
143
			$queryResult->addErrors( array(
144
				$this->getContext()->msg( 'smw_notemplategiven' )->inContentLanguage()->text()
145
			) );
146
			return '';
147
		}
148
149 16
		$this->templateRenderer = ApplicationFactory::getInstance()->newMwCollaboratorFactory()->newWikitextTemplateRenderer();
150
151 16
		$this->initializePrintingParameters( $queryResult );
152
153 16
		$result = '';
154
155
		// Set up floating divs if there's more than one column
156 16
		if ( $this->mColumns > 1 ) {
157
			$result .= '<div style="float: left; width: ' . $this->columnWidth . '%">' . "\n";
158
		}
159
160 16
		$result .= $this->header;
161
162 16
		if ( $this->mIntroTemplate !== '' ) {
163 1
			$this->addCommonTemplateFields( $queryResult );
164 1
			$this->templateRenderer->packFieldsForTemplate( $this->mIntroTemplate );
165 1
			$result .= $this->templateRenderer->render();
166
		}
167
168 16
		while ( $row = $queryResult->getNext() ) {
169 16
			$result .= $this->getRowText( $row, $queryResult );
170
		}
171
172 16
		if ( $this->mOutroTemplate !== '' ) {
173 1
			$this->addCommonTemplateFields( $queryResult );
174 1
			$this->templateRenderer->packFieldsForTemplate( $this->mOutroTemplate );
175 1
			$result .= $this->templateRenderer->render();
176
		}
177
178
		// Make label for finding further results
179 16
		if ( $this->linkFurtherResults( $queryResult ) &&
180 16
			( $this->mFormat != 'ol' || $this->getSearchLabel( SMW_OUTPUT_WIKI ) ) ) {
181 1
			$result .= trim( $this->getFurtherResultsText( $queryResult, $outputMode ) );
182
		}
183
184 16
		$result .= $this->footer;
185
186 16
		if ( $this->mColumns > 1 ) {
187
			$result .= "</div>\n" . '<br style="clear: both" />' . "\n";
188
		}
189
190
		// Display default if the result is empty
191 16
		if ( $result == '' ) {
192 2
			$result = $this->params['default'];
193
		}
194
195 16
		return $result;
196
	}
197
198
	/**
199
	 * Initialize the internal parameters that should be used to print this
200
	 * list, and reset row counters.
201
	 *
202
	 * @since 1.9
203
	 * @param SMWQueryResult $queryResult
204
	 */
205 16
	protected function initializePrintingParameters( SMWQueryResult $queryResult ) {
206 16
		$this->numRows = 0;
207 16
		$this->numRowsInColumn = 0;
208 16
		$this->rowSortkey = '';
209
210 16
		$this->columnWidth = floor( 100 / $this->mColumns );
0 ignored issues
show
Documentation Bug introduced by
The property $columnWidth was declared of type integer, but floor(100 / $this->mColumns) is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
211 16
		$this->rowsPerColumn = ceil( $queryResult->getCount() / $this->mColumns );
0 ignored issues
show
Documentation Bug introduced by
The property $rowsPerColumn was declared of type integer, but ceil($queryResult->getCount() / $this->mColumns) is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
212
213
		// Determine mark-up strings used around list items:
214 16
		if ( $this->mFormat == 'ul' || $this->mFormat == 'ol' ) {
215 1
			$this->header = "<" . $this->mFormat . ">\n";
216 1
			$this->footer = "</" . $this->mFormat . ">\n";
217 1
			$this->rowstart = "\t<li>";
218 1
			$this->rowend = "</li>\n";
219
		} else { // "list" and "template" format
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
220 15
			$this->header = '';
221 15
			$this->footer = '';
222 15
			$this->rowstart = '';
223 15
			$this->rowend = '';
224
		}
225
226
		// Define separators for list items
227 16
		if ( $this->params['format'] !== 'template' ){
228 10
			if ( $this->params['format'] === 'list' && $this->params['sep'] === ',' ){
229
				// Make default list ", , , and "
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
230 8
				$this->listsep = ', ';
231 8
				$this->finallistsep = $this->getContext()->msg( 'smw_finallistconjunct' )->inContentLanguage()->text() . ' ';
232
			} else {
233
				// Allow "_" for encoding spaces, as documented
234 2
				$this->listsep = str_replace( '_', ' ', $this->params['sep'] );
235 10
				$this->finallistsep = $this->listsep;
236
			}
237
		} else {
238
			// No default separators for format "template"
239 6
			$this->listsep = '';
240 6
			$this->finallistsep = '';
241
		}
242 16
	}
243
244
	/**
245
	 * Get result text for one result row as part of getResultText().
246
	 *
247
	 * @since 1.9
248
	 * @param SMWResultArray[] $row
249
	 * @param SMWQueryResult $res
250
	 * @return string
251
	 */
252 16
	protected function getRowText( array $row, SMWQueryResult $res ) {
253 16
		$result = '';
254
255
		// Start new column:
256 16
		if ( $this->numRowsInColumn == $this->rowsPerColumn ) {
257
			// If it's a numbered list, and it's split
258
			// into columns, add in the 'start='
259
			// attribute so that each additional column
260
			// starts at the right place. This attribute
261
			// is actually deprecated, but it appears to
262
			// still be supported by the major browsers...
263
			if ( $this->mFormat == 'ol' ) {
264
				$header = "<ol start=\"" . ( $this->numRows + 1 ) . "\">";
265
			} else {
266
				$header = $this->header;
267
			}
268
269
			$this->numRowsInColumn = 0;
270
271
			$result .= $this->footer . '</div>' .
272
				"<div style=\"float: left; width: {$this->columnWidth}%\">" .
273
				$header;
274
		}
275
276 16
		if ( $this->mTemplate !== '' ) { // Build template code
277 7
			$this->hasTemplates = true;
278
279 7
			$this->addTemplateContentFields( $row );
280 7
			$this->addCommonTemplateFields( $res );
281 7
			$this->templateRenderer->packFieldsForTemplate( $this->mTemplate );
282
283 7
			$result .= $this->getRowStart( $res ) . $this->templateRenderer->render();
284
		} else { // Build simple list
285 9
			$content = $this->getRowListContent( $row );
286 9
			$result .= $this->getRowStart( $res ) . $content;
287
		}
288
289 16
		$result .= $this->rowend;
290 16
		$this->numRows++;
291 16
		$this->numRowsInColumn++;
292 16
		$this->rowSortkey = '';
293
294 16
		return $result;
295
	}
296
297
	/**
298
	 * Returns row start element
299
	 *
300
	 * @since 1.9
301
	 *
302
	 * @param SMWQueryResult $res
303
	 *
304
	 * @return string
305
	 */
306 16
	protected function getRowStart( SMWQueryResult $res ) {
307
308 16
		if ( $this->numRows > 0 && $this->isPlainlist() )  {
309
			// Use comma between "rows" other than the last one:
310 4
			return ( $this->numRows <= $res->getCount() ) ? $this->listsep : $this->finallistsep;
311
		}
312
313 16
		if ( $this->rowSortkey !== '' ) {
314 1
			return "\t" . Html::openElement( 'li',
315 1
				array( 'data-sortkey' => mb_substr( $this->rowSortkey, 0, 1 ) )
316
			);
317
		}
318
319 15
		return $this->rowstart;
320
	}
321
322
	/**
323
	 * Returns text for one result row, formatted as a list.
324
	 *
325
	 * @since 1.9
326
	 * @todo The inner lists of values per field should use different separators.
327
	 * @todo Some spaces are hard-coded here; should probably be part of separators.
328
	 * @bug Bad HTML tag escaping with hardcoded exceptions (for datatype _qty)
329
	 *
330
	 * @param SMWResultArray[] $row
331
	 *
332
	 * @return string
333
	 */
334 9
	protected function getRowListContent( array $row ) {
335 9
		$firstField = true; // is this the first entry in this row?
336 9
		$extraFields = false; // has anything but the first field been printed?
337 9
		$result = '';
338
339 9
		foreach ( $row as $field ) {
340 9
			$firstValue = true; // is this the first value in this field?
341
342 9
			while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
343
344
				// Add sortkey for all non-list formats
345 8
				if ( $firstField && $this->params['format'] !== 'list' &&
346 8
					$dataValue->getDataItem()->getDIType() === SMWDataItem::TYPE_WIKIPAGE  ) {
347 1
					$this->rowSortkey = StoreFactory::getStore()->getWikiPageSortKey( $dataValue->getDataItem() );
348
				}
349
350 8
				$text = $dataValue->getShortText( SMW_OUTPUT_WIKI, $this->getLinker( $firstField ) );
351
352 8
				if ( !$firstField && !$extraFields ) { // first values after first column
353 2
					$result .= ' (';
354 2
					$extraFields = true;
355 8
				} elseif ( $extraFields || !$firstValue ) {
356
					// any value after '(' or non-first values on first column
357 1
					$result .= $this->listsep . ' ';
358
				}
359
360 8
				if ( $firstValue ) { // first value in any field, print header
361 8
					$firstValue = false;
362
363 8
					if ( ( $this->mShowHeaders != SMW_HEADERS_HIDE ) && ( $field->getPrintRequest()->getLabel() !== '' ) ) {
364 2
						$result .= $field->getPrintRequest()->getText( SMW_OUTPUT_WIKI, ( $this->mShowHeaders == SMW_HEADERS_PLAIN ? null:$this->mLinker ) ) . ' ';
365
					}
366
				}
367
368
				// Display the text with tags for all non-list type outputs and
369
				// where the property is of type _qty (to ensure the highlighter
370
				// is displayed) but for others remove tags so that lists are
371
				// not distorted by unresolved in-text tags
372
				// FIXME This is a hack that limits extendibility of SMW datatypes
373
				// by giving _qty a special status that no other type can have.
374 8
				if ( $dataValue->getTypeID() === '_qty' || $this->isPlainlist() ) {
375 7
					$result .=  $text;
376
				} else {
377 1
					$result .= Sanitizer::stripAllTags( $text );
378
				}
379
			}
380
381 9
			$firstField = false;
382
		}
383 9
		if ( $extraFields ) {
384 2
			$result .= ')';
385
		}
386
387 9
		return $result;
388
	}
389
390
	/**
391
	 * Returns text for one result row, formatted as a template call.
392
	 *
393
	 * @since 1.9
394
	 *
395
	 * @param $row
396
	 *
397
	 * @return string
398
	 */
399 7
	protected function addTemplateContentFields( $row ) {
400
401 7
		foreach ( $row as $i => $field ) {
402
403 7
			$value = '';
404 7
			$fieldName = '';
405
406 7
			if ( $this->mNamedArgs ) {
407 2
				$fieldName = '?' . $field->getPrintRequest()->getLabel();
408
			}
409
410 7
			if ( $fieldName === '' || $fieldName === '?' ) {
411 7
				$fieldName = $fieldName . $i + 1;
412
			}
413
414 7
			while ( ( $text = $field->getNextText( SMW_OUTPUT_WIKI, $this->getLinker( $i == 0 ) ) ) !== false ) {
415 7
				$value .= $value === '' ? $text : $this->params['sep'] . ' ' . $text;
416
			}
417
418 7
			$this->templateRenderer->addField( $fieldName, $value );
419
		}
420
421 7
		$this->templateRenderer->addField( '#', $this->numRows );
422 7
	}
423
424 7
	protected function addCommonTemplateFields( $queryResult ) {
425
426 7
		if ( $this->mUserParam ) {
427 3
			$this->templateRenderer->addField( 'userparam', $this->mUserParam );
428
		}
429
430 7
		$this->templateRenderer->addField(
431 7
			'smw-resultquerycondition',
432 7
			$queryResult->getQuery()->getQueryString()
433
		);
434
435 7
		$this->templateRenderer->addField(
436 7
			'smw-resultquerylimit',
437 7
			$queryResult->getQuery()->getLimit()
438
		);
439
440 7
		$this->templateRenderer->addField(
441 7
			'smw-resultqueryoffset',
442 7
			$queryResult->getQuery()->getOffset()
443
		);
444 7
	}
445
446
	/**
447
	 * Get text for further results link. Used only during getResultText().
448
	 *
449
	 * @since 1.9
450
	 * @param SMWQueryResult $res
451
	 * @param integer $outputMode
452
	 * @return string
453
	 */
454 1
	protected function getFurtherResultsText( SMWQueryResult $res, $outputMode ) {
455 1
		$link = $this->getFurtherResultsLink( $res, $outputMode );
456 1
		return $this->rowstart . ' ' .
457 1
			$link->getText( SMW_OUTPUT_WIKI, $this->mLinker ) .
458 1
			$this->rowend;
459
	}
460
461 30
	protected function isPlainlist() {
462 30
		return $this->mFormat != 'ul' && $this->mFormat != 'ol';
463
	}
464
465 30
	public function getParameters() {
466 30
		$params = parent::getParameters();
467
468 30
		$params['sep'] = array(
469
			'message' => 'smw-paramdesc-sep',
470
			'default' => ',',
471
		);
472
473 30
		$params['template'] = array(
474
			'message' => 'smw-paramdesc-template',
475
			'default' => '',
476
		);
477
478 30
		$params['named args'] = array(
479
			'type' => 'boolean',
480
			'message' => 'smw-paramdesc-named_args',
481
			'default' => false,
482
		);
483
484 30
		if ( !$this->isPlainlist() ) {
485 2
			$params['columns'] = array(
486
				'type' => 'integer',
487
				'message' => 'smw-paramdesc-columns',
488
				'default' => 1,
489
				'range' => array( 1, 10 ),
490
			);
491
		}
492
493 30
		$params['userparam'] = array(
494
			'message' => 'smw-paramdesc-userparam',
495
			'default' => '',
496
		);
497
498 30
		$params['introtemplate'] = array(
499
			'message' => 'smw-paramdesc-introtemplate',
500
			'default' => '',
501
		);
502
503 30
		$params['outrotemplate'] = array(
504
			'message' => 'smw-paramdesc-outrotemplate',
505
			'default' => '',
506
		);
507
508 30
		$params['import-annotation'] = array(
509
			'message' => 'smw-paramdesc-import-annotation',
510
			'type' => 'boolean',
511
			'default' => false
512
		);
513
514 30
		return $params;
515
	}
516
}
517