Index: trunk/extensions/SemanticForms/INSTALL |
— | — | @@ -1,4 +1,4 @@ |
2 | | -[[Semantic Forms 0.4.1]] |
| 2 | +[[Semantic Forms 0.4.2]] |
3 | 3 | |
4 | 4 | Contents: |
5 | 5 | * Disclaimer |
Index: trunk/extensions/SemanticForms/includes/SF_FormPrinter.inc |
— | — | @@ -4,11 +4,11 @@ |
5 | 5 | * |
6 | 6 | * @author Yaron Koren |
7 | 7 | * @author Nils Oppermann |
| 8 | + * @author Jeffrey Stuckman |
8 | 9 | */ |
9 | 10 | |
10 | 11 | class SFFormPrinter { |
11 | 12 | |
12 | | - |
13 | 13 | function formHTML($form_def, $form_submitted, $source_is_page, $existing_page_content = null, $page_title = null) { |
14 | 14 | global $wgRequest, $wgUser; |
15 | 15 | global $gTabIndex; // used to represent the current tab index in the form |
— | — | @@ -72,6 +72,8 @@ |
73 | 73 | $instance_num = 0; |
74 | 74 | $all_instances_printed = false; |
75 | 75 | $strict_parsing = false; |
| 76 | + $autocomplete_value_array = array(); |
| 77 | + $autocomplete_value_array['mappings'] = array(); |
76 | 78 | for ($section_num = 0; $section_num < count($form_def_sections); $section_num++) { |
77 | 79 | $tif = new SFTemplateInForm(); |
78 | 80 | $start_position = 0; |
— | — | @@ -269,8 +271,12 @@ |
270 | 272 | // determine whether user is a sysop by whether or not they're |
271 | 273 | // allowed to delete things - if there's a better way, please let |
272 | 274 | // me know |
273 | | - if ($is_restricted && ! $wgUser->isAllowed('delete')) { |
| 275 | + if ($is_restricted && (! $wgUser || ! $wgUser->isAllowed('delete'))) { |
| 276 | + // set background color to get around weird IE behavior (field |
| 277 | + // is disabled, but color stays white) |
274 | 278 | $gDisabledText = "disabled"; |
| 279 | + //$gDisabledText = "disabled style=\"background-color: #dddddd; border: 1px solid #ccccff;\""; |
| 280 | + //$gDisabledText = "disabled readonly"; |
275 | 281 | } |
276 | 282 | |
277 | 283 | // handle non-template fields - 'page title' and 'free text' |
— | — | @@ -343,7 +349,21 @@ |
344 | 350 | $input_name = $query_template_name . '[' . $field_name . ']'; |
345 | 351 | $new_text = SFFormPrinter::formTemplateFieldHTML($field_name, $input_name, $allow_multiple, |
346 | 352 | $cur_value, $is_mandatory, $is_hidden, $is_restricted, $input_type, $size, $num_rows, $num_cols, $no_autocomplete, |
347 | | - $autocomplete_category, $all_fields, $strict_parsing); |
| 353 | + $autocomplete_category, $all_fields, $strict_parsing, $autocomplete_value_array); |
| 354 | + |
| 355 | + // if this was field was disabled due to being 'restricted', |
| 356 | + // restore $gDisabledText back to its actual value, and add |
| 357 | + // a hidden field holding the value of this field, because |
| 358 | + // disabled inputs for some reason don't submit their value |
| 359 | + if ($is_restricted && ! $wgUser->isAllowed('delete')) { |
| 360 | + $gDisabledText = $actual_disabled_text; |
| 361 | + if ($field_name == 'free text') { |
| 362 | + $new_text .= SFFormPrinter::hiddenFieldHTML('free_text', '<free_text>'); |
| 363 | + } else { |
| 364 | + $new_text .= SFFormPrinter::hiddenFieldHTML($input_name, $cur_value); |
| 365 | + } |
| 366 | + } |
| 367 | + |
348 | 368 | if ($new_text) { |
349 | 369 | if (is_numeric($field_name)) { |
350 | 370 | // if the value is null, don't include it at all - |
— | — | @@ -366,8 +386,6 @@ |
367 | 387 | $start_position = $brackets_end_loc; |
368 | 388 | } |
369 | 389 | } |
370 | | - // restore back to actual value, if it changed |
371 | | - $gDisabledText = $actual_disabled_text; |
372 | 390 | } else { // tag is not one of the three allowed values |
373 | 391 | // ignore tag |
374 | 392 | $start_position = $brackets_end_loc; |
— | — | @@ -396,7 +414,7 @@ |
397 | 415 | $form_text .=<<<END |
398 | 416 | <div id="wrapper_$gTabIndex" class="multiple_template"> |
399 | 417 | $section |
400 | | - <input type="button" onclick="blankInstance('wrapper_$gTabIndex');" value="$remove_text" tabindex="$gTabIndex" /> |
| 418 | + <input type="button" onclick="removeInstance('wrapper_$gTabIndex');" value="$remove_text" tabindex="$gTabIndex" /> |
401 | 419 | </div> |
402 | 420 | |
403 | 421 | END; |
— | — | @@ -415,7 +433,6 @@ |
416 | 434 | $add_another = wfMsg('sf_editdata_addanother'); |
417 | 435 | $form_text .=<<<END |
418 | 436 | <p style="margin-left:10px;"> |
419 | | - <input type="hidden" value="0" id="numElements" /> |
420 | 437 | <p><input type="button" onclick="addInstance('starter_$query_template_name', 'main_$query_template_name', '$gTabIndex');" value="$add_another" tabindex="$gTabIndex" /></p> |
421 | 438 | |
422 | 439 | END; |
— | — | @@ -483,51 +500,130 @@ |
484 | 501 | return (num_errors == 0); |
485 | 502 | } |
486 | 503 | |
487 | | -// This Javascript code is heavily indebted to Dustin Diaz's 'add and remove |
488 | | -// HTML elements' tutorial - http://www.dustindiaz.com/add-and-remove-html-elements-dynamically-with-javascript/ |
489 | | -function addInstance(starter_div_id, main_div_id, tab_index) { |
490 | | - var starter_div = document.getElementById(starter_div_id); |
491 | | - var main_div = document.getElementById(main_div_id); |
492 | | - var numi = document.getElementById('numElements'); |
493 | | - var num = (document.getElementById('numElements').value -1)+ 2; |
494 | | - numi.value = num; |
495 | | - var newdiv = document.createElement('div'); |
496 | | - var div_id = 'div_' + num; |
497 | | - newdiv.setAttribute('id', div_id); |
498 | | - newdiv.setAttribute('class', 'multiple_template'); |
499 | | - // workaround for IE 6 bug |
500 | | - newdiv.setAttribute('className', 'multiple_template'); |
501 | | - // make internal ID unique for the relevant divs and spans, and replace |
502 | | - // the [num] index with an actual unique index |
503 | | - var orig_html = starter_div.innerHTML; |
504 | | - orig_html = orig_html.replace(/input_/g, 'input_' + num + '_'); |
505 | | - orig_html = orig_html.replace(/info_/g, 'info_' + num + '_'); |
506 | | - orig_html = orig_html.replace(/div_/g, 'div_' + num + '_'); |
507 | | - orig_html = orig_html.replace(/\[num\]/g, '[' + num + ']'); |
508 | | - newdiv.innerHTML = orig_html + '<input type="button" onclick="removeInstance(\'' + main_div_id + '\', \'' + div_id + '\');" value="$remove_text" tabindex="' + tab_index + '" />'; |
509 | | - main_div.appendChild(newdiv); |
| 504 | +var num_elements = 0; |
| 505 | + |
| 506 | +function addInstance(starter_div_id, main_div_id, tab_index) |
| 507 | +{ |
| 508 | + var starter_div = document.getElementById(starter_div_id); |
| 509 | + var main_div = document.getElementById(main_div_id); |
| 510 | + num_elements++; |
| 511 | + |
| 512 | + //Create the new instance |
| 513 | + var new_div = starter_div.cloneNode(true); |
| 514 | + var div_id = 'div_gen_' + num_elements; |
| 515 | + new_div.className = 'multiple_template'; |
| 516 | + new_div.id = div_id; |
| 517 | + new_div.style.display = 'block'; |
| 518 | + |
| 519 | + // make internal ID unique for the relevant divs and spans, and replace |
| 520 | + // the [num] index in the element names with an actual unique index |
| 521 | + var children = new_div.getElementsByTagName('*'); |
| 522 | + var x; |
| 523 | + for (x=0;x<children.length;x++) |
| 524 | + { |
| 525 | + if (children[x].name) |
| 526 | + children[x].name = children[x].name.replace(/\[num\]/g, '[' + num_elements + ']'); |
| 527 | + if (children[x].id) |
| 528 | + children[x].id = children[x].id |
| 529 | + .replace(/input_/g, 'input_' + num_elements + '_') |
| 530 | + .replace(/info_/g, 'info_' + num_elements + '_') |
| 531 | + .replace(/div_/g, 'div_' + num_elements + '_'); |
| 532 | + } |
| 533 | + |
| 534 | + //Create remove button |
| 535 | + var remove_button = document.createElement('input'); |
| 536 | + remove_button.type = 'button'; |
| 537 | + remove_button.value = '$remove_text'; |
| 538 | + remove_button.tabIndex = tab_index; |
| 539 | + remove_button.onclick = removeInstanceEventHandler(div_id); |
| 540 | + new_div.appendChild(remove_button); |
| 541 | + |
| 542 | + //Add the new instance |
| 543 | + main_div.appendChild(new_div); |
| 544 | + attachAutocompleteToAllFields(new_div); |
510 | 545 | } |
511 | 546 | |
512 | | -function removeInstance(main_div_id, this_div_id) { |
513 | | - var d = document.getElementById(main_div_id); |
514 | | - var olddiv = document.getElementById(this_div_id); |
515 | | - d.removeChild(olddiv); |
| 547 | +function removeInstanceEventHandler(this_div_id) |
| 548 | +{ |
| 549 | + return function() |
| 550 | + { |
| 551 | + removeInstance(this_div_id); |
| 552 | + }; |
516 | 553 | } |
517 | 554 | |
518 | | -// hack function - an element that's not a child of anything can't be |
519 | | -// removed, so do the next best thing: set its contents to blank and its |
520 | | -// display to 'none' |
521 | | -function blankInstance(div_id) { |
522 | | - var d = document.getElementById(div_id); |
523 | | - d.innerHTML = ""; |
524 | | - d.style.display = "none"; |
| 555 | +function removeInstance(div_id) { |
| 556 | + var olddiv = document.getElementById(div_id); |
| 557 | + olddiv.parentNode.removeChild(olddiv); |
525 | 558 | } |
526 | 559 | |
| 560 | +var autocompletestrings = new Array(); |
| 561 | +var autocompletemappings = new Array(); |
| 562 | + |
| 563 | +//Activate autocomplete functionality for every field on the document |
| 564 | +function attachAutocompleteToAllDocumentFields() |
| 565 | +{ |
| 566 | + var forms = document.getElementsByTagName("form"); |
| 567 | + var x; |
| 568 | + for (x=0;x<forms.length;x++) |
| 569 | + { |
| 570 | + if (forms[x].name == "createbox") |
| 571 | + { |
| 572 | + attachAutocompleteToAllFields(forms[x]); |
| 573 | + } |
| 574 | + } |
| 575 | +} |
| 576 | + |
| 577 | +//Activate autocomplete functionality for every field under the specified element |
| 578 | +function attachAutocompleteToAllFields(base) |
| 579 | +{ |
| 580 | + var inputs = base.getElementsByTagName("input"); |
| 581 | + var y; |
| 582 | + for (y=0;y<inputs.length;y++) |
| 583 | + { |
| 584 | + attachAutocompleteToField(inputs[y].id); |
| 585 | + } |
| 586 | +} |
| 587 | + |
| 588 | +//Activate autocomplete functionality for the specified field |
| 589 | +function attachAutocompleteToField(input_id) |
| 590 | +{ |
| 591 | + //Check input id for the proper format, to ensure this is for SF |
| 592 | + if (input_id.substr(0,6) == 'input_') |
| 593 | + { |
| 594 | + //Extract the field ID number from the input field |
| 595 | + var field_num = parseInt(input_id.substring(input_id.lastIndexOf('_') + 1, input_id.length),10); |
| 596 | + //Add the autocomplete string, if a mapping exists. |
| 597 | + if (autocompletemappings[field_num]) |
| 598 | + { |
| 599 | + var div_id = input_id.replace(/input_/g, 'div_'); |
| 600 | + new Autocompleter.Local(input_id, div_id, autocompletestrings[autocompletemappings[field_num]], {}); |
| 601 | + } |
| 602 | + } |
| 603 | +} |
| 604 | + |
| 605 | +Event.observe(window, 'load', attachAutocompleteToAllDocumentFields); |
| 606 | + |
527 | 607 | END; |
| 608 | + |
| 609 | + //Send the autocomplete values to the browser, along with the mappings of which values should apply to which fields |
| 610 | + foreach ($autocomplete_value_array as $autocomplete_key => $autocomplete_string) |
| 611 | + { |
| 612 | + if ($autocomplete_key == "mappings") |
| 613 | + { |
| 614 | + foreach ($autocomplete_string as $field_number => $field_key) |
| 615 | + { |
| 616 | + $javascript_text .= "autocompletemappings[$field_number] = '" . str_replace("'", "\'", $field_key) . "';\n"; |
| 617 | + } |
| 618 | + } |
| 619 | + else |
| 620 | + { |
| 621 | + $javascript_text .= "autocompletestrings['" . str_replace("'", "\'", $autocomplete_key) . "'] = $autocomplete_string;\n"; |
| 622 | + } |
| 623 | + } |
528 | 624 | return array($form_text, $javascript_text, $title, $data_text); |
529 | 625 | } |
530 | 626 | |
531 | | - function formTemplateFieldHTML($field_name, $input_name, $part_of_multiple, $cur_value, $is_mandatory, $is_hidden, $is_restricted, $input_type, $size, $num_rows, $num_cols, $no_autocomplete, $autocomplete_category, $all_fields, $strict_parsing) { |
| 627 | + function formTemplateFieldHTML($field_name, $input_name, $part_of_multiple, $cur_value, $is_mandatory, $is_hidden, $is_restricted, $input_type, $size, $num_rows, $num_cols, $no_autocomplete, $autocomplete_category, $all_fields, $strict_parsing, &$out_autocomplete_values) { |
532 | 628 | global $gTabIndex; |
533 | 629 | |
534 | 630 | // see if this field matches one of the fields defined for this template - |
— | — | @@ -565,19 +661,21 @@ |
566 | 662 | $the_field->autocomplete_category = $autocomplete_category; |
567 | 663 | $the_field->input_name = $input_name; |
568 | 664 | $the_field->part_of_multiple = $part_of_multiple; |
569 | | - $text = SFFormPrinter::formFieldHTML($the_field, $cur_value); |
| 665 | + $text = SFFormPrinter::formFieldHTML($the_field, $cur_value, $out_autocomplete_values); |
570 | 666 | return $text; |
571 | 667 | } |
572 | 668 | |
573 | | - function formFieldHTML($template_field, $cur_value) { |
| 669 | + function formFieldHTML($template_field, $cur_value, &$out_autocomplete_values) { |
574 | 670 | global $smwgContLang; |
575 | | - |
| 671 | + //When a field is generated that requires autocomplete, the autocomplete string will be added to out_autocomplete_values. Then, at the end, |
| 672 | + //Javascript will be generated to activate autocomplete field at once. |
| 673 | + |
576 | 674 | if ($template_field->is_hidden) { |
577 | 675 | $text = SFFormPrinter::hiddenFieldHTML($template_field->input_name, $cur_value); |
578 | 676 | } elseif ($template_field->autocomplete_category != null) { |
579 | 677 | $size = $template_field->size; |
580 | 678 | if ($size == null) $size = 35; |
581 | | - $text = SFFormPrinter::textEntryWithAutocompleteHTML($size, $template_field->input_name, $template_field->part_of_multiple, $template_field->autocomplete_category, false, $cur_value); |
| 679 | + $text = SFFormPrinter::textEntryWithAutocompleteHTML($size, $template_field->input_name, $template_field->part_of_multiple, $template_field->autocomplete_category, false, $cur_value, $out_autocomplete_values); |
582 | 680 | } elseif ($template_field->input_type == 'text') { |
583 | 681 | $size = $template_field->size; |
584 | 682 | if ($size == null) $size = 35; |
— | — | @@ -598,7 +696,7 @@ |
599 | 697 | if ($template_field->no_autocomplete) { |
600 | 698 | $text = SFFormPrinter::textEntryHTML($size, $template_field->input_name, $cur_value); |
601 | 699 | } else { |
602 | | - $text = SFFormPrinter::textEntryWithAutocompleteHTML($size, $template_field->input_name, $template_field->part_of_multiple, $template_field->semantic_field, true, $cur_value); |
| 700 | + $text = SFFormPrinter::textEntryWithAutocompleteHTML($size, $template_field->input_name, $template_field->part_of_multiple, $template_field->semantic_field, true, $cur_value, $out_autocomplete_values); |
603 | 701 | } |
604 | 702 | } else { // input type not defined in form, and not a relation |
605 | 703 | $attr_type = $template_field->attribute_type; |
— | — | @@ -663,7 +761,7 @@ |
664 | 762 | $info_id = "info_$gTabIndex"; |
665 | 763 | |
666 | 764 | $text =<<<END |
667 | | - <input id="$input_id" tabindex="$gTabIndex" class="createboxInput" name="$input_name" type="text" |
| 765 | + <input id="$input_id" tabindex="$gTabIndex" class="createboxInput" name="$input_name" type="text" |
668 | 766 | value="$cur_value" size="$size" $gDisabledText/> |
669 | 767 | <span id="$info_id" class="error_message"></span> |
670 | 768 | |
— | — | @@ -690,23 +788,25 @@ |
691 | 789 | return $text; |
692 | 790 | } |
693 | 791 | |
694 | | - function textEntryWithAutocompleteHTML($size, $input_name, $part_of_multiple, $semantic_field_name, $is_relation, $cur_value) { |
| 792 | + function textEntryWithAutocompleteHTML($size, $input_name, $part_of_multiple, $semantic_field_name, $is_relation, $cur_value, &$out_autocomplete_values) { |
695 | 793 | global $gTabIndex, $gDisabledText; |
696 | 794 | $gTabIndex++; |
697 | 795 | |
698 | 796 | $input_id = "input_" . $gTabIndex; |
699 | 797 | $info_id = "info_" . $gTabIndex; |
700 | 798 | $div_name = "div_" . $gTabIndex; |
701 | | - $options_str = SFFormPrinter::createAutocompleteValuesString(str_replace(' ', '_', $semantic_field_name), $is_relation); |
702 | 799 | $text =<<<END |
703 | 800 | <input tabindex="$gTabIndex" id="$input_id" name="$input_name" type="text" |
704 | 801 | value="" size="$size" $gDisabledText/> |
705 | 802 | <span id="$info_id" class="error_message"></span> |
706 | 803 | <div class="page_name_auto_complete" id="$div_name" style="display:none"></div> |
707 | 804 | <script type="text/javascript"> |
708 | | -new Autocompleter.Local('$input_id', '$div_name', $options_str, {}); |
709 | 805 | |
710 | 806 | END; |
| 807 | + $options_str_key = $semantic_field_name . ',' . $is_relation; |
| 808 | + if (!$out_autocomplete_values[$options_str_key]) |
| 809 | + $out_autocomplete_values[$options_str_key] = SFFormPrinter::createAutocompleteValuesString(str_replace(' ', '_', $semantic_field_name), $is_relation); |
| 810 | + $out_autocomplete_values['mappings'][$gTabIndex] = $options_str_key; |
711 | 811 | if ($cur_value) { |
712 | 812 | $text .= "document.getElementById('$input_id').value = \"$cur_value\"\n"; |
713 | 813 | } |
— | — | @@ -880,7 +980,7 @@ |
881 | 981 | |
882 | 982 | // Much of this function is based on MediaWiki's EditPage::showEditForm() |
883 | 983 | function formBottom($target_title = null) { |
884 | | - global $wgUser, $wgRightsText, $wgParser; |
| 984 | + global $wgVersion, $wgUser, $wgRightsText, $wgParser; |
885 | 985 | global $gTabIndex, $gDisabledText; |
886 | 986 | $sk = $wgUser->getSkin(); |
887 | 987 | |
— | — | @@ -899,10 +999,20 @@ |
900 | 1000 | else |
901 | 1001 | $cancel = $sk->makeKnownLink( $target_title->getPrefixedText(), |
902 | 1002 | wfMsgExt('cancel', array('parseinline')) ); |
903 | | - $edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' )); |
| 1003 | + |
| 1004 | + //Fix so extension works with MediaWiki 1.7 |
| 1005 | + if (substr_compare($wgVersion, '1.7', 0, 3) == 0) |
| 1006 | + { |
| 1007 | + $edithelpurl = $sk->makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' )); |
| 1008 | + } |
| 1009 | + else |
| 1010 | + { |
| 1011 | + $edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' )); |
| 1012 | + } |
| 1013 | + |
904 | 1014 | $edithelp = '<a target="helpwindow" href="'.$edithelpurl.'">'. |
905 | | - htmlspecialchars( wfMsg( 'edithelp' ) ).'</a> '. |
906 | | - htmlspecialchars( wfMsg( 'newwindow' ) ); |
| 1015 | + htmlspecialchars( wfMsg( 'edithelp' ) ).'</a> '. |
| 1016 | + htmlspecialchars( wfMsg( 'newwindow' ) ); |
907 | 1017 | |
908 | 1018 | $gTabIndex++; |
909 | 1019 | $text =<<<END |
Index: trunk/extensions/SemanticForms/includes/SF_GlobalFunctions.php |
— | — | @@ -3,7 +3,7 @@ |
4 | 4 | * Global functions and constants for Semantic Forms. |
5 | 5 | */ |
6 | 6 | |
7 | | -define('SF_VERSION','0.4.1'); |
| 7 | +define('SF_VERSION','0.4.2'); |
8 | 8 | |
9 | 9 | $wgExtensionFunctions[] = 'sfgSetupExtension'; |
10 | 10 | |