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

SRFWord::getFileName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 10
Bugs 0 Features 0
Metric Value
c 10
b 0
f 0
dl 0
loc 5
rs 9.4286
cc 2
eloc 3
nc 2
nop 1
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 Title;
12
13
/**
14
 * Semantic Results Format for Microsoft Word
15
 * @licence GNU GPL v2+
16
 *
17
 * @author Wolfgang Fahl < [email protected] >
18
 * @since 2.4
19
 */
20
class SRFWord extends FileExportPrinter {
21
	/**
22
	 * set to true for debug output
23
	 */
24
	private $debug = true;
25
	
26
	// show imageinformation in the word file
27
	private $imageDebug=false;
28
	
29
	/**
30
	 *
31
	 * @var int
32
	 */
33
	private $rowNum;
34
	
35
	/**
36
	 *
37
	 * @var int
38
	 */
39
	private $colNum;
40
	
41
	/**
42
	 * Some printers do not mainly produce embeddable HTML or Wikitext, but
43
	 * produce stand-alone files.
44
	 * An example is RSS or iCalendar. This function
45
	 * returns the mimetype string that this file would have, or FALSE if no
46
	 * standalone files are produced.
47
	 *
48
	 * If this function returns something other than FALSE, then the printer will
49
	 * not be regarded as a printer that displays in-line results. This is used to
50
	 * determine if a file output should be generated in Special:Ask.
51
	 *
52
	 * @param SMWQueryResult $queryResult        	
53
	 *
54
	 * @return string
55
	 */
56
	public function getMimeType(SMWQueryResult $queryResult) {
57
		return "application/msword";
58
	}
59
	
60
	/**
61
	 * get a file name for the Word file
62
	 *
63
	 * if the filename parameter is not specified a filename is generated
64
	 * from the current time
65
	 *
66
	 * @param SMWQueryResult $queryResult        	
67
	 *
68
	 * @return string
69
	 *
70
	 */
71
	public function getFileName(SMWQueryResult $queryResult) {
72
		// the filename can be given as a parameter
73
		$l_filename = $this->params ['filename'] ? $this->params ['filename'] : round ( microtime ( true ) * 1000 ) . '.docx';
74
		return $l_filename;
75
	}
76
	
77
	/**
78
	 * output the given query result with the given params as a file
79
	 *
80
	 * @param SMWQueryResult $queryResult        	
81
	 *
82
	 * @param array $params        	
83
	 *
84
	 */
85
	public function outputAsFile(SMWQueryResult $queryResult, array $params) {
86
		if ($this->isPHPWordInstalled ()) {
87
			parent::outputAsFile ( $queryResult, $params );
88
		} else {
89
			header ( 'Cache-Control: no-store, no-cache, must-revalidate' );
90
			echo $this->getResult ( $queryResult, $params, SMW_OUTPUT_FILE );
91
		}
92
	}
93
	
94
	/**
95
	 * return the parameter definitions
96
	 * searchlabel, templatefile and filename are possible
97
	 *
98
	 * @param $definitions \ParamProcessor\ParamDefinition[]        	
99
	 *
100
	 * @return array
101
	 */
102
	public function getParamDefinitions(array $definitions) {
103
		$params = parent::getParamDefinitions ( $definitions );
104
		
105
		$definitions ['searchlabel']->setDefault ( wfMessage ( 'srf-word-link' )->inContentLanguage ()->text () );
106
		
107
		$params ['templatefile'] = new StringParam ( 'string', 'templatefile', '' );
108
		$params ['filename'] = new StringParam ( 'string', 'filename', '' );
109
		
110
		return $params;
111
	}
112
	
113
	/**
114
	 * Return serialised results in specified format.
115
	 * 
116
	 * @param $res -
117
	 *        	the query result
118
	 * @param $outputMode -
119
	 *        	how to output - HTML or to a file?
120
	 */
121
	protected function getResultText(SMWQueryResult $res, $outputMode) {
122
		if ($outputMode == SMW_OUTPUT_FILE) {
123
			if ($this->isPHPWordInstalled ()) {
124
				$document = $this->createWordDocument ();
125
				// Get data rows
126
				$this->populateDocumentWithQueryData ( $res );
127
				$result = $this->writeDocumentToString ( $document );
128
			} else {
129
				$result = wfMessage ( 'srf-word-missing-phpword' )->parse ();
130
			}
131
		} else {
132
			$result = $this->getLink ( $res, $outputMode )->getText ( $outputMode, $this->mLinker );
133
			$this->isHTML = ($outputMode == SMW_OUTPUT_HTML);
134
		}
135
		
136
		return $result;
137
	}
138
	
139
	/*
140
	 * Turns the PHPWord document object into a string
141
	 * @param document - the document
142
	 */
143
	private function writeDocumentToString($document) {
144
		$l_tempFileName = $document->save ();
145
		// write to output pipe to allow downloading the resulting document
146
		ob_start ();
147
		readfile ( $l_tempFileName );
148
		// $objWriter->save('php://output');
149
		return ob_get_clean ();
150
	}
151
	
152
	/**
153
	 * Populates the PHPWord document with the query data
154
	 *
155
	 * @param $res SMWQueryResult
156
	 *        	the query result
157
	 */
158
	private function populateDocumentWithQueryData($res) {
159
		if ($this->debug)
160
			wfDebug ( "populating Document with Query data\n" );
161
		while ( $row = $res->getNext () ) {
162
			$this->rowNum ++;
163
			$this->colNum = 0;
164
			$this->readRowData ( $row );
165
		}
166
	}
167
	
168
	/**
169
	 * get the local ImageFilePath for the given filePageTitle
170
	 * 
171
	 * @param $p_FilePageTitle -
172
	 *        	the title of the File: page without prefix
173
	 * @return the local file path to the image file
174
	 */
175
	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...
176
		$l_localFilePath = null;
177
		$l_fileTitle = Title::newFromText ( $p_FilePageTitle, NS_FILE );
178
		if ($l_fileTitle !== null && $l_fileTitle->exists ()) {
179
			if ($this->debug)
180
				wfDebug ( "got file title " . $l_fileTitle->getFullURL () . "\n" );
181
			$l_filePage = new ImagePage ( $l_fileTitle, $this );
182
			
183
			$l_virtualFile = $l_filePage->getDisplayedFile ();
184
			$l_virtualFilePath = $l_virtualFile->getPath ();
185
			
186
			$l_localFile = $l_virtualFile->getRepo ()->getLocalReference ( $l_virtualFilePath );
187
			$l_localFilePath = $l_localFile->getPath ();
188
		}
189
		return $l_localFilePath;
190
	}
191
	
192
	/**
193
	 * Creates a new PHPWord document and returns it
194
	 *
195
	 * @return PHPWord
196
	 */
197
	private function createWordDocument() {
198
		// get the templatefile pageTitle
199
		$l_templatefile = $this->params ['templatefile'];
200
		// get the local image file path for the template
201
		$l_localFilePath = $this->getImageFilePath ( $l_templatefile );
202
		if ($l_localFilePath != null) {
203
			if ($this->debug)
204
				wfDebug ( "template " . $l_templatefile . " for Word is at " . $l_localFilePath . "\n" );
205
				// see https://github.com/PHPOffice/PHPWord
206
			$this->objPHPWord = new \PhpOffice\PhpWord\PhpWord ();
207
			// the document to be saved is based on the template
208
			$this->document = $this->objPHPWord->loadTemplate ( $l_localFilePath );
209
		} else {
210
			if ($this->debug)
211
				wfDebug ( "creating word object with no template\n" );
212
				// see https://github.com/PHPOffice/PHPWord
213
			$this->objPHPWord = new \PhpOffice\PhpWord\PhpWord ();
214
			$this->document = $this->objPHPWord;
215
		}
216
		if ($this->debug)
217
			wfDebug ( "setting creator\n" );
218
			
219
			// Set document properties
220
		$l_properties = $this->objPHPWord->getDocInfo ();
221
		$l_properties->setCreator ( "SemanticMediaWiki PHPWord Export" );
222
		if ($this->debug)
223
			wfDebug ( "creator set\n" );
224
		
225
		return $this->document;
226
	}
227
	
228
	/**
229
	 * filter labels
230
	 * 
231
	 * @param
232
	 *        	$label
233
	 * @return bool
234
	 */
235
	private function showLabel($label) {
236
		$l_show = true;
237
		// filter labels
238
		// $l_show=!(array_key_exists("mainlabel", $this->params) && $label === $this->params[ "mainlabel" ] . '#');
239
		return $l_show;
240
	}
241
	
242
	/**
243
	 * check that a string starts with a given other string
244
	 * 
245
	 * @param
246
	 *        	haystack - the string to search in
247
	 * @param
248
	 *        	needle - the string to search for
249
	 */
250
	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...
251
		// search backwards starting from haystack length characters from the end
252
		return $needle === "" || strrpos ( $haystack, $needle, - strlen ( $haystack ) ) !== FALSE;
253
	}
254
	
255
	/**
256
	 * set the Value for the given name and value in the word document
257
	 * 
258
	 * @param
259
	 *        	p_name the name of the field (needle)
260
	 * @param
261
	 *        	p_value the content of the field (value)
262
	 */
263
	private function setValue($p_name, $p_value) {
264
		if ($this->debug) {
265
			wfDebug ( "setting word document field " . $p_name . " with value '" . $p_value . "' \n" );
266
		}
267
		$this->document->setValue ( $p_name, $p_value );
268
	}
269
	
270
	/**
271
	 * get the Value for the given dataValue
272
	 * 
273
	 * @param
274
	 *        	dataValue - the dataValue to read the value from
275
	 * @param
276
	 *        	plabel - the label
277
	 */
278
	private function readValue(/* SMWDataValue */ $dataValue, $plabel) {
279
		$l_value = $dataValue->getShortWikiText ();
280
		// $l_value=$dataValue->getWikiValue();
281
		$l_type = $dataValue->getTypeID ();
282
		$l_ditype = "?";
283
		$l_dataItem = $dataValue->getDataItem ();
284
		if ($l_dataItem != null) {
285
			// get the data item type
286
			$l_ditype = $l_dataItem->getDIType ();
287
		}
288
		// uppercase/lower case handling?
289
		// $l_name=strtolower($plabel);
290
		$l_name = $plabel;
291
		if ($this->debug) {
292
			wfDebug ( "readValue from field: " . $l_name . "(type:" . $l_type . "/ditype:" . $l_ditype . ")=" . $l_value . "\n" );
293
		}
294
		// check whether the field points to an image
295
		$l_isimage = false;
296
		// first check if it is a wiki page
297
		if ($l_ditype == SMWDataItem::TYPE_WIKIPAGE) {
298
			$l_title = Title::newFromText ( $l_value );
299
			$l_ns = $l_title->getNamespace ();
300
			// then check whether it's in the file namespace
301
			if ($l_ns == NS_FILE) {
302
				$l_image = $this->getImageFilePath ( $l_value );
303
				// now we should check the mime type ...
304
				// $l_finfo = new finfo(FILEINFO_MIME);
305
				// $l_mimetype = $finfo->file('path/filename');
306
				$l_mimetype = mime_content_type ( $l_image );
307
				if ($this->startsWith ( $l_mimetype, "image" )) {
308
					$l_isimage = true;
309
					if ($this->debug) {
310
						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" );
311
					}
312
				}
313
			}
314
		}
315
		if ($l_isimage) {
316
			if (method_exists($this->document, "setImageValueAlt")) {
317
				$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...
318
				if ($this->imageDebug) {
319
					$l_rid = $this->document->searchImageId ( $l_name );
320
					$l_imagefile = $this->document->getImgFileName ( $l_rid );
321
					// only for debug
322
					$this->setValue ( $l_name, $l_value . "(" . $l_rid . "/" . $l_imagefile . ")" );
323
				} else {
324
					// text field version of image label
325
					$this->setValue ( $l_name, $l_value);
326
				}
327
			}
328
		} else {
329
			$this->setValue ( $l_name, $l_value );
330
		}
331
	}
332
	
333
	/**
334
	 * read data from the given row
335
	 * 
336
	 * @param $row -
337
	 *        	SMWResultArray
338
	 */
339
	private function readRowData($row) {
340
		// loop over fields of this row
341
		foreach ( $row as /* SMWResultArray */ $field ) {
342
			// http://kontext.fraunhofer.de/haenelt/kurs/Skripten/Wiki-Anleitungen/SMW-Ausgabeschnittstellex.pdf
343
			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...
344
				// can not do this "Fatal error: Nesting level too deep - recursive dependency?
345
				// $l_fielddump = var_export($field, true);
346
				// wfDebug("fielddump=".$l_fielddump."\n");
347
			}
348
			$l_fieldinfo = $field->getPrintRequest ();
349
			$l_label = $l_fieldinfo->getLabel ();
350
			
351
			if ($this->debug) {
352
				wfDebug ( "field label=" . $l_label . "\n" );
353
			}
354
			// shall we display the content of the field with this label?
355
			if ($this->showLabel ( $l_label )) {
356
				// how many values did we set?
357
				$l_printcount = 0;
358
				while ( ( /* SMWDataValue */ $dataValue = $field->getNextDataValue ()) !== false ) {
359
					$this->readValue ( $dataValue, $l_label );
360
					$l_printcount ++;
361
				}
362
				// if no value was set yet
363
				if ($l_printcount == 0) {
364
					// set an empty entry
365
					$this->setValue ( $l_label, "" );
366
				}
367
			}
368
		}
369
	}
370
	
371
	/**
372
	 * check whether PHP Word is installed
373
	 */
374
	private function isPHPWordInstalled() {
375
		$l_result = class_exists ( "PhpOffice\PhpWord\PhpWord" );
376
		if ($this->debug) {
377
			wfDebug ( "isPhpWordInstalled: " . $l_result . "\n" );
378
		}
379
		return $l_result;
380
	}
381
}
382
383