Completed
Pull Request — master (#133)
by
unknown
03:33
created

SRFWord::readValue()   C

Complexity

Conditions 9
Paths 60

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 26
Bugs 2 Features 6
Metric Value
c 26
b 2
f 6
dl 0
loc 49
rs 5.7447
cc 9
eloc 29
nc 60
nop 2
1
<?php
2
3
namespace SRF;
4
5
use ImagePage;
6
use SMW\FileExportPrinter;
7
use ParamProcessor\Definition\StringParam;
8
use SMWQueryResult;
9
use SMWDataItem;
10
use PhpOffice\PhpWord\PhpWord;
11
use PhpOffice\PhpWord\IOFactory;
12
use Sanitizer;
13
use Title;
14
15
/**
16
 * Semantic Results Format for Microsoft Word
17
 * @licence GNU GPL v2+
18
 *
19
 * @author Wolfgang Fahl < [email protected] >
20
 * @since 2.1.3
21
 */
22
class SRFWord extends FileExportPrinter {
23
	/**
24
	 * set to true for debug output
25
	 */
26
	private $debug = true;
27
	
28
	/**
29
	 *
30
	 * @var int
31
	 */
32
	private $rowNum;
33
	
34
	/**
35
	 *
36
	 * @var int
37
	 */
38
	private $colNum;
39
	
40
	/**
41
	 * Some printers do not mainly produce embeddable HTML or Wikitext, but
42
	 * produce stand-alone files.
43
	 * An example is RSS or iCalendar. This function
44
	 * returns the mimetype string that this file would have, or FALSE if no
45
	 * standalone files are produced.
46
	 *
47
	 * If this function returns something other than FALSE, then the printer will
48
	 * not be regarded as a printer that displays in-line results. This is used to
49
	 * determine if a file output should be generated in Special:Ask.
50
	 *
51
	 * @param SMWQueryResult $queryResult        	
52
	 *
53
	 * @return string
54
	 */
55
	public function getMimeType(SMWQueryResult $queryResult) {
56
		return "application/msword";
57
	}
58
	
59
	/**
60
	 * get a file name for the Word file
61
	 *
62
	 * if the filename parameter is not specified a filename is generated
63
	 * from the current time
64
	 *
65
	 * @param SMWQueryResult $queryResult        	
66
	 *
67
	 * @return string
68
	 *
69
	 */
70
	public function getFileName(SMWQueryResult $queryResult) {
71
		// the filename can be given as a parameter
72
		$l_filename = $this->params ['filename'] ? $this->params ['filename'] : round ( microtime ( true ) * 1000 ) . '.docx';
73
		return $l_filename;
74
	}
75
	
76
	/**
77
	 * output the given query result with the given params as a file
78
	 *
79
	 * @param SMWQueryResult $queryResult        	
80
	 *
81
	 * @param array $params        	
82
	 *
83
	 */
84
	public function outputAsFile(SMWQueryResult $queryResult, array $params) {
85
		if ($this->isPHPWordInstalled ()) {
86
			parent::outputAsFile ( $queryResult, $params );
87
		} else {
88
			header ( 'Cache-Control: no-store, no-cache, must-revalidate' );
89
			echo $this->getResult ( $queryResult, $params, SMW_OUTPUT_FILE );
90
		}
91
	}
92
	
93
	/**
94
	 * return the parameter definitions
95
	 * searchlabel, templatefile and filename are possible
96
	 *
97
	 * @param $definitions \ParamProcessor\ParamDefinition[]        	
98
	 *
99
	 * @return array
100
	 */
101
	public function getParamDefinitions(array $definitions) {
102
		$params = parent::getParamDefinitions ( $definitions );
103
		
104
		$definitions ['searchlabel']->setDefault ( wfMessage ( 'srf-word-link' )->inContentLanguage ()->text () );
105
		
106
		$params ['templatefile'] = new StringParam ( 'string', 'templatefile', '' );
107
		$params ['filename'] = new StringParam ( 'string', 'filename', '' );
108
		
109
		return $params;
110
	}
111
	
112
	/**
113
	 * Return serialised results in specified format.
114
	 * 
115
	 * @param $res -
116
	 *        	the query result
117
	 * @param $outputMode -
118
	 *        	how to output - HTML or to a file?
119
	 */
120
	protected function getResultText(SMWQueryResult $res, $outputMode) {
121
		if ($outputMode == SMW_OUTPUT_FILE) {
122
			if ($this->isPHPWordInstalled ()) {
123
				$document = $this->createWordDocument ();
124
				// Get data rows
125
				$this->populateDocumentWithQueryData ( $res );
126
				$result = $this->writeDocumentToString ( $document );
127
			} else {
128
				$result = wfMessage ( 'srf-word-missing-phpword' )->parse ();
129
			}
130
		} else {
131
			$result = $this->getLink ( $res, $outputMode )->getText ( $outputMode, $this->mLinker );
132
			$this->isHTML = ($outputMode == SMW_OUTPUT_HTML);
133
		}
134
		
135
		return $result;
136
	}
137
	
138
	/*
139
	 * Turns the PHPWord document object into a string
140
	 * @param document - the document
141
	 */
142
	private function writeDocumentToString($document) {
143
		$l_tempFileName = $document->save ();
144
		// write to output pipe to allow downloading the resulting document
145
		ob_start ();
146
		readfile ( $l_tempFileName );
147
		// $objWriter->save('php://output');
148
		return ob_get_clean ();
149
	}
150
	
151
	/**
152
	 * Populates the PHPWord document with the query data
153
	 *
154
	 * @param $res SMWQueryResult
155
	 *        	the query result
156
	 */
157
	private function populateDocumentWithQueryData($res) {
158
		if ($this->debug)
159
			wfDebug ( "populating Document with Query data\n" );
160
		while ( $row = $res->getNext () ) {
161
			$this->rowNum ++;
162
			$this->colNum = 0;
163
			$this->readRowData ( $row );
164
		}
165
	}
166
	
167
	/**
168
	 * get the local ImageFilePath for the given filePageTitle
169
	 * 
170
	 * @param $p_FilePageTitle -
171
	 *        	the title of the File: page without prefix
172
	 * @return the local file path to the image file
173
	 */
174
	function getImageFilePath($p_FilePageTitle) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for getImageFilePath.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
175
		$l_localFilePath = null;
176
		$l_fileTitle = Title::newFromText ( $p_FilePageTitle, NS_FILE );
177
		if ($l_fileTitle !== null && $l_fileTitle->exists ()) {
178
			if ($this->debug)
179
				wfDebug ( "got file title " . $l_fileTitle->getFullURL () . "\n" );
180
			$l_filePage = new ImagePage ( $l_fileTitle, $this );
181
			
182
			$l_virtualFile = $l_filePage->getDisplayedFile ();
183
			$l_virtualFilePath = $l_virtualFile->getPath ();
184
			
185
			$l_localFile = $l_virtualFile->getRepo ()->getLocalReference ( $l_virtualFilePath );
186
			$l_localFilePath = $l_localFile->getPath ();
187
		}
188
		return $l_localFilePath;
189
	}
190
	
191
	/**
192
	 * Creates a new PHPWord document and returns it
193
	 *
194
	 * @return PHPWord
195
	 */
196
	private function createWordDocument() {
197
		// get the templatefile pageTitle
198
		$l_templatefile = $this->params ['templatefile'];
199
		// get the local image file path for the template
200
		$l_localFilePath = $this->getImageFilePath ( $l_templatefile );
201
		if ($l_localFilePath != null) {
202
			if ($this->debug)
203
				wfDebug ( "template " . $l_templatefile . " for Word is at " . $l_localFilePath . "\n" );
204
				// see https://github.com/PHPOffice/PHPWord
205
			$this->objPHPWord = new \PhpOffice\PhpWord\PhpWord ();
206
			// the document to be saved is based on the template
207
			$this->document = $this->objPHPWord->loadTemplate ( $l_localFilePath );
208
		} else {
209
			if ($this->debug)
210
				wfDebug ( "creating word object with no template\n" );
211
				// see https://github.com/PHPOffice/PHPWord
212
			$this->objPHPWord = new \PhpOffice\PhpWord\PhpWord ();
213
			$this->document = $this->objPHPWord;
214
		}
215
		if ($this->debug)
216
			wfDebug ( "setting creator\n" );
217
			
218
			// Set document properties
219
		$l_properties = $this->objPHPWord->getDocInfo ();
220
		$l_properties->setCreator ( "SemanticMediaWiki PHPWord Export" );
221
		if ($this->debug)
222
			wfDebug ( "creator set\n" );
223
		
224
		return $this->document;
225
	}
226
	
227
	/**
228
	 * filter labels
229
	 * 
230
	 * @param
231
	 *        	$label
232
	 * @return bool
233
	 */
234
	private function showLabel($label) {
235
		$l_show = true;
236
		// filter labels
237
		// $l_show=!(array_key_exists("mainlabel", $this->params) && $label === $this->params[ "mainlabel" ] . '#');
238
		return $l_show;
239
	}
240
	
241
	/**
242
	 * check that a string starts with a given other string
243
	 * 
244
	 * @param
245
	 *        	haystack - the string to search in
246
	 * @param
247
	 *        	needle - the string to search for
248
	 */
249
	function startsWith($haystack, $needle) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
Comprehensibility Best Practice introduced by
It is recommend to declare an explicit visibility for startsWith.

Generally, we recommend to declare visibility for all methods in your source code. This has the advantage of clearly communication to other developers, and also yourself, how this method should be consumed.

If you are not sure which visibility to choose, it is a good idea to start with the most restrictive visibility, and then raise visibility as needed, i.e. start with private, and only raise it to protected if a sub-class needs to have access, or public if an external class needs access.

Loading history...
250
		// search backwards starting from haystack length characters from the end
251
		return $needle === "" || strrpos ( $haystack, $needle, - strlen ( $haystack ) ) !== FALSE;
252
	}
253
	
254
	/**
255
	 * set the Value for the given name and value in the word document
256
	 * 
257
	 * @param
258
	 *        	p_name the name of the field (needle)
259
	 * @param
260
	 *        	p_value the content of the field (value)
261
	 */
262
	private function setValue($p_name, $p_value) {
263
		if ($this->debug) {
264
			wfDebug ( "setting word document field " . $p_name . " with value '" . $p_value . "' \n" );
265
		}
266
		$this->document->setValue ( $p_name, $p_value );
267
	}
268
	
269
	/**
270
	 * get the Value for the given dataValue
271
	 * 
272
	 * @param
273
	 *        	dataValue - the dataValue to read the value from
274
	 * @param
275
	 *        	plabel - the label
276
	 */
277
	private function readValue(/* SMWDataValue */ $dataValue, $plabel) {
278
		$l_value = $dataValue->getShortWikiText ();
279
		// $l_value=$dataValue->getWikiValue();
280
		$l_type = $dataValue->getTypeID ();
281
		$l_ditype = "?";
282
		$l_dataItem = $dataValue->getDataItem ();
283
		if ($l_dataItem != null) {
284
			// get the data item type
285
			$l_ditype = $l_dataItem->getDIType ();
286
		}
287
		// uppercase/lower case handling?
288
		// $l_name=strtolower($plabel);
289
		$l_name = $plabel;
290
		if ($this->debug) {
291
			wfDebug ( "readValue from field: " . $l_name . "(type:" . $l_type . "/ditype:" . $l_ditype . ")=" . $l_value . "\n" );
292
		}
293
		// check whether the field points to an image
294
		$l_isimage = false;
295
		// first check if it is a wiki page
296
		if ($l_ditype == SMWDataItem::TYPE_WIKIPAGE) {
297
			$l_title = Title::newFromText ( $l_value );
298
			$l_ns = $l_title->getNamespace ();
299
			// then check whether it's in the file namespace
300
			if ($l_ns == NS_FILE) {
301
				$l_image = $this->getImageFilePath ( $l_value );
302
				// now we should check the mime type ...
303
				// $l_finfo = new finfo(FILEINFO_MIME);
304
				// $l_mimetype = $finfo->file('path/filename');
305
				$l_mimetype = mime_content_type ( $l_image );
306
				if ($this->startsWith ( $l_mimetype, "image" )) {
307
					$l_isimage = true;
308
					if ($this->debug) {
309
						wfDebug ( "field " . $l_name . " points to image rom FILE namespace with title: " . $l_title->getPrefixedText () . "\n\tlocated at " . $l_image . "\n\tmimetype " . $l_mimetype . " asked for \n" );
310
					}
311
				}
312
			}
313
		}
314
		if ($l_isimage) {
315
			if ($this->debug) {
316
				$l_rid = $this->document->searchImageId ( $l_name );
317
				$l_imagefile = $this->document->getImgFileName ( $l_rid );
318
				// only for debug
319
				$this->setValue ( $l_name, $l_value . "(" . $l_rid . "/" . $l_imagefile . ")" );
320
			}
321
			$this->document->setImageValueAlt ( $l_name, $l_image );
0 ignored issues
show
Bug introduced by
The variable $l_image 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...
322
		} else {
323
			$this->setValue ( $l_name, $l_value );
324
		}
325
	}
326
	
327
	/**
328
	 * read data from the given row
329
	 * 
330
	 * @param $row -
331
	 *        	SMWResultArray
332
	 */
333
	private function readRowData($row) {
334
		// loop over fields of this row
335
		foreach ( $row as /* SMWResultArray */ $field ) {
336
			// http://kontext.fraunhofer.de/haenelt/kurs/Skripten/Wiki-Anleitungen/SMW-Ausgabeschnittstellex.pdf
337
			if ($this->debug) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
338
				// can not do this "Fatal error: Nesting level too deep - recursive dependency?
339
				// $l_fielddump = var_export($field, true);
340
				// wfDebug("fielddump=".$l_fielddump."\n");
341
			}
342
			$l_fieldinfo = $field->getPrintRequest ();
343
			$l_label = $l_fieldinfo->getLabel ();
344
			
345
			if ($this->debug) {
346
				wfDebug ( "field label=" . $l_label . "\n" );
347
			}
348
			// shall we display the content of the field with this label?
349
			if ($this->showLabel ( $l_label )) {
350
				// how many values did we set?
351
				$l_printcount = 0;
352
				while ( ( /* SMWDataValue */ $dataValue = $field->getNextDataValue ()) !== false ) {
353
					$this->readValue ( $dataValue, $l_label );
354
					$l_printcount ++;
355
				}
356
				// if no value was set yet
357
				if ($l_printcount == 0) {
358
					// set an empty entry
359
					$this->setValue ( $l_label, "" );
360
				}
361
			}
362
		}
363
	}
364
	
365
	/**
366
	 * check whether PHP Word is installed
367
	 */
368
	private function isPHPWordInstalled() {
369
		$l_result = class_exists ( "PhpOffice\PhpWord\PhpWord" );
370
		if ($this->debug) {
371
			wfDebug ( "isPhpWordInstalled: " . $l_result . "\n" );
372
		}
373
		return $l_result;
374
	}
375
}
376
377