/** * 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('sf_free_text')) { if (!isset($existing_page_content) || $existing_page_content == '') { $existing_page_content = $wgRequest->getVal('sf_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 === '' || $page_name === null) { $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() && is_null($page_name_formula))) { $this->showDeletionLog($wgOut); } // Unfortunately, we can't just call userCan() here because, // since 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); // The handling of $wgReadOnly and $wgReadOnlyFile // has to be done separately. if (wfReadOnly()) { $permissionErrors = array(array('readonlytext', array(wfReadOnlyReason()))); } $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>", wfMessage('anoneditwarning')->text()); $form_text .= "<p>{$anon_edit_warning}</p>\n"; } } else { $form_is_disabled = true; $wgOut->setPageTitle(wfMessage('badaccess')->text()); $wgOut->addWikiText($wgOut->formatPermissionsErrorMessage($permissionErrors, 'edit')); $wgOut->addHTML("\n<hr />\n"); } // $oldParser = $wgParser; // $wgParser = unserialize( serialize( $oldParser ) ); // deep clone of parser if (!$wgParser->Options()) { $wgParser->Options(ParserOptions::newFromUser($wgUser)); } $wgParser->Title($this->mPageTitle); // This is needed in order to make sure $parser->mLinkHolders // is set. $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('{', '|', '}'), 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 = wfMessage('sf_formedit_addanother')->text(); $minimumInstances = null; $maximumInstances = null; // 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. // (Or don't.) //$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] == 'minimum instances') { $minimumInstances = $sub_components[1]; } elseif ($sub_components[0] == 'maximum instances') { $maximumInstances = $sub_components[1]; } elseif ($sub_components[0] == 'add button text') { $add_button_text = $wgParser->recursiveTagParse($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"'; if (!is_null($minimumInstances)) { $multipleTemplateString .= " minimumInstances=\"{$minimumInstances}\""; } if (!is_null($maximumInstances)) { $multipleTemplateString .= " maximumInstances=\"{$maximumInstances}\""; } $multipleTemplateString .= ">\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. // (Also add additional, blank instances if there's a minimum // number required in this form, and we haven't reached it yet.) if ($found_instance || $instance_num < $minimumInstances) { // Print another instance until we reach the minimum // instances, which is also the starting number. } 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">' . wfMessage('sf_formedit_mismatchedbrackets', $this->mPageTitle->getFullURL(array('action' => 'edit')))->text() . "</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. // @TODO - This is currently called regardless of whether the // input is from the form; the $wgRequest check doesn't do // anything. Is that a problem? if (!$source_is_page && $allow_multiple && $wgRequest) { if ($instance_num < $minimumInstances) { // Print another instance until we reach the minimum // instances, which is also the starting number. } else { $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; $delimiter = null; $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 '>' 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 query') { $pages = SFUtils::getAllPagesForQuery($sub_components[1]); foreach ($pages as $page) { $page_name_for_values = $page->getDbKey(); $possible_values[] = $page_name_for_values; } } 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[] = array($sub_components[1], $fullFieldName); } elseif ($sub_components[0] == 'property') { $semantic_property = $sub_components[1]; } elseif ($sub_components[0] == 'default filename') { $default_filename = str_replace('<page name>', $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]))); } if (!is_null($possible_values) && array_key_exists('mapping template', $field_args)) { $possible_values = SFUtils::getLabels($possible_values, $field_args['mapping template']); } } } // 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; } if (array_key_exists('delimiter', $field_args)) { $delimiter = $field_args['delimiter']; } else { $delimiter = ","; } // 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 && $field_query_val != '') { $mapping_template = null; if (array_key_exists('mapping_template', $template_instance_query_values) && array_key_exists($field_name, $template_instance_query_values['mapping_template'])) { $mapping_template = $template_instance_query_values['mapping_template'][$field_name]; } if (is_array($field_query_val)) { $cur_values = array(); foreach ($field_query_val as $key => $value) { if (!is_null($mapping_template) && !is_null($possible_values)) { $cur_values = array(); foreach ($field_query_val as $key => $val) { if ($key === 'is_list') { $cur_values[$key] = $val; } else { $cur_values[] = SFUtils::labelToValue($val, $possible_values, $mapping_template); } } } else { $cur_values[$key] = $value; } } $cur_value = $this->getStringFromPassedInArray($cur_values, $delimiter); } else { if (!is_null($mapping_template) && !is_null($possible_values)) { $cur_value = SFUtils::labelToValue($field_query_val, $possible_values, $mapping_template); } else { $cur_value = $field_query_val; } } } if (!$form_submitted && $field_query_val != '') { if (is_array($field_query_val)) { $cur_value = $this->getStringFromPassedInArray($field_query_val, $delimiter); } else { $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]); } elseif (isset($cur_value) && !empty($cur_value)) { // Do nothing. } 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 = Html::hidden('sf_free_text', '!free_text!'); } else { $sfgTabIndex++; $sfgFieldNum++; if ($cur_value === '' || is_null($cur_value)) { $default_value = '!free_text!'; } else { $default_value = $cur_value; } $new_text = SFTextAreaInput::getHTML($default_value, 'sf_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 = $wgParser->recursiveTagParse(wfMessage('edittools', array('content'))->text()); $new_text .= <<<END \t\t<div class="mw-editTools"> \t\t{$edittools_text} \t\t</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 = ""; 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 { // If it's not a list, it's probably from a checkbox or date input - // convert the values into a string. $cur_value_in_template = $this->getStringFromPassedInArray($cur_value, $delimiter); } } 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)) { // 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 { if (!empty($cur_value) && array_key_exists('mapping template', $field_args)) { $cur_value = SFUtils::valuesToLabels($cur_value, $field_args['mapping template'], $delimiter, $possible_values); } 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' && ($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' && ($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 .= Html::hidden('sf_free_text', '!free_text!'); } else { $new_text .= Html::hidden($input_name, $cur_value); } } if (array_key_exists('mapping template', $field_args)) { $new_text .= Html::hidden($template_name . '[mapping_template][' . $field_name . ']', $field_args['mapping template']); } 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 $is_checked = false; 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 ($sub_components[0] == 'checked') { $is_checked = true; } } elseif (count($sub_components) == 2) { switch ($sub_components[0]) { case 'label': $input_label = $wgParser->recursiveTagParse($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') { $value = $wgRequest->getVal('wpSummary'); $new_text = SFFormUtils::summaryInputHTML($form_is_disabled, $input_label, $attr, $value); } elseif ($input_name == 'minor edit') { $is_checked = $wgRequest->getCheck('wpMinoredit'); $new_text = SFFormUtils::minorEditInputHTML($form_submitted, $form_is_disabled, $is_checked, $input_label, $attr); } elseif ($input_name == 'watch') { $is_checked = $wgRequest->getCheck('wpWatchthis'); $new_text = SFFormUtils::watchInputHTML($form_submitted, $form_is_disabled, $is_checked, $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); // ===================================================== // for section processing // ===================================================== } elseif ($tag_title == 'section') { $section_name = trim($tag_components[1]); $is_mandatory = false; $is_hidden = false; $is_restricted = false; $header_level = 2; $other_args = array(); // cycle through the other components 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 === 'autogrow') { $other_args['autogrow'] = true; } $sub_components = array_map('trim', explode('=', $component, 2)); if (count($sub_components) === 2) { switch ($sub_components[0]) { case 'level': $header_level = $sub_components[1]; break; case 'rows': case 'cols': case 'class': case 'editor': $other_args[$sub_components[0]] = $sub_components[1]; break; default: // Ignore unknown } } } // Generate the wikitext for the section header $header_string = str_repeat("=", $header_level); $header_text = $header_string . $section_name . $header_string . "\n"; $data_text .= $header_text; // split the existing page contents into the textareas in the form $default_value = ""; $section_start_loc = 0; if ($source_is_page && $existing_page_content !== null) { $section_start_loc = strpos($existing_page_content, $header_text); $existing_page_content = str_replace($header_text, '', $existing_page_content); $section_end_loc = -1; // get the position of the next template or section defined in the form $next_section_start_loc = strpos($section, '{{{', $brackets_end_loc); if ($next_section_start_loc == false) { $section_end_loc = strpos($existing_page_content, '{{', $section_start_loc); } else { $next_section_end_loc = strpos($section, '}}}', $next_section_start_loc); $bracketed_string_next_section = substr($section, $next_section_start_loc + 3, $next_section_end_loc - ($next_section_start_loc + 3)); $tag_components_next_section = SFUtils::getFormTagComponents($bracketed_string_next_section); $tag_title_next_section = trim($tag_components_next_section[0]); if ($tag_title_next_section == 'section') { if (preg_match('/(^={1,6}' . $tag_components_next_section[1] . '?={1,6}\\s*?$)/m', $existing_page_content, $matches, PREG_OFFSET_CAPTURE)) { $section_end_loc = $matches[0][1]; } } } if ($section_end_loc === -1) { $default_value = $existing_page_content; $existing_page_content = ''; } else { $default_value = substr($existing_page_content, $section_start_loc, $section_end_loc - $section_start_loc); $existing_page_content = substr($existing_page_content, $section_end_loc); } } //if input is from the form $section_text = ""; if (!$source_is_page && $wgRequest) { $section_text = $wgRequest->getArray('_section'); $default_value = $section_text[trim($section_name)]; if ($default_value == "" || $default_value == null) { $data_text .= $default_value . "\n\n"; } else { $data_text .= rtrim($default_value) . "\n\n"; } } //set input name for query string $input_name = '_section' . '[' . trim($section_name) . ']'; if ($is_mandatory) { $other_args['mandatory'] = true; } if ($is_hidden) { $form_section_text = Html::hidden($input_name, $default_value); } else { $form_section_text = SFTextAreaInput::getHTML($default_value, $input_name, false, $form_is_disabled || $is_restricted, $other_args); } $section = substr_replace($section, $form_section_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) { // Escape $template_text, because values like $1 cause problems // for preg_replace(). $escaped_template_text = str_replace('$', '\\$', $template_text); $data_text = preg_replace('/' . $reptmp . '/', $escaped_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); } $escapedMultipleTemplateString = str_replace('$', '\\$', $multipleTemplateString); $form_text = preg_replace('/' . self::makePlaceholderInFormHTML($curPlaceholder) . '/', $escapedMultipleTemplateString, $form_text); } if (!$all_instances_printed) { // This will cause the section to be // re-parsed on the next go. $section_num--; $instance_num++; } } else { $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 .= Html::hidden('sf_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; } 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); } $form_text .= Html::hidden('partial', 1); } 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('sf_free_text')) { $free_text = $wgRequest->getVal('sf_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)); // 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 && is_null($page_name_formula) && $this->mPageTitle->exists() && $existing_page_content !== '' && !$source_page_matches_this_form) { $form_text = "\t" . '<div class="warningbox">' . wfMessage('sf_formedit_formwarning', $this->mPageTitle->getFullURL())->text() . "</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_submitted, $form_is_disabled); } } if (!$is_query) { $form_text .= Html::hidden('wpStarttime', wfTimestampNow()); $article = new Article($this->mPageTitle, 0); $form_text .= Html::hidden('wpEdittime', $article->getTimestamp()); $form_text .= Html::hidden('wpEditToken', $wgUser->getEditToken()); } $form_text .= "\t</form>\n"; $wgParser->replaceLinkHolders($form_text); // Add general Javascript code. $javascript_text = ""; wfRunHooks('sfAddJavascriptToForm', array(&$javascript_text)); // 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(); // addParserOutputMetadata was introduced in 1.24 when addParserOutputNoText was deprecated if (method_exists($wgOut, 'addParserOutputMetadata')) { $wgOut->addParserOutputMetadata($parserOutput); } else { $wgOut->addParserOutputNoText($parserOutput); } // $wgParser = $oldParser; wfProfileOut(__METHOD__); return array($form_text, $javascript_text, $data_text, $form_page_title, $generated_page_name); }
/** * 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 // initialize some variables $wiki_page = new SFWikiPage(); $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 = ""; // 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('sf_free_text')) { if (!isset($existing_page_content) || $existing_page_content == '') { $existing_page_content = $wgRequest->getVal('sf_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) { // We're in Special:RunQuery - just use that as the // title. global $wgTitle; $this->mPageTitle = $wgTitle; } elseif ($page_name === '' || $page_name === null) { $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() && is_null($page_name_formula))) { $this->showDeletionLog($wgOut); } // Unfortunately, we can't just call userCan() here because, // since 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); // The handling of $wgReadOnly and $wgReadOnlyFile // has to be done separately. if (wfReadOnly()) { $permissionErrors = array(array('readonlytext', array(wfReadOnlyReason()))); } $userCanEditPage = count($permissionErrors) == 0; Hooks::run('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. if ($wgUser->isAnon() && !$is_query) { // Based on code in MediaWiki's EditPage.php. $anonEditWarning = wfMessage('anoneditwarning', '{{fullurl:Special:UserLogin|returnto={{FULLPAGENAMEE}}}}', '{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}')->parse(); $form_text .= Html::rawElement('div', array('id' => 'mw-anon-edit-warning', 'class' => 'warningbox'), $anonEditWarning); } } else { $form_is_disabled = true; $wgOut->setPageTitle(wfMessage('badaccess')->text()); $wgOut->addWikiText($wgOut->formatPermissionsErrorMessage($permissionErrors, 'edit')); $wgOut->addHTML("\n<hr />\n"); } // $oldParser = $wgParser; // $wgParser = unserialize( serialize( $oldParser ) ); // deep clone of parser if (!$wgParser->Options()) { $wgParser->Options(ParserOptions::newFromUser($wgUser)); } $wgParser->Title($this->mPageTitle); // This is needed in order to make sure $parser->mLinkHolders // is set. $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; // 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('{', '|', '}'), 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. $tif = null; // 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; // 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 = SFUtils::getFormTagComponents($bracketed_string); $tag_title = trim($tag_components[0]); // ===================================================== // for template processing // ===================================================== if ($tag_title == 'for template') { if ($tif) { $previous_template_name = $tif->getTemplateName(); } else { $previous_template_name = ''; } $template_name = str_replace('_', ' ', $tag_components[1]); $is_new_template = $template_name != $previous_template_name; if ($is_new_template) { $tif = SFTemplateInForm::newFromFormTag($tag_components); } // Remove template tag. $section = substr_replace($section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); // 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. if ($source_is_page) { $tif->setPageRelatedInfo($existing_page_content); // Get the first instance of // this template on the page // being edited, even if there // are more. if ($tif->pageCallsThisTemplate()) { $tif->setFieldValuesFromPage($existing_page_content); $existing_template_text = $tif->getFullTextInPage(); // 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("\n" . '{{{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 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. $source_page_matches_this_form = true; } } if ($form_submitted) { $tif->setFieldValuesFromSubmit(); } $tif->checkIfAllInstancesPrinted($form_submitted, $source_is_page); if (!$tif->allInstancesPrinted()) { $wiki_page->addTemplate($tif); } // ===================================================== // end template processing // ===================================================== } elseif ($tag_title == 'end template') { if ($source_is_page) { // Add any unhandled template fields // in the page as hidden variables. $form_text .= SFFormUtils::unhandledFieldsHTML($tif); } // Remove this tag from the $section variable. $section = substr_replace($section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); $tif = null; // ===================================================== // field processing // ===================================================== } elseif ($tag_title == 'field') { // If the template is null, that (hopefully) // means we're handling the free text field. // Make the template a dummy variable. if ($tif == null) { $tif = new SFTemplateInForm(); } // We get the field name both here // and in the SFFormField constructor, // because SFFormField isn't equipped // to deal with the <freetext> hack, // among others. $field_name = trim($tag_components[1]); $form_field = SFFormField::newFromFormFieldTag($tag_components, $tif, $form_is_disabled); $cur_value = $form_field->getCurrentValue($tif->getValuesFromSubmit(), $form_submitted, $source_is_page); if ($form_field->holdsTemplate()) { $placeholderFields[] = self::placeholderFormat($tif->getTemplateName(), $field_name); } // 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 && ($tif->getFullTextInPage() != '' && !$form_submitted)) { if ($tif->hasValueFromPageForField($field_name)) { // Get value, and remove it, // so that at the end we // can have a list of all // the fields that weren't // handled by the form. $cur_value = $tif->getAndRemoveValueFromPageForField($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 ($form_field->holdsTemplate()) { $existing_page_content .= $cur_value; } } elseif (isset($cur_value) && !empty($cur_value)) { // Do nothing. } 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 ($form_field->isHidden()) { $new_text = Html::hidden('sf_free_text', '!free_text!'); } else { $sfgTabIndex++; $sfgFieldNum++; if ($cur_value === '' || is_null($cur_value)) { $default_value = '!free_text!'; } else { $default_value = $cur_value; } $new_text = SFTextAreaInput::getHTML($default_value, 'sf_free_text', false, $form_is_disabled || $form_field->isRestricted(), $form_field->getFieldArgs()); if ($form_field->hasFieldArg('edittools')) { // borrowed from EditPage::showEditTools() $edittools_text = $wgParser->recursiveTagParse(wfMessage('edittools', array('content'))->text()); $new_text .= <<<END \t\t<div class="mw-editTools"> \t\t{$edittools_text} \t\t</div> END; } } $free_text_was_included = true; $wiki_page->addFreeTextSection(); } if ($tif->getTemplateName() === '' || $field_name == '<freetext>') { $section = substr_replace($section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); } else { if (is_array($cur_value)) { // @TODO - is this code ever called? $delimiter = $form_field->getFieldArg('is_list'); // first, check if it's a list if (array_key_exists('is_list', $cur_value) && $cur_value['is_list'] == true) { $cur_value_in_template = ""; 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 { // If it's not a list, it's probably from a checkbox or date input - // convert the values into a string. $cur_value_in_template = self::getStringFromPassedInArray($cur_value, $delimiter); } } else { // value is not an array $cur_value_in_template = $cur_value; } // 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(' ', '_', $form_field->getInputName()); $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); } // Call hooks - unfortunately this has to be split into two // separate calls, because of the different variable names in // each case. if ($form_submitted) { Hooks::run('sfCreateFormField', array(&$form_field, &$cur_value_in_template, true)); } else { if (!empty($cur_value) && ($form_field->hasFieldArg('mapping template') || $form_field->hasFieldArg('mapping property') || $form_field->hasFieldArg('mapping cargo table') && $form_field->hasFieldArg('mapping cargo field'))) { $cur_value = SFUtils::valuesToLabels($cur_value, $delimiter, $form_field->getPossibleValues()); } Hooks::run('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 (!$form_field->hasFieldArg('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 ($form_field->getDefaultValue() == 'now' && ($cur_value == '' || $cur_value == 'now')) { $input_type = $form_field->getInputType(); if ($input_type == 'date' || $input_type == 'datetime' || $input_type == 'year' || $input_type == '' && $form_field->getTemplateField()->getPropertyType() == '_dat') { $cur_value_in_template = self::getStringForCurrentTime($input_type == 'datetime', $form_field->hasFieldArg('include 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 ($form_field->getDefaultValue() == 'current user' && ($cur_value === '' || $cur_value == 'current user')) { $cur_value_in_template = $wgUser->getName(); $cur_value = $cur_value_in_template; } // If all instances have been // printed, that means we're // now printing a "starter" // div - set the current value // to null. // (Ideally it wouldn't get // set at all, but that seems a // little harder.) if ($tif->allInstancesPrinted()) { $cur_value = null; } $new_text = $this->formFieldHTML($form_field, $cur_value); $new_text .= $form_field->additionalHTMLForInput($cur_value, $field_name, $tif->getTemplateName()); if ($new_text) { $wiki_page->addTemplateParam($template_name, $tif->getInstanceNum(), $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 $is_checked = false; 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] == 'checked') { $is_checked = true; } } elseif (count($sub_components) == 2) { switch ($sub_components[0]) { case 'label': $input_label = $wgParser->recursiveTagParse($sub_components[1]); break; case 'class': case 'style': $attr[$sub_components[0]] = $sub_components[1]; break; } } } if ($input_name == 'summary') { $value = $wgRequest->getVal('wpSummary'); $new_text = SFFormUtils::summaryInputHTML($form_is_disabled, $input_label, $attr, $value); } elseif ($input_name == 'minor edit') { $is_checked = $wgRequest->getCheck('wpMinoredit'); $new_text = SFFormUtils::minorEditInputHTML($form_submitted, $form_is_disabled, $is_checked, $input_label, $attr); } elseif ($input_name == 'watch') { $is_checked = $wgRequest->getCheck('wpWatchthis'); $new_text = SFFormUtils::watchInputHTML($form_submitted, $form_is_disabled, $is_checked, $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); // ===================================================== // for section processing // ===================================================== } elseif ($tag_title == 'section') { $section_name = trim($tag_components[1]); $page_section_in_form = SFPageSection::newFromFormTag($tag_components); // Split the existing page contents into the textareas in the form. $default_value = ""; $section_start_loc = 0; if ($source_is_page && $existing_page_content !== null) { // For the last section of the page, there is no trailing newline in // $existing_page_content, but the code below expects it. This code // ensures that there is always trailing newline. T72202 if (substr($existing_page_content, -1) !== "\n") { $existing_page_content .= "\n"; } $equalsSigns = str_pad('', $page_section_in_form->getSectionLevel(), '='); $searchStr = '/^' . preg_quote($equalsSigns, '/') . '[ ]*?' . preg_quote($section_name, '/') . '[ ]*?' . preg_quote($equalsSigns, '/') . '$/m'; if (preg_match($searchStr, $existing_page_content, $matches, PREG_OFFSET_CAPTURE)) { $section_start_loc = $matches[0][1]; $header_text = $matches[0][0]; $existing_page_content = str_replace($header_text, '', $existing_page_content); } else { $section_start_loc = 0; } $section_end_loc = -1; // get the position of the next template or section defined in the form $next_section_start_loc = strpos($section, '{{{', $brackets_end_loc); if ($next_section_start_loc == false) { $section_end_loc = strpos($existing_page_content, '{{', $section_start_loc); } else { $next_section_end_loc = strpos($section, '}}}', $next_section_start_loc); $bracketed_string_next_section = substr($section, $next_section_start_loc + 3, $next_section_end_loc - ($next_section_start_loc + 3)); $tag_components_next_section = SFUtils::getFormTagComponents($bracketed_string_next_section); $tag_title_next_section = trim($tag_components_next_section[0]); if ($tag_title_next_section == 'section') { if (preg_match('/(^={1,6}[ ]*?' . $tag_components_next_section[1] . '[ ]*?={1,6}\\s*?$)/m', $existing_page_content, $matches, PREG_OFFSET_CAPTURE)) { $section_end_loc = $matches[0][1]; } } } if ($section_end_loc === -1) { $section_text = $existing_page_content; $existing_page_content = ''; } else { $section_text = substr($existing_page_content, $section_start_loc, $section_end_loc - $section_start_loc); $existing_page_content = substr($existing_page_content, $section_end_loc); } } // If input is from the form. if (!$source_is_page && $wgRequest) { $text_per_section = $wgRequest->getArray('_section'); $section_text = $text_per_section[trim($section_name)]; $wiki_page->addSection($section_name, $page_section_in_form->getSectionLevel(), $section_text); } $section_text = trim($section_text); // Set input name for query string. $input_name = '_section' . '[' . trim($section_name) . ']'; $other_args = $page_section_in_form->getSectionArgs(); $other_args['isSection'] = true; if ($page_section_in_form->isMandatory()) { $other_args['mandatory'] = true; } if ($page_section_in_form->isHidden()) { $form_section_text = Html::hidden($input_name, $section_text); } else { $form_section_text = SFTextAreaInput::getHTML($section_text, $input_name, false, $form_is_disabled || $page_section_in_form->isRestricted(), $other_args); } $section = substr_replace($section, $form_section_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') { $wiki_page->makeFreeTextOnlyInclude(); } 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 ($tif && (!$tif->allowsMultiple() || $tif->allInstancesPrinted())) { $template_text = $wiki_page->createTemplateCallsForTemplateName($tif->getTemplateName()); // 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)) . "{{{insertionpoint}}}" . $existing_page_content; } } if ($tif && $tif->allowsMultiple()) { if ($tif->getInstanceNum() == 0) { $multipleTemplateHTML = $this->multipleTemplateStartHTML($tif); } else { $multipleTemplateHTML = ''; } if (!$tif->allInstancesPrinted()) { $multipleTemplateHTML .= $this->multipleTemplateInstanceHTML($tif, $form_is_disabled, $section); } else { $multipleTemplateHTML .= $this->multipleTemplateEndHTML($tif, $form_is_disabled, $section); } $placeholder = $tif->getPlaceholder(); if ($placeholder != null) { $multipleTemplateHTML .= self::makePlaceholderInFormHTML($placeholder); } if ($tif->allInstancesPrinted() && $tif->getLabel() != null) { $multipleTemplateHTML .= "</fieldset>\n"; } if ($placeholder == null) { // The normal process. $form_text .= $multipleTemplateHTML; } else { // The template text won't be appended // at the end of the template like for // usual multiple template forms. // The HTML text will instead be stored in // the $multipleTemplateHTML 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. // We replace the HTML into the current // placeholder tag, but also add another // placeholder tag, to keep track of it. $form_text = str_replace(self::makePlaceholderInFormHTML($placeholder), $multipleTemplateHTML, $form_text); } if (!$tif->allInstancesPrinted()) { // This will cause the section to be // re-parsed on the next go. $section_num--; $tif->incrementInstanceNum(); } } else { $form_text .= $section; } } // 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 @<insertHTML>@ tags from the generated // HTML form. $form_text = str_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 .= Html::hidden('sf_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; } else { $free_text = null; $existing_page_content = preg_replace(array('/�\\{/m', '/\\}�/m'), array('{{', '}}'), $existing_page_content); $existing_page_content = str_replace('{{{insertionpoint}}}', '', $existing_page_content); } $form_text .= Html::hidden('partial', 1); } 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('sf_free_text')) { $free_text = $wgRequest->getVal('sf_free_text'); if (!$free_text_was_included) { $wiki_page->addFreeTextSection(); } } else { $free_text = null; } if ($wiki_page->freeTextOnlyInclude()) { $free_text = str_replace("<onlyinclude>", '', $free_text); $free_text = str_replace("</onlyinclude>", '', $free_text); $free_text = trim($free_text); } $page_text = ''; // The first hook here is deprecated. Use the second. // Note: Hooks::run can take a third argument which indicates // a deprecated hook, but it expects a MediaWiki version, not // an extension version. Hooks::run('sfModifyFreeTextField', array(&$free_text, $existing_page_content)); Hooks::run('sfBeforeFreeTextSubstitution', array(&$free_text, $existing_page_content, &$page_text)); // Now that we have it, add free text to the page, and // substitute it into the form. if ($form_submitted) { $wiki_page->setFreeText($free_text); $page_text = $wiki_page->createPageText(); } $escaped_free_text = Sanitizer::safeEncodeAttribute($free_text); $form_text = str_replace('!free_text!', $escaped_free_text, $form_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 && is_null($page_name_formula) && $this->mPageTitle->exists() && $existing_page_content !== '' && !$source_page_matches_this_form) { $form_text = "\t" . '<div class="warningbox">' . wfMessage('sf_formedit_formwarning', $this->mPageTitle->getFullURL())->text() . "</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_submitted, $form_is_disabled); } } if (!$is_query) { $form_text .= Html::hidden('wpStarttime', wfTimestampNow()); $article = new Article($this->mPageTitle, 0); $form_text .= Html::hidden('wpEdittime', $article->getTimestamp()); $form_text .= Html::hidden('wpEditToken', $wgUser->getEditToken()); } $form_text .= "\t</form>\n"; $wgParser->replaceLinkHolders($form_text); Hooks::run('sfRenderingEnd', array(&$form_text)); // Add general Javascript code. $javascript_text = ""; Hooks::run('sfAddJavascriptToForm', array(&$javascript_text)); // Send the autocomplete values to the browser, along with the // mappings of which values should apply to which fields. // If doing a replace, the page text is actually the modified // original page. if ($wgRequest->getCheck('partial')) { $page_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 = ''; } // $wgParser = $oldParser; return array($form_text, $javascript_text, $page_text, $form_page_title, $generated_page_name); }