Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 393 |
|
0.00% |
0 / 22 |
CRAP | |
0.00% |
0 / 1 |
PFTemplate | |
0.00% |
0 / 393 |
|
0.00% |
0 / 22 |
26406 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
newFromName | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
loadTemplateParams | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
12 | |||
getTemplateParams | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
loadTemplateFields | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
20 | |||
loadTemplateFieldsSMWAndOther | |
0.00% |
0 / 55 |
|
0.00% |
0 / 1 |
702 | |||
loadPropertySettingInTemplate | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
loadTemplateFieldsCargo | |
0.00% |
0 / 62 |
|
0.00% |
0 / 1 |
462 | |||
getCargoTableAndSchema | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
getTemplateFields | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getFieldNamed | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
setConnectingProperty | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setCategoryName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setCargoTable | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setFullWikiTextStatus | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setAggregatingInfo | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
setFormat | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
createCargoDeclareCall | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
56 | |||
createCargoStoreCall | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
createText | |
0.00% |
0 / 167 |
|
0.00% |
0 / 1 |
5402 | |||
createTextForField | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
30 | |||
printCategoryTag | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | /** |
3 | * Defines a class, PFTemplate, that represents a MediaWiki "infobox" |
4 | * template that holds structured data, which may or may not be |
5 | * additionally stored by Cargo and/or Semantic MediaWiki. |
6 | * |
7 | * @author Yaron Koren |
8 | * @file |
9 | * @ingroup PF |
10 | */ |
11 | |
12 | use MediaWiki\MediaWikiServices; |
13 | |
14 | class PFTemplate { |
15 | private $mTemplateName; |
16 | private $mTemplateText; |
17 | private $mTemplateFields; |
18 | private $mTemplateParams; |
19 | private $mConnectingProperty; |
20 | private $mCategoryName; |
21 | private $mCargoTable; |
22 | private $mAggregatingProperty; |
23 | private $mAggregationLabel; |
24 | private $mTemplateFormat; |
25 | private $mFieldStart; |
26 | private $mFieldEnd; |
27 | private $mTemplateStart; |
28 | private $mTemplateEnd; |
29 | private $mFullWikiText; |
30 | |
31 | public function __construct( $templateName, $templateFields ) { |
32 | $this->mTemplateName = $templateName; |
33 | $this->mTemplateFields = $templateFields; |
34 | } |
35 | |
36 | public static function newFromName( $templateName ) { |
37 | $template = new PFTemplate( $templateName, [] ); |
38 | $template->loadTemplateParams(); |
39 | $template->loadTemplateFields(); |
40 | return $template; |
41 | } |
42 | |
43 | /** |
44 | * Get (and store in memory) the values from this template's |
45 | * #template_params call, if it exists. |
46 | */ |
47 | public function loadTemplateParams() { |
48 | $embeddedTemplate = null; |
49 | $templateTitle = Title::makeTitleSafe( NS_TEMPLATE, $this->mTemplateName ); |
50 | if ( $templateTitle === null ) { |
51 | return; |
52 | } |
53 | $properties = MediaWikiServices::getInstance()->getPageProps()->getProperties( |
54 | [ $templateTitle ], [ 'PageFormsTemplateParams' ] |
55 | ); |
56 | if ( count( $properties ) == 0 ) { |
57 | return; |
58 | } |
59 | |
60 | $paramsForPage = reset( $properties ); |
61 | $paramsForProperty = reset( $paramsForPage ); |
62 | $this->mTemplateParams = unserialize( $paramsForProperty ); |
63 | } |
64 | |
65 | public function getTemplateParams() { |
66 | return $this->mTemplateParams; |
67 | } |
68 | |
69 | /** |
70 | * @todo - fix so that this function only gets called once per |
71 | * template; right now it seems to get called once per field. (!) |
72 | */ |
73 | function loadTemplateFields() { |
74 | $templateTitle = Title::makeTitleSafe( NS_TEMPLATE, $this->mTemplateName ); |
75 | if ( !isset( $templateTitle ) ) { |
76 | return; |
77 | } |
78 | |
79 | $templateText = PFUtils::getPageText( $templateTitle ) ?? ''; |
80 | // Ignore 'noinclude' sections and 'includeonly' tags. |
81 | $templateText = StringUtils::delimiterReplace( '<noinclude>', '</noinclude>', '', $templateText ); |
82 | $this->mTemplateText = strtr( $templateText, [ '<includeonly>' => '', '</includeonly>' => '' ] ); |
83 | |
84 | // The Cargo-based function is more specific; it only gets |
85 | // data structure information from the template schema. If |
86 | // there's no Cargo schema for this template, we call |
87 | // loadTemplateFieldsSMWAndOther(), which doesn't require the |
88 | // presence of SMW and can get non-SMW information as well. |
89 | if ( defined( 'CARGO_VERSION' ) ) { |
90 | $this->loadTemplateFieldsCargo( $templateTitle ); |
91 | if ( count( $this->mTemplateFields ) > 0 ) { |
92 | return; |
93 | } |
94 | } |
95 | $this->loadTemplateFieldsSMWAndOther(); |
96 | } |
97 | |
98 | /** |
99 | * Get the fields of the template, along with the semantic property |
100 | * attached to each one (if any), by parsing the text of the template. |
101 | */ |
102 | function loadTemplateFieldsSMWAndOther() { |
103 | $templateFields = []; |
104 | $fieldNamesArray = []; |
105 | |
106 | // The way this works is that fields are found and then stored |
107 | // in an array based on their location in the template text, so |
108 | // that they can be returned in the order in which they appear |
109 | // in the template, not the order in which they were found. |
110 | // Some fields can be found more than once (especially if |
111 | // they're part of an "#if" statement), so they're only |
112 | // recorded the first time they're found. |
113 | |
114 | // Replace all calls to #set within #arraymap with standard |
115 | // SMW tags. This is done so that they will later get |
116 | // parsed correctly. |
117 | // This is "cheating", since it modifies the template text |
118 | // (the rest of the function doesn't do that), but trying to |
119 | // get the #arraymap check regexp to find both kinds of SMW |
120 | // property tags seemed too hard to do. |
121 | $this->mTemplateText = preg_replace( '/#arraymap.*{{\s*#set:\s*([^=]*)=([^}]*)}}/', '[[$1:$2]]', $this->mTemplateText ); |
122 | |
123 | // Look for "arraymap" parser function calls that map a |
124 | // property onto a list. |
125 | $ret = preg_match_all( '/{{#arraymap:{{{([^|}]*:?[^|}]*)[^\[]*\[\[([^:]*:?[^:]*)::/mis', $this->mTemplateText, $matches ); |
126 | if ( $ret ) { |
127 | foreach ( $matches[1] as $i => $field_name ) { |
128 | if ( !in_array( $field_name, $fieldNamesArray ) ) { |
129 | $propertyName = $matches[2][$i]; |
130 | $this->loadPropertySettingInTemplate( $field_name, $propertyName, true ); |
131 | $fieldNamesArray[] = $field_name; |
132 | } |
133 | } |
134 | } elseif ( $ret === false ) { |
135 | // There was an error in the preg_match_all() |
136 | // call - let the user know about it. |
137 | if ( preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR ) { |
138 | print 'Page Forms error: backtrace limit exceeded during parsing! Please increase the value of <a href="http://www.php.net/manual/en/pcre.configuration.php#ini.pcre.backtrack-limit">pcre.backtrack_limit</a> in php.ini or LocalSettings.php.'; |
139 | } |
140 | } |
141 | |
142 | // Look for normal property calls. |
143 | if ( preg_match_all( '/\[\[([^:|\[\]]*:*?[^:|\[\]]*)::{{{([^\]\|}]*).*?\]\]/mis', $this->mTemplateText, $matches ) ) { |
144 | foreach ( $matches[1] as $i => $propertyName ) { |
145 | $field_name = trim( $matches[2][$i] ); |
146 | if ( !in_array( $field_name, $fieldNamesArray ) ) { |
147 | $propertyName = trim( $propertyName ); |
148 | $this->loadPropertySettingInTemplate( $field_name, $propertyName, false ); |
149 | $fieldNamesArray[] = $field_name; |
150 | } |
151 | } |
152 | } |
153 | |
154 | // Then, get calls to #set, #set_internal and #subobject. |
155 | // (Thankfully, they all have similar syntax). |
156 | if ( preg_match_all( '/#(set|set_internal|subobject):(.*?}}})\s*}}/mis', $this->mTemplateText, $matches ) ) { |
157 | foreach ( $matches[2] as $match ) { |
158 | if ( preg_match_all( '/([^|{]*?)=\s*{{{([^|}]*)/mis', $match, $matches2 ) ) { |
159 | foreach ( $matches2[1] as $i => $propertyName ) { |
160 | $fieldName = trim( $matches2[2][$i] ); |
161 | if ( !in_array( $fieldName, $fieldNamesArray ) ) { |
162 | $propertyName = trim( $propertyName ); |
163 | $this->loadPropertySettingInTemplate( $fieldName, $propertyName, false ); |
164 | $fieldNamesArray[] = $fieldName; |
165 | } |
166 | } |
167 | } |
168 | } |
169 | } |
170 | |
171 | // Then, get calls to #declare. (This is really rather |
172 | // optional, since no one seems to use #declare.) |
173 | if ( preg_match_all( '/#declare:(.*?)}}/mis', $this->mTemplateText, $matches ) ) { |
174 | foreach ( $matches[1] as $match ) { |
175 | $setValues = explode( '|', $match ); |
176 | foreach ( $setValues as $valuePair ) { |
177 | $keyAndVal = explode( '=', $valuePair ); |
178 | if ( count( $keyAndVal ) == 2 ) { |
179 | $propertyName = trim( $keyAndVal[0] ); |
180 | $fieldName = trim( $keyAndVal[1] ); |
181 | if ( !in_array( $fieldName, $fieldNamesArray ) ) { |
182 | $this->loadPropertySettingInTemplate( $fieldName, $propertyName, false ); |
183 | $fieldNamesArray[] = $fieldName; |
184 | } |
185 | } |
186 | } |
187 | } |
188 | } |
189 | |
190 | // Finally, get any non-semantic fields defined. |
191 | if ( preg_match_all( '/{{{([^|}]*)/mis', $this->mTemplateText, $matches ) ) { |
192 | foreach ( $matches[1] as $fieldName ) { |
193 | $fieldName = trim( $fieldName ); |
194 | if ( !empty( $fieldName ) && ( !in_array( $fieldName, $fieldNamesArray ) ) ) { |
195 | $cur_pos = stripos( $this->mTemplateText, $fieldName ); |
196 | $this->mTemplateFields[$cur_pos] = PFTemplateField::create( $fieldName, PFUtils::getContLang()->ucfirst( $fieldName ) ); |
197 | $fieldNamesArray[] = $fieldName; |
198 | } |
199 | } |
200 | } |
201 | |
202 | // If #template_params was declared for this template, go |
203 | // through the declared fields, and, for any that were not |
204 | // already found by parsing the template, populate |
205 | // $mTemplateFields with it. |
206 | // @todo - it would be good to combine the #template_params |
207 | // data with any SMW data found, instead of just getting one |
208 | // or the other. In practice, though, it doesn't really matter. |
209 | if ( $this->mTemplateParams !== null ) { |
210 | foreach ( $this->mTemplateParams as $fieldName => $fieldParams ) { |
211 | if ( in_array( $fieldName, $fieldNamesArray ) ) { |
212 | continue; |
213 | } |
214 | $templateField = PFTemplateField::newFromParams( $fieldName, $fieldParams ); |
215 | $this->mTemplateFields[$fieldName] = $templateField; |
216 | } |
217 | return; |
218 | } |
219 | |
220 | ksort( $this->mTemplateFields ); |
221 | } |
222 | |
223 | /** |
224 | * For a field name and its attached property name located in the |
225 | * template text, create an PFTemplateField object out of it, and |
226 | * add it to $this->mTemplateFields. |
227 | * @param string $fieldName |
228 | * @param string $propertyName |
229 | * @param bool $isList |
230 | */ |
231 | function loadPropertySettingInTemplate( $fieldName, $propertyName, $isList ) { |
232 | $templateField = PFTemplateField::create( |
233 | $fieldName, PFUtils::getContLang()->ucfirst( $fieldName ), $propertyName, |
234 | $isList |
235 | ); |
236 | $cur_pos = stripos( $this->mTemplateText, $fieldName . '|' ); |
237 | $this->mTemplateFields[$cur_pos] = $templateField; |
238 | } |
239 | |
240 | function loadTemplateFieldsCargo( $templateTitle ) { |
241 | $cargoFieldsOfTemplateParams = []; |
242 | |
243 | // First, get the table name, and fields, declared for this |
244 | // template, if any. |
245 | list( $tableName, $tableSchema ) = $this->getCargoTableAndSchema( $templateTitle ); |
246 | if ( $tableName == null ) { |
247 | $fieldDescriptions = []; |
248 | } else { |
249 | $fieldDescriptions = $tableSchema->mFieldDescriptions; |
250 | } |
251 | |
252 | // If #template_params was declared for this template, our |
253 | // job is easy - we just go through the declared fields, get |
254 | // the Cargo data for each field if it exists, and populate |
255 | // $mTemplateFields with it. |
256 | if ( $this->mTemplateParams !== null ) { |
257 | foreach ( $this->mTemplateParams as $fieldName => $fieldParams ) { |
258 | $templateField = PFTemplateField::newFromParams( $fieldName, $fieldParams ); |
259 | $cargoField = $templateField->getExpectedCargoField(); |
260 | if ( array_key_exists( $cargoField, $fieldDescriptions ) ) { |
261 | $fieldDescription = $fieldDescriptions[$cargoField]; |
262 | $templateField->setCargoFieldData( $tableName, $cargoField, $fieldDescription ); |
263 | } |
264 | $this->mTemplateFields[$fieldName] = $templateField; |
265 | } |
266 | return; |
267 | } |
268 | |
269 | // If there are no declared template params *or* Cargo fields, |
270 | // exit. |
271 | if ( count( $fieldDescriptions ) == 0 ) { |
272 | return; |
273 | } |
274 | |
275 | // No #template_params call, so we have to do a more manual |
276 | // process. |
277 | // Match template params to Cargo table fields, by parsing |
278 | // call(s) to #cargo_store. |
279 | // Let's find every #cargo_store tag. |
280 | // Unfortunately, it doesn't seem possible to use a regexp |
281 | // search for this, because it's hard to know which set of |
282 | // double brackets represents the end of such a call. Instead, |
283 | // we'll do some manual parsing. |
284 | $cargoStoreLocations = []; |
285 | $curPos = 0; |
286 | while ( true ) { |
287 | $newPos = strpos( $this->mTemplateText, "#cargo_store:", $curPos ); |
288 | if ( $newPos === false ) { |
289 | break; |
290 | } |
291 | $curPos = $newPos + 13; |
292 | $cargoStoreLocations[] = $curPos; |
293 | } |
294 | |
295 | $cargoStoreCalls = []; |
296 | foreach ( $cargoStoreLocations as $locNum => $startPos ) { |
297 | $numUnclosedBrackets = 2; |
298 | if ( $locNum < count( $cargoStoreLocations ) - 1 ) { |
299 | $lastPos = $cargoStoreLocations[$locNum + 1]; |
300 | } else { |
301 | $lastPos = strlen( $this->mTemplateText ) - 1; |
302 | } |
303 | $curCargoStoreCall = ''; |
304 | $curPos = $startPos; |
305 | while ( $curPos <= $lastPos ) { |
306 | $curChar = $this->mTemplateText[$curPos]; |
307 | $curCargoStoreCall .= $curChar; |
308 | if ( $curChar == '}' ) { |
309 | $numUnclosedBrackets--; |
310 | } elseif ( $curChar == '{' ) { |
311 | $numUnclosedBrackets++; |
312 | } |
313 | if ( $numUnclosedBrackets == 0 ) { |
314 | break; |
315 | } |
316 | $curPos++; |
317 | } |
318 | $cargoStoreCalls[] = $curCargoStoreCall; |
319 | } |
320 | |
321 | foreach ( $cargoStoreCalls as $cargoStoreCall ) { |
322 | if ( preg_match_all( '/([^|{]*?)=\s*{{{([^|}]*)/mis', $cargoStoreCall, $matches ) ) { |
323 | foreach ( $matches[1] as $i => $cargoFieldName ) { |
324 | $templateParameter = trim( $matches[2][$i] ); |
325 | $cargoFieldsOfTemplateParams[$templateParameter] = $cargoFieldName; |
326 | } |
327 | } |
328 | } |
329 | |
330 | // Now, combine the two sets of information into an array of |
331 | // PFTemplateFields objects. |
332 | // First, go through the #cargo_store parameters, add add them |
333 | // all to the array, matching them with Cargo field descriptions |
334 | // where possible. |
335 | foreach ( $cargoFieldsOfTemplateParams as $templateParameter => $cargoField ) { |
336 | $templateField = PFTemplateField::create( $templateParameter, $templateParameter ); |
337 | if ( array_key_exists( $cargoField, $fieldDescriptions ) ) { |
338 | $fieldDescription = $fieldDescriptions[$cargoField]; |
339 | $templateField->setCargoFieldData( $tableName, $cargoField, $fieldDescription ); |
340 | } |
341 | $this->mTemplateFields[] = $templateField; |
342 | } |
343 | |
344 | // Now, go through the Cargo field descriptions, and add |
345 | // whichever ones were not in #cargo_store (as of version 3.0, |
346 | // Cargo does not require template parameters to be passed in |
347 | // to #cargo_store). |
348 | foreach ( $fieldDescriptions as $cargoField => $fieldDescription ) { |
349 | $templateParameter = array_search( $cargoField, $cargoFieldsOfTemplateParams ); |
350 | if ( $templateParameter !== false ) { |
351 | continue; |
352 | } |
353 | $templateParameter = str_replace( '_', ' ', $cargoField ); |
354 | $templateField = PFTemplateField::create( $templateParameter, $templateParameter ); |
355 | $templateField->setCargoFieldData( $tableName, $cargoField, $fieldDescription ); |
356 | $this->mTemplateFields[] = $templateField; |
357 | } |
358 | } |
359 | |
360 | function getCargoTableAndSchema( $templateTitle ) { |
361 | $templatePageID = $templateTitle->getArticleID(); |
362 | $tableSchemaString = CargoUtils::getPageProp( $templatePageID, 'CargoFields' ); |
363 | // See if there even is DB storage for this template - if not, |
364 | // exit. |
365 | if ( $tableSchemaString === null ) { |
366 | // There's no declared table - but see if there's an |
367 | // attached table. |
368 | list( $tableName, $isDeclared ) = CargoUtils::getTableNameForTemplate( $templateTitle ); |
369 | if ( $tableName == null ) { |
370 | return [ null, null ]; |
371 | } |
372 | $mainTemplatePageID = CargoUtils::getTemplateIDForDBTable( $tableName ); |
373 | $tableSchemaString = CargoUtils::getPageProp( $mainTemplatePageID, 'CargoFields' ); |
374 | } else { |
375 | $tableName = CargoUtils::getPageProp( $templatePageID, 'CargoTableName' ); |
376 | } |
377 | $tableSchema = CargoTableSchema::newFromDBString( $tableSchemaString ); |
378 | return [ $tableName, $tableSchema ]; |
379 | } |
380 | |
381 | public function getTemplateFields() { |
382 | return $this->mTemplateFields; |
383 | } |
384 | |
385 | public function getFieldNamed( $fieldName ) { |
386 | foreach ( $this->mTemplateFields as $curField ) { |
387 | if ( $curField->getFieldName() == $fieldName ) { |
388 | return $curField; |
389 | } |
390 | } |
391 | return null; |
392 | } |
393 | |
394 | public function setConnectingProperty( $connectingProperty ) { |
395 | $this->mConnectingProperty = $connectingProperty; |
396 | } |
397 | |
398 | public function setCategoryName( $categoryName ) { |
399 | $this->mCategoryName = $categoryName; |
400 | } |
401 | |
402 | public function setCargoTable( $cargoTable ) { |
403 | $this->mCargoTable = str_replace( ' ', '_', $cargoTable ); |
404 | } |
405 | |
406 | public function setFullWikiTextStatus( $status ) { |
407 | $this->mFullWikiText = $status; |
408 | } |
409 | |
410 | public function setAggregatingInfo( $aggregatingProperty, $aggregationLabel ) { |
411 | $this->mAggregatingProperty = $aggregatingProperty; |
412 | $this->mAggregationLabel = $aggregationLabel; |
413 | } |
414 | |
415 | public function setFormat( $templateFormat ) { |
416 | $this->mTemplateFormat = $templateFormat; |
417 | } |
418 | |
419 | public function createCargoDeclareCall() { |
420 | $text = '{{#cargo_declare:'; |
421 | $text .= '_table=' . $this->mCargoTable; |
422 | foreach ( $this->mTemplateFields as $i => $field ) { |
423 | if ( $field->getFieldType() == '' ) { |
424 | continue; |
425 | } |
426 | |
427 | $text .= '|'; |
428 | $text .= str_replace( ' ', '_', $field->getFieldName() ) . '='; |
429 | if ( $field->isList() ) { |
430 | $delimiter = $field->getDelimiter(); |
431 | if ( $delimiter == '' ) { |
432 | $delimiter = ','; |
433 | } |
434 | $text .= "List ($delimiter) of "; |
435 | } |
436 | $text .= $field->getFieldType(); |
437 | if ( $field->getHierarchyStructure() ) { |
438 | $hierarchyStructureString = $field->getHierarchyStructure(); |
439 | $text .= " (hierarchy;allowed values=$hierarchyStructureString)"; |
440 | } elseif ( count( $field->getPossibleValues() ) > 0 ) { |
441 | $allowedValuesString = implode( ',', $field->getPossibleValues() ); |
442 | $text .= " (allowed values=$allowedValuesString)"; |
443 | } |
444 | } |
445 | $text .= '}}'; |
446 | return $text; |
447 | } |
448 | |
449 | public function createCargoStoreCall() { |
450 | $text = '{{#cargo_store:'; |
451 | $text .= '_table=' . $this->mCargoTable; |
452 | if ( defined( 'CargoStore::PARAMS_OPTIONAL' ) ) { |
453 | // Cargo 3.0+ |
454 | $text .= '}}'; |
455 | return $text; |
456 | } |
457 | |
458 | foreach ( $this->mTemplateFields as $i => $field ) { |
459 | $text .= '|' . |
460 | str_replace( ' ', '_', $field->getFieldName() ) . |
461 | '={{{' . $field->getFieldName() . '|}}}'; |
462 | } |
463 | $text .= ' }}'; |
464 | return $text; |
465 | } |
466 | |
467 | /** |
468 | * Creates the text of a template, when called from |
469 | * Special:CreateTemplate, Special:CreateClass or the Page Schemas |
470 | * extension. |
471 | * @return string |
472 | */ |
473 | public function createText() { |
474 | // Avoid PHP 7.1 warning from passing $this by reference |
475 | $template = $this; |
476 | MediaWikiServices::getInstance()->getHookContainer()->run( 'PageForms::CreateTemplateText', [ &$template ] ); |
477 | // Check whether the user needs the full wikitext instead of #template_display |
478 | if ( $this->mFullWikiText ) { |
479 | $templateHeader = wfMessage( 'pf_template_docu', $this->mTemplateName )->inContentLanguage()->text(); |
480 | $text = <<<END |
481 | <noinclude> |
482 | $templateHeader |
483 | <pre> |
484 | |
485 | END; |
486 | $text .= '{{' . $this->mTemplateName; |
487 | if ( count( $this->mTemplateFields ) > 0 ) { |
488 | $text .= "\n"; |
489 | } |
490 | foreach ( $this->mTemplateFields as $field ) { |
491 | if ( $field->getFieldName() == '' ) { |
492 | continue; |
493 | } |
494 | $text .= "|" . $field->getFieldName() . "=\n"; |
495 | } |
496 | if ( defined( 'CARGO_VERSION' ) && !defined( 'SMW_VERSION' ) && $this->mCargoTable != '' ) { |
497 | $cargoInUse = true; |
498 | $cargoDeclareCall = $this->createCargoDeclareCall() . "\n"; |
499 | $cargoStoreCall = $this->createCargoStoreCall(); |
500 | } else { |
501 | $cargoInUse = false; |
502 | $cargoDeclareCall = ''; |
503 | $cargoStoreCall = ''; |
504 | } |
505 | |
506 | $templateFooter = wfMessage( 'pf_template_docufooter' )->inContentLanguage()->text(); |
507 | $text .= <<<END |
508 | }} |
509 | </pre> |
510 | $templateFooter |
511 | $cargoDeclareCall</noinclude><includeonly>$cargoStoreCall |
512 | END; |
513 | } else { |
514 | $text = <<<END |
515 | <noinclude> |
516 | {{#template_params: |
517 | END; |
518 | foreach ( $this->mTemplateFields as $i => $field ) { |
519 | if ( $field->getFieldName() == '' ) { |
520 | continue; |
521 | } |
522 | if ( $i > 0 ) { |
523 | $text .= "|"; |
524 | } |
525 | $text .= $field->toWikitext(); |
526 | } |
527 | if ( defined( 'CARGO_VERSION' ) && !defined( 'SMW_VERSION' ) && $this->mCargoTable != '' ) { |
528 | $cargoInUse = true; |
529 | $cargoDeclareCall = $this->createCargoDeclareCall() . "\n"; |
530 | $cargoStoreCall = $this->createCargoStoreCall(); |
531 | } else { |
532 | $cargoInUse = false; |
533 | $cargoDeclareCall = ''; |
534 | $cargoStoreCall = ''; |
535 | } |
536 | |
537 | $text .= <<<END |
538 | }} |
539 | $cargoDeclareCall</noinclude><includeonly>$cargoStoreCall |
540 | END; |
541 | if ( !defined( 'SMW_VERSION' ) ) { |
542 | $text .= "\n{{#template_display:"; |
543 | if ( $this->mTemplateFormat != null ) { |
544 | $text .= "_format=" . $this->mTemplateFormat; |
545 | } |
546 | $text .= "}}"; |
547 | $text .= $this->printCategoryTag(); |
548 | $text .= "</includeonly>"; |
549 | return $text; |
550 | } |
551 | } |
552 | |
553 | // Before text |
554 | $text .= $this->mTemplateStart; |
555 | |
556 | // $internalObjText can be either a call to #set_internal |
557 | // or to #subobject (or null); which one we go with |
558 | // depends on whether Semantic Internal Objects is installed, |
559 | // and on the SMW version. |
560 | // Thankfully, the syntaxes of #set_internal and #subobject |
561 | // are quite similar, so we don't need too much extra logic. |
562 | $internalObjText = null; |
563 | if ( $this->mConnectingProperty ) { |
564 | global $smwgDefaultStore; |
565 | if ( defined( 'SIO_VERSION' ) ) { |
566 | $useSubobject = false; |
567 | $internalObjText = '{{#set_internal:' . $this->mConnectingProperty; |
568 | } elseif ( $smwgDefaultStore == "SMWSQLStore3" ) { |
569 | $useSubobject = true; |
570 | $internalObjText = '{{#subobject:-|' . $this->mConnectingProperty . '={{PAGENAME}}'; |
571 | } |
572 | } |
573 | $setText = ''; |
574 | |
575 | // Topmost part of table depends on format. |
576 | if ( !$this->mTemplateFormat ) { |
577 | $this->mTemplateFormat = 'standard'; |
578 | } |
579 | if ( $this->mTemplateFormat == 'standard' ) { |
580 | $tableText = '{| class="wikitable"' . "\n"; |
581 | } elseif ( $this->mTemplateFormat == 'infobox' ) { |
582 | // A CSS style can't be used, unfortunately, since most |
583 | // MediaWiki setups don't have an 'infobox' or |
584 | // comparable CSS class. |
585 | $tableText = <<<END |
586 | {| style="width: 30em; font-size: 90%; border: 1px solid #aaaaaa; background-color: #f9f9f9; color: black; margin-bottom: 0.5em; margin-left: 1em; padding: 0.2em; float: right; clear: right; text-align:left;" |
587 | ! style="text-align: center; background-color:#ccccff;" colspan="2" |<span style="font-size: larger;">{{PAGENAME}}</span> |
588 | |- |
589 | |
590 | END; |
591 | } else { |
592 | $tableText = ''; |
593 | } |
594 | |
595 | foreach ( $this->mTemplateFields as $i => $field ) { |
596 | if ( $field->getFieldName() == '' ) { |
597 | continue; |
598 | } |
599 | |
600 | $fieldParam = '{{{' . $field->getFieldName() . '|}}}'; |
601 | if ( $field->getNamespace() === null ) { |
602 | $fieldString = $fieldParam; |
603 | } else { |
604 | $fieldString = $field->getNamespace() . ':' . $fieldParam; |
605 | } |
606 | $separator = ''; |
607 | |
608 | $fieldLabel = $field->getLabel(); |
609 | if ( $fieldLabel == '' ) { |
610 | $fieldLabel = $field->getFieldName(); |
611 | } |
612 | $fieldDisplay = $field->getDisplay(); |
613 | $fieldProperty = $field->getSemanticProperty(); |
614 | $fieldIsList = $field->isList(); |
615 | |
616 | // Header/field label column |
617 | if ( $fieldDisplay === null ) { |
618 | if ( $this->mTemplateFormat == 'standard' || $this->mTemplateFormat == 'infobox' ) { |
619 | if ( $i > 0 ) { |
620 | $tableText .= "|-\n"; |
621 | } |
622 | $tableText .= '! ' . $fieldLabel . "\n"; |
623 | } elseif ( $this->mTemplateFormat == 'plain' ) { |
624 | $tableText .= "\n'''" . $fieldLabel . ":''' "; |
625 | } elseif ( $this->mTemplateFormat == 'sections' ) { |
626 | $tableText .= "\n==" . $fieldLabel . "==\n"; |
627 | } |
628 | } elseif ( $fieldDisplay == 'nonempty' ) { |
629 | if ( $this->mTemplateFormat == 'plain' || $this->mTemplateFormat == 'sections' ) { |
630 | $tableText .= "\n"; |
631 | } |
632 | $tableText .= '{{#if:' . $fieldParam . '|'; |
633 | if ( $this->mTemplateFormat == 'standard' || $this->mTemplateFormat == 'infobox' ) { |
634 | if ( $i > 0 ) { |
635 | $tableText .= "\n{{!}}-\n"; |
636 | } |
637 | $tableText .= '! ' . $fieldLabel . "\n"; |
638 | $separator = '{{!}}'; |
639 | } elseif ( $this->mTemplateFormat == 'plain' ) { |
640 | $tableText .= "'''" . $fieldLabel . ":''' "; |
641 | $separator = ''; |
642 | } elseif ( $this->mTemplateFormat == 'sections' ) { |
643 | $tableText .= '==' . $fieldLabel . "==\n"; |
644 | $separator = ''; |
645 | } |
646 | } else { |
647 | // If it's 'hidden', do nothing |
648 | } |
649 | // Value column |
650 | if ( $this->mTemplateFormat == 'standard' || $this->mTemplateFormat == 'infobox' ) { |
651 | if ( $fieldDisplay == 'hidden' ) { |
652 | } elseif ( $fieldDisplay == 'nonempty' ) { |
653 | // $tableText .= "{{!}} "; |
654 | } else { |
655 | $tableText .= "| "; |
656 | } |
657 | } |
658 | |
659 | // If we're using Cargo, fields can simply be displayed |
660 | // normally - no need for any special tags - *unless* |
661 | // the field holds a list of Page values, in which case |
662 | // we need to apply #arraymap. |
663 | $isCargoListOfPages = $cargoInUse && $field->isList() && $field->getFieldType() == 'Page'; |
664 | if ( !$fieldProperty && !$isCargoListOfPages ) { |
665 | if ( $separator != '' ) { |
666 | $tableText .= "$separator "; |
667 | } |
668 | $tableText .= $this->createTextForField( $field ); |
669 | if ( $fieldDisplay == 'nonempty' ) { |
670 | $tableText .= " }}"; |
671 | } |
672 | $tableText .= "\n"; |
673 | } elseif ( $internalObjText !== null ) { |
674 | if ( $separator != '' ) { |
675 | $tableText .= "$separator "; |
676 | } |
677 | $tableText .= $this->createTextForField( $field ); |
678 | if ( $fieldDisplay == 'nonempty' ) { |
679 | $tableText .= " }}"; |
680 | } |
681 | $tableText .= "\n"; |
682 | if ( $field->isList() ) { |
683 | if ( $useSubobject ) { |
684 | $internalObjText .= '|' . $fieldProperty . '=' . $fieldString . '|+sep=,'; |
685 | } else { |
686 | $internalObjText .= '|' . $fieldProperty . '#list=' . $fieldString; |
687 | } |
688 | } else { |
689 | $internalObjText .= '|' . $fieldProperty . '=' . $fieldString; |
690 | } |
691 | } elseif ( $fieldDisplay == 'hidden' ) { |
692 | if ( $fieldIsList ) { |
693 | $setText .= $fieldProperty . '#list=' . $fieldString . '|'; |
694 | } else { |
695 | $setText .= $fieldProperty . '=' . $fieldString . '|'; |
696 | } |
697 | } elseif ( $fieldDisplay == 'nonempty' ) { |
698 | if ( $this->mTemplateFormat == 'standard' || $this->mTemplateFormat == 'infobox' ) { |
699 | $tableText .= '{{!}} '; |
700 | } |
701 | $tableText .= $this->createTextForField( $field ) . "\n}}\n"; |
702 | } else { |
703 | $tableText .= $this->createTextForField( $field ) . "\n"; |
704 | } |
705 | } |
706 | |
707 | // Add an inline query to the output text, for |
708 | // aggregation, if a property was specified. |
709 | if ( $this->mAggregatingProperty !== null && $this->mAggregatingProperty !== '' ) { |
710 | if ( $this->mTemplateFormat == 'standard' || $this->mTemplateFormat == 'infobox' ) { |
711 | if ( count( $this->mTemplateFields ) > 0 ) { |
712 | $tableText .= "|-\n"; |
713 | } |
714 | $tableText .= <<<END |
715 | ! $this->mAggregationLabel |
716 | | |
717 | END; |
718 | } elseif ( $this->mTemplateFormat == 'plain' ) { |
719 | $tableText .= "\n'''" . $this->mAggregationLabel . ":''' "; |
720 | } elseif ( $this->mTemplateFormat == 'sections' ) { |
721 | $tableText .= "\n==" . $this->mAggregationLabel . "==\n"; |
722 | } |
723 | $tableText .= "{{#ask:[[" . $this->mAggregatingProperty . "::{{SUBJECTPAGENAME}}]]|format=list}}\n"; |
724 | } |
725 | if ( $this->mTemplateFormat == 'standard' || $this->mTemplateFormat == 'infobox' ) { |
726 | $tableText .= "|}"; |
727 | } |
728 | // Leave out newlines if there's an internal property |
729 | // set here (which would mean that there are meant to be |
730 | // multiple instances of this template.) |
731 | if ( $internalObjText === null ) { |
732 | if ( $this->mTemplateFormat == 'standard' || $this->mTemplateFormat == 'infobox' ) { |
733 | $tableText .= "\n"; |
734 | } |
735 | } else { |
736 | $internalObjText .= "}}"; |
737 | $text .= $internalObjText; |
738 | } |
739 | |
740 | // Add a call to #set, if necessary |
741 | if ( $setText !== '' ) { |
742 | $setText = '{{#set:' . $setText . "}}\n"; |
743 | $text .= $setText; |
744 | } |
745 | |
746 | $text .= $tableText; |
747 | $text .= $this->printCategoryTag(); |
748 | |
749 | // After text |
750 | $text .= $this->mTemplateEnd; |
751 | |
752 | $text .= "</includeonly>\n"; |
753 | |
754 | return $text; |
755 | } |
756 | |
757 | function createTextForField( $field ) { |
758 | $text = ''; |
759 | $fieldStart = $this->mFieldStart; |
760 | $hookContainer = MediaWikiServices::getInstance()->getHookContainer(); |
761 | $hookContainer->run( 'PageForms::TemplateFieldStart', [ $field, &$fieldStart ] ); |
762 | if ( $fieldStart != '' ) { |
763 | $text .= "$fieldStart "; |
764 | } |
765 | |
766 | $cargoInUse = defined( 'CARGO_VERSION' ) && !defined( 'SMW_VERSION' ) && $this->mCargoTable != ''; |
767 | $text .= $field->createText( $cargoInUse ); |
768 | |
769 | $fieldEnd = $this->mFieldEnd; |
770 | $hookContainer->run( 'PageForms::TemplateFieldEnd', [ $field, &$fieldEnd ] ); |
771 | if ( $fieldEnd != '' ) { |
772 | $text .= " $fieldEnd"; |
773 | } |
774 | |
775 | return $text; |
776 | } |
777 | |
778 | function printCategoryTag() { |
779 | if ( ( $this->mCategoryName === '' || $this->mCategoryName === null ) ) { |
780 | return ''; |
781 | } |
782 | $namespaceLabels = PFUtils::getContLang()->getNamespaces(); |
783 | $categoryNamespace = $namespaceLabels[NS_CATEGORY]; |
784 | return "\n[[$categoryNamespace:" . $this->mCategoryName . "]]\n"; |
785 | } |
786 | |
787 | } |