Completed
Push — master ( f2b2dd...49fb6f )
by Jeroen De
75:28 queued 55:28
created

SRFDygraphs::makePageFromTitle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * A query printer that uses the dygraphs JavaScript library
5
 *
6
 * @see http://www.semantic-mediawiki.org/wiki/Help:Flot_timeseries_chart
7
 * @licence GNU GPL v2 or later
8
 *
9
 * @since 1.8
10
 *
11
 * @author mwjames
12
 */
13
class SRFDygraphs extends SMWResultPrinter {
14
15
	/**
16
	 * @see SMWResultPrinter::getName
17
	 * @return string
18
	 */
19
	public function getName() {
20
		return wfMessage( 'srf-printername-dygraphs' )->text();
21
	}
22
23
	/**
24
	 * @see SMWResultPrinter::getResultText
25
	 *
26
	 * @param SMWQueryResult $result
27
	 * @param $outputMode
28
	 *
29
	 * @return string
30
	 */
31
	protected function getResultText( SMWQueryResult $result, $outputMode ) {
32
33
		// Output mode is fixed
34
		$outputMode = SMW_OUTPUT_HTML;
35
36
		// Data processing
37
		$data = $this->getResultData( $result, $outputMode );
38
39
		// Post-data processing check
40
		if ( $data === [] ) {
41
			return $result->addErrors( [ wfMessage( 'srf-warn-empy-chart' )->inContentLanguage()->text() ] );
42
		} else {
43
			$options['sask'] = SRFUtils::htmlQueryResultLink( $this->getLink( $result, SMW_OUTPUT_HTML ) );
0 ignored issues
show
Coding Style Comprehensibility introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
44
			return $this->getFormatOutput( $data, $options );
45
		}
46
	}
47
48
	/**
49
	 * Returns an array with numerical data
50
	 *
51
	 * @since 1.8
52
	 *
53
	 * @param SMWQueryResult $result
54
	 * @param $outputMode
55
	 *
56
	 * @return array
57
	 */
58
	protected function getResultData( SMWQueryResult $result, $outputMode ) {
59
		$aggregatedValues = [];
60
		
61
		while ( $rows = $result->getNext() ) { // Objects (pages)
62
			$annotation = [];
63
			$dataSource = false;
64
65
			/**
66
			 * @var SMWResultArray $field
67
			 * @var SMWDataValue $dataValue
68
			 */
69
			foreach ( $rows as $field ) {
70
71
				// Use the subject marker to identify a possible data file
72
				$subject = $field->getResultSubject(); 
73
				if ( $this->params['datasource'] === 'file' && $subject->getTitle()->getNamespace() === NS_FILE && !$dataSource ){
74
					$aggregatedValues['subject'] = $this->makePageFromTitle( $subject->getTitle() )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) );
75
					$aggregatedValues['url'] = wfFindFile( $subject->getTitle() )->getUrl();
76
					$dataSource = true;
77
				}
78
79
				// Proceed only where a label is known otherwise items are of no use
80
				// for being a potential object identifier
81
				if ( $field->getPrintRequest()->getLabel() !== '' ){
82
					$propertyLabel = $field->getPrintRequest()->getLabel();
83
				}else{
84
					continue;
85
				}
86
87
				while ( ( $dataValue = $field->getNextDataValue() ) !== false ) { // Data values
88
89
					// Jump the column (indicated by continue) because we don't want the data source being part of the annotation array
90
					if ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'raw' && !$dataSource ){
91
						// Support data source = raw which pulls the url from a wikipage in raw format
92
						$aggregatedValues['subject'] = $this->makePageFromTitle( $dataValue->getTitle() )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) );
93
						$aggregatedValues['url'] = $dataValue->getTitle()->getLocalURL( 'action=raw' );
94
						$dataSource = true;
95
						continue;
96
					} elseif ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_WIKIPAGE && $this->params['datasource'] === 'file' && $dataValue->getTitle()->getNamespace() === NS_FILE && !$dataSource ) {
97
						// Support data source = file which pulls the url from a uploaded file
98
						$aggregatedValues['subject'] = $this->makePageFromTitle( $dataValue->getTitle() )->getLongHTMLText( $this->getLinker( $field->getResultSubject() ) );
99
						$aggregatedValues['url'] = wfFindFile( $dataValue->getTitle() )->getUrl();
100
						$dataSource = true;
101
						continue;
102
					} elseif ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_URI && $this->params['datasource'] === 'url'  && !$dataSource ){
103
						// Support data source = url, pointing to an url data source
104
						$aggregatedValues['link'] = $dataValue->getShortHTMLText( $this->getLinker( false ) );
105
						$aggregatedValues['url'] = $dataValue->getURL();
106
						$dataSource = true;
107
						continue;
108
					}
109
110
					// The annotation should adhere outlined conventions as the label identifies the array object key
111
					// series -> Required The name of the series to which the annotated point belongs
112
					// x -> Required The x value of the point
113
					// shortText -> Text that will appear as annotation flag
114
					// text -> A longer description of the annotation
115
					// @see  http://dygraphs.com/annotations.html
116
					if ( in_array( $propertyLabel, [ 'series', 'x', 'shortText', 'text' ] ) ){
117
						if ( $dataValue->getDataItem()->getDIType() == SMWDataItem::TYPE_NUMBER ){
118
							// Set unit if available
119
							$dataValue->setOutputFormat( $this->params['unit'] );
120
							// Check if unit is available
121
							$annotation[$propertyLabel] = $dataValue->getUnit() !== '' ? $dataValue->getShortWikiText() : $dataValue->getNumber() ;
122
						} else {
123
							$annotation[$propertyLabel] = $dataValue->getWikiValue();
124
						}
125
					}
126
				}
127
			}
128
			// Sum-up collected row items in a single array
129
			if ( $annotation !== [] ){
130
				$aggregatedValues['annotation'][] =  $annotation;
131
			}
132
		}
133
		return $aggregatedValues;
134
	}
135
136
	private function makePageFromTitle( \Title $title ) {
137
		$dataValue = new SMWWikiPageValue( '_wpg' );
138
		$dataItem = SMWDIWikiPage::newFromTitle( $title );
139
		$dataValue->setDataItem( $dataItem );
140
		return $dataValue;
141
	}
142
143
	/**
144
	 * Prepare data for the output
145
	 *
146
	 * @since 1.8
147
	 *
148
	 * @param array $data
149
	 *
150
	 * @return string
151
	 */
152
	protected function getFormatOutput( $data, $options ) {
153
154
		// Object count
155
		static $statNr = 0;
156
		$chartID = 'srf-dygraphs-' . ++$statNr;
157
158
		$this->isHTML = true;
159
160
		// Reorganize the raw data
161
		if ( $this->params['datasource'] === 'page' ){
162
			foreach ( $data as $key => $values ) {
163
				$dataObject[] =  [ 'label' => $key, 'data' => $values ];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$dataObject was never initialized. Although not strictly required by PHP, it is generally a good practice to add $dataObject = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
164
			}
165
		}else{
166
				$dataObject['source'] = $data;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$dataObject was never initialized. Although not strictly required by PHP, it is generally a good practice to add $dataObject = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
167
		}
168
169
		// Prepare transfer array
170
		$chartData =  [
171
			'data' => $dataObject,
0 ignored issues
show
Bug introduced by
The variable $dataObject does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
172
			'sask' => $options['sask'],
173
			'parameters' =>  [
174
				'width'        => $this->params['width'],
175
				'height'       => $this->params['height'],
176
				'xlabel'       => $this->params['xlabel'],
177
				'ylabel'       => $this->params['ylabel'],
178
				'charttitle'   => $this->params['charttitle'],
179
				'charttext'    => $this->params['charttext'],
180
				'infotext'     => $this->params['infotext'],
181
				'datasource'   => $this->params['datasource'],
182
				'rollerperiod' => $this->params['mavg'],
183
				'gridview'    => $this->params['gridview'],
184
				'errorbar'     => $this->params['errorbar'],
185
			]
186
		];
187
188
		// Array encoding and output
189
		$requireHeadItem =  [ $chartID => FormatJson::encode( $chartData ) ];
190
		SMWOutputs::requireHeadItem( $chartID, Skin::makeVariablesScript( $requireHeadItem ) );
191
192
		SMWOutputs::requireResource( 'ext.srf.dygraphs' );
193
194
		if ( $this->params['gridview'] === 'tabs' ) {
195
			SMWOutputs::requireResource( 'ext.srf.util.grid' );
196
		}
197
198
		// Chart/graph placeholder
199
		$chart = Html::rawElement(
200
			'div',
201
			['id' => $chartID, 'class' => 'container', 'style' => "display:none;" ],
202
			null
203
		);
204
205
		// Processing/loading image
206
		$processing = SRFUtils::htmlProcessingElement( $this->isHTML );
207
208
		// Beautify class selector
209
		$class = $this->params['class'] ? ' ' . $this->params['class'] : ' dygraphs-common';
210
211
		// General output marker
212
		return Html::rawElement(
213
			'div',
214
			[ 'class' => 'srf-dygraphs' . $class	],
215
			$processing . $chart
216
		);
217
	}
218
219
	/**
220
	 * @see SMWResultPrinter::getParamDefinitions
221
	 *
222
	 * @since 1.8
223
	 *
224
	 * @param $definitions array of IParamDefinition
225
	 *
226
	 * @return array of IParamDefinition|array
227
	 */
228
	public function getParamDefinitions( array $definitions ) {
229
		$params = parent::getParamDefinitions( $definitions );
230
231
		$params['datasource'] = [
232
			'message' => 'srf-paramdesc-datasource',
233
			'default' => 'file',
234
			'values' => [ 'file', 'raw', 'url' ],
235
		];
236
237
		$params['errorbar'] = [
238
			'message' => 'srf-paramdesc-errorbar',
239
			'default' => '',
240
			'values' => [ 'fraction', 'sigma', 'range' ],
241
		];
242
243
		$params['min'] = [
244
			'type' => 'integer',
245
			'message' => 'srf-paramdesc-minvalue',
246
			'default' => '',
247
		];
248
249
		$params['mavg'] = [
250
			'type' => 'integer',
251
			'message' => 'srf-paramdesc-movingaverage',
252
			'default' => 14,
253
			'lowerbound' => 0,
254
		];
255
256
		$params['gridview'] = [
257
			'message' => 'srf-paramdesc-gridview',
258
			'default' => 'none',
259
			'values' => [ 'none' , 'tabs' ],
260
		];
261
262
		$params['infotext'] = [
263
			'message' => 'srf-paramdesc-infotext',
264
			'default' => '',
265
		];
266
267
		$params['unit'] = [
268
			'message' => 'srf-paramdesc-unit',
269
			'default' => '',
270
		];
271
272
		$params['height'] = [
273
			'type' => 'integer',
274
			'message' => 'srf_paramdesc_chartheight',
275
			'default' => 400,
276
			'lowerbound' => 1,
277
		];
278
279
		$params['width'] = [
280
			'message' => 'srf_paramdesc_chartwidth',
281
			'default' => '100%',
282
		];
283
284
		$params['charttitle'] = [
285
			'message' => 'srf_paramdesc_charttitle',
286
			'default' => '',
287
		];
288
289
		$params['charttext'] = [
290
			'message' => 'srf-paramdesc-charttext',
291
			'default' => '',
292
		];
293
294
		$params['infotext'] = [
295
			'message' => 'srf-paramdesc-infotext',
296
			'default' => '',
297
		];
298
299
		$params['ylabel'] = [
300
			'message' => 'srf-paramdesc-yaxislabel',
301
			'default' => '',
302
		];
303
304
		$params['xlabel'] = [
305
			'message' => 'srf-paramdesc-xaxislabel',
306
			'default' => '',
307
		];
308
309
		$params['class'] = [
310
			'message' => 'srf-paramdesc-class',
311
			'default' => '',
312
		];
313
314
		return $params;
315
	}
316
}