Completed
Pull Request — master (#133)
by
unknown
06:17
created

SRFWord::writeDocumentToString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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