Check that no non-existent class is used with the "instanceof" operator
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * File holding the SRFFiltered class. |
||
5 | * |
||
6 | * @author Stephan Gambke |
||
7 | * |
||
8 | */ |
||
9 | |||
10 | namespace SRF\Filtered; |
||
11 | |||
12 | use Exception; |
||
13 | use Html; |
||
14 | use SMW\Message; |
||
15 | use SMW\Query\PrintRequest; |
||
16 | use SMW\Query\QueryLinker; |
||
17 | use SMW\Query\ResultPrinters\ResultPrinter; |
||
18 | use SMWOutputs; |
||
19 | use SMWPropertyValue; |
||
20 | use SMWQueryResult; |
||
21 | |||
22 | /** |
||
23 | * Result printer that displays results in switchable views and offers |
||
24 | * client-side (JavaScript based) filtering. |
||
25 | * |
||
26 | * This result printer is ultimately planned to replace exhibit. Currently only |
||
27 | * a list view is available. It is not yet possible to switch between views. |
||
28 | * There is also only the 'value' filter available yet. |
||
29 | * |
||
30 | * Syntax of the #ask call: |
||
31 | * (This is only a syntax example. For currently available features see the |
||
32 | * documentation of the various classes.) |
||
33 | * |
||
34 | * {{#ask:[[SomeCondition]] |
||
35 | * |? SomePrintout |+filter=value, someFutureFilter |+value filter switches=and or, disable, all, none |+someFutureFilter filter option=someOptionValue |
||
36 | * |? SomeOtherPrintout |+filter=value, someOtherFutureFilter |+someOtherFutureFilter filter option=someOptionValue |
||
37 | * |
||
38 | * |format=filtered |
||
39 | * |views=list, someFutureView, someOtherFutureView |
||
40 | * |
||
41 | * |list view type=list |
||
42 | * |list view template=ListItem |
||
43 | * |
||
44 | * |someFutureView view option=someOptionValue |
||
45 | * |
||
46 | * |someOtherFutureView view option=someOptionValue |
||
47 | * |
||
48 | * }} |
||
49 | * |
||
50 | * All format specific parameters are optional, although leaving the 'views' |
||
51 | * parameter empty probably does not make much sense. |
||
52 | * |
||
53 | */ |
||
54 | class Filtered extends ResultPrinter { |
||
55 | |||
56 | /** |
||
57 | * The available view types |
||
58 | * |
||
59 | * @var array of Strings |
||
60 | */ |
||
61 | private $mViewTypes = [ |
||
62 | 'list' => 'ListView', |
||
63 | 'calendar' => 'CalendarView', |
||
64 | 'table' => 'TableView', |
||
65 | 'map' => 'MapView', |
||
66 | ]; |
||
67 | |||
68 | /** |
||
69 | * The available filter types |
||
70 | * |
||
71 | * @var array of Strings |
||
72 | */ |
||
73 | private $mFilterTypes = [ |
||
74 | 'value' => 'ValueFilter', |
||
75 | 'distance' => 'DistanceFilter', |
||
76 | 'number' => 'NumberFilter', |
||
77 | ]; |
||
78 | |||
79 | private $viewNames; |
||
80 | private $parameters; |
||
81 | private $filtersOnTop; |
||
82 | private $printrequests; |
||
83 | |||
84 | private $parser; |
||
85 | |||
86 | /** |
||
87 | * @param string $valueList |
||
88 | * @param string $delimiter |
||
89 | * |
||
90 | * @return string[] |
||
91 | */ |
||
92 | 1 | public function getArrayFromValueList( $valueList, $delimiter = ',' ) { |
|
93 | 1 | return array_map( 'trim', explode( $delimiter, $valueList ) ); |
|
94 | } |
||
95 | |||
96 | /** |
||
97 | * @return \Parser | \StubObject | null |
||
98 | */ |
||
99 | 2 | public function getParser() { |
|
100 | |||
101 | 2 | if ( $this->parser === null ) { |
|
102 | 2 | $this->setParser( $GLOBALS['wgParser'] ); |
|
103 | } |
||
104 | |||
105 | 2 | return $this->parser; |
|
106 | } |
||
107 | |||
108 | /** |
||
109 | * @param \Parser | \StubObject $parser |
||
110 | */ |
||
111 | 2 | public function setParser( $parser ) { |
|
112 | 2 | $this->parser = $parser; |
|
113 | 2 | } |
|
114 | |||
115 | /** |
||
116 | * @return mixed |
||
117 | */ |
||
118 | public function getPrintrequests() { |
||
119 | return $this->printrequests; |
||
120 | } |
||
121 | |||
122 | public function hasTemplates( $hasTemplates = null ) { |
||
123 | $ret = $this->hasTemplates; |
||
124 | if ( is_bool( $hasTemplates ) ) { |
||
125 | $this->hasTemplates = $hasTemplates; |
||
126 | } |
||
127 | return $ret; |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * Get a human readable label for this printer. |
||
132 | * |
||
133 | * @return string |
||
134 | */ |
||
135 | public function getName() { |
||
136 | return wfMessage( 'srf-printername-filtered' )->text(); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * Does any additional parameter handling that needs to be done before the |
||
141 | * actual result is build. |
||
142 | * |
||
143 | * @param array $params |
||
144 | * @param $outputMode |
||
145 | */ |
||
146 | 2 | protected function handleParameters( array $params, $outputMode ) { |
|
147 | 2 | parent::handleParameters( $params, $outputMode ); |
|
148 | |||
149 | // // Set in SMWResultPrinter: |
||
150 | // $this->mIntro = $params['intro']; |
||
151 | // $this->mOutro = $params['outro']; |
||
152 | // $this->mSearchlabel = $params['searchlabel'] === false ? null : $params['searchlabel']; |
||
153 | // $this->mLinkFirst = true | false; |
||
154 | // $this->mLinkOthers = true | false; |
||
155 | // $this->mDefault = str_replace( '_', ' ', $params['default'] ); |
||
156 | // $this->mShowHeaders = SMW_HEADERS_HIDE | SMW_HEADERS_PLAIN | SMW_HEADERS_SHOW; |
||
157 | |||
158 | 2 | $this->mSearchlabel = null; |
|
159 | |||
160 | 2 | $this->parameters = $params; |
|
161 | 2 | $this->viewNames = explode( ',', $params['views'] ); |
|
162 | 2 | $this->filtersOnTop = $params['filter position'] === 'top'; |
|
163 | |||
164 | 2 | } |
|
165 | |||
166 | /** |
||
167 | * Return serialised results in specified format. |
||
168 | * |
||
169 | * @param SMWQueryResult $res |
||
170 | * @param $outputmode |
||
171 | * |
||
172 | * @return string |
||
173 | */ |
||
174 | 2 | protected function getResultText( SMWQueryResult $res, $outputmode ) { |
|
175 | |||
176 | // collect the query results in an array |
||
177 | /** @var ResultItem[] $resultItems */ |
||
178 | 2 | $resultItems = []; |
|
179 | 2 | while ( $row = $res->getNext() ) { |
|
180 | 2 | $resultItems[$this->uniqid()] = new ResultItem( $row, $this ); |
|
181 | 2 | usleep( 1 ); // This is ugly, but for now th opnly way to get all resultItems. See #288. |
|
182 | } |
||
183 | |||
184 | $config = [ |
||
185 | 2 | 'query' => $res->getQueryString(), |
|
186 | 'printrequests' => [], |
||
187 | 'views' => [], |
||
188 | 'data' => [], |
||
189 | ]; |
||
190 | |||
191 | 2 | list( $filterHtml, $printrequests ) = $this->getFilterHtml( $res, $resultItems ); |
|
192 | |||
193 | 2 | $this->printrequests = $printrequests; |
|
194 | 2 | $config['printrequests'] = $printrequests; |
|
195 | |||
196 | 2 | list( $viewHtml, $config ) = $this->getViewHtml( $res, $resultItems, $config ); |
|
197 | |||
198 | 2 | SMWOutputs::requireResource( 'ext.srf.filtered' ); |
|
199 | |||
200 | 2 | $id = $this->uniqid(); |
|
201 | // wrap all in a div |
||
202 | 2 | $html = '<div class="filtered-spinner"><div class="smw-overlay-spinner"></div></div>'; |
|
203 | 2 | $html .= $this->filtersOnTop ? $filterHtml . $viewHtml : $viewHtml . $filterHtml; |
|
204 | 2 | $html = Html::rawElement( 'div', [ 'class' => 'filtered ' . $id, 'id' => $id ], $html ); |
|
205 | |||
206 | 2 | $config['data'] = $this->getResultsForJs( $resultItems ); |
|
207 | |||
208 | 2 | $config['filtersOnTop'] = $this->filtersOnTop; |
|
209 | 2 | $this->addConfigToOutput( $id, $config ); |
|
210 | |||
211 | try { |
||
212 | 2 | $this->fullParams['limit']->getOriginalValue(); |
|
213 | } |
||
214 | 2 | catch ( Exception $exception ) { |
|
215 | 2 | $res->getQuery()->setLimit( 0 ); |
|
216 | } |
||
217 | |||
218 | 2 | $link = QueryLinker::get( $res->getQuery() ); |
|
219 | 2 | $link->setCaption( Message::get( "srf-filtered-noscript-link-caption" ) ); |
|
220 | 2 | $link->setParameter( 'table', 'format' ); |
|
221 | |||
222 | 2 | SMWOutputs::requireResource( 'ext.srf.filtered' ); |
|
223 | 2 | $this->registerResources( [], [ 'ext.srf.filtered' ] ); |
|
224 | |||
225 | 2 | return $html; |
|
226 | } |
||
227 | |||
228 | /** |
||
229 | * @see SMWResultPrinter::getParamDefinitions |
||
230 | * @see DefaultConfig.php of param-processor/param-processor for allowed types |
||
231 | * |
||
232 | * @since 1.8 |
||
233 | * |
||
234 | * @param $definitions array of IParamDefinition |
||
235 | * |
||
236 | * @return array of IParamDefinition|array |
||
237 | */ |
||
238 | 2 | public function getParamDefinitions( array $definitions ) { |
|
239 | 2 | $params = parent::getParamDefinitions( $definitions ); |
|
240 | |||
241 | 2 | $params[] = [ |
|
242 | // 'type' => 'string', |
||
243 | 'name' => 'views', |
||
244 | 'message' => 'srf-paramdesc-filtered-views', |
||
245 | 'default' => '', |
||
246 | // 'islist' => false, |
||
247 | ]; |
||
248 | |||
249 | 2 | $params[] = [ |
|
250 | // 'type' => 'string', |
||
251 | 'name' => 'filter position', |
||
252 | 'message' => 'srf-paramdesc-filtered-filter-position', |
||
253 | 'default' => 'top', |
||
254 | // 'islist' => false, |
||
255 | ]; |
||
256 | |||
257 | 2 | foreach ( $this->mViewTypes as $viewType ) { |
|
258 | 2 | $params = array_merge( $params, call_user_func( [ 'SRF\Filtered\View\\' . $viewType, 'getParameters' ] ) ); |
|
259 | } |
||
260 | |||
261 | 2 | return $params; |
|
262 | } |
||
263 | |||
264 | 2 | public function getLinker( $firstcol = false, $force = false ) { |
|
265 | 2 | return ( $force ) ? $this->mLinker : parent::getLinker( $firstcol ); |
|
266 | } |
||
267 | |||
268 | 2 | private function addConfigToOutput( $id, $config ) { |
|
269 | |||
270 | 2 | if ( $this->getParser()->getOutput() !== null ) { |
|
271 | 2 | $getter = [ $this->getParser()->getOutput(), 'getExtensionData' ]; |
|
272 | 2 | $setter = [ $this->getParser()->getOutput(), 'setExtensionData' ]; |
|
273 | } else { |
||
274 | $getter = [ \RequestContext::getMain()->getOutput(), 'getProperty' ]; |
||
275 | $setter = [ \RequestContext::getMain()->getOutput(), 'setProperty' ]; |
||
276 | } |
||
277 | |||
278 | 2 | $previousConfig = call_user_func( $getter, 'srf-filtered-config' ); |
|
279 | |||
280 | 2 | if ( $previousConfig === null ) { |
|
281 | 2 | $previousConfig = []; |
|
282 | } |
||
283 | |||
284 | 2 | $previousConfig[$id] = $config; |
|
285 | |||
286 | 2 | call_user_func( $setter, 'srf-filtered-config', $previousConfig ); |
|
287 | |||
288 | 2 | } |
|
289 | |||
290 | /** |
||
291 | * @param string | string[] | null $resourceModules |
||
292 | */ |
||
293 | 1 | protected function registerResourceModules( $resourceModules ) { |
|
294 | |||
295 | 1 | array_map( 'SMWOutputs::requireResource', (array)$resourceModules ); |
|
296 | 1 | } |
|
297 | |||
298 | /** |
||
299 | * @param string|null $id |
||
300 | * |
||
301 | * @return string |
||
302 | */ |
||
303 | 2 | public function uniqid( $id = null ) { |
|
304 | 2 | $hashedId = ( $id === null ) ? uniqid() : md5( $id ); |
|
305 | 2 | return base_convert( $hashedId, 16, 36 ); |
|
306 | } |
||
307 | |||
308 | /** |
||
309 | * @param ResultItem[] $result |
||
310 | * |
||
311 | * @return array |
||
312 | */ |
||
313 | 2 | protected function getResultsForJs( $result ) { |
|
314 | 2 | $resultAsArray = []; |
|
315 | 2 | foreach ( $result as $id => $row ) { |
|
316 | 2 | $resultAsArray[$id] = $row->getArrayRepresentation(); |
|
317 | } |
||
318 | 2 | return $resultAsArray; |
|
319 | } |
||
320 | |||
321 | 1 | public function addError( $errorMessage ) { |
|
322 | 1 | parent::addError( $errorMessage ); |
|
323 | 1 | } |
|
324 | |||
325 | /** |
||
326 | * @param SMWQueryResult $res |
||
327 | * @param $result |
||
328 | * |
||
329 | * @return array |
||
330 | */ |
||
331 | 2 | protected function getFilterHtml( SMWQueryResult $res, $result ) { |
|
332 | |||
333 | // prepare filter data for inclusion in HTML and JS |
||
334 | 2 | $filterHtml = ''; |
|
335 | |||
336 | 2 | $printrequests = []; |
|
337 | |||
338 | /** @var PrintRequest $printRequest */ |
||
339 | 2 | foreach ( $res->getPrintRequests() as $printRequest ) { |
|
340 | |||
341 | $prConfig = [ |
||
342 | 2 | 'mode' => $printRequest->getMode(), |
|
343 | 2 | 'label' => $printRequest->getLabel(), |
|
344 | 2 | 'outputformat' => $printRequest->getOutputFormat(), |
|
345 | 2 | 'type' => $printRequest->getTypeID(), |
|
346 | ]; |
||
347 | |||
348 | 2 | if ( $printRequest->getData() instanceof SMWPropertyValue ) { |
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
349 | 2 | $prConfig['property'] = $printRequest->getData()->getInceptiveProperty()->getKey(); |
|
350 | } |
||
351 | |||
352 | 2 | if ( filter_var( $printRequest->getParameter( 'hide' ), FILTER_VALIDATE_BOOLEAN ) ) { |
|
353 | $prConfig['hide'] = true; |
||
354 | } |
||
355 | |||
356 | 2 | $filtersParam = $printRequest->getParameter( 'filter' ); |
|
357 | |||
358 | 2 | if ( $filtersParam ) { |
|
359 | |||
360 | 1 | $filtersForPrintout = $this->getArrayFromValueList( $filtersParam ); |
|
361 | |||
362 | 1 | foreach ( $filtersForPrintout as $filterName ) { |
|
363 | |||
364 | 1 | if ( array_key_exists( $filterName, $this->mFilterTypes ) ) { |
|
365 | |||
366 | /** @var \SRF\Filtered\Filter\Filter $filter */ |
||
367 | 1 | $filterClassName = '\SRF\Filtered\Filter\\' . $this->mFilterTypes[$filterName]; |
|
368 | 1 | $filter = new $filterClassName( $result, $printRequest, $this ); |
|
369 | |||
370 | 1 | if ( $filter->isValidFilterForPropertyType() ) { |
|
371 | |||
372 | 1 | $this->registerResourceModules( $filter->getResourceModules() ); |
|
373 | |||
374 | 1 | $filterid = $this->uniqid(); |
|
375 | 1 | $filterHtml .= Html::rawElement( |
|
376 | 1 | 'div', |
|
377 | 1 | [ 'id' => $filterid, 'class' => "filtered-filter filtered-$filterName" ], |
|
378 | 1 | $filter->getResultText() |
|
379 | ); |
||
380 | |||
381 | 1 | $filterdata = $filter->getJsConfig(); |
|
382 | 1 | $filterdata['type'] = $filterName; |
|
383 | 1 | $filterdata['label'] = $printRequest->getLabel(); |
|
384 | |||
385 | 1 | $prConfig['filters'][$filterid] = $filterdata; |
|
386 | |||
387 | 1 | foreach ( $result as $row ) { |
|
388 | 1 | $row->setData( $filterid, $filter->getJsDataForRow( $row ) ); |
|
389 | } |
||
390 | } else { |
||
391 | // TODO: I18N |
||
392 | 1 | $this->addError( |
|
393 | 1 | "The '$filterName' filter can not be used on the '{$printRequest->getLabel()}' printout." |
|
394 | ); |
||
395 | } |
||
396 | |||
397 | } |
||
398 | } |
||
399 | } |
||
400 | |||
401 | 2 | $printrequests[$this->uniqid( $printRequest->getHash() )] = $prConfig; |
|
402 | } |
||
403 | |||
404 | 2 | $filterHtml .= '<div class="filtered-filter-spinner" style="display: none;"><div class="smw-overlay-spinner"></div></div>'; |
|
405 | |||
406 | // wrap filters in a div |
||
407 | 2 | $filterHtml = Html::rawElement( |
|
408 | 2 | 'div', |
|
409 | 2 | [ 'class' => 'filtered-filters', 'style' => 'display:none' ], |
|
410 | 2 | $filterHtml |
|
411 | ); |
||
412 | |||
413 | 2 | return [ $filterHtml, $printrequests ]; |
|
414 | } |
||
415 | |||
416 | /** |
||
417 | * @param SMWQueryResult $res |
||
418 | * @param $resultItems |
||
419 | * @param $config |
||
420 | * |
||
421 | * @return array |
||
422 | */ |
||
423 | 2 | protected function getViewHtml( SMWQueryResult $res, $resultItems, $config ) { |
|
424 | |||
425 | // prepare view data for inclusion in HTML and JS |
||
426 | 2 | $viewHtml = ''; |
|
427 | 2 | $viewSelectorsHtml = ''; |
|
428 | |||
429 | 2 | foreach ( $this->viewNames as $viewName ) { |
|
430 | |||
431 | // cut off the selector label (if one was specified) from the actual view name |
||
432 | 2 | $viewnameComponents = explode( '=', $viewName, 2 ); |
|
433 | |||
434 | 2 | $viewName = trim( $viewnameComponents[0] ); |
|
435 | |||
436 | 2 | if ( array_key_exists( $viewName, $this->mViewTypes ) ) { |
|
437 | |||
438 | // generate unique id |
||
439 | 2 | $viewid = $this->uniqid(); |
|
440 | |||
441 | 2 | if ( count( $viewnameComponents ) > 1 ) { |
|
442 | // a selector label was specified in the wiki text |
||
443 | $viewSelectorLabel = trim( $viewnameComponents[1] ); |
||
444 | } else { |
||
445 | // use the default selector label |
||
446 | 2 | $viewSelectorLabel = Message::get( 'srf-filtered-selectorlabel-' . $viewName ); |
|
447 | } |
||
448 | |||
449 | /** @var \SRF\Filtered\View\View $view */ |
||
450 | 2 | $viewClassName = '\SRF\Filtered\View\\' . $this->mViewTypes[$viewName]; |
|
451 | 2 | $view = new $viewClassName( $resultItems, $this->parameters, $this, $viewSelectorLabel ); |
|
452 | |||
453 | 2 | $initErrorMsg = $view->getInitError(); |
|
454 | |||
455 | 2 | if ( $initErrorMsg !== null ) { |
|
456 | 1 | $res->addErrors( [ $this->msg( $initErrorMsg )->text() ] ); |
|
457 | } else { |
||
458 | |||
459 | 1 | $this->registerResourceModules( $view->getResourceModules() ); |
|
460 | |||
461 | 1 | $viewHtml .= Html::rawElement( |
|
462 | 1 | 'div', |
|
463 | 1 | [ 'id' => $viewid, 'class' => "filtered-view filtered-$viewName $viewid" ], |
|
464 | 1 | $view->getResultText() |
|
465 | ); |
||
466 | 1 | $viewSelectorsHtml .= Html::rawElement( |
|
467 | 1 | 'div', |
|
468 | 1 | [ 'class' => "filtered-view-selector filtered-$viewName $viewid" ], |
|
469 | 1 | $viewSelectorLabel |
|
470 | ); |
||
471 | |||
472 | 1 | foreach ( $resultItems as $row ) { |
|
473 | 1 | $row->setData( $viewid, $view->getJsDataForRow( $row ) ); |
|
474 | } |
||
475 | |||
476 | 2 | $config['views'][$viewid] = array_merge( [ 'type' => $viewName ], $view->getJsConfig() ); |
|
477 | } |
||
478 | } |
||
479 | } |
||
480 | |||
481 | 2 | $viewHtml = Html::rawElement( |
|
482 | 2 | 'div', |
|
483 | 2 | [ 'class' => 'filtered-views', 'style' => 'display:none' ], |
|
484 | 2 | Html::rawElement( |
|
485 | 2 | 'div', |
|
486 | 2 | [ 'class' => 'filtered-views-selectors-container', 'style' => 'display:none' ], |
|
487 | 2 | $viewSelectorsHtml |
|
488 | ) . |
||
489 | 2 | Html::rawElement( 'div', [ 'class' => 'filtered-views-container' ], $viewHtml ) |
|
490 | ); |
||
491 | 2 | return [ $viewHtml, $config ]; |
|
492 | } |
||
493 | |||
494 | } |