Index: trunk/extensions/SemanticForms/includes/SF_FormPrinter.inc |
— | — | @@ -6,6 +6,7 @@ |
7 | 7 | * @author Nils Oppermann |
8 | 8 | * @author Jeffrey Stuckman |
9 | 9 | * @author Matt Williamson |
| 10 | + * @author Harold Solbrig |
10 | 11 | */ |
11 | 12 | |
12 | 13 | class SFFormPrinter { |
— | — | @@ -106,6 +107,32 @@ |
107 | 108 | $sfgTabIndex = 1; |
108 | 109 | $sfgFieldNum = 1; |
109 | 110 | $source_page_matches_this_form = false; |
| 111 | + $form_page_title = NULL; |
| 112 | + // $form_is_partial is true if: |
| 113 | + // (a) 'partial' == 1 in the arguments |
| 114 | + // (b) 'partial form' is found in the form definition |
| 115 | + // in the latter case, it may remain false until close to the end of |
| 116 | + // the parsing, so we have to assume that it will become a possibility |
| 117 | + $form_is_partial = false; |
| 118 | + |
| 119 | + // if we have existing content and we're not in an active replacement |
| 120 | + // situation, preserve the original content. We do this because we want |
| 121 | + // to pass the original content on IF this is a partial form |
| 122 | + // TODO: A better approach here would be to pass the revision id of the |
| 123 | + // existing page content through the replace value, which would |
| 124 | + // minimize the html traffic and would allow us to do a concurrent |
| 125 | + // update check. For now, we pass it through the hidden text field... |
| 126 | + |
| 127 | + if (! $wgRequest->getCheck('partial')) { |
| 128 | + $original_page_content = $existing_page_content; |
| 129 | + } else { |
| 130 | + $original_page_content = null; |
| 131 | + if($wgRequest->getCheck('free_text')) { |
| 132 | + $existing_page_content= $wgRequest->getVal('free_text'); |
| 133 | + $form_is_partial = true; |
| 134 | + } |
| 135 | + } |
| 136 | + |
110 | 137 | // disable all form elements if user doesn't have edit permission - |
111 | 138 | // two different checks are needed, because editing permissions can be |
112 | 139 | // set in different ways |
— | — | @@ -114,8 +141,7 @@ |
115 | 142 | $form_is_disabled = false; |
116 | 143 | $form_text = ""; |
117 | 144 | // show "Your IP address will be recorded" warning if user is |
118 | | - // anonymous - wikitext for bolding has to be replaced with HTML, until |
119 | | - // FormPrinter can parse wikitext |
| 145 | + // anonymous - wikitext for bolding has to be replaced with HTML |
120 | 146 | if ($wgUser->isAnon()) { |
121 | 147 | $anon_edit_warning = preg_replace("/'''(.*)'''/", "<strong>$1</strong>", wfMsg('anoneditwarning')); |
122 | 148 | $form_text .= "<p>$anon_edit_warning</p>\n"; |
— | — | @@ -141,6 +167,19 @@ |
142 | 168 | $form_def = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $form_def); |
143 | 169 | $form_def = strtr($form_def, array('<includeonly>' => '', '</includeonly>' => '')); |
144 | 170 | |
| 171 | + // parse wiki-text |
| 172 | + // add '<nowiki>' tags around every triple-bracketed form definition |
| 173 | + // element, so that the wiki parser won't touch it - the parser will |
| 174 | + // remove the '<nowiki>' tags, leaving us with what we need |
| 175 | + global $sfgDisableWikiTextParsing; |
| 176 | + if (! $sfgDisableWikiTextParsing) { |
| 177 | + $form_def = strtr($form_def, array('{{{' => '<nowiki>{{{', '}}}' => '}}}</nowiki>')); |
| 178 | + $parser = new Parser(); |
| 179 | + $parser->mOptions = new ParserOptions(); |
| 180 | + $parser->mOptions->initialiseFromUser($wgUser); |
| 181 | + $form_def = $parser->parse($form_def, $this->mPageTitle, $parser->mOptions)->getText(); |
| 182 | + } |
| 183 | + |
145 | 184 | // turn form definition file into an array of sections, one for each |
146 | 185 | // template definition (plus the first section) |
147 | 186 | $form_def_sections = array(); |
— | — | @@ -183,12 +222,14 @@ |
184 | 223 | // modified; is it necessary? |
185 | 224 | $section = " " . $form_def_sections[$section_num]; |
186 | 225 | |
187 | | -$while_instance = 0; |
188 | 226 | while ($brackets_loc = strpos($section, '{{{', $start_position)) { |
189 | 227 | $brackets_end_loc = strpos($section, "}}}", $brackets_loc); |
190 | 228 | $bracketed_string = substr($section, $brackets_loc + 3, $brackets_end_loc - ($brackets_loc + 3)); |
191 | 229 | $tag_components = explode('|', $bracketed_string); |
192 | 230 | $tag_title = trim($tag_components[0]); |
| 231 | + // ===================================================== |
| 232 | + // for template processing |
| 233 | + // ===================================================== |
193 | 234 | if ($tag_title == 'for template') { |
194 | 235 | $old_template_name = $template_name; |
195 | 236 | $template_name = trim($tag_components[1]); |
— | — | @@ -242,7 +283,7 @@ |
243 | 284 | // underlines in name |
244 | 285 | $search_template_str = str_replace('/', '\/', $tif->template_name); |
245 | 286 | $search_template_str = preg_replace('/[_| ]/', '[_| ]', $search_template_str); |
246 | | - if ($source_is_page) { |
| 287 | + if ($source_is_page || $form_is_partial) { |
247 | 288 | $matches = array(); |
248 | 289 | if ($allow_multiple) { |
249 | 290 | // find the number of instances of this template in the page - |
— | — | @@ -303,7 +344,17 @@ |
304 | 345 | } |
305 | 346 | } |
306 | 347 | // now remove this template from the text being edited |
307 | | - $existing_page_content = str_replace($matches[0], '', $existing_page_content); |
| 348 | + // if this is a partial form, establish a new insertion point |
| 349 | + if($existing_page_content && $form_is_partial && $wgRequest->getCheck('partial')) { |
| 350 | + // if something already exists, set the new insertion point |
| 351 | + // to its position; otherwise just let it lie |
| 352 | + if (strpos($existing_page_content, $matches[0])) { |
| 353 | + $existing_page_content = str_replace('{{{insertionpoint}}}', '', $existing_page_content); |
| 354 | + $existing_page_content = str_replace($matches[0], '{{{insertionpoint}}}', $existing_page_content); |
| 355 | + } |
| 356 | + } else { |
| 357 | + $existing_page_content = str_replace($matches[0], '', $existing_page_content); |
| 358 | + } |
308 | 359 | // if this is not a multiple-instance template, and we've found |
309 | 360 | // a match in the source page, there's a good chance that this |
310 | 361 | // page was created with this form - note that, so we don't |
— | — | @@ -339,6 +390,9 @@ |
340 | 391 | } |
341 | 392 | } |
342 | 393 | } |
| 394 | + // ===================================================== |
| 395 | + // end template processing |
| 396 | + // ===================================================== |
343 | 397 | } elseif ($tag_title == 'end template') { |
344 | 398 | // remove this tag, reset some variables, and close off form HTML tag |
345 | 399 | $section = substr_replace($section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); |
— | — | @@ -354,6 +408,9 @@ |
355 | 409 | // be hidden because it is empty and choosers are being used. So, |
356 | 410 | // hide it. |
357 | 411 | $form_text = str_replace("[[placeholder]]", "style='display:none'", $form_text); |
| 412 | + // ===================================================== |
| 413 | + // field processing |
| 414 | + // ===================================================== |
358 | 415 | } elseif ($tag_title == 'field') { |
359 | 416 | $field_name = trim($tag_components[1]); |
360 | 417 | // cycle through the other components |
— | — | @@ -401,6 +458,14 @@ |
402 | 459 | if ($cur_value && ! is_array($cur_value)) { |
403 | 460 | $cur_value = Sanitizer::safeEncodeAttribute($cur_value); |
404 | 461 | } |
| 462 | + if ($cur_value && is_array($cur_value)) { |
| 463 | + // HACK - this is sometimes necessary |
| 464 | + // TODO: the code around line 433 might correct for this. This |
| 465 | + // should be checked. |
| 466 | + if (!$cur_value[0] && $cur_value[1]) |
| 467 | + $cur_value = null; |
| 468 | + } |
| 469 | + |
405 | 470 | if ($cur_value == null) { |
406 | 471 | // set to default value specified in the form, if it's there |
407 | 472 | $cur_value = $default_value; |
— | — | @@ -589,7 +654,7 @@ |
590 | 655 | $sfgJSValidationCalls[] = "validate_mandatory_field ('$input_id" . "_1', '$info_id')"; |
591 | 656 | $sfgJSValidationCalls[] = "validate_mandatory_field ('$input_id" . "_2', '$info_id')"; |
592 | 657 | $sfgJSValidationCalls[] = "validate_mandatory_field ('$input_id" . "_3', '$info_id')"; |
593 | | - if ($input_type == 'datetime' || $input_type == 'datetime with timezone') { |
| 658 | + if ($input_type == 'datetime' || $input_type == 'datetime with timezone') { |
594 | 659 | // TODO - validate the time fields |
595 | 660 | if ($input_type == 'datetime with timezone') { |
596 | 661 | // TODO - validate the timezone |
— | — | @@ -607,6 +672,9 @@ |
608 | 673 | $start_position = $brackets_end_loc; |
609 | 674 | } |
610 | 675 | } |
| 676 | + // ===================================================== |
| 677 | + // standard input processing |
| 678 | + // ===================================================== |
611 | 679 | } elseif ($tag_title == 'standard input') { |
612 | 680 | // set a flag so that the standard 'form bottom' won't get displayed |
613 | 681 | $this->standardInputsIncluded = true; |
— | — | @@ -639,21 +707,67 @@ |
640 | 708 | $new_text = $this->cancelLinkHTML($form_is_disabled, $input_label); |
641 | 709 | } |
642 | 710 | $section = substr_replace($section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); |
| 711 | + // ===================================================== |
| 712 | + // page info processing |
| 713 | + // ===================================================== |
| 714 | + } elseif ($tag_title == 'info') { |
| 715 | + // TODO: Generate an error message if this is included more than once |
| 716 | + foreach(array_slice($tag_components, 1) as $component) { |
| 717 | + list($tag, $val) = explode('=', $component, 2); |
| 718 | + if ($tag == 'add title') { |
| 719 | + // handle this only if we're adding a page |
| 720 | + if (! $this->mPageTitle->exists()) { |
| 721 | + $form_page_title = $val; |
| 722 | + } |
| 723 | + } |
| 724 | + elseif($tag == 'edit title') { |
| 725 | + // handle this only if we're editing a page |
| 726 | + if ($this->mPageTitle->exists()) { |
| 727 | + $form_page_title = $val; |
| 728 | + } |
| 729 | + } |
| 730 | + elseif($tag == 'partial form') { |
| 731 | + $form_is_partial = true; |
| 732 | + // replacement pages may have minimal matches... |
| 733 | + $source_page_matches_this_form = true; |
| 734 | + } |
| 735 | + } |
| 736 | + $section = substr_replace($section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); |
| 737 | + // ===================================================== |
| 738 | + // default outer level processing |
| 739 | + // ===================================================== |
643 | 740 | } else { // tag is not one of the three allowed values |
644 | 741 | // ignore tag |
645 | 742 | $start_position = $brackets_end_loc; |
646 | 743 | } // end if |
647 | 744 | } // end while |
648 | 745 | |
649 | | - if (! $all_instances_printed && ($template_text != '')) { |
650 | | - // add another newline before the final bracket, if this template |
651 | | - // call is already more than one line |
652 | | - if (strpos($template_text, "\n")) { |
653 | | - $template_text .= "\n"; |
| 746 | + if (! $all_instances_printed ) { |
| 747 | + if ( $template_text != '' ) { |
| 748 | + // add another newline before the final bracket, if this template |
| 749 | + // call is already more than one line |
| 750 | + if (strpos($template_text, "\n")) |
| 751 | + $template_text .= "\n"; |
| 752 | + $template_text .= "}}"; |
| 753 | + $data_text .= $template_text . "\n"; |
| 754 | + // if there is a placeholder in the text, we know that we are |
| 755 | + // doing a replace |
| 756 | + if ($existing_page_content && strpos($existing_page_content, '{{{insertionpoint}}}', 0)) { |
| 757 | + $existing_page_content = preg_replace('/\{\{\{insertionpoint\}\}\}(\r?\n?)/', |
| 758 | + preg_replace('/\}\}/m', '}�', |
| 759 | + preg_replace('/\{\{/m', '�{', $template_text)) . |
| 760 | + "\n{{{insertionpoint}}}", |
| 761 | + $existing_page_content); |
| 762 | + // otherwise, if it's a partial form, we have to add the new |
| 763 | + // text somewhere |
| 764 | + } elseif ($form_is_partial && $wgRequest->getCheck('partial') ) { |
| 765 | + $existing_page_content = preg_replace('/\}\}/m', '}�', |
| 766 | + preg_replace('/\{\{/m', '�{', $template_text)) . |
| 767 | + "\n{{{insertionpoint}}}\n" . $existing_page_content; |
| 768 | + } |
654 | 769 | } |
655 | | - $template_text .= "}}\n"; |
656 | | - $data_text .= $template_text; |
657 | 770 | } |
| 771 | + |
658 | 772 | if ($allow_multiple) { |
659 | 773 | if (! $all_instances_printed) { |
660 | 774 | $section = str_replace('[num]', "[{$instance_num}a]", $section); |
— | — | @@ -704,7 +818,21 @@ |
705 | 819 | } |
706 | 820 | // get free text, and add to page data, as well as retroactively |
707 | 821 | // inserting it into the form |
708 | | - if ($source_is_page) { |
| 822 | + |
| 823 | + // If $form_is_partial is true then either: |
| 824 | + // (a) we're processing a replacement (param 'partial' == 1) |
| 825 | + // (b) we're sending out something to be replaced (param 'partial' is missing) |
| 826 | + if ($form_is_partial) { |
| 827 | + if(!$wgRequest->getCheck('partial')) { |
| 828 | + $free_text = $original_page_content; |
| 829 | + $form_text .= $this->hiddenFieldHTML('partial', 1); |
| 830 | + } else { |
| 831 | + $free_text = null; |
| 832 | + $existing_page_content = preg_replace('/�\{(.*?)\}�/s', '{{\1}}', $existing_page_content); |
| 833 | + $existing_page_content = preg_replace('/\{\{\{insertionpoint\}\}\}/', '', $existing_page_content); |
| 834 | + $existing_page_content = Sanitizer::safeEncodeAttribute($existing_page_content); |
| 835 | + } |
| 836 | + } elseif ($source_is_page) { |
709 | 837 | // if the page is the source, free_text will just be whatever in the |
710 | 838 | // page hasn't already been inserted into the form |
711 | 839 | $free_text = trim($existing_page_content); |
— | — | @@ -1062,9 +1190,13 @@ |
1063 | 1191 | |
1064 | 1192 | END; |
1065 | 1193 | |
1066 | | - // send the autocomplete values to the browser, along with the mappings of which values should apply to which fields |
| 1194 | + // send the autocomplete values to the browser, along with the mappings |
| 1195 | + // of which values should apply to which fields |
| 1196 | + // if doing a replace, the data text is actually the modified original page |
| 1197 | + if($wgRequest->getCheck('partial')) |
| 1198 | + $data_text = $existing_page_content; |
1067 | 1199 | $javascript_text .= $fields_javascript_text; |
1068 | | - return array($form_text, $javascript_text, $data_text); |
| 1200 | + return array($form_text, $javascript_text, $data_text, $form_page_title); |
1069 | 1201 | } |
1070 | 1202 | |
1071 | 1203 | function formFieldHTML($form_field, $cur_value) { |