Completed
Pull Request — master (#182)
by Stephan
03:07
created

SRFExcel   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 337
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 337
rs 8.2608
c 0
b 0
f 0
wmc 40
lcom 1
cbo 5

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getMimeType() 0 3 1
A getFileName() 0 4 2
A outputAsFile() 0 8 2
A getParamDefinitions() 0 21 1
A getResultText() 0 9 2
A writeDocumentToString() 0 7 1
A populateDocumentWithQueryData() 0 7 2
A getResultFileContents() 0 23 3
A setOrAppendStringDataValue() 0 13 2
A setNumberDataValue() 0 7 1
A setQuantityDataValue() 0 14 2
A setTimeDataValue() 0 15 2
A populateDocumentWithHeaders() 0 13 3
B createExcelDocument() 0 29 3
A showLabel() 0 3 2
A readFieldValue() 0 11 3
A setValueAccordingToType() 0 12 4
A readRowData() 0 8 3
A isPHPExcelInstalled() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like SRFExcel often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SRFExcel, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SRF;
4
5
use ImagePage;
6
use SMW\FileExportPrinter;
7
use ParamProcessor\Definition\StringParam;
8
use SMWQueryResult;
9
use PHPExcel;
10
use PHPExcel_IOFactory;
11
use PHPExcel_Cell_DataType;
12
use Sanitizer;
13
use Title;
14
15
/**
16
 * @author Kim Eik
17
 * @since 1.9
18
 */
19
class SRFExcel extends FileExportPrinter {
20
21
	const HEADER_ROW_OFFSET = 1;
22
23
	/**
24
	 * @var int
25
	 */
26
	protected $rowNum;
27
28
	/**
29
	 * @var int
30
	 */
31
	protected $colNum;
32
33
	/**
34
	 * @var \PHPExcel_Worksheet
35
	 */
36
	protected $sheet;
37
38
	protected $styled = false;
39
40
	/**
41
	 * Some printers do not mainly produce embeddable HTML or Wikitext, but
42
	 * produce stand-alone files. An example is RSS or iCalendar. This function
43
	 * returns the mimetype string that this file would have, or FALSE if no
44
	 * standalone files are produced.
45
	 *
46
	 * If this function returns something other than FALSE, then the printer will
47
	 * not be regarded as a printer that displays in-line results. This is used to
48
	 * determine if a file output should be generated in Special:Ask.
49
	 *
50
	 * @since 1.8
51
	 *
52
	 * @param SMWQueryResult $queryResult
53
	 *
54
	 * @return string
55
	 */
56
	public function getMimeType( SMWQueryResult $queryResult ) {
57
		return "application/vnd.ms-excel";
58
	}
59
60
	public function getFileName( SMWQueryResult $queryResult ) {
61
62
		return $this->params[ 'filename' ] ? $this->params[ 'filename' ] : round( microtime( true ) * 1000 ) . '.xls';
63
	}
64
65
	public function outputAsFile( SMWQueryResult $queryResult, array $params ) {
66
		if ( $this->isPHPExcelInstalled() ) {
67
			parent::outputAsFile( $queryResult, $params );
68
		} else {
69
			header( 'Cache-Control: no-store, no-cache, must-revalidate' );
70
			echo $this->getResult( $queryResult, $params, SMW_OUTPUT_FILE );
71
		}
72
	}
73
74
	/**
75
	 * @param $definitions \ParamProcessor\ParamDefinition[]
76
	 *
77
	 * @return array
78
	 */
79
	public function getParamDefinitions( array $definitions ) {
80
		$params = parent::getParamDefinitions( $definitions );
81
82
		$definitions[ 'searchlabel' ]->setDefault( wfMessage( 'srf-excel-link' )->inContentLanguage()->text() );
83
84
		$params[ 'templatefile' ] = array(
85
			'type' => 'string',
86
			'name' => 'templatefile',
87
			'default' => '',
88
			'message' => 'srf-paramdesc-excel-templatefile',
89
		);
90
91
		$params[ 'filename' ] = array(
92
			'type' => 'string',
93
			'name' => 'filename',
94
			'default' => '',
95
			'message' => 'srf-paramdesc-excel-filename',
96
		);
97
98
		return $params;
99
	}
100
101
	/**
102
	 * Return serialised results in specified format.
103
	 */
104
	protected function getResultText( SMWQueryResult $res, $outputMode ) {
105
106
		if ( $outputMode === SMW_OUTPUT_FILE ) {
107
			return $this->getResultFileContents( $res );
108
		}
109
110
		$this->isHTML = ( $outputMode === SMW_OUTPUT_HTML );
111
		return $this->getLink( $res, $outputMode )->getText( $outputMode, $this->mLinker );
112
	}
113
114
	/*
115
	 * Turns the PHPExcel document object into a string
116
	 */
117
	/**
118
	 * @param $document
119
	 * @return string
120
	 */
121
	protected function writeDocumentToString( $document ) {
122
		$objWriter = PHPExcel_IOFactory::createWriter( $document, 'Excel5' );
123
124
		ob_start();
125
		$objWriter->save('php://output');
126
		return ob_get_clean();
127
	}
128
129
	/**
130
	 * Populates the PHPExcel document with the query data
131
	 *
132
	 * @param $res SMWQueryResult the query result
133
	 */
134
	protected function populateDocumentWithQueryData( $res ) {
135
		while ( $row = $res->getNext() ) {
136
			$this->rowNum++;
137
			$this->colNum = 0;
138
			$this->readRowData($row);
139
		}
140
	}
141
142
	/**
143
	 * @param SMWQueryResult $res
144
	 * @return string
145
	 */
146
	protected function getResultFileContents( SMWQueryResult $res ) {
147
		if ( !$this->isPHPExcelInstalled() ) {
148
			throw new \RuntimeException( wfMessage( 'srf-excel-missing-phpexcel' )->parse() );
149
		}
150
151
		$document = $this->createExcelDocument();
152
		$this->sheet = $document->getSheet( 0 );
153
154
		$this->rowNum = 0;
155
		//Get headers
156
		if ( $this->mShowHeaders ) {
157
			$this->populateDocumentWithHeaders( $res );
158
			$this->rowNum++;
159
		}
160
161
		//Get data rows
162
		$this->populateDocumentWithQueryData( $res );
163
164
		$document->getActiveSheet()->getDefaultRowDimension()->setRowHeight();
165
166
		$result = $this->writeDocumentToString( $document );
167
		return $result;
168
	}
169
170
	/**
171
	 * Sets or appends a string value at the given col,row location
172
	 *
173
	 * If there already exists a value at a given col,row location, then
174
	 * convert the cell to a string and append the data value. Creating
175
	 * a list of comma separated entries.
176
	 *
177
	 * @param $object \SMWDataValue the raw data value object
178
	 */
179
	protected function setOrAppendStringDataValue( $object ) {
180
		$type = PHPExcel_Cell_DataType::TYPE_STRING;
181
		$value = $object->getWikiValue();
182
		$value = Sanitizer::decodeCharReferences( $value );
183
		$value = PHPExcel_Cell_DataType::checkString( $value );
184
185
		$cell = $this->sheet->getCellByColumnAndRow( $this->colNum, $this->rowNum );
186
		$existingValue = $cell->getValue();
187
		if ( $existingValue ) {
188
			$value = $existingValue . ', ' . $value;
189
		}
190
		$cell->setValueExplicit( $value, $type );
191
	}
192
193
	/**
194
	 * Sets a numeric value at the given col,row location
195
	 *
196
	 * @param $object \SMWDataValue the raw data value object
197
	 */
198
	protected function setNumberDataValue( $object ) {
199
		$type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
200
		$value = $object->getDataItem()->getNumber();
201
202
		$this->sheet->getCellByColumnAndRow( $this->colNum, $this->rowNum )
203
			->setValueExplicit( $value, $type );
204
	}
205
206
	/**
207
	 * Sets a quantity value at the given col,row location
208
	 *
209
	 * @param $object \SMWDataValue  the raw data value object
210
	 */
211
	protected function setQuantityDataValue( $object ) {
212
		$type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
213
		$unit = $object->getUnit();
214
		$value = $object->getNumber();
215
216
		$this->sheet->getCellByColumnAndRow( $this->colNum, $this->rowNum )
217
			->setValueExplicit( $value, $type );
218
219
		if ( !$this->styled ) {
220
			$this->sheet->getStyleByColumnAndRow( $this->colNum, $this->rowNum )
221
				->getNumberFormat()
222
				->setFormatCode( '0 "' . $unit . '"' );
223
		}
224
	}
225
226
	/**
227
	 * Sets a date/time value at the given col,row location
228
	 *
229
	 * @param \SMWTimeValue $object the raw data value object
230
	 */
231
	protected function setTimeDataValue ( \SMWTimeValue $object ) {
232
		$type = PHPExcel_Cell_DataType::TYPE_NUMERIC;
233
		$value = \PHPExcel_Shared_Date::stringToExcel( str_replace( 'T', ' ', $object->getISO8601Date() ) );
234
235
		$this->sheet
236
			->getCellByColumnAndRow( $this->colNum, $this->rowNum )
237
			->setValueExplicit( $value, $type );
238
239
		if ( !$this->styled ) {
240
			$this->sheet
241
				->getStyleByColumnAndRow( $this->colNum, $this->rowNum )
242
				->getNumberFormat()
243
				->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_DATE_DDMMYYYY);
244
		}
245
	}
246
247
	/**
248
	 * Populates the PHPExcel sheet with the headers from the result query
249
	 *
250
	 * @param SMWQueryResult $res The query result
251
	 */
252
	protected function populateDocumentWithHeaders( SMWQueryResult $res ) {
253
		$this->colNum = 0;
254
		foreach ( $res->getPrintRequests() as $pr ) {
255
			$header = $pr->getLabel();
256
			if($this->showLabel($header) ){
257
				$this->sheet->setCellValueByColumnAndRow( $this->colNum, self::HEADER_ROW_OFFSET, $header )
258
					->getStyleByColumnAndRow( $this->colNum, self::HEADER_ROW_OFFSET )
259
					->getFont()
260
					->setBold( true );
261
				$this->colNum++;
262
			}
263
		}
264
	}
265
266
	/**
267
	 * Creates a new PHPExcel document and returns it
268
	 *
269
	 * @return PHPExcel
270
	 */
271
	protected function createExcelDocument() {
272
273
		$fileTitle = Title::newFromText( $this->params[ 'templatefile' ], NS_FILE );
274
275
		if ( $fileTitle !== null && $fileTitle->exists() ) {
276
277
			$filePage = new ImagePage( $fileTitle, $this );
278
279
			$virtualFile = $filePage->getDisplayedFile();
280
			$virtualFilePath =  $virtualFile->getPath();
281
282
			$localFile= $virtualFile->getRepo()->getLocalReference( $virtualFilePath );
283
			$localFilePath = $localFile->getPath();
284
285
			$objPHPExcel = PHPExcel_IOFactory::load( $localFilePath );
286
287
			$this->styled = true;
288
289
		} else {
290
291
			$objPHPExcel = new PHPExcel();
292
293
		}
294
295
		// Set document properties
296
		$objPHPExcel->getProperties()->setCreator( "SemanticMediaWiki PHPExcel Export" );
297
298
		return $objPHPExcel;
299
	}
300
301
	/**
302
	 * Check for the existence of the extra mainlabel.
303
	 * @param $label
304
	 * @return bool
305
	 */
306
	private function showLabel( $label ) {
307
		return !(array_key_exists("mainlabel", $this->params) && $label === $this->params[ "mainlabel" ] . '#');
308
	}
309
310
	protected function readFieldValue( $field ) {
311
		$valueCount = 0;
312
		while ( ( $object = $field->getNextDataValue() ) !== false ) {
313
			if( $valueCount === 0 ) {
314
				$this->setValueAccordingToType($object);
315
			} else {
316
				$this->setOrAppendStringDataValue($object);
317
			}
318
			$valueCount++;
319
		}
320
	}
321
322
	/**
323
	 * Checks the type of the value, and set's it in the sheet accordingly
324
	 * @param $object
325
	 */
326
	protected function setValueAccordingToType( $object ) {
327
		//NOTE: must check against subclasses before superclasses
328
		if( $object instanceof \SMWQuantityValue ) {
329
			$this->setQuantityDataValue($object);
330
		} else if( $object instanceof \SMWNumberValue ) {
331
			$this->setNumberDataValue($object);
332
		} else if ( $object instanceof \SMWTimeValue ) {
333
			$this->setTimeDataValue( $object );
334
		} else {
335
			$this->setOrAppendStringDataValue($object);
336
		}
337
	}
338
339
	/**
340
	 * @param $row
341
	 */
342
	protected function readRowData( $row ) {
343
		foreach ( $row as $field ) {
344
			if( $this->showLabel($field->getPrintRequest()->getLabel()) ) {
345
				$this->readFieldValue($field);
346
				$this->colNum++;
347
			}
348
		}
349
	}
350
351
	private function isPHPExcelInstalled() {
352
		return class_exists( "PHPExcel" );
353
	}
354
355
}
356
357