/**
	 * This function is the real heart of the entire Semantic Forms
	 * extension. It handles two main actions: (1) displaying a form on the
	 * screen, given a form definition and possibly page contents (if an
	 * existing page is being edited); and (2) creating actual page
	 * contents, if the form was already submitted by the user.
	 *
	 * It also does some related tasks, like figuring out the page name (if
	 * only a page formula exists).
	 */
	function formHTML( $form_def, $form_submitted, $source_is_page, $form_id = null, $existing_page_content = null, $page_name = null, $page_name_formula = null, $is_query = false, $is_embedded = false ) {
		global $wgRequest, $wgUser, $wgParser;
		global $sfgTabIndex; // used to represent the current tab index in the form
		global $sfgFieldNum; // used for setting various HTML IDs

		wfProfileIn( __METHOD__ );

		// initialize some variables
		$sfgTabIndex = 1;
		$sfgFieldNum = 1;
		$source_page_matches_this_form = false;
		$form_page_title = null;
		$generated_page_name = $page_name_formula;
		// $form_is_partial is true if:
		// (a) 'partial' == 1 in the arguments
		// (b) 'partial form' is found in the form definition
		// in the latter case, it may remain false until close to the end of
		// the parsing, so we have to assume that it will become a possibility
		$form_is_partial = false;
		$new_text = "";
		// flag for placing "<onlyinclude>" tags in form output
		$onlyinclude_free_text = false;

		// If we have existing content and we're not in an active replacement
		// situation, preserve the original content. We do this because we want
		// to pass the original content on IF this is a partial form.
		// TODO: A better approach here would be to pass the revision ID of the
		// existing page content through the replace value, which would
		// minimize the html traffic and would allow us to do a concurrent
		// update check. For now, we pass it through a hidden text field.

		if ( ! $wgRequest->getCheck( 'partial' ) ) {
			$original_page_content = $existing_page_content;
		} else {
			$original_page_content = null;
			if ( $wgRequest->getCheck( 'free_text' ) ) {
				$existing_page_content = $wgRequest->getVal( 'free_text' );
				$form_is_partial = true;
			}
		}

		// Disable all form elements if user doesn't have edit
		// permission - two different checks are needed, because
		// editing permissions can be set in different ways.
		// HACK - sometimes we don't know the page name in advance, but
		// we still need to set a title here for testing permissions.
		if ( $is_embedded ) {
			// If this is an embedded form (probably a 'RunQuery'),
			// just use the name of the actual page we're on.
			global $wgTitle;
			$this->mPageTitle = $wgTitle;
		} elseif ( $is_query ) {
			$this->mPageTitle = Title::newFromText( 'RunQuery dummy title' );
		} elseif ( $page_name === '' ) {
			$this->mPageTitle = Title::newFromText(
				$wgRequest->getVal( 'namespace' ) . ":Semantic Forms permissions test" );
		} else {
			$this->mPageTitle = Title::newFromText( $page_name );
		}

		global $wgOut;
		// show previous set of deletions for this page, if it's been deleted before
		if ( ! $form_submitted && ( $this->mPageTitle && !$this->mPageTitle->exists() ) ) {
			$this->showDeletionLog( $wgOut );
		}
		// Unfortunately, we can't just call userCan() here because, as of MW 1.16,
		// it has a bug in which it ignores a setting of
		// "$wgEmailConfirmToEdit = true;". Instead, we'll just get the
		// permission errors from the start, and use those to determine whether
		// the page is editable.
		if ( !$is_query ) {
			// $userCanEditPage = ( $wgUser->isAllowed( 'edit' ) && $this->mPageTitle->userCan( 'edit' ) );
			$permissionErrors = $this->mPageTitle->getUserPermissionsErrors( 'edit', $wgUser );
			$userCanEditPage = count( $permissionErrors ) == 0;
			wfRunHooks( 'sfUserCanEditPage', array( $this->mPageTitle, &$userCanEditPage ) );
		}
		$form_text = "";
		if ( $is_query || $userCanEditPage ) {
			$form_is_disabled = false;
			// Show "Your IP address will be recorded" warning if
			// user is anonymous, and it's not a query -
			// wiki-text for bolding has to be replaced with HTML.
			if ( $wgUser->isAnon() && ! $is_query ) {
				$anon_edit_warning = preg_replace( "/'''(.*)'''/", "<strong>$1</strong>", wfMsg( 'anoneditwarning' ) );
				$form_text .= "<p>$anon_edit_warning</p>\n";
			}
		} else {
			$form_is_disabled = true;
			$wgOut->setPageTitle( wfMsg( 'badaccess' ) );
			$wgOut->addWikiText( $wgOut->formatPermissionsErrorMessage( $permissionErrors, 'edit' ) );
			$wgOut->addHTML( "\n<hr />\n" );
		}

		$oldParser = $wgParser;

		$wgParser = unserialize( serialize( $oldParser ) ); // deep clone of parser
		$wgParser->Options( ParserOptions::newFromUser( $wgUser ) );
		$wgParser->Title( $this->mPageTitle );
		$wgParser->clearState();

		$form_def = SFFormUtils::getFormDefinition( $wgParser, $form_def, $form_id );

		// Turn form definition file into an array of sections, one for each
		// template definition (plus the first section)
		$form_def_sections = array();
		$start_position = 0;
		$section_start = 0;
		$free_text_was_included = false;
		$free_text_preload_page = null;
		$free_text_components = array();
		$all_values_for_template = array();
		// Unencode any HTML-encoded representations of curly brackets and
		// pipes - this is a hack to allow for forms to include templates
		// that themselves contain form elements - the escaping was needed
		// to make sure that those elements don't get parsed too early.
		$form_def = str_replace( array( '&#123;', '&#124;', '&#125;' ), array( '{', '|', '}' ), $form_def );
		// And another hack - replace the 'free text' standard input with
		// a field declaration to get it to be handled as a field.
		$form_def = str_replace( 'standard input|free text', 'field|<freetext>', $form_def );
		while ( $brackets_loc = strpos( $form_def, "{{{", $start_position ) ) {
			$brackets_end_loc = strpos( $form_def, "}}}", $brackets_loc );
			$bracketed_string = substr( $form_def, $brackets_loc + 3, $brackets_end_loc - ( $brackets_loc + 3 ) );
			$tag_components = SFUtils::getFormTagComponents( $bracketed_string );
			$tag_title = trim( $tag_components[0] );
			if ( $tag_title == 'for template' || $tag_title == 'end template' ) {
				// Create a section for everything up to here
				$section = substr( $form_def, $section_start, $brackets_loc - $section_start );
				$form_def_sections[] = $section;
				$section_start = $brackets_loc;
			}
			$start_position = $brackets_loc + 1;
		} // end while
		$form_def_sections[] = trim( substr( $form_def, $section_start ) );

		// Cycle through the form definition file, and possibly an
		// existing article as well, finding template and field
		// declarations and replacing them with form elements, either
		// blank or pre-populated, as appropriate.
		$all_fields = array();
		$data_text = "";
		$template_name = "";
		$allow_multiple = false;
		$instance_num = 0;
		$all_instances_printed = false;
		$strict_parsing = false;

		// Placeholder name in the form
		$curPlaceholder = null;
		// Used to store the HTML code of the multiple template, to reinsert it into the right spot
		// This array will keep track of all the replaced @<name>@ strings
		$placeholderFields = array();

		for ( $section_num = 0; $section_num < count( $form_def_sections ); $section_num++ ) {
			$start_position = 0;
			$template_text = "";
			// the append is there to ensure that the original
			// array doesn't get modified; is it necessary?
			$section = " " . $form_def_sections[$section_num];


			$multipleTemplateString = "";

			while ( $brackets_loc = strpos( $section, '{{{', $start_position ) ) {
				$brackets_end_loc = strpos( $section, "}}}", $brackets_loc );
				$bracketed_string = substr( $section, $brackets_loc + 3, $brackets_end_loc - ( $brackets_loc + 3 ) );
				$tag_components = SFUtils::getFormTagComponents( $bracketed_string );
				$tag_title = trim( $tag_components[0] );
				// =====================================================
				// for template processing
				// =====================================================
				if ( $tag_title == 'for template' ) {
					$old_template_name = $template_name;
					$template_name = trim( $tag_components[1] );
					$tif = SFTemplateInForm::create( $template_name );
					$query_template_name = str_replace( ' ', '_', $template_name );
					$add_button_text = wfMsg( 'sf_formedit_addanother' );
					// Also replace periods with underlines, since that's what
					// POST does to strings anyway.
					$query_template_name = str_replace( '.', '_', $query_template_name );
					// ...and escape apostrophes.
					$query_template_name = str_replace( "'", "\'", $query_template_name );
					// Cycle through the other components.
					for ( $i = 2; $i < count( $tag_components ); $i++ ) {
						$component = $tag_components[$i];
						if ( $component == 'multiple' ) $allow_multiple = true;
						if ( $component == 'strict' ) $strict_parsing = true;
						$sub_components = array_map( 'trim', explode( '=', $component, 2 ) );
						if ( count( $sub_components ) == 2 ) {
							if ( $sub_components[0] == 'label' ) {
								$template_label = $sub_components[1];
							} elseif ( $sub_components[0] == 'add button text' ) {
								$add_button_text = $sub_components[1];
							} elseif ( $sub_components[0] == 'embed in field' ) {
								// Placeholder on form template level. Assume that the template form def
								// will have a multiple+placeholder parameters, and get the placeholder value.
								// We expect something like TemplateName[fieldName], and convert it to the
								// TemplateName___fieldName form used internally.
								preg_match( '/\s*(.*)\[(.*)\]\s*/', $sub_components[1], $matches );
								$curPlaceholder = ( count( $matches ) > 2 ) ? self::placeholderFormat( $matches[1], $matches[2] ) : null;
								unset ( $matches );
							}
						}
					}
					// If this is the first instance, add
					// the label into the form, if there is
					// one, and add the appropriate wrapper
					// div, if this is a multiple-instance
					// template.
					if ( $old_template_name != $template_name ) {
						if ( isset( $template_label ) ) {
							$multipleTemplateString .= "<fieldset>\n";
							$multipleTemplateString .= "<legend>$template_label</legend>\n";
						}
						// If $curPlaceholder is set, it means we want to insert a
						// multiple template form's HTML into the main form's HTML.
 						// So, the HTML will be stored in $multipleTemplateString.
 						if ( $allow_multiple ) {
 							$multipleTemplateString .= "\t" . '<div class="multipleTemplateWrapper">' . "\n";
 							$multipleTemplateString .= "\t" . '<div class="multipleTemplateList">' . "\n";
 						}
					}
					if ( $curPlaceholder == null ) {
						$form_text .= $multipleTemplateString;
					}
					$template_text .= "{{" . $template_name;
					$all_fields = $tif->getAllFields();
					// remove template tag
					$section = substr_replace( $section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc );
					$template_instance_query_values = $wgRequest->getArray( $query_template_name );
					// If we are editing a page, and this
					// template can be found more than
					// once in that page, and multiple
					// values are allowed, repeat this
					// section.
					$existing_template_text = null;
					if ( $source_is_page || $form_is_partial ) {
						// Replace underlines with spaces in template name, to allow for
						// searching on either.
						$search_template_str = str_replace( '_', ' ', $template_name );
						$preg_match_template_str = str_replace(
							array( '/', '(', ')' ),
							array( '\/', '\(', '\)' ),
							$search_template_str );
						$found_instance = preg_match( '/{{' . $preg_match_template_str . '\s*[\|}]/i', str_replace( '_', ' ', $existing_page_content ) );
						if ( $allow_multiple ) {
							// find instances of this template in the page -
							// if there's at least one, re-parse this section of the
							// definition form for the subsequent template instances in
							// this page; if there's none, don't include fields at all.
							// there has to be a more efficient way to handle multiple
							// instances of templates, one that doesn't involve re-parsing
							// the same tags, but I don't know what it is.
							if ( $found_instance ) {
								$instance_num++;
							} else {
								$all_instances_printed = true;
							}
						}
						// get the first instance of this template on the page being edited,
						// even if there are more
						if ( $found_instance ) {
							$matches = array();
							$search_pattern = '/{{' . $preg_match_template_str . '\s*[\|}]/i';
							$content_str = str_replace( '_', ' ', $existing_page_content );
							preg_match( $search_pattern, $content_str, $matches, PREG_OFFSET_CAPTURE );
							// is this check necessary?
							if ( array_key_exists( 0, $matches ) && array_key_exists( 1, $matches[0] ) ) {
								$start_char = $matches[0][1];
								$fields_start_char = $start_char + 2 + strlen( $search_template_str );
								// Skip ahead to the first real character.
								while ( in_array( $existing_page_content[$fields_start_char], array( ' ', '\n' ) ) ) {
									$fields_start_char++;
								}
								// If the next character is a pipe, skip that too.
								if ( $existing_page_content[$fields_start_char] == '|' ) {
									$fields_start_char++;
								}
								$template_contents = array( '0' => '' );
								// Cycle through template call, splitting it up by pipes ('|'),
								// except when that pipe is part of a piped link.
								$field = "";
								$uncompleted_square_brackets = 0;
								$uncompleted_curly_brackets = 2;
								$template_ended = false;
								for ( $i = $fields_start_char; ! $template_ended && ( $i < strlen( $existing_page_content ) ); $i++ ) {
									$c = $existing_page_content[$i];
									if ( $c == '[' ) {
										$uncompleted_square_brackets++;
									} elseif ( $c == ']' && $uncompleted_square_brackets > 0 ) {
										$uncompleted_square_brackets--;
									} elseif ( $c == '{' ) {
										$uncompleted_curly_brackets++;
									} elseif ( $c == '}' && $uncompleted_curly_brackets > 0 ) {
										$uncompleted_curly_brackets--;
									}
									// handle an end to a field and/or template declaration
									$template_ended = ( $uncompleted_curly_brackets == 0 && $uncompleted_square_brackets == 0 );
									$field_ended = ( $c == '|' && $uncompleted_square_brackets == 0 && $uncompleted_curly_brackets <= 2 );
									if ( $template_ended || $field_ended ) {
										// if this was the last character in the template, remove
										// the closing curly brackets
										if ( $template_ended ) {
											$field = substr( $field, 0, - 1 );
										}
										// either there's an equals sign near the beginning or not -
										// handling is similar in either way; if there's no equals
										// sign, the index of this field becomes the key
										$sub_fields = explode( '=', $field, 2 );
										if ( count( $sub_fields ) > 1 ) {
											$template_contents[trim( $sub_fields[0] )] = trim( $sub_fields[1] );
										} else {
											$template_contents[] = trim( $sub_fields[0] );
										}
										$field = '';
									} else {
										$field .= $c;
									}
								}
								// If there are uncompleted opening brackets, the whole form will get messed up -
								// display a warning.
								// (If there are too many *closing* brackets, some template stuff will end up in
								// the "free text" field - which is bad, but it's harder for the code to detect
								// the problem - though hopefully, easier for users.)
								if ( $uncompleted_curly_brackets > 0 || $uncompleted_square_brackets > 0 ) {
									$form_text .= "\t" . '<div class="warningbox">' . wfMsg( 'sf_formedit_mismatchedbrackets', $this->mPageTitle->getFullURL( array( 'action' => 'edit' ) ) ) . "</div>\n<br clear=\"both\" />\n";
								}
								$existing_template_text = substr( $existing_page_content, $start_char, $i - $start_char );
								// now remove this template from the text being edited
								// if this is a partial form, establish a new insertion point
								if ( $existing_page_content && $form_is_partial && $wgRequest->getCheck( 'partial' ) ) {
									// if something already exists, set the new insertion point
									// to its position; otherwise just let it lie
									if ( strpos( $existing_page_content, $existing_template_text ) !== false ) {
										$existing_page_content = str_replace( '{{{insertionpoint}}}', '', $existing_page_content );
										$existing_page_content = str_replace( $existing_template_text, '{{{insertionpoint}}}', $existing_page_content );
									}
								} else {
									$existing_page_content = $this->strReplaceFirst( $existing_template_text, '', $existing_page_content );
								}
								// If this is not a multiple-instance template, and we've found
								// a match in the source page, there's a good chance that this
								// page was created with this form - note that, so we don't
								// send the user a warning
								// (multiple-instance templates have a greater chance of
								// getting repeated from one form to the next)
								// - on second thought, allow even the presence of multiple-
								// instance templates to validate that this is the correct
								// form: the problem is that some forms contain *only* mutliple-
								// instance templates.
								// if (! $allow_multiple) {
								$source_page_matches_this_form = true;
								// }
							}
						}
					}
					// If the input is from the form (meaning the user has hit one
					// of the bottom row of buttons), and we're dealing with a
					// multiple template, get the values for this instance of this
					// template, then delete them from the array, so we can get the
					// next group next time - the next() command for arrays doesn't
					// seem to work here.
					if ( ( ! $source_is_page ) && $allow_multiple && $wgRequest ) {
						$all_instances_printed = true;
						if ( $old_template_name != $template_name ) {
							$all_values_for_template = $wgRequest->getArray( $query_template_name );
						}
						if ( $all_values_for_template ) {
							$cur_key = key( $all_values_for_template );
							// skip the input coming in from the "starter" div
							// TODO: this code is probably no longer necessary
							if ( $cur_key == 'num' ) {
								unset( $all_values_for_template[$cur_key] );
								$cur_key = key( $all_values_for_template );
							}
							if ( $template_instance_query_values = current( $all_values_for_template ) ) {
								$all_instances_printed = false;
								unset( $all_values_for_template[$cur_key] );
							}
						}
					}
				// =====================================================
				// end template processing
				// =====================================================
				} elseif ( $tag_title == 'end template' ) {
					if ( $source_is_page ) {
						// Add any unhandled template fields in the page as hidden variables.
						if ( isset( $template_contents ) ) {
							$form_text .= SFFormUtils::unhandledFieldsHTML( $template_name, $template_contents );
							$template_contents = null;
						}
					}
					// Remove this tag, reset some variables, and close off form HTML tag.
					$section = substr_replace( $section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc );
					$template_name = null;
					if ( isset( $template_label ) && $curPlaceholder == null ) {
						$form_text .= "</fieldset>\n";
						unset ( $template_label );
					}
					$allow_multiple = false;
					$all_instances_printed = false;
					$instance_num = 0;
				// =====================================================
				// field processing
				// =====================================================
				} elseif ( $tag_title == 'field' ) {
					$field_name = trim( $tag_components[1] );
					$fullFieldName = $template_name . '[' . $field_name . ']';
					// cycle through the other components
					$is_mandatory = false;
					$is_hidden = false;
					$is_restricted = false;
					$is_uploadable = false;
					$is_list = false;
					$input_type = null;
					$field_args = array();
					$show_on_select = array();
					$default_value = null;
					$values = null;
					$possible_values = null;
					$semantic_property = null;
					$preload_page = null;
					$holds_template = false;

					for ( $i = 2; $i < count( $tag_components ); $i++ ) {

						$component = trim( $tag_components[$i] );

						if ( $component == 'mandatory' ) {
							$is_mandatory = true;
						} elseif ( $component == 'hidden' ) {
							$is_hidden = true;
						} elseif ( $component == 'restricted' ) {
							$is_restricted = ( ! $wgUser || ! $wgUser->isAllowed( 'editrestrictedfields' ) );
						} elseif ( $component == 'list' ) {
							$is_list = true;
						} elseif ( $component == 'edittools' ) { // free text only
							$free_text_components[] = 'edittools';
						}

						$sub_components = array_map( 'trim', explode( '=', $component, 2 ) );

						if ( count( $sub_components ) == 1 ) {
							// add handling for single-value params, for custom input types
							$field_args[$sub_components[0]] = true;

							if ( $component == 'holds template' ) {
								$is_hidden = true;
								$holds_template = true;
								$placeholderFields[] = self::placeholderFormat( $template_name, $field_name );
							}
						} elseif ( count( $sub_components ) == 2 ) {
							// First, set each value as its own entry in $field_args.
							$field_args[$sub_components[0]] = $sub_components[1];

							// Then, do all special handling.
							if ( $sub_components[0] == 'input type' ) {
								$input_type = $sub_components[1];
							} elseif ( $sub_components[0] == 'default' ) {
								$default_value = $wgParser->recursiveTagParse( $sub_components[1] );
							} elseif ( $sub_components[0] == 'preload' ) {
								// free text field has special handling
								if ( $field_name == 'free text' || $field_name == '<freetext>' ) {
									$free_text_preload_page = $sub_components[1];
								} else {
									$preload_page = $sub_components[1];
								}
							} elseif ( $sub_components[0] == 'show on select' ) {
								// html_entity_decode() is needed to turn '&gt;' to '>'
								$vals = explode( ';', html_entity_decode( $sub_components[1] ) );
								foreach ( $vals as $val ) {
									$val = trim( $val );
									if ( empty( $val ) )
										continue;
									$option_div_pair = explode( '=>', $val, 2 );
									if ( count( $option_div_pair ) > 1 ) {
										$option = $option_div_pair[0];
										$div_id = $option_div_pair[1];
										if ( array_key_exists( $div_id, $show_on_select ) )
											$show_on_select[$div_id][] = $option;
										else
											$show_on_select[$div_id] = array( $option );
									} else {
										$show_on_select[$val] = array();
									}
								}
							} elseif ( $sub_components[0] == 'autocomplete on property' ) {
								$property_name = $sub_components[1];
								$propValue = SMWPropertyValue::makeUserProperty( $property_name );
								if ( $propValue->getPropertyTypeID() == '_wpg' ) {
									$field_args['autocomplete field type'] = 'relation';
								} else {
									$field_args['autocomplete field type'] = 'attribute';
								}
								$field_args['autocompletion source'] = $sub_components[1];
							} elseif ( $sub_components[0] == 'autocomplete on category' ) {
								$field_args['autocomplete field type'] = 'category';
								$field_args['autocompletion source'] = $sub_components[1];
							} elseif ( $sub_components[0] == 'autocomplete on concept' ) {
								$field_args['autocomplete field type'] = 'concept';
								$field_args['autocompletion source'] = $sub_components[1];
							} elseif ( $sub_components[0] == 'autocomplete on namespace' ) {
								$field_args['autocomplete field type'] = 'namespace';
								$autocompletion_source = $sub_components[1];
								// special handling for "main" (blank) namespace
								if ( $autocompletion_source == "" )
									$autocompletion_source = "main";
								$field_args['autocompletion source'] = $autocompletion_source;
							} elseif ( $sub_components[0] == 'autocomplete from url' ) {
								$field_args['autocomplete field type'] = 'external_url';
								$field_args['autocompletion source'] = $sub_components[1];
								// 'external' autocompletion is always done remotely, i.e. via API
								$field_args['remote autocompletion'] = true;
							} elseif ( $sub_components[0] == 'values' ) {
								// Handle this one only after 'delimiter' has
								// also been set.
								$values = $sub_components[1];
							} elseif ( $sub_components[0] == 'values from property' ) {
								$propertyName = $sub_components[1];
								$possible_values = SFUtils::getAllValuesForProperty( $propertyName );
							} elseif ( $sub_components[0] == 'values from category' ) {
								$category_name = ucfirst( $sub_components[1] );
								$possible_values = SFUtils::getAllPagesForCategory( $category_name, 10 );
							} elseif ( $sub_components[0] == 'values from concept' ) {
								$possible_values = SFUtils::getAllPagesForConcept( $sub_components[1] );
							} elseif ( $sub_components[0] == 'values from namespace' ) {
								$possible_values = SFUtils::getAllPagesForNamespace( $sub_components[1] );
							} elseif ( $sub_components[0] == 'values dependent on' ) {
								global $sfgDependentFields;
								$sfgDependentFields[$sub_components[1]] = $fullFieldName;
							} elseif ( $sub_components[0] == 'property' ) {
								$semantic_property = $sub_components[1];
							} elseif ( $sub_components[0] == 'default filename' ) {
								$default_filename = str_replace( '&lt;page name&gt;', $page_name, $sub_components[1] );
								// Parse value, so default filename can include parser functions.
								$default_filename = $wgParser->recursiveTagParse( $default_filename );
								$field_args['default filename'] = $default_filename;
							} elseif ( $sub_components[0] == 'restricted' ) {
								$is_restricted = !array_intersect(
									$wgUser->getEffectiveGroups(), array_map( 'trim', explode( ',', $sub_components[1] ) )
								);
							}
						}
					} // end for
					// Backwards compatibility
					if ( $input_type == 'datetime with timezone' ) {
						$input_type = 'datetime';
						$field_args['include timezone'] = true;
					} elseif ( $input_type == 'text' || $input_type == 'textarea' ) {
						// Also for backwards compatibility,
						// in that once b/c goes away,
						// this will no longer be
						// necessary.
						$field_args['no autocomplete'] = true;
					}
					if ( $allow_multiple ) {
						$field_args['part_of_multiple'] = $allow_multiple;
					}
					if ( count( $show_on_select ) > 0 ) {
						$field_args['show on select'] = $show_on_select;
					}
					// Get the value from the request, if
					// it's there, and if it's not an array.
					$cur_value = null;
					$escaped_field_name = str_replace( "'", "\'", $field_name );
					if ( isset( $template_instance_query_values ) &&
						$template_instance_query_values != null &&
						is_array( $template_instance_query_values ) ) {
						// If the field name contains an
						// apostrophe, the array sometimes
						// has the apostrophe escaped, and
						// sometimes not. For now, just check
						// for both versions.
						// @TODO - figure this out.
						$field_query_val = null;
						if ( array_key_exists( $escaped_field_name, $template_instance_query_values ) ) {
							$field_query_val = $template_instance_query_values[$escaped_field_name];
						} elseif ( array_key_exists( $field_name, $template_instance_query_values ) ) {
							$field_query_val = $template_instance_query_values[$field_name];
						}
						if ( $form_submitted || ( ! empty( $field_query_val ) && ! is_array( $field_query_val ) ) ) {
							$cur_value = $field_query_val;
						}
					}

					if ( empty( $cur_value ) && !$form_submitted ) {
						if ( !is_null( $default_value ) ) {
							// Set to the default value specified in the form, if it's there.
							$cur_value = $default_value;
						} elseif ( $preload_page ) {
							$cur_value = SFFormUtils::getPreloadedText( $preload_page );
						}
					}

					// If the user is editing a page, and that page contains a call to
					// the template being processed, get the current field's value
					// from the template call
					if ( $source_is_page && ( ! empty( $existing_template_text ) ) ) {
						if ( isset( $template_contents[$field_name] ) ) {
							$cur_value = $template_contents[$field_name];

							// If the field is a placeholder, the contents of this template
							// parameter should be treated as elements parsed by an another
							// multiple template form.
							// By putting that at the very end of the parsed string, we'll
							// have it processed as a regular multiple template form.
							if ( $holds_template ) {
								$existing_page_content = $existing_page_content . $cur_value;
							}

							// Now remove this value
							// from $template_contents,
							// so that at the end we
							// can have a list of all
							// the fields that weren't
							// handled by the form.
							unset( $template_contents[$field_name] );
						} else {
							$cur_value = '';
						}
					}

					// Handle the free text field.
					if ( $field_name == '<freetext>' ) {
						// Add placeholders for the free text in both the form and
						// the page, using <free_text> tags - once all the free text
						// is known (at the end), it will get substituted in.
						if ( $is_hidden ) {
							$new_text = SFFormUtils::hiddenFieldHTML( 'free_text', '!free_text!' );
						} else {
							$sfgTabIndex++;
							$sfgFieldNum++;
							if ( $cur_value === '' || is_null( $cur_value )  ) {
								$default_value = '!free_text!';
							} else {
								$default_value = $cur_value;
								// If the FCKeditor extension is installed and
								// active, the default value needs to be parsed
								// for use in the editor.
								global $wgFCKEditorDir;
								if ( $wgFCKEditorDir && strpos( $existing_page_content, '__NORICHEDITOR__' ) === false ) {
									$showFCKEditor = SFFormUtils::getShowFCKEditor();
									if ( !$form_submitted && ( $showFCKEditor & RTE_VISIBLE ) ) {
										$default_value = SFFormUtils::prepareTextForFCK( $cur_value );
									}
								}
							}
							$new_text = SFTextAreaInput::getHTML( $default_value, 'free_text', false, ( $form_is_disabled || $is_restricted ), $field_args );
							if ( in_array( 'edittools', $free_text_components ) ) {
								// borrowed from EditPage::showEditTools()
								$options[] = 'parse';
								$edittools_text = wfMsgExt( 'edittools', array( 'parse' ), array( 'content' ) );

								$new_text .= <<<END
		<div class="mw-editTools">
		$edittools_text
		</div>

END;
							}
						}
						$free_text_was_included = true;
						// add a similar placeholder to the data text
						$data_text .= "!free_text!\n";
					}

					if ( $template_name === '' || $field_name == '<freetext>' ) {
						$section = substr_replace( $section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc );
					} else {
						if ( is_array( $cur_value ) ) {
							// first, check if it's a list
							if ( array_key_exists( 'is_list', $cur_value ) &&
									$cur_value['is_list'] == true ) {
								$cur_value_in_template = "";
								if ( array_key_exists( 'delimiter', $field_args ) ) {
									$delimiter = $field_args['delimiter'];
								} else {
									$delimiter = ",";
								}
								foreach ( $cur_value as $key => $val ) {
									if ( $key !== "is_list" ) {
										if ( $cur_value_in_template != "" ) {
											$cur_value_in_template .= $delimiter . " ";
										}
										$cur_value_in_template .= $val;
									}
								}
							} else {
								// otherwise:
								// if it has 1 or 2 elements, assume it's a checkbox; if it has
								// 3 elements, assume it's a date
								// - this handling will have to get more complex if other
								// possibilities get added
								if ( count( $cur_value ) == 1 ) {
									$cur_value_in_template = SFUtils::getWordForYesOrNo( false );
								} elseif ( count( $cur_value ) == 2 ) {
									$cur_value_in_template = SFUtils::getWordForYesOrNo( true );
								// if it's 3 or greater, assume it's a date or datetime
								} elseif ( count( $cur_value ) >= 3 ) {
									$month = $cur_value['month'];
									$day = $cur_value['day'];
									if ( $day !== '' ) {
										global $wgAmericanDates;
										if ( $wgAmericanDates == false ) {
											// pad out day to always be two digits
											$day = str_pad( $day, 2, "0", STR_PAD_LEFT );
										}
									}
									$year = $cur_value['year'];
									$hour = $minute = $second = $ampm24h = $timezone = null;
									if ( isset( $cur_value['hour'] ) ) $hour = $cur_value['hour'];
									if ( isset( $cur_value['minute'] ) ) $minute = $cur_value['minute'];
									if ( isset( $cur_value['second'] ) ) $second = $cur_value['second'];
									if ( isset( $cur_value['ampm24h'] ) ) $ampm24h = $cur_value['ampm24h'];
									if ( isset( $cur_value['timezone'] ) ) $timezone = $cur_value['timezone'];
									if ( $month !== '' && $day !== '' && $year !== '' ) {
										// special handling for American dates - otherwise, just
										// the standard year/month/day (where month is a number)
										global $wgAmericanDates;
										if ( $wgAmericanDates == true ) {
											$cur_value_in_template = "$month $day, $year";
										} else {
											$cur_value_in_template = "$year/$month/$day";
										}
										// include whatever time information we have
										if ( ! is_null( $hour ) )
											$cur_value_in_template .= " " . str_pad( intval( substr( $hour, 0, 2 ) ), 2, '0', STR_PAD_LEFT ) . ":" . str_pad( intval( substr( $minute, 0, 2 ) ), 2, '0', STR_PAD_LEFT );
										if ( ! is_null( $second ) )
											$cur_value_in_template .= ":" . str_pad( intval( substr( $second, 0, 2 ) ), 2, '0', STR_PAD_LEFT );
										if ( ! is_null( $ampm24h ) )
											$cur_value_in_template .= " $ampm24h";
										if ( ! is_null( $timezone ) )
											$cur_value_in_template .= " $timezone";
									} else {
										$cur_value_in_template = "";
									}
								}
							}
						} else { // value is not an array
							$cur_value_in_template = $cur_value;
						}
						if ( $template_name == null || $template_name === '' ) {
							$input_name = $field_name;
						} elseif ( $allow_multiple ) {
							// 'num' will get replaced by an actual index, either in PHP
							// or in Javascript, later on
							$input_name = $template_name . '[num][' . $field_name . ']';
							$field_args['origName'] = $template_name . '[' . $field_name . ']';
						} else {
							$input_name = $template_name . '[' . $field_name . ']';
						}


						// If the 'values' parameter was set, separate it based on the
						// 'delimiter' parameter, if any.
						if ( ! empty( $values ) ) {
							if ( array_key_exists( 'delimiter', $field_args ) ) {
								$delimiter = $field_args['delimiter'];
							} else {
								$delimiter = ",";
							}
							// Remove whitespaces, and un-escape characters
							$possible_values = array_map( 'trim', explode( $delimiter, $values ) );
							$possible_values = array_map( 'htmlspecialchars_decode', $possible_values );
						}

						// if we're creating the page name from a formula based on
						// form values, see if the current input is part of that formula,
						// and if so, substitute in the actual value
						if ( $form_submitted && $generated_page_name !== '' ) {
							// this line appears to be unnecessary
							// $generated_page_name = str_replace('.', '_', $generated_page_name);
							$generated_page_name = str_replace( ' ', '_', $generated_page_name );
							$escaped_input_name = str_replace( ' ', '_', $input_name );
							$generated_page_name = str_ireplace( "<$escaped_input_name>", $cur_value_in_template, $generated_page_name );
							// once the substitution is done, replace underlines back
							// with spaces
							$generated_page_name = str_replace( '_', ' ', $generated_page_name );
						}
						// disable this field if either the whole form is disabled, or
						// it's a restricted field and user doesn't have sysop privileges
						$is_disabled = ( $form_is_disabled || $is_restricted );
						// Create an SFFormField instance based on all the parameters
						// in the form definition, and any information from the template
						// definition (contained in the $all_fields parameter).
						$form_field = SFFormField::createFromDefinition( $field_name,
							$input_name, $is_mandatory, $is_hidden, $is_uploadable,
							$possible_values, $is_disabled, $is_list, $input_type,
							$field_args, $all_fields, $strict_parsing );
						// If a property was set in the form definition, overwrite whatever
						// is set in the template field - this is somewhat of a hack, since
						// parameters set in the form definition are meant to go into the
						// SFFormField object, not the SFTemplateField object it contains;
						// it seemed like too much work, though, to create an
						// SFFormField::setSemanticProperty() function just for this call
						if ( $semantic_property != null ) {
							$form_field->template_field->setSemanticProperty( $semantic_property );
						}
						$semantic_property = $form_field->template_field->getSemanticProperty();
						if ( !is_null( $semantic_property ) ) {
							global $sfgFieldProperties;
							$sfgFieldProperties[$fullFieldName] = $semantic_property;
						}


						// call hooks - unfortunately this has to be split into two
						// separate calls, because of the different variable names in
						// each case
						if ( $form_submitted ) {
							wfRunHooks( 'sfCreateFormField', array( &$form_field, &$cur_value_in_template, true ) );
						} else {
							wfRunHooks( 'sfCreateFormField', array( &$form_field, &$cur_value, false ) );
						}
						// if this is not part of a 'multiple' template, increment the
						// global tab index (used for correct tabbing)
						if ( ! array_key_exists( 'part_of_multiple', $field_args ) ) {
							$sfgTabIndex++;
						}
						// increment the global field number regardless
						$sfgFieldNum++;
						// If the field is a date field, and its default value was set
						// to 'now', and it has no current value, set $cur_value to be
						// the current date.
						if ( $default_value == 'now' &&
								// if the date is hidden, cur_value will already be set
								// to the default value
								( $cur_value === '' || $cur_value == 'now' ) ) {
							if ( $input_type == 'date' || $input_type == 'datetime' ||
									$input_type == 'year' ||
									( $input_type === '' && $form_field->getTemplateField()->getPropertyType() == '_dat' ) ) {
								// Get current time, for the time zone specified in the wiki.
								global $wgLocaltimezone;
								if ( isset( $wgLocaltimezone ) ) {
									$serverTimezone = date_default_timezone_get();
									date_default_timezone_set( $wgLocaltimezone );
								}
								$cur_time = time();
								$year = date( "Y", $cur_time );
								$month = date( "n", $cur_time );
								$day = date( "j", $cur_time );
								global $wgAmericanDates, $sfg24HourTime;
								if ( $wgAmericanDates == true ) {
									$month_names = SFFormUtils::getMonthNames();
									$month_name = $month_names[$month - 1];
									$cur_value_in_template = "$month_name $day, $year";
								} else {
									$cur_value_in_template = "$year/$month/$day";
								}
								if ( isset( $wgLocaltimezone ) ) {
									date_default_timezone_set( $serverTimezone );
								}
								if ( $input_type ==	'datetime' ) {
									if ( $sfg24HourTime ) {
										$hour = str_pad( intval( substr( date( "G", $cur_time ), 0, 2 ) ), 2, '0', STR_PAD_LEFT );
									} else {
										$hour = str_pad( intval( substr( date( "g", $cur_time ), 0, 2 ) ), 2, '0', STR_PAD_LEFT );
									}
									$minute = str_pad( intval( substr( date( "i", $cur_time ), 0, 2 ) ), 2, '0', STR_PAD_LEFT );
									$second = str_pad( intval( substr( date( "s", $cur_time ), 0, 2 ) ), 2, '0', STR_PAD_LEFT );
									if ( $sfg24HourTime ) {
										$cur_value_in_template .= " $hour:$minute:$second";
									} else {
										$ampm = date( "A", $cur_time );
										$cur_value_in_template .= " $hour:$minute:$second $ampm";
									}
								}
								if ( array_key_exists( 'include timezone', $field_args ) ) {
									$timezone = date( "T", $cur_time );
									$cur_value_in_template .= " $timezone";
								}
							}
						}
						// If the field is a text field, and its default value was set
						// to 'current user', and it has no current value, set $cur_value
						// to be the current user.
						if ( $default_value == 'current user' &&
							// if the date is hidden, cur_value will already be set
							// to the default value
							( $cur_value === '' || $cur_value == 'current user' ) ) {

							$cur_value_in_template = $wgUser->getName();
							$cur_value = $cur_value_in_template;
						}

						// Generate a hidden field with a placeholder value that will be replaced
						// by the multiple-instances template output at form submission.
						//// <input type="hidden" value="@replace_Town___mayors@" name="Town[town_mayors]" />
						if ( $holds_template ) {
							$cur_value = self::makePlaceholderInWikiText( self::placeholderFormat( $template_name, $field_name ) );
						}

						$new_text = $this->formFieldHTML( $form_field, $cur_value );

						// Add a field just after the hidden field, within the HTML, to locate
						// where the multiple-templates HTML, stored in $multipleTemplateString,
						// should be inserted.
						if ( $holds_template ) {
							$new_text .= self::makePlaceholderInFormHTML( self::placeholderFormat( $template_name, $field_name ) );
						}

						// If this field is disabled, add a hidden field holding
						// the value of this field, because disabled inputs for some
						// reason don't submit their value.
						if ( $form_field->isDisabled() ) {
							if ( $field_name == 'free text' || $field_name == '<freetext>' ) {
								$new_text .= SFFormUtils::hiddenFieldHTML( 'free_text', '!free_text!' );
							} else {
								$new_text .= SFFormUtils::hiddenFieldHTML( $input_name, $cur_value );
							}
						}

						if ( $new_text ) {
							// Include the field name only for non-numeric field names.
							if ( is_numeric( $field_name ) ) {
								$template_text .= "|$cur_value_in_template";
							} else {
								// If the value is null, don't include it at all.
								if ( $cur_value_in_template !== '' ) {
									$template_text .= "\n|$field_name=$cur_value_in_template";
								}
							}
							$section = substr_replace( $section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc );
						} else {
							$start_position = $brackets_end_loc;
						}
					}
				// =====================================================
				// standard input processing
				// =====================================================
				} elseif ( $tag_title == 'standard input' ) {
					// handle all the possible values
					$input_name = $tag_components[1];
					$input_label = null;
					$attr = array();

					// if it's a query, ignore all standard inputs except run query
					if ( ( $is_query && $input_name != 'run query' ) || ( !$is_query && $input_name == 'run query' ) ) {
						$new_text = "";
						$section = substr_replace( $section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc );
						continue;
					}
					// set a flag so that the standard 'form bottom' won't get displayed
					$this->standardInputsIncluded = true;
					// cycle through the other components
					for ( $i = 2; $i < count( $tag_components ); $i++ ) {
						$component = $tag_components[$i];
						$sub_components = array_map( 'trim', explode( '=', $component ) );
						if ( count( $sub_components ) == 1 ) {
							if ( $sub_components[0] == 'edittools' ) {
								$free_text_components[] = 'edittools';
							}
						} elseif ( count( $sub_components ) == 2 ) {
							switch( $sub_components[0] ) {
							case 'label':
								$input_label = $sub_components[1];
								break;
							case 'class':
							case 'style':
								$attr[$sub_components[0]] = $sub_components[1];
								break;
							}
							// free text input needs more handling than the rest
							if ( $input_name == 'free text' || $input_name == '<freetext>' ) {
								if ( $sub_components[0] == 'preload' ) {
									$free_text_preload_page = $sub_components[1];
								}
							}
						}
					}
					if ( $input_name == 'summary' ) {
						$new_text = SFFormUtils::summaryInputHTML( $form_is_disabled, $input_label, $attr );
					} elseif ( $input_name == 'minor edit' ) {
						$new_text = SFFormUtils::minorEditInputHTML( $form_is_disabled, $input_label, $attr );
					} elseif ( $input_name == 'watch' ) {
						$new_text = SFFormUtils::watchInputHTML( $form_is_disabled, $input_label, $attr );
					} elseif ( $input_name == 'save' ) {
						$new_text = SFFormUtils::saveButtonHTML( $form_is_disabled, $input_label, $attr );
					} elseif ( $input_name == 'save and continue' ) {
						$new_text = SFFormUtils::saveAndContinueButtonHTML( $form_is_disabled, $input_label, $attr );
					} elseif ( $input_name == 'preview' ) {
						$new_text = SFFormUtils::showPreviewButtonHTML( $form_is_disabled, $input_label, $attr );
					} elseif ( $input_name == 'changes' ) {
						$new_text = SFFormUtils::showChangesButtonHTML( $form_is_disabled, $input_label, $attr );
					} elseif ( $input_name == 'cancel' ) {
						$new_text = SFFormUtils::cancelLinkHTML( $form_is_disabled, $input_label, $attr );
					} elseif ( $input_name == 'run query' ) {
						$new_text = SFFormUtils::runQueryButtonHTML( $form_is_disabled, $input_label, $attr );
					}
					$section = substr_replace( $section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc );
				// =====================================================
				// page info processing
				// =====================================================
				} elseif ( $tag_title == 'info' ) {
					// TODO: Generate an error message if this is included more than once
					foreach ( array_slice( $tag_components, 1 ) as $component ) {
						$sub_components = array_map( 'trim', explode( '=', $component, 2 ) );
						// Tag names are case-insensitive
						$tag = strtolower( $sub_components[0] );
						if ( $tag == 'create title' || $tag == 'add title' ) {
							// Handle this only if
							// we're adding a page.
							if ( !$is_query && !$this->mPageTitle->exists() ) {
								$form_page_title = $sub_components[1];
							}
						} elseif ( $tag == 'edit title' ) {
							// Handle this only if
							// we're editing a page.
							if ( !$is_query && $this->mPageTitle->exists() ) {
								$form_page_title = $sub_components[1];
							}
						} elseif ( $tag == 'query title' ) {
							// Handle this only if
							// we're in 'RunQuery'.
							if ( $is_query ) {
								$form_page_title = $sub_components[1];
							}
						} elseif ( $tag == 'partial form' ) {
							$form_is_partial = true;
							// replacement pages may have minimal matches...
							$source_page_matches_this_form = true;
						} elseif ( $tag == 'includeonly free text' || $tag == 'onlyinclude free text' ) {
							$onlyinclude_free_text = true;
						} elseif ( $tag == 'query form at top' ) {
							// TODO - this should be made a field of
							// some non-static class that actually
							// prints the form, instead of requiring
							// a global variable.
							global $sfgRunQueryFormAtTop;
							$sfgRunQueryFormAtTop = true;
						}
					}
					$section = substr_replace( $section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc );
				// =====================================================
				// default outer level processing
				// =====================================================
				} else { // Tag is not one of the three allowed values -
					// ignore the tag.
					$start_position = $brackets_end_loc;
				} // end if
			} // end while

			if ( ! $all_instances_printed ) {
				if ( $template_text !== '' ) {
					// For mostly aesthetic purposes, if the template call ends with
					// a bunch of pipes (i.e., it's an indexed template with unused
					// parameters at the end), remove the pipes.
					$template_text = preg_replace( '/\|*$/', '', $template_text );
					// add another newline before the final bracket, if this template
					// call is already more than one line
					if ( strpos( $template_text, "\n" ) ) {
						$template_text .= "\n";
					}
					// If we're editing an existing page, and there were fields in
					// the template call not handled by this form, preserve those.
					if ( !$allow_multiple ) {
						$template_text .= SFFormUtils::addUnhandledFields( $template_name );
					}
					$template_text .= "}}";

					// The base $template_text will contain strings like "@replace_xxx@"
					// in the hidden fields when the form is submitted.
					// On the following loops, the text for the multiple-instance templates
					// is progressively reinserted in the main data, always keeping a
					// trailing @replace_xxx@ for a given field
					// The trailing @replace_xxx@ is then deleted at the end.
					// Note: this cleanup step could also be done with a regexp, instead of
					// keeping a track array (e.g., /@replace_(.*)@/)
					$reptmp = self::makePlaceholderInWikiText( $curPlaceholder );
					if ( $curPlaceholder != null && $data_text && strpos( $data_text, $reptmp, 0 ) !== false ) {
						$data_text = preg_replace( '/' . $reptmp . '/', $template_text . $reptmp, $data_text );
					} else {
						$data_text .= $template_text . "\n";
					}

					// If there is a placeholder in the
					// text, we know that we are
					// doing a replace.
					if ( $existing_page_content && strpos( $existing_page_content, '{{{insertionpoint}}}', 0 ) !== false ) {
						$existing_page_content = preg_replace( '/\{\{\{insertionpoint\}\}\}(\r?\n?)/',
							preg_replace( '/\}\}/m', '}�',
								preg_replace( '/\{\{/m', '�{', $template_text ) ) .
							"\n{{{insertionpoint}}}",
							$existing_page_content );
					// otherwise, if it's a partial form, we have to add the new
					// text somewhere
					} elseif ( $form_is_partial && $wgRequest->getCheck( 'partial' ) ) {
						$existing_page_content = preg_replace( '/\}\}/m', '}�',
							preg_replace( '/\{\{/m', '�{', $template_text ) ) .
								"\n{{{insertionpoint}}}\n" . $existing_page_content;
					}
				}
			}

			if ( $allow_multiple ) {
				if ( $curPlaceholder == null ) {
					// The normal process.
					$form_text .= $this->multipleTemplateInstanceHTML( $form_is_disabled, $all_instances_printed, $section, $instance_num, $add_button_text );
				} else { // if ( $curPlaceholder != null ){
					// The template text won't be appended at the end of the template like for usual multiple template forms.
					// The HTML text will then be stored in the $multipleTemplateString variable,
					// and then added in the right @insertHTML_".$placeHolderField."@"; position
					// Optimization: actually, instead of separating the processes, the usual multiple
					// template forms could also be handled this way if a fitting placeholder tag was added.
					$multipleTemplateString .= $this->multipleTemplateInstanceHTML( $form_is_disabled, $all_instances_printed, $section, $instance_num, $add_button_text );
					// We replace the $multipleTemplateString HTML into the
					// current placeholder tag, but also add another
					// placeholder tag, to keep track of it.
					$multipleTemplateString .= self::makePlaceholderInFormHTML( $curPlaceholder );
					if ( isset( $template_label ) ) {
						$multipleTemplateString .= "</fieldset>\n";
						unset ( $template_label );
					}
					$form_text = preg_replace( '/' . self::makePlaceholderInFormHTML( $curPlaceholder ) . '/',
						$multipleTemplateString, $form_text );
				}
				if ( ! $all_instances_printed ) {
					// This will cause the section to be
					// re-parsed on the next go.
					$section_num--;
				}
			} else { // if ( $allow_multiple ) {
				$form_text .= $section;
			}
			$curPlaceholder = null;
		} // end for

		// Cleanup - everything has been browsed.
		// Remove all the remaining placeholder
		// tags in the HTML and wiki-text.
		foreach ( $placeholderFields as $stringToReplace ) {
			// remove the @<replacename>@ tags from the data that is submitted
			$data_text = preg_replace( '/' . self::makePlaceholderInWikiText( $stringToReplace ) . '/', '', $data_text );

			// remove the @<insertHTML>@ tags from the generated HTML form
			$form_text = preg_replace( '/' . self::makePlaceholderInFormHTML( $stringToReplace ) . '/', '', $form_text );
		}

		// if it wasn't included in the form definition, add the
		// 'free text' input as a hidden field at the bottom
		if ( ! $free_text_was_included ) {
			$form_text .= SFFormUtils::hiddenFieldHTML( 'free_text', '!free_text!' );
		}
		// Get free text, and add to page data, as well as retroactively
		// inserting it into the form.

		// If $form_is_partial is true then either:
		// (a) we're processing a replacement (param 'partial' == 1)
		// (b) we're sending out something to be replaced (param 'partial' is missing)
		if ( $form_is_partial ) {
			if ( !$wgRequest->getCheck( 'partial' ) ) {
				$free_text = $original_page_content;
				$form_text .= SFFormUtils::hiddenFieldHTML( 'partial', 1 );
			} else {
				$free_text = null;
				$existing_page_content = preg_replace( array( '/�\{/m', '/\}�/m' ),
					array( '{{', '}}' ),
					$existing_page_content );
				$existing_page_content = preg_replace( '/\{\{\{insertionpoint\}\}\}/', '', $existing_page_content );
			}
		} elseif ( $source_is_page ) {
			// if the page is the source, free_text will just be whatever in the
			// page hasn't already been inserted into the form
			$free_text = trim( $existing_page_content );
		// or get it from a form submission
		} elseif ( $wgRequest->getCheck( 'free_text' ) ) {
			$free_text = $wgRequest->getVal( 'free_text' );
			if ( ! $free_text_was_included ) {
				$data_text .= "!free_text!";
			}
		// or get it from the form definition
		} elseif ( $free_text_preload_page != null ) {
			$free_text = SFFormUtils::getPreloadedText( $free_text_preload_page );
		} else {
			$free_text = null;
		}
		if ( $onlyinclude_free_text ) {
			// modify free text and data text to insert <onlyinclude> tags
			$free_text = str_replace( "<onlyinclude>", '', $free_text );
			$free_text = str_replace( "</onlyinclude>", '', $free_text );
			$free_text = trim( $free_text );
			$data_text = str_replace( '!free_text!', '<onlyinclude>!free_text!</onlyinclude>', $data_text );
		}

		wfRunHooks( 'sfModifyFreeTextField', array( &$free_text, $existing_page_content ) );
		// if the FCKeditor extension is installed, use that for the free text input
		global $wgFCKEditorDir;
		if ( $wgFCKEditorDir && strpos( $existing_page_content, '__NORICHEDITOR__' ) === false ) {
			$showFCKEditor = SFFormUtils::getShowFCKEditor();
			if ( !$form_submitted && ( $showFCKEditor & RTE_VISIBLE ) ) {
				$free_text = SFFormUtils::prepareTextForFCK( $free_text );
			}
		} else {
			$showFCKEditor = 0;
		}
		// now that we have it, substitute free text into the form and page
		$escaped_free_text = Sanitizer::safeEncodeAttribute( $free_text );
		$form_text = str_replace( '!free_text!', $escaped_free_text, $form_text );
		$data_text = str_replace( '!free_text!', $free_text, $data_text );

		// Add a warning in, if we're editing an existing page and that
		// page appears to not have been created with this form.
		if ( !$is_query && $this->mPageTitle->exists() && ( $existing_page_content !== '' ) && ! $source_page_matches_this_form ) {
			$form_text = "\t" . '<div class="warningbox">' . wfMsg( 'sf_formedit_formwarning', $this->mPageTitle->getFullURL() ) . "</div>\n<br clear=\"both\" />\n" . $form_text;
		}

		// add form bottom, if no custom "standard inputs" have been defined
		if ( !$this->standardInputsIncluded ) {
			if ( $is_query )
				$form_text .= SFFormUtils::queryFormBottom( $form_is_disabled );
			else
				$form_text .= SFFormUtils::formBottom( $form_is_disabled );
		}

		$page_article = new Article( $this->mPageTitle, 0 );

		if ( !$is_query ) {
			$form_text .= SFFormUtils::hiddenFieldHTML( 'wpStarttime', wfTimestampNow() );
			$form_text .= SFFormUtils::hiddenFieldHTML( 'wpEdittime', $page_article->getTimestamp() );
		}

		$form_text .= "\t</form>\n";

		// Add general Javascript code.
		wfRunHooks( 'sfAddJavascriptToForm', array( &$javascript_text ) );

		// @TODO The FCKeditor Javascript should be handled within
		// the FCKeditor extension itself, using the hook.
		$javascript_text = "";
		if ( $free_text_was_included && $showFCKEditor > 0 ) {
			$javascript_text .= SFFormUtils::mainFCKJavascript( $showFCKEditor, $field_args );
			if ( $showFCKEditor & ( RTE_TOGGLE_LINK | RTE_POPUP ) ) {
				$javascript_text .= SFFormUTils::FCKToggleJavascript();
			}
			if ( $showFCKEditor & RTE_POPUP ) {
				$javascript_text .= SFFormUTils::FCKPopupJavascript();
			}
		}

		// Send the autocomplete values to the browser, along with the
		// mappings of which values should apply to which fields.
		// If doing a replace, the data text is actually the modified original page
		if ( $wgRequest->getCheck( 'partial' ) )
			$data_text = $existing_page_content;

		if ( !$is_embedded ) {
			$form_page_title = $wgParser->recursiveTagParse( str_replace( "{{!}}", "|", $form_page_title ) );
		} else {
			$form_page_title = null;
		}

		// If the form has already been submitted, i.e. this is just
		// the redirect page, get rid of all the Javascript, to avoid
		// JS errors.
		if ( $form_submitted ) {
			$javascript_text = '';
		}

		$parserOutput = $wgParser->getOutput();
		$wgOut->addParserOutputNoText( $parserOutput );

		$wgParser = $oldParser;

		wfProfileOut( __METHOD__ );

		return array( $form_text, $javascript_text, $data_text, $form_page_title, $generated_page_name );
	}
 private function printForm(&$parameters, WebRequest &$request)
 {
     global $wgOut, $sfgFormPrinter;
     // Prepare parameters for SFFormPrinter::formHTML
     // there is no ONE target page
     $targetTitle = null;
     // formDefinition
     $formName = $request->getText('form');
     // if query string did not contain these variables, try the URL
     if ($formName === '') {
         $queryparts = explode('/', $parameters);
         $formName = isset($queryparts[0]) ? $queryparts[0] : null;
         // if the form name wasn't in the URL either, throw an error
         if (is_null($formName) || $formName === '') {
             throw new SPSException(SPSUtils::buildMessage('spserror-noformname'));
         }
     }
     $formTitle = Title::makeTitleSafe(SF_NS_FORM, $formName);
     if (!$formTitle->exists()) {
         throw new SPSException(SPSUtils::buildMessage('spserror-formunknown', $formName));
     }
     $formArticle = new Article($formTitle);
     $formDefinition = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $formArticle->getContent());
     // formSubmitted
     $formSubmitted = false;
     // pageContents
     $pageContents = null;
     // get 'preload' query value, if it exists
     if ($request->getCheck('preload')) {
         $pageContents = SFFormUtils::getPreloadedText($request->getVal('preload'));
     } else {
         // let other extensions preload the page, if they want
         wfRunHooks('sfEditFormPreloadText', array(&$pageContents, $targetTitle, $formTitle));
     }
     // pageIsSource
     $pageIsSource = $pageContents != null;
     // pageNameFormula
     // parse the form to see if it has a 'page name' value set
     $matches;
     if (preg_match('/{{{info.*page name\\s*=\\s*(.*)}}}/m', $formDefinition, $matches)) {
         $pageNameElements = SFUtils::getFormTagComponents($matches[1]);
         $pageNameFormula = $pageNameElements[0];
     } else {
         return 'sf_formedit_badurl';
     }
     // get the iterator parameters
     $iteratorData = $this->buildIteratorParameters($request);
     // Call SFFormPrinter::formHTML
     list($formText, $javascriptText, $dataText, $formPageTitle, $generatedPageName) = $sfgFormPrinter->formHTML($formDefinition, $formSubmitted, $pageIsSource, $formArticle->getID(), $pageContents, '', $pageNameFormula);
     // Set Special page main header;
     // override the default title for this page if a title was specified in the form
     if ($formPageTitle != null) {
         $wgOut->setPageTitle($formPageTitle);
     } else {
         $wgOut->setPageTitle(SPSUtils::buildMessage('sf_formedit_createtitlenotarget', $formTitle->getText()));
     }
     $preFormHtml = '';
     wfRunHooks('sfHTMLBeforeForm', array(&$targetTitle, &$preFormHtml));
     $text = '<form name="createbox" id="sfForm" action="" method="post" class="createbox">' . $preFormHtml . "\n" . SFFormUtils::hiddenFieldHTML('iteratordata', $iteratorData) . $formText;
     SFUtils::addJavascriptAndCSS();
     if (!empty($javascriptText)) {
         $wgOut->addScript('		<script type="text/javascript">' . "\n{$javascriptText}\n" . '</script>' . "\n");
     }
     $wgOut->addHTML($text);
     return null;
 }
 function getCurrentValue($template_instance_query_values, $form_submitted, $source_is_page)
 {
     // Get the value from the request, if
     // it's there, and if it's not an array.
     $cur_value = null;
     $field_name = $this->template_field->getFieldName();
     $delimiter = $this->mFieldArgs['delimiter'];
     $escaped_field_name = str_replace("'", "\\'", $field_name);
     if (isset($template_instance_query_values) && $template_instance_query_values != null && is_array($template_instance_query_values)) {
         // If the field name contains an apostrophe, the array
         // sometimes has the apostrophe escaped, and sometimes
         // not. For now, just check for both versions.
         // @TODO - figure this out.
         $field_query_val = null;
         if (array_key_exists($escaped_field_name, $template_instance_query_values)) {
             $field_query_val = $template_instance_query_values[$escaped_field_name];
         } elseif (array_key_exists($field_name, $template_instance_query_values)) {
             $field_query_val = $template_instance_query_values[$field_name];
         }
         if ($form_submitted && $field_query_val != '') {
             $map_field = false;
             if (array_key_exists('map_field', $template_instance_query_values) && array_key_exists($field_name, $template_instance_query_values['map_field'])) {
                 $map_field = true;
             }
             if (is_array($field_query_val)) {
                 $cur_values = array();
                 if ($map_field && !is_null($this->mPossibleValues)) {
                     $cur_values = array();
                     foreach ($field_query_val as $key => $val) {
                         $val = trim($val);
                         if ($key === 'is_list') {
                             $cur_values[$key] = $val;
                         } else {
                             $cur_values[] = SFUtils::labelToValue($val, $this->mPossibleValues);
                         }
                     }
                 } else {
                     foreach ($field_query_val as $key => $val) {
                         $cur_values[$key] = $val;
                     }
                 }
                 return SFFormPrinter::getStringFromPassedInArray($cur_values, $delimiter);
             } else {
                 $field_query_val = trim($field_query_val);
                 if ($map_field && !is_null($this->mPossibleValues)) {
                     // this should be replaced with an input type neutral way of
                     // figuring out if this scalar input type is a list
                     if ($this->mInputType == "tokens") {
                         $is_list = true;
                     }
                     if ($is_list) {
                         $cur_values = array_map('trim', explode($delimiter, $field_query_val));
                         foreach ($cur_values as $key => $value) {
                             $cur_values[$key] = SFUtils::labelToValue($value, $this->mPossibleValues);
                         }
                         return implode($delimiter, $cur_values);
                     }
                     return SFUtils::labelToValue($field_query_val, $this->mPossibleValues);
                 }
                 return $field_query_val;
             }
         }
         if (!$form_submitted && $field_query_val != '') {
             if (is_array($field_query_val)) {
                 return SFFormPrinter::getStringFromPassedInArray($field_query_val, $delimiter);
             }
             return $field_query_val;
         }
     }
     if (!$source_is_page && empty($cur_value) && !$form_submitted) {
         if (!is_null($this->mDefaultValue)) {
             // Set to the default value specified in the form, if it's there.
             return $this->mDefaultValue;
         } elseif ($this->mPreloadPage) {
             return SFFormUtils::getPreloadedText($this->mPreloadPage);
         }
     }
     return $cur_value;
     // null
 }
Example #4
0
 static function printForm(&$form_name, &$target_name, $alt_forms = array(), $redirectOnError = false)
 {
     global $wgOut, $wgRequest, $wgUser, $sfgFormPrinter;
     // initialize some variables
     $target_title = null;
     $page_name_formula = null;
     $form_title = Title::makeTitleSafe(SF_NS_FORM, $form_name);
     // If the given form is not a valid title, bail out.
     if (!$form_title) {
         return 'sf_formedit_badurl';
     }
     $form_article = new Article($form_title, 0);
     $form_definition = $form_article->getContent();
     // If the form page is a redirect, use the other form
     // instead.
     if ($form_title->isRedirect()) {
         $form_title = Title::newFromRedirectRecurse($form_definition);
         $form_article = new Article($form_title, 0);
         $form_definition = $form_article->getContent();
     }
     $form_definition = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $form_definition);
     if (is_null($target_name)) {
         $target_name = '';
     }
     if ($target_name === '') {
         // parse the form to see if it has a 'page name' value set
         $matches;
         if (preg_match('/{{{info.*page name\\s*=\\s*(.*)}}}/m', $form_definition, $matches)) {
             $page_name_elements = SFUtils::getFormTagComponents($matches[1]);
             $page_name_formula = $page_name_elements[0];
         } elseif (count($alt_forms) == 0) {
             return 'sf_formedit_badurl';
         }
     } else {
         $target_title = Title::newFromText($target_name);
         if ($target_title && $target_title->exists()) {
             if ($wgRequest->getVal('query') == 'true') {
                 $page_contents = null;
                 //$page_is_source = false;
             } else {
                 // If page already exists and 'redlink'
                 // is in the query string, redirect to
                 // the actual page, just like
                 // MediaWiki does it.
                 if ($wgRequest->getBool('redlink')) {
                     $wgOut->redirect($target_title->getFullURL());
                     wfProfileOut(__METHOD__);
                     return;
                 }
                 $target_article = new Article($target_title, 0);
                 $page_contents = $target_article->getContent();
                 //$page_is_source = true;
             }
         } else {
             $target_name = str_replace('_', ' ', $target_name);
         }
     }
     if (!$form_title || !$form_title->exists()) {
         if (count($alt_forms) > 0) {
             $text = '<div class="infoMessage">' . wfMsg('sf_formedit_altformsonly') . ' ' . self::printAltFormsList($alt_forms, $form_name) . "</div>\n";
         } else {
             $text = Html::rawElement('p', array('class' => 'error'), wfMsgExt('sf_formstart_badform', 'parseinline', SFUtils::linkText(SF_NS_FORM, $form_name))) . "\n";
         }
     } elseif ($target_name === '' && $page_name_formula === '') {
         $text = Html::element('p', array('class' => 'error'), wfMsg('sf_formedit_badurl')) . "\n";
     } else {
         $save_page = $wgRequest->getCheck('wpSave');
         $preview_page = $wgRequest->getCheck('wpPreview');
         $diff_page = $wgRequest->getCheck('wpDiff');
         $form_submitted = $save_page || $preview_page || $diff_page;
         // get 'preload' query value, if it exists
         if (!$form_submitted) {
             if ($wgRequest->getCheck('preload')) {
                 $page_is_source = true;
                 $page_contents = SFFormUtils::getPreloadedText($wgRequest->getVal('preload'));
             } else {
                 // let other extensions preload the page, if they want
                 wfRunHooks('sfEditFormPreloadText', array(&$page_contents, $target_title, $form_title));
                 $page_is_source = $page_contents != null;
             }
         } else {
             $page_is_source = false;
             $page_contents = null;
         }
         list($form_text, $javascript_text, $data_text, $form_page_title, $generated_page_name) = $sfgFormPrinter->formHTML($form_definition, $form_submitted, $page_is_source, $form_article->getID(), $page_contents, $target_name, $page_name_formula);
         // Before we do anything else, set the form header
         // title - this needs to be done after formHTML() is
         // called, because otherwise it doesn't take hold
         // for some reason if the form is disabled.
         if (empty($target_title)) {
             $s = wfMsg('sf_formedit_createtitlenotarget', $form_title->getText());
         } elseif ($target_title->exists()) {
             $s = wfMsg('sf_formedit_edittitle', $form_title->getText(), $target_title->getPrefixedText());
         } else {
             $s = wfMsg('sf_formedit_createtitle', $form_title->getText(), $target_title->getPrefixedText());
         }
         $wgOut->setPageTitle($s);
         if ($form_submitted) {
             if (!is_null($page_name_formula) && $page_name_formula !== '') {
                 $target_name = $generated_page_name;
                 // prepend a super-page, if one was specified
                 if ($wgRequest->getCheck('super_page')) {
                     $target_name = $wgRequest->getVal('super_page') . '/' . $target_name;
                 }
                 // prepend a namespace, if one was specified
                 if ($wgRequest->getCheck('namespace')) {
                     $target_name = $wgRequest->getVal('namespace') . ':' . $target_name;
                 }
                 // replace "unique number" tag with one
                 // that won't get erased by the next line
                 $target_name = preg_replace('/<unique number(.*)>/', '{num\\1}', $target_name, 1);
                 // if any formula stuff is still in the
                 // name after the parsing, just remove it
                 $target_name = StringUtils::delimiterReplace('<', '>', '', $target_name);
                 // now run the parser on it
                 global $wgParser;
                 // ...but first, replace spaces back
                 // with underlines, in case a magic word
                 // or parser function name contains
                 // underlines - hopefully this won't
                 // cause problems of its own
                 $target_name = str_replace(' ', '_', $target_name);
                 $target_name = $wgParser->preprocess($target_name, $wgOut->getTitle(), ParserOptions::newFromUser(null));
                 $title_number = "";
                 $isRandom = false;
                 $randomNumHasPadding = false;
                 $randomNumDigits = 6;
                 if (strpos($target_name, '{num') !== false) {
                     // Random number
                     if (preg_match('/{num;random(;(0)?([1-9][0-9]*))?}/', $target_name, $matches)) {
                         $isRandom = true;
                         $randomNumHasPadding = array_key_exists(2, $matches);
                         $randomNumDigits = array_key_exists(3, $matches) ? $matches[3] : $randomNumDigits;
                         $title_number = self::makeRandomNumber($randomNumDigits, $randomNumHasPadding);
                     } else {
                         // get unique number start value
                         // from target name; if it's not
                         // there, or it's not a positive
                         // number, start it out as blank
                         preg_match('/{num.*start[_]*=[_]*([^;]*).*}/', $target_name, $matches);
                         if (count($matches) == 2 && is_numeric($matches[1]) && $matches[1] >= 0) {
                             // the "start" value"
                             $title_number = $matches[1];
                         }
                     }
                     // set target title
                     $target_title = Title::newFromText(preg_replace('/{num.*}/', $title_number, $target_name));
                     // if title exists already
                     // cycle through numbers for
                     // this tag until we find one
                     // that gives a nonexistent page
                     // title
                     while ($target_title->exists()) {
                         if ($isRandom) {
                             $title_number = self::makeRandomNumber($randomNumDigits, $randomNumHasPadding);
                         } elseif ($title_number == "") {
                             $title_number = 2;
                         } else {
                             $title_number = str_pad($title_number + 1, strlen($title_number), '0', STR_PAD_LEFT);
                         }
                         $target_title = Title::newFromText(preg_replace('/{num.*}/', $title_number, $target_name));
                     }
                     $target_name = $target_title->getPrefixedText();
                 } else {
                     $target_title = Title::newFromText($target_name);
                 }
             }
             if (is_null($target_title)) {
                 if ($target_name) {
                     return array('sf_formstart_badtitle', array($target_name));
                 } else {
                     return 'sf_formedit_emptytitle';
                 }
             }
             if ($save_page) {
                 $permErrors = $target_title->getUserPermissionsErrors('edit', $wgUser);
                 if ($permErrors) {
                     // just return the first error and let them fix it one by one
                     return array_shift($permErrors);
                 }
                 // Set up all the variables for the
                 // page save.
                 $data = array('wpTextbox1' => $data_text, 'wpSummary' => $wgRequest->getVal('wpSummary'), 'wpStarttime' => $wgRequest->getVal('wpStarttime'), 'wpEdittime' => $wgRequest->getVal('wpEdittime'), 'wpEditToken' => $wgUser->isLoggedIn() ? $wgUser->editToken() : EDIT_TOKEN_SUFFIX, 'wpSave' => '', 'action' => 'submit');
                 if ($wgRequest->getCheck('wpMinoredit')) {
                     $data['wpMinoredit'] = true;
                 }
                 if ($wgRequest->getCheck('wpWatchthis')) {
                     $data['wpWatchthis'] = true;
                 }
                 $request = new FauxRequest($data, true);
                 // Find existing article if it exists,
                 // or create a new one.
                 $article = new Article($target_title, 0);
                 $editor = new EditPage($article);
                 $editor->importFormData($request);
                 // Try to save the page!
                 $resultDetails = array();
                 $saveResult = $editor->internalAttemptSave($resultDetails);
                 // Return value was made an object in MW 1.19
                 if (is_object($saveResult)) {
                     $saveResultCode = $saveResult->value;
                 } else {
                     $saveResultCode = $saveResult;
                 }
                 if (($saveResultCode == EditPage::AS_HOOK_ERROR || $saveResultCode == EditPage::AS_HOOK_ERROR_EXPECTED) && $redirectOnError) {
                     $wgOut->clearHTML();
                     $wgOut->setArticleBodyOnly(true);
                     // Lets other code process additional form-definition syntax
                     wfRunHooks('sfWritePageData', array($form_name, $target_title, &$data_text));
                     $text = SFUtils::printRedirectForm($target_title, $data_text, $wgRequest->getVal('wpSummary'), $save_page, $preview_page, $diff_page, $wgRequest->getCheck('wpMinoredit'), $wgRequest->getCheck('wpWatchthis'), $wgRequest->getVal('wpStarttime'), $wgRequest->getVal('wpEdittime'));
                 } else {
                     if ($saveResultCode == EditPage::AS_SUCCESS_UPDATE || $saveResultCode == EditPage::AS_SUCCESS_NEW_ARTICLE) {
                         $wgOut->redirect($target_title->getFullURL());
                     }
                     return SFUtils::processEditErrors($saveResultCode);
                 }
             } else {
                 // Lets other code process additional form-definition syntax
                 wfRunHooks('sfWritePageData', array($form_name, $target_title, &$data_text));
                 $text = SFUtils::printRedirectForm($target_title, $data_text, $wgRequest->getVal('wpSummary'), $save_page, $preview_page, $diff_page, $wgRequest->getCheck('wpMinoredit'), $wgRequest->getCheck('wpWatchthis'), $wgRequest->getVal('wpStarttime'), $wgRequest->getVal('wpEdittime'));
                 // extract its data
             }
         } else {
             // override the default title for this page if
             // a title was specified in the form
             if ($form_page_title != null) {
                 if ($target_name === '') {
                     $wgOut->setPageTitle($form_page_title);
                 } else {
                     $wgOut->setPageTitle("{$form_page_title}: {$target_title->getPrefixedText()}");
                 }
             }
             $text = "";
             if (count($alt_forms) > 0) {
                 $text .= '<div class="infoMessage">' . wfMsg('sf_formedit_altforms') . ' ';
                 $text .= self::printAltFormsList($alt_forms, $target_name);
                 $text .= "</div>\n";
             }
             $text .= '<form name="createbox" id="sfForm" method="post" class="createbox">';
             $pre_form_html = '';
             wfRunHooks('sfHTMLBeforeForm', array(&$target_title, &$pre_form_html));
             $text .= $pre_form_html;
             $text .= $form_text;
         }
     }
     SFUtils::addJavascriptAndCSS();
     if (!empty($javascript_text)) {
         $wgOut->addScript('		<script type="text/javascript">' . "\n{$javascript_text}\n" . '</script>' . "\n");
     }
     $wgOut->addHTML($text);
     return null;
 }
Example #5
0
 public function formSerialize($form_def = '', $source_is_page = false, $existing_page_content = null, $page_title = null)
 {
     //	public function formSerialize($form_def, $source_is_page, $existing_page_content = null, $page_title = null, $page_name_formula = null) {
     global $wgRequest, $wgUser, $wgParser;
     global $sfgTabIndex;
     // used to represent the current tab index in the form
     global $sfgFieldNum;
     // used for setting various HTML IDs
     global $sfgJSValidationCalls;
     // array of Javascript calls to determine if page can be saved
     # define a var for all fields
     $__fields = array();
     // initialize some variables
     $sfgTabIndex = 1;
     $sfgFieldNum = 1;
     $source_page_matches_this_form = false;
     $form_page_title = NULL;
     // $form_is_partial is true if:
     // (a) 'partial' == 1 in the arguments
     // (b) 'partial form' is found in the form definition
     // in the latter case, it may remain false until close to the end of
     // the parsing, so we have to assume that it will become a possibility
     $form_is_partial = false;
     $new_text = "";
     // if we have existing content and we're not in an active replacement
     // situation, preserve the original content. We do this because we want
     // to pass the original content on IF this is a partial form
     // TODO: A better approach here would be to pass the revision id of the
     // existing page content through the replace value, which would
     // minimize the html traffic and would allow us to do a concurrent
     // update check.  For now, we pass it through the hidden text field...
     if (!$wgRequest->getCheck('partial')) {
         $original_page_content = $existing_page_content;
     } else {
         $original_page_content = null;
         if ($wgRequest->getCheck('free_text')) {
             $existing_page_content = $wgRequest->getVal('free_text');
             $form_is_partial = true;
         }
     }
     // disable all form elements if user doesn't have edit permission -
     // two different checks are needed, because editing permissions can be
     // set in different ways
     // HACK - sometimes we don't know the page name in advance, but we still
     // need to set a title here for testing permissions
     if ($page_title == '') {
         $this->mPageTitle = Title::newFromText("Semantic Forms permissions test");
     } else {
         $this->mPageTitle = Title::newFromText($page_title);
     }
     if ($wgUser->isAllowed('edit') && $this->mPageTitle->userCanEdit()) {
         $form_is_disabled = false;
         $form_text = "";
         // show "Your IP address will be recorded" warning if user is
         // anonymous - wikitext for bolding has to be replaced with HTML
         if ($wgUser->isAnon()) {
             $anon_edit_warning = preg_replace("/'''(.*)'''/", "<strong>\$1</strong>", wfMsg('anoneditwarning'));
             $form_text .= "<p>{$anon_edit_warning}</p>\n";
         }
     } else {
         $form_is_disabled = true;
         // display a message to the user explaining why they can't edit the
         // page - borrowed heavily from EditPage.php
         if ($wgUser->isAnon()) {
             $skin = $wgUser->getSkin();
             $loginTitle = SpecialPage::getTitleFor('Userlogin');
             $loginLink = $skin->makeKnownLinkObj($loginTitle, wfMsgHtml('loginreqlink'));
             $form_text = wfMsgWikiHtml('whitelistedittext', $loginLink);
         } else {
             $form_text = wfMsg('protectedpagetext');
         }
     }
     $javascript_text = "";
     $sfgJSValidationCalls = array();
     $fields_javascript_text = "";
     // Remove <noinclude> sections and <includeonly> tags from form definition
     $form_def = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $form_def);
     $form_def = strtr($form_def, array('<includeonly>' => '', '</includeonly>' => ''));
     // parse wiki-text
     // add '<nowiki>' tags around every triple-bracketed form definition
     // element, so that the wiki parser won't touch it - the parser will
     // remove the '<nowiki>' tags, leaving us with what we need
     global $sfgDisableWikiTextParsing;
     if (!$sfgDisableWikiTextParsing) {
         $form_def = "__NOEDITSECTION__" . strtr($form_def, array('{{{' => '<nowiki>{{{', '}}}' => '}}}</nowiki>'));
         $wgParser->mOptions = new ParserOptions();
         $wgParser->mOptions->initialiseFromUser($wgUser);
         $form_def = $wgParser->parse($form_def, $this->mPageTitle, $wgParser->mOptions)->getText();
     }
     // turn form definition file into an array of sections, one for each
     // template definition (plus the first section)
     $form_def_sections = array();
     $start_position = 0;
     $section_start = 0;
     $free_text_was_included = false;
     $free_text_preload_page = null;
     $all_values_for_template = array();
     // unencode and HTML-encoded representations of curly brackets and
     // pipes - this is a hack to allow for forms to include templates
     // that themselves contain form elements - the escaping is needed
     // to make sure that those elements don't get parsed too early
     $form_def = str_replace(array('&#123;', '&#124;', '&#125;'), array('{', '|', '}'), $form_def);
     // and another hack - replace the 'free text' standard input with
     // a field declaration to get it to be handled as a field
     $form_def = str_replace('standard input|free text', 'field|<freetext>', $form_def);
     while ($brackets_loc = strpos($form_def, "{{{", $start_position)) {
         $brackets_end_loc = strpos($form_def, "}}}", $brackets_loc);
         $bracketed_string = substr($form_def, $brackets_loc + 3, $brackets_end_loc - ($brackets_loc + 3));
         $tag_components = explode('|', $bracketed_string);
         $tag_title = trim($tag_components[0]);
         if ($tag_title == 'for template' || $tag_title == 'end template') {
             // create a section for everything up to here
             $section = substr($form_def, $section_start, $brackets_loc - $section_start);
             $form_def_sections[] = $section;
             $section_start = $brackets_loc;
         }
         $start_position = $brackets_loc + 1;
     }
     // end while
     $form_def_sections[] = trim(substr($form_def, $section_start));
     // cycle through form definition file (and possibly an existing article
     // as well), finding template and field declarations and replacing them
     // with form elements, either blank or pre-populated, as appropriate
     $all_fields = array();
     $data_text = "";
     $template_name = "";
     $allow_multiple = false;
     $instance_num = 0;
     $all_instances_printed = false;
     $strict_parsing = false;
     // initialize list of choosers (dropdowns with available templates)
     $choosers = array();
     for ($section_num = 0; $section_num < count($form_def_sections); $section_num++) {
         $tif = new SFTemplateInForm();
         $start_position = 0;
         $template_text = "";
         // the append is there to ensure that the original array doesn't get
         // modified; is it necessary?
         $section = " " . $form_def_sections[$section_num];
         while ($brackets_loc = strpos($section, '{{{', $start_position)) {
             $brackets_end_loc = strpos($section, "}}}", $brackets_loc);
             $bracketed_string = substr($section, $brackets_loc + 3, $brackets_end_loc - ($brackets_loc + 3));
             $tag_components = explode('|', $bracketed_string);
             $tag_title = trim($tag_components[0]);
             // =====================================================
             // for template processing
             // =====================================================
             if ($tag_title == 'for template') {
                 $old_template_name = $template_name;
                 $template_name = trim($tag_components[1]);
                 $tif->template_name = $template_name;
                 $query_template_name = str_replace(' ', '_', $template_name);
                 // also replace periods with underlines, since that's what
                 // POST does to strings anyway
                 $query_template_name = str_replace('.', '_', $query_template_name);
                 $chooser_name = false;
                 $chooser_caption = false;
                 // cycle through the other components
                 for ($i = 2; $i < count($tag_components); $i++) {
                     $component = $tag_components[$i];
                     if ($component == 'multiple') {
                         $allow_multiple = true;
                     }
                     if ($component == 'strict') {
                         $strict_parsing = true;
                     }
                     $sub_components = explode('=', $component);
                     if (count($sub_components) == 2) {
                         if ($sub_components[0] == 'label') {
                             $template_label = $sub_components[1];
                         } elseif ($sub_components[0] == 'chooser') {
                             $allow_multiple = true;
                             $chooser_name = $sub_components[1];
                         } elseif ($sub_components[0] == 'chooser caption') {
                             $chooser_caption = $sub_components[1];
                         }
                     }
                 }
                 // if this is the first instance, add the label in the form
                 if ($old_template_name != $template_name && isset($template_label)) {
                     // add a placeholder to the form text so the fieldset can be
                     // hidden if chooser support demands it
                     if ($chooser_name !== false) {
                         $form_text .= "<fieldset [[placeholder]] haschooser=true>\n";
                     } else {
                         $form_text .= "<fieldset>\n";
                     }
                     $form_text .= "<legend>{$template_label}</legend>\n";
                 }
                 $template_text .= "{{" . $tif->template_name;
                 # reads all the fields of the template definition page
                 $all_fields = $tif->getAllFields();
                 // remove template tag
                 $section = substr_replace($section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc);
                 $template_instance_query_values = $wgRequest->getArray($query_template_name);
                 // if we are editing a page, and this template can be found more than
                 // once in that page, and multiple values are allowed, repeat this
                 // section
                 $existing_template_text = null;
                 // replace underlines with spaces in template name, to allow for
                 // searching on either
                 $search_template_str = str_replace('_', ' ', $tif->template_name);
                 if ($source_is_page || $form_is_partial) {
                     if ($allow_multiple) {
                         // find instances of this template in the page -
                         // if there's at least one, re-parse this section of the
                         // definition form for the subsequent template instances in
                         // this page; if there's none, don't include fields at all.
                         // there has to be a more efficient way to handle multiple
                         // instances of templates, one that doesn't involve re-parsing
                         // the same tags, but I don't know what it is.
                         if (stripos(str_replace('_', ' ', $existing_page_content), '{{' . $search_template_str) !== false) {
                             $instance_num++;
                         } else {
                             $all_instances_printed = true;
                         }
                     }
                     // get the first instance of this template on the page being edited,
                     // even if there are more
                     if (($start_char = stripos(str_replace('_', ' ', $existing_page_content), '{{' . $search_template_str)) !== false) {
                         $fields_start_char = $start_char + 2 + strlen($search_template_str);
                         // skip ahead to the first real character
                         while (in_array($existing_page_content[$fields_start_char], array(' ', '\\n', '|'))) {
                             $fields_start_char++;
                         }
                         $template_contents = array('0' => '');
                         // cycle through template call, splitting it up by pipes ('|'),
                         // except when that pipe is part of a piped link
                         $field = "";
                         $uncompleted_square_brackets = 0;
                         $uncompleted_curly_brackets = 2;
                         $template_ended = false;
                         for ($i = $fields_start_char; !$template_ended && $i < strlen($existing_page_content); $i++) {
                             $c = $existing_page_content[$i];
                             if ($c == '[') {
                                 $uncompleted_square_brackets++;
                             } elseif ($c == ']' && $uncompleted_square_brackets > 0) {
                                 $uncompleted_square_brackets--;
                             } elseif ($c == '{') {
                                 $uncompleted_curly_brackets++;
                             } elseif ($c == '}' && $uncompleted_curly_brackets > 0) {
                                 $uncompleted_curly_brackets--;
                             }
                             // handle an end to a field and/or template declaration
                             $template_ended = $uncompleted_curly_brackets == 0 && $uncompleted_square_brackets == 0;
                             $field_ended = $c == '|' && $uncompleted_square_brackets == 0;
                             if ($template_ended || $field_ended) {
                                 // if this was the last character in the template, remove
                                 // the closing curly brackets
                                 if ($template_ended) {
                                     $field = substr($field, 0, -1);
                                 }
                                 // either there's an equals sign near the beginning or not -
                                 // handling is similar in either way; if there's no equals
                                 // sign, the index of this field becomes the key
                                 $sub_fields = explode('=', $field, 2);
                                 if (count($sub_fields) > 1) {
                                     $template_contents[trim($sub_fields[0])] = trim($sub_fields[1]);
                                 } else {
                                     $template_contents[] = trim($sub_fields[0]);
                                 }
                                 $field = '';
                             } else {
                                 $field .= $c;
                             }
                         }
                         $existing_template_text = substr($existing_page_content, $start_char, $i - $start_char);
                         // now remove this template from the text being edited
                         // if this is a partial form, establish a new insertion point
                         if ($existing_page_content && $form_is_partial && $wgRequest->getCheck('partial')) {
                             // if something already exists, set the new insertion point
                             // to its position; otherwise just let it lie
                             if (strpos($existing_page_content, $existing_template_text) !== false) {
                                 $existing_page_content = str_replace('{{{insertionpoint}}}', '', $existing_page_content);
                                 $existing_page_content = str_replace($existing_template_text, '{{{insertionpoint}}}', $existing_page_content);
                             }
                         } else {
                             $existing_page_content = str_replace($existing_template_text, '', $existing_page_content);
                         }
                         // if this is not a multiple-instance template, and we've found
                         // a match in the source page, there's a good chance that this
                         // page was created with this form - note that, so we don't
                         // send the user a warning
                         // (multiple-instance templates have a greater chance of
                         // getting repeated from one form to the next)
                         if (!$allow_multiple) {
                             $source_page_matches_this_form = true;
                         }
                     }
                 }
                 // if the input is from the form (meaning the user has hit one
                 // of the bottom row of buttons), and we're dealing with a
                 // multiple template, get the values for this instance of this
                 // template, then delete them from the array, so we can get the
                 // next group next time - the next() command for arrays doesn't
                 // seem to work here
                 if (!$source_is_page && $allow_multiple && $wgRequest) {
                     $all_instances_printed = true;
                     if ($old_template_name != $template_name) {
                         $all_values_for_template = $wgRequest->getArray($query_template_name);
                     }
                     if ($all_values_for_template) {
                         $cur_key = key($all_values_for_template);
                         // skip the input coming in from the "starter" div
                         if ($cur_key == 'num') {
                             unset($all_values_for_template[$cur_key]);
                             $cur_key = key($all_values_for_template);
                         }
                         if ($template_instance_query_values = current($all_values_for_template)) {
                             $all_instances_printed = false;
                             unset($all_values_for_template[$cur_key]);
                         }
                     }
                 }
                 //  save the template name
                 $field = array();
                 $field['tmpl_name'] = $tif->template_name;
                 // =====================================================
                 // end template processing
                 // =====================================================
             } elseif ($tag_title == 'end template') {
                 // remove this tag, reset some variables, and close off form HTML tag
                 $section = substr_replace($section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc);
                 $template_name = null;
                 if (isset($template_label)) {
                     $form_text .= "</fieldset>\n";
                     unset($template_label);
                 }
                 $allow_multiple = false;
                 $all_instances_printed = false;
                 $instance_num = 0;
                 // if the hiding placeholder is still around, this fieldset should
                 // be hidden because it is empty and choosers are being used. So,
                 // hide it.
                 $form_text = str_replace("[[placeholder]]", "style='display:none'", $form_text);
                 $__fields["template" . count($__fields)] = $field;
                 // =====================================================
                 // field processing
                 // =====================================================
             } elseif ($tag_title == 'field') {
                 $field_name = trim($tag_components[1]);
                 // cycle through the other components
                 $is_mandatory = false;
                 $is_hidden = false;
                 $is_restricted = false;
                 $is_uploadable = false;
                 $is_list = false;
                 $input_type = null;
                 $field_args = array();
                 $default_value = "";
                 $possible_values = null;
                 $preload_page = null;
                 for ($i = 2; $i < count($tag_components); $i++) {
                     $component = trim($tag_components[$i]);
                     if ($component == 'mandatory') {
                         $is_mandatory = true;
                     } elseif ($component == 'hidden') {
                         $is_hidden = true;
                     } elseif ($component == 'restricted') {
                         $is_restricted = true;
                     } elseif ($component == 'uploadable') {
                         $field_args['is_uploadable'] = true;
                     } elseif ($component == 'list') {
                         $is_list = true;
                     } elseif ($component == 'autocomplete') {
                         $field_args['autocomplete'] = true;
                     } elseif ($component == 'no autocomplete') {
                         $field_args['no autocomplete'] = true;
                     } elseif ($component == 'remote autocompletion') {
                         $field_args['remote autocompletion'] = true;
                     } else {
                         $sub_components = explode('=', $component);
                         if (count($sub_components) == 2) {
                             if ($sub_components[0] == 'input type') {
                                 $input_type = $sub_components[1];
                             } elseif ($sub_components[0] == 'default') {
                                 $default_value = $sub_components[1];
                             } elseif ($sub_components[0] == 'preload') {
                                 // free text field has special handling
                                 if ($field_name == 'free text' || ($field_name = '<freetext>')) {
                                     $free_text_preload_page = $sub_components[1];
                                 } else {
                                     // this variable is not used
                                     $preload_page = $sub_components[1];
                                 }
                             } elseif ($sub_components[0] == 'values') {
                                 $possible_values = explode(',', $sub_components[1]);
                             } elseif ($sub_components[0] == 'values from category') {
                                 $possible_values = SFUtils::getAllPagesForCategory($sub_components[1], 10);
                             } elseif ($sub_components[0] == 'values from concept') {
                                 $possible_values = SFUtils::getAllPagesForConcept($sub_components[1]);
                             } else {
                                 $field_args[$sub_components[0]] = $sub_components[1];
                             }
                             // for backwards compatibility
                             if ($sub_components[0] == 'autocomplete on' && $sub_components[1] == null) {
                                 $field_args['no autocomplete'] = true;
                             }
                         }
                     }
                 }
                 $field_args['part_of_multiple'] = $allow_multiple;
                 // get the value from the request, if it's there, and if it's not
                 // an array
                 $escaped_field_name = str_replace("'", "\\'", $field_name);
                 if (isset($template_instance_query_values) && $template_instance_query_values != null && array_key_exists($escaped_field_name, $template_instance_query_values)) {
                     $field_query_val = $template_instance_query_values[$escaped_field_name];
                     if ($field_query_val && !is_array($field_query_val)) {
                         $cur_value = $field_query_val;
                     }
                 } else {
                     $cur_value = '';
                 }
                 if ($cur_value && !is_array($cur_value)) {
                     # no escaping needed
                     // $cur_value = str_replace('"', '&quot;', $cur_value);
                 }
                 if ($cur_value == null) {
                     // set to default value specified in the form, if it's there
                     $cur_value = $default_value;
                 }
                 // if the user is starting to edit a page, and that page contains
                 // the current template being processed, get the current template
                 // field's value in the existing page
                 if ($source_is_page && !empty($existing_template_text)) {
                     if (isset($template_contents[$field_name])) {
                         $cur_value = $template_contents[$field_name];
                     } else {
                         $cur_value = '';
                     }
                     if ($cur_value) {
                         # no escaping needed
                         // $cur_value = str_replace('"', '&quot;', $cur_value);
                     }
                 }
                 // handle the free text field - if it was declared as
                 // "field|free text" (a deprecated usage), it has to be outside
                 // of a template
                 if ($template_name == '' && $field_name == 'free text' || $field_name == '<freetext>') {
                     // add placeholders for the free text in both the form and
                     // the page, using <free_text> tags - once all the free text
                     // is known (at the end), it will get substituted in
                     if ($is_hidden) {
                         $new_text = SFFormUtils::hiddenFieldHTML('free_text', '<free_text>');
                     } else {
                         if (!array_key_exists('rows', $field_args)) {
                             $field_args['rows'] = 5;
                         }
                         if (!array_key_exists('cols', $field_args)) {
                             $field_args['cols'] = 80;
                         }
                         $sfgTabIndex++;
                         $sfgFieldNum++;
                         list($new_text, $new_javascript_text) = SFFormInputs::textAreaHTML('<free_text>', 'free_text', false, $form_is_disabled || $is_restricted, $field_args);
                         $fields_javascript_text .= $new_javascript_text;
                     }
                     $free_text_was_included = true;
                     // add a similar placeholder to the data text
                     $data_text .= "<free_text>\n";
                 }
                 if ($template_name == '') {
                     $section = substr_replace($section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc);
                 } else {
                     if (is_array($cur_value)) {
                         // first, check if it's a list
                         if (array_key_exists('is_list', $cur_value) && $cur_value['is_list'] == true) {
                             $cur_value_in_template = "";
                             if (array_key_exists('delimiter', $field_args)) {
                                 $delimiter = $field_args['delimiter'];
                             } else {
                                 $delimiter = ",";
                             }
                             foreach ($cur_value as $key => $val) {
                                 if ($key !== "is_list") {
                                     if ($cur_value_in_template != "") {
                                         $cur_value_in_template .= $delimiter . " ";
                                     }
                                     $cur_value_in_template .= $val;
                                 }
                             }
                         } else {
                             // otherwise:
                             // if it has 1 or 2 elements, assume it's a checkbox; if it has
                             // 3 elements, assume it's a date
                             // - this handling will have to get more complex if other
                             // possibilities get added
                             if (count($cur_value) == 1) {
                                 // manually load SMW's message values here, in case they
                                 // didn't get loaded before
                                 wfLoadExtensionMessages('SemanticMediaWiki');
                                 $words_for_false = explode(',', wfMsgForContent('smw_false_words'));
                                 // for each language, there's a series of words that are
                                 // equal to false - get the word in the series that matches
                                 // "no"; generally, that's the third word
                                 $index_of_no = 2;
                                 if (count($words_for_false) > $index_of_no) {
                                     $no = ucwords($words_for_false[$index_of_no]);
                                 } elseif (count($words_for_false) == 0) {
                                     $no = "0";
                                     // some safe value if no words are found
                                 } else {
                                     $no = ucwords($words_for_false[0]);
                                 }
                                 $cur_value_in_template = $no;
                             } elseif (count($cur_value) == 2) {
                                 wfLoadExtensionMessages('SemanticMediaWiki');
                                 $words_for_true = explode(',', wfMsgForContent('smw_true_words'));
                                 // get the value in the 'true' series that tends to be "yes",
                                 // and go with that one - generally, that's the third word
                                 $index_of_yes = 2;
                                 if (count($words_for_true) > $index_of_yes) {
                                     $yes = ucwords($words_for_true[$index_of_yes]);
                                 } elseif (count($words_for_true) == 0) {
                                     $yes = "1";
                                     // some safe value if no words are found
                                 } else {
                                     $yes = ucwords($words_for_true[0]);
                                 }
                                 $cur_value_in_template = $yes;
                                 // if it's 3 or greater, assume it's a date or datetime
                             } elseif (count($cur_value) >= 3) {
                                 $month = $cur_value['month'];
                                 $day = $cur_value['day'];
                                 if ($day != '') {
                                     global $wgAmericanDates;
                                     if ($wgAmericanDates == false) {
                                         // pad out day to always be two digits
                                         $day = str_pad($day, 2, "0", STR_PAD_LEFT);
                                     }
                                 }
                                 $year = $cur_value['year'];
                                 if (isset($cur_value['hour'])) {
                                     $hour = $cur_value['hour'];
                                 }
                                 if (isset($cur_value['minute'])) {
                                     $minute = $cur_value['minute'];
                                 }
                                 if (isset($cur_value['second'])) {
                                     $second = $cur_value['second'];
                                 }
                                 if (isset($cur_value['ampm24h'])) {
                                     $ampm24h = $cur_value['ampm24h'];
                                 }
                                 if (isset($cur_value['timezone'])) {
                                     $timezone = $cur_value['timezone'];
                                 }
                                 if ($month != '' && $day != '' && $year != '') {
                                     // special handling for American dates - otherwise, just
                                     // the standard year/month/day (where month is a number)
                                     global $wgAmericanDates;
                                     if ($wgAmericanDates == true) {
                                         $cur_value_in_template = "{$month} {$day}, {$year}";
                                     } else {
                                         $cur_value_in_template = "{$year}/{$month}/{$day}";
                                     }
                                     // include whatever time information we have
                                     if (isset($hour)) {
                                         $cur_value_in_template .= " " . str_pad(intval(substr($hour, 0, 2)), 2, '0', STR_PAD_LEFT) . ":" . str_pad(intval(substr($minute, 0, 2)), 2, '0', STR_PAD_LEFT);
                                     }
                                     if (isset($second)) {
                                         $cur_value_in_template .= ":" . str_pad(intval(substr($second, 0, 2)), 2, '0', STR_PAD_LEFT);
                                     }
                                     if (isset($ampm24h)) {
                                         $cur_value_in_template .= " {$ampm24h}";
                                     }
                                     if (isset($timezone)) {
                                         $cur_value_in_template .= " {$timezone}";
                                     }
                                 } else {
                                     $cur_value_in_template = "";
                                 }
                             }
                         }
                     } else {
                         // value is not an array
                         $cur_value_in_template = $cur_value;
                     }
                     if ($query_template_name == null || $query_template_name == '') {
                         $input_name = $field_name;
                     } elseif ($allow_multiple) {
                         // 'num' will get replaced by an actual index, either in PHP
                         // or in Javascript, later on
                         $input_name = $query_template_name . '[num][' . $field_name . ']';
                     } else {
                         $input_name = $query_template_name . '[' . $field_name . ']';
                     }
                     // disable this field if either the whole form is disabled, or
                     // it's a restricted field and user doesn't have sysop privileges
                     $is_disabled = $form_is_disabled || $is_restricted && (!$wgUser || !$wgUser->isAllowed('editrestrictedfields'));
                     // create an SFFormTemplateField instance based on all the
                     // parameters in the form definition, and any information from
                     // the template definition (contained in the $all_fields parameter)
                     # creation of a form field from the definition page
                     $possible_values['_element'] = "value";
                     $form_field = $this->createFromDefinitionForSerialization($field_name, $input_name, $is_mandatory, $is_hidden, $is_uploadable, $possible_values, $is_disabled, $is_list, $input_type, $field_args, $all_fields, $strict_parsing);
                     // if this is not part of a 'multiple' template, incrememt the
                     // global tab index (used for correct tabbing)
                     if (!$field_args['part_of_multiple']) {
                         $sfgTabIndex++;
                     }
                     // increment the global field number regardless
                     $sfgFieldNum++;
                     // if the field is a date field, and its default value was set
                     // to 'now', and it has no current value, set $cur_value to be
                     // the current date
                     if ($default_value == 'now' && ($cur_value == '' || $cur_value == 'now')) {
                         if ($input_type == 'date' || $input_type == 'datetime' || $input_type == 'datetime with timezone' || $input_type == '' && $form_field->template_field->field_type == 'Date') {
                             $cur_time = time();
                             $year = date("Y", $cur_time);
                             $month = date("n", $cur_time);
                             $day = date("j", $cur_time);
                             global $wgAmericanDates, $sfg24HourTime;
                             if ($wgAmericanDates == true) {
                                 $month_names = SFFormUtils::getMonthNames();
                                 $month_name = $month_names[$month - 1];
                                 $cur_value_in_template = "{$month_name} {$day}, {$year}";
                             } else {
                                 $cur_value_in_template = "{$year}/{$month}/{$day}";
                             }
                             if ($input_type == 'datetime' || $input_type == 'datetime with timezone') {
                                 if ($sfg24HourTime) {
                                     $hour = str_pad(intval(substr(date("G", $cur_time), 0, 2)), 2, '0', STR_PAD_LEFT);
                                 } else {
                                     $hour = str_pad(intval(substr(date("g", $cur_time), 0, 2)), 2, '0', STR_PAD_LEFT);
                                 }
                                 $minute = str_pad(intval(substr(date("i", $cur_time), 0, 2)), 2, '0', STR_PAD_LEFT);
                                 $second = str_pad(intval(substr(date("s", $cur_time), 0, 2)), 2, '0', STR_PAD_LEFT);
                                 if ($sfg24HourTime) {
                                     $cur_value_in_template .= " {$hour}:{$minute}:{$second}";
                                 } else {
                                     $ampm = date("A", $cur_time);
                                     $cur_value_in_template .= " {$hour}:{$minute}:{$second} {$ampm}";
                                 }
                             }
                             if ($input_type == 'datetime with timezone') {
                                 $timezone = date("T", $cur_time);
                                 $cur_value_in_template .= " {$timezone}";
                             }
                         }
                     }
                     // if the field is a text field, and its default value was set
                     // to 'current user', and it has no current value, set $cur_value
                     // to be the current user
                     if ($default_value == 'current user' && ($cur_value == '' || $cur_value == 'current user')) {
                         if ($input_type == 'text' || $input_type == '') {
                             $cur_value_in_template = $wgUser->getName();
                             $cur_value = $cur_value_in_template;
                         }
                     }
                     # field + field value
                     $form_field->cur_value = $cur_value;
                     # possible_values hack
                     $__tmpValues = $form_field->template_field->possible_values;
                     $form_field->template_field->possible_values = array();
                     if ($__tmpValues != NULL) {
                         foreach ($__tmpValues as $key => $value) {
                             $form_field->template_field->possible_values["value" . $key] = $value;
                         }
                     }
                     $field["field" . count($field)] = $this->toArrayForSerialize($form_field);
                     $new_text = "dummy";
                     // set only in order to break
                     if ($new_text) {
                         $section = substr_replace($section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc);
                     } else {
                         $start_position = $brackets_end_loc;
                     }
                 }
             } else {
                 // tag is not one of the three allowed values
                 // ignore tag
                 $start_position = $brackets_end_loc;
             }
             // end if
         }
         // end while
     }
     // end for
     // get free text, and add to page data, as well as retroactively
     // inserting it into the form
     // If $form_is_partial is true then either:
     // (a) we're processing a replacement (param 'partial' == 1)
     // (b) we're sending out something to be replaced (param 'partial' is missing)
     if ($form_is_partial) {
         if (!$wgRequest->getCheck('partial')) {
             $free_text = $original_page_content;
             $form_text .= SFFormUtils::hiddenFieldHTML('partial', 1);
         } else {
             $free_text = null;
             $existing_page_content = preg_replace('/²\\{(.*?)\\}²/s', '{{\\1}}', $existing_page_content);
             $existing_page_content = preg_replace('/\\{\\{\\{insertionpoint\\}\\}\\}/', '', $existing_page_content);
             $existing_page_content = Sanitizer::safeEncodeAttribute($existing_page_content);
         }
     } elseif ($source_is_page) {
         // if the page is the source, free_text will just be whatever in the
         // page hasn't already been inserted into the form
         $free_text = trim($existing_page_content);
         // or get it from a form submission
     } elseif ($wgRequest->getCheck('free_text')) {
         $free_text = $wgRequest->getVal('free_text');
         if (!$free_text_was_included) {
             $data_text .= "<free_text>";
         }
         // or get it from the form definition
     } elseif ($free_text_preload_page != null) {
         $free_text = SFFormUtils::getPreloadedText($free_text_preload_page);
     } else {
         $free_text = null;
     }
     # the free text is set here
     // if the FCKeditor extension is installed, use that for the free text input
     global $wgFCKEditorDir;
     if ($wgFCKEditorDir) {
         $showFCKEditor = SFFormUtils::getShowFCKEditor();
         $free_text = htmlspecialchars($free_text);
         if ($showFCKEditor & RTE_VISIBLE) {
             $free_text = SFFormUtils::prepareTextForFCK($free_text);
         }
     } else {
         $showFCKEditor = 0;
         $free_text = Sanitizer::safeEncodeAttribute($free_text);
     }
     // now that we have it, substitute free text into the form and page
     $form_text = str_replace('<free_text>', $free_text, $form_text);
     $data_text = str_replace('<free_text>', $free_text, $data_text);
     # return the fields
     return $__fields;
 }