Completed
Pull Request — master (#316)
by mw
20:49 queued 19:00
created

Gallery::getCarouselWidget()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 0
cts 13
cp 0
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 18
nc 4
nop 0
crap 12
1
<?php
2
3
namespace SRF;
4
5
use SMW\ResultPrinter;
6
7
use SMWQueryResult;
8
use SMWPrintRequest;
9
use SMWDataItem;
10
use SMWOutputs;
11
use SRFUtils;
12
13
use Html;
14
use Title;
15
16
/**
17
 * Result printer that outputs query results as a image gallery.
18
 *
19
 * @author Jeroen De Dauw < [email protected] >
20
 * @author mwjames
21
 * @author Rowan Rodrik van der Molen
22
 */
23
class Gallery extends ResultPrinter {
24
25
	/**
26
	 * @see SMWResultPrinter::getName
27
	 *
28
	 * @return string
29
	 */
30
	public function getName() {
31
		return $this->msg( 'srf_printername_gallery' )->text();
32
	}
33
34
	/**
35
	 * @see SMWResultPrinter::buildResult
36
	 *
37
	 * @since 1.8
38
	 *
39
	 * @param SMWQueryResult $results
40
	 *
41
	 * @return string
42
	 */
43 1
	protected function buildResult( SMWQueryResult $results ) {
44
45
		// Intro/outro are not planned to work with the widget option
46 1
		if ( ( $this->params['intro'] !== '' || $this->params['outro'] !== '' ) && $this->params['widget'] !== '' ){
47
			return $results->addErrors( [
48
				$this->msg( 'srf-error-option-mix', 'widget' )->inContentLanguage()->text()
49
			] );
50
		};
51
52 1
		return $this->getResultText( $results, $this->outputMode );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getResultT...ts, $this->outputMode); (array) is incompatible with the return type of the parent method SMW\ResultPrinter::buildResult of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
53
	}
54
55
	/**
56
	 * @see SMWResultPrinter::getResultText
57
	 *
58
	 * @param $results SMWQueryResult
59
	 * @param $outputmode integer
60
	 *
61
	 * @return string
62
	 */
63 1
	public function getResultText( SMWQueryResult $results, $outputmode ) {
0 ignored issues
show
Coding Style introduced by
getResultText uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
64
65
		// #224
66 1
		$ig = class_exists( '\TraditionalImageGallery' ) ? new \TraditionalImageGallery() : new \ImageGallery();
67
68 1
		$ig->setShowBytes( false );
69 1
		$ig->setShowFilename( false );
70 1
		$ig->setCaption( $this->mIntro ); // set caption to IQ header
71
72
		// No need for a special page to use the parser but for the "normal" page
73
		// view we have to ensure caption text is parsed correctly through the parser
74 1
		if ( !$this->isSpecialPage() ) {
75 1
			$ig->setParser( $GLOBALS['wgParser'] );
76
		}
77
78
		// Initialize
79 1
		static $statNr = 0;
80 1
		$html          = '';
81 1
		$processing    = '';
82
83 1
		if ( $this->params['widget'] == 'carousel' ) {
84
			// Carousel widget
85
			$ig->setAttributes( $this->getCarouselWidget() );
86 1
		} elseif ( $this->params['widget'] == 'slideshow' ) {
87
			// Slideshow widget
88
			$ig->setAttributes( $this->getSlideshowWidget() );
89
		} else {
90
91
			// Standard gallery attributes
92
			$attribs = [
93 1
				'id' => uniqid(),
94 1
				'class' => $this->getImageOverlay(),
95
			];
96
97 1
			$ig->setAttributes( $attribs );
98
		}
99
100
		// Only use redirects where the overlay option is not used and redirect
101
		// thumb images towards a different target
102 1
		if ( $this->params['redirects'] !== '' && !$this->params['overlay'] ){
103
			SMWOutputs::requireResource( 'ext.srf.gallery.redirect' );
104
		}
105
106
		// For the carousel widget, the perrow option should not be set
107 1
		if ( $this->params['perrow'] !== '' && $this->params['widget'] !== 'carousel' ) {
108
			$ig->setPerRow( $this->params['perrow'] );
109
		}
110
111 1
		if ( $this->params['widths'] !== '' ) {
112
			$ig->setWidths( $this->params['widths'] );
113
		}
114
115 1
		if ( $this->params['heights'] !== '' ) {
116
			$ig->setHeights( $this->params['heights'] );
117
		}
118
119 1
		$printReqLabels = [];
120 1
		$redirectType = '';
121
122
		/**
123
		 * @var SMWPrintRequest $printReq
124
		 */
125 1
		foreach ( $results->getPrintRequests() as $printReq ) {
126 1
			$printReqLabels[] = $printReq->getLabel();
127
128
			// Get redirect type
129 1
			if ( $this->params['redirects'] === $printReq->getLabel() ){
130 1
			 $redirectType = $printReq->getTypeID();
131
			}
132
		}
133
134 1
		if ( $this->params['imageproperty'] !== '' && in_array( $this->params['imageproperty'], $printReqLabels ) ||
135 1
			$this->params['redirects'] !== '' && in_array( $this->params['redirects'], $printReqLabels ) ) {
136
137
			$this->addImageProperties(
138
				$results,
139
				$ig,
140
				$this->params['imageproperty'],
141
				$this->params['captionproperty'],
142
				$this->params['redirects'],
143
				$outputmode
144
			);
145
		} else {
146 1
			$this->addImagePages( $results, $ig );
147
		}
148
149
		// SRF Global settings
150 1
		SRFUtils::addGlobalJSVariables();
151
152
		// Display a processing image as long as the DOM is no ready
153 1
		if ( $this->params['widget'] !== '' ) {
154
			$processing = SRFUtils::htmlProcessingElement();
155
		}
156
157
		// Beautify the class selector
158 1
		$class = $this->params['widget'] ?  '-' . $this->params['widget'] . ' ' : '';
159 1
		$class = $this->params['redirects'] !== '' && $this->params['overlay'] === false ? $class . ' srf-redirect' . ' ': $class;
160 1
		$class = $this->params['class'] ? $class . ' ' . $this->params['class'] : $class ;
161
162
		// Separate content from result output
163 1
		if ( !$ig->isEmpty() ) {
164
			$attribs =  [
165 1
				'class'  => 'srf-gallery' . $class,
166 1
				'data-redirect-type' => $redirectType,
167 1
				'data-ns-text' => $this->getFileNsTextForPageLanguage()
168
			];
169
170 1
			$html = Html::rawElement( 'div', $attribs, $processing . $ig->toHTML() );
171
		}
172
173
		// If available, create a link that points to further results
174 1
		if ( $this->linkFurtherResults( $results ) ) {
175
			$html .= $this->getLink( $results, SMW_OUTPUT_HTML )->getText( SMW_OUTPUT_HTML, $this->mLinker );
176
		}
177
178 1
		return [ $html, 'nowiki' => true, 'isHTML' => true ];
179
	}
180
181
	/**
182
	 * Handles queries where the images (and optionally their captions) are specified as properties.
183
	 *
184
	 * @since 1.5.3
185
	 *
186
	 * @param SMWQueryResult $results
187
	 * @param ImageGallery $ig
188
	 * @param string $imageProperty
189
	 * @param string $captionProperty
190
	 * @param string $redirectProperty
191
	 * @param $outputMode
192
	 */
193
	protected function addImageProperties( SMWQueryResult $results, &$ig, $imageProperty, $captionProperty, $redirectProperty, $outputMode ) {
194
		while ( /* array of SMWResultArray */ $rows = $results->getNext() ) { // Objects (pages)
195
			$images = [];
196
			$captions = [];
197
			$redirects = [];
198
199
			for ( $i = 0, $n = count( $rows ); $i < $n; $i++ ) { // Properties
200
				/**
201
				 * @var SMWResultArray $resultArray
202
				 * @var SMWDataValue $dataValue
203
				 */
204
				$resultArray = $rows[$i];
205
206
				$label = $resultArray->getPrintRequest()->getMode() == SMWPrintRequest::PRINT_THIS
207
					? '-' : $resultArray->getPrintRequest()->getLabel();
208
209
				// Make sure always use real label here otherwise it results in an empty array
210
				if ( $resultArray->getPrintRequest()->getLabel() == $imageProperty ) {
211
					while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) { // Property values
212
						if ( $dataValue->getTypeID() == '_wpg' ) {
213
							$images[] = $dataValue->getTitle();
214
						}
215
					}
216
				} elseif ( $label == $captionProperty ) {
217
					while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) { // Property values
218
						$captions[] = $dataValue->getShortText( $outputMode, $this->getLinker( true ) );
219
					}
220
				} elseif ( $label == $redirectProperty ) {
221
					while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) { // Property values
222
						if ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_WIKIPAGE ) {
223
							$redirects[] = $dataValue->getTitle();
224
						} elseif ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_URI  ) {
225
						  $redirects[] = $dataValue->getURL();
226
						}
227
					}
228
				}
229
			}
230
231
			// Check available matches against captions
232
			$amountMatches = count( $captions ) == count( $images );
233
			$hasCaption = $amountMatches || count( $captions ) > 0;
234
235
			// Check available matches against redirects
236
			$amountRedirects = count( $redirects ) == count( $images );
237
			$hasRedirect = $amountRedirects || count( $redirects ) > 0;
238
239
			/**
240
			 * @var Title $imgTitle
241
			 */
242
			foreach ( $images as $imgTitle ) {
243
				if ( $imgTitle->exists() ) {
244
					$imgCaption = $hasCaption ? ( $amountMatches ? array_shift( $captions ) : $captions[0] ) : '';
245
					$imgRedirect = $hasRedirect ? ( $amountRedirects ? array_shift( $redirects ) : $redirects[0] ) : '';
246
					$this->addImageToGallery( $ig, $imgTitle, $imgCaption, $imgRedirect );
247
				}
248
			}
249
		}
250
	}
251
252
	/**
253
	 * Handles queries where the result objects are image pages.
254
	 *
255
	 * @since 1.5.3
256
	 *
257
	 * @param SMWQueryResult $results
258
	 * @param ImageGallery $ig
259
	 */
260 1
	protected function addImagePages( SMWQueryResult $results, &$ig ) {
261 1
		while ( $row = $results->getNext() ) {
262
			/**
263
			 * @var SMWResultArray $firstField
264
			 */
265 1
			$firstField = $row[0];
266 1
			$nextObject = $firstField->getNextDataValue();
267
268 1
			if ( $nextObject !== false ) {
269 1
				$imgTitle = $nextObject->getTitle();
270
271
				// Ensure the title belongs to the image namespace
272 1
				if ( $imgTitle instanceof Title && $imgTitle->getNamespace() === NS_FILE ) {
0 ignored issues
show
Bug introduced by
The class Title does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
273 1
					$imgCaption = '';
274
275
					// Is there a property queried for display with ?property
276 1
					if ( isset( $row[1] ) ) {
277 1
						$imgCaption = $row[1]->getNextDataValue();
278 1
						if ( is_object( $imgCaption ) ) {
279 1
							$imgCaption = $imgCaption->getShortText( $this->outputMode, $this->getLinker( true ) );
280
						}
281
					}
282
283 1
					$this->addImageToGallery( $ig, $imgTitle, $imgCaption );
284
				}
285
			}
286
		}
287 1
	}
288
289
	/**
290
	 * Adds a single image to the gallery.
291
	 * Takes care of automatically adding a caption when none is provided and parsing it's wikitext.
292
	 *
293
	 * @since 1.5.3
294
	 *
295
	 * @param ImageGallery $ig The gallery to add the image to
296
	 * @param Title $imgTitle The title object of the page of the image
297
	 * @param string $imgCaption An optional caption for the image
298
	 * @param string $imgRedirect
299
	 */
300 1
	protected function addImageToGallery( &$ig, Title $imgTitle, $imgCaption, $imgRedirect = '' ) {
301
302 1
		if ( empty( $imgCaption ) ) {
303
			if ( $this->m_params['autocaptions'] ) {
0 ignored issues
show
Deprecated Code introduced by
The property SMW\ResultPrinter::$m_params has been deprecated with message: Use $params instead. Will be removed in 1.10.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
304
				$imgCaption = $imgTitle->getBaseText();
305
306
				if ( !$this->m_params['fileextensions'] ) {
0 ignored issues
show
Deprecated Code introduced by
The property SMW\ResultPrinter::$m_params has been deprecated with message: Use $params instead. Will be removed in 1.10.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
307
					$imgCaption = preg_replace( '#\.[^.]+$#', '', $imgCaption );
308
				}
309
			} else {
310
				$imgCaption = '';
311
			}
312
		} else {
313 1
			if ( $imgTitle instanceof Title && $imgTitle->getNamespace() == NS_FILE && !$this->isSpecialPage() ) {
0 ignored issues
show
Bug introduced by
The class Title does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
314 1
				$imgCaption = $ig->mParser->recursiveTagParse( $imgCaption );
315
			}
316
		}
317
		// Use image alt as helper for either text
318 1
		$imgAlt =  $this->params['redirects'] === '' ? $imgCaption : $imgRedirect !== '' ? $imgRedirect : '' ;
319 1
		$ig->add( $imgTitle, $imgCaption, $imgAlt );
320 1
	}
321
322
	/**
323
	 * Returns the overlay setting
324
	 *
325
	 * @since 1.8
326
	 *
327
	 * @return string
328
	 */
329 1
	private function getImageOverlay() {
330 1
		if ( array_key_exists( 'overlay', $this->params ) && $this->params['overlay'] == true ) {
331
			SMWOutputs::requireResource( 'ext.srf.gallery.overlay' );
332
			return ' srf-overlay';
333
		} else {
334 1
			return '';
335
		}
336
	}
337
338
	/**
339
	 * Init carousel widget
340
	 *
341
	 * @since 1.8
342
	 *
343
	 * @return string
344
	 */
345
	private function getCarouselWidget() {
346
347
		// Set attributes for jcarousel
348
		$dataAttribs = [
349
			'wrap' => 'both', // Whether to wrap at the first/last item (or both) and jump back to the start/end.
350
			'vertical' => 'false', // Orientation: vertical = false means horizontal
351
			'rtl' => 'false', // Directionality: rtl = false means ltr
352
		];
353
354
		// Use the perrow parameter to determine the scroll sequence.
355
		if ( empty( $this->params['perrow'] ) ) {
356
			$dataAttribs['scroll'] = 1;  // default 1
357
		} else {
358
			$dataAttribs['scroll'] = $this->params['perrow'];
359
			$dataAttribs['visible'] = $this->params['perrow'];
360
		}
361
362
		$attribs = [
363
			'id' => uniqid(),
364
			'class' => 'jcarousel jcarousel-skin-smw' . $this->getImageOverlay(),
365
			'style' => 'display:none;',
366
		];
367
368
		foreach ( $dataAttribs as $name => $value ) {
369
			$attribs['data-' . $name] = $value;
370
		}
371
372
		SMWOutputs::requireResource( 'ext.srf.gallery.carousel' );
373
374
		return $attribs;
375
	}
376
377
378
	/**
379
	 * Init slideshow widget
380
	 *
381
	 * @since 1.8
382
	 *
383
	 * @return string
384
	 */
385
	private function getSlideshowWidget() {
386
387
		$attribs = [
388
			'id'    => uniqid(),
389
			'class' => $this->getImageOverlay(),
390
			'style' => 'display:none;',
391
			'data-nav-control' => $this->params['navigation']
392
		];
393
394
		SMWOutputs::requireResource( 'ext.srf.gallery.slideshow' );
395
396
		return $attribs;
397
	}
398
399
	/**
400
	 * @see SMWResultPrinter::getParamDefinitions
401
	 *
402
	 * @since 1.8
403
	 *
404
	 * @param $definitions array of IParamDefinition
405
	 *
406
	 * @return array of IParamDefinition|array
407
	 */
408 1
	public function getParamDefinitions( array $definitions ) {
409 1
		$params = parent::getParamDefinitions( $definitions );
410
411 1
		$params['class'] = [
412
			'type' => 'string',
413
			'message' => 'srf-paramdesc-class',
414
			'default' => ''
415
		];
416
417 1
		$params['widget'] = [
418
			'type' => 'string',
419
			'default' => '',
420
			'message' => 'srf-paramdesc-widget',
421
			'values' => [ 'carousel', 'slideshow', '' ]
422
		];
423
424 1
		$params['navigation'] = [
425
			'type' => 'string',
426
			'default' => 'nav',
427
			'message' => 'srf-paramdesc-navigation',
428
			'values' => [ 'nav', 'pager', 'auto' ]
429
		];
430
431 1
		$params['overlay'] = [
432
			'type' => 'boolean',
433
			'default' => false,
434
			'message' => 'srf-paramdesc-overlay'
435
		];
436
437 1
		$params['perrow'] = [
438
			'type' => 'integer',
439
			'default' => '',
440
			'message' => 'srf_paramdesc_perrow'
441
		];
442
443 1
		$params['widths'] = [
444
			'type' => 'integer',
445
			'default' => '',
446
			'message' => 'srf_paramdesc_widths'
447
		];
448
449 1
		$params['heights'] = [
450
			'type' => 'integer',
451
			'default' => '',
452
			'message' => 'srf_paramdesc_heights'
453
		];
454
455 1
		$params['autocaptions'] = [
456
			'type' => 'boolean',
457
			'default' => true,
458
			'message' => 'srf_paramdesc_autocaptions'
459
		];
460
461 1
		$params['fileextensions'] = [
462
			'type' => 'boolean',
463
			'default' => false,
464
			'message' => 'srf_paramdesc_fileextensions'
465
		];
466
467 1
		$params['captionproperty'] = [
468
			'type' => 'string',
469
			'default' => '',
470
			'message' => 'srf_paramdesc_captionproperty'
471
		];
472
473 1
		$params['imageproperty'] = [
474
			'type' => 'string',
475
			'default' => '',
476
			'message' => 'srf_paramdesc_imageproperty'
477
		];
478
479 1
		$params['redirects'] = [
480
			'type' => 'string',
481
			'default' => '',
482
			'message' => 'srf-paramdesc-redirects'
483
		];
484
485 1
		return $params;
486
	}
487
488 1
	private function isSpecialPage() {
0 ignored issues
show
Coding Style introduced by
isSpecialPage uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
489 1
		$title = $GLOBALS['wgTitle'];
490 1
		return $title instanceof Title && $title->isSpecialPage();
0 ignored issues
show
Bug introduced by
The class Title does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
491
	}
492
493 1
	private function getFileNsTextForPageLanguage() {
0 ignored issues
show
Coding Style introduced by
getFileNsTextForPageLanguage uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
494 1
		$title = $GLOBALS['wgTitle'];
495 1
		return $title instanceof Title ? $title->getPageLanguage()->getNsText( NS_FILE ) : null;
0 ignored issues
show
Bug introduced by
The class Title does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
496
	}
497
498
}
499