ListResultPrinter::getRowListContent()   C
last analyzed

Complexity

Conditions 16
Paths 76

Size

Total Lines 50
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 16

Importance

Changes 0
Metric Value
cc 16
eloc 26
nc 76
nop 1
dl 0
loc 50
ccs 22
cts 22
cp 1
crap 16
rs 5.3533
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 39
	protected function handleParameters( array $params, $outputmode ) {
113 39
		parent::handleParameters( $params, $outputmode );
114
115 39
		$this->mTemplate = trim( $params['template'] );
116 39
		$this->mNamedArgs = $params['named args'];
117 39
		$this->mUserParam = trim( $params['userparam'] );
118 39
		$this->mColumns = !$this->isPlainlist() ? $params['columns'] : 1;
119 39
		$this->mIntroTemplate = $params['introtemplate'];
120 39
		$this->mOutroTemplate = $params['outrotemplate'];
121 39
	}
122
123
	/**
124
	 * @see SMW\ResultPrinter::getName
125
	 *
126
	 */
127 1
	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 1
		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 29
	protected function getResultText( SMWQueryResult $queryResult, $outputMode ) {
142 29
		if ( $this->mFormat == 'template' && !$this->mTemplate ) {
143
			$queryResult->addErrors( array(
144
				$this->getContext()->msg( 'smw_notemplategiven' )->inContentLanguage()->text()
145
			) );
146
			return '';
147
		}
148
149 29
		$this->templateRenderer = ApplicationFactory::getInstance()->newMwCollaboratorFactory()->newWikitextTemplateRenderer();
150
151 29
		$this->initializePrintingParameters( $queryResult );
152
153 29
		$result = '';
154
155
		// Set up floating divs if there's more than one column
156 29
		if ( $this->mColumns > 1 ) {
157
			$result .= '<div style="float: left; width: ' . $this->columnWidth . '%">' . "\n";
158
		}
159
160 29
		$result .= $this->header;
161
162 29
		if ( $this->mIntroTemplate !== '' ) {
163 2
			$this->addCommonTemplateFields( $queryResult );
164 2
			$this->templateRenderer->packFieldsForTemplate( $this->mIntroTemplate );
165 2
			$result .= $this->templateRenderer->render();
166
		}
167
168 29
		while ( $row = $queryResult->getNext() ) {
169 29
			$result .= $this->getRowText( $row, $queryResult );
170
		}
171
172 29
		if ( $this->mOutroTemplate !== '' ) {
173 2
			$this->addCommonTemplateFields( $queryResult );
174 2
			$this->templateRenderer->packFieldsForTemplate( $this->mOutroTemplate );
175 2
			$result .= $this->templateRenderer->render();
176
		}
177
178
		// Make label for finding further results
179 29
		if ( $this->linkFurtherResults( $queryResult ) &&
180 29
			( $this->mFormat != 'ol' || $this->getSearchLabel( SMW_OUTPUT_WIKI ) ) ) {
181 1
			$result .= trim( $this->getFurtherResultsText( $queryResult, $outputMode ) );
182
		}
183
184 29
		$result .= $this->footer;
185
186 29
		if ( $this->mColumns > 1 ) {
187
			$result .= "</div>\n" . '<br style="clear: both" />' . "\n";
188
		}
189
190
		// Display default if the result is empty
191 29
		if ( $result == '' ) {
192 2
			$result = $this->params['default'];
193
		}
194
195 29
		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 29
	protected function initializePrintingParameters( SMWQueryResult $queryResult ) {
206 29
		$this->numRows = 0;
207 29
		$this->numRowsInColumn = 0;
208 29
		$this->rowSortkey = '';
209
210 29
		$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 29
		$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 29
		if ( $this->mFormat == 'ul' || $this->mFormat == 'ol' ) {
215 2
			$this->header = "<" . $this->mFormat . ">\n";
216 2
			$this->footer = "</" . $this->mFormat . ">\n";
217 2
			$this->rowstart = "\t<li>";
218 2
			$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 28
			$this->header = '';
221 28
			$this->footer = '';
222 28
			$this->rowstart = '';
223 28
			$this->rowend = '';
224
		}
225
226
		// #2022, #2090 The system defines no default sep in order for it to decide
227
		// how to apply a separator and avoiding a regression for users who did
228
		// not choose a sep in the first place, if no separator is selected then
229
		// the list, ul, ol will use , as default
230 29
		if ( $this->params['format'] === 'list' && $this->params['sep'] === ',' ) {
231
			// 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...
232
			$this->listsep = ', ';
233
			$this->finallistsep = $this->getContext()->msg( 'smw_finallistconjunct' )->inContentLanguage()->text() . ' ';
234 29
		} elseif ( $this->params['format'] !== 'template' && $this->params['sep'] === '' ) {
235 22
			$this->listsep = ', ';
236 22
			$this->finallistsep = $this->listsep;
237 8
		} elseif ( $this->params['sep'] !== '' ) {
238
			// Allow "_" for encoding spaces, as documented
239 4
			$this->listsep = str_replace( '_', ' ', $this->params['sep'] );
240 4
			$this->finallistsep = $this->listsep;
241
		} else {
242 5
			$this->listsep = '';
243 5
			$this->finallistsep = '';
244
		}
245 29
246
		// #2329
247
		if ( $this->params['format'] === 'template' && !$this->isEnabledFeature( SMW_RF_TEMPLATE_OUTSEP ) ) {
248
			$this->listsep = '';
249
			$this->finallistsep = '';
250
		}
251
	}
252
253
	/**
254
	 * Get result text for one result row as part of getResultText().
255 29
	 *
256 29
	 * @since 1.9
257
	 * @param SMWResultArray[] $row
258
	 * @param SMWQueryResult $res
259 29
	 * @return string
260
	 */
261
	protected function getRowText( array $row, SMWQueryResult $res ) {
262
		$result = '';
263
264
		// Start new column:
265
		if ( $this->numRowsInColumn == $this->rowsPerColumn ) {
266
			// If it's a numbered list, and it's split
267
			// into columns, add in the 'start='
268
			// attribute so that each additional column
269
			// starts at the right place. This attribute
270
			// is actually deprecated, but it appears to
271
			// still be supported by the major browsers...
272
			if ( $this->mFormat == 'ol' ) {
273
				$header = "<ol start=\"" . ( $this->numRows + 1 ) . "\">";
274
			} else {
275
				$header = $this->header;
276
			}
277
278
			$this->numRowsInColumn = 0;
279 29
280 9
			$result .= $this->footer . '</div>' .
281
				"<div style=\"float: left; width: {$this->columnWidth}%\">" .
282 9
				$header;
283 9
		}
284 9
285
		if ( $this->mTemplate !== '' ) { // Build template code
286 9
			$this->hasTemplates = true;
287
288 21
			$this->addTemplateContentFields( $row );
289 21
			$this->addCommonTemplateFields( $res );
290
			$this->templateRenderer->packFieldsForTemplate( $this->mTemplate );
291
292 29
			$result .= $this->getRowStart( $res ) . $this->templateRenderer->render();
293 29
		} else { // Build simple list
294 29
			$content = $this->getRowListContent( $row );
295 29
			$result .= $this->getRowStart( $res ) . $content;
296
		}
297 29
298
		$result .= $this->rowend;
299
		$this->numRows++;
300
		$this->numRowsInColumn++;
301
		$this->rowSortkey = '';
302
303
		return $result;
304
	}
305
306
	/**
307
	 * Returns row start element
308
	 *
309 29
	 * @since 1.9
310
	 *
311 29
	 * @param SMWQueryResult $res
312
	 *
313 5
	 * @return string
314
	 */
315
	protected function getRowStart( SMWQueryResult $res ) {
316 29
317 2
		if ( $this->numRows > 0 && $this->isPlainlist() )  {
318 2
			// Use comma between "rows" other than the last one:
319
			return ( $this->numRows <= $res->getCount() ) ? $this->listsep : $this->finallistsep;
320
		}
321
322 28
		if ( $this->rowSortkey !== '' ) {
323
			return "\t" . Html::openElement( 'li',
324
				array( 'data-sortkey' => mb_substr( $this->rowSortkey, 0, 1 ) )
325
			);
326
		}
327
328
		return $this->rowstart;
329
	}
330
331
	/**
332
	 * Returns text for one result row, formatted as a list.
333
	 *
334
	 * @since 1.9
335
	 * @todo The inner lists of values per field should use different separators.
336
	 * @todo Some spaces are hard-coded here; should probably be part of separators.
337 21
	 * @bug Bad HTML tag escaping with hardcoded exceptions (for datatype _qty)
338 21
	 *
339 21
	 * @param SMWResultArray[] $row
340 21
	 *
341
	 * @return string
342 21
	 */
343 21
	protected function getRowListContent( array $row ) {
344
345 21
		$firstField = true; // is this the first entry in this row?
346
		$extraFields = false; // has anything but the first field been printed?
347
348 20
		$result = '';
349 20
		$excluded = array( 'table', 'tr', 'th', 'td', 'dl', 'dd', 'ul', 'li', 'ol' );
350 2
351
		foreach ( $row as $field ) {
352
			$firstValue = true; // is this the first value in this field?
353 20
354
			while ( ( $dataValue = $field->getNextDataValue() ) !== false ) {
355 20
356 2
				// Add sortkey for all non-list formats
357 2
				if ( $firstField && $this->params['format'] !== 'list' &&
358 20
					$dataValue->getDataItem()->getDIType() === SMWDataItem::TYPE_WIKIPAGE  ) {
359
					$this->rowSortkey = StoreFactory::getStore()->getWikiPageSortKey( $dataValue->getDataItem() );
0 ignored issues
show
Compatibility introduced by
$dataValue->getDataItem() of type object<SMWDataItem> is not a sub-type of object<SMW\DIWikiPage>. It seems like you assume a child class of the class SMWDataItem to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
360 2
				}
361
362
				$text = $dataValue->getShortText( SMW_OUTPUT_WIKI, $this->getLinker( $firstField ) );
363 20
364 20
				if ( !$firstField && !$extraFields ) { // first values after first column
365
					$result .= ' (';
366 20
					$extraFields = true;
367 2
				} elseif ( $extraFields || !$firstValue ) {
368
					// any value after '(' or non-first values on first column
369
					$result .= $this->listsep . ' ';
370
				}
371
372
				if ( $firstValue ) { // first value in any field, print header
373
					$firstValue = false;
374
375
					if ( ( $this->mShowHeaders != SMW_HEADERS_HIDE ) && ( $field->getPrintRequest()->getLabel() !== '' ) ) {
376
						$result .= $field->getPrintRequest()->getText( SMW_OUTPUT_WIKI, ( $this->mShowHeaders == SMW_HEADERS_PLAIN ? null:$this->mLinker ) ) . ' ';
377 20
					}
378 19
				}
379
380 2
				// Remove seleted tags to avoid lists are distorted by unresolved
381
				// in-text tags
382
				$result .= $this->isPlainlist() ? $text : Sanitizer::removeHTMLtags( $text, null, array(), array(), $excluded );
383
			}
384 21
385
			$firstField = false;
386 21
		}
387 2
		if ( $extraFields ) {
388
			$result .= ')';
389
		}
390 21
391
		return $result;
392
	}
393
394
	/**
395
	 * Returns text for one result row, formatted as a template call.
396
	 *
397
	 * @since 1.9
398
	 *
399
	 * @param $row
400
	 *
401
	 * @return string
402 9
	 */
403
	protected function addTemplateContentFields( $row ) {
404 9
405
		// In case the sep is used as outer sep, switch to the valuesep
406 9
		$sep = $this->isEnabledFeature( SMW_RF_TEMPLATE_OUTSEP ) && $this->mFormat === 'template' ? $this->params['valuesep'] : $this->params['sep'];
407 9
408
		foreach ( $row as $i => $field ) {
409
410 9
			$value = '';
411 3
			$fieldName = '';
412
413
			// {{{?Foo}}}
0 ignored issues
show
Unused Code Comprehensibility introduced by
78% 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...
414
			if ( $this->mNamedArgs || $this->params['template arguments'] === 'legacy'  ) {
415 9
				$fieldName = '?' . $field->getPrintRequest()->getLabel();
416 2
			}
417
418
			// {{{Foo}}}
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
419
			if ( $fieldName === '' && $this->params['template arguments'] === 'named' ) {
420 9
				$fieldName = $field->getPrintRequest()->getLabel();
421 9
			}
422
423
			// {{{1}}}
0 ignored issues
show
Unused Code Comprehensibility introduced by
88% 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...
424 9
			if ( $fieldName === '' || $fieldName === '?' || $this->params['template arguments'] === 'numbered' ) {
425 9
				$fieldName = intval( $i + 1 );
426
			}
427
428 9
			while ( ( $text = $field->getNextText( SMW_OUTPUT_WIKI, $this->getLinker( $i == 0 ) ) ) !== false ) {
429
				$value .= $value === '' ? $text : $sep . ' ' . $text;
430
			}
431 9
432 9
			$this->templateRenderer->addField( $fieldName, $value );
433
		}
434 9
435
		$this->templateRenderer->addField( '#', $this->numRows );
436 9
	}
437 3
438
	protected function addCommonTemplateFields( $queryResult ) {
439
440 9
		if ( $this->mUserParam ) {
441 9
			$this->templateRenderer->addField( 'userparam', $this->mUserParam );
442 9
		}
443
444
		$this->templateRenderer->addField(
445 9
			'smw-resultquerycondition',
446 9
			$queryResult->getQuery()->getQueryString()
447 9
		);
448
449
		$this->templateRenderer->addField(
450 9
			'smw-resultquerylimit',
451 9
			$queryResult->getQuery()->getLimit()
452 9
		);
453
454 9
		$this->templateRenderer->addField(
455
			'smw-resultqueryoffset',
456
			$queryResult->getQuery()->getOffset()
457
		);
458
	}
459
460
	/**
461
	 * Get text for further results link. Used only during getResultText().
462
	 *
463
	 * @since 1.9
464 1
	 * @param SMWQueryResult $res
465 1
	 * @param integer $outputMode
466 1
	 * @return string
467 1
	 */
468 1
	protected function getFurtherResultsText( SMWQueryResult $res, $outputMode ) {
469
		$link = $this->getFurtherResultsLink( $res, $outputMode );
470
		return $this->rowstart . ' ' .
471 49
			$link->getText( SMW_OUTPUT_WIKI, $this->mLinker ) .
472 49
			$this->rowend;
473
	}
474
475 49
	protected function isPlainlist() {
476 49
		return $this->mFormat != 'ul' && $this->mFormat != 'ol';
477
	}
478 49
479
	public function getParameters() {
480
		$params = parent::getParameters();
0 ignored issues
show
Deprecated Code introduced by
The method SMW\ResultPrinter::getParameters() has been deprecated with message: since 1.8, use getParamDefinitions instead.

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...
481
482
		$params['sep'] = array(
483 49
			'message' => 'smw-paramdesc-sep',
484
			'default' => '',
485
		);
486
487
		if ( $this->mFormat === 'template' && $this->isEnabledFeature( SMW_RF_TEMPLATE_OUTSEP ) ) {
488 49
			$params['valuesep'] = array(
489
				'message' => 'smw-paramdesc-sep',
490
				'default' => '',
491
			);
492
		}
493
494 49
		$params['template'] = array(
495
			'message' => 'smw-paramdesc-template',
496
			'default' => '',
497
		);
498
499
		$params['template arguments'] = array(
500 49
			'message' => 'smw-paramdesc-template-arguments',
501 3
			'default' => '',
502
			'values' => array( 'numbered', 'named', 'legacy' ),
503
		);
504
505
		$params['named args'] = array(
506
			'type' => 'boolean',
507
			'message' => 'smw-paramdesc-named_args',
508
			'default' => false,
509 49
		);
510
511
		if ( !$this->isPlainlist() ) {
512
			$params['columns'] = array(
513
				'type' => 'integer',
514 49
				'message' => 'smw-paramdesc-columns',
515
				'default' => 1,
516
				'range' => array( 1, 10 ),
517
			);
518
		}
519 49
520
		$params['userparam'] = array(
521
			'message' => 'smw-paramdesc-userparam',
522
			'default' => '',
523
		);
524 49
525
		$params['introtemplate'] = array(
526
			'message' => 'smw-paramdesc-introtemplate',
527
			'default' => '',
528
		);
529
530 49
		$params['outrotemplate'] = array(
531
			'message' => 'smw-paramdesc-outrotemplate',
532
			'default' => '',
533
		);
534
535
		$params['import-annotation'] = array(
536
			'message' => 'smw-paramdesc-import-annotation',
537
			'type' => 'boolean',
538
			'default' => false
539
		);
540
541
		return $params;
542
	}
543
}
544