summaryrefslogtreecommitdiff
blob: 67f2da808cb3274a5f95ed418c52ba2ea4d21f11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
<?php
/**
 * Represents a template in a user-defined form.
 * @author Yaron Koren
 * @file
 * @ingroup SF
 */
class SFTemplateInForm {
	private $mTemplateName;
	private $mLabel;
	private $mAllowMultiple;
	private $mMaxAllowed;
	private $mFields;

	/**
	 * For a field name and its attached property name located in the
	 * template text, create an SFTemplateField object out of it, and
	 * add it to the $templateFields array.
	 */
	function handlePropertySettingInTemplate( $fieldName, $propertyName, $isList, &$templateFields, $templateText ) {
		global $wgContLang;
		$templateField = SFTemplateField::create( $fieldName, $wgContLang->ucfirst( $fieldName ), $propertyName, $isList );
		$cur_pos = stripos( $templateText, $fieldName.'|' );
		$templateFields[$cur_pos] = $templateField;
	}

	/**
	 * Get the fields of the template, along with the semantic property
	 * attached to each one (if any), by parsing the text of the template.
	 */
	function getAllFields() {
		global $wgContLang;
		$templateFields = array();
		$fieldNamesArray = array();

		// The way this works is that fields are found and then stored
		// in an array based on their location in the template text, so
		// that they can be returned in the order in which they appear
		// in the template, not the order in which they were found.
		// Some fields can be found more than once (especially if
		// they're part of an "#if" statement), so they're only
		// recorded the first time they're found.
		$template_title = Title::makeTitleSafe( NS_TEMPLATE, $this->mTemplateName );
		if ( isset( $template_title ) ) {
			$templateText = SFUtils::getPageText( $template_title );
			// Ignore 'noinclude' sections and 'includeonly' tags.
			$templateText = StringUtils::delimiterReplace( '<noinclude>', '</noinclude>', '', $templateText );
			$templateText = strtr( $templateText, array( '<includeonly>' => '', '</includeonly>' => '' ) );

			// First, look for "arraymap" parser function calls
			// that map a property onto a list.
			if ( $ret = preg_match_all( '/{{#arraymap:{{{([^|}]*:?[^|}]*)[^\[]*\[\[([^:]*:?[^:]*)::/mis', $templateText, $matches ) ) {
				foreach ( $matches[1] as $i => $field_name ) {
					if ( ! in_array( $field_name, $fieldNamesArray ) ) {
						$propertyName = $matches[2][$i];
						$this->handlePropertySettingInTemplate( $field_name, $propertyName, true, $templateFields, $templateText );
						$fieldNamesArray[] = $field_name;
					}
				}
			} elseif ( $ret === false ) {
				// There was an error in the preg_match_all()
				// call - let the user know about it.
				if ( preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR ) {
					print 'Semantic 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.';
				}
			}

			// Second, look for normal property calls.
			if ( preg_match_all( '/\[\[([^:|\[\]]*:*?[^:|\[\]]*)::{{{([^\]\|}]*).*?\]\]/mis', $templateText, $matches ) ) {
				foreach ( $matches[1] as $i => $propertyName ) {
					$field_name = trim( $matches[2][$i] );
					if ( ! in_array( $field_name, $fieldNamesArray ) ) {
						$propertyName = trim( $propertyName );
						$this->handlePropertySettingInTemplate( $field_name, $propertyName, false, $templateFields, $templateText );
						$fieldNamesArray[] = $field_name;
					}
				}
			}

			// Then, get calls to #set, #set_internal and
			// #subobject. (Thankfully, they all have similar
			// syntax).
			if ( preg_match_all( '/#(set|set_internal|subobject):(.*?}}})\s*}}/mis', $templateText, $matches ) ) {
				foreach ( $matches[2] as $match ) {
					if ( preg_match_all( '/([^|{]*?)=\s*{{{([^|}]*)/mis', $match, $matches2 ) ) {
						foreach ( $matches2[1] as $i => $propertyName ) {
							$fieldName = trim( $matches2[2][$i] );
							if ( ! in_array( $fieldName, $fieldNamesArray ) ) {
								$propertyName = trim( $propertyName );
								$this->handlePropertySettingInTemplate( $fieldName, $propertyName, false, $templateFields, $templateText );
								$fieldNamesArray[] = $fieldName;
							}
						}
					}
				}
			}

			// Then, get calls to #declare. (This is really rather
			// optional, since no one seems to use #declare.)
			if ( preg_match_all( '/#declare:(.*?)}}/mis', $templateText, $matches ) ) {
				foreach ( $matches[1] as $match ) {
					$setValues = explode( '|', $match );
					foreach ( $setValues as $valuePair ) {
						$keyAndVal = explode( '=', $valuePair );
						if ( count( $keyAndVal ) == 2 ) {
							$propertyName = trim( $keyAndVal[0] );
							$fieldName = trim( $keyAndVal[1] );
							if ( ! in_array( $fieldName, $fieldNamesArray ) ) {
								$this->handlePropertySettingInTemplate( $fieldName, $propertyName, false, $templateFields, $templateText );
								$fieldNamesArray[] = $fieldName;
							}
						}
					}
				}
			}

			// Finally, get any non-semantic fields defined.
			if ( preg_match_all( '/{{{([^|}]*)/mis', $templateText, $matches ) ) {
				foreach ( $matches[1] as $fieldName ) {
					$fieldName = trim( $fieldName );
					if ( !empty( $fieldName ) && ( ! in_array( $fieldName, $fieldNamesArray ) ) ) {
						$cur_pos = stripos( $templateText, $fieldName );
						$templateFields[$cur_pos] = SFTemplateField::create( $fieldName, $wgContLang->ucfirst( $fieldName ) );
						$fieldNamesArray[] = $fieldName;
					}
				}
			}
		}
		ksort( $templateFields );
		return $templateFields;
	}

	static function create( $name, $label = null, $allowMultiple = null, $maxAllowed = null, $formFields = null ) {
		$tif = new SFTemplateInForm();
		$tif->mTemplateName = str_replace( '_', ' ', $name );
		$tif->mFields = array();
		if ( is_null( $formFields ) ) {
			$fields = $tif->getAllFields();
			$field_num = 0;
			foreach ( $fields as $field ) {
				$tif->mFields[] = SFFormField::create( $field_num++, $field );
			}
		} else {
			$tif->mFields = $formFields;
		}
		$tif->mLabel = $label;
		$tif->mAllowMultiple = $allowMultiple;
		$tif->mMaxAllowed = $maxAllowed;
		return $tif;
	}

	function getTemplateName() {
		return $this->mTemplateName;
	}

	function getFields() {
		return $this->mFields;
	}

	function creationHTML( $template_num ) {
		$checked_str = ( $this->mAllowMultiple ) ? "checked" : "";
		$template_str = wfMessage( 'sf_createform_template' )->escaped();
		$template_label_input = wfMessage( 'sf_createform_templatelabelinput' )->escaped();
		$allow_multiple_text = wfMessage( 'sf_createform_allowmultiple' )->escaped();
		$text = <<<END
	<input type="hidden" name="template_$template_num" value="$this->mTemplateName">
	<div class="templateForm">
	<h2>$template_str '$this->mTemplateName'</h2>
	<p>$template_label_input <input size=25 name="label_$template_num" value="$this->mLabel"></p>
	<p><input type="checkbox" name="allow_multiple_$template_num" $checked_str> $allow_multiple_text</p>
	<hr>

END;
		foreach ( $this->mFields as $field ) {
			$text .= $field->creationHTML( $template_num );
		}
		$removeTemplateButton = Html::input(
			'del_' . $template_num,
			wfMessage( 'sf_createform_removetemplate' )->text(),
			'submit'
		);
		$text .= "\t" . Html::rawElement( 'p', null, $removeTemplateButton ) . "\n";
		$text .= "	</div>\n";
		return $text;
	}

	function createMarkup() {
		$text = "{{{for template|" . $this->mTemplateName;
		if ( $this->mAllowMultiple ) {
			$text .= "|multiple";
		}
		if ( $this->mLabel != '' ) {
			$text .= "|label=" . $this->mLabel;
		}
		$text .= "}}}\n";
		// For now, HTML for templates differs for multiple-instance
		// templates; this may change if handling of form definitions
		// gets more sophisticated.
		if ( ! $this->mAllowMultiple ) { $text .= "{| class=\"formtable\"\n"; }
		foreach ( $this->mFields as $i => $field ) {
			$is_last_field = ( $i == count( $this->mFields ) - 1 );
			$text .= $field->createMarkup( $this->mAllowMultiple, $is_last_field );
		}
		if ( ! $this->mAllowMultiple ) { $text .= "|}\n"; }
		$text .= "{{{end template}}}\n";
		return $text;
	}
}