1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - MoinForms library 4 5 @copyright: 2012, 2013 by Paul Boddie <paul@boddie.org.uk> 6 @license: GNU GPL (v2 or later), see COPYING.txt for details. 7 """ 8 9 from compiler import parse 10 from compiler.ast import Const, Dict, Discard, List, Module, Stmt 11 from MoinMoin.action import do_show 12 from MoinMoin.Page import Page 13 from MoinMoin import security, wikiutil 14 from MoinSupport import * 15 from ItemSupport import ItemStoreBase, getSubpageItemStoreForPage, \ 16 getDirectoryItemStoreForPage 17 import re 18 19 try: 20 from cStringIO import StringIO 21 except ImportError: 22 from StringIO import StringIO 23 24 __version__ = "0.1" 25 26 form_field_regexp_str = r"<<Form(Field|Message)\((.*?)\)>>" 27 form_field_regexp = re.compile(form_field_regexp_str, re.DOTALL) 28 29 class MoinFormDataError(Exception): 30 31 "An exception indicating a problem with form data." 32 33 pass 34 35 # Common action functionality. 36 37 class MoinFormHandlerAction: 38 39 "A handler action that can be specialised for individual forms." 40 41 def __init__(self, pagename, request): 42 self.pagename = pagename 43 self.request = request 44 self.access_handler = None 45 self.attributes = None 46 47 def getAccessHandler(self): 48 49 """ 50 Return an access handler for the form whose attributes have been 51 obtained and stored in this instance. 52 """ 53 54 return FormAccess(self.pagename, self.request, self.attributes) 55 56 def processForm(self): 57 58 """ 59 Interpret the request details and modify them according to the structure 60 of the interpreted information. 61 """ 62 63 _ = self.request.getText 64 65 # Get the form fields and obtain the hierarchical field structure. 66 67 form = get_form(self.request) 68 fields = getFields(form, remove=True) 69 70 # Detect any request to load data. 71 72 if fields.has_key("load"): 73 try: 74 number = int(fields["load"][0]) 75 except ValueError: 76 fields = {} 77 else: 78 self.attributes, text = self.getFormForFragment(fields) 79 self.access_handler = self.getAccessHandler() 80 81 # Attempt to load the form. 82 83 try: 84 headers, fields = self.loadFields(number) 85 86 # Absent or inaccessible forms will result in an IndexError. 87 88 except IndexError: 89 self.request.theme.add_msg(_("The stored data for this form cannot be accessed."), "error") 90 do_show(self.pagename, self.request) 91 return 92 93 # Bad data will result in a MoinFormDataError. 94 95 except MoinFormDataError: 96 self.request.theme.add_msg(_("The stored data for this form is in the wrong format."), "error") 97 do_show(self.pagename, self.request) 98 return 99 100 # Otherwise, process any supplied data. 101 102 else: 103 # Modify and validate the form. 104 105 self.modifyFields(fields) 106 107 # Get the form definition. 108 109 self.attributes, text = self.getFormForFragment(fields) 110 self.access_handler = self.getAccessHandler() 111 structure = getFormStructure(text, self.request) 112 113 # Check the permissions on the form. 114 115 if not self.checkPermissions("write"): 116 self.request.theme.add_msg(_("You do not appear to have access to this form."), "error") 117 do_show(self.pagename, self.request) 118 return 119 120 # Without any form definition, the page is probably the wrong one. 121 122 if not structure: 123 self.request.theme.add_msg(_("This page does not provide a form."), "error") 124 do_show(self.pagename, self.request) 125 return 126 127 # With a form definition, attempt to validate the fields. 128 129 if self.validateFields(fields, structure): 130 if self.shouldFinish(fields): 131 self.finished(fields, form) 132 return 133 134 self.unfinished(fields, form) 135 136 def finished(self, fields, form): 137 138 "Handle the finished 'fields' and 'form'." 139 140 self.storeFields(fields) 141 self.unfinished(fields, form) 142 143 def unfinished(self, fields, form): 144 145 "Handle the unfinished 'fields' and 'form'." 146 147 # Serialise and show the form. 148 149 self.serialiseFields(fields, form) 150 do_show(self.pagename, self.request) 151 152 def shouldFinish(self, fields): 153 154 """ 155 Subject to the attributes stored for the form in this instance, return 156 whether any field referenced by the "finishing" attribute is present 157 and thus indicate whether the form handling should finish. 158 """ 159 160 finishing = self.attributes.has_key("finishing") and self.attributes["finishing"].split(",") 161 162 if finishing: 163 for name in finishing: 164 if fields.has_key(name): 165 return True 166 167 return False 168 169 def getFormForFragment(self, fields): 170 171 "Return the attributes and text of the form being handled." 172 173 fragment = fields.get("fragment", [None])[0] 174 text = Page(self.request, self.pagename).get_raw_body() 175 return getFormForFragment(text, fragment) 176 177 def checkPermissions(self, action): 178 179 """ 180 Check the permissions of the user against any restrictions specified in 181 the form's 'attributes'. 182 """ 183 184 return self.access_handler.checkPermissions(action) 185 186 def validateFields(self, fields, structure): 187 188 """ 189 Validate the given 'fields' using the given form 'structure', 190 introducing error fields where the individual fields do not conform to 191 their descriptions. 192 """ 193 194 return self.validateFieldsUsingStructure(fields, structure) 195 196 def validateFieldsUsingStructure(self, fields, structure): 197 198 "Validate the given 'fields' using the given 'structure'." 199 200 _ = self.request.getText 201 valid = True 202 203 for key, definition in structure.items(): 204 value = fields.get(key) 205 206 # Enter form sections and validate them. 207 208 if isinstance(definition, dict): 209 if value: 210 for element in getSectionElements(value): 211 valid = self.validateFieldsUsingStructure(element, structure[key]) and valid 212 213 # Validate individual fields. 214 215 elif structure.has_key(key): 216 path, dictpage, label, section, field_args, allowed_values = definition 217 errors = [] 218 219 # Test for obligatory values. 220 221 if not value or not value[0]: 222 if field_args.get("required"): 223 224 # Detect new parts of the structure and avoid producing 225 # premature error messages. 226 227 if not fields.has_key("_new"): 228 errors.append(_("This field must be filled out.")) 229 else: 230 valid = False 231 else: 232 # Test for unacceptable values. 233 234 if allowed_values and set(value).difference(allowed_values): 235 errors.append(_("At least one of the choices is not acceptable.")) 236 237 # Test the number of values. 238 239 if field_args.get("type") == "select": 240 if field_args.has_key("maxselected"): 241 if len(value) > int(field_args["maxselected"]): 242 errors.append(_("Incorrect number of choices given: need %s.") % field_args["maxselected"]) 243 244 if errors: 245 fields["%s-error" % key] = errors 246 valid = False 247 248 return valid 249 250 def serialiseFields(self, fields, form, path=None): 251 252 """ 253 Serialise the given 'fields' to the given 'form', using the given 'path' 254 to name the entries. 255 """ 256 257 for key, value in fields.items(): 258 259 # Serialise sections. 260 261 if isinstance(value, dict): 262 for index, element in enumerate(getSectionElements(value)): 263 element_ref = "%s$%s" % (key, index) 264 265 self.serialiseFields(element, form, 266 path and ("%s/%s" % (path, element_ref)) or element_ref 267 ) 268 269 # Serialise fields. 270 271 else: 272 form[path and ("%s/%s" % (path, key)) or key] = value 273 274 def modifyFields(self, fields): 275 276 "Modify the given 'fields', removing and adding items." 277 278 # First, remove fields. 279 280 for key in fields.keys(): 281 if key.startswith("_remove="): 282 self.removeField(key[8:], fields) 283 284 # Then, add fields. 285 286 for key in fields.keys(): 287 if key.startswith("_add="): 288 self.addField(key[5:], fields) 289 290 def removeField(self, path, fields): 291 292 """ 293 Remove the section element indicated by the given 'path' from the 294 'fields'. 295 """ 296 297 section, (name, index) = getSectionForPath(path, fields) 298 try: 299 del section[name][index] 300 except KeyError: 301 pass 302 303 def addField(self, path, fields): 304 305 """ 306 Add a section element indicated by the given 'path' to the 'fields'. 307 """ 308 309 section, (name, index) = getSectionForPath(path, fields) 310 placeholder = {"_new" : ""} 311 312 if section.has_key(name): 313 indexes = section[name].keys() 314 max_index = max(map(int, indexes)) 315 section[name][max_index + 1] = placeholder 316 else: 317 max_index = -1 318 section[name] = {0 : placeholder} 319 320 # Storage of form submissions. 321 322 def storeFields(self, fields): 323 324 """ 325 Store the given 'fields' as a Python object representation with some 326 metadata headers. 327 """ 328 329 headers = ["Form-Page: %s" % self.pagename] 330 if self.attributes.has_key("fragment"): 331 headers.append("Form-Fragment: %s" % self.attributes["fragment"]) 332 333 item = "%s\n\n%s" % ("\n".join(headers), repr(fields)) 334 335 store = FormStore(self.access_handler) 336 store.append(item) 337 338 def loadFields(self, number): 339 340 "Load the fields associated with the given submission 'number'." 341 342 store = FormStore(self.access_handler) 343 return loadFields(store, number) 344 345 def loadFields(store, number): 346 347 """ 348 Load the fields from the 'store' that are associated with the given 349 submission 'number', returning the metadata headers and field structure. 350 """ 351 352 f = StringIO(store[number]) 353 354 headers = [] 355 lines = [] 356 357 # Find all lines before a blank line, marking the end of any headers. 358 359 line = f.readline() 360 while line.strip(): 361 lines.append(line) 362 line = f.readline() 363 364 # Get the remaining text. 365 366 text = f.read() 367 368 # If there were headers, converted the recorded lines. 369 370 if text: 371 for line in lines: 372 headers.append(line.strip().split(":", 1)) 373 374 # Otherwise, rewind to obtain the entire item text for field data. 375 376 else: 377 f.seek(0) 378 text = f.read() 379 380 # Check the text and evaluate it if it is well-formed. 381 382 module = parse(text) 383 384 if checkStoredFormData(module): 385 return headers, eval(text) 386 else: 387 raise MoinFormDataError, text 388 389 def checkStoredFormData(node): 390 391 """ 392 Check the syntax 'node' and its descendants for suitability as parts of 393 a field definition. 394 """ 395 396 have_child = False 397 398 for child in node.getChildNodes(): 399 have_child = True 400 if isinstance(child, Const): 401 pass 402 elif not isinstance(child, (Dict, Discard, List, Module, Stmt)) or not checkStoredFormData(child): 403 return False 404 405 return have_child 406 407 class FormAccess: 408 409 "A means of checking access to form data." 410 411 def __init__(self, pagename, request, attributes): 412 self.pagename = pagename 413 self.request = request 414 self.attributes = attributes 415 416 def checkPermissions(self, action): 417 418 """ 419 Check the permissions of the user against any restrictions specified in 420 the form's 'attributes'. 421 """ 422 423 user = self.request.user 424 425 # Use the access definition if one is given. 426 427 if self.attributes.has_key("access"): 428 access = self.attributes["access"] 429 acl = security.AccessControlList(self.request.cfg, [access]) 430 policy = lambda request, pagename, username, action: acl.may(request, username, action) 431 432 # Otherwise, use the page permissions. 433 434 else: 435 policy = security._check 436 437 # The "read" action is only satisfied by the "admin" role. 438 439 return user and ( 440 action != "read" and policy(self.request, self.pagename, user.name, action) or 441 action == "read" and policy(self.request, self.pagename, user.name, "admin") 442 ) 443 444 class FormStore(ItemStoreBase): 445 446 "A form-specific storage mechanism." 447 448 def __init__(self, handler): 449 450 "Initialise the store with the form 'handler'." 451 452 self.handler = handler 453 page = Page(handler.request, handler.pagename) 454 fragment = handler.attributes.get("fragment") 455 suffix = fragment and ("_%s" % fragment) or "" 456 formdir = wikiutil.quoteWikinameFS("form%s" % suffix) 457 lockdir = wikiutil.quoteWikinameFS("lock%s" % suffix) 458 459 # Use an alternative store type if indicated. 460 461 storetype = handler.attributes.get("storetype") 462 if storetype == "subpage": 463 store = getSubpageItemStoreForPage(page, "form_locks/%s" % lockdir) 464 else: 465 store = getDirectoryItemStoreForPage(page, "forms/%s" % formdir, "form_locks/%s" % lockdir) 466 ItemStoreBase.__init__(self, page, store) 467 468 def can_write(self): 469 470 """ 471 Permit writing of form data using the form attributes or page 472 permissions. 473 """ 474 475 return self.handler.checkPermissions("write") 476 477 def can_read(self): 478 479 """ 480 Permit reading of form data using the form attributes or page 481 permissions. 482 """ 483 484 return self.handler.checkPermissions("read") 485 486 # Form and field information. 487 488 def getFormStructure(text, request, path=None, structure=None): 489 490 """ 491 For the given form 'text' and using the 'request', return details of the 492 form for the section at the given 'path' (or the entire form if 'path' is 493 omitted), populating the given 'structure' (or populating a new structure if 494 'structure' is omitted). 495 """ 496 497 if structure is None: 498 structure = {} 499 500 for format, attributes, body in getFragments(text, True): 501 502 # Get field details at the current level. 503 504 if format is None: 505 structure.update(getFormFields(body, path, request)) 506 507 # Where a section is found, get details from within the section. 508 509 elif format == "form": 510 if attributes.has_key("section"): 511 section_name = attributes["section"] 512 section = structure[section_name] = {} 513 getFormStructure(body, request, path and ("%s/%s" % (path, section_name)) or section_name, section) 514 elif attributes.has_key("message"): 515 getFormStructure(body, request, path, structure) 516 elif attributes.has_key("not-message"): 517 getFormStructure(body, request, path, structure) 518 519 # Get field details from other kinds of region. 520 521 elif format != "form": 522 getFormStructure(body, request, path, structure) 523 524 return structure 525 526 def getFormForFragment(text, fragment=None): 527 528 """ 529 Return the form region from the given 'text' for the specified 'fragment'. 530 If no fragment is specified, the first form region is returned. The form 531 region is described using a tuple containing the attributes for the form 532 and the body text of the form. 533 """ 534 535 for format, attributes, body in getFragments(text): 536 if format == "form" and (not fragment or attributes.get("fragment") == fragment): 537 return attributes, body 538 539 return {}, None 540 541 def getFieldArguments(field_definition): 542 543 "Return the parsed arguments from the given 'field_definition' string." 544 545 field_args = {} 546 547 for field_arg in field_definition.split(): 548 if field_arg == "required": 549 field_args[field_arg] = True 550 continue 551 552 # Record the key-value details. 553 554 try: 555 argname, argvalue = field_arg.split("=", 1) 556 field_args[argname] = argvalue 557 558 # Single keywords are interpreted as type descriptions. 559 560 except ValueError: 561 if not field_args.has_key("type"): 562 field_args["type"] = field_arg 563 564 return field_args 565 566 # Common formatting functions. 567 568 def getFormOutput(text, fields, form_fragment=None, path=None, fragment=None, repeating=None, index=None): 569 570 """ 571 Combine regions found in the given 'text' and then return them as a single 572 block. The reason for doing this, as opposed to just passing each region to 573 a suitable parser for formatting, is that form sections may break up 574 regions, and such sections may not define separate subregions but instead 575 act as a means of conditional inclusion of text into an outer region. 576 577 The given 'fields' are used to populate fields provided in forms and to 578 control whether sections are populated or not. 579 580 The optional 'form_fragment' is used to indicate the form to which the 581 fields belong. 582 583 The optional 'path' is used to adjust form fields to refer to the correct 584 part of the form hierarchy. 585 586 The optional 'fragment' is used to indicate the form being output. If this 587 value is different to 'form_fragment', the structure of the form should not 588 be influenced by the 'fields'. 589 590 The optional 'repeating' and 'index' is used to refer to individual values 591 of a designated field. 592 """ 593 594 this_form = fragment and form_fragment == fragment or not fragment and not form_fragment 595 596 output = [] 597 section = fields 598 599 for region in getRegions(text, True): 600 format, attributes, body, header, close = getFragmentFromRegion(region) 601 602 # Adjust any FormField macros to use hierarchical names. 603 604 if format is None: 605 output.append((path or fragment or repeating) and 606 adjustFormFields(body, path, fragment, repeating, index) or body) 607 608 # Include form sections only if fields exist for those sections. 609 610 elif format == "form": 611 section_name = attributes.get("section") 612 message_name = attributes.get("message") 613 absent_message_name = attributes.get("not-message") 614 615 # Ignore sections not related to the supplied field data. 616 617 if not this_form: 618 pass 619 620 # Sections are groups of fields in their own namespace. 621 622 elif section_name and section.has_key(section_name): 623 624 # Iterate over the section contents ignoring the given indexes. 625 626 for index, element in enumerate(getSectionElements(section[section_name])): 627 element_ref = "%s$%s" % (section_name, index) 628 629 # Get the output for the section. 630 631 output.append(getFormOutput(body, element, form_fragment, 632 path and ("%s/%s" % (path, element_ref)) or element_ref, fragment)) 633 634 # Message regions are conditional on a particular field and 635 # reference the current namespace. 636 637 elif message_name and section.has_key(message_name): 638 639 if attributes.get("repeating"): 640 for index in range(0, len(section[message_name])): 641 output.append(getFormOutput(body, section, form_fragment, path, fragment, message_name, index)) 642 else: 643 output.append(getFormOutput(body, section, form_fragment, path, fragment)) 644 645 # Not-message regions are conditional on a particular field being 646 # absent. They reference the current namespace. 647 648 elif absent_message_name and not section.has_key(absent_message_name): 649 output.append(getFormOutput(body, section, form_fragment, path, fragment)) 650 651 # Inspect and include other regions. 652 653 else: 654 output.append(header) 655 output.append(getFormOutput(body, section, form_fragment, path, fragment, repeating, index)) 656 output.append(close) 657 658 return "".join(output) 659 660 def getFormFields(body, path, request): 661 662 "Return a dictionary of fields from the given 'body' at the given 'path'." 663 664 fields = {} 665 cache = {} 666 type = None 667 668 for i, match in enumerate(form_field_regexp.split(body)): 669 state = i % 3 670 671 if state == 1: 672 type = match 673 elif state == 2 and type == "Field": 674 args = {} 675 676 # Obtain the macro arguments, adjusted to consider the path. 677 678 name, path, dictpage, label, section, fragment = \ 679 getMacroArguments(adjustMacroArguments(parseMacroArguments(match), path)) 680 681 # Obtain field information from the cache, if possible. 682 683 cache_key = (name, dictpage) 684 685 if cache.has_key(cache_key): 686 field_args, allowed_values = cache[cache_key] 687 688 # Otherwise, obtain field information from any WikiDict. 689 690 else: 691 field_args = {} 692 allowed_values = None 693 694 if dictpage: 695 wikidict = getWikiDict(dictpage, request) 696 if wikidict: 697 field_definition = wikidict.get(name) 698 if field_definition: 699 field_args = getFieldArguments(field_definition) 700 if field_args.has_key("source"): 701 sourcedict = getWikiDict(field_args["source"], request) 702 if sourcedict: 703 allowed_values = sourcedict.keys() 704 705 cache[cache_key] = field_args, allowed_values 706 707 # Store the field information. 708 709 fields[name] = path, dictpage, label, section, field_args, allowed_values 710 711 return fields 712 713 def adjustFormFields(body, path, fragment, repeating=None, index=None): 714 715 """ 716 Return a version of the 'body' with the names in FormField macros updated to 717 incorporate the given 'path' and 'fragment'. If 'repeating' is specified, 718 any field with such a name will be adjusted to reference the value with the 719 given 'index'. 720 """ 721 722 result = [] 723 type = None 724 725 for i, match in enumerate(form_field_regexp.split(body)): 726 state = i % 3 727 728 # Reproduce normal text as is. 729 730 if state == 0: 731 result.append(match) 732 733 # Capture the macro type. 734 735 elif state == 1: 736 type = match 737 738 # Substitute the macro and modified arguments. 739 740 else: 741 result.append("<<Form%s(%s)>>" % (type, 742 quoteMacroArguments( 743 adjustMacroArguments( 744 parseMacroArguments(match), path, fragment, repeating, index 745 ) 746 ) 747 )) 748 749 return "".join(result) 750 751 def adjustMacroArguments(args, path, fragment=None, repeating=None, index=None): 752 753 """ 754 Adjust the given 'args' so that the path incorporates the given 755 'path' and 'fragment', returning a new list containing the revised path, 756 fragment and remaining arguments. If 'repeating' is specified, any field 757 with such a name will be adjusted to reference the value with the given 758 'index'. 759 """ 760 761 if not path and not fragment and not repeating: 762 return args 763 764 result = [] 765 old_path = None 766 found_name = None 767 768 for name, value in args: 769 if name == "path": 770 old_path = value 771 elif name == "fragment" and fragment: 772 pass 773 else: 774 result.append((name, value)) 775 776 # Remember any explicitly given name or where a keyword appears. 777 778 if name == "name" or name is None and found_name is None: 779 found_name = value 780 781 if path: 782 qualified = old_path and ("%s/%s" % (old_path, path)) or path 783 result.append(("path", qualified)) 784 785 if fragment: 786 result.append(("fragment", fragment)) 787 788 if repeating and repeating == found_name: 789 result.append(("index", index)) 790 791 return result 792 793 def getMacroArguments(parsed_args): 794 795 "Return the macro arguments decoded from 'parsed_args'." 796 797 found_name = None 798 path = None 799 dictpage = None 800 label = None 801 section = None 802 fragment = None 803 804 for name, value in parsed_args: 805 if name == "name": 806 found_name = value 807 808 elif name == "path": 809 path = value 810 811 elif name == "dict": 812 dictpage = value 813 814 elif name == "label": 815 label = value 816 817 elif name == "section": 818 section = value 819 820 elif name == "fragment": 821 fragment = value 822 823 # Keywords are interpreted as certain kinds of values. 824 825 elif name is None: 826 if found_name is None: 827 found_name = value 828 829 elif dictpage is None: 830 dictpage = value 831 832 return found_name, path, dictpage, label, section, fragment 833 834 def getFields(d, remove=False): 835 836 """ 837 Return the form fields hierarchy for the given dictionary 'd'. If the 838 optional 'remove' parameter is set to a true value, remove the entries for 839 the fields from 'd'. 840 """ 841 842 fields = {} 843 844 for key, value in d.items(): 845 846 # Detect modifying fields. 847 848 if key.find("=") != -1: 849 fields[key] = value 850 if remove: 851 del d[key] 852 continue 853 854 # Reproduce the original hierarchy of the fields. 855 856 section = fields 857 parts = getPathDetails(key) 858 859 for name, index in parts[:-1]: 860 861 # Add an entry for instances of the section. 862 863 if not section.has_key(name): 864 section[name] = {} 865 866 # Add an entry for the specific instance of the section. 867 868 if not section[name].has_key(index): 869 section[name][index] = {} 870 871 section = section[name][index] 872 873 section[parts[-1][0]] = value 874 875 if remove: 876 del d[key] 877 878 return fields 879 880 def getPathDetails(path): 881 882 """ 883 Return the given 'path' as a list of (name, index) tuples providing details 884 of section instances, with any specific field appearing as the last element 885 and having the form (name, None). 886 """ 887 888 parts = [] 889 890 for part in path.split("/"): 891 try: 892 name, index = part.split("$", 1) 893 index = int(index) 894 except ValueError: 895 name, index = part, None 896 897 parts.append((name, index)) 898 899 return parts 900 901 def getSectionForPath(path, fields): 902 903 """ 904 Obtain the section indicated by the given 'path' from the 'fields', 905 returning a tuple of the form (parent section, (name, index)), where the 906 parent section contains the referenced section, where name is the name of 907 the referenced section, and where index, if not None, is the index of a 908 specific section instance within the named section. 909 """ 910 911 parts = getPathDetails(path) 912 section = fields 913 914 for name, index in parts[:-1]: 915 section = fields[name][index] 916 917 return section, parts[-1] 918 919 def getSectionElements(section_elements): 920 921 "Return the given 'section_elements' as an ordered collection." 922 923 keys = map(int, section_elements.keys()) 924 keys.sort() 925 926 elements = [] 927 928 for key in keys: 929 elements.append(section_elements[key]) 930 931 return elements 932 933 # Parser-related formatting functions. 934 935 def formatForm(text, request, fmt, attrs=None, write=None): 936 937 """ 938 Format the given 'text' using the specified 'request' and formatter 'fmt'. 939 The optional 'attrs' can be used to control the presentation of the form. 940 941 If the 'write' parameter is specified, use it to write output; otherwise, 942 write output using the request. 943 """ 944 945 write = write or request.write 946 page = request.page 947 948 form = get_form(request) 949 form_fragment = form.get("fragment", [None])[0] 950 fields = getFields(form) 951 952 # Prepare the query string for the form action URL. 953 954 queryparams = [] 955 956 for argname, default in [("fragment", None), ("action", "MoinFormHandler")]: 957 if attrs and attrs.has_key(argname): 958 queryparams.append("%s=%s" % (argname, attrs[argname])) 959 elif default: 960 queryparams.append("%s=%s" % (argname, default)) 961 962 querystr = "&".join(queryparams) 963 fragment = attrs.get("fragment") 964 965 write(fmt.rawHTML('<form method="post" action="%s%s"%s>' % ( 966 escattr(page.url(request, querystr)), 967 fragment and ("#%s" % escattr(fragment)) or "", 968 fragment and (' id="%s"' % escattr(fragment)) or "" 969 ))) 970 971 # Obtain page text for the form, incorporating subregions and applicable 972 # sections. 973 974 output = getFormOutput(text, fields, form_fragment=form_fragment, fragment=fragment) 975 write(formatText(output, request, fmt, inhibit_p=False)) 976 977 write(fmt.rawHTML('</form>')) 978 979 def formatFormForOutputType(text, request, mimetype, attrs=None, write=None): 980 981 """ 982 Format the given 'text' using the specified 'request' for the given output 983 'mimetype'. 984 985 The optional 'attrs' can be used to control the presentation of the form. 986 987 If the 'write' parameter is specified, use it to write output; otherwise, 988 write output using the request. 989 """ 990 991 write = write or request.write 992 993 if mimetype == "text/html": 994 write('<html>') 995 write('<body>') 996 fmt = request.html_formatter 997 fmt.setPage(request.page) 998 formatForm(text, request, fmt, attrs, write) 999 write('</body>') 1000 write('</html>') 1001 1002 # vim: tabstop=4 expandtab shiftwidth=4