xowiki::FormPage instproc www-edit (public)
<instance of xowiki::FormPage> www-edit \ [ -validation_errors validation_errors ] \ [ -disable_input_fields disable_input_fields ] [ -view on|off ]
Defined in /var/www/openacs.org/packages/xowiki/tcl/xowiki-www-procs.tcl
This web-callable method renders a form page in "edit" mode (i.e. provide input fields). The following query parameters can be used to influene the results "return_url", "title", "detail_link", "text", and "description".
- Switches:
- -validation_errors (optional)
- -disable_input_fields (optional, defaults to
"0"
)- -view (optional, boolean, defaults to
"true"
)- Testcases:
- create_workflow_with_instance
Source code: #:log "edit [self args]" :include_header_info -prefix form_edit if {[::xo::cc mobile]} { :include_header_info -prefix mobile } set form [:get_form] set anon_instances [:get_anon_instances] #:log form=$form #:log anon_instances=$anon_instances set field_names [:field_names -form $form] #:log field_names=$field_names set form_fields [:create_form_fields $field_names] #foreach f0 $form_fields { # ns_log notice "... created ff [$f0 name] [$f0 info class] '[$f0 value]'" #} if {$form eq ""} { # # Since we have no form, we create it on the fly # from the template variables and the form field specifications. # set form "<form></form>" set formgiven 0 } else { set formgiven 1 } #:log formgiven=$formgiven # check name field: # - if it is for anon instances, hide it, # - if it is required but hidden, show it anyway # (might happen, when e.g. set via @cr_fields ... hidden) set name_field [:lookup_form_field -name _name $form_fields] if {$anon_instances} { #$name_field config_from_spec hidden } else { if {[$name_field istype ::xowiki::formfield::hidden] && [$name_field required] == true } { $name_field config_from_spec text,required $name_field type text } } # # Include _text only, if explicitly needed (in form # needed(_text))". # if {![dict exists ${:__field_needed} _text]} { #:msg "setting text hidden" set f [:lookup_form_field -name _text $form_fields] $f config_from_spec hidden } if {[:exists_form_parameter __disabled_fields]} { # # Disable some form-fields since these are disabled in the form # as well. # foreach name [:form_parameter __disabled_fields:0..n] { set f [:lookup_form_field -name $name $form_fields] $f set_disabled true } } #:show_fields $form_fields #:log "__form_action [:form_parameter __form_action {}]" if {[:form_parameter __form_action ""] eq "save-form-data"} { # # We want to save the form data, so we have to validate. # #:log "we have to validate" # # In case we are triggered internally, we might not have a # a connection. Therefore, do not validate the CSRF token. # if {![::${:package_id} exists __batch_mode]} { security::csrf::validate } lassign [:get_form_data $form_fields] validation_errors category_ids if {$validation_errors != 0} { # # We have validation errors. # #:log "$validation_errors validation errors in $form_fields" #foreach f $form_fields { :log "$f: [$f name] '[$f set value]' err: [$f error_msg] " } # # In case we are triggered internally, we might not have a # a connection, so we don't present the form with the # error messages again, but we return simply the validation # problems. # if {[::${:package_id} exists __batch_mode]} { set errors [list] foreach f $form_fields { if {[$f error_msg] ne ""} { lappend errors [list field [$f name] value [$f set value] error [$f error_msg]] } } set evaluation_errors "" if {[::${:package_id} exists __evaluation_error]} { set evaluation_errors "\nEvaluation error: [::${:package_id} set __evaluation_error]" ::${:package_id} unset __evaluation_error } error "[llength $errors] validation error(s): $errors $evaluation_errors" } # # Reset the name in error cases to the original one. # set :name [:form_parameter __object_name:signed,convert] } else { # # We have no validation errors, so we can save the content. # :save_data -use_given_publish_date [expr {"_publish_date" in $field_names}] [::xo::cc form_parameter __object_name:signed,convert ""] $category_ids # # The data might have references. Perform the rendering here to compute # the references instead on every view (which would be safer, but slower). This is # roughly the counterpart to edit_data and save_data in ad_forms. # set content [:render -update_references all] #:log "after save refs=[expr {[info exists :references]?${:references} : {NONE}}]" set redirect_method [:form_parameter __form_redirect_method:wordchar "view"] #:log "redirect_method $redirect_method" if {$redirect_method eq "__none"} { return } else { if {$redirect_method ne "view"} { set qp "?m=$redirect_method" } else { set qp "" } set url [:pretty_link]$qp # # The method query_parameter uses now "::xo::cc set_parameter ...." # with highest precedence # set return_url [::${:package_id} query_parameter return_url:localurl $url] #:log "${:name}: url=$url, return_url=$return_url" ::${:package_id} returnredirect $return_url return } } } elseif {[:form_parameter __form_action ""] eq "view-form-data" && ![info exists :__feedback_mode] } { # # We have nothing to save (maybe everything is read-only). Check # __feedback_mode to prevent recursive loops. # set redirect_method [:form_parameter __form_redirect_method:wordchar "view"] #:log "__redirect_method=$redirect_method" return [:www-view] } else { # # Build the input form and display the current values. # #:log "form_action is something different: <[:form_parameter __form_action {}]>" if {[:is_new_entry ${:name}]} { set :creator [::xo::get_user_name [::xo::cc user_id]] set :nls_language [::${:package_id} default_locale] } #array set __ia ${:instance_attributes} :load_values_into_form_fields $form_fields foreach f $form_fields { set ff([$f name]) $f } # # For named entries, just set the entry fields to empty, # without changing the instance variables # #:log "my is_new_entry ${:name} = [:is_new_entry ${:name}]" if {[:is_new_entry ${:name}]} { if {$anon_instances} { set basename [::xowiki::autoname basename [${:page_template} name]] set name [::xowiki::autoname new -name $basename -parent_id ${:parent_id}] #:log "generated name=$name, page_template-name=[${:page_template} name]" $ff(_name) value $name } else { $ff(_name) value [$ff(_name) default] } if {![$ff(_title) istype ::xowiki::formfield::hidden]} { $ff(_title) value [$ff(_title) default] } foreach param [list title detail_link:localurl text description] { regexp {^([^:]+):?} $param . var if {[:exists_query_parameter $var]} { set value [:query_parameter $param] switch -- $var { detail_link { set f [:lookup_form_field -name $var $form_fields] $f value [$f convert_to_external $value] } title - text - description { set f [:lookup_form_field -name _$var $form_fields] } } $f value [$f convert_to_external $value] } } } $ff(_name) set transmit_field_always 1 $ff(_nls_language) set transmit_field_always 1 } # # Some final sanity checks. # :form_fields_sanity_check $form_fields :post_process_form_fields $form_fields # # "dom parse -html" has two problems with ADP tags like "<adp:icon ...>": # a) If the tag name contains a colon or underscore, the tag is # treated like plain text, i.e. "<" and ">" are converted into # HTML entities. # b) These tags have to be closed "<adp:icon ...>" is invalid. # Several existomg ADP tags have not closing tag. # # Therefore, we resolve the ADP tags before parsing the text by # tdom. There should be some framework support to do this in # general, but until we have this, resolve this problem here locally. # set form [::template::adp_parse_tags [:substitute_markup $form]] # # The following command would be correct, but does not work due to a bug in # tdom. # set form [:regsub_eval # [template::adp_variable_regexp] $form # {:form_field_as_html -mode edit "\\\1" "\2" $form_fields}] # Due to this bug, we program around and replace the at-character # by \x03 to avoid conflict with the input and we replace these # magic chars finally with the fields resulting from tdom. set form [string map [list @ \x03] $form] #:msg form=$form dom parse -html -- $form :doc ${:doc} documentElement :root if {${:root} eq ""} { error "form '$form' is not valid" } ::xo::require_html_procs ${:root} firstChild fcn #:msg "orig fcn $fcn, root ${:root} [${:root} nodeType] [${:root} nodeName]" set formNode [lindex [${:root} selectNodes //form] 0] if {$formNode eq ""} { :msg "no form found in page [${:page_template} name]" ns_log notice "no form found in page [${:page_template} name]\n$form" set rootNode ${:root} $rootNode firstChild fcn } else { set rootNode $formNode $rootNode firstChild fcn # Normally, the root node is the formNode, fcn is the first # child (often a TEXT_NODE), but ic can be even empty. } # # Prepend some fields above the HTML contents of the form. # $rootNode insertBeforeFromScript { ::html::div { ::html::input -type hidden -name __object_name -value [::security::parameter::signed ${:name}] ::html::input -type hidden -name __form_action -value save-form-data ::html::input -type hidden -name __current_revision_id -value ${:revision_id} :extra_html_fields ::html::CSRFToken } # # Insert automatic form fields on top. # foreach att $field_names { #if {$formgiven && ![string match _* $att]} continue if {[dict exists ${:__field_in_form} $att]} continue set f [:lookup_form_field -name $att $form_fields] #:log "insert auto_field $att $f ([$f info class])" $f render_item } } $fcn # # Append some fields after the HTML contents of the form. # set button_class(wym) "" set button_class(xinha) "" set has_file 0 $rootNode appendFromScript { # append category fields foreach f $form_fields { #:msg "[$f name]: is wym? [$f has_instance_variable editor wym]" if {[string match "__category_*" [$f name]]} { $f render_item } elseif {[$f has_instance_variable editor wym]} { set button_class(wym) "wymupdate" } elseif {[$f has_instance_variable editor xinha]} { set button_class(xinha) "xinhaupdate" } if {[$f has_instance_variable type file]} { set has_file 1 } } # # Add a submit field(s) at bottom. # :render_form_action_buttons -CSSclass [string trim "$button_class(wym) $button_class(xinha)"] } if {$formNode ne ""} { if {[:exists_query_parameter "return_url"]} { set return_url [:query_parameter return_url:localurl] } else { # # When no return_url is specified and we edit a page different # from the invoked page, we use the calling page for default # redirection. We do not want to redirect to some "embedded" # object after the edit. This happens if one edits e.g. a page # through a link. # if {[::xo::cc exists invoke_object] && [::xo::cc invoke_object] ne [self] } { #:log "=== no return_url specified, using [::xo::cc url] or [[::${:package_id} context] url]" set return_url [::xo::cc url] set return_url [ad_urlencode_url $return_url] } } set m [:form_parameter __form_redirect_method:wordchar "edit"] set url [export_vars -no_base_encode -base [:action_url] {m return_url}] #:log "=== setting action <$url> for form-action my-name ${:name}" $formNode setAttribute action $url method POST role form if {$has_file} {$formNode setAttribute enctype multipart/form-data} Form add_dom_attribute_value $formNode class [${:page_template} css_class_name] } :set_form_data $form_fields if {$disable_input_fields} { # # (a) Disable explicit input fields. # foreach f $form_fields {$f set_disabled true} # # (b) Disable input in HTML-specified fields. # set disabled [Form dom_disable_input_fields $rootNode] # # Collect these variables in a hidden field to be able to # distinguish later between e.g. un unchecked checkmark and an # disabled field. Maybe, we have to add the fields from case (a) # as well. # $rootNode appendFromScript { ::html::input -type hidden -name "__disabled_fields" -value $disabled } } :post_process_dom_tree ${:doc} ${:root} $form_fields set html [${:root} asHTML] set html [:regsub_eval {(^|[^\\])\x03([[:alnum:]_:]+)\x03} $html {:form_field_as_html -mode edit "\\\1" "\2" $form_fields}] # # Replace unbalanced @ characters. # set html [string map [list \x03 @] $html] # # Handle unreported errors (in the future...). Unreported errors # might occur, when a form-field was rendered above without # "render_item". This can happen with inline rendering of the # input fields where validation errors occur. Inline rendering # happens very seldom (I know not a single occurrence in the # wild). For such cases, one should define an extra field in the # form with an idea, reparse the tree and insert the errors # there. But first look, if we find a single occurrence. # set unprocessed {} foreach f $form_fields { if {[$f set error_msg] ne "" && ![$f exists error_reported] } { ns_log notice "form-field [$f name] has unprocessed error msg '[$f set error_msg]'" #$f render_error_msg lappend unprocessed [$f name] } } #ns_log notice "=============== $unprocessed unprocessed error messages" if {[llength $unprocessed] > 0} { ad_log warning "form has [llength $unprocessed] unprocessed " "error messages in fields $unprocessed" } #:log "calling VIEW with HTML [string length $html]" if {$view} { :www-view $html } else { return $html }XQL Not present: Generic, PostgreSQL, Oracle